Reference documentation for deal.II version 9.1.1
\(\newcommand{\dealcoloneq}{\mathrel{\vcenter{:}}=}\)
index_set.cc
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2005 - 2019 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15 
16 #include <deal.II/base/index_set.h>
17 #include <deal.II/base/memory_consumption.h>
18 #include <deal.II/base/mpi.h>
19 
20 #include <vector>
21 
22 #ifdef DEAL_II_WITH_TRILINOS
23 # ifdef DEAL_II_WITH_MPI
24 # include <Epetra_MpiComm.h>
25 # endif
26 # include <Epetra_Map.h>
27 # include <Epetra_SerialComm.h>
28 #endif
29 
30 DEAL_II_NAMESPACE_OPEN
31 
32 
33 
34 #ifdef DEAL_II_WITH_TRILINOS
35 
36 // the 64-bit path uses a few different names, so put that into a separate
37 // implementation
38 
39 # ifdef DEAL_II_WITH_64BIT_INDICES
40 
41 IndexSet::IndexSet(const Epetra_BlockMap &map)
42  : is_compressed(true)
43  , index_space_size(1 + map.MaxAllGID64())
44  , largest_range(numbers::invalid_unsigned_int)
45 {
46  Assert(map.MinAllGID64() == 0,
47  ExcMessage(
48  "The Epetra_BlockMap does not contain the global index 0, "
49  "which means some entries are not present on any processor."));
50 
51  // For a contiguous map, we do not need to go through the whole data...
52  if (map.LinearMap())
53  add_range(size_type(map.MinMyGID64()), size_type(map.MaxMyGID64() + 1));
54  else
55  {
56  const size_type n_indices = map.NumMyElements();
57  size_type * indices =
58  reinterpret_cast<size_type *>(map.MyGlobalElements64());
59  add_indices(indices, indices + n_indices);
60  }
61  compress();
62 }
63 
64 # else
65 
66 // this is the standard 32-bit implementation
67 
68 IndexSet::IndexSet(const Epetra_BlockMap &map)
69  : is_compressed(true)
70  , index_space_size(1 + map.MaxAllGID())
71  , largest_range(numbers::invalid_unsigned_int)
72 {
73  Assert(map.MinAllGID() == 0,
74  ExcMessage(
75  "The Epetra_BlockMap does not contain the global index 0, "
76  "which means some entries are not present on any processor."));
77 
78  // For a contiguous map, we do not need to go through the whole data...
79  if (map.LinearMap())
80  add_range(size_type(map.MinMyGID()), size_type(map.MaxMyGID() + 1));
81  else
82  {
83  const size_type n_indices = map.NumMyElements();
84  unsigned int * indices =
85  reinterpret_cast<unsigned int *>(map.MyGlobalElements());
86  add_indices(indices, indices + n_indices);
87  }
88  compress();
89 }
90 
91 # endif
92 
93 #endif // ifdef DEAL_II_WITH_TRILINOS
94 
95 
96 
97 void
98 IndexSet::add_range(const size_type begin, const size_type end)
99 {
102  ExcIndexRangeType<size_type>(begin, 0, index_space_size));
104  ExcIndexRangeType<size_type>(end, 0, index_space_size + 1));
105  Assert(begin <= end, ExcIndexRangeType<size_type>(begin, 0, end));
106 
107  if (begin != end)
108  {
109  const Range new_range(begin, end);
110 
111  // the new index might be larger than the last index present in the
112  // ranges. Then we can skip the binary search
113  if (ranges.size() == 0 || begin > ranges.back().end)
114  ranges.push_back(new_range);
115  else
116  ranges.insert(Utilities::lower_bound(ranges.begin(),
117  ranges.end(),
118  new_range),
119  new_range);
120  is_compressed = false;
121  }
122 }
123 
124 
125 
126 void
128 {
129  // we will, in the following, modify mutable variables. this can only
130  // work in multithreaded applications if we lock the data structures
131  // via a mutex, so that users can call 'const' functions from threads
132  // in parallel (and these 'const' functions can then call compress()
133  // which itself calls the current function)
134  std::lock_guard<std::mutex> lock(compress_mutex);
135 
136  // see if any of the contiguous ranges can be merged. do not use
137  // std::vector::erase in-place as it is quadratic in the number of
138  // ranges. since the ranges are sorted by their first index, determining
139  // overlap isn't all that hard
140  std::vector<Range>::iterator store = ranges.begin();
141  for (std::vector<Range>::iterator i = ranges.begin(); i != ranges.end();)
142  {
143  std::vector<Range>::iterator next = i;
144  ++next;
145 
146  size_type first_index = i->begin;
147  size_type last_index = i->end;
148 
149  // see if we can merge any of the following ranges
150  while (next != ranges.end() && (next->begin <= last_index))
151  {
152  last_index = std::max(last_index, next->end);
153  ++next;
154  }
155  i = next;
156 
157  // store the new range in the slot we last occupied
158  *store = Range(first_index, last_index);
159  ++store;
160  }
161  // use a compact array with exactly the right amount of storage
162  if (store != ranges.end())
163  {
164  std::vector<Range> new_ranges(ranges.begin(), store);
165  ranges.swap(new_ranges);
166  }
167 
168  // now compute indices within set and the range with most elements
169  size_type next_index = 0, largest_range_size = 0;
170  for (std::vector<Range>::iterator i = ranges.begin(); i != ranges.end(); ++i)
171  {
172  Assert(i->begin < i->end, ExcInternalError());
173 
174  i->nth_index_in_set = next_index;
175  next_index += (i->end - i->begin);
176  if (i->end - i->begin > largest_range_size)
177  {
178  largest_range_size = i->end - i->begin;
179  largest_range = i - ranges.begin();
180  }
181  }
182  is_compressed = true;
183 
184  // check that next_index is correct. needs to be after the previous
185  // statement because we otherwise will get into an endless loop
186  Assert(next_index == n_elements(), ExcInternalError());
187 }
188 
189 
190 
191 IndexSet IndexSet::operator&(const IndexSet &is) const
192 {
193  Assert(size() == is.size(), ExcDimensionMismatch(size(), is.size()));
194 
195  compress();
196  is.compress();
197 
198  std::vector<Range>::const_iterator r1 = ranges.begin(),
199  r2 = is.ranges.begin();
200  IndexSet result(size());
201 
202  while ((r1 != ranges.end()) && (r2 != is.ranges.end()))
203  {
204  // if r1 and r2 do not overlap at all, then move the pointer that sits
205  // to the left of the other up by one
206  if (r1->end <= r2->begin)
207  ++r1;
208  else if (r2->end <= r1->begin)
209  ++r2;
210  else
211  {
212  // the ranges must overlap somehow
213  Assert(((r1->begin <= r2->begin) && (r1->end > r2->begin)) ||
214  ((r2->begin <= r1->begin) && (r2->end > r1->begin)),
215  ExcInternalError());
216 
217  // add the overlapping range to the result
218  result.add_range(std::max(r1->begin, r2->begin),
219  std::min(r1->end, r2->end));
220 
221  // now move that iterator that ends earlier one up. note that it has
222  // to be this one because a subsequent range may still have a chance
223  // of overlapping with the range that ends later
224  if (r1->end <= r2->end)
225  ++r1;
226  else
227  ++r2;
228  }
229  }
230 
231  result.compress();
232  return result;
233 }
234 
235 
236 
237 IndexSet
238 IndexSet::get_view(const size_type begin, const size_type end) const
239 {
240  Assert(begin <= end,
241  ExcMessage("End index needs to be larger or equal to begin index!"));
242  Assert(end <= size(), ExcMessage("Given range exceeds index set dimension"));
243 
244  IndexSet result(end - begin);
245  std::vector<Range>::const_iterator r1 = ranges.begin();
246 
247  while (r1 != ranges.end())
248  {
249  if ((r1->end > begin) && (r1->begin < end))
250  {
251  result.add_range(std::max(r1->begin, begin) - begin,
252  std::min(r1->end, end) - begin);
253  }
254  else if (r1->begin >= end)
255  break;
256 
257  ++r1;
258  }
259 
260  result.compress();
261  return result;
262 }
263 
264 
265 
266 void
268 {
269  compress();
270  other.compress();
271  is_compressed = false;
272 
273 
274  // we save new ranges to be added to our IndexSet in an temporary vector and
275  // add all of them in one go at the end.
276  std::vector<Range> new_ranges;
277 
278  std::vector<Range>::iterator own_it = ranges.begin();
279  std::vector<Range>::iterator other_it = other.ranges.begin();
280 
281  while (own_it != ranges.end() && other_it != other.ranges.end())
282  {
283  // advance own iterator until we get an overlap
284  if (own_it->end <= other_it->begin)
285  {
286  ++own_it;
287  continue;
288  }
289  // we are done with other_it, so advance
290  if (own_it->begin >= other_it->end)
291  {
292  ++other_it;
293  continue;
294  }
295 
296  // Now own_it and other_it overlap. First save the part of own_it that
297  // is before other_it (if not empty).
298  if (own_it->begin < other_it->begin)
299  {
300  Range r(own_it->begin, other_it->begin);
301  r.nth_index_in_set = 0; // fix warning of unused variable
302  new_ranges.push_back(r);
303  }
304  // change own_it to the sub range behind other_it. Do not delete own_it
305  // in any case. As removal would invalidate iterators, we just shrink
306  // the range to an empty one.
307  own_it->begin = other_it->end;
308  if (own_it->begin > own_it->end)
309  {
310  own_it->begin = own_it->end;
311  ++own_it;
312  }
313 
314  // continue without advancing iterators, the right one will be advanced
315  // next.
316  }
317 
318  // Now delete all empty ranges we might
319  // have created.
320  for (std::vector<Range>::iterator it = ranges.begin(); it != ranges.end();)
321  {
322  if (it->begin >= it->end)
323  it = ranges.erase(it);
324  else
325  ++it;
326  }
327 
328  // done, now add the temporary ranges
329  const std::vector<Range>::iterator end = new_ranges.end();
330  for (std::vector<Range>::iterator it = new_ranges.begin(); it != end; ++it)
331  add_range(it->begin, it->end);
332 
333  compress();
334 }
335 
336 
337 
340 {
341  Assert(is_empty() == false,
342  ExcMessage(
343  "pop_back() failed, because this IndexSet contains no entries."));
344 
345  const size_type index = ranges.back().end - 1;
346  --ranges.back().end;
347 
348  if (ranges.back().begin == ranges.back().end)
349  ranges.pop_back();
350 
351  return index;
352 }
353 
354 
355 
358 {
359  Assert(is_empty() == false,
360  ExcMessage(
361  "pop_front() failed, because this IndexSet contains no entries."));
362 
363  const size_type index = ranges.front().begin;
364  ++ranges.front().begin;
365 
366  if (ranges.front().begin == ranges.front().end)
367  ranges.erase(ranges.begin());
368 
369  // We have to set this in any case, because nth_index_in_set is no longer
370  // up to date for all but the first range
371  is_compressed = false;
372 
373  return index;
374 }
375 
376 
377 
378 void
379 IndexSet::add_indices(const IndexSet &other, const unsigned int offset)
380 {
381  if ((this == &other) && (offset == 0))
382  return;
383 
384  Assert(other.ranges.size() == 0 ||
385  other.ranges.back().end - 1 < index_space_size,
386  ExcIndexRangeType<size_type>(other.ranges.back().end - 1,
387  0,
389 
390  compress();
391  other.compress();
392 
393  std::vector<Range>::const_iterator r1 = ranges.begin(),
394  r2 = other.ranges.begin();
395 
396  std::vector<Range> new_ranges;
397  // just get the start and end of the ranges right in this method, everything
398  // else will be done in compress()
399  while (r1 != ranges.end() || r2 != other.ranges.end())
400  {
401  // the two ranges do not overlap or we are at the end of one of the
402  // ranges
403  if (r2 == other.ranges.end() ||
404  (r1 != ranges.end() && r1->end < (r2->begin + offset)))
405  {
406  new_ranges.push_back(*r1);
407  ++r1;
408  }
409  else if (r1 == ranges.end() || (r2->end + offset) < r1->begin)
410  {
411  new_ranges.emplace_back(r2->begin + offset, r2->end + offset);
412  ++r2;
413  }
414  else
415  {
416  // ok, we do overlap, so just take the combination of the current
417  // range (do not bother to merge with subsequent ranges)
418  Range next(std::min(r1->begin, r2->begin + offset),
419  std::max(r1->end, r2->end + offset));
420  new_ranges.push_back(next);
421  ++r1;
422  ++r2;
423  }
424  }
425  ranges.swap(new_ranges);
426 
427  is_compressed = false;
428  compress();
429 }
430 
431 
432 
433 void
434 IndexSet::write(std::ostream &out) const
435 {
436  compress();
437  out << size() << " ";
438  out << ranges.size() << std::endl;
439  std::vector<Range>::const_iterator r = ranges.begin();
440  for (; r != ranges.end(); ++r)
441  {
442  out << r->begin << " " << r->end << std::endl;
443  }
444 }
445 
446 
447 
448 void
449 IndexSet::read(std::istream &in)
450 {
451  AssertThrow(in, ExcIO());
452 
453  size_type s;
454  unsigned int n_ranges;
455 
456  in >> s >> n_ranges;
457  ranges.clear();
458  set_size(s);
459  for (unsigned int i = 0; i < n_ranges; ++i)
460  {
461  AssertThrow(in, ExcIO());
462 
463  size_type b, e;
464  in >> b >> e;
465  add_range(b, e);
466  }
467 }
468 
469 
470 void
471 IndexSet::block_write(std::ostream &out) const
472 {
473  AssertThrow(out, ExcIO());
474  out.write(reinterpret_cast<const char *>(&index_space_size),
475  sizeof(index_space_size));
476  std::size_t n_ranges = ranges.size();
477  out.write(reinterpret_cast<const char *>(&n_ranges), sizeof(n_ranges));
478  if (ranges.empty() == false)
479  out.write(reinterpret_cast<const char *>(&*ranges.begin()),
480  ranges.size() * sizeof(Range));
481  AssertThrow(out, ExcIO());
482 }
483 
484 void
485 IndexSet::block_read(std::istream &in)
486 {
487  size_type size;
488  std::size_t n_ranges;
489  in.read(reinterpret_cast<char *>(&size), sizeof(size));
490  in.read(reinterpret_cast<char *>(&n_ranges), sizeof(n_ranges));
491  // we have to clear ranges first
492  ranges.clear();
493  set_size(size);
494  ranges.resize(n_ranges, Range(0, 0));
495  if (n_ranges)
496  in.read(reinterpret_cast<char *>(&*ranges.begin()),
497  ranges.size() * sizeof(Range));
498 
499  do_compress(); // needed so that largest_range can be recomputed
500 }
501 
502 
503 
504 void
505 IndexSet::fill_index_vector(std::vector<size_type> &indices) const
506 {
507  compress();
508 
509  indices.clear();
510  indices.reserve(n_elements());
511 
512  for (const auto &range : ranges)
513  for (size_type entry = range.begin; entry < range.end; ++entry)
514  indices.push_back(entry);
515 
516  Assert(indices.size() == n_elements(), ExcInternalError());
517 }
518 
519 
520 
521 #ifdef DEAL_II_WITH_TRILINOS
522 # ifdef DEAL_II_TRILINOS_WITH_TPETRA
523 
524 Tpetra::Map<int, types::global_dof_index>
525 IndexSet::make_tpetra_map(const MPI_Comm &communicator,
526  const bool overlapping) const
527 {
528  compress();
529  (void)communicator;
530 
531 # ifdef DEBUG
532  if (!overlapping)
533  {
534  const size_type n_global_elements =
535  Utilities::MPI::sum(n_elements(), communicator);
536  Assert(n_global_elements == size(),
537  ExcMessage("You are trying to create an Tpetra::Map object "
538  "that partitions elements of an index set "
539  "between processors. However, the union of the "
540  "index sets on different processors does not "
541  "contain all indices exactly once: the sum of "
542  "the number of entries the various processors "
543  "want to store locally is " +
544  Utilities::to_string(n_global_elements) +
545  " whereas the total size of the object to be "
546  "allocated is " +
548  ". In other words, there are "
549  "either indices that are not spoken for "
550  "by any processor, or there are indices that are "
551  "claimed by multiple processors."));
552  }
553 # endif
554 
555  // Find out if the IndexSet is ascending and 1:1. This corresponds to a
556  // linear Tpetra::Map. Overlapping IndexSets are never 1:1.
557  const bool linear =
558  overlapping ? false : is_ascending_and_one_to_one(communicator);
559  if (linear)
560  return Tpetra::Map<int, types::global_dof_index>(
561  size(),
562  n_elements(),
563  0,
564 # ifdef DEAL_II_WITH_MPI
565  Teuchos::rcp(new Teuchos::MpiComm<int>(communicator))
566 # else
567  Teuchos::rcp(new Teuchos::Comm<int>())
568 # endif
569  );
570  else
571  {
572  std::vector<size_type> indices;
573  fill_index_vector(indices);
574  std::vector<types::global_dof_index> int_indices(indices.size());
575  std::copy(indices.begin(), indices.end(), int_indices.begin());
576  const Teuchos::ArrayView<types::global_dof_index> arr_view(int_indices);
577  return Tpetra::Map<int, types::global_dof_index>(
578  size(),
579  arr_view,
580  0,
581 # ifdef DEAL_II_WITH_MPI
582  Teuchos::rcp(new Teuchos::MpiComm<int>(communicator))
583 # else
584  Teuchos::rcp(new Teuchos::Comm<int>())
585 # endif
586  );
587  }
588 }
589 # endif
590 
591 
592 
593 Epetra_Map
594 IndexSet::make_trilinos_map(const MPI_Comm &communicator,
595  const bool overlapping) const
596 {
597  compress();
598  (void)communicator;
599 
600 # ifdef DEBUG
601  if (!overlapping)
602  {
603  const size_type n_global_elements =
604  Utilities::MPI::sum(n_elements(), communicator);
605  Assert(n_global_elements == size(),
606  ExcMessage("You are trying to create an Epetra_Map object "
607  "that partitions elements of an index set "
608  "between processors. However, the union of the "
609  "index sets on different processors does not "
610  "contain all indices exactly once: the sum of "
611  "the number of entries the various processors "
612  "want to store locally is " +
613  Utilities::to_string(n_global_elements) +
614  " whereas the total size of the object to be "
615  "allocated is " +
617  ". In other words, there are "
618  "either indices that are not spoken for "
619  "by any processor, or there are indices that are "
620  "claimed by multiple processors."));
621  }
622 # endif
623 
624  // Find out if the IndexSet is ascending and 1:1. This corresponds to a
625  // linear EpetraMap. Overlapping IndexSets are never 1:1.
626  const bool linear =
627  overlapping ? false : is_ascending_and_one_to_one(communicator);
628 
629  if (linear)
630  return Epetra_Map(TrilinosWrappers::types::int_type(size()),
631  TrilinosWrappers::types::int_type(n_elements()),
632  0,
633 # ifdef DEAL_II_WITH_MPI
634  Epetra_MpiComm(communicator)
635 # else
636  Epetra_SerialComm()
637 # endif
638  );
639  else
640  {
641  std::vector<size_type> indices;
642  fill_index_vector(indices);
643  return Epetra_Map(
644  TrilinosWrappers::types::int_type(-1),
645  TrilinosWrappers::types::int_type(n_elements()),
646  (n_elements() > 0 ?
647  reinterpret_cast<TrilinosWrappers::types::int_type *>(
648  indices.data()) :
649  nullptr),
650  0,
651 # ifdef DEAL_II_WITH_MPI
652  Epetra_MpiComm(communicator)
653 # else
654  Epetra_SerialComm()
655 # endif
656  );
657  }
658 }
659 #endif
660 
661 
662 
663 bool
664 IndexSet::is_ascending_and_one_to_one(const MPI_Comm &communicator) const
665 {
666  // If the sum of local elements does not add up to the total size,
667  // the IndexSet can't be complete.
668  const size_type n_global_elements =
669  Utilities::MPI::sum(n_elements(), communicator);
670  if (n_global_elements != size())
671  return false;
672 
673 #ifdef DEAL_II_WITH_MPI
674  // Non-contiguous IndexSets can't be linear.
675  const bool all_contiguous =
676  (Utilities::MPI::min(is_contiguous() ? 1 : 0, communicator) == 1);
677  if (!all_contiguous)
678  return false;
679 
680  bool is_globally_ascending = true;
681  // we know that there is only one interval
682  types::global_dof_index first_local_dof = (n_elements() > 0) ?
683  *(begin_intervals()->begin()) :
685 
686  const unsigned int my_rank = Utilities::MPI::this_mpi_process(communicator);
687  const std::vector<types::global_dof_index> global_dofs =
688  Utilities::MPI::gather(communicator, first_local_dof, 0);
689 
690  if (my_rank == 0)
691  {
692  // find out if the received std::vector is ascending
693  types::global_dof_index index = 0;
694  while (global_dofs[index] == numbers::invalid_dof_index)
695  ++index;
696  types::global_dof_index old_dof = global_dofs[index++];
697  for (; index < global_dofs.size(); ++index)
698  {
699  const types::global_dof_index new_dof = global_dofs[index];
700  if (new_dof != numbers::invalid_dof_index)
701  {
702  if (new_dof <= old_dof)
703  {
704  is_globally_ascending = false;
705  break;
706  }
707  else
708  old_dof = new_dof;
709  }
710  }
711  }
712 
713  // now broadcast the result
714  int is_ascending = is_globally_ascending ? 1 : 0;
715  int ierr = MPI_Bcast(&is_ascending, 1, MPI_INT, 0, communicator);
716  AssertThrowMPI(ierr);
717 
718  return (is_ascending == 1);
719 #else
720  return true;
721 #endif // DEAL_II_WITH_MPI
722 }
723 
724 
725 
726 std::size_t
728 {
732  sizeof(compress_mutex));
733 }
734 
735 
736 
737 DEAL_II_NAMESPACE_CLOSE
Iterator lower_bound(Iterator first, Iterator last, const T &val)
Definition: utilities.h:1067
ElementIterator end() const
Definition: index_set.h:1546
static ::ExceptionBase & ExcIO()
void block_read(std::istream &in)
Definition: index_set.cc:485
void add_indices(const ForwardIterator &begin, const ForwardIterator &end)
Definition: index_set.h:1641
#define AssertThrow(cond, exc)
Definition: exceptions.h:1519
types::global_dof_index size_type
Definition: index_set.h:85
std::string to_string(const number value, const unsigned int digits=numbers::invalid_unsigned_int)
Definition: utilities.cc:392
std::size_t memory_consumption() const
Definition: index_set.cc:727
size_type index_space_size
Definition: index_set.h:933
bool is_ascending_and_one_to_one(const MPI_Comm &communicator) const
Definition: index_set.cc:664
Epetra_Map make_trilinos_map(const MPI_Comm &communicator=MPI_COMM_WORLD, const bool overlapping=false) const
Definition: index_set.cc:594
size_type size() const
Definition: index_set.h:1600
static ::ExceptionBase & ExcMessage(std::string arg1)
void block_write(std::ostream &out) const
Definition: index_set.cc:471
std::vector< Range > ranges
Definition: index_set.h:917
T sum(const T &t, const MPI_Comm &mpi_communicator)
void subtract_set(const IndexSet &other)
Definition: index_set.cc:267
#define Assert(cond, exc)
Definition: exceptions.h:1407
static ::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
void do_compress() const
Definition: index_set.cc:127
std::vector< T > gather(const MPI_Comm &comm, const T &object_to_send, const unsigned int root_process=0)
size_type pop_back()
Definition: index_set.cc:339
void fill_index_vector(std::vector< size_type > &indices) const
Definition: index_set.cc:505
void add_range(const size_type begin, const size_type end)
Definition: index_set.cc:98
IndexSet operator &(const IndexSet &is) const
void set_size(const size_type size)
Definition: index_set.h:1588
unsigned int global_dof_index
Definition: types.h:89
IndexSet get_view(const size_type begin, const size_type end) const
Definition: index_set.cc:238
void compress() const
Definition: index_set.h:1608
IntervalIterator begin_intervals() const
Definition: index_set.h:1555
#define AssertThrowMPI(error_code)
Definition: exceptions.h:1695
bool is_contiguous() const
Definition: index_set.h:1715
Threads::Mutex compress_mutex
Definition: index_set.h:950
T min(const T &t, const MPI_Comm &mpi_communicator)
size_type pop_front()
Definition: index_set.cc:357
bool is_compressed
Definition: index_set.h:927
ElementIterator begin() const
Definition: index_set.h:1039
unsigned int this_mpi_process(const MPI_Comm &mpi_communicator)
Definition: mpi.cc:82
void write(std::ostream &out) const
Definition: index_set.cc:434
const types::global_dof_index invalid_dof_index
Definition: types.h:188
ElementIterator begin() const
Definition: index_set.h:1483
void read(std::istream &in)
Definition: index_set.cc:449
size_type n_elements() const
Definition: index_set.h:1732
bool is_empty() const
Definition: index_set.h:1724
size_type largest_range
Definition: index_set.h:944
std::enable_if< std::is_fundamental< T >::value, std::size_t >::type memory_consumption(const T &t)
static ::ExceptionBase & ExcInternalError()