ryujin 2.5.0 revision c64172211cf21344cb9b9b0f3a1266aa6400d392
Loading...
Searching...
No Matches
sparse_matrix.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3// Copyright (C) 2020 - 2026 by the ryujin authors
4//
5
6#pragma once
7
8#include <ryujin/base/compile_time_options.h>
9#include <ryujin/base/gpu.h>
11
12#include <deal.II/base/exceptions.h>
13#include <deal.II/base/partitioner.h>
14#include <deal.II/lac/affine_constraints.h>
15#include <deal.II/lac/dynamic_sparsity_pattern.h>
16
17#include <type_traits>
18
19// #define DEBUG_MPI_EXCHANGE
20
21#ifdef DEBUG_MPI_EXCHANGE
22#include <chrono>
23#endif
24
25namespace ryujin
26{
27 template <typename Number,
28 int n_comp,
29 int warp_size,
30 int simd_length,
31 typename MemorySpace = dealii::MemorySpace::Host,
32 bool writable = true>
33 class SparseMatrixView;
34
53 template <typename Number,
54 int n_comp = 1,
56 int simd_length = dealii::VectorizedArray<Number>::size()>
58 SparseMatrix<Number, n_comp, warp_size, simd_length>>
59 {
60 public:
61 static_assert(warp_size % simd_length == 0,
62 "The warp size must be an integer multiple of the SIMD "
63 "length");
64
69
73 SparseMatrix() = default;
74
81
96 void reinit(const SparsityPattern<warp_size> &sparsity,
99
103 ACCESSOR_READ_ONLY(sparsity_pattern);
104
106
110
119 template <typename MemorySpace = dealii::MemorySpace::Host>
122
130 template <typename MemorySpace = dealii::MemorySpace::Host>
132 view() const;
133
134 /*
135 * The is_resident(), copy_to_memory_space(), move_to_memory_space(),
136 * transfer_policy(), and set_transfer_policy() methods are inherited
137 * from the MirroredStorage base class.
138 */
139
141
145
149 template <typename MemorySpace>
151
156 template <typename MemorySpace>
158
164 template <typename MemorySpace>
165 void compress_on_memory_space(dealii::VectorOperation::values operation);
166
176 template <typename MemorySpace>
178
179 private:
181
185
186 const SparsityPattern<warp_size> *sparsity_pattern_ =
187 nullptr; // FIXME shared_ptr
188
189 /*
190 * Note: The storage is marked mutable so that the (logically const)
191 * copy_to_memory_space() operation can populate a mirror from within
192 * a const view() under the implicit_transfers policy.
193 */
194
195 using KokkosHost = dealii::MemorySpace::Host::kokkos_space;
196 mutable Kokkos::View<Number *, KokkosHost> data_host_;
197
198 using KokkosDefault = dealii::MemorySpace::Default::kokkos_space;
199 mutable Kokkos::View<Number *, KokkosDefault> data_default_;
200
201 /*
202 * Note: For the time being, let's assume MPI only operates on host
203 * memory. Meaning, when we populate exchange_buffer_default_ on the
204 * device, we will copy over to the host prior to sending.
205 */
206
207 mutable Kokkos::View<Number *, KokkosHost> exchange_buffer_host_;
208 mutable Kokkos::View<Number *, KokkosHost> ghost_buffer_host_;
209 mutable Kokkos::View<Number *, KokkosDefault> exchange_buffer_default_;
210
211 /*
212 * Storage primitives used by the MirroredStorage base class:
213 */
214
215 template <typename MemorySpace>
216 void allocate_storage() const;
217
218 template <typename To, typename From>
219 void deep_copy_storage() const;
220
221 template <typename MemorySpace>
222 void deallocate_storage();
223
224
225 friend class MirroredStorage<
226 SparseMatrix<Number, n_comp, warp_size, simd_length>>;
227
228 template <typename, int, int, int, typename, bool>
229 friend class SparseMatrixView;
230
232 };
233
234
249 template <typename Number,
250 int n_comp,
251 int warp_size,
252 int simd_length,
253 typename MemorySpace,
254 bool writable>
256 {
257 public:
258 static_assert(warp_size % simd_length == 0,
259 "The warp size must be an integer multiple of the SIMD "
260 "length");
261
266
267 SparseMatrixView() = default;
268
271 requires(writable);
272
274 &sparse_matrix)
275 requires(!writable);
276
285 template <bool other_writable>
286 DEAL_II_HOST_DEVICE
288 n_comp,
289 warp_size,
290 simd_length,
291 MemorySpace,
292 other_writable> &other)
293 requires(!writable && other_writable);
294
295 template <typename SparseMatrix>
296 void reinit(SparseMatrix &sparse_matrix)
297 requires(writable != std::is_const_v<SparseMatrix>);
298
302 ACCESSOR_READ_ONLY(sparsity_pattern);
303
305
309
310 /* Get scalar or tensor-valued entry: */
311
322 template <typename Number2 = Number>
323 DEAL_II_HOST_DEVICE Number2
324 read_entry(const unsigned int row, const unsigned int column_index) const;
325
337 template <typename Number2 = Number,
338 typename Tensor = dealii::Tensor<1, n_comp, Number2>>
339 DEAL_II_HOST_DEVICE Tensor
340 read_tensor(const unsigned int row, const unsigned int column_index) const;
341
342 /* Get transposed scalar or tensor-valued entry: */
343
355 template <typename Number2 = Number>
356 DEAL_II_HOST_DEVICE Number2 read_transposed_entry(
357 const unsigned int row, const unsigned int column_index) const;
358
370 template <typename Number2 = Number,
371 typename Tensor = dealii::Tensor<1, n_comp, Number2>>
372 DEAL_II_HOST_DEVICE Tensor read_transposed_tensor(
373 const unsigned int row, const unsigned int column_index) const;
374
375 /* Write scalar or tensor entry: */
376
388 template <typename Number2 = Number>
389 DEAL_II_HOST_DEVICE void
390 write_entry(const Number2 entry,
391 const unsigned int row,
392 const unsigned int column_index,
393 const bool do_streaming_store = false) const
394 requires(writable);
395
405 template <typename Number2 = Number,
406 typename Tensor = dealii::Tensor<1, n_comp, Number2>>
407 DEAL_II_HOST_DEVICE void
408 write_tensor(const Tensor &tensor,
409 const unsigned int row,
410 const unsigned int column_index,
411 const bool do_streaming_store = false) const
412 requires(writable);
413
425 template <typename Number2 = Number>
426 DEAL_II_HOST_DEVICE void add_entry(const Number2 entry,
427 const unsigned int row,
428 const unsigned int column_index) const
429 requires(writable);
430
440 template <typename Number2 = Number,
441 typename Tensor = dealii::Tensor<1, n_comp, Number2>>
442 DEAL_II_HOST_DEVICE void add_tensor(const Tensor &tensor,
443 const unsigned int row,
444 const unsigned int column_index) const
445 requires(writable);
446
448
452
454 requires(writable);
455
456 void update_ghost_rows() const
457 requires(writable);
458
459 void compress(dealii::VectorOperation::values operation) const
460 requires(writable);
461
462 private:
464
468
469 using SM = SparseMatrix<Number, n_comp, warp_size, simd_length>;
470 std::conditional_t<writable, SM *, const SM *> sparse_matrix_;
471
472 SparsityPatternView<warp_size, MemorySpace> sparsity_pattern_;
473
474 using KokkosSpace = typename MemorySpace::kokkos_space;
475 Kokkos::View<Number *, KokkosSpace> data_;
476
477 template <typename, int, int, int, typename, bool>
478 friend class SparseMatrixView;
479
481 };
482
483
484 /*
485 * Given a matrix contribution and row and column indices in (deal.II
486 * typical) global numbering, add the contribution to the sparse matrix.
487 * The function takes an @p affine_constraints object in (deal.II
488 * typical) global numbering and resolves constrained degrees of freedom
489 * prior to distributing to the matrix.
490 *
491 * @note The method will not modify the diagonal entry of constrained
492 * degrees of freedom, in contrast to the deal.II version
493 * AffineConstraints<Number>::distribute_local_to_global().
494 *
495 * @note For a vector-valued matrix with n_comp > 1 the @p cell_matrix
496 * must be a container with a subscript operator[] returning a matrix for
497 * each component.
498 */
499 template <typename Number,
500 int n_comp,
501 int warp_size,
502 int simd_length,
503 typename FullMatrix>
505 const FullMatrix &cell_matrix,
506 const std::vector<dealii::types::global_dof_index> &dof_indices_row,
507 const std::vector<dealii::types::global_dof_index> &dof_indices_column,
508 const dealii::AffineConstraints<Number> &affine_constraints,
509 SparseMatrix<Number, n_comp, warp_size, simd_length> &sparse_matrix);
510
511
512 /*
513 * Variant of the function above that takes a symmetric matrix
514 * constribution where the column and row indices are the same.
515 */
516 template <typename Number,
517 int n_comp,
518 int warp_size,
519 int simd_length,
520 typename FullMatrix,
521 typename Vector>
523 const FullMatrix &cell_matrix,
524 const std::vector<dealii::types::global_dof_index> &dof_indices,
525 const dealii::AffineConstraints<Number> &affine_constraints,
526 SparseMatrix<Number, n_comp, warp_size, simd_length> &sparse_matrix);
527
528
529#ifndef DOXYGEN
530 /*
531 * -------------------------------------------------------------------------
532 * Inline function definitions
533 * -------------------------------------------------------------------------
534 */
535
536
537 template <typename Number, int n_components, int warp_size, int simd_length>
539 const SparsityPattern<warp_size> &sparsity,
540 const TransferPolicy transfer_policy)
541 {
542 reinit(sparsity, transfer_policy);
543 }
544
545
546 template <typename Number, int n_components, int warp_size, int simd_length>
548 const SparsityPattern<warp_size> &sparsity,
549 const TransferPolicy transfer_policy)
550 {
551 /*
552 * Drop the transfer policy for the duration of the reinit so that a
553 * pinned memory space of a previous policy does not interfere:
554 */
555 this->set_transfer_policy(TransferPolicy::explicit_transfers);
556
557 this->sparsity_pattern_ = &sparsity;
558
559 const auto sparsity_view = sparsity.view();
560
561 using KokkosHost = dealii::MemorySpace::Host::kokkos_space;
562 using Aligned = Kokkos::MemoryTraits<Kokkos::Aligned>;
563
564 data_host_ = Kokkos::View<Number *, KokkosHost, Aligned>(
565 "sparse_matrix_data",
566 sparsity_view.n_nonzero_elements() * n_components);
567
568 const std::size_t n_indices = sparsity.entries_to_be_sent().size();
569
570 exchange_buffer_host_ = Kokkos::View<Number *, KokkosHost, Aligned>(
571 "sparse_matrix_exchange_buffer", n_components * n_indices);
572
573 if constexpr (have_separate_memory_spaces) {
574 const auto ghost_offset =
575 sparsity_view.template ghost_offset<n_components>();
576 const auto end_offset = sparsity_view.n_nonzero_elements() * n_components;
577
578 ghost_buffer_host_ = Kokkos::View<Number *, KokkosHost, Aligned>(
579 "sparse_matrix_ghost_buffer", end_offset - ghost_offset);
580 }
581
582 /*
583 * The matrix is resident on the host memory space only. Device
584 * storage is allocated lazily on the first copy_to_memory_space() /
585 * move_to_memory_space(); drop possibly stale device storage from a
586 * previous reinit():
587 */
588 data_default_ = {};
589 exchange_buffer_default_ = {};
590
591 this->reset_residency(/*host*/ true,
592 /*default*/ !have_separate_memory_spaces);
593
594 this->set_transfer_policy(transfer_policy);
595 }
596
597
598 template <typename Number, int n_comp, int warp_size, int simd_length>
599 template <typename MemorySpace>
600 SparseMatrixView<Number, n_comp, warp_size, simd_length, MemorySpace, true>
602 {
603 this->template prepare_write_access<MemorySpace>();
604
605 return SparseMatrixView<Number,
606 n_comp,
607 warp_size,
608 simd_length,
609 MemorySpace,
610 true>(*this);
611 }
612
613
614 template <typename Number, int n_comp, int warp_size, int simd_length>
615 template <typename MemorySpace>
616 SparseMatrixView<Number, n_comp, warp_size, simd_length, MemorySpace, false>
618 {
619 this->template prepare_read_access<MemorySpace>();
620
621 return SparseMatrixView<Number,
622 n_comp,
623 warp_size,
624 simd_length,
625 MemorySpace,
626 false>(*this);
627 }
628
629
630 template <typename Number, int n_components, int warp_size, int simd_length>
631 template <typename MemorySpace>
632 void
633 SparseMatrix<Number, n_components, warp_size, simd_length>::allocate_storage()
634 const
635 {
636 using HostSpace = dealii::MemorySpace::Host;
637 using Aligned = Kokkos::MemoryTraits<Kokkos::Aligned>;
638
639 Assert(sparsity_pattern_ != nullptr, dealii::ExcNotInitialized());
640
641 const auto sparsity_view = sparsity_pattern_->view();
642
643 /*
644 * Note: We allocate without initializing because a deep_copy_storage()
645 * always follows.
646 */
647
648 const std::size_t n_data =
649 sparsity_view.n_nonzero_elements() * n_components;
650 const std::size_t n_exchange =
651 n_components * sparsity_pattern_->entries_to_be_sent().size();
652
653 if constexpr (std::is_same_v<MemorySpace, HostSpace>) {
654 data_host_ = Kokkos::View<Number *, KokkosHost, Aligned>(
655 Kokkos::view_alloc(Kokkos::WithoutInitializing, "sparse_matrix_data"),
656 n_data);
657
658 } else {
659 data_default_ = Kokkos::View<Number *, KokkosDefault>(
660 Kokkos::view_alloc(Kokkos::WithoutInitializing, "sparse_matrix_data"),
661 n_data);
662
663 exchange_buffer_default_ = Kokkos::View<Number *, KokkosDefault>(
664 Kokkos::view_alloc(Kokkos::WithoutInitializing,
665 "sparse_matrix_exchange_buffer"),
666 n_exchange);
667 }
668 }
669
670
671 template <typename Number, int n_components, int warp_size, int simd_length>
672 template <typename To, typename From>
673 void SparseMatrix<Number, n_components, warp_size, simd_length>::
674 deep_copy_storage() const
675 {
676 using HostSpace = dealii::MemorySpace::Host;
677
678 if constexpr (std::is_same_v<To, HostSpace>) {
679 Kokkos::deep_copy(/*dst*/ data_host_, /*src*/ data_default_);
680 } else {
681 Kokkos::deep_copy(/*dst*/ data_default_, /*src*/ data_host_);
682 }
683 }
684
685
686 template <typename Number, int n_components, int warp_size, int simd_length>
687 template <typename MemorySpace>
688 void SparseMatrix<Number, n_components, warp_size, simd_length>::
689 deallocate_storage()
690 {
691 using HostSpace = dealii::MemorySpace::Host;
692
693 if constexpr (std::is_same_v<MemorySpace, HostSpace>) {
694 data_host_ = {};
695
696 } else {
697 data_default_ = {};
698 exchange_buffer_default_ = {};
699 }
700 }
701
702
703 template <typename Number, int n_components, int warp_size, int simd_length>
704 template <typename MemorySpace>
707 {
708 using HostSpace = dealii::MemorySpace::Host;
709 using DefaultSpace = dealii::MemorySpace::Default;
710
711 Assert(this->template is_resident<MemorySpace>(),
712 dealii::ExcMessage("The chosen memory space is not resident."));
713
714 AssertThrow((std::is_same_v<MemorySpace, HostSpace>),
715 dealii::ExcNotImplemented());
716
717 const auto sparsity_view = sparsity_pattern_->view();
718
719 const auto ghost_offset =
720 sparsity_view.template ghost_offset<n_components>();
721 const auto end_offset = sparsity_view.n_nonzero_elements() * n_components;
722 std::fill(data_host_.data() + ghost_offset,
723 data_host_.data() + end_offset,
724 Number{});
725 }
726
727
728 template <typename Number, int n_components, int warp_size, int simd_length>
729 template <typename MemorySpace>
732 {
733 using HostSpace = dealii::MemorySpace::Host;
734
735 const auto sparsity_view = sparsity_pattern_->template view<MemorySpace>();
736
737 const auto *entries_to_be_sent =
738 sparsity_pattern_->entries_to_be_sent().template view<MemorySpace>();
739 const std::size_t n_entries_to_be_sent =
740 sparsity_pattern_->entries_to_be_sent().size();
741
742 /*
743 * Note: If the host and default memory spaces coincide all views
744 * reference the host storage.
745 */
746 constexpr bool on_host =
747 !have_separate_memory_spaces || std::is_same_v<MemorySpace, HostSpace>;
748
749 const auto data = [&]() {
750 if constexpr (on_host)
751 return data_host_;
752 else
753 return data_default_;
754 }();
755
756 const auto exchange_buffer = [&]() {
757 if constexpr (on_host)
758 return exchange_buffer_host_;
759 else
760 return exchange_buffer_default_;
761 }();
762
763 using ExecutionSpace = typename MemorySpace::kokkos_space::execution_space;
764 using Policy =
765 Kokkos::RangePolicy<ExecutionSpace, Kokkos::IndexType<std::size_t>>;
766
767 const auto exec = ExecutionSpace{};
768
769 Kokkos::parallel_for(
770 "sparse_matrix_populate_exchange_buffer",
771 Policy(exec, 0, n_entries_to_be_sent),
772 KOKKOS_LAMBDA(const std::size_t c) {
773 const auto &[row, column_index] = entries_to_be_sent[c];
774 for (unsigned int d = 0; d < n_components; ++d) {
775 const auto offset = sparsity_view.template offset<n_components>(
776 row, column_index, d);
777 exchange_buffer(n_components * c + d) = data(offset);
778 }
779 });
780
781 exec.fence();
782 }
783
784
785 template <typename Number, int n_components, int warp_size, int simd_length>
786 template <typename MemorySpace>
789 {
790 using HostSpace = dealii::MemorySpace::Host;
791
792 Assert(this->template is_resident<MemorySpace>(),
793 dealii::ExcMessage("The chosen memory space is not resident."));
794
795 constexpr bool on_host =
796 !have_separate_memory_spaces || std::is_same_v<MemorySpace, HostSpace>;
797
798 const auto sparsity_view = sparsity_pattern_->view();
799
800 const auto &receive_targets = sparsity_pattern_->receive_targets();
801 const auto &send_targets = sparsity_pattern_->send_targets();
802
803 const unsigned int mpi_tag =
804 dealii::Utilities::MPI::internal::Tags::partitioner_export_start + 0;
805 Assert(mpi_tag <=
806 dealii::Utilities::MPI::internal::Tags::partitioner_export_end,
807 dealii::ExcInternalError());
808
809 const unsigned int n_requests =
810 receive_targets.size() + send_targets.size();
811 std::vector<MPI_Request> requests(n_requests);
812
813 const auto ghost_offset =
814 sparsity_view.template ghost_offset<n_components>();
815 const auto end_offset = sparsity_view.n_nonzero_elements() * n_components;
816
817 Number *const receive_pointer =
818 on_host ? data_host_.data() + ghost_offset : ghost_buffer_host_.data();
819 Number *const send_pointer = exchange_buffer_host_.data();
820
821 for (unsigned int p = 0; p < receive_targets.size(); ++p) {
822 const auto receive_offset =
823 n_components * (p == 0 ? 0 : receive_targets[p - 1].second);
824 const auto receive_size =
825 (receive_targets[p].second * n_components - receive_offset);
826
827#ifdef DEBUG_MPI_EXCHANGE
828 const auto mpi_rank =
829 dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD);
830 std::cout << "Rank " << mpi_rank << " receive from "
831 << receive_targets[p].first << " offset = " << receive_offset
832 << " size = " << receive_size << std::endl;
833#endif
834
835 const int ierr =
836 MPI_Irecv(receive_pointer + receive_offset,
837 receive_size,
838 dealii::Utilities::MPI::mpi_type_id_for_type<Number>,
839 receive_targets[p].first,
840 mpi_tag,
841 sparsity_pattern_->partitioner()->get_mpi_communicator(),
842 &requests[p]);
843 AssertThrowMPI(ierr);
844 }
845
846 populate_exchange_buffer_on_memory_space<MemorySpace>();
847
848 if constexpr (!on_host)
849 Kokkos::deep_copy(/*dst*/ exchange_buffer_host_,
850 /*src*/ exchange_buffer_default_);
851
852 for (unsigned int p = 0; p < send_targets.size(); ++p) {
853 const auto send_offset =
854 n_components * (p == 0 ? 0 : send_targets[p - 1].second);
855 const auto send_size =
856 (send_targets[p].second * n_components - send_offset);
857
858#ifdef DEBUG_MPI_EXCHANGE
859 const auto mpi_rank =
860 dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD);
861 std::cout << "Rank " << mpi_rank << " send to " << send_targets[p].first
862 << " offset = " << send_offset << " size = " << send_size
863 << std::endl;
864#endif
865
866 const int ierr =
867 MPI_Isend(send_pointer + send_offset,
868 send_size,
869 dealii::Utilities::MPI::mpi_type_id_for_type<Number>,
870 send_targets[p].first,
871 mpi_tag,
872 sparsity_pattern_->partitioner()->get_mpi_communicator(),
873 &requests[receive_targets.size() + p]);
874 AssertThrowMPI(ierr);
875 }
876
877#ifdef DEBUG_MPI_EXCHANGE
878 using namespace std::chrono_literals;
879 std::this_thread::sleep_for(200ms);
880#endif
881
882 const int ierr =
883 MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
884 AssertThrowMPI(ierr);
885
886 if constexpr (!on_host) {
887 /* Copy the received ghost range back into device memory: */
888 const auto ghost_range =
889 Kokkos::subview(data_default_,
890 Kokkos::make_pair(std::size_t(ghost_offset),
891 std::size_t(end_offset)));
892 Kokkos::deep_copy(/*dst*/ ghost_range, /*src*/ ghost_buffer_host_);
893 }
894 }
895
896
897 template <typename Number, int n_components, int warp_size, int simd_length>
898 template <typename MemorySpace>
900 compress_on_memory_space(dealii::VectorOperation::values operation
901 [[maybe_unused]])
902 {
903 AssertThrow(operation == dealii::VectorOperation::add,
904 dealii::ExcNotImplemented());
905
906 using HostSpace = dealii::MemorySpace::Host;
907 using DefaultSpace = dealii::MemorySpace::Default;
908
909 AssertThrow((std::is_same_v<MemorySpace, HostSpace>),
910 dealii::ExcNotImplemented());
911
912 Assert(this->template is_resident<MemorySpace>(),
913 dealii::ExcMessage("The chosen memory space is not resident."));
914
915 const auto sparsity_view = sparsity_pattern_->view();
916
917 const auto &receive_targets = sparsity_pattern_->receive_targets();
918 const auto &send_targets = sparsity_pattern_->send_targets();
919 const auto *entries_to_be_sent =
920 sparsity_pattern_->entries_to_be_sent().view();
921 const auto n_entries_to_be_sent =
922 sparsity_pattern_->entries_to_be_sent().size();
923
924 const unsigned int mpi_tag =
925 dealii::Utilities::MPI::internal::Tags::partitioner_export_start + 0;
926 Assert(mpi_tag <=
927 dealii::Utilities::MPI::internal::Tags::partitioner_export_end,
928 dealii::ExcInternalError());
929
930 const unsigned int n_requests =
931 receive_targets.size() + send_targets.size();
932 std::vector<MPI_Request> requests(n_requests);
933
934 /*
935 * Note: For the compress() operation we receive from the "send
936 * targets" and store in the exchange buffer.
937 */
938
939 for (unsigned int p = 0; p < send_targets.size(); ++p) {
940 const auto receive_offset =
941 n_components * (p == 0 ? 0 : send_targets[p - 1].second);
942 const auto receive_size =
943 (send_targets[p].second * n_components - receive_offset);
944
945#ifdef DEBUG_MPI_EXCHANGE
946 const auto mpi_rank =
947 dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD);
948 std::cout << "Rank " << mpi_rank << " receive from "
949 << send_targets[p].first << " offset = " << receive_offset
950 << " size = " << receive_size << std::endl;
951#endif
952
953 const int ierr =
954 MPI_Irecv(exchange_buffer_host_.data() + receive_offset,
955 receive_size,
956 dealii::Utilities::MPI::mpi_type_id_for_type<Number>,
957 send_targets[p].first,
958 mpi_tag,
959 sparsity_pattern_->partitioner()->get_mpi_communicator(),
960 &requests[p]);
961 AssertThrowMPI(ierr);
962 }
963
964 const auto ghost_offset =
965 sparsity_view.template ghost_offset<n_components>();
966
967 /*
968 * Note: For the compress() operation we send our ghost range to the
969 * "receive targets".
970 */
971
972 for (unsigned int p = 0; p < receive_targets.size(); ++p) {
973 const auto send_offset =
974 n_components * (p == 0 ? 0 : receive_targets[p - 1].second);
975 const auto send_size =
976 (receive_targets[p].second * n_components - send_offset);
977
978#ifdef DEBUG_MPI_EXCHANGE
979 const auto mpi_rank =
980 dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD);
981 std::cout << "Rank " << mpi_rank << " send to "
982 << receive_targets[p].first << " offset = " << send_offset
983 << " size = " << send_size << std::endl;
984#endif
985
986 const int ierr =
987 MPI_Isend(data_host_.data() + ghost_offset + send_offset,
988 send_size,
989 dealii::Utilities::MPI::mpi_type_id_for_type<Number>,
990 receive_targets[p].first,
991 mpi_tag,
992 sparsity_pattern_->partitioner()->get_mpi_communicator(),
993 &requests[send_targets.size() + p]);
994 AssertThrowMPI(ierr);
995 }
996
997#ifdef DEBUG_MPI_EXCHANGE
998 using namespace std::chrono_literals;
999 std::this_thread::sleep_for(200ms);
1000#endif
1001
1002 const int ierr =
1003 MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
1004 AssertThrowMPI(ierr);
1005
1006 /* Add back contributions and clear ghost range: */
1007
1008 for (std::size_t c = 0; c < n_entries_to_be_sent; ++c) {
1009 const auto &[row, column_index] = entries_to_be_sent[c];
1010 for (unsigned int d = 0; d < n_components; ++d) {
1011 const auto offset =
1012 sparsity_view.template offset<n_components>(row, column_index, d);
1013 data_host_(offset) += exchange_buffer_host_(n_components * c + d);
1014 }
1015 }
1016
1017 zero_out_ghost_rows_on_memory_space<MemorySpace>();
1018 }
1019
1020
1021 template <typename Number,
1022 int n_comp,
1023 int warp_size,
1024 int simd_length,
1025 typename MemorySpace,
1026 bool writable>
1027 SparseMatrixView<Number,
1028 n_comp,
1029 warp_size,
1030 simd_length,
1031 MemorySpace,
1032 writable>::
1034 SparseMatrix<Number, n_comp, warp_size, simd_length> &sparse_matrix)
1035 requires(writable)
1036 {
1037 reinit(sparse_matrix);
1038 }
1039
1040
1041 template <typename Number,
1042 int n_comp,
1043 int warp_size,
1044 int simd_length,
1045 typename MemorySpace,
1046 bool writable>
1047 SparseMatrixView<Number,
1048 n_comp,
1049 warp_size,
1050 simd_length,
1051 MemorySpace,
1052 writable>::
1054 const SparseMatrix<Number, n_comp, warp_size, simd_length>
1055 &sparse_matrix)
1056 requires(!writable)
1057 {
1058 reinit(sparse_matrix);
1059 }
1060
1061
1062 template <typename Number,
1063 int n_comp,
1064 int warp_size,
1065 int simd_length,
1066 typename MemorySpace,
1067 bool writable>
1068 template <bool other_writable>
1069 DEAL_II_HOST_DEVICE SparseMatrixView<
1070 Number,
1071 n_comp,
1072 warp_size,
1073 simd_length,
1074 MemorySpace,
1075 writable>::SparseMatrixView(const SparseMatrixView<Number,
1076 n_comp,
1077 warp_size,
1078 simd_length,
1079 MemorySpace,
1080 other_writable> &other)
1081 requires(!writable && other_writable)
1082 : sparse_matrix_(other.sparse_matrix_)
1083 , sparsity_pattern_(other.sparsity_pattern_)
1084 , data_(other.data_)
1085 {
1086 }
1087
1088
1089 template <typename Number,
1090 int n_comp,
1091 int warp_size,
1092 int simd_length,
1093 typename MemorySpace,
1094 bool writable>
1095 template <typename SparseMatrix>
1096 void SparseMatrixView<Number,
1097 n_comp,
1098 warp_size,
1099 simd_length,
1100 MemorySpace,
1101 writable>::reinit(SparseMatrix &sparse_matrix)
1102 requires(writable != std::is_const_v<SparseMatrix>)
1103 {
1104 using HostSpace = dealii::MemorySpace::Host;
1105 using DefaultSpace = dealii::MemorySpace::Default;
1106
1107 static_assert(std::is_same_v<MemorySpace, HostSpace> ||
1108 std::is_same_v<MemorySpace, DefaultSpace>,
1109 "Unexpected Kokkos memory space");
1110
1111 sparse_matrix_ = &sparse_matrix;
1112
1113 /*
1114 * Note: If the host and default memory spaces coincide all views
1115 * reference the host storage.
1116 */
1117 if constexpr (have_separate_memory_spaces &&
1118 !std::is_same_v<MemorySpace, HostSpace>) {
1119 data_ = sparse_matrix.data_default_;
1120 } else {
1121 data_ = sparse_matrix.data_host_;
1122 }
1123
1124 sparsity_pattern_ =
1125 sparse_matrix.sparsity_pattern_->template view<MemorySpace>();
1126 }
1127
1128
1129 template <typename Number,
1130 int n_comp,
1131 int warp_size,
1132 int simd_length,
1133 typename MemorySpace,
1134 bool writable>
1135 template <typename Number2>
1136 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number2
1137 SparseMatrixView<Number,
1138 n_comp,
1139 warp_size,
1140 simd_length,
1141 MemorySpace,
1142 writable>::read_entry(const unsigned int row,
1143 const unsigned int column_index) const
1144 {
1145 static_assert(
1146 n_comp == 1,
1147 "Attempted to write a scalar value into a tensor-valued matrix entry");
1148
1149 const auto result = read_tensor<Number2>(row, column_index);
1150 return result[0];
1151 }
1152
1153
1154 template <typename Number,
1155 int n_comp,
1156 int warp_size,
1157 int simd_length,
1158 typename MemorySpace,
1159 bool writable>
1160 template <typename Number2, typename Tensor>
1161 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Tensor
1162 SparseMatrixView<Number,
1163 n_comp,
1164 warp_size,
1165 simd_length,
1166 MemorySpace,
1167 writable>::read_tensor(const unsigned int row,
1168 const unsigned int column_index) const
1169 {
1170 static_assert(std::is_same_v<Number2, typename Tensor::value_type>,
1171 "type mismatch");
1172
1173 AssertIndexRange(row, sparsity_pattern_.n_rows());
1174 AssertIndexRange(column_index, sparsity_pattern_.row_length(row));
1175
1176 Tensor result;
1177
1178 using VA = dealii::VectorizedArray<Number, simd_length>;
1179 if constexpr (std::is_same_v<VA, Number2>) {
1180 /*
1181 * Vectorized fast access. Indices must be in the range
1182 * [0,n_internal), index must be divisible by simd_length
1183 */
1184
1185 Assert(row < sparsity_pattern_.n_internal_dofs(),
1186 dealii::ExcMessage(
1187 "Vectorized access only possible in vectorized part"));
1188 Assert(row % simd_length == 0,
1189 dealii::ExcMessage(
1190 "Access only supported for rows at the SIMD granularity"));
1191
1192 const Number *load_pos = data_.data();
1193 load_pos +=
1194 sparsity_pattern_.template offset_internal<n_comp>(row, column_index);
1195
1196 for (unsigned int d = 0; d < n_comp; ++d)
1197 result[d].load(load_pos + d * warp_size);
1198
1199 } else {
1200 /*
1201 * Non-vectorized slow access. Supports all row indices in [0,n_owned):
1202 */
1203
1204 for (unsigned int d = 0; d < n_comp; ++d) {
1205 const auto offset =
1206 sparsity_pattern_.template offset<n_comp>(row, column_index, d);
1207 result[d] = data_(offset);
1208 }
1209 }
1210
1211 return result;
1212 }
1213
1214
1215 template <typename Number,
1216 int n_comp,
1217 int warp_size,
1218 int simd_length,
1219 typename MemorySpace,
1220 bool writable>
1221 template <typename Number2>
1222 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number2
1223 SparseMatrixView<Number,
1224 n_comp,
1225 warp_size,
1226 simd_length,
1227 MemorySpace,
1228 writable>::read_transposed_entry(const unsigned int row,
1229 const unsigned int
1230 column_index) const
1231 {
1232 static_assert(
1233 n_comp == 1,
1234 "Attempted to write a scalar value into a tensor-valued matrix entry");
1235
1236 const auto result = read_transposed_tensor<Number2>(row, column_index);
1237 return result[0];
1238 }
1239
1240
1241 template <typename Number,
1242 int n_comp,
1243 int warp_size,
1244 int simd_length,
1245 typename MemorySpace,
1246 bool writable>
1247 template <typename Number2, typename Tensor>
1248 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Tensor
1249 SparseMatrixView<Number,
1250 n_comp,
1251 warp_size,
1252 simd_length,
1253 MemorySpace,
1254 writable>::read_transposed_tensor(const unsigned int row,
1255 const unsigned int
1256 column_index) const
1257 {
1258 static_assert(std::is_same_v<Number2, typename Tensor::value_type>,
1259 "type mismatch");
1260
1261 AssertIndexRange(row, sparsity_pattern_.n_rows());
1262 AssertIndexRange(column_index, sparsity_pattern_.row_length(row));
1263
1264 dealii::Tensor<1, n_comp, Number2> result;
1265
1266 using VA = dealii::VectorizedArray<Number, simd_length>;
1267 if constexpr (std::is_same_v<VA, Number2> && (n_comp == 1)) {
1268 /*
1269 * Vectorized fast access. Indices must be in the range
1270 * [0,n_internal), index must be divisible by simd_length
1271 */
1272
1273 Assert(row < sparsity_pattern_.n_internal_dofs(),
1274 dealii::ExcMessage(
1275 "Vectorized access only possible in vectorized part"));
1276 Assert(row % simd_length == 0,
1277 dealii::ExcMessage(
1278 "Access only supported for rows at the SIMD granularity"));
1279
1280 const auto offsets =
1281 sparsity_pattern_.template transposed_offset_internal<1>(
1282 row, column_index);
1283 result[0].gather(data_.data(), offsets);
1284
1285 } else if constexpr (std::is_same_v<VA, Number2> && (n_comp != 1)) {
1286
1287 /* not implemented */
1288 Assert(false,
1289 dealii::ExcMessage("Vectorized transposed access to multiple "
1290 "components is not implemented."));
1291 __builtin_trap();
1292
1293 } else {
1294 /*
1295 * Non-vectorized slow access. Supports all row indices in [0,n_owned):
1296 */
1297
1298 for (unsigned int d = 0; d < n_comp; ++d) {
1299 const auto offset =
1300 sparsity_pattern_.template transposed_offset<n_comp>(
1301 row, column_index, d);
1302 result[d] = data_(offset);
1303 }
1304 }
1305
1306 return result;
1307 }
1308
1309
1310 template <typename Number,
1311 int n_comp,
1312 int warp_size,
1313 int simd_length,
1314 typename MemorySpace,
1315 bool writable>
1316 template <typename Number2>
1317 DEAL_II_HOST_DEVICE_ALWAYS_INLINE void
1318 SparseMatrixView<Number,
1319 n_comp,
1320 warp_size,
1321 simd_length,
1322 MemorySpace,
1323 writable>::write_entry(const Number2 entry,
1324 const unsigned int row,
1325 const unsigned int column_index,
1326 const bool do_streaming_store) const
1327 requires(writable)
1328 {
1329 static_assert(
1330 n_comp == 1,
1331 "Attempted to write a scalar value into a tensor-valued matrix entry");
1332
1333 AssertIndexRange(row, sparsity_pattern_.n_rows());
1334 AssertIndexRange(column_index, sparsity_pattern_.row_length(row));
1335
1336 dealii::Tensor<1, n_comp, Number2> tensor;
1337 tensor[0] = entry;
1338
1339 write_tensor<Number2>(tensor, row, column_index, do_streaming_store);
1340 }
1341
1342
1343 template <typename Number,
1344 int n_comp,
1345 int warp_size,
1346 int simd_length,
1347 typename MemorySpace,
1348 bool writable>
1349 template <typename Number2, typename Tensor>
1350 DEAL_II_HOST_DEVICE_ALWAYS_INLINE void
1351 SparseMatrixView<Number,
1352 n_comp,
1353 warp_size,
1354 simd_length,
1355 MemorySpace,
1356 writable>::write_tensor(const Tensor &tensor,
1357 const unsigned int row,
1358 const unsigned int column_index,
1359 const bool do_streaming_store) const
1360 requires(writable)
1361 {
1362 AssertIndexRange(row, sparsity_pattern_.n_rows());
1363 AssertIndexRange(column_index, sparsity_pattern_.row_length(row));
1364
1365 using VA = dealii::VectorizedArray<Number, simd_length>;
1366 if constexpr (std::is_same_v<VA, Number2>) {
1367 /*
1368 * Vectorized fast access. Indices must be in the range [0,n_internal),
1369 * index must be divisible by simd_length:
1370 */
1371
1372 Assert(row < sparsity_pattern_.n_internal_dofs(),
1373 dealii::ExcMessage(
1374 "Vectorized access only possible in vectorized part"));
1375 Assert(row % simd_length == 0,
1376 dealii::ExcMessage(
1377 "Access only supported for rows at the SIMD granularity"));
1378
1379 Number *store_pos = data_.data();
1380 store_pos +=
1381 sparsity_pattern_.template offset_internal<n_comp>(row, column_index);
1382
1383 if (do_streaming_store)
1384 for (unsigned int d = 0; d < n_comp; ++d)
1385 tensor[d].streaming_store(store_pos + d * warp_size);
1386 else
1387 for (unsigned int d = 0; d < n_comp; ++d)
1388 tensor[d].store(store_pos + d * warp_size);
1389
1390 } else {
1391 /*
1392 * Non-vectorized slow access. Supports all row indices in [0,n_owned):
1393 */
1394
1395 for (unsigned int d = 0; d < n_comp; ++d) {
1396 const auto offset =
1397 sparsity_pattern_.template offset<n_comp>(row, column_index, d);
1398 data_(offset) = tensor[d];
1399 }
1400 }
1401 }
1402
1403
1404 template <typename Number,
1405 int n_comp,
1406 int warp_size,
1407 int simd_length,
1408 typename MemorySpace,
1409 bool writable>
1410 template <typename Number2>
1411 DEAL_II_HOST_DEVICE_ALWAYS_INLINE void
1412 SparseMatrixView<Number,
1413 n_comp,
1414 warp_size,
1415 simd_length,
1416 MemorySpace,
1417 writable>::add_entry(const Number2 entry,
1418 const unsigned int row,
1419 const unsigned int column_index) const
1420 requires(writable)
1421 {
1422 static_assert(
1423 n_comp == 1,
1424 "Attempted to write a scalar value into a tensor-valued matrix entry");
1425
1426 AssertIndexRange(row, sparsity_pattern_.n_rows());
1427 AssertIndexRange(column_index, sparsity_pattern_.row_length(row));
1428
1429 dealii::Tensor<1, n_comp, Number2> tensor;
1430 tensor[0] = entry;
1431
1432 add_tensor<Number2>(tensor, row, column_index);
1433 }
1434
1435
1436 template <typename Number,
1437 int n_comp,
1438 int warp_size,
1439 int simd_length,
1440 typename MemorySpace,
1441 bool writable>
1442 template <typename Number2, typename Tensor>
1443 DEAL_II_HOST_DEVICE_ALWAYS_INLINE void
1444 SparseMatrixView<Number,
1445 n_comp,
1446 warp_size,
1447 simd_length,
1448 MemorySpace,
1449 writable>::add_tensor(const Tensor &tensor,
1450 const unsigned int row,
1451 const unsigned int column_index) const
1452 requires(writable)
1453 {
1454 AssertIndexRange(row, sparsity_pattern_.n_rows());
1455 AssertIndexRange(column_index, sparsity_pattern_.row_length(row));
1456
1457 using VA = dealii::VectorizedArray<Number, simd_length>;
1458 if constexpr (std::is_same_v<VA, Number2>) {
1459 /*
1460 * Vectorized fast access. Indices must be in the range [0,n_internal),
1461 * index must be divisible by simd_length:
1462 */
1463
1464 Assert(row < sparsity_pattern_.n_internal_dofs(),
1465 dealii::ExcMessage(
1466 "Vectorized access only possible in vectorized part"));
1467 Assert(row % simd_length == 0,
1468 dealii::ExcMessage(
1469 "Access only supported for rows at the SIMD granularity"));
1470
1471 Number *store_pos = data_.data();
1472 store_pos +=
1473 sparsity_pattern_.template offset_internal<n_comp>(row, column_index);
1474
1475 for (unsigned int d = 0; d < n_comp; ++d) {
1476 auto temp = tensor[d];
1477 temp.load(store_pos + d * warp_size);
1478 temp += tensor[d];
1479 temp.store(store_pos + d * warp_size);
1480 }
1481
1482 } else {
1483 /*
1484 * Non-vectorized slow access. Supports all row indices in [0,n_owned):
1485 */
1486
1487 for (unsigned int d = 0; d < n_comp; ++d) {
1488 const auto offset =
1489 sparsity_pattern_.template offset<n_comp>(row, column_index, d);
1490 data_(offset) += tensor[d]; /*add*/
1491 ;
1492 }
1493 }
1494 }
1495
1496
1497 template <typename Number,
1498 int n_comp,
1499 int warp_size,
1500 int simd_length,
1501 typename MemorySpace,
1502 bool writable>
1503 void SparseMatrixView<Number,
1504 n_comp,
1505 warp_size,
1506 simd_length,
1507 MemorySpace,
1508 writable>::zero_out_ghost_rows() const
1509 requires(writable)
1510 {
1511 using HostSpace = dealii::MemorySpace::Host;
1512 using DefaultSpace = dealii::MemorySpace::Default;
1513
1514 static_assert(std::is_same_v<MemorySpace, HostSpace> ||
1515 std::is_same_v<MemorySpace, DefaultSpace>,
1516 "Unexpected Kokkos memory space");
1517
1518 Assert(sparse_matrix_->template is_resident<MemorySpace>(),
1519 dealii::ExcMessage("The chosen memory space is not resident."));
1520
1521 sparse_matrix_->template zero_out_ghost_rows_on_memory_space<MemorySpace>();
1522 }
1523
1524
1525 template <typename Number,
1526 int n_comp,
1527 int warp_size,
1528 int simd_length,
1529 typename MemorySpace,
1530 bool writable>
1531 void SparseMatrixView<Number,
1532 n_comp,
1533 warp_size,
1534 simd_length,
1535 MemorySpace,
1536 writable>::update_ghost_rows() const
1537 requires(writable)
1538 {
1539 using HostSpace = dealii::MemorySpace::Host;
1540 using DefaultSpace = dealii::MemorySpace::Default;
1541
1542 static_assert(std::is_same_v<MemorySpace, HostSpace> ||
1543 std::is_same_v<MemorySpace, DefaultSpace>,
1544 "Unexpected Kokkos memory space");
1545
1546 Assert(sparse_matrix_->template is_resident<MemorySpace>(),
1547 dealii::ExcMessage("The chosen memory space is not resident."));
1548
1549 sparse_matrix_->template update_ghost_rows_on_memory_space<MemorySpace>();
1550 }
1551
1552
1553 template <typename Number,
1554 int n_comp,
1555 int warp_size,
1556 int simd_length,
1557 typename MemorySpace,
1558 bool writable>
1559 void SparseMatrixView<Number,
1560 n_comp,
1561 warp_size,
1562 simd_length,
1563 MemorySpace,
1564 writable>::compress(dealii::VectorOperation::values
1565 operation) const
1566 requires(writable)
1567 {
1568 using HostSpace = dealii::MemorySpace::Host;
1569 using DefaultSpace = dealii::MemorySpace::Default;
1570
1571 static_assert(std::is_same_v<MemorySpace, HostSpace> ||
1572 std::is_same_v<MemorySpace, DefaultSpace>,
1573 "Unexpected Kokkos memory space");
1574
1575 Assert(sparse_matrix_->template is_resident<MemorySpace>(),
1576 dealii::ExcMessage("The chosen memory space is not resident."));
1577
1578 sparse_matrix_->template compress_on_memory_space<MemorySpace>(operation);
1579 }
1580
1581
1582 template <typename Number,
1583 int n_comp,
1584 int warp_size,
1585 int simd_length,
1586 typename FM>
1588 const FM &cell_matrix,
1589 const std::vector<dealii::types::global_dof_index> &dof_indices_row,
1590 const std::vector<dealii::types::global_dof_index> &dof_indices_column,
1591 const dealii::AffineConstraints<Number> &affine_constraints
1592 [[maybe_unused]],
1593 SparseMatrix<Number, n_comp, warp_size, simd_length> &sparse_matrix)
1594 {
1595 constexpr bool is_matrix = std::is_same_v<FM, dealii::FullMatrix<Number>>;
1596 constexpr bool is_array =
1597 std::is_same_v<FM, std::array<dealii::FullMatrix<Number>, n_comp>>;
1598 static_assert((n_comp == 1 && is_matrix) || is_array, "not implemented");
1599
1600 if constexpr (is_matrix) {
1601 Assert(cell_matrix.m() == dof_indices_row.size(),
1602 dealii::ExcInternalError());
1603 Assert(cell_matrix.n() == dof_indices_column.size(),
1604 dealii::ExcInternalError());
1605 } else if constexpr (is_array) {
1606 Assert(cell_matrix.size() == n_comp, dealii::ExcInternalError());
1607 for (unsigned int d = 0; d < n_comp; ++d) {
1608 Assert(cell_matrix[d].m() == dof_indices_row.size(),
1609 dealii::ExcInternalError());
1610 Assert(cell_matrix[d].n() == dof_indices_column.size(),
1611 dealii::ExcInternalError());
1612 }
1613 }
1614
1615 const auto sparse_matrix_view = sparse_matrix.view();
1616
1617 const auto &sparsity_pattern = sparse_matrix.sparsity_pattern();
1618 const auto sparsity_pattern_view = sparsity_pattern.view();
1619 const auto &partitioner = sparsity_pattern.partitioner();
1620
1621 /*
1622 * Helper that inserts a single entry into the matrix indexed by (r, c)
1623 * in the cell_matrix and by (i, j) in the sparse matrix and multiplied
1624 * by a weight c_ij.
1625 */
1626
1627 const auto insert_entry =
1628 [&](auto r, auto c, auto i, auto j, auto c_ij) DEAL_II_ALWAYS_INLINE {
1629 if constexpr (is_matrix) {
1630 const Number &entry = cell_matrix(r, c);
1631 if (entry == Number{})
1632 return;
1633 const auto col_idx = sparsity_pattern_view.column_index(i, j);
1634 sparse_matrix_view.add_entry(c_ij * entry, i, col_idx);
1635 } else if constexpr (is_array) {
1636 dealii::Tensor<1, n_comp, Number> entry;
1637 for (unsigned int k = 0; k < n_comp; ++k)
1638 entry[k] = cell_matrix[k](r, c);
1639 if (entry == dealii::Tensor<1, n_comp>{})
1640 return;
1641 const auto col_idx = sparsity_pattern_view.column_index(i, j);
1642 sparse_matrix_view.add_tensor(c_ij * entry, i, col_idx);
1643 }
1644 };
1645
1646 /*
1647 * Helper that iterates over row entries:
1648 */
1649
1650 const auto iterate_over_row_entries =
1651 [&](const auto r, const auto i, const auto c_i) DEAL_II_ALWAYS_INLINE {
1652 /* Iterate over columns: c - column index; j_global, j - dof index */
1653 for (unsigned int c = 0; c < dof_indices_column.size(); ++c) {
1654 const auto j_global = dof_indices_column[c];
1655 if (affine_constraints.is_constrained(j_global)) {
1656 const auto &line =
1657 *affine_constraints.get_constraint_entries(j_global);
1658 for (const auto &[k_global, c_k] : line) {
1659 const auto k = partitioner->global_to_local(k_global);
1660 insert_entry(r, c, i, k, c_i * c_k);
1661 }
1662 } else {
1663 const auto j = partitioner->global_to_local(j_global);
1664 insert_entry(r, c, i, j, c_i);
1665 }
1666 }
1667 };
1668
1669 /* Now, iterate over rows: r - row index; i_global, i - dof index */
1670 for (unsigned int r = 0; r < dof_indices_row.size(); ++r) {
1671 const auto i_global = dof_indices_row[r];
1672 if (affine_constraints.is_constrained(i_global)) {
1673 const auto &line = *affine_constraints.get_constraint_entries(i_global);
1674 for (const auto &[k_global, c_k] : line) {
1675 const auto k = partitioner->global_to_local(k_global);
1676 iterate_over_row_entries(r, k, c_k);
1677 }
1678 } else {
1679 const auto i = partitioner->global_to_local(i_global);
1680 iterate_over_row_entries(r, i, Number(1.));
1681 }
1682 }
1683 }
1684
1685
1686 template <typename Number,
1687 int n_comp,
1688 int warp_size,
1689 int simd_length,
1690 typename FM>
1692 const FM &cell_matrix,
1693 const std::vector<dealii::types::global_dof_index> &dof_indices,
1694 const dealii::AffineConstraints<Number> &affine_constraints,
1695 SparseMatrix<Number, n_comp, warp_size, simd_length> &sparse_matrix)
1696 {
1697 constexpr bool is_matrix = std::is_same_v<FM, dealii::FullMatrix<Number>>;
1698 constexpr bool is_array =
1699 std::is_same_v<FM, std::array<dealii::FullMatrix<Number>, n_comp>>;
1700 static_assert((n_comp == 1 && is_matrix) || is_array, "not implemented");
1701
1702 distribute_local_to_global(cell_matrix,
1703 dof_indices,
1704 dof_indices,
1705 affine_constraints,
1706 sparse_matrix);
1707 }
1708
1709#endif
1710} // namespace ryujin
TransferPolicy transfer_policy() const
void zero_out_ghost_rows() const
DEAL_II_HOST_DEVICE Tensor read_transposed_tensor(const unsigned int row, const unsigned int column_index) const
ACCESSOR_READ_ONLY(sparsity_pattern)
DEAL_II_HOST_DEVICE Number2 read_transposed_entry(const unsigned int row, const unsigned int column_index) const
DEAL_II_HOST_DEVICE void write_tensor(const Tensor &tensor, const unsigned int row, const unsigned int column_index, const bool do_streaming_store=false) const
void reinit(SparseMatrix &sparse_matrix)
DEAL_II_HOST_DEVICE void add_tensor(const Tensor &tensor, const unsigned int row, const unsigned int column_index) const
SparseMatrixView(const SparseMatrix< Number, n_comp, warp_size, simd_length > &sparse_matrix)
DEAL_II_HOST_DEVICE void write_entry(const Number2 entry, const unsigned int row, const unsigned int column_index, const bool do_streaming_store=false) const
DEAL_II_HOST_DEVICE SparseMatrixView(const SparseMatrixView< Number, n_comp, warp_size, simd_length, MemorySpace, other_writable > &other)
DEAL_II_HOST_DEVICE Number2 read_entry(const unsigned int row, const unsigned int column_index) const
DEAL_II_HOST_DEVICE void add_entry(const Number2 entry, const unsigned int row, const unsigned int column_index) const
void update_ghost_rows() const
SparseMatrixView(SparseMatrix< Number, n_comp, warp_size, simd_length > &sparse_matrix)
DEAL_II_HOST_DEVICE Tensor read_tensor(const unsigned int row, const unsigned int column_index) const
void compress(dealii::VectorOperation::values operation) const
void populate_exchange_buffer_on_memory_space()
SparseMatrixView< Number, n_comp, warp_size, simd_length, MemorySpace, false > view() const
void compress_on_memory_space(dealii::VectorOperation::values operation)
void update_ghost_rows_on_memory_space()
void zero_out_ghost_rows_on_memory_space()
SparseMatrixView< Number, n_comp, warp_size, simd_length, MemorySpace, true > view()
ACCESSOR_READ_ONLY(sparsity_pattern)
void reinit(const SparsityPattern< warp_size > &sparsity, const TransferPolicy transfer_policy=TransferPolicy::explicit_transfers)
SparseMatrix(const SparsityPattern< warp_size > &sparsity, const TransferPolicy transfer_policy=TransferPolicy::explicit_transfers)
constexpr unsigned int warp_size
Definition gpu.h:46
TransferPolicy
Definition gpu.h:88
constexpr bool have_separate_memory_spaces
Definition gpu.h:29
void distribute_local_to_global(const FullMatrix &cell_matrix, const std::vector< dealii::types::global_dof_index > &dof_indices_row, const std::vector< dealii::types::global_dof_index > &dof_indices_column, const dealii::AffineConstraints< Number > &affine_constraints, SparseMatrix< Number, n_comp, warp_size, simd_length > &sparse_matrix)