oomph::LagrangeEnforcedFlowPreconditioner Class Reference

#include <lagrange_enforced_flow_preconditioner.h>

+ Inheritance diagram for oomph::LagrangeEnforcedFlowPreconditioner:

Public Types

typedef Preconditioner *(* SubsidiaryPreconditionerFctPt) ()
 

Public Member Functions

 LagrangeEnforcedFlowPreconditioner ()
 Constructor - initialise variables. More...
 
virtual ~LagrangeEnforcedFlowPreconditioner ()
 Destructor. More...
 
 LagrangeEnforcedFlowPreconditioner (const LagrangeEnforcedFlowPreconditioner &)=delete
 Broken copy constructor. More...
 
void operator= (const LagrangeEnforcedFlowPreconditioner &)=delete
 Broken assignment operator. More...
 
void setup ()
 Setup method for the LagrangeEnforcedFlowPreconditioner. More...
 
void preconditioner_solve (const DoubleVector &r, DoubleVector &z)
 
void set_meshes (const Vector< Mesh * > &mesh_pt)
 
void use_norm_f_for_scaling_sigma ()
 
void set_scaling_sigma (const double &scaling_sigma)
 
double scaling_sigma () const
 Read (const) function to get the scaling sigma. More...
 
void set_navier_stokes_preconditioner (Preconditioner *new_ns_preconditioner_pt=0)
 
void set_superlu_for_navier_stokes_preconditioner ()
 
void clean_up_memory ()
 Clears the memory. More...
 
- Public Member Functions inherited from oomph::BlockPreconditioner< CRDoubleMatrix >
 BlockPreconditioner ()
 Constructor. More...
 
 BlockPreconditioner (const BlockPreconditioner &)=delete
 Broken copy constructor. More...
 
virtual ~BlockPreconditioner ()
 Destructor. More...
 
void operator= (const BlockPreconditioner &)=delete
 Broken assignment operator. More...
 
CRDoubleMatrixmatrix_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< CRDoubleMatrix > *master_block_prec_pt, const Vector< unsigned > &doftype_in_master_preconditioner_coarse)
 
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)
 
virtual void block_setup ()
 
void block_setup (const Vector< unsigned > &dof_to_block_map)
 
void get_block (const unsigned &i, const unsigned &j, CRDoubleMatrix &output_matrix, const bool &ignore_replacement_block=false) const
 
CRDoubleMatrix get_block (const unsigned &i, const unsigned &j, const bool &ignore_replacement_block=false) const
 
void set_master_matrix_pt (CRDoubleMatrix *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, CRDoubleMatrix *in_matrix_pt, CRDoubleMatrix &output_matrix)
 
void get_blocks (DenseMatrix< bool > &required_blocks, DenseMatrix< CRDoubleMatrix * > &block_matrix_pt) const
 
void get_dof_level_block (const unsigned &i, const unsigned &j, CRDoubleMatrix &output_block, const bool &ignore_replacement_block=false) const
 
void get_dof_level_block (const unsigned &block_i, const unsigned &block_j, CRDoubleMatrix &output_block, const bool &ignore_replacement_block) const
 
CRDoubleMatrix 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< CRDoubleMatrix > * 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, CRDoubleMatrix &output_block) const
 
void internal_get_block (const unsigned &block_i, const unsigned &block_j, CRDoubleMatrix &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 CRDoubleMatrix *block_matrix_pt) const
 
int get_index_of_value (const Vector< myType > &vec, const myType val, const bool sorted=false) 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_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 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)
 

Private Attributes

bool Preconditioner_has_been_setup
 
double Scaling_sigma
 Scaling for the augmentation: Scaling_sigma*(LL^T) More...
 
bool Use_norm_f_for_scaling_sigma
 
Vector< Vector< double > > Inv_w_diag_values
 Inverse W values. More...
 
PreconditionerNavier_stokes_preconditioner_pt
 Pointer to the 'preconditioner' for the Navier-Stokes block. More...
 
bool Navier_stokes_preconditioner_is_block_preconditioner
 
bool Using_superlu_ns_preconditioner
 Flag to indicate whether the default NS preconditioner is used. More...
 
Vector< Mesh * > My_mesh_pt
 
Vector< unsignedMy_ndof_types_in_mesh
 
unsigned My_nmesh
 
unsigned N_lagrange_doftypes
 The number of Lagrange multiplier DOF types. More...
 
unsigned N_fluid_doftypes
 The number of fluid DOF types (including pressure). More...
 
unsigned N_velocity_doftypes
 The number of velocity DOF types. More...
 

Additional Inherited Members

- Protected Member Functions inherited from oomph::BlockPreconditioner< CRDoubleMatrix >
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 inherited from oomph::BlockPreconditioner< CRDoubleMatrix >
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...
 

Detailed Description

The preconditioner for the Lagrange multiplier constrained Navier-Stokes equations. The velocity components are constrained by Lagrange multiplier, which are applied via OOMPH-LIB's FACE elements.

The linearised Jacobian takes the block form:

| F_ns | L^T | |---------—| | L | 0 |

where L correspond to the constrained block, F_ns is the Navier-Stokes block with the following block structure

| F | G^T | |-------—| | D | 0 |

Here F is the momentum block, G the discrete gradient operator, and D the discrete divergence operator. For unstabilised elements, we have D = G^T and in much of the literature the divergence matrix is denoted by B.

The Lagrange enforced flow preconditioner takes the form:

F_aug
Wd

where F_aug = F_ns + L^T*inv(Wd)*L is an augmented Navier-Stokes block and Wd=(1/Scaling_sigma)*diag(LL^T).

In our implementation of the preconditioner, the linear systems associated with the (1,1) block can either be solved "exactly", using SuperLU (in its incarnation as an exact preconditioner; this is the default) or by any other Preconditioner (inexact solver) specified via the access functions

LagrangeEnforcedFlowPreconditioner::set_navier_stokes_preconditioner(...)

Access to the elements is provided via meshes. However, a Vector of meshes is taken, each mesh contains a different type of block preconditionable element. This allows the (re-)classification of the constrained velocity DOF types.

The first mesh in the Vector Mesh_pt must be the 'bulk' mesh. The rest are assumed to contain FACEELMENTS.

Thus, the most general block structure (in 3D) is:

0 1 2 3 4 5 6 7 8 ..x x+0 x+1 x+2 x+3 x+4 [u v w p] [u v w l1 l2 ...] [u v w l1 l2 ...] ... Bulk Surface 1 Surface 2 ...

where the DOF types in [] are the DOF types associated with each mesh.

For example, consider a unit cube domain [0,1]^3 with parallel outflow imposed (in mesh 0) and tangential flow imposed (in mesh 1), then there are 13 DOF types and our implementation respects the following (natural) DOF type order:

bulk mesh 0 mesh 1 [0 1 2 3] [4 5 6 7 8 ] [9 10 11 12 ] [u v w p] [up vp wp Lp1 Lp2] [ut vt wt Lt1]

Via the appropriate mapping, the block_setup(...) function will re-order the DOF types into the following block types:

0 1 2 3 4 5 6 7 8 9 10 11 12 <- Block type 0 4 9 1 5 10 2 6 11 3 7 8 12 <- DOF type [u up ut v vp vt w wp wt ] [p] [Lp1 Lp2 Lt1]

Member Typedef Documentation

◆ SubsidiaryPreconditionerFctPt

typedef Preconditioner*(* oomph::LagrangeEnforcedFlowPreconditioner::SubsidiaryPreconditionerFctPt) ()

This preconditioner includes the option to use subsidiary operators other than SuperLUPreconditioner for this problem. This is the typedef of a function that should return an instance of a subsidiary preconditioning operator. This preconditioner is responsible for the destruction of the subsidiary preconditioners.

Constructor & Destructor Documentation

◆ LagrangeEnforcedFlowPreconditioner() [1/2]

oomph::LagrangeEnforcedFlowPreconditioner::LagrangeEnforcedFlowPreconditioner ( )
inline

Constructor - initialise variables.

178  : BlockPreconditioner<CRDoubleMatrix>()
179  {
180  // The null pointer.
182 
183  // By default, the linear systems associated with the diagonal blocks
184  // are solved "exactly" using SuperLU (in its incarnation as an exact
185  // preconditioner. This is not a block preconditioner.
187 
188  // Flag to indicate to use SuperLU or not.
190 
191  // Empty vector of meshes and set the number of meshes to zero.
192  My_mesh_pt.resize(0, 0);
193  My_nmesh = 0;
194 
195  // The number of DOF types within the meshes.
196  My_ndof_types_in_mesh.resize(0, 0);
197 
198  // Initialise other variables.
200  Scaling_sigma = 0.0;
202  N_fluid_doftypes = 0;
205  } // constructor
Preconditioner * Navier_stokes_preconditioner_pt
Pointer to the 'preconditioner' for the Navier-Stokes block.
Definition: lagrange_enforced_flow_preconditioner.h:319
unsigned N_lagrange_doftypes
The number of Lagrange multiplier DOF types.
Definition: lagrange_enforced_flow_preconditioner.h:342
unsigned N_fluid_doftypes
The number of fluid DOF types (including pressure).
Definition: lagrange_enforced_flow_preconditioner.h:345
bool Preconditioner_has_been_setup
Definition: lagrange_enforced_flow_preconditioner.h:306
bool Use_norm_f_for_scaling_sigma
Definition: lagrange_enforced_flow_preconditioner.h:313
Vector< unsigned > My_ndof_types_in_mesh
Definition: lagrange_enforced_flow_preconditioner.h:335
unsigned My_nmesh
Definition: lagrange_enforced_flow_preconditioner.h:339
double Scaling_sigma
Scaling for the augmentation: Scaling_sigma*(LL^T)
Definition: lagrange_enforced_flow_preconditioner.h:309
bool Using_superlu_ns_preconditioner
Flag to indicate whether the default NS preconditioner is used.
Definition: lagrange_enforced_flow_preconditioner.h:326
bool Navier_stokes_preconditioner_is_block_preconditioner
Definition: lagrange_enforced_flow_preconditioner.h:323
Vector< Mesh * > My_mesh_pt
Definition: lagrange_enforced_flow_preconditioner.h:331
unsigned N_velocity_doftypes
The number of velocity DOF types.
Definition: lagrange_enforced_flow_preconditioner.h:348

References My_mesh_pt, My_ndof_types_in_mesh, My_nmesh, N_fluid_doftypes, N_lagrange_doftypes, N_velocity_doftypes, Navier_stokes_preconditioner_is_block_preconditioner, Navier_stokes_preconditioner_pt, Preconditioner_has_been_setup, Scaling_sigma, Use_norm_f_for_scaling_sigma, and Using_superlu_ns_preconditioner.

◆ ~LagrangeEnforcedFlowPreconditioner()

virtual oomph::LagrangeEnforcedFlowPreconditioner::~LagrangeEnforcedFlowPreconditioner ( )
inlinevirtual

Destructor.

209  {
210  this->clean_up_memory();
211  }
void clean_up_memory()
Clears the memory.
Definition: lagrange_enforced_flow_preconditioner.cc:1250

References clean_up_memory().

◆ LagrangeEnforcedFlowPreconditioner() [2/2]

oomph::LagrangeEnforcedFlowPreconditioner::LagrangeEnforcedFlowPreconditioner ( const LagrangeEnforcedFlowPreconditioner )
delete

Broken copy constructor.

Member Function Documentation

◆ clean_up_memory()

void oomph::LagrangeEnforcedFlowPreconditioner::clean_up_memory ( )
virtual

Clears the memory.

Reimplemented from oomph::Preconditioner.

1251  {
1252  // clean the block preconditioner base class memory
1254 
1255  // Delete the Navier-Stokes preconditioner pointer if we have created it.
1257  {
1260  }
1261 
1263  } // func LagrangeEnforcedFlowPreconditioner::clean_up_memory
void clear_block_preconditioner_base()
Definition: block_preconditioner.h:2159

References oomph::BlockPreconditioner< CRDoubleMatrix >::clear_block_preconditioner_base(), Navier_stokes_preconditioner_pt, Preconditioner_has_been_setup, and Using_superlu_ns_preconditioner.

Referenced by setup(), and ~LagrangeEnforcedFlowPreconditioner().

◆ operator=()

void oomph::LagrangeEnforcedFlowPreconditioner::operator= ( const LagrangeEnforcedFlowPreconditioner )
delete

Broken assignment operator.

◆ preconditioner_solve()

void oomph::LagrangeEnforcedFlowPreconditioner::preconditioner_solve ( const DoubleVector r,
DoubleVector z 
)
virtual

Apply the preconditioner. r is the residual (rhs), z will contain the solution.

///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Apply the preconditioner. r is the residual (rhs), z will contain the solution.

Implements oomph::Preconditioner.

312  {
313 #ifdef PARANOID
314  if (Preconditioner_has_been_setup == false)
315  {
316  std::ostringstream error_message;
317  error_message << "setup() must be called before using "
318  << "preconditioner_solve()";
319  throw OomphLibError(
320  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
321  }
322  if (z.built())
323  {
324  if (z.nrow() != r.nrow())
325  {
326  std::ostringstream error_message;
327  error_message << "The vectors z and r must have the same number of "
328  << "of global rows";
329  throw OomphLibError(error_message.str(),
332  }
333  }
334 #endif
335 
336  // if z is not setup then give it the same distribution
337  if (!z.distribution_pt()->built())
338  {
339  z.build(r.distribution_pt(), 0.0);
340  }
341 
342  // Working vectors.
343  DoubleVector temp_vec;
344  DoubleVector another_temp_vec;
345  DoubleVector yet_another_temp_vec;
346 
347 
348  // -----------------------------------------------------------------------
349  // Step 1 - apply approximate W block inverse to Lagrange multiplier
350  // unknowns
351  // -----------------------------------------------------------------------
352  // For each subsystem associated with each Lagrange multiplier, we loop
353  // through and:
354  // 1) extract the block entries from r
355  // 2) apply the inverse
356  // 3) return the entries to z.
357  for (unsigned l_i = 0; l_i < N_lagrange_doftypes; l_i++)
358  {
359  // The Lagrange multiplier block type.
360  const unsigned l_ii = N_fluid_doftypes + l_i;
361 
362  // Extract the block
363  this->get_block_vector(l_ii, r, temp_vec);
364 
365  // Apply the inverse.
366  const unsigned vec_nrow_local = temp_vec.nrow_local();
367  double* vec_values_pt = temp_vec.values_pt();
368  for (unsigned i = 0; i < vec_nrow_local; i++)
369  {
370  vec_values_pt[i] = vec_values_pt[i] * Inv_w_diag_values[l_i][i];
371  } // for
372 
373  // Return the unknowns
374  this->return_block_vector(l_ii, temp_vec, z);
375 
376  // Clear vectors.
377  temp_vec.clear();
378  } // for
379 
380  // -----------------------------------------------------------------------
381  // Step 2 - apply the augmented Navier-Stokes matrix inverse to the
382  // velocity and pressure unknowns
383  // -----------------------------------------------------------------------
384 
385  // At this point, all vectors are cleared.
387  {
388  // Which block types corresponds to the fluid block types.
389  Vector<unsigned> fluid_block_indices(N_fluid_doftypes, 0);
390  for (unsigned b = 0; b < N_fluid_doftypes; b++)
391  {
392  fluid_block_indices[b] = b;
393  }
394 
395  this->get_concatenated_block_vector(fluid_block_indices, r, temp_vec);
396 
397  // temp_vec contains the (concatenated) fluid rhs.
399  another_temp_vec);
400 
401  temp_vec.clear();
402 
403  // Return it to the unknowns.
405  fluid_block_indices, another_temp_vec, z);
406 
407  another_temp_vec.clear();
408  }
409  else
410  {
411  // The Navier-Stokes preconditioner is a block preconditioner.
412  // Thus is handles all of the block vector extraction and returns.
414  }
415  } // end of preconditioner_solve
int i
Definition: BiCGSTAB_step_by_step.cpp:9
std::vector< double > DoubleVector
loads clump configuration
Definition: ClumpInput.h:26
Scalar * b
Definition: benchVecAdd.cpp:17
void return_block_vector(const unsigned &n, const DoubleVector &b, DoubleVector &v) const
Definition: block_preconditioner.cc:4489
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
void get_block_vector(const unsigned &n, const DoubleVector &v, DoubleVector &b) const
Definition: block_preconditioner.cc:4186
void get_concatenated_block_vector(const Vector< unsigned > &block_vec_number, const DoubleVector &v, DoubleVector &b)
Definition: block_preconditioner.cc:2608
Vector< Vector< double > > Inv_w_diag_values
Inverse W values.
Definition: lagrange_enforced_flow_preconditioner.h:316
virtual void preconditioner_solve(const DoubleVector &r, DoubleVector &z)=0
r
Definition: UniformPSDSelfTest.py:20
#define OOMPH_EXCEPTION_LOCATION
Definition: oomph_definitions.h:61
#define OOMPH_CURRENT_FUNCTION
Definition: oomph_definitions.h:86

References b, oomph::DoubleVector::build(), oomph::DoubleVector::built(), oomph::LinearAlgebraDistribution::built(), oomph::DoubleVector::clear(), oomph::DistributableLinearAlgebraObject::distribution_pt(), oomph::BlockPreconditioner< CRDoubleMatrix >::get_block_vector(), oomph::BlockPreconditioner< CRDoubleMatrix >::get_concatenated_block_vector(), i, Inv_w_diag_values, N_fluid_doftypes, N_lagrange_doftypes, Navier_stokes_preconditioner_pt, oomph::DistributableLinearAlgebraObject::nrow(), oomph::DistributableLinearAlgebraObject::nrow_local(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, Preconditioner_has_been_setup, oomph::Preconditioner::preconditioner_solve(), UniformPSDSelfTest::r, oomph::BlockPreconditioner< CRDoubleMatrix >::return_block_vector(), oomph::BlockPreconditioner< CRDoubleMatrix >::return_concatenated_block_vector(), Using_superlu_ns_preconditioner, and oomph::DoubleVector::values_pt().

◆ scaling_sigma()

double oomph::LagrangeEnforcedFlowPreconditioner::scaling_sigma ( ) const
inline

Read (const) function to get the scaling sigma.

278  {
279  return Scaling_sigma;
280  }

References Scaling_sigma.

Referenced by set_scaling_sigma().

◆ set_meshes()

void oomph::LagrangeEnforcedFlowPreconditioner::set_meshes ( const Vector< Mesh * > &  mesh_pt)

Set the meshes, the first mesh in the vector must be the bulk mesh.

421  {
422  // There should be at least two meshes passed to this preconditioner.
423  const unsigned nmesh = mesh_pt.size();
424 
425 #ifdef PARANOID
426  if (nmesh < 2)
427  {
428  std::ostringstream err_msg;
429  err_msg << "There should be at least two meshes.\n";
430  throw OomphLibError(
432  }
433 
434  // Check that all pointers are not null
435  for (unsigned mesh_i = 0; mesh_i < nmesh; mesh_i++)
436  {
437  if (mesh_pt[mesh_i] == 0)
438  {
439  std::ostringstream err_msg;
440  err_msg << "The pointer mesh_pt[" << mesh_i << "] is null.\n";
441  throw OomphLibError(
443  }
444  }
445 
446  // We assume that the first mesh is the Navier-Stokes "bulk" mesh.
447  // To check this, the elemental dimension must be the same as the
448  // nodal (spatial) dimension.
449  //
450  // We store the elemental dimension i.e. the number of local coordinates
451  // required to parametrise its geometry.
452  const unsigned elemental_dim = mesh_pt[0]->elemental_dimension();
453 
454  // The dimension of the nodes in the first element in the (supposedly)
455  // bulk mesh.
456  const unsigned nodal_dim = mesh_pt[0]->nodal_dimension();
457 
458  // Check if the first mesh is the "bulk" mesh.
459  // Here we assume only one mesh contains "bulk" elements.
460  // All subsequent meshes contain block preconditionable elements which
461  // re-classify the bulk velocity DOFs to constrained velocity DOFs.
462  if (elemental_dim != nodal_dim)
463  {
464  std::ostringstream err_msg;
465  err_msg << "In the first mesh, the elements have elemental dimension "
466  << "of " << elemental_dim << ",\n"
467  << "with a nodal dimension of " << nodal_dim << ".\n"
468  << "The first mesh does not contain 'bulk' elements.\n"
469  << "Please re-order your mesh_pt vector.\n";
470 
471  throw OomphLibError(
473  }
474 #endif
475 
476  // Set the number of meshes
477  this->set_nmesh(nmesh);
478 
479  // Set the meshes
480  for (unsigned mesh_i = 0; mesh_i < nmesh; mesh_i++)
481  {
482  this->set_mesh(mesh_i, mesh_pt[mesh_i]);
483  }
484 
485  // We also store the meshes and number of meshes locally in this class.
486  // This may seem slightly redundant, since we always have all the meshes
487  // stored in the upper most master block preconditioner.
488  // But at the moment there is no mapping/look up scheme between
489  // master and subsidiary block preconditioners for meshes.
490  //
491  // If this is a subsidiary block preconditioner, we don't know which of
492  // the master's meshes belong to us. We need this information to set up
493  // look up lists in the function setup(...).
494  // Thus we store them local to this class.
496  My_nmesh = nmesh;
497  } // function set_meshes
unsigned nmesh() const
Definition: block_preconditioner.h:1816
const Mesh * mesh_pt(const unsigned &i) const
Definition: block_preconditioner.h:1782
void set_nmesh(const unsigned &n)
Definition: block_preconditioner.h:2851
void set_mesh(const unsigned &i, const Mesh *const mesh_pt, const bool &allow_multiple_element_type_in_mesh=false)
Definition: block_preconditioner.h:2866
unsigned nodal_dimension() const
Return number of nodal dimension in mesh.
Definition: mesh.cc:9055
unsigned elemental_dimension() const
Return number of elemental dimension in mesh.
Definition: mesh.cc:8917

References oomph::Mesh::elemental_dimension(), oomph::BlockPreconditioner< CRDoubleMatrix >::mesh_pt(), My_mesh_pt, My_nmesh, oomph::BlockPreconditioner< CRDoubleMatrix >::nmesh(), oomph::Mesh::nodal_dimension(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, oomph::BlockPreconditioner< CRDoubleMatrix >::set_mesh(), and oomph::BlockPreconditioner< CRDoubleMatrix >::set_nmesh().

Referenced by TiltedCavityProblem< ELEMENT >::TiltedCavityProblem().

◆ set_navier_stokes_preconditioner()

void oomph::LagrangeEnforcedFlowPreconditioner::set_navier_stokes_preconditioner ( Preconditioner new_ns_preconditioner_pt = 0)

Set a new Navier-Stokes matrix preconditioner (inexact solver)

Function to set a new momentum matrix preconditioner (inexact solver)

1216  {
1217  // Check if pointer is non-zero.
1218  if (new_ns_preconditioner_pt == 0)
1219  {
1220  std::ostringstream warning_stream;
1221  warning_stream << "WARNING: \n"
1222  << "The LSC preconditioner point is null.\n"
1223  << "Using default (SuperLU) preconditioner.\n"
1224  << std::endl;
1225  OomphLibWarning(
1226  warning_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
1227 
1230  }
1231  else
1232  {
1233  // If the default SuperLU preconditioner has been used
1234  // clean it up now...
1237  {
1240  }
1241 
1242  Navier_stokes_preconditioner_pt = new_ns_preconditioner_pt;
1244  }
1245  } // func set_navier_stokes_preconditioner

References Navier_stokes_preconditioner_pt, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, and Using_superlu_ns_preconditioner.

Referenced by TiltedCavityProblem< ELEMENT >::TiltedCavityProblem().

◆ set_scaling_sigma()

void oomph::LagrangeEnforcedFlowPreconditioner::set_scaling_sigma ( const double scaling_sigma)
inline

Access function to set the scaling sigma. Note: this also sets the flag to use the infinite norm of the Navier-Stokes F matrix as the scaling sigma to false. Warning is given if trying to set scaling sigma to be equal to or greater than zero.

247  {
248  // Check if scaling sigma is zero or positive.
249 #ifdef PARANOID
250  if (scaling_sigma == 0.0)
251  {
252  std::ostringstream warning_stream;
253  warning_stream << "WARNING: \n"
254  << "Setting scaling_sigma = 0.0 may cause values.\n";
255  OomphLibWarning(warning_stream.str(),
258  }
259  if (scaling_sigma > 0.0)
260  {
261  std::ostringstream warning_stream;
262  warning_stream << "WARNING: " << std::endl
263  << "The scaling (scaling_sigma) is positive: "
264  << Scaling_sigma << "\n"
265  << "Performance may be degraded.\n";
266  OomphLibWarning(warning_stream.str(),
269  }
270 #endif
271 
274  }
double scaling_sigma() const
Read (const) function to get the scaling sigma.
Definition: lagrange_enforced_flow_preconditioner.h:277

References OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, scaling_sigma(), Scaling_sigma, and Use_norm_f_for_scaling_sigma.

◆ set_superlu_for_navier_stokes_preconditioner()

void oomph::LagrangeEnforcedFlowPreconditioner::set_superlu_for_navier_stokes_preconditioner ( )
inline

Set Navier-Stokes matrix preconditioner (inexact solver) to SuperLU

290  {
292  {
294  Navier_stokes_preconditioner_pt = new SuperLUPreconditioner;
296  }
297  }

References Navier_stokes_preconditioner_pt, and Using_superlu_ns_preconditioner.

Referenced by TiltedCavityProblem< ELEMENT >::TiltedCavityProblem().

◆ setup()

void oomph::LagrangeEnforcedFlowPreconditioner::setup ( )
virtual

Setup method for the LagrangeEnforcedFlowPreconditioner.

Setup the Lagrange enforced flow preconditioner. This extracts blocks corresponding to the velocity and Lagrange multiplier unknowns, creates the matrices actually needed in the application of the preconditioner and deletes what can be deleted... Note that this preconditioner needs a CRDoubleMatrix.

Implements oomph::Preconditioner.

508  {
509  // clean
510  this->clean_up_memory();
511 
512 #ifdef PARANOID
513  // Paranoid check that meshes have been set. In this preconditioner, we
514  // always have to set meshes.
515  if (My_nmesh == 0)
516  {
517  std::ostringstream err_msg;
518  err_msg << "There are no meshes set. Please call set_meshes(...)\n"
519  << "with at least two mesh.";
520  throw OomphLibError(
522  }
523 #endif
524 
525  // -----------------------------------------------------------------------
526  // Step 1 - Construct the dof_to_block_map vector.
527  // -----------------------------------------------------------------------
528  // Assumption: The first mesh is always the "bulk" mesh
529  // (Navier-Stokes mesh), which contains block preconditionable
530  // Navier-Stokes elements. Thus the bulk elements classify the velocity
531  // and pressure degrees of freedom (ndof_types = 3(4) in 2(3)D).
532  // All subsequent meshes contain the constrained velocity DOF types,
533  // then the Lagrange multiplier DOF types.
534  //
535  // Thus, a general ordering of DOF types (in 3D) follows the ordering:
536  //
537  // 0 1 2 3 4 5 6 7 8 ..x x+0 x+1 x+2 x+3 x+4
538  // [u v w p] [u v w l1 l2 ...] [u v w l1 l2 ...] ...
539  //
540  // where the square brackets [] represent the DOF types in each mesh.
541  //
542  // Example:
543  // Consider the case of imposing parallel outflow (3 constrained velocity
544  // DOF types and 2 Lagrange multiplier DOF types) and tangential flow (3
545  // constrained velocity DOF types and 1 Lagrange multiplier DOF type)
546  // along two different boundaries in 3D. The resulting natural ordering of
547  // the DOF types is:
548  //
549  // [0 1 2 3] [4 5 6 7 8 ] [9 10 11 12 ]
550  // [u v w p] [up vp wp Lp1 Lp2] [ut vt wt Lt1]
551  //
552  //
553  // In our implementation, the desired block structure is:
554  // | u v w | up vp wp | ut vt wt | p | Lp1 Lp2 Lt1 |
555  //
556  // The dof_to_block_map should have the following construction:
557  //
558  // dof_to_block_map[$dof_number] = $block_number
559  //
560  // Thus, the dof_to_block_map for the example above should be:
561  //
562  // To achieve this, we use the dof_to_block_map:
563  // dof_to_block_map = [0 1 2 9 3 4 5 10 11 6 7 8 12]
564  //
565  // To generalise the construction of the dof_to_block_map vector
566  // (to work for any number of meshes), we first require some auxiliary
567  // variables to aid us in this endeavour.
568  // -----------------------------------------------------------------------
569 
570  // Set up the My_ndof_types_in_mesh vector.
571  // If this is already constructed, we reuse it instead.
572  if (My_ndof_types_in_mesh.size() == 0)
573  {
574  for (unsigned mesh_i = 0; mesh_i < My_nmesh; mesh_i++)
575  {
576  My_ndof_types_in_mesh.push_back(My_mesh_pt[mesh_i]->ndof_types());
577  }
578  }
579 
580  // Get the spatial dimension of the problem.
581  unsigned spatial_dim = My_mesh_pt[0]->nodal_dimension();
582 
583  // Get the number of DOF types.
584  unsigned n_dof_types = ndof_types();
585 
586 #ifdef PARANOID
587  // We cannot check which DOF types are "correct" in the sense that there
588  // is a distinction between bulk and constrained velocity DOF tyoes.
589  // But we can at least check if the ndof_types matches the total number
590  // of DOF types from the meshes.
591  //
592  // This check is not necessary for the upper most master block
593  // preconditioner since the ndof_types() is calculated by looping
594  // through the meshes!
595  //
596  // This check is useful if this is a subsidiary block preconditioner and
597  // incorrect DOF type merging has taken place.
599  {
600  unsigned tmp_ndof_types = 0;
601  for (unsigned mesh_i = 0; mesh_i < My_nmesh; mesh_i++)
602  {
603  tmp_ndof_types += My_ndof_types_in_mesh[mesh_i];
604  }
605 
606  if (tmp_ndof_types != n_dof_types)
607  {
608  std::ostringstream err_msg;
609  err_msg << "The number of DOF types are incorrect.\n"
610  << "The total DOF types from the meshes is: " << tmp_ndof_types
611  << ".\n"
612  << "The number of DOF types from "
613  << "BlockPreconditioner::ndof_types() is " << n_dof_types
614  << ".\n";
615  throw OomphLibError(
617  }
618  }
619 #endif
620 
621  // The number of velocity DOF types: We assume that all meshes classify
622  // at least some of the velocity DOF types (bulk/constrained), thus the
623  // total number of velocity DOF types is the spatial dimension multiplied
624  // by the number of meshes.
625  N_velocity_doftypes = My_nmesh * spatial_dim;
626 
627  // Fluid has +1 for the pressure.
629 
630  // The rest are Lagrange multiplier DOF types.
631  N_lagrange_doftypes = n_dof_types - N_fluid_doftypes;
632 
634  // Now construct the DOF to block map for block_setup() //
636 
637  // Now that we have
638  //
639  // (1) spatial dimension of the problem and
640  // (2) the number of DOF types in each of the meshes.
641  //
642  // We observe that the problem dimension is 3 and
643  // My_ndof_type_in_mesh = [4, 5, 4].
644  //
645  // With these information we can construct the desired block structure:
646  // | u v w | up vp wp | ut vt wt | p | Lp1 Lp2 Lt1 |
647  //
648  // The block structure is determined by the vector dof_to_block_map we
649  // give to the function block_setup(...).
650 
651  // This preconditioner permutes the DOF numbers to get the block numbers.
652  // I.e. nblock_types = ndof_types, but they are re-ordered
653  // so that we have (in order):
654  // 1) bulk velocity DOF types
655  // 2) constrained velocity DOF types
656  // 3) pressure DOF type
657  // 4) Lagrange multiplier DOF types
658  //
659  // Recall that the natural ordering of the DOF types are ordered first by
660  // their meshes, then the elemental DOF type as described by the
661  // function get_dof_numbers_for_unknowns().
662  //
663  // Also recall that (for this problem), we assume/require that every mesh
664  // (re-)classify at least some of the velocity DOF types, furthermore,
665  // the velocity DOF type classification comes before the
666  // pressure / Lagrange multiplier DOF types.
667  //
668  // Consider the same example as shown previously, repeated here for your
669  // convenience:
670  //
671  // Natural DOF type ordering:
672  // 0 1 2 3 4 5 6 7 8 9 10 11 12 <- DOF number.
673  // [u v w p] [up vp wp Lp1 Lp2] [ut vt wt Lt1] <- DOF type.
674  //
675  // Desired block structure:
676  // 0 1 2 3 4 5 6 7 8 9 10 11 12 <- block number
677  // [u v w | up vp wp | ut vt wt ] [p | Lp1 Lp2 Lt1] <- DOF type
678  //
679  // dof_to_block_map[$dof_number] = $block_number
680  //
681  // Required dof_to_block_map:
682  // 0 1 2 9 3 4 5 10 11 6 7 8 12 <- block number
683  // [u v w p up vp wp Lp1 Lp2 ut vt wt Lt1] <- DOF type
684  //
685  // Consider the 4th entry of the dof_to_block_map, this represents the
686  // pressure DOF type, from the desired block structure we see this takes
687  // the block number 9.
688  //
689  // One way to generalise the construction of the dof_to_block_map is to
690  // lump the first $spatial_dimension number of DOF types
691  // from each mesh together, then lump the remaining DOF types together.
692  //
693  // Notice that the values in the velocity DOF types of the
694  // dof_to_block_map vector increases sequentially, from 0 to 8. The values
695  // of the Lagrange multiplier DOF types follow the same pattern, from 9 to
696  // 12. We follow this construction.
697 
698  // Storage for the dof_to_block_map vector.
699  Vector<unsigned> dof_to_block_map(n_dof_types, 0);
700 
701  // Index for the dof_to_block_map vector.
702  unsigned temp_index = 0;
703 
704  // Value for the velocity DOF type.
705  unsigned velocity_number = 0;
706 
707  // Value for the pressure/Lagrange multiplier DOF type.
708  unsigned pres_lgr_number = N_velocity_doftypes;
709 
710  // Loop through the number of meshes.
711  for (unsigned mesh_i = 0; mesh_i < My_nmesh; mesh_i++)
712  {
713  // Fill in the velocity DOF types.
714  for (unsigned dim_i = 0; dim_i < spatial_dim; dim_i++)
715  {
716  dof_to_block_map[temp_index++] = velocity_number++;
717  } // for
718 
719  // Loop through the pressure/Lagrange multiplier DOF types.
720  unsigned ndof_type_in_mesh_i = My_ndof_types_in_mesh[mesh_i];
721  for (unsigned doftype_i = spatial_dim; doftype_i < ndof_type_in_mesh_i;
722  doftype_i++)
723  {
724  dof_to_block_map[temp_index++] = pres_lgr_number++;
725  } // for
726  } // for
727 
728  // Call the block setup
729  this->block_setup(dof_to_block_map);
730 
731 
732  // -----------------------------------------------------------------------
733  // Step 2 - Get the infinite norm of Navier-Stokes F block.
734  // -----------------------------------------------------------------------
735 
736  // Extract the velocity blocks.
739 
740  for (unsigned row_i = 0; row_i < N_velocity_doftypes; row_i++)
741  {
742  for (unsigned col_i = 0; col_i < N_velocity_doftypes; col_i++)
743  {
744  v_aug_pt(row_i, col_i) = new CRDoubleMatrix;
745  this->get_block(row_i, col_i, *v_aug_pt(row_i, col_i));
746  } // for
747  } // for
748 
749  // Now get the infinite norm.
751  {
753  } // if
754 
755 #ifdef PARANOID
756  // Warning for division by zero.
757  if (Scaling_sigma == 0.0)
758  {
759  std::ostringstream warning_stream;
760  warning_stream << "WARNING: " << std::endl
761  << "The scaling (Scaling_sigma) is " << Scaling_sigma
762  << ".\n"
763  << "Division by 0 will occur."
764  << "\n";
765  OomphLibWarning(
766  warning_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
767  }
768  if (Scaling_sigma > 0.0)
769  {
770  std::ostringstream warning_stream;
771  warning_stream << "WARNING: " << std::endl
772  << "The scaling (Scaling_sigma) is positive: "
773  << Scaling_sigma << std::endl
774  << "Performance may be degraded." << std::endl;
775  OomphLibWarning(
776  warning_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
777  }
778 #endif
779 
780  // -----------------------------------------------------------------------
781  // Step 3 - Augment the constrained fluid blocks.
782  // -----------------------------------------------------------------------
783  // Loop through the Lagrange multipliers and do three things:
784  // For each Lagrange block:
785  // 3.1) Extract the mass matrices and store the location of non-zero mass
786  // matrices.
787  //
788  // 3.2) a) Create inv_w (for the augmentation)
789  // b) Store the diagonal values of inv_w (for preconditioner solve)
790  //
791  // 3.3) Perform the augmentation: v_aug + m_i * inv(w_i) * m_j
792  //
793  // 3.4) Clean up memory.
794 
795  // Storage for the inv w diag values.
796  Inv_w_diag_values.clear();
797  for (unsigned l_i = 0; l_i < N_lagrange_doftypes; l_i++)
798  {
799  // Step 3.1: Location and extraction of non-zero mass matrices.
800 
801  // Storage for the current Lagrange block mass matrices.
802  Vector<CRDoubleMatrix*> mm_pt;
803  Vector<CRDoubleMatrix*> mmt_pt;
804 
805  // Block type for the Lagrange multiplier.
806  const unsigned lgr_block_num = N_fluid_doftypes + l_i;
807 
808  // Store the mass matrix locations for the current Lagrange block.
809  Vector<unsigned> mm_locations;
810 
811  // Store the number of mass matrices.
812  unsigned n_mm = 0;
813 
814  // Go along the block columns for the current Lagrange block ROW.
815  for (unsigned col_i = 0; col_i < N_velocity_doftypes; col_i++)
816  {
817  // Get the block matrix for this block column.
818  CRDoubleMatrix* mm_temp_pt = new CRDoubleMatrix;
819  this->get_block(lgr_block_num, col_i, *mm_temp_pt);
820 
821  // Check if this is non-zero
822  if (mm_temp_pt->nnz() > 0)
823  {
824  mm_locations.push_back(col_i);
825  mm_pt.push_back(mm_temp_pt);
826  n_mm++;
827  }
828  else
829  {
830  // This is just an empty matrix. No need to keep this.
831  delete mm_temp_pt;
832  }
833  } // loop through the columns of the Lagrange row.
834 
835 #ifdef PARANOID
836  if (n_mm == 0)
837  {
838  std::ostringstream warning_stream;
839  warning_stream << "WARNING:\n"
840  << "There are no mass matrices on Lagrange block row "
841  << l_i << ".\n"
842  << "Perhaps the problem setup is incorrect."
843  << std::endl;
844  OomphLibWarning(warning_stream.str(),
847  }
848 #endif
849 
850  // Get the transpose of the mass matrices.
851  for (unsigned mm_i = 0; mm_i < n_mm; mm_i++)
852  {
853  // Get the block matrix for this block column.
854  CRDoubleMatrix* mm_temp_pt = new CRDoubleMatrix;
855  this->get_block(mm_locations[mm_i], lgr_block_num, *mm_temp_pt);
856 
857  if (mm_temp_pt->nnz() > 0)
858  {
859  mmt_pt.push_back(mm_temp_pt);
860  }
861  else
862  {
863  // There should be a non-zero mass matrix here, since L=(L^T)^T
864 #ifdef PARANOID
865  {
866  std::ostringstream warning_stream;
867  warning_stream << "WARNING:\n"
868  << "The mass matrix block " << mm_locations[mm_i]
869  << " in L^T block " << l_i << " is zero.\n"
870  << "Perhaps the problem setup is incorrect."
871  << std::endl;
872  OomphLibWarning(warning_stream.str(),
875  }
876 #endif
877  }
878  } // loop through the ROW of the Lagrange COLUMN.
879 
880  // Step 3.2: Create inv_w and store its diagonal entries.
881 
882  // Storage for inv_w
883  CRDoubleMatrix* inv_w_pt =
884  new CRDoubleMatrix(this->Block_distribution_pt[lgr_block_num]);
885 
886  // Get the number of local rows for this Lagrange block.
887  unsigned long l_i_nrow_local =
888  this->Block_distribution_pt[lgr_block_num]->nrow_local();
889 
890  // The first row, for the column offset (required in parallel).
891  unsigned l_i_first_row =
892  this->Block_distribution_pt[lgr_block_num]->first_row();
893 
894  // A vector to contain the results of mass matrices squared.
895  Vector<double> w_i_diag_values(l_i_nrow_local, 0);
896 
897  // Create mm*mm^T, and component-wise add the mass matrices
898  for (unsigned m_i = 0; m_i < n_mm; m_i++)
899  {
900  // Create mm*mm^T
901  CRDoubleMatrix temp_mm_sqrd;
902  temp_mm_sqrd.build(mm_pt[m_i]->distribution_pt());
903  mm_pt[m_i]->multiply(*mmt_pt[m_i], temp_mm_sqrd);
904 
905  // Extract diagonal entries
906  Vector<double> m_diag = temp_mm_sqrd.diagonal_entries();
907 
908  // Loop through the entries, add them.
909  for (unsigned long row_i = 0; row_i < l_i_nrow_local; row_i++)
910  {
911  w_i_diag_values[row_i] += m_diag[row_i];
912  }
913  }
914 
915  // Storage for inv_w matrix vectors
916  Vector<double> invw_i_diag_values(l_i_nrow_local, 0);
917  Vector<int> w_i_column_indices(l_i_nrow_local);
918  Vector<int> w_i_row_start(l_i_nrow_local + 1);
919 
920  // Divide by Scaling_sigma and create the inverse of w.
921  for (unsigned long row_i = 0; row_i < l_i_nrow_local; row_i++)
922  {
923  invw_i_diag_values[row_i] = Scaling_sigma / w_i_diag_values[row_i];
924 
925  w_i_column_indices[row_i] = row_i + l_i_first_row;
926  w_i_row_start[row_i] = row_i;
927  }
928 
929  w_i_row_start[l_i_nrow_local] = l_i_nrow_local;
930 
931  // Theses are square matrices. So we can use the l_i_nrow_global for the
932  // number of columns.
933  unsigned long l_i_nrow_global =
934  this->Block_distribution_pt[lgr_block_num]->nrow();
935  inv_w_pt->build(
936  l_i_nrow_global, invw_i_diag_values, w_i_column_indices, w_i_row_start);
937 
938  Inv_w_diag_values.push_back(invw_i_diag_values);
939 
940 
941  // Step 3.3: Perform the augmentation: v_aug + m_i * inv(w_i) * m_j
942 
944  // Now we create the augmented matrix in v_aug_pt.
945  // v_aug_pt is already re-ordered
946  // Loop through the mm_locations
947  for (unsigned ii = 0; ii < n_mm; ii++)
948  {
949  // Location of the mass matrix
950  unsigned aug_i = mm_locations[ii];
951 
952  for (unsigned jj = 0; jj < n_mm; jj++)
953  {
954  // Location of the mass matrix
955  unsigned aug_j = mm_locations[jj];
956 
957  // Storage for intermediate results.
958  CRDoubleMatrix aug_block;
959  CRDoubleMatrix another_aug_block;
960 
961  // aug_block = mmt*inv_w
962  mmt_pt[ii]->multiply((*inv_w_pt), (aug_block));
963 
964  // another_aug_block = aug_block*mm = mmt*inv_w*mm
965  aug_block.multiply(*mm_pt[jj], another_aug_block);
966 
967  // Add the augmentation.
968  v_aug_pt(aug_i, aug_j)
969  ->add(another_aug_block, *v_aug_pt(aug_i, aug_j));
970  } // loop jj
971  } // loop ii
972 
973  // Step 3.4: Clean up memory.
974  delete inv_w_pt;
975  inv_w_pt = 0;
976  for (unsigned m_i = 0; m_i < n_mm; m_i++)
977  {
978  delete mm_pt[m_i];
979  mm_pt[m_i] = 0;
980  delete mmt_pt[m_i];
981  mmt_pt[m_i] = 0;
982  }
983  } // loop through Lagrange multipliers.
984 
985  // -----------------------------------------------------------------------
986  // Step 4 - Replace all the velocity blocks
987  // -----------------------------------------------------------------------
988  // When we replace (DOF) blocks, the indices have to respect the DOF type
989  // ordering. This is because only DOF type information is passed between
990  // preconditioners. So we need to create the inverse of the
991  // dof_to_block_map... a block_to_dof_map!
992  //
993  // Note: Once the DOF blocks have been replaced, further calls to
994  // get_block at this preconditioning hierarchy level and/or lower will use
995  // the (nearest) replaced blocks.
996  Vector<unsigned> block_to_dof_map(n_dof_types, 0);
997  for (unsigned dof_i = 0; dof_i < n_dof_types; dof_i++)
998  {
999  block_to_dof_map[dof_to_block_map[dof_i]] = dof_i;
1000  }
1001 
1002  // Now do the replacement of all blocks in v_aug_pt
1003  for (unsigned block_row_i = 0; block_row_i < N_velocity_doftypes;
1004  block_row_i++)
1005  {
1006  unsigned temp_dof_row_i = block_to_dof_map[block_row_i];
1007  for (unsigned block_col_i = 0; block_col_i < N_velocity_doftypes;
1008  block_col_i++)
1009  {
1010  unsigned temp_dof_col_i = block_to_dof_map[block_col_i];
1012  temp_dof_row_i, temp_dof_col_i, v_aug_pt(block_row_i, block_col_i));
1013  }
1014  }
1015 
1016  // -----------------------------------------------------------------------
1017  // Step 5 - Set up Navier-Stokes preconditioner
1018  // If the subsidiary fluid preconditioner is a block preconditioner:
1019  // 5.1) Set up the dof_number_in_master_map
1020  // 5.2) Set up the doftype_coarsen_map
1021  // otherwise:
1022  // 5.3) Concatenate the fluid matrices into one big matrix and pass it
1023  // to the solver.
1024  // -----------------------------------------------------------------------
1025 
1026  // First we determine if we're using a block preconditioner or not.
1027  BlockPreconditioner<CRDoubleMatrix>* navier_stokes_block_preconditioner_pt =
1028  dynamic_cast<BlockPreconditioner<CRDoubleMatrix>*>(
1031  if (navier_stokes_block_preconditioner_pt == 0)
1032  {
1034  }
1035 
1036  // If the Navier-Stokes preconditioner is a block preconditioner, then we
1037  // need to turn it into a subsidiary block preconditioner.
1039  {
1040  // Assumption: All Navier-Stokes block preconditioners take $dim
1041  // number of velocity DOF types and 1 pressure DOF type.
1042 
1043  // Step 5.1: Set up the dof_number_in_master_map
1044 
1045  // First we create the dof_number_in_master_map vector to pass to the
1046  // subsidiary preconditioner's
1047  // turn_into_subsidiary_block_preconditioner(...) function.
1048  //
1049  // This vector maps the subsidiary DOF numbers and the master DOF
1050  // numbers and has the construction:
1051  //
1052  // dof_number_in_master_map[subsidiary DOF number] = master DOF number
1053  //
1054  // Example: Using the example above, our problem has the natural
1055  // DOF type ordering:
1056  //
1057  // 0 1 2 3 4 5 6 7 8 9 10 11 12 <- DOF number in master
1058  // [u v w p] [up vp wp Lp1 Lp2] [ut vt wt Lt1] <- DOF type
1059  //
1060  // For now, we ignore the fact that this preconditioner's number of
1061  // DOF types may be more fine grain than what is assumed by the
1062  // subsidiary block preconditioner. Normally, the order of the values in
1063  // dof_number_in_master_map matters, since the indices must match the
1064  // DOF type in the subsidiary block preconditioner (see the assumption
1065  // above). For example, if the (subsidiary) LSC block preconditioner
1066  // requires the DOF type ordering:
1067  //
1068  // [0 1 2 3]
1069  // u v w p
1070  //
1071  // Then the dof_number_in_master_map vector must match the u velocity
1072  // DOF type in the subsidiary preconditioner with the u velocity in the
1073  // master preconditioner, etc...
1074  //
1075  // However, we shall see (later) that it does not matter in this
1076  // instance because the DOF type coarsening feature overrides the
1077  // ordering provided here.
1078  //
1079  // For now, we only need to ensure that the subsidiary preconditioner
1080  // knows about 10 master DOF types (9 velocity and 1 pressure), the
1081  // ordering does not matter.
1082  //
1083  // We pass to the subsidiary block preconditioner the following
1084  // dof_number_in_master_map:
1085  // [0 1 2 4 5 6 9 10 11 3] <- DOF number in master
1086  // u v w up vp wp ut vt wt p <- corresponding DOF type
1087 
1088  // Variable to keep track of the DOF number.
1089  unsigned temp_dof_number = 0;
1090 
1091  // Storage for the dof_number_in_master_map
1092  Vector<unsigned> dof_number_in_master_map;
1093  for (unsigned mesh_i = 0; mesh_i < My_nmesh; mesh_i++)
1094  {
1095  // Store the velocity dof types.
1096  for (unsigned dim_i = 0; dim_i < spatial_dim; dim_i++)
1097  {
1098  dof_number_in_master_map.push_back(temp_dof_number + dim_i);
1099  } // for spatial_dim
1100 
1101  // Update the DOF index
1102  temp_dof_number += My_ndof_types_in_mesh[mesh_i];
1103  } // for My_nmesh
1104 
1105  // Push back the pressure DOF type
1106  dof_number_in_master_map.push_back(spatial_dim);
1107 
1108 
1109  // Step 5.2 DOF type coarsening.
1110 
1111  // Since this preconditioner works with more fine grained DOF types than
1112  // the subsidiary block preconditioner, we need to tell the subsidiary
1113  // preconditioner which DOF types to coarsen together.
1114  //
1115  // The LSC preconditioner expects 2(3) velocity dof types, and 1
1116  // pressure DOF types. Thus, we give it this list:
1117  // u [0, 3, 6]
1118  // v [1, 4, 7]
1119  // w [2, 5, 8]
1120  // p [9]
1121  //
1122  // See how the ordering of dof_number_in_master_map (constructed above)
1123  // does not matter as long as we construct the doftype_coarsen_map
1124  // correctly.
1125 
1126  // Storage for which subsidiary DOF types to coarsen
1127  Vector<Vector<unsigned>> doftype_coarsen_map;
1128  for (unsigned direction = 0; direction < spatial_dim; direction++)
1129  {
1130  Vector<unsigned> dir_doftypes_vec(My_nmesh, 0);
1131  for (unsigned mesh_i = 0; mesh_i < My_nmesh; mesh_i++)
1132  {
1133  dir_doftypes_vec[mesh_i] = spatial_dim * mesh_i + direction;
1134  }
1135  doftype_coarsen_map.push_back(dir_doftypes_vec);
1136  }
1137 
1138  Vector<unsigned> ns_p_vec(1, 0);
1139 
1140  // This is simply the number of velocity dof types,
1141  ns_p_vec[0] = My_nmesh * spatial_dim;
1142 
1143  doftype_coarsen_map.push_back(ns_p_vec);
1144 
1145  // Turn the Navier-Stokes block preconditioner into a subsidiary block
1146  // preconditioner.
1147  navier_stokes_block_preconditioner_pt
1148  ->turn_into_subsidiary_block_preconditioner(
1149  this, dof_number_in_master_map, doftype_coarsen_map);
1150 
1151  // Call the setup function
1152  navier_stokes_block_preconditioner_pt->setup(matrix_pt());
1153  }
1154  else
1155  {
1156  // Step 5.3: This is not a block preconditioner, thus we need to
1157  // concatenate all the fluid matrices and pass them to the solver.
1158 
1159  // Select all the fluid blocks (velocity and pressure)
1160  VectorMatrix<BlockSelector> f_aug_blocks(N_fluid_doftypes,
1162  for (unsigned block_i = 0; block_i < N_fluid_doftypes; block_i++)
1163  {
1164  for (unsigned block_j = 0; block_j < N_fluid_doftypes; block_j++)
1165  {
1166  f_aug_blocks[block_i][block_j].select_block(block_i, block_j, true);
1167  }
1168  }
1169 
1170  // Note: This will use the replaced blocks.
1171  CRDoubleMatrix f_aug_block = this->get_concatenated_block(f_aug_blocks);
1172 
1174  {
1176  {
1177  Navier_stokes_preconditioner_pt = new SuperLUPreconditioner;
1178  }
1179  }
1180  else
1181  {
1183  {
1184  std::ostringstream err_msg;
1185  err_msg << "Not using SuperLUPreconditioner for NS block,\n"
1186  << "but the Navier_stokes_preconditioner_pt is null.\n";
1187  throw OomphLibError(
1189  }
1190  }
1191 
1192  // Setup the solver.
1193  Navier_stokes_preconditioner_pt->setup(&f_aug_block);
1194 
1195  } // else
1196 
1197  // Clean up memory
1198  const unsigned v_aug_nrow = v_aug_pt.nrow();
1199  const unsigned v_aug_ncol = v_aug_pt.ncol();
1200  for (unsigned v_row = 0; v_row < v_aug_nrow; v_row++)
1201  {
1202  for (unsigned v_col = 0; v_col < v_aug_ncol; v_col++)
1203  {
1204  delete v_aug_pt(v_row, v_col);
1205  v_aug_pt(v_row, v_col) = 0;
1206  }
1207  }
1208 
1210  } // func setup
CRDoubleMatrix get_concatenated_block(const VectorMatrix< BlockSelector > &selected_block)
Definition: block_preconditioner.h:1134
Vector< LinearAlgebraDistribution * > Block_distribution_pt
The distribution for the blocks.
Definition: block_preconditioner.h:3277
void get_block(const unsigned &i, const unsigned &j, CRDoubleMatrix &output_matrix, const bool &ignore_replacement_block=false) const
Definition: block_preconditioner.h:673
void set_replacement_dof_block(const unsigned &block_i, const unsigned &block_j, CRDoubleMatrix *replacement_dof_block_pt)
Definition: block_preconditioner.h:2911
CRDoubleMatrix * matrix_pt() const
Definition: block_preconditioner.h:520
bool is_subsidiary_block_preconditioner() const
Definition: block_preconditioner.h:2061
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
LinearAlgebraDistribution * distribution_pt() const
access to the LinearAlgebraDistribution
Definition: linear_algebra_distribution.h:457
void setup(DoubleMatrixBase *matrix_pt)
Definition: preconditioner.h:94
double inf_norm(const DenseMatrix< CRDoubleMatrix * > &matrix_pt)
Compute infinity (maximum) norm of sub blocks as if it was one matrix.
Definition: matrices.cc:3731

References oomph::BlockPreconditioner< CRDoubleMatrix >::Block_distribution_pt, oomph::BlockPreconditioner< CRDoubleMatrix >::block_setup(), oomph::CRDoubleMatrix::build(), clean_up_memory(), oomph::CRDoubleMatrix::diagonal_entries(), oomph::DistributableLinearAlgebraObject::distribution_pt(), oomph::BlockPreconditioner< CRDoubleMatrix >::get_block(), oomph::BlockPreconditioner< CRDoubleMatrix >::get_concatenated_block(), oomph::CRDoubleMatrixHelpers::inf_norm(), Inv_w_diag_values, oomph::BlockPreconditioner< CRDoubleMatrix >::is_subsidiary_block_preconditioner(), oomph::BlockPreconditioner< CRDoubleMatrix >::matrix_pt(), oomph::CRDoubleMatrix::multiply(), My_mesh_pt, My_ndof_types_in_mesh, My_nmesh, N_fluid_doftypes, N_lagrange_doftypes, N_velocity_doftypes, Navier_stokes_preconditioner_is_block_preconditioner, Navier_stokes_preconditioner_pt, oomph::DenseMatrix< T >::ncol(), oomph::BlockPreconditioner< CRDoubleMatrix >::ndof_types(), oomph::CRDoubleMatrix::nnz(), oomph::DenseMatrix< T >::nrow(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, Preconditioner_has_been_setup, Scaling_sigma, oomph::BlockPreconditioner< CRDoubleMatrix >::set_replacement_dof_block(), oomph::Preconditioner::setup(), oomph::BlockPreconditioner< MATRIX >::turn_into_subsidiary_block_preconditioner(), Use_norm_f_for_scaling_sigma, and Using_superlu_ns_preconditioner.

◆ use_norm_f_for_scaling_sigma()

void oomph::LagrangeEnforcedFlowPreconditioner::use_norm_f_for_scaling_sigma ( )
inline

Set flag to use the infinite norm of the Navier-Stokes F matrix as the scaling sigma. This is the default behaviour. Note: the norm of the NS F matrix positive, however, we actually use the negative of the norm. This is because the underlying Navier-Stokes Jacobian is multiplied by -1. Ask Andrew/Matthias for more detail.

237  {
239  }

References Use_norm_f_for_scaling_sigma.

Member Data Documentation

◆ Inv_w_diag_values

Vector<Vector<double> > oomph::LagrangeEnforcedFlowPreconditioner::Inv_w_diag_values
private

Inverse W values.

Referenced by preconditioner_solve(), and setup().

◆ My_mesh_pt

Vector<Mesh*> oomph::LagrangeEnforcedFlowPreconditioner::My_mesh_pt
private

Storage for the meshes. In our implementation, the first mesh must always be the Navier-Stokes (bulk) mesh, followed by surface meshes.

Referenced by LagrangeEnforcedFlowPreconditioner(), set_meshes(), and setup().

◆ My_ndof_types_in_mesh

Vector<unsigned> oomph::LagrangeEnforcedFlowPreconditioner::My_ndof_types_in_mesh
private

The number of DOF types in each mesh. This is used create various lookup lists.

Referenced by LagrangeEnforcedFlowPreconditioner(), and setup().

◆ My_nmesh

unsigned oomph::LagrangeEnforcedFlowPreconditioner::My_nmesh
private

The number of meshes. This is used to create various lookup lists.

Referenced by LagrangeEnforcedFlowPreconditioner(), set_meshes(), and setup().

◆ N_fluid_doftypes

unsigned oomph::LagrangeEnforcedFlowPreconditioner::N_fluid_doftypes
private

The number of fluid DOF types (including pressure).

Referenced by LagrangeEnforcedFlowPreconditioner(), preconditioner_solve(), and setup().

◆ N_lagrange_doftypes

unsigned oomph::LagrangeEnforcedFlowPreconditioner::N_lagrange_doftypes
private

The number of Lagrange multiplier DOF types.

Referenced by LagrangeEnforcedFlowPreconditioner(), preconditioner_solve(), and setup().

◆ N_velocity_doftypes

unsigned oomph::LagrangeEnforcedFlowPreconditioner::N_velocity_doftypes
private

The number of velocity DOF types.

Referenced by LagrangeEnforcedFlowPreconditioner(), and setup().

◆ Navier_stokes_preconditioner_is_block_preconditioner

bool oomph::LagrangeEnforcedFlowPreconditioner::Navier_stokes_preconditioner_is_block_preconditioner
private

Flag to indicate if the preconditioner for the Navier-Stokes block is a block preconditioner or not.

Referenced by LagrangeEnforcedFlowPreconditioner(), and setup().

◆ Navier_stokes_preconditioner_pt

Preconditioner* oomph::LagrangeEnforcedFlowPreconditioner::Navier_stokes_preconditioner_pt
private

◆ Preconditioner_has_been_setup

bool oomph::LagrangeEnforcedFlowPreconditioner::Preconditioner_has_been_setup
private

Control flag is true if the preconditioner has been setup (used so we can wipe the data when the preconditioner is called again)

Referenced by clean_up_memory(), LagrangeEnforcedFlowPreconditioner(), preconditioner_solve(), and setup().

◆ Scaling_sigma

double oomph::LagrangeEnforcedFlowPreconditioner::Scaling_sigma
private

Scaling for the augmentation: Scaling_sigma*(LL^T)

Referenced by LagrangeEnforcedFlowPreconditioner(), scaling_sigma(), set_scaling_sigma(), and setup().

◆ Use_norm_f_for_scaling_sigma

bool oomph::LagrangeEnforcedFlowPreconditioner::Use_norm_f_for_scaling_sigma
private

Flag to indicate if we want to use the infinite norm of the Navier-Stokes momentum block for the scaling sigma.

Referenced by LagrangeEnforcedFlowPreconditioner(), set_scaling_sigma(), setup(), and use_norm_f_for_scaling_sigma().

◆ Using_superlu_ns_preconditioner

bool oomph::LagrangeEnforcedFlowPreconditioner::Using_superlu_ns_preconditioner
private

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