deal.II version 9.8.0
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
index_set.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2009 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
15#include <deal.II/base/mpi.h>
16
19
20#include <boost/container/small_vector.hpp>
21
22#include <vector>
23
24#ifdef DEAL_II_WITH_TRILINOS
26# ifdef DEAL_II_TRILINOS_WITH_EPETRA
27# ifdef DEAL_II_WITH_MPI
28# include <Epetra_MpiComm.h>
29# endif
30# include <Epetra_Map.h>
31# include <Epetra_SerialComm.h>
32# endif
33# ifdef DEAL_II_TRILINOS_WITH_TPETRA
34# include <Tpetra_Map.hpp>
35# endif
37#endif
38
40
41
42
43#ifdef DEAL_II_WITH_TRILINOS
44
45# ifdef DEAL_II_TRILINOS_WITH_TPETRA
46
47template <typename NodeType>
49 const Teuchos::RCP<
50 const Tpetra::Map<int, types::signed_global_dof_index, NodeType>> &map)
51 : is_compressed(true)
52 , index_space_size(1 + map->getMaxAllGlobalIndex())
53 , largest_range(numbers::invalid_unsigned_int)
54{
55 Assert(map->getMinAllGlobalIndex() == 0,
57 "The Tpetra::Map does not contain the global index 0, "
58 "which means some entries are not present on any processor."));
59
60 // For a contiguous map, we do not need to go through the whole data...
61 if (map->isContiguous())
62 add_range(size_type(map->getMinGlobalIndex()),
63 size_type(map->getMaxGlobalIndex() + 1));
64 else
65 {
66# if DEAL_II_TRILINOS_VERSION_GTE(13, 4, 0)
67 const size_type n_indices = map->getLocalNumElements();
68# else
69 const size_type n_indices = map->getNodeNumElements();
70# endif
71 const types::signed_global_dof_index *indices =
72 map->getMyGlobalIndices().data();
73 add_indices(indices, indices + n_indices);
74 }
75 compress();
76}
77
78# endif // DEAL_II_TRILINOS_WITH_TPETRA
79
80
81// the 64-bit path uses a few different names, so put that into a separate
82// implementation
83
84# ifdef DEAL_II_TRILINOS_WITH_EPETRA
85# ifdef DEAL_II_WITH_64BIT_INDICES
86
87IndexSet::IndexSet(const Epetra_BlockMap &map)
88 : is_compressed(true)
89 , index_space_size(1 + map.MaxAllGID64())
90 , largest_range(numbers::invalid_unsigned_int)
91{
92 Assert(map.MinAllGID64() == 0,
94 "The Epetra_BlockMap does not contain the global index 0, "
95 "which means some entries are not present on any processor."));
96
97 // For a contiguous map, we do not need to go through the whole data...
98 if (map.LinearMap())
99 add_range(size_type(map.MinMyGID64()), size_type(map.MaxMyGID64() + 1));
100 else
101 {
102 const size_type n_indices = map.NumMyElements();
103 size_type *indices =
104 reinterpret_cast<size_type *>(map.MyGlobalElements64());
105 add_indices(indices, indices + n_indices);
106 }
107 compress();
108}
109
110# else
111
112// this is the standard 32-bit implementation
113
114IndexSet::IndexSet(const Epetra_BlockMap &map)
115 : is_compressed(true)
116 , index_space_size(1 + map.MaxAllGID())
117 , largest_range(numbers::invalid_unsigned_int)
118{
119 Assert(map.MinAllGID() == 0,
121 "The Epetra_BlockMap does not contain the global index 0, "
122 "which means some entries are not present on any processor."));
123
124 // For a contiguous map, we do not need to go through the whole data...
125 if (map.LinearMap())
126 add_range(size_type(map.MinMyGID()), size_type(map.MaxMyGID() + 1));
127 else
128 {
129 const size_type n_indices = map.NumMyElements();
130 unsigned int *indices =
131 reinterpret_cast<unsigned int *>(map.MyGlobalElements());
132 add_indices(indices, indices + n_indices);
133 }
134 compress();
135}
136
137# endif
138# endif
139
140#endif // ifdef DEAL_II_WITH_TRILINOS
141
142
143
144void
146{
147 {
148 // we will, in the following, modify mutable variables. this can only
149 // work in multithreaded applications if we lock the data structures
150 // via a mutex, so that users can call 'const' functions from threads
151 // in parallel (and these 'const' functions can then call compress()
152 // which itself calls the current function)
153 std::scoped_lock lock(compress_mutex);
154
155 // see if any of the contiguous ranges can be merged. do not use
156 // std::vector::erase in-place as it is quadratic in the number of
157 // ranges. since the ranges are sorted by their first index, determining
158 // overlap isn't all that hard
159 std::vector<Range>::iterator store = ranges.begin();
160 for (std::vector<Range>::iterator i = ranges.begin(); i != ranges.end();)
161 {
162 std::vector<Range>::iterator next = i;
163 ++next;
164
165 size_type first_index = i->begin;
166 size_type last_index = i->end;
167
168 // see if we can merge any of the following ranges
169 while (next != ranges.end() && (next->begin <= last_index))
170 {
171 last_index = std::max(last_index, next->end);
172 ++next;
173 }
174 i = next;
175
176 // store the new range in the slot we last occupied
177 *store = Range(first_index, last_index);
178 ++store;
179 }
180 // use a compact array with exactly the right amount of storage
181 if (store != ranges.end())
182 {
183 std::vector<Range> new_ranges(ranges.begin(), store);
184 ranges.swap(new_ranges);
185 }
186
187 // now compute indices within set and the range with most elements
188 size_type next_index = 0, largest_range_size = 0;
189 for (std::vector<Range>::iterator i = ranges.begin(); i != ranges.end();
190 ++i)
191 {
192 Assert(i->begin < i->end, ExcInternalError());
193
194 i->nth_index_in_set = next_index;
195 next_index += (i->end - i->begin);
196 if (i->end - i->begin > largest_range_size)
197 {
198 largest_range_size = i->end - i->begin;
199 largest_range = i - ranges.begin();
200 }
201 }
202 is_compressed = true;
203
204 // check that next_index is correct. needs to be after the previous
205 // statement because we otherwise will get into an endless loop
206 Assert(next_index == n_elements(), ExcInternalError());
207 }
208
209 if constexpr (running_in_debug_mode())
210 {
211 // Avoid code duplication by doing consistency checks here instead of in
212 // calling functions:
213 //
214 // 1. Verify that we only added indices that are within the range of the
215 // index set.
216 //
217 // 2. Verify that we calculated the size in a consistent way.
218 size_type n_owned_elements = 0;
219 for (const auto &range : ranges)
220 {
221 Assert(
222 (range.begin < index_space_size) && (range.end <= index_space_size),
223 ExcMessage("In the process of creating the current IndexSet "
224 "object, you added indices beyond the size of the index "
225 "space. Specifically, you added elements that form the "
226 "range [" +
227 std::to_string(range.begin) + "," +
228 std::to_string(range.end) +
229 "), but the size of the index space is only " +
230 std::to_string(index_space_size) + "."));
231 n_owned_elements += (range.end - range.begin);
232 }
233
234 if (!ranges.empty())
235 {
236 const Range &r = ranges.back();
237 Assert(r.nth_index_in_set + r.end - r.begin == n_owned_elements,
239 }
240 }
241}
242
243
244
245#ifndef DOXYGEN
247IndexSet::operator&(const IndexSet &is) const
248{
249 Assert(size() == is.size(), ExcDimensionMismatch(size(), is.size()));
250
251 compress();
252 is.compress();
253
254 std::vector<Range>::const_iterator r1 = ranges.begin(),
255 r2 = is.ranges.begin();
256 IndexSet result(size());
257
258 while ((r1 != ranges.end()) && (r2 != is.ranges.end()))
259 {
260 // if r1 and r2 do not overlap at all, then move the pointer that sits
261 // to the left of the other up by one
262 if (r1->end <= r2->begin)
263 ++r1;
264 else if (r2->end <= r1->begin)
265 ++r2;
266 else
267 {
268 // the ranges must overlap somehow
269 Assert(((r1->begin <= r2->begin) && (r1->end > r2->begin)) ||
270 ((r2->begin <= r1->begin) && (r2->end > r1->begin)),
272
273 // add the overlapping range to the result
274 result.add_range(std::max(r1->begin, r2->begin),
275 std::min(r1->end, r2->end));
276
277 // now move that iterator that ends earlier one up. note that it has
278 // to be this one because a subsequent range may still have a chance
279 // of overlapping with the range that ends later
280 if (r1->end <= r2->end)
281 ++r1;
282 else
283 ++r2;
284 }
285 }
286
287 result.compress();
288 return result;
289}
290#endif
291
292
293
296{
297 Assert(begin <= end,
298 ExcMessage("End index needs to be larger or equal to begin index!"));
299 Assert(end <= size(),
300 ExcMessage("You are asking for a view into an IndexSet object "
301 "that would cover the sub-range [" +
302 std::to_string(begin) + ',' + std::to_string(end) +
303 "). But this is not a subset of the range "
304 "of the current object, which is [0," +
305 std::to_string(size()) + ")."));
306
307 IndexSet result(end - begin);
308 std::vector<Range>::const_iterator r1 = ranges.begin();
309
310 while (r1 != ranges.end())
311 {
312 if ((r1->end > begin) && (r1->begin < end))
313 {
314 result.add_range(std::max(r1->begin, begin) - begin,
315 std::min(r1->end, end) - begin);
316 }
317 else if (r1->begin >= end)
318 break;
319
320 ++r1;
321 }
322
323 result.compress();
324 return result;
325}
326
327
328
330IndexSet::get_view(const IndexSet &mask) const
331{
332 Assert(size() == mask.size(),
333 ExcMessage("The mask must have the same size index space "
334 "as the index set it is applied to."));
335
336 // If 'other' is an empty set, then the view is also empty:
337 if (mask == IndexSet())
338 return {};
339
340 // For everything, it is more efficient to work on compressed sets:
341 compress();
342 mask.compress();
343
344 // If 'other' has a single range, then we can just defer to the
345 // previous function
346 if (mask.ranges.size() == 1)
347 return get_view(mask.ranges[0].begin, mask.ranges[0].end);
348
349 // For the general case where the mask is an arbitrary set,
350 // the situation is slightly more complicated. We need to walk
351 // the ranges of the two index sets in parallel and search for
352 // overlaps, and then appropriately shift
353
354 // we save all new ranges to our IndexSet in an temporary vector and
355 // add all of them in one go at the end.
356 std::vector<Range> new_ranges;
357
358 std::vector<Range>::iterator own_it = ranges.begin();
359 std::vector<Range>::iterator mask_it = mask.ranges.begin();
360
361 while ((own_it != ranges.end()) && (mask_it != mask.ranges.end()))
362 {
363 // If our own range lies completely ahead of the current
364 // range in the mask, move forward and start the loop body
365 // anew. If this was the last range, the 'while' loop above
366 // will terminate, so we don't have to check for end iterators
367 if (own_it->end <= mask_it->begin)
368 {
369 ++own_it;
370 continue;
371 }
372
373 // Do the same if the current mask range lies completely ahead of
374 // the current range of the this object:
375 if (mask_it->end <= own_it->begin)
376 {
377 ++mask_it;
378 continue;
379 }
380
381 // Now own_it and other_it overlap. Check that that is true by
382 // enumerating the cases that can happen. This is
383 // surprisingly tricky because the two intervals can intersect in
384 // a number of different ways, but there really are only the four
385 // following possibilities:
386
387 // Case 1: our interval overlaps the left end of the other interval
388 //
389 // So we need to add the elements from the first element of the mask's
390 // interval to the end of our own interval. But we need to shift the
391 // indices so that they correspond to the how many'th element within the
392 // mask this is; fortunately (because we compressed the mask), this
393 // is recorded in the mask's ranges.
394 if ((own_it->begin <= mask_it->begin) && (own_it->end <= mask_it->end))
395 {
396 new_ranges.emplace_back(mask_it->begin - mask_it->nth_index_in_set,
397 own_it->end - mask_it->nth_index_in_set);
398 }
399 else
400 // Case 2:our interval overlaps the tail end of the other interval
401 if ((mask_it->begin <= own_it->begin) && (mask_it->end <= own_it->end))
402 {
403 const size_type offset_within_mask_interval =
404 own_it->begin - mask_it->begin;
405 new_ranges.emplace_back(mask_it->nth_index_in_set +
406 offset_within_mask_interval,
407 mask_it->nth_index_in_set +
408 (mask_it->end - mask_it->begin));
409 }
410 else
411 // Case 3: Our own interval completely encloses the other interval
412 if ((own_it->begin <= mask_it->begin) &&
413 (own_it->end >= mask_it->end))
414 {
415 new_ranges.emplace_back(mask_it->begin -
416 mask_it->nth_index_in_set,
417 mask_it->end - mask_it->nth_index_in_set);
418 }
419 else
420 // Case 3: The other interval completely encloses our own interval
421 if ((mask_it->begin <= own_it->begin) &&
422 (mask_it->end >= own_it->end))
423 {
424 const size_type offset_within_mask_interval =
425 own_it->begin - mask_it->begin;
426 new_ranges.emplace_back(mask_it->nth_index_in_set +
427 offset_within_mask_interval,
428 mask_it->nth_index_in_set +
429 offset_within_mask_interval +
430 (own_it->end - own_it->begin));
431 }
432 else
434
435 // We considered the overlap of these two intervals. It may of course
436 // be that one of them overlaps with another one, but that can only
437 // be the case for the interval that extends further to the right. So
438 // we can safely move on from the interval that terminates earlier:
439 if (own_it->end < mask_it->end)
440 ++own_it;
441 else if (mask_it->end < own_it->end)
442 ++mask_it;
443 else
444 {
445 // The intervals ended at the same point. We can move on from both.
446 // (The algorithm would also work if we only moved on from one,
447 // but we can micro-optimize here without too much effort.)
448 ++own_it;
449 ++mask_it;
450 }
451 }
452
453 // Now turn the ranges of overlap we have accumulated into an IndexSet in
454 // its own right:
455 IndexSet result(mask.n_elements());
456 for (const auto &range : new_ranges)
457 result.add_range(range.begin, range.end);
458 result.compress();
459
460 return result;
461}
462
463
464
465std::vector<IndexSet>
467 const std::vector<types::global_dof_index> &n_indices_per_block) const
468{
469 std::vector<IndexSet> partitioned;
470 const unsigned int n_blocks = n_indices_per_block.size();
471
472 partitioned.reserve(n_blocks);
473 types::global_dof_index start = 0;
474 for (const auto n_block_indices : n_indices_per_block)
475 {
476 partitioned.push_back(this->get_view(start, start + n_block_indices));
477 start += n_block_indices;
478 }
479
480 if constexpr (running_in_debug_mode())
481 {
483 for (const auto &partition : partitioned)
484 {
485 sum += partition.size();
486 }
487 AssertDimension(sum, this->size());
488 }
489
490 return partitioned;
491}
492
493
494
495void
497{
498 AssertDimension(size(), other.size());
499
500 compress();
501 other.compress();
502 is_compressed = false;
503
504
505 // we save all new ranges to our IndexSet in an temporary vector and
506 // add all of them in one go at the end.
507 std::vector<Range> new_ranges;
508
509 std::vector<Range>::iterator own_it = ranges.begin();
510 std::vector<Range>::iterator other_it = other.ranges.begin();
511
512 while (own_it != ranges.end() && other_it != other.ranges.end())
513 {
514 // advance own iterator until we get an overlap
515 if (own_it->end <= other_it->begin)
516 {
517 new_ranges.push_back(*own_it);
518 ++own_it;
519 continue;
520 }
521 // we are done with other_it, so advance
522 if (own_it->begin >= other_it->end)
523 {
524 ++other_it;
525 continue;
526 }
527
528 // Now own_it and other_it overlap. First save the part of own_it that
529 // is before other_it (if not empty).
530 if (own_it->begin < other_it->begin)
531 {
532 Range r(own_it->begin, other_it->begin);
533 r.nth_index_in_set = 0; // fix warning of unused variable
534 new_ranges.push_back(r);
535 }
536 // change own_it to the sub range behind other_it. Do not delete own_it
537 // in any case. As removal would invalidate iterators, we just shrink
538 // the range to an empty one.
539 own_it->begin = other_it->end;
540 if (own_it->begin > own_it->end)
541 {
542 own_it->begin = own_it->end;
543 ++own_it;
544 }
545
546 // continue without advancing iterators, the right one will be advanced
547 // next.
548 }
549
550 // make sure to take over the remaining ranges
551 for (; own_it != ranges.end(); ++own_it)
552 new_ranges.push_back(*own_it);
553
554 ranges.clear();
555
556 // done, now add the temporary ranges
557 const std::vector<Range>::iterator end = new_ranges.end();
558 for (std::vector<Range>::iterator it = new_ranges.begin(); it != end; ++it)
559 add_range(it->begin, it->end);
560
561 compress();
562}
563
564
565
568{
569 IndexSet set(this->size() * other.size());
570 for (const auto el : *this)
571 set.add_indices(other, el * other.size());
572 set.compress();
573 return set;
574}
575
576
577
580{
581 Assert(is_empty() == false,
583 "pop_back() failed, because this IndexSet contains no entries."));
584
585 const size_type index = ranges.back().end - 1;
586 --ranges.back().end;
587
588 if (ranges.back().begin == ranges.back().end)
589 ranges.pop_back();
590
591 return index;
592}
593
594
595
598{
599 Assert(is_empty() == false,
601 "pop_front() failed, because this IndexSet contains no entries."));
602
603 const size_type index = ranges.front().begin;
604 ++ranges.front().begin;
605
606 if (ranges.front().begin == ranges.front().end)
607 ranges.erase(ranges.begin());
608
609 // We have to set this in any case, because nth_index_in_set is no longer
610 // up to date for all but the first range
611 is_compressed = false;
612
613 return index;
614}
615
616
617
618void
620{
621 // if the inserted range is already within the range we find by lower_bound,
622 // there is no need to do anything; we do not try to be clever here and
623 // leave all other work to compress().
624 const auto insert_position =
625 Utilities::lower_bound(ranges.begin(), ranges.end(), new_range);
626 if (insert_position == ranges.end() ||
627 insert_position->begin > new_range.begin ||
628 insert_position->end < new_range.end)
629 ranges.insert(insert_position, new_range);
630}
631
632
633
634void
636 boost::container::small_vector<std::pair<size_type, size_type>, 200>
637 &tmp_ranges,
638 const bool ranges_are_sorted)
639{
640 if (!ranges_are_sorted)
641 std::sort(tmp_ranges.begin(), tmp_ranges.end());
642
643 // if we have many ranges, we first construct a temporary index set (where
644 // we add ranges in a consecutive way, so fast), otherwise, we work with
645 // add_range(). the number 9 is chosen heuristically given the fact that
646 // there are typically up to 8 independent ranges when adding the degrees of
647 // freedom on a 3d cell or 9 when adding degrees of freedom of faces. if
648 // doing cell-by-cell additions, we want to avoid repeated calls to
649 // IndexSet::compress() which gets called upon merging two index sets, so we
650 // want to be in the other branch then.
651 if (tmp_ranges.size() > 9)
652 {
653 IndexSet tmp_set(size());
654 tmp_set.ranges.reserve(tmp_ranges.size());
655 for (const auto &i : tmp_ranges)
656 tmp_set.add_range(i.first, i.second);
657
658 // Case if we have zero or just one range: Add into the other set with
659 // its indices, as that is cheaper
660 if (this->ranges.size() <= 1)
661 {
662 if (this->ranges.size() == 1)
663 tmp_set.add_range(ranges[0].begin, ranges[0].end);
664 std::swap(*this, tmp_set);
665 }
666 else
667 this->add_indices(tmp_set);
668 }
669 else
670 for (const auto &i : tmp_ranges)
671 add_range(i.first, i.second);
672}
673
674
675
676void
677IndexSet::add_indices(const IndexSet &other, const size_type offset)
678{
679 if ((this == &other) && (offset == 0))
680 return;
681
682 if (other.ranges.size() != 0)
683 {
684 AssertIndexRange(other.ranges.back().end - 1, index_space_size);
685 }
686
687 compress();
688 other.compress();
689
690 std::vector<Range>::const_iterator r1 = ranges.begin(),
691 r2 = other.ranges.begin();
692
693 std::vector<Range> new_ranges;
694 // just get the start and end of the ranges right in this method, everything
695 // else will be done in compress()
696 while (r1 != ranges.end() || r2 != other.ranges.end())
697 {
698 // the two ranges do not overlap or we are at the end of one of the
699 // ranges
700 if (r2 == other.ranges.end() ||
701 (r1 != ranges.end() && r1->end < (r2->begin + offset)))
702 {
703 new_ranges.push_back(*r1);
704 ++r1;
705 }
706 else if (r1 == ranges.end() || (r2->end + offset) < r1->begin)
707 {
708 new_ranges.emplace_back(r2->begin + offset, r2->end + offset);
709 ++r2;
710 }
711 else
712 {
713 // ok, we do overlap, so just take the combination of the current
714 // range (do not bother to merge with subsequent ranges)
715 Range next(std::min(r1->begin, r2->begin + offset),
716 std::max(r1->end, r2->end + offset));
717 new_ranges.push_back(next);
718 ++r1;
719 ++r2;
720 }
721 }
722 ranges.swap(new_ranges);
723
724 is_compressed = false;
725 compress();
726}
727
728
729
730bool
732{
733 Assert(size() == other.size(),
734 ExcMessage("One index set can only be a subset of another if they "
735 "describe index spaces of the same size. The ones in "
736 "question here have sizes " +
737 std::to_string(size()) + " and " +
738 std::to_string(other.size()) + "."));
739
740 // See whether there are indices in the current set that are not in 'other'.
741 // If so, then this is clearly not a subset of 'other'.
742 IndexSet A_minus_B = *this;
743 A_minus_B.subtract_set(other);
744 if (A_minus_B.n_elements() > 0)
745 return false;
746 else
747 // Else, every index in 'this' is also in 'other', since we ended up
748 // with an empty set upon subtraction. This means that we have a subset:
749 return true;
750}
751
752
753
754void
755IndexSet::write(std::ostream &out) const
756{
757 compress();
758 out << size() << " ";
759 out << ranges.size() << std::endl;
760 std::vector<Range>::const_iterator r = ranges.begin();
761 for (; r != ranges.end(); ++r)
762 {
763 out << r->begin << " " << r->end << std::endl;
764 }
765}
766
767
768
769void
770IndexSet::read(std::istream &in)
771{
772 AssertThrow(in.fail() == false, ExcIO());
773
774 size_type s;
775 unsigned int n_ranges;
776
777 in >> s >> n_ranges;
778 ranges.clear();
779 set_size(s);
780 for (unsigned int i = 0; i < n_ranges; ++i)
781 {
782 AssertThrow(in.fail() == false, ExcIO());
783
784 size_type b, e;
785 in >> b >> e;
786 add_range(b, e);
787 }
788}
789
790
791void
792IndexSet::block_write(std::ostream &out) const
793{
794 AssertThrow(out.fail() == false, ExcIO());
795 out.write(reinterpret_cast<const char *>(&index_space_size),
796 sizeof(index_space_size));
797 std::size_t n_ranges = ranges.size();
798 out.write(reinterpret_cast<const char *>(&n_ranges), sizeof(n_ranges));
799 if (ranges.empty() == false)
800 out.write(reinterpret_cast<const char *>(&*ranges.begin()),
801 ranges.size() * sizeof(Range));
802 AssertThrow(out.fail() == false, ExcIO());
803}
804
805void
806IndexSet::block_read(std::istream &in)
807{
809 std::size_t n_ranges;
810 in.read(reinterpret_cast<char *>(&size), sizeof(size));
811 in.read(reinterpret_cast<char *>(&n_ranges), sizeof(n_ranges));
812 // we have to clear ranges first
813 ranges.clear();
814 set_size(size);
815 ranges.resize(n_ranges, Range(0, 0));
816 if (n_ranges != 0u)
817 in.read(reinterpret_cast<char *>(&*ranges.begin()),
818 ranges.size() * sizeof(Range));
819
820 do_compress(); // needed so that largest_range can be recomputed
821}
822
823
824
825bool
827{
828 // get the element after which we would have to insert a range that
829 // consists of all elements from this element to the end of the index
830 // range plus one. after this call we know that if p!=end() then
831 // p->begin<=index unless there is no such range at all
832 //
833 // if the searched for element is an element of this range, then we're
834 // done. otherwise, the element can't be in one of the following ranges
835 // because otherwise p would be a different iterator
836 //
837 // since we already know the position relative to the largest range (we
838 // called compress!), we can perform the binary search on ranges with
839 // lower/higher number compared to the largest range
840 std::vector<Range>::const_iterator p = std::upper_bound(
841 ranges.begin() +
842 (index < ranges[largest_range].begin ? 0 : largest_range + 1),
843 index < ranges[largest_range].begin ? ranges.begin() + largest_range :
844 ranges.end(),
845 Range(index, size() + 1));
846
847 if (p == ranges.begin())
848 return ((index >= p->begin) && (index < p->end));
849
850 Assert((p == ranges.end()) || (p->begin > index), ExcInternalError());
851
852 // now move to that previous range
853 --p;
854 Assert(p->begin <= index, ExcInternalError());
855
856 return (p->end > index);
857}
858
859
860
863{
864 // find out which chunk the local index n belongs to by using a binary
865 // search. the comparator is based on the end of the ranges.
866 Range r(n, n + 1);
867 r.nth_index_in_set = n;
868
869 const std::vector<Range>::const_iterator p = Utilities::lower_bound(
870 ranges.begin(), ranges.end(), r, Range::nth_index_compare);
871
872 Assert(p != ranges.end(), ExcInternalError());
873 return p->begin + (n - p->nth_index_in_set);
874}
875
876
877
880{
881 // we could try to use the main range for splitting up the search range, but
882 // since we only come here when the largest range did not contain the index,
883 // there is little gain from doing a first step manually.
884 Range r(n, n);
885 std::vector<Range>::const_iterator p =
887
888 // if n is not in this set
889 if (p == ranges.end() || p->end == n || p->begin > n)
891
892 Assert(p != ranges.end(), ExcInternalError());
893 Assert(p->begin <= n, ExcInternalError());
894 Assert(n < p->end, ExcInternalError());
895 return (n - p->begin) + p->nth_index_in_set;
896}
897
898
899
901IndexSet::at(const size_type global_index) const
902{
903 compress();
904 AssertIndexRange(global_index, size());
905
906 if (ranges.empty())
907 return end();
908
909 std::vector<Range>::const_iterator main_range =
910 ranges.begin() + largest_range;
911
912 Range r(global_index, global_index + 1);
913 // This optimization makes the bounds for lower_bound smaller by checking
914 // the largest range first.
915 std::vector<Range>::const_iterator range_begin, range_end;
916 if (global_index < main_range->begin)
917 {
918 range_begin = ranges.begin();
919 range_end = main_range;
920 }
921 else
922 {
923 range_begin = main_range;
924 range_end = ranges.end();
925 }
926
927 // This will give us the first range p=[a,b[ with b>=global_index using
928 // a binary search
929 const std::vector<Range>::const_iterator p =
930 Utilities::lower_bound(range_begin, range_end, r, Range::end_compare);
931
932 // We couldn't find a range, which means we have no range that contains
933 // global_index and also no range behind it, meaning we need to return end().
934 if (p == ranges.end())
935 return end();
936
937 // Finally, we can have two cases: Either global_index is not in [a,b[,
938 // which means we need to return an iterator to a because global_index, ...,
939 // a-1 is not in the IndexSet (if branch). Alternatively, global_index is in
940 // [a,b[ and we will return an iterator pointing directly at global_index
941 // (else branch).
942 if (global_index < p->begin)
943 return {this, static_cast<size_type>(p - ranges.begin()), p->begin};
944 else
945 return {this, static_cast<size_type>(p - ranges.begin()), global_index};
946}
947
948
949
950std::vector<IndexSet::size_type>
952{
953 compress();
954
955 std::vector<size_type> indices;
956 indices.reserve(n_elements());
957
958 for (const auto &range : ranges)
959 for (size_type entry = range.begin; entry < range.end; ++entry)
960 indices.push_back(entry);
961
962 Assert(indices.size() == n_elements(), ExcInternalError());
963
964 return indices;
965}
966
967
968
969#ifdef DEAL_II_TRILINOS_WITH_TPETRA
970
971template <typename NodeType>
972Tpetra::Map<int, types::signed_global_dof_index, NodeType>
974 const bool overlapping) const
975{
976 return *make_tpetra_map_rcp<NodeType>(communicator, overlapping);
977}
978
979
980
981template <typename NodeType>
982Teuchos::RCP<Tpetra::Map<int, types::signed_global_dof_index, NodeType>>
984 const bool overlapping) const
985{
986 compress();
987 (void)communicator;
988
989 if constexpr (running_in_debug_mode())
990 {
991 if (!overlapping)
992 {
993 const size_type n_global_elements =
994 Utilities::MPI::sum(n_elements(), communicator);
995 Assert(n_global_elements == size(),
996 ExcMessage("You are trying to create an Tpetra::Map object "
997 "that partitions elements of an index set "
998 "between processors. However, the union of the "
999 "index sets on different processors does not "
1000 "contain all indices exactly once: the sum of "
1001 "the number of entries the various processors "
1002 "want to store locally is " +
1003 std::to_string(n_global_elements) +
1004 " whereas the total size of the object to be "
1005 "allocated is " +
1006 std::to_string(size()) +
1007 ". In other words, there are "
1008 "either indices that are not spoken for "
1009 "by any processor, or there are indices that are "
1010 "claimed by multiple processors."));
1011 }
1012 }
1013
1014 // Find out if the IndexSet is ascending and 1:1. This corresponds to a
1015 // linear Tpetra::Map. Overlapping IndexSets are never 1:1.
1016 const bool linear =
1017 overlapping ? false : is_ascending_and_one_to_one(communicator);
1018 if (linear)
1020 Tpetra::Map<int, types::signed_global_dof_index, NodeType>>(
1021 size(),
1022 n_elements(),
1023 0,
1024# ifdef DEAL_II_WITH_MPI
1026 communicator)
1027# else
1028 Utilities::Trilinos::internal::make_rcp<Teuchos::Comm<int>>()
1029# endif // DEAL_II_WITH_MPI
1030 );
1031 else
1032 {
1033 const std::vector<size_type> indices = get_index_vector();
1034 std::vector<types::signed_global_dof_index> int_indices(indices.size());
1035 std::copy(indices.begin(), indices.end(), int_indices.begin());
1036 const Teuchos::ArrayView<types::signed_global_dof_index> arr_view(
1037 int_indices);
1038
1040 Tpetra::Map<int, types::signed_global_dof_index, NodeType>>(
1041 size(),
1042 arr_view,
1043 0,
1044# ifdef DEAL_II_WITH_MPI
1046 communicator)
1047# else
1048 Utilities::Trilinos::internal::make_rcp<Teuchos::Comm<int>>()
1049# endif // DEAL_II_WITH_MPI
1050 );
1051 }
1052}
1053#endif
1054
1055
1056
1057#ifdef DEAL_II_TRILINOS_WITH_EPETRA
1058Epetra_Map
1060 const bool overlapping) const
1061{
1062 compress();
1063 (void)communicator;
1064
1065 if constexpr (running_in_debug_mode())
1066 {
1067 if (!overlapping)
1068 {
1069 const size_type n_global_elements =
1070 Utilities::MPI::sum(n_elements(), communicator);
1071 Assert(n_global_elements == size(),
1072 ExcMessage("You are trying to create an Epetra_Map object "
1073 "that partitions elements of an index set "
1074 "between processors. However, the union of the "
1075 "index sets on different processors does not "
1076 "contain all indices exactly once: the sum of "
1077 "the number of entries the various processors "
1078 "want to store locally is " +
1079 std::to_string(n_global_elements) +
1080 " whereas the total size of the object to be "
1081 "allocated is " +
1082 std::to_string(size()) +
1083 ". In other words, there are "
1084 "either indices that are not spoken for "
1085 "by any processor, or there are indices that are "
1086 "claimed by multiple processors."));
1087 }
1088 }
1089
1090 // Find out if the IndexSet is ascending and 1:1. This corresponds to a
1091 // linear EpetraMap. Overlapping IndexSets are never 1:1.
1092 const bool linear =
1093 overlapping ? false : is_ascending_and_one_to_one(communicator);
1094
1095 if (linear)
1096 return Epetra_Map(TrilinosWrappers::types::int_type(size()),
1098 0,
1099# ifdef DEAL_II_WITH_MPI
1100 Epetra_MpiComm(communicator)
1101# else
1102 Epetra_SerialComm()
1103# endif
1104 );
1105 else
1106 {
1107 const std::vector<size_type> indices = get_index_vector();
1108 return Epetra_Map(
1111 (n_elements() > 0 ?
1112 reinterpret_cast<const TrilinosWrappers::types::int_type *>(
1113 indices.data()) :
1114 nullptr),
1115 0,
1116# ifdef DEAL_II_WITH_MPI
1117 Epetra_MpiComm(communicator)
1118# else
1119 Epetra_SerialComm()
1120# endif
1121 );
1122 }
1123}
1124#endif
1125
1126
1127#ifdef DEAL_II_WITH_PETSC
1128IS
1129IndexSet::make_petsc_is(const MPI_Comm communicator) const
1130{
1131 const std::vector<size_type> indices = get_index_vector();
1132
1133 // If the size of the index set can be converted to a PetscInt then every
1134 // value can also be converted
1135 AssertThrowIntegerConversion(static_cast<PetscInt>(size()), size());
1136 const auto local_size = static_cast<PetscInt>(n_elements());
1137 AssertIntegerConversion(local_size, n_elements());
1138
1139 size_type i = 0;
1140 std::vector<PetscInt> petsc_indices(n_elements());
1141 for (const auto &index : *this)
1142 {
1143 const auto petsc_index = static_cast<PetscInt>(index);
1144 AssertIntegerConversion(petsc_index, index);
1145 petsc_indices[i] = petsc_index;
1146 ++i;
1147 }
1148
1149 IS is;
1150 PetscErrorCode ierr = ISCreateGeneral(
1151 communicator, local_size, petsc_indices.data(), PETSC_COPY_VALUES, &is);
1152 AssertThrow(ierr == 0, ExcPETScError(ierr));
1153
1154 return is;
1155}
1156#endif
1157
1158
1159
1160bool
1162{
1163 // If the sum of local elements does not add up to the total size,
1164 // the IndexSet can't be complete.
1165 const size_type n_global_elements =
1166 Utilities::MPI::sum(n_elements(), communicator);
1167 if (n_global_elements != size())
1168 return false;
1169
1170 if (n_global_elements == 0)
1171 return true;
1172
1173#ifdef DEAL_II_WITH_MPI
1174 // Non-contiguous IndexSets can't be linear.
1175 const bool all_contiguous =
1177 if (!all_contiguous)
1178 return false;
1179
1180 bool is_globally_ascending = true;
1181 // we know that there is only one interval
1182 types::global_dof_index first_local_dof = (n_elements() > 0) ?
1183 *(begin_intervals()->begin()) :
1185
1186 const unsigned int my_rank = Utilities::MPI::this_mpi_process(communicator);
1187 const std::vector<types::global_dof_index> global_dofs =
1188 Utilities::MPI::gather(communicator, first_local_dof, 0);
1189
1190 if (my_rank == 0)
1191 {
1192 // find out if the received std::vector is ascending
1193 types::global_dof_index index = 0;
1194 while (global_dofs[index] == numbers::invalid_dof_index)
1195 ++index;
1196 types::global_dof_index old_dof = global_dofs[index++];
1197 for (; index < global_dofs.size(); ++index)
1198 {
1199 const types::global_dof_index new_dof = global_dofs[index];
1200 if (new_dof != numbers::invalid_dof_index)
1201 {
1202 if (new_dof <= old_dof)
1203 {
1204 is_globally_ascending = false;
1205 break;
1206 }
1207 else
1208 old_dof = new_dof;
1209 }
1210 }
1211 }
1212
1213 // now broadcast the result
1214 int is_ascending = is_globally_ascending ? 1 : 0;
1215 int ierr = MPI_Bcast(&is_ascending, 1, MPI_INT, 0, communicator);
1216 AssertThrowMPI(ierr);
1217
1218 return (is_ascending == 1);
1219#else
1220 return true;
1221#endif // DEAL_II_WITH_MPI
1222}
1223
1224
1225
1226std::size_t
1234
1235// explicit template instantiations
1236
1237#ifndef DOXYGEN
1238# ifdef DEAL_II_WITH_TRILINOS
1239# ifdef DEAL_II_TRILINOS_WITH_TPETRA
1240
1241template IndexSet::IndexSet(
1242 const Teuchos::RCP<const Tpetra::Map<
1243 int,
1246 &);
1247
1248# if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || \
1249 defined(KOKKOS_ENABLE_SYCL)
1250template IndexSet::IndexSet(
1251 const Teuchos::RCP<const Tpetra::Map<
1252 int,
1255 &);
1256# endif
1257
1261 const MPI_Comm,
1262 bool) const;
1263
1264# if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || \
1265 defined(KOKKOS_ENABLE_SYCL)
1270 const MPI_Comm,
1271 bool) const;
1272# endif
1273
1274template Teuchos::RCP<
1278 const MPI_Comm,
1279 bool) const;
1280
1281# if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || \
1282 defined(KOKKOS_ENABLE_SYCL)
1283template Teuchos::RCP<
1287 const MPI_Comm,
1288 bool) const;
1289# endif
1290
1291# endif
1292# endif
1293#endif
1294
*  x_component_mask set(0, true)
bool is_subset_of(const IndexSet &other) const
Definition index_set.cc:731
bool is_element_binary_search(const size_type local_index) const
Definition index_set.cc:826
size_type index_within_set_binary_search(const size_type global_index) const
Definition index_set.cc:879
size_type largest_range
Definition index_set.h:1132
IS make_petsc_is(const MPI_Comm communicator=MPI_COMM_WORLD) const
bool is_ascending_and_one_to_one(const MPI_Comm communicator) const
bool is_contiguous() const
Definition index_set.h:1924
Tpetra::Map< int, types::signed_global_dof_index, NodeType > make_tpetra_map(const MPI_Comm communicator=MPI_COMM_WORLD, const bool overlapping=false) const
Definition index_set.cc:973
void do_compress() const
Definition index_set.cc:145
ElementIterator at(const size_type global_index) const
Definition index_set.cc:901
size_type size() const
Definition index_set.h:1783
bool is_empty() const
Definition index_set.h:1933
std::vector< IndexSet > split_by_block(const std::vector< types::global_dof_index > &n_indices_per_block) const
Definition index_set.cc:466
size_type n_elements() const
Definition index_set.h:1941
void add_range_lower_bound(const Range &range)
Definition index_set.cc:619
ElementIterator begin() const
Definition index_set.h:1717
size_type pop_front()
Definition index_set.cc:597
void set_size(const size_type size)
Definition index_set.h:1771
void read(std::istream &in)
Definition index_set.cc:770
Epetra_Map make_trilinos_map(const MPI_Comm communicator=MPI_COMM_WORLD, const bool overlapping=false) const
IndexSet tensor_product(const IndexSet &other) const
Definition index_set.cc:567
void write(std::ostream &out) const
Definition index_set.cc:755
void block_read(std::istream &in)
Definition index_set.cc:806
bool is_compressed
Definition index_set.h:1115
void add_ranges_internal(boost::container::small_vector< std::pair< size_type, size_type >, 200 > &tmp_ranges, const bool ranges_are_sorted)
Definition index_set.cc:635
IntervalIterator begin_intervals() const
Definition index_set.h:1738
std::vector< Range > ranges
Definition index_set.h:1105
void subtract_set(const IndexSet &other)
Definition index_set.cc:496
ElementIterator end() const
Definition index_set.h:1729
Threads::Mutex compress_mutex
Definition index_set.h:1138
size_type index_space_size
Definition index_set.h:1121
void block_write(std::ostream &out) const
Definition index_set.cc:792
IndexSet get_view(const size_type begin, const size_type end) const
Definition index_set.cc:295
void add_range(const size_type begin, const size_type end)
Definition index_set.h:1810
std::size_t memory_consumption() const
size_type nth_index_in_set_binary_search(const size_type local_index) const
Definition index_set.cc:862
void compress() const
Definition index_set.h:1791
size_type pop_back()
Definition index_set.cc:579
std::vector< size_type > get_index_vector() const
Definition index_set.cc:951
Teuchos::RCP< Tpetra::Map< int, types::signed_global_dof_index, NodeType > > make_tpetra_map_rcp(const MPI_Comm communicator=MPI_COMM_WORLD, const bool overlapping=false) const
Definition index_set.cc:983
types::global_dof_index size_type
Definition index_set.h:85
void add_indices(const ForwardIterator &begin, const ForwardIterator &end)
Definition index_set.h:1838
IndexSet operator&(const IndexSet &is) const
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_DISABLE_EXTRA_DIAGNOSTICS
Definition config.h:628
constexpr bool running_in_debug_mode()
Definition config.h:76
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_ENABLE_EXTRA_DIAGNOSTICS
Definition config.h:672
#define AssertIntegerConversion(index1, index2)
#define DEAL_II_ASSERT_UNREACHABLE()
#define AssertThrowIntegerConversion(index1, index2)
static ::ExceptionBase & ExcIO()
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
#define AssertThrowMPI(error_code)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
Tpetra::KokkosCompat::KokkosDeviceWrapperNode< typename MemorySpace::kokkos_space::execution_space, typename MemorySpace::kokkos_space > NodeType
Tpetra::Map< LO, GO, NodeType< MemorySpace > > MapType
std::enable_if_t< std::is_fundamental_v< T >, std::size_t > memory_consumption(const T &t)
T sum(const T &t, const MPI_Comm mpi_communicator)
bool logical_and(const bool t, const MPI_Comm mpi_communicator)
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
Definition mpi.cc:118
std::vector< T > gather(const MPI_Comm comm, const T &object_to_send, const unsigned int root_process=0)
Teuchos::RCP< T > make_rcp(Args &&...args)
Iterator lower_bound(Iterator first, Iterator last, const T &val)
Definition utilities.h:1003
constexpr types::global_dof_index invalid_dof_index
Definition types.h:267
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
int signed_global_dof_index
Definition types.h:103
unsigned int global_dof_index
Definition types.h:92
static bool end_compare(const IndexSet::Range &x, const IndexSet::Range &y)
Definition index_set.h:1063
static bool nth_index_compare(const IndexSet::Range &x, const IndexSet::Range &y)
Definition index_set.h:1069
size_type end
Definition index_set.h:1031
size_type nth_index_in_set
Definition index_set.h:1033
size_type begin
Definition index_set.h:1030