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