ryujin 2.5.0 revision c64172211cf21344cb9b9b0f3a1266aa6400d392
Loading...
Searching...
No Matches
sparsity_pattern.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3// Copyright (C) 2026 by the ryujin authors
4//
5
6#pragma once
7
8#include <ryujin/base/compile_time_options.h>
10#include <ryujin/base/gpu.h>
11
12#include <deal.II/base/aligned_vector.h>
13#include <deal.II/base/config.h>
14#include <deal.II/base/partitioner.h>
15#include <deal.II/lac/dynamic_sparsity_pattern.h>
16
17namespace ryujin
18{
19 template <int warp_size, typename MemorySpace = dealii::MemorySpace::Host>
20 class SparsityPatternView;
21
22
48 template <int warp_size>
49 class SparsityPattern : public MirroredStorage<SparsityPattern<warp_size>>
50 {
51 public:
56
64 unsigned int row;
65 unsigned int column_index;
66 };
67
69
73
78
85 const unsigned int n_internal_dofs,
86 const dealii::DynamicSparsityPattern &sparsity,
87 const std::shared_ptr<const dealii::Utilities::MPI::Partitioner>
88 &partitioner,
89 bool symmetrize_ghost_range = true,
92
103 void reinit(const unsigned int n_internal_dofs,
104 const dealii::DynamicSparsityPattern &sparsity,
105 const std::shared_ptr<const dealii::Utilities::MPI::Partitioner>
106 &partitioner,
107 bool symmetrize_ghost_range = true,
110
115 template <typename MemorySpace = dealii::MemorySpace::Host>
117
119
127 ACCESSOR_READ_ONLY(entries_to_be_sent);
128
134 ACCESSOR_READ_ONLY(send_targets);
135
147 ACCESSOR_READ_ONLY(receive_targets);
148
149 private:
151
155
156 std::shared_ptr<const dealii::Utilities::MPI::Partitioner> partitioner_;
157
158 unsigned int n_internal_dofs_;
159 unsigned int n_locally_owned_dofs_;
160
161 using KokkosHost = dealii::MemorySpace::Host::kokkos_space;
162 mutable Kokkos::View<unsigned int *, KokkosHost> row_starts_host_;
163 mutable Kokkos::View<unsigned int *, KokkosHost> column_indices_host_;
164 mutable Kokkos::View<unsigned int *, KokkosHost> indices_transposed_host_;
165
166 using KokkosDefault = dealii::MemorySpace::Default::kokkos_space;
167 mutable Kokkos::View<unsigned int *, KokkosDefault> row_starts_default_;
168 mutable Kokkos::View<unsigned int *, KokkosDefault> column_indices_default_;
169 mutable Kokkos::View<unsigned int *, KokkosDefault>
170 indices_transposed_default_;
171
172 Mirrored<ExchangeDescription *> entries_to_be_sent_{
173 "sparsity_pattern_entries_to_be_sent"};
174
175 std::vector<std::pair<unsigned int, unsigned int>> send_targets_;
176 std::vector<std::pair<unsigned int, unsigned int>> receive_targets_;
177
178 /*
179 * Storage primitives used by the MirroredStorage base class:
180 */
181
182 template <typename MemorySpace>
183 void allocate_storage() const;
184
185 template <typename To, typename From>
186 void deep_copy_storage() const;
187
188 template <typename MemorySpace>
189 void deallocate_storage();
190
191
193
194 template <int, typename>
197 };
198
199
214 template <int warp_size, typename MemorySpace>
216 {
217 public:
222
224
226
227 void reinit(const SparsityPattern<warp_size> &sparsity_pattern);
228
230
234
239 DEAL_II_HOST_DEVICE
240 unsigned int n_internal_dofs() const;
241
245 DEAL_II_HOST_DEVICE
246 unsigned int n_locally_owned_dofs() const;
247
253 DEAL_II_HOST_DEVICE
254 unsigned int n_rows() const;
255
259 DEAL_II_HOST_DEVICE
260 unsigned int n_nonzero_elements() const;
261
263
267
274 DEAL_II_HOST_DEVICE
275 unsigned int stride_of_row(const unsigned int row) const;
276
286 DEAL_II_HOST_DEVICE
287 const unsigned int *columns(const unsigned int row) const;
288
292 DEAL_II_HOST_DEVICE
293 unsigned int row_length(const unsigned int row) const;
294
300 DEAL_II_HOST_DEVICE
301 unsigned int column_index(const unsigned int row,
302 const unsigned int column) const;
303
305
309
315 template <unsigned int n_components = 1>
316 DEAL_II_HOST_DEVICE unsigned int
317 offset(const unsigned int row,
318 const unsigned int column_index,
319 const unsigned int component = 0) const;
320
333 template <unsigned int n_components = 1>
334 DEAL_II_HOST_DEVICE unsigned int
335 offset_internal(const unsigned int row,
336 const unsigned int column_index) const;
337
343 template <unsigned int n_components = 1>
344 DEAL_II_HOST_DEVICE unsigned int
345 transposed_offset(const unsigned int row,
346 const unsigned int column_index,
347 const unsigned int component = 0) const;
348
356 template <unsigned int n_components = 1>
357 DEAL_II_HOST_DEVICE const unsigned int *
358 transposed_offset_internal(const unsigned int row,
359 const unsigned int column_index) const;
360
370 template <unsigned int n_components = 1>
371 DEAL_II_HOST_DEVICE unsigned int ghost_offset() const;
372
374
378
379 private:
380 unsigned int n_internal_dofs_;
381 unsigned int n_locally_owned_dofs_;
382
383 using KokkosSpace = typename MemorySpace::kokkos_space;
384 Kokkos::View<const unsigned int *, KokkosSpace> row_starts_;
385 Kokkos::View<const unsigned int *, KokkosSpace> column_indices_;
386 Kokkos::View<const unsigned int *, KokkosSpace> indices_transposed_;
388 };
389
390
391#ifndef DOXYGEN
392 /*
393 * -------------------------------------------------------------------------
394 * Inline function definitions
395 * -------------------------------------------------------------------------
396 */
397
398
399 template <int warp_size>
400 template <typename MemorySpace>
403 {
404 this->template prepare_read_access<MemorySpace>();
405
407 }
408
409
410 template <int warp_size>
411 template <typename MemorySpace>
412 void SparsityPattern<warp_size>::allocate_storage() const
413 {
414 using HostSpace = dealii::MemorySpace::Host;
415 using Aligned = Kokkos::MemoryTraits<Kokkos::Aligned>;
416
417 /* Note: We allocate without initializing. */
418
419 if constexpr (std::is_same_v<MemorySpace, HostSpace>) {
420 row_starts_host_ = Kokkos::View<unsigned int *, KokkosHost, Aligned>(
421 Kokkos::view_alloc(Kokkos::WithoutInitializing,
422 "sparsity_pattern_row_starts"),
423 row_starts_default_.extent(0));
424
425 column_indices_host_ = Kokkos::View<unsigned int *, KokkosHost, Aligned>(
426 Kokkos::view_alloc(Kokkos::WithoutInitializing,
427 "sparsity_pattern_column_indices"),
428 column_indices_default_.extent(0));
429
430 indices_transposed_host_ =
431 Kokkos::View<unsigned int *, KokkosHost, Aligned>(
432 Kokkos::view_alloc(Kokkos::WithoutInitializing,
433 "sparsity_pattern_indices_transposed"),
434 indices_transposed_default_.extent(0));
435
436 } else {
437 row_starts_default_ = Kokkos::View<unsigned int *, KokkosDefault>(
438 Kokkos::view_alloc(Kokkos::WithoutInitializing,
439 "sparsity_pattern_row_starts"),
440 row_starts_host_.extent(0));
441
442 column_indices_default_ = Kokkos::View<unsigned int *, KokkosDefault>(
443 Kokkos::view_alloc(Kokkos::WithoutInitializing,
444 "sparsity_pattern_column_indices"),
445 column_indices_host_.extent(0));
446
447 indices_transposed_default_ = Kokkos::View<unsigned int *, KokkosDefault>(
448 Kokkos::view_alloc(Kokkos::WithoutInitializing,
449 "sparsity_pattern_indices_transposed"),
450 indices_transposed_host_.extent(0));
451 }
452 }
453
454
455 template <int warp_size>
456 template <typename To, typename From>
457 void SparsityPattern<warp_size>::deep_copy_storage() const
458 {
459 using HostSpace = dealii::MemorySpace::Host;
460
461 if constexpr (std::is_same_v<To, HostSpace>) {
462 Kokkos::deep_copy(/*dst*/ row_starts_host_, /*src*/ row_starts_default_);
463 Kokkos::deep_copy(/*dst*/ column_indices_host_,
464 /*src*/ column_indices_default_);
465 Kokkos::deep_copy(/*dst*/ indices_transposed_host_,
466 /*src*/ indices_transposed_default_);
467 } else {
468 Kokkos::deep_copy(/*dst*/ row_starts_default_, /*src*/ row_starts_host_);
469 Kokkos::deep_copy(/*dst*/ column_indices_default_,
470 /*src*/ column_indices_host_);
471 Kokkos::deep_copy(/*dst*/ indices_transposed_default_,
472 /*src*/ indices_transposed_host_);
473 }
474 }
475
476
477 template <int warp_size>
478 template <typename MemorySpace>
479 void SparsityPattern<warp_size>::deallocate_storage()
480 {
481 using HostSpace = dealii::MemorySpace::Host;
482
483 if constexpr (std::is_same_v<MemorySpace, HostSpace>) {
484 row_starts_host_ = {};
485 column_indices_host_ = {};
486 indices_transposed_host_ = {};
487
488 } else {
489 row_starts_default_ = {};
490 column_indices_default_ = {};
491 indices_transposed_default_ = {};
492 }
493 }
494
495
496 template <int warp_size, typename MemorySpace>
498 const SparsityPattern<warp_size> &sparsity_pattern)
499 {
500 reinit(sparsity_pattern);
501 }
502
503
504 template <int warp_size, typename MemorySpace>
506 const SparsityPattern<warp_size> &sparsity_pattern)
507 {
508 n_internal_dofs_ = sparsity_pattern.n_internal_dofs_;
509 n_locally_owned_dofs_ = sparsity_pattern.n_locally_owned_dofs_;
510
511 using HostSpace = dealii::MemorySpace::Host;
512 using DefaultSpace = dealii::MemorySpace::Default;
513
514 static_assert(std::is_same_v<MemorySpace, HostSpace> ||
515 std::is_same_v<MemorySpace, DefaultSpace>,
516 "Unexpected memory space");
517
518 /*
519 * Note: If the host and default memory spaces coincide all views
520 * reference the host storage.
521 */
522 if constexpr (have_separate_memory_spaces &&
523 !std::is_same_v<MemorySpace, HostSpace>) {
524 row_starts_ = sparsity_pattern.row_starts_default_;
525 column_indices_ = sparsity_pattern.column_indices_default_;
526 indices_transposed_ = sparsity_pattern.indices_transposed_default_;
527 } else {
528 row_starts_ = sparsity_pattern.row_starts_host_;
529 column_indices_ = sparsity_pattern.column_indices_host_;
530 indices_transposed_ = sparsity_pattern.indices_transposed_host_;
531 }
532 }
533
534
535 template <int warp_size, typename MemorySpace>
536 DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int
538 {
539 return n_internal_dofs_;
540 }
541
542
543 template <int warp_size, typename MemorySpace>
544 DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int
546 {
547 return n_locally_owned_dofs_;
548 }
549
550
551 template <int warp_size, typename MemorySpace>
552 DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int
554 {
555 Assert(row_starts_.size() > 0, dealii::ExcNotInitialized());
556
557 return row_starts_.size() - 1;
558 }
559
560
561 template <int warp_size, typename MemorySpace>
562 DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int
564 {
565 Assert(row_starts_.size() > 0, dealii::ExcNotInitialized());
566
567 return row_starts_(row_starts_.size() - 1);
568 }
569
570
571 template <int warp_size, typename MemorySpace>
572 DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int
574 const unsigned int row) const
575 {
576 AssertIndexRange(row, n_rows());
577
578 if (row < n_internal_dofs_)
579 return warp_size;
580 else
581 return 1;
582 }
583
584
585 template <int warp_size, typename MemorySpace>
586 DEAL_II_HOST_DEVICE_ALWAYS_INLINE const unsigned int *
588 const unsigned int row) const
589 {
590 AssertIndexRange(row, n_rows());
591
592 if (row < n_internal_dofs_)
593 return column_indices_.data() + row_starts_(row / warp_size) +
594 row % warp_size;
595 else
596 return column_indices_.data() + row_starts_(row);
597 }
598
599
600 template <int warp_size, typename MemorySpace>
601 DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int
603 const unsigned int row) const
604 {
605 AssertIndexRange(row, n_rows());
606
607 if (row < n_internal_dofs_) {
608 const unsigned int warp = row / warp_size;
609 return (row_starts_(warp + 1) - row_starts_(warp)) / warp_size;
610 } else {
611 return row_starts_(row + 1) - row_starts_(row);
612 }
613 }
614
615
616 template <int warp_size, typename MemorySpace>
617 DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int
619 const unsigned int row, const unsigned int column) const
620 {
621 const auto &row_length = this->row_length(row);
622 const auto &stride_size = this->stride_of_row(row);
623
624 // FIXME: with C++23 use std::views::stride and binary search instead...
625
626 const unsigned int *js = columns(row);
627 for (unsigned int k = 0; k < row_length; ++k)
628 if (js[k * stride_size] == column)
629 return k;
630
631 Assert(false, dealii::ExcMessage("Column index not found in given row"));
632 return -1;
633 }
634
635
636 template <int warp_size, typename MemorySpace>
637 template <unsigned int n_components>
638 DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int
640 const unsigned int row,
641 const unsigned int column_index,
642 const unsigned int comp) const
643 {
644 AssertIndexRange(row, n_rows());
645 AssertIndexRange(column_index, row_length(row));
646 AssertIndexRange(comp, n_components);
647
648 const unsigned int warp = row / warp_size;
649 const unsigned int lane = row % warp_size;
650
651 if (row < n_internal_dofs_) {
652 const unsigned int scalar_offset =
653 row_starts_(warp) + column_index * warp_size;
654 return scalar_offset * n_components + comp * warp_size + lane;
655
656 } else {
657 const unsigned int scalar_offset = row_starts_(row) + column_index;
658
659 return scalar_offset * n_components + comp;
660 }
661 }
662
663
664 template <int warp_size, typename MemorySpace>
665 template <unsigned int n_components>
666 DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int
668 const unsigned int row,
669 const unsigned int column_index,
670 const unsigned int component) const
671 {
672 AssertIndexRange(row, n_rows());
673 AssertIndexRange(column_index, row_length(row));
674 AssertIndexRange(component, n_components);
675
676 // Compute the transposed index from the (scalar) numbering stored in
677 // the sparsity pattern...
678 const unsigned int scalar_offset = offset(row, column_index);
679 const unsigned int transposed_scalar_offset =
680 indices_transposed_(scalar_offset);
681
682 // ... and reconstruct the proper index for a view with n_components:
683 const unsigned int j = column_indices_(scalar_offset);
684
685 unsigned int transposed_offset = transposed_scalar_offset;
686 if constexpr (n_components > 1) {
687 if (j < n_internal_dofs_) {
688 transposed_offset = //
689 transposed_offset / warp_size * warp_size * n_components +
690 transposed_offset % warp_size;
691 return transposed_offset + component * warp_size;
692
693 } else {
694
695 transposed_offset *= n_components;
696 return transposed_offset + component;
697 }
698
699 } else {
700
701 return transposed_offset;
702 }
703 }
704
705
706 template <int warp_size, typename MemorySpace>
707 template <unsigned int n_components>
708 DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int
710 const unsigned int row, const unsigned int column_index) const
711 {
712 AssertIndexRange(row, n_rows());
713 AssertIndexRange(column_index, row_length(row));
714 AssertIndexRange(row, n_internal_dofs_);
715
716 const unsigned int warp = row / warp_size;
717 const unsigned int lane = row % warp_size;
718
719 const unsigned int scalar_offset =
720 row_starts_(warp) + column_index * warp_size;
721
722 return scalar_offset * n_components + lane;
723 }
724
725
726 template <int warp_size, typename MemorySpace>
727 template <unsigned int n_components>
728 DEAL_II_HOST_DEVICE_ALWAYS_INLINE const unsigned int *
730 const unsigned int row, const unsigned int column_index) const
731 {
732 static_assert(n_components == 1,
733 "Vectorized transposed access to multiple components is not "
734 "yet implemented.");
735 AssertIndexRange(row, row_starts_.size() - 1);
736 AssertIndexRange(column_index, row_length(row));
737 AssertIndexRange(row, n_internal_dofs_);
738
739 const unsigned int warp = row / warp_size;
740 const unsigned int lane = row % warp_size;
741
742 const unsigned int scalar_offset =
743 row_starts_(warp) + column_index * warp_size;
744
745 // n_components == 1
746 return indices_transposed_.data() + scalar_offset + lane;
747 }
748
749
750 template <int warp_size, typename MemorySpace>
751 template <unsigned int n_components>
752 DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int
754 {
755 const auto scalar_offset = row_starts_(n_locally_owned_dofs_);
756 return scalar_offset * n_components;
757 }
758
759
760#endif
761} // namespace ryujin
DEAL_II_HOST_DEVICE unsigned int offset_internal(const unsigned int row, const unsigned int column_index) const
DEAL_II_HOST_DEVICE unsigned int n_nonzero_elements() const
DEAL_II_HOST_DEVICE const unsigned int * transposed_offset_internal(const unsigned int row, const unsigned int column_index) const
DEAL_II_HOST_DEVICE unsigned int n_internal_dofs() const
void reinit(const SparsityPattern< warp_size > &sparsity_pattern)
DEAL_II_HOST_DEVICE unsigned int stride_of_row(const unsigned int row) const
SparsityPatternView(const SparsityPattern< warp_size > &sparsity_pattern)
DEAL_II_HOST_DEVICE unsigned int row_length(const unsigned int row) const
DEAL_II_HOST_DEVICE const unsigned int * columns(const unsigned int row) const
DEAL_II_HOST_DEVICE unsigned int offset(const unsigned int row, const unsigned int column_index, const unsigned int component=0) const
DEAL_II_HOST_DEVICE unsigned int n_locally_owned_dofs() const
DEAL_II_HOST_DEVICE unsigned int ghost_offset() const
DEAL_II_HOST_DEVICE unsigned int n_rows() const
DEAL_II_HOST_DEVICE unsigned int transposed_offset(const unsigned int row, const unsigned int column_index, const unsigned int component=0) const
DEAL_II_HOST_DEVICE unsigned int column_index(const unsigned int row, const unsigned int column) const
ACCESSOR_READ_ONLY(entries_to_be_sent)
SparsityPatternView< warp_size, MemorySpace > view() const
void reinit(const unsigned int n_internal_dofs, const dealii::DynamicSparsityPattern &sparsity, const std::shared_ptr< const dealii::Utilities::MPI::Partitioner > &partitioner, bool symmetrize_ghost_range=true, const TransferPolicy transfer_policy=TransferPolicy::explicit_transfers)
SparsityPattern(const unsigned int n_internal_dofs, const dealii::DynamicSparsityPattern &sparsity, const std::shared_ptr< const dealii::Utilities::MPI::Partitioner > &partitioner, bool symmetrize_ghost_range=true, const TransferPolicy transfer_policy=TransferPolicy::explicit_transfers)
ACCESSOR_READ_ONLY(receive_targets)
ACCESSOR_READ_ONLY(send_targets)
ACCESSOR_READ_ONLY_NO_DEREFERENCE(partitioner)
constexpr unsigned int warp_size
Definition gpu.h:46
TransferPolicy
Definition gpu.h:88
constexpr bool have_separate_memory_spaces
Definition gpu.h:29