oomph::CRDoubleMatrix Class Reference

#include <matrices.h>

+ Inheritance diagram for oomph::CRDoubleMatrix:

Classes

struct  CRDoubleMatrixComparisonHelper
 Create a struct to provide a comparison function for std::sort. More...
 

Public Member Functions

 CRDoubleMatrix ()
 Default constructor. More...
 
 CRDoubleMatrix (const LinearAlgebraDistribution *distribution_pt, const unsigned &ncol, const Vector< double > &value, const Vector< int > &column_index, const Vector< int > &row_start)
 
 CRDoubleMatrix (const LinearAlgebraDistribution *distribution_pt)
 
 CRDoubleMatrix (const CRDoubleMatrix &matrix)
 Copy constructor. More...
 
void operator= (const CRDoubleMatrix &)=delete
 Broken assignment operator. More...
 
virtual ~CRDoubleMatrix ()
 Destructor. More...
 
const Vector< intget_index_of_diagonal_entries () const
 
bool entries_are_sorted (const bool &doc_unordered_entries=false) const
 
void sort_entries ()
 
void build (const LinearAlgebraDistribution *distribution_pt, const unsigned &ncol, const Vector< double > &value, const Vector< int > &column_index, const Vector< int > &row_start)
 
void build (const LinearAlgebraDistribution *distribution_pt)
 Rebuild the matrix - assembles an empty matrix with a defined distribution. More...
 
void build (const unsigned &ncol, const Vector< double > &value, const Vector< int > &column_index, const Vector< int > &row_start)
 keeps the existing distribution and just matrix that is stored More...
 
void build_without_copy (const unsigned &ncol, const unsigned &nnz, double *value, int *column_index, int *row_start)
 method to rebuild the matrix, but not the distribution More...
 
void redistribute (const LinearAlgebraDistribution *const &dist_pt)
 
void clear ()
 clear More...
 
unsigned long nrow () const
 Return the number of rows of the matrix. More...
 
unsigned long ncol () const
 Return the number of columns of the matrix. More...
 
void output_bottom_right_zero_helper (std::ostream &outfile) const
 
void sparse_indexed_output_helper (std::ostream &outfile) const
 
void sparse_indexed_output_with_offset (std::string filename)
 
double operator() (const unsigned long &i, const unsigned long &j) const
 
introw_start ()
 Access to C-style row_start array. More...
 
const introw_start () const
 Access to C-style row_start array (const version) More...
 
intcolumn_index ()
 Access to C-style column index array. More...
 
const intcolumn_index () const
 Access to C-style column index array (const version) More...
 
doublevalue ()
 Access to C-style value array. More...
 
const doublevalue () const
 Access to C-style value array (const version) More...
 
unsigned long nnz () const
 Return the number of nonzero entries (the local nnz) More...
 
virtual void ludecompose ()
 Do LU decomposition. More...
 
virtual void lubksub (DoubleVector &rhs)
 LU back solve for given RHS. More...
 
void multiply (const DoubleVector &x, DoubleVector &soln) const
 Multiply the matrix by the vector x: soln=Ax. More...
 
void multiply_transpose (const DoubleVector &x, DoubleVector &soln) const
 Multiply the transposed matrix by the vector x: soln=A^T x. More...
 
void multiply (const CRDoubleMatrix &matrix_in, CRDoubleMatrix &result) const
 
void matrix_reduction (const double &alpha, CRDoubleMatrix &reduced_matrix)
 
unsignedserial_matrix_matrix_multiply_method ()
 
const unsignedserial_matrix_matrix_multiply_method () const
 
unsigneddistributed_matrix_matrix_multiply_method ()
 
const unsigneddistributed_matrix_matrix_multiply_method () const
 
bool built () const
 
CRDoubleMatrixglobal_matrix () const
 
void get_matrix_transpose (CRDoubleMatrix *result) const
 Returns the transpose of this matrix. More...
 
double inf_norm () const
 returns the inf-norm of this matrix More...
 
Vector< doublediagonal_entries () const
 
void add (const CRDoubleMatrix &matrix_in, CRDoubleMatrix &result_matrix) const
 element-wise addition of this matrix with matrix_in. More...
 
- Public Member Functions inherited from oomph::Matrix< double, CRDoubleMatrix >
 Matrix ()
 (Empty) constructor More...
 
 Matrix (const Matrix &matrix)=delete
 Broken copy constructor. More...
 
void operator= (const Matrix &)=delete
 Broken assignment operator. More...
 
virtual ~Matrix ()
 Virtual (empty) destructor. More...
 
double operator() (const unsigned long &i, const unsigned long &j) const
 
doubleoperator() (const unsigned long &i, const unsigned long &j)
 
virtual void output (std::ostream &outfile) const
 
void sparse_indexed_output (std::ostream &outfile, const unsigned &precision=0, const bool &output_bottom_right_zero=false) const
 
void sparse_indexed_output (std::string filename, const unsigned &precision=0, const bool &output_bottom_right_zero=false) const
 
- Public Member Functions inherited from oomph::DoubleMatrixBase
 DoubleMatrixBase ()
 (Empty) constructor. More...
 
 DoubleMatrixBase (const DoubleMatrixBase &matrix)=delete
 Broken copy constructor. More...
 
void operator= (const DoubleMatrixBase &)=delete
 Broken assignment operator. More...
 
virtual ~DoubleMatrixBase ()
 virtual (empty) destructor More...
 
LinearSolver *& linear_solver_pt ()
 Return a pointer to the linear solver object. More...
 
LinearSolver *const & linear_solver_pt () const
 Return a pointer to the linear solver object (const version) More...
 
void solve (DoubleVector &rhs)
 
void solve (const DoubleVector &rhs, DoubleVector &soln)
 
void solve (Vector< double > &rhs)
 
void solve (const Vector< double > &rhs, Vector< double > &soln)
 
virtual void residual (const DoubleVector &x, const DoubleVector &b, DoubleVector &residual_)
 Find the residual, i.e. r=b-Ax the residual. More...
 
virtual double max_residual (const DoubleVector &x, const DoubleVector &rhs)
 
- 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)
 

Public Attributes

struct oomph::CRDoubleMatrix::CRDoubleMatrixComparisonHelper Comparison_struct
 

Private Attributes

Vector< intIndex_of_diagonal_entries
 
unsigned Serial_matrix_matrix_multiply_method
 
unsigned Distributed_matrix_matrix_multiply_method
 
CRMatrix< doubleCR_matrix
 Storage for the Matrix in CR Format. More...
 
bool Built
 

Additional Inherited Members

- Protected Member Functions inherited from oomph::Matrix< double, CRDoubleMatrix >
void range_check (const unsigned long &i, const unsigned long &j) const
 
- Protected Member Functions inherited from oomph::DistributableLinearAlgebraObject
void clear_distribution ()
 
- Protected Attributes inherited from oomph::DoubleMatrixBase
LinearSolverLinear_solver_pt
 
LinearSolverDefault_linear_solver_pt
 

Detailed Description

A class for compressed row matrices. This is a distributable object.

Constructor & Destructor Documentation

◆ CRDoubleMatrix() [1/4]

oomph::CRDoubleMatrix::CRDoubleMatrix ( )

Default constructor.

//////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////

1215  {
1216  // set the default solver
1217  Linear_solver_pt = Default_linear_solver_pt = new SuperLUSolver;
1218 
1219  // matrix not built
1220  Built = false;
1221 
1222  // set the serial matrix-matrix multiply method
1223 #ifdef OOMPH_HAS_TRILINOS
1224  // Serial_matrix_matrix_multiply_method = 4;
1226 #else
1228 #endif
1229  }
bool Built
Definition: matrices.h:1253
unsigned Serial_matrix_matrix_multiply_method
Definition: matrices.h:1242
LinearSolver * Default_linear_solver_pt
Definition: matrices.h:267
LinearSolver * Linear_solver_pt
Definition: matrices.h:264

References Built, oomph::DoubleMatrixBase::Default_linear_solver_pt, oomph::DoubleMatrixBase::Linear_solver_pt, and Serial_matrix_matrix_multiply_method.

Referenced by global_matrix().

◆ CRDoubleMatrix() [2/4]

oomph::CRDoubleMatrix::CRDoubleMatrix ( const LinearAlgebraDistribution dist_pt,
const unsigned ncol,
const Vector< double > &  value,
const Vector< int > &  column_index,
const Vector< int > &  row_start 
)

Constructor: vector of values, vector of column indices, vector of row starts and number of rows and columns.

Constructor: Takes the distribution and the number of columns, as well as the vector of values, vector of column indices,vector of row starts.

1316  {
1317  // build the compressed row matrix
1318  CR_matrix.build(
1319  value, column_index, row_start, dist_pt->nrow_local(), ncol);
1320 
1321  // store the Distribution
1322  this->build_distribution(dist_pt);
1323 
1324  // set the linear solver
1325  Linear_solver_pt = Default_linear_solver_pt = new SuperLUSolver;
1326 
1327  // set the serial matrix-matrix multiply method
1328 #ifdef OOMPH_HAS_TRILINOS
1329  // Serial_matrix_matrix_multiply_method = 4;
1331 #else
1333 #endif
1334 
1335  // matrix has been built
1336  Built = true;
1337  }
int * column_index()
Access to C-style column index array.
Definition: matrices.h:1072
int * row_start()
Access to C-style row_start array.
Definition: matrices.h:1060
unsigned long ncol() const
Return the number of columns of the matrix.
Definition: matrices.h:1008
double * value()
Access to C-style value array.
Definition: matrices.h:1084
CRMatrix< double > CR_matrix
Storage for the Matrix in CR Format.
Definition: matrices.h:1249
void build(const Vector< T > &value, const Vector< int > &column_index, const Vector< int > &row_start, const unsigned long &n, const unsigned long &m)
Definition: matrices.h:3404
void build_distribution(const LinearAlgebraDistribution *const dist_pt)
Definition: linear_algebra_distribution.h:507

References oomph::CRMatrix< T >::build(), oomph::DistributableLinearAlgebraObject::build_distribution(), Built, column_index(), CR_matrix, oomph::DoubleMatrixBase::Default_linear_solver_pt, oomph::DoubleMatrixBase::Linear_solver_pt, ncol(), oomph::LinearAlgebraDistribution::nrow_local(), row_start(), Serial_matrix_matrix_multiply_method, and value().

◆ CRDoubleMatrix() [3/4]

oomph::CRDoubleMatrix::CRDoubleMatrix ( const LinearAlgebraDistribution distribution_pt)

Constructor: just stores the distribution but does not build the matrix

1288  {
1290 
1291  // set the default solver
1292  Linear_solver_pt = Default_linear_solver_pt = new SuperLUSolver;
1293 
1294  // matrix not built
1295  Built = false;
1296 
1297 // set the serial matrix-matrix multiply method
1298 #ifdef OOMPH_HAS_TRILINOS
1299  // Serial_matrix_matrix_multiply_method = 4;
1301 #else
1303 #endif
1304  }
LinearAlgebraDistribution * distribution_pt() const
access to the LinearAlgebraDistribution
Definition: linear_algebra_distribution.h:457

References oomph::DistributableLinearAlgebraObject::build_distribution(), Built, oomph::DoubleMatrixBase::Default_linear_solver_pt, oomph::DoubleMatrixBase::Linear_solver_pt, and Serial_matrix_matrix_multiply_method.

◆ CRDoubleMatrix() [4/4]

oomph::CRDoubleMatrix::CRDoubleMatrix ( const CRDoubleMatrix matrix)

Copy constructor.

1235  {
1236  // copy the distribution
1237  this->build_distribution(other_matrix.distribution_pt());
1238 
1239  // copy coefficients
1240  const double* values_pt = other_matrix.value();
1241  const int* column_indices = other_matrix.column_index();
1242  const int* row_start = other_matrix.row_start();
1243 
1244  // This is the local nnz.
1245  const unsigned nnz = other_matrix.nnz();
1246 
1247  // Using number of local rows since the underlying CRMatrix is local to
1248  // each processor.
1249  const unsigned nrow_local = other_matrix.nrow_local();
1250 
1251  // Storage for the (yet to be copied) data.
1252  double* my_values_pt = new double[nnz];
1253  int* my_column_indices = new int[nnz];
1254  int* my_row_start = new int[nrow_local + 1];
1255 
1256  // Copying over the data.
1257  std::copy(values_pt, values_pt + nnz, my_values_pt);
1258  std::copy(column_indices, column_indices + nnz, my_column_indices);
1259  std::copy(row_start, row_start + nrow_local + 1, my_row_start);
1260 
1261 
1262  // Build without copy since we have made a deep copy of the data structure.
1263  this->build_without_copy(
1264  other_matrix.ncol(), nnz, my_values_pt, my_column_indices, my_row_start);
1265 
1266  // set the default solver
1267  Linear_solver_pt = Default_linear_solver_pt = new SuperLUSolver;
1268 
1269  // matrix is built
1270  Built = true;
1271 
1272  // set the serial matrix-matrix multiply method
1273 #ifdef OOMPH_HAS_TRILINOS
1274  // Serial_matrix_matrix_multiply_method = 4;
1276 #else
1278 #endif
1279  }
unsigned long nnz() const
Return the number of nonzero entries (the local nnz)
Definition: matrices.h:1096
void build_without_copy(const unsigned &ncol, const unsigned &nnz, double *value, int *column_index, int *row_start)
method to rebuild the matrix, but not the distribution
Definition: matrices.cc:1710
unsigned nrow_local() const
access function for the num of local rows on this processor.
Definition: linear_algebra_distribution.h:469
EIGEN_BLAS_FUNC() copy(int *n, RealScalar *px, int *incx, RealScalar *py, int *incy)
Definition: level1_impl.h:32

References oomph::DistributableLinearAlgebraObject::build_distribution(), build_without_copy(), Built, column_index(), copy(), oomph::DoubleMatrixBase::Default_linear_solver_pt, oomph::DistributableLinearAlgebraObject::distribution_pt(), oomph::DoubleMatrixBase::Linear_solver_pt, ncol(), nnz(), oomph::DistributableLinearAlgebraObject::nrow_local(), row_start(), Serial_matrix_matrix_multiply_method, and value().

◆ ~CRDoubleMatrix()

oomph::CRDoubleMatrix::~CRDoubleMatrix ( )
virtual

Destructor.

1344  {
1345  this->clear();
1346  delete Default_linear_solver_pt;
1348  }
void clear()
clear
Definition: matrices.cc:1657

References clear(), and oomph::DoubleMatrixBase::Default_linear_solver_pt.

Member Function Documentation

◆ add()

void oomph::CRDoubleMatrix::add ( const CRDoubleMatrix matrix_in,
CRDoubleMatrix result_matrix 
) const

element-wise addition of this matrix with matrix_in.

Element-wise addition of this matrix with matrix_in.

3517  {
3518 #ifdef PARANOID
3519  // Check if this matrix is built.
3520  if (!this->built())
3521  {
3522  std::ostringstream error_message;
3523  error_message << "The matrix is not built.\n"
3524  << "Please build the matrix!\n";
3525  throw OomphLibError(
3526  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3527  }
3528 
3529  // Check if this matrix_in is built.
3530  if (!matrix_in.built())
3531  {
3532  std::ostringstream error_message;
3533  error_message << "The matrix matrix_in is not built.\n"
3534  << "Please build the matrix!\n";
3535  throw OomphLibError(
3536  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3537  }
3538 
3539  // Check if the dimensions of this matrix and matrix_in are the same.
3540  unsigned long this_nrow = this->nrow();
3541  unsigned long matrix_in_nrow = matrix_in.nrow();
3542  if (this_nrow != matrix_in_nrow)
3543  {
3544  std::ostringstream error_message;
3545  error_message << "matrix_in has a different number of rows than\n"
3546  << "this matrix.\n";
3547  throw OomphLibError(
3548  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3549  }
3550 
3551  unsigned long this_ncol = this->ncol();
3552  unsigned long matrix_in_ncol = matrix_in.ncol();
3553  if (this_ncol != matrix_in_ncol)
3554  {
3555  std::ostringstream error_message;
3556  error_message << "matrix_in has a different number of columns than\n"
3557  << "this matrix.\n";
3558  throw OomphLibError(
3559  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3560  }
3561 
3562  // Check if the distribution is the same (Otherwise we may have to send and
3563  // receive data from other processors - which is not implemented!)
3564  if (*(this->distribution_pt()) != *(matrix_in.distribution_pt()))
3565  {
3566  std::ostringstream error_message;
3567  error_message << "matrix_in must have the same distribution as\n"
3568  << "this matrix.\n";
3569  throw OomphLibError(
3570  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3571  }
3572 
3573  // If the matrix is built, check that it's existing distribution is the
3574  // same as the in matrix. Since we'll use the same distribution instead
3575  // of completely rebuilding it.
3576  if (result_matrix.built() &&
3577  (*result_matrix.distribution_pt() != *matrix_in.distribution_pt()))
3578  {
3579  std::ostringstream error_message;
3580  error_message << "The result_matrix is built. "
3581  << "But has a different distribution from matrix_in \n"
3582  << "They need to be the same.\n";
3583  throw OomphLibError(
3584  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3585  }
3586 #endif
3587 
3588  // To add the elements of two CRDoubleMatrices, we need to know the union of
3589  // the sparsity patterns. This is determined by the column indices.
3590  // We add the column indices and values (entries) as a key-value pair in
3591  // a map (per row). We then read these out into two column indices and
3592  // values vector for the result matrix.
3593 
3594  unsigned nrow_local = this->nrow_local();
3595  Vector<int> res_column_indices;
3596  Vector<double> res_values;
3597  Vector<int> res_row_start;
3598  res_row_start.reserve(nrow_local + 1);
3599 
3600  // The row_start and column_indices
3601  const int* this_column_indices = this->column_index();
3602  const int* this_row_start = this->row_start();
3603  const int* in_column_indices = matrix_in.column_index();
3604  const int* in_row_start = matrix_in.row_start();
3605 
3606  // Values from this matrix and matrix_in.
3607  const double* this_values = this->value();
3608  const double* in_values = matrix_in.value();
3609 
3610 
3611  // The first entry in row_start is always zero.
3612  res_row_start.push_back(0);
3613 
3614  // Loop through the rows of both matrices and insert the column indices and
3615  // values as a key-value pair.
3616  for (unsigned row_i = 0; row_i < nrow_local; row_i++)
3617  {
3618  // Create the map for this row.
3619  std::map<int, double> res_row_map;
3620 
3621  // Insert the column and value pair for this matrix.
3622  for (int i = this_row_start[row_i]; i < this_row_start[row_i + 1]; i++)
3623  {
3624  res_row_map[this_column_indices[i]] = this_values[i];
3625  }
3626 
3627  // Insert the column and value pair for in matrix.
3628  for (int i = in_row_start[row_i]; i < in_row_start[row_i + 1]; i++)
3629  {
3630  res_row_map[in_column_indices[i]] += in_values[i];
3631  }
3632 
3633  // Fill in the row start
3634  res_row_start.push_back(res_row_start.back() + res_row_map.size());
3635 
3636  // Now insert the key into res_column_indices and value into res_values
3637  for (std::map<int, double>::iterator it = res_row_map.begin();
3638  it != res_row_map.end();
3639  ++it)
3640  {
3641  res_column_indices.push_back(it->first);
3642  res_values.push_back(it->second);
3643  }
3644  }
3645 
3646  // Finally build the result_matrix.
3647  if (result_matrix.distribution_pt()->built())
3648  {
3649  // Use the existing distribution.
3650  result_matrix.build(
3651  this->ncol(), res_values, res_column_indices, res_row_start);
3652  }
3653  else
3654  {
3655  // Build with THIS distribution
3656  result_matrix.build(this->distribution_pt(),
3657  this->ncol(),
3658  res_values,
3659  res_column_indices,
3660  res_row_start);
3661  }
3662  }
int i
Definition: BiCGSTAB_step_by_step.cpp:9
bool built() const
Definition: matrices.h:1210
unsigned long nrow() const
Return the number of rows of the matrix.
Definition: matrices.h:1002
#define OOMPH_EXCEPTION_LOCATION
Definition: oomph_definitions.h:61
#define OOMPH_CURRENT_FUNCTION
Definition: oomph_definitions.h:86

References build(), oomph::LinearAlgebraDistribution::built(), built(), column_index(), oomph::DistributableLinearAlgebraObject::distribution_pt(), i, ncol(), nrow(), oomph::DistributableLinearAlgebraObject::nrow_local(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, row_start(), and value().

Referenced by main().

◆ build() [1/3]

void oomph::CRDoubleMatrix::build ( const LinearAlgebraDistribution distribution_pt)

Rebuild the matrix - assembles an empty matrix with a defined distribution.

rebuild the matrix - assembles an empty matrix will a defined distribution

1354  {
1355  this->clear();
1357  }

References oomph::DistributableLinearAlgebraObject::build_distribution(), and clear().

◆ build() [2/3]

void oomph::CRDoubleMatrix::build ( const LinearAlgebraDistribution distribution_pt,
const unsigned ncol,
const Vector< double > &  value,
const Vector< int > &  column_index,
const Vector< int > &  row_start 
)

build method: vector of values, vector of column indices, vector of row starts and number of rows and columns.

build method: Takes the distribution and the number of columns, as well as the vector of values, vector of column indices,vector of row starts.

1677  {
1678  // clear
1679  this->clear();
1680 
1681  // store the Distribution
1683 
1684  // set the linear solver
1685  Default_linear_solver_pt = new SuperLUSolver;
1686 
1687  // now build the matrix
1688  this->build(ncol, value, column_index, row_start);
1689  }
void build(const LinearAlgebraDistribution *distribution_pt, const unsigned &ncol, const Vector< double > &value, const Vector< int > &column_index, const Vector< int > &row_start)
Definition: matrices.cc:1672

References oomph::DistributableLinearAlgebraObject::build_distribution(), clear(), column_index(), oomph::DoubleMatrixBase::Default_linear_solver_pt, row_start(), and value().

Referenced by add(), oomph::CRDoubleMatrixHelpers::concatenate(), oomph::CRDoubleMatrixHelpers::concatenate_without_communication(), oomph::CRDoubleMatrixHelpers::create_uniformly_distributed_matrix(), oomph::CRDoubleMatrixHelpers::deep_copy(), oomph::Problem::get_eigenproblem_matrices(), oomph::Problem::get_jacobian(), get_matrix_transpose(), oomph::BlockPreconditioner< MATRIX >::internal_get_block(), main(), matrix_reduction(), oomph::TrilinosEpetraHelpers::multiply(), multiply(), redistribute(), oomph::LagrangeEnforcedFlowPreconditioner::setup(), and oomph::HelmholtzMGPreconditioner< DIM >::setup_mg_structures().

◆ build() [3/3]

void oomph::CRDoubleMatrix::build ( const unsigned ncol,
const Vector< double > &  value,
const Vector< int > &  column_index,
const Vector< int > &  row_start 
)

keeps the existing distribution and just matrix that is stored

method to rebuild the matrix, but not the distribution

1698  {
1699  // call the underlying build method
1702 
1703  // matrix has been build
1704  Built = true;
1705  }
void clean_up_memory()
Wipe matrix data and set all values to 0.
Definition: matrices.h:3323

References oomph::CRMatrix< T >::build(), Built, oomph::CRMatrix< T >::clean_up_memory(), column_index(), CR_matrix, oomph::DistributableLinearAlgebraObject::nrow_local(), row_start(), and value().

◆ build_without_copy()

void oomph::CRDoubleMatrix::build_without_copy ( const unsigned ncol,
const unsigned nnz,
double value,
int column_index,
int row_start 
)

method to rebuild the matrix, but not the distribution

keeps the existing distribution and just matrix that is stored without copying the matrix data

1715  {
1716  // call the underlying build method
1719  value, column_index, row_start, nnz, this->nrow_local(), ncol);
1720 
1721  // matrix has been build
1722  Built = true;
1723  }
void build_without_copy(T *value, int *column_index, int *row_start, const unsigned long &nnz, const unsigned long &n, const unsigned long &m)
Definition: matrices.h:3354

References oomph::CRMatrix< T >::build_without_copy(), Built, oomph::CRMatrix< T >::clean_up_memory(), column_index(), CR_matrix, nnz(), oomph::DistributableLinearAlgebraObject::nrow_local(), row_start(), and value().

Referenced by oomph::NavierStokesSchurComplementPreconditioner::assemble_inv_press_and_veloc_mass_matrix_diagonal(), oomph::PressureBasedSolidLSCPreconditioner::assemble_mass_matrix_diagonal(), oomph::CRDoubleMatrixHelpers::concatenate_without_communication(), CRDoubleMatrix(), oomph::CRDoubleMatrixHelpers::deep_copy(), oomph::Problem::get_eigenproblem_matrices(), oomph::Problem::get_jacobian(), global_matrix(), oomph::BlockPreconditioner< MATRIX >::internal_get_block(), oomph::TrilinosEpetraHelpers::multiply(), multiply(), and redistribute().

◆ built()

◆ clear()

void oomph::CRDoubleMatrix::clear ( )

◆ column_index() [1/2]

◆ column_index() [2/2]

const int* oomph::CRDoubleMatrix::column_index ( ) const
inline

Access to C-style column index array (const version)

1079  {
1080  return CR_matrix.column_index();
1081  }

References oomph::CRMatrix< T >::column_index(), and CR_matrix.

◆ diagonal_entries()

Vector< double > oomph::CRDoubleMatrix::diagonal_entries ( ) const

returns a Vector of diagonal entries of this matrix. This only works with square matrices. This condition may be relaxed in the future if need be.

Return the diagonal entries of the matrix. This only works with square matrices. This condition may be relaxed in the future if need be.

3466  {
3467 #ifdef PARANOID
3468  // Check if the matrix has been built.
3469  if (!this->built())
3470  {
3471  std::ostringstream error_message;
3472  error_message << "The matrix has not been built.\n"
3473  << "Please build it...\n";
3474  throw OomphLibError(
3475  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3476  }
3477 
3478  // Check if this is a square matrix.
3479  if (this->nrow() != this->ncol())
3480  {
3481  std::ostringstream error_message;
3482  error_message << "The matrix is not square. Can only get the diagonal\n"
3483  << "entries of a square matrix.\n";
3484  throw OomphLibError(
3485  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3486  }
3487 #endif
3488 
3489  // We get the diagonal entries on this processor.
3490  unsigned nrow_local = this->nrow_local();
3491 
3492  // Create storage for the diagonal entries.
3493  Vector<double> result_vec;
3494  result_vec.reserve(nrow_local);
3495 
3496  // Get the first row for the column offset.
3497  unsigned first_row = this->first_row();
3498 
3499  // Loop through the local rows.
3500  for (unsigned i = 0; i < nrow_local; i++)
3501  {
3502  // The column entries are globally indexed.
3503  unsigned diag_entry_col = first_row + i;
3504 
3505  // Push back the diagonal entry.
3506  result_vec.push_back(CR_matrix.get_entry(i, diag_entry_col));
3507  }
3508 
3509  return result_vec;
3510  }
T get_entry(const unsigned long &i, const unsigned long &j) const
Definition: matrices.h:748
unsigned first_row() const
access function for the first row on this processor
Definition: linear_algebra_distribution.h:481

References built(), CR_matrix, oomph::DistributableLinearAlgebraObject::first_row(), oomph::CRMatrix< T >::get_entry(), i, ncol(), nrow(), oomph::DistributableLinearAlgebraObject::nrow_local(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Referenced by oomph::ComplexDampedJacobi< MATRIX >::complex_smoother_setup(), and oomph::LagrangeEnforcedFlowPreconditioner::setup().

◆ distributed_matrix_matrix_multiply_method() [1/2]

unsigned& oomph::CRDoubleMatrix::distributed_matrix_matrix_multiply_method ( )
inline

Access function to Distributed_matrix_matrix_multiply_method, the flag which determines the matrix matrix multiplication method used for distributed matrices. Method 1: Trilinos Epetra Matrix Matrix multiply. Method 2: Trilinos Epetra Matrix Matrix multiply (ML based).

1192  {
1194  }
unsigned Distributed_matrix_matrix_multiply_method
Definition: matrices.h:1246

References Distributed_matrix_matrix_multiply_method.

Referenced by oomph::CRDoubleMatrixHelpers::deep_copy().

◆ distributed_matrix_matrix_multiply_method() [2/2]

const unsigned& oomph::CRDoubleMatrix::distributed_matrix_matrix_multiply_method ( ) const
inline

Read only access function (const version) to Distributed_matrix_matrix_multiply_method, the flag which determines the matrix matrix multiplication method used for distributed matrices. Method 1: Trilinos Epetra Matrix Matrix multiply. Method 2: Trilinos Epetra Matrix Matrix multiply (ML based).

1203  {
1205  }

References Distributed_matrix_matrix_multiply_method.

◆ entries_are_sorted()

bool oomph::CRDoubleMatrix::entries_are_sorted ( const bool doc_unordered_entries = false) const

Runs through the column index vector and checks if the entries follow the regular lexicographical ordering of matrix entries, i.e. it will check (at the i-th row of the matrix) if the entries in the column index vector associated with this row are in increasing order

Runs through the column index vector and checks if the entries are arranged arbitrarily or if they follow the regular lexicographical of matrices. If a boolean argument is provided with the assignment TRUE then information on the first entry which is not in the correct position will also be given

1368  {
1369 #ifdef OOMPH_HAS_MPI
1370  // We only need to produce a warning if the matrix is distributed
1371  if (this->distributed())
1372  {
1373  // Create an ostringstream object to store the warning message
1374  std::ostringstream warning_message;
1375 
1376  // Create the warning messsage
1377  warning_message << "This method currently works for serial but "
1378  << "has not been implemented for use with MPI!\n";
1379 
1380  // Issue the warning
1381  OomphLibWarning(warning_message.str(),
1384  }
1385 #endif
1386 
1387  // Get the number of rows in this matrix
1388  unsigned n_rows = this->nrow();
1389 
1390  // Acquire access to the value, row_start and column_index arrays from
1391  // the CR matrix. Since we do not change anything in row_start_pt we
1392  // give it the const prefix
1393  const int* column_index_pt = this->column_index();
1394  const int* row_start_pt = this->row_start();
1395 
1396  // Loop over the rows of matrix
1397  for (unsigned i = 0; i < n_rows; i++)
1398  {
1399  // Calculate the number of nonzeros in the i-th row
1400  unsigned nnz_row_i = *(row_start_pt + i + 1) - *(row_start_pt + i);
1401 
1402  // Get the index of the first entry in row i
1403  unsigned i_row_start = *(row_start_pt + i);
1404 
1405  // Loop over the entries of the i-th row
1406  for (unsigned j = 0; j < nnz_row_i - 1; j++)
1407  {
1408  // Check if the column index of the following entry is greater than the
1409  // current entry
1410  if ((*(column_index_pt + i_row_start + j + 1)) <
1411  (*(column_index_pt + i_row_start + j)))
1412  {
1413  // If the input argument was set to TRUE we document we output
1414  // information of the first entry which is not in the correct position
1415  if (doc_unordered_entries)
1416  {
1417  // Tell the user
1418  oomph_info << "Matrix has not been correctly sorted!"
1419  << "\nOn row: " << i << "\nEntry: " << j
1420  << "\nEntry 1 = " << *(column_index_pt + i_row_start + j)
1421  << "\nEntry 2 = "
1422  << *(column_index_pt + i_row_start + j + 1) << std::endl;
1423  }
1424 
1425  // It hasn't worked
1426  return false;
1427  } // if ((*(column_index_pt+i_row_start+j+1)) ...
1428  } // for (unsigned j=0;j<nnz_row_i-1;j++)
1429  } // for (unsigned i=0;i<n_rows;i++)
1430 
1431  // If it gets here without a warning then the entries in each row of
1432  // the matrix are ordered by increasing column index
1433  return true;
1434  } // End of entries_are_sorted()
bool distributed() const
distribution is serial or distributed
Definition: linear_algebra_distribution.h:493
OomphInfo oomph_info
Definition: oomph_definitions.cc:319
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2

References column_index(), oomph::DistributableLinearAlgebraObject::distributed(), i, j, nrow(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, oomph::oomph_info, and row_start().

◆ get_index_of_diagonal_entries()

const Vector<int> oomph::CRDoubleMatrix::get_index_of_diagonal_entries ( ) const
inline

Access function: returns the vector Index_of_diagonal_entries. The i-th entry of the vector contains the index of the last entry below or on the diagonal. If there are no entries below or on the diagonal then the corresponding entry is -1. If, however, there are no entries in the row then the entry is irrelevant and is kept as the initialised value; 0.

921  {
922  // Check to see if the vector has been set up
923  if (Index_of_diagonal_entries.size() == 0)
924  {
925  // Make the warning
926  std::string err_strng =
927  "The Index_of_diagonal_entries vector has not been ";
928  err_strng += "set up yet. Run sort_entries() to set this vector up.";
929 
930  // Throw the warning
931  OomphLibWarning(err_strng,
932  "CRDoubleMatrix::get_index_of_diagonal_entries()",
934  }
935 
936  // Return the vector
938  } // End of index_of_diagonal_entries
Vector< int > Index_of_diagonal_entries
Definition: matrices.h:1238
std::string string(const unsigned &i)
Definition: oomph_definitions.cc:286

References Index_of_diagonal_entries, OOMPH_EXCEPTION_LOCATION, and oomph::Global_string_for_annotation::string().

◆ get_matrix_transpose()

void oomph::CRDoubleMatrix::get_matrix_transpose ( CRDoubleMatrix result) const

Returns the transpose of this matrix.

Compute transpose of matrix.

3272  {
3273  // Get the number of non_zeros
3274  unsigned long nnon_zeros = this->nnz();
3275 
3276  // Find the number of rows and columns in the transposed
3277  // matrix. We differentiate these from those associated
3278  // with the original matrix by appending the characters
3279  // '_t' onto the end
3280  unsigned long n_rows = this->nrow();
3281  unsigned long n_rows_t = this->ncol();
3282 
3283 #ifdef OOMPH_HAS_MPI
3284  // We only need to produce a warning if the matrix is distributed
3285  if (this->distributed())
3286  {
3287  // Create an ostringstream object to store the warning message
3288  std::ostringstream warning_message;
3289 
3290  // Create the warning messsage
3291  warning_message << "This method currently works for serial but "
3292  << "has not been tested with MPI!\n";
3293 
3294  // Issue the warning
3295  OomphLibWarning(warning_message.str(),
3298  }
3299 #endif
3300 
3301  // Set up the distribution for the transposed matrix
3302  result->distribution_pt()->build(
3303  this->distribution_pt()->communicator_pt(), n_rows_t, false);
3304 
3305  // Acquire access to the value, row_start and column_index
3306  // arrays from the CR matrix
3307  const double* value_pt = this->value();
3308  const int* row_start_pt = this->row_start();
3309  const int* column_index_pt = this->column_index();
3310 
3311  // Allocate space for the row_start and column_index vectors
3312  // associated with the transpose of the current matrix.
3313  Vector<double> value_t(nnon_zeros, 0.0);
3314  Vector<int> column_index_t(nnon_zeros, 0);
3315  Vector<int> row_start_t(n_rows_t + 1, 0);
3316 
3317  // Loop over the column index vector and count how many times
3318  // each column number occurs and increment the appropriate
3319  // entry in row_start_t whose i+1'th entry will contain the
3320  // number of non-zeros in the i'th column of the matrix (this
3321  // is done so that the cumulative sum done next returns the
3322  // correct result)
3323  for (unsigned i = 0; i < nnon_zeros; i++)
3324  {
3325  // Assign entries to row_start_t (noting the first
3326  // entry is left as 0 for the cumulative sum done later)
3327  row_start_t[*(column_index_pt + i) + 1]++;
3328  }
3329 
3330  // Calculate the sum of the first i entries in the row_start_t
3331  // vector and store the values in the i'th entry of row_start_t
3332  for (unsigned i = 1; i < n_rows_t + 1; i++)
3333  {
3334  // Calculate the cumulative sum
3335  row_start_t[i] += row_start_t[i - 1];
3336  }
3337 
3338  // Allocate space for variables to store the indices of the
3339  // start and end of the non-zeros in a given row of the matrix
3340  unsigned i_row_s = 0;
3341  unsigned i_row_e = 0;
3342 
3343  // Initialise 3 extra variables for readability of the
3344  // code in the subsequent piece of code
3345  unsigned a = 0;
3346  unsigned b = 0;
3347  unsigned c = 0;
3348 
3349  // Vector needed to count the number of entries added
3350  // to each segment in column_index_t where each segment
3351  // is associated with each row in the transposed matrix
3352  Vector<int> counter(n_rows_t, 0);
3353 
3354  // Set the entries in column_index_t. To do this we loop
3355  // over each row of the original matrix (equivalently
3356  // the number of columns in the transpose)
3357  for (unsigned i_row = 0; i_row < n_rows; i_row++)
3358  {
3359  // Here we find the indices of the start and end
3360  // of the non-zeros in i_row'th row of the matrix.
3361  // [Note, there should be a -1 on i_row_e but this
3362  // is ignored so that we use a strict inequality
3363  // in the subsequent for-loop!]
3364  i_row_s = *(row_start_pt + i_row);
3365  i_row_e = *(row_start_pt + i_row + 1);
3366 
3367  // Loop over the entries in the i_row'th row
3368  // of the matrix
3369  for (unsigned j = i_row_s; j < i_row_e; j++)
3370  {
3371  // The value of a is the column index of the j'th
3372  // element in the i_row'th row of the original matrix
3373  // (which is also the row index of the associated
3374  // non-zero in the transposed matrix)
3375  a = *(column_index_pt + j);
3376 
3377  // The value of b will be used to find the start
3378  // of the appropriate segment of column_index_t
3379  // that the non-zero belongs in
3380  b = row_start_t[a];
3381 
3382  // Find the number of elements already added to
3383  // this segment (to find which entry of the segment
3384  // the value i_row, the column index of the non-zero
3385  // in the transposed matrix, should be assigned to)
3386  c = counter[*(column_index_pt + j)];
3387 
3388  // Assign the value i_row to the appropriate entry
3389  // in column_index_t
3390  column_index_t[b + c] = i_row;
3391  value_t[b + c] = *(value_pt + j);
3392 
3393  // Increment the j'th value of the vector counter
3394  // to indicate another non-zero index has been
3395  // added into the
3396  counter[*(column_index_pt + j)]++;
3397 
3398  } // End of for-loop over i_row'th row of the matrix
3399 
3400  } // End of for-loop over rows of the matrix
3401 
3402  // Build the matrix (note: the value of n_cols for the
3403  // transposed matrix is n_rows for the original matrix)
3404  result->build(n_rows, value_t, column_index_t, row_start_t);
3405 
3406  } // End of the function
Scalar * b
Definition: benchVecAdd.cpp:17
const Scalar * a
Definition: level2_cplx_impl.h:32
int c
Definition: calibrate.py:100

References a, b, build(), oomph::LinearAlgebraDistribution::build(), calibrate::c, column_index(), oomph::DistributableLinearAlgebraObject::distributed(), oomph::DistributableLinearAlgebraObject::distribution_pt(), i, j, ncol(), nnz(), nrow(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, row_start(), and value().

◆ global_matrix()

CRDoubleMatrix * oomph::CRDoubleMatrix::global_matrix ( ) const

if this matrix is distributed then a the equivalent global matrix is built using new and returned. The calling method is responsible for the destruction of the new matrix.

if this matrix is distributed then the equivalent global matrix is built using new and returned. The calling method is responsible for the destruction of the new matrix.

2432  {
2433 #ifdef OOMPH_HAS_MPI
2434  // if this matrix is not distributed then this method is redundant
2435  if (!this->distributed() ||
2436  this->distribution_pt()->communicator_pt()->nproc() == 1)
2437  {
2438  return new CRDoubleMatrix(*this);
2439  }
2440 
2441  // nnz
2442  int nnz = this->nnz();
2443 
2444  // my nrow local
2445  unsigned nrow_local = this->nrow_local();
2446 
2447  // nrow global
2448  unsigned nrow = this->nrow();
2449 
2450  // cache nproc
2451  int nproc = this->distribution_pt()->communicator_pt()->nproc();
2452 
2453  // get the nnzs on the other processors
2454  int* dist_nnz_pt = new int[nproc];
2455  MPI_Allgather(&nnz,
2456  1,
2457  MPI_INT,
2458  dist_nnz_pt,
2459  1,
2460  MPI_INT,
2461  this->distribution_pt()->communicator_pt()->mpi_comm());
2462 
2463  // create a int vector of first rows and nrow local and compute nnz global
2464  int* dist_first_row = new int[nproc];
2465  int* dist_nrow_local = new int[nproc];
2466  int nnz_global = 0;
2467  for (int p = 0; p < nproc; p++)
2468  {
2469  nnz_global += dist_nnz_pt[p];
2470  dist_first_row[p] = this->first_row(p);
2471  dist_nrow_local[p] = this->nrow_local(p);
2472  }
2473 
2474  // compute the offset for the values and column index data
2475  int* nnz_offset = new int[nproc];
2476  nnz_offset[0] = 0;
2477  for (int p = 1; p < nproc; p++)
2478  {
2479  nnz_offset[p] = nnz_offset[p - 1] + dist_nnz_pt[p - 1];
2480  }
2481 
2482  // get pointers to the (current) distributed data
2483  // const_cast required because MPI requires non-const data when sending
2484  // data
2485  int* dist_row_start = const_cast<int*>(this->row_start());
2486  int* dist_column_index = const_cast<int*>(this->column_index());
2487  double* dist_value = const_cast<double*>(this->value());
2488 
2489  // space for the global matrix
2490  int* global_row_start = new int[nrow + 1];
2491  int* global_column_index = new int[nnz_global];
2492  double* global_value = new double[nnz_global];
2493 
2494  // get the row starts
2495  MPI_Allgatherv(dist_row_start,
2496  nrow_local,
2497  MPI_INT,
2498  global_row_start,
2499  dist_nrow_local,
2500  dist_first_row,
2501  MPI_INT,
2502  this->distribution_pt()->communicator_pt()->mpi_comm());
2503 
2504  // get the column indexes
2505  MPI_Allgatherv(dist_column_index,
2506  nnz,
2507  MPI_INT,
2508  global_column_index,
2509  dist_nnz_pt,
2510  nnz_offset,
2511  MPI_INT,
2512  this->distribution_pt()->communicator_pt()->mpi_comm());
2513 
2514  // get the values
2515  MPI_Allgatherv(dist_value,
2516  nnz,
2517  MPI_DOUBLE,
2518  global_value,
2519  dist_nnz_pt,
2520  nnz_offset,
2521  MPI_DOUBLE,
2522  this->distribution_pt()->communicator_pt()->mpi_comm());
2523 
2524  // finally the last row start
2525  global_row_start[nrow] = nnz_global;
2526 
2527  // update the other row start
2528  for (int p = 0; p < nproc; p++)
2529  {
2530  for (int i = 0; i < dist_nrow_local[p]; i++)
2531  {
2532  unsigned j = dist_first_row[p] + i;
2533  global_row_start[j] += nnz_offset[p];
2534  }
2535  }
2536 
2537  // create the global distribution
2538  LinearAlgebraDistribution* dist_pt = new LinearAlgebraDistribution(
2539  this->distribution_pt()->communicator_pt(), nrow, false);
2540 
2541  // create the matrix
2542  CRDoubleMatrix* matrix_pt = new CRDoubleMatrix(dist_pt);
2543 
2544  // copy of distribution taken so delete
2545  delete dist_pt;
2546 
2547  // pass data into matrix
2548  matrix_pt->build_without_copy(this->ncol(),
2549  nnz_global,
2550  global_value,
2551  global_column_index,
2552  global_row_start);
2553 
2554  // clean up
2555  delete[] dist_first_row;
2556  delete[] dist_nrow_local;
2557  delete[] nnz_offset;
2558  delete[] dist_nnz_pt;
2559 
2560  // and return
2561  return matrix_pt;
2562 #else
2563  return new CRDoubleMatrix(*this);
2564 #endif
2565  }
float * p
Definition: Tutorial_Map_using.cpp:9
CRDoubleMatrix()
Default constructor.
Definition: matrices.cc:1214
OomphCommunicator * communicator_pt() const
const access to the communicator pointer
Definition: linear_algebra_distribution.h:335
int nproc() const
number of processors
Definition: communicator.h:157

References build_without_copy(), column_index(), oomph::LinearAlgebraDistribution::communicator_pt(), CRDoubleMatrix(), oomph::DistributableLinearAlgebraObject::distributed(), oomph::DistributableLinearAlgebraObject::distribution_pt(), oomph::DistributableLinearAlgebraObject::first_row(), i, j, ncol(), nnz(), oomph::OomphCommunicator::nproc(), nrow(), oomph::DistributableLinearAlgebraObject::nrow_local(), p, row_start(), and value().

Referenced by oomph::ILUZeroPreconditioner< CRDoubleMatrix >::setup().

◆ inf_norm()

double oomph::CRDoubleMatrix::inf_norm ( ) const

returns the inf-norm of this matrix

Compute infinity (maximum) norm of matrix.

3413  {
3414 #ifdef PARANOID
3415  // paranoid check that the vector is setup
3416  if (!this->distribution_built())
3417  {
3418  std::ostringstream error_message;
3419  error_message << "This matrix must be setup.";
3420  throw OomphLibError(
3421  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3422  }
3423 #endif
3424 
3425  // compute the local norm
3426  unsigned nrow_local = this->nrow_local();
3427  double n = 0;
3428  const int* row_start = CR_matrix.row_start();
3429  const double* value = CR_matrix.value();
3430  for (unsigned i = 0; i < nrow_local; i++)
3431  {
3432  double a = 0;
3433  for (int j = row_start[i]; j < row_start[i + 1]; j++)
3434  {
3435  a += fabs(value[j]);
3436  }
3437  n = std::max(n, a);
3438  }
3439 
3440  // if this vector is distributed and on multiple processors then gather
3441 #ifdef OOMPH_HAS_MPI
3442  double n2 = n;
3443  if (this->distributed() &&
3444  this->distribution_pt()->communicator_pt()->nproc() > 1)
3445  {
3446  MPI_Allreduce(&n,
3447  &n2,
3448  1,
3449  MPI_DOUBLE,
3450  MPI_MAX,
3451  this->distribution_pt()->communicator_pt()->mpi_comm());
3452  }
3453  n = n2;
3454 #endif
3455 
3456  // and return
3457  return n;
3458  }
const unsigned n
Definition: CG3DPackingUnitTest.cpp:11
int * row_start()
Access to C-style row_start array.
Definition: matrices.h:785
bool distribution_built() const
Definition: linear_algebra_distribution.h:500
T * value()
Access to C-style value array.
Definition: matrices.h:616
#define max(a, b)
Definition: datatypes.h:23
Real fabs(const Real &a)
Definition: boostmultiprec.cpp:117

References a, CR_matrix, oomph::DistributableLinearAlgebraObject::distributed(), oomph::DistributableLinearAlgebraObject::distribution_built(), oomph::DistributableLinearAlgebraObject::distribution_pt(), boost::multiprecision::fabs(), i, j, max, n, oomph::DistributableLinearAlgebraObject::nrow_local(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, oomph::CRMatrix< T >::row_start(), row_start(), oomph::SparseMatrix< T, MATRIX_TYPE >::value(), and value().

Referenced by oomph::PseudoElasticPreconditionerScalingHelperOld::s_inf_norm().

◆ lubksub()

void oomph::CRDoubleMatrix::lubksub ( DoubleVector rhs)
virtual

LU back solve for given RHS.

Do back-substitution.

1750  {
1751 #ifdef PARANOID
1752  // check that the rhs vector is setup
1753  if (!rhs.built())
1754  {
1755  std::ostringstream error_message_stream;
1756  error_message_stream << "The vector rhs has not been setup";
1757  throw OomphLibError(error_message_stream.str(),
1760  }
1761  // check that the rhs vector has the same distribution as this matrix
1762  if (!(*this->distribution_pt() == *rhs.distribution_pt()))
1763  {
1764  std::ostringstream error_message_stream;
1765  error_message_stream
1766  << "The vector rhs must have the same distribution as the matrix";
1767  throw OomphLibError(error_message_stream.str(),
1770  }
1771 #endif
1772 
1773  // backsub
1774  DoubleVector rhs_copy(rhs);
1775  static_cast<SuperLUSolver*>(Default_linear_solver_pt)
1776  ->backsub(rhs_copy, rhs);
1777  }
std::vector< double > DoubleVector
loads clump configuration
Definition: ClumpInput.h:26

References oomph::DoubleVector::built(), oomph::DoubleMatrixBase::Default_linear_solver_pt, oomph::DistributableLinearAlgebraObject::distribution_pt(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

◆ ludecompose()

void oomph::CRDoubleMatrix::ludecompose ( )
virtual

Do LU decomposition.

LU decomposition using SuperLU if matrix is not distributed or distributed onto a single processor.

1729  {
1730 #ifdef PARANOID
1731  // check that the this matrix is built
1732  if (!Built)
1733  {
1734  std::ostringstream error_message_stream;
1735  error_message_stream << "This matrix has not been built.";
1736  throw OomphLibError(error_message_stream.str(),
1739  }
1740 #endif
1741 
1742  // factorise using superlu or superlu dist if we oomph has mpi
1743  static_cast<SuperLUSolver*>(Default_linear_solver_pt)->factorise(this);
1744  }

References Built, oomph::DoubleMatrixBase::Default_linear_solver_pt, OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

◆ matrix_reduction()

void oomph::CRDoubleMatrix::matrix_reduction ( const double alpha,
CRDoubleMatrix reduced_matrix 
)

For every row, find the maximum absolute value of the entries in this row. Set all values that are less than alpha times this maximum to zero and return the resulting matrix in reduced_matrix. Note: Diagonal entries are retained regardless of their size.

2367  {
2368  // number of rows in matrix
2369  long n_row = nrow_local();
2370  double max_row;
2371 
2372  // Here's the packed format for the new matrix
2373  Vector<int> B_row_start(1);
2374  Vector<int> B_column_index;
2375  Vector<double> B_value;
2376 
2377  // get pointers to the underlying data
2378  const int* row_start = CR_matrix.row_start();
2379  const int* column_index = CR_matrix.column_index();
2380  const double* value = CR_matrix.value();
2381 
2382  // k is counter for the number of entries in the reduced matrix
2383  unsigned k = 0;
2384 
2385  // Initialise row start
2386  B_row_start[0] = 0;
2387 
2388  // Loop over rows
2389  for (long i = 0; i < n_row; i++)
2390  {
2391  // Initialise max value in row
2392  max_row = 0.0;
2393 
2394  // Loop over entries in columns
2395  for (long j = row_start[i]; j < row_start[i + 1]; j++)
2396  {
2397  // Find max. value in row
2398  if (std::fabs(value[j]) > max_row)
2399  {
2400  max_row = std::fabs(value[j]);
2401  }
2402  }
2403 
2404  // Decide if we need to retain the entries in the row
2405  for (long j = row_start[i]; j < row_start[i + 1]; j++)
2406  {
2407  // If we're on the diagonal or the value is sufficiently large: retain
2408  // i.e. copy across.
2409  if (i == column_index[j] || std::fabs(value[j]) > alpha * max_row)
2410  {
2411  B_value.push_back(value[j]);
2412  B_column_index.push_back(column_index[j]);
2413  k++;
2414  }
2415  }
2416  // This writes the row start for the next row -- equal to
2417  // to the number of entries written so far (C++ zero-based indexing!)
2418  B_row_start.push_back(k);
2419  }
2420 
2421  // Build the matrix from the compressed format
2422  dynamic_cast<CRDoubleMatrix&>(reduced_matrix)
2423  .build(this->ncol(), B_value, B_column_index, B_row_start);
2424  }
RealScalar alpha
Definition: level1_cplx_impl.h:151
char char char int int * k
Definition: level2_impl.h:374

References alpha, build(), oomph::CRMatrix< T >::column_index(), column_index(), CR_matrix, boost::multiprecision::fabs(), i, j, k, ncol(), oomph::DistributableLinearAlgebraObject::nrow_local(), oomph::CRMatrix< T >::row_start(), row_start(), oomph::SparseMatrix< T, MATRIX_TYPE >::value(), and value().

◆ multiply() [1/2]

void oomph::CRDoubleMatrix::multiply ( const CRDoubleMatrix matrix_in,
CRDoubleMatrix result 
) const

Function to multiply this matrix by the CRDoubleMatrix matrix_in. In a serial matrix, there are 4 methods available: Method 1: First runs through this matrix and matrix_in to find the storage requirements for result - arrays of the correct size are then allocated before performing the calculation. Minimises memory requirements but more costly. Method 2: Grows storage for values and column indices of result 'on the fly' using an array of maps. Faster but more memory intensive. Method 3: Grows storage for values and column indices of result 'on the fly' using a vector of vectors. Not particularly impressive on the platforms we tried... Method 4: Trilinos Epetra Matrix Matrix multiply. Method 5: Trilinox Epetra Matrix Matrix Mulitply (ml based) If Trilinos is installed then Method 4 is employed by default, otherwise Method 2 is employed by default. In a distributed matrix, only Trilinos Epetra Matrix Matrix multiply is available.

1994  {
1995 #ifdef PARANOID
1996  // check that this matrix is built
1997  if (!Built)
1998  {
1999  std::ostringstream error_message_stream;
2000  error_message_stream << "This matrix has not been built";
2001  throw OomphLibError(error_message_stream.str(),
2004  }
2005  // check that this matrix is built
2006  if (!matrix_in.built())
2007  {
2008  std::ostringstream error_message_stream;
2009  error_message_stream << "This matrix matrix_in has not been built";
2010  throw OomphLibError(error_message_stream.str(),
2013  }
2014  // if soln is setup then it should have the same distribution as x
2015  if (result.built())
2016  {
2017  if (!(*result.distribution_pt() == *this->distribution_pt()))
2018  {
2019  std::ostringstream error_message_stream;
2020  error_message_stream
2021  << "The matrix result is setup and therefore must have the same "
2022  << "distribution as the vector x";
2023  throw OomphLibError(error_message_stream.str(),
2026  }
2027  }
2028 #endif
2029 
2030  // if the result has not been setup, then store the distribution
2031  if (!result.distribution_built())
2032  {
2033  result.build(this->distribution_pt());
2034  }
2035 
2036  // short name for Serial_matrix_matrix_multiply_method
2037  unsigned method = Serial_matrix_matrix_multiply_method;
2038 
2039  // if this matrix is not distributed and matrix in is not distributed
2040  if (!this->distributed() && !matrix_in.distributed() &&
2041  ((method == 1) || (method == 2) || (method == 3)))
2042  {
2043  // NB N is number of rows!
2044  unsigned long N = this->nrow();
2045  unsigned long M = matrix_in.ncol();
2046  unsigned long Nnz = 0;
2047 
2048  // pointers to arrays which store result
2049  int* Row_start = 0;
2050  double* Value = 0;
2051  int* Column_index = 0;
2052 
2053  // get pointers to matrix_in
2054  const int* matrix_in_row_start = matrix_in.row_start();
2055  const int* matrix_in_column_index = matrix_in.column_index();
2056  const double* matrix_in_value = matrix_in.value();
2057 
2058  // get pointers to this matrix
2059  const double* this_value = this->value();
2060  const int* this_row_start = this->row_start();
2061  const int* this_column_index = this->column_index();
2062 
2063  // clock_t clock1 = clock();
2064 
2065  // METHOD 1
2066  // --------
2067  if (method == 1)
2068  {
2069  // allocate storage for row starts
2070  Row_start = new int[N + 1];
2071  Row_start[0] = 0;
2072 
2073  // a set to store number of non-zero columns in each row of result
2074  std::set<unsigned> columns;
2075 
2076  // run through rows of this matrix and matrix_in to find number of
2077  // non-zero entries in each row of result
2078  for (unsigned long this_row = 0; this_row < N; this_row++)
2079  {
2080  // run through non-zeros in this_row of this matrix
2081  for (int this_ptr = this_row_start[this_row];
2082  this_ptr < this_row_start[this_row + 1];
2083  this_ptr++)
2084  {
2085  // find column index for non-zero
2086  int matrix_in_row = this_column_index[this_ptr];
2087 
2088  // run through corresponding row in matrix_in
2089  for (int matrix_in_ptr = matrix_in_row_start[matrix_in_row];
2090  matrix_in_ptr < matrix_in_row_start[matrix_in_row + 1];
2091  matrix_in_ptr++)
2092  {
2093  // find column index for non-zero in matrix_in and store in
2094  // columns
2095  columns.insert(matrix_in_column_index[matrix_in_ptr]);
2096  }
2097  }
2098  // update Row_start
2099  Row_start[this_row + 1] = Row_start[this_row] + columns.size();
2100 
2101  // wipe values in columns
2102  columns.clear();
2103  }
2104 
2105  // set Nnz
2106  Nnz = Row_start[N];
2107 
2108  // allocate arrays for result
2109  Value = new double[Nnz];
2110  Column_index = new int[Nnz];
2111 
2112  // set all values of Column_index to -1
2113  for (unsigned long i = 0; i < Nnz; i++)
2114  {
2115  Column_index[i] = -1;
2116  }
2117 
2118  // Calculate values for result - first run through rows of this matrix
2119  for (unsigned long this_row = 0; this_row < N; this_row++)
2120  {
2121  // run through non-zeros in this_row
2122  for (int this_ptr = this_row_start[this_row];
2123  this_ptr < this_row_start[this_row + 1];
2124  this_ptr++)
2125  {
2126  // find value of non-zero
2127  double this_val = this_value[this_ptr];
2128 
2129  // find column associated with non-zero
2130  int matrix_in_row = this_column_index[this_ptr];
2131 
2132  // run through corresponding row in matrix_in
2133  for (int matrix_in_ptr = matrix_in_row_start[matrix_in_row];
2134  matrix_in_ptr < matrix_in_row_start[matrix_in_row + 1];
2135  matrix_in_ptr++)
2136  {
2137  // find column index for non-zero in matrix_in
2138  int col = matrix_in_column_index[matrix_in_ptr];
2139 
2140  // find position in result to insert value
2141  for (int ptr = Row_start[this_row];
2142  ptr <= Row_start[this_row + 1];
2143  ptr++)
2144  {
2145  if (ptr == Row_start[this_row + 1])
2146  {
2147  // error - have passed end of row without finding
2148  // correct column
2149  std::ostringstream error_message;
2150  error_message << "Error inserting value in result";
2151 
2152  throw OomphLibError(error_message.str(),
2155  }
2156  else if (Column_index[ptr] == -1)
2157  {
2158  // first entry for this column index
2159  Column_index[ptr] = col;
2160  Value[ptr] = this_val * matrix_in_value[matrix_in_ptr];
2161  break;
2162  }
2163  else if (Column_index[ptr] == col)
2164  {
2165  // column index already exists - add value
2166  Value[ptr] += this_val * matrix_in_value[matrix_in_ptr];
2167  break;
2168  }
2169  }
2170  }
2171  }
2172  }
2173  }
2174 
2175  // METHOD 2
2176  // --------
2177  else if (method == 2)
2178  {
2179  // generate array of maps to store values for result
2180  std::map<int, double>* result_maps = new std::map<int, double>[N];
2181 
2182  // run through rows of this matrix
2183  for (unsigned long this_row = 0; this_row < N; this_row++)
2184  {
2185  // run through non-zeros in this_row
2186  for (int this_ptr = this_row_start[this_row];
2187  this_ptr < this_row_start[this_row + 1];
2188  this_ptr++)
2189  {
2190  // find value of non-zero
2191  double this_val = this_value[this_ptr];
2192 
2193  // find column index associated with non-zero
2194  int matrix_in_row = this_column_index[this_ptr];
2195 
2196  // run through corresponding row in matrix_in
2197  for (int matrix_in_ptr = matrix_in_row_start[matrix_in_row];
2198  matrix_in_ptr < matrix_in_row_start[matrix_in_row + 1];
2199  matrix_in_ptr++)
2200  {
2201  // find column index for non-zero in matrix_in
2202  int col = matrix_in_column_index[matrix_in_ptr];
2203 
2204  // insert value
2205  result_maps[this_row][col] +=
2206  this_val * matrix_in_value[matrix_in_ptr];
2207  }
2208  }
2209  }
2210 
2211  // allocate Row_start
2212  Row_start = new int[N + 1];
2213 
2214  // copy across row starts
2215  Row_start[0] = 0;
2216  for (unsigned long row = 0; row < N; row++)
2217  {
2218  int size = result_maps[row].size();
2219  Row_start[row + 1] = Row_start[row] + size;
2220  }
2221 
2222  // set Nnz
2223  Nnz = Row_start[N];
2224 
2225  // allocate other arrays
2226  Value = new double[Nnz];
2227  Column_index = new int[Nnz];
2228 
2229  // copy values and column indices
2230  for (unsigned long row = 0; row < N; row++)
2231  {
2232  unsigned ptr = Row_start[row];
2233  for (std::map<int, double>::iterator i = result_maps[row].begin();
2234  i != result_maps[row].end();
2235  i++)
2236  {
2237  Column_index[ptr] = i->first;
2238  Value[ptr] = i->second;
2239  ptr++;
2240  }
2241  }
2242 
2243  // tidy up memory
2244  delete[] result_maps;
2245  }
2246 
2247  // METHOD 3
2248  // --------
2249  else if (method == 3)
2250  {
2251  // vectors of vectors to store results
2252  std::vector<std::vector<int>> result_cols(N);
2253  std::vector<std::vector<double>> result_vals(N);
2254 
2255  // run through the rows of this matrix
2256  for (unsigned long this_row = 0; this_row < N; this_row++)
2257  {
2258  // run through non-zeros in this_row
2259  for (int this_ptr = this_row_start[this_row];
2260  this_ptr < this_row_start[this_row + 1];
2261  this_ptr++)
2262  {
2263  // find value of non-zero
2264  double this_val = this_value[this_ptr];
2265 
2266  // find column index associated with non-zero
2267  int matrix_in_row = this_column_index[this_ptr];
2268 
2269  // run through corresponding row in matrix_in
2270  for (int matrix_in_ptr = matrix_in_row_start[matrix_in_row];
2271  matrix_in_ptr < matrix_in_row_start[matrix_in_row + 1];
2272  matrix_in_ptr++)
2273  {
2274  // find column index for non-zero in matrix_in
2275  int col = matrix_in_column_index[matrix_in_ptr];
2276 
2277  // insert value
2278  int size = result_cols[this_row].size();
2279  for (int i = 0; i <= size; i++)
2280  {
2281  if (i == size)
2282  {
2283  // first entry for this column
2284  result_cols[this_row].push_back(col);
2285  result_vals[this_row].push_back(
2286  this_val * matrix_in_value[matrix_in_ptr]);
2287  }
2288  else if (col == result_cols[this_row][i])
2289  {
2290  // column already exists
2291  result_vals[this_row][i] +=
2292  this_val * matrix_in_value[matrix_in_ptr];
2293  break;
2294  }
2295  }
2296  }
2297  }
2298  }
2299 
2300  // allocate Row_start
2301  Row_start = new int[N + 1];
2302 
2303  // copy across row starts
2304  Row_start[0] = 0;
2305  for (unsigned long row = 0; row < N; row++)
2306  {
2307  int size = result_cols[row].size();
2308  Row_start[row + 1] = Row_start[row] + size;
2309  }
2310 
2311  // set Nnz
2312  Nnz = Row_start[N];
2313 
2314  // allocate other arrays
2315  Value = new double[Nnz];
2316  Column_index = new int[Nnz];
2317 
2318  // copy across values and column indices
2319  for (unsigned long row = 0; row < N; row++)
2320  {
2321  unsigned ptr = Row_start[row];
2322  unsigned nnn = result_cols[row].size();
2323  for (unsigned i = 0; i < nnn; i++)
2324  {
2325  Column_index[ptr] = result_cols[row][i];
2326  Value[ptr] = result_vals[row][i];
2327  ptr++;
2328  }
2329  }
2330  }
2331 
2332  // build
2333  result.build_without_copy(M, Nnz, Value, Column_index, Row_start);
2334  }
2335 
2336  // else we have to use trilinos
2337  else
2338  {
2339 #ifdef OOMPH_HAS_TRILINOS
2340  bool use_ml = false;
2341  if (method == 5)
2342  {
2343  use_ml = true;
2344  }
2345  TrilinosEpetraHelpers::multiply(*this, matrix_in, result, use_ml);
2346 #else
2347  std::ostringstream error_message;
2348  error_message << "Serial_matrix_matrix_multiply_method = "
2350  << " requires trilinos.";
2351  throw OomphLibError(
2352  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
2353 #endif
2354  }
2355  }
m col(1)
m row(1)
Scalar Scalar int size
Definition: benchVecAdd.cpp:17
The matrix class, also used for vectors and row-vectors.
Definition: Eigen/Eigen/src/Core/Matrix.h:186
@ N
Definition: constructor.cpp:22
void multiply(const CRDoubleMatrix *matrix, const DoubleVector &x, DoubleVector &soln)
Definition: trilinos_helpers.cc:657
GenericValue< UTF8<> > Value
Value with UTF8 encoding.
Definition: document.h:679

References build(), build_without_copy(), built(), Built, col(), column_index(), oomph::DistributableLinearAlgebraObject::distributed(), oomph::DistributableLinearAlgebraObject::distribution_built(), oomph::DistributableLinearAlgebraObject::distribution_pt(), i, oomph::TrilinosEpetraHelpers::multiply(), N, ncol(), nrow(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, row(), row_start(), Serial_matrix_matrix_multiply_method, size, and value().

◆ multiply() [2/2]

void oomph::CRDoubleMatrix::multiply ( const DoubleVector x,
DoubleVector soln 
) const
virtual

Multiply the matrix by the vector x: soln=Ax.

Multiply the matrix by the vector x.

Implements oomph::DoubleMatrixBase.

1783  {
1784 #ifdef PARANOID
1785  // check that this matrix is built
1786  if (!Built)
1787  {
1788  std::ostringstream error_message_stream;
1789  error_message_stream << "This matrix has not been built";
1790  throw OomphLibError(error_message_stream.str(),
1793  }
1794  // check that the distribution of x is setup
1795  if (!x.built())
1796  {
1797  std::ostringstream error_message_stream;
1798  error_message_stream << "The distribution of the vector x must be setup";
1799  throw OomphLibError(error_message_stream.str(),
1802  }
1803  // Check to see if x.size() = ncol().
1804  if (this->ncol() != x.distribution_pt()->nrow())
1805  {
1806  std::ostringstream error_message_stream;
1807  error_message_stream << "The number of rows in the x vector and the "
1808  "number of columns in the "
1809  << "matrix must be the same";
1810  throw OomphLibError(error_message_stream.str(),
1813  }
1814  // if the soln is distributed
1815  if (soln.built())
1816  {
1817  if (!(*soln.distribution_pt() == *this->distribution_pt()))
1818  {
1819  std::ostringstream error_message_stream;
1820  error_message_stream
1821  << "The soln vector is setup and therefore must have the same "
1822  << "distribution as the matrix";
1823  throw OomphLibError(error_message_stream.str(),
1826  }
1827  }
1828 #endif
1829 
1830  // if soln is not setup then setup the distribution
1831  if (!soln.built())
1832  {
1833  // Resize and initialize the solution vector
1834  soln.build(this->distribution_pt(), 0.0);
1835  }
1836 
1837  // Initialise
1838  soln.initialise(0.0);
1839 
1840  // if distributed and on more than one processor use trilinos
1841  // otherwise use the oomph-lib methods
1842  if (this->distributed() &&
1843  this->distribution_pt()->communicator_pt()->nproc() > 1)
1844  {
1845 #ifdef OOMPH_HAS_TRILINOS
1846  // This will only work if we have trilinos on board
1847  TrilinosEpetraHelpers::multiply(this, x, soln);
1848 #else
1849  std::ostringstream error_message_stream;
1850  error_message_stream
1851  << "Matrix-vector product on multiple processors with distributed "
1852  << "CRDoubleMatrix requires Trilinos.";
1853  throw OomphLibError(error_message_stream.str(),
1856 #endif
1857  }
1858  else
1859  {
1860  unsigned n = this->nrow();
1861  const int* row_start = CR_matrix.row_start();
1862  const int* column_index = CR_matrix.column_index();
1863  const double* value = CR_matrix.value();
1864  double* soln_pt = soln.values_pt();
1865  const double* x_pt = x.values_pt();
1866  for (unsigned long i = 0; i < n; i++)
1867  {
1868  soln_pt[i] = 0.0;
1869  for (long k = row_start[i]; k < row_start[i + 1]; k++)
1870  {
1871  unsigned long j = column_index[k];
1872  double a_ij = value[k];
1873  soln_pt[i] += a_ij * x_pt[j];
1874  }
1875  }
1876  }
1877  }
list x
Definition: plotDoE.py:28

References oomph::DoubleVector::build(), oomph::DoubleVector::built(), Built, oomph::CRMatrix< T >::column_index(), column_index(), CR_matrix, oomph::DistributableLinearAlgebraObject::distributed(), oomph::DistributableLinearAlgebraObject::distribution_pt(), i, oomph::DoubleVector::initialise(), j, k, oomph::TrilinosEpetraHelpers::multiply(), n, ncol(), oomph::LinearAlgebraDistribution::nrow(), nrow(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, oomph::CRMatrix< T >::row_start(), row_start(), oomph::SparseMatrix< T, MATRIX_TYPE >::value(), value(), oomph::DoubleVector::values_pt(), and plotDoE::x.

Referenced by oomph::ProblemBasedShiftInvertOperator::apply(), oomph::AugmentedProblemGMRES::augmented_matrix_multiply(), main(), oomph::MatrixVectorProduct::multiply(), oomph::DoubleVector::norm(), oomph::GMRESBlockPreconditioner::preconditioner_solve(), oomph::LagrangeEnforcedFlowPreconditioner::setup(), oomph::NavierStokesSchurComplementPreconditioner::setup(), oomph::PressureBasedSolidLSCPreconditioner::setup(), and oomph::SpaceTimeNavierStokesSubsidiaryPreconditioner::setup().

◆ multiply_transpose()

void oomph::CRDoubleMatrix::multiply_transpose ( const DoubleVector x,
DoubleVector soln 
) const
virtual

Multiply the transposed matrix by the vector x: soln=A^T x.

Implements oomph::DoubleMatrixBase.

1884  {
1885 #ifdef PARANOID
1886  // check that this matrix is built
1887  if (!Built)
1888  {
1889  std::ostringstream error_message_stream;
1890  error_message_stream << "This matrix has not been built";
1891  throw OomphLibError(error_message_stream.str(),
1894  }
1895  // Check to see if x.size() = ncol().
1896  if (!(*this->distribution_pt() == *x.distribution_pt()))
1897  {
1898  std::ostringstream error_message_stream;
1899  error_message_stream
1900  << "The x vector and this matrix must have the same distribution.";
1901  throw OomphLibError(error_message_stream.str(),
1904  }
1905  // if soln is setup then it should have the same distribution as x
1906  if (soln.built())
1907  {
1908  if (soln.distribution_pt()->nrow() != this->ncol())
1909  {
1910  std::ostringstream error_message_stream;
1911  error_message_stream
1912  << "The soln vector is setup and therefore must have the same "
1913  << "number of rows as the vector x";
1914  throw OomphLibError(error_message_stream.str(),
1917  }
1918  }
1919 #endif
1920 
1921  // if soln is not setup then setup the distribution
1922  if (!soln.built())
1923  {
1924  LinearAlgebraDistribution* dist_pt =
1925  new LinearAlgebraDistribution(x.distribution_pt()->communicator_pt(),
1926  this->ncol(),
1927  this->distributed());
1928  soln.build(dist_pt, 0.0);
1929  delete dist_pt;
1930  }
1931 
1932  // Initialise
1933  soln.initialise(0.0);
1934 
1935  if (this->distributed() &&
1936  this->distribution_pt()->communicator_pt()->nproc() > 1)
1937  {
1938 #ifdef OOMPH_HAS_TRILINOS
1939  // This will only work if we have trilinos on board
1940  TrilinosEpetraHelpers::multiply(this, x, soln);
1941 #else
1942  std::ostringstream error_message_stream;
1943  error_message_stream
1944  << "Matrix-vector product on multiple processors with distributed "
1945  << "CRDoubleMatrix requires Trilinos.";
1946  throw OomphLibError(error_message_stream.str(),
1949 #endif
1950  }
1951  else
1952  {
1953  unsigned n = this->nrow();
1954  const int* row_start = CR_matrix.row_start();
1955  const int* column_index = CR_matrix.column_index();
1956  const double* value = CR_matrix.value();
1957  double* soln_pt = soln.values_pt();
1958  const double* x_pt = x.values_pt();
1959  // Matrix vector product
1960  for (unsigned long i = 0; i < n; i++)
1961  {
1962  for (long k = row_start[i]; k < row_start[i + 1]; k++)
1963  {
1964  unsigned long j = column_index[k];
1965  double a_ij = value[k];
1966  soln_pt[j] += a_ij * x_pt[i];
1967  }
1968  }
1969  }
1970  }

References oomph::DoubleVector::build(), oomph::DoubleVector::built(), Built, oomph::CRMatrix< T >::column_index(), column_index(), CR_matrix, oomph::DistributableLinearAlgebraObject::distributed(), oomph::DistributableLinearAlgebraObject::distribution_pt(), i, oomph::DoubleVector::initialise(), j, k, oomph::TrilinosEpetraHelpers::multiply(), n, oomph::LinearAlgebraDistribution::nrow(), nrow(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, oomph::CRMatrix< T >::row_start(), row_start(), oomph::SparseMatrix< T, MATRIX_TYPE >::value(), value(), oomph::DoubleVector::values_pt(), and plotDoE::x.

Referenced by oomph::MatrixVectorProduct::multiply_transpose(), oomph::SuperLUSolver::solve(), and oomph::SuperLUSolver::solve_transpose().

◆ ncol()

◆ nnz()

◆ nrow()

unsigned long oomph::CRDoubleMatrix::nrow ( ) const
inlinevirtual

Return the number of rows of the matrix.

Implements oomph::DoubleMatrixBase.

1003  {
1005  }
unsigned nrow() const
access function to the number of global rows.
Definition: linear_algebra_distribution.h:463

References oomph::DistributableLinearAlgebraObject::nrow().

Referenced by add(), oomph::AugmentedProblemGMRES::augmented_matrix_multiply(), oomph::ComplexDampedJacobi< MATRIX >::complex_smoother_setup(), oomph::CRDoubleMatrixHelpers::concatenate(), oomph::TrilinosEpetraHelpers::create_distributed_epetra_matrix(), oomph::TrilinosEpetraHelpers::create_distributed_epetra_matrix_for_aztecoo(), oomph::HypreHelpers::create_HYPRE_Matrix(), diagonal_entries(), entries_are_sorted(), oomph::Problem::get_eigenproblem_matrices(), get_matrix_transpose(), global_matrix(), oomph::HypreInterface::hypre_matrix_setup(), main(), oomph::MGSolver< DIM >::modify_restriction_matrices(), oomph::TrilinosEpetraHelpers::multiply(), multiply(), multiply_transpose(), redistribute(), oomph::ILUZeroPreconditioner< CRDoubleMatrix >::setup(), oomph::HyprePreconditioner::setup(), oomph::BandedBlockTriangularPreconditioner< MATRIX >::setup(), oomph::SpaceTimeNavierStokesSubsidiaryPreconditioner::setup(), oomph::GMRESBlockPreconditioner::setup(), oomph::HelmholtzMGPreconditioner< DIM >::setup_coarsest_level_structures(), oomph::SuperLUSolver::solve(), oomph::SuperLUSolver::solve_transpose(), and sort_entries().

◆ operator()()

double oomph::CRDoubleMatrix::operator() ( const unsigned long &  i,
const unsigned long &  j 
) const
inlinevirtual

Overload the round-bracket access operator for read-only access. In a distributed matrix i refers to the local row index.

Implements oomph::DoubleMatrixBase.

1055  {
1056  return CR_matrix.get_entry(i, j);
1057  }

References CR_matrix, oomph::CRMatrix< T >::get_entry(), i, and j.

◆ operator=()

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

Broken assignment operator.

◆ output_bottom_right_zero_helper()

void oomph::CRDoubleMatrix::output_bottom_right_zero_helper ( std::ostream &  outfile) const
inlinevirtual

Output the "bottom right" entry regardless of it being zero or not (this allows automatic detection of matrix size in e.g. matlab, python).

Implements oomph::Matrix< double, CRDoubleMatrix >.

1017  {
1019  }
void output_bottom_right_zero_helper(std::ostream &outfile) const
Definition: matrices.h:811

References CR_matrix, and oomph::CRMatrix< T >::output_bottom_right_zero_helper().

◆ redistribute()

void oomph::CRDoubleMatrix::redistribute ( const LinearAlgebraDistribution *const &  dist_pt)

The contents of the matrix are redistributed to match the new distribution. In a non-MPI build this method does nothing. NOTE 1: The current distribution and the new distribution must have the same number of global rows. NOTE 2: The current distribution and the new distribution must have the same Communicator.

2577  {
2578 #ifdef OOMPH_HAS_MPI
2579 #ifdef PARANOID
2580  // paranoid check that the nrows for both distributions is the
2581  // same
2582  if (dist_pt->nrow() != this->distribution_pt()->nrow())
2583  {
2584  std::ostringstream error_message;
2585  error_message << "The number of global rows in the new distribution ("
2586  << dist_pt->nrow() << ") is not equal to the number"
2587  << " of global rows in the current distribution ("
2588  << this->distribution_pt()->nrow() << ").\n";
2589  throw OomphLibError(
2590  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
2591  }
2592  // paranoid check that the current distribution and the new distribution
2593  // have the same Communicator
2594  OomphCommunicator temp_comm(*dist_pt->communicator_pt());
2595  if (!(temp_comm == *this->distribution_pt()->communicator_pt()))
2596  {
2597  std::ostringstream error_message;
2598  error_message << "The new distribution and the current distribution must "
2599  << "have the same communicator.";
2600  throw OomphLibError(
2601  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
2602  }
2603  // paranoid check that the matrix is build
2604  if (!this->built())
2605  {
2606  std::ostringstream error_message;
2607  error_message << "The matrix must be build to be redistributed";
2608  throw OomphLibError(
2609  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
2610  }
2611 #endif
2612 
2613  // if the two distributions are not the same
2614  // =========================================
2615  if (!((*this->distribution_pt()) == *dist_pt))
2616  {
2617  // Get the number of columns to build the matrix.
2618  unsigned long ncol = this->ncol();
2619 
2620  // current data
2621  int* current_row_start = this->row_start();
2622  int* current_column_index = this->column_index();
2623  double* current_value = this->value();
2624 
2625  // get the rank and the number of processors
2626  int my_rank = this->distribution_pt()->communicator_pt()->my_rank();
2627  int nproc = this->distribution_pt()->communicator_pt()->nproc();
2628 
2629  // if both distributions are distributed
2630  // =====================================
2631  if (this->distributed() && dist_pt->distributed())
2632  {
2633  // new nrow_local and first_row data
2634  Vector<unsigned> new_first_row(nproc);
2635  Vector<unsigned> new_nrow_local(nproc);
2636  Vector<unsigned> current_first_row(nproc);
2637  Vector<unsigned> current_nrow_local(nproc);
2638  for (int i = 0; i < nproc; i++)
2639  {
2640  new_first_row[i] = dist_pt->first_row(i);
2641  new_nrow_local[i] = dist_pt->nrow_local(i);
2642  current_first_row[i] = this->first_row(i);
2643  current_nrow_local[i] = this->nrow_local(i);
2644  }
2645 
2646  // compute which local rows are expected to be received from each
2647  // processor / sent to each processor
2648  Vector<unsigned> first_row_for_proc(nproc, 0);
2649  Vector<unsigned> nrow_local_for_proc(nproc, 0);
2650  Vector<unsigned> first_row_from_proc(nproc, 0);
2651  Vector<unsigned> nrow_local_from_proc(nproc, 0);
2652 
2653  // for every processor compute first_row and nrow_local that will
2654  // will sent and received by this processor
2655  for (int p = 0; p < nproc; p++)
2656  {
2657  // start with data to be sent
2658  if ((new_first_row[p] <
2659  (current_first_row[my_rank] + current_nrow_local[my_rank])) &&
2660  (current_first_row[my_rank] <
2661  (new_first_row[p] + new_nrow_local[p])))
2662  {
2663  first_row_for_proc[p] =
2664  std::max(current_first_row[my_rank], new_first_row[p]);
2665  nrow_local_for_proc[p] =
2666  std::min(
2667  (current_first_row[my_rank] + current_nrow_local[my_rank]),
2668  (new_first_row[p] + new_nrow_local[p])) -
2669  first_row_for_proc[p];
2670  }
2671 
2672  // and data to be received
2673  if ((new_first_row[my_rank] <
2674  (current_first_row[p] + current_nrow_local[p])) &&
2675  (current_first_row[p] <
2676  (new_first_row[my_rank] + new_nrow_local[my_rank])))
2677  {
2678  first_row_from_proc[p] =
2679  std::max(current_first_row[p], new_first_row[my_rank]);
2680  nrow_local_from_proc[p] =
2681  std::min((current_first_row[p] + current_nrow_local[p]),
2682  (new_first_row[my_rank] + new_nrow_local[my_rank])) -
2683  first_row_from_proc[p];
2684  }
2685  }
2686 
2687  // determine how many nnzs to send to each processor
2688  Vector<unsigned> nnz_for_proc(nproc, 0);
2689  for (int p = 0; p < nproc; p++)
2690  {
2691  if (nrow_local_for_proc[p] > 0)
2692  {
2693  nnz_for_proc[p] = (current_row_start[first_row_for_proc[p] -
2694  current_first_row[my_rank] +
2695  nrow_local_for_proc[p]] -
2696  current_row_start[first_row_for_proc[p] -
2697  current_first_row[my_rank]]);
2698  }
2699  }
2700 
2701  // next post non-blocking sends and recv for the nnzs
2702  Vector<unsigned> nnz_from_proc(nproc, 0);
2703  Vector<MPI_Request> send_req;
2704  Vector<MPI_Request> nnz_recv_req;
2705  for (int p = 0; p < nproc; p++)
2706  {
2707  if (p != my_rank)
2708  {
2709  // send
2710  if (nrow_local_for_proc[p] > 0)
2711  {
2712  MPI_Request req;
2713  MPI_Isend(&nnz_for_proc[p],
2714  1,
2715  MPI_UNSIGNED,
2716  p,
2717  0,
2718  this->distribution_pt()->communicator_pt()->mpi_comm(),
2719  &req);
2720  send_req.push_back(req);
2721  }
2722 
2723  // recv
2724  if (nrow_local_from_proc[p] > 0)
2725  {
2726  MPI_Request req;
2727  MPI_Irecv(&nnz_from_proc[p],
2728  1,
2729  MPI_UNSIGNED,
2730  p,
2731  0,
2732  this->distribution_pt()->communicator_pt()->mpi_comm(),
2733  &req);
2734  nnz_recv_req.push_back(req);
2735  }
2736  }
2737  // "send to self"
2738  else
2739  {
2740  nnz_from_proc[p] = nnz_for_proc[p];
2741  }
2742  }
2743 
2744  // allocate new storage for the new row_start
2745  int* new_row_start = new int[new_nrow_local[my_rank] + 1];
2746 
2747  // wait for recvs to complete
2748  unsigned n_recv_req = nnz_recv_req.size();
2749  if (n_recv_req > 0)
2750  {
2751  Vector<MPI_Status> recv_status(n_recv_req);
2752  MPI_Waitall(n_recv_req, &nnz_recv_req[0], &recv_status[0]);
2753  }
2754 
2755  // compute the nnz offset for each processor
2756  unsigned next_row = 0;
2757  unsigned nnz_count = 0;
2758  Vector<unsigned> nnz_offset(nproc, 0);
2759  for (int p = 0; p < nproc; p++)
2760  {
2761  unsigned pp = 0;
2762  while (new_first_row[pp] != next_row)
2763  {
2764  pp++;
2765  }
2766  nnz_offset[pp] = nnz_count;
2767  nnz_count += nnz_from_proc[pp];
2768  next_row += new_nrow_local[pp];
2769  }
2770 
2771  // allocate storage for the values and column indices
2772  int* new_column_index = new int[nnz_count];
2773  double* new_value = new double[nnz_count];
2774 
2775  // post the sends and recvs for the matrix data
2776  Vector<MPI_Request> recv_req;
2777  MPI_Aint base_address;
2778  MPI_Get_address(new_value, &base_address);
2779  for (int p = 0; p < nproc; p++)
2780  {
2781  // communicated with other processors
2782  if (p != my_rank)
2783  {
2784  // SEND
2785  if (nrow_local_for_proc[p] > 0)
2786  {
2787  // array of datatypes
2788  MPI_Datatype types[3];
2789 
2790  // array of offsets
2791  MPI_Aint offsets[3];
2792 
2793  // array of lengths
2794  int len[3];
2795 
2796  // row start
2797  unsigned first_row_to_send =
2798  first_row_for_proc[p] - current_first_row[my_rank];
2799  MPI_Type_contiguous(nrow_local_for_proc[p], MPI_INT, &types[0]);
2800  MPI_Type_commit(&types[0]);
2801  len[0] = 1;
2802  MPI_Get_address(current_row_start + first_row_to_send,
2803  &offsets[0]);
2804  offsets[0] -= base_address;
2805 
2806  // values
2807  unsigned first_coef_to_send =
2808  current_row_start[first_row_to_send];
2809  MPI_Type_contiguous(nnz_for_proc[p], MPI_DOUBLE, &types[1]);
2810  MPI_Type_commit(&types[1]);
2811  len[1] = 1;
2812  MPI_Get_address(current_value + first_coef_to_send, &offsets[1]);
2813  offsets[1] -= base_address;
2814 
2815  // column index
2816  MPI_Type_contiguous(nnz_for_proc[p], MPI_DOUBLE, &types[2]);
2817  MPI_Type_commit(&types[2]);
2818  len[2] = 1;
2819  MPI_Get_address(current_column_index + first_coef_to_send,
2820  &offsets[2]);
2821  offsets[2] -= base_address;
2822 
2823  // build the combined datatype
2824  MPI_Datatype send_type;
2825  MPI_Type_create_struct(3, len, offsets, types, &send_type);
2826  MPI_Type_commit(&send_type);
2827  MPI_Type_free(&types[0]);
2828  MPI_Type_free(&types[1]);
2829  MPI_Type_free(&types[2]);
2830 
2831  // and send
2832  MPI_Request req;
2833  MPI_Isend(new_value,
2834  1,
2835  send_type,
2836  p,
2837  1,
2838  this->distribution_pt()->communicator_pt()->mpi_comm(),
2839  &req);
2840  send_req.push_back(req);
2841  MPI_Type_free(&send_type);
2842  }
2843 
2844  // RECV
2845  if (nrow_local_from_proc[p] > 0)
2846  {
2847  // array of datatypes
2848  MPI_Datatype types[3];
2849 
2850  // array of offsets
2851  MPI_Aint offsets[3];
2852 
2853  // array of lengths
2854  int len[3];
2855 
2856  // row start
2857  unsigned first_row_to_recv =
2858  first_row_from_proc[p] - new_first_row[my_rank];
2859  MPI_Type_contiguous(nrow_local_from_proc[p], MPI_INT, &types[0]);
2860  MPI_Type_commit(&types[0]);
2861  len[0] = 1;
2862  MPI_Get_address(new_row_start + first_row_to_recv, &offsets[0]);
2863  offsets[0] -= base_address;
2864 
2865  // values
2866  unsigned first_coef_to_recv = nnz_offset[p];
2867  MPI_Type_contiguous(nnz_from_proc[p], MPI_DOUBLE, &types[1]);
2868  MPI_Type_commit(&types[1]);
2869  len[1] = 1;
2870  MPI_Get_address(new_value + first_coef_to_recv, &offsets[1]);
2871  offsets[1] -= base_address;
2872 
2873  // column index
2874  MPI_Type_contiguous(nnz_from_proc[p], MPI_INT, &types[2]);
2875  MPI_Type_commit(&types[2]);
2876  len[2] = 1;
2877  MPI_Get_address(new_column_index + first_coef_to_recv,
2878  &offsets[2]);
2879  offsets[2] -= base_address;
2880 
2881  // build the combined datatype
2882  MPI_Datatype recv_type;
2883  MPI_Type_create_struct(3, len, offsets, types, &recv_type);
2884  MPI_Type_commit(&recv_type);
2885  MPI_Type_free(&types[0]);
2886  MPI_Type_free(&types[1]);
2887  MPI_Type_free(&types[2]);
2888 
2889  // and send
2890  MPI_Request req;
2891  MPI_Irecv(new_value,
2892  1,
2893  recv_type,
2894  p,
2895  1,
2896  this->distribution_pt()->communicator_pt()->mpi_comm(),
2897  &req);
2898  recv_req.push_back(req);
2899  MPI_Type_free(&recv_type);
2900  }
2901  }
2902  // other wise transfer data internally
2903  else
2904  {
2905  unsigned j =
2906  first_row_for_proc[my_rank] - current_first_row[my_rank];
2907  unsigned k = first_row_from_proc[my_rank] - new_first_row[my_rank];
2908  for (unsigned i = 0; i < nrow_local_for_proc[my_rank]; i++)
2909  {
2910  new_row_start[k + i] = current_row_start[j + i];
2911  }
2912  unsigned first_coef_to_send = current_row_start[j];
2913  for (unsigned i = 0; i < nnz_for_proc[my_rank]; i++)
2914  {
2915  new_value[nnz_offset[p] + i] =
2916  current_value[first_coef_to_send + i];
2917  new_column_index[nnz_offset[p] + i] =
2918  current_column_index[first_coef_to_send + i];
2919  }
2920  }
2921  }
2922 
2923  // wait for all recvs to complete
2924  n_recv_req = recv_req.size();
2925  if (n_recv_req > 0)
2926  {
2927  Vector<MPI_Status> recv_status(n_recv_req);
2928  MPI_Waitall(n_recv_req, &recv_req[0], &recv_status[0]);
2929  }
2930 
2931  // next we need to update the row starts
2932  for (int p = 0; p < nproc; p++)
2933  {
2934  if (nrow_local_from_proc[p] > 0)
2935  {
2936  unsigned first_row =
2937  first_row_from_proc[p] - new_first_row[my_rank];
2938  unsigned last_row = first_row + nrow_local_from_proc[p] - 1;
2939  int update = nnz_offset[p] - new_row_start[first_row];
2940  for (unsigned i = first_row; i <= last_row; i++)
2941  {
2942  new_row_start[i] += update;
2943  }
2944  }
2945  }
2946  new_row_start[dist_pt->nrow_local()] = nnz_count;
2947 
2948  // wait for sends to complete
2949  unsigned n_send_req = send_req.size();
2950  if (n_recv_req > 0)
2951  {
2952  Vector<MPI_Status> send_status(n_recv_req);
2953  MPI_Waitall(n_send_req, &send_req[0], &send_status[0]);
2954  }
2955  // if (my_rank == 0)
2956  // {
2957  // CRDoubleMatrix* m_pt = this->global_matrix();
2958  // m_pt->sparse_indexed_output("m1.dat");
2959  // }
2960 
2961  //
2962  this->build(dist_pt);
2963  this->build_without_copy(
2964  ncol, nnz_count, new_value, new_column_index, new_row_start);
2965  // if (my_rank == 0)
2966  // {
2967  // CRDoubleMatrix* m_pt = this->global_matrix();
2968  // m_pt->sparse_indexed_output("m2.dat");
2969  // }
2970  // this->sparse_indexed_output(oomph_info);
2971  abort();
2972  }
2973 
2974  // if this matrix is distributed but the new distributed matrix is global
2975  // ======================================================================
2976  else if (this->distributed() && !dist_pt->distributed())
2977  {
2978  // nnz
2979  int nnz = this->nnz();
2980 
2981  // nrow global
2982  unsigned nrow = this->nrow();
2983 
2984  // cache nproc
2985  int nproc = this->distribution_pt()->communicator_pt()->nproc();
2986 
2987  // get the nnzs on the other processors
2988  int* dist_nnz_pt = new int[nproc];
2989  MPI_Allgather(&nnz,
2990  1,
2991  MPI_INT,
2992  dist_nnz_pt,
2993  1,
2994  MPI_INT,
2995  this->distribution_pt()->communicator_pt()->mpi_comm());
2996 
2997  // create an int array of first rows and nrow local and
2998  // compute nnz global
2999  int* dist_first_row = new int[nproc];
3000  int* dist_nrow_local = new int[nproc];
3001  for (int p = 0; p < nproc; p++)
3002  {
3003  dist_first_row[p] = this->first_row(p);
3004  dist_nrow_local[p] = this->nrow_local(p);
3005  }
3006 
3007  // conpute the offset for the values and column index data
3008  // compute the nnz offset for each processor
3009  int next_row = 0;
3010  unsigned nnz_count = 0;
3011  Vector<unsigned> nnz_offset(nproc, 0);
3012  for (int p = 0; p < nproc; p++)
3013  {
3014  unsigned pp = 0;
3015  while (dist_first_row[pp] != next_row)
3016  {
3017  pp++;
3018  }
3019  nnz_offset[pp] = nnz_count;
3020  nnz_count += dist_nnz_pt[pp];
3021  next_row += dist_nrow_local[pp];
3022  }
3023 
3024  // get pointers to the (current) distributed data
3025  int* dist_row_start = this->row_start();
3026  int* dist_column_index = this->column_index();
3027  double* dist_value = this->value();
3028 
3029  // space for the global matrix
3030  int* global_row_start = new int[nrow + 1];
3031  int* global_column_index = new int[nnz_count];
3032  double* global_value = new double[nnz_count];
3033 
3034  // post the sends and recvs for the matrix data
3035  Vector<MPI_Request> recv_req;
3036  Vector<MPI_Request> send_req;
3037  MPI_Aint base_address;
3038  MPI_Get_address(global_value, &base_address);
3039 
3040  // SEND
3041  if (dist_nrow_local[my_rank] > 0)
3042  {
3043  // types
3044  MPI_Datatype types[3];
3045 
3046  // offsets
3047  MPI_Aint offsets[3];
3048 
3049  // lengths
3050  int len[3];
3051 
3052  // row start
3053  MPI_Type_contiguous(dist_nrow_local[my_rank], MPI_INT, &types[0]);
3054  MPI_Type_commit(&types[0]);
3055  MPI_Get_address(dist_row_start, &offsets[0]);
3056  offsets[0] -= base_address;
3057  len[0] = 1;
3058 
3059  // value
3060  MPI_Type_contiguous(nnz, MPI_DOUBLE, &types[1]);
3061  MPI_Type_commit(&types[1]);
3062  MPI_Get_address(dist_value, &offsets[1]);
3063  offsets[1] -= base_address;
3064  len[1] = 1;
3065 
3066  // column indices
3067  MPI_Type_contiguous(nnz, MPI_INT, &types[2]);
3068  MPI_Type_commit(&types[2]);
3069  MPI_Get_address(dist_column_index, &offsets[2]);
3070  offsets[2] -= base_address;
3071  len[2] = 1;
3072 
3073  // build the send type
3074  MPI_Datatype send_type;
3075  MPI_Type_create_struct(3, len, offsets, types, &send_type);
3076  MPI_Type_commit(&send_type);
3077  MPI_Type_free(&types[0]);
3078  MPI_Type_free(&types[1]);
3079  MPI_Type_free(&types[2]);
3080 
3081  // and send
3082  for (int p = 0; p < nproc; p++)
3083  {
3084  if (p != my_rank)
3085  {
3086  MPI_Request req;
3087  MPI_Isend(global_value,
3088  1,
3089  send_type,
3090  p,
3091  1,
3092  this->distribution_pt()->communicator_pt()->mpi_comm(),
3093  &req);
3094  send_req.push_back(req);
3095  }
3096  }
3097  MPI_Type_free(&send_type);
3098  }
3099 
3100  // RECV
3101  for (int p = 0; p < nproc; p++)
3102  {
3103  // communicated with other processors
3104  if (p != my_rank)
3105  {
3106  // RECV
3107  if (dist_nrow_local[p] > 0)
3108  {
3109  // types
3110  MPI_Datatype types[3];
3111 
3112  // offsets
3113  MPI_Aint offsets[3];
3114 
3115  // lengths
3116  int len[3];
3117 
3118  // row start
3119  MPI_Type_contiguous(dist_nrow_local[p], MPI_INT, &types[0]);
3120  MPI_Type_commit(&types[0]);
3121  MPI_Get_address(global_row_start + dist_first_row[p],
3122  &offsets[0]);
3123  offsets[0] -= base_address;
3124  len[0] = 1;
3125 
3126  // value
3127  MPI_Type_contiguous(dist_nnz_pt[p], MPI_DOUBLE, &types[1]);
3128  MPI_Type_commit(&types[1]);
3129  MPI_Get_address(global_value + nnz_offset[p], &offsets[1]);
3130  offsets[1] -= base_address;
3131  len[1] = 1;
3132 
3133  // column indices
3134  MPI_Type_contiguous(dist_nnz_pt[p], MPI_INT, &types[2]);
3135  MPI_Type_commit(&types[2]);
3136  MPI_Get_address(global_column_index + nnz_offset[p], &offsets[2]);
3137  offsets[2] -= base_address;
3138  len[2] = 1;
3139 
3140  // build the send type
3141  MPI_Datatype recv_type;
3142  MPI_Type_create_struct(3, len, offsets, types, &recv_type);
3143  MPI_Type_commit(&recv_type);
3144  MPI_Type_free(&types[0]);
3145  MPI_Type_free(&types[1]);
3146  MPI_Type_free(&types[2]);
3147 
3148  // and send
3149  MPI_Request req;
3150  MPI_Irecv(global_value,
3151  1,
3152  recv_type,
3153  p,
3154  1,
3155  this->distribution_pt()->communicator_pt()->mpi_comm(),
3156  &req);
3157  recv_req.push_back(req);
3158  MPI_Type_free(&recv_type);
3159  }
3160  }
3161  // otherwise send to self
3162  else
3163  {
3164  unsigned nrow_local = dist_nrow_local[my_rank];
3165  unsigned first_row = dist_first_row[my_rank];
3166  for (unsigned i = 0; i < nrow_local; i++)
3167  {
3168  global_row_start[first_row + i] = dist_row_start[i];
3169  }
3170  unsigned offset = nnz_offset[my_rank];
3171  for (int i = 0; i < nnz; i++)
3172  {
3173  global_value[offset + i] = dist_value[i];
3174  global_column_index[offset + i] = dist_column_index[i];
3175  }
3176  }
3177  }
3178 
3179  // wait for all recvs to complete
3180  unsigned n_recv_req = recv_req.size();
3181  if (n_recv_req > 0)
3182  {
3183  Vector<MPI_Status> recv_status(n_recv_req);
3184  MPI_Waitall(n_recv_req, &recv_req[0], &recv_status[0]);
3185  }
3186 
3187  // finally the last row start
3188  global_row_start[nrow] = nnz_count;
3189 
3190  // update the other row start
3191  for (int p = 0; p < nproc; p++)
3192  {
3193  for (int i = 0; i < dist_nrow_local[p]; i++)
3194  {
3195  unsigned j = dist_first_row[p] + i;
3196  global_row_start[j] += nnz_offset[p];
3197  }
3198  }
3199 
3200  // wait for sends to complete
3201  unsigned n_send_req = send_req.size();
3202  if (n_recv_req > 0)
3203  {
3204  Vector<MPI_Status> send_status(n_recv_req);
3205  MPI_Waitall(n_send_req, &send_req[0], &send_status[0]);
3206  }
3207 
3208  // rebuild the matrix
3209  LinearAlgebraDistribution* dist_pt = new LinearAlgebraDistribution(
3210  this->distribution_pt()->communicator_pt(), nrow, false);
3211  this->build(dist_pt);
3212  this->build_without_copy(
3213  ncol, nnz_count, global_value, global_column_index, global_row_start);
3214 
3215  // clean up
3216  delete dist_pt;
3217  delete[] dist_first_row;
3218  delete[] dist_nrow_local;
3219  delete[] dist_nnz_pt;
3220  }
3221 
3222  // other the matrix is not distributed but it needs to be turned
3223  // into a distributed matrix
3224  // =============================================================
3225  else if (!this->distributed() && dist_pt->distributed())
3226  {
3227  // cache the new nrow_local
3228  unsigned nrow_local = dist_pt->nrow_local();
3229 
3230  // and first_row
3231  unsigned first_row = dist_pt->first_row();
3232 
3233  // get pointers to the (current) distributed data
3234  int* global_row_start = this->row_start();
3235  int* global_column_index = this->column_index();
3236  double* global_value = this->value();
3237 
3238  // determine the number of non zeros required by this processor
3239  unsigned nnz = global_row_start[first_row + nrow_local] -
3240  global_row_start[first_row];
3241 
3242  // allocate
3243  int* dist_row_start = new int[nrow_local + 1];
3244  int* dist_column_index = new int[nnz];
3245  double* dist_value = new double[nnz];
3246 
3247  // copy
3248  int offset = global_row_start[first_row];
3249  for (unsigned i = 0; i <= nrow_local; i++)
3250  {
3251  dist_row_start[i] = global_row_start[first_row + i] - offset;
3252  }
3253  for (unsigned i = 0; i < nnz; i++)
3254  {
3255  dist_column_index[i] = global_column_index[offset + i];
3256  dist_value[i] = global_value[offset + i];
3257  }
3258 
3259  // rebuild
3260  this->build(dist_pt);
3261  this->build_without_copy(
3262  ncol, nnz, dist_value, dist_column_index, dist_row_start);
3263  }
3264  }
3265 #endif
3266  }
unsigned nrow() const
access function to the number of global rows.
Definition: linear_algebra_distribution.h:186
int my_rank() const
my rank
Definition: communicator.h:176
#define min(a, b)
Definition: datatypes.h:22

References build(), build_without_copy(), built(), column_index(), oomph::LinearAlgebraDistribution::communicator_pt(), oomph::LinearAlgebraDistribution::distributed(), oomph::DistributableLinearAlgebraObject::distributed(), oomph::DistributableLinearAlgebraObject::distribution_pt(), oomph::LinearAlgebraDistribution::first_row(), oomph::DistributableLinearAlgebraObject::first_row(), i, j, k, max, min, oomph::OomphCommunicator::my_rank(), ncol(), nnz(), oomph::OomphCommunicator::nproc(), oomph::LinearAlgebraDistribution::nrow(), nrow(), oomph::LinearAlgebraDistribution::nrow_local(), oomph::DistributableLinearAlgebraObject::nrow_local(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, p, row_start(), and value().

Referenced by oomph::CRDoubleMatrixHelpers::create_uniformly_distributed_matrix(), oomph::Problem::get_eigenproblem_matrices(), and oomph::Problem::get_jacobian().

◆ row_start() [1/2]

◆ row_start() [2/2]

const int* oomph::CRDoubleMatrix::row_start ( ) const
inline

Access to C-style row_start array (const version)

1067  {
1068  return CR_matrix.row_start();
1069  }

References CR_matrix, and oomph::CRMatrix< T >::row_start().

◆ serial_matrix_matrix_multiply_method() [1/2]

unsigned& oomph::CRDoubleMatrix::serial_matrix_matrix_multiply_method ( )
inline

Access function to Serial_matrix_matrix_multiply_method, the flag which determines the matrix matrix multiplication method used for serial matrices. Method 1: First runs through this matrix and matrix_in to find the storage requirements for result - arrays of the correct size are then allocated before performing the calculation. Minimises memory requirements but more costly. Method 2: Grows storage for values and column indices of result 'on the fly' using an array of maps. Faster but more memory intensive. Method 3: Grows storage for values and column indices of result 'on the fly' using a vector of vectors. Not particularly impressive on the platforms we tried... Method 4: Trilinos Epetra Matrix Matrix multiply. Method 5: Trilinos Epetra Matrix Matrix multiply (ML based).

1160  {
1162  }

References Serial_matrix_matrix_multiply_method.

Referenced by oomph::CRDoubleMatrixHelpers::deep_copy(), and main().

◆ serial_matrix_matrix_multiply_method() [2/2]

const unsigned& oomph::CRDoubleMatrix::serial_matrix_matrix_multiply_method ( ) const
inline

Read only access function (const version) to Serial_matrix_matrix_multiply_method, the flag which determines the matrix matrix multiplication method used for serial matrices. Method 1: First runs through this matrix and matrix_in to find the storage requirements for result - arrays of the correct size are then allocated before performing the calculation. Minimises memory requirements but more costly. Method 2: Grows storage for values and column indices of result 'on the fly' using an array of maps. Faster but more memory intensive. Method 3: Grows storage for values and column indices of result 'on the fly' using a vector of vectors. Not particularly impressive on the platforms we tried... Method 4: Trilinos Epetra Matrix Matrix multiply. Method 5: Trilinos Epetra Matrix Matrix multiply (ML based).

1182  {
1184  }

References Serial_matrix_matrix_multiply_method.

◆ sort_entries()

void oomph::CRDoubleMatrix::sort_entries ( )

Sorts the entries associated with each row of the matrix in the column index vector and the value vector into ascending order and sets up the Index_of_diagonal_entries vector

This helper function sorts the entries in the column index vector and the value vector. During the construction of the matrix the entries were most likely assigned in an arbitrary order. As a result, it cannot be assumed that the entries in the column index vector corresponding to each row of the matrix have been arranged in increasing order. During the setup an additional vector will be set up; Index_of_diagonal_entries. The i-th entry of this vector contains the index of the last entry below or on the diagonal. If there are no entries below or on the diagonal then the corresponding entry is -1. If, however, there are no entries in the row then the entry is irrelevant and is kept as the initialised value; 0.

1450  {
1451 #ifdef OOMPH_HAS_MPI
1452  // We only need to produce a warning if the matrix is distributed
1453  if (this->distributed())
1454  {
1455  // Create an ostringstream object to store the warning message
1456  std::ostringstream warning_message;
1457 
1458  // Create the warning messsage
1459  warning_message << "This method currently works for serial but "
1460  << "has not been tested with MPI!\n";
1461 
1462  // Issue the warning
1463  OomphLibWarning(warning_message.str(),
1466  }
1467 #endif
1468 
1469  // Get the number of rows in the matrix
1470  unsigned n_rows = this->nrow();
1471 
1472  // Acquire access to the value, row_start and column_index arrays from
1473  // the CR matrix. Since we do not change anything in row_start_pt we
1474  // give it the const prefix
1475  double* value_pt = this->value();
1476  int* column_index_pt = this->column_index();
1477  const int* row_start_pt = this->row_start();
1478 
1479  // Resize the Index_of_diagonal_entries vector
1480  Index_of_diagonal_entries.resize(n_rows, 0);
1481 
1482  // Vector of pairs to store the column_index of each value in the i-th row
1483  // and its corresponding matrix entry
1484  Vector<std::pair<int, double>> column_index_and_value_row_i;
1485 
1486  // Loop over the rows of the matrix
1487  for (unsigned i = 0; i < n_rows; i++)
1488  {
1489  // Find the number of nonzeros in the i-th row
1490  unsigned nnz_row_i = *(row_start_pt + i + 1) - *(row_start_pt + i);
1491 
1492  // Variable to store the start of the i-th row
1493  unsigned i_row_start = *(row_start_pt + i);
1494 
1495  // If there are no nonzeros in this row then the i-th entry of the vector
1496  // Index_of_diagonal_entries is irrelevant so we can simply let it be 0
1497  if (nnz_row_i == 0)
1498  {
1499  // Set the i-th entry
1501  }
1502  // If there are nonzeros in the i-th row
1503  else
1504  {
1505  // If there is more than one entry in the row resize the vector
1506  // column_index_and_value_row_i
1507  column_index_and_value_row_i.resize(nnz_row_i);
1508 
1509  // Loop over the entries in the row
1510  for (unsigned j = 0; j < nnz_row_i; j++)
1511  {
1512  // Assign the appropriate entries to column_index_and_value_row_i
1513  column_index_and_value_row_i[j] =
1514  std::make_pair(*(column_index_pt + i_row_start + j),
1515  *(value_pt + i_row_start + j));
1516  }
1517 
1518  // Sort the vector of pairs using the struct
1519  // CRDoubleMatrixComparisonHelper
1520  std::sort(column_index_and_value_row_i.begin(),
1521  column_index_and_value_row_i.end(),
1523 
1524  //-----------------------------------------------------------------------
1525  // Now that the entries of the i-th row have been sorted we can read
1526  // them back into value_pt and column_index_pt:
1527  //-----------------------------------------------------------------------
1528 
1529  // Create a boolean variable to indicate whether or not the i-th entry
1530  // of Index_of_diagonal_entries has been set
1531  bool is_ith_entry_set = false;
1532 
1533  // Loop over the entries in the vector column_index_and_value_row_i and
1534  // assign its entries to value_pt and column_index_pt
1535  for (unsigned j = 0; j < nnz_row_i; j++)
1536  {
1537  // Set the column index of the j-th nonzero value in the i-th row of
1538  // the matrix
1539  *(column_index_pt + i_row_start + j) =
1540  column_index_and_value_row_i[j].first;
1541 
1542  // Set the value of the j-th nonzero value in the i-th row of
1543  // the matrix
1544  *(value_pt + i_row_start + j) =
1545  column_index_and_value_row_i[j].second;
1546 
1547  // This if statement is used to set the i-th entry of the vector
1548  // Index_of_diagonal_entries if it has not yet been set
1549  if (!is_ith_entry_set)
1550  {
1551  // If the column index of the first entry in row i is greater than
1552  // the row number then the first entry must lie above the diagonal
1553  if (unsigned(*(column_index_pt + i_row_start)) > i)
1554  {
1555  // If the column index of the first entry in the row is greater
1556  // than the row number, i, then the i-th entry of
1557  // Index_of_diagonal_entries needs to be set to -1 to indicate
1558  // there are no entries below or on the diagonal
1560 
1561  // Indicate that the i-th entry of Index_of_diagonal_entries has
1562  // been set
1563  is_ith_entry_set = true;
1564  }
1565  // If there are entries below or on the diagonal
1566  else
1567  {
1568  // If there is only one entry in the row then we know that this
1569  // will be the last entry below or on the diagonal because we have
1570  // eliminated the possibility that if there is only one entry,
1571  // that it lies above the diagonal
1572  if (nnz_row_i == 1)
1573  {
1574  // Set the index of the current entry to be the value of i-th
1575  // entry of Index_of_diagonal_entries
1576  Index_of_diagonal_entries[i] = i_row_start + j;
1577 
1578  // Indicate that the i-th entry of Index_of_diagonal_entries has
1579  // been set
1580  is_ith_entry_set = true;
1581  }
1582  // It remains to now check the case that there is more than one
1583  // entry in the row. If there is more than one entry in the row
1584  // and there are entries below or on the diagonal then we have
1585  // three cases:
1586  // (1) The current entry lies on the diagonal;
1587  // (2) The current entry lies above the diagonal;
1588  // (3) The current entry lies below the diagonal;
1589  // The first case can easily be checked as done below. If the
1590  // second case occurs then we have just passed the last entry. We
1591  // know this because at least one entry lies on or below the
1592  // diagonal. If the second case it true then we need to assign the
1593  // previous entry to the vector Index_of_diagonal_entries.
1594  // Finally, we are left with case (3), which can be split into two
1595  // cases:
1596  // (3.1) The current entry lies below the diagonal but it
1597  // is not the last entry below or on the diagonal;
1598  // (3.2) The current entry lies below the diagonal and is
1599  // the last entry below or on the diagonal.
1600  // If case (3.1) holds then we can simply wait until we get to the
1601  // next entry in the row and examine that. If the next entry lies
1602  // on the diagonal then it will be swept up by case (1). If the
1603  // next entry lies above the diagonal then case (2) will sweep it
1604  // up and if neither is the case then we wait until the next entry
1605  // and so on. If, instead, case (3.2) holds then our last check
1606  // simply needs to check if the current entry is the last entry in
1607  // the row because if the last entry lies on the diagonal, case
1608  // (1) will sweep it up. If it lies above the diagonal, case (2)
1609  // will take care of it. Therefore, the only remaining case is
1610  // that it lies strictly below the diagonal and since it is the
1611  // last entry in the row it means the index of this entry needs to
1612  // be assigned to Index_of_diagonal_entries
1613 
1614  // Case (1) : The current entry lies on the diagonal
1615  else if (unsigned(*(column_index_pt + i_row_start + j)) == i)
1616  {
1617  // Set the index of the current entry to be the value of i-th
1618  // entry of Index_of_diagonal_entries
1619  Index_of_diagonal_entries[i] = i_row_start + j;
1620 
1621  // Indicate that the i-th entry of Index_of_diagonal_entries has
1622  // been set
1623  is_ith_entry_set = true;
1624  }
1625  // Case (2) : The current entry lies above the diagonal
1626  else if (unsigned(*(column_index_pt + i_row_start + j)) > i)
1627  {
1628  // Set the index of the current entry to be the value of i-th
1629  // entry of Index_of_diagonal_entries
1630  Index_of_diagonal_entries[i] = i_row_start + j - 1;
1631 
1632  // Indicate that the i-th entry of Index_of_diagonal_entries has
1633  // been set
1634  is_ith_entry_set = true;
1635  }
1636  // Case (3.2) : The current entry is the last entry in the row
1637  else if (j == nnz_row_i - 1)
1638  {
1639  // Set the index of the current entry to be the value of i-th
1640  // entry of Index_of_diagonal_entries
1641  Index_of_diagonal_entries[i] = i_row_start + j;
1642 
1643  // Indicate that the i-th entry of Index_of_diagonal_entries has
1644  // been set
1645  is_ith_entry_set = true;
1646  } // if (nnz_row_i==1) else if
1647  } // if (*(column_index_pt+i_row_start)>i)
1648  } // if (!is_ith_entry_set)
1649  } // for (unsigned j=0;j<nnz_row_i;j++)
1650  } // if (nnz_row_i==0) else
1651  } // for (unsigned i=0;i<n_rows;i++)
1652  } // End of sort_entries()
struct oomph::CRDoubleMatrix::CRDoubleMatrixComparisonHelper Comparison_struct

References column_index(), Comparison_struct, oomph::DistributableLinearAlgebraObject::distributed(), i, Index_of_diagonal_entries, j, nrow(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, row_start(), and value().

◆ sparse_indexed_output_helper()

void oomph::CRDoubleMatrix::sparse_indexed_output_helper ( std::ostream &  outfile) const
inlinevirtual

Indexed output function to print a matrix to the stream outfile as i,j,a(i,j) for a(i,j)!=0 only.

Implements oomph::Matrix< double, CRDoubleMatrix >.

1024  {
1026  }
void sparse_indexed_output_helper(std::ostream &outfile) const
Definition: matrices.h:828

References CR_matrix, and oomph::CRMatrix< T >::sparse_indexed_output_helper().

◆ sparse_indexed_output_with_offset()

void oomph::CRDoubleMatrix::sparse_indexed_output_with_offset ( std::string  filename)
inline

Indexed output function to print a matrix to a file as i,j,a(i,j) for a(i,j)!=0 only. Specify filename. This uses acual global row numbers.

1032  {
1033  // Get offset
1034  unsigned first_row = distribution_pt()->first_row();
1035 
1036  // Open file
1037  std::ofstream some_file;
1038  some_file.open(filename.c_str());
1039  unsigned n = nrow_local();
1040  for (unsigned long i = 0; i < n; i++)
1041  {
1042  for (long j = row_start()[i]; j < row_start()[i + 1]; j++)
1043  {
1044  some_file << first_row + i << " " << column_index()[j] << " "
1045  << value()[j] << std::endl;
1046  }
1047  }
1048  some_file.close();
1049  }
unsigned first_row() const
Definition: linear_algebra_distribution.h:261
string filename
Definition: MergeRestartFiles.py:39

References column_index(), oomph::DistributableLinearAlgebraObject::distribution_pt(), MergeRestartFiles::filename, oomph::LinearAlgebraDistribution::first_row(), oomph::DistributableLinearAlgebraObject::first_row(), i, j, n, oomph::DistributableLinearAlgebraObject::nrow_local(), row_start(), and value().

Referenced by oomph::SpaceTimeNavierStokesSubsidiaryPreconditioner::setup().

◆ value() [1/2]

◆ value() [2/2]

const double* oomph::CRDoubleMatrix::value ( ) const
inline

Access to C-style value array (const version)

1091  {
1092  return CR_matrix.value();
1093  }

References CR_matrix, and oomph::SparseMatrix< T, MATRIX_TYPE >::value().

Member Data Documentation

◆ Built

bool oomph::CRDoubleMatrix::Built
private

Flag to indicate whether the matrix has been built - i.e. the distribution has been setup AND the matrix has been assembled.

Referenced by build(), build_without_copy(), built(), clear(), CRDoubleMatrix(), ludecompose(), multiply(), and multiply_transpose().

◆ Comparison_struct

struct oomph::CRDoubleMatrix::CRDoubleMatrixComparisonHelper oomph::CRDoubleMatrix::Comparison_struct

Referenced by sort_entries().

◆ CR_matrix

◆ Distributed_matrix_matrix_multiply_method

unsigned oomph::CRDoubleMatrix::Distributed_matrix_matrix_multiply_method
private

Flag to determine which matrix-matrix multiplication method is used (for distributed matrices)

Referenced by distributed_matrix_matrix_multiply_method().

◆ Index_of_diagonal_entries

Vector<int> oomph::CRDoubleMatrix::Index_of_diagonal_entries
private

Vector whose i'th entry contains the index of the last entry below or on the diagonal of the i'th row of the matrix

Referenced by get_index_of_diagonal_entries(), and sort_entries().

◆ Serial_matrix_matrix_multiply_method

unsigned oomph::CRDoubleMatrix::Serial_matrix_matrix_multiply_method
private

Flag to determine which matrix-matrix multiplication method is used (for serial (or global) matrices)

Referenced by CRDoubleMatrix(), multiply(), and serial_matrix_matrix_multiply_method().


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