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