oomph::BlockPreconditioner< MATRIX > Class Template Reference

#include <block_preconditioner.h>

+ Inheritance diagram for oomph::BlockPreconditioner< MATRIX >:

Public Member Functions

 BlockPreconditioner ()
 Constructor. More...
 
virtual ~BlockPreconditioner ()
 Destructor. More...
 
 BlockPreconditioner (const BlockPreconditioner &)=delete
 Broken copy constructor. More...
 
void operator= (const BlockPreconditioner &)=delete
 Broken assignment operator. More...
 
MATRIX * matrix_pt () const
 
void turn_on_recursive_debug_flag ()
 
void turn_off_recursive_debug_flag ()
 
void turn_on_debug_flag ()
 Toggles on the debug flag. More...
 
void turn_off_debug_flag ()
 Toggles off the debug flag. More...
 
void turn_into_subsidiary_block_preconditioner (BlockPreconditioner< MATRIX > *master_block_prec_pt, const Vector< unsigned > &doftype_in_master_preconditioner_coarse)
 
void turn_into_subsidiary_block_preconditioner (BlockPreconditioner< MATRIX > *master_block_prec_pt, const Vector< unsigned > &doftype_in_master_preconditioner_coarse, const Vector< Vector< unsigned >> &doftype_coarsen_map_coarse)
 
virtual void block_setup ()
 
void block_setup (const Vector< unsigned > &dof_to_block_map)
 
void get_block (const unsigned &i, const unsigned &j, MATRIX &output_matrix, const bool &ignore_replacement_block=false) const
 
MATRIX get_block (const unsigned &i, const unsigned &j, const bool &ignore_replacement_block=false) const
 
void set_master_matrix_pt (MATRIX *in_matrix_pt)
 Set the matrix_pt in the upper-most master preconditioner. More...
 
void get_block_other_matrix (const unsigned &i, const unsigned &j, MATRIX *in_matrix_pt, MATRIX &output_matrix)
 
void get_blocks (DenseMatrix< bool > &required_blocks, DenseMatrix< MATRIX * > &block_matrix_pt) const
 
void get_dof_level_block (const unsigned &i, const unsigned &j, MATRIX &output_block, const bool &ignore_replacement_block=false) const
 
MATRIX get_concatenated_block (const VectorMatrix< BlockSelector > &selected_block)
 
void get_concatenated_block_vector (const Vector< unsigned > &block_vec_number, const DoubleVector &v, DoubleVector &b)
 
void return_concatenated_block_vector (const Vector< unsigned > &block_vec_number, const DoubleVector &b, DoubleVector &v) const
 Takes concatenated block ordered vector, b, and copies its. More...
 
void get_block_vectors (const Vector< unsigned > &block_vec_number, const DoubleVector &v, Vector< DoubleVector > &s) const
 
void get_block_vectors (const DoubleVector &v, Vector< DoubleVector > &s) const
 
void return_block_vectors (const Vector< unsigned > &block_vec_number, const Vector< DoubleVector > &s, DoubleVector &v) const
 
void return_block_vectors (const Vector< DoubleVector > &s, DoubleVector &v) const
 
void get_block_vector (const unsigned &n, const DoubleVector &v, DoubleVector &b) const
 
void return_block_vector (const unsigned &n, const DoubleVector &b, DoubleVector &v) const
 
void get_block_ordered_preconditioner_vector (const DoubleVector &v, DoubleVector &w)
 
void return_block_ordered_preconditioner_vector (const DoubleVector &w, DoubleVector &v) const
 
unsigned nblock_types () const
 Return the number of block types. More...
 
unsigned ndof_types () const
 Return the total number of DOF types. More...
 
const Meshmesh_pt (const unsigned &i) const
 
unsigned nmesh () const
 
int block_number (const unsigned &i_dof) const
 Return the block number corresponding to a global index i_dof. More...
 
int index_in_block (const unsigned &i_dof) const
 
const LinearAlgebraDistributionblock_distribution_pt (const unsigned &b) const
 Access function to the block distributions (const version). More...
 
LinearAlgebraDistributionblock_distribution_pt (const unsigned b)
 Access function to the block distributions (non-const version). More...
 
LinearAlgebraDistributiondof_block_distribution_pt (const unsigned &b)
 Access function to the dof-level block distributions. More...
 
const LinearAlgebraDistributionmaster_distribution_pt () const
 
unsigned ndof_types_in_mesh (const unsigned &i) const
 
bool is_subsidiary_block_preconditioner () const
 
bool is_master_block_preconditioner () const
 
void set_block_output_to_files (const std::string &basefilename)
 
void disable_block_output_to_files ()
 Turn off output of blocks (by clearing the basefilename string). More...
 
bool block_output_on () const
 Test if output of blocks is on or not. More...
 
void output_blocks_to_files (const std::string &basefilename, const unsigned &precision=8) const
 
void post_block_matrix_assembly_partial_clear ()
 
BlockPreconditioner< MATRIX > * master_block_preconditioner_pt () const
 Access function to the master block preconditioner pt. More...
 
void clear_block_preconditioner_base ()
 
void document ()
 
Vector< Vector< unsigned > > doftype_coarsen_map_fine () const
 
Vector< unsignedget_fine_grain_dof_types_in (const unsigned &i) const
 
unsigned nfine_grain_dof_types_in (const unsigned &i) const
 
MapMatrix< unsigned, CRDoubleMatrix * > replacement_dof_block_pt () const
 Access function to the replaced dof-level blocks. More...
 
void setup_matrix_vector_product (MatrixVectorProduct *matvec_prod_pt, CRDoubleMatrix *block_pt, const Vector< unsigned > &block_col_indices)
 
void setup_matrix_vector_product (MatrixVectorProduct *matvec_prod_pt, CRDoubleMatrix *block_pt, const unsigned &block_col_index)
 
void internal_get_block_ordered_preconditioner_vector (const DoubleVector &v, DoubleVector &w) const
 
void internal_return_block_ordered_preconditioner_vector (const DoubleVector &w, DoubleVector &v) const
 
unsigned internal_nblock_types () const
 
unsigned internal_ndof_types () const
 
void internal_return_block_vector (const unsigned &n, const DoubleVector &b, DoubleVector &v) const
 
void internal_get_block_vector (const unsigned &n, const DoubleVector &v, DoubleVector &b) const
 
void internal_get_block_vectors (const Vector< unsigned > &block_vec_number, const DoubleVector &v, Vector< DoubleVector > &s) const
 
void internal_get_block_vectors (const DoubleVector &v, Vector< DoubleVector > &s) const
 
void internal_return_block_vectors (const Vector< unsigned > &block_vec_number, const Vector< DoubleVector > &s, DoubleVector &v) const
 
void internal_return_block_vectors (const Vector< DoubleVector > &s, DoubleVector &v) const
 
void internal_get_block (const unsigned &i, const unsigned &j, MATRIX &output_block) const
 
int internal_block_number (const unsigned &i_dof) const
 
int internal_index_in_block (const unsigned &i_dof) const
 
const LinearAlgebraDistributioninternal_block_distribution_pt (const unsigned &b) const
 Access function to the internal block distributions. More...
 
void insert_auxiliary_block_distribution (const Vector< unsigned > &block_vec_number, LinearAlgebraDistribution *dist_pt)
 
void block_matrix_test (const unsigned &i, const unsigned &j, const MATRIX *block_matrix_pt) const
 
template<typename myType >
int get_index_of_value (const Vector< myType > &vec, const myType val, const bool sorted=false) const
 
void internal_get_block (const unsigned &block_i, const unsigned &block_j, CRDoubleMatrix &output_block) const
 
void get_dof_level_block (const unsigned &block_i, const unsigned &block_j, CRDoubleMatrix &output_block, const bool &ignore_replacement_block) const
 
- Public Member Functions inherited from oomph::Preconditioner
 Preconditioner ()
 Constructor. More...
 
 Preconditioner (const Preconditioner &)=delete
 Broken copy constructor. More...
 
void operator= (const Preconditioner &)=delete
 Broken assignment operator. More...
 
virtual ~Preconditioner ()
 Destructor (empty) More...
 
virtual void preconditioner_solve (const DoubleVector &r, DoubleVector &z)=0
 
virtual void preconditioner_solve_transpose (const DoubleVector &r, DoubleVector &z)
 
void setup (DoubleMatrixBase *matrix_pt)
 
void setup (const Problem *problem_pt, DoubleMatrixBase *matrix_pt)
 
void enable_silent_preconditioner_setup ()
 Set up the block preconditioner quietly! More...
 
void disable_silent_preconditioner_setup ()
 Be verbose in the block preconditioner setup. More...
 
virtual void setup ()=0
 
virtual void clean_up_memory ()
 Clean up memory (empty). Generic interface function. More...
 
virtual void set_matrix_pt (DoubleMatrixBase *matrix_pt)
 Set the matrix pointer. More...
 
virtual const OomphCommunicatorcomm_pt () const
 Get function for comm pointer. More...
 
virtual void set_comm_pt (const OomphCommunicator *const comm_pt)
 Set the communicator pointer. More...
 
double setup_time () const
 Returns the time to setup the preconditioner. More...
 
virtual void turn_into_subsidiary_block_preconditioner (BlockPreconditioner< CRDoubleMatrix > *master_block_prec_pt, const Vector< unsigned > &doftype_in_master_preconditioner_coarse)
 
virtual void turn_into_subsidiary_block_preconditioner (BlockPreconditioner< CRDoubleMatrix > *master_block_prec_pt, const Vector< unsigned > &doftype_in_master_preconditioner_coarse, const Vector< Vector< unsigned >> &doftype_coarsen_map_coarse)
 
- Public Member Functions inherited from oomph::DistributableLinearAlgebraObject
 DistributableLinearAlgebraObject ()
 Default constructor - create a distribution. More...
 
 DistributableLinearAlgebraObject (const DistributableLinearAlgebraObject &matrix)=delete
 Broken copy constructor. More...
 
void operator= (const DistributableLinearAlgebraObject &)=delete
 Broken assignment operator. More...
 
virtual ~DistributableLinearAlgebraObject ()
 Destructor. More...
 
LinearAlgebraDistributiondistribution_pt () const
 access to the LinearAlgebraDistribution More...
 
unsigned nrow () const
 access function to the number of global rows. More...
 
unsigned nrow_local () const
 access function for the num of local rows on this processor. More...
 
unsigned nrow_local (const unsigned &p) const
 access function for the num of local rows on this processor. More...
 
unsigned first_row () const
 access function for the first row on this processor More...
 
unsigned first_row (const unsigned &p) const
 access function for the first row on this processor More...
 
bool distributed () const
 distribution is serial or distributed More...
 
bool distribution_built () const
 
void build_distribution (const LinearAlgebraDistribution *const dist_pt)
 
void build_distribution (const LinearAlgebraDistribution &dist)
 

Protected Member Functions

void set_nmesh (const unsigned &n)
 
void set_mesh (const unsigned &i, const Mesh *const mesh_pt, const bool &allow_multiple_element_type_in_mesh=false)
 
void set_replacement_dof_block (const unsigned &block_i, const unsigned &block_j, CRDoubleMatrix *replacement_dof_block_pt)
 
bool any_mesh_distributed () const
 
int internal_dof_number (const unsigned &i_dof) const
 
unsigned internal_index_in_dof (const unsigned &i_dof) const
 
unsigned internal_block_dimension (const unsigned &b) const
 
unsigned internal_dof_block_dimension (const unsigned &i) const
 
unsigned master_nrow () const
 
unsigned internal_master_dof_number (const unsigned &b) const
 
const LinearAlgebraDistributioninternal_preconditioner_matrix_distribution_pt () const
 
const LinearAlgebraDistributionpreconditioner_matrix_distribution_pt () const
 
- Protected Member Functions inherited from oomph::DistributableLinearAlgebraObject
void clear_distribution ()
 

Protected Attributes

MapMatrix< unsigned, CRDoubleMatrix * > Replacement_dof_block_pt
 The replacement dof-level blocks. More...
 
Vector< LinearAlgebraDistribution * > Block_distribution_pt
 The distribution for the blocks. More...
 
Vector< Vector< unsigned > > Block_to_dof_map_coarse
 
Vector< Vector< unsigned > > Block_to_dof_map_fine
 Mapping for the block types to the most fine grain dof types. More...
 
Vector< Vector< unsigned > > Doftype_coarsen_map_coarse
 
Vector< Vector< unsigned > > Doftype_coarsen_map_fine
 
Vector< LinearAlgebraDistribution * > Internal_block_distribution_pt
 Storage for the default distribution for each internal block. More...
 
Vector< LinearAlgebraDistribution * > Dof_block_distribution_pt
 
Vector< unsignedAllow_multiple_element_type_in_mesh
 
Vector< const Mesh * > Mesh_pt
 
Vector< unsignedNdof_types_in_mesh
 
unsigned Internal_nblock_types
 
unsigned Internal_ndof_types
 
- Protected Attributes inherited from oomph::Preconditioner
bool Silent_preconditioner_setup
 Boolean to indicate whether or not the build should be done silently. More...
 
std::ostream * Stream_pt
 Pointer to the output stream – defaults to std::cout. More...
 

Private Attributes

bool Recursive_debug_flag
 
bool Debug_flag
 
std::map< Vector< unsigned >, LinearAlgebraDistribution * > Auxiliary_block_distribution_pt
 
unsigned Nrow
 
BlockPreconditioner< MATRIX > * Master_block_preconditioner_pt
 
Vector< unsignedDoftype_in_master_preconditioner_fine
 
Vector< unsignedDoftype_in_master_preconditioner_coarse
 
Vector< unsignedIndex_in_dof_block_dense
 
Vector< unsignedDof_number_dense
 
Vector< unsignedDof_dimension
 
Vector< Vector< unsigned > > Global_index
 
Vector< Vector< unsigned > > Block_number_to_dof_number_lookup
 
Vector< unsignedDof_number_to_block_number_lookup
 Vector to the mapping from DOF number to block number. More...
 
Vector< unsignedNdof_in_block
 Number of types of degree of freedom associated with each block. More...
 
LinearAlgebraDistributionInternal_preconditioner_matrix_distribution_pt
 
LinearAlgebraDistributionPreconditioner_matrix_distribution_pt
 
std::string Output_base_filename
 

Static Private Attributes

static bool Run_block_matrix_test = false
 

Detailed Description

template<typename MATRIX>
class oomph::BlockPreconditioner< MATRIX >

Block Preconditioner base class. The block structure of the overall problem is determined from the Mesh's constituent elements. Each constituent element must be block-preconditionable - i.e must implement the GeneralisedElements functions ndof_types() and get_dof_numbers_for_unknowns(...). A Problem can have several Meshes, but each Mesh must contain elements with the same DOF types. The association between global degrees of freedom and their unique local dof numbers is therefore based on information provided by the elements. We refer to the local dof numbers provided by the elements as the elemental dof numbers.

By default each type of DOF is assumed to be unique type of block, but DOF types can be grouped together in a single block when block_setup(...) is called.

This class can function in one of two ways. Either it acts as a stand-alone block preconditioner which computes and stores the association between global degrees of freedom and their unique global block numbers itself. Alternatively, the block preconditioner can act as a subsidiary block preconditioner within a (larger) master block preconditioner (pointed to by Master_block_preconditioner_pt). The master block preconditioner must have an equal or greater number of block types. Examples are the FSI preconditioner which is the 3x3 "master block preconditioner" for the Navier-Stokes preconditioners which deals with the 2x2 fluid-blocks within the overall structure. In this case, only the master block preconditioner computes and stores the master lookup schemes. All block preconditioners compute and store their own optimised lookup schemes.

In cases where a Problem contains elements of different element types (e.g. fluid and solid elements in a fluid-structure interaction problem), access to the elements of the same type must be provided via pointers to (possibly auxiliary) Meshes that only contain elements of a single type. The block preconditioner will then create global block numbers by concatenating the block types. Consider, e.g. a fluid-structure interaction problem in which the first Mesh contains (fluid) elements whose degrees of freedom have been subdivided into types "0" (the velocity, say) and "1" (the pressure say), while the second Mesh contains (solid) elements whose degrees of freedom are the nodal displacements, classified as its type "0". The combined block preconditioner then has three "block types": "0": Fluid velocity, "1": Fluid pressure, "2": Solid nodal positions. NOTE: currently this preconditioner uses the same communicator as the underlying problem. We may need to change this in the future.

Constructor & Destructor Documentation

◆ BlockPreconditioner() [1/2]

template<typename MATRIX >
oomph::BlockPreconditioner< MATRIX >::BlockPreconditioner ( )
inline

Constructor.

425  : Ndof_types_in_mesh(0)
426  {
427  // Initially set the master block preconditioner pointer to zero
428  // indicating that this is stand-alone preconditioner (i.e. the upper most
429  // level preconditioner) that will set up its own block lookup schemes
430  // etc.
432 
433  // The distribution of the concatenation of the internal block
434  // distributions.
435  // I.e. LinearAlgebraDistributionHelpers::concatenate
436  // (distributions of internal blocks).
437  //
438  // The concatenation of the internal block distributions is stored in two
439  // places depending on if this is the upper-most master block
440  // preconditioner or not.
441  //
442  // If this is a master block preconditioner
443  // (Master_block_preconditioner_pt is null), then it is stored in the
444  // variable Internal_preconditioner_matrix_distribution_pt (below). For
445  // subsidiary block preconditioners, this remains null.
446  //
447  // Because BlockPreconditioners are DistributedLinearAlgebraObjects, they
448  // have a distribution. For the upper-most master block preconditioner,
449  // this is the distribution of the underlying Jacobian.
450  //
451  // For all subsidiary block preconditioners, this remains null. The
452  // concatenation of the distribution of the internal blocks are stored
453  // as the distribution of the BlockPreconditioner.
454  //
455  // This seems inconsistent and I cannot figure out why this is done.
457 
458  // The concatenation of the external block distributions.
460 
461  // Initialise number of rows in this block preconditioner.
462  // This information is maintained if used as subsidiary or
463  // stand-alone block preconditioner (in the latter case it
464  // obviously stores the number of rows within the subsidiary
465  // preconditioner.
466  Nrow = 0;
467 
468  // Initialise number of different block types in this preconditioner.
469  // This information is maintained if used as subsidiary or
470  // stand-alone block preconditioner (in the latter case it
471  // obviously stores the number of rows within the subsidiary
472  // preconditioner.
474 
475  // Initialise number of different dof types in this preconditioner.
476  // This information is maintained if used as subsidiary or
477  // stand-alone block preconditioner (in the latter case it
478  // obviously stores the number of rows within the subsidiary
479  // preconditioner.
481 
482  // There are no blocks to start off with.
483  Block_distribution_pt.resize(0);
484 
485  // The distributions of the underlying internal blocks.
487 
488  // The distribution of the dof-level blocks, these are used during the
489  // concatenation process to create the distribution of the blocks.
490  Dof_block_distribution_pt.resize(0);
491 
492  // Clear both the Block_to_dof_map_coarse and Block_to_dof_map_fine
493  // vectors.
494  Block_to_dof_map_coarse.resize(0);
495  Block_to_dof_map_fine.resize(0);
496 
497  // Default the debug flag to false.
498  Recursive_debug_flag = false;
499 
500  // Default the debug flag to false.
501  Debug_flag = false;
502  } // EOFunc constructor
unsigned Internal_nblock_types
Definition: block_preconditioner.h:3362
Vector< LinearAlgebraDistribution * > Dof_block_distribution_pt
Definition: block_preconditioner.h:3341
Vector< Vector< unsigned > > Block_to_dof_map_fine
Mapping for the block types to the most fine grain dof types.
Definition: block_preconditioner.h:3288
Vector< LinearAlgebraDistribution * > Internal_block_distribution_pt
Storage for the default distribution for each internal block.
Definition: block_preconditioner.h:3337
unsigned Internal_ndof_types
Definition: block_preconditioner.h:3368
Vector< unsigned > Ndof_types_in_mesh
Definition: block_preconditioner.h:3356
unsigned Nrow
Definition: block_preconditioner.h:3393
LinearAlgebraDistribution * Internal_preconditioner_matrix_distribution_pt
Definition: block_preconditioner.h:3519
Vector< LinearAlgebraDistribution * > Block_distribution_pt
The distribution for the blocks.
Definition: block_preconditioner.h:3277
BlockPreconditioner< MATRIX > * Master_block_preconditioner_pt
Definition: block_preconditioner.h:3399
bool Debug_flag
Definition: block_preconditioner.h:3380
LinearAlgebraDistribution * Preconditioner_matrix_distribution_pt
Definition: block_preconditioner.h:3523
Vector< Vector< unsigned > > Block_to_dof_map_coarse
Definition: block_preconditioner.h:3285
bool Recursive_debug_flag
Definition: block_preconditioner.h:3375

References oomph::BlockPreconditioner< MATRIX >::Block_distribution_pt, oomph::BlockPreconditioner< MATRIX >::Block_to_dof_map_coarse, oomph::BlockPreconditioner< MATRIX >::Block_to_dof_map_fine, oomph::BlockPreconditioner< MATRIX >::Debug_flag, oomph::BlockPreconditioner< MATRIX >::Dof_block_distribution_pt, oomph::BlockPreconditioner< MATRIX >::Internal_block_distribution_pt, oomph::BlockPreconditioner< MATRIX >::Internal_nblock_types, oomph::BlockPreconditioner< MATRIX >::Internal_ndof_types, oomph::BlockPreconditioner< MATRIX >::Internal_preconditioner_matrix_distribution_pt, oomph::BlockPreconditioner< MATRIX >::Master_block_preconditioner_pt, oomph::BlockPreconditioner< MATRIX >::Nrow, oomph::BlockPreconditioner< MATRIX >::Preconditioner_matrix_distribution_pt, and oomph::BlockPreconditioner< MATRIX >::Recursive_debug_flag.

◆ ~BlockPreconditioner()

template<typename MATRIX >
virtual oomph::BlockPreconditioner< MATRIX >::~BlockPreconditioner ( )
inlinevirtual

Destructor.

507  {
509  } // EOFunc destructor
void clear_block_preconditioner_base()
Definition: block_preconditioner.h:2159

References oomph::BlockPreconditioner< MATRIX >::clear_block_preconditioner_base().

◆ BlockPreconditioner() [2/2]

template<typename MATRIX >
oomph::BlockPreconditioner< MATRIX >::BlockPreconditioner ( const BlockPreconditioner< MATRIX > &  )
delete

Broken copy constructor.

Member Function Documentation

◆ any_mesh_distributed()

template<typename MATRIX >
bool oomph::BlockPreconditioner< MATRIX >::any_mesh_distributed ( ) const
inlineprotected

Check if any of the meshes are distributed. This is equivalent to problem.distributed() and is used as a replacement.

3026  {
3027 #ifdef OOMPH_HAS_MPI
3028  // is_mesh_distributed() is only available with MPI
3029  for (unsigned i = 0, n = nmesh(); i < n; i++)
3030  {
3031  if (mesh_pt(i)->is_mesh_distributed())
3032  {
3033  return true;
3034  }
3035  }
3036 #endif
3037  return false;
3038  }
int i
Definition: BiCGSTAB_step_by_step.cpp:9
const unsigned n
Definition: CG3DPackingUnitTest.cpp:11
unsigned nmesh() const
Definition: block_preconditioner.h:1816
const Mesh * mesh_pt(const unsigned &i) const
Definition: block_preconditioner.h:1782

References i, oomph::BlockPreconditioner< MATRIX >::mesh_pt(), n, and oomph::BlockPreconditioner< MATRIX >::nmesh().

◆ block_distribution_pt() [1/2]

template<typename MATRIX >
const LinearAlgebraDistribution* oomph::BlockPreconditioner< MATRIX >::block_distribution_pt ( const unsigned b) const
inline

Access function to the block distributions (const version).

1905  {
1906 #ifdef PARANOID
1907  if (Block_distribution_pt.size() == 0)
1908  {
1909  std::ostringstream error_msg;
1910  error_msg << "Block distributions are not set up.\n"
1911  << "Have you called block_setup(...)?\n"
1912  << std::endl;
1913  throw OomphLibError(
1915  }
1916  if (b > nblock_types())
1917  {
1918  std::ostringstream error_msg;
1919  error_msg << "You requested the distribution for the block " << b
1920  << ".\n"
1921  << "But there are only " << nblock_types()
1922  << " block types.\n"
1923  << std::endl;
1924  throw OomphLibError(
1926  }
1927 #endif
1928 
1929  return Block_distribution_pt[b];
1930  } // EOFunc block_distribution_pt(...)
Scalar * b
Definition: benchVecAdd.cpp:17
unsigned nblock_types() const
Return the number of block types.
Definition: block_preconditioner.h:1670
#define OOMPH_EXCEPTION_LOCATION
Definition: oomph_definitions.h:61
#define OOMPH_CURRENT_FUNCTION
Definition: oomph_definitions.h:86

References b, oomph::BlockPreconditioner< MATRIX >::Block_distribution_pt, oomph::BlockPreconditioner< MATRIX >::nblock_types(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Referenced by oomph::BlockPreconditioner< MATRIX >::get_concatenated_block().

◆ block_distribution_pt() [2/2]

template<typename MATRIX >
LinearAlgebraDistribution* oomph::BlockPreconditioner< MATRIX >::block_distribution_pt ( const unsigned  b)
inline

Access function to the block distributions (non-const version).

1934  {
1935 #ifdef PARANOID
1936  if (Block_distribution_pt.size() == 0)
1937  {
1938  std::ostringstream error_msg;
1939  error_msg << "Block distributions are not set up.\n"
1940  << "Have you called block_setup(...)?\n"
1941  << std::endl;
1942  throw OomphLibError(
1944  }
1945  if (b > nblock_types())
1946  {
1947  std::ostringstream error_msg;
1948  error_msg << "You requested the distribution for the block " << b
1949  << ".\n"
1950  << "But there are only " << nblock_types()
1951  << " block types.\n"
1952  << std::endl;
1953  throw OomphLibError(
1955  }
1956 #endif
1957 
1958  return Block_distribution_pt[b];
1959  } // EOFunc block_distribution_pt(...)

References b, oomph::BlockPreconditioner< MATRIX >::Block_distribution_pt, oomph::BlockPreconditioner< MATRIX >::nblock_types(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

◆ block_matrix_test()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::block_matrix_test ( const unsigned block_i,
const unsigned block_j,
const MATRIX *  block_matrix_pt 
) const

Private helper function to check that every element in the block matrix (i,j) matches the corresponding element in the original matrix

test function to check that every element in the block matrix (block_i,block_j) matches the corresponding element in the original matrix

5843  {
5844  // boolean flag to indicate whether test is passed
5845  bool check = true;
5846 
5847  // number of rows in matrix
5848  unsigned n_row = matrix_pt()->nrow();
5849 
5850  // number of columns in matrix
5851  unsigned n_col = matrix_pt()->ncol();
5852 
5853  // loop over rows of original matrix
5854  for (unsigned i = 0; i < n_row; i++)
5855  {
5856  // if this coefficient is associated with a block in this block
5857  // preconditioner
5858  if (static_cast<int>(block_i) == this->internal_block_number(i))
5859  {
5860  // loop over columns of original matrix
5861  for (unsigned j = 0; j < n_col; j++)
5862  {
5863  // if the coeeficient is associated with a block in this block
5864  // preconditioner
5865  if (static_cast<int>(block_j) == this->internal_block_number(j))
5866  {
5867  // check whether elements in original matrix and matrix of block
5868  // pointers match
5869  if (matrix_pt()->operator()(i, j) !=
5870  block_matrix_pt->operator()(internal_index_in_block(i),
5872  {
5873  check = false;
5874  }
5875  }
5876  }
5877  }
5878  }
5879 
5880  // throw error
5881  if (!check)
5882  {
5883  std::ostringstream error_message;
5884  error_message << "The require elements have not been successfully copied"
5885  << " from the original matrix to the block matrices";
5886  throw OomphLibError(
5887  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
5888  }
5889  }
MATRIX * matrix_pt() const
Definition: block_preconditioner.h:520
int internal_block_number(const unsigned &i_dof) const
Definition: block_preconditioner.h:2689
int internal_index_in_block(const unsigned &i_dof) const
Definition: block_preconditioner.h:2705
void check(bool b, bool ref)
Definition: fastmath.cpp:12
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2

References check(), i, j, OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

◆ block_number()

template<typename MATRIX >
int oomph::BlockPreconditioner< MATRIX >::block_number ( const unsigned i_dof) const
inline

Return the block number corresponding to a global index i_dof.

1823  {
1824  int internal_block_number = this->internal_block_number(i_dof);
1825 
1826  if (internal_block_number == -1)
1827  {
1828  return internal_block_number;
1829  }
1830  else
1831  {
1832  // Map the internal block to the "external" block number, i.e. what the
1833  // writer of the preconditioner is expects.
1834  unsigned block_i = 0;
1835  while (std::find(Block_to_dof_map_fine[block_i].begin(),
1836  Block_to_dof_map_fine[block_i].end(),
1838  Block_to_dof_map_fine[block_i].end())
1839  {
1840  block_i++;
1841  }
1842 
1843  return block_i;
1844  }
1845  }
static constexpr lastp1_t end
Definition: IndexedViewHelper.h:79

References oomph::BlockPreconditioner< MATRIX >::Block_to_dof_map_fine, Eigen::placeholders::end, and oomph::BlockPreconditioner< MATRIX >::internal_block_number().

Referenced by oomph::BlockPreconditioner< MATRIX >::index_in_block().

◆ block_output_on()

template<typename MATRIX >
bool oomph::BlockPreconditioner< MATRIX >::block_output_on ( ) const
inline

Test if output of blocks is on or not.

2089  {
2090  return Output_base_filename.size() > 0;
2091  } // EOFunc block_output_on()
std::string Output_base_filename
Definition: block_preconditioner.h:3531

References oomph::BlockPreconditioner< MATRIX >::Output_base_filename.

◆ block_setup() [1/2]

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::block_setup
virtual

Determine the size of the matrix blocks and setup the lookup schemes relating the global degrees of freedom with their "blocks" and their indices (row/column numbers) in those blocks. The distributions of the preconditioner and the internal blocks are automatically specified (and assumed to be uniform) at this stage. This method should be used if the identity dof-to-block mapping is okay, i.e. dof number 0 corresponds to block number 0 dof number 1 corresponds to block number 1 dof number 2 corresponds to block number 2 etc...

Determine the size of the matrix blocks and setup the lookup schemes relating the global degrees of freedom with their "blocks" and their indices (row/column numbers) in those blocks. The distributions of the preconditioner and the blocks are automatically specified (and assumed to be uniform) at this stage. This method should be used if each DOF type corresponds to a unique block type.

2484  {
2485 #ifdef PARANOID
2486 
2487  // Subsidiary preconditioners don't really need the meshes
2488  if (this->is_master_block_preconditioner())
2489  {
2490  std::ostringstream err_msg;
2491  unsigned n = nmesh();
2492  if (n == 0)
2493  {
2494  err_msg << "No meshes have been set for this block preconditioner!\n"
2495  << "Set one with set_nmesh(...), set_mesh(...)" << std::endl;
2496  throw OomphLibError(
2498  for (unsigned m = 0; m < n; m++)
2499  {
2500  if (Mesh_pt[m] == 0)
2501  {
2502  err_msg << "The mesh pointer to mesh " << m << " is null!\n"
2503  << "Set a non-null one with set_mesh(...)" << std::endl;
2504  throw OomphLibError(
2506  }
2507  }
2508  }
2509  }
2510 #endif
2511 
2512  // Get the number of dof types.
2513  unsigned internal_n_dof_types = ndof_types();
2514 
2515  // Build the dof to block map - assume that each type of dof corresponds
2516  // to a different type of block.
2517  Vector<unsigned> dof_to_block_lookup(internal_n_dof_types);
2518  for (unsigned i = 0; i < internal_n_dof_types; i++)
2519  {
2520  dof_to_block_lookup[i] = i;
2521  }
2522 
2523  // call the block setup method
2524  this->block_setup(dof_to_block_lookup);
2525  }
bool is_master_block_preconditioner() const
Definition: block_preconditioner.h:2068
Vector< const Mesh * > Mesh_pt
Definition: block_preconditioner.h:3352
unsigned ndof_types() const
Return the total number of DOF types.
Definition: block_preconditioner.h:1694
virtual void block_setup()
Definition: block_preconditioner.cc:2483
int * m
Definition: level2_cplx_impl.h:294

References m, n, OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Referenced by oomph::GeneralPurposeBlockPreconditioner< MATRIX >::gp_preconditioner_block_setup(), oomph::DummyBlockPreconditioner< MATRIX >::setup(), oomph::HelmholtzGMRESMG< MATRIX >::solve(), and oomph::HelmholtzFGMRESMG< MATRIX >::solve().

◆ block_setup() [2/2]

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::block_setup ( const Vector< unsigned > &  dof_to_block_map_in)

Determine the size of the matrix blocks and setup the lookup schemes relating the global degrees of freedom with their "blocks" and their indices (row/column numbers) in those blocks. The distributions of the preconditioner and the blocks are automatically specified (and assumed to be uniform) at this stage. This method should be used if anything other than the identity dof-to-block mapping is required. The argument vector dof_to_block_map should be of length ndof. The indices represents the dof types whilst the value represents the block types. In general we want:

dof_to_block_map[dof_number] = block_number.

Determine the size of the matrix blocks and setup the lookup schemes relating the global degrees of freedom with their "blocks" and their indices (row/column numbers) in those blocks. The distributions of the preconditioner and the blocks are automatically specified (and assumed to be uniform) at this stage. This method should be used if any block contains more than one type of DOF. The argument vector dof_to_block_map should be of length ndof. Each element should contain an integer indicating the block number corresponding to that type of DOF.

52  {
53 #ifdef PARANOID
54  // Subsidiary preconditioners don't really need the meshes
56  {
57  std::ostringstream err_msg;
58  unsigned n = nmesh();
59  if (n == 0)
60  {
61  err_msg << "No meshes have been set for this block preconditioner!\n"
62  << "Set one with set_nmesh(...), set_mesh(...)" << std::endl;
63  throw OomphLibError(
65  for (unsigned m = 0; m < n; m++)
66  {
67  if (Mesh_pt[m] == 0)
68  {
69  err_msg << "The mesh pointer to mesh " << m << " is null!\n"
70  << "Set a non-null one with set_mesh(...)" << std::endl;
71  throw OomphLibError(
73  }
74  }
75  }
76  }
77 #endif
78 
79  // Create a copy of the vector input so that we can modify it below
80  Vector<unsigned> dof_to_block_map = dof_to_block_map_in;
81 
83  {
84 #ifdef PARANOID
85  // Get the size of the Doftype_in_master_preconditioner_coarse.
86  unsigned para_doftype_in_master_preconditioner_coarse_size =
88 
89  // Check that the Doftype_in_master_preconditioner_coarse vector is not
90  // empty. This must be set (via the function
91  // turn_into_subsidiary_block_preconditioner) if this is a
92  // subsidiary block preconditioner.
93  if (para_doftype_in_master_preconditioner_coarse_size == 0)
94  {
95  std::ostringstream err_msg;
96  err_msg << "The mapping from the dof types of the master "
97  << "block preconditioner \n"
98  << "to the subsidiary block preconditioner is empty.\n"
99  << "Doftype_in_master_preconditioner_coarse.size() == 0 \n"
100  << "has turn_into_subsidiary_block_preconditioner(...)\n"
101  << "been called with the correct parameters?\n"
102  << std::endl;
103  throw OomphLibError(
105  }
106 
107 
108  // PARANOID checks for Doftype_coarsen_map_coarse
109  // This is also set in the function
110  // turn_into_subsidiary_block_preconditioner(...).
111  //
112  // The Doftype_coarsen_map_coarse vector must satisfy two conditions
113  // for it to be valid.
114  //
115  // 1) The dof type numbers in the dof_coarsen_map vector must be
116  // unique. For example, it does not make sense to have the vector
117  // [[0,1][1,2]] because the first inner vector says
118  // "treat dof types 0 and 1 as dof type 0" and the second inner vector
119  // says "treat dof type 1 and 2 as dof type 1", but dof type 1 is
120  // already being treated as dof type 0.
121  //
122  // 2) Every SUBSIDIARY dof type must be mapped to a dof type in the
123  // Doftype_coarsen_map_coarse vector.
124  // For example, if there are 5 dof types (passed down from the master
125  // block preconditioner), and this block subsidiary block
126  // preconditioner only deals with 3 dof types, then all 5 dof types
127  // must be mapped to a dof type in the subsidiary preconditioner. For
128  // example if the dof_map is [1,2,3,4,5], then the subsidiary block
129  // preconditioner knows that 5 dof types have been passed down. But if
130  // it only works with three dof types, we MUST have three inner vectors
131  // in the doftype_coarsen_map vector (which corresponds to dof types 0,
132  // 1 and 2), the union of the dof types in the three inner vectors must
133  // contain dof types 0, 1, 2, 3 and 4 exactly once. It cannot contain,
134  // say, 0, 1, 5, 7, 9, even though it passes the uniqueness check. We
135  // ensure this by two conditions:
136  //
137  // 2.1) The Doftype_coarsen_map_coarse vector must contain the same
138  // number of dof types as the dof_map vector.
139  // In other words, recall that Doftype_coarsen_map_coarse is a
140  // 2D vector, this must contain the same number of vectors as
141  // there are elements in the dof_to_block_map_in vector.
142  //
143  // 2.2) The maximum element in the doftype_coarsen_map_coarse vector
144  // is the length of the dof_map vector minus 1.
145 
146  // A set is deal for checking the above three conditions, we shall insert
147  // all the elements in the doftype_coarsen_map_coarse vector into this
148  // set.
149  std::set<unsigned> doftype_map_set;
150 
151  // Condition (1): Check for uniqueness by inserting all the values of
152  // Doftype_coarsen_map_coarse into a set.
153  unsigned para_doftype_coarsen_map_coarse_size =
155 
156  // Loop through the outer vector of Doftype_coarsen_map_coarse
157  // then loop through the inner vectors and attempt to insert each
158  // element of Doftype_coarsen_map_coarse into doftype_map_set.
159  //
160  // The inner for loop will throw an error if we cannot insert the
161  // element, this means that it is already inserted and thus not unique.
162  for (unsigned i = 0; i < para_doftype_coarsen_map_coarse_size; i++)
163  {
164  // Loop through the inner vector
165  unsigned para_doftype_coarsen_map_coarse_i_size =
167  for (unsigned j = 0; j < para_doftype_coarsen_map_coarse_i_size; j++)
168  {
169  // Attempt to insert all the values of the inner vector into a set.
170  std::pair<std::set<unsigned>::iterator, bool> doftype_map_ret =
171  doftype_map_set.insert(Doftype_coarsen_map_coarse[i][j]);
172 
173  if (!doftype_map_ret.second)
174  {
175  std::ostringstream err_msg;
176  err_msg << "Error: the doftype number "
178  << " is already inserted." << std::endl;
179  throw OomphLibError(
181  }
182  }
183  }
184 
185  // Condition (2.1): Check that the doftype_map_set describes as many
186  // values as doftype_in_master_preconditioner_coarse. I.e. if dof_map
187  // contains 5 dof types, then the doftype_coarsen_map_coarse vector must
188  // also contain 5 dof types.
189  if (para_doftype_in_master_preconditioner_coarse_size !=
190  doftype_map_set.size())
191  {
192  std::ostringstream err_msg;
193  err_msg << "The size of doftype_in_master_preconditioner_coarse "
194  << "must be the same as the total\n"
195  << "number of values in the doftype_coarsen_map_coarse vector."
196  << std::endl;
197  throw OomphLibError(
199  }
200 
201  // Condition (2.2): Check that the maximum element in the
202  // doftype_coarsen_map_coarse vector is the length of the
203  // doftype_in_master_preconditioner_coarse minus 1.
204  unsigned para_doftype_in_master_preconditioner_coarse_size_minus_one =
205  para_doftype_in_master_preconditioner_coarse_size - 1;
206  if (para_doftype_in_master_preconditioner_coarse_size_minus_one !=
207  *doftype_map_set.rbegin())
208  {
209  std::ostringstream err_msg;
210  err_msg << "The maximum dof type number in the "
211  << "doftype_coarsen_map vector must be "
212  << para_doftype_in_master_preconditioner_coarse_size_minus_one
213  << std::endl;
214  throw OomphLibError(
216  }
217 #endif
218 
219  // Set the mapping from the master preconditioner DOF types to the
220  // subsidiary preconditioner DOF types.
221  //
222  // IMPORTANT: Since DOF types may be coarsened in the master block
223  // preconditioner, this may no longer reflect the actual underlying dof
224  // types. We must get the actual underlying dof types for the
225  // block_setup(...) function to work properly so all the look up schemes
226  // for this (subsidiary) block preconditioner is correct and works
227  // properly, this is for backwards compatibility purposes and to make sure
228  // Richard Muddle's still works at this (subsidiary) level, although it
229  // may not be used.
230  //
231  // If we do not want to make it backwards compatible, we may as well
232  // kill the block_setup(...) for subsidiary block preconditioners -
233  // but other thing may break. Do it at your own risk (take time to
234  // fully understand the whole block preconditioning framework code).
235 
236  // Create the corresponding Doftype_in_master_preconditioner_fine and
237  // Doftype_coarsen_map_fine vectors.
238 
239  // First resize the vectors.
241  Doftype_coarsen_map_fine.resize(0);
242 
243  // The Doftype_in_master_preconditioner_fine vector is easy. We know that
244  // the Doftype_coarsen_map_fine in the master preconditioner must be
245  // constructed already. So we simply loop through the values in
246  // doftype_in_master_preconditioner_coarse, then get the most fine grain
247  // dof types from the master preconditioner's Doftype_coarsen_map_fine
248  // vector.
249  //
250  // For example, if the master preconditioner has the vector:
251  // Doftype_coarsen_map_fine = [0,1,2,3][4,5,6,7][8,9,10,11][12,13][14,15]
252  //
253  // and passes the two vectors
254  // doftype_in_master_preconditioner_coarse = [1,2,3]
255  // doftype_coarsen_map_coarse = [[0][1,2]]
256  //
257  // Then we want
258  // Doftype_in_master_preconditioner_fine = [4,5,6,7,8,9,10,11,12,13]
259  //
260  // We achieve this by looking up the corresponding fine dof types in the
261  // masters' Doftype_coarsen_map_fine vector which corresponds to the
262  // values in Doftype_in_master_preconditioner_coarse.
263  //
264  // That is, the values in Doftype_in_master_preconditioner_coarse gives us
265  // the index of sub vector we want in the master's
266  // Doftype_coarsen_map_fine vector.
267 
268 #ifdef PARANOID
269  // Check that the master block preconditioner's Doftype_coarsen_map_fine
270  // is set up. Under the current implementation, this would always be set
271  // up properly, but we check it just in case!
273  0)
274  {
275  std::ostringstream err_msg;
276  err_msg << "The master block preconditioner's "
277  << "Doftype_coarsen_map_fine is not\n"
278  << "set up properly.\n"
279  << "\n"
280  << "This vector is constructed in the function "
281  << "block_setup(...).\n"
282  << std::endl;
283  throw OomphLibError(
285  }
286 #endif
287 
288  unsigned doftype_in_master_preconditioner_coarse_size =
290  for (unsigned i = 0; i < doftype_in_master_preconditioner_coarse_size;
291  i++)
292  {
293  // The index of the sub vector we want.
294  unsigned subvec_index = Doftype_in_master_preconditioner_coarse[i];
295 
296  // Get the corresponding most fine grain sub vector from the master
297  // block preconditioner
298  Vector<unsigned> tmp_master_dof_subvec =
299  Master_block_preconditioner_pt->get_fine_grain_dof_types_in(
300  subvec_index);
301 
304  tmp_master_dof_subvec.begin(),
305  tmp_master_dof_subvec.end());
306  }
307 
308  // The Doftype_coarsen_map_fine vector is a bit more tricky.
309  // The Doftype_coarsen_map_coarse vector describes which coarse dof types
310  // of THIS preconditioner are grouped together. We have to translate this
311  // into the most fine grain dof types.
312  //
313  // For example, if
314  // Doftype_coarsen_map_coarse = [[0][1,2]]
315  // Doftype_in_master_preconditioner_coarse = [1,2,3]
316  //
317  // and the MASTER preconditioner has:
318  // Doftype_coarsen_map_fine= [[0,1,2,3][4,5,6,7][8,9,10,11][12,13][14,15]]
319  //
320  // Then [[0][1,2]] tell us that the most fine grain DOF types 1 of the
321  // master preconditioner most be grouped together, and the most fine
322  // grained dof types 2 and 3 of the master preconditioner must be grouped
323  // together.
324  //
325  // This gives the vector [[4,5,6,7] [8,9,10,11,12,13]], translating this
326  // into the local DOF types of this preconditioner we have
327  // Doftype_coarsen_map_fine = [[0,1,2,3][4,5,6,7,8,9]]. This corresponds
328  // with the Doftype_in_master_preconditioner_fine vector we created above:
329  // Doftype_in_master_preconditioner_fine = [4,5,6,7,8,9,10,11,12,13]
330  //
331  // Together, the master block preconditioner says to THIS subsidiary block
332  // preconditioner "work on my DOF types [4,5,6,7,8,9,10,11,12,13], but
333  // group your DOF type [0,1,2,3] together as DOF type 0 and [4,5,6,7,8,9]
334  // together together as DOF type 1".
335  //
336  // Think of it like this: For each DOF type in Doftype_coarsen_map_coarse
337  // we look at how many values this corresponds to in the master
338  // preconditioner. In this case, Doftype_coarsen_map_coarse:
339  //
340  // 1 - corresponds to fine DOF types 0,1,2,3 in this preconditioner,
341  // and 4,5,6,7 in the master preconditioner;
342  //
343  // 2 - corresponds to fine DOF types 4,5,6,7 in this preconditioner,
344  // and 8,9,10,11 in the master preconditioner;
345  //
346  // 3 - corresponds to fine DOF types 8,9 in this preconditioner,
347  // and 12,13 in the master preconditioner.
348  //
349  // Thus Doftype_coarsen_map_fine = [[0,1,2,3][4,5,6,7,8,9]]
350  //
352  //
353  // How to do this: First we create a 2D vector which has the corresponds
354  // to the fine dof types in the master preconditioner but starting from
355  // 0. For example, take the above example (repeated below):
356  // Passed to this prec by the master prec:
357  // Doftype_coarsen_map_coarse = [[0][1,2]]
358  // Doftype_in_master_preconditioner_coarse = [1,2,3]
359  //
360  // and the MASTER preconditioner has:
361  // Doftype_coarsen_map_fine= [[0,1,2,3][4,5,6,7][8,9,10,11][12,13][14,15]]
362  //
363  // Step 1:
364  // Then, the temp 2D vector we want to create is:
365  // master_fine_doftype_translated = [[0 1 2 3], [4,5,6,7], [8,9]]
366  // This comes from using Doftype_in_master_preconditioner_coarse
367  // then get the number of fine dof types in the master.
368  //
369  // Step 2:
370  // Then:
371  // Loop through the vector Doftype_coarsen_map_coarse,
372  // Loop over the inner vectors in Doftype_coarsen_map_coarse
373  // Each element in the inner vector corresponds to a vector in
374  // master_fine_doftype_translated. We push in the vectors of
375  // master_fine_doftype_translated intp Doftype_coarsen_map_fine
376  //
377 
378  Vector<Vector<unsigned>> master_fine_doftype_translated;
379  unsigned dof_type_index = 0;
380  for (unsigned i = 0; i < doftype_in_master_preconditioner_coarse_size;
381  i++)
382  {
383  // How many fine DOF types are in the master's
384  // Doftype_in_master_preconditioner_coarse[i]?
385  unsigned coarse_dof = Doftype_in_master_preconditioner_coarse[i];
386 
387  unsigned n_master_fine_doftypes =
388  Master_block_preconditioner_pt->nfine_grain_dof_types_in(coarse_dof);
389 
390  Vector<unsigned> tmp_sub_vec;
391  for (unsigned j = 0; j < n_master_fine_doftypes; j++)
392  {
393  tmp_sub_vec.push_back(dof_type_index);
394  dof_type_index++;
395  }
396  master_fine_doftype_translated.push_back(tmp_sub_vec);
397  }
398 
399 
400  // master_fine_doftype_translated now contains vectors with values are
401  // from 0, 1, 2, ..,
402  //
403  // Now read out the values of master_fine_doftype_translated and place
404  // them in order according to Doftype_coarsen_map_coarse.
405  unsigned doftype_coarsen_map_coarse_size =
407  for (unsigned i = 0; i < doftype_coarsen_map_coarse_size; i++)
408  {
409  Vector<unsigned> tmp_vec;
410  unsigned doftype_coarsen_map_coarse_i_size =
412  for (unsigned j = 0; j < doftype_coarsen_map_coarse_i_size; j++)
413  {
414  unsigned subvec_i = Doftype_coarsen_map_coarse[i][j];
415 
416  tmp_vec.insert(tmp_vec.end(),
417  master_fine_doftype_translated[subvec_i].begin(),
418  master_fine_doftype_translated[subvec_i].end());
419  }
420 
421  Doftype_coarsen_map_fine.push_back(tmp_vec);
422  }
423 
424  // Get the number of block types (and DOF types) in this preconditioner
425  // from the length of the dof_map vector.
427 
428  // Nblock_types is later updated in block_setup(...)
430 
431  // Compute number of rows in this (sub) preconditioner using data from
432  // the master.
433  Nrow = 0;
434  for (unsigned b = 0; b < Internal_ndof_types; b++)
435  {
437  }
438 
439 #ifdef PARANOID
440  if (Nrow == 0)
441  {
442  std::ostringstream error_message;
443  error_message
444  << "Nrow=0 in subsidiary preconditioner. This seems fishy and\n"
445  << "suggests that block_setup() was not called for the \n"
446  << "master block preconditioner yet.";
447  throw OomphLibWarning(error_message.str(),
450  }
451 #endif
452  }
453 
454  // If this is a master block preconditioner, then set the
455  // Doftype_coarsen_map_fine and Doftype_coarsen_map_coarse to the
456  // identity. Recall that the Doftype_coarsen_map_fine maps the dof types
457  // that this preconditioner requires with the most fine grain dof types (the
458  // internal dof types) and the Doftype_coarsen_map_coarse maps the dof
459  // types that this preconditioner requires with the dof types which this
460  // preconditioner is given from a master preconditioner (these dof types may
461  // or may not be coarsened). In the case of the master preconditioner, these
462  // are the same (since dof types are not coarsened), furthermore the
463  // identity mapping is provided to say that dof type 0 maps to dof type 0,
464  // dof type 1 maps to dof type 1,
465  // dof type 2 maps to dof type 2,
466  // etc...
467  //
468  // If this is not a master block preconditioner, then the vectors
469  // Doftype_coarsen_map_fine and Doftype_coarsen_map_coarse is handled
470  // by the turn_into_subsidiary_block_preconditioner(...) function.
472  {
473  // How many dof types does this preconditioner work with?
474  unsigned n_external_dof_types = dof_to_block_map.size();
475 
476  // Note: at the master level, the n_external_dof_types should be the same
477  // as the internal_ndof_types(), since the dof_to_block_map MUST describe
478  // the mapping between every dof type (not yet coarsened - so it is the
479  // same number as the internal dof types) to the block types. But we
480  // distinguish them for clarity. We also check that this is the case.
481 #ifdef PARANOID
482  unsigned n_internal_dof_types = internal_ndof_types();
483 
484  if (n_internal_dof_types != n_external_dof_types)
485  {
486  std::ostringstream err_msg;
487  err_msg
488  << "The internal ndof types and the length of the dof_to_block_map\n"
489  << "vector is not the same. Since this is the master block "
490  << "preconditioner,\n"
491  << "you must describe which block each DOF type belongs to,\n"
492  << "no more, no less."
493  << "internal_ndof_types = " << n_internal_dof_types << "\n"
494  << "dof_to_block_map.size() = " << n_external_dof_types << "\n";
495  throw OomphLibWarning(
497  }
498 #endif
499 
500  // Clear and reserve space.
501  Doftype_coarsen_map_fine.clear();
503  Doftype_coarsen_map_fine.reserve(n_external_dof_types);
504  Doftype_coarsen_map_coarse.reserve(n_external_dof_types);
505 
506  // Now push back the identity mapping.
507  for (unsigned i = 0; i < n_external_dof_types; i++)
508  {
509  // Create a vector and push it in.
510  Vector<unsigned> tmp_vec(1, i);
511  Doftype_coarsen_map_fine.push_back(tmp_vec);
512  Doftype_coarsen_map_coarse.push_back(tmp_vec);
513  }
514  }
515  else
516  // Else this is a subsidiary block preconditioner.
517  {
518  // Both the Doftype_coarsen_map_fine and Doftype_coarsen_map_coarse
519  // vectors must be already be handled by the
520  // turn_into_subsidiary_block_preconditioner(...) function. We check this.
521 #ifdef PARANOID
522  if ((Doftype_coarsen_map_fine.size() == 0) ||
523  (Doftype_coarsen_map_coarse.size() == 0))
524  {
525  std::ostringstream err_msg;
526  err_msg << "Either the Doftype_coarsen_map_fine or the \n"
527  << "Doftype_coarsen_map_coarse vectors is of size 0.\n"
528  << "Did you remember to call the function "
529  << "turn_into_subsidiary_block_preconditioner(...)?";
530  throw OomphLibWarning(
532  }
533 #endif
534  }
535 
536 
537  // Now we create the vector Block_to_dof_map_coarse.
538  // Recall that the vector describe which dof types are in which block with
539  // the relationship:
540  //
541  // Block_to_dof_map_coarse[block_number] = Vector[dof types];
542  //
543  // Note that this is not the internal (underlying) dof type.
544  // Nor is this in relation to the parent block preconditioner's dof type.
545  // The number of elements in it is the same as dof_to_block_map vector.
546  //
547  // Since the dof type coarsening feature is added later, we encapsulate this
548  // bit of the code so it does not affect things below.
549  {
550  // Check that the dof_to_block map "makes sense" for the
551  // Doftype_coarsen_map_coarse.
552  // The Doftype_coarsen_map_coarse describes which doftypes should be
553  // considered as a single doftype in THIS preconditioner.
554  //
555  // For example, if this preconditioner is the LSC block preconditioner
556  // applied to a 3D problem, it expects 4 doftypes:
557  // 3 velocity, [u, v, w] and 1 pressure [p],
558  // giving us the dof type ordering
559  // [u v w p].
560  //
561  // The LSC preconditioner groups the velocity and pressure doftypes
562  // separately, thus the dof_to_block_map will be:
563  // [0 0 0 1]
564  //
565  // Then the Doftype_coarsen_map_coarse MUST have length 4, to identify
566  // which of the OTHER (possibly coarsened) dof types should be grouped
567  // together to be considered as a single dof types of THIS preconditioner.
568  //
569  // For example, if the preconditioner above this one has the dof type
570  // ordering:
571  // 0 1 2 3 4 5 6 7 8 9
572  // ub vb wb up vp wp ut vt wt p
573  // Then we want to tell THIS preconditioner which dof types belongs to
574  // u, v, w and p, by providing the optional argument
575  // Doftype_coarsen_map_coarse to the
576  // turn_into_subsidiary_block_preconditioner(...) function
577  // [[0 3 6] [1 4 7] [2 5 8] [9]]
578  //
579  // So, it is important that the length of dof_to_block_map is the same as
580  // the length of Doftype_coarsen_map_coarse. We check this.
581  unsigned dof_to_block_map_size = dof_to_block_map.size();
582 
583 #ifdef PARANOID
584  if (dof_to_block_map_size != Doftype_coarsen_map_coarse.size())
585  {
586  std::ostringstream err_msg;
587  err_msg
588  << "The size of dof_to_block_map and Doftype_coarsen_map_coarse is "
589  "not "
590  << "the same.\n"
591  << "dof_to_block_map.size() = " << dof_to_block_map_size << "\n"
592  << "Doftype_coarsen_map_coarse.size() = "
593  << Doftype_coarsen_map_coarse.size() << ".\n"
594  << "One of the two list is incorrect, please look at the comments\n"
595  << "in the source code for more details.";
596  throw OomphLibWarning(
598  }
599 #endif
600 
601  // Create the Block_to_dof_map_coarse from
602  // the dof_to_block_map and Doftype_coarsen_map_coarse.
603 
604  // find the maximum block number
605  unsigned max_block_number =
606  *std::max_element(dof_to_block_map.begin(), dof_to_block_map.end());
607 
608  // Now we do the following:
609  // Lets say the Doftype_coarsen_map_coarse is:
610  // [0 3 6]
611  // [1 4 7]
612  // [2 5 8]
613  // [9]
614  //
615  // (this is the same as the above example)
616  //
617  // and the dof_to_block_map is [0 0 0 1].
618  //
619  // Then we need to form the Block_to_dof_map_coarse:
620  // [0 3 6 1 4 7 2 5 8]
621  // [9]
622 
623  // Clear anything in the Block_to_dof_map_coarse
624  Block_to_dof_map_coarse.clear();
625 
626  const unsigned tmp_nblock = max_block_number + 1;
627 
628  Block_to_dof_map_coarse.resize(tmp_nblock);
629 
630  for (unsigned i = 0; i < dof_to_block_map_size; i++)
631  {
632  Block_to_dof_map_coarse[dof_to_block_map[i]].push_back(i);
633  }
634 
635  Block_to_dof_map_fine.clear();
636  Block_to_dof_map_fine.resize(tmp_nblock);
637  for (unsigned block_i = 0; block_i < tmp_nblock; block_i++)
638  {
639  // get the dof types in this block.
640  const unsigned ndof_in_block = Block_to_dof_map_coarse[block_i].size();
641  for (unsigned dof_i = 0; dof_i < ndof_in_block; dof_i++)
642  {
643  const unsigned coarsened_dof_i =
644  Block_to_dof_map_coarse[block_i][dof_i];
645 
646  // Insert the most fine grain dofs which this dof_i corresponds to
647  // into block_i
648  Vector<unsigned> dof_i_dofs =
649  Doftype_coarsen_map_fine[coarsened_dof_i];
650 
651  Block_to_dof_map_fine[block_i].insert(
652  Block_to_dof_map_fine[block_i].end(),
653  dof_i_dofs.begin(),
654  dof_i_dofs.end());
655  }
656  }
657 
658  // Now set the dof_to_block_map to the identify.
659  // NOTE: We are now using the internal n dof types. This is because the
660  // dof type coarsening feature was built on top of the existing block
661  // preconditioning framework which does not handle coarsening of dof
662  // types. Hence, under the hood, it still works with the most fine grain
663  // dof types and does not do any coarsening.
664 
665  // Locally cache the internal ndof types (using access function because
666  // the Internal_ndof_type variable may not be set up yet if this is a
667  // master preconditioner).
668  unsigned tmp_internal_ndof_types = internal_ndof_types();
669 
670  dof_to_block_map.resize(tmp_internal_ndof_types, 0);
671 
672  for (unsigned i = 0; i < tmp_internal_ndof_types; i++)
673  {
674  dof_to_block_map[i] = i;
675  }
676  } // end of Block_to_dof_map_coarse encapsulation
677 
678 #ifdef PARANOID
679 
680  // Check that the meshes are ok. This only needs to be done in the master
681  // because subsidiary preconditioners don't do anything with the meshes
682  // here.
684  {
685  // This is declared as local_nmesh because there are other variables
686  // called nmesh floating about... but this will not exist if PARANOID is
687  // switched on.
688  unsigned local_nmesh = nmesh();
689 
690  // Check that some mesh pointers have been assigned.
691  if (local_nmesh == 0)
692  {
693  std::ostringstream error_msg;
694  error_msg << "Cannot setup blocks because no meshes have been set.";
695  throw OomphLibError(
697  }
698 
699  // Each mesh must contain elements with the same number of dof.
700  // A stricter check is to ensure that the mesh contains only one type of
701  // elements. This is relaxed in same cases.
702  for (unsigned mesh_i = 0; mesh_i < local_nmesh; mesh_i++)
703  {
704  // The number of elements in the current mesh.
705  unsigned n_element = mesh_pt(mesh_i)->nelement();
706 
707  // When the bulk mesh is distributed, there may not be any elements
708  // in the surface mesh(es).
709  if (n_element > 0)
710  {
711  // The string of the first element in the current mesh.
712  std::string first_element_string =
713  typeid(*(mesh_pt(mesh_i)->element_pt(0))).name();
714 
715  // If there are multiple element types in the current mesh,
716  // we can at least make sure that they contain the same types of DOFs.
717  if (bool(Allow_multiple_element_type_in_mesh[mesh_i]))
718  {
719  // The ndof types of the first element.
720  unsigned first_element_ndof_type =
721  mesh_pt(mesh_i)->element_pt(0)->ndof_types();
722 
723  // Loop through the meshes and compare the number of types of DOFs.
724  for (unsigned el_i = 1; el_i < n_element; el_i++)
725  {
726  // The ndof type of the current element.
727  unsigned current_element_ndof_type =
728  mesh_pt(mesh_i)->element_pt(el_i)->ndof_types();
729 
730  // The string of the current element.
731  std::string current_element_string =
732  typeid(*(mesh_pt(mesh_i)->element_pt(el_i))).name();
733 
734  // Compare against the first element.
735  if (current_element_ndof_type != first_element_ndof_type)
736  {
737  std::ostringstream error_message;
738  error_message
739  << "Elements in the same mesh MUST have the same number of "
740  "types "
741  << "of DOFs.\n"
742  << "The element in mesh " << mesh_i << ", at position "
743  << el_i << " is: \n"
744  << current_element_string << ", \n"
745  << "with ndof types: " << current_element_ndof_type << ".\n"
746  << "The first element in the same mesh is: \n"
747  << first_element_string << ", \n"
748  << "with ndof types: " << first_element_ndof_type << ".\n";
749  throw OomphLibError(error_message.str(),
752  }
753  }
754  }
755  else
756  // There should be only one type of elements in the current mesh.
757  // Check that this is the case!
758  {
759  // Loop through the elements in the current mesh.
760  for (unsigned el_i = 1; el_i < n_element; el_i++)
761  {
762  // The string of the current element.
763  std::string current_element_string =
764  typeid(*(mesh_pt(mesh_i)->element_pt(el_i))).name();
765 
766  // Compare against the first element.
767  if (current_element_string.compare(first_element_string) != 0)
768  {
769  std::ostringstream error_message;
770  error_message
771  << "By default, a mesh containing block preconditionable "
772  << "elements must contain only one type of element.\n"
773  << "The element in mesh " << mesh_i << ", at position "
774  << el_i << " is: \n"
775  << current_element_string << "\n"
776  << "The first element in the same mesh is: \n"
777  << first_element_string << "\n"
778  << "If this is correct, consider calling the set_mesh(...) "
779  "with\n"
780  << "the optional argument set true to allow multiple "
781  "element\n"
782  << "types in the same mesh.\n"
783  << "Note: A minimal requirement is that the elements in the "
784  "same\n"
785  << "mesh MUST have the same number of DOF types.";
786  throw OomphLibError(error_message.str(),
789  }
790  }
791  }
792  }
793  }
794  }
795 
796 #endif
797  // clear the memory
799 
800  // get my_rank and nproc
801 #ifdef OOMPH_HAS_MPI
802  unsigned my_rank = comm_pt()->my_rank();
803  unsigned nproc = comm_pt()->nproc();
804 #endif
805 
806 
808  // start of master block preconditioner only operations
810 #ifdef OOMPH_HAS_MPI
811  unsigned* nreq_sparse = new unsigned[nproc]();
812  unsigned* nreq_sparse_for_proc = new unsigned[nproc]();
813  unsigned** index_in_dof_block_sparse_send = new unsigned*[nproc]();
814  unsigned** dof_number_sparse_send = new unsigned*[nproc]();
815  Vector<MPI_Request> send_requests_sparse;
816  Vector<MPI_Request> recv_requests_sparse;
817 #endif
818 
819  // If this preconditioner is the master preconditioner then we need
820  // to assemble the vectors : Dof_number
821  // Index_in_dof_block
823  {
824  // Get the number of dof types in each mesh.
825  Ndof_types_in_mesh.assign(nmesh(), 0);
826  for (unsigned i = 0; i < nmesh(); i++)
827  {
829  }
830  // Setup the distribution of this preconditioner, assumed to be the same
831  // as the matrix if the matrix is distributable.
832  if (dynamic_cast<DistributableLinearAlgebraObject*>(matrix_pt()))
833  {
834  this->build_distribution(
836  ->distribution_pt());
837  }
838  else
839  {
840  LinearAlgebraDistribution dist(comm_pt(), matrix_pt()->nrow(), false);
841  this->build_distribution(dist);
842  }
843  Nrow = matrix_pt()->nrow();
844 
845  // Boolean to indicate whether the matrix is actually distributed,
846  // ie distributed and on more than one processor.
847  bool matrix_distributed =
848  (this->distribution_pt()->distributed() &&
849  this->distribution_pt()->communicator_pt()->nproc() > 1);
850 
851 
852  // Matrix must be a CR matrix.
853  CRDoubleMatrix* cr_matrix_pt = dynamic_cast<CRDoubleMatrix*>(matrix_pt());
854 
855  if (cr_matrix_pt == 0)
856  {
857  std::ostringstream error_message;
858  error_message << "Block setup for distributed matrices only works "
859  << "for CRDoubleMatrices";
860  throw OomphLibError(error_message.str(),
863  }
864 
865 
866  // Get distribution.
867  unsigned first_row = this->distribution_pt()->first_row();
868  unsigned nrow_local = this->distribution_pt()->nrow_local();
869  unsigned last_row = first_row + nrow_local - 1;
870 
871 #ifdef OOMPH_HAS_MPI
872  // storage for the rows required by each processor in the dense
873  // block lookup storage scheme
874  // dense_required_rows(p,0) is the minimum global index required by proc p
875  // ...(p,1) is the maximum global index required by proc p
876  DenseMatrix<unsigned> dense_required_rows(nproc, 2);
877  for (unsigned p = 0; p < nproc; p++)
878  {
879  dense_required_rows(p, 0) = this->distribution_pt()->first_row(p);
880  dense_required_rows(p, 1) = this->distribution_pt()->first_row(p) +
881  this->distribution_pt()->nrow_local(p) - 1;
882  }
883 
884  // determine the global rows That are not in the range first_row to
885  // first_row+nrow_local for which we should store the
886  // Dof_index and Index_in_dof_block for
887  // then send the lists to other processors
888  std::set<unsigned> sparse_global_rows_for_block_lookup;
889  if (matrix_distributed)
890  {
891  unsigned nnz = cr_matrix_pt->nnz();
892  int* column_index = cr_matrix_pt->column_index();
893  for (unsigned i = 0; i < nnz; i++)
894  {
895  unsigned ci = column_index[i];
896  if (ci < first_row || ci > last_row)
897  {
898  sparse_global_rows_for_block_lookup.insert(ci);
899  }
900  }
901  }
902 
903  int nsparse = sparse_global_rows_for_block_lookup.size();
904 
905  Global_index_sparse.resize(0);
906  std::copy(sparse_global_rows_for_block_lookup.begin(),
907  sparse_global_rows_for_block_lookup.end(),
908  std::back_inserter(Global_index_sparse));
909 
910  Index_in_dof_block_sparse.resize(nsparse);
911  Dof_number_sparse.resize(nsparse);
912  sparse_global_rows_for_block_lookup.clear();
913 
914  Vector<MPI_Request> recv_requests_sparse_nreq;
915  if (matrix_distributed)
916  {
917  MPI_Aint base_displacement_sparse;
918  MPI_Get_address(nreq_sparse, &base_displacement_sparse);
919 
920  int zero = 0;
921  for (unsigned p = 0; p < nproc; p++)
922  {
923  // determine the global eqn numbers required by this processor
924  // that can be classified by processor p
925  int begin = 0;
926  for (int i = 0; i < nsparse; ++i)
927  {
928  if (Global_index_sparse[i] < dense_required_rows(p, 0))
929  {
930  ++begin;
931  }
932  else
933  {
934  if (Global_index_sparse[i] <= dense_required_rows(p, 1))
935  {
936  ++nreq_sparse[p];
937  }
938  else
939  {
940  break;
941  }
942  }
943  }
944 
945  // if this processor has rows to be classified by proc p
946  if (nreq_sparse[p] > 0)
947  {
948  // send the number of global eqn numbers
949  MPI_Request req1;
950  MPI_Isend(&nreq_sparse[p],
951  1,
952  MPI_UNSIGNED,
953  p,
954  31,
955  comm_pt()->mpi_comm(),
956  &req1);
957  send_requests_sparse.push_back(req1);
958 
959  // send the global eqn numbers
960  MPI_Request req2;
961  MPI_Isend(&Global_index_sparse[begin],
962  nreq_sparse[p],
963  MPI_UNSIGNED,
964  p,
965  32,
966  comm_pt()->mpi_comm(),
967  &req2);
968  send_requests_sparse.push_back(req2);
969 
970  // post the recvs for the data that will be returned
971 
972  // the datatypes, displacements, lengths for the two datatypes
973  MPI_Datatype types[2];
974  MPI_Aint displacements[2];
975  int lengths[2];
976 
977  // index in dof block
978  MPI_Type_contiguous(nreq_sparse[p], MPI_UNSIGNED, &types[0]);
979  MPI_Type_commit(&types[0]);
980  MPI_Get_address(&Index_in_dof_block_sparse[begin],
981  &displacements[0]);
982  displacements[0] -= base_displacement_sparse;
983  lengths[0] = 1;
984 
985  // dof number
986  MPI_Type_contiguous(nreq_sparse[p], MPI_UNSIGNED, &types[1]);
987  MPI_Type_commit(&types[1]);
988  MPI_Get_address(&Dof_number_sparse[begin], &displacements[1]);
989  displacements[1] -= base_displacement_sparse;
990  lengths[1] = 1;
991 
992  // build the final type
993  MPI_Datatype recv_type;
994  MPI_Type_create_struct(
995  2, lengths, displacements, types, &recv_type);
996  MPI_Type_commit(&recv_type);
997  MPI_Type_free(&types[0]);
998  MPI_Type_free(&types[1]);
999 
1000  // and recv
1001  MPI_Request req;
1002  MPI_Irecv(
1003  nreq_sparse, 1, recv_type, p, 33, comm_pt()->mpi_comm(), &req);
1004  recv_requests_sparse.push_back(req);
1005  MPI_Type_free(&recv_type);
1006  }
1007 
1008  // if no communication required, confirm this
1009  if (nreq_sparse[p] == 0)
1010  {
1011  MPI_Request req1;
1012  MPI_Isend(
1013  &zero, 1, MPI_UNSIGNED, p, 31, comm_pt()->mpi_comm(), &req1);
1014  send_requests_sparse.push_back(req1);
1015  }
1016 
1017  //
1018  MPI_Request req;
1019  MPI_Irecv(&nreq_sparse_for_proc[p],
1020  1,
1021  MPI_UNSIGNED,
1022  p,
1023  31,
1024  comm_pt()->mpi_comm(),
1025  &req);
1026  recv_requests_sparse_nreq.push_back(req);
1027  }
1028  }
1029 #endif
1030 
1031  // resize the storage
1032  Dof_number_dense.resize(nrow_local);
1034 
1035  // zero the number of dof types
1036  Internal_ndof_types = 0;
1037 
1038 #ifdef PARANOID
1039  // Vector to keep track of previously assigned block numbers
1040  // to check consistency between multiple assignments.
1041  Vector<int> previously_assigned_block_number(nrow_local,
1043 #endif
1044 
1045  // determine whether the problem is distribution
1046  bool problem_distributed = false;
1047 
1048  // the problem method distributed() is only accessible with MPI
1049 #ifdef OOMPH_HAS_MPI
1050  problem_distributed = any_mesh_distributed();
1051 #endif
1052 
1053  // if the problem is not distributed
1054  if (!problem_distributed)
1055  {
1056  // Offset for the block type in the overall system.
1057  // Different meshes contain different block-preconditionable
1058  // elements -- their blocks are added one after the other.
1059  unsigned dof_offset = 0;
1060 
1061  // Loop over all meshes.
1062  for (unsigned m = 0; m < nmesh(); m++)
1063  {
1064  // Number of elements in this mesh.
1065  unsigned n_element = mesh_pt(m)->nelement();
1066 
1067  // Find the number of block types that the elements in this mesh
1068  // are in charge of.
1069  unsigned ndof_in_element = ndof_types_in_mesh(m);
1070  Internal_ndof_types += ndof_in_element;
1071 
1072  for (unsigned e = 0; e < n_element; e++)
1073  {
1074  // List containing pairs of global equation number and
1075  // dof number for each global dof in an element.
1076  std::list<std::pair<unsigned long, unsigned>> dof_lookup_list;
1077 
1078  // Get list of blocks associated with the element's global unknowns.
1080  dof_lookup_list);
1081 
1082  // Loop over all entries in the list
1083  // and store the block number.
1084  typedef std::list<std::pair<unsigned long, unsigned>>::iterator IT;
1085  for (IT it = dof_lookup_list.begin(); it != dof_lookup_list.end();
1086  it++)
1087  {
1088  unsigned long global_dof = it->first;
1089  if (global_dof >= unsigned(first_row) &&
1090  global_dof <= unsigned(last_row))
1091  {
1092  unsigned dof_number = (it->second) + dof_offset;
1093  Dof_number_dense[global_dof - first_row] = dof_number;
1094 
1095 #ifdef PARANOID
1096  // Check consistency of block numbers if assigned multiple times
1097  if (previously_assigned_block_number[global_dof - first_row] <
1098  0)
1099  {
1100  previously_assigned_block_number[global_dof - first_row] =
1101  dof_number;
1102  }
1103 #endif
1104  }
1105  }
1106  }
1107 
1108  // About to do the next mesh which contains block preconditionable
1109  // elements of a different type; all the dofs that these elements are
1110  // "in charge of" differ from the ones considered so far.
1111  // Bump up the block counter to make sure we're not overwriting
1112  // anything here
1113  dof_offset += ndof_in_element;
1114  }
1115 
1116 #ifdef PARANOID
1117  // check that every global equation number has been allocated a dof type
1118  for (unsigned i = 0; i < nrow_local; i++)
1119  {
1120  if (previously_assigned_block_number[i] < 0)
1121  {
1122  std::ostringstream error_message;
1123  error_message << "Not all degrees of freedom have had DOF type "
1124  << "numbers allocated. Dof number " << i
1125  << " is unallocated.";
1126  throw OomphLibError(error_message.str(),
1129  }
1130  }
1131 #endif
1132  }
1133  // else the problem is distributed
1134  else
1135  {
1136 #ifdef OOMPH_HAS_MPI
1137  // Offset for the block type in the overall system.
1138  // Different meshes contain different block-preconditionable
1139  // elements -- their blocks are added one after the other...
1140  unsigned dof_offset = 0;
1141 
1142  // the set of global degrees of freedom and their corresponding dof
1143  // number on this processor
1144  std::map<unsigned long, unsigned> my_dof_map;
1145 
1146  // Loop over all meshes
1147  for (unsigned m = 0; m < nmesh(); m++)
1148  {
1149  // Number of elements in this mesh
1150  unsigned n_element = this->mesh_pt(m)->nelement();
1151 
1152  // Find the number of block types that the elements in this mesh
1153  // are in charge of
1154  unsigned ndof_in_element = ndof_types_in_mesh(m);
1155  Internal_ndof_types += ndof_in_element;
1156 
1157  // Loop over all elements
1158  for (unsigned e = 0; e < n_element; e++)
1159  {
1160  // if the element is not a halo element
1161  if (!this->mesh_pt(m)->element_pt(e)->is_halo())
1162  {
1163  // List containing pairs of global equation number and
1164  // dof number for each global dof in an element
1165  std::list<std::pair<unsigned long, unsigned>> dof_lookup_list;
1166 
1167  // Get list of blocks associated with the element's global
1168  // unknowns
1170  dof_lookup_list);
1171 
1172  // update the block numbers and put it in the map.
1173  typedef std::list<std::pair<unsigned long, unsigned>>::iterator
1174  IT;
1175  for (IT it = dof_lookup_list.begin(); it != dof_lookup_list.end();
1176  it++)
1177  {
1178  it->second = (it->second) + dof_offset;
1179  my_dof_map[it->first] = it->second;
1180  }
1181  }
1182  }
1183 
1184  // About to do the next mesh which contains block preconditionable
1185  // elements of a different type; all the dofs that these elements are
1186  // "in charge of" differ from the ones considered so far.
1187  // Bump up the block counter to make sure we're not overwriting
1188  // anything here
1189  dof_offset += ndof_in_element;
1190  }
1191 
1192  // next copy the map of my dofs to two vectors to send
1193  unsigned my_ndof = my_dof_map.size();
1194  unsigned long* my_global_dofs = new unsigned long[my_ndof];
1195  unsigned* my_dof_numbers = new unsigned[my_ndof];
1196  typedef std::map<unsigned long, unsigned>::iterator IT;
1197  unsigned pt = 0;
1198  for (IT it = my_dof_map.begin(); it != my_dof_map.end(); it++)
1199  {
1200  my_global_dofs[pt] = it->first;
1201  my_dof_numbers[pt] = it->second;
1202  pt++;
1203  }
1204 
1205  // and then clear the map
1206  my_dof_map.clear();
1207 
1208  // count up how many DOFs need to be sent to each processor
1209  int* first_dof_to_send = new int[nproc];
1210  int* ndof_to_send = new int[nproc];
1211  unsigned ptr = 0;
1212  for (unsigned p = 0; p < nproc; p++)
1213  {
1214  first_dof_to_send[p] = 0;
1215  ndof_to_send[p] = 0;
1216  while (ptr < my_ndof &&
1217  my_global_dofs[ptr] < dense_required_rows(p, 0))
1218  {
1219  ptr++;
1220  }
1221  first_dof_to_send[p] = ptr;
1222  while (ptr < my_ndof &&
1223  my_global_dofs[ptr] <= dense_required_rows(p, 1))
1224  {
1225  ndof_to_send[p]++;
1226  ptr++;
1227  }
1228  }
1229 
1230  // next communicate to each processor how many dofs it expects to recv
1231  int* ndof_to_recv = new int[nproc];
1232  MPI_Alltoall(ndof_to_send,
1233  1,
1234  MPI_INT,
1235  ndof_to_recv,
1236  1,
1237  MPI_INT,
1238  comm_pt()->mpi_comm());
1239 
1240  // the base displacements for the sends
1241  MPI_Aint base_displacement;
1242  MPI_Get_address(my_global_dofs, &base_displacement);
1243 
1244 #ifdef PARANOID
1245  // storage for paranoid check to ensure that every row as been
1246  // imported
1247  std::vector<bool> dof_recv(nrow_local, false);
1248 #endif
1249 
1250  // next send and recv
1251  Vector<MPI_Request> send_requests;
1252  Vector<MPI_Request> recv_requests;
1253  Vector<unsigned long*> global_dofs_recv(nproc, 0);
1254  Vector<unsigned*> dof_numbers_recv(nproc, 0);
1255  Vector<unsigned> proc;
1256  for (unsigned p = 0; p < nproc; p++)
1257  {
1258  if (p != my_rank)
1259  {
1260  // send
1261  if (ndof_to_send[p] > 0)
1262  {
1263  // the datatypes, displacements, lengths for the two datatypes
1264  MPI_Datatype types[2];
1265  MPI_Aint displacements[2];
1266  int lengths[2];
1267 
1268  // my global dofs
1269  MPI_Type_contiguous(
1270  ndof_to_send[p], MPI_UNSIGNED_LONG, &types[0]);
1271  MPI_Type_commit(&types[0]);
1272  MPI_Get_address(my_global_dofs + first_dof_to_send[p],
1273  &displacements[0]);
1274  displacements[0] -= base_displacement;
1275  lengths[0] = 1;
1276 
1277  // my dof numbers
1278  MPI_Type_contiguous(ndof_to_send[p], MPI_UNSIGNED, &types[1]);
1279  MPI_Type_commit(&types[1]);
1280  MPI_Get_address(my_dof_numbers + first_dof_to_send[p],
1281  &displacements[1]);
1282  displacements[1] -= base_displacement;
1283  lengths[1] = 1;
1284 
1285  // build the final type
1286  MPI_Datatype send_type;
1287  MPI_Type_create_struct(
1288  2, lengths, displacements, types, &send_type);
1289  MPI_Type_commit(&send_type);
1290  MPI_Type_free(&types[0]);
1291  MPI_Type_free(&types[1]);
1292 
1293  // and send
1294  MPI_Request req;
1295  MPI_Isend(my_global_dofs,
1296  1,
1297  send_type,
1298  p,
1299  2,
1300  comm_pt()->mpi_comm(),
1301  &req);
1302  send_requests.push_back(req);
1303  MPI_Type_free(&send_type);
1304  }
1305 
1306  // and recv
1307  if (ndof_to_recv[p] > 0)
1308  {
1309  // resize the storage
1310  global_dofs_recv[p] = new unsigned long[ndof_to_recv[p]];
1311  dof_numbers_recv[p] = new unsigned[ndof_to_recv[p]];
1312  proc.push_back(p);
1313 
1314  // the datatypes, displacements, lengths for the two datatypes
1315  MPI_Datatype types[2];
1316  MPI_Aint displacements[2];
1317  int lengths[2];
1318 
1319  // my global dofs
1320  MPI_Type_contiguous(
1321  ndof_to_recv[p], MPI_UNSIGNED_LONG, &types[0]);
1322  MPI_Type_commit(&types[0]);
1323  MPI_Get_address(global_dofs_recv[p], &displacements[0]);
1324  displacements[0] -= base_displacement;
1325  lengths[0] = 1;
1326 
1327  // my dof numbers
1328  MPI_Type_contiguous(ndof_to_recv[p], MPI_UNSIGNED, &types[1]);
1329  MPI_Type_commit(&types[1]);
1330  MPI_Get_address(dof_numbers_recv[p], &displacements[1]);
1331  displacements[1] -= base_displacement;
1332  lengths[1] = 1;
1333 
1334  // build the final type
1335  MPI_Datatype recv_type;
1336  MPI_Type_create_struct(
1337  2, lengths, displacements, types, &recv_type);
1338  MPI_Type_commit(&recv_type);
1339  MPI_Type_free(&types[0]);
1340  MPI_Type_free(&types[1]);
1341 
1342  // and recv
1343  MPI_Request req;
1344  MPI_Irecv(my_global_dofs,
1345  1,
1346  recv_type,
1347  p,
1348  2,
1349  comm_pt()->mpi_comm(),
1350  &req);
1351  recv_requests.push_back(req);
1352  MPI_Type_free(&recv_type);
1353  }
1354  }
1355  // send to self
1356  else
1357  {
1358  unsigned u = first_dof_to_send[p] + ndof_to_recv[p];
1359  for (unsigned i = first_dof_to_send[p]; i < u; i++)
1360  {
1361 #ifdef PARANOID
1362  // indicate that this dof has ben recv
1363  dof_recv[my_global_dofs[i] - first_row] = true;
1364 #endif
1365  Dof_number_dense[my_global_dofs[i] - first_row] =
1366  my_dof_numbers[i];
1367  }
1368  }
1369  }
1370 
1371  // recv and store the data
1372  unsigned c_recv = recv_requests.size();
1373  while (c_recv > 0)
1374  {
1375  // wait for any communication to finish
1376  int req_number;
1377  MPI_Waitany(
1378  c_recv, &recv_requests[0], &req_number, MPI_STATUS_IGNORE);
1379  recv_requests.erase(recv_requests.begin() + req_number);
1380  c_recv--;
1381 
1382  // determine the source processor
1383  unsigned p = proc[req_number];
1384  proc.erase(proc.begin() + req_number);
1385 
1386  // import the data
1387  for (int i = 0; i < ndof_to_recv[p]; i++)
1388  {
1389 #ifdef PARANOID
1390  // indicate that this dof has ben recv
1391  dof_recv[global_dofs_recv[p][i] - first_row] = true;
1392 #endif
1393  Dof_number_dense[global_dofs_recv[p][i] - first_row] =
1394  dof_numbers_recv[p][i];
1395  }
1396 
1397  // delete the data
1398  delete[] global_dofs_recv[p];
1399  delete[] dof_numbers_recv[p];
1400  }
1401 
1402  // finally wait for the send requests to complete as we are leaving
1403  // an MPI block of code
1404  unsigned csr = send_requests.size();
1405  if (csr)
1406  {
1407  MPI_Waitall(csr, &send_requests[0], MPI_STATUS_IGNORE);
1408  }
1409 
1410  // clean up
1411  delete[] ndof_to_send;
1412  delete[] first_dof_to_send;
1413  delete[] ndof_to_recv;
1414  delete[] my_global_dofs;
1415  delete[] my_dof_numbers;
1416 #ifdef PARANOID
1417  unsigned all_recv = true;
1418  for (unsigned i = 0; i < nrow_local; i++)
1419  {
1420  if (!dof_recv[i])
1421  {
1422  all_recv = false;
1423  }
1424  }
1425  if (!all_recv)
1426  {
1427  std::ostringstream error_message;
1428  error_message << "Not all the DOF numbers required were received";
1429  throw OomphLibError(error_message.str(),
1432  }
1433 #endif
1434 #else
1435  std::ostringstream error_message;
1436  error_message
1437  << "The problem appears to be distributed, MPI is required";
1438  throw OomphLibError(error_message.str(),
1441 #endif
1442  }
1443 #ifdef OOMPH_HAS_MPI
1444  Vector<unsigned*> sparse_rows_for_proc(nproc, 0);
1445  Vector<MPI_Request> sparse_rows_for_proc_requests;
1446  if (matrix_distributed)
1447  {
1448  // wait for number of sparse rows each processor requires
1449  // post recvs for that data
1450  if (recv_requests_sparse_nreq.size() > 0)
1451  {
1452  MPI_Waitall(recv_requests_sparse_nreq.size(),
1453  &recv_requests_sparse_nreq[0],
1454  MPI_STATUS_IGNORE);
1455  }
1456  for (unsigned p = 0; p < nproc; ++p)
1457  {
1458  if (nreq_sparse_for_proc[p] > 0)
1459  {
1460  MPI_Request req;
1461  sparse_rows_for_proc[p] = new unsigned[nreq_sparse_for_proc[p]];
1462  MPI_Irecv(sparse_rows_for_proc[p],
1463  nreq_sparse_for_proc[p],
1464  MPI_UNSIGNED,
1465  p,
1466  32,
1467  comm_pt()->mpi_comm(),
1468  &req);
1469  sparse_rows_for_proc_requests.push_back(req);
1470  }
1471  }
1472  }
1473 #endif
1474 
1475 
1476  // for every global degree of freedom required by this processor we now
1477  // have the corresponding dof number
1478 
1479  // clear the Ndof_in_dof_block storage
1481 
1482  // first consider a non distributed matrix
1483  if (!matrix_distributed)
1484  {
1485  // set the Index_in_dof_block
1486  unsigned nrow = this->distribution_pt()->nrow();
1487  Index_in_dof_block_dense.resize(nrow);
1489  for (unsigned i = 0; i < nrow; i++)
1490  {
1493  }
1494  }
1495 
1496  // next a distributed matrix
1497  else
1498  {
1499 #ifdef OOMPH_HAS_MPI
1500 
1501 
1502  // first compute how many instances of each dof are on this
1503  // processor
1504  unsigned* my_nrows_in_dof_block = new unsigned[Internal_ndof_types];
1505  for (unsigned i = 0; i < Internal_ndof_types; i++)
1506  {
1507  my_nrows_in_dof_block[i] = 0;
1508  }
1509  for (unsigned i = 0; i < nrow_local; i++)
1510  {
1511  my_nrows_in_dof_block[Dof_number_dense[i]]++;
1512  }
1513 
1514  // next share the data
1515  unsigned* nrow_in_dof_block_recv =
1516  new unsigned[Internal_ndof_types * nproc];
1517  MPI_Allgather(my_nrows_in_dof_block,
1519  MPI_UNSIGNED,
1520  nrow_in_dof_block_recv,
1522  MPI_UNSIGNED,
1523  comm_pt()->mpi_comm());
1524  delete[] my_nrows_in_dof_block;
1525 
1526  // compute my first dof index and Nrows_in_dof_block
1527  Vector<unsigned> my_first_dof_index(Internal_ndof_types, 0);
1528  for (unsigned i = 0; i < Internal_ndof_types; i++)
1529  {
1530  for (unsigned p = 0; p < my_rank; p++)
1531  {
1532  my_first_dof_index[i] +=
1533  nrow_in_dof_block_recv[p * Internal_ndof_types + i];
1534  }
1535  Dof_dimension[i] = my_first_dof_index[i];
1536  for (unsigned p = my_rank; p < nproc; p++)
1537  {
1538  Dof_dimension[i] +=
1539  nrow_in_dof_block_recv[p * Internal_ndof_types + i];
1540  }
1541  }
1542  delete[] nrow_in_dof_block_recv;
1543 
1544  // next compute Index in dof block
1547  Vector<unsigned> dof_counter(Internal_ndof_types, 0);
1548  for (unsigned i = 0; i < nrow_local; i++)
1549  {
1551  my_first_dof_index[Dof_number_dense[i]] +
1552  dof_counter[Dof_number_dense[i]];
1553  dof_counter[Dof_number_dense[i]]++;
1554  }
1555 
1556  // the base displacements for the sends
1557  if (sparse_rows_for_proc_requests.size() > 0)
1558  {
1559  MPI_Waitall(sparse_rows_for_proc_requests.size(),
1560  &sparse_rows_for_proc_requests[0],
1561  MPI_STATUS_IGNORE);
1562  }
1563  MPI_Aint base_displacement;
1564  MPI_Get_address(dof_number_sparse_send, &base_displacement);
1565  unsigned first_row = this->distribution_pt()->first_row();
1566  for (unsigned p = 0; p < nproc; ++p)
1567  {
1568  if (nreq_sparse_for_proc[p] > 0)
1569  {
1570  // construct the data
1571  index_in_dof_block_sparse_send[p] =
1572  new unsigned[nreq_sparse_for_proc[p]];
1573  dof_number_sparse_send[p] = new unsigned[nreq_sparse_for_proc[p]];
1574  for (unsigned i = 0; i < nreq_sparse_for_proc[p]; ++i)
1575  {
1576  unsigned r = sparse_rows_for_proc[p][i];
1577  r -= first_row;
1578  index_in_dof_block_sparse_send[p][i] =
1580  dof_number_sparse_send[p][i] = Dof_number_dense[r];
1581  }
1582  delete[] sparse_rows_for_proc[p];
1583 
1584  // send the data
1585  // the datatypes, displacements, lengths for the two datatypes
1586  MPI_Datatype types[2];
1587  MPI_Aint displacements[2];
1588  int lengths[2];
1589 
1590  // index in dof block
1591  MPI_Type_contiguous(
1592  nreq_sparse_for_proc[p], MPI_UNSIGNED, &types[0]);
1593  MPI_Type_commit(&types[0]);
1594  MPI_Get_address(index_in_dof_block_sparse_send[p],
1595  &displacements[0]);
1596  displacements[0] -= base_displacement;
1597  lengths[0] = 1;
1598 
1599  // dof number
1600  MPI_Type_contiguous(
1601  nreq_sparse_for_proc[p], MPI_UNSIGNED, &types[1]);
1602  MPI_Type_commit(&types[1]);
1603  MPI_Get_address(dof_number_sparse_send[p], &displacements[1]);
1604  displacements[1] -= base_displacement;
1605  lengths[1] = 1;
1606 
1607  // build the final type
1608  MPI_Datatype send_type;
1609  MPI_Type_create_struct(
1610  2, lengths, displacements, types, &send_type);
1611  MPI_Type_commit(&send_type);
1612  MPI_Type_free(&types[0]);
1613  MPI_Type_free(&types[1]);
1614 
1615  // and recv
1616  MPI_Request req;
1617  MPI_Isend(dof_number_sparse_send,
1618  1,
1619  send_type,
1620  p,
1621  33,
1622  comm_pt()->mpi_comm(),
1623  &req);
1624  send_requests_sparse.push_back(req);
1625  MPI_Type_free(&send_type);
1626  }
1627  else
1628  {
1629  index_in_dof_block_sparse_send[p] = 0;
1630  dof_number_sparse_send[p] = 0;
1631  }
1632  }
1633 #endif
1634  }
1635  }
1636 
1638  // end of master block preconditioner only operations
1640 
1641  // compute the number of rows in each block
1642 
1643 #ifdef PARANOID
1644  // check the vector is the correct length
1645  if (dof_to_block_map.size() != Internal_ndof_types)
1646  {
1647  std::ostringstream error_message;
1648  error_message << "The dof_to_block_map vector (size="
1649  << dof_to_block_map.size()
1650  << ") must be of size Internal_ndof_types="
1652  throw OomphLibError(
1653  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
1654  }
1655 #endif
1656 
1657  // find the maximum block number RAYAY use std::max_element
1658  unsigned max_block_number = 0;
1659  for (unsigned i = 0; i < Internal_ndof_types; i++)
1660  {
1661  if (dof_to_block_map[i] > max_block_number)
1662  {
1663  max_block_number = dof_to_block_map[i];
1664  }
1665  }
1666 
1667  // resize the storage the the block to dof map
1669  Block_number_to_dof_number_lookup.resize(max_block_number + 1);
1670  Ndof_in_block.clear();
1671  Ndof_in_block.resize(max_block_number + 1);
1672 
1673  // resize storage
1675 
1676  // build the storage for the two maps (block to dof) and (dof to block)
1677  for (unsigned i = 0; i < Internal_ndof_types; i++)
1678  {
1679  Dof_number_to_block_number_lookup[i] = dof_to_block_map[i];
1680  Block_number_to_dof_number_lookup[dof_to_block_map[i]].push_back(i);
1681  Ndof_in_block[dof_to_block_map[i]]++;
1682  }
1683 
1684 #ifdef PARANOID
1685  // paranoid check that every block number has at least one DOF associated
1686  // with it
1687  for (unsigned i = 0; i < max_block_number + 1; i++)
1688  {
1690  {
1691  std::ostringstream error_message;
1692  error_message << "block number " << i
1693  << " does not have any DOFs associated with it";
1694  throw OomphLibWarning(error_message.str(),
1697  }
1698  }
1699 #endif
1700 
1701  // Update the number of blocks types.
1702  Internal_nblock_types = max_block_number + 1;
1703 
1704  // Distributed or not, depends on if we have more than one processor.
1706 
1707  // Create the new block distributions.
1709  for (unsigned i = 0; i < Internal_nblock_types; i++)
1710  {
1711  unsigned block_dim = 0;
1712  for (unsigned j = 0; j < Ndof_in_block[i]; j++)
1713  {
1714  block_dim +=
1716  }
1718  new LinearAlgebraDistribution(comm_pt(), block_dim, distributed);
1719  }
1720 
1721  // Work out the distribution of the dof-level blocks.
1722  // Since several dof types may be coarsened into a single dof type.
1723  // We get the dof-level block distributions from the parent preconditioner.
1724 
1725  // How many dof types are there?
1727  {
1728  // Delete any pre-existing distributions.
1729  const unsigned dof_block_distribution_size =
1731  for (unsigned dof_i = 0; dof_i < dof_block_distribution_size; dof_i++)
1732  {
1733  delete Dof_block_distribution_pt[dof_i];
1734  }
1735  const unsigned ndofs = this->ndof_types();
1736  Dof_block_distribution_pt.resize(ndofs, 0);
1737 
1738  // For each dof type, work out how many parent preconditioner dof types
1739  // are in it.
1740  for (unsigned dof_i = 0; dof_i < ndofs; dof_i++)
1741  {
1742  // For each external dof, we get the dofs coarsened into it (from the
1743  // parent preconditioner level, not the most fine grain level).
1744  const unsigned ncoarsened_dofs_in_dof_i =
1745  Doftype_coarsen_map_coarse[dof_i].size();
1746  Vector<LinearAlgebraDistribution*> tmp_dist_pt(ncoarsened_dofs_in_dof_i,
1747  0);
1748  for (unsigned parent_dof_i = 0; parent_dof_i < ncoarsened_dofs_in_dof_i;
1749  parent_dof_i++)
1750  {
1751  tmp_dist_pt[parent_dof_i] =
1752  master_block_preconditioner_pt()->dof_block_distribution_pt(
1754  [Doftype_coarsen_map_coarse[dof_i][parent_dof_i]]);
1755  }
1756 
1757  Dof_block_distribution_pt[dof_i] = new LinearAlgebraDistribution;
1758 
1759 
1761  tmp_dist_pt, *Dof_block_distribution_pt[dof_i]);
1762  }
1763  }
1764 
1765  // Create Block_distribution_pt
1766  {
1767  // Delete any existing distributions in Block_distribution_pt.
1768  // (This should already be deleted in clear_block_preconditioner_base(...)
1769  // but we are just being extra safe!).
1770  unsigned n_existing_block_dist = Block_distribution_pt.size();
1771  for (unsigned dist_i = 0; dist_i < n_existing_block_dist; dist_i++)
1772  {
1773  delete Block_distribution_pt[dist_i];
1774  }
1775 
1776  Block_distribution_pt.clear();
1777 
1778  // Work out the distributions of the concatenated blocks.
1779  unsigned super_block_size = Block_to_dof_map_coarse.size();
1780  Block_distribution_pt.resize(super_block_size, 0);
1781  for (unsigned super_block_i = 0; super_block_i < super_block_size;
1782  super_block_i++)
1783  {
1784  unsigned sub_block_size = Block_to_dof_map_coarse[super_block_i].size();
1785  Vector<LinearAlgebraDistribution*> tmp_dist_pt(sub_block_size, 0);
1786 
1787  for (unsigned sub_block_i = 0; sub_block_i < sub_block_size;
1788  sub_block_i++)
1789  {
1790  tmp_dist_pt[sub_block_i] = dof_block_distribution_pt(
1791  Block_to_dof_map_coarse[super_block_i][sub_block_i]);
1792  }
1793 
1794  Block_distribution_pt[super_block_i] = new LinearAlgebraDistribution;
1795 
1797  tmp_dist_pt, *Block_distribution_pt[super_block_i]);
1798  }
1799 
1800  } // Creating Block_distribution_pt.
1801 
1802 
1803  // Create the distribution of the preconditioner matrix,
1804  // if this preconditioner is a subsidiary preconditioner then it stored
1805  // at Distribution_pt;
1806  // if this preconditioner is a master preconditioner then it is stored
1807  // at Internal_preconditioner_matrix_distribution_pt.
1808  LinearAlgebraDistribution dist;
1811 
1812  // Build the distribution.
1814  {
1815  this->build_distribution(dist);
1816  }
1817  else
1818  {
1820  new LinearAlgebraDistribution(dist);
1821  }
1822 
1823  Preconditioner_matrix_distribution_pt = new LinearAlgebraDistribution;
1826 
1827  // Clear all distributions in Auxiliary_block_distribution_pt, except for
1828  // the one which corresponds to the preconditioner matrix distribution. This
1829  // is already deleted by clear_block_preconditioner_base(...)
1830 
1831  // Create the key which corresponds to
1832  // preconditioner_matrix_distribution_pt.
1833  {
1834  const unsigned nblocks = Block_distribution_pt.size();
1835  Vector<unsigned> preconditioner_matrix_key(nblocks, 0);
1836  for (unsigned i = 0; i < nblocks; i++)
1837  {
1838  preconditioner_matrix_key[i] = i;
1839  }
1840 
1841  // Now iterate through Auxiliary_block_distribution_pt and delete
1842  // everything except for the value which corresponds to
1843  // preconditioner_matrix_key.
1844  std::map<Vector<unsigned>, LinearAlgebraDistribution*>::iterator iter =
1846  while (iter != Auxiliary_block_distribution_pt.end())
1847  {
1848  if (iter->first != preconditioner_matrix_key)
1849  {
1850  delete iter->second;
1851  iter++;
1852  }
1853  else
1854  {
1855  ++iter;
1856  }
1857  }
1858 
1859  // Clear it just to be safe!
1861 
1862  // Insert the preconditioner matrix distribution.
1864  preconditioner_matrix_key, Preconditioner_matrix_distribution_pt);
1865  } // End of Auxiliary_block_distribution_pt encapsulation.
1866 
1867  // Clearing up after comm to assemble sparse lookup schemes.
1868 #ifdef OOMPH_HAS_MPI
1869  if (send_requests_sparse.size() > 0)
1870  {
1871  MPI_Waitall(send_requests_sparse.size(),
1872  &send_requests_sparse[0],
1873  MPI_STATUS_IGNORE);
1874  }
1875  if (recv_requests_sparse.size() > 0)
1876  {
1877  MPI_Waitall(recv_requests_sparse.size(),
1878  &recv_requests_sparse[0],
1879  MPI_STATUS_IGNORE);
1880  }
1881  for (unsigned p = 0; p < nproc; p++)
1882  {
1883  delete[] index_in_dof_block_sparse_send[p];
1884  delete[] dof_number_sparse_send[p];
1885  }
1886  delete[] index_in_dof_block_sparse_send;
1887  delete[] dof_number_sparse_send;
1888  delete[] nreq_sparse;
1889  delete[] nreq_sparse_for_proc;
1890 #endif
1891 
1892  // Next we assemble the lookup schemes for the rows
1893  // if the matrix is not distributed then we assemble Global_index
1894  // if the matrix is distributed then Rows_to_send_..., Rows_to_recv_... etc.
1895  if (!distributed)
1896  {
1897  // Resize the storage.
1899  for (unsigned b = 0; b < Internal_nblock_types; b++)
1900  {
1902  }
1903 
1904  // Compute:
1905  unsigned nrow = this->master_nrow();
1906  for (unsigned i = 0; i < nrow; i++)
1907  {
1908  // the dof type number;
1909  int dof_number = this->internal_dof_number(i);
1910  if (dof_number >= 0)
1911  {
1912  // the block number;
1913  unsigned block_number = Dof_number_to_block_number_lookup[dof_number];
1914 
1915  // the index in the block.
1916  unsigned index_in_block = 0;
1917  unsigned ptr = 0;
1918  while (int(Block_number_to_dof_number_lookup[block_number][ptr]) !=
1919  dof_number)
1920  {
1923  ptr++;
1924  }
1927  }
1928  }
1929  }
1930  // otherwise the matrix is distributed
1931  else
1932  {
1933 #ifdef OOMPH_HAS_MPI
1934 
1935  // the pointer to the master distribution
1936  const LinearAlgebraDistribution* master_distribution_pt =
1937  this->master_distribution_pt();
1938 
1939  // resize the nrows... storage
1940  Nrows_to_send_for_get_block.resize(Internal_nblock_types, nproc);
1941  Nrows_to_send_for_get_block.initialise(0);
1942  Nrows_to_send_for_get_ordered.resize(nproc);
1943  Nrows_to_send_for_get_ordered.initialise(0);
1944 
1945  // loop over my rows
1948  for (unsigned i = 0; i < nrow_local; i++)
1949  {
1950  // the block number
1951  int b = this->internal_block_number(first_row + i);
1952 
1953  // check that the DOF i is associated with this preconditioner
1954  if (b >= 0)
1955  {
1956  // the block index
1957  unsigned j = this->internal_index_in_block(first_row + i);
1958 
1959  // the processor this row will be sent to
1960  unsigned block_p = 0;
1961  while (!(Internal_block_distribution_pt[b]->first_row(block_p) <= j &&
1964  j)))
1965  {
1966  block_p++;
1967  }
1968 
1969  // and increment the counter
1970  Nrows_to_send_for_get_block(b, block_p)++;
1971  Nrows_to_send_for_get_ordered[block_p]++;
1972  }
1973  }
1974 
1975  // resize the storage for Nrows_to_recv
1976  Nrows_to_recv_for_get_block.resize(Internal_nblock_types, nproc);
1977  Nrows_to_recv_for_get_block.initialise(0);
1978  Nrows_to_recv_for_get_ordered.resize(nproc);
1979  Nrows_to_recv_for_get_ordered.initialise(0);
1980 
1981  // next we send the number of rows that will be sent by this processor
1982  Vector<unsigned*> nrows_to_send(nproc, 0);
1983  Vector<unsigned*> nrows_to_recv(nproc, 0);
1984  Vector<MPI_Request> send_requests_nrow;
1985  Vector<MPI_Request> recv_requests_nrow;
1986  Vector<unsigned> proc;
1987  for (unsigned p = 0; p < nproc; p++)
1988  {
1989  if (p != my_rank)
1990  {
1991  // send
1992  proc.push_back(p);
1993  nrows_to_send[p] = new unsigned[Internal_nblock_types];
1994  for (unsigned b = 0; b < Internal_nblock_types; b++)
1995  {
1996  nrows_to_send[p][b] = Nrows_to_send_for_get_block(b, p);
1997  }
1998  MPI_Request s_req;
1999  MPI_Isend(nrows_to_send[p],
2001  MPI_UNSIGNED,
2002  p,
2003  3,
2004  comm_pt()->mpi_comm(),
2005  &s_req);
2006  send_requests_nrow.push_back(s_req);
2007 
2008  // recv
2009  nrows_to_recv[p] = new unsigned[Internal_nblock_types];
2010  MPI_Request r_req;
2011  MPI_Irecv(nrows_to_recv[p],
2013  MPI_UNSIGNED,
2014  p,
2015  3,
2016  comm_pt()->mpi_comm(),
2017  &r_req);
2018  recv_requests_nrow.push_back(r_req);
2019  }
2020  // send to self
2021  else
2022  {
2023  for (unsigned b = 0; b < Internal_nblock_types; b++)
2024  {
2025  Nrows_to_recv_for_get_block(b, p) =
2026  Nrows_to_send_for_get_block(b, p);
2027  }
2028  Nrows_to_recv_for_get_ordered[p] = Nrows_to_send_for_get_ordered[p];
2029  }
2030  }
2031 
2032  // create some temporary storage for the global row indices that will
2033  // be received from another processor.
2034  DenseMatrix<int*> block_rows_to_send(Internal_nblock_types, nproc, 0);
2035  Vector<int*> ordered_rows_to_send(nproc, 0);
2036 
2037  // resize the rows... storage
2038  Rows_to_send_for_get_block.resize(Internal_nblock_types, nproc);
2039  Rows_to_send_for_get_block.initialise(0);
2040  Rows_to_send_for_get_ordered.resize(nproc);
2041  Rows_to_send_for_get_ordered.initialise(0);
2042  Rows_to_recv_for_get_block.resize(Internal_nblock_types, nproc);
2043  Rows_to_recv_for_get_block.initialise(0);
2044 
2045  // resize the storage
2046  for (unsigned p = 0; p < nproc; p++)
2047  {
2048  for (unsigned b = 0; b < Internal_nblock_types; b++)
2049  {
2050  Rows_to_send_for_get_block(b, p) =
2051  new int[Nrows_to_send_for_get_block(b, p)];
2052  if (p != my_rank)
2053  {
2054  block_rows_to_send(b, p) =
2055  new int[Nrows_to_send_for_get_block(b, p)];
2056  }
2057  else
2058  {
2059  Rows_to_recv_for_get_block(b, p) =
2060  new int[Nrows_to_send_for_get_block(b, p)];
2061  }
2062  }
2063  Rows_to_send_for_get_ordered[p] =
2064  new int[Nrows_to_send_for_get_ordered[p]];
2065  }
2066 
2067 
2068  // loop over my rows to allocate the nrows
2069  DenseMatrix<unsigned> ptr_block(Internal_nblock_types, nproc, 0);
2070  for (unsigned i = 0; i < nrow_local; i++)
2071  {
2072  // the block number
2073  int b = this->internal_block_number(first_row + i);
2074 
2075  // check that the DOF i is associated with this preconditioner
2076  if (b >= 0)
2077  {
2078  // the block index
2079  unsigned j = this->internal_index_in_block(first_row + i);
2080 
2081  // the processor this row will be sent to
2082  unsigned block_p = 0;
2083  while (!(Internal_block_distribution_pt[b]->first_row(block_p) <= j &&
2086  j)))
2087  {
2088  block_p++;
2089  }
2090 
2091  // and store the row
2092  Rows_to_send_for_get_block(b, block_p)[ptr_block(b, block_p)] = i;
2093  if (block_p != my_rank)
2094  {
2095  block_rows_to_send(b, block_p)[ptr_block(b, block_p)] =
2096  j - Internal_block_distribution_pt[b]->first_row(block_p);
2097  }
2098  else
2099  {
2100  Rows_to_recv_for_get_block(b, block_p)[ptr_block(b, block_p)] =
2101  j - Internal_block_distribution_pt[b]->first_row(block_p);
2102  }
2103  ptr_block(b, block_p)++;
2104  }
2105  }
2106 
2107  // next block ordered
2108  for (unsigned p = 0; p < nproc; ++p)
2109  {
2110  int pt = 0;
2111  for (unsigned b = 0; b < Internal_nblock_types; ++b)
2112  {
2113  for (unsigned i = 0; i < Nrows_to_send_for_get_block(b, p); ++i)
2114  {
2115  Rows_to_send_for_get_ordered[p][pt] =
2116  Rows_to_send_for_get_block(b, p)[i];
2117  pt++;
2118  }
2119  }
2120  }
2121 
2122  // next process the nrow recvs as they complete
2123 
2124  // recv and store the data
2125  unsigned c = recv_requests_nrow.size();
2126  while (c > 0)
2127  {
2128  // wait for any communication to finish
2129  int req_number;
2130  MPI_Waitany(c, &recv_requests_nrow[0], &req_number, MPI_STATUS_IGNORE);
2131  recv_requests_nrow.erase(recv_requests_nrow.begin() + req_number);
2132  c--;
2133 
2134  // determine the source processor
2135  unsigned p = proc[req_number];
2136  proc.erase(proc.begin() + req_number);
2137 
2138  // copy the data to its final storage
2139  Nrows_to_recv_for_get_ordered[p] = 0;
2140  for (unsigned b = 0; b < Internal_nblock_types; b++)
2141  {
2142  Nrows_to_recv_for_get_block(b, p) = nrows_to_recv[p][b];
2143  Nrows_to_recv_for_get_ordered[p] += nrows_to_recv[p][b];
2144  }
2145 
2146  // and clear
2147  delete[] nrows_to_recv[p];
2148  }
2149 
2150  // resize the storage for the incoming rows data
2151  Rows_to_recv_for_get_ordered.resize(nproc, 0);
2152  for (unsigned p = 0; p < nproc; p++)
2153  {
2154  if (p != my_rank)
2155  {
2156  for (unsigned b = 0; b < Internal_nblock_types; b++)
2157  {
2158  Rows_to_recv_for_get_block(b, p) =
2159  new int[Nrows_to_recv_for_get_block(b, p)];
2160  }
2161  }
2162  }
2163 
2164  // compute the number of sends and recv from this processor
2165  // to each other processor
2166  Vector<unsigned> nsend_for_rows(nproc, 0);
2167  Vector<unsigned> nrecv_for_rows(nproc, 0);
2168  for (unsigned p = 0; p < nproc; p++)
2169  {
2170  if (p != my_rank)
2171  {
2172  for (unsigned b = 0; b < Internal_nblock_types; b++)
2173  {
2174  if (Nrows_to_send_for_get_block(b, p) > 0)
2175  {
2176  nsend_for_rows[p]++;
2177  }
2178  if (Nrows_to_recv_for_get_block(b, p) > 0)
2179  {
2180  nrecv_for_rows[p]++;
2181  }
2182  }
2183  }
2184  }
2185 
2186  // finally post the sends and recvs
2187  MPI_Aint base_displacement;
2188  MPI_Get_address(matrix_pt(), &base_displacement);
2189  Vector<MPI_Request> req_rows;
2190  for (unsigned p = 0; p < nproc; p++)
2191  {
2192  if (p != my_rank)
2193  {
2194  // send
2195  if (nsend_for_rows[p] > 0)
2196  {
2197  MPI_Datatype send_types[nsend_for_rows[p]];
2198  MPI_Aint send_displacements[nsend_for_rows[p]];
2199  int send_sz[nsend_for_rows[p]];
2200  unsigned send_ptr = 0;
2201  for (unsigned b = 0; b < Internal_nblock_types; b++)
2202  {
2203  if (Nrows_to_send_for_get_block(b, p) > 0)
2204  {
2205  MPI_Type_contiguous(Nrows_to_send_for_get_block(b, p),
2206  MPI_INT,
2207  &send_types[send_ptr]);
2208  MPI_Type_commit(&send_types[send_ptr]);
2209  MPI_Get_address(block_rows_to_send(b, p),
2210  &send_displacements[send_ptr]);
2211  send_displacements[send_ptr] -= base_displacement;
2212  send_sz[send_ptr] = 1;
2213  send_ptr++;
2214  }
2215  }
2216  MPI_Datatype final_send_type;
2217  MPI_Type_create_struct(nsend_for_rows[p],
2218  send_sz,
2219  send_displacements,
2220  send_types,
2221  &final_send_type);
2222  MPI_Type_commit(&final_send_type);
2223  for (unsigned i = 0; i < nsend_for_rows[p]; i++)
2224  {
2225  MPI_Type_free(&send_types[i]);
2226  }
2227  MPI_Request send_req;
2228  MPI_Isend(matrix_pt(),
2229  1,
2230  final_send_type,
2231  p,
2232  4,
2233  comm_pt()->mpi_comm(),
2234  &send_req);
2235  req_rows.push_back(send_req);
2236  MPI_Type_free(&final_send_type);
2237  }
2238 
2239  // recv
2240  if (nrecv_for_rows[p] > 0)
2241  {
2242  MPI_Datatype recv_types[nrecv_for_rows[p]];
2243  MPI_Aint recv_displacements[nrecv_for_rows[p]];
2244  int recv_sz[nrecv_for_rows[p]];
2245  unsigned recv_ptr = 0;
2246  for (unsigned b = 0; b < Internal_nblock_types; b++)
2247  {
2248  if (Nrows_to_recv_for_get_block(b, p) > 0)
2249  {
2250  MPI_Type_contiguous(Nrows_to_recv_for_get_block(b, p),
2251  MPI_INT,
2252  &recv_types[recv_ptr]);
2253  MPI_Type_commit(&recv_types[recv_ptr]);
2254  MPI_Get_address(Rows_to_recv_for_get_block(b, p),
2255  &recv_displacements[recv_ptr]);
2256  recv_displacements[recv_ptr] -= base_displacement;
2257  recv_sz[recv_ptr] = 1;
2258  recv_ptr++;
2259  }
2260  }
2261  MPI_Datatype final_recv_type;
2262  MPI_Type_create_struct(nrecv_for_rows[p],
2263  recv_sz,
2264  recv_displacements,
2265  recv_types,
2266  &final_recv_type);
2267  MPI_Type_commit(&final_recv_type);
2268  for (unsigned i = 0; i < nrecv_for_rows[p]; i++)
2269  {
2270  MPI_Type_free(&recv_types[i]);
2271  }
2272  MPI_Request recv_req;
2273  MPI_Irecv(matrix_pt(),
2274  1,
2275  final_recv_type,
2276  p,
2277  4,
2278  comm_pt()->mpi_comm(),
2279  &recv_req);
2280  req_rows.push_back(recv_req);
2281  MPI_Type_free(&final_recv_type);
2282  }
2283  }
2284  }
2285 
2286  // cleaning up Waitalls
2287 
2288 
2289  // wait for the recv requests so we can compute
2290  // Nrows_to_recv_for_get_ordered
2291  unsigned n_req_rows = req_rows.size();
2292  if (n_req_rows)
2293  {
2294  MPI_Waitall(n_req_rows, &req_rows[0], MPI_STATUS_IGNORE);
2295  }
2296 
2297  // resize the storage
2298  Rows_to_recv_for_get_ordered.resize(nproc);
2299  Rows_to_recv_for_get_ordered.initialise(0);
2300 
2301  // construct block offset
2302  Vector<int> vec_offset(Internal_nblock_types, 0);
2303  for (unsigned b = 1; b < Internal_nblock_types; ++b)
2304  {
2305  vec_offset[b] = vec_offset[b - 1] +
2306  Internal_block_distribution_pt[b - 1]->nrow_local();
2307  }
2308 
2309  //
2310  for (unsigned p = 0; p < nproc; p++)
2311  {
2312  int pt = 0;
2313  Rows_to_recv_for_get_ordered[p] =
2314  new int[Nrows_to_recv_for_get_ordered[p]];
2315  for (unsigned b = 0; b < Internal_nblock_types; b++)
2316  {
2317  for (unsigned i = 0; i < Nrows_to_recv_for_get_block(b, p); i++)
2318  {
2319  Rows_to_recv_for_get_ordered[p][pt] =
2320  Rows_to_recv_for_get_block(b, p)[i] + vec_offset[b];
2321  pt++;
2322  }
2323  }
2324  }
2325 
2326  // clean up
2327  for (unsigned p = 0; p < nproc; p++)
2328  {
2329  if (p != my_rank)
2330  {
2331  for (unsigned b = 0; b < Internal_nblock_types; b++)
2332  {
2333  delete[] block_rows_to_send(b, p);
2334  }
2335  if (Nrows_to_send_for_get_ordered[p] > 0)
2336  {
2337  delete[] ordered_rows_to_send[p];
2338  }
2339  }
2340  }
2341 
2342  // and the send reqs
2343  unsigned n_req_send_nrow = send_requests_nrow.size();
2344  if (n_req_send_nrow)
2345  {
2346  MPI_Waitall(n_req_send_nrow, &send_requests_nrow[0], MPI_STATUS_IGNORE);
2347  }
2348  for (unsigned p = 0; p < nproc; p++)
2349  {
2350  delete[] nrows_to_send[p];
2351  }
2352 #endif
2353  }
2354 
2355  // If we asked for output of blocks to a file then do it.
2357  }
Array< double, 1, 3 > e(1./3., 0.5, 2.)
float * p
Definition: Tutorial_Map_using.cpp:9
Scalar Scalar int size
Definition: benchVecAdd.cpp:17
const LinearAlgebraDistribution * master_distribution_pt() const
Definition: block_preconditioner.h:2017
Vector< Vector< unsigned > > Doftype_coarsen_map_coarse
Definition: block_preconditioner.h:3326
Vector< unsigned > Index_in_dof_block_dense
Definition: block_preconditioner.h:3415
bool block_output_on() const
Test if output of blocks is on or not.
Definition: block_preconditioner.h:2088
unsigned internal_index_in_dof(const unsigned &i_dof) const
Definition: block_preconditioner.h:3113
LinearAlgebraDistribution * dof_block_distribution_pt(const unsigned &b)
Access function to the dof-level block distributions.
Definition: block_preconditioner.h:1962
Vector< unsigned > Doftype_in_master_preconditioner_coarse
Definition: block_preconditioner.h:3411
Vector< unsigned > Allow_multiple_element_type_in_mesh
Definition: block_preconditioner.h:3345
Vector< unsigned > Dof_number_dense
Definition: block_preconditioner.h:3420
Vector< Vector< unsigned > > doftype_coarsen_map_fine() const
Definition: block_preconditioner.h:2334
unsigned master_nrow() const
Definition: block_preconditioner.h:3222
unsigned ndof_types_in_mesh(const unsigned &i) const
Definition: block_preconditioner.h:2037
unsigned internal_ndof_types() const
Definition: block_preconditioner.h:2564
Vector< unsigned > Dof_dimension
Definition: block_preconditioner.h:3457
Vector< Vector< unsigned > > Block_number_to_dof_number_lookup
Definition: block_preconditioner.h:3468
bool any_mesh_distributed() const
Definition: block_preconditioner.h:3025
int index_in_block(const unsigned &i_dof) const
Definition: block_preconditioner.h:1850
Vector< Vector< unsigned > > Global_index
Definition: block_preconditioner.h:3463
Vector< unsigned > Ndof_in_block
Number of types of degree of freedom associated with each block.
Definition: block_preconditioner.h:3474
int block_number(const unsigned &i_dof) const
Return the block number corresponding to a global index i_dof.
Definition: block_preconditioner.h:1822
bool is_subsidiary_block_preconditioner() const
Definition: block_preconditioner.h:2061
void insert_auxiliary_block_distribution(const Vector< unsigned > &block_vec_number, LinearAlgebraDistribution *dist_pt)
Definition: block_preconditioner.h:2769
Vector< Vector< unsigned > > Doftype_coarsen_map_fine
Definition: block_preconditioner.h:3334
std::map< Vector< unsigned >, LinearAlgebraDistribution * > Auxiliary_block_distribution_pt
Definition: block_preconditioner.h:3387
BlockPreconditioner< MATRIX > * master_block_preconditioner_pt() const
Access function to the master block preconditioner pt.
Definition: block_preconditioner.h:2141
int internal_dof_number(const unsigned &i_dof) const
Definition: block_preconditioner.h:3045
unsigned internal_dof_block_dimension(const unsigned &i) const
Definition: block_preconditioner.h:3187
void output_blocks_to_files(const std::string &basefilename, const unsigned &precision=8) const
Definition: block_preconditioner.h:2095
Vector< unsigned > Dof_number_to_block_number_lookup
Vector to the mapping from DOF number to block number.
Definition: block_preconditioner.h:3471
Vector< unsigned > Doftype_in_master_preconditioner_fine
Definition: block_preconditioner.h:3404
static long Is_unclassified
Definition: nodes.h:192
bool distributed() const
distribution is serial or distributed
Definition: linear_algebra_distribution.h:493
LinearAlgebraDistribution * distribution_pt() const
access to the LinearAlgebraDistribution
Definition: linear_algebra_distribution.h:457
unsigned nrow() const
access function to the number of global rows.
Definition: linear_algebra_distribution.h:463
unsigned nrow_local() const
access function for the num of local rows on this processor.
Definition: linear_algebra_distribution.h:469
unsigned first_row() const
access function for the first row on this processor
Definition: linear_algebra_distribution.h:481
void build_distribution(const LinearAlgebraDistribution *const dist_pt)
Definition: linear_algebra_distribution.h:507
DistributableLinearAlgebraObject()
Default constructor - create a distribution.
Definition: linear_algebra_distribution.h:438
virtual unsigned ndof_types() const
Definition: elements.h:1202
virtual void get_dof_numbers_for_unknowns(std::list< std::pair< unsigned long, unsigned >> &dof_lookup_list) const
Definition: elements.h:1221
bool distributed() const
Definition: linear_algebra_distribution.h:329
unsigned first_row() const
Definition: linear_algebra_distribution.h:261
OomphCommunicator * communicator_pt() const
const access to the communicator pointer
Definition: linear_algebra_distribution.h:335
unsigned nrow() const
access function to the number of global rows.
Definition: linear_algebra_distribution.h:186
unsigned nrow_local() const
Definition: linear_algebra_distribution.h:193
unsigned ndof_types() const
Return number of dof types in mesh.
Definition: mesh.cc:8764
GeneralisedElement *& element_pt(const unsigned long &e)
Return pointer to element e.
Definition: mesh.h:448
unsigned long nelement() const
Return number of elements in the mesh.
Definition: mesh.h:590
int my_rank() const
my rank
Definition: communicator.h:176
int nproc() const
number of processors
Definition: communicator.h:157
virtual const OomphCommunicator * comm_pt() const
Get function for comm pointer.
Definition: preconditioner.h:171
void initialise(const _Tp &__value)
Iterate over all values and set to the desired value.
Definition: oomph-lib/src/generic/Vector.h:167
EIGEN_BLAS_FUNC() copy(int *n, RealScalar *px, int *incx, RealScalar *py, int *incy)
Definition: level1_impl.h:32
r
Definition: UniformPSDSelfTest.py:20
int c
Definition: calibrate.py:100
std::string string(const unsigned &i)
Definition: oomph_definitions.cc:286
void concatenate(const Vector< LinearAlgebraDistribution * > &in_distribution_pt, LinearAlgebraDistribution &out_distribution)
Definition: linear_algebra_distribution.cc:367
EIGEN_DONT_INLINE Scalar zero()
Definition: svd_common.h:232

References b, i, j, m, n, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, and size.

◆ clear_block_preconditioner_base()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::clear_block_preconditioner_base ( )
inline

Clears all BlockPreconditioner data. Called by the destructor and the block_setup(...) methods

2160  {
2161  Replacement_dof_block_pt.clear();
2162 
2163  // clear the Distributions
2164  this->clear_distribution();
2165  unsigned nblock = Internal_block_distribution_pt.size();
2166  for (unsigned b = 0; b < nblock; b++)
2167  {
2169  }
2171 
2172  // clear the global index
2173  Global_index.clear();
2174 
2175  // call the post block matrix assembly clear
2177 
2178 #ifdef OOMPH_HAS_MPI
2179  // storage if the matrix is distributed
2180  unsigned nr = Rows_to_send_for_get_block.nrow();
2181  unsigned nc = Rows_to_send_for_get_block.ncol();
2182  for (unsigned p = 0; p < nc; p++)
2183  {
2184  delete[] Rows_to_send_for_get_ordered[p];
2185  delete[] Rows_to_recv_for_get_ordered[p];
2186  for (unsigned b = 0; b < nr; b++)
2187  {
2188  delete[] Rows_to_recv_for_get_block(b, p);
2189  delete[] Rows_to_send_for_get_block(b, p);
2190  }
2191  }
2192  Rows_to_recv_for_get_block.resize(0, 0);
2193  Nrows_to_recv_for_get_block.resize(0, 0);
2194  Rows_to_send_for_get_block.resize(0, 0);
2195  Nrows_to_send_for_get_block.resize(0, 0);
2196  Rows_to_recv_for_get_ordered.clear();
2197  Nrows_to_recv_for_get_ordered.clear();
2198  Rows_to_send_for_get_ordered.clear();
2199  Nrows_to_send_for_get_ordered.clear();
2200 
2201 #endif
2202 
2203  // zero
2205  {
2206  Nrow = 0;
2207  Internal_ndof_types = 0;
2209  }
2210 
2211  // delete the prec matrix dist pt
2216 
2217  // Delete any existing (external) block distributions.
2218  const unsigned n_existing_block_dist = Block_distribution_pt.size();
2219  for (unsigned dist_i = 0; dist_i < n_existing_block_dist; dist_i++)
2220  {
2221  delete Block_distribution_pt[dist_i];
2222  }
2223 
2224  // Clear the vector.
2225  Block_distribution_pt.clear();
2226 
2227 
2228  // Create the identity key.
2229  Vector<unsigned> preconditioner_matrix_key(n_existing_block_dist, 0);
2230  for (unsigned i = 0; i < n_existing_block_dist; i++)
2231  {
2232  preconditioner_matrix_key[i] = i;
2233  }
2234 
2235  // Now iterate through Auxiliary_block_distribution_pt
2236  // and delete all distributions, except for the one which corresponds
2237  // to the identity since this is already deleted.
2238  std::map<Vector<unsigned>, LinearAlgebraDistribution*>::iterator iter =
2240 
2241  while (iter != Auxiliary_block_distribution_pt.end())
2242  {
2243  if (iter->first != preconditioner_matrix_key)
2244  {
2245  delete iter->second;
2246  iter++;
2247  }
2248  else
2249  {
2250  ++iter;
2251  }
2252  }
2253 
2254  // Now clear it.
2256 
2257  // Delete any dof block distributions
2258  const unsigned ndof_block_dist = Dof_block_distribution_pt.size();
2259  for (unsigned dof_i = 0; dof_i < ndof_block_dist; dof_i++)
2260  {
2261  delete Dof_block_distribution_pt[dof_i];
2262  }
2263  Dof_block_distribution_pt.clear();
2264 
2265  } // EOFunc clear_block_preconditioner_base()
void post_block_matrix_assembly_partial_clear()
Definition: block_preconditioner.h:2120
MapMatrix< unsigned, CRDoubleMatrix * > Replacement_dof_block_pt
The replacement dof-level blocks.
Definition: block_preconditioner.h:3274
void clear_distribution()
Definition: linear_algebra_distribution.h:522

References oomph::BlockPreconditioner< MATRIX >::Auxiliary_block_distribution_pt, b, oomph::BlockPreconditioner< MATRIX >::Block_distribution_pt, oomph::DistributableLinearAlgebraObject::clear_distribution(), oomph::BlockPreconditioner< MATRIX >::Dof_block_distribution_pt, oomph::BlockPreconditioner< MATRIX >::Global_index, i, oomph::BlockPreconditioner< MATRIX >::Internal_block_distribution_pt, oomph::BlockPreconditioner< MATRIX >::Internal_nblock_types, oomph::BlockPreconditioner< MATRIX >::Internal_ndof_types, oomph::BlockPreconditioner< MATRIX >::Internal_preconditioner_matrix_distribution_pt, oomph::BlockPreconditioner< MATRIX >::is_master_block_preconditioner(), oomph::BlockPreconditioner< MATRIX >::Nrow, p, oomph::BlockPreconditioner< MATRIX >::post_block_matrix_assembly_partial_clear(), oomph::BlockPreconditioner< MATRIX >::Preconditioner_matrix_distribution_pt, and oomph::BlockPreconditioner< MATRIX >::Replacement_dof_block_pt.

Referenced by oomph::GeneralPurposeBlockPreconditioner< MATRIX >::clean_up_memory(), and oomph::BlockPreconditioner< MATRIX >::~BlockPreconditioner().

◆ disable_block_output_to_files()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::disable_block_output_to_files ( )
inline

Turn off output of blocks (by clearing the basefilename string).

2083  {
2084  Output_base_filename.clear();
2085  } // EOFunc disable_block_output_to_files()

References oomph::BlockPreconditioner< MATRIX >::Output_base_filename.

◆ document()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::document ( )
inline

debugging method to document the setup. Should only be called after block_setup(...).

2270  {
2271  oomph_info << std::endl;
2272  oomph_info << "===========================================" << std::endl;
2273  oomph_info << "Block Preconditioner Documentation" << std::endl
2274  << std::endl;
2275  oomph_info << "Number of DOF types: " << internal_ndof_types()
2276  << std::endl;
2277  oomph_info << "Number of block types: " << internal_nblock_types()
2278  << std::endl;
2279  oomph_info << std::endl;
2281  {
2282  for (unsigned d = 0; d < Internal_ndof_types; d++)
2283  {
2284  oomph_info << "Master DOF number " << d << " : "
2285  << this->internal_master_dof_number(d) << std::endl;
2286  }
2287  }
2288  oomph_info << std::endl;
2289  for (unsigned b = 0; b < internal_nblock_types(); b++)
2290  {
2291  oomph_info << "Block " << b << " DOF types:";
2292  for (unsigned i = 0; i < Block_number_to_dof_number_lookup[b].size();
2293  i++)
2294  {
2296  }
2297  oomph_info << std::endl;
2298  }
2299  oomph_info << std::endl;
2300  oomph_info << "Master block preconditioner distribution:" << std::endl;
2301  oomph_info << *master_distribution_pt() << std::endl;
2302  oomph_info << "Internal preconditioner matrix distribution:" << std::endl;
2304  << std::endl;
2305  oomph_info << "Preconditioner matrix distribution:" << std::endl;
2307  for (unsigned b = 0; b < Internal_nblock_types; b++)
2308  {
2309  oomph_info << "Internal block " << b << " distribution:" << std::endl;
2310  oomph_info << *Internal_block_distribution_pt[b] << std::endl;
2311  }
2312  for (unsigned b = 0; b < nblock_types(); b++)
2313  {
2314  oomph_info << "Block " << b << " distribution:" << std::endl;
2315  oomph_info << *Block_distribution_pt[b] << std::endl;
2316  }
2317 
2318  // DS: the functions called here no longer exist and this function is
2319  // never used as far as I can tell, so it should be fine to comment this
2320  // bit out:
2321  // if (is_master_block_preconditioner())
2322  // {
2323  // oomph_info << "First look-up row: " << this->first_lookup_row()
2324  // << std::endl;
2325  // oomph_info << "Number of look-up rows: "
2326  // << this->nlookup_rows() << std::endl;
2327  // }
2328  oomph_info << "===========================================" << std::endl;
2329  oomph_info << std::endl;
2330  } // EOFunc document()
const LinearAlgebraDistribution * internal_preconditioner_matrix_distribution_pt() const
Definition: block_preconditioner.h:3251
unsigned internal_master_dof_number(const unsigned &b) const
Definition: block_preconditioner.h:3238
const LinearAlgebraDistribution * preconditioner_matrix_distribution_pt() const
Definition: block_preconditioner.h:3267
unsigned internal_nblock_types() const
Definition: block_preconditioner.h:2522
OomphInfo oomph_info
Definition: oomph_definitions.cc:319

References b, oomph::BlockPreconditioner< MATRIX >::Block_distribution_pt, oomph::BlockPreconditioner< MATRIX >::Block_number_to_dof_number_lookup, i, oomph::BlockPreconditioner< MATRIX >::Internal_block_distribution_pt, oomph::BlockPreconditioner< MATRIX >::internal_master_dof_number(), oomph::BlockPreconditioner< MATRIX >::internal_nblock_types(), oomph::BlockPreconditioner< MATRIX >::Internal_nblock_types, oomph::BlockPreconditioner< MATRIX >::internal_ndof_types(), oomph::BlockPreconditioner< MATRIX >::Internal_ndof_types, oomph::BlockPreconditioner< MATRIX >::internal_preconditioner_matrix_distribution_pt(), oomph::BlockPreconditioner< MATRIX >::is_subsidiary_block_preconditioner(), oomph::BlockPreconditioner< MATRIX >::master_distribution_pt(), oomph::BlockPreconditioner< MATRIX >::nblock_types(), oomph::oomph_info, and oomph::BlockPreconditioner< MATRIX >::preconditioner_matrix_distribution_pt().

◆ dof_block_distribution_pt()

template<typename MATRIX >
LinearAlgebraDistribution* oomph::BlockPreconditioner< MATRIX >::dof_block_distribution_pt ( const unsigned b)
inline

Access function to the dof-level block distributions.

1963  {
1964 #ifdef PARANOID
1965  if (b > ndof_types())
1966  {
1967  std::ostringstream error_msg;
1968  error_msg << "You requested the distribution for the dof block " << b
1969  << ".\n"
1970  << "But there are only " << ndof_types() << " DOF types.\n"
1971  << std::endl;
1972  throw OomphLibError(
1974  }
1975 
1976 #endif
1977 
1979  {
1980 #ifdef PARANOID
1981  if (Internal_block_distribution_pt.size() == 0)
1982  {
1983  std::ostringstream error_msg;
1984  error_msg << "Internal block distributions are not set up.\n"
1985  << "Have you called block_setup(...)?\n"
1986  << std::endl;
1987  throw OomphLibError(
1989  }
1990 #endif
1991 
1992  // The dof block is distribution is the same as the internal
1993  // block distribution.
1995  }
1996  else
1997  {
1998 #ifdef PARANOID
1999  if (Dof_block_distribution_pt.size() == 0)
2000  {
2001  std::ostringstream error_msg;
2002  error_msg << "Dof block distributions are not set up.\n"
2003  << "Have you called block_setup(...)?\n"
2004  << std::endl;
2005  throw OomphLibError(
2007  }
2008 #endif
2009  return Dof_block_distribution_pt[b];
2010  }
2011  } // EOFunc block_distribution_pt(...)

References b, oomph::BlockPreconditioner< MATRIX >::Dof_block_distribution_pt, oomph::BlockPreconditioner< MATRIX >::Internal_block_distribution_pt, oomph::BlockPreconditioner< MATRIX >::is_master_block_preconditioner(), oomph::BlockPreconditioner< MATRIX >::ndof_types(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Referenced by oomph::BlockPreconditioner< MATRIX >::set_replacement_dof_block().

◆ doftype_coarsen_map_fine()

template<typename MATRIX >
Vector<Vector<unsigned> > oomph::BlockPreconditioner< MATRIX >::doftype_coarsen_map_fine ( ) const
inline

Access function for the Doftype_coarsen_map_fine variable.

2335  {
2336  return Doftype_coarsen_map_fine;
2337  }

References oomph::BlockPreconditioner< MATRIX >::Doftype_coarsen_map_fine.

◆ get_block() [1/2]

template<typename MATRIX >
MATRIX oomph::BlockPreconditioner< MATRIX >::get_block ( const unsigned i,
const unsigned j,
const bool ignore_replacement_block = false 
) const
inline

Return block (i,j). If the optional argument ignore_replacement_block is true, then any blocks in Replacement_dof_block_pt will be ignored throughout the preconditioning hierarchy.

991  {
992  MATRIX output_matrix;
993  get_block(i, j, output_matrix, ignore_replacement_block);
994  return output_matrix;
995  } // EOFunc get_block(...)
void get_block(const unsigned &i, const unsigned &j, MATRIX &output_matrix, const bool &ignore_replacement_block=false) const
Definition: block_preconditioner.h:673

References oomph::BlockPreconditioner< MATRIX >::get_block(), i, and j.

◆ get_block() [2/2]

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::get_block ( const unsigned i,
const unsigned j,
MATRIX &  output_matrix,
const bool ignore_replacement_block = false 
) const
inline

Put block (i,j) into output_matrix. This block accounts for any coarsening of dof types and any replaced dof-level blocks above this preconditioner.

677  {
678 #ifdef PARANOID
679  // Check the range of i and j, they should not be greater than
680  // nblock_types
681  unsigned n_block_types = this->nblock_types();
682  if ((i > n_block_types) || (j > n_block_types))
683  {
684  std::ostringstream err_msg;
685  err_msg << "Requested block(" << i << "," << j << ")"
686  << "\n"
687  << "but nblock_types() is " << n_block_types << ".\n"
688  << "Maybe your argument to block_setup(...) is incorrect?"
689  << std::endl;
690  throw OomphLibError(
692  }
693 #endif
694 
695  // The logic is this:
696  //
697  // Block_to_dof_map_coarse tells us which dof types belongs in each block.
698  // This is relative to this preconditioner, and describes the external
699  // block and dof type mappings (what the preconditioner writer
700  // expects/sees).
701  //
702  // As such, the dof types in Block_to_dof_map_coarse describes the
703  // dof-level blocks needed to be concatenated to produce block(i,j). These
704  // dofs may have been coarsened.
705  //
706  // Now, the dof blocks to concatenate comes from:
707  // If the dof block exists in Replacement_dof_block_pt, then we make a
708  // deep copy of it. Otherwise, if this is the upper-most master block
709  // preconditioner then we get it from the original matrix via function
710  // internal_get_block(...) otherwise, if this is a subsidiary
711  // block preconditioner, we go one level up the hierarchy and repeat the
712  // process.
713  //
714  //
715  // A small note about indirections which caused me some headache.
716  // Thought I would mention it here in case the below code needs to be
717  // re-visited.
718  //
719  // A small subtlety with indirections:
720  //
721  // The underlying ordering of the dof-level blocks is STILL AND ALWAYS the
722  // `natural' ordering determined by first the elements then the order of
723  // the meshes.
724  //
725  // But during the process of block_setup(...), the external (perceived)
726  // block ordering may have changed. So some indirection has to take place,
727  // this mapping is maintained in Block_to_dof_map_coarse.
728  //
729  // For example, take the Lagrangian preconditioner, which expects the
730  // natural dof type ordering:
731  // 0 1 2 3 4 5
732  // u v p uc vc L
733  //
734  // If the mapping given to block_setup(...) is:
735  // dof_to_block_map = [0, 1, 4, 2, 3, 5]
736  // saying that: dof type 0 goes to block 0
737  // dof type 1 goes to block 1
738  // dof type 2 goes to block 4
739  // dof type 3 goes to block 2
740  // dof type 4 goes to block 3
741  // dof type 5 goes to block 5
742  //
743  // The function block_setup(...) will populate the vector
744  // Block_to_dof_map_coarse with [[0][1][3][4][2][5]],
745  // which says that get_block(0,0) will get the u block
746  // get_block(1,1) will get the v block
747  // get_block(2,2) will get the uc block
748  // get_block(3,3) will get the vc block
749  // get_block(4,4) will get the p block
750  // get_block(5,5) will get the L block
751  //
752  // i.e. the ordering of the block types is a permutation of the dof types,
753  // so that we now have the following block ordering:
754  // 0 1 2 3 4 5 <- block ordering
755  // u v uc vc p L
756  //
757  // Now, the writer expects to work with the block ordering. I.e. when we
758  // replace a dof-level block, say the pressure block, we call
759  // set_replacement_dof_block(4,4,Matrix);
760  // Similarly, when we want the pressure block, we call
761  // get_block(4,4);
762  //
763  // Now, the below code uses the indirection maintained in
764  // Block_to_dof_map_coarse. I.e. When we call get_block(4,4), we use the
765  // mapping Block_to_dof_map_coarse[4] = 2, we get the block (2,2)
766  // (from Replacement_dof_block_pt or internal_get_block), since the
767  // underlying dof_to_block mapping is still the identity, i.e. it has not
768  // changed from:
769  // 0 1 2 3 4 5
770  // u v p uc vc L
771  //
772  // So, block (4,4) (pressure block) maps to the block (2,2).
773 
774  // How many external dof types are in this block?
775  const unsigned ndofblock_in_row = Block_to_dof_map_coarse[i].size();
776  const unsigned ndofblock_in_col = Block_to_dof_map_coarse[j].size();
777 
778  // If there is only one dof block in this block then there is no need to
779  // concatenate.
780  if (ndofblock_in_row == 1 && ndofblock_in_col == 1)
781  {
782  // Get the indirection for the dof we want.
783  const unsigned wanted_dof_row = Block_to_dof_map_coarse[i][0];
784  const unsigned wanted_dof_col = Block_to_dof_map_coarse[j][0];
785 
786  // If the block has NOT been replaced or if we want to ignore the
787  // replacement, then we call get_dof_level_block(...) which will get the
788  // dof-level blocks up the preconditioning hierarchy, dragging down
789  // any replacement dof blocks of parent preconditioners if required.
790  if ((Replacement_dof_block_pt.get(wanted_dof_row, wanted_dof_col) ==
791  0) ||
792  ignore_replacement_block)
793  {
794  get_dof_level_block(wanted_dof_row,
795  wanted_dof_col,
796  output_matrix,
797  ignore_replacement_block);
798  }
799  else
800  // Replacement_dof_block_pt.get(wanted_dof_row,wanted_dof_col) is not
801  // null, this means that the block has been replaced. We simply make
802  // a deep copy of it.
803  {
805  Replacement_dof_block_pt.get(wanted_dof_row, wanted_dof_col),
806  output_matrix);
807  }
808  }
809  else
810  // This block contains more than one dof-level block. So we need to
811  // concatenate the (external) dof-level blocks.
812  {
813  // The CRDoubleMatrixHelpers::concatenate_without_communication(...)
814  // takes a DenseMatrix of pointers to CRDoubleMatrices to concatenate.
815  DenseMatrix<CRDoubleMatrix*> tmp_blocks_pt(
816  ndofblock_in_row, ndofblock_in_col, 0);
817 
818  // Vector of Vectors of unsigns to indicate whether we have created
819  // CRDoubleMatrices with new or not... so we can delete it later.
820  // 0 - no new CRDoubleMatrix is created.
821  // 1 - a new CRDoubleMatrix is created.
822  // If we ever use C++11, remove this and use smart pointers.
823  Vector<Vector<unsigned>> new_block(
824  ndofblock_in_row, Vector<unsigned>(ndofblock_in_col, 0));
825 
826  // Loop through the number of dof block rows and then the number of dof
827  // block columns, either get the pointer from Replacement_dof_block_pt
828  // or from get_dof_level_block(...).
829  for (unsigned row_dofblock = 0; row_dofblock < ndofblock_in_row;
830  row_dofblock++)
831  {
832  // Indirection for the row, as discuss in the large chunk of text
833  // previously.
834  const unsigned wanted_dof_row =
835  Block_to_dof_map_coarse[i][row_dofblock];
836 
837  for (unsigned col_dofblock = 0; col_dofblock < ndofblock_in_col;
838  col_dofblock++)
839  {
840  // Indirection for the column as discussed in the large chunk of
841  // text above.
842  const unsigned wanted_dof_col =
843  Block_to_dof_map_coarse[j][col_dofblock];
844 
845  // Get the pointer from Replacement_dof_block_pt.
846  tmp_blocks_pt(row_dofblock, col_dofblock) =
847  Replacement_dof_block_pt.get(wanted_dof_row, wanted_dof_col);
848 
849  // If the pointer from Replacement_dof_block_pt is null, or if
850  // we have to ignore replacement blocks, build a new CRDoubleMatrix
851  // via get_dof_level_block.
852  if ((tmp_blocks_pt(row_dofblock, col_dofblock) == 0) ||
853  ignore_replacement_block)
854  {
855  // We have to create a new CRDoubleMatrix, so put in 1 to indicate
856  // that we have to delete it later.
857  new_block[row_dofblock][col_dofblock] = 1;
858 
859  // Create the new CRDoubleMatrix. Note that we do not use the
860  // indirection, since the indirection is only used one way.
861  tmp_blocks_pt(row_dofblock, col_dofblock) = new CRDoubleMatrix;
862 
863  // Get the dof-level block, as discussed above.
864  get_dof_level_block(wanted_dof_row,
865  wanted_dof_col,
866  *tmp_blocks_pt(row_dofblock, col_dofblock),
867  ignore_replacement_block);
868  }
869  }
870  }
871 
872  // Concatenation of matrices require the distribution of the individual
873  // sub-matrices (for both row and column). This is because concatenation
874  // is implemented without communication in such a way that the rows
875  // and column values are both permuted, the permutation is defined by
876  // the individual distributions of the sub blocks.
877  // Without a vector of distributions describing the distributions of
878  // of the rows, we do not know how to permute the rows. For the columns,
879  // although CRDoubleMatrices does not have a column distribution, the
880  // concatenated matrix must have it's columns permuted, so we mirror
881  // the diagonal and get the corresponding row distribution.
882  //
883  // Confused? - Example: Say we have a matrix with dof blocking
884  //
885  // | a | b | c | d | e |
886  // --|---|---|---|---|---|
887  // a | | | | | |
888  // --|---|---|---|---|---|
889  // b | | | | | |
890  // --|---|---|---|---|---|
891  // c | | | | | |
892  // --|---|---|---|---|---|
893  // d | | | | | |
894  // --|---|---|---|---|---|
895  // e | | | | | |
896  // --|---|---|---|---|---|
897  //
898  // We wish to concatenate the blocks
899  //
900  // | d | e |
901  // --|---|---|
902  // a | | |
903  // --|---|---|
904  // b | | |
905  // --|---|---|
906  //
907  // Then clearly the row distributions required are the distributions for
908  // the dof blocks a and b. But block(a,d) does not have a column
909  // distribution since it is a CRDoubleMatrix! - We use the distribution
910  // mirrored by the diagonal, so the column distributions required to
911  // concatenate these blocks is the same as the distributions of the rows
912  // for dof block d and e.
913 
914  // First we do the row distribution.
915 
916  // Storage for the row distribution pointers.
917  Vector<LinearAlgebraDistribution*> tmp_row_dist_pt(ndofblock_in_row, 0);
918 
919  // Loop through the number of dof blocks in the row. For the upper-most
920  // master block preconditioner, the external dof distributions is the
921  // same as the internal BLOCK distributions. Recall what we said above
922  // about the underlying blocks maintaining it's natural ordering.
923  //
924  // If this is a subsidiary block preconditioner, then the distributions
925  // for the dof blocks are stored in Dof_block_distribution_pt. The
926  // reason why this is different for subsidiary block preconditioners is
927  // because subsidiary block preconditioners would have it's dof types
928  // coarsened. Then a single dof distribution in a subsidiary block
929  // preconditioner could be a concatenation of many dof distributions of
930  // the master dof distributions.
931  for (unsigned row_dof_i = 0; row_dof_i < ndofblock_in_row; row_dof_i++)
932  {
933  const unsigned mapped_dof_i = Block_to_dof_map_coarse[i][row_dof_i];
935  {
936  tmp_row_dist_pt[row_dof_i] =
937  Internal_block_distribution_pt[mapped_dof_i];
938  }
939  else
940  {
941  tmp_row_dist_pt[row_dof_i] =
942  Dof_block_distribution_pt[mapped_dof_i];
943  }
944  }
945 
946  // Storage for the column distribution pointers.
947  Vector<LinearAlgebraDistribution*> tmp_col_dist_pt(ndofblock_in_col, 0);
948 
949  // We do the same thing as before.
950  for (unsigned col_dof_i = 0; col_dof_i < ndofblock_in_col; col_dof_i++)
951  {
952  const unsigned mapped_dof_j = Block_to_dof_map_coarse[j][col_dof_i];
954  {
955  tmp_col_dist_pt[col_dof_i] =
956  Internal_block_distribution_pt[mapped_dof_j];
957  }
958  else
959  {
960  tmp_col_dist_pt[col_dof_i] =
961  Dof_block_distribution_pt[mapped_dof_j];
962  }
963  }
964 
965  // Perform the concatenation.
967  tmp_row_dist_pt, tmp_col_dist_pt, tmp_blocks_pt, output_matrix);
968 
969  // Delete any new CRDoubleMatrices we have created.
970  for (unsigned row_i = 0; row_i < ndofblock_in_row; row_i++)
971  {
972  for (unsigned col_i = 0; col_i < ndofblock_in_col; col_i++)
973  {
974  if (new_block[row_i][col_i])
975  {
976  delete tmp_blocks_pt(row_i, col_i);
977  }
978  }
979  }
980  } // else need to concatenate
981  } // EOFunc get_block(...)
void get_dof_level_block(const unsigned &i, const unsigned &j, MATRIX &output_block, const bool &ignore_replacement_block=false) const
void deep_copy(const CRDoubleMatrix *const in_matrix_pt, CRDoubleMatrix &out_matrix)
Create a deep copy of the matrix pointed to by in_matrix_pt.
Definition: matrices.h:3490
void concatenate_without_communication(const Vector< LinearAlgebraDistribution * > &row_distribution_pt, const Vector< LinearAlgebraDistribution * > &col_distribution_pt, const DenseMatrix< CRDoubleMatrix * > &matrix_pt, CRDoubleMatrix &result_matrix)
Definition: matrices.cc:5223

References oomph::BlockPreconditioner< MATRIX >::Block_to_dof_map_coarse, oomph::CRDoubleMatrixHelpers::concatenate_without_communication(), oomph::CRDoubleMatrixHelpers::deep_copy(), oomph::BlockPreconditioner< MATRIX >::Dof_block_distribution_pt, oomph::BlockPreconditioner< MATRIX >::get_dof_level_block(), i, oomph::BlockPreconditioner< MATRIX >::Internal_block_distribution_pt, oomph::BlockPreconditioner< MATRIX >::is_master_block_preconditioner(), j, oomph::BlockPreconditioner< MATRIX >::nblock_types(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, and oomph::BlockPreconditioner< MATRIX >::Replacement_dof_block_pt.

Referenced by oomph::BlockPreconditioner< MATRIX >::get_block(), oomph::BlockPreconditioner< MATRIX >::get_block_other_matrix(), oomph::BlockPreconditioner< MATRIX >::get_concatenated_block(), oomph::BlockPreconditioner< MATRIX >::output_blocks_to_files(), oomph::HelmholtzGMRESMG< MATRIX >::solve(), and oomph::HelmholtzFGMRESMG< MATRIX >::solve().

◆ get_block_ordered_preconditioner_vector()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::get_block_ordered_preconditioner_vector ( const DoubleVector v,
DoubleVector w 
)

Given the naturally ordered vector, v, return the vector rearranged in block order in w. This function calls get_concatenated_block_vector(...) with the identity block mapping.

This function has been re-written to work with the new dof type coarsening feature. The old function is kept alive in internal_get_block_ordered_preconditioner_vector(...) and is moved to the private section of the code. The differences between the two are:

1) This function extracts all the block vectors (in one go) via the function internal_get_block_vectors(...), and concatenates them.

2) The old function makes use of the variables ending in "get_ordered", thus is slightly more efficient since it does not have to concatenate any block vectors.

3) The old function no longer respect the new indirections if dof types have been coarsened.

4) This function extracts the most fine grain dof-level vectors and concatenates them. These dof-level vectors respect the re-ordering caused by the coarsening of dof types. The overhead associated with concatenating DoubleVectors without communication is very small.

This function should be used.

4795  {
4796 #ifdef PARANOID
4797  if (!v.built())
4798  {
4799  std::ostringstream error_message;
4800  error_message << "The distribution of the global vector v must be setup.";
4801  throw OomphLibError(
4802  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4803  }
4804  if (*v.distribution_pt() != *this->master_distribution_pt())
4805  {
4806  std::ostringstream error_message;
4807  error_message << "The distribution of the global vector v must match the "
4808  << " specified master_distribution_pt(). \n"
4809  << "i.e. Distribution_pt in the master preconditioner";
4810  throw OomphLibError(
4811  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4812  }
4813 #endif
4814 
4815  // Get the number of blocks.
4816  unsigned nblocks = this->nblock_types();
4817 
4818  // Fill in the identity mapping.
4819  Vector<unsigned> block_vec_number(nblocks, 0);
4820  for (unsigned b = 0; b < nblocks; b++)
4821  {
4822  block_vec_number[b] = b;
4823  }
4824 
4825  // Do the work.
4826  get_concatenated_block_vector(block_vec_number, v, w);
4827  } // get_block_ordered_preconditioner_vector(...)
Array< int, Dynamic, 1 > v
Definition: Array_initializer_list_vector_cxx11.cpp:1
RowVector3d w
Definition: Matrix_resize_int.cpp:3
void get_concatenated_block_vector(const Vector< unsigned > &block_vec_number, const DoubleVector &v, DoubleVector &b)
Definition: block_preconditioner.cc:2608

References b, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, v, and w.

◆ get_block_other_matrix()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::get_block_other_matrix ( const unsigned i,
const unsigned j,
MATRIX *  in_matrix_pt,
MATRIX &  output_matrix 
)
inline

Get a block from a different matrix using the blocking scheme that has already been set up.

1016  {
1017  MATRIX* backup_matrix_pt = matrix_pt();
1018  set_master_matrix_pt(in_matrix_pt);
1019  get_block(i, j, output_matrix, true);
1020  set_master_matrix_pt(backup_matrix_pt);
1021  } // EOFunc get_block_other_matrix(...)
void set_master_matrix_pt(MATRIX *in_matrix_pt)
Set the matrix_pt in the upper-most master preconditioner.
Definition: block_preconditioner.h:998

References oomph::BlockPreconditioner< MATRIX >::get_block(), i, j, oomph::BlockPreconditioner< MATRIX >::matrix_pt(), and oomph::BlockPreconditioner< MATRIX >::set_master_matrix_pt().

◆ get_block_vector()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::get_block_vector ( const unsigned b,
const DoubleVector v,
DoubleVector w 
) const

Takes the naturally ordered vector, v and returns the n-th block vector, b. Here n is the block number in the current preconditioner.

4189  {
4190 #ifdef PARANOID
4191  // the number of blocks
4192  const unsigned para_n_blocks = nblock_types();
4193 
4194  // paranoid check that block i is in this block preconditioner
4195  if (b >= para_n_blocks)
4196  {
4197  std::ostringstream err_msg;
4198  err_msg << "Requested block vector " << b
4199  << ", however this preconditioner has only " << para_n_blocks
4200  << " block types"
4201  << ".\n";
4202  throw OomphLibError(
4204  }
4205 
4206  if (!v.built())
4207  {
4208  std::ostringstream err_msg;
4209  err_msg << "The distribution of the global vector v must be setup.";
4210  throw OomphLibError(
4212  }
4213  if (*(v.distribution_pt()) != *(this->master_distribution_pt()))
4214  {
4215  std::ostringstream err_msg;
4216  err_msg << "The distribution of the global vector v must match the "
4217  << " specified master_distribution_pt(). \n"
4218  << "i.e. Distribution_pt in the master preconditioner";
4219  throw OomphLibError(
4221  }
4222 #endif
4223 
4224  // Recall that, the relationship between the external blocks and the
4225  // external dof types, as seen by the preconditioner writer is stored in the
4226  // mapping Block_to_dof_map_coarse.
4227  //
4228  // However, each dof type could have been coarsened! The relationship
4229  // between the dof types of this preconditioner and the parent
4230  // preconditioner is stored in the mapping Doftype_coarsen_map_coarse. The
4231  // dof numbers in this map is relative to this preconditioner.
4232  //
4233  // Finally, the relationship between the dof types of this preconditioner
4234  // and the most fine grain dof types is stored in the mapping
4235  // Doftype_coarsen_map_fine. Again, the dof numbers in this map is relative
4236  // to this preconditioner.
4237  //
4238  // Furthermore, we note that concatenation of vectors without communication
4239  // is associative, but not commutative. I.e.
4240  // (V1+V2)+V3 = V1 + (V2 + V3), where + is concatenation without
4241  // communication.
4242  //
4243  // So all we need is the vectors listed in the correct order.
4244  //
4245  // We need only Block_to_dof_map_coarse to tell us which external dof types
4246  // are in this block, then Doftype_coarsen_map_fine to tell us which most
4247  // fine grain dofs to concatenate!
4248  //
4249  // All the mapping vectors are constructed to respect the ordering of
4250  // the dof types.
4251 
4252  // Get the most fine grain block to dof mapping.
4253  Vector<unsigned> most_fine_grain_dof = Block_to_dof_map_fine[b];
4254 
4255  // How many vectors do we need to concatenate?
4256  const unsigned n_dof_vec = most_fine_grain_dof.size();
4257 
4258  if (n_dof_vec == 1)
4259  // No need to concatenate, just extract the vector.
4260  {
4261  internal_get_block_vector(most_fine_grain_dof[0], v, w);
4262  }
4263  else
4264  // Need to concatenate dof-level vectors.
4265  {
4266  Vector<DoubleVector> dof_vector(n_dof_vec);
4267 
4268  // Get all the dof-level vectors in one go
4269  internal_get_block_vectors(most_fine_grain_dof, v, dof_vector);
4270  // Build w with the correct distribution.
4271  w.build(Block_distribution_pt[b], 0);
4272 
4273  // Concatenate the vectors.
4275 
4276  dof_vector.clear();
4277  }
4278  } // get_block_vector(...)
void internal_get_block_vectors(const Vector< unsigned > &block_vec_number, const DoubleVector &v, Vector< DoubleVector > &s) const
Definition: block_preconditioner.cc:3131
void internal_get_block_vector(const unsigned &n, const DoubleVector &v, DoubleVector &b) const
Definition: block_preconditioner.cc:4009
void concatenate_without_communication(const Vector< DoubleVector * > &in_vector_pt, DoubleVector &out_vector)
Definition: double_vector.cc:1856

References b, oomph::DoubleVectorHelpers::concatenate_without_communication(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, v, and w.

◆ get_block_vectors() [1/2]

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::get_block_vectors ( const DoubleVector v,
Vector< DoubleVector > &  s 
) const

Takes the naturally ordered vector and rearranges it into a vector of sub vectors corresponding to the blocks, so s[b][i] contains the i-th entry in the vector associated with block b. Note: If the preconditioner is a subsidiary preconditioner then only the sub-vectors associated with the blocks of the subsidiary preconditioner will be included. Hence the length of v is master_nrow() whereas the total length of the s vectors is Nrow. This is simply a wrapper around the other get_block_vectors(...) function where the block_vec_number Vector is the identity, i.e. block_vec_number is [0, 1, ..., nblock_types - 1].

3104  {
3105  // Get the number of blocks in this block preconditioner.
3106  const unsigned n_block = nblock_types();
3107 
3108  // Create the identity vector.
3109  Vector<unsigned> required_block(n_block, 0);
3110  for (unsigned i = 0; i < n_block; i++)
3111  {
3112  required_block[i] = i;
3113  }
3114 
3115  // Call the other function which does the work.
3116  get_block_vectors(required_block, v, s);
3117  }
void get_block_vectors(const Vector< unsigned > &block_vec_number, const DoubleVector &v, Vector< DoubleVector > &s) const
Definition: block_preconditioner.cc:2939
RealScalar s
Definition: level1_cplx_impl.h:130

References i, s, and v.

◆ get_block_vectors() [2/2]

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::get_block_vectors ( const Vector< unsigned > &  block_vec_number,
const DoubleVector v,
Vector< DoubleVector > &  s 
) const

Takes the naturally ordered vector and rearranges it into a vector of sub vectors corresponding to the blocks, so s[b][i] contains the i-th entry in the vector associated with block b. Note: If the preconditioner is a subsidiary preconditioner then only the sub-vectors associated with the blocks of the subsidiary preconditioner will be included. Hence the length of v is master_nrow() whereas the total length of the s vectors is the sum of the lengths of the individual block vectors defined in block_vec_number.

2943  {
2944 #ifdef PARANOID
2945 
2946  // Check if v is built.
2947  if (!v.built())
2948  {
2949  std::ostringstream err_msg;
2950  err_msg << "The distribution of the global vector v must be setup.";
2951  throw OomphLibError(
2953  }
2954 
2955  // v must have the same distribution as the upper-most master block
2956  // preconditioner, since the upper-most master block preconditioner
2957  // should have the same distribution as the matrix pointed to
2958  // by matrix_pt().
2959  if (*(v.distribution_pt()) != *(this->master_distribution_pt()))
2960  {
2961  std::ostringstream err_msg;
2962  err_msg << "The distribution of the global vector v must match the "
2963  << " specified master_distribution_pt(). \n"
2964  << "i.e. Distribution_pt in the master preconditioner";
2965  throw OomphLibError(
2967  }
2968 
2969  // Check to see if there are more blocks defined in the block_vec_number
2970  // vector than the number of block types. This is not allowed.
2971  const unsigned para_nblock_types = nblock_types();
2972  const unsigned para_block_vec_number_size = block_vec_number.size();
2973  if (para_block_vec_number_size > para_nblock_types)
2974  {
2975  std::ostringstream err_msg;
2976  err_msg << "You have requested " << para_block_vec_number_size
2977  << " number of blocks, (block_vec_number.size() is "
2978  << para_block_vec_number_size << ").\n"
2979  << "But there are only " << para_nblock_types
2980  << " nblock_types.\n"
2981  << "Please make sure that block_vec_number is correctly sized.\n";
2982  throw OomphLibError(
2984  }
2985 
2986  // Check if any block numbers defined in block_vec_number is equal to or
2987  // greater than the number of block types.
2988  // E.g. if there are 5 block types, we can only have block numbers:
2989  // 0, 1, 2, 3 and 4.
2990  for (unsigned i = 0; i < para_block_vec_number_size; i++)
2991  {
2992  const unsigned para_required_block = block_vec_number[i];
2993  if (para_required_block > para_nblock_types)
2994  {
2995  std::ostringstream err_msg;
2996  err_msg << "block_vec_number[" << i << "] is " << para_required_block
2997  << ".\n"
2998  << "But there are only " << para_nblock_types
2999  << " nblock_types.\n";
3000  throw OomphLibError(
3002  }
3003  }
3004  // Check that no block number is inserted twice.
3005  std::set<unsigned> para_set;
3006  for (unsigned b = 0; b < para_block_vec_number_size; b++)
3007  {
3008  std::pair<std::set<unsigned>::iterator, bool> para_set_ret;
3009  para_set_ret = para_set.insert(block_vec_number[b]);
3010 
3011  if (!para_set_ret.second)
3012  {
3013  std::ostringstream err_msg;
3014  err_msg << "Error: the block number " << block_vec_number[b]
3015  << " appears twice.\n";
3016  throw OomphLibError(
3018  }
3019  }
3020 #endif
3021 
3022  // Number of blocks to get.
3023  const unsigned n_block = block_vec_number.size();
3024  s.resize(n_block);
3025 
3026  // Each block is made of dof types. We get the most fine grain dof types.
3027  // Most fine grain in the sense that these are the dof types that belongs
3028  // in this block before any coarsening of dof types has taken place.
3029  // The ordering of the dof types matters, this is handled properly when
3030  // creating the Block_to_dof_map_fine vector and must be respected here.
3031  // I.e. we cannot arbitrarily insert dof types (even if they are correct)
3032  // in the vector most_fine_grain_dof.
3033  Vector<unsigned> most_fine_grain_dof;
3034  for (unsigned b = 0; b < n_block; b++)
3035  {
3036  const unsigned mapped_b = block_vec_number[b];
3037 
3038  most_fine_grain_dof.insert(most_fine_grain_dof.end(),
3039  Block_to_dof_map_fine[mapped_b].begin(),
3040  Block_to_dof_map_fine[mapped_b].end());
3041  }
3042 
3043  // Get all the dof level vectors in one go.
3044  Vector<DoubleVector> dof_vector;
3045  internal_get_block_vectors(most_fine_grain_dof, v, dof_vector);
3046 
3047  // For each block vector requested,
3048  // build the block s[b],
3049  // concatenate the corresponding dof vector
3050 
3051  // Since all the dof vectors are in dof_vector,
3052  // we need to loop through this.
3053  // The offset helps us loop through this.
3054  unsigned offset = 0;
3055 
3056  for (unsigned b = 0; b < n_block; b++)
3057  {
3058  // The actual block number required.
3059  const unsigned mapped_b = block_vec_number[b];
3060 
3061  // How many most fine grain dofs are in this block?
3062  const unsigned n_dof = Block_to_dof_map_fine[mapped_b].size();
3063 
3064  if (n_dof == 1)
3065  // No need to concatenate, just copy the DoubleVector.
3066  {
3067  s[b] = dof_vector[offset];
3068  }
3069  else
3070  // Concatenate the relevant dof vectors into s[b].
3071  {
3072  s[b].build(Block_distribution_pt[mapped_b], 0);
3073  Vector<DoubleVector*> tmp_vec_pt(n_dof, 0);
3074  for (unsigned vec_i = 0; vec_i < n_dof; vec_i++)
3075  {
3076  tmp_vec_pt[vec_i] = &dof_vector[offset + vec_i];
3077  }
3078 
3080  s[b]);
3081  }
3082 
3083  // Update the offset.
3084  offset += n_dof;
3085  }
3086  } // get_block_vectors(...)

References b, oomph::DoubleVectorHelpers::concatenate_without_communication(), i, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, s, and v.

Referenced by oomph::HelmholtzGMRESMG< MATRIX >::update().

◆ get_blocks()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::get_blocks ( DenseMatrix< bool > &  required_blocks,
DenseMatrix< MATRIX * > &  block_matrix_pt 
) const

Get all the block matrices required by the block preconditioner. Takes a pointer to a matrix of bools that indicate if a specified sub-block is required for the preconditioning operation. Computes the required block matrices, and stores pointers to them in the matrix block_matrix_pt. If an entry in block_matrix_pt is equal to NULL on return, that sub-block has not been requested and is therefore not available.

WARNING: the matrix pointers are created using new so you must delete them all manually!

WARNING 2: the matrix pointers in block_matrix_pt MUST be null because Richard in all his wisdom decided to call delete on any non-null pointers. Presumably to avoid fixing his memory leaks properly...

Get the block matrices required for the block preconditioner. Takes a pointer to a matrix of bools that indicate if a specified sub-block is required for the preconditioning operation. Computes the required block matrices, and stores pointers to them in the matrix block_matrix_pt. If an entry in block_matrix_pt is equal to NULL that sub-block has not been requested and is therefore not available.

2540  {
2541  // Cache number of block types
2542  const unsigned n_block_types = nblock_types();
2543 
2544 #ifdef PARANOID
2545  // If required blocks matrix pointer is not the correct size then abort.
2546  if ((required_blocks.nrow() != n_block_types) ||
2547  (required_blocks.ncol() != n_block_types))
2548  {
2549  std::ostringstream error_message;
2550  error_message << "The size of the matrix of bools required_blocks "
2551  << "(which indicates which blocks are required) is not the "
2552  << "right size, required_blocks is "
2553  << required_blocks.ncol() << " x " << required_blocks.nrow()
2554  << ", whereas it should "
2555  << "be " << n_block_types << " x " << n_block_types;
2556  throw OomphLibError(
2557  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
2558  }
2559 
2560  // If block matrix pointer is not the correct size then abort.
2561  if ((block_matrix_pt.nrow() != n_block_types) ||
2562  (block_matrix_pt.ncol() != n_block_types))
2563  {
2564  std::ostringstream error_message;
2565  error_message << "The size of the block matrix pt is not the "
2566  << "right size, block_matrix_pt is "
2567  << block_matrix_pt.ncol() << " x " << block_matrix_pt.nrow()
2568  << ", whereas it should "
2569  << "be " << n_block_types << " x " << n_block_types;
2570  throw OomphLibError(
2571  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
2572  }
2573 
2574 #endif
2575 
2576  // Loop over the blocks
2577  for (unsigned i = 0; i < n_block_types; i++)
2578  {
2579  for (unsigned j = 0; j < n_block_types; j++)
2580  {
2581  // If block(i,j) is required then create a matrix and fill it in.
2582  if (required_blocks(i, j))
2583  {
2584  //??ds might want to remove this use of new as well?
2585  block_matrix_pt(i, j) = new MATRIX;
2586  get_block(i, j, *block_matrix_pt(i, j));
2587  }
2588 
2589  // Otherwise set pointer to null.
2590  else
2591  {
2592  block_matrix_pt(i, j) = 0;
2593  }
2594  }
2595  }
2596  }
unsigned long nrow() const
Return the number of rows of the matrix.
Definition: matrices.h:485
unsigned long ncol() const
Return the number of columns of the matrix.
Definition: matrices.h:491

References i, j, oomph::DenseMatrix< T >::ncol(), oomph::DenseMatrix< T >::nrow(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

◆ get_concatenated_block()

template<typename MATRIX >
MATRIX oomph::BlockPreconditioner< MATRIX >::get_concatenated_block ( const VectorMatrix< BlockSelector > &  selected_block)
inline

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.

For each entry in the VectorMatrix, the following variables must correctly set: BlockSelector::Row_index - Refers to the row index of the block. BlockSelector::Column_index - Refers to the column index of the block. BlockSelector::Wanted - Do we want the block? BlockSelector::Replacement_block_pt - If not null, this block will be used instead of get_block(Row_index,Column_index).

For example, assume that we have a matrix of the following blocking: 0 1 2 3 4 | a | b | c | d | e | –|—|—|—|—|—|

0 a
1 b
2 c
3 d
4 e

If we want a block matrix corresponding to the concatenation of the blocks [(a,d), (a,e) , (b,e)* ] where top left and top right blocks comes from the function get_block(...), the bottom left entry is empty, and the bottom right is a modified block.

Then we create a VectorMatrix of size 2 by 2 VectorMatrix<BlockSelector> selected_block(2,2);

In the [0][0] entry: row index is 0, column index is 3, do we want this block? - yes (true). selected_block[0][0].select_block(0,3,true);

In the [0][1] entry: row index is 0, column index is 4, do we want this block? - yes (true). selected_block[0][0].select_block(0,4,true);

In the [1][0] entry: row index is 1, column index is 3, do we want this block? - no (false). selected_block[0][0].select_block(1,3,false);

In the [1][1] entry: row index is 1, column index is 4, do we want this block? - yes (true). selected_block[0][0].select_block(1,4,true,block_pt);

where block_pt is a pointer to the modified block.

Then we can call:

CRDoubleMatrix my_block = get_concatenated_block(selected_block);

NOTE: It is important to set the row and column indices even if you do not want the block. This is because, if we allow the row and column indices to be "not set", then we can have a whole empty block row without any indices. But concatenation of blocks without communication requires both the row and column distributions, so we know how to permute the rows and columns. So in short, we require that the column and row indices to always be set for every entry in the VectorMatrix<BlockSelector> object for convenience and consistency checks.

1136  {
1137 #ifdef PARANOID
1138 
1139  unsigned const para_selected_block_nrow = selected_block.nrow();
1140  unsigned const para_selected_block_ncol = selected_block.ncol();
1141  unsigned const para_nblocks = this->nblock_types();
1142 
1143  // Check that selected_block size is not 0.
1144  if (para_selected_block_nrow == 0)
1145  {
1146  std::ostringstream error_msg;
1147  error_msg << "selected_block has nrow = 0.\n";
1148  throw OomphLibError(
1150  }
1151 
1152  // Check that the number of blocks is not outside of the range
1153  // nblock_types(). Since this function builds matrices for block
1154  // preconditioning, it does not make sense for us to concatenate more
1155  // blocks than nblock_types().
1156  if ((para_selected_block_nrow > para_nblocks) ||
1157  (para_selected_block_ncol > para_nblocks))
1158  {
1159  std::ostringstream error_msg;
1160  error_msg << "Trying to concatenate a " << para_selected_block_nrow
1161  << " by " << para_selected_block_ncol << " block matrix,\n"
1162  << "but there are only " << para_nblocks << " block types.\n";
1163  throw OomphLibError(
1165  }
1166 
1167  // Check that selected_block make sense, i.e. the row indices of each row
1168  // are the same, and the column indices of each column are the same.
1169 
1170  // First check if the row indices are consistent.
1171  // For each row, loop through the columns, comparing the row index against
1172  // the first column.
1173  for (unsigned row_i = 0; row_i < para_selected_block_nrow; row_i++)
1174  {
1175  const unsigned col_0_row_index = selected_block[row_i][0].row_index();
1176 
1177  for (unsigned col_i = 0; col_i < para_selected_block_ncol; col_i++)
1178  {
1179  const unsigned para_b_i = selected_block[row_i][col_i].row_index();
1180  const unsigned para_b_j = selected_block[row_i][col_i].column_index();
1181 
1182  if (col_0_row_index != para_b_i)
1183  {
1184  std::ostringstream err_msg;
1185  err_msg << "Block index for block(" << row_i << "," << col_i << ") "
1186  << "contains block indices (" << para_b_i << "," << para_b_j
1187  << ").\n"
1188  << "But the row index for the first column is "
1189  << col_0_row_index << ", they must be the same!\n";
1190  throw OomphLibError(
1192  }
1193  }
1194  }
1195 
1196  // Do the same for the column indices, consistency check.
1197  // For each column, loop through the rows, comparing the column index
1198  // against the first row.
1199  for (unsigned col_i = 0; col_i < para_selected_block_ncol; col_i++)
1200  {
1201  const unsigned row_0_col_index =
1202  selected_block[0][col_i].column_index();
1203 
1204  for (unsigned row_i = 0; row_i < para_selected_block_nrow; row_i++)
1205  {
1206  const unsigned para_b_i = selected_block[row_i][col_i].row_index();
1207  const unsigned para_b_j = selected_block[row_i][col_i].column_index();
1208 
1209  if (row_0_col_index != para_b_j)
1210  {
1211  std::ostringstream err_msg;
1212  err_msg << "Block index for block(" << row_i << "," << col_i << ") "
1213  << "contains block indices (" << para_b_i << "," << para_b_j
1214  << ").\n"
1215  << "But the col index for the first row is "
1216  << row_0_col_index << ", they must be the same!\n";
1217  throw OomphLibError(
1219  }
1220  }
1221  }
1222 
1223  // Check to see if the values in selected_block is within the range
1224  // nblock_types()
1225  //
1226  // Since we know that the column and row indices are consistent (by the
1227  // two paranoia checks above), we only need to check the column indices
1228  // in the first row, and the row indices in the first column.
1229 
1230  // Check that the row indices in the first column are within the range
1231  // nblock_types()
1232  for (unsigned row_i = 0; row_i < para_selected_block_nrow; row_i++)
1233  {
1234  const unsigned para_b_i = selected_block[row_i][0].row_index();
1235  const unsigned para_b_j = selected_block[row_i][0].column_index();
1236  if (para_b_i > para_nblocks)
1237  {
1238  std::ostringstream err_msg;
1239  err_msg << "Block index for block(" << row_i << ",0) "
1240  << "contains block indices (" << para_b_i << "," << para_b_j
1241  << ").\n"
1242  << "But there are only " << para_nblocks
1243  << " nblock_types().\n";
1244  throw OomphLibError(
1246  }
1247  }
1248 
1249  // Check that the col indices in the first row are within the range
1250  // nblock_types()
1251  for (unsigned col_i = 0; col_i < para_selected_block_ncol; col_i++)
1252  {
1253  const unsigned para_b_i = selected_block[0][col_i].row_index();
1254  const unsigned para_b_j = selected_block[0][col_i].column_index();
1255  if (para_b_j > para_nblocks)
1256  {
1257  std::ostringstream err_msg;
1258  err_msg << "Block index for block(0," << col_i << ") "
1259  << "contains block indices (" << para_b_i << "," << para_b_j
1260  << ").\n"
1261  << "But there are only " << para_nblocks
1262  << " nblock_types().\n";
1263  throw OomphLibError(
1265  }
1266  }
1267 
1268  // Stricter test - can be removed is required in the future. For the first
1269  // column, check that the row indices does not repeat.
1270  std::set<unsigned> row_index_set;
1271  for (unsigned row_i = 0; row_i < para_selected_block_nrow; row_i++)
1272  {
1273  std::pair<std::set<unsigned>::iterator, bool> row_index_set_ret;
1274 
1275  const unsigned row_i_index = selected_block[row_i][0].row_index();
1276 
1277  row_index_set_ret = row_index_set.insert(row_i_index);
1278 
1279  if (!row_index_set_ret.second)
1280  {
1281  std::ostringstream err_msg;
1282  err_msg << "The row " << row_i_index << " is already inserted.\n";
1283  throw OomphLibError(
1285  }
1286  }
1287 
1288  // Stricter test - can be removed is required in the future. For the first
1289  // row, check that the column indices does not repeat.
1290  std::set<unsigned> col_index_set;
1291  for (unsigned col_i = 0; col_i < para_selected_block_ncol; col_i++)
1292  {
1293  std::pair<std::set<unsigned>::iterator, bool> col_index_set_ret;
1294 
1295  const unsigned col_i_index = selected_block[0][col_i].column_index();
1296 
1297  col_index_set_ret = col_index_set.insert(col_i_index);
1298 
1299  if (!col_index_set_ret.second)
1300  {
1301  std::ostringstream err_msg;
1302  err_msg << "The col " << col_i_index << " is already inserted.\n";
1303  throw OomphLibError(
1305  }
1306  }
1307 
1308  // Loop through all the block_pt and check:
1309  // 1) The non-null pointers point to built matrices.
1310  // 2) The distribution matches those defined by selected_block within
1311  // Block_distribution_pt.
1312  for (unsigned block_i = 0; block_i < para_selected_block_nrow; block_i++)
1313  {
1314  for (unsigned block_j = 0; block_j < para_selected_block_ncol;
1315  block_j++)
1316  {
1317  const CRDoubleMatrix* tmp_block_pt =
1318  selected_block[block_i][block_j].replacement_block_pt();
1319 
1320  if (tmp_block_pt != 0)
1321  {
1322  if (!tmp_block_pt->built())
1323  {
1324  std::ostringstream err_msg;
1325  err_msg << "The matrix pointed to by block(" << block_i << ","
1326  << block_j << ") is not built.\n";
1327  throw OomphLibError(err_msg.str(),
1330  }
1331 
1332  const LinearAlgebraDistribution* const tmp_block_dist_pt =
1333  tmp_block_pt->distribution_pt();
1334 
1335  const unsigned row_selected_block =
1336  selected_block[block_i][block_j].row_index();
1337 
1338  const LinearAlgebraDistribution* const another_tmp_block_dist_pt =
1339  block_distribution_pt(row_selected_block);
1340 
1341  if (*tmp_block_dist_pt != *another_tmp_block_dist_pt)
1342  {
1343  std::ostringstream err_msg;
1344  err_msg << "block_distribution_pt " << row_selected_block << "\n"
1345  << "does not match the distribution from the block_pt() "
1346  << " in selected_block[" << block_i << "][" << block_j
1347  << "].\n";
1348  throw OomphLibError(err_msg.str(),
1351  }
1352  }
1353  }
1354  }
1355 
1356  // Attempt a similar check for the column index. This is not as rigorous
1357  // since a CRDoubleMatrix does not have a distribution for the columns.
1358  // However, we can check if the ncol of the matrices in block_pt matches
1359  // those in the block_distribution_pt corresponding to the columns.
1360  // (I hope this makes sense... both the row and columns are permuted in
1361  // CRDoubleMatrixHelpers::concatenate_without_communication(...))
1362  //
1363  // The test for the row distributions checks if the nrow_local is correct.
1364  // We do not have the equivalent for columns.
1365  for (unsigned block_i = 0; block_i < para_selected_block_nrow; block_i++)
1366  {
1367  for (unsigned block_j = 0; block_j < para_selected_block_ncol;
1368  block_j++)
1369  {
1370  // Cache the block_pt
1371  const CRDoubleMatrix* tmp_block_pt =
1372  selected_block[block_i][block_j].replacement_block_pt();
1373 
1374  if (tmp_block_pt != 0)
1375  {
1376  const unsigned tmp_block_ncol = tmp_block_pt->ncol();
1377 
1378  const unsigned selected_block_col =
1379  selected_block[block_i][block_j].column_index();
1380 
1381  // YES, nrow, this is not incorrect.
1382  const unsigned another_tmp_block_ncol =
1383  block_distribution_pt(selected_block_col)->nrow();
1384 
1385  if (tmp_block_ncol != another_tmp_block_ncol)
1386  {
1387  std::ostringstream err_msg;
1388  err_msg << "block_pt in selected_block[" << block_i << "]["
1389  << block_j << "] "
1390  << "has ncol = " << tmp_block_ncol << ".\n"
1391  << "But the corresponding block_distribution_pt("
1392  << selected_block_col
1393  << ") has nrow = " << another_tmp_block_ncol << ").\n";
1394  throw OomphLibError(err_msg.str(),
1397  }
1398  }
1399  }
1400  }
1401 #endif
1402 
1403  // The return matrix.
1404  MATRIX output_matrix;
1405 
1406  // How many sub matrices are there in the row and column?
1407  const unsigned nblock_row = selected_block.nrow();
1408  const unsigned nblock_col = selected_block.ncol();
1409 
1410  // Get the row and col distributions, this is required for concatenation
1411  // without communication.
1412  Vector<LinearAlgebraDistribution*> row_dist_pt(nblock_row, 0);
1413  Vector<LinearAlgebraDistribution*> col_dist_pt(nblock_col, 0);
1414 
1415  // For the row distributions, use the first column of selected_block
1416  // Also, store the index of the block rows.
1417  Vector<unsigned> block_row_index(nblock_row, 0);
1418  for (unsigned row_i = 0; row_i < nblock_row; row_i++)
1419  {
1420  const unsigned selected_row_index =
1421  selected_block[row_i][0].row_index();
1422 
1423  row_dist_pt[row_i] = Block_distribution_pt[selected_row_index];
1424  block_row_index[row_i] = selected_row_index;
1425  }
1426 
1427  // For the col distributions, use the first row of selected_block
1428  Vector<unsigned> block_col_index(nblock_col, 0);
1429  for (unsigned col_i = 0; col_i < nblock_col; col_i++)
1430  {
1431  const unsigned selected_col_index =
1432  selected_block[0][col_i].column_index();
1433 
1434  col_dist_pt[col_i] = Block_distribution_pt[selected_col_index];
1435  block_col_index[col_i] = selected_col_index;
1436  }
1437 
1438  // Now build the output matrix. The output_matrix needs a distribution,
1439  // this distribution is a concatenation of the block rows. But because
1440  // concatenation of distributions requires communication, we try to
1441  // minimise this process by creating it once, then store a key to the
1442  // concatenated distribution. First check to see if the block row indices
1443  // is already a key in Auxiliary_block_distribution_pt, if it is in there,
1444  // we use the distribution it corresponds to. Otherwise, we create the
1445  // distribution and store it for possible further use.
1446  std::map<Vector<unsigned>, LinearAlgebraDistribution*>::const_iterator
1447  iter;
1448 
1449  iter = Auxiliary_block_distribution_pt.find(block_row_index);
1450 
1451  if (iter != Auxiliary_block_distribution_pt.end())
1452  {
1453  output_matrix.build(iter->second);
1454  }
1455  else
1456  {
1457  LinearAlgebraDistribution* tmp_dist_pt = new LinearAlgebraDistribution;
1459  *tmp_dist_pt);
1460  insert_auxiliary_block_distribution(block_row_index, tmp_dist_pt);
1461  output_matrix.build(tmp_dist_pt);
1462  }
1463 
1464  // Do the same for the column dist, since we might need it for the RHS
1465  // vector..
1466  iter = Auxiliary_block_distribution_pt.find(block_col_index);
1467  if (iter == Auxiliary_block_distribution_pt.end())
1468  {
1469  LinearAlgebraDistribution* tmp_dist_pt = new LinearAlgebraDistribution;
1471  *tmp_dist_pt);
1472  insert_auxiliary_block_distribution(block_col_index, tmp_dist_pt);
1473  }
1474 
1475  // Storage for the pointers to CRDoubleMatrices to concatenate.
1476  DenseMatrix<CRDoubleMatrix*> block_pt(nblock_row, nblock_col, 0);
1477 
1478  // Vector of Vectors of unsigns to indicate whether we have created
1479  // CRDoubleMatrices with new or not... so we can delete it later.
1480  // 0 - no new CRDoubleMatrix is created.
1481  // 1 - a new CRDoubleMatrix is created.
1482  // If we ever use C++11, remove this and use smart pointers.
1483  Vector<Vector<unsigned>> new_block(nblock_row,
1484  Vector<unsigned>(nblock_col, 0));
1485 
1486  // Get blocks if wanted.
1487  for (unsigned block_i = 0; block_i < nblock_row; block_i++)
1488  {
1489  for (unsigned block_j = 0; block_j < nblock_col; block_j++)
1490  {
1491  const bool block_wanted = selected_block[block_i][block_j].wanted();
1492 
1493  if (block_wanted)
1494  {
1495  CRDoubleMatrix* tmp_block_pt =
1496  selected_block[block_i][block_j].replacement_block_pt();
1497 
1498  if (tmp_block_pt == 0)
1499  {
1500  new_block[block_i][block_j] = 1;
1501 
1502  block_pt(block_i, block_j) = new CRDoubleMatrix;
1503 
1504  // temp variables for readability purposes.
1505  const unsigned tmp_block_i = block_row_index[block_i];
1506  const unsigned tmp_block_j = block_col_index[block_j];
1507 
1508  // Get the block.
1509  this->get_block(
1510  tmp_block_i, tmp_block_j, *block_pt(block_i, block_j));
1511  }
1512  else
1513  {
1514  block_pt(block_i, block_j) = tmp_block_pt;
1515  }
1516  }
1517  }
1518  }
1519 
1520  // Perform the concatenation.
1522  row_dist_pt, col_dist_pt, block_pt, output_matrix);
1523 
1524  // Delete any new CRDoubleMatrices we created.
1525  for (unsigned block_i = 0; block_i < nblock_row; block_i++)
1526  {
1527  for (unsigned block_j = 0; block_j < nblock_col; block_j++)
1528  {
1529  if (new_block[block_i][block_j])
1530  {
1531  delete block_pt(block_i, block_j);
1532  }
1533  }
1534  }
1535 
1536  return output_matrix;
1537  } // EOFunc get_concatenated_block(...)
const LinearAlgebraDistribution * block_distribution_pt(const unsigned &b) const
Access function to the block distributions (const version).
Definition: block_preconditioner.h:1903

References oomph::BlockPreconditioner< MATRIX >::Auxiliary_block_distribution_pt, oomph::BlockPreconditioner< MATRIX >::block_distribution_pt(), oomph::BlockPreconditioner< MATRIX >::Block_distribution_pt, oomph::CRDoubleMatrix::built(), oomph::LinearAlgebraDistributionHelpers::concatenate(), oomph::CRDoubleMatrixHelpers::concatenate_without_communication(), oomph::DistributableLinearAlgebraObject::distribution_pt(), oomph::BlockPreconditioner< MATRIX >::get_block(), oomph::BlockPreconditioner< MATRIX >::insert_auxiliary_block_distribution(), oomph::BlockPreconditioner< MATRIX >::nblock_types(), oomph::CRDoubleMatrix::ncol(), oomph::VectorMatrix< VALUE_TYPE >::ncol(), oomph::LinearAlgebraDistribution::nrow(), oomph::VectorMatrix< VALUE_TYPE >::nrow(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

◆ get_concatenated_block_vector()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::get_concatenated_block_vector ( const Vector< unsigned > &  block_vec_number,
const DoubleVector v,
DoubleVector w 
)

Takes the naturally ordered vector and extracts the blocks indicated by the block number (the values) in the Vector block_vec_number all at once, then concatenates them without communication. Here, the values in block_vec_number is the block number in the current preconditioner. This is a non-const function because distributions may be created and stored in Auxiliary_block_distribution_pt for future use.

2612  {
2613 #ifdef PARANOID
2614 
2615  // Check if v is built.
2616  if (!v.built())
2617  {
2618  std::ostringstream err_msg;
2619  err_msg << "The distribution of the global vector v must be setup.";
2620  throw OomphLibError(
2622  }
2623 
2624  // v must have the same distribution as the upper-most master block
2625  // preconditioner, since the upper-most master block preconditioner
2626  // should have the same distribution as the matrix pointed to
2627  // by matrix_pt().
2628  if (*(v.distribution_pt()) != *(this->master_distribution_pt()))
2629  {
2630  std::ostringstream err_msg;
2631  err_msg << "The distribution of the global vector v must match the "
2632  << " specified master_distribution_pt(). \n"
2633  << "i.e. Distribution_pt in the master preconditioner";
2634  throw OomphLibError(
2636  }
2637 
2638  // Check to see if there are more blocks defined in the block_vec_number
2639  // vector than the number of block types. This is not allowed.
2640  const unsigned para_nblock_types = nblock_types();
2641  const unsigned para_block_vec_number_size = block_vec_number.size();
2642  if (para_block_vec_number_size > para_nblock_types)
2643  {
2644  std::ostringstream err_msg;
2645  err_msg << "You have requested " << para_block_vec_number_size
2646  << " number of blocks, (block_vec_number.size() is "
2647  << para_block_vec_number_size << ").\n"
2648  << "But there are only " << para_nblock_types
2649  << " nblock_types.\n"
2650  << "Please make sure that block_vec_number is correctly sized.\n";
2651  throw OomphLibError(
2653  }
2654 
2655  // Check if any block numbers defined in block_vec_number is equal to or
2656  // greater than the number of block types.
2657  // E.g. if there are 5 block types, we can only have block numbers:
2658  // 0, 1, 2, 3 and 4.
2659  for (unsigned i = 0; i < para_block_vec_number_size; i++)
2660  {
2661  const unsigned para_required_block = block_vec_number[i];
2662  if (para_required_block >= para_nblock_types)
2663  {
2664  std::ostringstream err_msg;
2665  err_msg << "block_vec_number[" << i << "] is " << para_required_block
2666  << ".\n"
2667  << "But there are only " << para_nblock_types
2668  << " nblock_types.\n";
2669  throw OomphLibError(
2671  }
2672  }
2673 
2674  // Check that no block number is inserted twice.
2675  std::set<unsigned> para_set;
2676  for (unsigned b = 0; b < para_block_vec_number_size; b++)
2677  {
2678  std::pair<std::set<unsigned>::iterator, bool> para_set_ret;
2679  para_set_ret = para_set.insert(block_vec_number[b]);
2680 
2681  if (!para_set_ret.second)
2682  {
2683  std::ostringstream err_msg;
2684  err_msg << "Error: the block number " << block_vec_number[b]
2685  << " appears twice.\n";
2686  throw OomphLibError(
2688  }
2689  }
2690 #endif
2691 
2692  // Number of blocks to get.
2693  const unsigned n_block = block_vec_number.size();
2694 
2695  // Each block is made of dof types. We get the most fine grain dof types.
2696  // Most fine grain in the sense that these are the dof types that belongs
2697  // in this block before any coarsening of dof types has taken place.
2698  // The ordering of the dof types matters, this is handled properly when
2699  // creating the Block_to_dof_map_fine vector and must be respected here.
2700  // I.e. we cannot arbitrarily insert dof types (even if they are correct)
2701  // in the vector most_fine_grain_dof.
2702  Vector<unsigned> most_fine_grain_dof;
2703  for (unsigned b = 0; b < n_block; b++)
2704  {
2705  const unsigned mapped_b = block_vec_number[b];
2706  most_fine_grain_dof.insert(most_fine_grain_dof.end(),
2707  Block_to_dof_map_fine[mapped_b].begin(),
2708  Block_to_dof_map_fine[mapped_b].end());
2709  }
2710 
2711  // Get all the dof level vectors in one go.
2712  Vector<DoubleVector> dof_block_vector;
2713  internal_get_block_vectors(most_fine_grain_dof, v, dof_block_vector);
2714 
2715  // Next we need to build the output DoubleVector w with the correct
2716  // distribution: the concatenation of the distributions of all the
2717  // dof-level vectors. This is the same as the concatenation of the
2718  // distributions of the blocks within this preconditioner.
2719  //
2720  // So we first check if it exists already, if not, we create it and
2721  // store it for future use. We store it because concatenation of
2722  // distributions requires communication, so concatenation of
2723  // distributions on-the-fly should be avoided.
2724  std::map<Vector<unsigned>, LinearAlgebraDistribution*>::const_iterator iter;
2725 
2726  // Attempt to get an iterator pointing to the pair with the value
2727  // block_vec_number.
2728  iter = Auxiliary_block_distribution_pt.find(block_vec_number);
2729 
2730  if (iter != Auxiliary_block_distribution_pt.end())
2731  // If it exists, build w with the distribution pointed to
2732  // by pair::second.
2733  {
2734  w.build(iter->second);
2735  }
2736  else
2737  // Else, we need to create the distribution and store it in
2738  // Auxiliary_block_distribution_pt.
2739  {
2740  Vector<LinearAlgebraDistribution*> tmp_vec_dist_pt(n_block, 0);
2741  for (unsigned b = 0; b < n_block; b++)
2742  {
2743  tmp_vec_dist_pt[b] = Block_distribution_pt[block_vec_number[b]];
2744  }
2745 
2746  // Note that the distribution is created with new but not deleted here.
2747  // This is handled in the clean up functions.
2748  LinearAlgebraDistribution* tmp_dist_pt = new LinearAlgebraDistribution;
2750  *tmp_dist_pt);
2751 
2752  // Store the pair of Vector<unsigned> and LinearAlgebraDistribution*
2753  insert_auxiliary_block_distribution(block_vec_number, tmp_dist_pt);
2754 
2755  // Build w.
2756  w.build(tmp_dist_pt);
2757  }
2758 
2759  // Now concatenate all the dof level vectors into the vector w.
2761 
2762  } // get_concatenated_block_vector(...)

◆ get_dof_level_block() [1/2]

void oomph::BlockPreconditioner< CRDoubleMatrix >::get_dof_level_block ( const unsigned block_i,
const unsigned block_j,
CRDoubleMatrix output_block,
const bool ignore_replacement_block 
) const

Gets dof-level block (i,j). If Replacement_dof_block_pt(i,j) is not null, then the replacement block is returned via a deep copy.

Otherwise if this is the uppermost block preconditioner then it calls internal_get_block(i,j), else if it is a subsidiary block preconditioner, it will call it's master block preconditioners' get_dof_level_block function.

5676  {
5677 #ifdef PARANOID
5678  // the number of dof types.
5679  unsigned para_ndofs = ndof_types();
5680 
5681  // paranoid check that block i is in this block preconditioner
5682  if (block_i >= para_ndofs || block_j >= para_ndofs)
5683  {
5684  std::ostringstream err_msg;
5685  err_msg << "Requested dof block (" << block_i << "," << block_j
5686  << "), however this preconditioner has ndof_types() "
5687  << "= " << para_ndofs << std::endl;
5688  throw OomphLibError(
5690  }
5691 #endif
5692 
5693  CRDoubleMatrix* tmp_block_pt =
5694  Replacement_dof_block_pt.get(block_i, block_j);
5695 
5696  if ((tmp_block_pt == 0) || ignore_replacement_block)
5697  {
5698  // Getting the block from parent preconditioner
5699  const unsigned ndof_in_parent_i =
5700  Doftype_coarsen_map_coarse[block_i].size();
5701  const unsigned ndof_in_parent_j =
5702  Doftype_coarsen_map_coarse[block_j].size();
5703 
5704  if (ndof_in_parent_i == 1 && ndof_in_parent_j == 1)
5705  {
5706  unsigned parent_dof_i = Doftype_coarsen_map_coarse[block_i][0];
5707  unsigned parent_dof_j = Doftype_coarsen_map_coarse[block_j][0];
5708 
5710  {
5711  internal_get_block(parent_dof_i, parent_dof_j, output_block);
5712  }
5713  else
5714  {
5715  parent_dof_i = Doftype_in_master_preconditioner_coarse[parent_dof_i];
5716  parent_dof_j = Doftype_in_master_preconditioner_coarse[parent_dof_j];
5717 
5718  master_block_preconditioner_pt()->get_dof_level_block(
5719  parent_dof_i, parent_dof_j, output_block, ignore_replacement_block);
5720  }
5721  }
5722  else
5723  {
5724  DenseMatrix<CRDoubleMatrix*> tmp_blocks_pt(
5725  ndof_in_parent_i, ndof_in_parent_j, 0);
5726 
5727  Vector<Vector<unsigned>> new_block(
5728  ndof_in_parent_i, Vector<unsigned>(ndof_in_parent_j, 0));
5729 
5730  for (unsigned dof_i = 0; dof_i < ndof_in_parent_i; dof_i++)
5731  {
5732  unsigned parent_dof_i = Doftype_coarsen_map_coarse[block_i][dof_i];
5734  {
5735  parent_dof_i =
5737  }
5738 
5739  for (unsigned dof_j = 0; dof_j < ndof_in_parent_j; dof_j++)
5740  {
5741  unsigned parent_dof_j = Doftype_coarsen_map_coarse[block_j][dof_j];
5742 
5743  tmp_blocks_pt(dof_i, dof_j) = new CRDoubleMatrix;
5744 
5745  new_block[dof_i][dof_j] = 1;
5746 
5748  {
5750  parent_dof_i, parent_dof_j, *tmp_blocks_pt(dof_i, dof_j));
5751  }
5752  else
5753  {
5754  parent_dof_j =
5756 
5757  master_block_preconditioner_pt()->get_dof_level_block(
5758  parent_dof_i,
5759  parent_dof_j,
5760  *tmp_blocks_pt(dof_i, dof_j),
5761  ignore_replacement_block);
5762  }
5763  }
5764  }
5765 
5766  Vector<LinearAlgebraDistribution*> tmp_row_dist_pt(ndof_in_parent_i, 0);
5767 
5768  for (unsigned parent_dof_i = 0; parent_dof_i < ndof_in_parent_i;
5769  parent_dof_i++)
5770  {
5771  unsigned mapped_dof_i =
5772  Doftype_coarsen_map_coarse[block_i][parent_dof_i];
5773 
5775  {
5776  tmp_row_dist_pt[parent_dof_i] =
5777  Internal_block_distribution_pt[mapped_dof_i];
5778  }
5779  else
5780  {
5781  mapped_dof_i =
5783 
5784  tmp_row_dist_pt[parent_dof_i] =
5785  master_block_preconditioner_pt()->dof_block_distribution_pt(
5786  mapped_dof_i);
5787  }
5788  }
5789 
5790  Vector<LinearAlgebraDistribution*> tmp_col_dist_pt(ndof_in_parent_j, 0);
5791 
5792  for (unsigned parent_dof_j = 0; parent_dof_j < ndof_in_parent_j;
5793  parent_dof_j++)
5794  {
5795  unsigned mapped_dof_j =
5796  Doftype_coarsen_map_coarse[block_j][parent_dof_j];
5797 
5799  {
5800  tmp_col_dist_pt[parent_dof_j] =
5801  Internal_block_distribution_pt[mapped_dof_j];
5802  }
5803  else
5804  {
5805  mapped_dof_j =
5807  tmp_col_dist_pt[parent_dof_j] =
5808  master_block_preconditioner_pt()->dof_block_distribution_pt(
5809  mapped_dof_j);
5810  }
5811  }
5812 
5814  tmp_row_dist_pt, tmp_col_dist_pt, tmp_blocks_pt, output_block);
5815 
5816  for (unsigned dof_i = 0; dof_i < ndof_in_parent_i; dof_i++)
5817  {
5818  for (unsigned dof_j = 0; dof_j < ndof_in_parent_j; dof_j++)
5819  {
5820  if (new_block[dof_i][dof_j])
5821  {
5822  delete tmp_blocks_pt(dof_i, dof_j);
5823  }
5824  }
5825  }
5826  }
5827  }
5828  else
5829  {
5830  CRDoubleMatrixHelpers::deep_copy(tmp_block_pt, output_block);
5831  }
5832  }
void internal_get_block(const unsigned &i, const unsigned &j, MATRIX &output_block) const

References oomph::DoubleVectorHelpers::concatenate_without_communication(), oomph::CRDoubleMatrixHelpers::deep_copy(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

◆ get_dof_level_block() [2/2]

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::get_dof_level_block ( const unsigned i,
const unsigned j,
MATRIX &  output_block,
const bool ignore_replacement_block = false 
) const

Gets dof-level block (i,j). If Replacement_dof_block_pt(i,j) is not null, then the replacement block is returned via a deep copy.

Otherwise if this is the uppermost block preconditioner then it calls internal_get_block(i,j), else if it is a subsidiary block preconditioner, it will call it's master block preconditioners' get_dof_level_block function.

Referenced by oomph::BlockPreconditioner< MATRIX >::get_block().

◆ get_fine_grain_dof_types_in()

template<typename MATRIX >
Vector<unsigned> oomph::BlockPreconditioner< MATRIX >::get_fine_grain_dof_types_in ( const unsigned i) const
inline

Returns the most fine grain dof types in a (possibly coarsened) dof type.

2342  {
2343 #ifdef PARANOID
2344  const unsigned n_dof_types = ndof_types();
2345 
2346  if (i >= n_dof_types)
2347  {
2348  std::ostringstream err_msg;
2349  err_msg << "Trying to get the most fine grain dof types in dof type "
2350  << i << ",\nbut there are only " << n_dof_types
2351  << " number of dof types.\n";
2352  throw OomphLibError(
2354  }
2355 #endif
2356  return Doftype_coarsen_map_fine[i];
2357  }

References oomph::BlockPreconditioner< MATRIX >::Doftype_coarsen_map_fine, i, oomph::BlockPreconditioner< MATRIX >::ndof_types(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

◆ get_index_of_value()

template<typename MATRIX >
template<typename myType >
int oomph::BlockPreconditioner< MATRIX >::get_index_of_value ( const Vector< myType > &  vec,
const myType  val,
const bool  sorted = false 
) const
inline

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.

2830  {
2831  if (sorted)
2832  {
2833  typename Vector<myType>::const_iterator low =
2834  std::lower_bound(vec.begin(), vec.end(), val);
2835 
2836  return (low == vec.end() || *low != val) ? -1 : (low - vec.begin());
2837  }
2838  else
2839  {
2840  int pos = std::find(vec.begin(), vec.end(), val) - vec.begin();
2841  return (pos < int(vec.size()) && pos >= 0) ? pos : -1;
2842  }
2843  }
val
Definition: calibrate.py:119

References calibrate::val.

◆ index_in_block()

template<typename MATRIX >
int oomph::BlockPreconditioner< MATRIX >::index_in_block ( const unsigned i_dof) const
inline

Given a global dof number, returns the index in the block it belongs to. This is the overall index, not local block (in parallel).

1851  {
1852  // the dof block number
1853  int internal_dof_block_number = this->internal_dof_number(i_dof);
1854 
1855  if (internal_dof_block_number >= 0)
1856  {
1857  // the external block number
1858  unsigned ex_blk_number = this->block_number(i_dof);
1859 
1860  int internal_index_in_dof = this->internal_index_in_dof(i_dof);
1861 
1862  // find the processor which this global index in block belongs to.
1863  unsigned block_proc =
1864  internal_block_distribution_pt(internal_dof_block_number)
1866 
1867  // Add up all of the first rows.
1868  const unsigned ndof_in_block =
1869  Block_to_dof_map_fine[ex_blk_number].size();
1870 
1871  unsigned index = 0;
1872  for (unsigned dof_i = 0; dof_i < ndof_in_block; dof_i++)
1873  {
1875  Block_to_dof_map_fine[ex_blk_number][dof_i])
1876  ->first_row(block_proc);
1877  }
1878 
1879  // Now add up all the nrow_local up to this dof block.
1880  unsigned j = 0;
1881 
1882  while (int(Block_to_dof_map_fine[ex_blk_number][j]) !=
1883  internal_dof_block_number)
1884  {
1886  Block_to_dof_map_fine[ex_blk_number][j])
1887  ->nrow_local(block_proc);
1888  j++;
1889  }
1890 
1891  // Now add the index of this block...
1892  index += (internal_index_in_dof -
1893  internal_block_distribution_pt(internal_dof_block_number)
1894  ->first_row(block_proc));
1895 
1896  return index;
1897  }
1898 
1899  return -1;
1900  }
const LinearAlgebraDistribution * internal_block_distribution_pt(const unsigned &b) const
Access function to the internal block distributions.
Definition: block_preconditioner.h:2734
unsigned rank_of_global_row(const unsigned i) const
return the processor rank of the global row number i
Definition: linear_algebra_distribution.h:387

References oomph::BlockPreconditioner< MATRIX >::block_number(), oomph::BlockPreconditioner< MATRIX >::Block_to_dof_map_fine, oomph::LinearAlgebraDistribution::first_row(), oomph::BlockPreconditioner< MATRIX >::internal_block_distribution_pt(), oomph::BlockPreconditioner< MATRIX >::internal_dof_number(), oomph::BlockPreconditioner< MATRIX >::internal_index_in_dof(), j, oomph::LinearAlgebraDistribution::nrow_local(), and oomph::LinearAlgebraDistribution::rank_of_global_row().

◆ insert_auxiliary_block_distribution()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::insert_auxiliary_block_distribution ( const Vector< unsigned > &  block_vec_number,
LinearAlgebraDistribution dist_pt 
)
inline

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.

2772  {
2773 #ifdef PARANOID
2774  const unsigned max_block_number =
2775  *std::max_element(block_vec_number.begin(), block_vec_number.end());
2776 
2777  const unsigned nblocks = nblock_types();
2778  if (max_block_number >= nblocks)
2779  {
2780  std::ostringstream err_msg;
2781  err_msg << "Cannot insert into Auxiliary_block_distribution_pt\n"
2782  << "because " << max_block_number << " is equal to or \n"
2783  << "greater than " << nblocks << ".\n";
2784  throw OomphLibError(
2786  }
2787 
2788  // Now check if the pair already exists in
2789  // Auxiliary_block_distribution_pt. This is a stricter test and can be
2790  // removed if required.
2791 
2792  // Attempt to get an iterator pointing to the pair with the value
2793  // block_vec_number.
2794  std::map<Vector<unsigned>, LinearAlgebraDistribution*>::const_iterator
2795  iter = Auxiliary_block_distribution_pt.find(block_vec_number);
2796 
2797  if (iter != Auxiliary_block_distribution_pt.end())
2798  // If it exists, we throw an error
2799  {
2800  std::ostringstream err_msg;
2801  err_msg << "Cannot insert into Auxiliary_block_distribution_pt\n"
2802  << "because the first in the pair already exists.\n";
2803  throw OomphLibError(
2805  }
2806 #endif
2807 
2809  std::make_pair(block_vec_number, dist_pt));
2810  } // insert_auxiliary_block_distribution(...)

References oomph::BlockPreconditioner< MATRIX >::Auxiliary_block_distribution_pt, oomph::BlockPreconditioner< MATRIX >::nblock_types(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Referenced by oomph::BlockPreconditioner< MATRIX >::get_concatenated_block(), and oomph::BlockPreconditioner< MATRIX >::setup_matrix_vector_product().

◆ internal_block_dimension()

template<typename MATRIX >
unsigned oomph::BlockPreconditioner< MATRIX >::internal_block_dimension ( const unsigned b) const
inlineprotected

Return the number of degrees of freedom in block b. Note that if this preconditioner acts as a subsidiary preconditioner then b refers to the block number in the subsidiary preconditioner not the master block preconditioner.

3166  {
3167 #ifdef PARANOID
3168  const unsigned i_nblock_types = internal_nblock_types();
3169  if (b >= i_nblock_types)
3170  {
3171  std::ostringstream err_msg;
3172  err_msg << "Trying to get internal block dimension for \n"
3173  << "internal block " << b << ".\n"
3174  << "But there are only " << i_nblock_types
3175  << " internal dof types.\n";
3176  throw OomphLibError(
3178  }
3179 #endif
3180  return Internal_block_distribution_pt[b]->nrow();
3181  }

References b, oomph::BlockPreconditioner< MATRIX >::Internal_block_distribution_pt, oomph::BlockPreconditioner< MATRIX >::internal_nblock_types(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

◆ internal_block_distribution_pt()

template<typename MATRIX >
const LinearAlgebraDistribution* oomph::BlockPreconditioner< MATRIX >::internal_block_distribution_pt ( const unsigned b) const
inline

Access function to the internal block distributions.

2736  {
2737 #ifdef PARANOID
2738  if (Internal_block_distribution_pt.size() == 0)
2739  {
2740  std::ostringstream error_msg;
2741  error_msg << "Internal block distributions are not set up.\n"
2742  << "Have you called block_setup(...)?\n"
2743  << std::endl;
2744  throw OomphLibError(
2746  }
2747  if (b > internal_nblock_types())
2748  {
2749  std::ostringstream error_msg;
2750  error_msg << "You requested the distribution for the internal block "
2751  << b << ".\n"
2752  << "But there are only " << internal_nblock_types()
2753  << " block types.\n"
2754  << std::endl;
2755  throw OomphLibError(
2757  }
2758 #endif
2760  } // EOFunc internal_block_distribution_pt(...)

References b, oomph::BlockPreconditioner< MATRIX >::Internal_block_distribution_pt, oomph::BlockPreconditioner< MATRIX >::internal_nblock_types(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Referenced by oomph::BlockPreconditioner< MATRIX >::index_in_block().

◆ internal_block_number()

template<typename MATRIX >
int oomph::BlockPreconditioner< MATRIX >::internal_block_number ( const unsigned i_dof) const
inline

Return the block number corresponding to a global index i_dof. This returns the block number corresponding to the internal blocks. What this means is that this returns the most fine grain dof-block number which this global index i_dof corresponds to. Since the writer of the preconditioner does not need to care about the internal block types, this function should not be used and thus moved to private. This function should not be removed since it is still used deep within the inner workings of the block preconditioning framework.

2690  {
2691  int dn = internal_dof_number(i_dof);
2692  if (dn == -1)
2693  {
2694  return dn;
2695  }
2696  else
2697  {
2699  }
2700  } // EOFunc internal_block_number(...)

References oomph::BlockPreconditioner< MATRIX >::Dof_number_to_block_number_lookup, and oomph::BlockPreconditioner< MATRIX >::internal_dof_number().

Referenced by oomph::BlockPreconditioner< MATRIX >::block_number(), and oomph::BlockPreconditioner< MATRIX >::internal_index_in_block().

◆ internal_dof_block_dimension()

template<typename MATRIX >
unsigned oomph::BlockPreconditioner< MATRIX >::internal_dof_block_dimension ( const unsigned i) const
inlineprotected

Return the size of the dof "block" i, i.e. how many degrees of freedom are associated with it. Note that if this preconditioner acts as a subsidiary preconditioner, then i refers to the block number in the subsidiary preconditioner not the master block preconditioner

3188  {
3189 #ifdef PARANOID
3190  const unsigned i_n_dof_types = internal_ndof_types();
3191  if (i >= i_n_dof_types)
3192  {
3193  std::ostringstream err_msg;
3194  err_msg << "Trying to get internal dof block dimension for \n"
3195  << "internal dof block " << i << ".\n"
3196  << "But there are only " << i_n_dof_types
3197  << " internal dof types.\n";
3198  throw OomphLibError(
3200  }
3201 #endif
3202  // I don't understand the difference between this function and
3203  // block_dimension(...) but I'm not going to mess with it... David
3204 
3206  {
3207  return Dof_dimension[i];
3208  }
3209  else
3210  {
3211  unsigned master_i = internal_master_dof_number(i);
3212  return Master_block_preconditioner_pt->internal_dof_block_dimension(
3213  master_i);
3214  }
3215  }

References oomph::BlockPreconditioner< MATRIX >::Dof_dimension, i, oomph::BlockPreconditioner< MATRIX >::internal_master_dof_number(), oomph::BlockPreconditioner< MATRIX >::internal_ndof_types(), oomph::BlockPreconditioner< MATRIX >::is_master_block_preconditioner(), oomph::BlockPreconditioner< MATRIX >::Master_block_preconditioner_pt, OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Referenced by oomph::BlockPreconditioner< MATRIX >::internal_index_in_block().

◆ internal_dof_number()

template<typename MATRIX >
int oomph::BlockPreconditioner< MATRIX >::internal_dof_number ( const unsigned i_dof) const
inlineprotected

Return the number of the block associated with global unknown i_dof. If this preconditioner is a subsidiary block preconditioner then the block number in the subsidiary block preconditioner is returned. If a particular global DOF is not associated with this preconditioner then -1 is returned

3046  {
3048  {
3049 #ifdef OOMPH_HAS_MPI
3050  unsigned first_row = this->distribution_pt()->first_row();
3051  unsigned nrow_local = this->distribution_pt()->nrow_local();
3052  unsigned last_row = first_row + nrow_local - 1;
3053  if (i_dof >= first_row && i_dof <= last_row)
3054  {
3055  return static_cast<int>(Dof_number_dense[i_dof - first_row]);
3056  }
3057  else
3058  {
3059  // int index = this->get_index_of_element(Global_index_sparse,i_dof);
3060  int index =
3061  get_index_of_value<unsigned>(Global_index_sparse, i_dof, true);
3062  if (index >= 0)
3063  {
3064  return Dof_number_sparse[index];
3065  }
3066  }
3067  // if we here we couldn't find the i_dof
3068 #ifdef PARANOID
3069  unsigned my_rank = comm_pt()->my_rank();
3070  std::ostringstream error_message;
3071  error_message << "Proc " << my_rank
3072  << ": Requested internal_dof_number(...) for global DOF "
3073  << i_dof << "\n"
3074  << "cannot be found.\n";
3075  throw OomphLibError(error_message.str(),
3078 #endif
3079 #else
3080  return static_cast<int>(Dof_number_dense[i_dof]);
3081 #endif
3082  }
3083  // else this preconditioner is a subsidiary one, and its Block_number
3084  // lookup schemes etc haven't been set up
3085  else
3086  {
3087  // Block number in master prec
3088  unsigned blk_num =
3089  Master_block_preconditioner_pt->internal_dof_number(i_dof);
3090 
3091  // Search through the Block_number_in_master_preconditioner for master
3092  // block blk_num and return the block number in this preconditioner
3093  for (unsigned i = 0; i < this->internal_ndof_types(); i++)
3094  {
3095  if (Doftype_in_master_preconditioner_fine[i] == blk_num)
3096  {
3097  return static_cast<int>(i);
3098  }
3099  }
3100  // if the master block preconditioner number is not found return -1
3101  return -1;
3102  }
3103 
3104  // Shouldn't get here
3105  throw OomphLibError(
3106  "Never get here\n", OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3107  // Dummy return
3108  return -1;
3109  }

References oomph::Preconditioner::comm_pt(), oomph::DistributableLinearAlgebraObject::distribution_pt(), oomph::BlockPreconditioner< MATRIX >::Dof_number_dense, oomph::BlockPreconditioner< MATRIX >::Doftype_in_master_preconditioner_fine, oomph::LinearAlgebraDistribution::first_row(), oomph::DistributableLinearAlgebraObject::first_row(), i, oomph::BlockPreconditioner< MATRIX >::internal_ndof_types(), oomph::BlockPreconditioner< MATRIX >::is_master_block_preconditioner(), oomph::BlockPreconditioner< MATRIX >::Master_block_preconditioner_pt, oomph::OomphCommunicator::my_rank(), oomph::LinearAlgebraDistribution::nrow_local(), oomph::DistributableLinearAlgebraObject::nrow_local(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Referenced by oomph::BlockPreconditioner< MATRIX >::index_in_block(), oomph::BlockPreconditioner< MATRIX >::internal_block_number(), and oomph::BlockPreconditioner< MATRIX >::internal_index_in_block().

◆ internal_get_block() [1/2]

void oomph::BlockPreconditioner< CRDoubleMatrix >::internal_get_block ( const unsigned block_i,
const unsigned block_j,
CRDoubleMatrix output_block 
) const

Gets block (i,j) from the matrix pointed to by Matrix_pt and returns it in output_block. This is associated with the internal blocks. Please use the other get_block(...) function.

5105  {
5106 #ifdef PARANOID
5107  // the number of blocks
5108  const unsigned n_blocks = this->internal_nblock_types();
5109 
5110  // paranoid check that block i is in this block preconditioner
5111  if (block_i >= n_blocks || block_j >= n_blocks)
5112  {
5113  std::ostringstream error_message;
5114  error_message
5115  << "Requested block (" << block_i << "," << block_j
5116  << "), however this preconditioner has internal_nblock_types() "
5117  << "= " << internal_nblock_types() << std::endl;
5118  throw OomphLibError(
5119  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
5120  }
5121 
5122  // Check that the matrix is the same as that of the master
5124  {
5126  {
5127  std::string err = "Master and subs should have same matrix.";
5128  throw OomphLibError(
5130  }
5131  }
5132 #endif
5133 
5134  // Cast the pointer
5135  CRDoubleMatrix* cr_matrix_pt = dynamic_cast<CRDoubleMatrix*>(matrix_pt());
5136 
5137  // if + only one processor
5138  // + more than one processor but matrix_pt is not distributed
5139  // then use the serial get_block method
5140  if (cr_matrix_pt->distribution_pt()->communicator_pt()->nproc() == 1 ||
5141  !cr_matrix_pt->distribution_pt()->distributed())
5142  {
5143  // pointers for the jacobian matrix is compressed row sparse format
5144  int* j_row_start;
5145  int* j_column_index;
5146  double* j_value;
5147 
5148  // sets pointers to jacobian matrix
5149  j_row_start = cr_matrix_pt->row_start();
5150  j_column_index = cr_matrix_pt->column_index();
5151  j_value = cr_matrix_pt->value();
5152 
5153  // get the block dimensions
5154  unsigned block_nrow = this->internal_block_dimension(block_i);
5155  unsigned block_ncol = this->internal_block_dimension(block_j);
5156 
5157  // allocate temporary storage for the component vectors of block (i,j)
5158  // temp_ptr is used to point to an element in each column - required as
5159  // cannot assume that order of block's rows in jacobian and the block
5160  // matrix will be the same
5161  int* temp_row_start = new int[block_nrow + 1];
5162  for (unsigned i = 0; i <= block_nrow; i++)
5163  {
5164  temp_row_start[i] = 0;
5165  }
5166  Vector<int> temp_ptr(block_nrow + 1);
5167  int block_nnz = 0;
5168 
5169  // get number of rows in source matrix
5170  unsigned master_nrow = this->master_nrow();
5171 
5172  // determine how many non zeros there are in the block (i,j)
5173  // also determines how many non zeros are stored in each row or column -
5174  // stored in temp_ptr temporarily
5175  for (unsigned k = 0; k < master_nrow; k++)
5176  {
5177  if (internal_block_number(k) == static_cast<int>(block_i))
5178  {
5179  for (int l = j_row_start[k]; l < j_row_start[k + 1]; l++)
5180  {
5181  if (internal_block_number(j_column_index[l]) ==
5182  static_cast<int>(block_j))
5183  {
5184  block_nnz++;
5185  temp_ptr[internal_index_in_block(k) + 1]++;
5186  }
5187  }
5188  }
5189  }
5190 
5191  // if the matrix is not empty
5192  int* temp_column_index = new int[block_nnz];
5193  double* temp_value = new double[block_nnz];
5194  if (block_nnz > 0)
5195  {
5196  // uses number of elements in each column of block to determine values
5197  // for the block column start (temp_row_start)
5198  temp_row_start[0] = 0;
5199  for (unsigned k = 1; k <= block_nrow; k++)
5200  {
5201  temp_row_start[k] = temp_row_start[k - 1] + temp_ptr[k];
5202  temp_ptr[k] = temp_row_start[k];
5203  }
5204 
5205  // copies the relevant elements of the jacobian to the correct entries
5206  // of the block matrix
5207  for (unsigned k = 0; k < master_nrow; k++)
5208  {
5209  if (internal_block_number(k) == static_cast<int>(block_i))
5210  {
5211  for (int l = j_row_start[k]; l < j_row_start[k + 1]; l++)
5212  {
5213  if (internal_block_number(j_column_index[l]) ==
5214  static_cast<int>(block_j))
5215  {
5216  int kk = temp_ptr[internal_index_in_block(k)]++;
5217  temp_value[kk] = j_value[l];
5218  temp_column_index[kk] =
5219  internal_index_in_block(j_column_index[l]);
5220  }
5221  }
5222  }
5223  }
5224  }
5225 
5226 
5227  // Fill in the compressed row matrix ??ds Note: I kept the calls to
5228  // build as close as I could to before (had to replace new(dist) with
5229  // .build(dist) ).
5230  output_block.build(Internal_block_distribution_pt[block_i]);
5231  output_block.build_without_copy(
5232  block_ncol, block_nnz, temp_value, temp_column_index, temp_row_start);
5233 
5234 #ifdef PARANOID
5235  // checks to see if block matrix has been set up correctly
5236  // block_matrix_test(matrix_pt,block_i,block_j,block_pt);
5238  {
5239  // checks to see if block matrix has been set up correctly
5240  block_matrix_test(block_i, block_j, &output_block);
5241  }
5242 #endif
5243  }
5244 
5245 
5246  // otherwise we are dealing with a distributed matrix
5247  else
5248  {
5249 #ifdef OOMPH_HAS_MPI
5250  // number of processors
5251  unsigned nproc = this->distribution_pt()->communicator_pt()->nproc();
5252 
5253  // my rank
5254  unsigned my_rank = this->distribution_pt()->communicator_pt()->my_rank();
5255 
5256  // sets pointers to jacobian matrix
5257  int* j_row_start = cr_matrix_pt->row_start();
5258  int* j_column_index = cr_matrix_pt->column_index();
5259  double* j_value = cr_matrix_pt->value();
5260 
5261  // number of non zeros in each row to be sent
5262  Vector<int*> nnz_send(nproc, 0);
5263 
5264  // number of non zeros in each row to be received
5265  Vector<int*> nnz_recv(nproc, 0);
5266 
5267  // storage for data to be sent
5268  Vector<int*> column_index_for_proc(nproc, 0);
5269  Vector<double*> values_for_proc(nproc, 0);
5270 
5271  // number of non zeros to be sent to each processor
5272  Vector<unsigned> total_nnz_send(nproc, 0);
5273 
5274  // number of rows of the block matrix on this processor
5275  unsigned nrow_local =
5276  Internal_block_distribution_pt[block_i]->nrow_local();
5277 
5278  // resize the nnz storage and compute nnz_send
5279  // and send and recv the nnz
5280  Vector<MPI_Request> send_req;
5281  Vector<MPI_Request> recv1_req;
5282  for (unsigned p = 0; p < nproc; p++)
5283  {
5284  int nrow_send = Nrows_to_send_for_get_block(block_i, p);
5285  int nrow_recv = Nrows_to_recv_for_get_block(block_i, p);
5286 
5287  // assemble nnz recv
5288  nnz_recv[p] = new int[nrow_recv];
5289 
5290  // assemble the storage to send
5291  if (nrow_send > 0 && p != my_rank)
5292  {
5293  nnz_send[p] = new int[nrow_send];
5294  }
5295 
5296  // compute the number of nnzs in each row and the total number
5297  // of nnzs
5298  for (int i = 0; i < nrow_send; i++)
5299  {
5300  unsigned row = Rows_to_send_for_get_block(block_i, p)[i];
5301  int c = 0;
5302  for (int r = j_row_start[row]; r < j_row_start[row + 1]; r++)
5303  {
5304  if (internal_block_number(j_column_index[r]) == int(block_j))
5305  {
5306  c++;
5307  }
5308  }
5309  if (p != my_rank)
5310  {
5311  nnz_send[p][i] = c;
5312  }
5313  else
5314  {
5315  nnz_recv[p][i] = c;
5316  }
5317  total_nnz_send[p] += c;
5318  }
5319 
5320  // send
5321  if (p != my_rank)
5322  {
5323  if (nrow_send)
5324  {
5325  MPI_Request req;
5326  MPI_Isend(nnz_send[p],
5327  nrow_send,
5328  MPI_INT,
5329  p,
5330  0,
5331  this->distribution_pt()->communicator_pt()->mpi_comm(),
5332  &req);
5333  send_req.push_back(req);
5334  }
5335 
5336  // recv
5337  if (nrow_recv)
5338  {
5339  MPI_Request req;
5340  MPI_Irecv(nnz_recv[p],
5341  nrow_recv,
5342  MPI_INT,
5343  p,
5344  0,
5345  this->distribution_pt()->communicator_pt()->mpi_comm(),
5346  &req);
5347  recv1_req.push_back(req);
5348  }
5349  }
5350  }
5351 
5352  // next assemble the values and row_start data to be sent for each
5353  // processor
5354  for (unsigned p = 0; p < nproc; p++)
5355  {
5356  int nrow_send = Nrows_to_send_for_get_block(block_i, p);
5357 
5358  // assemble the storage for the values and column indices to be sent
5359  if (p != my_rank)
5360  {
5361  if (total_nnz_send[p] > 0)
5362  {
5363  values_for_proc[p] = new double[total_nnz_send[p]];
5364  column_index_for_proc[p] = new int[total_nnz_send[p]];
5365 
5366  // copy the values and column indices to the storage
5367  unsigned ptr = 0;
5368  for (int i = 0; i < nrow_send; i++)
5369  {
5370  unsigned row = Rows_to_send_for_get_block(block_i, p)[i];
5371  for (int r = j_row_start[row]; r < j_row_start[row + 1]; r++)
5372  {
5373  if (internal_block_number(j_column_index[r]) == int(block_j))
5374  {
5375  values_for_proc[p][ptr] = j_value[r];
5376  column_index_for_proc[p][ptr] =
5377  internal_index_in_block(j_column_index[r]);
5378  ptr++;
5379  }
5380  }
5381  }
5382 
5383  // create the datatypes
5384  MPI_Datatype types[2];
5385  MPI_Type_contiguous(total_nnz_send[p], MPI_DOUBLE, &types[0]);
5386  MPI_Type_commit(&types[0]);
5387  MPI_Type_contiguous(total_nnz_send[p], MPI_INT, &types[1]);
5388  MPI_Type_commit(&types[1]);
5389 
5390  // get the start address of the vectors
5391  MPI_Aint displacement[2];
5392  MPI_Get_address(values_for_proc[p], &displacement[0]);
5393  MPI_Get_address(column_index_for_proc[p], &displacement[1]);
5394 
5395  // compute the displacements
5396  displacement[1] -= displacement[0];
5397  displacement[0] -= displacement[0];
5398 
5399  // compute the block lengths
5400  int length[2];
5401  length[0] = length[1] = 1;
5402 
5403  // build the struct data type
5404  MPI_Datatype final_type;
5405  MPI_Type_create_struct(2, length, displacement, types, &final_type);
5406  MPI_Type_commit(&final_type);
5407  MPI_Type_free(&types[0]);
5408  MPI_Type_free(&types[1]);
5409 
5410  // and send
5411  MPI_Request req;
5412  MPI_Isend(values_for_proc[p],
5413  1,
5414  final_type,
5415  p,
5416  1,
5417  this->distribution_pt()->communicator_pt()->mpi_comm(),
5418  &req);
5419  send_req.push_back(req);
5420  MPI_Type_free(&final_type);
5421  }
5422  }
5423  }
5424 
5425  // wait for the recv to complete (the row_start recv which actually
5426  // contains the number of nnzs in each row)
5427  int c_recv = recv1_req.size();
5428  if (c_recv != 0)
5429  {
5430  MPI_Waitall(c_recv, &recv1_req[0], MPI_STATUS_IGNORE);
5431  }
5432 
5433  // compute the total number of nnzs to be received
5434  Vector<int> total_nnz_recv_from_proc(nproc);
5435  int local_block_nnz = 0;
5436  for (unsigned p = 0; p < nproc; p++)
5437  {
5438  // compute the total nnzs
5439  for (unsigned i = 0; i < Nrows_to_recv_for_get_block(block_i, p); i++)
5440  {
5441  total_nnz_recv_from_proc[p] += nnz_recv[p][i];
5442  }
5443  local_block_nnz += total_nnz_recv_from_proc[p];
5444  }
5445 
5446  // compute the offset for each block of nnzs (a matrix row) in the
5447  // values_recv and column_index_recv vectors
5448 
5449  // fisrt determine how many blocks of rows are to be recv
5450  Vector<int> n_recv_block(nproc, 0);
5451  for (unsigned p = 0; p < nproc; p++)
5452  {
5453  if (Nrows_to_recv_for_get_block(block_i, p) > 0)
5454  {
5455  n_recv_block[p] = 1;
5456  }
5457  for (unsigned i = 1; i < Nrows_to_recv_for_get_block(block_i, p); i++)
5458  {
5459  if (Rows_to_recv_for_get_block(block_i, p)[i] !=
5460  Rows_to_recv_for_get_block(block_i, p)[i - 1] + 1)
5461  {
5462  n_recv_block[p]++;
5463  }
5464  }
5465  }
5466 
5467  // next assemble row start recv
5468  int* row_start_recv = new int[nrow_local + 1];
5469  for (unsigned i = 0; i <= nrow_local; i++)
5470  {
5471  row_start_recv[i] = 0;
5472  }
5473  for (unsigned p = 0; p < nproc; p++)
5474  {
5475  for (unsigned i = 0; i < Nrows_to_recv_for_get_block(block_i, p); i++)
5476  {
5477  row_start_recv[Rows_to_recv_for_get_block(block_i, p)[i]] =
5478  nnz_recv[p][i];
5479  }
5480  }
5481  int g = row_start_recv[0];
5482  row_start_recv[0] = 0;
5483  for (unsigned i = 1; i < nrow_local; i++)
5484  {
5485  int temp_g = g;
5486  g = row_start_recv[i];
5487  row_start_recv[i] = row_start_recv[i - 1] + temp_g;
5488  }
5489  row_start_recv[nrow_local] = row_start_recv[nrow_local - 1] + g;
5490 
5491  // next assemble the offset and the number of nzs in each recv block
5492  Vector<int*> offset_recv_block(nproc, 0);
5493  Vector<int*> nnz_recv_block(nproc, 0);
5494  for (unsigned p = 0; p < nproc; p++)
5495  {
5496  if (Nrows_to_recv_for_get_block(block_i, p) > 0)
5497  {
5498  offset_recv_block[p] = new int[n_recv_block[p]];
5499  offset_recv_block[p][0] = 0;
5500  nnz_recv_block[p] = new int[n_recv_block[p]];
5501  for (int i = 0; i < n_recv_block[p]; i++)
5502  {
5503  nnz_recv_block[p][i] = 0;
5504  }
5505  unsigned ptr = 0;
5506  nnz_recv_block[p][ptr] += nnz_recv[p][0];
5507  offset_recv_block[p][0] =
5508  row_start_recv[Rows_to_recv_for_get_block(block_i, p)[0]];
5509  for (unsigned i = 1; i < Nrows_to_recv_for_get_block(block_i, p); i++)
5510  {
5511  if (Rows_to_recv_for_get_block(block_i, p)[i] !=
5512  Rows_to_recv_for_get_block(block_i, p)[i - 1] + 1)
5513  {
5514  ptr++;
5515  offset_recv_block[p][ptr] =
5516  row_start_recv[Rows_to_recv_for_get_block(block_i, p)[i]];
5517  }
5518  nnz_recv_block[p][ptr] += nnz_recv[p][i];
5519  }
5520  }
5521  delete[] nnz_recv[p];
5522  }
5523 
5524  // post the receives
5525  int* column_index_recv = new int[local_block_nnz];
5526  double* values_recv = new double[local_block_nnz];
5527  Vector<MPI_Request> recv2_req;
5528  for (unsigned p = 0; p < nproc; p++)
5529  {
5530  if (p != my_rank)
5531  {
5532  if (total_nnz_recv_from_proc[p] != 0)
5533  {
5534  // create the datatypes
5535  MPI_Datatype types[2];
5536  MPI_Type_indexed(n_recv_block[p],
5537  nnz_recv_block[p],
5538  offset_recv_block[p],
5539  MPI_DOUBLE,
5540  &types[0]);
5541  MPI_Type_commit(&types[0]);
5542  MPI_Type_indexed(n_recv_block[p],
5543  nnz_recv_block[p],
5544  offset_recv_block[p],
5545  MPI_INT,
5546  &types[1]);
5547  MPI_Type_commit(&types[1]);
5548 
5549  // compute the displacements
5550  MPI_Aint displacements[2];
5551  MPI_Get_address(values_recv, &displacements[0]);
5552  MPI_Get_address(column_index_recv, &displacements[1]);
5553  displacements[1] -= displacements[0];
5554  displacements[0] -= displacements[0];
5555 
5556  // compute the block lengths
5557  int length[2];
5558  length[0] = length[1] = 1;
5559 
5560  // create the final datatype
5561  MPI_Datatype final_type;
5562  MPI_Type_create_struct(
5563  2, length, displacements, types, &final_type);
5564  MPI_Type_commit(&final_type);
5565  MPI_Type_free(&types[0]);
5566  MPI_Type_free(&types[1]);
5567 
5568  // and the recv
5569  MPI_Request req;
5570  MPI_Irecv(values_recv,
5571  1,
5572  final_type,
5573  p,
5574  1,
5575  this->distribution_pt()->communicator_pt()->mpi_comm(),
5576  &req);
5577  recv2_req.push_back(req);
5578  MPI_Type_free(&final_type);
5579  }
5580  }
5581  else
5582  {
5583  // next send the values and column indices to self
5584  unsigned block_ptr = 0;
5585  unsigned counter = 0;
5586  int nrow_send = Nrows_to_send_for_get_block(block_i, my_rank);
5587  if (nrow_send > 0)
5588  {
5589  unsigned offset = offset_recv_block[my_rank][0];
5590  for (int i = 0; i < nrow_send; i++)
5591  {
5592  if (i > 0)
5593  {
5594  if (Rows_to_recv_for_get_block(block_i, p)[i] !=
5595  Rows_to_recv_for_get_block(block_i, p)[i - 1] + 1)
5596  {
5597  counter = 0;
5598  block_ptr++;
5599  offset = offset_recv_block[my_rank][block_ptr];
5600  }
5601  }
5602  unsigned row = Rows_to_send_for_get_block(block_i, my_rank)[i];
5603  for (int r = j_row_start[row]; r < j_row_start[row + 1]; r++)
5604  {
5605  if (internal_block_number(j_column_index[r]) == int(block_j))
5606  {
5607  values_recv[offset + counter] = j_value[r];
5608  column_index_recv[offset + counter] =
5609  internal_index_in_block(j_column_index[r]);
5610  counter++;
5611  }
5612  }
5613  }
5614  }
5615  }
5616  }
5617 
5618  // wait for the recv to complete (for the column_index and the values_
5619  c_recv = recv2_req.size();
5620  if (c_recv != 0)
5621  {
5622  MPI_Waitall(c_recv, &recv2_req[0], MPI_STATUS_IGNORE);
5623  }
5624 
5625  // Fill in the compressed row matrix
5626  output_block.build(Internal_block_distribution_pt[block_i]);
5627  output_block.build_without_copy(this->internal_block_dimension(block_j),
5628  local_block_nnz,
5629  values_recv,
5630  column_index_recv,
5631  row_start_recv);
5632 
5633  // wait for the send to complete (nnz / row_start)
5634  int c_send = send_req.size();
5635  if (c_send)
5636  {
5637  MPI_Waitall(c_send, &send_req[0], MPI_STATUS_IGNORE);
5638  }
5639 
5640  // delete temp storage used for assembling data for communication
5641  for (unsigned p = 0; p < nproc; p++)
5642  {
5643  delete[] nnz_send[p];
5644  delete[] column_index_for_proc[p];
5645  delete[] values_for_proc[p];
5646  delete[] offset_recv_block[p];
5647  delete[] nnz_recv_block[p];
5648  }
5649 #else
5650  // throw error
5651  std::ostringstream error_message;
5652  error_message << "The matrix is distributed and on more than one "
5653  << "processor. MPI is required.";
5654  throw OomphLibError(
5655  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
5656 #endif
5657  }
5658  }
m row(1)
void block_matrix_test(const unsigned &i, const unsigned &j, const MATRIX *block_matrix_pt) const
Definition: block_preconditioner.cc:5839
static bool Run_block_matrix_test
Definition: block_preconditioner.h:3527
unsigned internal_block_dimension(const unsigned &b) const
Definition: block_preconditioner.h:3165
char char char int int * k
Definition: level2_impl.h:374

References oomph::CRDoubleMatrix::build(), oomph::CRDoubleMatrix::build_without_copy(), calibrate::c, oomph::CRDoubleMatrix::column_index(), oomph::LinearAlgebraDistribution::communicator_pt(), oomph::LinearAlgebraDistribution::distributed(), oomph::DistributableLinearAlgebraObject::distribution_pt(), i, k, oomph::OomphCommunicator::nproc(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, p, UniformPSDSelfTest::r, row(), oomph::CRDoubleMatrix::row_start(), oomph::Global_string_for_annotation::string(), and oomph::CRDoubleMatrix::value().

◆ internal_get_block() [2/2]

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::internal_get_block ( const unsigned i,
const unsigned j,
MATRIX &  output_block 
) const

Gets block (i,j) from the matrix pointed to by Matrix_pt and returns it in output_block. This is associated with the internal blocks. Please use the other get_block(...) function.

◆ internal_get_block_ordered_preconditioner_vector()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::internal_get_block_ordered_preconditioner_vector ( const DoubleVector v,
DoubleVector w 
) const

Given the naturally ordered vector, v, return the vector rearranged in block order in w. This is a legacy function from the old block preconditioning framework. Kept alive in case it may be needed again.

This uses the variables ending in "get_ordered". We no longer use this type of method. This function copy values from v and re-order them in "block order" and place them in w. Block order means that the values in w are the same as the concatenated block vectors.

I.e. - v is naturally ordered. v -> s_b, v is ordered into blocks vectors (requires communication) concatenate_without_communication(s_{0,...,nblocks},w) gives w.

But this function skips out the concatenation part and builds w directly from v.

This is nice but the function is implemented in such a way that it always use all the (internal) blocks and concatenated with the identity ordering. I.e. if this preconditioner has 3 block types, then w will always be: concatenate_without_communication([s_0, s_1, s_2], w). There is easy way to change this.

Furthermore, it does not take into account the new dof type coarsening feature. So this function will most likely produce the incorrect vector w from what the user intended. It still works, but w will be the concatenation of the most fine grain dof block vectors with the "natural" dof type ordering.

This has been superseded by the function get_block_ordered_preconditioner_vector(...) which does the correct thing.

4601  {
4602 #ifdef PARANOID
4603  if (!v.built())
4604  {
4605  std::ostringstream error_message;
4606  error_message << "The distribution of the global vector v must be setup.";
4607  throw OomphLibError(
4608  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4609  }
4610  if (*v.distribution_pt() != *this->master_distribution_pt())
4611  {
4612  std::ostringstream error_message;
4613  error_message << "The distribution of the global vector v must match the "
4614  << " specified master_distribution_pt(). \n"
4615  << "i.e. Distribution_pt in the master preconditioner";
4616  throw OomphLibError(
4617  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4618  }
4619 #endif
4620 
4621  // Cleared and resized w for reordered vector
4623 
4624  // if + only one processor
4625  // + more than one processor but matrix_pt is not distributed
4626  // then use the serial get_block method
4627  if (this->distribution_pt()->communicator_pt()->nproc() == 1 ||
4628  !this->distribution_pt()->distributed())
4629  {
4630  // number of blocks
4631  unsigned nblock = this->Internal_nblock_types;
4632 
4633  // copy to w
4634  unsigned block_offset = 0;
4635  double* w_pt = w.values_pt();
4636  const double* v_pt = v.values_pt();
4637  for (unsigned b = 0; b < nblock; b++)
4638  {
4639  unsigned block_nrow = this->internal_block_dimension(b);
4640  for (unsigned i = 0; i < block_nrow; i++)
4641  {
4642  w_pt[block_offset + i] = v_pt[this->Global_index[b][i]];
4643  }
4644  block_offset += block_nrow;
4645  }
4646  }
4647  // otherwise use mpi
4648  else
4649  {
4650 #ifdef OOMPH_HAS_MPI
4651 
4652  // my rank
4653  unsigned my_rank = this->distribution_pt()->communicator_pt()->my_rank();
4654 
4655  // the number of processors
4656  unsigned nproc = this->distribution_pt()->communicator_pt()->nproc();
4657 
4658  // determine the maximum number of rows to be sent or recv
4659  unsigned max_n_send_or_recv = 0;
4660  for (unsigned p = 0; p < nproc; p++)
4661  {
4662  max_n_send_or_recv =
4663  std::max(max_n_send_or_recv, Nrows_to_send_for_get_ordered[p]);
4664  max_n_send_or_recv =
4665  std::max(max_n_send_or_recv, Nrows_to_recv_for_get_ordered[p]);
4666  }
4667 
4668  // create a vectors of 1s (the size of the nblock for the mpi indexed
4669  // data types
4670  int* block_lengths = new int[max_n_send_or_recv];
4671  for (unsigned i = 0; i < max_n_send_or_recv; i++)
4672  {
4673  block_lengths[i] = 1;
4674  }
4675 
4676  // perform the sends and receives
4677  Vector<MPI_Request> requests;
4678  for (unsigned p = 0; p < nproc; p++)
4679  {
4680  // send and recv with other processors
4681  if (p != my_rank)
4682  {
4683  if (Nrows_to_send_for_get_ordered[p] > 0)
4684  {
4685  // create the send datatype
4686  MPI_Datatype type_send;
4687  MPI_Type_indexed(Nrows_to_send_for_get_ordered[p],
4688  block_lengths,
4689  Rows_to_send_for_get_ordered[p],
4690  MPI_DOUBLE,
4691  &type_send);
4692  MPI_Type_commit(&type_send);
4693 
4694  // send
4695  MPI_Request send_req;
4696  MPI_Isend(const_cast<double*>(v.values_pt()),
4697  1,
4698  type_send,
4699  p,
4700  0,
4701  this->distribution_pt()->communicator_pt()->mpi_comm(),
4702  &send_req);
4703  MPI_Type_free(&type_send);
4704  requests.push_back(send_req);
4705  }
4706 
4707  if (Nrows_to_recv_for_get_ordered[p] > 0)
4708  {
4709  // create the recv datatype
4710  MPI_Datatype type_recv;
4711  MPI_Type_indexed(Nrows_to_recv_for_get_ordered[p],
4712  block_lengths,
4713  Rows_to_recv_for_get_ordered[p],
4714  MPI_DOUBLE,
4715  &type_recv);
4716  MPI_Type_commit(&type_recv);
4717 
4718  // recv
4719  MPI_Request recv_req;
4720  MPI_Irecv(w.values_pt(),
4721  1,
4722  type_recv,
4723  p,
4724  0,
4725  this->distribution_pt()->communicator_pt()->mpi_comm(),
4726  &recv_req);
4727  MPI_Type_free(&type_recv);
4728  requests.push_back(recv_req);
4729  }
4730  }
4731 
4732  // communicate with self
4733  else
4734  {
4735  double* w_values_pt = w.values_pt();
4736  const double* v_values_pt = v.values_pt();
4737  for (unsigned i = 0; i < Nrows_to_send_for_get_ordered[p]; i++)
4738  {
4739  w_values_pt[Rows_to_recv_for_get_ordered[p][i]] =
4740  v_values_pt[Rows_to_send_for_get_ordered[p][i]];
4741  }
4742  }
4743  }
4744 
4745  // and then just wait
4746  unsigned c = requests.size();
4747  Vector<MPI_Status> stat(c);
4748  if (c)
4749  {
4750  MPI_Waitall(c, &requests[0], &stat[0]);
4751  }
4752  delete[] block_lengths;
4753 
4754 #else
4755  // throw error
4756  std::ostringstream error_message;
4757  error_message << "The preconditioner is distributed and on more than one "
4758  << "processor. MPI is required.";
4759  throw OomphLibError(
4760  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4761 #endif
4762  }
4763  }
#define max(a, b)
Definition: datatypes.h:23

References b, calibrate::c, i, max, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, p, v, and w.

◆ internal_get_block_vector()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::internal_get_block_vector ( const unsigned b,
const DoubleVector v,
DoubleVector w 
) const

A helper function, takes the naturally ordered vector, v, and extracts the n-th block vector, b. Here n is the block number in the current preconditioner. NOTE: The ordering of the vector b is the same as the ordering of the block matrix from internal_get_block(...).

4011  {
4012 #ifdef PARANOID
4013  // the number of blocks
4014  const unsigned n_blocks = this->internal_nblock_types();
4015 
4016  // paranoid check that block i is in this block preconditioner
4017  if (b >= n_blocks)
4018  {
4019  std::ostringstream error_message;
4020  error_message
4021  << "Requested block vector " << b
4022  << ", however this preconditioner has internal_nblock_types() "
4023  << "= " << internal_nblock_types() << std::endl;
4024  throw OomphLibError(
4025  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4026  }
4027  if (!v.built())
4028  {
4029  std::ostringstream error_message;
4030  error_message << "The distribution of the global vector v must be setup.";
4031  throw OomphLibError(
4032  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4033  }
4034  if (*(v.distribution_pt()) != *(this->master_distribution_pt()))
4035  {
4036  std::ostringstream error_message;
4037  error_message << "The distribution of the global vector v must match the "
4038  << " specified master_distribution_pt(). \n"
4039  << "i.e. Distribution_pt in the master preconditioner";
4040  throw OomphLibError(
4041  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4042  }
4043 #endif
4044 
4045  // rebuild the block vector
4046  w.build(Internal_block_distribution_pt[b], 0.0);
4047 
4048  // if + only one processor
4049  // + more than one processor but matrix_pt is not distributed
4050  // then use the serial get_block method
4051  if (this->distribution_pt()->communicator_pt()->nproc() == 1 ||
4052  !this->distribution_pt()->distributed())
4053  {
4054  double* w_pt = w.values_pt();
4055  const double* v_pt = v.values_pt();
4056  unsigned n_row = w.nrow();
4057  for (unsigned i = 0; i < n_row; i++)
4058  {
4059  w_pt[i] = v_pt[this->Global_index[b][i]];
4060  }
4061  }
4062  // otherwise use mpi
4063  else
4064  {
4065 #ifdef OOMPH_HAS_MPI
4066 
4067  // my rank
4068  unsigned my_rank = this->distribution_pt()->communicator_pt()->my_rank();
4069 
4070  // the number of processors
4071  unsigned nproc = this->distribution_pt()->communicator_pt()->nproc();
4072 
4073  // determine the maximum number of rows to be sent or recv
4074  unsigned max_n_send_or_recv = 0;
4075  for (unsigned p = 0; p < nproc; p++)
4076  {
4077  max_n_send_or_recv =
4078  std::max(max_n_send_or_recv, Nrows_to_send_for_get_block(b, p));
4079  max_n_send_or_recv =
4080  std::max(max_n_send_or_recv, Nrows_to_recv_for_get_block(b, p));
4081  }
4082 
4083  // create a vectors of 1s (the size of the nblock for the mpi indexed
4084  // data types
4085  int* block_lengths = new int[max_n_send_or_recv];
4086  for (unsigned i = 0; i < max_n_send_or_recv; i++)
4087  {
4088  block_lengths[i] = 1;
4089  }
4090 
4091  // perform the sends and receives
4092  Vector<MPI_Request> requests;
4093  for (unsigned p = 0; p < nproc; p++)
4094  {
4095  // send and recv with other processors
4096  if (p != my_rank)
4097  {
4098  if (Nrows_to_send_for_get_block(b, p) > 0)
4099  {
4100  // create the send datatype
4101  MPI_Datatype type_send;
4102  MPI_Type_indexed(Nrows_to_send_for_get_block(b, p),
4103  block_lengths,
4104  Rows_to_send_for_get_block(b, p),
4105  MPI_DOUBLE,
4106  &type_send);
4107  MPI_Type_commit(&type_send);
4108 
4109  // send
4110  MPI_Request send_req;
4111  MPI_Isend(const_cast<double*>(v.values_pt()),
4112  1,
4113  type_send,
4114  p,
4115  0,
4116  this->distribution_pt()->communicator_pt()->mpi_comm(),
4117  &send_req);
4118  MPI_Type_free(&type_send);
4119  requests.push_back(send_req);
4120  }
4121 
4122  if (Nrows_to_recv_for_get_block(b, p) > 0)
4123  {
4124  // create the recv datatype
4125  MPI_Datatype type_recv;
4126  MPI_Type_indexed(Nrows_to_recv_for_get_block(b, p),
4127  block_lengths,
4128  Rows_to_recv_for_get_block(b, p),
4129  MPI_DOUBLE,
4130  &type_recv);
4131  MPI_Type_commit(&type_recv);
4132 
4133  // recv
4134  MPI_Request recv_req;
4135  MPI_Irecv(w.values_pt(),
4136  1,
4137  type_recv,
4138  p,
4139  0,
4140  this->distribution_pt()->communicator_pt()->mpi_comm(),
4141  &recv_req);
4142  MPI_Type_free(&type_recv);
4143  requests.push_back(recv_req);
4144  }
4145  }
4146 
4147  // communicate with self
4148  else
4149  {
4150  double* w_values_pt = w.values_pt();
4151  const double* v_values_pt = v.values_pt();
4152  for (unsigned i = 0; i < Nrows_to_send_for_get_block(b, p); i++)
4153  {
4154  w_values_pt[Rows_to_recv_for_get_block(b, p)[i]] =
4155  v_values_pt[Rows_to_send_for_get_block(b, p)[i]];
4156  }
4157  }
4158  }
4159 
4160  // and then just wait
4161  unsigned c = requests.size();
4162  Vector<MPI_Status> stat(c);
4163  if (c)
4164  {
4165  MPI_Waitall(c, &requests[0], &stat[0]);
4166  }
4167  delete[] block_lengths;
4168 
4169 #else
4170  // throw error
4171  std::ostringstream error_message;
4172  error_message << "The preconditioner is distributed and on more than one "
4173  << "processor. MPI is required.";
4174  throw OomphLibError(
4175  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4176 #endif
4177  }
4178  }

References b, calibrate::c, i, max, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, p, v, and w.

◆ internal_get_block_vectors() [1/2]

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::internal_get_block_vectors ( const DoubleVector v,
Vector< DoubleVector > &  s 
) const

A helper function, takes the naturally ordered vector and rearranges it into a vector of sub vectors corresponding to the blocks, so s[b][i] contains the i-th entry in the vector associated with block b. The block_vec_number indicates which blocks we want. These blocks and vectors are those corresponding to the internal blocks. Note: If the preconditioner is a subsidiary preconditioner then only the sub-vectors associated with the blocks of the subsidiary preconditioner will be included. Hence the length of v is master_nrow() whereas the total length of the s vectors is the sum of the Nrow of the sub vectors. This is simply a wrapper around the other internal_get_block_vectors(...) function with the identity block_vec_number vector.

3422  {
3423  // Number of block types
3424  const unsigned nblock = this->internal_nblock_types();
3425  Vector<unsigned> block_vec_number(nblock, 0);
3426  for (unsigned b = 0; b < nblock; b++)
3427  {
3428  block_vec_number[b] = b;
3429  }
3430 
3431  internal_get_block_vectors(block_vec_number, v, s);
3432  }

References b, s, and v.

◆ internal_get_block_vectors() [2/2]

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::internal_get_block_vectors ( const Vector< unsigned > &  block_vec_number,
const DoubleVector v,
Vector< DoubleVector > &  s 
) const

Takes the naturally ordered vector and rearranges it into a vector of sub vectors corresponding to the blocks, so s[b][i] contains the i-th entry in the vector associated with block b. The block_vec_number indicates which blocks we want. These blocks and vectors are those corresponding to the internal blocks. Note: If the preconditioner is a subsidiary preconditioner then only the sub-vectors associated with the blocks of the subsidiary preconditioner will be included. Hence the length of v is master_nrow() whereas the total length of the s vectors is the sum of the Nrow of the sub vectors.

3135  {
3136 #ifdef PARANOID
3137  if (!v.built())
3138  {
3139  std::ostringstream error_message;
3140  error_message << "The distribution of the global vector v must be setup.";
3141  throw OomphLibError(
3142  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3143  }
3144  if (*(v.distribution_pt()) != *(this->master_distribution_pt()))
3145  {
3146  std::ostringstream error_message;
3147  error_message << "The distribution of the global vector v must match the "
3148  << " specified master_distribution_pt(). \n"
3149  << "i.e. Distribution_pt in the master preconditioner";
3150  throw OomphLibError(
3151  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3152  }
3153 #endif
3154 
3155  // Number of block types
3156  // const unsigned nblock = this->internal_nblock_types();
3157  const unsigned nblock = block_vec_number.size();
3158 
3159  // if + only one processor
3160  // + more than one processor but matrix_pt is not distributed
3161  // then use the serial get_block method
3162  if (this->distribution_pt()->communicator_pt()->nproc() == 1 ||
3163  !this->distribution_pt()->distributed())
3164  {
3165  // Vector of vectors for each section of residual vector
3166  s.resize(nblock);
3167 
3168  // pointer to the data in v
3169  const double* v_pt = v.values_pt();
3170 
3171  // setup the block vector and then insert the data
3172  for (unsigned b = 0; b < nblock; b++)
3173  {
3174  const unsigned required_block = block_vec_number[b];
3175  s[b].build(Internal_block_distribution_pt[required_block], 0.0);
3176  double* s_pt = s[b].values_pt();
3177  unsigned nrow = s[b].nrow();
3178  for (unsigned i = 0; i < nrow; i++)
3179  {
3180  s_pt[i] = v_pt[this->Global_index[required_block][i]];
3181  }
3182  }
3183  }
3184  // otherwise use mpi
3185  else
3186  {
3187 #ifdef OOMPH_HAS_MPI
3188  // my rank
3189  unsigned my_rank = this->distribution_pt()->communicator_pt()->my_rank();
3190 
3191  // the number of processors
3192  unsigned nproc = this->distribution_pt()->communicator_pt()->nproc();
3193 
3194  // build the vectors
3195  s.resize(nblock);
3196  for (unsigned b = 0; b < nblock; b++)
3197  {
3198  const unsigned required_block = block_vec_number[b];
3199  s[b].build(Internal_block_distribution_pt[required_block], 0.0);
3200  }
3201 
3202  // determine the maximum number of rows to be sent or recv
3203  // and determine the number of blocks each processor will send and recv
3204  // communication for
3205  Vector<int> nblock_send(nproc, 0);
3206  Vector<int> nblock_recv(nproc, 0);
3207  unsigned max_n_send_or_recv = 0;
3208  for (unsigned p = 0; p < nproc; p++)
3209  {
3210  for (unsigned b = 0; b < nblock; b++)
3211  {
3212  const unsigned required_block = block_vec_number[b];
3213  max_n_send_or_recv = std::max(
3214  max_n_send_or_recv, Nrows_to_send_for_get_block(required_block, p));
3215  max_n_send_or_recv = std::max(
3216  max_n_send_or_recv, Nrows_to_recv_for_get_block(required_block, p));
3217  if (Nrows_to_send_for_get_block(required_block, p) > 0)
3218  {
3219  nblock_send[p]++;
3220  }
3221  if (Nrows_to_recv_for_get_block(required_block, p) > 0)
3222  {
3223  nblock_recv[p]++;
3224  }
3225  }
3226  }
3227 
3228  // create a vectors of 1s the size of the nblock for the mpi indexed
3229  // data types
3230  int* block_lengths = new int[max_n_send_or_recv];
3231  for (unsigned i = 0; i < max_n_send_or_recv; i++)
3232  {
3233  block_lengths[i] = 1;
3234  }
3235 
3236  // perform the sends and receives
3237  Vector<MPI_Request> requests;
3238  for (unsigned p = 0; p < nproc; p++)
3239  {
3240  // send and recv with other processors
3241  if (p != my_rank)
3242  {
3243  // send
3244  if (nblock_send[p] > 0)
3245  {
3246  // create the datatypes vector
3247  MPI_Datatype block_send_types[nblock_send[p]];
3248 
3249  // create the datatypes
3250  unsigned ptr = 0;
3251  for (unsigned b = 0; b < nblock; b++)
3252  {
3253  const unsigned required_block = block_vec_number[b];
3254 
3255  if (Nrows_to_send_for_get_block(required_block, p) > 0)
3256  {
3257  MPI_Type_indexed(Nrows_to_send_for_get_block(required_block, p),
3258  block_lengths,
3259  Rows_to_send_for_get_block(required_block, p),
3260  MPI_DOUBLE,
3261  &block_send_types[ptr]);
3262  MPI_Type_commit(&block_send_types[ptr]);
3263  ptr++;
3264  }
3265  }
3266 
3267  // compute the displacements and lengths
3268  MPI_Aint displacements[nblock_send[p]];
3269  int lengths[nblock_send[p]];
3270  for (int i = 0; i < nblock_send[p]; i++)
3271  {
3272  lengths[i] = 1;
3273  displacements[i] = 0;
3274  }
3275 
3276  // build the final datatype
3277  MPI_Datatype type_send;
3278  MPI_Type_create_struct(nblock_send[p],
3279  lengths,
3280  displacements,
3281  block_send_types,
3282  &type_send);
3283  MPI_Type_commit(&type_send);
3284 
3285  // send
3286  MPI_Request send_req;
3287  MPI_Isend(const_cast<double*>(v.values_pt()),
3288  1,
3289  type_send,
3290  p,
3291  0,
3292  this->distribution_pt()->communicator_pt()->mpi_comm(),
3293  &send_req);
3294  MPI_Type_free(&type_send);
3295  for (int i = 0; i < nblock_send[p]; i++)
3296  {
3297  MPI_Type_free(&block_send_types[i]);
3298  }
3299  requests.push_back(send_req);
3300  }
3301 
3302  // recv
3303  if (nblock_recv[p] > 0)
3304  {
3305  // create the datatypes vector
3306  MPI_Datatype block_recv_types[nblock_recv[p]];
3307 
3308  // and the displacements
3309  MPI_Aint displacements[nblock_recv[p]];
3310 
3311  // and the lengths
3312  int lengths[nblock_recv[p]];
3313 
3314  // all displacements are computed relative to s[0] values
3315  MPI_Aint displacements_base;
3316  MPI_Get_address(s[0].values_pt(), &displacements_base);
3317 
3318  // now build
3319  unsigned ptr = 0;
3320  for (unsigned b = 0; b < nblock; b++)
3321  {
3322  const unsigned required_block = block_vec_number[b];
3323 
3324  if (Nrows_to_recv_for_get_block(required_block, p) > 0)
3325  {
3326  MPI_Type_indexed(Nrows_to_recv_for_get_block(required_block, p),
3327  block_lengths,
3328  Rows_to_recv_for_get_block(required_block, p),
3329  MPI_DOUBLE,
3330  &block_recv_types[ptr]);
3331  MPI_Type_commit(&block_recv_types[ptr]);
3332  MPI_Get_address(s[b].values_pt(), &displacements[ptr]);
3333  displacements[ptr] -= displacements_base;
3334  lengths[ptr] = 1;
3335  ptr++;
3336  }
3337  }
3338 
3339  // build the final data type
3340  MPI_Datatype type_recv;
3341  MPI_Type_create_struct(nblock_recv[p],
3342  lengths,
3343  displacements,
3344  block_recv_types,
3345  &type_recv);
3346  MPI_Type_commit(&type_recv);
3347 
3348  // recv
3349  MPI_Request recv_req;
3350  MPI_Irecv(s[0].values_pt(),
3351  1,
3352  type_recv,
3353  p,
3354  0,
3355  this->distribution_pt()->communicator_pt()->mpi_comm(),
3356  &recv_req);
3357  MPI_Type_free(&type_recv);
3358  for (int i = 0; i < nblock_recv[p]; i++)
3359  {
3360  MPI_Type_free(&block_recv_types[i]);
3361  }
3362  requests.push_back(recv_req);
3363  }
3364  }
3365 
3366  // communicate with self
3367  else
3368  {
3369  const double* v_values_pt = v.values_pt();
3370  for (unsigned b = 0; b < nblock; b++)
3371  {
3372  const unsigned required_block = block_vec_number[b];
3373 
3374  double* w_values_pt = s[b].values_pt();
3375  for (unsigned i = 0;
3376  i < Nrows_to_send_for_get_block(required_block, p);
3377  i++)
3378  {
3379  w_values_pt[Rows_to_recv_for_get_block(required_block, p)[i]] =
3380  v_values_pt[Rows_to_send_for_get_block(required_block, p)[i]];
3381  }
3382  }
3383  }
3384  }
3385 
3386  // and then just wait
3387  unsigned c = requests.size();
3388  Vector<MPI_Status> stat(c);
3389  if (c)
3390  {
3391  MPI_Waitall(c, &requests[0], &stat[0]);
3392  }
3393  delete[] block_lengths;
3394 
3395 #else
3396  // throw error
3397  std::ostringstream error_message;
3398  error_message << "The preconditioner is distributed and on more than one "
3399  << "processor. MPI is required.";
3400  throw OomphLibError(
3401  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3402 #endif
3403  }
3404  }

References b, calibrate::c, i, max, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, p, s, and v.

◆ internal_index_in_block()

template<typename MATRIX >
int oomph::BlockPreconditioner< MATRIX >::internal_index_in_block ( const unsigned i_dof) const
inline

Return the index in the block corresponding to a global block number i_dof. The index returned corresponds to the internal blocks, which is the most fine grain dof blocks.

2706  {
2707  // the index in the dof block
2708  unsigned index = internal_index_in_dof(i_dof);
2709 
2710  // the dof block number
2711  int internal_dof_block_number = internal_dof_number(i_dof);
2712  if (internal_dof_block_number >= 0)
2713  {
2714  // the 'actual' block number
2715  unsigned blk_number = internal_block_number(i_dof);
2716 
2717  // compute the index in the block
2718  unsigned j = 0;
2719  while (int(Block_number_to_dof_number_lookup[blk_number][j]) !=
2720  internal_dof_block_number)
2721  {
2723  Block_number_to_dof_number_lookup[blk_number][j]);
2724  j++;
2725  }
2726 
2727  // and return
2728  return index;
2729  }
2730  return -1;
2731  } // EOFunc internal_index_in_block(...)

References oomph::BlockPreconditioner< MATRIX >::Block_number_to_dof_number_lookup, oomph::BlockPreconditioner< MATRIX >::internal_block_number(), oomph::BlockPreconditioner< MATRIX >::internal_dof_block_dimension(), oomph::BlockPreconditioner< MATRIX >::internal_dof_number(), oomph::BlockPreconditioner< MATRIX >::internal_index_in_dof(), and j.

◆ internal_index_in_dof()

template<typename MATRIX >
unsigned oomph::BlockPreconditioner< MATRIX >::internal_index_in_dof ( const unsigned i_dof) const
inlineprotected

Return the row/column number of global unknown i_dof within it's block.

3114  {
3116  {
3117 #ifdef OOMPH_HAS_MPI
3118  unsigned first_row = this->distribution_pt()->first_row();
3119  unsigned nrow_local = this->distribution_pt()->nrow_local();
3120  unsigned last_row = first_row + nrow_local - 1;
3121  if (i_dof >= first_row && i_dof <= last_row)
3122  {
3123  return static_cast<int>(Index_in_dof_block_dense[i_dof - first_row]);
3124  }
3125  else
3126  {
3127  // int index = this->get_index_of_element(Global_index_sparse,i_dof);
3128  int index =
3129  get_index_of_value<unsigned>(Global_index_sparse, i_dof, true);
3130  if (index >= 0)
3131  {
3132  return Index_in_dof_block_sparse[index];
3133  }
3134  }
3135  // if we here we couldn't find the i_dof
3136 #ifdef PARANOID
3137  std::ostringstream error_message;
3138  error_message << "Requested internal_index_in_dof(...) for global DOF "
3139  << i_dof << "\n"
3140  << "cannot be found.\n";
3141  throw OomphLibError(error_message.str(),
3144 #endif
3145 #else
3146  return Index_in_dof_block_dense[i_dof];
3147 #endif
3148  }
3149  else
3150  {
3151  return Master_block_preconditioner_pt->internal_index_in_dof(i_dof);
3152  }
3153 
3154  // Shouldn't get here
3155  throw OomphLibError(
3156  "Never get here\n", OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3157  // Dummy return
3158  return -1;
3159  }

References oomph::DistributableLinearAlgebraObject::distribution_pt(), oomph::LinearAlgebraDistribution::first_row(), oomph::DistributableLinearAlgebraObject::first_row(), oomph::BlockPreconditioner< MATRIX >::Index_in_dof_block_dense, oomph::BlockPreconditioner< MATRIX >::is_master_block_preconditioner(), oomph::BlockPreconditioner< MATRIX >::Master_block_preconditioner_pt, oomph::LinearAlgebraDistribution::nrow_local(), oomph::DistributableLinearAlgebraObject::nrow_local(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Referenced by oomph::BlockPreconditioner< MATRIX >::index_in_block(), and oomph::BlockPreconditioner< MATRIX >::internal_index_in_block().

◆ internal_master_dof_number()

template<typename MATRIX >
unsigned oomph::BlockPreconditioner< MATRIX >::internal_master_dof_number ( const unsigned b) const
inlineprotected

Takes the block number within this preconditioner and returns the corresponding block number in the master preconditioner. If this preconditioner does not have a master block preconditioner then the block number passed is returned

3239  {
3240  if (is_master_block_preconditioner()) return b;
3241  else
3243  }

References b, oomph::BlockPreconditioner< MATRIX >::Doftype_in_master_preconditioner_fine, and oomph::BlockPreconditioner< MATRIX >::is_master_block_preconditioner().

Referenced by oomph::BlockPreconditioner< MATRIX >::document(), and oomph::BlockPreconditioner< MATRIX >::internal_dof_block_dimension().

◆ internal_nblock_types()

template<typename MATRIX >
unsigned oomph::BlockPreconditioner< MATRIX >::internal_nblock_types ( ) const
inline

Return the number internal blocks. This should be the same as the number of internal dof types. Internally, the block preconditioning framework always work with the most fine grain blocks. I.e. it always deal with the most fine grain dof-level blocks. This allows for coarsening of dof types. When we extract a block, we look at the Block_to_dof_map_fine vector to find out which most fine grain dof types belongs to this block.

The preconditioner writer should not have to deal with internal dof/block types and thus this function has been moved to private.

This is legacy code from before the coarsening dof type functionality was added. This is kept alive because it is still used in the internal workings of the block preconditioning framework.

The function nblock_types(...) should be used if the number of block types is required.

2523  {
2524 #ifdef PARANOID
2525  if (Internal_nblock_types == 0)
2526  {
2527  std::ostringstream err_msg;
2528  err_msg
2529  << "(Internal_nblock_types == 0) is true. \n"
2530  << "Did you remember to call the function block_setup(...)?\n\n"
2531 
2532  << "This variable is always set up within block_setup(...).\n"
2533  << "If block_setup() is already called, then perhaps there is\n"
2534  << "something wrong with your block preconditionable elements.\n"
2535  << std::endl;
2536  throw OomphLibError(
2538  }
2539 
2541  {
2542  std::ostringstream err_msg;
2543  err_msg
2544  << "The number of internal block types and "
2545  << "internal dof types does not match... \n\n"
2546  << "Internally, the number of block types and the number of dof "
2547  << "types must be the same.\n"
2548  << std::endl;
2549  throw OomphLibError(
2551  }
2552 #endif
2553 
2554  // return the number of internal block types.
2555  return Internal_nblock_types;
2556  } // EOFunc internal_nblock_types(...)

References oomph::BlockPreconditioner< MATRIX >::Internal_nblock_types, oomph::BlockPreconditioner< MATRIX >::internal_ndof_types(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Referenced by oomph::BlockPreconditioner< MATRIX >::document(), oomph::BlockPreconditioner< MATRIX >::internal_block_dimension(), oomph::BlockPreconditioner< MATRIX >::internal_block_distribution_pt(), and oomph::BlockPreconditioner< MATRIX >::output_blocks_to_files().

◆ internal_ndof_types()

template<typename MATRIX >
unsigned oomph::BlockPreconditioner< MATRIX >::internal_ndof_types ( ) const
inline

Return the number of internal dof types. This is the number of most fine grain dof types. The preconditioner writer should not have to concern him/her-self with the internal dof/block types. Thus this fuction is moved to private. We have kept this function alive since it it still used deep within the inner workings of the block preconditioning framework.

2565  {
2567  // If this is a subsidiary block preconditioner, then the variable
2568  // Internal_ndof_types must always be set up.
2569  {
2570 #ifdef PARANOID
2571  if (Internal_ndof_types == 0)
2572  {
2573  std::ostringstream error_msg;
2574  error_msg
2575  << "(Internal_ndof_types == 0) is true.\n"
2576  << "This means that the Master_block_preconditioner_pt pointer is\n"
2577  << "set but possibly not by the function\n"
2578  << "turn_into_subsidiary_block_preconditioner(...).\n\n"
2579 
2580  << "This goes against the block preconditioning framework "
2581  << "methodology.\n"
2582  << "Many machinery relies on the look up lists set up by the \n"
2583  << "function turn_into_subsidiary_block_preconditioner(...) \n"
2584  << "between the parent and child block preconditioners.\n"
2585  << std::endl;
2586  throw OomphLibError(
2588  }
2589 #endif
2590  return Internal_ndof_types;
2591  }
2592  else
2593  // Else, this is a master block preconditioner, calculate the number of
2594  // dof types from the meshes.
2595  {
2596  unsigned ndof = 0;
2597  for (unsigned i = 0; i < nmesh(); i++)
2598  {
2599  ndof += ndof_types_in_mesh(i);
2600  }
2601  return ndof;
2602  }
2603  } // EOFunc internal_ndof_types(...)

References i, oomph::BlockPreconditioner< MATRIX >::Internal_ndof_types, oomph::BlockPreconditioner< MATRIX >::is_subsidiary_block_preconditioner(), oomph::BlockPreconditioner< MATRIX >::ndof_types_in_mesh(), oomph::BlockPreconditioner< MATRIX >::nmesh(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Referenced by oomph::BlockPreconditioner< MATRIX >::document(), oomph::BlockPreconditioner< MATRIX >::internal_dof_block_dimension(), oomph::BlockPreconditioner< MATRIX >::internal_dof_number(), oomph::BlockPreconditioner< MATRIX >::internal_nblock_types(), and oomph::BlockPreconditioner< MATRIX >::ndof_types().

◆ internal_preconditioner_matrix_distribution_pt()

template<typename MATRIX >
const LinearAlgebraDistribution* oomph::BlockPreconditioner< MATRIX >::internal_preconditioner_matrix_distribution_pt ( ) const
inlineprotected

access function to the internal preconditioner matrix distribution pt. preconditioner_matrix_distribution_pt always returns the concatenation of the internal block distributions. Since the writer of the preconditioner does not need to concern themselves with the internal dof/block, please use preconditioner_matrix_distribution_pt().

3253  {
3256  else
3257  return this->distribution_pt();
3258  }

References oomph::DistributableLinearAlgebraObject::distribution_pt(), oomph::BlockPreconditioner< MATRIX >::Internal_preconditioner_matrix_distribution_pt, and oomph::BlockPreconditioner< MATRIX >::is_master_block_preconditioner().

Referenced by oomph::BlockPreconditioner< MATRIX >::document().

◆ internal_return_block_ordered_preconditioner_vector()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::internal_return_block_ordered_preconditioner_vector ( const DoubleVector w,
DoubleVector v 
) const

Takes the block ordered vector, w, and reorders it in the natural order. Reordered vector is returned in v. Note: If the preconditioner is a subsidiary preconditioner then only the components of the vector associated with the blocks of the subsidiary preconditioner will be included. Hence the length of v is master_nrow() whereas that of the vector w is of length this->nrow().

This is the return function for the function internal_get_block_ordered_preconditioner_vector(...). Both internal_get_block_ordered_preconditioner_vector(...) and internal_return_block_ordered_preconditioner_vector(...) has been superseded by the functions

get_block_ordered_preconditioner_vector(...) and return_block_ordered_preconditioner_vector(...),

Thus this function is moved to the private section of the code.

4852  {
4853 #ifdef PARANOID
4854  if (!v.built())
4855  {
4856  std::ostringstream error_message;
4857  error_message << "The distribution of the global vector v must be setup.";
4858  throw OomphLibError(
4859  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4860  }
4861  if (*v.distribution_pt() != *this->master_distribution_pt())
4862  {
4863  std::ostringstream error_message;
4864  error_message << "The distribution of the global vector v must match the "
4865  << " specified master_distribution_pt(). \n"
4866  << "i.e. Distribution_pt in the master preconditioner";
4867  throw OomphLibError(
4868  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4869  }
4870  if (!w.built())
4871  {
4872  std::ostringstream error_message;
4873  error_message << "The distribution of the block vector w must be setup.";
4874  throw OomphLibError(
4875  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4876  }
4877  if (*w.distribution_pt() !=
4878  *this->internal_preconditioner_matrix_distribution_pt())
4879  {
4880  std::ostringstream error_message;
4881  error_message << "The distribution of the block vector w must match the "
4882  << " specified distribution at Distribution_pt[b]";
4883  throw OomphLibError(
4884  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4885  }
4886 #endif
4887 
4888 
4889  // if + only one processor
4890  // + more than one processor but matrix_pt is not distributed
4891  // then use the serial get_block method
4892  if (this->distribution_pt()->communicator_pt()->nproc() == 1 ||
4893  !this->distribution_pt()->distributed())
4894  {
4895  // number of blocks
4896  unsigned nblock = this->Internal_nblock_types;
4897 
4898  // copy to w
4899  unsigned block_offset = 0;
4900  const double* w_pt = w.values_pt();
4901  double* v_pt = v.values_pt();
4902  for (unsigned b = 0; b < nblock; b++)
4903  {
4904  unsigned block_nrow = this->internal_block_dimension(b);
4905  for (unsigned i = 0; i < block_nrow; i++)
4906  {
4907  v_pt[this->Global_index[b][i]] = w_pt[block_offset + i];
4908  }
4909  block_offset += block_nrow;
4910  }
4911  }
4912  // otherwise use mpi
4913  else
4914  {
4915 #ifdef OOMPH_HAS_MPI
4916 
4917  // my rank
4918  unsigned my_rank = this->distribution_pt()->communicator_pt()->my_rank();
4919 
4920  // the number of processors
4921  unsigned nproc = this->distribution_pt()->communicator_pt()->nproc();
4922 
4923  // determine the maximum number of rows to be sent or recv
4924  unsigned max_n_send_or_recv = 0;
4925  for (unsigned p = 0; p < nproc; p++)
4926  {
4927  max_n_send_or_recv =
4928  std::max(max_n_send_or_recv, Nrows_to_send_for_get_ordered[p]);
4929  max_n_send_or_recv =
4930  std::max(max_n_send_or_recv, Nrows_to_recv_for_get_ordered[p]);
4931  }
4932 
4933  // create a vectors of 1s (the size of the nblock for the mpi indexed
4934  // data types
4935  int* block_lengths = new int[max_n_send_or_recv];
4936  for (unsigned i = 0; i < max_n_send_or_recv; i++)
4937  {
4938  block_lengths[i] = 1;
4939  }
4940 
4941  // perform the sends and receives
4942  Vector<MPI_Request> requests;
4943  for (unsigned p = 0; p < nproc; p++)
4944  {
4945  // send and recv with other processors
4946  if (p != my_rank)
4947  {
4948  if (Nrows_to_recv_for_get_ordered[p] > 0)
4949  {
4950  // create the send datatype
4951  MPI_Datatype type_send;
4952  MPI_Type_indexed(Nrows_to_recv_for_get_ordered[p],
4953  block_lengths,
4954  Rows_to_recv_for_get_ordered[p],
4955  MPI_DOUBLE,
4956  &type_send);
4957  MPI_Type_commit(&type_send);
4958 
4959  // send
4960  MPI_Request send_req;
4961  MPI_Isend(const_cast<double*>(w.values_pt()),
4962  1,
4963  type_send,
4964  p,
4965  0,
4966  this->distribution_pt()->communicator_pt()->mpi_comm(),
4967  &send_req);
4968  MPI_Type_free(&type_send);
4969  requests.push_back(send_req);
4970  }
4971 
4972  if (Nrows_to_send_for_get_ordered[p] > 0)
4973  {
4974  // create the recv datatype
4975  MPI_Datatype type_recv;
4976  MPI_Type_indexed(Nrows_to_send_for_get_ordered[p],
4977  block_lengths,
4978  Rows_to_send_for_get_ordered[p],
4979  MPI_DOUBLE,
4980  &type_recv);
4981  MPI_Type_commit(&type_recv);
4982 
4983  // recv
4984  MPI_Request recv_req;
4985  MPI_Irecv(v.values_pt(),
4986  1,
4987  type_recv,
4988  p,
4989  0,
4990  this->distribution_pt()->communicator_pt()->mpi_comm(),
4991  &recv_req);
4992  MPI_Type_free(&type_recv);
4993  requests.push_back(recv_req);
4994  }
4995  }
4996 
4997  // communicate wih self
4998  else
4999  {
5000  const double* w_values_pt = w.values_pt();
5001  double* v_values_pt = v.values_pt();
5002  for (unsigned i = 0; i < Nrows_to_send_for_get_ordered[p]; i++)
5003  {
5004  v_values_pt[Rows_to_send_for_get_ordered[p][i]] =
5005  w_values_pt[Rows_to_recv_for_get_ordered[p][i]];
5006  }
5007  }
5008  }
5009 
5010  // and then just wait
5011  unsigned c = requests.size();
5012  Vector<MPI_Status> stat(c);
5013  if (c)
5014  {
5015  MPI_Waitall(c, &requests[0], &stat[0]);
5016  }
5017  delete[] block_lengths;
5018 
5019 #else
5020  // throw error
5021  std::ostringstream error_message;
5022  error_message << "The preconditioner is distributed and on more than one "
5023  << "processor. MPI is required.";
5024  throw OomphLibError(
5025  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
5026 #endif
5027  } // else use mpi
5028  } // function return_block_ordered_preconditioner_vector

References b, calibrate::c, i, max, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, p, v, and w.

◆ internal_return_block_vector()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::internal_return_block_vector ( const unsigned b,
const DoubleVector w,
DoubleVector v 
) const

Takes the n-th block ordered vector, b, and copies its entries to the appropriate entries in the naturally ordered vector, v. Here n is the block number in the current block preconditioner. If the preconditioner is a subsidiary block preconditioner the other entries in v that are not associated with it are left alone.

This version works with the internal block types. This is legacy code but is kept alive, hence moved to private. Please use the function "return_block_vector(...)".

4295  {
4296 #ifdef PARANOID
4297  // the number of blocks
4298  const unsigned n_blocks = this->internal_nblock_types();
4299 
4300  // paranoid check that block i is in this block preconditioner
4301  if (b >= n_blocks)
4302  {
4303  std::ostringstream error_message;
4304  error_message
4305  << "Requested block vector " << b
4306  << ", however this preconditioner has internal_nblock_types() "
4307  << "= " << internal_nblock_types() << std::endl;
4308  throw OomphLibError(
4309  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4310  }
4311  if (!v.built())
4312  {
4313  std::ostringstream error_message;
4314  error_message << "The distribution of the global vector v must be setup.";
4315  throw OomphLibError(
4316  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4317  }
4318  if (*v.distribution_pt() != *this->master_distribution_pt())
4319  {
4320  std::ostringstream error_message;
4321  error_message << "The distribution of the global vector v must match the "
4322  << " specified master_distribution_pt(). \n"
4323  << "i.e. Distribution_pt in the master preconditioner";
4324  throw OomphLibError(
4325  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4326  }
4327  if (!w.built())
4328  {
4329  std::ostringstream error_message;
4330  error_message << "The distribution of the block vector w must be setup.";
4331  throw OomphLibError(
4332  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4333  }
4334  if (*w.distribution_pt() != *Internal_block_distribution_pt[b])
4335  {
4336  std::ostringstream error_message;
4337  error_message
4338  << "The distribution of the block vector w must match the "
4339  << " specified distribution at Internal_block_distribution_pt[b]";
4340  throw OomphLibError(
4341  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4342  }
4343 #endif
4344 
4345  // if + only one processor
4346  // + more than one processor but matrix_pt is not distributed
4347  // then use the serial get_block method
4348  if (this->distribution_pt()->communicator_pt()->nproc() == 1 ||
4349  !this->distribution_pt()->distributed())
4350  {
4351  // length of vector
4352  unsigned n_row = this->internal_block_dimension(b);
4353 
4354  // copy back from the block vector to the naturally ordered vector
4355  double* v_pt = v.values_pt();
4356  const double* w_pt = w.values_pt();
4357  for (unsigned i = 0; i < n_row; i++)
4358  {
4359  v_pt[this->Global_index[b][i]] = w_pt[i];
4360  }
4361  }
4362  // otherwise use mpi
4363  else
4364  {
4365 #ifdef OOMPH_HAS_MPI
4366 
4367  // my rank
4368  unsigned my_rank = this->distribution_pt()->communicator_pt()->my_rank();
4369 
4370  // the number of processors
4371  unsigned nproc = this->distribution_pt()->communicator_pt()->nproc();
4372 
4373  // determine the maximum number of rows to be sent or recv
4374  unsigned max_n_send_or_recv = 0;
4375  for (unsigned p = 0; p < nproc; p++)
4376  {
4377  max_n_send_or_recv =
4378  std::max(max_n_send_or_recv, Nrows_to_send_for_get_block(b, p));
4379  max_n_send_or_recv =
4380  std::max(max_n_send_or_recv, Nrows_to_recv_for_get_block(b, p));
4381  }
4382 
4383  // create a vectors of 1s (the size of the nblock for the mpi indexed
4384  // data types
4385  int* block_lengths = new int[max_n_send_or_recv];
4386  for (unsigned i = 0; i < max_n_send_or_recv; i++)
4387  {
4388  block_lengths[i] = 1;
4389  }
4390 
4391  // perform the sends and receives
4392  Vector<MPI_Request> requests;
4393  for (unsigned p = 0; p < nproc; p++)
4394  {
4395  // send and recv with other processors
4396  if (p != my_rank)
4397  {
4398  if (Nrows_to_recv_for_get_block(b, p) > 0)
4399  {
4400  // create the send datatype
4401  MPI_Datatype type_send;
4402  MPI_Type_indexed(Nrows_to_recv_for_get_block(b, p),
4403  block_lengths,
4404  Rows_to_recv_for_get_block(b, p),
4405  MPI_DOUBLE,
4406  &type_send);
4407  MPI_Type_commit(&type_send);
4408 
4409  // send
4410  MPI_Request send_req;
4411  MPI_Isend(const_cast<double*>(w.values_pt()),
4412  1,
4413  type_send,
4414  p,
4415  0,
4416  this->distribution_pt()->communicator_pt()->mpi_comm(),
4417  &send_req);
4418  MPI_Type_free(&type_send);
4419  requests.push_back(send_req);
4420  }
4421 
4422  if (Nrows_to_send_for_get_block(b, p) > 0)
4423  {
4424  // create the recv datatype
4425  MPI_Datatype type_recv;
4426  MPI_Type_indexed(Nrows_to_send_for_get_block(b, p),
4427  block_lengths,
4428  Rows_to_send_for_get_block(b, p),
4429  MPI_DOUBLE,
4430  &type_recv);
4431  MPI_Type_commit(&type_recv);
4432 
4433  // recv
4434  MPI_Request recv_req;
4435  MPI_Irecv(v.values_pt(),
4436  1,
4437  type_recv,
4438  p,
4439  0,
4440  this->distribution_pt()->communicator_pt()->mpi_comm(),
4441  &recv_req);
4442  MPI_Type_free(&type_recv);
4443  requests.push_back(recv_req);
4444  }
4445  }
4446 
4447  // communicate wih self
4448  else
4449  {
4450  const double* w_values_pt = w.values_pt();
4451  double* v_values_pt = v.values_pt();
4452  for (unsigned i = 0; i < Nrows_to_send_for_get_block(b, p); i++)
4453  {
4454  v_values_pt[Rows_to_send_for_get_block(b, p)[i]] =
4455  w_values_pt[Rows_to_recv_for_get_block(b, p)[i]];
4456  }
4457  }
4458  }
4459 
4460  // and then just wait
4461  unsigned c = requests.size();
4462  Vector<MPI_Status> stat(c);
4463  if (c)
4464  {
4465  MPI_Waitall(c, &requests[0], &stat[0]);
4466  }
4467  delete[] block_lengths;
4468 
4469 #else
4470  // throw error
4471  std::ostringstream error_message;
4472  error_message << "The preconditioner is distributed and on more than one "
4473  << "processor. MPI is required.";
4474  throw OomphLibError(
4475  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4476 #endif
4477  }
4478  }

References b, calibrate::c, i, max, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, p, v, and w.

◆ internal_return_block_vectors() [1/2]

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::internal_return_block_vectors ( const Vector< DoubleVector > &  s,
DoubleVector v 
) const

A helper function, takes the vector of block vectors, s, and copies its entries into the naturally ordered vector, v. If this is a subsidiary block preconditioner only those entries in v that are associated with its blocks are affected. This is simple a wrapper around the other internal_return_block_vectors(...) function with the identity block_vec_number vector.

A helper function, takes the naturally ordered vector and rearranges it into a vector of sub vectors corresponding to the blocks, so s[b][i] contains the i-th entry in the vector associated with block b. The block_vec_number indicates which blocks we want. These blocks and vectors are those corresponding to the internal blocks. Note: If the preconditioner is a subsidiary preconditioner then only the sub-vectors associated with the blocks of the subsidiary preconditioner will be included. Hence the length of v is master_nrow() whereas the total length of the s vectors is the sum of the Nrow of the sub vectors. This is simply a wrapper around the other internal_get_block_vectors(...) function with the identity block_vec_number vector.

3989  {
3990  // the number of blocks
3991  const unsigned nblock = this->internal_nblock_types();
3992  Vector<unsigned> block_vec_number(nblock, 0);
3993  for (unsigned b = 0; b < nblock; b++)
3994  {
3995  block_vec_number[b] = b;
3996  }
3997 
3998  internal_return_block_vectors(block_vec_number, s, v);
3999  }
void internal_return_block_vectors(const Vector< unsigned > &block_vec_number, const Vector< DoubleVector > &s, DoubleVector &v) const
Definition: block_preconditioner.cc:3684

References b, s, and v.

◆ internal_return_block_vectors() [2/2]

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::internal_return_block_vectors ( const Vector< unsigned > &  block_vec_number,
const Vector< DoubleVector > &  s,
DoubleVector v 
) const

A helper function, takes the vector of block vectors, s, and copies its entries into the naturally ordered vector, v. If this is a subsidiary block preconditioner only those entries in v that are associated with its blocks are affected.

Takes the naturally ordered vector and rearranges it into a vector of sub vectors corresponding to the blocks, so s[b][i] contains the i-th entry in the vector associated with block b. The block_vec_number indicates which blocks we want. These blocks and vectors are those corresponding to the internal blocks. Note: If the preconditioner is a subsidiary preconditioner then only the sub-vectors associated with the blocks of the subsidiary preconditioner will be included. Hence the length of v is master_nrow() whereas the total length of the s vectors is the sum of the Nrow of the sub vectors.

3688  {
3689  // the number of blocks
3690  const unsigned nblock = block_vec_number.size();
3691 
3692 #ifdef PARANOID
3693  if (!v.built())
3694  {
3695  std::ostringstream error_message;
3696  error_message << "The distribution of the global vector v must be setup.";
3697  throw OomphLibError(
3698  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3699  }
3700  if (*(v.distribution_pt()) != *(this->master_distribution_pt()))
3701  {
3702  std::ostringstream error_message;
3703  error_message << "The distribution of the global vector v must match the "
3704  << " specified master_distribution_pt(). \n"
3705  << "i.e. Distribution_pt in the master preconditioner";
3706  throw OomphLibError(
3707  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3708  }
3709  for (unsigned b = 0; b < nblock; b++)
3710  {
3711  if (!s[b].built())
3712  {
3713  std::ostringstream error_message;
3714  error_message << "The distribution of the block vector " << b
3715  << " must be setup.";
3716  throw OomphLibError(error_message.str(),
3719  }
3720  const unsigned required_block = block_vec_number[b];
3721  if (*(s[b].distribution_pt()) !=
3722  *(Internal_block_distribution_pt[required_block]))
3723  {
3724  std::ostringstream error_message;
3725  error_message
3726  << "The distribution of the block vector " << b << " must match the"
3727  << " specified distribution at Internal_block_distribution_pt[" << b
3728  << "]";
3729  throw OomphLibError(error_message.str(),
3732  }
3733  }
3734 #endif
3735 
3736  // if + only one processor
3737  // + more than one processor but matrix_pt is not distributed
3738  // then use the serial get_block method
3739  if (this->distribution_pt()->communicator_pt()->nproc() == 1 ||
3740  !this->distribution_pt()->distributed())
3741  {
3742  double* v_pt = v.values_pt();
3743  for (unsigned b = 0; b < nblock; b++)
3744  {
3745  const unsigned required_block = block_vec_number[b];
3746 
3747  const double* s_pt = s[b].values_pt();
3748  unsigned nrow = this->internal_block_dimension(required_block);
3749  for (unsigned i = 0; i < nrow; i++)
3750  {
3751  v_pt[this->Global_index[required_block][i]] = s_pt[i];
3752  }
3753  }
3754  }
3755  // otherwise use mpi
3756  else
3757  {
3758 #ifdef OOMPH_HAS_MPI
3759 
3760  // my rank
3761  unsigned my_rank = this->distribution_pt()->communicator_pt()->my_rank();
3762 
3763  // the number of processors
3764  unsigned nproc = this->distribution_pt()->communicator_pt()->nproc();
3765 
3766  // determine the maximum number of rows to be sent or recv
3767  // and determine the number of blocks each processor will send and recv
3768  // communication for
3769  Vector<int> nblock_send(nproc, 0);
3770  Vector<int> nblock_recv(nproc, 0);
3771  unsigned max_n_send_or_recv = 0;
3772  for (unsigned p = 0; p < nproc; p++)
3773  {
3774  for (unsigned b = 0; b < nblock; b++)
3775  {
3776  const unsigned required_block = block_vec_number[b];
3777 
3778  max_n_send_or_recv = std::max(
3779  max_n_send_or_recv, Nrows_to_send_for_get_block(required_block, p));
3780  max_n_send_or_recv = std::max(
3781  max_n_send_or_recv, Nrows_to_recv_for_get_block(required_block, p));
3782  if (Nrows_to_send_for_get_block(required_block, p) > 0)
3783  {
3784  nblock_recv[p]++;
3785  }
3786  if (Nrows_to_recv_for_get_block(required_block, p) > 0)
3787  {
3788  nblock_send[p]++;
3789  }
3790  }
3791  }
3792 
3793  // create a vectors of 1s the size of the nblock for the mpi indexed
3794  // data types
3795  int* block_lengths = new int[max_n_send_or_recv];
3796  for (unsigned i = 0; i < max_n_send_or_recv; i++)
3797  {
3798  block_lengths[i] = 1;
3799  }
3800 
3801  // perform the sends and receives
3802  Vector<MPI_Request> requests;
3803  for (unsigned p = 0; p < nproc; p++)
3804  {
3805  // send and recv with other processors
3806  if (p != my_rank)
3807  {
3808  // recv
3809  if (nblock_recv[p] > 0)
3810  {
3811  // create the datatypes vector
3812  MPI_Datatype block_recv_types[nblock_recv[p]];
3813 
3814  // create the datatypes
3815  unsigned ptr = 0;
3816  for (unsigned b = 0; b < nblock; b++)
3817  {
3818  const unsigned required_block = block_vec_number[b];
3819 
3820  if (Nrows_to_send_for_get_block(required_block, p) > 0)
3821  {
3822  MPI_Type_indexed(Nrows_to_send_for_get_block(required_block, p),
3823  block_lengths,
3824  Rows_to_send_for_get_block(required_block, p),
3825  MPI_DOUBLE,
3826  &block_recv_types[ptr]);
3827  MPI_Type_commit(&block_recv_types[ptr]);
3828  ptr++;
3829  }
3830  }
3831 
3832  // compute the displacements and lengths
3833  MPI_Aint displacements[nblock_recv[p]];
3834  int lengths[nblock_recv[p]];
3835  for (int i = 0; i < nblock_recv[p]; i++)
3836  {
3837  lengths[i] = 1;
3838  displacements[i] = 0;
3839  }
3840 
3841  // build the final datatype
3842  MPI_Datatype type_recv;
3843  MPI_Type_create_struct(nblock_recv[p],
3844  lengths,
3845  displacements,
3846  block_recv_types,
3847  &type_recv);
3848  MPI_Type_commit(&type_recv);
3849 
3850  // recv
3851  MPI_Request recv_req;
3852  MPI_Irecv(v.values_pt(),
3853  1,
3854  type_recv,
3855  p,
3856  0,
3857  this->distribution_pt()->communicator_pt()->mpi_comm(),
3858  &recv_req);
3859  MPI_Type_free(&type_recv);
3860  for (int i = 0; i < nblock_recv[p]; i++)
3861  {
3862  MPI_Type_free(&block_recv_types[i]);
3863  }
3864  requests.push_back(recv_req);
3865  }
3866 
3867  // send
3868  if (nblock_send[p] > 0)
3869  {
3870  // create the datatypes vector
3871  MPI_Datatype block_send_types[nblock_send[p]];
3872 
3873  // and the displacements
3874  MPI_Aint displacements[nblock_send[p]];
3875 
3876  // and the lengths
3877  int lengths[nblock_send[p]];
3878 
3879  // all displacements are computed relative to s[0] values
3880  MPI_Aint displacements_base;
3881  MPI_Get_address(const_cast<double*>(s[0].values_pt()),
3882  &displacements_base);
3883 
3884  // now build
3885  unsigned ptr = 0;
3886  for (unsigned b = 0; b < nblock; b++)
3887  {
3888  const unsigned required_block = block_vec_number[b];
3889 
3890  if (Nrows_to_recv_for_get_block(required_block, p) > 0)
3891  {
3892  MPI_Type_indexed(Nrows_to_recv_for_get_block(required_block, p),
3893  block_lengths,
3894  Rows_to_recv_for_get_block(required_block, p),
3895  MPI_DOUBLE,
3896  &block_send_types[ptr]);
3897  MPI_Type_commit(&block_send_types[ptr]);
3898  MPI_Get_address(const_cast<double*>(s[b].values_pt()),
3899  &displacements[ptr]);
3900  displacements[ptr] -= displacements_base;
3901  lengths[ptr] = 1;
3902  ptr++;
3903  }
3904  }
3905 
3906  // build the final data type
3907  MPI_Datatype type_send;
3908  MPI_Type_create_struct(nblock_send[p],
3909  lengths,
3910  displacements,
3911  block_send_types,
3912  &type_send);
3913  MPI_Type_commit(&type_send);
3914 
3915  // send
3916  MPI_Request send_req;
3917  MPI_Isend(const_cast<double*>(s[0].values_pt()),
3918  1,
3919  type_send,
3920  p,
3921  0,
3922  this->distribution_pt()->communicator_pt()->mpi_comm(),
3923  &send_req);
3924  MPI_Type_free(&type_send);
3925  for (int i = 0; i < nblock_send[p]; i++)
3926  {
3927  MPI_Type_free(&block_send_types[i]);
3928  }
3929  requests.push_back(send_req);
3930  }
3931  }
3932 
3933  // communicate wih self
3934  else
3935  {
3936  double* v_values_pt = v.values_pt();
3937  for (unsigned b = 0; b < nblock; b++)
3938  {
3939  const unsigned required_block = block_vec_number[b];
3940 
3941  const double* w_values_pt = s[b].values_pt();
3942  for (unsigned i = 0;
3943  i < Nrows_to_send_for_get_block(required_block, p);
3944  i++)
3945  {
3946  v_values_pt[Rows_to_send_for_get_block(required_block, p)[i]] =
3947  w_values_pt[Rows_to_recv_for_get_block(required_block, p)[i]];
3948  }
3949  }
3950  }
3951  }
3952 
3953  // and then just wait
3954  unsigned c = requests.size();
3955  Vector<MPI_Status> stat(c);
3956  if (c)
3957  {
3958  MPI_Waitall(c, &requests[0], &stat[0]);
3959  }
3960  delete[] block_lengths;
3961 
3962 #else
3963  // throw error
3964  std::ostringstream error_message;
3965  error_message << "The preconditioner is distributed and on more than one "
3966  << "processor. MPI is required.";
3967  throw OomphLibError(
3968  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3969 #endif
3970  }
3971  }

References b, calibrate::c, i, max, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, p, s, and v.

◆ is_master_block_preconditioner()

◆ is_subsidiary_block_preconditioner()

◆ master_block_preconditioner_pt()

template<typename MATRIX >
BlockPreconditioner<MATRIX>* oomph::BlockPreconditioner< MATRIX >::master_block_preconditioner_pt ( ) const
inline

Access function to the master block preconditioner pt.

2142  {
2143 #ifdef PARANOID
2145  {
2146  std::ostringstream error_message;
2147  error_message << "This block preconditioner does not have "
2148  << "a master preconditioner.";
2149  throw OomphLibError(error_message.str(),
2152  }
2153 #endif
2155  } // EOFunc master_block_preconditioner_pt()

References oomph::BlockPreconditioner< MATRIX >::is_master_block_preconditioner(), oomph::BlockPreconditioner< MATRIX >::Master_block_preconditioner_pt, OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Referenced by oomph::BlockPreconditioner< MATRIX >::matrix_pt(), oomph::BlockPreconditioner< MATRIX >::set_master_matrix_pt(), oomph::BlockPreconditioner< MATRIX >::turn_off_recursive_debug_flag(), and oomph::BlockPreconditioner< MATRIX >::turn_on_recursive_debug_flag().

◆ master_distribution_pt()

template<typename MATRIX >
const LinearAlgebraDistribution* oomph::BlockPreconditioner< MATRIX >::master_distribution_pt ( ) const
inline

Access function to the distribution of the master preconditioner. If this preconditioner does not have a master preconditioner then the distribution of this preconditioner is returned.

2018  {
2020  {
2021  return this->distribution_pt();
2022  }
2023  else
2024  {
2025  return Master_block_preconditioner_pt->master_distribution_pt();
2026  }
2027  } // EOFunc master_distribution_pt(...)

References oomph::DistributableLinearAlgebraObject::distribution_pt(), oomph::BlockPreconditioner< MATRIX >::is_master_block_preconditioner(), and oomph::BlockPreconditioner< MATRIX >::Master_block_preconditioner_pt.

Referenced by oomph::BlockPreconditioner< MATRIX >::document().

◆ master_nrow()

template<typename MATRIX >
unsigned oomph::BlockPreconditioner< MATRIX >::master_nrow ( ) const
inlineprotected

Return the number of dofs (number of rows or columns) in the overall problem. The prefix "master_" is sort of redundant when used as a stand-alone block preconditioner but is required to avoid ambiguities. The latter is stored (and maintained) separately for each specific block preconditioner regardless of its role.

3223  {
3225  {
3226  return Nrow;
3227  }
3228  else
3229  {
3230  return (this->Master_block_preconditioner_pt->master_nrow());
3231  }
3232  }

References oomph::BlockPreconditioner< MATRIX >::is_master_block_preconditioner(), oomph::BlockPreconditioner< MATRIX >::Master_block_preconditioner_pt, and oomph::BlockPreconditioner< MATRIX >::Nrow.

◆ matrix_pt()

template<typename MATRIX >
MATRIX* oomph::BlockPreconditioner< MATRIX >::matrix_pt ( ) const
inlinevirtual

Access function to matrix_pt. If this is the master then cast the matrix pointer to MATRIX*, error check and return. Otherwise ask the master for its matrix pointer.

Reimplemented from oomph::Preconditioner.

521  {
523  {
524  return master_block_preconditioner_pt()->matrix_pt();
525  }
526  else
527  {
528  MATRIX* m_pt = dynamic_cast<MATRIX*>(Preconditioner::matrix_pt());
529 #ifdef PARANOID
530  if (m_pt == 0)
531  {
532  std::ostringstream error_msg;
533  error_msg << "Matrix is not correct type.";
534  throw OomphLibError(
536  }
537 #endif
538  return m_pt;
539  }
540  } // EOFunc matrix_pt()
virtual DoubleMatrixBase * matrix_pt() const
Get function for matrix pointer.
Definition: preconditioner.h:150

References oomph::BlockPreconditioner< MATRIX >::is_subsidiary_block_preconditioner(), oomph::BlockPreconditioner< MATRIX >::master_block_preconditioner_pt(), oomph::Preconditioner::matrix_pt(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Referenced by oomph::BlockPreconditioner< MATRIX >::get_block_other_matrix(), oomph::HelmholtzGMRESMG< MATRIX >::resolve(), oomph::HelmholtzGMRESMG< MATRIX >::solve(), and oomph::HelmholtzFGMRESMG< MATRIX >::solve().

◆ mesh_pt()

template<typename MATRIX >
const Mesh* oomph::BlockPreconditioner< MATRIX >::mesh_pt ( const unsigned i) const
inline

Access to i-th mesh (of the various meshes that contain block preconditionable elements of the same number of dof type).

WARNING: This should only be used if the derived class is the upper-most master block preconditioner. An error is thrown is this function is called from a subsidiary preconditioner. They (and since every block preconditioner can in principle be used as s subsidiary preconditioner: all block preconditioners) should store local copies of "their meshes" (if they're needed for anything)

1783  {
1784 #ifdef PARANOID
1786  {
1787  std::ostringstream error_msg;
1788  error_msg << "The mesh_pt() function should not be called on a\n"
1789  << "subsidiary block preconditioner." << std::endl;
1790  throw OomphLibError(
1792  }
1793 #endif
1794 
1795  const Mesh* mesh_i_pt = Mesh_pt[i];
1796 
1797 #ifdef PARANOID
1798  if (mesh_i_pt == 0)
1799  {
1800  std::ostringstream error_msg;
1801  error_msg << "Mesh pointer " << mesh_i_pt << " is null.";
1802  throw OomphLibError(
1804  }
1805 #endif
1806 
1807  return mesh_i_pt;
1808  } // EOFunc mesh_pt(...)

References i, oomph::BlockPreconditioner< MATRIX >::is_subsidiary_block_preconditioner(), oomph::BlockPreconditioner< MATRIX >::Mesh_pt, OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Referenced by oomph::SimpleBlockDiagonalPreconditioner< MATRIX >::add_mesh(), oomph::GeneralPurposeBlockPreconditioner< MATRIX >::add_mesh(), oomph::BlockPreconditioner< MATRIX >::any_mesh_distributed(), oomph::BlockPreconditioner< MATRIX >::ndof_types_in_mesh(), oomph::BlockPreconditioner< MATRIX >::set_mesh(), SimpleFSIPreconditioner< MATRIX >::set_navier_stokes_mesh(), oomph::SimpleFSIPreconditioner< MATRIX >::set_navier_stokes_mesh(), SimpleFSIPreconditioner< MATRIX >::set_solid_mesh(), and oomph::SimpleFSIPreconditioner< MATRIX >::set_wall_mesh().

◆ nblock_types()

template<typename MATRIX >
unsigned oomph::BlockPreconditioner< MATRIX >::nblock_types ( ) const
inline

Return the number of block types.

1671  {
1672 #ifdef PARANOID
1673  if (Block_to_dof_map_coarse.size() == 0)
1674  {
1675  std::ostringstream error_msg;
1676  error_msg
1677  << "The Block_to_dof_map_coarse vector is not setup for \n"
1678  << "this block preconditioner.\n\n"
1679 
1680  << "This vector is always set up within block_setup(...).\n"
1681  << "If block_setup() is already called, then perhaps there is\n"
1682  << "something wrong with your block preconditionable elements.\n"
1683  << std::endl;
1684  throw OomphLibError(
1686  }
1687 #endif
1688 
1689  // Return the number of block types.
1690  return Block_to_dof_map_coarse.size();
1691  } // EOFunc nblock_types(...)

References oomph::BlockPreconditioner< MATRIX >::Block_to_dof_map_coarse, OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Referenced by oomph::BlockPreconditioner< MATRIX >::block_distribution_pt(), oomph::BlockPreconditioner< MATRIX >::document(), oomph::BlockPreconditioner< MATRIX >::get_block(), oomph::BlockPreconditioner< MATRIX >::get_concatenated_block(), oomph::BlockPreconditioner< MATRIX >::insert_auxiliary_block_distribution(), oomph::BlockPreconditioner< MATRIX >::set_replacement_dof_block(), oomph::HelmholtzGMRESMG< MATRIX >::solve(), and oomph::HelmholtzFGMRESMG< MATRIX >::solve().

◆ ndof_types()

template<typename MATRIX >
unsigned oomph::BlockPreconditioner< MATRIX >::ndof_types ( ) const
inline

Return the total number of DOF types.

1695  {
1696 #ifdef PARANOID
1697  // Subsidiary preconditioners don't really need the meshes
1698  if (this->is_master_block_preconditioner())
1699  {
1700  std::ostringstream err_msg;
1701  unsigned n = nmesh();
1702  if (n == 0)
1703  {
1704  err_msg << "No meshes have been set for this block preconditioner!\n"
1705  << "Set one with set_nmesh(...), set_mesh(...)" << std::endl;
1706  throw OomphLibError(
1708  for (unsigned m = 0; m < n; m++)
1709  {
1710  if (Mesh_pt[m] == 0)
1711  {
1712  err_msg << "The mesh pointer to mesh " << m << " is null!\n"
1713  << "Set a non-null one with set_mesh(...)" << std::endl;
1714  throw OomphLibError(err_msg.str(),
1717  }
1718  }
1719  }
1720  }
1721 #endif
1722 
1723  // If this is a subsidiary block preconditioner, then the function
1724  // turn_into_subsidiary_block_preconditioner(...) should have been called,
1725  // this function would have set up the look up lists between coarsened
1726  // dof types and the internal dof types. Of coarse, the user (the writer
1727  // of the preconditioners) should not care about the internal dof types
1728  // and what happens under the hood. Thus they should get the number of
1729  // coarsened dof types (i.e. the number of dof types the preconditioner
1730  // above (parent preconditioner) decides to give to this preconditioner).
1732  {
1733 #ifdef PARANOID
1734  if (Doftype_coarsen_map_coarse.size() == 0)
1735  {
1736  std::ostringstream error_msg;
1737  error_msg
1738  << "The Doftype_coarsen_map_coarse vector is not setup for \n"
1739  << "this SUBSIDIARY block preconditioner.\n\n"
1740 
1741  << "For SUBSIDARY block preconditioners at any level, this\n"
1742  << "vector is set up in the function \n"
1743  << "turn_into_subsidiary_block_preconditioner(...).\n\n"
1744 
1745  << "Being a SUBSIDIARY block preconditioner means that \n"
1746  << "(Master_block_preconditioner_pt == 0) is true.\n"
1747  << "The Master_block_preconditioner_pt MUST be set in the "
1748  << "function \n"
1749  << "turn_into_subsidiary_block_preconditioner(...).\n\n"
1750 
1751  << "Somewhere, the Master_block_preconditioner_pt pointer is\n"
1752  << "set but not by the function\n"
1753  << "turn_into_subsidiary_block_preconditioner(...).\n"
1754  << std::endl;
1755  throw OomphLibError(
1757  }
1758 #endif
1759  // return the number of dof types.
1760  return Doftype_coarsen_map_coarse.size();
1761  }
1762  else
1763  // Otherwise the number of ndof types is the same as the internal number
1764  // of dof types, since no coarsening of the dof types is done at the
1765  // top-most master level.
1766  {
1767  return internal_ndof_types();
1768  }
1769  } // EOFunc ndof_types(...)

References oomph::BlockPreconditioner< MATRIX >::Doftype_coarsen_map_coarse, oomph::BlockPreconditioner< MATRIX >::internal_ndof_types(), oomph::BlockPreconditioner< MATRIX >::is_master_block_preconditioner(), oomph::BlockPreconditioner< MATRIX >::is_subsidiary_block_preconditioner(), m, oomph::BlockPreconditioner< MATRIX >::Mesh_pt, n, oomph::BlockPreconditioner< MATRIX >::nmesh(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Referenced by oomph::BlockPreconditioner< MATRIX >::dof_block_distribution_pt(), oomph::BlockPreconditioner< MATRIX >::get_fine_grain_dof_types_in(), oomph::BlockPreconditioner< MATRIX >::nfine_grain_dof_types_in(), and oomph::BlockPreconditioner< MATRIX >::set_replacement_dof_block().

◆ ndof_types_in_mesh()

template<typename MATRIX >
unsigned oomph::BlockPreconditioner< MATRIX >::ndof_types_in_mesh ( const unsigned i) const
inline

Return the number of DOF types in mesh i. WARNING: This should only be used by the upper-most master block preconditioner. An error is thrown is this function is called from a subsidiary preconditioner. They (and since every block preconditioner can in principle be used as s subsidiary preconditioner: all block preconditioners) should store local copies of "their meshes" (if they're needed for anything)

2038  {
2039 #ifdef PARANOID
2041  {
2042  std::ostringstream err_msg;
2043  err_msg << "A subsidiary block preconditioner should not care about\n"
2044  << "anything to do with meshes.";
2045  throw OomphLibError(
2047  }
2048 #endif
2049  if (Ndof_types_in_mesh.size() == 0)
2050  {
2051  return mesh_pt(i)->ndof_types();
2052  }
2053  else
2054  {
2055  return Ndof_types_in_mesh[i];
2056  }
2057  } // EOFunc ndof_types_in_mesh(...)

References i, oomph::BlockPreconditioner< MATRIX >::is_subsidiary_block_preconditioner(), oomph::BlockPreconditioner< MATRIX >::mesh_pt(), oomph::Mesh::ndof_types(), oomph::BlockPreconditioner< MATRIX >::Ndof_types_in_mesh, OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Referenced by oomph::BlockPreconditioner< MATRIX >::internal_ndof_types().

◆ nfine_grain_dof_types_in()

template<typename MATRIX >
unsigned oomph::BlockPreconditioner< MATRIX >::nfine_grain_dof_types_in ( const unsigned i) const
inline

Access function for the number of most fine grain dof types in a (possibly coarsened) dof type.

2362  {
2363 #ifdef PARANOID
2364  const unsigned n_dof_types = ndof_types();
2365 
2366  if (i >= n_dof_types)
2367  {
2368  std::ostringstream err_msg;
2369  err_msg << "Trying to get the number of most fine grain dof types "
2370  << "in dof type " << i << ",\n"
2371  << "but there are only " << n_dof_types
2372  << " number of dof types.\n";
2373  throw OomphLibError(
2375  }
2376 #endif
2377  return Doftype_coarsen_map_fine[i].size();
2378  }

References oomph::BlockPreconditioner< MATRIX >::Doftype_coarsen_map_fine, i, oomph::BlockPreconditioner< MATRIX >::ndof_types(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

◆ nmesh()

template<typename MATRIX >
unsigned oomph::BlockPreconditioner< MATRIX >::nmesh ( ) const
inline

Return the number of meshes in Mesh_pt.

WARNING: This should only be used if the derived class is the upper-most master block preconditioner. All block preconditioners) should store local copies of "their meshes" (if they're needed for anything)

1817  {
1818  return Mesh_pt.size();
1819  } // EOFunc nmesh()

References oomph::BlockPreconditioner< MATRIX >::Mesh_pt.

Referenced by oomph::BlockPreconditioner< MATRIX >::any_mesh_distributed(), oomph::GeneralPurposeBlockPreconditioner< MATRIX >::gp_preconditioner_set_all_meshes(), oomph::BlockPreconditioner< MATRIX >::internal_ndof_types(), oomph::BlockPreconditioner< MATRIX >::ndof_types(), and oomph::BlockPreconditioner< MATRIX >::set_mesh().

◆ operator=()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::operator= ( const BlockPreconditioner< MATRIX > &  )
delete

Broken assignment operator.

◆ output_blocks_to_files()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::output_blocks_to_files ( const std::string &  basefilename,
const unsigned precision = 8 
) const
inline

Output all blocks to numbered files. Called at the end of get blocks if an output filename has been set.

2097  {
2098  unsigned nblocks = internal_nblock_types();
2099 
2100  for (unsigned i = 0; i < nblocks; i++)
2101  {
2102  for (unsigned j = 0; j < nblocks; j++)
2103  {
2104  // Construct the filename.
2105  std::string filename(basefilename + "_block_" +
2108 
2109  // Write out the block.
2110  get_block(i, j).sparse_indexed_output(filename, precision, true);
2111  }
2112  }
2113  } // EOFunc output_blocks_to_files(...)
string filename
Definition: MergeRestartFiles.py:39
std::string to_string(T object, unsigned float_precision=8)
Definition: oomph_utilities.h:189

References MergeRestartFiles::filename, oomph::BlockPreconditioner< MATRIX >::get_block(), i, oomph::BlockPreconditioner< MATRIX >::internal_nblock_types(), j, oomph::Global_string_for_annotation::string(), and oomph::StringConversion::to_string().

◆ post_block_matrix_assembly_partial_clear()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::post_block_matrix_assembly_partial_clear ( )
inline

A helper method to reduce the memory requirements of block preconditioners. Once the methods get_block(...), get_blocks(...) and build_preconditioner_matrix(...) have been called in this and all subsidiary block preconditioners this method can be called to clean up.

2121  {
2123  {
2124  Index_in_dof_block_dense.clear();
2125  Dof_number_dense.clear();
2126 #ifdef OOMPH_HAS_MPI
2127  Index_in_dof_block_sparse.clear();
2128  Dof_number_sparse.clear();
2129  Global_index_sparse.clear();
2130  Index_in_dof_block_sparse.clear();
2131  Dof_number_sparse.clear();
2132 #endif
2133  Dof_dimension.clear();
2134  }
2135  Ndof_in_block.clear();
2138  } // EOFunc post_block_matrix_assembly_partial_clear()

References oomph::BlockPreconditioner< MATRIX >::Block_number_to_dof_number_lookup, oomph::BlockPreconditioner< MATRIX >::Dof_dimension, oomph::BlockPreconditioner< MATRIX >::Dof_number_dense, oomph::BlockPreconditioner< MATRIX >::Dof_number_to_block_number_lookup, oomph::BlockPreconditioner< MATRIX >::Index_in_dof_block_dense, oomph::BlockPreconditioner< MATRIX >::is_master_block_preconditioner(), and oomph::BlockPreconditioner< MATRIX >::Ndof_in_block.

Referenced by oomph::BlockPreconditioner< MATRIX >::clear_block_preconditioner_base().

◆ preconditioner_matrix_distribution_pt()

template<typename MATRIX >
const LinearAlgebraDistribution* oomph::BlockPreconditioner< MATRIX >::preconditioner_matrix_distribution_pt ( ) const
inlineprotected

Access function to the preconditioner matrix distribution pointer. This is the concatenation of the block distributions with the identity ordering. I.e. if this preconditioner has three block types, with the three associated block distributions dist_b0, dist_b1 and dist_b2, then this distribution is: LinearAlgebraDistributionHelpers::concatenate(dist_b0, dist_b1, dist_b2).

3269  {
3271  }

References oomph::BlockPreconditioner< MATRIX >::Preconditioner_matrix_distribution_pt.

Referenced by oomph::BlockPreconditioner< MATRIX >::document().

◆ replacement_dof_block_pt()

template<typename MATRIX >
MapMatrix<unsigned, CRDoubleMatrix*> oomph::BlockPreconditioner< MATRIX >::replacement_dof_block_pt ( ) const
inline

Access function to the replaced dof-level blocks.

2382  {
2383  return Replacement_dof_block_pt;
2384  } // EOFunc replacement_block_pt()

References oomph::BlockPreconditioner< MATRIX >::Replacement_dof_block_pt.

Referenced by oomph::BlockPreconditioner< MATRIX >::set_replacement_dof_block().

◆ return_block_ordered_preconditioner_vector()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::return_block_ordered_preconditioner_vector ( const DoubleVector w,
DoubleVector v 
) const

Takes the block ordered vector, w, and reorders it in natural order. Reordered vector is returned in v. Note: If the preconditioner is a subsidiary preconditioner then only the components of the vector associated with the blocks of the subsidiary preconditioner will be included. Hence the length of v is master_nrow() whereas that of the vector w is of length this->nrow().

This is the return function for the function get_block_ordered_preconditioner_vector(...).

It calls the function return_concatenated_block_vector(...) with the identity block number ordering.

5048  {
5049 #ifdef PARANOID
5050  if (!v.built())
5051  {
5052  std::ostringstream error_message;
5053  error_message << "The distribution of the global vector v must be setup.";
5054  throw OomphLibError(
5055  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
5056  }
5057  if (*v.distribution_pt() != *this->master_distribution_pt())
5058  {
5059  std::ostringstream error_message;
5060  error_message << "The distribution of the global vector v must match the "
5061  << " specified master_distribution_pt(). \n"
5062  << "i.e. Distribution_pt in the master preconditioner";
5063  throw OomphLibError(
5064  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
5065  }
5066  if (!w.built())
5067  {
5068  std::ostringstream error_message;
5069  error_message << "The distribution of the block vector w must be setup.";
5070  throw OomphLibError(
5071  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
5072  }
5073  if (*w.distribution_pt() != *this->preconditioner_matrix_distribution_pt())
5074  {
5075  std::ostringstream error_message;
5076  error_message << "The distribution of the block vector w must match the "
5077  << "concatenations of distributions in "
5078  << "Block_distribution_pt.\n";
5079  throw OomphLibError(
5080  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
5081  }
5082 #endif
5083 
5084  // Split into the block vectors.
5085  const unsigned nblocks = nblock_types();
5086  Vector<unsigned> block_vec_number(nblocks, 0);
5087  for (unsigned b = 0; b < nblocks; b++)
5088  {
5089  block_vec_number[b] = b;
5090  }
5091 
5092  return_concatenated_block_vector(block_vec_number, w, v);
5093  } // function return_block_ordered_preconditioner_vector
void return_concatenated_block_vector(const Vector< unsigned > &block_vec_number, const DoubleVector &b, DoubleVector &v) const
Takes concatenated block ordered vector, b, and copies its.
Definition: block_preconditioner.cc:2774

References b, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, v, and w.

◆ return_block_vector()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::return_block_vector ( const unsigned n,
const DoubleVector b,
DoubleVector v 
) const

Takes the n-th block ordered vector, b, and copies its entries to the appropriate entries in the naturally ordered vector, v. Here n is the block number in the current block preconditioner. If the preconditioner is a subsidiary block preconditioner the other entries in v that are not associated with it are left alone.

4492  {
4493 #ifdef PARANOID
4494  // the number of blocks
4495  const unsigned para_n_blocks = nblock_types();
4496 
4497  // paranoid check that block i is in this block preconditioner
4498  if (n >= para_n_blocks)
4499  {
4500  std::ostringstream err_msg;
4501  err_msg << "Requested block vector " << b
4502  << ", however this preconditioner has " << para_n_blocks
4503  << " block types.\n";
4504  throw OomphLibError(
4506  }
4507  if (!v.built())
4508  {
4509  std::ostringstream err_msg;
4510  err_msg << "The distribution of the global vector v must be setup.";
4511  throw OomphLibError(
4513  }
4514  if (*v.distribution_pt() != *this->master_distribution_pt())
4515  {
4516  std::ostringstream err_msg;
4517  err_msg << "The distribution of the global vector v must match the "
4518  << " specified master_distribution_pt(). \n"
4519  << "i.e. Distribution_pt in the master preconditioner";
4520  throw OomphLibError(
4522  }
4523  if (!b.built())
4524  {
4525  std::ostringstream err_msg;
4526  err_msg << "The distribution of the block vector b must be setup.";
4527  throw OomphLibError(
4529  }
4530 
4531 #endif
4532 
4533  // Get the most fine grain dof
4534  Vector<unsigned> most_fine_grain_dof = Block_to_dof_map_fine[n];
4535 
4536  // How many dofs are in this block?
4537  const unsigned n_dof_vec = Block_to_dof_map_fine[n].size();
4538 
4539  if (n_dof_vec == 1)
4540  // There is only one dof, no need to split.
4541  {
4542  internal_return_block_vector(most_fine_grain_dof[0], b, v);
4543  }
4544  else
4545  // Need to split the vector up before we insert them all in one go.
4546  {
4547  Vector<DoubleVector> dof_vector(n_dof_vec);
4548  for (unsigned d = 0; d < n_dof_vec; d++)
4549  {
4550  dof_vector[d].build(
4551  internal_block_distribution_pt(most_fine_grain_dof[d]));
4552  }
4553 
4555 
4556  // return to v
4557  internal_return_block_vectors(most_fine_grain_dof, dof_vector, v);
4558  }
4559  } // return_block_vector(...)
void internal_return_block_vector(const unsigned &n, const DoubleVector &b, DoubleVector &v) const
Definition: block_preconditioner.cc:4293
void split_without_communication(const DoubleVector &in_vector, Vector< DoubleVector * > &out_vector_pt)
Definition: double_vector.cc:2075

References b, n, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, oomph::DoubleVectorHelpers::split_without_communication(), and v.

◆ return_block_vectors() [1/2]

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::return_block_vectors ( const Vector< DoubleVector > &  s,
DoubleVector v 
) const

Takes the vector of block vectors, s, and copies its entries into the naturally ordered vector, v. If this is a subsidiary block preconditioner only those entries in v that are associated with its blocks are affected. The block_vec_number indicates which block the vectors in s came from. The block number corresponds to the block numbers in this preconditioner. This is simply a wrapper around the other return_block_vectors(...) function where the block_vec_number Vector is the identity, i.e. block_vec_number is [0, 1, ..., nblock_types - 1].

3657  {
3658  // The number of block types in this preconditioner.
3659  const unsigned n_block = nblock_types();
3660 
3661  // Create the identity vector.
3662  Vector<unsigned> required_block(n_block, 0);
3663  for (unsigned i = 0; i < n_block; i++)
3664  {
3665  required_block[i] = i;
3666  }
3667 
3668  // Call the other return_block_vectors function which does the work.
3669  return_block_vectors(required_block, s, v);
3670  } // return_block_vectors(...)
void return_block_vectors(const Vector< unsigned > &block_vec_number, const Vector< DoubleVector > &s, DoubleVector &v) const
Definition: block_preconditioner.cc:3443

References i, s, and v.

◆ return_block_vectors() [2/2]

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::return_block_vectors ( const Vector< unsigned > &  block_vec_number,
const Vector< DoubleVector > &  s,
DoubleVector v 
) const

Takes the vector of block vectors, s, and copies its entries into the naturally ordered vector, v. If this is a subsidiary block preconditioner only those entries in v that are associated with its blocks are affected. The block_vec_number indicates which block the vectors in s came from. The block number corresponds to the block numbers in this preconditioner.

3447  {
3448 #ifdef PARANOID
3449 
3450  // Check if v is built.
3451  if (!v.built())
3452  {
3453  std::ostringstream err_msg;
3454  err_msg << "The distribution of the global vector v must be setup.";
3455  throw OomphLibError(
3457  }
3458 
3459  // v must have the same distribution as the upper-most master block
3460  // preconditioner, since the upper-most master block preconditioner
3461  // should have the same distribution as the matrix pointed to
3462  // by matrix_pt().
3463  if (*(v.distribution_pt()) != *(this->master_distribution_pt()))
3464  {
3465  std::ostringstream err_msg;
3466  err_msg << "The distribution of the global vector v must match the "
3467  << " specified master_distribution_pt(). \n"
3468  << "i.e. Distribution_pt in the master preconditioner";
3469  throw OomphLibError(
3471  }
3472 
3473  // Check if the number of vectors in s is the same as the number of block
3474  // numbers described in block_vec_number.
3475  const unsigned para_block_vec_number_size = block_vec_number.size();
3476  const unsigned para_s_size = s.size();
3477  if (para_block_vec_number_size != para_s_size)
3478  {
3479  std::ostringstream err_msg;
3480  err_msg << "block_vec_number.size() is " << para_block_vec_number_size
3481  << "\n."
3482  << "s.size() is " << para_s_size << ".\n"
3483  << "But they must be the same size!\n";
3484  throw OomphLibError(
3486  }
3487 
3488  // Check to see if there are more blocks defined in the block_vec_number
3489  // vector than the number of block types. This is not allowed.
3490  const unsigned para_n_block = nblock_types();
3491  if (para_block_vec_number_size > para_n_block)
3492  {
3493  std::ostringstream err_msg;
3494  err_msg << "Trying to return " << para_block_vec_number_size
3495  << " block vectors.\n"
3496  << "But there are only " << para_n_block << " block types.\n";
3497  throw OomphLibError(
3499  }
3500 
3501  // Check if any block numbers defined in block_vec_number is equal to or
3502  // greater than the number of block types.
3503  // E.g. if there are 5 block types, we can only have block numbers:
3504  // 0, 1, 2, 3 and 4.
3505  for (unsigned b = 0; b < para_block_vec_number_size; b++)
3506  {
3507  const unsigned para_required_block = block_vec_number[b];
3508  if (para_required_block > para_n_block)
3509  {
3510  std::ostringstream err_msg;
3511  err_msg << "block_vec_number[" << b << "] is " << para_required_block
3512  << ".\n"
3513  << "But there are only " << para_n_block << " block types.\n";
3514  throw OomphLibError(
3516  }
3517  }
3518 
3519  // Check that no block number is inserted twice.
3520  std::set<unsigned> para_set;
3521  for (unsigned b = 0; b < para_block_vec_number_size; b++)
3522  {
3523  std::pair<std::set<unsigned>::iterator, bool> para_set_ret;
3524  para_set_ret = para_set.insert(block_vec_number[b]);
3525 
3526  if (!para_set_ret.second)
3527  {
3528  std::ostringstream err_msg;
3529  err_msg << "Error: the block number " << block_vec_number[b]
3530  << " appears twice.\n";
3531  throw OomphLibError(
3533  }
3534  }
3535 
3536  // Check to see that all the vectors in s are built
3537  // (since we are trying to return them).
3538  for (unsigned b = 0; b < para_block_vec_number_size; b++)
3539  {
3540  if (!s[b].built())
3541  {
3542  std::ostringstream err_msg;
3543  err_msg << "The distribution of the block vector s[" << b
3544  << "] must be setup.\n";
3545  throw OomphLibError(
3547  }
3548  }
3549 
3550  // Since these are built, we check that the distributions are correct.
3551  // This are incorrect if the block numbers in block_vec_number and
3552  // the vectors in s does not match.
3553  for (unsigned b = 0; b < para_block_vec_number_size; b++)
3554  {
3555  if (*(s[b].distribution_pt()) !=
3556  *(Block_distribution_pt[block_vec_number[b]]))
3557  {
3558  std::ostringstream error_message;
3559  error_message
3560  << "The distribution of the block vector " << b << " must match the"
3561  << " specified distribution at "
3562  << "Block_distribution_pt[" << block_vec_number[b] << "].\n"
3563  << "The distribution of the Block_distribution_pt is determined by\n"
3564  << "the vector block_vec_number. Perhaps it is incorrect?\n";
3565  throw OomphLibError(error_message.str(),
3568  }
3569  }
3570 #endif
3571 
3572  // Number of blocks to get.
3573  const unsigned n_block = block_vec_number.size();
3574 
3575  // Each block is made of dof types. We get the most fine grain dof types.
3576  // Most fine grain in the sense that these are the dof types that belongs
3577  // in this block before any coarsening of dof types has taken place.
3578  // The ordering of the dof types matters, this is handled properly when
3579  // creating the Block_to_dof_map_fine vector and must be respected here.
3580  // I.e. we cannot arbitrarily insert dof types (even if they are correct)
3581  // in the vector most_fine_grain_dof.
3582  Vector<unsigned> most_fine_grain_dof;
3583  for (unsigned b = 0; b < n_block; b++)
3584  {
3585  const unsigned mapped_b = block_vec_number[b];
3586 
3587  most_fine_grain_dof.insert(most_fine_grain_dof.end(),
3588  Block_to_dof_map_fine[mapped_b].begin(),
3589  Block_to_dof_map_fine[mapped_b].end());
3590  }
3591 
3592  // Split all the blocks into it's most fine grain dof vector.
3593  Vector<DoubleVector> dof_vector(most_fine_grain_dof.size());
3594 
3595  unsigned offset = 0;
3596 
3597  // Perform the splitting for each block.
3598  for (unsigned b = 0; b < n_block; b++)
3599  {
3600  // The actual block number.
3601  const unsigned mapped_b = block_vec_number[b];
3602 
3603  // How many most fine grain dof types are associated with this block?
3604  const unsigned ndof = Block_to_dof_map_fine[mapped_b].size();
3605 
3606  if (ndof == 1)
3607  // No need to split, just copy.
3608  {
3609  dof_vector[offset] = s[b];
3610  }
3611  else
3612  // Need to split s[b] into it's most fine grain dof vectors
3613  {
3614  // To store pointers to the dof vectors associated with this block.
3615  Vector<DoubleVector*> tmp_dof_vector_pt(ndof, 0);
3616 
3617  for (unsigned d = 0; d < ndof; d++)
3618  {
3619  const unsigned offset_plus_d = offset + d;
3620 
3621  // build the dof vector.
3622  dof_vector[offset_plus_d].build(
3623  Internal_block_distribution_pt[most_fine_grain_dof[offset_plus_d]]);
3624 
3625  // Store the pointer.
3626  tmp_dof_vector_pt[d] = &dof_vector[offset_plus_d];
3627  }
3628 
3629  // Split without communication.
3631  tmp_dof_vector_pt);
3632  }
3633 
3634  // Update the offset!
3635  offset += ndof;
3636  }
3637 
3638  // Return the block vectors all in one go.
3639  internal_return_block_vectors(most_fine_grain_dof, dof_vector, v);
3640  } // return_block_vectors(...)

References b, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, s, oomph::DoubleVectorHelpers::split_without_communication(), and v.

Referenced by oomph::HelmholtzGMRESMG< MATRIX >::update().

◆ return_concatenated_block_vector()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::return_concatenated_block_vector ( const Vector< unsigned > &  block_vec_number,
const DoubleVector b,
DoubleVector v 
) const

Takes concatenated block ordered vector, b, and copies its.

Takes concatenated block ordered vector, b, and copies its entries to the appropriate entries in the naturally ordered vector, v. Here the values in block_vec_number indicates which blocks the vector b is a concatenation of. The block number are those in the current preconditioner. If the preconditioner is a subsidiary block preconditioner the other entries in v that are not associated with it are left alone.

2778  {
2779 #ifdef PARANOID
2780 
2781  // Check if v is built.
2782  if (!v.built())
2783  {
2784  std::ostringstream err_msg;
2785  err_msg << "The distribution of the global vector v must be setup.";
2786  throw OomphLibError(
2788  }
2789 
2790  // v must have the same distribution as the upper-most master block
2791  // preconditioner, since the upper-most master block preconditioner
2792  // should have the same distribution as the matrix pointed to
2793  // by matrix_pt().
2794  if (*(v.distribution_pt()) != *(this->master_distribution_pt()))
2795  {
2796  std::ostringstream err_msg;
2797  err_msg << "The distribution of the global vector v must match the "
2798  << " specified master_distribution_pt(). \n"
2799  << "i.e. Distribution_pt in the master preconditioner";
2800  throw OomphLibError(
2802  }
2803 
2804  // Check to see if there are more blocks defined in the block_vec_number
2805  // vector than the number of block types. This is not allowed.
2806  const unsigned para_block_vec_number_size = block_vec_number.size();
2807  const unsigned para_n_block = nblock_types();
2808  if (para_block_vec_number_size > para_n_block)
2809  {
2810  std::ostringstream err_msg;
2811  err_msg << "Trying to return " << para_block_vec_number_size
2812  << " block vectors.\n"
2813  << "But there are only " << para_n_block << " block types.\n";
2814  throw OomphLibError(
2816  }
2817 
2818  // Check if any block numbers defined in block_vec_number is equal to or
2819  // greater than the number of block types.
2820  // E.g. if there are 5 block types, we can only have block numbers:
2821  // 0, 1, 2, 3 and 4.
2822  for (unsigned b = 0; b < para_block_vec_number_size; b++)
2823  {
2824  const unsigned para_required_block = block_vec_number[b];
2825  if (para_required_block > para_n_block)
2826  {
2827  std::ostringstream err_msg;
2828  err_msg << "block_vec_number[" << b << "] is " << para_required_block
2829  << ".\n"
2830  << "But there are only " << para_n_block << " block types.\n";
2831  throw OomphLibError(
2833  }
2834  }
2835 
2836  // Check that no block number is inserted twice.
2837  std::set<unsigned> para_set;
2838  for (unsigned b = 0; b < para_block_vec_number_size; b++)
2839  {
2840  std::pair<std::set<unsigned>::iterator, bool> para_set_ret;
2841  para_set_ret = para_set.insert(block_vec_number[b]);
2842 
2843  if (!para_set_ret.second)
2844  {
2845  std::ostringstream err_msg;
2846  err_msg << "Error: the block number " << block_vec_number[b]
2847  << " appears twice.\n";
2848  throw OomphLibError(
2850  }
2851  }
2852 
2853  // Check that w is built.
2854  if (!w.built())
2855  {
2856  std::ostringstream err_msg;
2857  err_msg << "The distribution of the block vector w must be setup.";
2858  throw OomphLibError(
2860  }
2861 
2862  // Check that the distributions defined by block_vec_number is correct for
2863  // the distribution from w.
2864  // Recall that w is the concatenation of the block vectors defined by
2865  // the values in block_vec_number. We check that this is the case.
2866  Vector<LinearAlgebraDistribution*> para_vec_dist_pt(
2867  para_block_vec_number_size, 0);
2868 
2869  for (unsigned b = 0; b < para_block_vec_number_size; b++)
2870  {
2871  para_vec_dist_pt[b] = Block_distribution_pt[block_vec_number[b]];
2872  }
2873 
2874  LinearAlgebraDistribution para_tmp_dist;
2875 
2877  para_tmp_dist);
2878 
2879  if (*w.distribution_pt() != para_tmp_dist)
2880  {
2881  std::ostringstream err_msg;
2882  err_msg << "The distribution of the block vector w does not match \n"
2883  << "the concatenation of the block distributions defined in \n"
2884  << "block_vec_number.\n";
2885  throw OomphLibError(
2887  }
2888 #endif
2889 
2890  // Number of blocks to return.
2891  const unsigned n_block = block_vec_number.size();
2892 
2893  // Each block is made of dof types. We get the most fine grain dof types.
2894  // Most fine grain in the sense that these are the dof types that belongs
2895  // in this block before any coarsening of dof types has taken place.
2896  // The ordering of the dof types matters, this is handled properly when
2897  // creating the Block_to_dof_map_fine vector and must be respected here.
2898  // I.e. we cannot arbitrarily insert dof types (even if they are correct)
2899  // in the vector most_fine_grain_dof.
2900  Vector<unsigned> most_fine_grain_dof;
2901  for (unsigned b = 0; b < n_block; b++)
2902  {
2903  const unsigned mapped_b = block_vec_number[b];
2904  most_fine_grain_dof.insert(most_fine_grain_dof.end(),
2905  Block_to_dof_map_fine[mapped_b].begin(),
2906  Block_to_dof_map_fine[mapped_b].end());
2907  }
2908 
2909  // The number of most fine grain dof types associated with the blocks
2910  // defined by block_vec_number.
2911  const unsigned ndof = most_fine_grain_dof.size();
2912 
2913  // Build each dof level vector with the correct distribution.
2914  Vector<DoubleVector> dof_vector(ndof);
2915  for (unsigned d = 0; d < ndof; d++)
2916  {
2917  dof_vector[d].build(
2918  internal_block_distribution_pt(most_fine_grain_dof[d]));
2919  }
2920 
2921  // Perform the splitting of w into the most fine grain dof level vectors.
2923 
2924  // Return all the dof level vectors in one go.
2925  internal_return_block_vectors(most_fine_grain_dof, dof_vector, v);
2926  } // return_concatenated_block_vector(...)

References OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, and v.

◆ set_block_output_to_files()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::set_block_output_to_files ( const std::string &  basefilename)
inline

Set the base part of the filename to output blocks to. If it is set then all blocks will be output at the end of block_setup. If it is left empty nothing will be output.

2077  {
2078  Output_base_filename = basefilename;
2079  } // EOFunc set_block_output_to_files(...)

References oomph::BlockPreconditioner< MATRIX >::Output_base_filename.

◆ set_master_matrix_pt()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::set_master_matrix_pt ( MATRIX *  in_matrix_pt)
inline

Set the matrix_pt in the upper-most master preconditioner.

999  {
1001  {
1002  master_block_preconditioner_pt()->set_master_matrix_pt(in_matrix_pt);
1003  }
1004  else
1005  {
1006  this->set_matrix_pt(in_matrix_pt);
1007  }
1008  }
virtual void set_matrix_pt(DoubleMatrixBase *matrix_pt)
Set the matrix pointer.
Definition: preconditioner.h:165

References oomph::BlockPreconditioner< MATRIX >::is_subsidiary_block_preconditioner(), oomph::BlockPreconditioner< MATRIX >::master_block_preconditioner_pt(), and oomph::Preconditioner::set_matrix_pt().

Referenced by oomph::BlockPreconditioner< MATRIX >::get_block_other_matrix().

◆ set_mesh()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::set_mesh ( const unsigned i,
const Mesh *const  mesh_pt,
const bool allow_multiple_element_type_in_mesh = false 
)
inlineprotected

Set the i-th mesh for this block preconditioner. Note: The method set_nmesh(...) must be called before this method to specify the number of meshes. By default, it is assumed that each mesh only contains elements of the same type. This condition may be relaxed by setting the boolean allow_multiple_element_type_in_mesh to true, however, each mesh must only contain elements with the same number of dof types.

2869  {
2870 #ifdef PARANOID
2871  // paranoid check that mesh i can be set
2872  if (i >= nmesh())
2873  {
2874  std::ostringstream err_msg;
2875  err_msg << "The mesh pointer has space for " << nmesh() << " meshes.\n"
2876  << "Cannot store a mesh at entry " << i << "\n"
2877  << "Has set_nmesh(...) been called?";
2878  throw OomphLibError(
2880  }
2881 
2882  // Check that the mesh pointer is not null.
2883  if (mesh_pt == 0)
2884  {
2885  std::ostringstream err_msg;
2886  err_msg << "Tried to set the " << i
2887  << "-th mesh pointer, but it is null.";
2888  throw OomphLibError(
2890  }
2891 #endif
2892 
2893  // store the mesh pt and n dof types
2894  Mesh_pt[i] = mesh_pt;
2895 
2896  // Does this mesh contain multiple element types?
2898  unsigned(allow_multiple_element_type_in_mesh);
2899  } // EOFunc set_mesh(...)

References oomph::BlockPreconditioner< MATRIX >::Allow_multiple_element_type_in_mesh, i, oomph::BlockPreconditioner< MATRIX >::mesh_pt(), oomph::BlockPreconditioner< MATRIX >::Mesh_pt, oomph::BlockPreconditioner< MATRIX >::nmesh(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Referenced by oomph::GeneralPurposeBlockPreconditioner< MATRIX >::gp_preconditioner_set_all_meshes(), oomph::HelmholtzGMRESMG< MATRIX >::solve(), and oomph::HelmholtzFGMRESMG< MATRIX >::solve().

◆ set_nmesh()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::set_nmesh ( const unsigned n)
inlineprotected

Specify the number of meshes required by this block preconditioner. Note: elements in different meshes correspond to different types of DOF.

2852  {
2853  Mesh_pt.resize(n, 0);
2855  } // EOFunc set_nmesh(...)

References oomph::BlockPreconditioner< MATRIX >::Allow_multiple_element_type_in_mesh, oomph::BlockPreconditioner< MATRIX >::Mesh_pt, and n.

Referenced by oomph::GeneralPurposeBlockPreconditioner< MATRIX >::gp_preconditioner_set_all_meshes(), oomph::SimpleFSIPreconditioner< MATRIX >::SimpleFSIPreconditioner(), oomph::HelmholtzGMRESMG< MATRIX >::solve(), and oomph::HelmholtzFGMRESMG< MATRIX >::solve().

◆ set_replacement_dof_block()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::set_replacement_dof_block ( const unsigned block_i,
const unsigned block_j,
CRDoubleMatrix replacement_dof_block_pt 
)
inlineprotected

Set replacement dof-level blocks. Only dof-level blocks can be set. This is important due to how the dof type coarsening feature operates.

IMPORTANT: The block indices (block_i, block_j) is the dof-level ordering, NOT the block-ordering. The block-ordering is determined by the parameters given to block_setup(...). The DOF-ordering is determined by the two-level ordering scheme of first the elements, then the meshes.

2914  {
2915 #ifdef PARANOID
2916  // Check if block_setup(...) has been called.
2917  if (nblock_types() == 0)
2918  {
2919  std::ostringstream err_msg;
2920  err_msg << "nblock_types() is 0, has block_setup(...) been called?\n";
2921  throw OomphLibError(
2923  }
2924 
2925 
2926  // Range checking for replacement dof block.
2927  unsigned para_ndof_types = this->ndof_types();
2928 
2929  if ((block_i >= para_ndof_types) || (block_j >= para_ndof_types))
2930  {
2931  std::ostringstream err_msg;
2932  err_msg << "Replacement dof block (" << block_i << "," << block_j
2933  << ") is outside of range:\n"
2934  << para_ndof_types;
2935  throw OomphLibError(
2937  }
2938 
2939 
2940  // // Check that the most fine grain mapping has been used in
2941  // block_setup(...)
2942  // // i.e. nblock_types() == ndof_types()
2943  // if(ndof_types() != nblock_types())
2944  // {
2945  // std::ostringstream err_msg;
2946  // err_msg << "ndof_types() != nblock_types()\n"
2947  // << "Only the dof-level blocks can be replaced.\n"
2948  // << "Please re-think your blocking scheme.\n";
2949  // throw OomphLibError(err_msg.str(),
2950  // OOMPH_CURRENT_FUNCTION,
2951  // OOMPH_EXCEPTION_LOCATION);
2952  // }
2953 
2954  // Check that the replacement block pt is not null
2955  if (replacement_dof_block_pt == 0)
2956  {
2957  std::ostringstream err_msg;
2958  err_msg << "Replacing block(" << block_i << "," << block_i << ")\n"
2959  << " but the pointer is NULL." << std::endl;
2960  throw OomphLibError(
2962  }
2963 
2964  // Check that the replacement block has been built
2965  if (!replacement_dof_block_pt->built())
2966  {
2967  std::ostringstream err_msg;
2968  err_msg << "Replacement block(" << block_i << "," << block_i << ")"
2969  << " is not built." << std::endl;
2970  throw OomphLibError(
2972  }
2973 
2974  // Check if the distribution matches. Determine which natural ordering dof
2975  // this should go to. I.e. we convert from dof-block index to dof index.
2976  // Luckily, this is stored in Block_to_dof_map_coarse.
2977  // const unsigned para_dof_block_i =
2978  // Block_to_dof_map_coarse[block_i][0];
2979  const unsigned para_dof_block_i = block_i;
2980 
2981  if (*dof_block_distribution_pt(para_dof_block_i) !=
2982  *replacement_dof_block_pt->distribution_pt())
2983  {
2984  std::ostringstream err_msg;
2985  err_msg << "The distribution of the replacement dof_block_pt\n"
2986  << "is different from the Dof_block_distribution_pt["
2987  << para_dof_block_i << "].\n";
2988  throw OomphLibError(
2990  }
2991 
2992  // Now that we know the distribution of the replacement block is
2993  // correct, we check the number of columns.
2994  const unsigned para_dof_block_j = block_j;
2995  unsigned para_replacement_block_ncol = replacement_dof_block_pt->ncol();
2996  unsigned para_required_ncol =
2997  dof_block_distribution_pt(para_dof_block_j)->nrow();
2998  if (para_replacement_block_ncol != para_required_ncol)
2999  {
3000  std::ostringstream err_msg;
3001  err_msg << "Replacement dof block has ncol = "
3002  << para_replacement_block_ncol << ".\n"
3003  << "But required ncol is " << para_required_ncol << ".\n";
3004  throw OomphLibError(
3006  }
3007 #endif
3008 
3009  // Block_to_dof_map_coarse[x][0] sense because we only can use this if
3010  // nblock_types() == ndof_types(), i.e. each sub-vector is of length 1.
3011  //
3012  // We use this indirection so that the placement of the pointer is
3013  // consistent with internal_get_block(...).
3014  // const unsigned dof_block_i = Block_to_dof_map_coarse[block_i][0];
3015  // const unsigned dof_block_j = Block_to_dof_map_coarse[block_j][0];
3016 
3017  // Replacement_dof_block_pt(dof_block_i,dof_block_j)
3018  // = replacement_dof_block_pt;
3019 
3021  }
MapMatrix< unsigned, CRDoubleMatrix * > replacement_dof_block_pt() const
Access function to the replaced dof-level blocks.
Definition: block_preconditioner.h:2381

References oomph::BlockPreconditioner< MATRIX >::dof_block_distribution_pt(), oomph::BlockPreconditioner< MATRIX >::nblock_types(), oomph::BlockPreconditioner< MATRIX >::ndof_types(), oomph::LinearAlgebraDistribution::nrow(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, oomph::BlockPreconditioner< MATRIX >::replacement_dof_block_pt(), and oomph::BlockPreconditioner< MATRIX >::Replacement_dof_block_pt.

◆ setup_matrix_vector_product() [1/2]

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::setup_matrix_vector_product ( MatrixVectorProduct matvec_prod_pt,
CRDoubleMatrix block_pt,
const unsigned block_col_index 
)
inline

Setup matrix vector product. This is simply a wrapper around the other setup_matrix_vector_product function.

2441  {
2442  Vector<unsigned> col_index_vector(1, block_col_index);
2443  setup_matrix_vector_product(matvec_prod_pt, block_pt, col_index_vector);
2444  } // EOFunc setup_matrix_vector_product(...)
void setup_matrix_vector_product(MatrixVectorProduct *matvec_prod_pt, CRDoubleMatrix *block_pt, const Vector< unsigned > &block_col_indices)
Definition: block_preconditioner.h:2397

References oomph::BlockPreconditioner< MATRIX >::setup_matrix_vector_product().

◆ setup_matrix_vector_product() [2/2]

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::setup_matrix_vector_product ( MatrixVectorProduct matvec_prod_pt,
CRDoubleMatrix block_pt,
const Vector< unsigned > &  block_col_indices 
)
inline

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.

The distribution of the block column must be the same as the RHS vector being solved. By default, OOMPH-LIB's uniform row distribution is employed. When block matrices are concatenated without communication, the columns are permuted, as a result, the distribution of the columns may no longer be uniform.

2400  {
2401  const unsigned nblock = block_col_indices.size();
2402 
2403  if (nblock == 1)
2404  {
2405  const unsigned col_index = block_col_indices[0];
2406  matvec_prod_pt->setup(block_pt, Block_distribution_pt[col_index]);
2407  }
2408  else
2409  {
2410  std::map<Vector<unsigned>, LinearAlgebraDistribution*>::const_iterator
2411  iter;
2412 
2413  iter = Auxiliary_block_distribution_pt.find(block_col_indices);
2414  if (iter != Auxiliary_block_distribution_pt.end())
2415  {
2416  matvec_prod_pt->setup(block_pt, iter->second);
2417  }
2418  else
2419  {
2420  Vector<LinearAlgebraDistribution*> tmp_vec_dist_pt(nblock, 0);
2421  for (unsigned b = 0; b < nblock; b++)
2422  {
2423  tmp_vec_dist_pt[b] = Block_distribution_pt[block_col_indices[b]];
2424  }
2425 
2426  LinearAlgebraDistribution* tmp_dist_pt =
2427  new LinearAlgebraDistribution;
2429  *tmp_dist_pt);
2430  insert_auxiliary_block_distribution(block_col_indices, tmp_dist_pt);
2431  matvec_prod_pt->setup(block_pt, tmp_dist_pt);
2432  }
2433  }
2434  } // EOFunc setup_matrix_vector_product(...)

References oomph::BlockPreconditioner< MATRIX >::Auxiliary_block_distribution_pt, b, oomph::BlockPreconditioner< MATRIX >::Block_distribution_pt, oomph::LinearAlgebraDistributionHelpers::concatenate(), oomph::BlockPreconditioner< MATRIX >::insert_auxiliary_block_distribution(), and oomph::MatrixVectorProduct::setup().

Referenced by oomph::BlockPreconditioner< MATRIX >::setup_matrix_vector_product().

◆ turn_into_subsidiary_block_preconditioner() [1/2]

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::turn_into_subsidiary_block_preconditioner ( BlockPreconditioner< MATRIX > *  master_block_prec_pt,
const Vector< unsigned > &  doftype_in_master_preconditioner_coarse 
)

Function to turn this preconditioner into a subsidiary preconditioner that operates within a bigger "master block preconditioner (e.g. a Navier-Stokes 2x2 block preconditioner dealing with the fluid sub-blocks within a 3x3 FSI preconditioner. Once this is done the master block preconditioner deals with the block setup etc. The vector doftype_in_master_preconditioner_coarse must specify the dof number in the master preconditioner that corresponds to a dof number in this preconditioner. 1. The length of the vector is used to determine the number of blocks in this preconditioner therefore it must be correctly sized. 2. block_setup(...) should be called in the master preconditioner before this method is called. 3. block_setup(...) should be called in the corresponding subsidiary preconditioner after this method is called.

This calls the other turn_into_subsidiary_block_preconditioner function with the identity mapping for doftype_coarsen_map_coarse vector.

Function to turn this preconditioner into a subsidiary preconditioner that operates within a bigger "master block preconditioner (e.g. a Navier-Stokes 2x2 block preconditioner dealing with the fluid sub-blocks within a 3x3 FSI preconditioner. Once this is done the master block preconditioner deals with the block setup etc. The vector block_map must specify the dof number in the master preconditioner that corresponds to a block number in this preconditioner. ??ds horribly misleading comment! The length of the vector is used to determine the number of blocks in this preconditioner therefore it must be correctly sized. This calls the other turn_into_subsidiary_block_preconditioner(...) function providing an empty doftype_to_doftype_map vector.

2379  {
2380  // Create the identity dof_coarsen_map
2381  Vector<Vector<unsigned>> doftype_coarsen_map_coarse;
2382  unsigned doftype_in_master_preconditioner_coarse_size =
2383  doftype_in_master_preconditioner_coarse.size();
2384 
2385  for (unsigned dof_i = 0;
2386  dof_i < doftype_in_master_preconditioner_coarse_size;
2387  dof_i++)
2388  {
2389  // Create a vector of size 1 and value i,
2390  // then push it into the dof_coarsen_map vector.
2391  Vector<unsigned> tmp_vec(1, dof_i);
2392  doftype_coarsen_map_coarse.push_back(tmp_vec);
2393  }
2394 
2395  // Call the other turn_into_subsidiary_block_preconditioner function.
2397  master_block_prec_pt,
2398  doftype_in_master_preconditioner_coarse,
2399  doftype_coarsen_map_coarse);
2400  }
void turn_into_subsidiary_block_preconditioner(BlockPreconditioner< MATRIX > *master_block_prec_pt, const Vector< unsigned > &doftype_in_master_preconditioner_coarse)
Definition: block_preconditioner.cc:2376

Referenced by oomph::CoarseTwoIntoOne< MATRIX >::setup(), oomph::TwoPlusThreeUpperTriangularWithOneLevelSubsidiary< MATRIX >::setup(), oomph::TwoPlusOneUpperTriangularPreconditioner< MATRIX >::setup(), oomph::TwoPlusThreeUpperTriangularWithTwoLevelSubsidiary< MATRIX >::setup(), oomph::TwoPlusThreeUpperTriangularWithReplace< MATRIX >::setup(), oomph::CoarseTwoPlusTwoPlusOne< MATRIX >::setup(), oomph::OnePlusFourWithTwoCoarse< MATRIX >::setup(), oomph::FSIPreconditioner::setup(), oomph::PseudoElasticFSIPreconditioner::setup(), oomph::PseudoElasticPreconditioner::setup(), oomph::PseudoElasticPreconditionerOld::setup(), oomph::LagrangeEnforcedFlowPreconditioner::setup(), oomph::PressureBasedSolidLSCPreconditioner::setup(), and oomph::GMRESBlockPreconditioner::setup().

◆ turn_into_subsidiary_block_preconditioner() [2/2]

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::turn_into_subsidiary_block_preconditioner ( BlockPreconditioner< MATRIX > *  master_block_prec_pt,
const Vector< unsigned > &  doftype_in_master_preconditioner_coarse,
const Vector< Vector< unsigned >> &  doftype_coarsen_map_coarse 
)

Function to turn this preconditioner into a subsidiary preconditioner that operates within a bigger "master block preconditioner (e.g. a Navier-Stokes 2x2 block preconditioner dealing with the fluid sub-blocks within a 3x3 FSI preconditioner. Once this is done the master block preconditioner deals with the block setup etc. The vector doftype_in_master_preconditioner_coarse must specify the dof number in the master preconditioner that corresponds to a dof number in this preconditioner. 1. The length of the vector is used to determine the number of blocks in this preconditioner therefore it must be correctly sized. 2. block_setup(...) should be called in the master preconditioner before this method is called. 3. block_setup(...) should be called in the corresponding subsidiary preconditioner after this method is called.

The doftype_coarsen_map_coarse is a mapping of the dof numbers in the master preconditioner to the dof numbers REQUIRED by THIS preconditioner. This allows for coarsening of the dof types if the master preconditioner has a more fine grain dof type splitting.

For example, the Lagrangian preconditioner (in 3D with one constraint) has doftypes: 0 1 2 3 4 5 6 7 ub vb wb uc vc wc p Lc

We wish to use an existing Navier-Stokes preconditioner, for example, LSC, to solve the sub-system associated with the dof numbers 0, 1, 2, 3, 4, 5, 6. But the existing LSC preconditioner only works with four dof types (3 velocity dof types and 1 pressure dof types). We need to coarsen the number of dof types in the master preconditioner.

If the LSC preconditioner requires the dof ordering: u, v, w, p. Then the doftype_coarsen_map_coarse will be: [0 3] -> u velocity dof type [1 4] -> v velocity dof type [2 5] -> w velocity dof type [6] -> pressure dof type.

Function to turn this block preconditioner into a subsidiary block preconditioner that operates within a bigger master block preconditioner (e.g. a Navier-Stokes 2x2 block preconditioner dealing with the fluid sub-blocks within a 3x3 FSI preconditioner. Once this is done the master block preconditioner deals with the block setup etc.

The vector doftype_map must specify the dof type in the master preconditioner that corresponds to a dof type in this block preconditioner.

In general, we want: doftype_map[doftype in subsidiary prec] = doftype in master prec.

It tells this block preconditioner which dof types of the master block preconditioner it is working with.

The length of the vector is used to determine the number of dof types in THIS block preconditioner therefore it must be correctly sized.

For example, let the master block preconditioner have 5 dof types in total and a 1-4 dof type splitting where the block (0,0) corresponds to dof type 0 and the block (1,1) corresponds to dof types 1, 2, 3 and 4 (i.e. it would have given to block_setup the vector [0,1,1,1,1]). Furthermore, it solves (1,1) block with subsidiary block preconditioner. Then the doftype_map passed to this function of the subsidiary block preconditioner would be [1, 2, 3, 4].

Dof type coarsening (following on from the example above): Let the subsidiary block preconditioner (THIS block preconditioner) only works with two DOF types, then the master block preconditioner must "coarsen" the dof types by providing the optional argument doftype_coarsen_map vector.

The doftype_coarsen_map vector (in this case) might be [[0,1], [2,3]] telling the subsidiary block preconditioner that the SUBSIDIARY dof types 0 and 1 should be treated as dof type 0 and the subsidiary dof types 2 and 3 should be treated as subsidiary dof type 1.

If no doftype_coarsen_map vector is provided, then the identity is used automatically (see the turn_into_subsidiary_block_preconditioner(...) function with only two arguments). In the above case, the identity doftype_coarsen_map vector for the subsidiary block preconditioner would be the 2D vector [[0], [1], [2], [3]] which means dof type 0 is treated as dof type 0, dof type 1 is treated as dof type 1, dof type 2 is treated as dof type 2, and dof type 3 is treated as dof type 3.

2459  {
2460  // Set the master block preconditioner pointer
2461  Master_block_preconditioner_pt = master_block_prec_pt;
2462 
2463  // Set the Doftype_coarsen_map_coarse.
2464  Doftype_coarsen_map_coarse = doftype_coarsen_map_coarse;
2465 
2467  doftype_in_master_preconditioner_coarse;
2468  } // end of turn_into_subsidiary_block_preconditioner(...)

◆ turn_off_debug_flag()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::turn_off_debug_flag ( )
inline

Toggles off the debug flag.

569  {
570  Debug_flag = false;
571  }

References oomph::BlockPreconditioner< MATRIX >::Debug_flag.

◆ turn_off_recursive_debug_flag()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::turn_off_recursive_debug_flag ( )
inline

Toggles off the recursive debug flag. The change goes up the block preconditioning hierarchy.

555  {
556  Recursive_debug_flag = false;
558  this->master_block_preconditioner_pt()->turn_off_recursive_debug_flag();
559  }

References oomph::BlockPreconditioner< MATRIX >::is_subsidiary_block_preconditioner(), oomph::BlockPreconditioner< MATRIX >::master_block_preconditioner_pt(), and oomph::BlockPreconditioner< MATRIX >::Recursive_debug_flag.

◆ turn_on_debug_flag()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::turn_on_debug_flag ( )
inline

Toggles on the debug flag.

563  {
564  Debug_flag = true;
565  }

References oomph::BlockPreconditioner< MATRIX >::Debug_flag.

◆ turn_on_recursive_debug_flag()

template<typename MATRIX >
void oomph::BlockPreconditioner< MATRIX >::turn_on_recursive_debug_flag ( )
inline

Toggles on the recursive debug flag. The change goes up the block preconditioning hierarchy.

546  {
547  Recursive_debug_flag = true;
549  this->master_block_preconditioner_pt()->turn_on_recursive_debug_flag();
550  }

References oomph::BlockPreconditioner< MATRIX >::is_subsidiary_block_preconditioner(), oomph::BlockPreconditioner< MATRIX >::master_block_preconditioner_pt(), and oomph::BlockPreconditioner< MATRIX >::Recursive_debug_flag.

Member Data Documentation

◆ Allow_multiple_element_type_in_mesh

template<typename MATRIX >
Vector<unsigned> oomph::BlockPreconditioner< MATRIX >::Allow_multiple_element_type_in_mesh
protected

Vector of unsigned to indicate which meshes contain multiple element types.

Referenced by oomph::BlockPreconditioner< MATRIX >::set_mesh(), and oomph::BlockPreconditioner< MATRIX >::set_nmesh().

◆ Auxiliary_block_distribution_pt

template<typename MATRIX >
std::map<Vector<unsigned>, LinearAlgebraDistribution*> oomph::BlockPreconditioner< MATRIX >::Auxiliary_block_distribution_pt
private

Stores any block-level distributions / concatenation of block-level distributions required. The first in the pair (Vector<unsigned>) represents the block numbers of the distributions concatenated to get the second in the pair (LinearAlgebraDistribution*).

Referenced by oomph::BlockPreconditioner< MATRIX >::clear_block_preconditioner_base(), oomph::BlockPreconditioner< MATRIX >::get_concatenated_block(), oomph::BlockPreconditioner< MATRIX >::insert_auxiliary_block_distribution(), and oomph::BlockPreconditioner< MATRIX >::setup_matrix_vector_product().

◆ Block_distribution_pt

◆ Block_number_to_dof_number_lookup

template<typename MATRIX >
Vector<Vector<unsigned> > oomph::BlockPreconditioner< MATRIX >::Block_number_to_dof_number_lookup
private

Vector of vectors to store the mapping from block number to the DOF number (each element could be a vector because we allow multiple DOFs types in a single block).

Referenced by oomph::BlockPreconditioner< MATRIX >::document(), oomph::BlockPreconditioner< MATRIX >::internal_index_in_block(), and oomph::BlockPreconditioner< MATRIX >::post_block_matrix_assembly_partial_clear().

◆ Block_to_dof_map_coarse

template<typename MATRIX >
Vector<Vector<unsigned> > oomph::BlockPreconditioner< MATRIX >::Block_to_dof_map_coarse
protected

Mapping for block types to dof types. These are the dof types the writer of the preconditioner expects. For the upper-most master block preconditioner, this would be the sum of the dof types in the meshes. For subsidiary block preconditioners, this is determined by the parent preconditioner when passing in the doftype_coarsen_map_coarse vector in turn_into_subsidiary_block_preconditioner(...).

Referenced by oomph::BlockPreconditioner< MATRIX >::BlockPreconditioner(), oomph::BlockPreconditioner< MATRIX >::get_block(), and oomph::BlockPreconditioner< MATRIX >::nblock_types().

◆ Block_to_dof_map_fine

template<typename MATRIX >
Vector<Vector<unsigned> > oomph::BlockPreconditioner< MATRIX >::Block_to_dof_map_fine
protected

◆ Debug_flag

template<typename MATRIX >
bool oomph::BlockPreconditioner< MATRIX >::Debug_flag
private

Debugging variable. Set true or false via the access functions turn_on_debug_flag(...) turn_off_debug_flag(...)

Referenced by oomph::BlockPreconditioner< MATRIX >::BlockPreconditioner(), oomph::BlockPreconditioner< MATRIX >::turn_off_debug_flag(), and oomph::BlockPreconditioner< MATRIX >::turn_on_debug_flag().

◆ Dof_block_distribution_pt

◆ Dof_dimension

template<typename MATRIX >
Vector<unsigned> oomph::BlockPreconditioner< MATRIX >::Dof_dimension
private

Vector containing the size of each block, i.e. the number of global DOFs associated with it. (Empty if this preconditioner has a master preconditioner as this information is obtain from the master preconditioner.)

Referenced by oomph::BlockPreconditioner< MATRIX >::internal_dof_block_dimension(), and oomph::BlockPreconditioner< MATRIX >::post_block_matrix_assembly_partial_clear().

◆ Dof_number_dense

template<typename MATRIX >
Vector<unsigned> oomph::BlockPreconditioner< MATRIX >::Dof_number_dense
private

Vector to store the mapping from the global DOF number to its block. Empty if this preconditioner has a master preconditioner, in this case the information is obtained from the master preconditioner.

Referenced by oomph::BlockPreconditioner< MATRIX >::internal_dof_number(), and oomph::BlockPreconditioner< MATRIX >::post_block_matrix_assembly_partial_clear().

◆ Dof_number_to_block_number_lookup

template<typename MATRIX >
Vector<unsigned> oomph::BlockPreconditioner< MATRIX >::Dof_number_to_block_number_lookup
private

◆ Doftype_coarsen_map_coarse

template<typename MATRIX >
Vector<Vector<unsigned> > oomph::BlockPreconditioner< MATRIX >::Doftype_coarsen_map_coarse
protected

Mapping for dof types within THIS precondition. This is usually passed down from the parent preconditioner. This list is used to tell which does types should be considered as a single dof type within this preconditioner. I.e. we "coarsen" the dof types. The values are local to this preconditioner, for example, even if the Doftype_in_master_preconditioner_coarse = [2,3,4], the vector Doftype_coarsen_map_coarse = [[0],[1,2]], saying your local dof types 0 should be considered as dof type 0 and dof types 1 and 2 are considered as dof type 1.

Furthermore, the dof types are that the preconditioner above this one knows; these dof types may or may not be coarsened. For example, say that this preconditioner expects two dof types, 0 and 1. The preconditioner above this one wishes to use this preconditioner to solve the block associated with it's dof types 2, 3 and 4. It passes the Vector [2,3,4] to this preconditioner via the function turn_into_subsidiary_block_preconditioner(...), this list is to be stored in Doftype_in_master_preconditioner_coarse. It also passes in the 2D vector [[0][1,2]] (as described above), this list is to be stored in Doftype_coarsen_map_coarse. BUT, the master's preconditioner dof types may also be coarsened. I.e. the underlying dof types of the master block preconditioner may be [0,1,2,3,4,5,6,7], for which it may have the Doftype_coarsen_map_coarse = [[0,1][2,3][4,5][6,7]].

An additional list has to be kept for the most fine grain dof type mapping. This is stored in Doftype_coarsen_map_fine, in this case it would be:

Doftype_coarsen_map_fine = [[0,1][2,3,4,5]], since the dof types passed to this preconditioner is [2, 3, 4] from the master preconditioner, but it actually refers to the underlying dof types [2,3,4,5,6,7].

In the case of the top most master block preconditioner, the block_setup(...) function fills the vector with the identity mapping.

Referenced by oomph::BlockPreconditioner< MATRIX >::ndof_types().

◆ Doftype_coarsen_map_fine

template<typename MATRIX >
Vector<Vector<unsigned> > oomph::BlockPreconditioner< MATRIX >::Doftype_coarsen_map_fine
protected

Mapping the dof types within this preconditioner. The values in here refers to the most grain dof types. This list is automatically generated either in block_setup(...) (for the top-most preconditioner) or the turn_into_subsidiary_block_preconditioner(...) function. Please refer to the comment above Doftype_coarsen_map_coarse for more details.

Referenced by oomph::BlockPreconditioner< MATRIX >::doftype_coarsen_map_fine(), oomph::BlockPreconditioner< MATRIX >::get_fine_grain_dof_types_in(), and oomph::BlockPreconditioner< MATRIX >::nfine_grain_dof_types_in().

◆ Doftype_in_master_preconditioner_coarse

template<typename MATRIX >
Vector<unsigned> oomph::BlockPreconditioner< MATRIX >::Doftype_in_master_preconditioner_coarse
private

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.

◆ Doftype_in_master_preconditioner_fine

template<typename MATRIX >
Vector<unsigned> oomph::BlockPreconditioner< MATRIX >::Doftype_in_master_preconditioner_fine
private

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.

Referenced by oomph::BlockPreconditioner< MATRIX >::internal_dof_number(), and oomph::BlockPreconditioner< MATRIX >::internal_master_dof_number().

◆ Global_index

template<typename MATRIX >
Vector<Vector<unsigned> > oomph::BlockPreconditioner< MATRIX >::Global_index
private

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.

Referenced by oomph::BlockPreconditioner< MATRIX >::clear_block_preconditioner_base().

◆ Index_in_dof_block_dense

template<typename MATRIX >
Vector<unsigned> oomph::BlockPreconditioner< MATRIX >::Index_in_dof_block_dense
private

This was uncommented Presumably a non-distributed analogue of Index_in_dof_block_sparse.

Referenced by oomph::BlockPreconditioner< MATRIX >::internal_index_in_dof(), and oomph::BlockPreconditioner< MATRIX >::post_block_matrix_assembly_partial_clear().

◆ Internal_block_distribution_pt

◆ Internal_nblock_types

template<typename MATRIX >
unsigned oomph::BlockPreconditioner< MATRIX >::Internal_nblock_types
protected

Number of different block types in this preconditioner. Note that this information is maintained if used as a subsidiary or stand-alone block preconditioner, in the latter case it stores the number of blocks within the subsidiary preconditioner.

Referenced by oomph::BlockPreconditioner< MATRIX >::BlockPreconditioner(), oomph::BlockPreconditioner< MATRIX >::clear_block_preconditioner_base(), oomph::BlockPreconditioner< MATRIX >::document(), and oomph::BlockPreconditioner< MATRIX >::internal_nblock_types().

◆ Internal_ndof_types

template<typename MATRIX >
unsigned oomph::BlockPreconditioner< MATRIX >::Internal_ndof_types
protected

Number of different DOF types in this preconditioner. Note that this information is maintained if used as a subsidiary or stand-alone block preconditioner, in the latter case it stores the number of dofs within the subsidiary preconditioner.

Referenced by oomph::BlockPreconditioner< MATRIX >::BlockPreconditioner(), oomph::BlockPreconditioner< MATRIX >::clear_block_preconditioner_base(), oomph::BlockPreconditioner< MATRIX >::document(), and oomph::BlockPreconditioner< MATRIX >::internal_ndof_types().

◆ Internal_preconditioner_matrix_distribution_pt

template<typename MATRIX >
LinearAlgebraDistribution* oomph::BlockPreconditioner< MATRIX >::Internal_preconditioner_matrix_distribution_pt
private

The distribution of the (internal) preconditioner matrix. This is formed by concatenating the distribution of the internal blocks. This is obsolete code, maintained for backwards compatibility. Below is the old comment:

  • only used if this preconditioner is a master preconditioner. Warning: always use the access function internal_preconditioner_matrix_distribution_pt().

Referenced by oomph::BlockPreconditioner< MATRIX >::BlockPreconditioner(), oomph::BlockPreconditioner< MATRIX >::clear_block_preconditioner_base(), and oomph::BlockPreconditioner< MATRIX >::internal_preconditioner_matrix_distribution_pt().

◆ Master_block_preconditioner_pt

◆ Mesh_pt

template<typename MATRIX >
Vector<const Mesh*> oomph::BlockPreconditioner< MATRIX >::Mesh_pt
protected

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.

Referenced by oomph::BlockPreconditioner< MATRIX >::mesh_pt(), oomph::BlockPreconditioner< MATRIX >::ndof_types(), oomph::BlockPreconditioner< MATRIX >::nmesh(), oomph::BlockPreconditioner< MATRIX >::set_mesh(), and oomph::BlockPreconditioner< MATRIX >::set_nmesh().

◆ Ndof_in_block

template<typename MATRIX >
Vector<unsigned> oomph::BlockPreconditioner< MATRIX >::Ndof_in_block
private

Number of types of degree of freedom associated with each block.

Referenced by oomph::BlockPreconditioner< MATRIX >::post_block_matrix_assembly_partial_clear().

◆ Ndof_types_in_mesh

template<typename MATRIX >
Vector<unsigned> oomph::BlockPreconditioner< MATRIX >::Ndof_types_in_mesh
protected

Storage for number of types of degree of freedom of the elements in each mesh.

Referenced by oomph::BlockPreconditioner< MATRIX >::ndof_types_in_mesh().

◆ Nrow

template<typename MATRIX >
unsigned oomph::BlockPreconditioner< MATRIX >::Nrow
private

Number of DOFs (# of rows or columns in the matrix) in this preconditioner. Note that this information is maintained if used as a subsidiary or stand-alone block preconditioner, in the latter case it stores the number of rows within the subsidiary preconditioner.

Referenced by oomph::BlockPreconditioner< MATRIX >::BlockPreconditioner(), oomph::BlockPreconditioner< MATRIX >::clear_block_preconditioner_base(), and oomph::BlockPreconditioner< MATRIX >::master_nrow().

◆ Output_base_filename

template<typename MATRIX >
std::string oomph::BlockPreconditioner< MATRIX >::Output_base_filename
private

String giving the base of the files to write block data into. If empty then do not output blocks. Default is empty.

Referenced by oomph::BlockPreconditioner< MATRIX >::block_output_on(), oomph::BlockPreconditioner< MATRIX >::disable_block_output_to_files(), and oomph::BlockPreconditioner< MATRIX >::set_block_output_to_files().

◆ Preconditioner_matrix_distribution_pt

template<typename MATRIX >
LinearAlgebraDistribution* oomph::BlockPreconditioner< MATRIX >::Preconditioner_matrix_distribution_pt
private

◆ Recursive_debug_flag

template<typename MATRIX >
bool oomph::BlockPreconditioner< MATRIX >::Recursive_debug_flag
private

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.

Referenced by oomph::BlockPreconditioner< MATRIX >::BlockPreconditioner(), oomph::BlockPreconditioner< MATRIX >::turn_off_recursive_debug_flag(), and oomph::BlockPreconditioner< MATRIX >::turn_on_recursive_debug_flag().

◆ Replacement_dof_block_pt

◆ Run_block_matrix_test

template<typename MATRIX >
bool oomph::BlockPreconditioner< MATRIX >::Run_block_matrix_test = false
staticprivate

Static boolean to allow block_matrix_test(...) to be run. Defaults to false.


The documentation for this class was generated from the following files: