Reference documentation for deal.II version 8.5.1
block_linear_operator.h
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2010 - 2017 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 at
12 // the top level of the deal.II distribution.
13 //
14 // ---------------------------------------------------------------------
15 
16 #ifndef dealii__block_linear_operator_h
17 #define dealii__block_linear_operator_h
18 
19 #include <deal.II/base/config.h>
20 #include <deal.II/base/exceptions.h>
21 
22 #include <deal.II/lac/linear_operator.h>
23 
24 #ifdef DEAL_II_WITH_CXX11
25 
26 DEAL_II_NAMESPACE_OPEN
27 
28 // Forward declarations:
29 
30 namespace internal
31 {
32  namespace BlockLinearOperator
33  {
34  template<typename PayloadBlockType = internal::LinearOperator::EmptyPayload>
36  }
37 }
38 
39 template <typename Number> class BlockVector;
40 
41 template <typename Range = BlockVector<double>,
42  typename Domain = Range,
43  typename BlockPayload = internal::BlockLinearOperator::EmptyBlockPayload<> >
45 
46 template <typename Range = BlockVector<double>,
47  typename Domain = Range,
48  typename BlockPayload = internal::BlockLinearOperator::EmptyBlockPayload<>,
49  typename BlockMatrixType>
51 block_operator(const BlockMatrixType &matrix);
52 
53 template <size_t m, size_t n,
54  typename Range = BlockVector<double>,
55  typename Domain = Range,
59 
60 template <size_t m,
61  typename Range = BlockVector<double>,
62  typename Domain = Range,
66 
67 template <size_t m,
68  typename Range = BlockVector<double>,
69  typename Domain = Range,
73 
74 // This is a workaround for a bug in <=gcc-4.7 that does not like partial
75 // template default values in combination with local lambda expressions [1]
76 //
77 // [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53624
78 //
79 // Forward declare functions with partial template defaults:
80 
81 template <typename Range = BlockVector<double>,
82  typename Domain = Range,
83  typename BlockPayload = internal::BlockLinearOperator::EmptyBlockPayload<>,
84  typename BlockMatrixType>
86 block_diagonal_operator(const BlockMatrixType &block_matrix);
87 
88 template <typename Range = BlockVector<double>,
89  typename Domain = Range,
90  typename BlockPayload = internal::BlockLinearOperator::EmptyBlockPayload<> >
94 
95 template <typename Range = BlockVector<double>,
96  typename Domain = Range,
97  typename BlockPayload = internal::BlockLinearOperator::EmptyBlockPayload<> >
101 
102 // end of workaround
103 
104 
105 
145 template <typename Range, typename Domain, typename BlockPayload>
146 class BlockLinearOperator : public LinearOperator<Range, Domain, typename BlockPayload::BlockType>
147 {
148 public:
149 
151 
159  BlockLinearOperator(const BlockPayload &payload)
160  : LinearOperator<Range, Domain, typename BlockPayload::BlockType>(typename BlockPayload::BlockType(payload,payload))
161  {
162  n_block_rows = []() -> unsigned int
163  {
164  Assert(false, ExcMessage("Uninitialized BlockLinearOperator<Range, Domain>::n_block_rows called"));
165  return 0;
166  };
167 
168  n_block_cols = []() -> unsigned int
169  {
170  Assert(false, ExcMessage("Uninitialized BlockLinearOperator<Range, Domain>::n_block_cols called"));
171  return 0;
172  };
173 
174  block = [](unsigned int, unsigned int) -> BlockType
175  {
176  Assert(false, ExcMessage("Uninitialized BlockLinearOperator<Range, Domain>::block called"));
177  return BlockType();
178  };
179  }
180 
185  default;
186 
192  template<typename Op>
193  BlockLinearOperator(const Op &op)
194  {
195  *this = block_operator<Range, Domain, BlockPayload, Op>(op);
196  }
197 
203  template<size_t m, size_t n>
204  BlockLinearOperator(const std::array<std::array<BlockType, n>, m> &ops)
205  {
206  *this = block_operator<m, n, Range, Domain, BlockPayload>(ops);
207  }
208 
214  template<size_t m>
215  BlockLinearOperator(const std::array<BlockType, m> &ops)
216  {
217  *this = block_diagonal_operator<m, Range, Domain, BlockPayload>(ops);
218  }
219 
225 
230  template <typename Op>
232  {
233  *this = block_operator<Range, Domain, BlockPayload, Op>(op);
234  return *this;
235  }
236 
242  template <size_t m, size_t n>
244  operator=(const std::array<std::array<BlockType, n>, m> &ops)
245  {
246  *this = block_operator<m, n, Range, Domain, BlockPayload>(ops);
247  return *this;
248  }
249 
255  template <size_t m>
257  operator=(const std::array<BlockType, m> &ops)
258  {
259  *this = block_diagonal_operator<m, Range, Domain, BlockPayload>(ops);
260  return *this;
261  }
262 
267  std::function<unsigned int()> n_block_rows;
268 
273  std::function<unsigned int()> n_block_cols;
274 
280  std::function<BlockType(unsigned int, unsigned int)> block;
281 };
282 
283 
284 
285 namespace internal
286 {
287  namespace BlockLinearOperator
288  {
289  // Populate the LinearOperator interfaces with the help of the
290  // BlockLinearOperator functions
291  template <typename Range, typename Domain, typename BlockPayload>
292  inline void
293  populate_linear_operator_functions(
295  {
296  op.reinit_range_vector = [=](Range &v, bool omit_zeroing_entries)
297  {
298  const unsigned int m = op.n_block_rows();
299 
300  // Reinitialize the block vector to m blocks:
301  v.reinit(m);
302 
303  // And reinitialize every individual block with reinit_range_vectors:
304  for (unsigned int i = 0; i < m; ++i)
305  op.block(i, 0).reinit_range_vector(v.block(i), omit_zeroing_entries);
306 
307  v.collect_sizes();
308  };
309 
310  op.reinit_domain_vector = [=](Domain &v, bool omit_zeroing_entries)
311  {
312  const unsigned int n = op.n_block_cols();
313 
314  // Reinitialize the block vector to n blocks:
315  v.reinit(n);
316 
317  // And reinitialize every individual block with reinit_domain_vectors:
318  for (unsigned int i = 0; i < n; ++i)
319  op.block(0, i).reinit_domain_vector(v.block(i), omit_zeroing_entries);
320 
321  v.collect_sizes();
322  };
323 
324  op.vmult = [=](Range &v, const Domain &u)
325  {
326  const unsigned int m = op.n_block_rows();
327  const unsigned int n = op.n_block_cols();
328  Assert(v.n_blocks() == m, ExcDimensionMismatch(v.n_blocks(), m));
329  Assert(u.n_blocks() == n, ExcDimensionMismatch(u.n_blocks(), n));
330 
331  for (unsigned int i = 0; i < m; ++i)
332  {
333  op.block(i, 0).vmult(v.block(i), u.block(0));
334  for (unsigned int j = 1; j < n; ++j)
335  op.block(i, j).vmult_add(v.block(i), u.block(j));
336  }
337  };
338 
339  op.vmult_add = [=](Range &v, const Domain &u)
340  {
341  const unsigned int m = op.n_block_rows();
342  const unsigned int n = op.n_block_cols();
343  Assert(v.n_blocks() == m, ExcDimensionMismatch(v.n_blocks(), m));
344  Assert(u.n_blocks() == n, ExcDimensionMismatch(u.n_blocks(), n));
345 
346  for (unsigned int i = 0; i < m; ++i)
347  for (unsigned int j = 0; j < n; ++j)
348  op.block(i, j).vmult_add(v.block(i), u.block(j));
349  };
350 
351  op.Tvmult = [=](Domain &v, const Range &u)
352  {
353  const unsigned int n = op.n_block_cols();
354  const unsigned int m = op.n_block_rows();
355  Assert(v.n_blocks() == n, ExcDimensionMismatch(v.n_blocks(), n));
356  Assert(u.n_blocks() == m, ExcDimensionMismatch(u.n_blocks(), m));
357 
358  for (unsigned int i = 0; i < n; ++i)
359  {
360  op.block(0, i).Tvmult(v.block(i), u.block(0));
361  for (unsigned int j = 1; j < m; ++j)
362  op.block(j, i).Tvmult_add(v.block(i), u.block(j));
363  }
364  };
365 
366  op.Tvmult_add = [=](Domain &v, const Range &u)
367  {
368  const unsigned int n = op.n_block_cols();
369  const unsigned int m = op.n_block_rows();
370  Assert(v.n_blocks() == n, ExcDimensionMismatch(v.n_blocks(), n));
371  Assert(u.n_blocks() == m, ExcDimensionMismatch(u.n_blocks(), m));
372 
373  for (unsigned int i = 0; i < n; ++i)
374  for (unsigned int j = 0; j < m; ++j)
375  op.block(j, i).Tvmult_add(v.block(i), u.block(j));
376  };
377  }
378 
379 
394  template<typename PayloadBlockType>
395  class EmptyBlockPayload
396  {
397  public:
401  typedef PayloadBlockType BlockType;
402 
410  template <typename... Args>
411  EmptyBlockPayload (const Args &...)
412  { }
413  };
414 
415  } /*namespace BlockLinearOperator*/
416 } /*namespace internal*/
417 
418 
419 
424 
436 template <typename Range,
437  typename Domain,
438  typename BlockPayload,
439  typename BlockMatrixType>
441 block_operator(const BlockMatrixType &block_matrix)
442 {
444 
445  BlockLinearOperator<Range, Domain, BlockPayload> return_op (BlockPayload(block_matrix,block_matrix));
446 
447  return_op.n_block_rows = [&block_matrix]() -> unsigned int
448  {
449  return block_matrix.n_block_rows();
450  };
451 
452  return_op.n_block_cols = [&block_matrix]() -> unsigned int
453  {
454  return block_matrix.n_block_cols();
455  };
456 
457  return_op.block = [&block_matrix](unsigned int i, unsigned int j) -> BlockType
458  {
459 #ifdef DEBUG
460  const unsigned int m = block_matrix.n_block_rows();
461  const unsigned int n = block_matrix.n_block_cols();
462  Assert(i < m, ExcIndexRange (i, 0, m));
463  Assert(j < n, ExcIndexRange (j, 0, n));
464 #endif
465 
466  return BlockType(block_matrix.block(i, j));
467  };
468 
469  internal::BlockLinearOperator::populate_linear_operator_functions(return_op);
470  return return_op;
471 }
472 
473 
474 
502 template <size_t m, size_t n, typename Range, typename Domain, typename BlockPayload>
505 {
506  static_assert(m > 0 && n > 0,
507  "a blocked LinearOperator must consist of at least one block");
508 
510 
511  BlockLinearOperator<Range, Domain, BlockPayload> return_op ((BlockPayload())); // TODO: Create block payload so that this can be initialised correctly
512 
513  return_op.n_block_rows = []() -> unsigned int
514  {
515  return m;
516  };
517 
518  return_op.n_block_cols = []() -> unsigned int
519  {
520  return n;
521  };
522 
523  return_op.block = [ops](unsigned int i, unsigned int j) -> BlockType
524  {
525  Assert(i < m, ExcIndexRange (i, 0, m));
526  Assert(j < n, ExcIndexRange (j, 0, n));
527 
528  return ops[i][j];
529  };
530 
531  internal::BlockLinearOperator::populate_linear_operator_functions(return_op);
532  return return_op;
533 }
534 
535 
536 
552 template <typename Range,
553  typename Domain,
554  typename BlockPayload,
555  typename BlockMatrixType>
557 block_diagonal_operator(const BlockMatrixType &block_matrix)
558 {
560 
561  BlockLinearOperator<Range, Domain, BlockPayload> return_op (BlockPayload(block_matrix,block_matrix));
562 
563  return_op.n_block_rows = [&block_matrix]() -> unsigned int
564  {
565  return block_matrix.n_block_rows();
566  };
567 
568  return_op.n_block_cols = [&block_matrix]() -> unsigned int
569  {
570  return block_matrix.n_block_cols();
571  };
572 
573  return_op.block = [&block_matrix](unsigned int i, unsigned int j) -> BlockType
574  {
575 #ifdef DEBUG
576  const unsigned int m = block_matrix.n_block_rows();
577  const unsigned int n = block_matrix.n_block_cols();
578  Assert(m == n, ExcDimensionMismatch(m, n));
579  Assert(i < m, ExcIndexRange (i, 0, m));
580  Assert(j < n, ExcIndexRange (j, 0, n));
581 #endif
582  if (i == j)
583  return BlockType(block_matrix.block(i, j));
584  else
585  return null_operator(BlockType(block_matrix.block(i, j)));
586  };
587 
588  internal::BlockLinearOperator::populate_linear_operator_functions(return_op);
589  return return_op;
590 }
591 
592 
593 
611 template <size_t m, typename Range, typename Domain, typename BlockPayload>
614 {
615  static_assert(m > 0,
616  "a blockdiagonal LinearOperator must consist of at least one block");
617 
619 
620  std::array<std::array<BlockType, m>, m> new_ops;
621 
622  // This is a bit tricky. We have to make sure that the off-diagonal
623  // elements of return_op.ops are populated correctly. They must be
624  // null_operators, but with correct reinit_domain_vector and
625  // reinit_range_vector functions.
626  for (unsigned int i = 0; i < m; ++i)
627  for (unsigned int j = 0; j < m; ++j)
628  if (i == j)
629  {
630  // diagonal elements are easy:
631  new_ops[i][j] = ops[i];
632  }
633  else
634  {
635  // create a null-operator...
636  new_ops[i][j] = null_operator(ops[i]);
637  // ... and fix up reinit_domain_vector:
638  new_ops[i][j].reinit_domain_vector = ops[j].reinit_domain_vector;
639  }
640 
641  return block_operator<m,m,Range,Domain>(new_ops);
642 }
643 
644 
645 
655 template <size_t m, typename Range, typename Domain, typename BlockPayload>
658 {
659  static_assert(m > 0,
660  "a blockdiagonal LinearOperator must consist of at least "
661  "one block");
662 
664  std::array<BlockType, m> new_ops;
665  new_ops.fill(op);
666 
667  return block_diagonal_operator(new_ops);
668 }
669 
670 
671 
673 
677 
713 template <typename Range, typename Domain, typename BlockPayload>
716  const BlockLinearOperator<Domain, Range, BlockPayload> &diagonal_inverse)
717 {
718  LinearOperator<Range, Range, typename BlockPayload::BlockType> return_op ((typename BlockPayload::BlockType(diagonal_inverse)));
719 
720  return_op.reinit_range_vector = diagonal_inverse.reinit_range_vector;
721  return_op.reinit_domain_vector = diagonal_inverse.reinit_domain_vector;
722 
723  return_op.vmult = [block_operator, diagonal_inverse](Range &v, const Range &u)
724  {
725  const unsigned int m = block_operator.n_block_rows();
726  Assert(block_operator.n_block_cols() == m,
727  ExcDimensionMismatch(block_operator.n_block_cols(), m));
728  Assert(diagonal_inverse.n_block_rows() == m,
729  ExcDimensionMismatch(diagonal_inverse.n_block_rows(), m));
730  Assert(diagonal_inverse.n_block_cols() == m,
731  ExcDimensionMismatch(diagonal_inverse.n_block_cols(), m));
732  Assert(v.n_blocks() == m, ExcDimensionMismatch(v.n_blocks(), m));
733  Assert(u.n_blocks() == m, ExcDimensionMismatch(u.n_blocks(), m));
734 
735  if (m == 0)
736  return;
737 
738  diagonal_inverse.block(0, 0).vmult(v.block(0), u.block(0));
739  for (unsigned int i = 1; i < m; ++i)
740  {
741  auto &dst = v.block(i);
742  dst = u.block(i);
743  dst *= -1.;
744  for (unsigned int j = 0; j < i; ++j)
745  block_operator.block(i, j).vmult_add(dst, v.block(j));
746  dst *= -1.;
747  diagonal_inverse.block(i, i).vmult(dst, dst); // uses intermediate storage
748  }
749  };
750 
751  return_op.vmult_add = [block_operator, diagonal_inverse](Range &v, const Range &u)
752  {
753  const unsigned int m = block_operator.n_block_rows();
754  Assert(block_operator.n_block_cols() == m,
755  ExcDimensionMismatch(block_operator.n_block_cols(), m));
756  Assert(diagonal_inverse.n_block_rows() == m,
757  ExcDimensionMismatch(diagonal_inverse.n_block_rows(), m));
758  Assert(diagonal_inverse.n_block_cols() == m,
759  ExcDimensionMismatch(diagonal_inverse.n_block_cols(), m));
760  Assert(v.n_blocks() == m, ExcDimensionMismatch(v.n_blocks(), m));
761  Assert(u.n_blocks() == m, ExcDimensionMismatch(u.n_blocks(), m));
762 
763  if (m == 0)
764  return;
765 
767  typename Range::BlockType *tmp = vector_memory.alloc();
768 
769  diagonal_inverse.block(0, 0).vmult_add(v.block(0), u.block(0));
770 
771  for (unsigned int i = 1; i < m; ++i)
772  {
773  diagonal_inverse.block(i, i).reinit_range_vector(*tmp, /*bool omit_zeroing_entries=*/ true);
774  *tmp = u.block(i);
775  *tmp *= -1.;
776  for (unsigned int j = 0; j < i; ++j)
777  block_operator.block(i, j).vmult_add(*tmp, v.block(j));
778  *tmp *= -1.;
779  diagonal_inverse.block(i, i).vmult_add(v.block(i),*tmp);
780  }
781 
782  vector_memory.free(tmp);
783  };
784 
785  return return_op;
786 }
787 
788 
789 
825 template <typename Range, typename Domain, typename BlockPayload>
828  const BlockLinearOperator<Domain, Range, BlockPayload> &diagonal_inverse)
829 {
830  LinearOperator<Range, Range, typename BlockPayload::BlockType> return_op ((typename BlockPayload::BlockType(diagonal_inverse)));
831 
832  return_op.reinit_range_vector = diagonal_inverse.reinit_range_vector;
833  return_op.reinit_domain_vector = diagonal_inverse.reinit_domain_vector;
834 
835  return_op.vmult = [block_operator, diagonal_inverse](Range &v, const Range &u)
836  {
837  const unsigned int m = block_operator.n_block_rows();
838  Assert(block_operator.n_block_cols() == m,
839  ExcDimensionMismatch(block_operator.n_block_cols(), m));
840  Assert(diagonal_inverse.n_block_rows() == m,
841  ExcDimensionMismatch(diagonal_inverse.n_block_rows(), m));
842  Assert(diagonal_inverse.n_block_cols() == m,
843  ExcDimensionMismatch(diagonal_inverse.n_block_cols(), m));
844  Assert(v.n_blocks() == m, ExcDimensionMismatch(v.n_blocks(), m));
845  Assert(u.n_blocks() == m, ExcDimensionMismatch(u.n_blocks(), m));
846 
847  if (m == 0)
848  return;
849 
850  diagonal_inverse.block(m-1, m-1).vmult(v.block(m-1),u.block(m-1));
851 
852  for (int i = m - 2; i >= 0; --i)
853  {
854  auto &dst = v.block(i);
855  dst = u.block(i);
856  dst *= -1.;
857  for (unsigned int j = i + 1; j < m; ++j)
858  block_operator.block(i, j).vmult_add(dst, v.block(j));
859  dst *= -1.;
860  diagonal_inverse.block(i, i).vmult(dst, dst); // uses intermediate storage
861  }
862  };
863 
864  return_op.vmult_add = [block_operator, diagonal_inverse](Range &v, const Range &u)
865  {
866  const unsigned int m = block_operator.n_block_rows();
867  Assert(block_operator.n_block_cols() == m,
868  ExcDimensionMismatch(block_operator.n_block_cols(), m));
869  Assert(diagonal_inverse.n_block_rows() == m,
870  ExcDimensionMismatch(diagonal_inverse.n_block_rows(), m));
871  Assert(diagonal_inverse.n_block_cols() == m,
872  ExcDimensionMismatch(diagonal_inverse.n_block_cols(), m));
873  Assert(v.n_blocks() == m, ExcDimensionMismatch(v.n_blocks(), m));
874  Assert(u.n_blocks() == m, ExcDimensionMismatch(u.n_blocks(), m));
876  typename Range::BlockType *tmp = vector_memory.alloc();
877 
878  if (m == 0)
879  return;
880 
881  diagonal_inverse.block(m-1, m-1).vmult_add(v.block(m-1),u.block(m-1));
882 
883  for (int i = m - 2; i >= 0; --i)
884  {
885  diagonal_inverse.block(i, i).reinit_range_vector(*tmp, /*bool omit_zeroing_entries=*/ true);
886  *tmp = u.block(i);
887  *tmp *= -1.;
888  for (unsigned int j = i + 1; j < m; ++j)
889  block_operator.block(i, j).vmult_add(*tmp,v.block(j));
890  *tmp *= -1.;
891  diagonal_inverse.block(i, i).vmult_add(v.block(i),*tmp);
892  }
893 
894  vector_memory.free(tmp);
895  };
896 
897  return return_op;
898 }
899 
901 
902 DEAL_II_NAMESPACE_CLOSE
903 
904 #endif // DEAL_II_WITH_CXX11
905 #endif
BlockLinearOperator(const std::array< BlockType, m > &ops)
LinearOperator< Domain, Range, typename BlockPayload::BlockType > block_forward_substitution(const BlockLinearOperator< Range, Domain, BlockPayload > &block_operator, const BlockLinearOperator< Domain, Range, BlockPayload > &diagonal_inverse)
BlockLinearOperator(const Op &op)
std::function< void(Range &v, bool omit_zeroing_entries)> reinit_range_vector
BlockLinearOperator< Range, Domain, BlockPayload > block_operator(const std::array< std::array< LinearOperator< typename Range::BlockType, typename Domain::BlockType, typename BlockPayload::BlockType >, n >, m > &ops)
std::function< void(Domain &v, const Range &u)> Tvmult_add
std::function< void(Domain &v, const Range &u)> Tvmult
BlockLinearOperator< Range, Domain, BlockPayload > & operator=(const std::array< BlockType, m > &ops)
BlockLinearOperator< Range, Domain, BlockPayload > & operator=(const Op &op)
static ::ExceptionBase & ExcIndexRange(int arg1, int arg2, int arg3)
LinearOperator< Range, Domain, Payload > null_operator(const LinearOperator< Range, Domain, Payload > &op)
BlockLinearOperator< Range, Domain, BlockPayload > & operator=(const std::array< std::array< BlockType, n >, m > &ops)
static ::ExceptionBase & ExcMessage(std::string arg1)
BlockLinearOperator< Range, Domain, BlockPayload > block_operator(const BlockMatrixType &block_matrix)
virtual VectorType * alloc()
#define Assert(cond, exc)
Definition: exceptions.h:313
static ::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
BlockLinearOperator< Range, Domain, BlockPayload > & operator=(const BlockLinearOperator< Range, Domain, BlockPayload > &)=default
LinearOperator< Domain, Range, typename BlockPayload::BlockType > block_back_substitution(const BlockLinearOperator< Range, Domain, BlockPayload > &block_operator, const BlockLinearOperator< Domain, Range, BlockPayload > &diagonal_inverse)
std::function< void(Range &v, const Domain &u)> vmult
virtual void free(const VectorType *const)
std::function< void(Domain &v, bool omit_zeroing_entries)> reinit_domain_vector
BlockLinearOperator(const std::array< std::array< BlockType, n >, m > &ops)
std::function< void(Range &v, const Domain &u)> vmult_add
std::function< BlockType(unsigned int, unsigned int)> block
std::function< unsigned int()> n_block_cols
BlockLinearOperator(const BlockPayload &payload)
BlockLinearOperator< Range, Domain, BlockPayload > block_diagonal_operator(const BlockMatrixType &block_matrix)
std::function< unsigned int()> n_block_rows
BlockLinearOperator< Range, Domain, BlockPayload > block_diagonal_operator(const LinearOperator< typename Range::BlockType, typename Domain::BlockType, typename BlockPayload::BlockType > &op)
BlockLinearOperator< Range, Domain, BlockPayload > block_diagonal_operator(const std::array< LinearOperator< typename Range::BlockType, typename Domain::BlockType, typename BlockPayload::BlockType >, m > &ops)