block_preconditioner.h
Go to the documentation of this file.
1 // LIC// ====================================================================
2 // LIC// This file forms part of oomph-lib, the object-oriented,
3 // LIC// multi-physics finite-element library, available
4 // LIC// at http://www.oomph-lib.org.
5 // LIC//
6 // LIC// Copyright (C) 2006-2022 Matthias Heil and Andrew Hazel
7 // LIC//
8 // LIC// This library is free software; you can redistribute it and/or
9 // LIC// modify it under the terms of the GNU Lesser General Public
10 // LIC// License as published by the Free Software Foundation; either
11 // LIC// version 2.1 of the License, or (at your option) any later version.
12 // LIC//
13 // LIC// This library is distributed in the hope that it will be useful,
14 // LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // LIC// Lesser General Public License for more details.
17 // LIC//
18 // LIC// You should have received a copy of the GNU Lesser General Public
19 // LIC// License along with this library; if not, write to the Free Software
20 // LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 // LIC// 02110-1301 USA.
22 // LIC//
23 // LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24 // LIC//
25 // LIC//====================================================================
26 // Include guards
27 #ifndef OOMPH_BLOCK_PRECONDITION_HEADER
28 #define OOMPH_BLOCK_PRECONDITION_HEADER
29 
30 
31 // Config header generated by autoconfig
32 #ifdef HAVE_CONFIG_H
33 #include <oomph-lib-config.h>
34 #endif
35 
36 // c++ include
37 #include <math.h>
38 #include <typeinfo>
39 
40 // oomph-lib includes
41 #include "matrices.h"
42 #include "mesh.h"
43 #include "vector_matrix.h"
44 
45 // #include "problem.h"
46 #include "preconditioner.h"
47 #include "SuperLU_preconditioner.h"
48 #include "matrix_vector_product.h"
49 
50 namespace oomph
51 {
52  //=============================================================================
122  //=============================================================================
124  {
125  public:
129  {
130  // Needs to be set to zero because if the build function leaves the
131  // Replacement_block_pt alone if replacement_block_pt = 0 (the default
132  // argument).
134  this->build(0, 0, false);
135  }
136 
141  BlockSelector(const unsigned& row_index,
142  const unsigned& column_index,
143  const bool& wanted,
145  {
146 #ifdef PARANOID
147  if ((wanted == false) && (replacement_block_pt != 0))
148  {
149  std::ostringstream err_msg;
150  err_msg << "Trying to construct a BlockSelector object with:\n"
151  << "replacement_block_pt != 0 and wanted == false"
152  << "If you require the block, please set wanted == true.\n";
153  throw OomphLibError(
155  }
156 #endif
157 
158  // Needs to be set to zero because if the build function leaves the
159  // Replacement_block_pt alone if replacement_block_pt = 0 (the default
160  // argument). Thus if it is not set here, it would not be initialised to
161  // null.
163 
164  this->build(row_index, column_index, wanted, replacement_block_pt);
165  }
166 
168  virtual ~BlockSelector()
169  {
170 #ifdef PARANOID
171  if (Replacement_block_pt != 0)
172  {
173  std::ostringstream warning_msg;
174  warning_msg << "Warning: BlockSelector destructor is called but...\n"
175  << "replacement_block_pt() is not null.\n"
176  << "Please remember to null this via the function\n"
177  << "BlockSelector::null_replacement_block_pt()\n"
178  << "Row_index: " << Row_index << "\n"
179  << "Column_index: " << Column_index << std::endl;
180 
183  }
184 #endif
185  }
186 
188  void select_block(const unsigned& row_index,
189  const unsigned& column_index,
190  const bool& wanted,
192  {
193 #ifdef PARANOID
194  if ((wanted == false) && (replacement_block_pt != 0))
195  {
196  std::ostringstream err_msg;
197  err_msg << "Trying to select block with:\n"
198  << "replacement_block_pt != 0 and wanted == false"
199  << "If you require the block, please set wanted == true.\n"
200  << "row_index: " << row_index << "\n"
201  << "column_index: " << column_index << "\n";
202  throw OomphLibError(
204  }
205 #endif
206 
207  this->build(row_index, column_index, wanted, replacement_block_pt);
208  }
209 
210 
212  void want_block()
213  {
214  Wanted = true;
215  }
216 
219  {
220 #ifdef PARANOID
221  if (Replacement_block_pt != 0)
222  {
223  std::ostringstream warning_msg;
224  warning_msg << "Trying to set Wanted = false, but replacement_block_pt "
225  "is not null.\n"
226  << "Please call null_replacement_block_pt()\n"
227  << "(remember to free memory if necessary)\n"
228  << "Row_index: " << Row_index << "\n"
229  << "Column_index: " << Column_index << "\n";
232  }
233 #endif
234 
236 
237  Wanted = false;
238  }
239 
242  {
244  }
245 
248  {
249 #ifdef PARANOID
250  if (Wanted == false)
251  {
252  std::ostringstream err_msg;
253  err_msg << "Trying to set replacement_block_pt, but Wanted == false.\n"
254  << "Please call want_block()\n"
255  << "Row_index: " << Row_index << "\n"
256  << "Column_index: " << Column_index << "\n";
257  throw OomphLibError(
259  }
260 #endif
261 
263  }
264 
267  {
268  return Replacement_block_pt;
269  }
270 
272  void set_row_index(const unsigned& row_index)
273  {
275  }
276 
278  const unsigned& row_index() const
279  {
280  return Row_index;
281  }
282 
284  void set_column_index(const unsigned& column_index)
285  {
287  }
288 
290  const unsigned& column_index() const
291  {
292  return Column_index;
293  }
294 
296  const bool& wanted() const
297  {
298  return Wanted;
299  }
300 
301 
307  friend std::ostream& operator<<(std::ostream& o_stream,
308  const BlockSelector& block_selector)
309  {
310  o_stream << "Row_index = " << block_selector.row_index() << "\n"
311  << "Column_index = " << block_selector.column_index() << "\n"
312  << "Wanted = " << block_selector.wanted() << "\n"
313  << "Replacement_block_pt = ";
314  if (block_selector.replacement_block_pt() == 0)
315  {
316  o_stream << 0;
317  }
318 
319  return o_stream;
320  }
321 
322  private:
326  void build(const unsigned& row_index,
327  const unsigned& column_index,
328  const bool& wanted,
330  {
333  Wanted = wanted;
334 
335  // Only set the replacement_block_pt if it is wanted. Otherwise we leave
336  // it alone. All constructors should set Replacement_block_pt to 0.
337  if (replacement_block_pt != 0)
338  {
339 #ifdef PARANOID
340  if (Wanted == false)
341  {
342  std::ostringstream err_msg;
343  err_msg
344  << "Trying to set replacement_block_pt, but Wanted == false.\n"
345  << "Please either not set the replacement_block_pt or call the "
346  "function\n"
347  << "do_not_want_block()\n"
348  << "Row_index: " << Row_index << "\n"
349  << "Column_index: " << Column_index << "\n";
350  throw OomphLibError(
352  }
353 #endif
354 
356  }
357  }
358 
360  unsigned Row_index;
361 
363  unsigned Column_index;
364 
366  bool Wanted;
367 
370  };
371 
372 
373  //============================================================================
419  //============================================================================
420  template<typename MATRIX>
422  {
423  public:
426  {
427  // Initially set the master block preconditioner pointer to zero
428  // indicating that this is stand-alone preconditioner (i.e. the upper most
429  // level preconditioner) that will set up its own block lookup schemes
430  // etc.
432 
433  // The distribution of the concatenation of the internal block
434  // distributions.
435  // I.e. LinearAlgebraDistributionHelpers::concatenate
436  // (distributions of internal blocks).
437  //
438  // The concatenation of the internal block distributions is stored in two
439  // places depending on if this is the upper-most master block
440  // preconditioner or not.
441  //
442  // If this is a master block preconditioner
443  // (Master_block_preconditioner_pt is null), then it is stored in the
444  // variable Internal_preconditioner_matrix_distribution_pt (below). For
445  // subsidiary block preconditioners, this remains null.
446  //
447  // Because BlockPreconditioners are DistributedLinearAlgebraObjects, they
448  // have a distribution. For the upper-most master block preconditioner,
449  // this is the distribution of the underlying Jacobian.
450  //
451  // For all subsidiary block preconditioners, this remains null. The
452  // concatenation of the distribution of the internal blocks are stored
453  // as the distribution of the BlockPreconditioner.
454  //
455  // This seems inconsistent and I cannot figure out why this is done.
457 
458  // The concatenation of the external block distributions.
460 
461  // Initialise number of rows in this block preconditioner.
462  // This information is maintained if used as subsidiary or
463  // stand-alone block preconditioner (in the latter case it
464  // obviously stores the number of rows within the subsidiary
465  // preconditioner.
466  Nrow = 0;
467 
468  // Initialise number of different block types in this preconditioner.
469  // This information is maintained if used as subsidiary or
470  // stand-alone block preconditioner (in the latter case it
471  // obviously stores the number of rows within the subsidiary
472  // preconditioner.
474 
475  // Initialise number of different dof types in this preconditioner.
476  // This information is maintained if used as subsidiary or
477  // stand-alone block preconditioner (in the latter case it
478  // obviously stores the number of rows within the subsidiary
479  // preconditioner.
481 
482  // There are no blocks to start off with.
483  Block_distribution_pt.resize(0);
484 
485  // The distributions of the underlying internal blocks.
487 
488  // The distribution of the dof-level blocks, these are used during the
489  // concatenation process to create the distribution of the blocks.
490  Dof_block_distribution_pt.resize(0);
491 
492  // Clear both the Block_to_dof_map_coarse and Block_to_dof_map_fine
493  // vectors.
494  Block_to_dof_map_coarse.resize(0);
495  Block_to_dof_map_fine.resize(0);
496 
497  // Default the debug flag to false.
498  Recursive_debug_flag = false;
499 
500  // Default the debug flag to false.
501  Debug_flag = false;
502  } // EOFunc constructor
503 
504 
507  {
509  } // EOFunc destructor
510 
513 
515  void operator=(const BlockPreconditioner&) = delete;
516 
520  MATRIX* matrix_pt() const
521  {
523  {
524  return master_block_preconditioner_pt()->matrix_pt();
525  }
526  else
527  {
528  MATRIX* m_pt = dynamic_cast<MATRIX*>(Preconditioner::matrix_pt());
529 #ifdef PARANOID
530  if (m_pt == 0)
531  {
532  std::ostringstream error_msg;
533  error_msg << "Matrix is not correct type.";
534  throw OomphLibError(
536  }
537 #endif
538  return m_pt;
539  }
540  } // EOFunc matrix_pt()
541 
542 
546  {
547  Recursive_debug_flag = true;
549  this->master_block_preconditioner_pt()->turn_on_recursive_debug_flag();
550  }
551 
555  {
556  Recursive_debug_flag = false;
558  this->master_block_preconditioner_pt()->turn_off_recursive_debug_flag();
559  }
560 
563  {
564  Debug_flag = true;
565  }
566 
569  {
570  Debug_flag = false;
571  }
572 
593  BlockPreconditioner<MATRIX>* master_block_prec_pt,
594  const Vector<unsigned>& doftype_in_master_preconditioner_coarse);
595 
635  BlockPreconditioner<MATRIX>* master_block_prec_pt,
636  const Vector<unsigned>& doftype_in_master_preconditioner_coarse,
637  const Vector<Vector<unsigned>>& doftype_coarsen_map_coarse);
638 
639 
653  virtual void block_setup();
654 
668  void block_setup(const Vector<unsigned>& dof_to_block_map);
669 
673  void get_block(const unsigned& i,
674  const unsigned& j,
675  MATRIX& output_matrix,
676  const bool& ignore_replacement_block = false) const
677  {
678 #ifdef PARANOID
679  // Check the range of i and j, they should not be greater than
680  // nblock_types
681  unsigned n_block_types = this->nblock_types();
682  if ((i > n_block_types) || (j > n_block_types))
683  {
684  std::ostringstream err_msg;
685  err_msg << "Requested block(" << i << "," << j << ")"
686  << "\n"
687  << "but nblock_types() is " << n_block_types << ".\n"
688  << "Maybe your argument to block_setup(...) is incorrect?"
689  << std::endl;
690  throw OomphLibError(
692  }
693 #endif
694 
695  // The logic is this:
696  //
697  // Block_to_dof_map_coarse tells us which dof types belongs in each block.
698  // This is relative to this preconditioner, and describes the external
699  // block and dof type mappings (what the preconditioner writer
700  // expects/sees).
701  //
702  // As such, the dof types in Block_to_dof_map_coarse describes the
703  // dof-level blocks needed to be concatenated to produce block(i,j). These
704  // dofs may have been coarsened.
705  //
706  // Now, the dof blocks to concatenate comes from:
707  // If the dof block exists in Replacement_dof_block_pt, then we make a
708  // deep copy of it. Otherwise, if this is the upper-most master block
709  // preconditioner then we get it from the original matrix via function
710  // internal_get_block(...) otherwise, if this is a subsidiary
711  // block preconditioner, we go one level up the hierarchy and repeat the
712  // process.
713  //
714  //
715  // A small note about indirections which caused me some headache.
716  // Thought I would mention it here in case the below code needs to be
717  // re-visited.
718  //
719  // A small subtlety with indirections:
720  //
721  // The underlying ordering of the dof-level blocks is STILL AND ALWAYS the
722  // `natural' ordering determined by first the elements then the order of
723  // the meshes.
724  //
725  // But during the process of block_setup(...), the external (perceived)
726  // block ordering may have changed. So some indirection has to take place,
727  // this mapping is maintained in Block_to_dof_map_coarse.
728  //
729  // For example, take the Lagrangian preconditioner, which expects the
730  // natural dof type ordering:
731  // 0 1 2 3 4 5
732  // u v p uc vc L
733  //
734  // If the mapping given to block_setup(...) is:
735  // dof_to_block_map = [0, 1, 4, 2, 3, 5]
736  // saying that: dof type 0 goes to block 0
737  // dof type 1 goes to block 1
738  // dof type 2 goes to block 4
739  // dof type 3 goes to block 2
740  // dof type 4 goes to block 3
741  // dof type 5 goes to block 5
742  //
743  // The function block_setup(...) will populate the vector
744  // Block_to_dof_map_coarse with [[0][1][3][4][2][5]],
745  // which says that get_block(0,0) will get the u block
746  // get_block(1,1) will get the v block
747  // get_block(2,2) will get the uc block
748  // get_block(3,3) will get the vc block
749  // get_block(4,4) will get the p block
750  // get_block(5,5) will get the L block
751  //
752  // i.e. the ordering of the block types is a permutation of the dof types,
753  // so that we now have the following block ordering:
754  // 0 1 2 3 4 5 <- block ordering
755  // u v uc vc p L
756  //
757  // Now, the writer expects to work with the block ordering. I.e. when we
758  // replace a dof-level block, say the pressure block, we call
759  // set_replacement_dof_block(4,4,Matrix);
760  // Similarly, when we want the pressure block, we call
761  // get_block(4,4);
762  //
763  // Now, the below code uses the indirection maintained in
764  // Block_to_dof_map_coarse. I.e. When we call get_block(4,4), we use the
765  // mapping Block_to_dof_map_coarse[4] = 2, we get the block (2,2)
766  // (from Replacement_dof_block_pt or internal_get_block), since the
767  // underlying dof_to_block mapping is still the identity, i.e. it has not
768  // changed from:
769  // 0 1 2 3 4 5
770  // u v p uc vc L
771  //
772  // So, block (4,4) (pressure block) maps to the block (2,2).
773 
774  // How many external dof types are in this block?
775  const unsigned ndofblock_in_row = Block_to_dof_map_coarse[i].size();
776  const unsigned ndofblock_in_col = Block_to_dof_map_coarse[j].size();
777 
778  // If there is only one dof block in this block then there is no need to
779  // concatenate.
780  if (ndofblock_in_row == 1 && ndofblock_in_col == 1)
781  {
782  // Get the indirection for the dof we want.
783  const unsigned wanted_dof_row = Block_to_dof_map_coarse[i][0];
784  const unsigned wanted_dof_col = Block_to_dof_map_coarse[j][0];
785 
786  // If the block has NOT been replaced or if we want to ignore the
787  // replacement, then we call get_dof_level_block(...) which will get the
788  // dof-level blocks up the preconditioning hierarchy, dragging down
789  // any replacement dof blocks of parent preconditioners if required.
790  if ((Replacement_dof_block_pt.get(wanted_dof_row, wanted_dof_col) ==
791  0) ||
792  ignore_replacement_block)
793  {
794  get_dof_level_block(wanted_dof_row,
795  wanted_dof_col,
796  output_matrix,
797  ignore_replacement_block);
798  }
799  else
800  // Replacement_dof_block_pt.get(wanted_dof_row,wanted_dof_col) is not
801  // null, this means that the block has been replaced. We simply make
802  // a deep copy of it.
803  {
805  Replacement_dof_block_pt.get(wanted_dof_row, wanted_dof_col),
806  output_matrix);
807  }
808  }
809  else
810  // This block contains more than one dof-level block. So we need to
811  // concatenate the (external) dof-level blocks.
812  {
813  // The CRDoubleMatrixHelpers::concatenate_without_communication(...)
814  // takes a DenseMatrix of pointers to CRDoubleMatrices to concatenate.
815  DenseMatrix<CRDoubleMatrix*> tmp_blocks_pt(
816  ndofblock_in_row, ndofblock_in_col, 0);
817 
818  // Vector of Vectors of unsigns to indicate whether we have created
819  // CRDoubleMatrices with new or not... so we can delete it later.
820  // 0 - no new CRDoubleMatrix is created.
821  // 1 - a new CRDoubleMatrix is created.
822  // If we ever use C++11, remove this and use smart pointers.
823  Vector<Vector<unsigned>> new_block(
824  ndofblock_in_row, Vector<unsigned>(ndofblock_in_col, 0));
825 
826  // Loop through the number of dof block rows and then the number of dof
827  // block columns, either get the pointer from Replacement_dof_block_pt
828  // or from get_dof_level_block(...).
829  for (unsigned row_dofblock = 0; row_dofblock < ndofblock_in_row;
830  row_dofblock++)
831  {
832  // Indirection for the row, as discuss in the large chunk of text
833  // previously.
834  const unsigned wanted_dof_row =
835  Block_to_dof_map_coarse[i][row_dofblock];
836 
837  for (unsigned col_dofblock = 0; col_dofblock < ndofblock_in_col;
838  col_dofblock++)
839  {
840  // Indirection for the column as discussed in the large chunk of
841  // text above.
842  const unsigned wanted_dof_col =
843  Block_to_dof_map_coarse[j][col_dofblock];
844 
845  // Get the pointer from Replacement_dof_block_pt.
846  tmp_blocks_pt(row_dofblock, col_dofblock) =
847  Replacement_dof_block_pt.get(wanted_dof_row, wanted_dof_col);
848 
849  // If the pointer from Replacement_dof_block_pt is null, or if
850  // we have to ignore replacement blocks, build a new CRDoubleMatrix
851  // via get_dof_level_block.
852  if ((tmp_blocks_pt(row_dofblock, col_dofblock) == 0) ||
853  ignore_replacement_block)
854  {
855  // We have to create a new CRDoubleMatrix, so put in 1 to indicate
856  // that we have to delete it later.
857  new_block[row_dofblock][col_dofblock] = 1;
858 
859  // Create the new CRDoubleMatrix. Note that we do not use the
860  // indirection, since the indirection is only used one way.
861  tmp_blocks_pt(row_dofblock, col_dofblock) = new CRDoubleMatrix;
862 
863  // Get the dof-level block, as discussed above.
864  get_dof_level_block(wanted_dof_row,
865  wanted_dof_col,
866  *tmp_blocks_pt(row_dofblock, col_dofblock),
867  ignore_replacement_block);
868  }
869  }
870  }
871 
872  // Concatenation of matrices require the distribution of the individual
873  // sub-matrices (for both row and column). This is because concatenation
874  // is implemented without communication in such a way that the rows
875  // and column values are both permuted, the permutation is defined by
876  // the individual distributions of the sub blocks.
877  // Without a vector of distributions describing the distributions of
878  // of the rows, we do not know how to permute the rows. For the columns,
879  // although CRDoubleMatrices does not have a column distribution, the
880  // concatenated matrix must have it's columns permuted, so we mirror
881  // the diagonal and get the corresponding row distribution.
882  //
883  // Confused? - Example: Say we have a matrix with dof blocking
884  //
885  // | a | b | c | d | e |
886  // --|---|---|---|---|---|
887  // a | | | | | |
888  // --|---|---|---|---|---|
889  // b | | | | | |
890  // --|---|---|---|---|---|
891  // c | | | | | |
892  // --|---|---|---|---|---|
893  // d | | | | | |
894  // --|---|---|---|---|---|
895  // e | | | | | |
896  // --|---|---|---|---|---|
897  //
898  // We wish to concatenate the blocks
899  //
900  // | d | e |
901  // --|---|---|
902  // a | | |
903  // --|---|---|
904  // b | | |
905  // --|---|---|
906  //
907  // Then clearly the row distributions required are the distributions for
908  // the dof blocks a and b. But block(a,d) does not have a column
909  // distribution since it is a CRDoubleMatrix! - We use the distribution
910  // mirrored by the diagonal, so the column distributions required to
911  // concatenate these blocks is the same as the distributions of the rows
912  // for dof block d and e.
913 
914  // First we do the row distribution.
915 
916  // Storage for the row distribution pointers.
917  Vector<LinearAlgebraDistribution*> tmp_row_dist_pt(ndofblock_in_row, 0);
918 
919  // Loop through the number of dof blocks in the row. For the upper-most
920  // master block preconditioner, the external dof distributions is the
921  // same as the internal BLOCK distributions. Recall what we said above
922  // about the underlying blocks maintaining it's natural ordering.
923  //
924  // If this is a subsidiary block preconditioner, then the distributions
925  // for the dof blocks are stored in Dof_block_distribution_pt. The
926  // reason why this is different for subsidiary block preconditioners is
927  // because subsidiary block preconditioners would have it's dof types
928  // coarsened. Then a single dof distribution in a subsidiary block
929  // preconditioner could be a concatenation of many dof distributions of
930  // the master dof distributions.
931  for (unsigned row_dof_i = 0; row_dof_i < ndofblock_in_row; row_dof_i++)
932  {
933  const unsigned mapped_dof_i = Block_to_dof_map_coarse[i][row_dof_i];
935  {
936  tmp_row_dist_pt[row_dof_i] =
937  Internal_block_distribution_pt[mapped_dof_i];
938  }
939  else
940  {
941  tmp_row_dist_pt[row_dof_i] =
942  Dof_block_distribution_pt[mapped_dof_i];
943  }
944  }
945 
946  // Storage for the column distribution pointers.
947  Vector<LinearAlgebraDistribution*> tmp_col_dist_pt(ndofblock_in_col, 0);
948 
949  // We do the same thing as before.
950  for (unsigned col_dof_i = 0; col_dof_i < ndofblock_in_col; col_dof_i++)
951  {
952  const unsigned mapped_dof_j = Block_to_dof_map_coarse[j][col_dof_i];
954  {
955  tmp_col_dist_pt[col_dof_i] =
956  Internal_block_distribution_pt[mapped_dof_j];
957  }
958  else
959  {
960  tmp_col_dist_pt[col_dof_i] =
961  Dof_block_distribution_pt[mapped_dof_j];
962  }
963  }
964 
965  // Perform the concatenation.
967  tmp_row_dist_pt, tmp_col_dist_pt, tmp_blocks_pt, output_matrix);
968 
969  // Delete any new CRDoubleMatrices we have created.
970  for (unsigned row_i = 0; row_i < ndofblock_in_row; row_i++)
971  {
972  for (unsigned col_i = 0; col_i < ndofblock_in_col; col_i++)
973  {
974  if (new_block[row_i][col_i])
975  {
976  delete tmp_blocks_pt(row_i, col_i);
977  }
978  }
979  }
980  } // else need to concatenate
981  } // EOFunc get_block(...)
982 
983 
988  MATRIX get_block(const unsigned& i,
989  const unsigned& j,
990  const bool& ignore_replacement_block = false) const
991  {
992  MATRIX output_matrix;
993  get_block(i, j, output_matrix, ignore_replacement_block);
994  return output_matrix;
995  } // EOFunc get_block(...)
996 
998  void set_master_matrix_pt(MATRIX* in_matrix_pt)
999  {
1001  {
1002  master_block_preconditioner_pt()->set_master_matrix_pt(in_matrix_pt);
1003  }
1004  else
1005  {
1006  this->set_matrix_pt(in_matrix_pt);
1007  }
1008  }
1009 
1012  void get_block_other_matrix(const unsigned& i,
1013  const unsigned& j,
1014  MATRIX* in_matrix_pt,
1015  MATRIX& output_matrix)
1016  {
1017  MATRIX* backup_matrix_pt = matrix_pt();
1018  set_master_matrix_pt(in_matrix_pt);
1019  get_block(i, j, output_matrix, true);
1020  set_master_matrix_pt(backup_matrix_pt);
1021  } // EOFunc get_block_other_matrix(...)
1022 
1038  void get_blocks(DenseMatrix<bool>& required_blocks,
1039  DenseMatrix<MATRIX*>& block_matrix_pt) const;
1040 
1050  const unsigned& i,
1051  const unsigned& j,
1052  MATRIX& output_block,
1053  const bool& ignore_replacement_block = false) const;
1054 
1055 
1135  const VectorMatrix<BlockSelector>& selected_block)
1136  {
1137 #ifdef PARANOID
1138 
1139  unsigned const para_selected_block_nrow = selected_block.nrow();
1140  unsigned const para_selected_block_ncol = selected_block.ncol();
1141  unsigned const para_nblocks = this->nblock_types();
1142 
1143  // Check that selected_block size is not 0.
1144  if (para_selected_block_nrow == 0)
1145  {
1146  std::ostringstream error_msg;
1147  error_msg << "selected_block has nrow = 0.\n";
1148  throw OomphLibError(
1150  }
1151 
1152  // Check that the number of blocks is not outside of the range
1153  // nblock_types(). Since this function builds matrices for block
1154  // preconditioning, it does not make sense for us to concatenate more
1155  // blocks than nblock_types().
1156  if ((para_selected_block_nrow > para_nblocks) ||
1157  (para_selected_block_ncol > para_nblocks))
1158  {
1159  std::ostringstream error_msg;
1160  error_msg << "Trying to concatenate a " << para_selected_block_nrow
1161  << " by " << para_selected_block_ncol << " block matrix,\n"
1162  << "but there are only " << para_nblocks << " block types.\n";
1163  throw OomphLibError(
1165  }
1166 
1167  // Check that selected_block make sense, i.e. the row indices of each row
1168  // are the same, and the column indices of each column are the same.
1169 
1170  // First check if the row indices are consistent.
1171  // For each row, loop through the columns, comparing the row index against
1172  // the first column.
1173  for (unsigned row_i = 0; row_i < para_selected_block_nrow; row_i++)
1174  {
1175  const unsigned col_0_row_index = selected_block[row_i][0].row_index();
1176 
1177  for (unsigned col_i = 0; col_i < para_selected_block_ncol; col_i++)
1178  {
1179  const unsigned para_b_i = selected_block[row_i][col_i].row_index();
1180  const unsigned para_b_j = selected_block[row_i][col_i].column_index();
1181 
1182  if (col_0_row_index != para_b_i)
1183  {
1184  std::ostringstream err_msg;
1185  err_msg << "Block index for block(" << row_i << "," << col_i << ") "
1186  << "contains block indices (" << para_b_i << "," << para_b_j
1187  << ").\n"
1188  << "But the row index for the first column is "
1189  << col_0_row_index << ", they must be the same!\n";
1190  throw OomphLibError(
1192  }
1193  }
1194  }
1195 
1196  // Do the same for the column indices, consistency check.
1197  // For each column, loop through the rows, comparing the column index
1198  // against the first row.
1199  for (unsigned col_i = 0; col_i < para_selected_block_ncol; col_i++)
1200  {
1201  const unsigned row_0_col_index =
1202  selected_block[0][col_i].column_index();
1203 
1204  for (unsigned row_i = 0; row_i < para_selected_block_nrow; row_i++)
1205  {
1206  const unsigned para_b_i = selected_block[row_i][col_i].row_index();
1207  const unsigned para_b_j = selected_block[row_i][col_i].column_index();
1208 
1209  if (row_0_col_index != para_b_j)
1210  {
1211  std::ostringstream err_msg;
1212  err_msg << "Block index for block(" << row_i << "," << col_i << ") "
1213  << "contains block indices (" << para_b_i << "," << para_b_j
1214  << ").\n"
1215  << "But the col index for the first row is "
1216  << row_0_col_index << ", they must be the same!\n";
1217  throw OomphLibError(
1219  }
1220  }
1221  }
1222 
1223  // Check to see if the values in selected_block is within the range
1224  // nblock_types()
1225  //
1226  // Since we know that the column and row indices are consistent (by the
1227  // two paranoia checks above), we only need to check the column indices
1228  // in the first row, and the row indices in the first column.
1229 
1230  // Check that the row indices in the first column are within the range
1231  // nblock_types()
1232  for (unsigned row_i = 0; row_i < para_selected_block_nrow; row_i++)
1233  {
1234  const unsigned para_b_i = selected_block[row_i][0].row_index();
1235  const unsigned para_b_j = selected_block[row_i][0].column_index();
1236  if (para_b_i > para_nblocks)
1237  {
1238  std::ostringstream err_msg;
1239  err_msg << "Block index for block(" << row_i << ",0) "
1240  << "contains block indices (" << para_b_i << "," << para_b_j
1241  << ").\n"
1242  << "But there are only " << para_nblocks
1243  << " nblock_types().\n";
1244  throw OomphLibError(
1246  }
1247  }
1248 
1249  // Check that the col indices in the first row are within the range
1250  // nblock_types()
1251  for (unsigned col_i = 0; col_i < para_selected_block_ncol; col_i++)
1252  {
1253  const unsigned para_b_i = selected_block[0][col_i].row_index();
1254  const unsigned para_b_j = selected_block[0][col_i].column_index();
1255  if (para_b_j > para_nblocks)
1256  {
1257  std::ostringstream err_msg;
1258  err_msg << "Block index for block(0," << col_i << ") "
1259  << "contains block indices (" << para_b_i << "," << para_b_j
1260  << ").\n"
1261  << "But there are only " << para_nblocks
1262  << " nblock_types().\n";
1263  throw OomphLibError(
1265  }
1266  }
1267 
1268  // Stricter test - can be removed is required in the future. For the first
1269  // column, check that the row indices does not repeat.
1270  std::set<unsigned> row_index_set;
1271  for (unsigned row_i = 0; row_i < para_selected_block_nrow; row_i++)
1272  {
1273  std::pair<std::set<unsigned>::iterator, bool> row_index_set_ret;
1274 
1275  const unsigned row_i_index = selected_block[row_i][0].row_index();
1276 
1277  row_index_set_ret = row_index_set.insert(row_i_index);
1278 
1279  if (!row_index_set_ret.second)
1280  {
1281  std::ostringstream err_msg;
1282  err_msg << "The row " << row_i_index << " is already inserted.\n";
1283  throw OomphLibError(
1285  }
1286  }
1287 
1288  // Stricter test - can be removed is required in the future. For the first
1289  // row, check that the column indices does not repeat.
1290  std::set<unsigned> col_index_set;
1291  for (unsigned col_i = 0; col_i < para_selected_block_ncol; col_i++)
1292  {
1293  std::pair<std::set<unsigned>::iterator, bool> col_index_set_ret;
1294 
1295  const unsigned col_i_index = selected_block[0][col_i].column_index();
1296 
1297  col_index_set_ret = col_index_set.insert(col_i_index);
1298 
1299  if (!col_index_set_ret.second)
1300  {
1301  std::ostringstream err_msg;
1302  err_msg << "The col " << col_i_index << " is already inserted.\n";
1303  throw OomphLibError(
1305  }
1306  }
1307 
1308  // Loop through all the block_pt and check:
1309  // 1) The non-null pointers point to built matrices.
1310  // 2) The distribution matches those defined by selected_block within
1311  // Block_distribution_pt.
1312  for (unsigned block_i = 0; block_i < para_selected_block_nrow; block_i++)
1313  {
1314  for (unsigned block_j = 0; block_j < para_selected_block_ncol;
1315  block_j++)
1316  {
1317  const CRDoubleMatrix* tmp_block_pt =
1318  selected_block[block_i][block_j].replacement_block_pt();
1319 
1320  if (tmp_block_pt != 0)
1321  {
1322  if (!tmp_block_pt->built())
1323  {
1324  std::ostringstream err_msg;
1325  err_msg << "The matrix pointed to by block(" << block_i << ","
1326  << block_j << ") is not built.\n";
1327  throw OomphLibError(err_msg.str(),
1330  }
1331 
1332  const LinearAlgebraDistribution* const tmp_block_dist_pt =
1333  tmp_block_pt->distribution_pt();
1334 
1335  const unsigned row_selected_block =
1336  selected_block[block_i][block_j].row_index();
1337 
1338  const LinearAlgebraDistribution* const another_tmp_block_dist_pt =
1339  block_distribution_pt(row_selected_block);
1340 
1341  if (*tmp_block_dist_pt != *another_tmp_block_dist_pt)
1342  {
1343  std::ostringstream err_msg;
1344  err_msg << "block_distribution_pt " << row_selected_block << "\n"
1345  << "does not match the distribution from the block_pt() "
1346  << " in selected_block[" << block_i << "][" << block_j
1347  << "].\n";
1348  throw OomphLibError(err_msg.str(),
1351  }
1352  }
1353  }
1354  }
1355 
1356  // Attempt a similar check for the column index. This is not as rigorous
1357  // since a CRDoubleMatrix does not have a distribution for the columns.
1358  // However, we can check if the ncol of the matrices in block_pt matches
1359  // those in the block_distribution_pt corresponding to the columns.
1360  // (I hope this makes sense... both the row and columns are permuted in
1361  // CRDoubleMatrixHelpers::concatenate_without_communication(...))
1362  //
1363  // The test for the row distributions checks if the nrow_local is correct.
1364  // We do not have the equivalent for columns.
1365  for (unsigned block_i = 0; block_i < para_selected_block_nrow; block_i++)
1366  {
1367  for (unsigned block_j = 0; block_j < para_selected_block_ncol;
1368  block_j++)
1369  {
1370  // Cache the block_pt
1371  const CRDoubleMatrix* tmp_block_pt =
1372  selected_block[block_i][block_j].replacement_block_pt();
1373 
1374  if (tmp_block_pt != 0)
1375  {
1376  const unsigned tmp_block_ncol = tmp_block_pt->ncol();
1377 
1378  const unsigned selected_block_col =
1379  selected_block[block_i][block_j].column_index();
1380 
1381  // YES, nrow, this is not incorrect.
1382  const unsigned another_tmp_block_ncol =
1383  block_distribution_pt(selected_block_col)->nrow();
1384 
1385  if (tmp_block_ncol != another_tmp_block_ncol)
1386  {
1387  std::ostringstream err_msg;
1388  err_msg << "block_pt in selected_block[" << block_i << "]["
1389  << block_j << "] "
1390  << "has ncol = " << tmp_block_ncol << ".\n"
1391  << "But the corresponding block_distribution_pt("
1392  << selected_block_col
1393  << ") has nrow = " << another_tmp_block_ncol << ").\n";
1394  throw OomphLibError(err_msg.str(),
1397  }
1398  }
1399  }
1400  }
1401 #endif
1402 
1403  // The return matrix.
1404  MATRIX output_matrix;
1405 
1406  // How many sub matrices are there in the row and column?
1407  const unsigned nblock_row = selected_block.nrow();
1408  const unsigned nblock_col = selected_block.ncol();
1409 
1410  // Get the row and col distributions, this is required for concatenation
1411  // without communication.
1412  Vector<LinearAlgebraDistribution*> row_dist_pt(nblock_row, 0);
1413  Vector<LinearAlgebraDistribution*> col_dist_pt(nblock_col, 0);
1414 
1415  // For the row distributions, use the first column of selected_block
1416  // Also, store the index of the block rows.
1417  Vector<unsigned> block_row_index(nblock_row, 0);
1418  for (unsigned row_i = 0; row_i < nblock_row; row_i++)
1419  {
1420  const unsigned selected_row_index =
1421  selected_block[row_i][0].row_index();
1422 
1423  row_dist_pt[row_i] = Block_distribution_pt[selected_row_index];
1424  block_row_index[row_i] = selected_row_index;
1425  }
1426 
1427  // For the col distributions, use the first row of selected_block
1428  Vector<unsigned> block_col_index(nblock_col, 0);
1429  for (unsigned col_i = 0; col_i < nblock_col; col_i++)
1430  {
1431  const unsigned selected_col_index =
1432  selected_block[0][col_i].column_index();
1433 
1434  col_dist_pt[col_i] = Block_distribution_pt[selected_col_index];
1435  block_col_index[col_i] = selected_col_index;
1436  }
1437 
1438  // Now build the output matrix. The output_matrix needs a distribution,
1439  // this distribution is a concatenation of the block rows. But because
1440  // concatenation of distributions requires communication, we try to
1441  // minimise this process by creating it once, then store a key to the
1442  // concatenated distribution. First check to see if the block row indices
1443  // is already a key in Auxiliary_block_distribution_pt, if it is in there,
1444  // we use the distribution it corresponds to. Otherwise, we create the
1445  // distribution and store it for possible further use.
1446  std::map<Vector<unsigned>, LinearAlgebraDistribution*>::const_iterator
1447  iter;
1448 
1449  iter = Auxiliary_block_distribution_pt.find(block_row_index);
1450 
1451  if (iter != Auxiliary_block_distribution_pt.end())
1452  {
1453  output_matrix.build(iter->second);
1454  }
1455  else
1456  {
1459  *tmp_dist_pt);
1460  insert_auxiliary_block_distribution(block_row_index, tmp_dist_pt);
1461  output_matrix.build(tmp_dist_pt);
1462  }
1463 
1464  // Do the same for the column dist, since we might need it for the RHS
1465  // vector..
1466  iter = Auxiliary_block_distribution_pt.find(block_col_index);
1467  if (iter == Auxiliary_block_distribution_pt.end())
1468  {
1471  *tmp_dist_pt);
1472  insert_auxiliary_block_distribution(block_col_index, tmp_dist_pt);
1473  }
1474 
1475  // Storage for the pointers to CRDoubleMatrices to concatenate.
1476  DenseMatrix<CRDoubleMatrix*> block_pt(nblock_row, nblock_col, 0);
1477 
1478  // Vector of Vectors of unsigns to indicate whether we have created
1479  // CRDoubleMatrices with new or not... so we can delete it later.
1480  // 0 - no new CRDoubleMatrix is created.
1481  // 1 - a new CRDoubleMatrix is created.
1482  // If we ever use C++11, remove this and use smart pointers.
1483  Vector<Vector<unsigned>> new_block(nblock_row,
1484  Vector<unsigned>(nblock_col, 0));
1485 
1486  // Get blocks if wanted.
1487  for (unsigned block_i = 0; block_i < nblock_row; block_i++)
1488  {
1489  for (unsigned block_j = 0; block_j < nblock_col; block_j++)
1490  {
1491  const bool block_wanted = selected_block[block_i][block_j].wanted();
1492 
1493  if (block_wanted)
1494  {
1495  CRDoubleMatrix* tmp_block_pt =
1496  selected_block[block_i][block_j].replacement_block_pt();
1497 
1498  if (tmp_block_pt == 0)
1499  {
1500  new_block[block_i][block_j] = 1;
1501 
1502  block_pt(block_i, block_j) = new CRDoubleMatrix;
1503 
1504  // temp variables for readability purposes.
1505  const unsigned tmp_block_i = block_row_index[block_i];
1506  const unsigned tmp_block_j = block_col_index[block_j];
1507 
1508  // Get the block.
1509  this->get_block(
1510  tmp_block_i, tmp_block_j, *block_pt(block_i, block_j));
1511  }
1512  else
1513  {
1514  block_pt(block_i, block_j) = tmp_block_pt;
1515  }
1516  }
1517  }
1518  }
1519 
1520  // Perform the concatenation.
1522  row_dist_pt, col_dist_pt, block_pt, output_matrix);
1523 
1524  // Delete any new CRDoubleMatrices we created.
1525  for (unsigned block_i = 0; block_i < nblock_row; block_i++)
1526  {
1527  for (unsigned block_j = 0; block_j < nblock_col; block_j++)
1528  {
1529  if (new_block[block_i][block_j])
1530  {
1531  delete block_pt(block_i, block_j);
1532  }
1533  }
1534  }
1535 
1536  return output_matrix;
1537  } // EOFunc get_concatenated_block(...)
1538 
1546  void get_concatenated_block_vector(const Vector<unsigned>& block_vec_number,
1547  const DoubleVector& v,
1548  DoubleVector& b);
1549 
1558  const Vector<unsigned>& block_vec_number,
1559  const DoubleVector& b,
1560  DoubleVector& v) const;
1561 
1570  void get_block_vectors(const Vector<unsigned>& block_vec_number,
1571  const DoubleVector& v,
1572  Vector<DoubleVector>& s) const;
1573 
1584  void get_block_vectors(const DoubleVector& v,
1585  Vector<DoubleVector>& s) const;
1586 
1593  void return_block_vectors(const Vector<unsigned>& block_vec_number,
1594  const Vector<DoubleVector>& s,
1595  DoubleVector& v) const;
1596 
1607  DoubleVector& v) const;
1608 
1612  void get_block_vector(const unsigned& n,
1613  const DoubleVector& v,
1614  DoubleVector& b) const;
1615 
1622  void return_block_vector(const unsigned& n,
1623  const DoubleVector& b,
1624  DoubleVector& v) const;
1625 
1652  DoubleVector& w);
1653 
1667  DoubleVector& v) const;
1668 
1670  unsigned nblock_types() const
1671  {
1672 #ifdef PARANOID
1673  if (Block_to_dof_map_coarse.size() == 0)
1674  {
1675  std::ostringstream error_msg;
1676  error_msg
1677  << "The Block_to_dof_map_coarse vector is not setup for \n"
1678  << "this block preconditioner.\n\n"
1679 
1680  << "This vector is always set up within block_setup(...).\n"
1681  << "If block_setup() is already called, then perhaps there is\n"
1682  << "something wrong with your block preconditionable elements.\n"
1683  << std::endl;
1684  throw OomphLibError(
1686  }
1687 #endif
1688 
1689  // Return the number of block types.
1690  return Block_to_dof_map_coarse.size();
1691  } // EOFunc nblock_types(...)
1692 
1694  unsigned ndof_types() const
1695  {
1696 #ifdef PARANOID
1697  // Subsidiary preconditioners don't really need the meshes
1698  if (this->is_master_block_preconditioner())
1699  {
1700  std::ostringstream err_msg;
1701  unsigned n = nmesh();
1702  if (n == 0)
1703  {
1704  err_msg << "No meshes have been set for this block preconditioner!\n"
1705  << "Set one with set_nmesh(...), set_mesh(...)" << std::endl;
1706  throw OomphLibError(
1708  for (unsigned m = 0; m < n; m++)
1709  {
1710  if (Mesh_pt[m] == 0)
1711  {
1712  err_msg << "The mesh pointer to mesh " << m << " is null!\n"
1713  << "Set a non-null one with set_mesh(...)" << std::endl;
1714  throw OomphLibError(err_msg.str(),
1717  }
1718  }
1719  }
1720  }
1721 #endif
1722 
1723  // If this is a subsidiary block preconditioner, then the function
1724  // turn_into_subsidiary_block_preconditioner(...) should have been called,
1725  // this function would have set up the look up lists between coarsened
1726  // dof types and the internal dof types. Of coarse, the user (the writer
1727  // of the preconditioners) should not care about the internal dof types
1728  // and what happens under the hood. Thus they should get the number of
1729  // coarsened dof types (i.e. the number of dof types the preconditioner
1730  // above (parent preconditioner) decides to give to this preconditioner).
1732  {
1733 #ifdef PARANOID
1734  if (Doftype_coarsen_map_coarse.size() == 0)
1735  {
1736  std::ostringstream error_msg;
1737  error_msg
1738  << "The Doftype_coarsen_map_coarse vector is not setup for \n"
1739  << "this SUBSIDIARY block preconditioner.\n\n"
1740 
1741  << "For SUBSIDARY block preconditioners at any level, this\n"
1742  << "vector is set up in the function \n"
1743  << "turn_into_subsidiary_block_preconditioner(...).\n\n"
1744 
1745  << "Being a SUBSIDIARY block preconditioner means that \n"
1746  << "(Master_block_preconditioner_pt == 0) is true.\n"
1747  << "The Master_block_preconditioner_pt MUST be set in the "
1748  << "function \n"
1749  << "turn_into_subsidiary_block_preconditioner(...).\n\n"
1750 
1751  << "Somewhere, the Master_block_preconditioner_pt pointer is\n"
1752  << "set but not by the function\n"
1753  << "turn_into_subsidiary_block_preconditioner(...).\n"
1754  << std::endl;
1755  throw OomphLibError(
1757  }
1758 #endif
1759  // return the number of dof types.
1760  return Doftype_coarsen_map_coarse.size();
1761  }
1762  else
1763  // Otherwise the number of ndof types is the same as the internal number
1764  // of dof types, since no coarsening of the dof types is done at the
1765  // top-most master level.
1766  {
1767  return internal_ndof_types();
1768  }
1769  } // EOFunc ndof_types(...)
1770 
1771 
1782  const Mesh* mesh_pt(const unsigned& i) const
1783  {
1784 #ifdef PARANOID
1786  {
1787  std::ostringstream error_msg;
1788  error_msg << "The mesh_pt() function should not be called on a\n"
1789  << "subsidiary block preconditioner." << std::endl;
1790  throw OomphLibError(
1792  }
1793 #endif
1794 
1795  const Mesh* mesh_i_pt = Mesh_pt[i];
1796 
1797 #ifdef PARANOID
1798  if (mesh_i_pt == 0)
1799  {
1800  std::ostringstream error_msg;
1801  error_msg << "Mesh pointer " << mesh_i_pt << " is null.";
1802  throw OomphLibError(
1804  }
1805 #endif
1806 
1807  return mesh_i_pt;
1808  } // EOFunc mesh_pt(...)
1809 
1816  unsigned nmesh() const
1817  {
1818  return Mesh_pt.size();
1819  } // EOFunc nmesh()
1820 
1822  int block_number(const unsigned& i_dof) const
1823  {
1824  int internal_block_number = this->internal_block_number(i_dof);
1825 
1826  if (internal_block_number == -1)
1827  {
1828  return internal_block_number;
1829  }
1830  else
1831  {
1832  // Map the internal block to the "external" block number, i.e. what the
1833  // writer of the preconditioner is expects.
1834  unsigned block_i = 0;
1835  while (std::find(Block_to_dof_map_fine[block_i].begin(),
1836  Block_to_dof_map_fine[block_i].end(),
1838  Block_to_dof_map_fine[block_i].end())
1839  {
1840  block_i++;
1841  }
1842 
1843  return block_i;
1844  }
1845  }
1846 
1850  int index_in_block(const unsigned& i_dof) const
1851  {
1852  // the dof block number
1853  int internal_dof_block_number = this->internal_dof_number(i_dof);
1854 
1855  if (internal_dof_block_number >= 0)
1856  {
1857  // the external block number
1858  unsigned ex_blk_number = this->block_number(i_dof);
1859 
1860  int internal_index_in_dof = this->internal_index_in_dof(i_dof);
1861 
1862  // find the processor which this global index in block belongs to.
1863  unsigned block_proc =
1864  internal_block_distribution_pt(internal_dof_block_number)
1866 
1867  // Add up all of the first rows.
1868  const unsigned ndof_in_block =
1869  Block_to_dof_map_fine[ex_blk_number].size();
1870 
1871  unsigned index = 0;
1872  for (unsigned dof_i = 0; dof_i < ndof_in_block; dof_i++)
1873  {
1875  Block_to_dof_map_fine[ex_blk_number][dof_i])
1876  ->first_row(block_proc);
1877  }
1878 
1879  // Now add up all the nrow_local up to this dof block.
1880  unsigned j = 0;
1881 
1882  while (int(Block_to_dof_map_fine[ex_blk_number][j]) !=
1883  internal_dof_block_number)
1884  {
1886  Block_to_dof_map_fine[ex_blk_number][j])
1887  ->nrow_local(block_proc);
1888  j++;
1889  }
1890 
1891  // Now add the index of this block...
1892  index += (internal_index_in_dof -
1893  internal_block_distribution_pt(internal_dof_block_number)
1894  ->first_row(block_proc));
1895 
1896  return index;
1897  }
1898 
1899  return -1;
1900  }
1901 
1904  const unsigned& b) const
1905  {
1906 #ifdef PARANOID
1907  if (Block_distribution_pt.size() == 0)
1908  {
1909  std::ostringstream error_msg;
1910  error_msg << "Block distributions are not set up.\n"
1911  << "Have you called block_setup(...)?\n"
1912  << std::endl;
1913  throw OomphLibError(
1915  }
1916  if (b > nblock_types())
1917  {
1918  std::ostringstream error_msg;
1919  error_msg << "You requested the distribution for the block " << b
1920  << ".\n"
1921  << "But there are only " << nblock_types()
1922  << " block types.\n"
1923  << std::endl;
1924  throw OomphLibError(
1926  }
1927 #endif
1928 
1929  return Block_distribution_pt[b];
1930  } // EOFunc block_distribution_pt(...)
1931 
1934  {
1935 #ifdef PARANOID
1936  if (Block_distribution_pt.size() == 0)
1937  {
1938  std::ostringstream error_msg;
1939  error_msg << "Block distributions are not set up.\n"
1940  << "Have you called block_setup(...)?\n"
1941  << std::endl;
1942  throw OomphLibError(
1944  }
1945  if (b > nblock_types())
1946  {
1947  std::ostringstream error_msg;
1948  error_msg << "You requested the distribution for the block " << b
1949  << ".\n"
1950  << "But there are only " << nblock_types()
1951  << " block types.\n"
1952  << std::endl;
1953  throw OomphLibError(
1955  }
1956 #endif
1957 
1958  return Block_distribution_pt[b];
1959  } // EOFunc block_distribution_pt(...)
1960 
1963  {
1964 #ifdef PARANOID
1965  if (b > ndof_types())
1966  {
1967  std::ostringstream error_msg;
1968  error_msg << "You requested the distribution for the dof block " << b
1969  << ".\n"
1970  << "But there are only " << ndof_types() << " DOF types.\n"
1971  << std::endl;
1972  throw OomphLibError(
1974  }
1975 
1976 #endif
1977 
1979  {
1980 #ifdef PARANOID
1981  if (Internal_block_distribution_pt.size() == 0)
1982  {
1983  std::ostringstream error_msg;
1984  error_msg << "Internal block distributions are not set up.\n"
1985  << "Have you called block_setup(...)?\n"
1986  << std::endl;
1987  throw OomphLibError(
1989  }
1990 #endif
1991 
1992  // The dof block is distribution is the same as the internal
1993  // block distribution.
1995  }
1996  else
1997  {
1998 #ifdef PARANOID
1999  if (Dof_block_distribution_pt.size() == 0)
2000  {
2001  std::ostringstream error_msg;
2002  error_msg << "Dof block distributions are not set up.\n"
2003  << "Have you called block_setup(...)?\n"
2004  << std::endl;
2005  throw OomphLibError(
2007  }
2008 #endif
2009  return Dof_block_distribution_pt[b];
2010  }
2011  } // EOFunc block_distribution_pt(...)
2012 
2013 
2018  {
2020  {
2021  return this->distribution_pt();
2022  }
2023  else
2024  {
2025  return Master_block_preconditioner_pt->master_distribution_pt();
2026  }
2027  } // EOFunc master_distribution_pt(...)
2028 
2037  unsigned ndof_types_in_mesh(const unsigned& i) const
2038  {
2039 #ifdef PARANOID
2041  {
2042  std::ostringstream err_msg;
2043  err_msg << "A subsidiary block preconditioner should not care about\n"
2044  << "anything to do with meshes.";
2045  throw OomphLibError(
2047  }
2048 #endif
2049  if (Ndof_types_in_mesh.size() == 0)
2050  {
2051  return mesh_pt(i)->ndof_types();
2052  }
2053  else
2054  {
2055  return Ndof_types_in_mesh[i];
2056  }
2057  } // EOFunc ndof_types_in_mesh(...)
2058 
2062  {
2063  return (this->Master_block_preconditioner_pt != 0);
2064  } // EOFunc is_subsidiary_block_preconditioner()
2065 
2069  {
2070  return (this->Master_block_preconditioner_pt == 0);
2071  } // EOFunc is_master_block_preconditioner()
2072 
2076  void set_block_output_to_files(const std::string& basefilename)
2077  {
2078  Output_base_filename = basefilename;
2079  } // EOFunc set_block_output_to_files(...)
2080 
2083  {
2084  Output_base_filename.clear();
2085  } // EOFunc disable_block_output_to_files()
2086 
2088  bool block_output_on() const
2089  {
2090  return Output_base_filename.size() > 0;
2091  } // EOFunc block_output_on()
2092 
2095  void output_blocks_to_files(const std::string& basefilename,
2096  const unsigned& precision = 8) const
2097  {
2098  unsigned nblocks = internal_nblock_types();
2099 
2100  for (unsigned i = 0; i < nblocks; i++)
2101  {
2102  for (unsigned j = 0; j < nblocks; j++)
2103  {
2104  // Construct the filename.
2105  std::string filename(basefilename + "_block_" +
2108 
2109  // Write out the block.
2110  get_block(i, j).sparse_indexed_output(filename, precision, true);
2111  }
2112  }
2113  } // EOFunc output_blocks_to_files(...)
2114 
2121  {
2123  {
2124  Index_in_dof_block_dense.clear();
2125  Dof_number_dense.clear();
2126 #ifdef OOMPH_HAS_MPI
2127  Index_in_dof_block_sparse.clear();
2128  Dof_number_sparse.clear();
2129  Global_index_sparse.clear();
2130  Index_in_dof_block_sparse.clear();
2131  Dof_number_sparse.clear();
2132 #endif
2133  Dof_dimension.clear();
2134  }
2135  Ndof_in_block.clear();
2138  } // EOFunc post_block_matrix_assembly_partial_clear()
2139 
2142  {
2143 #ifdef PARANOID
2145  {
2146  std::ostringstream error_message;
2147  error_message << "This block preconditioner does not have "
2148  << "a master preconditioner.";
2149  throw OomphLibError(error_message.str(),
2152  }
2153 #endif
2155  } // EOFunc master_block_preconditioner_pt()
2156 
2160  {
2161  Replacement_dof_block_pt.clear();
2162 
2163  // clear the Distributions
2164  this->clear_distribution();
2165  unsigned nblock = Internal_block_distribution_pt.size();
2166  for (unsigned b = 0; b < nblock; b++)
2167  {
2169  }
2171 
2172  // clear the global index
2173  Global_index.clear();
2174 
2175  // call the post block matrix assembly clear
2177 
2178 #ifdef OOMPH_HAS_MPI
2179  // storage if the matrix is distributed
2180  unsigned nr = Rows_to_send_for_get_block.nrow();
2181  unsigned nc = Rows_to_send_for_get_block.ncol();
2182  for (unsigned p = 0; p < nc; p++)
2183  {
2184  delete[] Rows_to_send_for_get_ordered[p];
2185  delete[] Rows_to_recv_for_get_ordered[p];
2186  for (unsigned b = 0; b < nr; b++)
2187  {
2188  delete[] Rows_to_recv_for_get_block(b, p);
2189  delete[] Rows_to_send_for_get_block(b, p);
2190  }
2191  }
2192  Rows_to_recv_for_get_block.resize(0, 0);
2193  Nrows_to_recv_for_get_block.resize(0, 0);
2194  Rows_to_send_for_get_block.resize(0, 0);
2195  Nrows_to_send_for_get_block.resize(0, 0);
2196  Rows_to_recv_for_get_ordered.clear();
2197  Nrows_to_recv_for_get_ordered.clear();
2198  Rows_to_send_for_get_ordered.clear();
2199  Nrows_to_send_for_get_ordered.clear();
2200 
2201 #endif
2202 
2203  // zero
2205  {
2206  Nrow = 0;
2207  Internal_ndof_types = 0;
2209  }
2210 
2211  // delete the prec matrix dist pt
2216 
2217  // Delete any existing (external) block distributions.
2218  const unsigned n_existing_block_dist = Block_distribution_pt.size();
2219  for (unsigned dist_i = 0; dist_i < n_existing_block_dist; dist_i++)
2220  {
2221  delete Block_distribution_pt[dist_i];
2222  }
2223 
2224  // Clear the vector.
2225  Block_distribution_pt.clear();
2226 
2227 
2228  // Create the identity key.
2229  Vector<unsigned> preconditioner_matrix_key(n_existing_block_dist, 0);
2230  for (unsigned i = 0; i < n_existing_block_dist; i++)
2231  {
2232  preconditioner_matrix_key[i] = i;
2233  }
2234 
2235  // Now iterate through Auxiliary_block_distribution_pt
2236  // and delete all distributions, except for the one which corresponds
2237  // to the identity since this is already deleted.
2238  std::map<Vector<unsigned>, LinearAlgebraDistribution*>::iterator iter =
2240 
2241  while (iter != Auxiliary_block_distribution_pt.end())
2242  {
2243  if (iter->first != preconditioner_matrix_key)
2244  {
2245  delete iter->second;
2246  iter++;
2247  }
2248  else
2249  {
2250  ++iter;
2251  }
2252  }
2253 
2254  // Now clear it.
2256 
2257  // Delete any dof block distributions
2258  const unsigned ndof_block_dist = Dof_block_distribution_pt.size();
2259  for (unsigned dof_i = 0; dof_i < ndof_block_dist; dof_i++)
2260  {
2261  delete Dof_block_distribution_pt[dof_i];
2262  }
2263  Dof_block_distribution_pt.clear();
2264 
2265  } // EOFunc clear_block_preconditioner_base()
2266 
2269  void document()
2270  {
2271  oomph_info << std::endl;
2272  oomph_info << "===========================================" << std::endl;
2273  oomph_info << "Block Preconditioner Documentation" << std::endl
2274  << std::endl;
2275  oomph_info << "Number of DOF types: " << internal_ndof_types()
2276  << std::endl;
2277  oomph_info << "Number of block types: " << internal_nblock_types()
2278  << std::endl;
2279  oomph_info << std::endl;
2281  {
2282  for (unsigned d = 0; d < Internal_ndof_types; d++)
2283  {
2284  oomph_info << "Master DOF number " << d << " : "
2285  << this->internal_master_dof_number(d) << std::endl;
2286  }
2287  }
2288  oomph_info << std::endl;
2289  for (unsigned b = 0; b < internal_nblock_types(); b++)
2290  {
2291  oomph_info << "Block " << b << " DOF types:";
2292  for (unsigned i = 0; i < Block_number_to_dof_number_lookup[b].size();
2293  i++)
2294  {
2296  }
2297  oomph_info << std::endl;
2298  }
2299  oomph_info << std::endl;
2300  oomph_info << "Master block preconditioner distribution:" << std::endl;
2301  oomph_info << *master_distribution_pt() << std::endl;
2302  oomph_info << "Internal preconditioner matrix distribution:" << std::endl;
2304  << std::endl;
2305  oomph_info << "Preconditioner matrix distribution:" << std::endl;
2307  for (unsigned b = 0; b < Internal_nblock_types; b++)
2308  {
2309  oomph_info << "Internal block " << b << " distribution:" << std::endl;
2310  oomph_info << *Internal_block_distribution_pt[b] << std::endl;
2311  }
2312  for (unsigned b = 0; b < nblock_types(); b++)
2313  {
2314  oomph_info << "Block " << b << " distribution:" << std::endl;
2315  oomph_info << *Block_distribution_pt[b] << std::endl;
2316  }
2317 
2318  // DS: the functions called here no longer exist and this function is
2319  // never used as far as I can tell, so it should be fine to comment this
2320  // bit out:
2321  // if (is_master_block_preconditioner())
2322  // {
2323  // oomph_info << "First look-up row: " << this->first_lookup_row()
2324  // << std::endl;
2325  // oomph_info << "Number of look-up rows: "
2326  // << this->nlookup_rows() << std::endl;
2327  // }
2328  oomph_info << "===========================================" << std::endl;
2329  oomph_info << std::endl;
2330  } // EOFunc document()
2331 
2335  {
2336  return Doftype_coarsen_map_fine;
2337  }
2338 
2342  {
2343 #ifdef PARANOID
2344  const unsigned n_dof_types = ndof_types();
2345 
2346  if (i >= n_dof_types)
2347  {
2348  std::ostringstream err_msg;
2349  err_msg << "Trying to get the most fine grain dof types in dof type "
2350  << i << ",\nbut there are only " << n_dof_types
2351  << " number of dof types.\n";
2352  throw OomphLibError(
2354  }
2355 #endif
2356  return Doftype_coarsen_map_fine[i];
2357  }
2358 
2361  unsigned nfine_grain_dof_types_in(const unsigned& i) const
2362  {
2363 #ifdef PARANOID
2364  const unsigned n_dof_types = ndof_types();
2365 
2366  if (i >= n_dof_types)
2367  {
2368  std::ostringstream err_msg;
2369  err_msg << "Trying to get the number of most fine grain dof types "
2370  << "in dof type " << i << ",\n"
2371  << "but there are only " << n_dof_types
2372  << " number of dof types.\n";
2373  throw OomphLibError(
2375  }
2376 #endif
2377  return Doftype_coarsen_map_fine[i].size();
2378  }
2379 
2382  {
2383  return Replacement_dof_block_pt;
2384  } // EOFunc replacement_block_pt()
2385 
2398  CRDoubleMatrix* block_pt,
2399  const Vector<unsigned>& block_col_indices)
2400  {
2401  const unsigned nblock = block_col_indices.size();
2402 
2403  if (nblock == 1)
2404  {
2405  const unsigned col_index = block_col_indices[0];
2406  matvec_prod_pt->setup(block_pt, Block_distribution_pt[col_index]);
2407  }
2408  else
2409  {
2410  std::map<Vector<unsigned>, LinearAlgebraDistribution*>::const_iterator
2411  iter;
2412 
2413  iter = Auxiliary_block_distribution_pt.find(block_col_indices);
2414  if (iter != Auxiliary_block_distribution_pt.end())
2415  {
2416  matvec_prod_pt->setup(block_pt, iter->second);
2417  }
2418  else
2419  {
2420  Vector<LinearAlgebraDistribution*> tmp_vec_dist_pt(nblock, 0);
2421  for (unsigned b = 0; b < nblock; b++)
2422  {
2423  tmp_vec_dist_pt[b] = Block_distribution_pt[block_col_indices[b]];
2424  }
2425 
2426  LinearAlgebraDistribution* tmp_dist_pt =
2429  *tmp_dist_pt);
2430  insert_auxiliary_block_distribution(block_col_indices, tmp_dist_pt);
2431  matvec_prod_pt->setup(block_pt, tmp_dist_pt);
2432  }
2433  }
2434  } // EOFunc setup_matrix_vector_product(...)
2435 
2439  CRDoubleMatrix* block_pt,
2440  const unsigned& block_col_index)
2441  {
2442  Vector<unsigned> col_index_vector(1, block_col_index);
2443  setup_matrix_vector_product(matvec_prod_pt, block_pt, col_index_vector);
2444  } // EOFunc setup_matrix_vector_product(...)
2445 
2446  // private:
2447 
2483  const DoubleVector& v, DoubleVector& w) const;
2484 
2503  const DoubleVector& w, DoubleVector& v) const;
2504 
2522  unsigned internal_nblock_types() const
2523  {
2524 #ifdef PARANOID
2525  if (Internal_nblock_types == 0)
2526  {
2527  std::ostringstream err_msg;
2528  err_msg
2529  << "(Internal_nblock_types == 0) is true. \n"
2530  << "Did you remember to call the function block_setup(...)?\n\n"
2531 
2532  << "This variable is always set up within block_setup(...).\n"
2533  << "If block_setup() is already called, then perhaps there is\n"
2534  << "something wrong with your block preconditionable elements.\n"
2535  << std::endl;
2536  throw OomphLibError(
2538  }
2539 
2541  {
2542  std::ostringstream err_msg;
2543  err_msg
2544  << "The number of internal block types and "
2545  << "internal dof types does not match... \n\n"
2546  << "Internally, the number of block types and the number of dof "
2547  << "types must be the same.\n"
2548  << std::endl;
2549  throw OomphLibError(
2551  }
2552 #endif
2553 
2554  // return the number of internal block types.
2555  return Internal_nblock_types;
2556  } // EOFunc internal_nblock_types(...)
2557 
2564  unsigned internal_ndof_types() const
2565  {
2567  // If this is a subsidiary block preconditioner, then the variable
2568  // Internal_ndof_types must always be set up.
2569  {
2570 #ifdef PARANOID
2571  if (Internal_ndof_types == 0)
2572  {
2573  std::ostringstream error_msg;
2574  error_msg
2575  << "(Internal_ndof_types == 0) is true.\n"
2576  << "This means that the Master_block_preconditioner_pt pointer is\n"
2577  << "set but possibly not by the function\n"
2578  << "turn_into_subsidiary_block_preconditioner(...).\n\n"
2579 
2580  << "This goes against the block preconditioning framework "
2581  << "methodology.\n"
2582  << "Many machinery relies on the look up lists set up by the \n"
2583  << "function turn_into_subsidiary_block_preconditioner(...) \n"
2584  << "between the parent and child block preconditioners.\n"
2585  << std::endl;
2586  throw OomphLibError(
2588  }
2589 #endif
2590  return Internal_ndof_types;
2591  }
2592  else
2593  // Else, this is a master block preconditioner, calculate the number of
2594  // dof types from the meshes.
2595  {
2596  unsigned ndof = 0;
2597  for (unsigned i = 0; i < nmesh(); i++)
2598  {
2599  ndof += ndof_types_in_mesh(i);
2600  }
2601  return ndof;
2602  }
2603  } // EOFunc internal_ndof_types(...)
2604 
2615  void internal_return_block_vector(const unsigned& n,
2616  const DoubleVector& b,
2617  DoubleVector& v) const;
2618 
2624  void internal_get_block_vector(const unsigned& n,
2625  const DoubleVector& v,
2626  DoubleVector& b) const;
2627 
2628 
2638  void internal_get_block_vectors(const Vector<unsigned>& block_vec_number,
2639  const DoubleVector& v,
2640  Vector<DoubleVector>& s) const;
2641 
2654  Vector<DoubleVector>& s) const;
2655 
2660  void internal_return_block_vectors(const Vector<unsigned>& block_vec_number,
2661  const Vector<DoubleVector>& s,
2662  DoubleVector& v) const;
2663 
2672  DoubleVector& v) const;
2673 
2677  void internal_get_block(const unsigned& i,
2678  const unsigned& j,
2679  MATRIX& output_block) const;
2680 
2689  int internal_block_number(const unsigned& i_dof) const
2690  {
2691  int dn = internal_dof_number(i_dof);
2692  if (dn == -1)
2693  {
2694  return dn;
2695  }
2696  else
2697  {
2699  }
2700  } // EOFunc internal_block_number(...)
2701 
2705  int internal_index_in_block(const unsigned& i_dof) const
2706  {
2707  // the index in the dof block
2708  unsigned index = internal_index_in_dof(i_dof);
2709 
2710  // the dof block number
2711  int internal_dof_block_number = internal_dof_number(i_dof);
2712  if (internal_dof_block_number >= 0)
2713  {
2714  // the 'actual' block number
2715  unsigned blk_number = internal_block_number(i_dof);
2716 
2717  // compute the index in the block
2718  unsigned j = 0;
2719  while (int(Block_number_to_dof_number_lookup[blk_number][j]) !=
2720  internal_dof_block_number)
2721  {
2723  Block_number_to_dof_number_lookup[blk_number][j]);
2724  j++;
2725  }
2726 
2727  // and return
2728  return index;
2729  }
2730  return -1;
2731  } // EOFunc internal_index_in_block(...)
2732 
2735  const unsigned& b) const
2736  {
2737 #ifdef PARANOID
2738  if (Internal_block_distribution_pt.size() == 0)
2739  {
2740  std::ostringstream error_msg;
2741  error_msg << "Internal block distributions are not set up.\n"
2742  << "Have you called block_setup(...)?\n"
2743  << std::endl;
2744  throw OomphLibError(
2746  }
2747  if (b > internal_nblock_types())
2748  {
2749  std::ostringstream error_msg;
2750  error_msg << "You requested the distribution for the internal block "
2751  << b << ".\n"
2752  << "But there are only " << internal_nblock_types()
2753  << " block types.\n"
2754  << std::endl;
2755  throw OomphLibError(
2757  }
2758 #endif
2760  } // EOFunc internal_block_distribution_pt(...)
2761 
2770  const Vector<unsigned>& block_vec_number,
2771  LinearAlgebraDistribution* dist_pt)
2772  {
2773 #ifdef PARANOID
2774  const unsigned max_block_number =
2775  *std::max_element(block_vec_number.begin(), block_vec_number.end());
2776 
2777  const unsigned nblocks = nblock_types();
2778  if (max_block_number >= nblocks)
2779  {
2780  std::ostringstream err_msg;
2781  err_msg << "Cannot insert into Auxiliary_block_distribution_pt\n"
2782  << "because " << max_block_number << " is equal to or \n"
2783  << "greater than " << nblocks << ".\n";
2784  throw OomphLibError(
2786  }
2787 
2788  // Now check if the pair already exists in
2789  // Auxiliary_block_distribution_pt. This is a stricter test and can be
2790  // removed if required.
2791 
2792  // Attempt to get an iterator pointing to the pair with the value
2793  // block_vec_number.
2794  std::map<Vector<unsigned>, LinearAlgebraDistribution*>::const_iterator
2795  iter = Auxiliary_block_distribution_pt.find(block_vec_number);
2796 
2797  if (iter != Auxiliary_block_distribution_pt.end())
2798  // If it exists, we throw an error
2799  {
2800  std::ostringstream err_msg;
2801  err_msg << "Cannot insert into Auxiliary_block_distribution_pt\n"
2802  << "because the first in the pair already exists.\n";
2803  throw OomphLibError(
2805  }
2806 #endif
2807 
2809  std::make_pair(block_vec_number, dist_pt));
2810  } // insert_auxiliary_block_distribution(...)
2811 
2814  void block_matrix_test(const unsigned& i,
2815  const unsigned& j,
2816  const MATRIX* block_matrix_pt) const;
2817 
2826  template<typename myType>
2827  inline int get_index_of_value(const Vector<myType>& vec,
2828  const myType val,
2829  const bool sorted = false) const
2830  {
2831  if (sorted)
2832  {
2833  typename Vector<myType>::const_iterator low =
2834  std::lower_bound(vec.begin(), vec.end(), val);
2835 
2836  return (low == vec.end() || *low != val) ? -1 : (low - vec.begin());
2837  }
2838  else
2839  {
2840  int pos = std::find(vec.begin(), vec.end(), val) - vec.begin();
2841  return (pos < int(vec.size()) && pos >= 0) ? pos : -1;
2842  }
2843  }
2844 
2845  private:
2846  protected:
2851  void set_nmesh(const unsigned& n)
2852  {
2853  Mesh_pt.resize(n, 0);
2855  } // EOFunc set_nmesh(...)
2856 
2857 
2866  void set_mesh(const unsigned& i,
2867  const Mesh* const mesh_pt,
2868  const bool& allow_multiple_element_type_in_mesh = false)
2869  {
2870 #ifdef PARANOID
2871  // paranoid check that mesh i can be set
2872  if (i >= nmesh())
2873  {
2874  std::ostringstream err_msg;
2875  err_msg << "The mesh pointer has space for " << nmesh() << " meshes.\n"
2876  << "Cannot store a mesh at entry " << i << "\n"
2877  << "Has set_nmesh(...) been called?";
2878  throw OomphLibError(
2880  }
2881 
2882  // Check that the mesh pointer is not null.
2883  if (mesh_pt == 0)
2884  {
2885  std::ostringstream err_msg;
2886  err_msg << "Tried to set the " << i
2887  << "-th mesh pointer, but it is null.";
2888  throw OomphLibError(
2890  }
2891 #endif
2892 
2893  // store the mesh pt and n dof types
2894  Mesh_pt[i] = mesh_pt;
2895 
2896  // Does this mesh contain multiple element types?
2898  unsigned(allow_multiple_element_type_in_mesh);
2899  } // EOFunc set_mesh(...)
2900 
2901 
2911  void set_replacement_dof_block(const unsigned& block_i,
2912  const unsigned& block_j,
2914  {
2915 #ifdef PARANOID
2916  // Check if block_setup(...) has been called.
2917  if (nblock_types() == 0)
2918  {
2919  std::ostringstream err_msg;
2920  err_msg << "nblock_types() is 0, has block_setup(...) been called?\n";
2921  throw OomphLibError(
2923  }
2924 
2925 
2926  // Range checking for replacement dof block.
2927  unsigned para_ndof_types = this->ndof_types();
2928 
2929  if ((block_i >= para_ndof_types) || (block_j >= para_ndof_types))
2930  {
2931  std::ostringstream err_msg;
2932  err_msg << "Replacement dof block (" << block_i << "," << block_j
2933  << ") is outside of range:\n"
2934  << para_ndof_types;
2935  throw OomphLibError(
2937  }
2938 
2939 
2940  // // Check that the most fine grain mapping has been used in
2941  // block_setup(...)
2942  // // i.e. nblock_types() == ndof_types()
2943  // if(ndof_types() != nblock_types())
2944  // {
2945  // std::ostringstream err_msg;
2946  // err_msg << "ndof_types() != nblock_types()\n"
2947  // << "Only the dof-level blocks can be replaced.\n"
2948  // << "Please re-think your blocking scheme.\n";
2949  // throw OomphLibError(err_msg.str(),
2950  // OOMPH_CURRENT_FUNCTION,
2951  // OOMPH_EXCEPTION_LOCATION);
2952  // }
2953 
2954  // Check that the replacement block pt is not null
2955  if (replacement_dof_block_pt == 0)
2956  {
2957  std::ostringstream err_msg;
2958  err_msg << "Replacing block(" << block_i << "," << block_i << ")\n"
2959  << " but the pointer is NULL." << std::endl;
2960  throw OomphLibError(
2962  }
2963 
2964  // Check that the replacement block has been built
2965  if (!replacement_dof_block_pt->built())
2966  {
2967  std::ostringstream err_msg;
2968  err_msg << "Replacement block(" << block_i << "," << block_i << ")"
2969  << " is not built." << std::endl;
2970  throw OomphLibError(
2972  }
2973 
2974  // Check if the distribution matches. Determine which natural ordering dof
2975  // this should go to. I.e. we convert from dof-block index to dof index.
2976  // Luckily, this is stored in Block_to_dof_map_coarse.
2977  // const unsigned para_dof_block_i =
2978  // Block_to_dof_map_coarse[block_i][0];
2979  const unsigned para_dof_block_i = block_i;
2980 
2981  if (*dof_block_distribution_pt(para_dof_block_i) !=
2982  *replacement_dof_block_pt->distribution_pt())
2983  {
2984  std::ostringstream err_msg;
2985  err_msg << "The distribution of the replacement dof_block_pt\n"
2986  << "is different from the Dof_block_distribution_pt["
2987  << para_dof_block_i << "].\n";
2988  throw OomphLibError(
2990  }
2991 
2992  // Now that we know the distribution of the replacement block is
2993  // correct, we check the number of columns.
2994  const unsigned para_dof_block_j = block_j;
2995  unsigned para_replacement_block_ncol = replacement_dof_block_pt->ncol();
2996  unsigned para_required_ncol =
2997  dof_block_distribution_pt(para_dof_block_j)->nrow();
2998  if (para_replacement_block_ncol != para_required_ncol)
2999  {
3000  std::ostringstream err_msg;
3001  err_msg << "Replacement dof block has ncol = "
3002  << para_replacement_block_ncol << ".\n"
3003  << "But required ncol is " << para_required_ncol << ".\n";
3004  throw OomphLibError(
3006  }
3007 #endif
3008 
3009  // Block_to_dof_map_coarse[x][0] sense because we only can use this if
3010  // nblock_types() == ndof_types(), i.e. each sub-vector is of length 1.
3011  //
3012  // We use this indirection so that the placement of the pointer is
3013  // consistent with internal_get_block(...).
3014  // const unsigned dof_block_i = Block_to_dof_map_coarse[block_i][0];
3015  // const unsigned dof_block_j = Block_to_dof_map_coarse[block_j][0];
3016 
3017  // Replacement_dof_block_pt(dof_block_i,dof_block_j)
3018  // = replacement_dof_block_pt;
3019 
3021  }
3022 
3026  {
3027 #ifdef OOMPH_HAS_MPI
3028  // is_mesh_distributed() is only available with MPI
3029  for (unsigned i = 0, n = nmesh(); i < n; i++)
3030  {
3031  if (mesh_pt(i)->is_mesh_distributed())
3032  {
3033  return true;
3034  }
3035  }
3036 #endif
3037  return false;
3038  }
3039 
3045  int internal_dof_number(const unsigned& i_dof) const
3046  {
3048  {
3049 #ifdef OOMPH_HAS_MPI
3050  unsigned first_row = this->distribution_pt()->first_row();
3051  unsigned nrow_local = this->distribution_pt()->nrow_local();
3052  unsigned last_row = first_row + nrow_local - 1;
3053  if (i_dof >= first_row && i_dof <= last_row)
3054  {
3055  return static_cast<int>(Dof_number_dense[i_dof - first_row]);
3056  }
3057  else
3058  {
3059  // int index = this->get_index_of_element(Global_index_sparse,i_dof);
3060  int index =
3061  get_index_of_value<unsigned>(Global_index_sparse, i_dof, true);
3062  if (index >= 0)
3063  {
3064  return Dof_number_sparse[index];
3065  }
3066  }
3067  // if we here we couldn't find the i_dof
3068 #ifdef PARANOID
3069  unsigned my_rank = comm_pt()->my_rank();
3070  std::ostringstream error_message;
3071  error_message << "Proc " << my_rank
3072  << ": Requested internal_dof_number(...) for global DOF "
3073  << i_dof << "\n"
3074  << "cannot be found.\n";
3075  throw OomphLibError(error_message.str(),
3078 #endif
3079 #else
3080  return static_cast<int>(Dof_number_dense[i_dof]);
3081 #endif
3082  }
3083  // else this preconditioner is a subsidiary one, and its Block_number
3084  // lookup schemes etc haven't been set up
3085  else
3086  {
3087  // Block number in master prec
3088  unsigned blk_num =
3089  Master_block_preconditioner_pt->internal_dof_number(i_dof);
3090 
3091  // Search through the Block_number_in_master_preconditioner for master
3092  // block blk_num and return the block number in this preconditioner
3093  for (unsigned i = 0; i < this->internal_ndof_types(); i++)
3094  {
3095  if (Doftype_in_master_preconditioner_fine[i] == blk_num)
3096  {
3097  return static_cast<int>(i);
3098  }
3099  }
3100  // if the master block preconditioner number is not found return -1
3101  return -1;
3102  }
3103 
3104  // Shouldn't get here
3105  throw OomphLibError(
3106  "Never get here\n", OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3107  // Dummy return
3108  return -1;
3109  }
3110 
3113  unsigned internal_index_in_dof(const unsigned& i_dof) const
3114  {
3116  {
3117 #ifdef OOMPH_HAS_MPI
3118  unsigned first_row = this->distribution_pt()->first_row();
3119  unsigned nrow_local = this->distribution_pt()->nrow_local();
3120  unsigned last_row = first_row + nrow_local - 1;
3121  if (i_dof >= first_row && i_dof <= last_row)
3122  {
3123  return static_cast<int>(Index_in_dof_block_dense[i_dof - first_row]);
3124  }
3125  else
3126  {
3127  // int index = this->get_index_of_element(Global_index_sparse,i_dof);
3128  int index =
3129  get_index_of_value<unsigned>(Global_index_sparse, i_dof, true);
3130  if (index >= 0)
3131  {
3132  return Index_in_dof_block_sparse[index];
3133  }
3134  }
3135  // if we here we couldn't find the i_dof
3136 #ifdef PARANOID
3137  std::ostringstream error_message;
3138  error_message << "Requested internal_index_in_dof(...) for global DOF "
3139  << i_dof << "\n"
3140  << "cannot be found.\n";
3141  throw OomphLibError(error_message.str(),
3144 #endif
3145 #else
3146  return Index_in_dof_block_dense[i_dof];
3147 #endif
3148  }
3149  else
3150  {
3151  return Master_block_preconditioner_pt->internal_index_in_dof(i_dof);
3152  }
3153 
3154  // Shouldn't get here
3155  throw OomphLibError(
3156  "Never get here\n", OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3157  // Dummy return
3158  return -1;
3159  }
3160 
3165  unsigned internal_block_dimension(const unsigned& b) const
3166  {
3167 #ifdef PARANOID
3168  const unsigned i_nblock_types = internal_nblock_types();
3169  if (b >= i_nblock_types)
3170  {
3171  std::ostringstream err_msg;
3172  err_msg << "Trying to get internal block dimension for \n"
3173  << "internal block " << b << ".\n"
3174  << "But there are only " << i_nblock_types
3175  << " internal dof types.\n";
3176  throw OomphLibError(
3178  }
3179 #endif
3180  return Internal_block_distribution_pt[b]->nrow();
3181  }
3182 
3187  unsigned internal_dof_block_dimension(const unsigned& i) const
3188  {
3189 #ifdef PARANOID
3190  const unsigned i_n_dof_types = internal_ndof_types();
3191  if (i >= i_n_dof_types)
3192  {
3193  std::ostringstream err_msg;
3194  err_msg << "Trying to get internal dof block dimension for \n"
3195  << "internal dof block " << i << ".\n"
3196  << "But there are only " << i_n_dof_types
3197  << " internal dof types.\n";
3198  throw OomphLibError(
3200  }
3201 #endif
3202  // I don't understand the difference between this function and
3203  // block_dimension(...) but I'm not going to mess with it... David
3204 
3206  {
3207  return Dof_dimension[i];
3208  }
3209  else
3210  {
3211  unsigned master_i = internal_master_dof_number(i);
3212  return Master_block_preconditioner_pt->internal_dof_block_dimension(
3213  master_i);
3214  }
3215  }
3216 
3222  unsigned master_nrow() const
3223  {
3225  {
3226  return Nrow;
3227  }
3228  else
3229  {
3230  return (this->Master_block_preconditioner_pt->master_nrow());
3231  }
3232  }
3233 
3238  unsigned internal_master_dof_number(const unsigned& b) const
3239  {
3240  if (is_master_block_preconditioner()) return b;
3241  else
3243  }
3244 
3252  const
3253  {
3256  else
3257  return this->distribution_pt();
3258  }
3259 
3268  const
3269  {
3271  }
3272 
3275 
3278 
3286 
3289 
3327 
3335 
3338 
3342 
3346 
3353 
3357 
3363 
3369 
3370  private:
3376 
3381 
3386  std::map<Vector<unsigned>, LinearAlgebraDistribution*>
3388 
3393  unsigned Nrow;
3394 
3400 
3405 
3412 
3416 
3421 
3422 #ifdef OOMPH_HAS_MPI
3423 
3424  // The following three vectors store data on the matrix rows/matrix
3425  // columns/dofs (the three are equivalent) that are not on this processor.
3426 
3433  Vector<unsigned> Global_index_sparse;
3434 
3442  Vector<unsigned> Index_in_dof_block_sparse;
3443 
3450  Vector<unsigned> Dof_number_sparse;
3451 #endif
3452 
3458 
3464 
3469 
3472 
3475 
3476 
3477 #ifdef OOMPH_HAS_MPI
3480  DenseMatrix<int*> Rows_to_send_for_get_block;
3481 
3484  DenseMatrix<unsigned> Nrows_to_send_for_get_block;
3485 
3488  DenseMatrix<int*> Rows_to_recv_for_get_block;
3489 
3492  DenseMatrix<unsigned> Nrows_to_recv_for_get_block;
3493 
3496  Vector<int*> Rows_to_send_for_get_ordered;
3497 
3500  Vector<unsigned> Nrows_to_send_for_get_ordered;
3501 
3504  Vector<int*> Rows_to_recv_for_get_ordered;
3505 
3508  Vector<unsigned> Nrows_to_recv_for_get_ordered;
3509 #endif
3510 
3520 
3524 
3528 
3532  };
3533 
3534 
3535 } // namespace oomph
3536 #endif
Array< int, Dynamic, 1 > v
Definition: Array_initializer_list_vector_cxx11.cpp:1
int i
Definition: BiCGSTAB_step_by_step.cpp:9
const unsigned n
Definition: CG3DPackingUnitTest.cpp:11
RowVector3d w
Definition: Matrix_resize_int.cpp:3
float * p
Definition: Tutorial_Map_using.cpp:9
Scalar * b
Definition: benchVecAdd.cpp:17
Definition: block_preconditioner.h:422
const LinearAlgebraDistribution * master_distribution_pt() const
Definition: block_preconditioner.h:2017
Vector< Vector< unsigned > > Doftype_coarsen_map_coarse
Definition: block_preconditioner.h:3326
void internal_return_block_vector(const unsigned &n, const DoubleVector &b, DoubleVector &v) const
Definition: block_preconditioner.cc:4293
BlockPreconditioner(const BlockPreconditioner &)=delete
Broken copy constructor.
unsigned Internal_nblock_types
Definition: block_preconditioner.h:3362
Vector< unsigned > Index_in_dof_block_dense
Definition: block_preconditioner.h:3415
bool block_output_on() const
Test if output of blocks is on or not.
Definition: block_preconditioner.h:2088
void turn_on_recursive_debug_flag()
Definition: block_preconditioner.h:545
unsigned nmesh() const
Definition: block_preconditioner.h:1816
const LinearAlgebraDistribution * internal_block_distribution_pt(const unsigned &b) const
Access function to the internal block distributions.
Definition: block_preconditioner.h:2734
void return_block_vector(const unsigned &n, const DoubleVector &b, DoubleVector &v) const
Definition: block_preconditioner.cc:4489
unsigned internal_index_in_dof(const unsigned &i_dof) const
Definition: block_preconditioner.h:3113
LinearAlgebraDistribution * dof_block_distribution_pt(const unsigned &b)
Access function to the dof-level block distributions.
Definition: block_preconditioner.h:1962
Vector< LinearAlgebraDistribution * > Dof_block_distribution_pt
Definition: block_preconditioner.h:3341
void turn_on_debug_flag()
Toggles on the debug flag.
Definition: block_preconditioner.h:562
Vector< Vector< unsigned > > Block_to_dof_map_fine
Mapping for the block types to the most fine grain dof types.
Definition: block_preconditioner.h:3288
void document()
Definition: block_preconditioner.h:2269
void internal_return_block_vectors(const Vector< unsigned > &block_vec_number, const Vector< DoubleVector > &s, DoubleVector &v) const
Definition: block_preconditioner.cc:3684
Vector< unsigned > Doftype_in_master_preconditioner_coarse
Definition: block_preconditioner.h:3411
Vector< unsigned > Allow_multiple_element_type_in_mesh
Definition: block_preconditioner.h:3345
void get_dof_level_block(const unsigned &i, const unsigned &j, MATRIX &output_block, const bool &ignore_replacement_block=false) const
Vector< unsigned > Dof_number_dense
Definition: block_preconditioner.h:3420
Vector< LinearAlgebraDistribution * > Internal_block_distribution_pt
Storage for the default distribution for each internal block.
Definition: block_preconditioner.h:3337
unsigned Internal_ndof_types
Definition: block_preconditioner.h:3368
const LinearAlgebraDistribution * internal_preconditioner_matrix_distribution_pt() const
Definition: block_preconditioner.h:3251
Vector< Vector< unsigned > > doftype_coarsen_map_fine() const
Definition: block_preconditioner.h:2334
const Mesh * mesh_pt(const unsigned &i) const
Definition: block_preconditioner.h:1782
unsigned master_nrow() const
Definition: block_preconditioner.h:3222
Vector< unsigned > get_fine_grain_dof_types_in(const unsigned &i) const
Definition: block_preconditioner.h:2341
Vector< unsigned > Ndof_types_in_mesh
Definition: block_preconditioner.h:3356
unsigned ndof_types_in_mesh(const unsigned &i) const
Definition: block_preconditioner.h:2037
void disable_block_output_to_files()
Turn off output of blocks (by clearing the basefilename string).
Definition: block_preconditioner.h:2082
void turn_off_debug_flag()
Toggles off the debug flag.
Definition: block_preconditioner.h:568
unsigned Nrow
Definition: block_preconditioner.h:3393
MATRIX get_block(const unsigned &i, const unsigned &j, const bool &ignore_replacement_block=false) const
Definition: block_preconditioner.h:988
void block_matrix_test(const unsigned &i, const unsigned &j, const MATRIX *block_matrix_pt) const
Definition: block_preconditioner.cc:5839
unsigned internal_ndof_types() const
Definition: block_preconditioner.h:2564
unsigned nfine_grain_dof_types_in(const unsigned &i) const
Definition: block_preconditioner.h:2361
MATRIX get_concatenated_block(const VectorMatrix< BlockSelector > &selected_block)
Definition: block_preconditioner.h:1134
Vector< unsigned > Dof_dimension
Definition: block_preconditioner.h:3457
MapMatrix< unsigned, CRDoubleMatrix * > replacement_dof_block_pt() const
Access function to the replaced dof-level blocks.
Definition: block_preconditioner.h:2381
LinearAlgebraDistribution * Internal_preconditioner_matrix_distribution_pt
Definition: block_preconditioner.h:3519
Vector< LinearAlgebraDistribution * > Block_distribution_pt
The distribution for the blocks.
Definition: block_preconditioner.h:3277
Vector< Vector< unsigned > > Block_number_to_dof_number_lookup
Definition: block_preconditioner.h:3468
void setup_matrix_vector_product(MatrixVectorProduct *matvec_prod_pt, CRDoubleMatrix *block_pt, const unsigned &block_col_index)
Definition: block_preconditioner.h:2438
void internal_return_block_ordered_preconditioner_vector(const DoubleVector &w, DoubleVector &v) const
Definition: block_preconditioner.cc:4850
void get_blocks(DenseMatrix< bool > &required_blocks, DenseMatrix< MATRIX * > &block_matrix_pt) const
Definition: block_preconditioner.cc:2537
void return_concatenated_block_vector(const Vector< unsigned > &block_vec_number, const DoubleVector &b, DoubleVector &v) const
Takes concatenated block ordered vector, b, and copies its.
Definition: block_preconditioner.cc:2774
static bool Run_block_matrix_test
Definition: block_preconditioner.h:3527
bool any_mesh_distributed() const
Definition: block_preconditioner.h:3025
void return_block_vectors(const Vector< unsigned > &block_vec_number, const Vector< DoubleVector > &s, DoubleVector &v) const
Definition: block_preconditioner.cc:3443
unsigned internal_master_dof_number(const unsigned &b) const
Definition: block_preconditioner.h:3238
void get_block(const unsigned &i, const unsigned &j, MATRIX &output_matrix, const bool &ignore_replacement_block=false) const
Definition: block_preconditioner.h:673
void set_master_matrix_pt(MATRIX *in_matrix_pt)
Set the matrix_pt in the upper-most master preconditioner.
Definition: block_preconditioner.h:998
BlockPreconditioner()
Constructor.
Definition: block_preconditioner.h:425
const LinearAlgebraDistribution * preconditioner_matrix_distribution_pt() const
Definition: block_preconditioner.h:3267
int index_in_block(const unsigned &i_dof) const
Definition: block_preconditioner.h:1850
BlockPreconditioner< MATRIX > * Master_block_preconditioner_pt
Definition: block_preconditioner.h:3399
const LinearAlgebraDistribution * block_distribution_pt(const unsigned &b) const
Access function to the block distributions (const version).
Definition: block_preconditioner.h:1903
void clear_block_preconditioner_base()
Definition: block_preconditioner.h:2159
virtual ~BlockPreconditioner()
Destructor.
Definition: block_preconditioner.h:506
Vector< Vector< unsigned > > Global_index
Definition: block_preconditioner.h:3463
unsigned internal_nblock_types() const
Definition: block_preconditioner.h:2522
void get_block_vector(const unsigned &n, const DoubleVector &v, DoubleVector &b) const
Definition: block_preconditioner.cc:4186
bool is_master_block_preconditioner() const
Definition: block_preconditioner.h:2068
unsigned nblock_types() const
Return the number of block types.
Definition: block_preconditioner.h:1670
Vector< unsigned > Ndof_in_block
Number of types of degree of freedom associated with each block.
Definition: block_preconditioner.h:3474
Vector< const Mesh * > Mesh_pt
Definition: block_preconditioner.h:3352
void set_replacement_dof_block(const unsigned &block_i, const unsigned &block_j, CRDoubleMatrix *replacement_dof_block_pt)
Definition: block_preconditioner.h:2911
int block_number(const unsigned &i_dof) const
Return the block number corresponding to a global index i_dof.
Definition: block_preconditioner.h:1822
void turn_off_recursive_debug_flag()
Definition: block_preconditioner.h:554
void operator=(const BlockPreconditioner &)=delete
Broken assignment operator.
std::string Output_base_filename
Definition: block_preconditioner.h:3531
void return_block_ordered_preconditioner_vector(const DoubleVector &w, DoubleVector &v) const
Definition: block_preconditioner.cc:5046
MATRIX * matrix_pt() const
Definition: block_preconditioner.h:520
bool is_subsidiary_block_preconditioner() const
Definition: block_preconditioner.h:2061
void insert_auxiliary_block_distribution(const Vector< unsigned > &block_vec_number, LinearAlgebraDistribution *dist_pt)
Definition: block_preconditioner.h:2769
void get_block_other_matrix(const unsigned &i, const unsigned &j, MATRIX *in_matrix_pt, MATRIX &output_matrix)
Definition: block_preconditioner.h:1012
Vector< Vector< unsigned > > Doftype_coarsen_map_fine
Definition: block_preconditioner.h:3334
void post_block_matrix_assembly_partial_clear()
Definition: block_preconditioner.h:2120
std::map< Vector< unsigned >, LinearAlgebraDistribution * > Auxiliary_block_distribution_pt
Definition: block_preconditioner.h:3387
void set_block_output_to_files(const std::string &basefilename)
Definition: block_preconditioner.h:2076
bool Debug_flag
Definition: block_preconditioner.h:3380
unsigned ndof_types() const
Return the total number of DOF types.
Definition: block_preconditioner.h:1694
BlockPreconditioner< MATRIX > * master_block_preconditioner_pt() const
Access function to the master block preconditioner pt.
Definition: block_preconditioner.h:2141
void turn_into_subsidiary_block_preconditioner(BlockPreconditioner< MATRIX > *master_block_prec_pt, const Vector< unsigned > &doftype_in_master_preconditioner_coarse)
Definition: block_preconditioner.cc:2376
int internal_dof_number(const unsigned &i_dof) const
Definition: block_preconditioner.h:3045
void internal_get_block(const unsigned &i, const unsigned &j, MATRIX &output_block) const
unsigned internal_dof_block_dimension(const unsigned &i) const
Definition: block_preconditioner.h:3187
unsigned internal_block_dimension(const unsigned &b) const
Definition: block_preconditioner.h:3165
int get_index_of_value(const Vector< myType > &vec, const myType val, const bool sorted=false) const
Definition: block_preconditioner.h:2827
LinearAlgebraDistribution * block_distribution_pt(const unsigned b)
Access function to the block distributions (non-const version).
Definition: block_preconditioner.h:1933
void internal_get_block_vectors(const Vector< unsigned > &block_vec_number, const DoubleVector &v, Vector< DoubleVector > &s) const
Definition: block_preconditioner.cc:3131
MapMatrix< unsigned, CRDoubleMatrix * > Replacement_dof_block_pt
The replacement dof-level blocks.
Definition: block_preconditioner.h:3274
void get_block_vectors(const Vector< unsigned > &block_vec_number, const DoubleVector &v, Vector< DoubleVector > &s) const
Definition: block_preconditioner.cc:2939
LinearAlgebraDistribution * Preconditioner_matrix_distribution_pt
Definition: block_preconditioner.h:3523
void setup_matrix_vector_product(MatrixVectorProduct *matvec_prod_pt, CRDoubleMatrix *block_pt, const Vector< unsigned > &block_col_indices)
Definition: block_preconditioner.h:2397
void output_blocks_to_files(const std::string &basefilename, const unsigned &precision=8) const
Definition: block_preconditioner.h:2095
Vector< Vector< unsigned > > Block_to_dof_map_coarse
Definition: block_preconditioner.h:3285
void set_nmesh(const unsigned &n)
Definition: block_preconditioner.h:2851
Vector< unsigned > Dof_number_to_block_number_lookup
Vector to the mapping from DOF number to block number.
Definition: block_preconditioner.h:3471
void internal_get_block_vector(const unsigned &n, const DoubleVector &v, DoubleVector &b) const
Definition: block_preconditioner.cc:4009
bool Recursive_debug_flag
Definition: block_preconditioner.h:3375
Vector< unsigned > Doftype_in_master_preconditioner_fine
Definition: block_preconditioner.h:3404
int internal_block_number(const unsigned &i_dof) const
Definition: block_preconditioner.h:2689
virtual void block_setup()
Definition: block_preconditioner.cc:2483
void get_concatenated_block_vector(const Vector< unsigned > &block_vec_number, const DoubleVector &v, DoubleVector &b)
Definition: block_preconditioner.cc:2608
void set_mesh(const unsigned &i, const Mesh *const mesh_pt, const bool &allow_multiple_element_type_in_mesh=false)
Definition: block_preconditioner.h:2866
void internal_get_block_ordered_preconditioner_vector(const DoubleVector &v, DoubleVector &w) const
Definition: block_preconditioner.cc:4599
void get_block_ordered_preconditioner_vector(const DoubleVector &v, DoubleVector &w)
Definition: block_preconditioner.cc:4793
int internal_index_in_block(const unsigned &i_dof) const
Definition: block_preconditioner.h:2705
Definition: block_preconditioner.h:124
void null_replacement_block_pt()
Set Replacement_block_pt to null.
Definition: block_preconditioner.h:241
void do_not_want_block()
Indicate that we do not want the block (set Wanted to false).
Definition: block_preconditioner.h:218
void want_block()
Indicate that we require the block (set Wanted to true).
Definition: block_preconditioner.h:212
const unsigned & column_index() const
returns the column index.
Definition: block_preconditioner.h:290
virtual ~BlockSelector()
Default destructor.
Definition: block_preconditioner.h:168
void build(const unsigned &row_index, const unsigned &column_index, const bool &wanted, CRDoubleMatrix *replacement_block_pt=0)
Definition: block_preconditioner.h:326
void select_block(const unsigned &row_index, const unsigned &column_index, const bool &wanted, CRDoubleMatrix *replacement_block_pt=0)
Select a block.
Definition: block_preconditioner.h:188
BlockSelector(const unsigned &row_index, const unsigned &column_index, const bool &wanted, CRDoubleMatrix *replacement_block_pt=0)
Definition: block_preconditioner.h:141
friend std::ostream & operator<<(std::ostream &o_stream, const BlockSelector &block_selector)
Definition: block_preconditioner.h:307
BlockSelector()
Definition: block_preconditioner.h:128
const unsigned & row_index() const
returns the row index.
Definition: block_preconditioner.h:278
void set_column_index(const unsigned &column_index)
Set the column index.
Definition: block_preconditioner.h:284
const bool & wanted() const
returns whether the block is wanted or not.
Definition: block_preconditioner.h:296
void set_row_index(const unsigned &row_index)
Set the row index.
Definition: block_preconditioner.h:272
CRDoubleMatrix * Replacement_block_pt
Pointer to the block.
Definition: block_preconditioner.h:369
CRDoubleMatrix * replacement_block_pt() const
Returns Replacement_block_pt.
Definition: block_preconditioner.h:266
bool Wanted
Bool to indicate if we require this block.
Definition: block_preconditioner.h:366
unsigned Column_index
Column index of the block.
Definition: block_preconditioner.h:363
unsigned Row_index
Row index of the block.
Definition: block_preconditioner.h:360
void set_replacement_block_pt(CRDoubleMatrix *replacement_block_pt)
set Replacement_block_pt.
Definition: block_preconditioner.h:247
Definition: matrices.h:888
unsigned long ncol() const
Return the number of columns of the matrix.
Definition: matrices.h:1008
bool built() const
Definition: matrices.h:1210
Definition: matrices.h:386
void clear_distribution()
Definition: linear_algebra_distribution.h:522
LinearAlgebraDistribution * distribution_pt() const
access to the LinearAlgebraDistribution
Definition: linear_algebra_distribution.h:457
unsigned nrow_local() const
access function for the num of local rows on this processor.
Definition: linear_algebra_distribution.h:469
unsigned first_row() const
access function for the first row on this processor
Definition: linear_algebra_distribution.h:481
Definition: double_vector.h:58
Definition: linear_algebra_distribution.h:64
unsigned first_row() const
Definition: linear_algebra_distribution.h:261
unsigned nrow() const
access function to the number of global rows.
Definition: linear_algebra_distribution.h:186
unsigned nrow_local() const
Definition: linear_algebra_distribution.h:193
unsigned rank_of_global_row(const unsigned i) const
return the processor rank of the global row number i
Definition: linear_algebra_distribution.h:387
Definition: map_matrix.h:508
Definition: matrix_vector_product.h:51
void setup(CRDoubleMatrix *matrix_pt, const LinearAlgebraDistribution *col_dist_pt=0)
Definition: matrix_vector_product.cc:41
Definition: mesh.h:67
unsigned ndof_types() const
Return number of dof types in mesh.
Definition: mesh.cc:8764
int my_rank() const
my rank
Definition: communicator.h:176
Definition: oomph_definitions.h:222
Definition: oomph_definitions.h:267
Definition: preconditioner.h:54
virtual DoubleMatrixBase * matrix_pt() const
Get function for matrix pointer.
Definition: preconditioner.h:150
virtual const OomphCommunicator * comm_pt() const
Get function for comm pointer.
Definition: preconditioner.h:171
virtual void set_matrix_pt(DoubleMatrixBase *matrix_pt)
Set the matrix pointer.
Definition: preconditioner.h:165
Definition: vector_matrix.h:79
const unsigned ncol() const
Definition: vector_matrix.h:146
const unsigned nrow() const
returns the number of rows. This is the outer Vector size.
Definition: vector_matrix.h:107
static constexpr lastp1_t end
Definition: IndexedViewHelper.h:79
RealScalar s
Definition: level1_cplx_impl.h:130
int * m
Definition: level2_cplx_impl.h:294
string filename
Definition: MergeRestartFiles.py:39
val
Definition: calibrate.py:119
void deep_copy(const CRDoubleMatrix *const in_matrix_pt, CRDoubleMatrix &out_matrix)
Create a deep copy of the matrix pointed to by in_matrix_pt.
Definition: matrices.h:3490
void concatenate_without_communication(const Vector< LinearAlgebraDistribution * > &row_distribution_pt, const Vector< LinearAlgebraDistribution * > &col_distribution_pt, const DenseMatrix< CRDoubleMatrix * > &matrix_pt, CRDoubleMatrix &result_matrix)
Definition: matrices.cc:5223
std::string string(const unsigned &i)
Definition: oomph_definitions.cc:286
void concatenate(const Vector< LinearAlgebraDistribution * > &in_distribution_pt, LinearAlgebraDistribution &out_distribution)
Definition: linear_algebra_distribution.cc:367
std::string to_string(T object, unsigned float_precision=8)
Definition: oomph_utilities.h:189
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).
Definition: AnisotropicHookean.h:10
OomphInfo oomph_info
Definition: oomph_definitions.cc:319
#define OOMPH_EXCEPTION_LOCATION
Definition: oomph_definitions.h:61
#define OOMPH_CURRENT_FUNCTION
Definition: oomph_definitions.h:86
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2