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// Version 1.0; svn revision $LastChangedRevision$
7 //LIC//
8 //LIC// $LastChangedDate$
9 //LIC//
10 //LIC// Copyright (C) 2006-2016 Matthias Heil and Andrew Hazel
11 //LIC//
12 //LIC// This library is free software; you can redistribute it and/or
13 //LIC// modify it under the terms of the GNU Lesser General Public
14 //LIC// License as published by the Free Software Foundation; either
15 //LIC// version 2.1 of the License, or (at your option) any later version.
16 //LIC//
17 //LIC// This library is distributed in the hope that it will be useful,
18 //LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 //LIC// Lesser General Public License for more details.
21 //LIC//
22 //LIC// You should have received a copy of the GNU Lesser General Public
23 //LIC// License along with this library; if not, write to the Free Software
24 //LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 //LIC// 02110-1301 USA.
26 //LIC//
27 //LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
28 //LIC//
29 //LIC//====================================================================
30 //Include guards
31 #ifndef OOMPH_BLOCK_PRECONDITION_HEADER
32 #define OOMPH_BLOCK_PRECONDITION_HEADER
33 
34 
35 // Config header generated by autoconfig
36 #ifdef HAVE_CONFIG_H
37 #include <oomph-lib-config.h>
38 #endif
39 
40 // c++ include
41 #include <math.h>
42 #include <typeinfo>
43 
44 // oomph-lib includes
45 #include "matrices.h"
46 #include "mesh.h"
47 #include "vector_matrix.h"
48 
49 // #include "problem.h"
50 #include "preconditioner.h"
51 #include "SuperLU_preconditioner.h"
52 #include "matrix_vector_product.h"
53 
54 namespace oomph
55 {
56 
57 //=============================================================================
58 /// \short Data structure to store information about a certain "block" or
59 /// sub-matrix from the overall matrix in the block preconditioning framework.
60 ///
61 /// Example of use: Let's assume we want to form a concatenated matrix
62 /// from the blocks of a Jacobian matrix that contains the following blocks:
63 ///
64 /// [J_00, J_01, J_02
65 /// J_10, J_11, J_12
66 /// J_20, J_21, J_22]
67 ///
68 /// so that the new matrix has the entries
69 ///
70 /// [ J_01, J_00
71 /// J_21, 0 ]
72 ///
73 /// where "0" indicates zero matrix of the required size.
74 ///
75 /// To do this we create a 2x2 (the block size of the new concatenated
76 /// matrix) VectorMatrix of BlockSelectors and then declare for each
77 /// entry the block indices in the original matrix and if the entry
78 /// is to be included (and copied from the corresponding entry in
79 /// the Jacobian (final boolean argument true) or if the block is
80 /// to be omitted and replaced by an appropriately sized zero matrix.
81 /// For the example above this would be done as follows:
82 ///
83 /// VectorMatrix<BlockSelector> required_block(2,2);
84 /// required_block[0][0].select_block(0,1,true);
85 /// required_block[0][1].select_block(0,0,true);
86 /// required_block[1][0].select_block(2,1,true);
87 /// required_block[1][1].select_block(2,0,false);
88 ///
89 /// and the concatenated matrix would then be built as
90 ///
91 /// CRDoubleMatrix concatenated_block1
92 /// = get_concatenated_block(required_block);
93 ///
94 /// Note that it is necessary to identify the row and column indices of any
95 /// omitted blocks (here block J_20 in the original matrix) to enable
96 /// the correct setup of the sparse matrix storage.
97 ///
98 /// The initial assignment of the boolean may be over-written with the
99 /// do_not_want_block() member function; this can again be reversed
100 /// with the want_block() counterpart. So if we call
101 ///
102 /// required_block[0][0].do_not_want_block();
103 ///
104 /// and the build a new conctatenated matrix with
105 ///
106 /// CRDoubleMatrix concatenated_block2
107 /// = get_concatenated_block(required_block);
108 ///
109 /// the resulting matrix would the anti-diagonal matrix
110 ///
111 /// [ 0 , J_00
112 /// J_21 , 0 ]
113 ///
114 /// Finally it is possible to specify a replacement block
115 /// by specifying a pointer to an appropriately sized matrix
116 /// that is to be used instead of the block in the Jacobian
117 /// matrix, so if replacement_block_pt points to a matrix, R, say,
118 /// of the same size as J_01, then
119 ///
120 /// selected_block[0][0].select_block(0,1,true,replacement_block_pt);
121 ///
122 /// then the resulting concatenated matrix would contain
123 ///
124 /// [ R , J_00
125 /// J_21 , 0 ]
126 ///
127 //=============================================================================
129 {
130  public:
131 
132  /// \short Default constructor,
133  /// initialise block index i, j to 0 and bool to false.
135  {
136  // Needs to be set to zero because if the build function leaves the
137  // Replacement_block_pt alone if replacement_block_pt = 0 (the default argument).
139  this->build(0,0,false);
140  }
141 
142  /// \short Constructor, takes the row and column indices
143  /// and a boolean indicating if the block is required or not. The optional
144  /// parameter replacement_block_pt is set to null. If the block is not required a block
145  /// of the correct dimensions full of 0s is used.
146  BlockSelector(const unsigned& row_index,
147  const unsigned& column_index,
148  const bool& wanted,
150  {
151 #ifdef PARANOID
152  if((wanted == false) && (replacement_block_pt !=0))
153  {
154  std::ostringstream err_msg;
155  err_msg << "Trying to construct a BlockSelector object with:\n"
156  << "replacement_block_pt != 0 and wanted == false"
157  << "If you require the block, please set wanted == true.\n";
158  throw OomphLibError(err_msg.str(),
159  OOMPH_CURRENT_FUNCTION,
160  OOMPH_EXCEPTION_LOCATION);
161  }
162 #endif
163 
164  // Needs to be set to zero because if the build function leaves the
165  // Replacement_block_pt alone if replacement_block_pt = 0 (the default argument).
166  // Thus if it is not set here, it would not be initialised to null.
168 
169  this->build(row_index,column_index,wanted,replacement_block_pt);
170  }
171 
172  /// \short Default destructor.
173  virtual ~BlockSelector()
174  {
175 #ifdef PARANOID
176  if(Replacement_block_pt != 0)
177  {
178  std::ostringstream warning_msg;
179  warning_msg << "Warning: BlockSelector destructor is called but...\n"
180  << "replacement_block_pt() is not null.\n"
181  << "Please remember to null this via the function\n"
182  << "BlockSelector::null_replacement_block_pt()\n"
183  << "Row_index: " << Row_index << "\n"
184  << "Column_index: " << Column_index << std::endl;
185 
186  OomphLibWarning(warning_msg.str(),
187  OOMPH_CURRENT_FUNCTION,
188  OOMPH_EXCEPTION_LOCATION);
189  }
190 #endif
191  }
192 
193  /// \short Select a block.
194  void select_block(const unsigned& row_index,
195  const unsigned& column_index,
196  const bool& wanted,
198  {
199 #ifdef PARANOID
200  if((wanted == false) && (replacement_block_pt !=0))
201  {
202  std::ostringstream err_msg;
203  err_msg << "Trying to select block with:\n"
204  << "replacement_block_pt != 0 and wanted == false"
205  << "If you require the block, please set wanted == true.\n"
206  << "row_index: " << row_index << "\n"
207  << "column_index: " << column_index << "\n";
208  throw OomphLibError(err_msg.str(),
209  OOMPH_CURRENT_FUNCTION,
210  OOMPH_EXCEPTION_LOCATION);
211  }
212 #endif
213 
214  this->build(row_index,column_index,wanted,replacement_block_pt);
215  }
216 
217 
218  /// \short Indicate that we require the block (set Wanted to true).
219  void want_block()
220  {
221  Wanted = true;
222  }
223 
224  /// \short Indicate that we do not want the block (set Wanted to false).
226  {
227 #ifdef PARANOID
228  if(Replacement_block_pt !=0)
229  {
230  std::ostringstream warning_msg;
231  warning_msg << "Trying to set Wanted = false, but replacement_block_pt is not null.\n"
232  << "Please call null_replacement_block_pt()\n"
233  << "(remember to free memory if necessary)\n"
234  << "Row_index: " << Row_index << "\n"
235  << "Column_index: " << Column_index << "\n";
236  OomphLibWarning(warning_msg.str(),
237  OOMPH_CURRENT_FUNCTION,
238  OOMPH_EXCEPTION_LOCATION);
239  }
240 #endif
241 
243 
244  Wanted = false;
245  }
246 
247  /// \short Set Replacement_block_pt to null.
249  {
251  }
252 
253  /// \short set Replacement_block_pt.
255  {
256 #ifdef PARANOID
257  if(Wanted == false)
258  {
259  std::ostringstream err_msg;
260  err_msg << "Trying to set replacement_block_pt, but Wanted == false.\n"
261  << "Please call want_block()\n"
262  << "Row_index: " << Row_index << "\n"
263  << "Column_index: " << Column_index << "\n";
264  throw OomphLibError(err_msg.str(),
265  OOMPH_CURRENT_FUNCTION,
266  OOMPH_EXCEPTION_LOCATION);
267  }
268 #endif
269 
271  }
272 
273  /// \short Returns Replacement_block_pt
275  {
276  return Replacement_block_pt;
277  }
278 
279  /// \short Set the row index.
280  void set_row_index(const unsigned& row_index)
281  {
283  }
284 
285  /// \short returns the row index.
286  const unsigned& row_index() const
287  {
288  return Row_index;
289  }
290 
291  /// \short Set the column index.
292  void set_column_index(const unsigned& column_index)
293  {
295  }
296 
297  /// \short returns the column index.
298  const unsigned& column_index() const
299  {
300  return Column_index;
301  }
302 
303  /// \short returns whether the block is wanted or not.
304  const bool& wanted() const
305  {
306  return Wanted;
307  }
308 
309 
310  /// \short Output function, outputs the Row_index, Column_index, Wanted and
311  /// the address of the Replacement_block_pt.
312  /// P.M.: The address of a null pointer on a Mac is 0x0 but for self-tests
313  /// the address needs to be simply 0. Easy (but hacky) check sorts that out...
314  friend std::ostream& operator<<(std::ostream& o_stream,
315  const BlockSelector& block_selector)
316  {
317  o_stream << "Row_index = " << block_selector.row_index() << "\n"
318  << "Column_index = " << block_selector.column_index() << "\n"
319  << "Wanted = " << block_selector.wanted() << "\n"
320  << "Replacement_block_pt = ";
321  if (block_selector.replacement_block_pt()==0)
322  {
323  o_stream << 0;
324  }
325 
326  return o_stream;
327  }
328 
329  private:
330 
331  /// Build function, sets the Row_index, Column_index and Wanted variables.
332  /// the Replacement_block_pt is only set if it is not null. Otherwise it is left alone.
333  void build(const unsigned& row_index,
334  const unsigned& column_index,
335  const bool& wanted,
337  {
340  Wanted = wanted;
341 
342  // Only set the replacement_block_pt if it is wanted. Otherwise we leave it alone.
343  // All constructors should set Replacement_block_pt to 0.
344  if(replacement_block_pt != 0)
345  {
346 #ifdef PARANOID
347  if(Wanted == false)
348  {
349  std::ostringstream err_msg;
350  err_msg << "Trying to set replacement_block_pt, but Wanted == false.\n"
351  << "Please either not set the replacement_block_pt or call the function\n"
352  << "do_not_want_block()\n"
353  << "Row_index: " << Row_index << "\n"
354  << "Column_index: " << Column_index << "\n";
355  throw OomphLibError(err_msg.str(),
356  OOMPH_CURRENT_FUNCTION,
357  OOMPH_EXCEPTION_LOCATION);
358  }
359 #endif
360 
362  }
363  }
364 
365  /// Row index of the block.
366  unsigned Row_index;
367 
368  /// Column index of the block.
369  unsigned Column_index;
370 
371  /// Bool to indicate if we require this block.
372  bool Wanted;
373 
374  /// Pointer to the block.
376 
377 };
378 
379 
380  //============================================================================
381  /// Block Preconditioner base class. The block structure of the
382  /// overall problem is determined from the \c Mesh's constituent
383  /// elements. Each constituent element must be block-preconditionable - i.e
384  /// must implement the \c GeneralisedElements functions \c ndof_types() and
385  /// get_dof_numbers_for_unknowns(...). A \c Problem can have several
386  /// \c Meshes, but each \c Mesh must contain elements with the same DOF types.
387  /// The association between global degrees of freedom and their unique local
388  /// dof numbers is therefore based on information provided by the elements.
389  /// We refer to the local dof numbers provided by the elements as the
390  /// elemental dof numbers.
391  ///
392  /// By default each type of DOF is assumed to be unique type of block,
393  /// but DOF types can be grouped together in a single block when
394  /// block_setup(...) is called.
395  ///
396  /// This class can function in one of two ways. Either it acts as a
397  /// stand-alone block preconditioner which computes and stores
398  /// the association between global degrees of freedom and their unique global
399  /// block numbers itself. Alternatively, the block preconditioner can act as
400  /// a subsidiary block preconditioner within a (larger) master block
401  /// preconditioner (pointed to by Master_block_preconditioner_pt).
402  /// The master block preconditioner
403  /// must have an equal or greater number of block types. Examples
404  /// are the FSI preconditioner which is the 3x3 "master block preconditioner"
405  /// for the Navier-Stokes preconditioners which deals with the
406  /// 2x2 fluid-blocks within the overall structure. In this case, \b only
407  /// the master block preconditioner computes and stores the master
408  /// lookup schemes. All block preconditioners compute and store their own
409  /// optimised lookup schemes.
410  ///
411  /// In cases where a \c Problem contains elements of different element types
412  /// (e.g. fluid and solid elements in a fluid-structure interaction problem),
413  /// access to the elements of the same type must be provided via pointers to
414  /// (possibly auxiliary) \c Meshes that only contain elements of a single
415  /// type. The block preconditioner will then create global block
416  /// numbers by concatenating the block types. Consider, e.g. a fluid-structure
417  /// interaction problem in which the first \c Mesh contains (fluid)
418  /// elements whose degrees of freedom have been subdivided into
419  /// types "0" (the velocity, say) and "1" (the pressure say), while
420  /// the second \c Mesh contains (solid) elements whose degrees of freedom
421  /// are the nodal displacements, classified as its type "0".
422  /// The combined block preconditioner then has three "block types":
423  /// "0": Fluid velocity, "1": Fluid pressure, "2": Solid nodal positions.
424  /// NOTE: currently this preconditioner uses the same communicator as the
425  /// underlying problem. We may need to change this in the future.
426  //============================================================================
427  template<typename MATRIX>
429  {
430  public:
431 
432  /// \short Constructor
434  : Ndof_types_in_mesh(0)
435  {
436  // Initially set the master block preconditioner pointer to zero
437  // indicating that this is stand-alone preconditioner (i.e. the upper most
438  // level preconditioner) that will set up its own block lookup schemes etc.
439  Master_block_preconditioner_pt = 0;
440 
441  // The distribution of the concatenation of the internal block
442  // distributions.
443  // I.e. LinearAlgebraDistributionHelpers::concatenate
444  // (distributions of internal blocks).
445  //
446  // The concatenation of the internal block distributions is stored in two
447  // places depending on if this is the upper-most master block preconditioner
448  // or not.
449  //
450  // If this is a master block preconditioner (Master_block_preconditioner_pt
451  // is null), then it is stored in the variable
452  // Internal_preconditioner_matrix_distribution_pt (below).
453  // For subsidiary block preconditioners, this remains null.
454  //
455  // Because BlockPreconditioners are DistributedLinearAlgebraObjects, they
456  // have a distribution. For the upper-most master block preconditioner,
457  // this is the distribution of the underlying Jacobian.
458  //
459  // For all subsidiary block preconditioners, this remains null. The
460  // concatenation of the distribution of the internal blocks are stored
461  // as the distribution of the BlockPreconditioner.
462  //
463  // This seems inconsistent and I cannot figure out why this is done.
464  Internal_preconditioner_matrix_distribution_pt = 0;
465 
466  // The concatenation of the external block distributions.
467  Preconditioner_matrix_distribution_pt = 0;
468 
469  // Initialise number of rows in this block preconditioner.
470  // This information is maintained if used as subsidiary or
471  // stand-alone block preconditioner (in the latter case it
472  // obviously stores the number of rows within the subsidiary
473  // preconditioner.
474  Nrow=0;
475 
476  // Initialise number of different block types in this preconditioner.
477  // This information is maintained if used as subsidiary or
478  // stand-alone block preconditioner (in the latter case it
479  // obviously stores the number of rows within the subsidiary
480  // preconditioner.
481  Internal_nblock_types=0;
482 
483  // Initialise number of different dof types in this preconditioner.
484  // This information is maintained if used as subsidiary or
485  // stand-alone block preconditioner (in the latter case it
486  // obviously stores the number of rows within the subsidiary
487  // preconditioner.
488  Internal_ndof_types=0;
489 
490  // There are no blocks to start off with.
491  Block_distribution_pt.resize(0);
492 
493  // The distributions of the underlying internal blocks.
494  Internal_block_distribution_pt.resize(0);
495 
496  // The distribution of the dof-level blocks, these are used during the
497  // concatenation process to create the distribution of the blocks.
498  Dof_block_distribution_pt.resize(0);
499 
500  // Clear both the Block_to_dof_map_coarse and Block_to_dof_map_fine
501  // vectors.
502  Block_to_dof_map_coarse.resize(0);
503  Block_to_dof_map_fine.resize(0);
504 
505  // Default the debug flag to false.
506  Recursive_debug_flag = false;
507 
508  // Default the debug flag to false.
509  Debug_flag = false;
510  } // EOFunc constructor
511 
512 
513 
514  /// Destructor
516  {
517  this->clear_block_preconditioner_base();
518  } // EOFunc destructor
519 
520  /// Broken copy constructor
522  {
523  BrokenCopy::broken_copy("BlockPreconditioner");
524  }
525 
526  /// Broken assignment operator
528  {
529  BrokenCopy::broken_assign("BlockPreconditioner");
530  }
531 
532  /// \short Access function to matrix_pt. If this is the master then cast
533  /// the matrix pointer to MATRIX*, error check and return. Otherwise ask
534  /// the master for its matrix pointer.
535  MATRIX* matrix_pt() const
536  {
537  if(is_subsidiary_block_preconditioner())
538  {
539  return master_block_preconditioner_pt()->matrix_pt();
540  }
541  else
542  {
543  MATRIX* m_pt = dynamic_cast<MATRIX*>(Preconditioner::matrix_pt());
544 #ifdef PARANOID
545  if(m_pt == 0)
546  {
547  std::ostringstream error_msg;
548  error_msg << "Matrix is not correct type.";
549  throw OomphLibError(error_msg.str(),
550  OOMPH_CURRENT_FUNCTION,
551  OOMPH_EXCEPTION_LOCATION);
552  }
553 #endif
554  return m_pt;
555  }
556  } // EOFunc matrix_pt()
557 
558 
559  /// \short Toggles on the recursive debug flag. The change goes
560  /// up the block preconditioning hierarchy.
562  {
563  Recursive_debug_flag = true;
564  if(is_subsidiary_block_preconditioner())
565  this->master_block_preconditioner_pt()
566  ->turn_on_recursive_debug_flag();
567  }
568 
569  /// \short Toggles off the recursive debug flag. The change goes
570  /// up the block preconditioning hierarchy.
572  {
573  Recursive_debug_flag = false;
574  if(is_subsidiary_block_preconditioner())
575  this->master_block_preconditioner_pt()
576  ->turn_off_recursive_debug_flag();
577  }
578 
579  /// \short Toggles on the debug flag.
581  {
582  Debug_flag = true;
583  }
584 
585  /// \short Toggles off the debug flag.
587  {
588  Debug_flag = false;
589  }
590 
591  /// \short Function to turn this preconditioner into a
592  /// subsidiary preconditioner that operates within a bigger
593  /// "master block preconditioner (e.g. a Navier-Stokes 2x2 block
594  /// preconditioner dealing with the fluid sub-blocks within a
595  /// 3x3 FSI preconditioner. Once this is done the master block
596  /// preconditioner deals with the block setup etc.
597  /// The vector doftype_in_master_preconditioner_coarse must specify the
598  /// dof number in the master preconditioner that corresponds to a dof number
599  /// in this preconditioner.
600  /// \b 1. The length of the vector is used to determine the number of
601  /// blocks in this preconditioner therefore it must be correctly sized.
602  /// \b 2. block_setup(...) should be called in the master preconditioner
603  /// before this method is called.
604  /// \b 3. block_setup(...) should be called in the corresponding subsidiary
605  /// preconditioner after this method is called.
606  ///
607  /// This calls the other turn_into_subsidiary_block_preconditioner
608  /// function with the identity mapping for doftype_coarsen_map_coarse
609  /// vector.
610  void turn_into_subsidiary_block_preconditioner
611  (BlockPreconditioner<MATRIX>* master_block_prec_pt,
612  const Vector<unsigned>& doftype_in_master_preconditioner_coarse);
613 
614  /// \short Function to turn this preconditioner into a
615  /// subsidiary preconditioner that operates within a bigger
616  /// "master block preconditioner (e.g. a Navier-Stokes 2x2 block
617  /// preconditioner dealing with the fluid sub-blocks within a
618  /// 3x3 FSI preconditioner. Once this is done the master block
619  /// preconditioner deals with the block setup etc.
620  /// The vector doftype_in_master_preconditioner_coarse must specify the
621  /// dof number in the master preconditioner that corresponds to a dof number
622  /// in this preconditioner.
623  /// \b 1. The length of the vector is used to determine the number of
624  /// blocks in this preconditioner therefore it must be correctly sized.
625  /// \b 2. block_setup(...) should be called in the master preconditioner
626  /// before this method is called.
627  /// \b 3. block_setup(...) should be called in the corresponding subsidiary
628  /// preconditioner after this method is called.
629  ///
630  /// The doftype_coarsen_map_coarse is a mapping of the dof numbers in the
631  /// master preconditioner to the dof numbers REQUIRED by THIS preconditioner.
632  /// This allows for coarsening of the dof types if the master preconditioner
633  /// has a more fine grain dof type splitting.
634  ///
635  /// For example, the Lagrangian preconditioner (in 3D with one constraint)
636  /// has doftypes:
637  /// 0 1 2 3 4 5 6 7
638  /// ub vb wb uc vc wc p Lc
639  ///
640  /// We wish to use an existing Navier-Stokes preconditioner, for example,
641  /// LSC, to solve the sub-system associated with the dof numbers
642  /// 0, 1, 2, 3, 4, 5, 6. But the existing LSC preconditioner only works
643  /// with four dof types (3 velocity dof types and 1 pressure dof types).
644  /// We need to coarsen the number of dof types in the master preconditioner.
645  ///
646  /// If the LSC preconditioner requires the dof ordering: u, v, w, p. Then
647  /// the doftype_coarsen_map_coarse will be:
648  /// [0 3] -> u velocity dof type
649  /// [1 4] -> v velocity dof type
650  /// [2 5] -> w velocity dof type
651  /// [6] -> pressure dof type.
652  void turn_into_subsidiary_block_preconditioner
653  (BlockPreconditioner<MATRIX>* master_block_prec_pt,
654  const Vector<unsigned>& doftype_in_master_preconditioner_coarse,
655  const Vector<Vector<unsigned> > & doftype_coarsen_map_coarse);
656 
657 
658 
659  /// \short Determine the size of the matrix blocks and setup the
660  /// lookup schemes relating the global degrees of freedom with
661  /// their "blocks" and their indices (row/column numbers) in those
662  /// blocks.
663  /// The distributions of the preconditioner and the internal blocks are
664  /// automatically specified (and assumed to be uniform) at this
665  /// stage.
666  /// This method should be used if the identity dof-to-block mapping is okay,
667  /// i.e.
668  /// dof number 0 corresponds to block number 0
669  /// dof number 1 corresponds to block number 1
670  /// dof number 2 corresponds to block number 2
671  /// etc...
672  virtual void block_setup();
673 
674  /// \short Determine the size of the matrix blocks and setup the
675  /// lookup schemes relating the global degrees of freedom with
676  /// their "blocks" and their indices (row/column numbers) in those
677  /// blocks.
678  /// The distributions of the preconditioner and the blocks are
679  /// automatically specified (and assumed to be uniform) at this
680  /// stage.
681  /// This method should be used if anything other than the identity
682  /// dof-to-block mapping is required. The argument vector dof_to_block_map
683  /// should be of length ndof. The indices represents the dof types whilst the
684  /// value represents the block types. In general we want:
685  ///
686  /// dof_to_block_map[dof_number] = block_number.
687  void block_setup(const Vector<unsigned>& dof_to_block_map);
688 
689  /// \short Put block (i,j) into output_matrix. This block accounts for any
690  /// coarsening of dof types and any replaced dof-level blocks above this
691  /// preconditioner.
692  void get_block(const unsigned& i, const unsigned& j,
693  MATRIX& output_matrix,
694  const bool& ignore_replacement_block = false) const
695  {
696 #ifdef PARANOID
697  // Check the range of i and j, they should not be greater than nblock_types
698  unsigned n_block_types = this->nblock_types();
699  if((i > n_block_types) || (j > n_block_types))
700  {
701  std::ostringstream err_msg;
702  err_msg << "Requested block("<< i <<","<< j <<")"<<"\n"
703  << "but nblock_types() is " << n_block_types << ".\n"
704  << "Maybe your argument to block_setup(...) is incorrect?"
705  << std::endl;
706  throw OomphLibError(err_msg.str(),
707  OOMPH_CURRENT_FUNCTION,
708  OOMPH_EXCEPTION_LOCATION);
709  }
710 #endif
711 
712  // The logic is this:
713  //
714  // Block_to_dof_map_coarse tells us which dof types belongs in each block.
715  // This is relative to this preconditioner, and describes the external
716  // block and dof type mappings (what the preconditioner writer
717  // expects/sees).
718  //
719  // As such, the dof types in Block_to_dof_map_coarse describes the
720  // dof-level blocks needed to be concatenated to produce block(i,j). These
721  // dofs may have been coarsened.
722  //
723  // Now, the dof blocks to concatenate comes from:
724  // If the dof block exists in Replacement_dof_block_pt, then we make a
725  // deep copy of it. Otherwise, if this is the upper-most master block
726  // preconditioner then we get it from the original matrix via function
727  // internal_get_block(...) otherwise, if this is a subsidiary
728  // block preconditioner, we go one level up the hierarchy and repeat the
729  // process.
730  //
731  //
732  // A small note about indirections which caused me some headache.
733  // Thought I would mention it here in case the below code needs to be
734  // re-visited.
735  //
736  // A small subtlety with indirections:
737  //
738  // The underlying ordering of the dof-level blocks is STILL AND ALWAYS the
739  // `natural' ordering determined by first the elements then the order of
740  // the meshes.
741  //
742  // But during the process of block_setup(...), the external (perceived)
743  // block ordering may have changed. So some indirection has to take place,
744  // this mapping is maintained in Block_to_dof_map_coarse.
745  //
746  // For example, take the Lagrangian preconditioner, which expects the
747  // natural dof type ordering:
748  // 0 1 2 3 4 5
749  // u v p uc vc L
750  //
751  // If the mapping given to block_setup(...) is:
752  // dof_to_block_map = [0, 1, 4, 2, 3, 5]
753  // saying that: dof type 0 goes to block 0
754  // dof type 1 goes to block 1
755  // dof type 2 goes to block 4
756  // dof type 3 goes to block 2
757  // dof type 4 goes to block 3
758  // dof type 5 goes to block 5
759  //
760  // The function block_setup(...) will populate the vector
761  // Block_to_dof_map_coarse with [[0][1][3][4][2][5]],
762  // which says that get_block(0,0) will get the u block
763  // get_block(1,1) will get the v block
764  // get_block(2,2) will get the uc block
765  // get_block(3,3) will get the vc block
766  // get_block(4,4) will get the p block
767  // get_block(5,5) will get the L block
768  //
769  // i.e. the ordering of the block types is a permutation of the dof types,
770  // so that we now have the following block ordering:
771  // 0 1 2 3 4 5 <- block ordering
772  // u v uc vc p L
773  //
774  // Now, the writer expects to work with the block ordering. I.e. when we
775  // replace a dof-level block, say the pressure block, we call
776  // set_replacement_dof_block(4,4,Matrix);
777  // Similarly, when we want the pressure block, we call
778  // get_block(4,4);
779  //
780  // Now, the below code uses the indirection maintained in
781  // Block_to_dof_map_coarse. I.e. When we call get_block(4,4), we use the
782  // mapping Block_to_dof_map_coarse[4] = 2, we get the block (2,2)
783  // (from Replacement_dof_block_pt or internal_get_block), since the
784  // underlying dof_to_block mapping is still the identity, i.e. it has not
785  // changed from:
786  // 0 1 2 3 4 5
787  // u v p uc vc L
788  //
789  // So, block (4,4) (pressure block) maps to the block (2,2).
790 
791  // How many external dof types are in this block?
792  const unsigned ndofblock_in_row = Block_to_dof_map_coarse[i].size();
793  const unsigned ndofblock_in_col = Block_to_dof_map_coarse[j].size();
794 
795  // If there is only one dof block in this block then there is no need to
796  // concatenate.
797  if(ndofblock_in_row == 1 && ndofblock_in_col == 1)
798  {
799  // Get the indirection for the dof we want.
800  const unsigned wanted_dof_row = Block_to_dof_map_coarse[i][0];
801  const unsigned wanted_dof_col = Block_to_dof_map_coarse[j][0];
802 
803  // If the block has NOT been replaced or if we want to ignore the
804  // replacement, then we call get_dof_level_block(...) which will get the
805  // dof-level blocks up the preconditioning hierarchy, dragging down
806  // any replacement dof blocks of parent preconditioners if required.
807  if((Replacement_dof_block_pt.get(wanted_dof_row,wanted_dof_col) == 0) ||
808  ignore_replacement_block)
809  {
810  get_dof_level_block(wanted_dof_row, wanted_dof_col,
811  output_matrix, ignore_replacement_block);
812  }
813  else
814  // Replacement_dof_block_pt.get(wanted_dof_row,wanted_dof_col) is not
815  // null, this means that the block has been replaced. We simply make
816  // a deep copy of it.
817  {
819  Replacement_dof_block_pt.get(wanted_dof_row, wanted_dof_col),
820  output_matrix);
821  }
822  }
823  else
824  // This block contains more than one dof-level block. So we need to
825  // concatenate the (external) dof-level blocks.
826  {
827  // The CRDoubleMatrixHelpers::concatenate_without_communication(...)
828  // takes a DenseMatrix of pointers to CRDoubleMatrices to concatenate.
829  DenseMatrix<CRDoubleMatrix*> tmp_blocks_pt(ndofblock_in_row,
830  ndofblock_in_col,0);
831 
832  // Vector of Vectors of unsigns to indicate whether we have created
833  // CRDoubleMatrices with new or not... so we can delete it later.
834  // 0 - no new CRDoubleMatrix is created.
835  // 1 - a new CRDoubleMatrix is created.
836  // If we ever use C++11, remove this and use smart pointers.
837  Vector<Vector<unsigned> >new_block(ndofblock_in_row,
838  Vector<unsigned>(ndofblock_in_col,0));
839 
840  // Loop through the number of dof block rows and then the number of dof
841  // block columns, either get the pointer from Replacement_dof_block_pt
842  // or from get_dof_level_block(...).
843  for (unsigned row_dofblock = 0; row_dofblock < ndofblock_in_row;
844  row_dofblock++)
845  {
846  // Indirection for the row, as discuss in the large chunk of text
847  // previously.
848  const unsigned wanted_dof_row
849  = Block_to_dof_map_coarse[i][row_dofblock];
850 
851  for (unsigned col_dofblock = 0; col_dofblock < ndofblock_in_col;
852  col_dofblock++)
853  {
854  // Indirection for the column as discussed in the large chunk of text
855  // above.
856  const unsigned wanted_dof_col
857  = Block_to_dof_map_coarse[j][col_dofblock];
858 
859  // Get the pointer from Replacement_dof_block_pt.
860  tmp_blocks_pt(row_dofblock,col_dofblock)
861  = Replacement_dof_block_pt.get(wanted_dof_row,wanted_dof_col);
862 
863  // If the pointer from Replacement_dof_block_pt is null, or if
864  // we have to ignore replacement blocks, build a new CRDoubleMatrix
865  // via get_dof_level_block.
866  if((tmp_blocks_pt(row_dofblock,col_dofblock) == 0) ||
867  ignore_replacement_block)
868  {
869  // We have to create a new CRDoubleMatrix, so put in 1 to indicate
870  // that we have to delete it later.
871  new_block[row_dofblock][col_dofblock] = 1;
872 
873  // Create the new CRDoubleMatrix. Note that we do not use the
874  // indirection, since the indirection is only used one way.
875  tmp_blocks_pt(row_dofblock,col_dofblock) = new CRDoubleMatrix;
876 
877  // Get the dof-level block, as discussed above.
878  get_dof_level_block(wanted_dof_row,
879  wanted_dof_col,
880  *tmp_blocks_pt(row_dofblock,col_dofblock),
881  ignore_replacement_block);
882  }
883  }
884  }
885 
886  // Concatenation of matrices require the distribution of the individual
887  // sub-matrices (for both row and column). This is because concatenation
888  // is implemented without communication in such a way that the rows
889  // and column values are both permuted, the permutation is defined by
890  // the individual distributions of the sub blocks.
891  // Without a vector of distributions describing the distributions of
892  // of the rows, we do not know how to permute the rows. For the columns,
893  // although CRDoubleMatrices does not have a column distribution, the
894  // concatenated matrix must have it's columns permuted, so we mirror
895  // the diagonal and get the corresponding row distribution.
896  //
897  // Confused? - Example: Say we have a matrix with dof blocking
898  //
899  // | a | b | c | d | e |
900  // --|---|---|---|---|---|
901  // a | | | | | |
902  // --|---|---|---|---|---|
903  // b | | | | | |
904  // --|---|---|---|---|---|
905  // c | | | | | |
906  // --|---|---|---|---|---|
907  // d | | | | | |
908  // --|---|---|---|---|---|
909  // e | | | | | |
910  // --|---|---|---|---|---|
911  //
912  // We wish to concatenate the blocks
913  //
914  // | d | e |
915  // --|---|---|
916  // a | | |
917  // --|---|---|
918  // b | | |
919  // --|---|---|
920  //
921  // Then clearly the row distributions required are the distributions for
922  // the dof blocks a and b. But block(a,d) does not have a column
923  // distribution since it is a CRDoubleMatrix! - We use the distribution
924  // mirrored by the diagonal, so the column distributions required to
925  // concatenate these blocks is the same as the distributions of the rows
926  // for dof block d and e.
927 
928  // First we do the row distribution.
929 
930  // Storage for the row distribution pointers.
931  Vector<LinearAlgebraDistribution*> tmp_row_dist_pt(ndofblock_in_row,0);
932 
933  // Loop through the number of dof blocks in the row. For the upper-most
934  // master block preconditioner, the external dof distributions is the
935  // same as the internal BLOCK distributions. Recall what we said above
936  // about the underlying blocks maintaining it's natural ordering.
937  //
938  // If this is a subsidiary block preconditioner, then the distributions
939  // for the dof blocks are stored in Dof_block_distribution_pt. The reason
940  // why this is different for subsidiary block preconditioners is because
941  // subsidiary block preconditioners would have it's dof types coarsened.
942  // Then a single dof distribution in a subsidiary block preconditioner
943  // could be a concatenation of many dof distributions of the master
944  // dof distributions.
945  for (unsigned row_dof_i = 0; row_dof_i < ndofblock_in_row; row_dof_i++)
946  {
947  const unsigned mapped_dof_i = Block_to_dof_map_coarse[i][row_dof_i];
948  if(is_master_block_preconditioner())
949  {
950  tmp_row_dist_pt[row_dof_i]
951  = Internal_block_distribution_pt[mapped_dof_i];
952  }
953  else
954  {
955  tmp_row_dist_pt[row_dof_i]
956  = Dof_block_distribution_pt[mapped_dof_i];
957  }
958  }
959 
960  // Storage for the column distribution pointers.
961  Vector<LinearAlgebraDistribution*> tmp_col_dist_pt(ndofblock_in_col,0);
962 
963  // We do the same thing as before.
964  for (unsigned col_dof_i = 0; col_dof_i < ndofblock_in_col; col_dof_i++)
965  {
966  const unsigned mapped_dof_j = Block_to_dof_map_coarse[j][col_dof_i];
967  if(is_master_block_preconditioner())
968  {
969  tmp_col_dist_pt[col_dof_i]
970  = Internal_block_distribution_pt[mapped_dof_j];
971  }
972  else
973  {
974  tmp_col_dist_pt[col_dof_i]
975  = Dof_block_distribution_pt[mapped_dof_j];
976  }
977  }
978 
979  // Perform the concatenation.
981  tmp_col_dist_pt,
982  tmp_blocks_pt,
983  output_matrix);
984 
985  // Delete any new CRDoubleMatrices we have created.
986  for (unsigned row_i = 0; row_i < ndofblock_in_row; row_i++)
987  {
988  for (unsigned col_i = 0; col_i < ndofblock_in_col; col_i++)
989  {
990  if(new_block[row_i][col_i])
991  {
992  delete tmp_blocks_pt(row_i,col_i);
993  }
994  }
995  }
996  }// else need to concatenate
997  } // EOFunc get_block(...)
998 
999 
1000  /// \short Return block (i,j). If the optional argument
1001  /// ignore_replacement_block is true, then any blocks in
1002  /// Replacement_dof_block_pt will be ignored throughout the preconditioning
1003  /// hierarchy.
1004  MATRIX get_block(const unsigned& i, const unsigned& j,
1005  const bool& ignore_replacement_block = false) const
1006  {
1007  MATRIX output_matrix;
1008  get_block(i, j, output_matrix, ignore_replacement_block);
1009  return output_matrix;
1010  } // EOFunc get_block(...)
1011 
1012  /// \short Set the matrix_pt in the upper-most master preconditioner.
1013  void set_master_matrix_pt(MATRIX* in_matrix_pt)
1014  {
1015  if(is_subsidiary_block_preconditioner())
1016  {
1017  master_block_preconditioner_pt()
1018  ->set_master_matrix_pt(in_matrix_pt);
1019  }
1020  else
1021  {
1022  this->set_matrix_pt(in_matrix_pt);
1023  }
1024  }
1025 
1026  /// \short Get a block from a different matrix using the blocking scheme
1027  /// that has already been set up.
1028  void get_block_other_matrix(const unsigned& i, const unsigned& j,
1029  MATRIX* in_matrix_pt,
1030  MATRIX& output_matrix)
1031  {
1032  MATRIX* backup_matrix_pt = matrix_pt();
1033  set_master_matrix_pt(in_matrix_pt);
1034  get_block(i, j, output_matrix, true);
1035  set_master_matrix_pt(backup_matrix_pt);
1036  } // EOFunc get_block_other_matrix(...)
1037 
1038  /// \short Get all the block matrices required by the block
1039  /// preconditioner. Takes a pointer to a matrix of bools that indicate
1040  /// if a specified sub-block is required for the preconditioning
1041  /// operation. Computes the required block matrices, and stores pointers
1042  /// to them in the matrix block_matrix_pt. If an entry in block_matrix_pt
1043  /// is equal to NULL on return, that sub-block has not been requested and
1044  /// is therefore not available.
1045  ///
1046  /// WARNING: the matrix pointers are created using new so you must delete
1047  /// them all manually!
1048  ///
1049  /// WARNING 2: the matrix pointers in block_matrix_pt MUST be null
1050  /// because Richard in all his wisdom decided to call delete on any
1051  /// non-null pointers. Presumably to avoid fixing his memory leaks
1052  /// properly...
1053  void get_blocks(DenseMatrix<bool>& required_blocks,
1054  DenseMatrix<MATRIX*>& block_matrix_pt) const;
1055 
1056  /// \short Gets dof-level block (i,j).
1057  /// If Replacement_dof_block_pt(i,j) is not null, then the replacement
1058  /// block is returned via a deep copy.
1059  ///
1060  /// Otherwise if this is the uppermost block preconditioner then it calls
1061  /// internal_get_block(i,j), else if it is a subsidiary
1062  /// block preconditioner, it will call it's master block preconditioners'
1063  /// get_dof_level_block function.
1064  void get_dof_level_block(const unsigned& i, const unsigned& j,
1065  MATRIX& output_block,
1066  const bool& ignore_replacement_block = false) const;
1067 
1068 
1069  /// \short Returns a concatenation of the block matrices specified by the
1070  /// argument selected_block. The VectorMatrix selected_block must be
1071  /// correctly sized as it is used to determine the number of sub block
1072  /// matrices to concatenate.
1073  ///
1074  /// For each entry in the VectorMatrix, the following variables must
1075  /// correctly set:
1076  /// BlockSelector::Row_index - Refers to the row index of the block.
1077  /// BlockSelector::Column_index - Refers to the column index of the block.
1078  /// BlockSelector::Wanted - Do we want the block?
1079  /// BlockSelector::Replacement_block_pt - If not null, this block will be used instead of
1080  /// get_block(Row_index,Column_index).
1081  ///
1082  /// For example, assume that we have a matrix of the following blocking:
1083  /// 0 1 2 3 4
1084  /// | a | b | c | d | e |
1085  /// --|---|---|---|---|---|
1086  /// 0 a | | | | | |
1087  /// --|---|---|---|---|---|
1088  /// 1 b | | | | | |
1089  /// --|---|---|---|---|---|
1090  /// 2 c | | | | | |
1091  /// --|---|---|---|---|---|
1092  /// 3 d | | | | | |
1093  /// --|---|---|---|---|---|
1094  /// 4 e | | | | | |
1095  /// --|---|---|---|---|---|
1096  ///
1097  /// If we want a block matrix corresponding to the concatenation of the
1098  /// blocks [(a,d), (a,e)
1099  /// , (b,e)* ]
1100  /// where top left and top right blocks comes from the function
1101  /// get_block(...), the bottom left entry is empty, and the bottom right is
1102  /// a modified block.
1103  ///
1104  /// Then we create a VectorMatrix of size 2 by 2
1105  /// VectorMatrix<BlockSelector> selected_block(2,2);
1106  ///
1107  /// In the [0][0] entry:
1108  /// row index is 0,
1109  /// column index is 3,
1110  /// do we want this block? - yes (true).
1111  /// selected_block[0][0].select_block(0,3,true);
1112  ///
1113  /// In the [0][1] entry:
1114  /// row index is 0,
1115  /// column index is 4,
1116  /// do we want this block? - yes (true).
1117  /// selected_block[0][0].select_block(0,4,true);
1118  ///
1119  /// In the [1][0] entry:
1120  /// row index is 1,
1121  /// column index is 3,
1122  /// do we want this block? - no (false).
1123  /// selected_block[0][0].select_block(1,3,false);
1124  ///
1125  /// In the [1][1] entry:
1126  /// row index is 1,
1127  /// column index is 4,
1128  /// do we want this block? - yes (true).
1129  /// selected_block[0][0].select_block(1,4,true,block_pt);
1130  ///
1131  /// where block_pt is a pointer to the modified block.
1132  ///
1133  /// Then we can call:
1134  ///
1135  /// CRDoubleMatrix my_block = get_concatenated_block(selected_block);
1136  ///
1137  /// NOTE: It is important to set the row and column indices even if you do
1138  /// not want the block. This is because, if we allow the row and column
1139  /// indices to be "not set", then we can have a whole empty block row without
1140  /// any indices. But concatenation of blocks without communication requires
1141  /// both the row and column distributions, so we know how to permute the
1142  /// rows and columns. So in short, we require that the column and row
1143  /// indices to always be set for every entry in the
1144  /// VectorMatrix<BlockSelector> object for convenience and consistency
1145  /// checks.
1147  &selected_block)
1148  {
1149 #ifdef PARANOID
1150 
1151  unsigned const para_selected_block_nrow = selected_block.nrow();
1152  unsigned const para_selected_block_ncol = selected_block.ncol();
1153  unsigned const para_nblocks = this->nblock_types();
1154 
1155  // Check that selected_block size is not 0.
1156  if(para_selected_block_nrow == 0)
1157  {
1158  std::ostringstream error_msg;
1159  error_msg << "selected_block has nrow = 0.\n";
1160  throw OomphLibError(error_msg.str(),
1161  OOMPH_CURRENT_FUNCTION,
1162  OOMPH_EXCEPTION_LOCATION);
1163  }
1164 
1165  // Check that the number of blocks is not outside of the range
1166  // nblock_types(). Since this function builds matrices for block
1167  // preconditioning, it does not make sense for us to concatenate more
1168  // blocks than nblock_types().
1169  if((para_selected_block_nrow > para_nblocks) ||
1170  (para_selected_block_ncol > para_nblocks))
1171  {
1172  std::ostringstream error_msg;
1173  error_msg << "Trying to concatenate a "
1174  << para_selected_block_nrow << " by "
1175  << para_selected_block_ncol
1176  << " block matrix,\n"
1177  << "but there are only "
1178  << para_nblocks << " block types.\n";
1179  throw OomphLibError(error_msg.str(),
1180  OOMPH_CURRENT_FUNCTION,
1181  OOMPH_EXCEPTION_LOCATION);
1182  }
1183 
1184  // Check that selected_block make sense, i.e. the row indices of each row
1185  // are the same, and the column indices of each column are the same.
1186 
1187  // First check if the row indices are consistent.
1188  // For each row, loop through the columns, comparing the row index against
1189  // the first column.
1190  for (unsigned row_i = 0; row_i < para_selected_block_nrow; row_i++)
1191  {
1192  const unsigned col_0_row_index = selected_block[row_i][0].row_index();
1193 
1194  for (unsigned col_i = 0; col_i < para_selected_block_ncol; col_i++)
1195  {
1196  const unsigned para_b_i = selected_block[row_i][col_i].row_index();
1197  const unsigned para_b_j = selected_block[row_i][col_i].column_index();
1198 
1199  if(col_0_row_index != para_b_i)
1200  {
1201  std::ostringstream err_msg;
1202  err_msg << "Block index for block(" << row_i << "," << col_i <<") "
1203  << "contains block indices (" << para_b_i << ","
1204  << para_b_j << ").\n"
1205  << "But the row index for the first column is "
1206  << col_0_row_index <<", they must be the same!\n";
1207  throw OomphLibError(err_msg.str(),
1208  OOMPH_CURRENT_FUNCTION,
1209  OOMPH_EXCEPTION_LOCATION);
1210  }
1211  }
1212  }
1213 
1214  // Do the same for the column indices, consistency check.
1215  // For each column, loop through the rows, comparing the column index
1216  // against the first row.
1217  for (unsigned col_i = 0; col_i < para_selected_block_ncol; col_i++)
1218  {
1219  const unsigned row_0_col_index = selected_block[0][col_i].column_index();
1220 
1221  for (unsigned row_i = 0; row_i < para_selected_block_nrow; row_i++)
1222  {
1223  const unsigned para_b_i = selected_block[row_i][col_i].row_index();
1224  const unsigned para_b_j = selected_block[row_i][col_i].column_index();
1225 
1226  if(row_0_col_index != para_b_j)
1227  {
1228  std::ostringstream err_msg;
1229  err_msg << "Block index for block(" << row_i << "," << col_i <<") "
1230  << "contains block indices (" << para_b_i << ","
1231  << para_b_j << ").\n"
1232  << "But the col index for the first row is "
1233  << row_0_col_index <<", they must be the same!\n";
1234  throw OomphLibError(err_msg.str(),
1235  OOMPH_CURRENT_FUNCTION,
1236  OOMPH_EXCEPTION_LOCATION);
1237  }
1238  }
1239  }
1240 
1241  // Check to see if the values in selected_block is within the range
1242  // nblock_types()
1243  //
1244  // Since we know that the column and row indices are consistent (by the
1245  // two paranoia checks above), we only need to check the column indices
1246  // in the first row, and the row indices in the first column.
1247 
1248  // Check that the row indices in the first column are within the range
1249  // nblock_types()
1250  for (unsigned row_i = 0; row_i < para_selected_block_nrow; row_i++)
1251  {
1252  const unsigned para_b_i = selected_block[row_i][0].row_index();
1253  const unsigned para_b_j = selected_block[row_i][0].column_index();
1254  if(para_b_i > para_nblocks)
1255  {
1256  std::ostringstream err_msg;
1257  err_msg << "Block index for block(" << row_i << ",0) "
1258  << "contains block indices (" << para_b_i << ","
1259  << para_b_j << ").\n"
1260  << "But there are only " << para_nblocks
1261  << " nblock_types().\n";
1262  throw OomphLibError(err_msg.str(),
1263  OOMPH_CURRENT_FUNCTION,
1264  OOMPH_EXCEPTION_LOCATION);
1265  }
1266  }
1267 
1268  // Check that the col indices in the first row are within the range
1269  // nblock_types()
1270  for (unsigned col_i = 0; col_i < para_selected_block_ncol; col_i++)
1271  {
1272  const unsigned para_b_i = selected_block[0][col_i].row_index();
1273  const unsigned para_b_j = selected_block[0][col_i].column_index();
1274  if(para_b_j > para_nblocks)
1275  {
1276  std::ostringstream err_msg;
1277  err_msg << "Block index for block(0," << col_i << ") "
1278  << "contains block indices (" << para_b_i << ","
1279  << para_b_j << ").\n"
1280  << "But there are only " << para_nblocks
1281  << " nblock_types().\n";
1282  throw OomphLibError(err_msg.str(),
1283  OOMPH_CURRENT_FUNCTION,
1284  OOMPH_EXCEPTION_LOCATION);
1285  }
1286  }
1287 
1288  // Stricter test - can be removed is required in the future. For the first
1289  // column, check that the row indices does not repeat.
1290  std::set<unsigned> row_index_set;
1291  for (unsigned row_i = 0; row_i < para_selected_block_nrow; row_i++)
1292  {
1293  std::pair<std::set<unsigned>::iterator,bool> row_index_set_ret;
1294 
1295  const unsigned row_i_index = selected_block[row_i][0].row_index();
1296 
1297  row_index_set_ret
1298  = row_index_set.insert(row_i_index);
1299 
1300  if(!row_index_set_ret.second)
1301  {
1302  std::ostringstream err_msg;
1303  err_msg << "The row " << row_i_index << " is already inserted.\n";
1304  throw OomphLibError(err_msg.str(),
1305  OOMPH_CURRENT_FUNCTION,
1306  OOMPH_EXCEPTION_LOCATION);
1307  }
1308  }
1309 
1310  // Stricter test - can be removed is required in the future. For the first
1311  // row, check that the column indices does not repeat.
1312  std::set<unsigned> col_index_set;
1313  for (unsigned col_i = 0; col_i < para_selected_block_ncol; col_i++)
1314  {
1315  std::pair<std::set<unsigned>::iterator,bool> col_index_set_ret;
1316 
1317  const unsigned col_i_index = selected_block[0][col_i].column_index();
1318 
1319  col_index_set_ret
1320  = col_index_set.insert(col_i_index);
1321 
1322  if(!col_index_set_ret.second)
1323  {
1324  std::ostringstream err_msg;
1325  err_msg << "The col " << col_i_index << " is already inserted.\n";
1326  throw OomphLibError(err_msg.str(),
1327  OOMPH_CURRENT_FUNCTION,
1328  OOMPH_EXCEPTION_LOCATION);
1329  }
1330  }
1331 
1332  // Loop through all the block_pt and check:
1333  // 1) The non-null pointers point to built matrices.
1334  // 2) The distribution matches those defined by selected_block within
1335  // Block_distribution_pt.
1336  for (unsigned block_i = 0; block_i < para_selected_block_nrow; block_i++)
1337  {
1338  for (unsigned block_j = 0; block_j < para_selected_block_ncol; block_j++)
1339  {
1340  const CRDoubleMatrix* tmp_block_pt
1341  = selected_block[block_i][block_j].replacement_block_pt();
1342 
1343  if(tmp_block_pt != 0)
1344  {
1345  if(!tmp_block_pt->built())
1346  {
1347  std::ostringstream err_msg;
1348  err_msg << "The matrix pointed to by block("
1349  << block_i << "," << block_j << ") is not built.\n";
1350  throw OomphLibError(err_msg.str(),
1351  OOMPH_CURRENT_FUNCTION,
1352  OOMPH_EXCEPTION_LOCATION);
1353  }
1354 
1355  const LinearAlgebraDistribution* const tmp_block_dist_pt
1356  = tmp_block_pt->distribution_pt();
1357 
1358  const unsigned row_selected_block
1359  = selected_block[block_i][block_j].row_index();
1360 
1361  const LinearAlgebraDistribution* const another_tmp_block_dist_pt
1362  = block_distribution_pt(row_selected_block);
1363 
1364  if(*tmp_block_dist_pt != *another_tmp_block_dist_pt)
1365  {
1366  std::ostringstream err_msg;
1367  err_msg << "block_distribution_pt " << row_selected_block << "\n"
1368  << "does not match the distribution from the block_pt() "
1369  << " in selected_block["
1370  << block_i << "][" << block_j <<"].\n";
1371  throw OomphLibError(err_msg.str(),
1372  OOMPH_CURRENT_FUNCTION,
1373  OOMPH_EXCEPTION_LOCATION);
1374  }
1375  }
1376  }
1377  }
1378 
1379  // Attempt a similar check for the column index. This is not as rigorous
1380  // since a CRDoubleMatrix does not have a distribution for the columns.
1381  // However, we can check if the ncol of the matrices in block_pt matches
1382  // those in the block_distribution_pt corresponding to the columns.
1383  // (I hope this makes sense... both the row and columns are permuted in
1384  // CRDoubleMatrixHelpers::concatenate_without_communication(...))
1385  //
1386  // The test for the row distributions checks if the nrow_local is correct.
1387  // We do not have the equivalent for columns.
1388  for (unsigned block_i = 0; block_i < para_selected_block_nrow; block_i++)
1389  {
1390  for (unsigned block_j = 0; block_j < para_selected_block_ncol; block_j++)
1391  {
1392  // Cache the block_pt
1393  const CRDoubleMatrix* tmp_block_pt
1394  = selected_block[block_i][block_j].replacement_block_pt();
1395 
1396  if(tmp_block_pt != 0)
1397  {
1398  const unsigned tmp_block_ncol = tmp_block_pt->ncol();
1399 
1400  const unsigned selected_block_col
1401  = selected_block[block_i][block_j].column_index();
1402 
1403  // YES, nrow, this is not incorrect.
1404  const unsigned another_tmp_block_ncol
1405  = block_distribution_pt(selected_block_col)->nrow();
1406 
1407  if(tmp_block_ncol != another_tmp_block_ncol)
1408  {
1409  std::ostringstream err_msg;
1410  err_msg << "block_pt in selected_block["
1411  << block_i << "][" << block_j << "] "
1412  << "has ncol = " << tmp_block_ncol << ".\n"
1413  << "But the corresponding block_distribution_pt("
1414  << selected_block_col << ") has nrow = "
1415  << another_tmp_block_ncol
1416  << ").\n";
1417  throw OomphLibError(err_msg.str(),
1418  OOMPH_CURRENT_FUNCTION,
1419  OOMPH_EXCEPTION_LOCATION);
1420  }
1421  }
1422  }
1423  }
1424 #endif
1425 
1426  // The return matrix.
1427  MATRIX output_matrix;
1428 
1429  // How many sub matrices are there in the row and column?
1430  const unsigned nblock_row = selected_block.nrow();
1431  const unsigned nblock_col = selected_block.ncol();
1432 
1433  // Get the row and col distributions, this is required for concatenation
1434  // without communication.
1435  Vector<LinearAlgebraDistribution*> row_dist_pt(nblock_row,0);
1436  Vector<LinearAlgebraDistribution*> col_dist_pt(nblock_col,0);
1437 
1438  // For the row distributions, use the first column of selected_block
1439  // Also, store the index of the block rows.
1440  Vector<unsigned> block_row_index(nblock_row,0);
1441  for (unsigned row_i = 0; row_i < nblock_row; row_i++)
1442  {
1443  const unsigned selected_row_index
1444  = selected_block[row_i][0].row_index();
1445 
1446  row_dist_pt[row_i] = Block_distribution_pt[selected_row_index];
1447  block_row_index[row_i] = selected_row_index;
1448  }
1449 
1450  // For the col distributions, use the first row of selected_block
1451  Vector<unsigned> block_col_index(nblock_col,0);
1452  for (unsigned col_i = 0; col_i < nblock_col; col_i++)
1453  {
1454  const unsigned selected_col_index
1455  = selected_block[0][col_i].column_index();
1456 
1457  col_dist_pt[col_i] = Block_distribution_pt[selected_col_index];
1458  block_col_index[col_i] = selected_col_index;
1459  }
1460 
1461  // Now build the output matrix. The output_matrix needs a distribution,
1462  // this distribution is a concatenation of the block rows. But because
1463  // concatenation of distributions requires communication, we try to
1464  // minimise this process by creating it once, then store a key to the
1465  // concatenated distribution. First check to see if the block row indices
1466  // is already a key in Auxiliary_block_distribution_pt, if it is in there,
1467  // we use the distribution it corresponds to. Otherwise, we create the
1468  // distribution and store it for possible further use.
1469  std::map<Vector<unsigned>,
1470  LinearAlgebraDistribution* >::const_iterator iter;
1471 
1472  iter = Auxiliary_block_distribution_pt.find(block_row_index);
1473 
1474  if(iter != Auxiliary_block_distribution_pt.end())
1475  {
1476  output_matrix.build(iter->second);
1477  }
1478  else
1479  {
1481  LinearAlgebraDistributionHelpers::concatenate(row_dist_pt,*tmp_dist_pt);
1482  insert_auxiliary_block_distribution(block_row_index,tmp_dist_pt);
1483  output_matrix.build(tmp_dist_pt);
1484  }
1485 
1486  // Do the same for the column dist, since we might need it for the RHS
1487  // vector..
1488  iter = Auxiliary_block_distribution_pt.find(block_col_index);
1489  if(iter == Auxiliary_block_distribution_pt.end())
1490  {
1492  LinearAlgebraDistributionHelpers::concatenate(col_dist_pt,*tmp_dist_pt);
1493  insert_auxiliary_block_distribution(block_col_index,tmp_dist_pt);
1494  }
1495 
1496  // Storage for the pointers to CRDoubleMatrices to concatenate.
1497  DenseMatrix<CRDoubleMatrix*> block_pt(nblock_row,nblock_col,0);
1498 
1499  // Vector of Vectors of unsigns to indicate whether we have created
1500  // CRDoubleMatrices with new or not... so we can delete it later.
1501  // 0 - no new CRDoubleMatrix is created.
1502  // 1 - a new CRDoubleMatrix is created.
1503  // If we ever use C++11, remove this and use smart pointers.
1504  Vector<Vector<unsigned> > new_block(nblock_row,
1505  Vector<unsigned>(nblock_col,0));
1506 
1507  // Get blocks if wanted.
1508  for (unsigned block_i = 0; block_i < nblock_row; block_i++)
1509  {
1510  for (unsigned block_j = 0; block_j < nblock_col; block_j++)
1511  {
1512  const bool block_wanted = selected_block[block_i][block_j].wanted();
1513 
1514  if(block_wanted)
1515  {
1516  CRDoubleMatrix* tmp_block_pt
1517  = selected_block[block_i][block_j].replacement_block_pt();
1518 
1519  if(tmp_block_pt == 0)
1520  {
1521  new_block[block_i][block_j] = 1;
1522 
1523  block_pt(block_i,block_j) = new CRDoubleMatrix;
1524 
1525  // temp variables for readability purposes.
1526  const unsigned tmp_block_i = block_row_index[block_i];
1527  const unsigned tmp_block_j = block_col_index[block_j];
1528 
1529  // Get the block.
1530  this->get_block(tmp_block_i,tmp_block_j,
1531  *block_pt(block_i,block_j));
1532  }
1533  else
1534  {
1535  block_pt(block_i,block_j) = tmp_block_pt;
1536  }
1537  }
1538  }
1539  }
1540 
1541  // Perform the concatenation.
1543  col_dist_pt,
1544  block_pt,
1545  output_matrix);
1546 
1547  // Delete any new CRDoubleMatrices we created.
1548  for (unsigned block_i = 0; block_i < nblock_row; block_i++)
1549  {
1550  for (unsigned block_j = 0; block_j < nblock_col; block_j++)
1551  {
1552  if(new_block[block_i][block_j])
1553  {
1554  delete block_pt(block_i,block_j);
1555  }
1556  }
1557  }
1558 
1559  return output_matrix;
1560  } // EOFunc get_concatenated_block(...)
1561 
1562  /// \short Takes the naturally ordered vector and extracts the blocks
1563  /// indicated by the block number (the values) in the Vector
1564  /// block_vec_number all at once, then concatenates them without
1565  /// communication. Here, the values in block_vec_number is the block number
1566  /// in the current preconditioner.
1567  /// This is a non-const function because distributions may be created
1568  /// and stored in Auxiliary_block_distribution_pt for future use.
1569  void get_concatenated_block_vector(const Vector<unsigned>& block_vec_number,
1570  const DoubleVector& v,
1571  DoubleVector& b);
1572 
1573  /// \short Takes concatenated block ordered vector, b, and copies its
1574  /// entries to the appropriate entries in the naturally ordered vector, v.
1575  /// Here the values in block_vec_number indicates which blocks the vector
1576  /// b is a concatenation of. The block number are those in the current
1577  /// preconditioner. If the preconditioner is a subsidiary block
1578  /// preconditioner the other entries in v that are not associated with it
1579  /// are left alone.
1580  void return_concatenated_block_vector(
1581  const Vector<unsigned>& block_vec_number,
1582  const DoubleVector& b,
1583  DoubleVector& v) const;
1584 
1585  /// \short Takes the naturally ordered vector and rearranges it into a
1586  /// vector of sub vectors corresponding to the blocks, so s[b][i] contains
1587  /// the i-th entry in the vector associated with block b.
1588  /// Note: If the preconditioner is a subsidiary preconditioner then only the
1589  /// sub-vectors associated with the blocks of the subsidiary preconditioner
1590  /// will be included. Hence the length of v is master_nrow() whereas the
1591  /// total length of the s vectors is the sum of the lengths of the
1592  /// individual block vectors defined in block_vec_number.
1593  void get_block_vectors(const Vector<unsigned> & block_vec_number,
1594  const DoubleVector& v,
1595  Vector<DoubleVector >& s) const;
1596 
1597  /// \short Takes the naturally ordered vector and rearranges it into a
1598  /// vector of sub vectors corresponding to the blocks, so s[b][i] contains
1599  /// the i-th entry in the vector associated with block b.
1600  /// Note: If the preconditioner is a subsidiary preconditioner then only the
1601  /// sub-vectors associated with the blocks of the subsidiary preconditioner
1602  /// will be included. Hence the length of v is master_nrow() whereas the
1603  /// total length of the s vectors is Nrow.
1604  /// This is simply a wrapper around the other get_block_vectors(...) function
1605  /// where the block_vec_number Vector is the identity, i.e.
1606  /// block_vec_number is [0, 1, ..., nblock_types - 1].
1607  void get_block_vectors(const DoubleVector& v,
1608  Vector<DoubleVector >& s) const;
1609 
1610  /// \short Takes the vector of block vectors, s, and copies its entries into
1611  /// the naturally ordered vector, v. If this is a subsidiary block
1612  /// preconditioner only those entries in v that are associated with its
1613  /// blocks are affected. The block_vec_number indicates which block the
1614  /// vectors in s came from. The block number corresponds to the block
1615  /// numbers in this preconditioner.
1616  void return_block_vectors(const Vector<unsigned>& block_vec_number,
1617  const Vector<DoubleVector >& s,
1618  DoubleVector& v) const;
1619 
1620  /// \short Takes the vector of block vectors, s, and copies its entries into
1621  /// the naturally ordered vector, v. If this is a subsidiary block
1622  /// preconditioner only those entries in v that are associated with its
1623  /// blocks are affected. The block_vec_number indicates which block the
1624  /// vectors in s came from. The block number corresponds to the block
1625  /// numbers in this preconditioner.
1626  /// This is simply a wrapper around the other return_block_vectors(...)
1627  /// function where the block_vec_number Vector is the identity, i.e.
1628  /// block_vec_number is [0, 1, ..., nblock_types - 1].
1629  void return_block_vectors(const Vector<DoubleVector >& s,
1630  DoubleVector& v) const;
1631 
1632  /// \short Takes the naturally ordered vector, v and returns the n-th
1633  /// block vector, b. Here n is the block number in the current
1634  /// preconditioner.
1635  void get_block_vector(const unsigned& n, const DoubleVector& v,
1636  DoubleVector& b) const;
1637 
1638  /// \short Takes the n-th block ordered vector, b, and copies its entries
1639  /// to the appropriate entries in the naturally ordered vector, v.
1640  /// Here n is the block number in the current block preconditioner.
1641  /// If the preconditioner is a subsidiary block preconditioner
1642  /// the other entries in v that are not associated with it
1643  /// are left alone.
1644  void return_block_vector(const unsigned& n, const DoubleVector& b,
1645  DoubleVector& v) const;
1646 
1647  /// \short Given the naturally ordered vector, v, return
1648  /// the vector rearranged in block order in w. This function calls
1649  /// get_concatenated_block_vector(...) with the identity block mapping.
1650  ///
1651  /// This function has been re-written to work with the new dof type
1652  /// coarsening feature. The old function is kept alive in
1653  /// internal_get_block_ordered_preconditioner_vector(...) and is moved to
1654  /// the private section of the code. The differences between the two are:
1655  ///
1656  /// 1) This function extracts all the block vectors (in one go) via the
1657  /// function internal_get_block_vectors(...), and concatenates them.
1658  ///
1659  /// 2) The old function makes use of the variables ending in "get_ordered",
1660  /// thus is slightly more efficient since it does not have to concatenate
1661  /// any block vectors.
1662  ///
1663  /// 3) The old function no longer respect the new indirections if dof types
1664  /// have been coarsened.
1665  ///
1666  /// 4) This function extracts the most fine grain dof-level vectors and
1667  /// concatenates them. These dof-level vectors respect the re-ordering
1668  /// caused by the coarsening of dof types. The overhead associated with
1669  /// concatenating DoubleVectors without communication is very small.
1670  ///
1671  /// This function should be used.
1672  void get_block_ordered_preconditioner_vector(const DoubleVector& v,
1673  DoubleVector& w);
1674 
1675  /// \short Takes the block ordered vector, w, and reorders it in natural
1676  /// order. Reordered vector is returned in v. Note: If the preconditioner is
1677  /// a subsidiary preconditioner then only the components of the vector
1678  /// associated with the blocks of the subsidiary preconditioner will be
1679  /// included. Hence the length of v is master_nrow() whereas that of the
1680  /// vector w is of length this->nrow().
1681  ///
1682  /// This is the return function for the function
1683  /// get_block_ordered_preconditioner_vector(...).
1684  ///
1685  /// It calls the function return_concatenated_block_vector(...) with the
1686  /// identity block number ordering.
1687  void return_block_ordered_preconditioner_vector(const DoubleVector& w,
1688  DoubleVector& v) const;
1689 
1690  /// \short Return the number of block types.
1691  unsigned nblock_types() const
1692  {
1693 #ifdef PARANOID
1694  if(Block_to_dof_map_coarse.size() == 0)
1695  {
1696  std::ostringstream error_msg;
1697  error_msg <<"The Block_to_dof_map_coarse vector is not setup for \n"
1698  << "this block preconditioner.\n\n"
1699 
1700  << "This vector is always set up within block_setup(...).\n"
1701  << "If block_setup() is already called, then perhaps there is\n"
1702  << "something wrong with your block preconditionable elements.\n"
1703  << std::endl;
1704  throw OomphLibError(error_msg.str(),
1705  OOMPH_CURRENT_FUNCTION,
1706  OOMPH_EXCEPTION_LOCATION);
1707  }
1708 #endif
1709 
1710  // Return the number of block types.
1711  return Block_to_dof_map_coarse.size();
1712  } // EOFunc nblock_types(...)
1713 
1714  /// \short Return the total number of DOF types.
1715  unsigned ndof_types() const
1716  {
1717 #ifdef PARANOID
1718  // Subsidiary preconditioners don't really need the meshes
1719  if (this->is_master_block_preconditioner())
1720  {
1721  std::ostringstream err_msg;
1722  unsigned n=nmesh();
1723  if (n==0)
1724  {
1725  err_msg << "No meshes have been set for this block preconditioner!\n"
1726  << "Set one with set_nmesh(...), set_mesh(...)" << std::endl;
1727  throw OomphLibError(err_msg.str(),
1728  OOMPH_CURRENT_FUNCTION,
1729  OOMPH_EXCEPTION_LOCATION);
1730  for (unsigned m=0;m<n;m++)
1731  {
1732  if (Mesh_pt[m]==0)
1733  {
1734  err_msg << "The mesh pointer to mesh " << m << " is null!\n"
1735  << "Set a non-null one with set_mesh(...)" << std::endl;
1736  throw OomphLibError(err_msg.str(),
1737  OOMPH_CURRENT_FUNCTION,
1738  OOMPH_EXCEPTION_LOCATION);
1739 
1740  }
1741  }
1742  }
1743  }
1744 #endif
1745 
1746  // If this is a subsidiary block preconditioner, then the function
1747  // turn_into_subsidiary_block_preconditioner(...) should have been called,
1748  // this function would have set up the look up lists between coarsened
1749  // dof types and the internal dof types. Of coarse, the user (the writer
1750  // of the preconditioners) should not care about the internal dof types
1751  // and what happens under the hood. Thus they should get the number of
1752  // coarsened dof types (i.e. the number of dof types the preconditioner
1753  // above (parent preconditioner) decides to give to this preconditioner).
1754  if(is_subsidiary_block_preconditioner())
1755  {
1756 #ifdef PARANOID
1757  if(Doftype_coarsen_map_coarse.size() == 0)
1758  {
1759  std::ostringstream error_msg;
1760  error_msg <<"The Doftype_coarsen_map_coarse vector is not setup for \n"
1761  << "this SUBSIDIARY block preconditioner.\n\n"
1762 
1763  << "For SUBSIDARY block preconditioners at any level, this\n"
1764  << "vector is set up in the function \n"
1765  << "turn_into_subsidiary_block_preconditioner(...).\n\n"
1766 
1767  << "Being a SUBSIDIARY block preconditioner means that \n"
1768  << "(Master_block_preconditioner_pt == 0) is true.\n"
1769  << "The Master_block_preconditioner_pt MUST be set in the "
1770  << "function \n"
1771  << "turn_into_subsidiary_block_preconditioner(...).\n\n"
1772 
1773  << "Somewhere, the Master_block_preconditioner_pt pointer is\n"
1774  << "set but not by the function\n"
1775  << "turn_into_subsidiary_block_preconditioner(...).\n"
1776  << std::endl;
1777  throw OomphLibError(error_msg.str(),
1778  OOMPH_CURRENT_FUNCTION,
1779  OOMPH_EXCEPTION_LOCATION);
1780  }
1781 #endif
1782  // return the number of dof types.
1783  return Doftype_coarsen_map_coarse.size();
1784  }
1785  else
1786  // Otherwise the number of ndof types is the same as the internal number
1787  // of dof types, since no coarsening of the dof types is done at the
1788  // top-most master level.
1789  {
1790  return internal_ndof_types();
1791  }
1792  } // EOFunc ndof_types(...)
1793 
1794 
1795  /// \short Access to i-th mesh (of the various meshes that contain block
1796  /// preconditionable elements of the same number of dof type).
1797  ///
1798  /// WARNING: This should only be used if the derived class is the
1799  /// upper-most master block preconditioner. An error is thrown is
1800  /// this function is called from a subsidiary preconditioner.
1801  /// They (and since every block preconditioner can in principle
1802  /// be used as s subsidiary preconditioner: all block preconditioners)
1803  /// should store local copies of "their meshes" (if they're needed
1804  /// for anything)
1805  const Mesh* mesh_pt(const unsigned& i) const
1806  {
1807 #ifdef PARANOID
1808  if(is_subsidiary_block_preconditioner())
1809  {
1810  std::ostringstream error_msg;
1811  error_msg << "The mesh_pt() function should not be called on a\n"
1812  << "subsidiary block preconditioner." << std::endl;
1813  throw OomphLibError(error_msg.str(),
1814  OOMPH_CURRENT_FUNCTION,
1815  OOMPH_EXCEPTION_LOCATION);
1816  }
1817 #endif
1818 
1819  const Mesh* mesh_i_pt = Mesh_pt[i];
1820 
1821 #ifdef PARANOID
1822  if(mesh_i_pt == 0)
1823  {
1824  std::ostringstream error_msg;
1825  error_msg << "Mesh pointer " << mesh_i_pt << " is null.";
1826  throw OomphLibError(error_msg.str(),
1827  OOMPH_CURRENT_FUNCTION,
1828  OOMPH_EXCEPTION_LOCATION);
1829  }
1830 #endif
1831 
1832  return mesh_i_pt;
1833  } // EOFunc mesh_pt(...)
1834 
1835  /// \short Return the number of meshes in Mesh_pt.
1836  ///
1837  /// WARNING: This should only be used if the derived class is the
1838  /// upper-most master block preconditioner. All block preconditioners)
1839  /// should store local copies of "their meshes" (if they're needed
1840  /// for anything)
1841  unsigned nmesh() const
1842  {
1843  return Mesh_pt.size();
1844  } // EOFunc nmesh()
1845 
1846  /// \short Return the block number corresponding to a global index i_dof.
1847  int block_number(const unsigned& i_dof) const
1848  {
1849  int internal_block_number = this->internal_block_number(i_dof);
1850 
1851  if(internal_block_number == -1)
1852  {
1853  return internal_block_number;
1854  }
1855  else
1856  {
1857  // Map the internal block to the "external" block number, i.e. what the
1858  // writer of the preconditioner is expects.
1859  unsigned block_i = 0;
1860  while(std::find(Block_to_dof_map_fine[block_i].begin(),
1861  Block_to_dof_map_fine[block_i].end(),
1862  internal_block_number)
1863  == Block_to_dof_map_fine[block_i].end())
1864  {
1865  block_i++;
1866  }
1867 
1868  return block_i;
1869  }
1870  }
1871 
1872  /// \short Given a global dof number, returns the index in the block it
1873  /// belongs to.
1874  /// This is the overall index, not local block (in parallel).
1875  int index_in_block(const unsigned& i_dof) const
1876  {
1877  // the dof block number
1878  int internal_dof_block_number = this->internal_dof_number(i_dof);
1879 
1880  if(internal_dof_block_number >= 0)
1881  {
1882  // the external block number
1883  unsigned ex_blk_number = this->block_number(i_dof);
1884 
1885  int internal_index_in_dof = this->internal_index_in_dof(i_dof);
1886 
1887  // find the processor which this global index in block belongs to.
1888  unsigned block_proc
1889  = internal_block_distribution_pt(internal_dof_block_number)
1890  ->rank_of_global_row(internal_index_in_dof);
1891 
1892  // Add up all of the first rows.
1893  const unsigned ndof_in_block
1894  = Block_to_dof_map_fine[ex_blk_number].size();
1895 
1896  unsigned index = 0;
1897  for (unsigned dof_i = 0; dof_i < ndof_in_block; dof_i++)
1898  {
1899  index += internal_block_distribution_pt(
1900  Block_to_dof_map_fine[ex_blk_number][dof_i])
1901  ->first_row(block_proc);
1902  }
1903 
1904  // Now add up all the nrow_local up to this dof block.
1905  unsigned j = 0;
1906 
1907  while(int(Block_to_dof_map_fine[ex_blk_number][j])!= internal_dof_block_number)
1908  {
1909  index += internal_block_distribution_pt(
1910  Block_to_dof_map_fine[ex_blk_number][j])->nrow_local(block_proc);
1911  j++;
1912  }
1913 
1914  // Now add the index of this block...
1915  index += (internal_index_in_dof -
1916  internal_block_distribution_pt(
1917  internal_dof_block_number)
1918  ->first_row(block_proc));
1919 
1920  return index;
1921  }
1922 
1923  return -1;
1924  }
1925 
1926  /// \short Access function to the block distributions (const version).
1928  block_distribution_pt(const unsigned& b) const
1929  {
1930 #ifdef PARANOID
1931  if(Block_distribution_pt.size() == 0)
1932  {
1933  std::ostringstream error_msg;
1934  error_msg <<"Block distributions are not set up.\n"
1935  << "Have you called block_setup(...)?\n"
1936  << std::endl;
1937  throw OomphLibError(error_msg.str(),
1938  OOMPH_CURRENT_FUNCTION,
1939  OOMPH_EXCEPTION_LOCATION);
1940  }
1941  if(b > nblock_types())
1942  {
1943  std::ostringstream error_msg;
1944  error_msg <<"You requested the distribution for the block "
1945  << b << ".\n"
1946  << "But there are only " << nblock_types() << " block types.\n"
1947  << std::endl;
1948  throw OomphLibError(error_msg.str(),
1949  OOMPH_CURRENT_FUNCTION,
1950  OOMPH_EXCEPTION_LOCATION);
1951  }
1952 #endif
1953 
1954  return Block_distribution_pt[b];
1955  } // EOFunc block_distribution_pt(...)
1956 
1957  /// \short Access function to the block distributions (non-const version).
1959  block_distribution_pt(const unsigned b)
1960  {
1961 #ifdef PARANOID
1962  if(Block_distribution_pt.size() == 0)
1963  {
1964  std::ostringstream error_msg;
1965  error_msg <<"Block distributions are not set up.\n"
1966  << "Have you called block_setup(...)?\n"
1967  << std::endl;
1968  throw OomphLibError(error_msg.str(),
1969  OOMPH_CURRENT_FUNCTION,
1970  OOMPH_EXCEPTION_LOCATION);
1971  }
1972  if(b > nblock_types())
1973  {
1974  std::ostringstream error_msg;
1975  error_msg <<"You requested the distribution for the block "
1976  << b << ".\n"
1977  << "But there are only " << nblock_types() << " block types.\n"
1978  << std::endl;
1979  throw OomphLibError(error_msg.str(),
1980  OOMPH_CURRENT_FUNCTION,
1981  OOMPH_EXCEPTION_LOCATION);
1982  }
1983 #endif
1984 
1985  return Block_distribution_pt[b];
1986  } // EOFunc block_distribution_pt(...)
1987 
1988  /// \short Access function to the dof-level block distributions.
1990  dof_block_distribution_pt(const unsigned& b)
1991  {
1992 #ifdef PARANOID
1993  if(b > ndof_types())
1994  {
1995  std::ostringstream error_msg;
1996  error_msg <<"You requested the distribution for the dof block "
1997  << b << ".\n"
1998  << "But there are only " << ndof_types() << " DOF types.\n"
1999  << std::endl;
2000  throw OomphLibError(error_msg.str(),
2001  OOMPH_CURRENT_FUNCTION,
2002  OOMPH_EXCEPTION_LOCATION);
2003  }
2004 
2005 #endif
2006 
2007  if(is_master_block_preconditioner())
2008  {
2009 #ifdef PARANOID
2010  if(Internal_block_distribution_pt.size() == 0)
2011  {
2012  std::ostringstream error_msg;
2013  error_msg <<"Internal block distributions are not set up.\n"
2014  << "Have you called block_setup(...)?\n"
2015  << std::endl;
2016  throw OomphLibError(error_msg.str(),
2017  OOMPH_CURRENT_FUNCTION,
2018  OOMPH_EXCEPTION_LOCATION);
2019  }
2020 #endif
2021 
2022  // The dof block is distribution is the same as the internal
2023  // block distribution.
2024  return Internal_block_distribution_pt[b];
2025  }
2026  else
2027  {
2028 #ifdef PARANOID
2029  if(Dof_block_distribution_pt.size() == 0)
2030  {
2031  std::ostringstream error_msg;
2032  error_msg <<"Dof block distributions are not set up.\n"
2033  << "Have you called block_setup(...)?\n"
2034  << std::endl;
2035  throw OomphLibError(error_msg.str(),
2036  OOMPH_CURRENT_FUNCTION,
2037  OOMPH_EXCEPTION_LOCATION);
2038  }
2039 #endif
2040  return Dof_block_distribution_pt[b];
2041  }
2042  } // EOFunc block_distribution_pt(...)
2043 
2044 
2045  /// \short Access function to the distribution of the master
2046  /// preconditioner. If this preconditioner does not have a master
2047  /// preconditioner then the distribution of this preconditioner is returned.
2049  {
2050  if (is_master_block_preconditioner())
2051  {
2052  return this->distribution_pt();
2053  }
2054  else
2055  {
2056  return Master_block_preconditioner_pt->master_distribution_pt();
2057  }
2058  } // EOFunc master_distribution_pt(...)
2059 
2060  /// \short Return the number of DOF types in mesh i.
2061  /// WARNING: This should only be used by the upper-most master block
2062  /// preconditioner. An error is thrown is
2063  /// this function is called from a subsidiary preconditioner.
2064  /// They (and since every block preconditioner can in principle
2065  /// be used as s subsidiary preconditioner: all block preconditioners)
2066  /// should store local copies of "their meshes" (if they're needed
2067  /// for anything)
2068  unsigned ndof_types_in_mesh(const unsigned& i) const
2069  {
2070 #ifdef PARANOID
2071  if (is_subsidiary_block_preconditioner())
2072  {
2073  std::ostringstream err_msg;
2074  err_msg << "A subsidiary block preconditioner should not care about\n"
2075  << "anything to do with meshes.";
2076  throw OomphLibError(err_msg.str(), OOMPH_CURRENT_FUNCTION,
2077  OOMPH_EXCEPTION_LOCATION);
2078  }
2079 #endif
2080  if(Ndof_types_in_mesh.size() == 0)
2081  {
2082  return mesh_pt(i)->ndof_types();
2083  }
2084  else
2085  {
2086  return Ndof_types_in_mesh[i];
2087  }
2088  } // EOFunc ndof_types_in_mesh(...)
2089 
2090  /// \short Return true if this preconditioner is a subsidiary
2091  /// preconditioner.
2093  {
2094  return (this->Master_block_preconditioner_pt != 0);
2095  } // EOFunc is_subsidiary_block_preconditioner()
2096 
2097  /// \short Return true if this preconditioner is the master block
2098  /// preconditioner.
2100  {
2101  return (this->Master_block_preconditioner_pt == 0);
2102  } // EOFunc is_master_block_preconditioner()
2103 
2104  /// \short Set the base part of the filename to output blocks to. If it is
2105  /// set then all blocks will be output at the end of block_setup. If it is
2106  /// left empty nothing will be output.
2107  void set_block_output_to_files(const std::string& basefilename)
2108  {
2109  Output_base_filename = basefilename;
2110  } // EOFunc set_block_output_to_files(...)
2111 
2112  /// \short Turn off output of blocks (by clearing the basefilename string).
2114  {
2115  Output_base_filename.clear();
2116  } // EOFunc disable_block_output_to_files()
2117 
2118  /// \short Test if output of blocks is on or not.
2119  bool block_output_on() const
2120  {
2121  return Output_base_filename.size() > 0;
2122  } // EOFunc block_output_on()
2123 
2124  /// Output all blocks to numbered files. Called at the end of get blocks if
2125  /// an output filename has been set.
2126  void output_blocks_to_files(const std::string& basefilename,
2127  const unsigned& precision = 8) const
2128  {
2129  unsigned nblocks = internal_nblock_types();
2130 
2131  for(unsigned i=0; i<nblocks; i++)
2132  {
2133  for(unsigned j=0; j<nblocks; j++)
2134  {
2135  // Construct the filename.
2136  std::string filename(basefilename + "_block_"
2138  + "_" + StringConversion::to_string(j));
2139 
2140  // Write out the block.
2141  get_block(i,j).sparse_indexed_output(filename, precision, true);
2142  }
2143  }
2144  } // EOFunc output_blocks_to_files(...)
2145 
2146  /// \short A helper method to reduce the memory requirements of block
2147  /// preconditioners. Once the methods get_block(...), get_blocks(...)
2148  /// and build_preconditioner_matrix(...) have been called in this and
2149  /// all subsidiary block preconditioners this method can be called to
2150  /// clean up.
2152  {
2153  if (is_master_block_preconditioner())
2154  {
2155  Index_in_dof_block_dense.clear();
2156  Dof_number_dense.clear();
2157 #ifdef OOMPH_HAS_MPI
2158  Index_in_dof_block_sparse.clear();
2159  Dof_number_sparse.clear();
2160  Global_index_sparse.clear();
2161  Index_in_dof_block_sparse.clear();
2162  Dof_number_sparse.clear();
2163 #endif
2164  Dof_dimension.clear();
2165  }
2166  Ndof_in_block.clear();
2167  Dof_number_to_block_number_lookup.clear();
2168  Block_number_to_dof_number_lookup.clear();
2169  } // EOFunc post_block_matrix_assembly_partial_clear()
2170 
2171  /// \short Access function to the master block preconditioner pt.
2173  {
2174 #ifdef PARANOID
2175  if (is_master_block_preconditioner())
2176  {
2177  std::ostringstream error_message;
2178  error_message << "This block preconditioner does not have "
2179  << "a master preconditioner.";
2180  throw OomphLibError(error_message.str(), OOMPH_CURRENT_FUNCTION,
2181  OOMPH_EXCEPTION_LOCATION);
2182  }
2183 #endif
2184  return Master_block_preconditioner_pt;
2185  } // EOFunc master_block_preconditioner_pt()
2186 
2187  /// \short Clears all BlockPreconditioner data. Called by the destructor
2188  /// and the block_setup(...) methods
2190  {
2191 
2192  Replacement_dof_block_pt.clear();
2193 
2194  // clear the Distributions
2195  this->clear_distribution();
2196  unsigned nblock = Internal_block_distribution_pt.size();
2197  for (unsigned b = 0; b < nblock; b++)
2198  {
2199  delete Internal_block_distribution_pt[b];
2200  }
2201  Internal_block_distribution_pt.resize(0);
2202 
2203  // clear the global index
2204  Global_index.clear();
2205 
2206  // call the post block matrix assembly clear
2207  this->post_block_matrix_assembly_partial_clear();
2208 
2209 #ifdef OOMPH_HAS_MPI
2210  // storage if the matrix is distributed
2211  unsigned nr = Rows_to_send_for_get_block.nrow();
2212  unsigned nc = Rows_to_send_for_get_block.ncol();
2213  for (unsigned p = 0; p < nc; p++)
2214  {
2215  delete[] Rows_to_send_for_get_ordered[p];
2216  delete[] Rows_to_recv_for_get_ordered[p];
2217  for (unsigned b = 0; b < nr; b++)
2218  {
2219  delete[] Rows_to_recv_for_get_block(b,p);
2220  delete[] Rows_to_send_for_get_block(b,p);
2221  }
2222  }
2223  Rows_to_recv_for_get_block.resize(0,0);
2224  Nrows_to_recv_for_get_block.resize(0,0);
2225  Rows_to_send_for_get_block.resize(0,0);
2226  Nrows_to_send_for_get_block.resize(0,0);
2227  Rows_to_recv_for_get_ordered.clear();
2228  Nrows_to_recv_for_get_ordered.clear();
2229  Rows_to_send_for_get_ordered.clear();
2230  Nrows_to_send_for_get_ordered.clear();
2231 
2232 #endif
2233 
2234  // zero
2235  if (is_master_block_preconditioner())
2236  {
2237  Nrow = 0;
2238  Internal_ndof_types = 0;
2239  Internal_nblock_types = 0;
2240  }
2241 
2242  // delete the prec matrix dist pt
2243  delete Internal_preconditioner_matrix_distribution_pt;
2244  Internal_preconditioner_matrix_distribution_pt = 0;
2245  delete Preconditioner_matrix_distribution_pt;
2246  Preconditioner_matrix_distribution_pt = 0;
2247 
2248  // Delete any existing (external) block distributions.
2249  const unsigned n_existing_block_dist
2250  = Block_distribution_pt.size();
2251  for (unsigned dist_i = 0; dist_i < n_existing_block_dist; dist_i++)
2252  {
2253  delete Block_distribution_pt[dist_i];
2254  }
2255 
2256  // Clear the vector.
2257  Block_distribution_pt.clear();
2258 
2259 
2260  // Create the identity key.
2261  Vector<unsigned> preconditioner_matrix_key(n_existing_block_dist,0);
2262  for (unsigned i = 0; i < n_existing_block_dist; i++)
2263  {
2264  preconditioner_matrix_key[i] = i;
2265  }
2266 
2267  // Now iterate through Auxiliary_block_distribution_pt
2268  // and delete all distributions, except for the one which corresponds
2269  // to the identity since this is already deleted.
2270  std::map<Vector<unsigned>, LinearAlgebraDistribution*>::iterator iter
2271  = Auxiliary_block_distribution_pt.begin();
2272 
2273  while(iter != Auxiliary_block_distribution_pt.end())
2274  {
2275  if(iter->first != preconditioner_matrix_key)
2276  {
2277  delete iter->second;
2278  iter++;
2279  }
2280  else
2281  {
2282  ++iter;
2283  }
2284  }
2285 
2286  // Now clear it.
2287  Auxiliary_block_distribution_pt.clear();
2288 
2289  // Delete any dof block distributions
2290  const unsigned ndof_block_dist = Dof_block_distribution_pt.size();
2291  for (unsigned dof_i = 0; dof_i < ndof_block_dist; dof_i++)
2292  {
2293  delete Dof_block_distribution_pt[dof_i];
2294  }
2295  Dof_block_distribution_pt.clear();
2296 
2297  } // EOFunc clear_block_preconditioner_base()
2298 
2299  /// \short debugging method to document the setup.
2300  /// Should only be called after block_setup(...).
2301  void document()
2302  {
2303  oomph_info << std::endl;
2304  oomph_info << "===========================================" << std::endl;
2305  oomph_info << "Block Preconditioner Documentation" << std::endl
2306  << std::endl;
2307  oomph_info << "Number of DOF types: " << internal_ndof_types() << std::endl;
2308  oomph_info << "Number of block types: " << internal_nblock_types()
2309  << std::endl;
2310  oomph_info << std::endl;
2311  if (is_subsidiary_block_preconditioner())
2312  {
2313  for (unsigned d = 0; d < Internal_ndof_types; d++)
2314  {
2315  oomph_info << "Master DOF number " << d << " : "
2316  << this->internal_master_dof_number(d) << std::endl;
2317  }
2318  }
2319  oomph_info << std::endl;
2320  for (unsigned b = 0; b < internal_nblock_types(); b++)
2321  {
2322  oomph_info << "Block " << b << " DOF types:";
2323  for (unsigned i = 0; i < Block_number_to_dof_number_lookup[b].size();
2324  i++)
2325  {
2326  oomph_info << " " << Block_number_to_dof_number_lookup[b][i];
2327  }
2328  oomph_info << std::endl;
2329  }
2330  oomph_info << std::endl;
2331  oomph_info << "Master block preconditioner distribution:" << std::endl;
2332  oomph_info << *master_distribution_pt() << std::endl;
2333  oomph_info << "Internal preconditioner matrix distribution:" << std::endl;
2334  oomph_info << *internal_preconditioner_matrix_distribution_pt() << std::endl;
2335  oomph_info << "Preconditioner matrix distribution:" << std::endl;
2336  oomph_info << *preconditioner_matrix_distribution_pt() << std::endl;
2337  for (unsigned b = 0; b < Internal_nblock_types; b++)
2338  {
2339  oomph_info << "Internal block " << b << " distribution:" << std::endl;
2340  oomph_info << *Internal_block_distribution_pt[b] << std::endl;
2341  }
2342  for (unsigned b = 0; b < nblock_types(); b++)
2343  {
2344  oomph_info << "Block " << b << " distribution:" << std::endl;
2345  oomph_info << *Block_distribution_pt[b] << std::endl;
2346  }
2347 
2348  // DS: the functions called here no longer exist and this function is
2349  // never used as far as I can tell, so it should be fine to comment this
2350  // bit out:
2351  // if (is_master_block_preconditioner())
2352  // {
2353  // oomph_info << "First look-up row: " << this->first_lookup_row()
2354  // << std::endl;
2355  // oomph_info << "Number of look-up rows: "
2356  // << this->nlookup_rows() << std::endl;
2357  // }
2358  oomph_info << "===========================================" << std::endl;
2359  oomph_info << std::endl;
2360  } // EOFunc document()
2361 
2362  /// \short Access function for the Doftype_coarsen_map_fine
2363  /// variable.
2365  {
2366  return Doftype_coarsen_map_fine;
2367  }
2368 
2369  /// \short Returns the most fine grain dof types in a (possibly coarsened)
2370  /// dof type.
2372  {
2373 #ifdef PARANOID
2374  const unsigned n_dof_types = ndof_types();
2375 
2376  if(i >= n_dof_types)
2377  {
2378  std::ostringstream err_msg;
2379  err_msg << "Trying to get the most fine grain dof types in dof type "
2380  << i << ",\nbut there are only " << n_dof_types
2381  << " number of dof types.\n";
2382  throw OomphLibError(err_msg.str(),
2383  OOMPH_CURRENT_FUNCTION,
2384  OOMPH_EXCEPTION_LOCATION);
2385  }
2386 #endif
2387  return Doftype_coarsen_map_fine[i];
2388  }
2389 
2390  /// \short Access function for the number of most fine grain dof types in
2391  /// a (possibly coarsened) dof type.
2392  unsigned nfine_grain_dof_types_in(const unsigned& i) const
2393  {
2394 #ifdef PARANOID
2395  const unsigned n_dof_types = ndof_types();
2396 
2397  if(i >= n_dof_types)
2398  {
2399  std::ostringstream err_msg;
2400  err_msg << "Trying to get the number of most fine grain dof types "
2401  << "in dof type " << i << ",\n"
2402  << "but there are only " << n_dof_types
2403  << " number of dof types.\n";
2404  throw OomphLibError(err_msg.str(),
2405  OOMPH_CURRENT_FUNCTION,
2406  OOMPH_EXCEPTION_LOCATION);
2407  }
2408 #endif
2409  return Doftype_coarsen_map_fine[i].size();
2410  }
2411 
2412  /// \short Access function to the replaced dof-level blocks.
2414  {
2415  return Replacement_dof_block_pt;
2416  } // EOFunc replacement_block_pt()
2417 
2418  /// \short Setup a matrix vector product.
2419  /// matvec_prod_pt is a pointer to the MatrixVectorProduct,
2420  /// block_pt is a pointer to the block matrix,
2421  /// block_col_indices is a vector indicating which block indices does the
2422  /// RHS vector we want to multiply the matrix by.
2423  ///
2424  /// The distribution of the block column must be the same as the
2425  /// RHS vector being solved. By default, OOMPH-LIB's uniform row distribution
2426  /// is employed. When block matrices are concatenated without communication,
2427  /// the columns are permuted, as a result, the distribution of the columns
2428  /// may no longer be uniform.
2430  CRDoubleMatrix* block_pt,
2431  const Vector<unsigned>& block_col_indices)
2432  {
2433  const unsigned nblock = block_col_indices.size();
2434 
2435  if(nblock == 1)
2436  {
2437  const unsigned col_index = block_col_indices[0];
2438  matvec_prod_pt->setup(block_pt,
2439  Block_distribution_pt[col_index]);
2440  }
2441  else
2442  {
2443  std::map<Vector<unsigned>,
2444  LinearAlgebraDistribution* >::const_iterator iter;
2445 
2446  iter = Auxiliary_block_distribution_pt.find(block_col_indices);
2447  if(iter != Auxiliary_block_distribution_pt.end())
2448  {
2449  matvec_prod_pt->setup(block_pt,iter->second);
2450  }
2451  else
2452  {
2453  Vector<LinearAlgebraDistribution*> tmp_vec_dist_pt(nblock,0);
2454  for (unsigned b = 0; b < nblock; b++)
2455  {
2456  tmp_vec_dist_pt[b] = Block_distribution_pt[block_col_indices[b]];
2457  }
2458 
2461  *tmp_dist_pt);
2462  insert_auxiliary_block_distribution(block_col_indices,tmp_dist_pt);
2463  matvec_prod_pt->setup(block_pt,tmp_dist_pt);
2464  }
2465  }
2466  } // EOFunc setup_matrix_vector_product(...)
2467 
2468  /// \short Setup matrix vector product. This is simply a wrapper
2469  /// around the other setup_matrix_vector_product function.
2471  CRDoubleMatrix* block_pt,
2472  const unsigned& block_col_index)
2473  {
2474  Vector<unsigned> col_index_vector(1,block_col_index);
2475  setup_matrix_vector_product(matvec_prod_pt,block_pt,col_index_vector);
2476  } // EOFunc setup_matrix_vector_product(...)
2477 
2478 // private:
2479 
2480  /// \short Given the naturally ordered vector, v, return
2481  /// the vector rearranged in block order in w. This is a legacy function
2482  /// from the old block preconditioning framework. Kept alive in case it may
2483  /// be needed again.
2484  ///
2485  /// This uses the variables ending in "get_ordered". We no longer use this
2486  /// type of method. This function copy values from v and re-order them
2487  /// in "block order" and place them in w. Block order means that the
2488  /// values in w are the same as the concatenated block vectors.
2489  ///
2490  /// I.e. - v is naturally ordered.
2491  /// v -> s_b, v is ordered into blocks vectors
2492  /// (requires communication)
2493  /// concatenate_without_communication(s_{0,...,nblocks},w) gives w.
2494  ///
2495  /// But this function skips out the concatenation part and builds w directly
2496  /// from v.
2497  ///
2498  /// This is nice but the function is implemented in such a way that it
2499  /// always use all the (internal) blocks and concatenated with the
2500  /// identity ordering. I.e. if this preconditioner has 3 block types, then
2501  /// w will always be:
2502  /// concatenate_without_communication([s_0, s_1, s_2], w). There is easy
2503  /// way to change this.
2504  ///
2505  /// Furthermore, it does not take into account the new dof type coarsening
2506  /// feature. So this function will most likely produce the incorrect vector
2507  /// w from what the user intended. It still works, but w will be the
2508  /// concatenation of the most fine grain dof block vectors with the
2509  /// "natural" dof type ordering.
2510  ///
2511  /// This has been superseded by the function
2512  /// get_block_ordered_preconditioner_vector(...) which does the correct
2513  /// thing.
2514  void internal_get_block_ordered_preconditioner_vector(const DoubleVector& v,
2515  DoubleVector& w) const;
2516 
2517  /// \short Takes the block ordered vector, w, and reorders it in the natural
2518  /// order. Reordered vector is returned in v. Note: If the preconditioner is
2519  /// a subsidiary preconditioner then only the components of the vector
2520  /// associated with the blocks of the subsidiary preconditioner will be
2521  /// included. Hence the length of v is master_nrow() whereas that of the
2522  /// vector w is of length this->nrow().
2523  ///
2524  /// This is the return function for the function
2525  /// internal_get_block_ordered_preconditioner_vector(...).
2526  /// Both internal_get_block_ordered_preconditioner_vector(...) and
2527  /// internal_return_block_ordered_preconditioner_vector(...) has been
2528  /// superseded by the functions
2529  ///
2530  /// get_block_ordered_preconditioner_vector(...) and
2531  /// return_block_ordered_preconditioner_vector(...),
2532  ///
2533  /// Thus this function is moved to the private section of the code.
2534  void internal_return_block_ordered_preconditioner_vector(
2535  const DoubleVector& w, DoubleVector& v) const;
2536 
2537  /// \short Return the number internal blocks. This should be the same
2538  /// as the number of internal dof types. Internally, the block
2539  /// preconditioning framework always work with the most fine grain
2540  /// blocks. I.e. it always deal with the most fine grain dof-level blocks.
2541  /// This allows for coarsening of dof types. When we extract a block,
2542  /// we look at the Block_to_dof_map_fine vector to find out which most fine
2543  /// grain dof types belongs to this block.
2544  ///
2545  /// The preconditioner writer should not have to deal with internal
2546  /// dof/block types and thus this function has been moved to private.
2547  ///
2548  /// This is legacy code from before the coarsening dof type functionality
2549  /// was added. This is kept alive because it is still used in the
2550  /// internal workings of the block preconditioning framework.
2551  ///
2552  /// The function nblock_types(...) should be used if the number of block
2553  /// types is required.
2554  unsigned internal_nblock_types() const
2555  {
2556 #ifdef PARANOID
2557  if(Internal_nblock_types == 0)
2558  {
2559  std::ostringstream err_msg;
2560  err_msg <<"(Internal_nblock_types == 0) is true. \n"
2561  << "Did you remember to call the function block_setup(...)?\n\n"
2562 
2563  << "This variable is always set up within block_setup(...).\n"
2564  << "If block_setup() is already called, then perhaps there is\n"
2565  << "something wrong with your block preconditionable elements.\n"
2566  << std::endl;
2567  throw OomphLibError(err_msg.str(),
2568  OOMPH_CURRENT_FUNCTION,
2569  OOMPH_EXCEPTION_LOCATION);
2570  }
2571 
2572  if(Internal_nblock_types != internal_ndof_types())
2573  {
2574  std::ostringstream err_msg;
2575  err_msg << "The number of internal block types and "
2576  << "internal dof types does not match... \n\n"
2577  << "Internally, the number of block types and the number of dof "
2578  << "types must be the same.\n"
2579  << std::endl;
2580  throw OomphLibError(err_msg.str(),
2581  OOMPH_CURRENT_FUNCTION,
2582  OOMPH_EXCEPTION_LOCATION);
2583  }
2584 #endif
2585 
2586  // return the number of internal block types.
2587  return Internal_nblock_types;
2588  } // EOFunc internal_nblock_types(...)
2589 
2590  /// \short Return the number of internal dof types. This is the number of
2591  /// most fine grain dof types. The preconditioner writer should not have to
2592  /// concern him/her-self with the internal dof/block types. Thus this fuction
2593  /// is moved to private.
2594  /// We have kept this function alive since it it still used deep within
2595  /// the inner workings of the block preconditioning framework.
2596  unsigned internal_ndof_types() const
2597  {
2598  if (is_subsidiary_block_preconditioner())
2599  // If this is a subsidiary block preconditioner, then the variable
2600  // Internal_ndof_types must always be set up.
2601  {
2602 #ifdef PARANOID
2603  if(Internal_ndof_types == 0)
2604  {
2605  std::ostringstream error_msg;
2606  error_msg <<"(Internal_ndof_types == 0) is true.\n"
2607  << "This means that the Master_block_preconditioner_pt pointer is\n"
2608  << "set but possibly not by the function\n"
2609  << "turn_into_subsidiary_block_preconditioner(...).\n\n"
2610 
2611  << "This goes against the block preconditioning framework "
2612  << "methodology.\n"
2613  << "Many machinery relies on the look up lists set up by the \n"
2614  << "function turn_into_subsidiary_block_preconditioner(...) \n"
2615  << "between the parent and child block preconditioners.\n"
2616  << std::endl;
2617  throw OomphLibError(error_msg.str(),
2618  OOMPH_CURRENT_FUNCTION,
2619  OOMPH_EXCEPTION_LOCATION);
2620  }
2621 #endif
2622  return Internal_ndof_types;
2623  }
2624  else
2625  // Else, this is a master block preconditioner, calculate the number of
2626  // dof types from the meshes.
2627  {
2628  unsigned ndof = 0;
2629  for (unsigned i = 0; i < nmesh(); i++)
2630  {ndof += ndof_types_in_mesh(i);}
2631  return ndof;
2632  }
2633  } // EOFunc internal_ndof_types(...)
2634 
2635  /// \short Takes the n-th block ordered vector, b, and copies its entries
2636  /// to the appropriate entries in the naturally ordered vector, v.
2637  /// Here n is the block number in the current block preconditioner.
2638  /// If the preconditioner is a subsidiary block preconditioner
2639  /// the other entries in v that are not associated with it
2640  /// are left alone.
2641  ///
2642  /// This version works with the internal block types. This is legacy code
2643  /// but is kept alive, hence moved to private. Please use the
2644  /// function "return_block_vector(...)".
2645  void internal_return_block_vector(const unsigned& n,
2646  const DoubleVector& b,
2647  DoubleVector& v) const;
2648 
2649  /// \short A helper function, takes the naturally ordered vector, v,
2650  /// and extracts the n-th block vector, b.
2651  /// Here n is the block number in the current preconditioner.
2652  /// NOTE: The ordering of the vector b is the same as the
2653  /// ordering of the block matrix from internal_get_block(...).
2654  void internal_get_block_vector(
2655  const unsigned& n, const DoubleVector& v, DoubleVector& b) const;
2656 
2657 
2658  /// \short Takes the naturally ordered vector and
2659  /// rearranges it into a vector of sub vectors corresponding to the blocks,
2660  /// so s[b][i] contains the i-th entry in the vector associated with block b.
2661  /// The block_vec_number indicates which blocks we want.
2662  /// These blocks and vectors are those corresponding to the internal blocks.
2663  /// Note: If the preconditioner is a subsidiary preconditioner then only the
2664  /// sub-vectors associated with the blocks of the subsidiary preconditioner
2665  /// will be included. Hence the length of v is master_nrow() whereas the
2666  /// total length of the s vectors is the sum of the Nrow of the sub vectors.
2667  void internal_get_block_vectors(
2668  const Vector<unsigned>& block_vec_number,
2669  const DoubleVector& v, Vector<DoubleVector >& s) const;
2670 
2671  /// \short A helper function, takes the naturally ordered vector and
2672  /// rearranges it into a vector of sub vectors corresponding to the blocks,
2673  /// so s[b][i] contains the i-th entry in the vector associated with block b.
2674  /// The block_vec_number indicates which blocks we want.
2675  /// These blocks and vectors are those corresponding to the internal blocks.
2676  /// Note: If the preconditioner is a subsidiary preconditioner then only the
2677  /// sub-vectors associated with the blocks of the subsidiary preconditioner
2678  /// will be included. Hence the length of v is master_nrow() whereas the
2679  /// total length of the s vectors is the sum of the Nrow of the sub vectors.
2680  /// This is simply a wrapper around the other internal_get_block_vectors(...)
2681  /// function with the identity block_vec_number vector.
2682  void internal_get_block_vectors(
2683  const DoubleVector& v, Vector<DoubleVector >& s) const;
2684 
2685  /// \short A helper function, takes the vector of block vectors, s, and
2686  /// copies its entries into the naturally ordered vector, v.
2687  /// If this is a subsidiary block preconditioner only those entries in v
2688  /// that are associated with its blocks are affected.
2689  void internal_return_block_vectors(
2690  const Vector<unsigned>& block_vec_number,
2691  const Vector<DoubleVector >& s, DoubleVector& v) const;
2692 
2693  /// \short A helper function, takes the vector of block vectors, s, and
2694  /// copies its entries into the naturally ordered vector, v.
2695  /// If this is a subsidiary block preconditioner only those entries in v
2696  /// that are associated with its blocks are affected.
2697  /// This is simple a wrapper around the other
2698  /// internal_return_block_vectors(...) function with the identity
2699  /// block_vec_number vector.
2700  void internal_return_block_vectors(
2701  const Vector<DoubleVector >& s, DoubleVector& v) const;
2702 
2703  /// \short Gets block (i,j) from the matrix pointed to by
2704  /// Matrix_pt and returns it in output_block. This is associated with the
2705  /// internal blocks. Please use the other get_block(...) function.
2706  void internal_get_block(const unsigned& i, const unsigned& j,
2707  MATRIX& output_block) const;
2708 
2709  /// \short Return the block number corresponding to a global index i_dof.
2710  /// This returns the block number corresponding to the internal blocks.
2711  /// What this means is that this returns the most fine grain dof-block
2712  /// number which this global index i_dof corresponds to. Since the writer
2713  /// of the preconditioner does not need to care about the internal block
2714  /// types, this function should not be used and thus moved to private.
2715  /// This function should not be removed since it is still used deep within
2716  /// the inner workings of the block preconditioning framework.
2717  int internal_block_number(const unsigned& i_dof) const
2718  {
2719  int dn = internal_dof_number(i_dof);
2720  if (dn == -1)
2721  {
2722  return dn;
2723  }
2724  else
2725  {
2726  return Dof_number_to_block_number_lookup[dn];
2727  }
2728  } // EOFunc internal_block_number(...)
2729 
2730  /// \short Return the index in the block corresponding to a global block
2731  /// number i_dof. The index returned corresponds to the internal blocks,
2732  /// which is the most fine grain dof blocks.
2733  int internal_index_in_block(const unsigned& i_dof) const
2734  {
2735  // the index in the dof block
2736  unsigned index = internal_index_in_dof(i_dof);
2737 
2738  // the dof block number
2739  int internal_dof_block_number = internal_dof_number(i_dof);
2740  if (internal_dof_block_number >= 0)
2741  {
2742 
2743  // the 'actual' block number
2744  unsigned blk_number = internal_block_number(i_dof);
2745 
2746  // compute the index in the block
2747  unsigned j = 0;
2748  while (int(Block_number_to_dof_number_lookup[blk_number][j]) !=
2749  internal_dof_block_number)
2750  {
2751  index +=
2752  internal_dof_block_dimension
2753  (Block_number_to_dof_number_lookup[blk_number][j]);
2754  j++;
2755  }
2756 
2757  // and return
2758  return index;
2759  }
2760  return -1;
2761  } // EOFunc internal_index_in_block(...)
2762 
2763  /// \short Access function to the internal block distributions.
2765  internal_block_distribution_pt(const unsigned& b) const
2766  {
2767 #ifdef PARANOID
2768  if(Internal_block_distribution_pt.size() == 0)
2769  {
2770  std::ostringstream error_msg;
2771  error_msg <<"Internal block distributions are not set up.\n"
2772  << "Have you called block_setup(...)?\n"
2773  << std::endl;
2774  throw OomphLibError(error_msg.str(),
2775  OOMPH_CURRENT_FUNCTION,
2776  OOMPH_EXCEPTION_LOCATION);
2777  }
2778  if(b > internal_nblock_types())
2779  {
2780  std::ostringstream error_msg;
2781  error_msg <<"You requested the distribution for the internal block "
2782  << b << ".\n" << "But there are only "
2783  << internal_nblock_types()
2784  << " block types.\n" << std::endl;
2785  throw OomphLibError(error_msg.str(),
2786  OOMPH_CURRENT_FUNCTION,
2787  OOMPH_EXCEPTION_LOCATION);
2788  }
2789 #endif
2790  return Internal_block_distribution_pt[b];
2791  } // EOFunc internal_block_distribution_pt(...)
2792 
2793  /// \short insert a Vector<unsigned> and LinearAlgebraDistribution* pair
2794  /// into Auxiliary_block_distribution_pt. The
2795  /// Auxiliary_block_distribution_pt should only contain pointers to
2796  /// distributions concatenated at this block level. We try to ensure this by
2797  /// checking if the block_vec_number vector is within the range
2798  /// nblock_types(). Of course, this does not guarantee correctness, but this
2799  /// is the least we can do.
2801  const Vector<unsigned>& block_vec_number,
2802  LinearAlgebraDistribution* dist_pt)
2803  {
2804  #ifdef PARANOID
2805  const unsigned max_block_number
2806  = *std::max_element(block_vec_number.begin(),
2807  block_vec_number.end());
2808 
2809  const unsigned nblocks = nblock_types();
2810  if(max_block_number >= nblocks)
2811  {
2812  std::ostringstream err_msg;
2813  err_msg << "Cannot insert into Auxiliary_block_distribution_pt\n"
2814  << "because " << max_block_number << " is equal to or \n"
2815  << "greater than " << nblocks << ".\n";
2816  throw OomphLibError(err_msg.str(),
2817  OOMPH_CURRENT_FUNCTION,
2818  OOMPH_EXCEPTION_LOCATION);
2819  }
2820 
2821  // Now check if the pair already exists in Auxiliary_block_distribution_pt.
2822  // This is a stricter test and can be removed if required.
2823 
2824  // Attempt to get an iterator pointing to the pair with the value
2825  // block_vec_number.
2826  std::map<Vector<unsigned>,
2827  LinearAlgebraDistribution* >::const_iterator iter
2828  = Auxiliary_block_distribution_pt.find(block_vec_number);
2829 
2830  if(iter != Auxiliary_block_distribution_pt.end())
2831  // If it exists, we throw an error
2832  {
2833  std::ostringstream err_msg;
2834  err_msg << "Cannot insert into Auxiliary_block_distribution_pt\n"
2835  << "because the first in the pair already exists.\n";
2836  throw OomphLibError(err_msg.str(),
2837  OOMPH_CURRENT_FUNCTION,
2838  OOMPH_EXCEPTION_LOCATION);
2839  }
2840  #endif
2841 
2842  Auxiliary_block_distribution_pt.insert(
2843  std::make_pair(block_vec_number,
2844  dist_pt));
2845  } // insert_auxiliary_block_distribution(...)
2846 
2847  /// \short Private helper function to check that every element in the block
2848  /// matrix (i,j) matches the corresponding element in the original matrix
2849  void block_matrix_test(const unsigned& i,
2850  const unsigned& j,
2851  const MATRIX* block_matrix_pt) const;
2852 
2853  /// \short Get the index of first occurrence of value in a vector.
2854  /// If the element does not exist, -1 is returned.
2855  /// The optional parameter indicates of the Vector is sorted or not.
2856  /// Complexity: if the Vector is sorted, then on average, logarithmic in the
2857  /// distance between first and last: Performs approximately log2(N)+2
2858  /// element comparisons.
2859  /// Otherwise, up to linear in the distance between first and last:
2860  /// Compares elements until a match is found.
2861  template<typename myType>
2862  inline int get_index_of_value(const Vector<myType>& vec,
2863  const myType val,
2864  const bool sorted = false) const
2865  {
2866  if(sorted)
2867  {
2868  typename Vector<myType>::const_iterator low
2869  = std::lower_bound(vec.begin(),vec.end(),val);
2870 
2871  return (low == vec.end() || *low != val) ? -1 : (low - vec.begin());
2872  }
2873  else
2874  {
2875  int pos = std::find(vec.begin(),vec.end(),val) - vec.begin();
2876  return (pos < int(vec.size()) && pos >=0) ? pos : -1;
2877  }
2878  }
2879 
2880  private:
2881 
2882  protected:
2883 
2884  /// \short Specify the number of meshes required by this block
2885  /// preconditioner.
2886  /// Note: elements in different meshes correspond to different types
2887  /// of DOF.
2888  void set_nmesh(const unsigned& n)
2889  {
2890  Mesh_pt.resize(n,0);
2891  Allow_multiple_element_type_in_mesh.resize(n,0);
2892  } // EOFunc set_nmesh(...)
2893 
2894 
2895  /// \short Set the i-th mesh for this block preconditioner.
2896  /// Note:
2897  /// The method set_nmesh(...) must be called before this method
2898  /// to specify the number of meshes.
2899  /// By default, it is assumed that each mesh only contains elements of the
2900  /// same type. This condition may be relaxed by setting the boolean
2901  /// allow_multiple_element_type_in_mesh to true, however, each mesh must only
2902  /// contain elements with the same number of dof types.
2903  void set_mesh(const unsigned& i, const Mesh* const mesh_pt,
2904  const bool &allow_multiple_element_type_in_mesh = false)
2905  {
2906 #ifdef PARANOID
2907  // paranoid check that mesh i can be set
2908  if (i >= nmesh())
2909  {
2910  std::ostringstream err_msg;
2911  err_msg
2912  << "The mesh pointer has space for " << nmesh()
2913  << " meshes.\n" << "Cannot store a mesh at entry " << i << "\n"
2914  << "Has set_nmesh(...) been called?";
2915  throw OomphLibError(err_msg.str(),
2916  OOMPH_CURRENT_FUNCTION,
2917  OOMPH_EXCEPTION_LOCATION);
2918  }
2919 
2920  // Check that the mesh pointer is not null.
2921  if(mesh_pt == 0)
2922  {
2923  std::ostringstream err_msg;
2924  err_msg
2925  << "Tried to set the " << i << "-th mesh pointer, but it is null.";
2926  throw OomphLibError(err_msg.str(),
2927  OOMPH_CURRENT_FUNCTION,
2928  OOMPH_EXCEPTION_LOCATION);
2929  }
2930 #endif
2931 
2932  // store the mesh pt and n dof types
2933  Mesh_pt[i]=mesh_pt;
2934 
2935  // Does this mesh contain multiple element types?
2936  Allow_multiple_element_type_in_mesh[i]
2937  = unsigned(allow_multiple_element_type_in_mesh);
2938  } // EOFunc set_mesh(...)
2939 
2940 
2941  /// \short Set replacement dof-level blocks.
2942  /// Only dof-level blocks can be set. This is important due to how the
2943  /// dof type coarsening feature operates.
2944  ///
2945  /// IMPORTANT: The block indices (block_i, block_j) is the dof-level
2946  /// ordering, NOT the block-ordering. The block-ordering is determined by
2947  /// the parameters given to block_setup(...).
2948  /// The DOF-ordering is determined by the two-level ordering scheme of
2949  /// first the elements, then the meshes.
2950  void set_replacement_dof_block(const unsigned &block_i,
2951  const unsigned &block_j,
2952  CRDoubleMatrix* replacement_dof_block_pt)
2953  {
2954 #ifdef PARANOID
2955  // Check if block_setup(...) has been called.
2956  if(nblock_types() == 0)
2957  {
2958  std::ostringstream err_msg;
2959  err_msg << "nblock_types() is 0, has block_setup(...) been called?\n";
2960  throw OomphLibError(err_msg.str(),
2961  OOMPH_CURRENT_FUNCTION,
2962  OOMPH_EXCEPTION_LOCATION);
2963  }
2964 
2965 
2966  // Range checking for replacement dof block.
2967  unsigned para_ndof_types = this->ndof_types();
2968 
2969  if((block_i >= para_ndof_types) ||
2970  (block_j >= para_ndof_types))
2971  {
2972  std::ostringstream err_msg;
2973  err_msg << "Replacement dof block ("
2974  << block_i << "," << block_j << ") is outside of range:\n"
2975  << para_ndof_types;
2976  throw OomphLibError(err_msg.str(),
2977  OOMPH_CURRENT_FUNCTION,
2978  OOMPH_EXCEPTION_LOCATION);
2979  }
2980 
2981 
2982 // // Check that the most fine grain mapping has been used in block_setup(...)
2983 // // i.e. nblock_types() == ndof_types()
2984 // if(ndof_types() != nblock_types())
2985 // {
2986 // std::ostringstream err_msg;
2987 // err_msg << "ndof_types() != nblock_types()\n"
2988 // << "Only the dof-level blocks can be replaced.\n"
2989 // << "Please re-think your blocking scheme.\n";
2990 // throw OomphLibError(err_msg.str(),
2991 // OOMPH_CURRENT_FUNCTION,
2992 // OOMPH_EXCEPTION_LOCATION);
2993 // }
2994 
2995  // Check that the replacement block pt is not null
2996  if(replacement_dof_block_pt == 0)
2997  {
2998  std::ostringstream err_msg;
2999  err_msg << "Replacing block(" << block_i << "," << block_i << ")\n"
3000  << " but the pointer is NULL." << std::endl;
3001  throw OomphLibError(err_msg.str(),
3002  OOMPH_CURRENT_FUNCTION,
3003  OOMPH_EXCEPTION_LOCATION);
3004  }
3005 
3006  // Check that the replacement block has been built
3007  if(!replacement_dof_block_pt->built())
3008  {
3009  std::ostringstream err_msg;
3010  err_msg << "Replacement block(" << block_i << "," << block_i << ")"
3011  << " is not built." << std::endl;
3012  throw OomphLibError(err_msg.str(),
3013  OOMPH_CURRENT_FUNCTION,
3014  OOMPH_EXCEPTION_LOCATION);
3015  }
3016 
3017  // Check if the distribution matches. Determine which natural ordering dof
3018  // this should go to. I.e. we convert from dof-block index to dof index.
3019  // Luckily, this is stored in Block_to_dof_map_coarse.
3020 // const unsigned para_dof_block_i = Block_to_dof_map_coarse[block_i][0];
3021  const unsigned para_dof_block_i = block_i;
3022 
3023  if(*dof_block_distribution_pt(para_dof_block_i) !=
3024  *replacement_dof_block_pt->distribution_pt() )
3025  {
3026  std::ostringstream err_msg;
3027  err_msg << "The distribution of the replacement dof_block_pt\n"
3028  << "is different from the Dof_block_distribution_pt["
3029  << para_dof_block_i<<"].\n";
3030  throw OomphLibError(err_msg.str(),
3031  OOMPH_CURRENT_FUNCTION,
3032  OOMPH_EXCEPTION_LOCATION);
3033  }
3034 
3035  // Now that we know the distribution of the replacement block is
3036  // correct, we check the number of columns.
3037  const unsigned para_dof_block_j = block_j;
3038  unsigned para_replacement_block_ncol = replacement_dof_block_pt->ncol();
3039  unsigned para_required_ncol
3040  = dof_block_distribution_pt(para_dof_block_j)->nrow();
3041  if(para_replacement_block_ncol != para_required_ncol)
3042  {
3043  std::ostringstream err_msg;
3044  err_msg << "Replacement dof block has ncol = "
3045  << para_replacement_block_ncol << ".\n"
3046  << "But required ncol is " << para_required_ncol << ".\n";
3047  throw OomphLibError(err_msg.str(),
3048  OOMPH_CURRENT_FUNCTION,
3049  OOMPH_EXCEPTION_LOCATION);
3050  }
3051 #endif
3052 
3053  // Block_to_dof_map_coarse[x][0] sense because we only can use this if
3054  // nblock_types() == ndof_types(), i.e. each sub-vector is of length 1.
3055  //
3056  // We use this indirection so that the placement of the pointer is
3057  // consistent with internal_get_block(...).
3058 // const unsigned dof_block_i = Block_to_dof_map_coarse[block_i][0];
3059 // const unsigned dof_block_j = Block_to_dof_map_coarse[block_j][0];
3060 
3061 // Replacement_dof_block_pt(dof_block_i,dof_block_j)
3062 // = replacement_dof_block_pt;
3063 
3064  Replacement_dof_block_pt(block_i,block_j)
3065  = replacement_dof_block_pt;
3066  }
3067 
3068  /// \short Check if any of the meshes are distributed. This is equivalent
3069  /// to problem.distributed() and is used as a replacement.
3071  {
3072 #ifdef OOMPH_HAS_MPI
3073  // is_mesh_distributed() is only available with MPI
3074  for(unsigned i=0, n=nmesh(); i<n; i++)
3075  {
3076  if(mesh_pt(i)->is_mesh_distributed()) { return true; }
3077  }
3078 #endif
3079  return false;
3080  }
3081 
3082  /// \short Return the number of the block associated with global unknown
3083  /// i_dof. If this preconditioner is a subsidiary block preconditioner then
3084  /// the block number in the subsidiary block preconditioner is returned. If
3085  /// a particular global DOF is not associated with this preconditioner then
3086  /// -1 is returned
3087  int internal_dof_number(const unsigned& i_dof) const
3088  {
3089 
3090  if (is_master_block_preconditioner())
3091  {
3092 #ifdef OOMPH_HAS_MPI
3093  unsigned first_row = this->distribution_pt()->first_row();
3094  unsigned nrow_local = this->distribution_pt()->nrow_local();
3095  unsigned last_row = first_row+nrow_local-1;
3096  if (i_dof >= first_row && i_dof <= last_row)
3097  {
3098  return static_cast<int>(Dof_number_dense[i_dof-first_row]);
3099  }
3100  else
3101  {
3102  //int index = this->get_index_of_element(Global_index_sparse,i_dof);
3103  int index = get_index_of_value<unsigned>(Global_index_sparse,i_dof,true);
3104  if (index >= 0)
3105  {
3106  return Dof_number_sparse[index];
3107  }
3108  }
3109  // if we here we couldn't find the i_dof
3110 #ifdef PARANOID
3111  unsigned my_rank = comm_pt()->my_rank();
3112  std::ostringstream error_message;
3113  error_message
3114  << "Proc " << my_rank<<": Requested internal_dof_number(...) for global DOF "
3115  << i_dof << "\n"
3116  << "cannot be found.\n";
3117  throw OomphLibError(
3118  error_message.str(),
3119  OOMPH_CURRENT_FUNCTION,
3120  OOMPH_EXCEPTION_LOCATION);
3121 #endif
3122 #else
3123  return static_cast<int>(Dof_number_dense[i_dof]);
3124 #endif
3125  }
3126  // else this preconditioner is a subsidiary one, and its Block_number
3127  // lookup schemes etc haven't been set up
3128  else
3129  {
3130  // Block number in master prec
3131  unsigned blk_num = Master_block_preconditioner_pt->internal_dof_number(i_dof);
3132 
3133  // Search through the Block_number_in_master_preconditioner for master
3134  // block blk_num and return the block number in this preconditioner
3135  for (unsigned i = 0; i < this->internal_ndof_types(); i++)
3136  {
3137  if (Doftype_in_master_preconditioner_fine[i] == blk_num)
3138  {return static_cast<int>(i);}
3139  }
3140  // if the master block preconditioner number is not found return -1
3141  return -1;
3142  }
3143 
3144  // Shouldn't get here
3145  throw OomphLibError("Never get here\n",
3146  OOMPH_CURRENT_FUNCTION,
3147  OOMPH_EXCEPTION_LOCATION);
3148  // Dummy return
3149  return -1;
3150  }
3151 
3152  /// \short Return the row/column number of global unknown i_dof within it's
3153  /// block.
3154  unsigned internal_index_in_dof(const unsigned& i_dof) const
3155  {
3156  if (is_master_block_preconditioner())
3157  {
3158 #ifdef OOMPH_HAS_MPI
3159  unsigned first_row = this->distribution_pt()->first_row();
3160  unsigned nrow_local = this->distribution_pt()->nrow_local();
3161  unsigned last_row = first_row+nrow_local-1;
3162  if (i_dof >= first_row && i_dof <= last_row)
3163  {
3164  return static_cast<int>(Index_in_dof_block_dense[i_dof-first_row]);
3165  }
3166  else
3167  {
3168  //int index = this->get_index_of_element(Global_index_sparse,i_dof);
3169  int index = get_index_of_value<unsigned>(Global_index_sparse,i_dof,true);
3170  if (index >= 0)
3171  {
3172  return Index_in_dof_block_sparse[index];
3173  }
3174  }
3175  // if we here we couldn't find the i_dof
3176 #ifdef PARANOID
3177  std::ostringstream error_message;
3178  error_message
3179  << "Requested internal_index_in_dof(...) for global DOF " << i_dof << "\n"
3180  << "cannot be found.\n";
3181  throw OomphLibError(
3182  error_message.str(),
3183  OOMPH_CURRENT_FUNCTION,
3184  OOMPH_EXCEPTION_LOCATION);
3185 #endif
3186 #else
3187  return Index_in_dof_block_dense[i_dof];
3188 #endif
3189  }
3190  else
3191  {
3192  return Master_block_preconditioner_pt->internal_index_in_dof(i_dof);
3193  }
3194 
3195  // Shouldn't get here
3196  throw OomphLibError("Never get here\n",
3197  OOMPH_CURRENT_FUNCTION,
3198  OOMPH_EXCEPTION_LOCATION);
3199  // Dummy return
3200  return -1;
3201  }
3202 
3203  /// \short Return the number of degrees of freedom in block b. Note that if
3204  /// this preconditioner acts as a subsidiary preconditioner then b refers
3205  /// to the block number in the subsidiary preconditioner not the master
3206  /// block preconditioner.
3207  unsigned internal_block_dimension(const unsigned& b) const
3208  {
3209 #ifdef PARANOID
3210  const unsigned i_nblock_types = internal_nblock_types();
3211  if(b >= i_nblock_types)
3212  {
3213  std::ostringstream err_msg;
3214  err_msg << "Trying to get internal block dimension for \n"
3215  << "internal block " << b <<".\n"
3216  << "But there are only " << i_nblock_types
3217  << " internal dof types.\n";
3218  throw OomphLibError(err_msg.str(),
3219  OOMPH_CURRENT_FUNCTION,
3220  OOMPH_EXCEPTION_LOCATION);
3221  }
3222 #endif
3223  return Internal_block_distribution_pt[b]->nrow();
3224  }
3225 
3226  /// \short Return the size of the dof "block" i, i.e. how many degrees of
3227  /// freedom are associated with it. Note that if this preconditioner acts as
3228  /// a subsidiary preconditioner, then i refers to the block number in the
3229  /// subsidiary preconditioner not the master block preconditioner
3230  unsigned internal_dof_block_dimension(const unsigned& i) const
3231  {
3232 #ifdef PARANOID
3233  const unsigned i_n_dof_types = internal_ndof_types();
3234  if(i >= i_n_dof_types)
3235  {
3236  std::ostringstream err_msg;
3237  err_msg << "Trying to get internal dof block dimension for \n"
3238  << "internal dof block " << i <<".\n"
3239  << "But there are only " << i_n_dof_types
3240  << " internal dof types.\n";
3241  throw OomphLibError(err_msg.str(),
3242  OOMPH_CURRENT_FUNCTION,
3243  OOMPH_EXCEPTION_LOCATION);
3244  }
3245 #endif
3246  // I don't understand the difference between this function and
3247  // block_dimension(...) but I'm not going to mess with it... David
3248 
3249  if(is_master_block_preconditioner())
3250  {
3251  return Dof_dimension[i];
3252  }
3253  else
3254  {
3255  unsigned master_i = internal_master_dof_number(i);
3256  return Master_block_preconditioner_pt->internal_dof_block_dimension(master_i);
3257  }
3258  }
3259 
3260  /// \short Return the number of dofs (number of rows or columns) in the
3261  /// overall problem. The prefix "master_" is sort of redundant when used as
3262  /// a stand-alone block preconditioner but is required to avoid ambiguities.
3263  /// The latter is stored (and maintained) separately for each specific block
3264  /// preconditioner regardless of its role.
3265  unsigned master_nrow() const
3266  {
3267  if (is_master_block_preconditioner())
3268  {
3269  return Nrow;
3270  }
3271  else
3272  {
3273  return (this->Master_block_preconditioner_pt->master_nrow());
3274  }
3275  }
3276 
3277  /// \short Takes the block number within this preconditioner and returns the
3278  /// corresponding block number in the master preconditioner. If this
3279  /// preconditioner does not have a master block preconditioner then the
3280  /// block number passed is returned
3281  unsigned internal_master_dof_number(const unsigned& b) const
3282  {
3283  if (is_master_block_preconditioner())
3284  return b;
3285  else
3286  return Doftype_in_master_preconditioner_fine[b];
3287  }
3288 
3289  /// \short access function to the internal
3290  /// preconditioner matrix distribution pt.
3291  /// preconditioner_matrix_distribution_pt always returns the concatenation
3292  /// of the internal block distributions. Since the writer of the
3293  /// preconditioner does not need to concern themselves with the internal
3294  /// dof/block, please use preconditioner_matrix_distribution_pt().
3297  {
3298  if (is_master_block_preconditioner())
3299  return Internal_preconditioner_matrix_distribution_pt;
3300  else
3301  return this->distribution_pt();
3302  }
3303 
3304  /// \short Access function to the preconditioner matrix distribution pointer.
3305  /// This is the concatenation of the block distributions with the identity
3306  /// ordering. I.e. if this preconditioner has three block types, with the
3307  /// three associated block distributions dist_b0, dist_b1 and dist_b2, then
3308  /// this distribution is:
3309  /// LinearAlgebraDistributionHelpers::concatenate(dist_b0, dist_b1, dist_b2).
3312  {
3313  return Preconditioner_matrix_distribution_pt;
3314  }
3315 
3316  /// \short The replacement dof-level blocks.
3318 
3319  /// \short The distribution for the blocks.
3321 
3322  /// \short Mapping for block types to dof types. These are the dof types
3323  /// the writer of the preconditioner expects. For the upper-most master
3324  /// block preconditioner, this would be the sum of the dof types in the
3325  /// meshes. For subsidiary block preconditioners, this is determined by
3326  /// the parent preconditioner when passing in the doftype_coarsen_map_coarse
3327  /// vector in turn_into_subsidiary_block_preconditioner(...).
3329 
3330  /// \short Mapping for the block types to the most fine grain dof types.
3332 
3333  /// \short Mapping for dof types within THIS precondition. This is usually
3334  /// passed down from the parent preconditioner.
3335  /// This list is used to tell which does types should
3336  /// be considered as a single dof type within this preconditioner. I.e. we
3337  /// "coarsen" the dof types. The values are local to this preconditioner,
3338  /// for example, even if the
3339  /// Doftype_in_master_preconditioner_coarse = [2,3,4], the vector
3340  /// Doftype_coarsen_map_coarse = [[0],[1,2]], saying your local dof types
3341  /// 0 should be considered as dof type 0 and dof types 1 and 2 are considered
3342  /// as dof type 1.
3343  ///
3344  /// Furthermore, the dof types are that the preconditioner above this one
3345  /// knows; these dof types may or may not be coarsened. For example, say that
3346  /// this preconditioner expects two dof types, 0 and 1.
3347  /// The preconditioner above this one wishes to use this preconditioner to
3348  /// solve the block associated with it's dof types 2, 3 and 4. It passes the
3349  /// Vector [2,3,4] to this preconditioner via the function
3350  /// turn_into_subsidiary_block_preconditioner(...), this list is to be stored
3351  /// in Doftype_in_master_preconditioner_coarse. It also passes in the
3352  /// 2D vector [[0][1,2]] (as described above), this list is to be stored in
3353  /// Doftype_coarsen_map_coarse. BUT, the master's preconditioner dof types
3354  /// may also be coarsened. I.e. the underlying dof types of the master block
3355  /// preconditioner may be [0,1,2,3,4,5,6,7], for which it may have the
3356  /// Doftype_coarsen_map_coarse = [[0,1][2,3][4,5][6,7]].
3357  ///
3358  /// An additional list has to be kept for the most fine grain dof type
3359  /// mapping. This is stored in Doftype_coarsen_map_fine, in this case it
3360  /// would be:
3361  ///
3362  /// Doftype_coarsen_map_fine = [[0,1][2,3,4,5]], since the dof types passed
3363  /// to this preconditioner is [2, 3, 4] from the master preconditioner, but
3364  /// it actually refers to the underlying dof types [2,3,4,5,6,7].
3365  ///
3366  /// In the case of the top most master block
3367  /// preconditioner, the block_setup(...) function fills the vector with the
3368  /// identity mapping.
3370 
3371  /// \short Mapping the dof types within this preconditioner. The values in
3372  /// here refers to the most grain dof types. This list is automatically
3373  /// generated either in block_setup(...) (for the top-most preconditioner)
3374  /// or the turn_into_subsidiary_block_preconditioner(...) function.
3375  /// Please refer to the comment above Doftype_coarsen_map_coarse for more
3376  /// details.
3378 
3379  /// \short Storage for the default distribution for each internal block.
3381 
3382  /// \short Storage for the default distribution for each dof block at
3383  /// this level.
3385 
3386  /// \short Vector of unsigned to indicate which meshes contain multiple
3387  /// element types.
3389 
3390  /// \short Vector of pointers to the meshes containing the elements used in
3391  /// the block preconditioner. Const pointers to prevent modification of the
3392  /// mesh by the preconditioner (this could be relaxed if needed). If this is
3393  /// a subsidiary preconditioner, then the information is looked up in the
3394  /// master preconditioner.
3396 
3397  /// \short Storage for number of types of degree of freedom of the elements
3398  /// in each mesh.
3400 
3401  /// \short Number of different block types in this preconditioner. Note that
3402  /// this information is maintained if used as a subsidiary or stand-alone
3403  /// block preconditioner, in the latter case it stores the number of blocks
3404  /// within the subsidiary preconditioner.
3406 
3407  ///\short Number of different DOF types in this preconditioner. Note that
3408  /// this information is maintained if used as a subsidiary or stand-alone
3409  /// block preconditioner, in the latter case it stores the number of dofs
3410  /// within the subsidiary preconditioner.
3412 
3413  private:
3414 
3415  /// \short Debugging variable. Set true or false via the access functions
3416  /// turn_on_recursive_debug_flag(...)
3417  /// turn_off_recursive_debug_flag(...)
3418  /// These will turn on/off the debug flag up the hierarchy.
3420 
3421  /// \short Debugging variable. Set true or false via the access functions
3422  /// turn_on_debug_flag(...)
3423  /// turn_off_debug_flag(...)
3425 
3426  /// \short Stores any block-level distributions / concatenation of
3427  /// block-level distributions required. The first in the pair
3428  /// (Vector<unsigned>) represents the block numbers of the distributions
3429  /// concatenated to get the second in the pair (LinearAlgebraDistribution*).
3430  std::map<Vector<unsigned>,LinearAlgebraDistribution*>
3432 
3433  /// \short Number of DOFs (# of rows or columns in the matrix) in this
3434  /// preconditioner. Note that this information is maintained if used as a
3435  /// subsidiary or stand-alone block preconditioner, in the latter case it
3436  /// stores the number of rows within the subsidiary preconditioner.
3437  unsigned Nrow;
3438 
3439  /// \short If the block preconditioner is acting a subsidiary block
3440  /// preconditioner then a pointer to the master preconditioner is stored
3441  /// here. If the preconditioner does not have a master block preconditioner
3442  /// then this pointer remains null.
3444 
3445  /// \short The map between the dof types in this preconditioner and the master
3446  /// preconditioner. If there is no master preconditioner it remains empty.
3447  /// This list contains the mapping for the underlying dof types.
3449 
3450  /// \short The map between the dof types in this preconditioner and the
3451  /// master preconditioner. If there is no master preconditioner, it remains
3452  /// empty. This is the version for which the master preconditioner expects.
3453  /// The dof types in here may or may not be coarsened in the preconditioner
3454  /// above this one.
3456 
3457  /// \short **This was uncommented** Presumably a non-distributed analogue of
3458  /// Index_in_dof_block_sparse.
3460 
3461  /// \short Vector to store the mapping from the global DOF number to its
3462  /// block. Empty if this preconditioner has a master preconditioner, in this
3463  /// case the information is obtained from the master preconditioner.
3465 
3466 #ifdef OOMPH_HAS_MPI
3467 
3468  // The following three vectors store data on the matrix rows/matrix
3469  // columns/dofs (the three are equivalent) that are not on this processor.
3470 
3471  /// \short For global indices outside of the range this->first_row()
3472  /// to this->first_row()+this->nrow_local(), the Index_in_dof_block
3473  /// and Dof_number are stored sparsely in the vectors:
3474  /// + Index_in_dof_block_sparse;
3475  /// + Dof_number_sparse;
3476  /// The corresponding global indices are stored in this vector.
3478 
3479  /// \short Vector to store the mapping from the global DOF number to the
3480  /// index (row/column number) within its block (empty if this preconditioner
3481  /// has a master preconditioner as this information is obtained from the
3482  /// master preconditioner). Sparse version: for global indices outside of the
3483  /// range this->first_row() to this->first_row()+this->nrow_local(). The
3484  /// global index of an element in this vector is defined in
3485  /// Global_index_sparse.
3487 
3488  /// \short Vector to store the mapping from the global DOF number to its
3489  /// block (empty if this preconditioner has a master preconditioner as this
3490  /// information is obtained from the master preconditioner). Sparse
3491  /// version: for global indices outside of the range this->first_row() to
3492  /// this->first_row()+this->nrow_local(). The global index of an element in
3493  /// this vector is defined in Global_index_sparse.
3495 #endif
3496 
3497  /// \short Vector containing the size of each block, i.e. the number of
3498  /// global DOFs associated with it. (Empty if this preconditioner has a
3499  /// master preconditioner as this information is obtain from the master
3500  /// preconditioner.)
3502 
3503  /// \short Vectors of vectors for the mapping from block number and block
3504  /// row to global row number. Empty if this preconditioner has a master
3505  /// preconditioner as this information is obtain from the master
3506  /// preconditioner.
3508 
3509  /// \short Vector of vectors to store the mapping from block number to the
3510  /// DOF number (each element could be a vector because we allow multiple
3511  /// DOFs types in a single block).
3513 
3514  /// \short Vector to the mapping from DOF number to block number.
3516 
3517  /// \short Number of types of degree of freedom associated with each block.
3519 
3520 
3521 
3522 #ifdef OOMPH_HAS_MPI
3523  /// \short The global rows to be sent of block b to processor p (matrix
3524  /// indexed [b][p]).
3526 
3527  /// \short The number of global rows to be sent of block b to processor p
3528  /// (matrix indexed [b][p]).
3530 
3531  /// \short The block rows to be received from processor p for block b
3532  /// (matrix indexed [b][p]).
3534 
3535  /// \short The number of block rows to be received from processor p for
3536  /// block b (matrix indexed [b][p]).
3538 
3539  /// \short The global rows to be sent to processor p for
3540  /// get_block_ordered_... type methods.
3542 
3543  /// \short The number global rows to be sent to processor p for
3544  /// get_block_ordered_... type methods.
3546 
3547  /// \short The preconditioner rows to be received from processor p for
3548  /// get_block_ordered_... type methods.
3550 
3551  /// \short The number of preconditioner rows to be received from processor
3552  /// p for get_block_ordered_... type methods.
3554 #endif
3555 
3556  /// \short The distribution of the (internal) preconditioner matrix. This is
3557  /// formed by concatenating the distribution of the internal blocks.
3558  /// This is obsolete code, maintained for backwards compatibility.
3559  /// Below is the old comment:
3560  ///
3561  /// - only used if this preconditioner is a master preconditioner.
3562  /// Warning: always use the access function
3563  /// internal_preconditioner_matrix_distribution_pt().
3565 
3566  /// \short The distribution of the preconditioner matrix. This is the
3567  /// concatenation of the block distribution.
3569 
3570  /// \short Static boolean to allow block_matrix_test(...) to be run.
3571  /// Defaults to false.
3573 
3574  /// \short String giving the base of the files to write block data into. If
3575  /// empty then do not output blocks. Default is empty.
3577  };
3578 
3579 
3580 }
3581 #endif
virtual DoubleMatrixBase * matrix_pt() const
Get function for matrix pointer.
const LinearAlgebraDistribution * preconditioner_matrix_distribution_pt() const
Access function to the preconditioner matrix distribution pointer. This is the concatenation of the b...
void set_replacement_block_pt(CRDoubleMatrix *replacement_block_pt)
set Replacement_block_pt.
bool block_output_on() const
Test if output of blocks is on or not.
unsigned internal_block_dimension(const unsigned &b) const
Return the number of degrees of freedom in block b. Note that if this preconditioner acts as a subsid...
unsigned Column_index
Column index of the block.
Vector< unsigned > Global_index_sparse
For global indices outside of the range this->first_row() to this->first_row()+this->nrow_local(), the Index_in_dof_block and Dof_number are stored sparsely in the vectors:
Vector< int * > Rows_to_send_for_get_ordered
The global rows to be sent to processor p for get_block_ordered_... type methods. ...
void clear_block_preconditioner_base()
Clears all BlockPreconditioner data. Called by the destructor and the block_setup(...) methods.
Vector< const Mesh * > Mesh_pt
Vector of pointers to the meshes containing the elements used in the block preconditioner. Const pointers to prevent modification of the mesh by the preconditioner (this could be relaxed if needed). If this is a subsidiary preconditioner, then the information is looked up in the master preconditioner.
void broken_copy(const std::string &class_name)
Issue error message and terminate execution.
Vector< Vector< unsigned > > doftype_coarsen_map_fine() const
Access function for the Doftype_coarsen_map_fine variable.
Vector< unsigned > Nrows_to_recv_for_get_ordered
The number of preconditioner rows to be received from processor p for get_block_ordered_... type methods.
bool any_mesh_distributed() const
Check if any of the meshes are distributed. This is equivalent to problem.distributed() and is used a...
int index_in_block(const unsigned &i_dof) const
Given a global dof number, returns the index in the block it belongs to. This is the overall index...
Vector< unsigned > get_fine_grain_dof_types_in(const unsigned &i) const
Returns the most fine grain dof types in a (possibly coarsened) dof type.
friend std::ostream & operator<<(std::ostream &o_stream, const BlockSelector &block_selector)
Output function, outputs the Row_index, Column_index, Wanted and the address of the Replacement_block...
int internal_dof_number(const unsigned &i_dof) const
Return the number of the block associated with global unknown i_dof. If this preconditioner is a subs...
void set_column_index(const unsigned &column_index)
Set the column index.
Vector< unsigned > Dof_number_to_block_number_lookup
Vector to the mapping from DOF number to block number.
void insert_auxiliary_block_distribution(const Vector< unsigned > &block_vec_number, LinearAlgebraDistribution *dist_pt)
insert a Vector<unsigned> and LinearAlgebraDistribution* pair into Auxiliary_block_distribution_pt. The Auxiliary_block_distribution_pt should only contain pointers to distributions concatenated at this block level. We try to ensure this by checking if the block_vec_number vector is within the range nblock_types(). Of course, this does not guarantee correctness, but this is the least we can do.
bool built() const
access function to the Built flag - indicates whether the matrix has been build - i...
Definition: matrices.h:1177
LinearAlgebraDistribution * Internal_preconditioner_matrix_distribution_pt
The distribution of the (internal) preconditioner matrix. This is formed by concatenating the distrib...
unsigned internal_index_in_dof(const unsigned &i_dof) const
Return the row/column number of global unknown i_dof within it&#39;s block.
Vector< LinearAlgebraDistribution * > Block_distribution_pt
The distribution for the blocks.
cstr elem_len * i
Definition: cfortran.h:607
LinearAlgebraDistribution * block_distribution_pt(const unsigned b)
Access function to the block distributions (non-const version).
void turn_off_debug_flag()
Toggles off the debug flag.
bool Wanted
Bool to indicate if we require this block.
LinearAlgebraDistribution * Preconditioner_matrix_distribution_pt
The distribution of the preconditioner matrix. This is the concatenation of the block distribution...
void concatenate(const Vector< LinearAlgebraDistribution *> &in_distribution_pt, LinearAlgebraDistribution &out_distribution)
Takes a vector of LinearAlgebraDistribution objects and concatenates them such that the nrow_local of...
bool Debug_flag
Debugging variable. Set true or false via the access functions turn_on_debug_flag(...) turn_off_debug_flag(...)
Vector< unsigned > Allow_multiple_element_type_in_mesh
Vector of unsigned to indicate which meshes contain multiple element types.
void do_not_want_block()
Indicate that we do not want the block (set Wanted to false).
CRDoubleMatrix * replacement_block_pt() const
Returns Replacement_block_pt.
BlockSelector()
Default constructor, initialise block index i, j to 0 and bool to false.
const bool & wanted() const
returns whether the block is wanted or not.
Vector< unsigned > Ndof_in_block
Number of types of degree of freedom associated with each block.
void build(const OomphCommunicator *const comm_pt, const unsigned &first_row, const unsigned &nrow_local, const unsigned &nrow=0)
Sets the distribution. Takes first_row, nrow_local and nrow as arguments. If nrow is not provided or ...
int get_index_of_value(const Vector< myType > &vec, const myType val, const bool sorted=false) const
Get the index of first occurrence of value in a vector. If the element does not exist, -1 is returned. The optional parameter indicates of the Vector is sorted or not. Complexity: if the Vector is sorted, then on average, logarithmic in the distance between first and last: Performs approximately log2(N)+2 element comparisons. Otherwise, up to linear in the distance between first and last: Compares elements until a match is found.
MapMatrix< unsigned, CRDoubleMatrix * > replacement_dof_block_pt() const
Access function to the replaced dof-level blocks.
unsigned master_nrow() const
Return the number of dofs (number of rows or columns) in the overall problem. The prefix "master_" is...
Vector< unsigned > Index_in_dof_block_dense
This was uncommented Presumably a non-distributed analogue of Index_in_dof_block_sparse.
void turn_on_debug_flag()
Toggles on the debug flag.
Vector< unsigned > Nrows_to_send_for_get_ordered
The number global rows to be sent to processor p for get_block_ordered_... type methods.
MapMatrix< unsigned, CRDoubleMatrix * > Replacement_dof_block_pt
The replacement dof-level blocks.
Vector< unsigned > Dof_number_dense
Vector to store the mapping from the global DOF number to its block. Empty if this preconditioner has...
Vector< unsigned > Index_in_dof_block_sparse
Vector to store the mapping from the global DOF number to the index (row/column number) within its bl...
OomphInfo oomph_info
void null_replacement_block_pt()
Set Replacement_block_pt to null.
void setup_matrix_vector_product(MatrixVectorProduct *matvec_prod_pt, CRDoubleMatrix *block_pt, const Vector< unsigned > &block_col_indices)
Setup a matrix vector product. matvec_prod_pt is a pointer to the MatrixVectorProduct, block_pt is a pointer to the block matrix, block_col_indices is a vector indicating which block indices does the RHS vector we want to multiply the matrix by.
Vector< Vector< unsigned > > Doftype_coarsen_map_coarse
Mapping for dof types within THIS precondition. This is usually passed down from the parent precondit...
unsigned ndof_types() const
Return the total number of DOF types.
Vector< unsigned > Doftype_in_master_preconditioner_fine
The map between the dof types in this preconditioner and the master preconditioner. If there is no master preconditioner it remains empty. This list contains the mapping for the underlying dof types.
const Mesh * mesh_pt(const unsigned &i) const
Access to i-th mesh (of the various meshes that contain block preconditionable elements of the same n...
BlockSelector(const unsigned &row_index, const unsigned &column_index, const bool &wanted, CRDoubleMatrix *replacement_block_pt=0)
Constructor, takes the row and column indices and a boolean indicating if the block is required or no...
Vector< Vector< unsigned > > Global_index
Vectors of vectors for the mapping from block number and block row to global row number. Empty if this preconditioner has a master preconditioner as this information is obtain from the master preconditioner.
void operator=(const BlockPreconditioner &)
Broken assignment operator.
static bool Run_block_matrix_test
Static boolean to allow block_matrix_test(...) to be run. Defaults to false.
bool is_subsidiary_block_preconditioner() const
Return true if this preconditioner is a subsidiary preconditioner.
CRDoubleMatrix * Replacement_block_pt
Pointer to the block.
const LinearAlgebraDistribution * master_distribution_pt() const
Access function to the distribution of the master preconditioner. If this preconditioner does not hav...
const LinearAlgebraDistribution * block_distribution_pt(const unsigned &b) const
Access function to the block distributions (const version).
unsigned Nrow
Number of DOFs (# of rows or columns in the matrix) in this preconditioner. Note that this informatio...
void build(const unsigned &row_index, const unsigned &column_index, const bool &wanted, CRDoubleMatrix *replacement_block_pt=0)
void set_replacement_dof_block(const unsigned &block_i, const unsigned &block_j, CRDoubleMatrix *replacement_dof_block_pt)
Set replacement dof-level blocks. Only dof-level blocks can be set. This is important due to how the ...
void output_blocks_to_files(const std::string &basefilename, const unsigned &precision=8) const
Describes the distribution of a distributable linear algebra type object. Typically this is a contain...
int internal_index_in_block(const unsigned &i_dof) const
Return the index in the block corresponding to a global block number i_dof. The index returned corres...
void post_block_matrix_assembly_partial_clear()
A helper method to reduce the memory requirements of block preconditioners. Once the methods get_bloc...
void setup_matrix_vector_product(MatrixVectorProduct *matvec_prod_pt, CRDoubleMatrix *block_pt, const unsigned &block_col_index)
Setup matrix vector product. This is simply a wrapper around the other setup_matrix_vector_product fu...
void want_block()
Indicate that we require the block (set Wanted to true).
unsigned long ncol() const
Return the number of columns of the matrix.
Definition: matrices.h:998
void turn_on_recursive_debug_flag()
Toggles on the recursive debug flag. The change goes up the block preconditioning hierarchy...
virtual ~BlockSelector()
Default destructor.
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:3244
bool Recursive_debug_flag
Debugging variable. Set true or false via the access functions turn_on_recursive_debug_flag(...) turn_off_recursive_debug_flag(...) These will turn on/off the debug flag up the hierarchy.
int block_number(const unsigned &i_dof) const
Return the block number corresponding to a global index i_dof.
MATRIX * matrix_pt() const
Access function to matrix_pt. If this is the master then cast the matrix pointer to MATRIX*...
virtual ~BlockPreconditioner()
Destructor.
void get_block(const unsigned &i, const unsigned &j, MATRIX &output_matrix, const bool &ignore_replacement_block=false) const
Put block (i,j) into output_matrix. This block accounts for any coarsening of dof types and any repla...
unsigned internal_ndof_types() const
Return the number of internal dof types. This is the number of most fine grain dof types...
unsigned nmesh() const
Return the number of meshes in Mesh_pt.
static char t char * s
Definition: cfortran.h:572
void get_block_other_matrix(const unsigned &i, const unsigned &j, MATRIX *in_matrix_pt, MATRIX &output_matrix)
Get a block from a different matrix using the blocking scheme that has already been set up...
void set_nmesh(const unsigned &n)
Specify the number of meshes required by this block preconditioner. Note: elements in different meshe...
unsigned Internal_nblock_types
Number of different block types in this preconditioner. Note that this information is maintained if u...
const unsigned & row_index() const
returns the row index.
std::string Output_base_filename
String giving the base of the files to write block data into. If empty then do not output blocks...
std::map< Vector< unsigned >, LinearAlgebraDistribution * > Auxiliary_block_distribution_pt
Stores any block-level distributions / concatenation of block-level distributions required...
unsigned internal_master_dof_number(const unsigned &b) const
Takes the block number within this preconditioner and returns the corresponding block number in the m...
void setup(CRDoubleMatrix *matrix_pt, const LinearAlgebraDistribution *col_dist_pt=0)
Setup the matrix vector product operator. WARNING: This class is wrapper to Trilinos Epetra matrix ve...
void set_mesh(const unsigned &i, const Mesh *const mesh_pt, const bool &allow_multiple_element_type_in_mesh=false)
Set the i-th mesh for this block preconditioner. Note: The method set_nmesh(...) must be called befor...
Vector< LinearAlgebraDistribution * > Internal_block_distribution_pt
Storage for the default distribution for each internal block.
void set_block_output_to_files(const std::string &basefilename)
Set the base part of the filename to output blocks to. If it is set then all blocks will be output at...
void select_block(const unsigned &row_index, const unsigned &column_index, const bool &wanted, CRDoubleMatrix *replacement_block_pt=0)
Select a block.
Vector< Vector< unsigned > > Block_to_dof_map_fine
Mapping for the block types to the most fine grain dof types.
unsigned nblock_types() const
Return the number of block types.
BlockPreconditioner< MATRIX > * Master_block_preconditioner_pt
If the block preconditioner is acting a subsidiary block preconditioner then a pointer to the master ...
DenseMatrix< unsigned > Nrows_to_send_for_get_block
The number of global rows to be sent of block b to processor p (matrix indexed [b][p]).
DenseMatrix< int * > Rows_to_recv_for_get_block
The block rows to be received from processor p for block b (matrix indexed [b][p]).
const unsigned nrow() const
returns the number of rows. This is the outer Vector size.
Preconditioner base class. Gives an interface to call all other preconditioners through and stores th...
LinearAlgebraDistribution * dof_block_distribution_pt(const unsigned &b)
Access function to the dof-level block distributions.
Vector< unsigned > Dof_number_sparse
Vector to store the mapping from the global DOF number to its block (empty if this preconditioner has...
Vector< int * > Rows_to_recv_for_get_ordered
The preconditioner rows to be received from processor p for get_block_ordered_... type methods...
unsigned internal_dof_block_dimension(const unsigned &i) const
Return the size of the dof "block" i, i.e. how many degrees of freedom are associated with it...
void broken_assign(const std::string &class_name)
Issue error message and terminate execution.
void document()
debugging method to document the setup. Should only be called after block_setup(...).
unsigned Internal_ndof_types
Number of different DOF types in this preconditioner. Note that this information is maintained if use...
int internal_block_number(const unsigned &i_dof) const
Return the block number corresponding to a global index i_dof. This returns the block number correspo...
std::string string(const unsigned &i)
Return the i-th string or "" if the relevant string hasn&#39;t been defined.
Matrix vector product helper class - primarily a wrapper to Trilinos&#39;s Epetra matrix vector product m...
Vector< Vector< unsigned > > Block_to_dof_map_coarse
Mapping for block types to dof types. These are the dof types the writer of the preconditioner expect...
unsigned internal_nblock_types() const
Return the number internal blocks. This should be the same as the number of internal dof types...
Class for dense matrices, storing all the values of the matrix as a pointer to a pointer with assorte...
Definition: communicator.h:50
Vector< LinearAlgebraDistribution * > Dof_block_distribution_pt
Storage for the default distribution for each dof block at this level.
Vector< Vector< unsigned > > Block_number_to_dof_number_lookup
Vector of vectors to store the mapping from block number to the DOF number (each element could be a v...
unsigned nfine_grain_dof_types_in(const unsigned &i) const
Access function for the number of most fine grain dof types in a (possibly coarsened) dof type...
Vector< Vector< unsigned > > Doftype_coarsen_map_fine
Mapping the dof types within this preconditioner. The values in here refers to the most grain dof typ...
A vector in the mathematical sense, initially developed for linear algebra type applications. If MPI then this vector can be distributed - its distribution is described by the LinearAlgebraDistribution object at Distribution_pt. Data is stored in a C-style pointer vector (double*)
Definition: double_vector.h:61
DenseMatrix< unsigned > Nrows_to_recv_for_get_block
The number of block rows to be received from processor p for block b (matrix indexed [b][p])...
void clear()
clears the distribution
Vector< unsigned > Doftype_in_master_preconditioner_coarse
The map between the dof types in this preconditioner and the master preconditioner. If there is no master preconditioner, it remains empty. This is the version for which the master preconditioner expects. The dof types in here may or may not be coarsened in the preconditioner above this one.
unsigned Row_index
Row index of the block.
bool is_master_block_preconditioner() const
Return true if this preconditioner is the master block preconditioner.
Vector< unsigned > Dof_dimension
Vector containing the size of each block, i.e. the number of global DOFs associated with it...
MATRIX get_block(const unsigned &i, const unsigned &j, const bool &ignore_replacement_block=false) const
Return block (i,j). If the optional argument ignore_replacement_block is true, then any blocks in Rep...
Data structure to store information about a certain "block" or sub-matrix from the overall matrix in ...
void set_master_matrix_pt(MATRIX *in_matrix_pt)
Set the matrix_pt in the upper-most master preconditioner.
const unsigned ncol() const
return the number of columns. This is the size of the first inner vectors, or returns 0 if the outer ...
void concatenate_without_communication(const Vector< LinearAlgebraDistribution *> &row_distribution_pt, const Vector< LinearAlgebraDistribution *> &col_distribution_pt, const DenseMatrix< CRDoubleMatrix *> &matrix_pt, CRDoubleMatrix &result_matrix)
Concatenate CRDoubleMatrix matrices.
Definition: matrices.cc:5164
BlockPreconditioner(const BlockPreconditioner &)
Broken copy constructor.
unsigned ndof_types_in_mesh(const unsigned &i) const
Return the number of DOF types in mesh i. WARNING: This should only be used by the upper-most master ...
A class for compressed row matrices. This is a distributable object.
Definition: matrices.h:872
LinearAlgebraDistribution * distribution_pt() const
access to the LinearAlgebraDistribution
void disable_block_output_to_files()
Turn off output of blocks (by clearing the basefilename string).
Vector< unsigned > Ndof_types_in_mesh
Storage for number of types of degree of freedom of the elements in each mesh.
A general mesh class.
Definition: mesh.h:74
DenseMatrix< int * > Rows_to_send_for_get_block
The global rows to be sent of block b to processor p (matrix indexed [b][p]).
std::string to_string(T object, unsigned float_precision=8)
Conversion function that should work for anything with operator<< defined (at least all basic types)...
BlockPreconditioner< MATRIX > * master_block_preconditioner_pt() const
Access function to the master block preconditioner pt.
const LinearAlgebraDistribution * internal_preconditioner_matrix_distribution_pt() const
access function to the internal preconditioner matrix distribution pt. preconditioner_matrix_distribu...
MATRIX get_concatenated_block(const VectorMatrix< BlockSelector > &selected_block)
Returns a concatenation of the block matrices specified by the argument selected_block. The VectorMatrix selected_block must be correctly sized as it is used to determine the number of sub block matrices to concatenate.
const LinearAlgebraDistribution * internal_block_distribution_pt(const unsigned &b) const
Access function to the internal block distributions.
const unsigned & column_index() const
returns the column index.
void set_row_index(const unsigned &row_index)
Set the row index.
void turn_off_recursive_debug_flag()
Toggles off the recursive debug flag. The change goes up the block preconditioning hierarchy...