deal.II version GIT relicensing-6834-g5b78e6bcdf 2026-10-01 11:20:01+00:00
\(\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
578void
580{
581 // if the inserted range is already within the range we find by lower_bound,
582 // there is no need to do anything; we do not try to be clever here and
583 // leave all other work to compress().
584 const auto insert_position =
585 Utilities::lower_bound(ranges.begin(), ranges.end(), new_range);
586 if (insert_position == ranges.end() ||
587 insert_position->begin > new_range.begin ||
588 insert_position->end < new_range.end)
589 ranges.insert(insert_position, new_range);
590}
591
592
593
594void
596 boost::container::small_vector<std::pair<size_type, size_type>, 200>
597 &tmp_ranges,
598 const bool ranges_are_sorted)
599{
600 if (!ranges_are_sorted)
601 std::sort(tmp_ranges.begin(), tmp_ranges.end());
602
603 // if we have many ranges, we first construct a temporary index set (where
604 // we add ranges in a consecutive way, so fast), otherwise, we work with
605 // add_range(). the number 9 is chosen heuristically given the fact that
606 // there are typically up to 8 independent ranges when adding the degrees of
607 // freedom on a 3d cell or 9 when adding degrees of freedom of faces. if
608 // doing cell-by-cell additions, we want to avoid repeated calls to
609 // IndexSet::compress() which gets called upon merging two index sets, so we
610 // want to be in the other branch then.
611 if (tmp_ranges.size() > 9)
612 {
613 IndexSet tmp_set(size());
614 tmp_set.ranges.reserve(tmp_ranges.size());
615 for (const auto &i : tmp_ranges)
616 tmp_set.add_range(i.first, i.second);
617
618 // Case if we have zero or just one range: Add into the other set with
619 // its indices, as that is cheaper
620 if (this->ranges.size() <= 1)
621 {
622 if (this->ranges.size() == 1)
623 tmp_set.add_range(ranges[0].begin, ranges[0].end);
624 std::swap(*this, tmp_set);
625 }
626 else
627 this->add_indices(tmp_set);
628 }
629 else
630 for (const auto &i : tmp_ranges)
631 add_range(i.first, i.second);
632}
633
634
635
636void
637IndexSet::add_indices(const IndexSet &other, const size_type offset)
638{
639 if ((this == &other) && (offset == 0))
640 return;
641
642 if (other.ranges.size() != 0)
643 {
644 AssertIndexRange(other.ranges.back().end - 1, index_space_size);
645 }
646
647 compress();
648 other.compress();
649
650 std::vector<Range>::const_iterator r1 = ranges.begin(),
651 r2 = other.ranges.begin();
652
653 std::vector<Range> new_ranges;
654 // just get the start and end of the ranges right in this method, everything
655 // else will be done in compress()
656 while (r1 != ranges.end() || r2 != other.ranges.end())
657 {
658 // the two ranges do not overlap or we are at the end of one of the
659 // ranges
660 if (r2 == other.ranges.end() ||
661 (r1 != ranges.end() && r1->end < (r2->begin + offset)))
662 {
663 new_ranges.push_back(*r1);
664 ++r1;
665 }
666 else if (r1 == ranges.end() || (r2->end + offset) < r1->begin)
667 {
668 new_ranges.emplace_back(r2->begin + offset, r2->end + offset);
669 ++r2;
670 }
671 else
672 {
673 // ok, we do overlap, so just take the combination of the current
674 // range (do not bother to merge with subsequent ranges)
675 Range next(std::min(r1->begin, r2->begin + offset),
676 std::max(r1->end, r2->end + offset));
677 new_ranges.push_back(next);
678 ++r1;
679 ++r2;
680 }
681 }
682 ranges.swap(new_ranges);
683
684 is_compressed = false;
685 compress();
686}
687
688
689
690bool
692{
693 Assert(size() == other.size(),
694 ExcMessage("One index set can only be a subset of another if they "
695 "describe index spaces of the same size. The ones in "
696 "question here have sizes " +
697 std::to_string(size()) + " and " +
698 std::to_string(other.size()) + "."));
699
700 // See whether there are indices in the current set that are not in 'other'.
701 // If so, then this is clearly not a subset of 'other'.
702 IndexSet A_minus_B = *this;
703 A_minus_B.subtract_set(other);
704 if (A_minus_B.n_elements() > 0)
705 return false;
706 else
707 // Else, every index in 'this' is also in 'other', since we ended up
708 // with an empty set upon subtraction. This means that we have a subset:
709 return true;
710}
711
712
713
714void
715IndexSet::write(std::ostream &out) const
716{
717 compress();
718 out << size() << " ";
719 out << ranges.size() << std::endl;
720 std::vector<Range>::const_iterator r = ranges.begin();
721 for (; r != ranges.end(); ++r)
722 {
723 out << r->begin << " " << r->end << std::endl;
724 }
725}
726
727
728
729void
730IndexSet::read(std::istream &in)
731{
732 AssertThrow(in.fail() == false, ExcIO());
733
734 size_type s;
735 unsigned int n_ranges;
736
737 in >> s >> n_ranges;
738 ranges.clear();
739 set_size(s);
740 for (unsigned int i = 0; i < n_ranges; ++i)
741 {
742 AssertThrow(in.fail() == false, ExcIO());
743
744 size_type b, e;
745 in >> b >> e;
746 add_range(b, e);
747 }
748}
749
750
751void
752IndexSet::block_write(std::ostream &out) const
753{
754 AssertThrow(out.fail() == false, ExcIO());
755 out.write(reinterpret_cast<const char *>(&index_space_size),
756 sizeof(index_space_size));
757 std::size_t n_ranges = ranges.size();
758 out.write(reinterpret_cast<const char *>(&n_ranges), sizeof(n_ranges));
759 if (ranges.empty() == false)
760 out.write(reinterpret_cast<const char *>(&*ranges.begin()),
761 ranges.size() * sizeof(Range));
762 AssertThrow(out.fail() == false, ExcIO());
763}
764
765void
766IndexSet::block_read(std::istream &in)
767{
769 std::size_t n_ranges;
770 in.read(reinterpret_cast<char *>(&size), sizeof(size));
771 in.read(reinterpret_cast<char *>(&n_ranges), sizeof(n_ranges));
772 // we have to clear ranges first
773 ranges.clear();
774 set_size(size);
775 ranges.resize(n_ranges, Range(0, 0));
776 if (n_ranges != 0u)
777 in.read(reinterpret_cast<char *>(&*ranges.begin()),
778 ranges.size() * sizeof(Range));
779
780 do_compress(); // needed so that largest_range can be recomputed
781}
782
783
784
785bool
787{
788 // get the element after which we would have to insert a range that
789 // consists of all elements from this element to the end of the index
790 // range plus one. after this call we know that if p!=end() then
791 // p->begin<=index unless there is no such range at all
792 //
793 // if the searched for element is an element of this range, then we're
794 // done. otherwise, the element can't be in one of the following ranges
795 // because otherwise p would be a different iterator
796 //
797 // since we already know the position relative to the largest range (we
798 // called compress!), we can perform the binary search on ranges with
799 // lower/higher number compared to the largest range
800 std::vector<Range>::const_iterator p = std::upper_bound(
801 ranges.begin() +
802 (index < ranges[largest_range].begin ? 0 : largest_range + 1),
803 index < ranges[largest_range].begin ? ranges.begin() + largest_range :
804 ranges.end(),
805 Range(index, size() + 1));
806
807 if (p == ranges.begin())
808 return ((index >= p->begin) && (index < p->end));
809
810 Assert((p == ranges.end()) || (p->begin > index), ExcInternalError());
811
812 // now move to that previous range
813 --p;
814 Assert(p->begin <= index, ExcInternalError());
815
816 return (p->end > index);
817}
818
819
820
823{
824 // find out which chunk the local index n belongs to by using a binary
825 // search. the comparator is based on the end of the ranges.
826 Range r(n, n + 1);
827 r.nth_index_in_set = n;
828
829 const std::vector<Range>::const_iterator p = Utilities::lower_bound(
830 ranges.begin(), ranges.end(), r, Range::nth_index_compare);
831
832 Assert(p != ranges.end(), ExcInternalError());
833 return p->begin + (n - p->nth_index_in_set);
834}
835
836
837
840{
841 // we could try to use the main range for splitting up the search range, but
842 // since we only come here when the largest range did not contain the index,
843 // there is little gain from doing a first step manually.
844 Range r(n, n);
845 std::vector<Range>::const_iterator p =
847
848 // if n is not in this set
849 if (p == ranges.end() || p->end == n || p->begin > n)
851
852 Assert(p != ranges.end(), ExcInternalError());
853 Assert(p->begin <= n, ExcInternalError());
854 Assert(n < p->end, ExcInternalError());
855 return (n - p->begin) + p->nth_index_in_set;
856}
857
858
859
861IndexSet::at(const size_type global_index) const
862{
863 compress();
864 AssertIndexRange(global_index, size());
865
866 if (ranges.empty())
867 return end();
868
869 std::vector<Range>::const_iterator main_range =
870 ranges.begin() + largest_range;
871
872 Range r(global_index, global_index + 1);
873 // This optimization makes the bounds for lower_bound smaller by checking
874 // the largest range first.
875 std::vector<Range>::const_iterator range_begin, range_end;
876 if (global_index < main_range->begin)
877 {
878 range_begin = ranges.begin();
879 range_end = main_range;
880 }
881 else
882 {
883 range_begin = main_range;
884 range_end = ranges.end();
885 }
886
887 // This will give us the first range p=[a,b[ with b>=global_index using
888 // a binary search
889 const std::vector<Range>::const_iterator p =
890 Utilities::lower_bound(range_begin, range_end, r, Range::end_compare);
891
892 // We couldn't find a range, which means we have no range that contains
893 // global_index and also no range behind it, meaning we need to return end().
894 if (p == ranges.end())
895 return end();
896
897 // Finally, we can have two cases: Either global_index is not in [a,b[,
898 // which means we need to return an iterator to a because global_index, ...,
899 // a-1 is not in the IndexSet (if branch). Alternatively, global_index is in
900 // [a,b[ and we will return an iterator pointing directly at global_index
901 // (else branch).
902 if (global_index < p->begin)
903 return {this, static_cast<size_type>(p - ranges.begin()), p->begin};
904 else
905 return {this, static_cast<size_type>(p - ranges.begin()), global_index};
906}
907
908
909
910std::vector<IndexSet::size_type>
912{
913 compress();
914
915 std::vector<size_type> indices;
916 indices.reserve(n_elements());
917
918 for (const auto &range : ranges)
919 for (size_type entry = range.begin; entry < range.end; ++entry)
920 indices.push_back(entry);
921
922 Assert(indices.size() == n_elements(), ExcInternalError());
923
924 return indices;
925}
926
927
928
929#ifdef DEAL_II_TRILINOS_WITH_TPETRA
930
931template <typename NodeType>
932Tpetra::Map<int, types::signed_global_dof_index, NodeType>
934 const bool overlapping) const
935{
936 return *make_tpetra_map_rcp<NodeType>(communicator, overlapping);
937}
938
939
940
941template <typename NodeType>
942Teuchos::RCP<Tpetra::Map<int, types::signed_global_dof_index, NodeType>>
944 const bool overlapping) const
945{
946 compress();
947 (void)communicator;
948
949 if constexpr (running_in_debug_mode())
950 {
951 if (!overlapping)
952 {
953 const size_type n_global_elements =
954 Utilities::MPI::sum(n_elements(), communicator);
955 Assert(n_global_elements == size(),
956 ExcMessage("You are trying to create an Tpetra::Map object "
957 "that partitions elements of an index set "
958 "between processors. However, the union of the "
959 "index sets on different processors does not "
960 "contain all indices exactly once: the sum of "
961 "the number of entries the various processors "
962 "want to store locally is " +
963 std::to_string(n_global_elements) +
964 " whereas the total size of the object to be "
965 "allocated is " +
966 std::to_string(size()) +
967 ". In other words, there are "
968 "either indices that are not spoken for "
969 "by any processor, or there are indices that are "
970 "claimed by multiple processors."));
971 }
972 }
973
974 // Find out if the IndexSet is ascending and 1:1. This corresponds to a
975 // linear Tpetra::Map. Overlapping IndexSets are never 1:1.
976 const bool linear =
977 overlapping ? false : is_ascending_and_one_to_one(communicator);
978 if (linear)
980 Tpetra::Map<int, types::signed_global_dof_index, NodeType>>(
981 size(),
982 n_elements(),
983 0,
984# ifdef DEAL_II_WITH_MPI
985 Utilities::Trilinos::internal::make_rcp<Teuchos::MpiComm<int>>(
986 communicator)
987# else
988 Utilities::Trilinos::internal::make_rcp<Teuchos::Comm<int>>()
989# endif // DEAL_II_WITH_MPI
990 );
991 else
992 {
993 const std::vector<size_type> indices = get_index_vector();
994 std::vector<types::signed_global_dof_index> int_indices(indices.size());
995 std::copy(indices.begin(), indices.end(), int_indices.begin());
996 const Teuchos::ArrayView<types::signed_global_dof_index> arr_view(
997 int_indices);
998
1000 Tpetra::Map<int, types::signed_global_dof_index, NodeType>>(
1001 size(),
1002 arr_view,
1003 0,
1004# ifdef DEAL_II_WITH_MPI
1005 Utilities::Trilinos::internal::make_rcp<Teuchos::MpiComm<int>>(
1006 communicator)
1007# else
1008 Utilities::Trilinos::internal::make_rcp<Teuchos::Comm<int>>()
1009# endif // DEAL_II_WITH_MPI
1010 );
1011 }
1012}
1013#endif
1014
1015
1016
1017#ifdef DEAL_II_TRILINOS_WITH_EPETRA
1018Epetra_Map
1020 const bool overlapping) const
1021{
1022 compress();
1023 (void)communicator;
1024
1025 if constexpr (running_in_debug_mode())
1026 {
1027 if (!overlapping)
1028 {
1029 const size_type n_global_elements =
1030 Utilities::MPI::sum(n_elements(), communicator);
1031 Assert(n_global_elements == size(),
1032 ExcMessage("You are trying to create an Epetra_Map object "
1033 "that partitions elements of an index set "
1034 "between processors. However, the union of the "
1035 "index sets on different processors does not "
1036 "contain all indices exactly once: the sum of "
1037 "the number of entries the various processors "
1038 "want to store locally is " +
1039 std::to_string(n_global_elements) +
1040 " whereas the total size of the object to be "
1041 "allocated is " +
1042 std::to_string(size()) +
1043 ". In other words, there are "
1044 "either indices that are not spoken for "
1045 "by any processor, or there are indices that are "
1046 "claimed by multiple processors."));
1047 }
1048 }
1049
1050 // Find out if the IndexSet is ascending and 1:1. This corresponds to a
1051 // linear EpetraMap. Overlapping IndexSets are never 1:1.
1052 const bool linear =
1053 overlapping ? false : is_ascending_and_one_to_one(communicator);
1054
1055 if (linear)
1056 return Epetra_Map(TrilinosWrappers::types::int_type(size()),
1058 0,
1059# ifdef DEAL_II_WITH_MPI
1060 Epetra_MpiComm(communicator)
1061# else
1062 Epetra_SerialComm()
1063# endif
1064 );
1065 else
1066 {
1067 const std::vector<size_type> indices = get_index_vector();
1068 return Epetra_Map(
1071 (n_elements() > 0 ?
1072 reinterpret_cast<const TrilinosWrappers::types::int_type *>(
1073 indices.data()) :
1074 nullptr),
1075 0,
1076# ifdef DEAL_II_WITH_MPI
1077 Epetra_MpiComm(communicator)
1078# else
1079 Epetra_SerialComm()
1080# endif
1081 );
1082 }
1083}
1084#endif
1085
1086
1087#ifdef DEAL_II_WITH_PETSC
1088IS
1089IndexSet::make_petsc_is(const MPI_Comm communicator) const
1090{
1091 const std::vector<size_type> indices = get_index_vector();
1092
1093 // If the size of the index set can be converted to a PetscInt then every
1094 // value can also be converted
1095 AssertThrowIntegerConversion(static_cast<PetscInt>(size()), size());
1096 const auto local_size = static_cast<PetscInt>(n_elements());
1097 AssertIntegerConversion(local_size, n_elements());
1098
1099 size_type i = 0;
1100 std::vector<PetscInt> petsc_indices(n_elements());
1101 for (const auto &index : *this)
1102 {
1103 const auto petsc_index = static_cast<PetscInt>(index);
1104 AssertIntegerConversion(petsc_index, index);
1105 petsc_indices[i] = petsc_index;
1106 ++i;
1107 }
1108
1109 IS is;
1110 PetscErrorCode ierr = ISCreateGeneral(
1111 communicator, local_size, petsc_indices.data(), PETSC_COPY_VALUES, &is);
1112 AssertThrow(ierr == 0, ExcPETScError(ierr));
1113
1114 return is;
1115}
1116#endif
1117
1118
1119
1120bool
1122{
1123 // If the sum of local elements does not add up to the total size,
1124 // the IndexSet can't be complete.
1125 const size_type n_global_elements =
1126 Utilities::MPI::sum(n_elements(), communicator);
1127 if (n_global_elements != size())
1128 return false;
1129
1130 if (n_global_elements == 0)
1131 return true;
1132
1133#ifdef DEAL_II_WITH_MPI
1134 // Non-contiguous IndexSets can't be linear.
1135 const bool all_contiguous =
1137 if (!all_contiguous)
1138 return false;
1139
1140 bool is_globally_ascending = true;
1141 // we know that there is only one interval
1142 types::global_dof_index first_local_dof = (n_elements() > 0) ?
1143 *(begin_intervals()->begin()) :
1145
1146 const unsigned int my_rank = Utilities::MPI::this_mpi_process(communicator);
1147 const std::vector<types::global_dof_index> global_dofs =
1148 Utilities::MPI::gather(communicator, first_local_dof, 0);
1149
1150 if (my_rank == 0)
1151 {
1152 // find out if the received std::vector is ascending
1153 types::global_dof_index index = 0;
1154 while (global_dofs[index] == numbers::invalid_dof_index)
1155 ++index;
1156 types::global_dof_index old_dof = global_dofs[index++];
1157 for (; index < global_dofs.size(); ++index)
1158 {
1159 const types::global_dof_index new_dof = global_dofs[index];
1160 if (new_dof != numbers::invalid_dof_index)
1161 {
1162 if (new_dof <= old_dof)
1163 {
1164 is_globally_ascending = false;
1165 break;
1166 }
1167 else
1168 old_dof = new_dof;
1169 }
1170 }
1171 }
1172
1173 // now broadcast the result
1174 int is_ascending = is_globally_ascending ? 1 : 0;
1175 int ierr = MPI_Bcast(&is_ascending, 1, MPI_INT, 0, communicator);
1176 AssertThrowMPI(ierr);
1177
1178 return (is_ascending == 1);
1179#else
1180 return true;
1181#endif // DEAL_II_WITH_MPI
1182}
1183
1184
1185
1186std::size_t
1194
1195// explicit template instantiations
1196
1197#ifndef DOXYGEN
1198# ifdef DEAL_II_WITH_TRILINOS
1199# ifdef DEAL_II_TRILINOS_WITH_TPETRA
1200
1201template IndexSet::IndexSet(
1202 const Teuchos::RCP<const Tpetra::Map<
1203 int,
1206 &);
1207
1208# if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || \
1209 defined(KOKKOS_ENABLE_SYCL)
1210template IndexSet::IndexSet(
1211 const Teuchos::RCP<const Tpetra::Map<
1212 int,
1215 &);
1216# endif
1217
1221 const MPI_Comm,
1222 bool) const;
1223
1224# if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || \
1225 defined(KOKKOS_ENABLE_SYCL)
1230 const MPI_Comm,
1231 bool) const;
1232# endif
1233
1234template Teuchos::RCP<
1238 const MPI_Comm,
1239 bool) const;
1240
1241# if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || \
1242 defined(KOKKOS_ENABLE_SYCL)
1243template Teuchos::RCP<
1247 const MPI_Comm,
1248 bool) const;
1249# endif
1250
1251# endif
1252# endif
1253#endif
1254
*  iterator end()
*  *  iterator begin()
*  x_component_mask set(0, true)
ElementIterator begin() const
Definition index_set.h:1248
bool is_subset_of(const IndexSet &other) const
Definition index_set.cc:691
bool is_element_binary_search(const size_type local_index) const
Definition index_set.cc:786
size_type index_within_set_binary_search(const size_type global_index) const
Definition index_set.cc:839
size_type largest_range
Definition index_set.h:1108
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:1900
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:933
void do_compress() const
Definition index_set.cc:145
ElementIterator at(const size_type global_index) const
Definition index_set.cc:861
size_type size() const
Definition index_set.h:1759
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:1917
void add_range_lower_bound(const Range &range)
Definition index_set.cc:579
ElementIterator begin() const
Definition index_set.h:1693
void set_size(const size_type size)
Definition index_set.h:1747
void read(std::istream &in)
Definition index_set.cc:730
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:715
void block_read(std::istream &in)
Definition index_set.cc:766
bool is_compressed
Definition index_set.h:1091
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:595
IntervalIterator begin_intervals() const
Definition index_set.h:1714
std::vector< Range > ranges
Definition index_set.h:1081
void subtract_set(const IndexSet &other)
Definition index_set.cc:496
ElementIterator end() const
Definition index_set.h:1705
Threads::Mutex compress_mutex
Definition index_set.h:1114
size_type index_space_size
Definition index_set.h:1097
void block_write(std::ostream &out) const
Definition index_set.cc:752
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:1786
std::size_t memory_consumption() const
size_type nth_index_in_set_binary_search(const size_type local_index) const
Definition index_set.cc:822
void compress() const
Definition index_set.h:1767
std::vector< size_type > get_index_vector() const
Definition index_set.cc:911
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:943
types::global_dof_index size_type
Definition index_set.h:85
void add_indices(const ForwardIterator &begin, const ForwardIterator &end)
Definition index_set.h:1814
IndexSet operator&(const IndexSet &is) const
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_DISABLE_EXTRA_DIAGNOSTICS
Definition config.h:636
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:680
#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)
const unsigned int my_rank
Definition mpi.cc:917
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:259
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
static bool end_compare(const IndexSet::Range &x, const IndexSet::Range &y)
Definition index_set.h:1039
static bool nth_index_compare(const IndexSet::Range &x, const IndexSet::Range &y)
Definition index_set.h:1045
size_type end
Definition index_set.h:1007
size_type nth_index_in_set
Definition index_set.h:1009
size_type begin
Definition index_set.h:1006