8#include <ryujin/base/compile_time_options.h>
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>
21#ifdef DEBUG_MPI_EXCHANGE
27 template <
typename Number,
31 typename MemorySpace = dealii::MemorySpace::Host,
33 class SparseMatrixView;
53 template <
typename Number,
56 int simd_length = dealii::VectorizedArray<Number>::size()>
58 SparseMatrix<Number, n_comp, warp_size, simd_length>>
61 static_assert(
warp_size % simd_length == 0,
62 "The warp size must be an integer multiple of the SIMD "
119 template <
typename MemorySpace = dealii::MemorySpace::Host>
130 template <
typename MemorySpace = dealii::MemorySpace::Host>
149 template <
typename MemorySpace>
156 template <
typename MemorySpace>
164 template <
typename MemorySpace>
176 template <
typename MemorySpace>
195 using KokkosHost = dealii::MemorySpace::Host::kokkos_space;
196 mutable Kokkos::View<Number *, KokkosHost> data_host_;
198 using KokkosDefault = dealii::MemorySpace::Default::kokkos_space;
199 mutable Kokkos::View<Number *, KokkosDefault> data_default_;
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_;
215 template <
typename MemorySpace>
216 void allocate_storage()
const;
218 template <
typename To,
typename From>
219 void deep_copy_storage()
const;
221 template <
typename MemorySpace>
222 void deallocate_storage();
228 template <
typename,
int,
int,
int,
typename,
bool>
249 template <
typename Number,
253 typename MemorySpace,
258 static_assert(
warp_size % simd_length == 0,
259 "The warp size must be an integer multiple of the SIMD "
285 template <
bool other_writable>
292 other_writable> &other)
293 requires(!writable && other_writable);
295 template <
typename SparseMatrix>
297 requires(writable != std::is_const_v<SparseMatrix>);
322 template <
typename Number2 = Number>
323 DEAL_II_HOST_DEVICE Number2
324 read_entry(
const unsigned int row,
const unsigned int column_index)
const;
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;
355 template <
typename Number2 = Number>
357 const unsigned int row,
const unsigned int column_index)
const;
370 template <
typename Number2 = Number,
371 typename Tensor = dealii::Tensor<1, n_comp, Number2>>
373 const unsigned int row,
const unsigned int column_index)
const;
388 template <
typename Number2 = Number>
389 DEAL_II_HOST_DEVICE
void
391 const unsigned int row,
392 const unsigned int column_index,
393 const bool do_streaming_store =
false) const
405 template <typename Number2 = Number,
406 typename Tensor = dealii::Tensor<1, n_comp, Number2>>
407 DEAL_II_HOST_DEVICE
void
409 const
unsigned int row,
410 const
unsigned int column_index,
411 const
bool do_streaming_store = false) const
425 template <typename Number2 = Number>
427 const
unsigned int row,
428 const
unsigned int column_index) const
440 template <typename Number2 = Number,
441 typename Tensor = dealii::Tensor<1, n_comp, Number2>>
443 const
unsigned int row,
444 const
unsigned int column_index) const
459 void compress(dealii::VectorOperation::values operation) const
470 std::conditional_t<writable,
SM *, const
SM *> sparse_matrix_;
474 using KokkosSpace = typename MemorySpace::kokkos_space;
475 Kokkos::View<Number *, KokkosSpace> data_;
477 template <typename,
int,
int,
int, typename,
bool>
499 template <typename Number,
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,
516 template <typename Number,
523 const FullMatrix &cell_matrix,
524 const std::vector<dealii::types::global_dof_index> &dof_indices,
525 const dealii::AffineConstraints<Number> &affine_constraints,
537 template <
typename Number,
int n_components,
int warp_size,
int simd_length>
542 reinit(sparsity, transfer_policy);
546 template <
typename Number,
int n_components,
int warp_size,
int simd_length>
548 const SparsityPattern<warp_size> &sparsity,
557 this->sparsity_pattern_ = &sparsity;
559 const auto sparsity_view = sparsity.view();
561 using KokkosHost = dealii::MemorySpace::Host::kokkos_space;
562 using Aligned = Kokkos::MemoryTraits<Kokkos::Aligned>;
564 data_host_ = Kokkos::View<Number *, KokkosHost, Aligned>(
565 "sparse_matrix_data",
566 sparsity_view.n_nonzero_elements() * n_components);
568 const std::size_t n_indices = sparsity.entries_to_be_sent().size();
570 exchange_buffer_host_ = Kokkos::View<Number *, KokkosHost, Aligned>(
571 "sparse_matrix_exchange_buffer", n_components * n_indices);
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;
578 ghost_buffer_host_ = Kokkos::View<Number *, KokkosHost, Aligned>(
579 "sparse_matrix_ghost_buffer", end_offset - ghost_offset);
589 exchange_buffer_default_ = {};
591 this->reset_residency(
true,
594 this->set_transfer_policy(transfer_policy);
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>
603 this->
template prepare_write_access<MemorySpace>();
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>
619 this->
template prepare_read_access<MemorySpace>();
630 template <
typename Number,
int n_components,
int warp_size,
int simd_length>
631 template <
typename MemorySpace>
633 SparseMatrix<Number, n_components, warp_size, simd_length>::allocate_storage()
636 using HostSpace = dealii::MemorySpace::Host;
637 using Aligned = Kokkos::MemoryTraits<Kokkos::Aligned>;
639 Assert(sparsity_pattern_ !=
nullptr, dealii::ExcNotInitialized());
641 const auto sparsity_view = sparsity_pattern_->view();
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();
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"),
659 data_default_ = Kokkos::View<Number *, KokkosDefault>(
660 Kokkos::view_alloc(Kokkos::WithoutInitializing,
"sparse_matrix_data"),
663 exchange_buffer_default_ = Kokkos::View<Number *, KokkosDefault>(
664 Kokkos::view_alloc(Kokkos::WithoutInitializing,
665 "sparse_matrix_exchange_buffer"),
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
676 using HostSpace = dealii::MemorySpace::Host;
678 if constexpr (std::is_same_v<To, HostSpace>) {
679 Kokkos::deep_copy( data_host_, data_default_);
681 Kokkos::deep_copy( data_default_, data_host_);
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>::
691 using HostSpace = dealii::MemorySpace::Host;
693 if constexpr (std::is_same_v<MemorySpace, HostSpace>) {
698 exchange_buffer_default_ = {};
703 template <
typename Number,
int n_components,
int warp_size,
int simd_length>
704 template <
typename MemorySpace>
708 using HostSpace = dealii::MemorySpace::Host;
709 using DefaultSpace = dealii::MemorySpace::Default;
711 Assert(this->
template is_resident<MemorySpace>(),
712 dealii::ExcMessage(
"The chosen memory space is not resident."));
714 AssertThrow((std::is_same_v<MemorySpace, HostSpace>),
715 dealii::ExcNotImplemented());
717 const auto sparsity_view = sparsity_pattern_->view();
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,
728 template <
typename Number,
int n_components,
int warp_size,
int simd_length>
729 template <
typename MemorySpace>
733 using HostSpace = dealii::MemorySpace::Host;
735 const auto sparsity_view = sparsity_pattern_->template view<MemorySpace>();
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();
746 constexpr bool on_host =
749 const auto data = [&]() {
750 if constexpr (on_host)
753 return data_default_;
756 const auto exchange_buffer = [&]() {
757 if constexpr (on_host)
758 return exchange_buffer_host_;
760 return exchange_buffer_default_;
763 using ExecutionSpace =
typename MemorySpace::kokkos_space::execution_space;
765 Kokkos::RangePolicy<ExecutionSpace, Kokkos::IndexType<std::size_t>>;
767 const auto exec = ExecutionSpace{};
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);
785 template <
typename Number,
int n_components,
int warp_size,
int simd_length>
786 template <
typename MemorySpace>
790 using HostSpace = dealii::MemorySpace::Host;
792 Assert(this->
template is_resident<MemorySpace>(),
793 dealii::ExcMessage(
"The chosen memory space is not resident."));
795 constexpr bool on_host =
798 const auto sparsity_view = sparsity_pattern_->view();
800 const auto &receive_targets = sparsity_pattern_->receive_targets();
801 const auto &send_targets = sparsity_pattern_->send_targets();
803 const unsigned int mpi_tag =
804 dealii::Utilities::MPI::internal::Tags::partitioner_export_start + 0;
806 dealii::Utilities::MPI::internal::Tags::partitioner_export_end,
807 dealii::ExcInternalError());
809 const unsigned int n_requests =
810 receive_targets.size() + send_targets.size();
811 std::vector<MPI_Request> requests(n_requests);
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;
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();
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);
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;
836 MPI_Irecv(receive_pointer + receive_offset,
838 dealii::Utilities::MPI::mpi_type_id_for_type<Number>,
839 receive_targets[p].first,
841 sparsity_pattern_->partitioner()->get_mpi_communicator(),
843 AssertThrowMPI(ierr);
846 populate_exchange_buffer_on_memory_space<MemorySpace>();
848 if constexpr (!on_host)
849 Kokkos::deep_copy( exchange_buffer_host_,
850 exchange_buffer_default_);
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);
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
867 MPI_Isend(send_pointer + send_offset,
869 dealii::Utilities::MPI::mpi_type_id_for_type<Number>,
870 send_targets[p].first,
872 sparsity_pattern_->partitioner()->get_mpi_communicator(),
873 &requests[receive_targets.size() + p]);
874 AssertThrowMPI(ierr);
877#ifdef DEBUG_MPI_EXCHANGE
878 using namespace std::chrono_literals;
879 std::this_thread::sleep_for(200ms);
883 MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
884 AssertThrowMPI(ierr);
886 if constexpr (!on_host) {
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( ghost_range, ghost_buffer_host_);
897 template <
typename Number,
int n_components,
int warp_size,
int simd_length>
898 template <
typename MemorySpace>
903 AssertThrow(operation == dealii::VectorOperation::add,
904 dealii::ExcNotImplemented());
906 using HostSpace = dealii::MemorySpace::Host;
907 using DefaultSpace = dealii::MemorySpace::Default;
909 AssertThrow((std::is_same_v<MemorySpace, HostSpace>),
910 dealii::ExcNotImplemented());
912 Assert(this->
template is_resident<MemorySpace>(),
913 dealii::ExcMessage(
"The chosen memory space is not resident."));
915 const auto sparsity_view = sparsity_pattern_->view();
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();
924 const unsigned int mpi_tag =
925 dealii::Utilities::MPI::internal::Tags::partitioner_export_start + 0;
927 dealii::Utilities::MPI::internal::Tags::partitioner_export_end,
928 dealii::ExcInternalError());
930 const unsigned int n_requests =
931 receive_targets.size() + send_targets.size();
932 std::vector<MPI_Request> requests(n_requests);
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);
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;
954 MPI_Irecv(exchange_buffer_host_.data() + receive_offset,
956 dealii::Utilities::MPI::mpi_type_id_for_type<Number>,
957 send_targets[p].first,
959 sparsity_pattern_->partitioner()->get_mpi_communicator(),
961 AssertThrowMPI(ierr);
964 const auto ghost_offset =
965 sparsity_view.template ghost_offset<n_components>();
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);
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;
987 MPI_Isend(data_host_.data() + ghost_offset + send_offset,
989 dealii::Utilities::MPI::mpi_type_id_for_type<Number>,
990 receive_targets[p].first,
992 sparsity_pattern_->partitioner()->get_mpi_communicator(),
993 &requests[send_targets.size() + p]);
994 AssertThrowMPI(ierr);
997#ifdef DEBUG_MPI_EXCHANGE
998 using namespace std::chrono_literals;
999 std::this_thread::sleep_for(200ms);
1003 MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
1004 AssertThrowMPI(ierr);
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) {
1012 sparsity_view.template offset<n_components>(row, column_index, d);
1013 data_host_(offset) += exchange_buffer_host_(n_components * c + d);
1017 zero_out_ghost_rows_on_memory_space<MemorySpace>();
1021 template <
typename Number,
1025 typename MemorySpace,
1034 SparseMatrix<Number, n_comp, warp_size, simd_length> &sparse_matrix)
1041 template <
typename Number,
1045 typename MemorySpace,
1054 const SparseMatrix<Number, n_comp, warp_size, simd_length>
1062 template <
typename Number,
1066 typename MemorySpace,
1068 template <
bool other_writable>
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_)
1089 template <
typename Number,
1093 typename MemorySpace,
1095 template <
typename SparseMatrix>
1101 writable>
::reinit(SparseMatrix &sparse_matrix)
1102 requires(writable != std::is_const_v<SparseMatrix>)
1104 using HostSpace = dealii::MemorySpace::Host;
1105 using DefaultSpace = dealii::MemorySpace::Default;
1107 static_assert(std::is_same_v<MemorySpace, HostSpace> ||
1108 std::is_same_v<MemorySpace, DefaultSpace>,
1109 "Unexpected Kokkos memory space");
1111 sparse_matrix_ = &sparse_matrix;
1118 !std::is_same_v<MemorySpace, HostSpace>) {
1119 data_ = sparse_matrix.data_default_;
1121 data_ = sparse_matrix.data_host_;
1125 sparse_matrix.sparsity_pattern_->template view<MemorySpace>();
1129 template <
typename Number,
1133 typename MemorySpace,
1135 template <
typename Number2>
1136 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number2
1143 const unsigned int column_index)
const
1147 "Attempted to write a scalar value into a tensor-valued matrix entry");
1149 const auto result = read_tensor<Number2>(row, column_index);
1154 template <
typename Number,
1158 typename MemorySpace,
1160 template <
typename Number2,
typename Tensor>
1161 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Tensor
1168 const unsigned int column_index)
const
1170 static_assert(std::is_same_v<Number2, typename Tensor::value_type>,
1173 AssertIndexRange(row, sparsity_pattern_.n_rows());
1174 AssertIndexRange(column_index, sparsity_pattern_.row_length(row));
1178 using VA = dealii::VectorizedArray<Number, simd_length>;
1179 if constexpr (std::is_same_v<VA, Number2>) {
1185 Assert(row < sparsity_pattern_.n_internal_dofs(),
1187 "Vectorized access only possible in vectorized part"));
1188 Assert(row % simd_length == 0,
1190 "Access only supported for rows at the SIMD granularity"));
1192 const Number *load_pos = data_.data();
1194 sparsity_pattern_.template offset_internal<n_comp>(row, column_index);
1196 for (
unsigned int d = 0; d < n_comp; ++d)
1197 result[d].load(load_pos + d *
warp_size);
1204 for (
unsigned int d = 0; d < n_comp; ++d) {
1206 sparsity_pattern_.template offset<n_comp>(row, column_index, d);
1207 result[d] = data_(offset);
1215 template <
typename Number,
1219 typename MemorySpace,
1221 template <
typename Number2>
1222 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number2
1234 "Attempted to write a scalar value into a tensor-valued matrix entry");
1236 const auto result = read_transposed_tensor<Number2>(row, column_index);
1241 template <
typename Number,
1245 typename MemorySpace,
1247 template <
typename Number2,
typename Tensor>
1248 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Tensor
1258 static_assert(std::is_same_v<Number2, typename Tensor::value_type>,
1261 AssertIndexRange(row, sparsity_pattern_.n_rows());
1262 AssertIndexRange(column_index, sparsity_pattern_.row_length(row));
1264 dealii::Tensor<1, n_comp, Number2> result;
1266 using VA = dealii::VectorizedArray<Number, simd_length>;
1267 if constexpr (std::is_same_v<VA, Number2> && (n_comp == 1)) {
1273 Assert(row < sparsity_pattern_.n_internal_dofs(),
1275 "Vectorized access only possible in vectorized part"));
1276 Assert(row % simd_length == 0,
1278 "Access only supported for rows at the SIMD granularity"));
1280 const auto offsets =
1281 sparsity_pattern_.template transposed_offset_internal<1>(
1283 result[0].gather(data_.data(), offsets);
1285 }
else if constexpr (std::is_same_v<VA, Number2> && (n_comp != 1)) {
1289 dealii::ExcMessage(
"Vectorized transposed access to multiple "
1290 "components is not implemented."));
1298 for (
unsigned int d = 0; d < n_comp; ++d) {
1300 sparsity_pattern_.template transposed_offset<n_comp>(
1301 row, column_index, d);
1302 result[d] = data_(offset);
1310 template <
typename Number,
1314 typename MemorySpace,
1316 template <
typename Number2>
1317 DEAL_II_HOST_DEVICE_ALWAYS_INLINE
void
1324 const unsigned int row,
1325 const unsigned int column_index,
1326 const bool do_streaming_store)
const
1331 "Attempted to write a scalar value into a tensor-valued matrix entry");
1333 AssertIndexRange(row, sparsity_pattern_.n_rows());
1334 AssertIndexRange(column_index, sparsity_pattern_.row_length(row));
1336 dealii::Tensor<1, n_comp, Number2> tensor;
1339 write_tensor<Number2>(tensor, row, column_index, do_streaming_store);
1343 template <
typename Number,
1347 typename MemorySpace,
1349 template <
typename Number2,
typename Tensor>
1350 DEAL_II_HOST_DEVICE_ALWAYS_INLINE
void
1357 const unsigned int row,
1358 const unsigned int column_index,
1359 const bool do_streaming_store)
const
1362 AssertIndexRange(row, sparsity_pattern_.n_rows());
1363 AssertIndexRange(column_index, sparsity_pattern_.row_length(row));
1365 using VA = dealii::VectorizedArray<Number, simd_length>;
1366 if constexpr (std::is_same_v<VA, Number2>) {
1372 Assert(row < sparsity_pattern_.n_internal_dofs(),
1374 "Vectorized access only possible in vectorized part"));
1375 Assert(row % simd_length == 0,
1377 "Access only supported for rows at the SIMD granularity"));
1379 Number *store_pos = data_.data();
1381 sparsity_pattern_.template offset_internal<n_comp>(row, column_index);
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);
1387 for (
unsigned int d = 0; d < n_comp; ++d)
1388 tensor[d].store(store_pos + d *
warp_size);
1395 for (
unsigned int d = 0; d < n_comp; ++d) {
1397 sparsity_pattern_.template offset<n_comp>(row, column_index, d);
1398 data_(offset) = tensor[d];
1404 template <
typename Number,
1408 typename MemorySpace,
1410 template <
typename Number2>
1411 DEAL_II_HOST_DEVICE_ALWAYS_INLINE
void
1418 const unsigned int row,
1419 const unsigned int column_index)
const
1424 "Attempted to write a scalar value into a tensor-valued matrix entry");
1426 AssertIndexRange(row, sparsity_pattern_.n_rows());
1427 AssertIndexRange(column_index, sparsity_pattern_.row_length(row));
1429 dealii::Tensor<1, n_comp, Number2> tensor;
1432 add_tensor<Number2>(tensor, row, column_index);
1436 template <
typename Number,
1440 typename MemorySpace,
1442 template <
typename Number2,
typename Tensor>
1443 DEAL_II_HOST_DEVICE_ALWAYS_INLINE
void
1450 const unsigned int row,
1451 const unsigned int column_index)
const
1454 AssertIndexRange(row, sparsity_pattern_.n_rows());
1455 AssertIndexRange(column_index, sparsity_pattern_.row_length(row));
1457 using VA = dealii::VectorizedArray<Number, simd_length>;
1458 if constexpr (std::is_same_v<VA, Number2>) {
1464 Assert(row < sparsity_pattern_.n_internal_dofs(),
1466 "Vectorized access only possible in vectorized part"));
1467 Assert(row % simd_length == 0,
1469 "Access only supported for rows at the SIMD granularity"));
1471 Number *store_pos = data_.data();
1473 sparsity_pattern_.template offset_internal<n_comp>(row, column_index);
1475 for (
unsigned int d = 0; d < n_comp; ++d) {
1476 auto temp = tensor[d];
1487 for (
unsigned int d = 0; d < n_comp; ++d) {
1489 sparsity_pattern_.template offset<n_comp>(row, column_index, d);
1490 data_(offset) += tensor[d];
1497 template <
typename Number,
1501 typename MemorySpace,
1511 using HostSpace = dealii::MemorySpace::Host;
1512 using DefaultSpace = dealii::MemorySpace::Default;
1514 static_assert(std::is_same_v<MemorySpace, HostSpace> ||
1515 std::is_same_v<MemorySpace, DefaultSpace>,
1516 "Unexpected Kokkos memory space");
1518 Assert(sparse_matrix_->template is_resident<MemorySpace>(),
1519 dealii::ExcMessage(
"The chosen memory space is not resident."));
1521 sparse_matrix_->template zero_out_ghost_rows_on_memory_space<MemorySpace>();
1525 template <
typename Number,
1529 typename MemorySpace,
1539 using HostSpace = dealii::MemorySpace::Host;
1540 using DefaultSpace = dealii::MemorySpace::Default;
1542 static_assert(std::is_same_v<MemorySpace, HostSpace> ||
1543 std::is_same_v<MemorySpace, DefaultSpace>,
1544 "Unexpected Kokkos memory space");
1546 Assert(sparse_matrix_->template is_resident<MemorySpace>(),
1547 dealii::ExcMessage(
"The chosen memory space is not resident."));
1549 sparse_matrix_->template update_ghost_rows_on_memory_space<MemorySpace>();
1553 template <
typename Number,
1557 typename MemorySpace,
1564 writable>
::compress(dealii::VectorOperation::values
1568 using HostSpace = dealii::MemorySpace::Host;
1569 using DefaultSpace = dealii::MemorySpace::Default;
1571 static_assert(std::is_same_v<MemorySpace, HostSpace> ||
1572 std::is_same_v<MemorySpace, DefaultSpace>,
1573 "Unexpected Kokkos memory space");
1575 Assert(sparse_matrix_->template is_resident<MemorySpace>(),
1576 dealii::ExcMessage(
"The chosen memory space is not resident."));
1578 sparse_matrix_->template compress_on_memory_space<MemorySpace>(operation);
1582 template <
typename Number,
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
1593 SparseMatrix<Number, n_comp, warp_size, simd_length> &sparse_matrix)
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");
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());
1615 const auto sparse_matrix_view = sparse_matrix.view();
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();
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{})
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>{})
1641 const auto col_idx = sparsity_pattern_view.column_index(i, j);
1642 sparse_matrix_view.add_tensor(c_ij * entry, i, col_idx);
1650 const auto iterate_over_row_entries =
1651 [&](
const auto r,
const auto i,
const auto c_i) DEAL_II_ALWAYS_INLINE {
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)) {
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);
1663 const auto j = partitioner->global_to_local(j_global);
1664 insert_entry(r, c, i, j, c_i);
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);
1679 const auto i = partitioner->global_to_local(i_global);
1680 iterate_over_row_entries(r, i, Number(1.));
1686 template <
typename Number,
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)
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");
TransferPolicy transfer_policy() const
friend class SparseMatrixView
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)
SparseMatrixView()=default
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
constexpr bool have_separate_memory_spaces
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)