oomph::CRDoubleMatrixHelpers Namespace Reference

Namespace for helper functions for CRDoubleMatrices. More...

Functions

void create_uniformly_distributed_matrix (const unsigned &nrow, const unsigned &ncol, const OomphCommunicator *const comm_pt, const Vector< double > &values, const Vector< int > &column_indices, const Vector< int > &row_start, CRDoubleMatrix &matrix_out)
 
double inf_norm (const DenseMatrix< CRDoubleMatrix * > &matrix_pt)
 Compute infinity (maximum) norm of sub blocks as if it was one matrix. More...
 
double gershgorin_eigenvalue_estimate (const DenseMatrix< CRDoubleMatrix * > &matrix_pt)
 
void concatenate (const DenseMatrix< CRDoubleMatrix * > &matrix_pt, CRDoubleMatrix &result_matrix)
 
void concatenate_without_communication (const Vector< LinearAlgebraDistribution * > &row_distribution_pt, const Vector< LinearAlgebraDistribution * > &col_distribution_pt, const DenseMatrix< CRDoubleMatrix * > &matrix_pt, CRDoubleMatrix &result_matrix)
 
void concatenate_without_communication (const Vector< LinearAlgebraDistribution * > &block_distribution_pt, const DenseMatrix< CRDoubleMatrix * > &matrix_pt, CRDoubleMatrix &result_matrix)
 
void deep_copy (const CRDoubleMatrix *const in_matrix_pt, CRDoubleMatrix &out_matrix)
 Create a deep copy of the matrix pointed to by in_matrix_pt. More...
 

Detailed Description

Namespace for helper functions for CRDoubleMatrices.

Function Documentation

◆ concatenate()

void oomph::CRDoubleMatrixHelpers::concatenate ( const DenseMatrix< CRDoubleMatrix * > &  matrix_pt,
CRDoubleMatrix result_matrix 
)

Concatenate CRDoubleMatrix matrices. The in matrices are concatenated such that the block structure of the in matrices are preserved in the result matrix. Communication between processors is required. If the block structure of the sub matrices does not need to be preserved, consider using CRDoubleMatrixHelpers::concatenate_without_communication(...).

The matrix manipulation functions CRDoubleMatrixHelpers::concatenate(...) and CRDoubleMatrixHelpers::concatenate_without_communication(...) are analogous to the Vector manipulation functions DoubleVectorHelpers::concatenate(...) and DoubleVectorHelpers::concatenate_without_communication(...). Please look at the DoubleVector functions for an illustration of the differences between concatenate(...) and concatenate_without_communication(...).

Distribution of the result matrix: If the result matrix does not have a distribution built, then it will be given a uniform row distribution. Otherwise we use the existing distribution. This gives the user the ability to define their own distribution, or save computing power if a distribution has been pre-built.

NOTE: ALL the matrices pointed to by matrix_pt has to be built. This is not the case with concatenate_without_communication(...)

4351  {
4352  // The number of block rows and block columns.
4353  unsigned matrix_nrow = matrix_pt.nrow();
4354  unsigned matrix_ncol = matrix_pt.ncol();
4355 
4356  // PARANOID checks involving only the in matrices.
4357 #ifdef PARANOID
4358  // Are there matrices to concatenate?
4359  if (matrix_nrow == 0)
4360  {
4361  std::ostringstream error_message;
4362  error_message << "There are no matrices to concatenate.\n";
4363  throw OomphLibError(error_message.str(),
4366  }
4367 
4368  // Does this matrix need concatenating?
4369  if ((matrix_nrow == 1) && (matrix_ncol == 1))
4370  {
4371  std::ostringstream warning_message;
4372  warning_message << "There is only one matrix to concatenate...\n"
4373  << "This does not require concatenating...\n";
4374  OomphLibWarning(warning_message.str(),
4377  }
4378 
4379  // Are all sub matrices built?
4380  for (unsigned block_row_i = 0; block_row_i < matrix_nrow; block_row_i++)
4381  {
4382  for (unsigned block_col_i = 0; block_col_i < matrix_ncol; block_col_i++)
4383  {
4384  if (!(matrix_pt(block_row_i, block_col_i)->built()))
4385  {
4386  std::ostringstream error_message;
4387  error_message << "The sub matrix (" << block_row_i << ","
4388  << block_col_i << ")\n"
4389  << "is not built. \n";
4390  throw OomphLibError(error_message.str(),
4393  }
4394  }
4395  }
4396 
4397  // Do all dimensions of sub matrices "make sense"?
4398  // Compare the number of rows of each block matrix in a block row.
4399  for (unsigned block_row_i = 0; block_row_i < matrix_nrow; block_row_i++)
4400  {
4401  // Use the first column to compare against the rest.
4402  unsigned long current_block_nrow = matrix_pt(block_row_i, 0)->nrow();
4403 
4404  // Compare against columns 1 to matrix_ncol - 1
4405  for (unsigned block_col_i = 1; block_col_i < matrix_ncol; block_col_i++)
4406  {
4407  // Get the nrow for this sub block.
4408  unsigned long subblock_nrow =
4409  matrix_pt(block_row_i, block_col_i)->nrow();
4410 
4411  if (current_block_nrow != subblock_nrow)
4412  {
4413  std::ostringstream error_message;
4414  error_message << "The sub matrix (" << block_row_i << ","
4415  << block_col_i << ")\n"
4416  << "requires nrow = " << current_block_nrow
4417  << ", but has nrow = " << subblock_nrow << ".\n";
4418  throw OomphLibError(error_message.str(),
4421  }
4422  }
4423  }
4424 
4425  // Compare the number of columns of each block matrix in a block column.
4426  for (unsigned block_col_i = 0; block_col_i < matrix_ncol; block_col_i++)
4427  {
4428  // Use the first row to compare against the rest.
4429  unsigned long current_block_ncol = matrix_pt(0, block_col_i)->ncol();
4430 
4431  // Compare against rows 1 to matrix_nrow - 1
4432  for (unsigned block_row_i = 1; block_row_i < matrix_nrow; block_row_i++)
4433  {
4434  // Get the ncol for this sub block.
4435  unsigned long subblock_ncol =
4436  matrix_pt(block_row_i, block_col_i)->ncol();
4437 
4438  if (current_block_ncol != subblock_ncol)
4439  {
4440  std::ostringstream error_message;
4441  error_message << "The sub matrix (" << block_row_i << ","
4442  << block_col_i << ")\n"
4443  << "requires ncol = " << current_block_ncol
4444  << ", but has ncol = " << subblock_ncol << ".\n";
4445  throw OomphLibError(error_message.str(),
4448  }
4449  }
4450  }
4451 #endif
4452 
4453  // The communicator pointer from block (0,0)
4454  const OomphCommunicator* const comm_pt =
4455  matrix_pt(0, 0)->distribution_pt()->communicator_pt();
4456 
4457  // Check if the block (0,0) is distributed or not.
4458  bool distributed = matrix_pt(0, 0)->distributed();
4459 
4460  // If the result matrix does not have a distribution, we create a uniform
4461  // distribution.
4462  if (!result_matrix.distribution_pt()->built())
4463  {
4464  // Sum of sub matrix nrow. We use the first column.
4465  unsigned tmp_nrow = 0;
4466  for (unsigned block_row_i = 0; block_row_i < matrix_nrow; block_row_i++)
4467  {
4468  tmp_nrow += matrix_pt(block_row_i, 0)->nrow();
4469  }
4470 
4471  LinearAlgebraDistribution tmp_distribution(
4472  comm_pt, tmp_nrow, distributed);
4473 
4474  result_matrix.build(&tmp_distribution);
4475  }
4476  else
4477  // A distribution is supplied for the result matrix.
4478  {
4479 #ifdef PARANOID
4480  // Check if the sum of the nrow from the sub matrices is the same as the
4481  // the nrow from the result matrix.
4482 
4483  // Sum of sub matrix nrow. We use the first column.
4484  unsigned tmp_nrow = 0;
4485  for (unsigned block_row_i = 0; block_row_i < matrix_nrow; block_row_i++)
4486  {
4487  tmp_nrow += matrix_pt(block_row_i, 0)->nrow();
4488  }
4489 
4490  if (tmp_nrow != result_matrix.nrow())
4491  {
4492  std::ostringstream error_message;
4493  error_message << "The total number of rows from the matrices to\n"
4494  << "concatenate does not match the nrow from the\n"
4495  << "result matrix\n";
4496  throw OomphLibError(error_message.str(),
4499  }
4500 #endif
4501  }
4502 
4503 #ifdef PARANOID
4504 
4505  // Are all the communicators the same?
4506  // Compare the communicator for sub matrices (against the result matrix).
4507  {
4508  const OomphCommunicator communicator =
4509  *(result_matrix.distribution_pt()->communicator_pt());
4510 
4511  // Are all communicator pointers the same?
4512  for (unsigned block_row_i = 0; block_row_i < matrix_nrow; block_row_i++)
4513  {
4514  for (unsigned block_col_i = 0; block_col_i < matrix_ncol;
4515  block_col_i++)
4516  {
4517  const OomphCommunicator another_communicator =
4518  *(matrix_pt(block_row_i, block_col_i)
4519  ->distribution_pt()
4520  ->communicator_pt());
4521 
4522  if (!(communicator == another_communicator))
4523  {
4524  std::ostringstream error_message;
4525  error_message << "The OomphCommunicator of the sub matrix ("
4526  << block_row_i << "," << block_col_i << ")\n"
4527  << "does not have the same communicator as the "
4528  "result matrix. \n";
4529  throw OomphLibError(error_message.str(),
4532  }
4533  }
4534  }
4535  }
4536 
4537  // Are all the distributed boolean the same? This only applies if we have
4538  // more than one processor. If there is only one processor, then it does
4539  // not matter if it is distributed or not - they are conceptually the
4540  // same.
4541  if (comm_pt->nproc() != 1)
4542  {
4543  // Compare distributed for sub matrices (against the result matrix).
4544  const bool res_distributed = result_matrix.distributed();
4545 
4546  // Loop over all sub blocks.
4547  for (unsigned block_row_i = 0; block_row_i < matrix_nrow; block_row_i++)
4548  {
4549  for (unsigned block_col_i = 0; block_col_i < matrix_ncol;
4550  block_col_i++)
4551  {
4552  const bool another_distributed =
4553  matrix_pt(block_row_i, block_col_i)->distributed();
4554 
4555  if (res_distributed != another_distributed)
4556  {
4557  std::ostringstream error_message;
4558  error_message << "The distributed boolean of the sub matrix ("
4559  << block_row_i << "," << block_col_i << ")\n"
4560  << "is not the same as the result matrix. \n";
4561  throw OomphLibError(error_message.str(),
4564  }
4565  }
4566  }
4567  }
4568 #endif
4569 
4570 
4571  // Get the number of columns up to each block for offset
4572  // in calculating the result column indices.
4573  // Since the number of columns in each block column is the same,
4574  // we only loop through the first block row (row zero).
4575  Vector<unsigned long> sum_of_ncol_up_to_block(matrix_ncol);
4576 
4577  // Also compute the total number of columns to build the resulting matrix.
4578  unsigned long res_ncol = 0;
4579 
4580  for (unsigned block_col_i = 0; block_col_i < matrix_ncol; block_col_i++)
4581  {
4582  sum_of_ncol_up_to_block[block_col_i] = res_ncol;
4583  res_ncol += matrix_pt(0, block_col_i)->ncol();
4584  }
4585 
4586  // We begin the process of extracting and ordering the values,
4587  // column_indices and row_start of all the sub blocks.
4588  if ((comm_pt->nproc() == 1) || !distributed)
4589  // Serial version of the code.
4590  {
4591  // Get the total number of non zero entries so we can reserve storage
4592  // for the values and column_indices vectors.
4593  unsigned long res_nnz = 0;
4594  for (unsigned block_row_i = 0; block_row_i < matrix_nrow; block_row_i++)
4595  {
4596  for (unsigned block_col_i = 0; block_col_i < matrix_ncol;
4597  block_col_i++)
4598  {
4599  res_nnz += matrix_pt(block_row_i, block_col_i)->nnz();
4600  }
4601  }
4602 
4603  // Declare the vectors required to build a CRDoubleMatrix
4604  Vector<double> res_values;
4605  Vector<int> res_column_indices;
4606  Vector<int> res_row_start;
4607 
4608  // Reserve space for the vectors.
4609  res_values.reserve(res_nnz);
4610  res_column_indices.reserve(res_nnz);
4611  res_row_start.reserve(result_matrix.nrow() + 1);
4612 
4613  // Now we fill in the data.
4614 
4615  // Running sum of nnz per row.
4616  int nnz_running_sum = 0;
4617 
4618  // Loop through the block rows.
4619  for (unsigned block_row_i = 0; block_row_i < matrix_nrow; block_row_i++)
4620  {
4621  // Get the number of rows in this block row, from the first block.
4622  unsigned long block_row_nrow = matrix_pt(block_row_i, 0)->nrow();
4623 
4624  // Loop through the number of rows in this block row
4625  for (unsigned row_i = 0; row_i < block_row_nrow; row_i++)
4626  {
4627  // The row start is the nnz at the start of the row.
4628  res_row_start.push_back(nnz_running_sum);
4629 
4630  // Loop through the block columns
4631  for (unsigned block_col_i = 0; block_col_i < matrix_ncol;
4632  block_col_i++)
4633  {
4634  // Get the current block.
4635  CRDoubleMatrix* current_block_pt =
4636  matrix_pt(block_row_i, block_col_i);
4637 
4638  // Get the values, column_indices and row_start for this block.
4639  double* current_block_values = current_block_pt->value();
4640  int* current_block_column_indices =
4641  current_block_pt->column_index();
4642  int* current_block_row_start = current_block_pt->row_start();
4643 
4644  for (int val_i = current_block_row_start[row_i];
4645  val_i < current_block_row_start[row_i + 1];
4646  val_i++)
4647  {
4648  res_values.push_back(current_block_values[val_i]);
4649  res_column_indices.push_back(
4650  current_block_column_indices[val_i] +
4651  sum_of_ncol_up_to_block[block_col_i]);
4652  }
4653 
4654  // Update the running sum of nnz per row
4655  nnz_running_sum += current_block_row_start[row_i + 1] -
4656  current_block_row_start[row_i];
4657  } // for block cols
4658  } // for rows
4659  } // for block rows
4660 
4661  // Fill in the last row start
4662  res_row_start.push_back(res_nnz);
4663 
4664  // Build the matrix
4665  result_matrix.build(
4666  res_ncol, res_values, res_column_indices, res_row_start);
4667  }
4668  // Otherwise we are dealing with a distributed matrix.
4669  else
4670  {
4671 #ifdef OOMPH_HAS_MPI
4672 
4673  // Flag to enable timing. This is for debugging
4674  // and/or testing purposes only.
4675  bool enable_timing = false;
4676 
4677  // Get the number of processors
4678  unsigned nproc = comm_pt->nproc();
4679 
4680  // My rank
4681  unsigned my_rank = comm_pt->my_rank();
4682 
4683  // Storage for the data (per processor) to send.
4684  Vector<Vector<unsigned>> column_indices_to_send(nproc);
4685  Vector<Vector<double>> values_to_send(nproc);
4686 
4687  // The sum of the nrow for the sub blocks (so far). This is used as an
4688  // offset to calculate the global equation number in the result matrix.
4689  unsigned long sum_of_block_nrow = 0;
4690 
4691  double t_prep_data_start;
4692  if (enable_timing)
4693  {
4694  t_prep_data_start = TimingHelpers::timer();
4695  }
4696 
4697  // Get the pointer to the result distribution, for convenience...
4698  LinearAlgebraDistribution* res_distribution_pt =
4699  result_matrix.distribution_pt();
4700 
4701  // loop over the sub blocks to calculate the global_eqn, get the values
4702  // and column indices.
4703  for (unsigned block_row_i = 0; block_row_i < matrix_nrow; block_row_i++)
4704  {
4705  // Get the number of local rows in this block_row from the first
4706  // block.
4707  unsigned current_block_nrow_local =
4708  matrix_pt(block_row_i, 0)->nrow_local();
4709 
4710  // Get the first_row for this block_row
4711  unsigned current_block_row_first_row =
4712  matrix_pt(block_row_i, 0)->first_row();
4713 
4714  // Loop through the number of local rows
4715  for (unsigned sub_local_eqn = 0;
4716  sub_local_eqn < current_block_nrow_local;
4717  sub_local_eqn++)
4718  {
4719  // Calculate the corresponding (res_global_eqn) equation number
4720  // for this local row number in this block.
4721  unsigned long res_global_eqn =
4722  sub_local_eqn + current_block_row_first_row + sum_of_block_nrow;
4723 
4724  // Get the processor that this global row belongs to.
4725  // The rank_of_global_row(...) function loops through all the
4726  // processors and does two unsigned comparisons. Since we have to do
4727  // this for every row, it may be better to store a list mapping for
4728  // very large number of processors.
4729  unsigned res_p =
4730  res_distribution_pt->rank_of_global_row(res_global_eqn);
4731 
4732  // With the res_p, we get the res_first_row to
4733  // work out the res_local_eqn
4734  unsigned res_first_row = res_distribution_pt->first_row(res_p);
4735  unsigned res_local_eqn = res_global_eqn - res_first_row;
4736 
4737  // Loop through the block columns, calculate the nnz. This is used
4738  // to reserve space for the value and column_indices Vectors.
4739  unsigned long current_row_nnz = 0;
4740  for (unsigned block_col_i = 0; block_col_i < matrix_ncol;
4741  block_col_i++)
4742  {
4743  // Get the row_start
4744  int* current_block_row_start =
4745  matrix_pt(block_row_i, block_col_i)->row_start();
4746 
4747  // Update the nnz for this row.
4748  current_row_nnz += current_block_row_start[sub_local_eqn + 1] -
4749  current_block_row_start[sub_local_eqn];
4750  } // for block column, get nnz.
4751 
4752  // Reserve space for efficiency.
4753  // unsigned capacity_in_res_p_vec
4754  // = column_indices_to_send[res_p].capacity();
4755 
4756  // Reserve memory for nnz+2, since we need to store the
4757  // res_local_eqn and nnz as well as the data (values/column
4758  // indices). Note: The two reserve functions are called per row. If
4759  // the matrix is very sparse (just a few elements per row), it will
4760  // be more efficient to not reserve and let the STL vector handle
4761  // this. On average, this implementation is more efficient.
4762  // column_indices_to_send[res_p].reserve(capacity_in_res_p_vec
4763  // + current_row_nnz+2);
4764  // values_to_send[res_p].reserve(capacity_in_res_p_vec
4765  // + current_row_nnz+2);
4766 
4767  // Push back the res_local_eqn and nnz
4768  column_indices_to_send[res_p].push_back(res_local_eqn);
4769  column_indices_to_send[res_p].push_back(current_row_nnz);
4770  values_to_send[res_p].push_back(res_local_eqn);
4771  values_to_send[res_p].push_back(current_row_nnz);
4772 
4773  // Loop through the block columns again and get the values
4774  // and column_indices
4775  for (unsigned block_col_i = 0; block_col_i < matrix_ncol;
4776  block_col_i++)
4777  {
4778  // Cache the pointer to the current block for convenience.
4779  CRDoubleMatrix* current_block_pt =
4780  matrix_pt(block_row_i, block_col_i);
4781 
4782  // Values, column indices and row_start for the current block.
4783  double* current_block_values = current_block_pt->value();
4784  int* current_block_column_indices =
4785  current_block_pt->column_index();
4786  int* current_block_row_start = current_block_pt->row_start();
4787 
4788  // Loop though the values and column_indices
4789  for (int val_i = current_block_row_start[sub_local_eqn];
4790  val_i < current_block_row_start[sub_local_eqn + 1];
4791  val_i++)
4792  {
4793  // Push back the value.
4794  values_to_send[res_p].push_back(current_block_values[val_i]);
4795 
4796  // Push back the (offset) column index.
4797  column_indices_to_send[res_p].push_back(
4798  current_block_column_indices[val_i] +
4799  sum_of_ncol_up_to_block[block_col_i]);
4800  } // for block columns
4801  } // for block column, get values and column_indices.
4802  } // for sub_local_eqn
4803 
4804  // update the sum_of_block_nrow
4805  sum_of_block_nrow += matrix_pt(block_row_i, 0)->nrow();
4806 
4807  } // for block_row
4808 
4809  if (enable_timing)
4810  {
4811  double t_prep_data_finish = TimingHelpers::timer();
4812  double t_prep_data_time = t_prep_data_finish - t_prep_data_start;
4813  oomph_info << "Time for prep data: " << t_prep_data_time << std::endl;
4814  }
4815 
4816  // Prepare to send data!
4817 
4818  // Storage for the number of data to be sent to each processor.
4819  Vector<int> send_n(nproc, 0);
4820 
4821  // Storage for all the values/column indices to be sent
4822  // to each processor.
4823  Vector<double> send_values_data;
4824  Vector<unsigned> send_column_indices_data;
4825 
4826  // Storage location within send_values_data
4827  // (and send_column_indices_data) for data to be sent to each processor.
4828  Vector<int> send_displacement(nproc, 0);
4829 
4830  double t_total_ndata_start;
4831  if (enable_timing) t_total_ndata_start = TimingHelpers::timer();
4832 
4833  // Get the total amount of data which needs to be sent, so we can
4834  // reserve space for it.
4835  unsigned total_ndata = 0;
4836  for (unsigned rank = 0; rank < nproc; rank++)
4837  {
4838  if (rank != my_rank)
4839  {
4840  total_ndata += values_to_send[rank].size();
4841  }
4842  }
4843 
4844  if (enable_timing)
4845  {
4846  double t_total_ndata_finish = TimingHelpers::timer();
4847  double t_total_ndata_time =
4848  t_total_ndata_finish - t_total_ndata_start;
4849  oomph_info << "Time for total_ndata: " << t_total_ndata_time
4850  << std::endl;
4851  }
4852 
4853  double t_flat_pack_start;
4854  if (enable_timing) t_flat_pack_start = TimingHelpers::timer();
4855 
4856  // Now we don't have to re-allocate data/memory when push_back is
4857  // called. Nb. Using push_back without reserving memory may cause
4858  // multiple re-allocation behind the scenes, this is expensive.
4859  send_values_data.reserve(total_ndata);
4860  send_column_indices_data.reserve(total_ndata);
4861 
4862  // Loop over all the processors to "flat pack" the data for sending.
4863  for (unsigned rank = 0; rank < nproc; rank++)
4864  {
4865  // Set the offset for the current processor
4866  // This only has to be done once for both values and column indices.
4867  send_displacement[rank] = send_values_data.size();
4868 
4869  // Don't bother to do anything if
4870  // the processor in the loop is the current processor.
4871  if (rank != my_rank)
4872  {
4873  // Put the values into the send data vector.
4874  // n_data is the same for both values and column indices.
4875  unsigned n_data = values_to_send[rank].size();
4876  for (unsigned j = 0; j < n_data; j++)
4877  {
4878  send_values_data.push_back(values_to_send[rank][j]);
4879  send_column_indices_data.push_back(
4880  column_indices_to_send[rank][j]);
4881  } // for
4882  } // if rank != my_rank
4883 
4884  // Find the number of data to be added to the vector.
4885  // send_n is the same for both values and column indices.
4886  send_n[rank] = send_values_data.size() - send_displacement[rank];
4887  } // loop over processors
4888 
4889  if (enable_timing)
4890  {
4891  double t_flat_pack_finish = TimingHelpers::timer();
4892  double t_flat_pack_time = t_flat_pack_finish - t_flat_pack_start;
4893  oomph_info << "t_flat_pack_time: " << t_flat_pack_time << std::endl;
4894  }
4895 
4896  double t_sendn_start;
4897  if (enable_timing) t_sendn_start = TimingHelpers::timer();
4898 
4899  // Strorage for the number of data to be received from each processor
4900  Vector<int> receive_n(nproc, 0);
4901 
4902  MPI_Alltoall(&send_n[0],
4903  1,
4904  MPI_INT,
4905  &receive_n[0],
4906  1,
4907  MPI_INT,
4908  comm_pt->mpi_comm());
4909 
4910  if (enable_timing)
4911  {
4912  double t_sendn_finish = TimingHelpers::timer();
4913  double t_sendn_time = t_sendn_finish - t_sendn_start;
4914  oomph_info << "t_sendn_time: " << t_sendn_time << std::endl;
4915  }
4916 
4917 
4918  // Prepare the data to be received
4919  // by working out the displacement from the received data
4920  // receive_displacement is the same for both values and column indices.
4921  Vector<int> receive_displacement(nproc, 0);
4922  int receive_data_count = 0;
4923  for (unsigned rank = 0; rank < nproc; rank++)
4924  {
4925  receive_displacement[rank] = receive_data_count;
4926  receive_data_count += receive_n[rank];
4927  }
4928 
4929  // Now resize the receive buffer for all data from all processors.
4930  // Make sure that it has a size of at least one.
4931  if (receive_data_count == 0)
4932  {
4933  receive_data_count++;
4934  }
4935  Vector<double> receive_values_data(receive_data_count);
4936  Vector<unsigned> receive_column_indices_data(receive_data_count);
4937 
4938  // Make sure that the send buffer has size at least one
4939  // so that we don't get a segmentation fault.
4940  if (send_values_data.size() == 0)
4941  {
4942  send_values_data.resize(1);
4943  }
4944 
4945  double t_send_data_start;
4946  if (enable_timing) t_send_data_start = TimingHelpers::timer();
4947 
4948  // Now send the data between all processors
4949  MPI_Alltoallv(&send_values_data[0],
4950  &send_n[0],
4951  &send_displacement[0],
4952  MPI_DOUBLE,
4953  &receive_values_data[0],
4954  &receive_n[0],
4955  &receive_displacement[0],
4956  MPI_DOUBLE,
4957  comm_pt->mpi_comm());
4958 
4959  // Now send the data between all processors
4960  MPI_Alltoallv(&send_column_indices_data[0],
4961  &send_n[0],
4962  &send_displacement[0],
4963  MPI_UNSIGNED,
4964  &receive_column_indices_data[0],
4965  &receive_n[0],
4966  &receive_displacement[0],
4967  MPI_UNSIGNED,
4968  comm_pt->mpi_comm());
4969 
4970  if (enable_timing)
4971  {
4972  double t_send_data_finish = TimingHelpers::timer();
4973  double t_send_data_time = t_send_data_finish - t_send_data_start;
4974  oomph_info << "t_send_data_time: " << t_send_data_time << std::endl;
4975  }
4976 
4977  // All the rows for this processor are stored in:
4978  // from other processors:
4979  // receive_column_indices_data and receive_values_data
4980  // from this processor:
4981  // column_indices_to_send[my_rank] and values_to_send[my_rank]
4982  //
4983  // They are in some order determined by the distribution.
4984  // We need to re-arrange them. To do this, we do some pre-processing.
4985 
4986  // nrow_local for this processor.
4987  unsigned res_nrow_local = res_distribution_pt->nrow_local();
4988 
4989  // Per row, store:
4990  // 1) where this row came from, 0 - this proc, 1 - other procs.
4991  // 2) the nnz,
4992  // 3) the offset - where the values/columns in the receive data vectors
4993  // begins. This is different from the offset of where
4994  // the data from a certain processor starts.
4995  Vector<Vector<unsigned>> value_column_locations(res_nrow_local,
4996  Vector<unsigned>(3, 0));
4997 
4998  // Store the local nnz so we can reserve space for
4999  // the values and column indices.
5000  unsigned long res_nnz_local = 0;
5001 
5002  double t_locations_start;
5003  if (enable_timing) t_locations_start = TimingHelpers::timer();
5004 
5005  // Loop through the data currently on this processor.
5006  unsigned location_i = 0;
5007  unsigned my_column_indices_to_send_size =
5008  column_indices_to_send[my_rank].size();
5009  while (location_i < my_column_indices_to_send_size)
5010  {
5011  unsigned current_local_eqn =
5012  column_indices_to_send[my_rank][location_i++];
5013 
5014  unsigned current_nnz = column_indices_to_send[my_rank][location_i++];
5015 
5016  // No need to fill [*][0] with 0 since it is already initialised to 0.
5017 
5018  // Store the nnz.
5019  value_column_locations[current_local_eqn][1] = current_nnz;
5020 
5021  // Also increment the res_local_nnz
5022  res_nnz_local += current_nnz;
5023 
5024  // Store the offset.
5025  value_column_locations[current_local_eqn][2] = location_i;
5026 
5027  // Update the location_i so it starts at the next row.
5028  location_i += current_nnz;
5029  }
5030 
5031  // Loop through the data from different processors.
5032 
5033  // Check to see if data has been received.
5034  bool data_has_been_received = false;
5035  unsigned send_rank = 0;
5036  while (send_rank < nproc)
5037  {
5038  if (receive_n[send_rank] > 0)
5039  {
5040  data_has_been_received = true;
5041  break;
5042  }
5043  send_rank++;
5044  }
5045 
5046  location_i = 0; // start at 0.
5047  if (data_has_been_received)
5048  {
5049  unsigned receive_column_indices_data_size =
5050  receive_column_indices_data.size();
5051  while (location_i < receive_column_indices_data_size)
5052  {
5053  unsigned current_local_eqn =
5054  receive_column_indices_data[location_i++];
5055  unsigned current_nnz = receive_column_indices_data[location_i++];
5056 
5057  // These comes from other processors.
5058  value_column_locations[current_local_eqn][0] = 1;
5059 
5060  // Store the nnz.
5061  value_column_locations[current_local_eqn][1] = current_nnz;
5062 
5063  // Also increment the res_local_nnz
5064  res_nnz_local += current_nnz;
5065 
5066  // Store the offset.
5067  value_column_locations[current_local_eqn][2] = location_i;
5068 
5069  // Update the location_i so it starts at the next row.
5070  location_i += current_nnz;
5071  }
5072  }
5073 
5074  if (enable_timing)
5075  {
5076  double t_locations_finish = TimingHelpers::timer();
5077  double t_locations_time = t_locations_finish - t_locations_start;
5078  oomph_info << "t_locations_time: " << t_locations_time << std::endl;
5079  }
5080 
5081  double t_fillvecs_start;
5082  if (enable_timing) t_fillvecs_start = TimingHelpers::timer();
5083 
5084  // Now loop through the locations and store the values
5085  // the column indices in the correct order.
5086  Vector<int> res_column_indices;
5087  Vector<double> res_values;
5088  Vector<int> res_row_start;
5089 
5090  res_column_indices.reserve(res_nnz_local);
5091  res_values.reserve(res_nnz_local);
5092  res_row_start.reserve(res_nrow_local + 1);
5093 
5094  // Running sum of nnz for the row_start. Must be int because
5095  // res_row_start is templated with int.
5096  int nnz_running_sum = 0;
5097 
5098  // Now insert the rows.
5099  for (unsigned local_row_i = 0; local_row_i < res_nrow_local;
5100  local_row_i++)
5101  {
5102  // Fill the res_row_start with the nnz so far.
5103  res_row_start.push_back(nnz_running_sum);
5104 
5105  bool data_is_from_other_proc =
5106  bool(value_column_locations[local_row_i][0]);
5107 
5108  unsigned row_i_nnz = value_column_locations[local_row_i][1];
5109 
5110  unsigned row_i_offset = value_column_locations[local_row_i][2];
5111 
5112  if (data_is_from_other_proc)
5113  {
5114  // Insert range [offset, offset+nnz) from
5115  // receive_column_indices_data and receive_values_data into
5116  // res_column_indices and res_values respectively.
5117  res_column_indices.insert(
5118  res_column_indices.end(),
5119  receive_column_indices_data.begin() + row_i_offset,
5120  receive_column_indices_data.begin() + row_i_offset + row_i_nnz);
5121 
5122  res_values.insert(res_values.end(),
5123  receive_values_data.begin() + row_i_offset,
5124  receive_values_data.begin() + row_i_offset +
5125  row_i_nnz);
5126  }
5127  else
5128  {
5129  res_column_indices.insert(res_column_indices.end(),
5130  column_indices_to_send[my_rank].begin() +
5131  row_i_offset,
5132  column_indices_to_send[my_rank].begin() +
5133  row_i_offset + row_i_nnz);
5134 
5135  res_values.insert(res_values.end(),
5136  values_to_send[my_rank].begin() + row_i_offset,
5137  values_to_send[my_rank].begin() + row_i_offset +
5138  row_i_nnz);
5139  }
5140 
5141  // Update the running sum of nnz
5142  nnz_running_sum += row_i_nnz;
5143  }
5144 
5145  // Insert the last row_start value
5146  res_row_start.push_back(res_nnz_local);
5147 
5148  if (enable_timing)
5149  {
5150  double t_fillvecs_finish = TimingHelpers::timer();
5151  double t_fillvecs_time = t_fillvecs_finish - t_fillvecs_start;
5152  oomph_info << "t_fillvecs_time: " << t_fillvecs_time << std::endl;
5153  }
5154 
5155  double t_buildres_start;
5156  if (enable_timing) t_buildres_start = TimingHelpers::timer();
5157 
5158  // build the matrix.
5159  result_matrix.build(
5160  res_ncol, res_values, res_column_indices, res_row_start);
5161 
5162  if (enable_timing)
5163  {
5164  double t_buildres_finish = TimingHelpers::timer();
5165  double t_buildres_time = t_buildres_finish - t_buildres_start;
5166  oomph_info << "t_buildres_time: " << t_buildres_time << std::endl;
5167  }
5168  // */
5169 #endif
5170  }
5171  }
OomphInfo oomph_info
Definition: oomph_definitions.cc:319
#define OOMPH_EXCEPTION_LOCATION
Definition: oomph_definitions.h:61
#define OOMPH_CURRENT_FUNCTION
Definition: oomph_definitions.h:86
double timer
Definition: oomph_metis_from_parmetis_3.1.1/struct.h:210
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2

References oomph::CRDoubleMatrix::build(), oomph::LinearAlgebraDistribution::built(), oomph::CRDoubleMatrix::column_index(), oomph::LinearAlgebraDistribution::communicator_pt(), oomph::DistributableLinearAlgebraObject::distributed(), oomph::DistributableLinearAlgebraObject::distribution_pt(), oomph::LinearAlgebraDistribution::first_row(), j, oomph::OomphCommunicator::my_rank(), oomph::DenseMatrix< T >::ncol(), oomph::OomphCommunicator::nproc(), oomph::DenseMatrix< T >::nrow(), oomph::CRDoubleMatrix::nrow(), oomph::LinearAlgebraDistribution::nrow_local(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, oomph::oomph_info, oomph::LinearAlgebraDistribution::rank_of_global_row(), oomph::CRDoubleMatrix::row_start(), oomph::TimingHelpers::timer(), and oomph::CRDoubleMatrix::value().

Referenced by main(), and oomph::SpaceTimeNavierStokesSubsidiaryPreconditioner::setup().

◆ concatenate_without_communication() [1/2]

void oomph::CRDoubleMatrixHelpers::concatenate_without_communication ( const Vector< LinearAlgebraDistribution * > &  block_distribution_pt,
const DenseMatrix< CRDoubleMatrix * > &  matrix_pt,
CRDoubleMatrix result_matrix 
)

Concatenate CRDoubleMatrix matrices. This calls the other concatenate_without_communication(...) function, passing block_distribution_pt as both the row_distribution_pt and col_distribution_pt. This should only be called for block square matrices.

5889  {
5890 #ifdef PARANOID
5891  // The number of block rows and block columns.
5892  unsigned matrix_nrow = matrix_pt.nrow();
5893  unsigned matrix_ncol = matrix_pt.ncol();
5894 
5895  // Are there matrices to concatenate?
5896  if (matrix_nrow == 0)
5897  {
5898  std::ostringstream error_message;
5899  error_message << "There are no matrices to concatenate.\n";
5900  throw OomphLibError(error_message.str(),
5903  }
5904 
5905  // Ensure that the sub matrices is a square block matrix.
5906  if (matrix_nrow != matrix_ncol)
5907  {
5908  std::ostringstream error_message;
5909  error_message
5910  << "The number of block rows and block columns\n"
5911  << "must be the same. Otherwise, call the other\n"
5912  << "concatenate_without_communication function, passing in\n"
5913  << "a Vector of distributions describing how to permute the\n"
5914  << "columns.";
5915  throw OomphLibError(error_message.str(),
5918  }
5919 #endif
5920 
5922  block_distribution_pt, block_distribution_pt, matrix_pt, result_matrix);
5923  }
void concatenate_without_communication(const Vector< LinearAlgebraDistribution * > &block_distribution_pt, const DenseMatrix< CRDoubleMatrix * > &matrix_pt, CRDoubleMatrix &result_matrix)
Definition: matrices.cc:5885

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

◆ concatenate_without_communication() [2/2]

void oomph::CRDoubleMatrixHelpers::concatenate_without_communication ( const Vector< LinearAlgebraDistribution * > &  row_distribution_pt,
const Vector< LinearAlgebraDistribution * > &  col_distribution_pt,
const DenseMatrix< CRDoubleMatrix * > &  matrix_pt,
CRDoubleMatrix result_matrix 
)

Concatenate CRDoubleMatrix matrices.

The Vector row_distribution_pt contains the LinearAlgebraDistribution of each block row. The Vector col_distribution_pt contains the LinearAlgebraDistribution of each block column. The DenseMatrix matrix_pt contains pointers to the CRDoubleMatrices to concatenate. The CRDoubleMatrix result_matrix is the result matrix.

The result matrix is a permutation of the sub matrices such that the data stays on the same processor when the result matrix is built, there is no communication between processors. Thus the block structure of the sub matrices are NOT preserved in the result matrix. The rows are block-permuted, defined by the concatenation of the distributions in row_distribution_pt. Similarly, the columns are block-permuted, defined by the concatenation of the distributions in col_distribution_pt. For more details on the block-permutation, see LinearAlgebraDistributionHelpers::concatenate(...).

If one wishes to preserve the block structure of the sub matrices in the result matrix, consider using CRDoubleMatrixHelpers::concatenate(...), which uses communication between processors to ensure that the block structure of the sub matrices are preserved.

The matrix manipulation functions CRDoubleMatrixHelpers::concatenate(...) and CRDoubleMatrixHelpers::concatenate_without_communication(...) are analogous to the Vector manipulation functions DoubleVectorHelpers::concatenate(...) and DoubleVectorHelpers::concatenate_without_communication(...). Please look at the DoubleVector functions for an illustration of the differences between concatenate(...) and concatenate_without_communication(...).

Distribution of the result matrix: If the result matrix does not have a distribution built, then it will be given a distribution built from the concatenation of the distributions from row_distribution_pt, see LinearAlgebraDistributionHelpers::concatenate(...) for more detail. Otherwise we use the existing distribution. If there is an existing distribution then it must be the same as the distribution from the concatenation of row distributions as described above. Why don't we always compute the distribution "on the fly"? Because a non-uniform distribution requires communication. All block preconditioner distributions are concatenations of the distributions of the individual blocks.

/////////////// END OF PARANOID TESTS ////////////////////////////////////

5228  {
5229  // The number of block rows and block columns.
5230  unsigned matrix_nrow = matrix_pt.nrow();
5231  unsigned matrix_ncol = matrix_pt.ncol();
5232 
5233  // PARANOID checks involving in matrices and block_distribution only.
5234  // PARANOID checks involving the result matrix will come later since
5235  // we have to create the result matrix distribution from the in
5236  // distribution if it does not already exist.
5237 #ifdef PARANOID
5238 
5239  // Are there matrices to concatenate?
5240  if (matrix_nrow == 0 || matrix_ncol == 0)
5241  {
5242  std::ostringstream error_message;
5243  error_message << "There are no matrices to concatenate.\n";
5244  throw OomphLibError(error_message.str(),
5247  }
5248 
5249  // Does this matrix need concatenating?
5250  if ((matrix_nrow == 1) && (matrix_ncol == 1))
5251  {
5252  std::ostringstream warning_message;
5253  warning_message << "There is only one matrix to concatenate...\n"
5254  << "This does not require concatenating...\n";
5255  OomphLibWarning(warning_message.str(),
5258  }
5259 
5260 
5261  // The distribution for each block row is stored in row_distribution_pt.
5262  // So the number of distributions in row_distribution_pt must be the
5263  // same as matrix_nrow.
5264  if (matrix_nrow != row_distribution_pt.size())
5265  {
5266  std::ostringstream error_message;
5267  error_message << "The number of row distributions must be the same as\n"
5268  << "the number of block rows.";
5269  throw OomphLibError(error_message.str(),
5272  }
5273 
5274  // The number of distributions for the columns must match the number of
5275  // block columns.
5276  if (matrix_ncol != col_distribution_pt.size())
5277  {
5278  std::ostringstream error_message;
5279  error_message
5280  << "The number of column distributions must be the same as\n"
5281  << "the number of block columns.";
5282  throw OomphLibError(error_message.str(),
5285  }
5286 
5287  // Check that all pointers in row_distribution_pt is not null.
5288  for (unsigned block_row_i = 0; block_row_i < matrix_nrow; block_row_i++)
5289  {
5290  if (row_distribution_pt[block_row_i] == 0)
5291  {
5292  std::ostringstream error_message;
5293  error_message << "The row distribution pointer in position "
5294  << block_row_i << " is null.\n";
5295  throw OomphLibError(error_message.str(),
5298  }
5299  }
5300 
5301  // Check that all pointers in row_distribution_pt is not null.
5302  for (unsigned block_col_i = 0; block_col_i < matrix_ncol; block_col_i++)
5303  {
5304  if (col_distribution_pt[block_col_i] == 0)
5305  {
5306  std::ostringstream error_message;
5307  error_message << "The column distribution pointer in position "
5308  << block_col_i << " is null.\n";
5309  throw OomphLibError(error_message.str(),
5312  }
5313  }
5314 
5315  // Check that all distributions are built.
5316  // First the row distributions
5317  for (unsigned block_row_i = 0; block_row_i < matrix_nrow; block_row_i++)
5318  {
5319  if (!row_distribution_pt[block_row_i]->built())
5320  {
5321  std::ostringstream error_message;
5322  error_message << "The distribution pointer in position "
5323  << block_row_i << " is not built.\n";
5324  throw OomphLibError(error_message.str(),
5327  }
5328  }
5329  // Now the column distributions
5330  for (unsigned block_col_i = 0; block_col_i < matrix_ncol; block_col_i++)
5331  {
5332  if (!col_distribution_pt[block_col_i]->built())
5333  {
5334  std::ostringstream error_message;
5335  error_message << "The distribution pointer in position "
5336  << block_col_i << " is not built.\n";
5337  throw OomphLibError(error_message.str(),
5340  }
5341  }
5342 
5343  // Check that all communicators in row_distribution_pt are the same.
5344  const OomphCommunicator first_row_comm =
5345  *(row_distribution_pt[0]->communicator_pt());
5346 
5347  for (unsigned block_row_i = 1; block_row_i < matrix_nrow; block_row_i++)
5348  {
5349  const OomphCommunicator current_comm =
5350  *(row_distribution_pt[block_row_i]->communicator_pt());
5351 
5352  if (first_row_comm != current_comm)
5353  {
5354  std::ostringstream error_message;
5355  error_message
5356  << "The communicator from the row distribution in position "
5357  << block_row_i << " is not the same as the first "
5358  << "communicator from row_distribution_pt";
5359  throw OomphLibError(error_message.str(),
5362  }
5363  }
5364 
5365  // Check that all communicators in col_distribution_pt are the same as the
5366  // first row communicator from above.
5367  for (unsigned block_col_i = 0; block_col_i < matrix_ncol; block_col_i++)
5368  {
5369  const OomphCommunicator current_comm =
5370  *(col_distribution_pt[block_col_i]->communicator_pt());
5371 
5372  if (first_row_comm != current_comm)
5373  {
5374  std::ostringstream error_message;
5375  error_message
5376  << "The communicator from the col distribution in position "
5377  << block_col_i << " is not the same as the first "
5378  << "communicator from row_distribution_pt";
5379  throw OomphLibError(error_message.str(),
5382  }
5383  }
5384 
5385  // Are all sub matrices built? If the matrix_pt is not null, make sure
5386  // that it is built.
5387  for (unsigned block_row_i = 0; block_row_i < matrix_nrow; block_row_i++)
5388  {
5389  for (unsigned block_col_i = 0; block_col_i < matrix_ncol; block_col_i++)
5390  {
5391  if (matrix_pt(block_row_i, block_col_i) != 0 &&
5392  !(matrix_pt(block_row_i, block_col_i)->built()))
5393  {
5394  std::ostringstream error_message;
5395  error_message << "The sub matrix_pt(" << block_row_i << ","
5396  << block_col_i << ")\n"
5397  << "is not built.\n";
5398  throw OomphLibError(error_message.str(),
5401  }
5402  }
5403  }
5404 
5405  // For the matrices which are built, do they have the same communicator as
5406  // the first communicator from row_distribution_pt?
5407  for (unsigned block_row_i = 0; block_row_i < matrix_nrow; block_row_i++)
5408  {
5409  for (unsigned block_col_i = 0; block_col_i < matrix_ncol; block_col_i++)
5410  {
5411  if (matrix_pt(block_row_i, block_col_i) != 0)
5412  {
5413  const OomphCommunicator current_comm =
5414  *(matrix_pt(block_row_i, block_col_i)
5415  ->distribution_pt()
5416  ->communicator_pt());
5417  if (first_row_comm != current_comm)
5418  {
5419  std::ostringstream error_message;
5420  error_message
5421  << "The sub matrix_pt(" << block_row_i << "," << block_col_i
5422  << ")\n"
5423  << "does not have the same communicator pointer as those in\n"
5424  << "(row|col)_distribution_pt.\n";
5425  throw OomphLibError(error_message.str(),
5428  }
5429  }
5430  }
5431  }
5432 
5433  // Do all dimensions of sub matrices "make sense"?
5434  // Compare the number of rows of each block matrix in a block row.
5435  for (unsigned block_row_i = 0; block_row_i < matrix_nrow; block_row_i++)
5436  {
5437  // Use the first column to compare against the rest.
5438  unsigned long current_block_nrow =
5439  row_distribution_pt[block_row_i]->nrow();
5440 
5441  // Compare against columns 0 to matrix_ncol - 1
5442  for (unsigned block_col_i = 0; block_col_i < matrix_ncol; block_col_i++)
5443  {
5444  // Perform the check if the matrix_pt is not null.
5445  if (matrix_pt(block_row_i, block_col_i) != 0)
5446  {
5447  // Get the nrow for this sub block.
5448  unsigned long subblock_nrow =
5449  matrix_pt(block_row_i, block_col_i)->nrow();
5450 
5451  if (current_block_nrow != subblock_nrow)
5452  {
5453  std::ostringstream error_message;
5454  error_message << "The sub matrix (" << block_row_i << ","
5455  << block_col_i << ")\n"
5456  << "requires nrow = " << current_block_nrow
5457  << ", but has nrow = " << subblock_nrow << ".\n"
5458  << "Either the row_distribution_pt is incorrect or "
5459  << "the sub matrices are incorrect.\n";
5460  throw OomphLibError(error_message.str(),
5463  }
5464  }
5465  }
5466  }
5467 
5468  // Compare the number of columns of each block matrix in a block column.
5469  for (unsigned block_col_i = 0; block_col_i < matrix_ncol; block_col_i++)
5470  {
5471  // Get the current block ncol from the linear algebra distribution.
5472  // Note that we assume that the dimensions are symmetrical.
5473  unsigned current_block_ncol = col_distribution_pt[block_col_i]->nrow();
5474 
5475  for (unsigned block_row_i = 0; block_row_i < matrix_nrow; block_row_i++)
5476  {
5477  if (matrix_pt(block_row_i, block_col_i) != 0)
5478  {
5479  // Get the ncol for this sub block.
5480  unsigned subblock_ncol =
5481  matrix_pt(block_row_i, block_col_i)->ncol();
5482 
5483  if (current_block_ncol != subblock_ncol)
5484  {
5485  std::ostringstream error_message;
5486  error_message << "The sub matrix (" << block_row_i << ","
5487  << block_col_i << ")\n"
5488  << "requires ncol = " << current_block_ncol
5489  << ", but has ncol = " << subblock_ncol << ".\n"
5490  << "Either the col_distribution_pt is incorrect or "
5491  << "the sub matrices are incorrect.\n";
5492  throw OomphLibError(error_message.str(),
5495  }
5496  }
5497  }
5498  }
5499 
5500  // Ensure that the distributions for all sub matrices in the same block
5501  // row are the same. This is because we permute the row across several
5502  // matrices.
5503 
5504  // Loop through each block row.
5505  for (unsigned block_row_i = 0; block_row_i < matrix_nrow; block_row_i++)
5506  {
5507  // Get the distribution from the first block in this row.
5508  LinearAlgebraDistribution* block_row_distribution_pt =
5509  row_distribution_pt[block_row_i];
5510 
5511  // Loop through the block columns
5512  for (unsigned block_col_i = 0; block_col_i < matrix_ncol; block_col_i++)
5513  {
5514  if (matrix_pt(block_row_i, block_col_i) != 0)
5515  {
5516  // Get the distribution for this block.
5517  LinearAlgebraDistribution* current_block_distribution_pt =
5518  matrix_pt(block_row_i, block_col_i)->distribution_pt();
5519 
5520  // Ensure that the in matrices is a square block matrix.
5521  if ((*block_row_distribution_pt) !=
5522  (*current_block_distribution_pt))
5523  {
5524  std::ostringstream error_message;
5525  error_message
5526  << "Sub block(" << block_row_i << "," << block_col_i << ")"
5527  << "does not have the same distributoin as the first"
5528  << "block in this block row.\n"
5529  << "All distributions on a block row must be the same"
5530  << "for this function to concatenate matrices.\n";
5531  throw OomphLibError(error_message.str(),
5534  }
5535  }
5536  }
5537  }
5538 #endif
5539 
5540  // The communicator pointer from the first row_distribution_pt
5541  const OomphCommunicator* const comm_pt =
5542  row_distribution_pt[0]->communicator_pt();
5543 
5544  // Renamed for so it makes more sense.
5545  unsigned nblock_row = matrix_nrow;
5546 
5547  // If the result matrix does not have a distribution, then we concatenate
5548  // the sub matrix distributions.
5549  if (!result_matrix.distribution_pt()->built())
5550  {
5551  // The result distribution
5552  LinearAlgebraDistribution tmp_distribution;
5554  tmp_distribution);
5555 
5556  result_matrix.build(&tmp_distribution);
5557  }
5558  else
5559  // A distribution is supplied for the result matrix.
5560  {
5561 #ifdef PARANOID
5562  // Check that the result distribution is a concatenation of the
5563  // distributions of the sub matrices.
5564 
5565  LinearAlgebraDistribution wanted_distribution;
5566 
5568  wanted_distribution);
5569 
5570  if (*(result_matrix.distribution_pt()) != wanted_distribution)
5571  {
5572  std::ostringstream error_message;
5573  error_message
5574  << "The result distribution is not correct.\n"
5575  << "Please call the function without a result\n"
5576  << "distribution (clear the result matrix) or check the\n"
5577  << "distribution of the result matrix.\n"
5578  << "The result distribution must be the same as the one \n"
5579  << "created by\n"
5580  << "LinearAlgebraDistributionHelpers::concatenate(...)";
5581  throw OomphLibError(error_message.str(),
5584  }
5585 #endif
5586  }
5587 
5588  // The rest of the paranoid checks.
5589 #ifdef PARANOID
5590 
5591  // Make sure that the communicator from the result matrix is the same as
5592  // all the others. This test is redundant if this function created the
5593  // result matrix distribution, since then it is guaranteed that the
5594  // communicators are the same.
5595  {
5596  // Communicator from the result matrix.
5597  const OomphCommunicator res_comm =
5598  *(result_matrix.distribution_pt()->communicator_pt());
5599 
5600  // Is the result communicator pointer the same as the others?
5601  // Since we have already tested the others, we only need to compare
5602  // against one of them. Say the first communicator from
5603  // row_distribution_pt.
5604  const OomphCommunicator first_comm =
5605  *(row_distribution_pt[0]->communicator_pt());
5606 
5607  if (res_comm != first_comm)
5608  {
5609  std::ostringstream error_message;
5610  error_message << "The OomphCommunicator of the result matrix is not "
5611  "the same as the "
5612  << "others!";
5613  throw OomphLibError(error_message.str(),
5616  }
5617  }
5618 
5619  // Are all the distributed boolean the same? This only applies if we have
5620  // more than one processor. If there is only one processor, then it does
5621  // not matter if it is distributed or not - they are conceptually the
5622  // same.
5623  if (comm_pt->nproc() != 1)
5624  {
5625  // Compare distributed for sub matrices (against the result matrix).
5626  const bool res_distributed = result_matrix.distributed();
5627 
5628  // Loop over all sub blocks.
5629  for (unsigned block_row_i = 0; block_row_i < matrix_nrow; block_row_i++)
5630  {
5631  for (unsigned block_col_i = 0; block_col_i < matrix_ncol;
5632  block_col_i++)
5633  {
5634  if (matrix_pt(block_row_i, block_col_i) != 0)
5635  {
5636  const bool another_distributed =
5637  matrix_pt(block_row_i, block_col_i)->distributed();
5638 
5639  if (res_distributed != another_distributed)
5640  {
5641  std::ostringstream error_message;
5642  error_message << "The distributed boolean of the sub matrix ("
5643  << block_row_i << "," << block_col_i << ")\n"
5644  << "is not the same as the result matrix. \n";
5645  throw OomphLibError(error_message.str(),
5648  }
5649  }
5650  }
5651  }
5652 
5653  // Do this test for row_distribution_pt
5654  const bool first_row_distribution_distributed =
5655  row_distribution_pt[0]->distributed();
5656 
5657  for (unsigned block_row_i = 1; block_row_i < matrix_nrow; block_row_i++)
5658  {
5659  const bool another_distributed =
5660  row_distribution_pt[block_row_i]->distributed();
5661 
5662  if (first_row_distribution_distributed != another_distributed)
5663  {
5664  std::ostringstream error_message;
5665  error_message
5666  << "The distributed boolean of row_distribution_pt["
5667  << block_row_i << "]\n"
5668  << "is not the same as the one from row_distribution_pt[0]. \n";
5669  throw OomphLibError(error_message.str(),
5672  }
5673  }
5674 
5675  // Repeat for col_distribution_pt
5676  for (unsigned block_col_i = 0; block_col_i < matrix_ncol; block_col_i++)
5677  {
5678  const bool another_distributed =
5679  col_distribution_pt[block_col_i]->distributed();
5680 
5681  if (first_row_distribution_distributed != another_distributed)
5682  {
5683  std::ostringstream error_message;
5684  error_message
5685  << "The distributed boolean of col_distribution_pt["
5686  << block_col_i << "]\n"
5687  << "is not the same as the one from row_distribution_pt[0]. \n";
5688  throw OomphLibError(error_message.str(),
5691  }
5692  }
5693  }
5694 #endif
5695 
5698 
5699  // The number of processors.
5700  unsigned nproc = comm_pt->nproc();
5701 
5702  // Cache the result distribution pointer for convenience.
5703  LinearAlgebraDistribution* res_distribution_pt =
5704  result_matrix.distribution_pt();
5705 
5706  // nrow_local for the result matrix
5707  unsigned res_nrow_local = res_distribution_pt->nrow_local();
5708 
5709  // renamed for readability.
5710  unsigned nblock_col = matrix_ncol;
5711 
5712  // construct the block offset
5713  // DenseMatrix<unsigned> col_offset(nproc,nblock_col,0);
5714  std::vector<std::vector<unsigned>> col_offset(
5715  nproc, std::vector<unsigned>(nblock_col));
5716  unsigned off = 0;
5717  for (unsigned proc_i = 0; proc_i < nproc; proc_i++)
5718  {
5719  for (unsigned block_i = 0; block_i < nblock_col; block_i++)
5720  {
5721  col_offset[proc_i][block_i] = off;
5722  off += col_distribution_pt[block_i]->nrow_local(proc_i);
5723  }
5724  }
5725 
5726  // Do some pre-processing for the processor number a global row number is
5727  // on. This is required when permuting the column entries.
5728  // We need to do this for each distribution, so we have a vector of
5729  // vectors. First index corresponds to the distribution, the second is
5730  // the processor number.
5731  std::vector<std::vector<unsigned>> p_for_rows(nblock_col,
5732  std::vector<unsigned>());
5733  // initialise 2D vector
5734  for (unsigned blocki = 0; blocki < nblock_col; blocki++)
5735  {
5736  int blockinrow = col_distribution_pt[blocki]->nrow();
5737  p_for_rows[blocki].resize(blockinrow);
5738  // FOR each global index in the block, work out the corresponding proc.
5739  for (int rowi = 0; rowi < blockinrow; rowi++)
5740  {
5741  unsigned p = 0;
5742  int b_first_row = col_distribution_pt[blocki]->first_row(p);
5743  int b_nrow_local = col_distribution_pt[blocki]->nrow_local(p);
5744 
5745  while (rowi < b_first_row || rowi >= b_nrow_local + b_first_row)
5746  {
5747  p++;
5748  b_first_row = col_distribution_pt[blocki]->first_row(p);
5749  b_nrow_local = col_distribution_pt[blocki]->nrow_local(p);
5750  }
5751  p_for_rows[blocki][rowi] = p;
5752  }
5753  }
5754 
5755  // determine nnz of all blocks on this processor only.
5756  // This is used to create storage space.
5757  unsigned long res_nnz = 0;
5758  for (unsigned row_i = 0; row_i < nblock_row; row_i++)
5759  {
5760  for (unsigned col_i = 0; col_i < nblock_col; col_i++)
5761  {
5762  if (matrix_pt(row_i, col_i) != 0)
5763  {
5764  res_nnz += matrix_pt(row_i, col_i)->nnz();
5765  }
5766  }
5767  }
5768 
5769  // My rank
5770  // unsigned my_rank = comm_pt->my_rank();
5771  // my_rank = my_rank;
5772 
5773  // Turn the above into a string.
5774  // std::ostringstream myrankstream;
5775  // myrankstream << "THISDOESNOTHINGnp" << my_rank << std::endl;
5776  // std::string myrankstring = myrankstream.str();
5777 
5778 
5779  // CALLGRIND_ZERO_STATS;
5780  // CALLGRIND_START_INSTRUMENTATION;
5781 
5782  // storage for the result matrix.
5783  int* res_row_start = new int[res_nrow_local + 1];
5784  int* res_column_index = new int[res_nnz];
5785  double* res_value = new double[res_nnz];
5786 
5787  // initialise the zero-th entry
5788  res_row_start[0] = 0;
5789 
5790  // loop over the block rows
5791  unsigned long res_i = 0; // index for the result matrix.
5792  unsigned long res_row_i = 0; // index for the row
5793  for (unsigned i = 0; i < nblock_row; i++)
5794  {
5795  // loop over the rows of the current block local rows.
5796  unsigned block_nrow = row_distribution_pt[i]->nrow_local();
5797  for (unsigned k = 0; k < block_nrow; k++)
5798  {
5799  // initialise res_row_start
5800  res_row_start[res_row_i + 1] = res_row_start[res_row_i];
5801 
5802  // Loop over the block columns
5803  for (unsigned j = 0; j < nblock_col; j++)
5804  {
5805  // if block(i,j) pointer is not null then
5806  if (matrix_pt(i, j) != 0)
5807  {
5808  // get pointers for the elements in the current block
5809  int* b_row_start = matrix_pt(i, j)->row_start();
5810  int* b_column_index = matrix_pt(i, j)->column_index();
5811  double* b_value = matrix_pt(i, j)->value();
5812 
5813  // memcpy( &dst[dstIdx], &src[srcIdx], numElementsToCopy * sizeof(
5814  // Element ) );
5815  // no ele to copy
5816  int numEleToCopy = b_row_start[k + 1] - b_row_start[k];
5817  memcpy(res_value + res_i,
5818  b_value + b_row_start[k],
5819  numEleToCopy * sizeof(double));
5820  // Loop through the current local row.
5821  for (int l = b_row_start[k]; l < b_row_start[k + 1]; l++)
5822  {
5823  // if b_column_index[l] was a row index, what processor
5824  // would it be on
5825  // unsigned p = col_distribution_pt[j]
5826  // ->rank_of_global_row_map(b_column_index[l]);
5827  unsigned p = p_for_rows[j][b_column_index[l]];
5828 
5829  int b_first_row = col_distribution_pt[j]->first_row(p);
5830  // int b_nrow_local =
5831  // col_distribution_pt[j]->nrow_local(p);
5832 
5833  // while (b_column_index[l] < b_first_row ||
5834  // b_column_index[l] >=
5835  // b_nrow_local+b_first_row)
5836  // {
5837  // p++;
5838  // b_first_row =
5839  // col_distribution_pt[j]->first_row(p);
5840  // b_nrow_local =
5841  // col_distribution_pt[j]->nrow_local(p);
5842  // }
5843 
5844  // determine the local equation number in the block j/processor
5845  // p "column block"
5846  int eqn = b_column_index[l] - b_first_row;
5847 
5848  // add to the result matrix
5849  // res_value[res_i] = b_value[l];
5850  res_column_index[res_i] = col_offset[p][j] + eqn;
5851  res_row_start[res_row_i + 1]++;
5852  res_i++;
5853  }
5854  }
5855  }
5856 
5857  // increment the row pt
5858  res_row_i++;
5859  }
5860  }
5861  // CALLGRIND_STOP_INSTRUMENTATION;
5862  // CALLGRIND_DUMP_STATS_AT(myrankstring.c_str());
5863 
5864 
5865  // Get the number of columns of the result matrix.
5866  unsigned res_ncol = 0;
5867  for (unsigned block_col_i = 0; block_col_i < matrix_ncol; block_col_i++)
5868  {
5869  res_ncol += col_distribution_pt[block_col_i]->nrow();
5870  }
5871 
5872  // Build the result matrix.
5873  result_matrix.build_without_copy(
5874  res_ncol, res_nnz, res_value, res_column_index, res_row_start);
5875  }
int i
Definition: BiCGSTAB_step_by_step.cpp:9
float * p
Definition: Tutorial_Map_using.cpp:9
char char char int int * k
Definition: level2_impl.h:374
void concatenate(const DenseMatrix< CRDoubleMatrix * > &matrix_pt, CRDoubleMatrix &result_matrix)
Definition: matrices.cc:4349

References oomph::CRDoubleMatrix::build(), oomph::CRDoubleMatrix::build_without_copy(), oomph::LinearAlgebraDistribution::built(), oomph::LinearAlgebraDistribution::communicator_pt(), oomph::LinearAlgebraDistributionHelpers::concatenate(), oomph::DistributableLinearAlgebraObject::distributed(), oomph::DistributableLinearAlgebraObject::distribution_pt(), i, j, k, oomph::DenseMatrix< T >::ncol(), oomph::OomphCommunicator::nproc(), oomph::DenseMatrix< T >::nrow(), oomph::LinearAlgebraDistribution::nrow_local(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, and p.

Referenced by concatenate_without_communication(), oomph::BlockPreconditioner< MATRIX >::get_block(), oomph::BlockPreconditioner< MATRIX >::get_concatenated_block(), and main().

◆ create_uniformly_distributed_matrix()

void oomph::CRDoubleMatrixHelpers::create_uniformly_distributed_matrix ( const unsigned nrow,
const unsigned ncol,
const OomphCommunicator *const  comm_pt,
const Vector< double > &  values,
const Vector< int > &  column_indices,
const Vector< int > &  row_start,
CRDoubleMatrix matrix_out 
)

Builds a uniformly distributed matrix. A locally replicated matrix is constructed then redistributed using OOMPH-LIB's default uniform row distribution. This is memory intensive thus should be used for testing or small problems only.

Builds a uniformly distributed matrix. A locally replicated matrix is constructed then redistributed using OOMPH-LIB's default uniform row distribution. This is memory intensive thus should be used for testing or small problems only. The resulting matrix (mat_out) must not have been built.

3684  {
3685 #ifdef PARANOID
3686  // Check if the communicator exists.
3687  if (comm_pt == 0)
3688  {
3689  std::ostringstream error_message;
3690  error_message << "Please supply the communicator.\n";
3691  throw OomphLibError(error_message.str(),
3694  }
3695  // Is the out matrix built? We need an empty matrix!
3696  if (matrix_out.built())
3697  {
3698  std::ostringstream error_message;
3699  error_message << "The result matrix has been built.\n"
3700  << "Please clear the matrix.\n";
3701  throw OomphLibError(error_message.str(),
3704  }
3705 #endif
3706 
3707  // Create the locally replicated distribution.
3708  bool distributed = false;
3709  LinearAlgebraDistribution locally_replicated_distribution(
3710  comm_pt, nrow, distributed);
3711 
3712  // Create the matrix.
3713  matrix_out.build(&locally_replicated_distribution,
3714  ncol,
3715  values,
3716  column_indices,
3717  row_start);
3718 
3719  // Create the distributed distribution.
3720  distributed = true;
3721  LinearAlgebraDistribution distributed_distribution(
3722  comm_pt, nrow, distributed);
3723 
3724  // Redistribute the matrix.
3725  matrix_out.redistribute(&distributed_distribution);
3726  }

References oomph::CRDoubleMatrix::build(), oomph::CRDoubleMatrix::built(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, and oomph::CRDoubleMatrix::redistribute().

Referenced by main().

◆ deep_copy()

void oomph::CRDoubleMatrixHelpers::deep_copy ( const CRDoubleMatrix *const  in_matrix_pt,
CRDoubleMatrix out_matrix 
)
inline

Create a deep copy of the matrix pointed to by in_matrix_pt.

3492  {
3493 #ifdef PARANOID
3494  // Is the out matrix built? We need an empty out matrix!
3495  if (out_matrix.built())
3496  {
3497  std::ostringstream err_msg;
3498  err_msg << "The result matrix has been built.\n"
3499  << "Please clear the matrix.\n";
3500  throw OomphLibError(
3502  }
3503 
3504  // Check that the in matrix pointer is not null.
3505  if (in_matrix_pt == 0)
3506  {
3507  std::ostringstream err_msg;
3508  err_msg << "The in_matrix_pt is null.\n";
3509  throw OomphLibError(
3511  }
3512 
3513  // Check that the in matrix is built.
3514  if (!in_matrix_pt->built())
3515  {
3516  std::ostringstream err_msg;
3517  err_msg << "The in_matrix_pt is null.\n";
3518  throw OomphLibError(
3520  }
3521 #endif
3522 
3523  // First set the matrix matrix multiply methods (for both serial and
3524  // distributed)
3525  out_matrix.serial_matrix_matrix_multiply_method() =
3526  in_matrix_pt->serial_matrix_matrix_multiply_method();
3527 
3528  out_matrix.distributed_matrix_matrix_multiply_method() =
3529  in_matrix_pt->distributed_matrix_matrix_multiply_method();
3530 
3531 
3532  // The local nrow and nnz of the in matrix
3533  const unsigned in_nrow_local = in_matrix_pt->nrow_local();
3534  const unsigned long in_nnz = in_matrix_pt->nnz();
3535 
3536  // Storage for the values, column indices and row start
3537  double* out_values = new double[in_nnz];
3538  int* out_column_indices = new int[in_nnz];
3539  int* out_row_start = new int[in_nrow_local + 1];
3540 
3541  // The data to copy over
3542  const double* const in_values = in_matrix_pt->value();
3543  const int* const in_column_indices = in_matrix_pt->column_index();
3544  const int* const in_row_start = in_matrix_pt->row_start();
3545 
3546  // Copy the data
3547  std::copy(in_values, in_values + in_nnz, out_values);
3548 
3549  std::copy(
3550  in_column_indices, in_column_indices + in_nnz, out_column_indices);
3551 
3552  std::copy(
3553  in_row_start, in_row_start + (in_nrow_local + 1), out_row_start);
3554 
3555  // Build the matrix
3556  out_matrix.build(in_matrix_pt->distribution_pt());
3557 
3558  out_matrix.build_without_copy(in_matrix_pt->ncol(),
3559  in_nnz,
3560  out_values,
3561  out_column_indices,
3562  out_row_start);
3563 
3564  // The only thing we haven't copied over is the default linear solver
3565  // pointer, but I cannot figure out how to copy over a solver since
3566  // I do not know what it is.
3567  } // EoFunc deep_copy
EIGEN_BLAS_FUNC() copy(int *n, RealScalar *px, int *incx, RealScalar *py, int *incy)
Definition: level1_impl.h:32

References oomph::CRDoubleMatrix::build(), oomph::CRDoubleMatrix::build_without_copy(), oomph::CRDoubleMatrix::built(), oomph::CRDoubleMatrix::column_index(), copy(), oomph::CRDoubleMatrix::distributed_matrix_matrix_multiply_method(), oomph::DistributableLinearAlgebraObject::distribution_pt(), oomph::CRDoubleMatrix::ncol(), oomph::CRDoubleMatrix::nnz(), oomph::DistributableLinearAlgebraObject::nrow_local(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, oomph::CRDoubleMatrix::row_start(), oomph::CRDoubleMatrix::serial_matrix_matrix_multiply_method(), and oomph::CRDoubleMatrix::value().

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

◆ gershgorin_eigenvalue_estimate()

double oomph::CRDoubleMatrixHelpers::gershgorin_eigenvalue_estimate ( const DenseMatrix< CRDoubleMatrix * > &  matrix_pt)

Calculates the largest Gershgorin disc whilst preserving the sign. Let A be an n by n matrix, with entries aij. For \( i \in \{ 1,...,n \} \) let \( R_i = \sum_{i\neq j} |a_{ij}| \) be the sum of the absolute values of the non-diagonal entries in the i-th row. Let \( D(a_{ii},R_i) \) be the closed disc centered at \( a_{ii} \) with radius \( R_i \), such a disc is called a Gershgorin disc.


We calculate \( |D(a_{ii},R_i)|_{max} \) and multiply by the sign of the diagonal entry.


The DenseMatrix of CRDoubleMatrices are treated as if they are one large matrix. Therefore the dimensions of the sub matrices has to "make sense", there is a paranoid check for this.

Calculates the largest Gershgorin disc whilst preserving the sign. Let A be an n by n matrix, with entries aij. For \( i \in \{ 1,...,n \} \) let \( R_i = \sum_{i\neq j} |a_{ij}| \) be the sum of the absolute values of the non-diagonal entries in the i-th row. Let \( D(a_{ii},R_i) \) be the closed disc centered at \( a_{ii} \) with radius \( R_i \), such a disc is called a Gershgorin disc.


We calculate \( |D(a_{ii},R_i)|_{max} \)and multiply by the sign of the diagonal entry.


The DenseMatrix of CRDoubleMatrices are treated as if they are one large matrix. Therefore the dimensions of the sub matrices has to "make sense", there is a paranoid check for this.

4005  {
4006  // The number of block rows and columns
4007  const unsigned nblockrow = matrix_pt.nrow();
4008  const unsigned nblockcol = matrix_pt.ncol();
4009 
4010 #ifdef PARANOID
4011  // Check that tehre is at least one matrix.
4012  if (matrix_pt.nrow() == 0)
4013  {
4014  std::ostringstream error_message;
4015  error_message << "There are no matrices... \n";
4016  throw OomphLibError(error_message.str(),
4019  }
4020 
4021 
4022  // Check that all matrix_pt pointers are not null
4023  // and the matrices are built.
4024  for (unsigned block_row_i = 0; block_row_i < nblockrow; block_row_i++)
4025  {
4026  for (unsigned block_col_i = 0; block_col_i < nblockcol; block_col_i++)
4027  {
4028  if (matrix_pt(block_row_i, block_col_i) == 0)
4029  {
4030  std::ostringstream error_message;
4031  error_message << "The pointer martrix_pt(" << block_row_i << ","
4032  << block_col_i << ") is null.\n";
4033  throw OomphLibError(error_message.str(),
4036  }
4037 
4038  if (!matrix_pt(block_row_i, block_col_i)->built())
4039  {
4040  std::ostringstream error_message;
4041  error_message << "The matrix at martrix_pt(" << block_row_i << ","
4042  << block_col_i << ") is not built.\n";
4043  throw OomphLibError(error_message.str(),
4046  }
4047  }
4048  }
4049 #endif
4050 
4051 
4052 #ifdef OOMPH_HAS_MPI
4053 
4054  // The communicator pointer from block (0,0)
4055  // All communicators should be the same, we check this next.
4056  const OomphCommunicator* const comm_pt =
4057  matrix_pt(0, 0)->distribution_pt()->communicator_pt();
4058 
4059 #ifdef PARANOID
4060 
4061  // Check that all communicators are the same
4062  for (unsigned block_row_i = 0; block_row_i < nblockrow; block_row_i++)
4063  {
4064  for (unsigned block_col_i = 0; block_col_i < nblockcol; block_col_i++)
4065  {
4066  // Communicator for this block matrix.
4067  const OomphCommunicator current_block_comm =
4068  *(matrix_pt(block_row_i, block_col_i)
4069  ->distribution_pt()
4070  ->communicator_pt());
4071  if (*comm_pt != current_block_comm)
4072  {
4073  std::ostringstream error_message;
4074  error_message << "The communicator of block martrix_pt("
4075  << block_row_i << "," << block_col_i
4076  << ") is not the same as block "
4077  << "matrix_pt(0,0).\n";
4078  throw OomphLibError(error_message.str(),
4081  }
4082  }
4083  }
4084 
4085  // Check that all distributed boolean are the same (if on more than 1
4086  // core)
4087  if (comm_pt->nproc() > 1)
4088  {
4089  // Get the distributed boolean from matrix_pt(0,0)
4090  bool first_distributed = matrix_pt(0, 0)->distributed();
4091 
4092  for (unsigned block_row_i = 0; block_row_i < nblockrow; block_row_i++)
4093  {
4094  for (unsigned block_col_i = 0; block_col_i < nblockcol; block_col_i++)
4095  {
4096  // Is the current block distributed?
4097  bool current_distributed =
4098  matrix_pt(block_row_i, block_col_i)->distributed();
4099 
4100  if (first_distributed != current_distributed)
4101  {
4102  std::ostringstream error_message;
4103  error_message << "Block matrix_pt(" << block_row_i << ","
4104  << block_col_i << ") and block matrix_pt(0,0) "
4105  << "have a different distributed boolean.\n";
4106  throw OomphLibError(error_message.str(),
4109  }
4110  }
4111  }
4112  }
4113 
4114  // Check that all sub matrix dimensions "make sense"
4115  // We need to check that all the matrices in the same row has the same
4116  // nrow. Then repeat for the columns.
4117 
4118  // Check the nrow of each block row.
4119  for (unsigned block_row_i = 0; block_row_i < nblockrow; block_row_i++)
4120  {
4121  // Get the nrow to compare against from the first column.
4122  const unsigned first_block_nrow = matrix_pt(block_row_i, 0)->nrow();
4123 
4124  // Loop through the block columns.
4125  for (unsigned block_col_i = 1; block_col_i < nblockcol; block_col_i++)
4126  {
4127  // If the nrow of this block is not the same as the nrow from the
4128  // first block in this block row, throw an error.
4129  const unsigned current_block_nrow =
4130  matrix_pt(block_row_i, block_col_i)->nrow();
4131 
4132  if (first_block_nrow != current_block_nrow)
4133  {
4134  std::ostringstream error_message;
4135  error_message << "First block has nrow = " << current_block_nrow
4136  << ". But martrix_pt(" << block_row_i << ","
4137  << block_col_i
4138  << ") has nrow = " << current_block_nrow << ".\n";
4139  throw OomphLibError(error_message.str(),
4142  }
4143  }
4144  }
4145 
4146  // Check the ncol of each block column.
4147  for (unsigned block_col_i = 0; block_col_i < nblockcol; block_col_i++)
4148  {
4149  // Get the ncol from the first block row to compare against.
4150  const unsigned first_block_ncol = matrix_pt(0, block_col_i)->ncol();
4151 
4152  for (unsigned block_row_i = 1; block_row_i < nblockrow; block_row_i++)
4153  {
4154  // Get the ncol for the current block.
4155  const unsigned current_block_ncol =
4156  matrix_pt(block_row_i, block_col_i)->ncol();
4157 
4158  if (first_block_ncol != current_block_ncol)
4159  {
4160  std::ostringstream error_message;
4161  error_message << "First block has ncol = " << current_block_ncol
4162  << ". But martrix_pt(" << block_row_i << ","
4163  << block_col_i
4164  << ") has ncol = " << current_block_ncol << ".\n";
4165  throw OomphLibError(error_message.str(),
4168  }
4169  }
4170  }
4171 
4172  // Check that the distribution for each block row is the same.
4173  for (unsigned block_row_i = 0; block_row_i < nblockrow; block_row_i++)
4174  {
4175  // The first distribution of this block row.
4176  const LinearAlgebraDistribution first_dist =
4177  *(matrix_pt(block_row_i, 0)->distribution_pt());
4178 
4179  // Loop through the rest of the block columns.
4180  for (unsigned block_col_i = 1; block_col_i < nblockcol; block_col_i++)
4181  {
4182  // Get the distribution from the current block.
4183  const LinearAlgebraDistribution current_dist =
4184  matrix_pt(block_row_i, block_col_i)->distribution_pt();
4185 
4186  // Compare the first distribution against the current.
4187  if (first_dist != current_dist)
4188  {
4189  std::ostringstream error_message;
4190  error_message << "First distribution of block row " << block_row_i
4191  << " is different from the distribution from "
4192  << "martrix_pt(" << block_row_i << "," << block_col_i
4193  << ").\n";
4194  throw OomphLibError(error_message.str(),
4197  }
4198  }
4199  }
4200 
4201 #endif
4202 #endif
4203 
4204  // Loop thrpugh the block rows, then block columns to
4205  // compute the local inf norm
4206  double extreme_disc = 0;
4207  for (unsigned block_row_i = 0; block_row_i < nblockrow; block_row_i++)
4208  {
4209  // Get the number of local rows from the first block.
4210  unsigned block_nrow_local = matrix_pt(block_row_i, 0)->nrow_local();
4211 
4212  // Loop through the block_nrow_local in this block row
4213  for (unsigned local_row_i = 0; local_row_i < block_nrow_local;
4214  local_row_i++)
4215  {
4216  double abs_sum_of_row = 0;
4217  // Loop through the block columns
4218  for (unsigned block_col_i = 0; block_col_i < nblockcol; block_col_i++)
4219  {
4220  // Locally cache the pointer to the current block.
4221  CRDoubleMatrix* block_pt = matrix_pt(block_row_i, block_col_i);
4222 
4223  const int* row_start = block_pt->row_start();
4224  const double* value = block_pt->value();
4225 
4226  // Loop through the values
4227  for (int val_i = row_start[local_row_i];
4228  val_i < row_start[local_row_i + 1];
4229  val_i++)
4230  {
4231  abs_sum_of_row += fabs(value[val_i]);
4232  }
4233  }
4234 
4235  // Now minus the diagonal entry...
4236  // Locate the diagonal block matrix.
4237  double* s_values = matrix_pt(block_row_i, block_row_i)->value();
4238  int* s_column_index =
4239  matrix_pt(block_row_i, block_row_i)->column_index();
4240  int* s_row_start = matrix_pt(block_row_i, block_row_i)->row_start();
4241  // int s_nrow_local =
4242  // matrix_pt(block_row_i,block_row_i)->nrow_local();
4243  int s_first_row = matrix_pt(block_row_i, block_row_i)->first_row();
4244 
4245  // Get the diagonal value...
4246  double diagonal_value = 0;
4247  bool found = false;
4248  for (int j = s_row_start[local_row_i];
4249  j < s_row_start[local_row_i + 1] && !found;
4250  j++)
4251  {
4252  if (s_column_index[j] == int(local_row_i + s_first_row))
4253  {
4254  diagonal_value = s_values[j];
4255  found = true;
4256  }
4257  }
4258 
4259  // Check if the diagonal entry is found.
4260  if (!found)
4261  {
4262  std::ostringstream error_message;
4263  error_message << "The diagonal entry for the block(" << block_row_i
4264  << "," << block_row_i << ")\n"
4265  << "on local row " << local_row_i
4266  << " does not exist." << std::endl;
4267  throw OomphLibError(error_message.str(),
4270  }
4271 
4272  // This is the disc.
4273  abs_sum_of_row -= fabs(diagonal_value);
4274 
4275  // Now we have to check if the diagonal entry is
4276  // on the left or right side of zero.
4277  if (diagonal_value > 0)
4278  {
4279  double extreme_disc_local = diagonal_value + abs_sum_of_row;
4280  extreme_disc = std::max(extreme_disc_local, extreme_disc);
4281  }
4282  else
4283  {
4284  double extreme_disc_local = diagonal_value - abs_sum_of_row;
4285  extreme_disc = std::min(extreme_disc_local, extreme_disc);
4286  }
4287  } // Loop through local row (of all block column)
4288  } // Loop through block row
4289 
4290  // if this vector is distributed and on multiple processors then gather
4291 #ifdef OOMPH_HAS_MPI
4292  double extreme_disc_local = extreme_disc;
4293  if (matrix_pt(0, 0)->distributed() && comm_pt->nproc() > 1)
4294  {
4295  if (extreme_disc > 0)
4296  {
4297  MPI_Allreduce(&extreme_disc,
4298  &extreme_disc_local,
4299  1,
4300  MPI_DOUBLE,
4301  MPI_MAX,
4302  comm_pt->mpi_comm());
4303  }
4304  else
4305  {
4306  MPI_Allreduce(&extreme_disc,
4307  &extreme_disc_local,
4308  1,
4309  MPI_DOUBLE,
4310  MPI_MIN,
4311  comm_pt->mpi_comm());
4312  }
4313  }
4314  extreme_disc = extreme_disc_local;
4315 #endif
4316 
4317  // and return
4318  return extreme_disc;
4319  }
#define min(a, b)
Definition: datatypes.h:22
#define max(a, b)
Definition: datatypes.h:23
squared absolute value
Definition: GlobalFunctions.h:87
bool found
Definition: MergeRestartFiles.py:24
Real fabs(const Real &a)
Definition: boostmultiprec.cpp:117

References boost::multiprecision::fabs(), MergeRestartFiles::found, j, max, min, oomph::DenseMatrix< T >::ncol(), oomph::OomphCommunicator::nproc(), oomph::DenseMatrix< T >::nrow(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, oomph::CRDoubleMatrix::row_start(), Eigen::value, and oomph::CRDoubleMatrix::value().

◆ inf_norm()

double oomph::CRDoubleMatrixHelpers::inf_norm ( const DenseMatrix< CRDoubleMatrix * > &  matrix_pt)

Compute infinity (maximum) norm of sub blocks as if it was one matrix.

Calculates the infinity (maximum) norm of a DenseMartrix of CRDoubleMatrices as if it was one large matrix. This avoids creating a concatenation of the sub-blocks just to calculate the infinity norm.

3732  {
3733  // The number of block rows and columns
3734  const unsigned nblockrow = matrix_pt.nrow();
3735  const unsigned nblockcol = matrix_pt.ncol();
3736 
3737 #ifdef PARANOID
3738  // Check that tehre is at least one matrix.
3739  if (matrix_pt.nrow() == 0)
3740  {
3741  std::ostringstream error_message;
3742  error_message << "There are no matrices... \n";
3743  throw OomphLibError(error_message.str(),
3746  }
3747 
3748 
3749  // Check that all matrix_pt pointers are not null
3750  // and the matrices are built.
3751  for (unsigned block_row_i = 0; block_row_i < nblockrow; block_row_i++)
3752  {
3753  for (unsigned block_col_i = 0; block_col_i < nblockcol; block_col_i++)
3754  {
3755  if (matrix_pt(block_row_i, block_col_i) == 0)
3756  {
3757  std::ostringstream error_message;
3758  error_message << "The pointer martrix_pt(" << block_row_i << ","
3759  << block_col_i << ") is null.\n";
3760  throw OomphLibError(error_message.str(),
3763  }
3764 
3765  if (!matrix_pt(block_row_i, block_col_i)->built())
3766  {
3767  std::ostringstream error_message;
3768  error_message << "The matrix at martrix_pt(" << block_row_i << ","
3769  << block_col_i << ") is not built.\n";
3770  throw OomphLibError(error_message.str(),
3773  }
3774  }
3775  }
3776 #endif
3777 
3778 #ifdef OOMPH_HAS_MPI
3779 
3780  // The communicator pointer from block (0,0)
3781  const OomphCommunicator* const comm_pt =
3782  matrix_pt(0, 0)->distribution_pt()->communicator_pt();
3783 
3784 #ifdef PARANOID
3785 
3786 
3787  // Check that all communicators are the same
3788  for (unsigned block_row_i = 0; block_row_i < nblockrow; block_row_i++)
3789  {
3790  for (unsigned block_col_i = 0; block_col_i < nblockcol; block_col_i++)
3791  {
3792  // Communicator for this block matrix.
3793  const OomphCommunicator current_block_comm =
3794  *(matrix_pt(block_row_i, block_col_i)
3795  ->distribution_pt()
3796  ->communicator_pt());
3797  if (*comm_pt != current_block_comm)
3798  {
3799  std::ostringstream error_message;
3800  error_message << "The communicator of block martrix_pt("
3801  << block_row_i << "," << block_col_i
3802  << ") is not the same as block "
3803  << "matrix_pt(0,0).\n";
3804  throw OomphLibError(error_message.str(),
3807  }
3808  }
3809  }
3810 
3811  // Check that all distributed boolean are the same (if on more than 1
3812  // core)
3813  if (comm_pt->nproc() > 1)
3814  {
3815  // Get the distributed boolean from matrix_pt(0,0)
3816  bool first_distributed = matrix_pt(0, 0)->distributed();
3817 
3818  for (unsigned block_row_i = 0; block_row_i < nblockrow; block_row_i++)
3819  {
3820  for (unsigned block_col_i = 0; block_col_i < nblockcol; block_col_i++)
3821  {
3822  // Is the current block distributed?
3823  bool current_distributed =
3824  matrix_pt(block_row_i, block_col_i)->distributed();
3825 
3826  if (first_distributed != current_distributed)
3827  {
3828  std::ostringstream error_message;
3829  error_message << "Block matrix_pt(" << block_row_i << ","
3830  << block_col_i << ") and block matrix_pt(0,0) "
3831  << "have a different distributed boolean.\n";
3832  throw OomphLibError(error_message.str(),
3835  }
3836  }
3837  }
3838  }
3839 
3840  // Check that all sub matrix dimensions "make sense"
3841  // We need to check that all the matrices in the same row has the same
3842  // nrow. Then repeat for the columns.
3843 
3844  // Check the nrow of each block row.
3845  for (unsigned block_row_i = 0; block_row_i < nblockrow; block_row_i++)
3846  {
3847  // Get the nrow to compare against from the first column.
3848  const unsigned first_block_nrow = matrix_pt(block_row_i, 0)->nrow();
3849 
3850  // Loop through the block columns.
3851  for (unsigned block_col_i = 1; block_col_i < nblockcol; block_col_i++)
3852  {
3853  // If the nrow of this block is not the same as the nrow from the
3854  // first block in this block row, throw an error.
3855  const unsigned current_block_nrow =
3856  matrix_pt(block_row_i, block_col_i)->nrow();
3857 
3858  if (first_block_nrow != current_block_nrow)
3859  {
3860  std::ostringstream error_message;
3861  error_message << "First block has nrow = " << current_block_nrow
3862  << ". But martrix_pt(" << block_row_i << ","
3863  << block_col_i
3864  << ") has nrow = " << current_block_nrow << ".\n";
3865  throw OomphLibError(error_message.str(),
3868  }
3869  }
3870  }
3871 
3872  // Check the ncol of each block column.
3873  for (unsigned block_col_i = 0; block_col_i < nblockcol; block_col_i++)
3874  {
3875  // Get the ncol from the first block row to compare against.
3876  const unsigned first_block_ncol = matrix_pt(0, block_col_i)->ncol();
3877 
3878  for (unsigned block_row_i = 1; block_row_i < nblockrow; block_row_i++)
3879  {
3880  // Get the ncol for the current block.
3881  const unsigned current_block_ncol =
3882  matrix_pt(block_row_i, block_col_i)->ncol();
3883 
3884  if (first_block_ncol != current_block_ncol)
3885  {
3886  std::ostringstream error_message;
3887  error_message << "First block has ncol = " << current_block_ncol
3888  << ". But martrix_pt(" << block_row_i << ","
3889  << block_col_i
3890  << ") has ncol = " << current_block_ncol << ".\n";
3891  throw OomphLibError(error_message.str(),
3894  }
3895  }
3896  }
3897 
3898  // Check that the distribution for each block row is the same.
3899  for (unsigned block_row_i = 0; block_row_i < nblockrow; block_row_i++)
3900  {
3901  // The first distribution of this block row.
3902  const LinearAlgebraDistribution first_dist =
3903  *(matrix_pt(block_row_i, 0)->distribution_pt());
3904 
3905  // Loop through the rest of the block columns.
3906  for (unsigned block_col_i = 1; block_col_i < nblockcol; block_col_i++)
3907  {
3908  // Get the distribution from the current block.
3909  const LinearAlgebraDistribution current_dist =
3910  matrix_pt(block_row_i, block_col_i)->distribution_pt();
3911 
3912  // Compare the first distribution against the current.
3913  if (first_dist != current_dist)
3914  {
3915  std::ostringstream error_message;
3916  error_message << "First distribution of block row " << block_row_i
3917  << " is different from the distribution from "
3918  << "martrix_pt(" << block_row_i << "," << block_col_i
3919  << ").\n";
3920  throw OomphLibError(error_message.str(),
3923  }
3924  }
3925  }
3926 #endif
3927 
3928 #endif
3929 
3930  // Loop thrpugh the block rows, then block columns to
3931  // compute the local inf norm
3932  double inf_norm = 0;
3933  for (unsigned block_row_i = 0; block_row_i < nblockrow; block_row_i++)
3934  {
3935  // Get the number of local rows from the first block.
3936  unsigned block_nrow_local = matrix_pt(block_row_i, 0)->nrow_local();
3937 
3938  // Loop through the block_nrow_local in this block row
3939  for (unsigned local_row_i = 0; local_row_i < block_nrow_local;
3940  local_row_i++)
3941  {
3942  double abs_sum_of_row = 0;
3943  // Loop through the block columns
3944  for (unsigned block_col_i = 0; block_col_i < nblockcol; block_col_i++)
3945  {
3946  // Locally cache the pointer to the current block.
3947  CRDoubleMatrix* block_pt = matrix_pt(block_row_i, block_col_i);
3948 
3949  const int* row_start = block_pt->row_start();
3950  const double* value = block_pt->value();
3951 
3952  // Loop through the values
3953  for (int val_i = row_start[local_row_i];
3954  val_i < row_start[local_row_i + 1];
3955  val_i++)
3956  {
3957  abs_sum_of_row += fabs(value[val_i]);
3958  }
3959  }
3960  // Store the max row
3961  inf_norm = std::max(inf_norm, abs_sum_of_row);
3962  }
3963  }
3964 
3965  // if this vector is distributed and on multiple processors then gather
3966 #ifdef OOMPH_HAS_MPI
3967  double inf_norm_local = inf_norm;
3968  if (matrix_pt(0, 0)->distributed() && comm_pt->nproc() > 1)
3969  {
3970  MPI_Allreduce(&inf_norm,
3971  &inf_norm_local,
3972  1,
3973  MPI_DOUBLE,
3974  MPI_MAX,
3975  comm_pt->mpi_comm());
3976  }
3977  inf_norm = inf_norm_local;
3978 #endif
3979 
3980  // and return
3981  return inf_norm;
3982  }
double inf_norm(const DenseMatrix< CRDoubleMatrix * > &matrix_pt)
Compute infinity (maximum) norm of sub blocks as if it was one matrix.
Definition: matrices.cc:3731

References boost::multiprecision::fabs(), max, oomph::DenseMatrix< T >::ncol(), oomph::OomphCommunicator::nproc(), oomph::DenseMatrix< T >::nrow(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, oomph::CRDoubleMatrix::row_start(), Eigen::value, and oomph::CRDoubleMatrix::value().

Referenced by oomph::PseudoElasticPreconditioner::setup(), and oomph::LagrangeEnforcedFlowPreconditioner::setup().