1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2000 - 2020 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 #ifndef dealii_block_indices_h
17 #define dealii_block_indices_h
18
19
20 #include <deal.II/base/config.h>
21
22 #include <deal.II/base/exceptions.h>
23 #include <deal.II/base/logstream.h>
24 #include <deal.II/base/subscriptor.h>
25 #include <deal.II/base/utilities.h>
26
27 #include <cstddef>
28 #include <vector>
29
30 DEAL_II_NAMESPACE_OPEN
31
32
33 /**
34 * BlockIndices represents a range of indices (such as the range $[0,N)$
35 * of valid indices for elements of a vector) and how this one range
36 * is broken down into smaller but contiguous "blocks" (such as the velocity
37 * and pressure parts of a solution vector). In particular, it provides the
38 * ability to translate between global indices and the indices <i>within</i>
39 * a block. This class is used, for example, in the BlockVector,
40 * BlockSparsityPattern, and BlockMatrixBase classes.
41 *
42 * The information that can be obtained from this class falls into two groups.
43 * First, it is possible to query the global size of the index space (through
44 * the total_size() member function), and the number of blocks and their sizes
45 * (via size() and the block_size() functions).
46 *
47 * Secondly, this class manages the conversion of global indices to the
48 * local indices within this block, and the other way around. This is required,
49 * for example, when you address a global element in a block vector and want to
50 * know within which block this is, and which index within this block it
51 * corresponds to. It is also useful if a matrix is composed of several
52 * blocks, where you have to translate global row and column indices to local
53 * ones.
54 *
55 * @ingroup data
56 * @see
57 * @ref GlossBlockLA "Block (linear algebra)"
58 */
59 class BlockIndices : public Subscriptor
60 {
61 public:
62 /**
63 * Declare the type for container size.
64 */
65 using size_type = types::global_dof_index;
66
67 /**
68 * Default constructor. Initialize for zero blocks.
69 */
70 BlockIndices();
71
72 /**
73 * Constructor. Initialize the number of entries in each block @p i as
74 * <tt>block_sizes[i]</tt>. The number of blocks will be the size of @p
75 * block_sizes.
76 */
77 BlockIndices(const std::vector<size_type> &block_sizes);
78
79 /**
80 * Move constructor. Initialize a new object by stealing the internal data of
81 * another BlockIndices object.
82 */
83 BlockIndices(BlockIndices &&b) noexcept;
84
85 /**
86 * Copy constructor.
87 */
88 BlockIndices(const BlockIndices &) = default;
89
90 /**
91 * Specialized constructor for a structure with blocks of equal size.
92 */
93 explicit BlockIndices(const unsigned int n_blocks,
94 const size_type block_size = 0);
95
96 /**
97 * Reinitialize the number of blocks and assign each block the same number
98 * of elements.
99 */
100 void
101 reinit(const unsigned int n_blocks, const size_type n_elements_per_block);
102
103 /**
104 * Reinitialize the number of indices within each block from the given
105 * argument. The number of blocks will be adjusted to the size of
106 * <tt>block_sizes</tt> and the size of block @p i is set to
107 * <tt>block_sizes[i]</tt>.
108 */
109 void
110 reinit(const std::vector<size_type> &block_sizes);
111
112 /**
113 * Add another block of given size to the end of the block structure.
114 */
115 void
116 push_back(const size_type size);
117
118 /**
119 * @name Size information
120 */
121 //@{
122
123 /**
124 * Number of blocks in index field.
125 */
126 unsigned int
127 size() const;
128
129 /**
130 * Return the total number of indices accumulated over all blocks, that is,
131 * the dimension of the vector space of the block vector.
132 */
133 size_type
134 total_size() const;
135
136 /**
137 * The size of the @p ith block.
138 */
139 size_type
140 block_size(const unsigned int i) const;
141
142 /**
143 * String representation of the block sizes. The output is of the form
144 * `[nb->b1,b2,b3|s]`, where `nb` is n_blocks(), `s` is total_size() and
145 * `b1` etc. are the values returned by block_size() for each of the blocks.
146 */
147 std::string
148 to_string() const;
149
150 //@}
151
152 /**
153 * @name Index conversion
154 *
155 * Functions in this group assume an object, which was created after sorting
156 * by block, such that each block forms a set of consecutive indices in the
157 * object. If applied to other objects, the numbers obtained from these
158 * functions are meaningless.
159 */
160 //@{
161
162 /**
163 * Return the block and the index within that block for the global index @p
164 * i. The first element of the pair is the block, the second the index
165 * within it.
166 */
167 std::pair<unsigned int, size_type>
168 global_to_local(const size_type i) const;
169
170 /**
171 * Return the global index of @p index in block @p block.
172 */
173 size_type
174 local_to_global(const unsigned int block, const size_type index) const;
175
176 /**
177 * The start index of the ith block.
178 */
179 size_type
180 block_start(const unsigned int i) const;
181 //@}
182
183 /**
184 * Copy operator.
185 */
186 BlockIndices &
187 operator=(const BlockIndices &b);
188
189 /**
190 * Move assignment operator. Move another BlockIndices object onto the
191 * current one by transferring its contents.
192 */
193 BlockIndices &
194 operator=(BlockIndices &&) noexcept;
195
196 /**
197 * Compare whether two objects are the same, i.e. whether the number of
198 * blocks and the sizes of all blocks are equal.
199 */
200 bool
201 operator==(const BlockIndices &b) const;
202
203 /**
204 * Swap the contents of these two objects.
205 */
206 void
207 swap(BlockIndices &b);
208
209 /**
210 * Determine an estimate for the memory consumption (in bytes) of this
211 * object.
212 */
213 std::size_t
214 memory_consumption() const;
215
216 private:
217 /**
218 * Number of blocks. While this value could be obtained through
219 * <tt>start_indices.size()-1</tt>, we cache this value for faster access.
220 */
221 unsigned int n_blocks;
222
223 /**
224 * Global starting index of each vector. The last and redundant value is the
225 * total number of entries.
226 */
227 std::vector<size_type> start_indices;
228 };
229
230
231 /**
232 * Operator for logging BlockIndices. Writes the number of blocks, the size of
233 * each block and the total size of the index field.
234 *
235 * @ref BlockIndices
236 */
237 inline LogStream &
238 operator<<(LogStream &s, const BlockIndices &bi)
239 {
240 const unsigned int n = bi.size();
241 s << n << ":[";
242 // Write first size without leading space
243 if (n > 0)
244 s << bi.block_size(0);
245 // Write all other sizes
246 for (unsigned int i = 1; i < n; ++i)
247 s << ' ' << bi.block_size(i);
248 s << "]->" << bi.total_size();
249 return s;
250 }
251
252
253
254 /* ---------------------- template and inline functions ------------------- */
255
256 inline void
reinit(const unsigned int nb,const size_type block_size)257 BlockIndices::reinit(const unsigned int nb, const size_type block_size)
258 {
259 n_blocks = nb;
260 start_indices.resize(n_blocks + 1);
261 for (size_type i = 0; i <= n_blocks; ++i)
262 start_indices[i] = i * block_size;
263 }
264
265
266
267 inline void
reinit(const std::vector<size_type> & block_sizes)268 BlockIndices::reinit(const std::vector<size_type> &block_sizes)
269 {
270 if (start_indices.size() != block_sizes.size() + 1)
271 {
272 n_blocks = static_cast<unsigned int>(block_sizes.size());
273 start_indices.resize(n_blocks + 1);
274 }
275 start_indices[0] = 0;
276 for (size_type i = 1; i <= n_blocks; ++i)
277 start_indices[i] = start_indices[i - 1] + block_sizes[i - 1];
278 }
279
280
BlockIndices()281 inline BlockIndices::BlockIndices()
282 : n_blocks(0)
283 , start_indices(1, 0)
284 {}
285
286
287
BlockIndices(const unsigned int n_blocks,const size_type block_size)288 inline BlockIndices::BlockIndices(const unsigned int n_blocks,
289 const size_type block_size)
290 : n_blocks(n_blocks)
291 , start_indices(n_blocks + 1)
292 {
293 for (size_type i = 0; i <= n_blocks; ++i)
294 start_indices[i] = i * block_size;
295 }
296
297
298
BlockIndices(const std::vector<size_type> & block_sizes)299 inline BlockIndices::BlockIndices(const std::vector<size_type> &block_sizes)
300 : n_blocks(static_cast<unsigned int>(block_sizes.size()))
301 , start_indices(block_sizes.size() + 1)
302 {
303 reinit(block_sizes);
304 }
305
306
307
BlockIndices(BlockIndices && b)308 inline BlockIndices::BlockIndices(BlockIndices &&b) noexcept
309 : n_blocks(b.n_blocks)
310 , start_indices(std::move(b.start_indices))
311 {
312 b.n_blocks = 0;
313 b.start_indices = std::vector<size_type>(1, 0);
314 }
315
316
317
318 inline void
push_back(const size_type sz)319 BlockIndices::push_back(const size_type sz)
320 {
321 start_indices.push_back(start_indices[n_blocks] + sz);
322 ++n_blocks;
323 AssertDimension(start_indices.size(), n_blocks + 1);
324 }
325
326
327 inline std::pair<unsigned int, BlockIndices::size_type>
global_to_local(const size_type i)328 BlockIndices::global_to_local(const size_type i) const
329 {
330 AssertIndexRange(i, total_size());
331 Assert(n_blocks > 0, ExcLowerRangeType<size_type>(i, size_type(1)));
332
333 // start_indices[0] == 0 so we might as well start from the next one
334 const auto it =
335 --std::upper_bound(++start_indices.begin(), start_indices.end(), i);
336
337 return {std::distance(start_indices.begin(), it), i - *it};
338 }
339
340
341 inline BlockIndices::size_type
local_to_global(const unsigned int block,const size_type index)342 BlockIndices::local_to_global(const unsigned int block,
343 const size_type index) const
344 {
345 AssertIndexRange(block, n_blocks);
346 AssertIndexRange(index, start_indices[block + 1] - start_indices[block]);
347
348 return start_indices[block] + index;
349 }
350
351
352 inline unsigned int
size()353 BlockIndices::size() const
354 {
355 return n_blocks;
356 }
357
358
359
360 inline BlockIndices::size_type
total_size()361 BlockIndices::total_size() const
362 {
363 if (n_blocks == 0)
364 return 0;
365 return start_indices[n_blocks];
366 }
367
368
369
370 inline BlockIndices::size_type
block_size(const unsigned int block)371 BlockIndices::block_size(const unsigned int block) const
372 {
373 AssertIndexRange(block, n_blocks);
374 return start_indices[block + 1] - start_indices[block];
375 }
376
377
378
379 inline std::string
to_string()380 BlockIndices::to_string() const
381 {
382 std::string result = "[" + Utilities::int_to_string(n_blocks) + "->";
383 for (unsigned int i = 0; i < n_blocks; ++i)
384 {
385 if (i > 0)
386 result += ',';
387 result += std::to_string(block_size(i));
388 }
389 result += "|" + std::to_string(total_size()) + ']';
390 return result;
391 }
392
393
394
395 inline BlockIndices::size_type
block_start(const unsigned int block)396 BlockIndices::block_start(const unsigned int block) const
397 {
398 AssertIndexRange(block, n_blocks);
399 return start_indices[block];
400 }
401
402
403
404 inline BlockIndices &
405 BlockIndices::operator=(const BlockIndices &b)
406 {
407 start_indices = b.start_indices;
408 n_blocks = b.n_blocks;
409 return *this;
410 }
411
412
413
414 inline BlockIndices &
415 BlockIndices::operator=(BlockIndices &&b) noexcept
416 {
417 start_indices = std::move(b.start_indices);
418 n_blocks = b.n_blocks;
419
420 b.start_indices = std::vector<size_type>(1, 0);
421 b.n_blocks = 0;
422
423 return *this;
424 }
425
426
427
428 inline bool
429 BlockIndices::operator==(const BlockIndices &b) const
430 {
431 if (n_blocks != b.n_blocks)
432 return false;
433
434 for (size_type i = 0; i <= n_blocks; ++i)
435 if (start_indices[i] != b.start_indices[i])
436 return false;
437
438 return true;
439 }
440
441
442
443 inline void
swap(BlockIndices & b)444 BlockIndices::swap(BlockIndices &b)
445 {
446 std::swap(n_blocks, b.n_blocks);
447 std::swap(start_indices, b.start_indices);
448 }
449
450
451
452 inline std::size_t
memory_consumption()453 BlockIndices::memory_consumption() const
454 {
455 return (sizeof(*this) + start_indices.size() * sizeof(start_indices[0]));
456 }
457
458
459
460 /* ----------------- global functions ---------------------------- */
461
462
463 /**
464 * Global function @p swap which overloads the default implementation of the
465 * C++ standard library which uses a temporary object. The function simply
466 * exchanges the data of the two objects.
467 *
468 * @relatesalso BlockIndices
469 */
470 inline void
swap(BlockIndices & u,BlockIndices & v)471 swap(BlockIndices &u, BlockIndices &v)
472 {
473 u.swap(v);
474 }
475
476
477
478 DEAL_II_NAMESPACE_CLOSE
479
480 #endif
481