KratosMultiphysics
KRATOS Multiphysics (Kratos) is a framework for building parallel, multi-disciplinary simulation software, aiming at modularity, extensibility, and high performance. Kratos is written in C++, and counts with an extensive Python interface.
sparse_matrix_multiplication_utility.h
Go to the documentation of this file.
1 // | / |
2 // ' / __| _` | __| _ \ __|
3 // . \ | ( | | ( |\__ `
4 // _|\_\_| \__,_|\__|\___/ ____/
5 // Multi-Physics
6 //
7 // License: BSD License
8 // Kratos default license: kratos/license.txt
9 //
10 // Main authors: Vicente Mataix Ferrandiz
11 //
12 
13 #if !defined(KRATOS_SPARSE_MATRIX_MULTIPLICATION_UTILITY_H_INCLUDED )
14 #define KRATOS_SPARSE_MATRIX_MULTIPLICATION_UTILITY_H_INCLUDED
15 
16 // System includes
17 #include <vector>
18 #include <cmath>
19 #include <algorithm>
20 #include <numeric>
21 #ifdef _OPENMP
22 #include <omp.h>
23 #endif
24 
25 // External includes
26 #include "amgcl/value_type/interface.hpp"
27 
28 // Project includes
29 #include "includes/define.h"
31 
32 namespace Kratos
33 {
36 
40 
44 
48 
52 
62 {
63 public:
66 
69 
71  typedef std::size_t SizeType;
72 
74  typedef std::size_t IndexType;
75 
77  typedef std::ptrdiff_t SignedIndexType;
78 
81 
84 
88 
91 
94 
98 
102 
104  template <class T, class Enable = void>
105  struct value_type {
106  typedef typename T::value_type type;
107  };
108 
116  template <class AMatrix, class BMatrix, class CMatrix>
117  static void MatrixMultiplication(
118  const AMatrix& rA,
119  const BMatrix& rB,
120  CMatrix& rC
121  )
122  {
123  #ifdef _OPENMP
124  const int nt = omp_get_max_threads();
125  #else
126  const int nt = 1;
127  #endif
128 
129  if (nt > 16) {
130  MatrixMultiplicationRMerge(rA, rB, rC);
131  } else {
132  MatrixMultiplicationSaad(rA, rB, rC);
133  }
134  }
135 
143  template <class AMatrix, class BMatrix, class CMatrix>
145  const AMatrix& A,
146  const BMatrix& B,
147  CMatrix& C
148  )
149  {
150  typedef typename value_type<CMatrix>::type ValueType;
151 
152  // Auxiliar sizes
153  const SizeType nrows = A.size1();
154  const SizeType ncols = B.size2();
155 
156  // Exiting just in case of empty matrix
157  if ((nrows == 0) || (ncols == 0))
158  return void();
159 
160  // Get access to A, B and C data
161  const IndexType* index1_a = A.index1_data().begin();
162  const IndexType* index2_a = A.index2_data().begin();
163  const double* values_a = A.value_data().begin();
164  const IndexType* index1_b = B.index1_data().begin();
165  const IndexType* index2_b = B.index2_data().begin();
166  const double* values_b = B.value_data().begin();
167  IndexType* c_ptr = new IndexType[nrows + 1];
168 
169  c_ptr[0] = 0;
170 
171  #pragma omp parallel
172  {
174  for (int i_fill = 0; i_fill < static_cast<int>(ncols); ++i_fill)
175  marker[i_fill] = -1;
176 
177  #pragma omp for
178  for(int ia = 0; ia < static_cast<int>(nrows); ++ia) {
179  const IndexType row_begin_a = index1_a[ia];
180  const IndexType row_end_a = index1_a[ia+1];
181 
182  IndexType C_cols = 0;
183  for(IndexType ja = row_begin_a; ja < row_end_a; ++ja) {
184  const IndexType ca = index2_a[ja];
185  const IndexType row_begin_b = index1_b[ca];
186  const IndexType row_end_b = index1_b[ca+1];
187 
188  for(IndexType jb = row_begin_b; jb < row_end_b; ++jb) {
189  const IndexType cb = index2_b[jb];
190  if (marker[cb] != ia) {
191  marker[cb] = ia;
192  ++C_cols;
193  }
194  }
195  }
196  c_ptr[ia + 1] = C_cols;
197  }
198  }
199 
200  // We initialize the sparse matrix
201  std::partial_sum(c_ptr, c_ptr + nrows + 1, c_ptr);
202  const SizeType nonzero_values = c_ptr[nrows];
203  IndexType* aux_index2_c = new IndexType[nonzero_values];
204  ValueType* aux_val_c = new ValueType[nonzero_values];
205 
206  #pragma omp parallel
207  {
209  for (int i_fill = 0; i_fill < static_cast<int>(ncols); ++i_fill)
210  marker[i_fill] = -1;
211 
212  #pragma omp for
213  for(int ia = 0; ia < static_cast<int>(nrows); ++ia) {
214  const IndexType row_begin_a = index1_a[ia];
215  const IndexType row_end_a = index1_a[ia+1];
216 
217  const IndexType row_beg = c_ptr[ia];
218  IndexType row_end = row_beg;
219 
220  for(IndexType ja = row_begin_a; ja < row_end_a; ++ja) {
221  const IndexType ca = index2_a[ja];
222  const ValueType va = values_a[ja];
223 
224  const IndexType row_begin_b = index1_b[ca];
225  const IndexType row_end_b = index1_b[ca+1];
226 
227  for(IndexType jb = row_begin_b; jb < row_end_b; ++jb) {
228  const IndexType cb = index2_b[jb];
229  const ValueType vb = values_b[jb];
230 
231  if (marker[cb] < static_cast<SignedIndexType>(row_beg)) {
232  marker[cb] = row_end;
233  aux_index2_c[row_end] = cb;
234  aux_val_c[row_end] = va * vb;
235  ++row_end;
236  } else {
237  aux_val_c[marker[cb]] += va * vb;
238  }
239  }
240  }
241  }
242  }
243 
244  // We reorder the rows
245  SortRows(c_ptr, nrows, ncols, aux_index2_c, aux_val_c);
246 
247  // We fill the matrix
248  CreateSolutionMatrix(C, nrows, ncols, c_ptr, aux_index2_c, aux_val_c);
249 
250  // Release memory
251  delete[] c_ptr;
252  delete[] aux_index2_c;
253  delete[] aux_val_c;
254  }
255 
263  template <class AMatrix, class BMatrix, class CMatrix>
265  const AMatrix &A,
266  const BMatrix &B,
267  CMatrix &C
268  )
269  {
270  typedef typename value_type<CMatrix>::type ValueType;
271 
272  // Auxiliar sizes
273  const SizeType nrows = A.size1();
274  const SizeType ncols = B.size2();
275 
276  // Exiting just in case of empty matrix
277  if ((nrows == 0) || (ncols == 0))
278  return void();
279 
280  // Get access to A and B data
281  const IndexType* index1_a = A.index1_data().begin();
282  const IndexType* index2_a = A.index2_data().begin();
283  const double* values_a = A.value_data().begin();
284  const IndexType* index1_b = B.index1_data().begin();
285  const IndexType* index2_b = B.index2_data().begin();
286  const double* values_b = B.value_data().begin();
287 
288  IndexType max_row_width = 0;
289 
290  #pragma omp parallel
291  {
292  IndexType my_max = 0;
293 
294  #pragma omp for
295  for(int i = 0; i < static_cast<int>(nrows); ++i) {
296  const IndexType row_beg = index1_a[i];
297  const IndexType row_end = index1_a[i+1];
298 
299  IndexType row_width = 0;
300  for(IndexType j = row_beg; j < row_end; ++j) {
301  const IndexType a_col = index2_a[j];
302  row_width += index1_b[a_col + 1] - index1_b[a_col];
303  }
304  my_max = std::max(my_max, row_width);
305  }
306 
307  #pragma omp critical
308  max_row_width = std::max(max_row_width, my_max);
309  }
310 
311  #ifdef _OPENMP
312  const int nthreads = omp_get_max_threads();
313  #else
314  const int nthreads = 1;
315  #endif
316 
317  std::vector< std::vector<IndexType> > tmp_col(nthreads);
318  std::vector< std::vector<ValueType> > tmp_val(nthreads);
319 
320  for(int i = 0; i < nthreads; ++i) {
321  tmp_col[i].resize(3 * max_row_width);
322  tmp_val[i].resize(2 * max_row_width);
323  }
324 
325  // We create the c_ptr auxiliar variable
326  IndexType* c_ptr = new IndexType[nrows + 1];
327  c_ptr[0] = 0;
328 
329  #pragma omp parallel
330  {
331  #ifdef _OPENMP
332  const int tid = omp_get_thread_num();
333  #else
334  const int tid = 0;
335  #endif
336 
337  IndexType* t_col = &tmp_col[tid][0];
338 
339  #pragma omp for
340  for(int i = 0; i < static_cast<int>(nrows); ++i) {
341  const IndexType row_beg = index1_a[i];
342  const IndexType row_end = index1_a[i+1];
343 
344  c_ptr[i+1] = ProdRowWidth( index2_a + row_beg, index2_a + row_end, index1_b, index2_b, t_col, t_col + max_row_width, t_col + 2 * max_row_width );
345  }
346  }
347 
348  // We initialize the sparse matrix
349  std::partial_sum(c_ptr, c_ptr + nrows + 1, c_ptr);
350  const SizeType nonzero_values = c_ptr[nrows];
351  IndexType* aux_index2_c = new IndexType[nonzero_values];
352  ValueType* aux_val_c = new ValueType[nonzero_values];
353 
354  #pragma omp parallel
355  {
356  #ifdef _OPENMP
357  const int tid = omp_get_thread_num();
358  #else
359  const int tid = 0;
360  #endif
361 
362  IndexType* t_col = tmp_col[tid].data();
363  ValueType *t_val = tmp_val[tid].data();
364 
365  #pragma omp for
366  for(int i = 0; i < static_cast<int>(nrows); ++i) {
367  const IndexType row_beg = index1_a[i];
368  const IndexType row_end = index1_a[i+1];
369 
370  ProdRow(index2_a + row_beg, index2_a + row_end, values_a + row_beg,
371  index1_b, index2_b, values_b, aux_index2_c + c_ptr[i], aux_val_c + c_ptr[i], t_col, t_val, t_col + max_row_width, t_val + max_row_width );
372  }
373  }
374 
375  // We fill the matrix
376  CreateSolutionMatrix(C, nrows, ncols, c_ptr, aux_index2_c, aux_val_c);
377 
378  // Release memory
379  delete[] c_ptr;
380  delete[] aux_index2_c;
381  delete[] aux_val_c;
382  }
383 
389  template <class AMatrix, class BMatrix>
390  static void MatrixAdd(
391  AMatrix& A,
392  const BMatrix& B,
393  const double Factor = 1.0
394  )
395  {
396  typedef typename value_type<AMatrix>::type ValueType;
397 
398  // Auxiliar sizes
399  const SizeType nrows = A.size1();
400  const SizeType ncols = A.size2();
401 
402  /* Some checks */
403  // Exiting just in case of empty matrix
404  if ((nrows == 0) || (ncols == 0))
405  return void();
406 
407  KRATOS_ERROR_IF_NOT(nrows == B.size1()) << "The second matrix has a wrong number of rows" << std::endl;
408  KRATOS_ERROR_IF_NOT(ncols == B.size2()) << "The second matrix has a wrong number of columns" << std::endl;
409 
410  // Get access to A and B data
411  const IndexType* index1_a = A.index1_data().begin();
412  const IndexType* index2_a = A.index2_data().begin();
413  const double* values_a = A.value_data().begin();
414  const IndexType* index1_b = B.index1_data().begin();
415  const IndexType* index2_b = B.index2_data().begin();
416  const double* values_b = B.value_data().begin();
417 
418  IndexType* new_a_ptr = new IndexType[nrows + 1];
419  new_a_ptr[0] = 0;
420 
421  #pragma omp parallel
422  {
423  #pragma omp for
424  for(int ia = 0; ia < static_cast<int>(nrows); ++ia) {
425 
427  for (int i = 0; i < static_cast<int>(ncols); ++i)
428  marker[i] = -1;
429 
430  // Initialize
431  IndexType new_A_cols = 0;
432 
433  // Iterate over A
434  const IndexType row_begin_a = index1_a[ia];
435  const IndexType row_end_a = index1_a[ia+1];
436  for(IndexType ja = row_begin_a; ja < row_end_a; ++ja) {
437  const IndexType ca = index2_a[ja];
438  marker[ca] = 1;
439  ++new_A_cols;
440  }
441 
442  // Iterate over B
443  const IndexType row_begin_b = index1_b[ia];
444  const IndexType row_end_b = index1_b[ia+1];
445  for(IndexType jb = row_begin_b; jb < row_end_b; ++jb) {
446  const IndexType cb = index2_b[jb];
447  if (marker[cb] < 0) {
448  marker[cb] = 1;
449  ++new_A_cols;
450  }
451  }
452  new_a_ptr[ia + 1] = new_A_cols;
453  }
454  }
455 
456  // We initialize the sparse matrix
457  std::partial_sum(new_a_ptr, new_a_ptr + nrows + 1, new_a_ptr);
458  const SizeType nonzero_values = new_a_ptr[nrows];
459  IndexType* aux_index2_new_a = new IndexType[nonzero_values];
460  ValueType* aux_val_new_a = new ValueType[nonzero_values];
461 
462  #pragma omp parallel
463  {
464  #pragma omp for
465  for(int ia = 0; ia < static_cast<int>(nrows); ++ia) {
466 
468  for (int i = 0; i < static_cast<int>(ncols); ++i)
469  marker[i] = -1;
470 
471  // Initialize
472  const IndexType row_beg = new_a_ptr[ia];
473  IndexType row_end = row_beg;
474 
475  // Iterate over A
476  const IndexType row_begin_a = index1_a[ia];
477  const IndexType row_end_a = index1_a[ia+1];
478  for(IndexType ja = row_begin_a; ja < row_end_a; ++ja) {
479  const IndexType ca = index2_a[ja];
480  const ValueType va = values_a[ja];
481 
482  marker[ca] = row_end;
483  aux_index2_new_a[row_end] = ca;
484  aux_val_new_a[row_end] = va;
485  ++row_end;
486  }
487 
488  // Iterate over B
489  const IndexType row_begin_b = index1_b[ia];
490  const IndexType row_end_b = index1_b[ia+1];
491  for(IndexType jb = row_begin_b; jb < row_end_b; ++jb) {
492  const IndexType cb = index2_b[jb];
493  const ValueType vb = values_b[jb];
494 
495  if (marker[cb] < 0) {
496  marker[cb] = row_end;
497  aux_index2_new_a[row_end] = cb;
498  aux_val_new_a[row_end] = Factor * vb;
499  ++row_end;
500  } else {
501  aux_val_new_a[marker[cb]] += Factor * vb;
502  }
503  }
504  }
505  }
506 
507  // We reorder the rows
508  SortRows(new_a_ptr, nrows, ncols, aux_index2_new_a, aux_val_new_a);
509 
510  // We fill the matrix
511  CreateSolutionMatrix(A, nrows, ncols, new_a_ptr, aux_index2_new_a, aux_val_new_a);
512 
513  // Release memory
514  delete[] new_a_ptr;
515  delete[] aux_index2_new_a;
516  delete[] aux_val_new_a;
517  }
518 
524  template <class AMatrix, class BMatrix>
525  static void TransposeMatrix(
526  AMatrix& rA,
527  const BMatrix& rB,
528  const double Factor = 1.0
529  )
530  {
531  typedef typename value_type<AMatrix>::type ValueType;
532 
533  // Get access to B data
534  const IndexType* index1 = rB.index1_data().begin();
535  const IndexType* index2 = rB.index2_data().begin();
536  const ValueType* data = rB.value_data().begin();
537  const SizeType transpose_nonzero_values = rB.value_data().end() - rB.value_data().begin();
538 
539  const SizeType size_system_1 = rB.size1();
540  const SizeType size_system_2 = rB.size2();
541 
542  if (rA.size1() != size_system_2 || rA.size2() != size_system_1 ) {
543  rA.resize(size_system_2, size_system_1, false);
544  }
545 
546  IndexVectorType new_a_ptr(size_system_2 + 1);
547  #pragma omp parallel for
548  for (int i = 0; i < static_cast<int>(size_system_2 + 1); ++i)
549  new_a_ptr[i] = 0;
550  IndexVectorType aux_index2_new_a(transpose_nonzero_values);
551  DenseVector<ValueType> aux_val_new_a(transpose_nonzero_values);
552 
553  // Auxiliar index 1
554  const IndexType aux_1_index = 1;
555 
556  #pragma omp parallel for
557  for (int i=0; i<static_cast<int>(size_system_1); ++i) {
558  IndexType row_begin = index1[i];
559  IndexType row_end = index1[i+1];
560 
561  for (IndexType j=row_begin; j<row_end; j++) {
562  AtomicAdd(new_a_ptr[index2[j] + 1], aux_1_index);
563  }
564  }
565 
566  // We initialize the blocks sparse matrix
567  std::partial_sum(new_a_ptr.begin(), new_a_ptr.end(), &new_a_ptr[0]);
568 
569  IndexVectorType aux_indexes(size_system_2);
570  #pragma omp parallel for
571  for (int i = 0; i < static_cast<int>(size_system_2); ++i)
572  aux_indexes[i] = 0;
573 
574 // #pragma omp parallel for
575  for (int i=0; i<static_cast<int>(size_system_1); ++i) {
576  IndexType row_begin = index1[i];
577  IndexType row_end = index1[i+1];
578 
579  for (IndexType j=row_begin; j<row_end; j++) {
580  const IndexType current_row = index2[j];
581  const IndexType initial_position = new_a_ptr[current_row];
582 
583  const IndexType current_index = initial_position + aux_indexes[current_row];
584  aux_index2_new_a[current_index] = i;
585  aux_val_new_a[current_index] = Factor * data[j];
586 
587  // AtomicAdd(aux_indexes[current_row], aux_1_index);
588  aux_indexes[current_row] += 1;
589  }
590 
591  }
592 
593  // We reorder the rows
594  SortRows(&new_a_ptr[0], size_system_2, size_system_1, &aux_index2_new_a[0], &aux_val_new_a[0]);
595 
596  // We fill the matrix
597  CreateSolutionMatrix(rA, size_system_2, size_system_1, &new_a_ptr[0], &aux_index2_new_a[0], &aux_val_new_a[0]);
598  }
599 
609  template <class CMatrix, typename TSize, typename Ptr, typename IndexType, typename ValueType>
610  static inline void CreateSolutionMatrix(
611  CMatrix& C,
612  const TSize NRows,
613  const TSize NCols,
614  const Ptr* CPtr,
615  const IndexType* AuxIndex2C,
616  const ValueType* AuxValC
617  )
618  {
619  // Exiting just in case of empty matrix
620  if ((NRows == 0) || (NCols == 0))
621  return void();
622 
623  // Auxiliar values
624  const TSize nonzero_values = CPtr[NRows];
625 
626  C = CMatrix(NRows, NCols, nonzero_values);
627  IndexType* index1_c = C.index1_data().begin();
628  IndexType* index2_c = C.index2_data().begin();
629  double* values_c = C.value_data().begin();
630 
631  index1_c[0] = 0;
632  for (TSize i = 0; i < NRows; i++)
633  index1_c[i+1] = index1_c[i] + (CPtr[i+1] - CPtr[i]);
634 
635  #pragma omp parallel for
636  for (int i = 0; i < static_cast<int>(nonzero_values); i++) {
637  KRATOS_DEBUG_ERROR_IF(AuxIndex2C[i] > static_cast<IndexType>(NCols)) << "Index " << AuxIndex2C[i] <<" is greater than the number of columns " << NCols << std::endl;
638  index2_c[i] = AuxIndex2C[i];
639  values_c[i] = AuxValC[i];
640  }
641 
642  C.set_filled(NRows+1, nonzero_values);
643  }
644 
653  template <typename TSize, typename Col, typename TIndexType, typename ValueType>
654  static inline void SortRows(
655  const TIndexType* CPtr,
656  const TSize NRows,
657  const TSize NCols,
658  Col* Columns,
659  ValueType* Values
660  )
661  {
662  #pragma omp parallel
663  {
664  #pragma omp for
665  for (int i_row=0; i_row<static_cast<int>(NRows); i_row++) {
666  const TIndexType row_beg = CPtr[i_row];
667  const TIndexType row_end = CPtr[i_row + 1];
668 
669  for(IndexType j = 1; j < row_end - row_beg; ++j) {
670  const IndexType c = Columns[j + row_beg];
671  const double v = Values[j + row_beg];
672 
673  SignedIndexType i = j - 1;
674 
675  while(i >= 0 && Columns[i + row_beg] > c) {
676  KRATOS_DEBUG_ERROR_IF(Columns[i + row_beg] > static_cast<Col>(NCols)) << " Index for column: " << i + row_beg << ". Index " << Columns[i + row_beg] <<" is greater than the number of columns " << NCols << std::endl;
677  Columns[i + 1 + row_beg] = Columns[i + row_beg];
678  Values[i + 1 + row_beg] = Values[i + row_beg];
679  i--;
680  }
681 
682  Columns[i + 1 + row_beg] = c;
683  Values[i + 1 + row_beg] = v;
684  }
685  }
686  }
687  }
688 
695  static inline void AssembleSparseMatrixByBlocks(
696  CompressedMatrix& rMatrix,
697  const DenseMatrix<CompressedMatrix*>& rMatricespBlocks,
698  DenseMatrix<double> ContributionCoefficients = DenseMatrix<double>(0,0),
699  DenseMatrix<bool> TransposeBlocks = DenseMatrix<bool>(0,0)
700  )
701  {
702  const SizeType number_of_rows_blocks = rMatricespBlocks.size1();
703  const SizeType number_of_columns_blocks = rMatricespBlocks.size2();
704 
705  // Fill the matrices if they are empty
706  if (ContributionCoefficients.size1() == 0 && ContributionCoefficients.size2() == 0) {
707  ContributionCoefficients.resize(number_of_rows_blocks, number_of_columns_blocks);
708  for (IndexType i = 0; i < number_of_rows_blocks; ++i) {
709  for (IndexType j = 0; j < number_of_columns_blocks; ++j) {
710  ContributionCoefficients(i, j) = 1.0;
711  }
712  }
713  } else {
714  KRATOS_ERROR_IF(ContributionCoefficients.size1() != number_of_rows_blocks || ContributionCoefficients.size2() != number_of_columns_blocks) << "The ContributionCoefficients dimensions" << ContributionCoefficients.size1() << " and " << ContributionCoefficients.size2() << "do not coincide with the dimensions of rMatricespBlocks" << number_of_rows_blocks << "and " << number_of_columns_blocks << std::endl;
715  }
716  if (TransposeBlocks.size1() == 0 && TransposeBlocks.size2() == 0) {
717  TransposeBlocks.resize(number_of_rows_blocks, number_of_columns_blocks);
718  for (IndexType i = 0; i < number_of_rows_blocks; ++i) {
719  for (IndexType j = 0; j < number_of_rows_blocks; ++j) {
720  TransposeBlocks(i, j) = false;
721  }
722  }
723  } else {
724  KRATOS_ERROR_IF(TransposeBlocks.size1() != number_of_rows_blocks || TransposeBlocks.size2() != number_of_columns_blocks) << "The TransposeBlocks dimensions" << TransposeBlocks.size1() << " and " << TransposeBlocks.size2() << "do not coincide with the dimensions of rMatricespBlocks" << number_of_rows_blocks << "and " << number_of_columns_blocks << std::endl;
725  }
726 
727  // Compute total size and check consistency of the different blocks
728  SizeType nrows = 0, ncols = 0;
729  std::vector<SizeType> row_sizes(number_of_rows_blocks);
730  std::vector<SizeType> column_sizes(number_of_columns_blocks);
731  for (int i=0; i<static_cast<int>(number_of_rows_blocks); ++i) {
732  if (TransposeBlocks(i, 0)) {
733  row_sizes[i] = (*rMatricespBlocks(i, 0)).size2();
734  } else {
735  row_sizes[i] = (*rMatricespBlocks(i, 0)).size1();
736  }
737  nrows += row_sizes[i];
738  }
739  for (int j=0; j<static_cast<int>(number_of_columns_blocks); ++j) {
740  if (TransposeBlocks(0, j)) {
741  column_sizes[j] = (*rMatricespBlocks(0, j)).size1();
742  } else {
743  column_sizes[j] = (*rMatricespBlocks(0, j)).size2();
744  }
745  ncols += column_sizes[j];
746  }
747 
748  // Check consistency of all blocks
749  for (int i=0; i<static_cast<int>(number_of_rows_blocks); ++i) {
750  for (int j=0; j<static_cast<int>(number_of_columns_blocks); ++j) {
751  if (TransposeBlocks(i, j)) {
752  KRATOS_ERROR_IF((*rMatricespBlocks(i, j)).size2() != row_sizes[i] || (*rMatricespBlocks(i, j)).size1() != column_sizes[j]) << " Not consistent size in block " << i << ", " << j << ".\t" << (*rMatricespBlocks(i, j)).size2() << ", " << (*rMatricespBlocks(i, j)).size1() << " vs " << row_sizes[i] << ", " << row_sizes[j] << std::endl;
753  } else {
754  KRATOS_ERROR_IF((*rMatricespBlocks(i, j)).size1() != row_sizes[i] || (*rMatricespBlocks(i, j)).size2() != column_sizes[j]) << " Not consistent size in block " << i << ", " << j << ".\t" << (*rMatricespBlocks(i, j)).size1() << ", " << (*rMatricespBlocks(i, j)).size2() << " vs " << row_sizes[i] << ", " << row_sizes[j] << std::endl;
755  }
756  }
757  }
758  // Exiting just in case of empty matrix
759  if ((nrows == 0) || (ncols == 0))
760  return void();
761 
762  // We will compute nonzero terms
763  IndexType* matrix_ptr = new IndexType[nrows + 1];
764  #pragma omp parallel for
765  for (int i = 0; i < static_cast<int>(nrows + 1); ++i)
766  matrix_ptr[i] = 0;
767 
768  #ifdef KRATOS_DEBUG
769  IndexType check_non_zero = 0;
770  DenseMatrix<IndexType> check_non_zero_blocks(number_of_rows_blocks, number_of_columns_blocks);
771  for (int i=0; i<static_cast<int>(number_of_rows_blocks); ++i) {
772  for (int j=0; j<static_cast<int>(number_of_columns_blocks); ++j) {
773  check_non_zero_blocks(i, j) = 0;
774  }
775  }
776  #endif
777 
778  #pragma omp parallel
779  {
780  #pragma omp for
781  for (int i=0; i<static_cast<int>(number_of_rows_blocks); ++i) {
782  for (int k=0; k<static_cast<int>(row_sizes[i]); ++k) {
783  IndexType matrix_cols_aux = 0;
784  for (int j=0; j<static_cast<int>(number_of_columns_blocks); ++j) {
785  #ifdef KRATOS_DEBUG
786  IndexType partial_matrix_cols_aux = 0;
787  #endif
788  // Skip if empty matrix
789  CompressedMatrix& r_matrix = *rMatricespBlocks(i, j);
790  if (r_matrix.nnz() > 0) {
791  if (TransposeBlocks(i, j)) {
792  // We compute the transposed matrix
793  const SizeType size_system_1 = r_matrix.size1();
794  const SizeType size_system_2 = r_matrix.size2();
795  CompressedMatrix transpose(size_system_2, size_system_1);
796  TransposeMatrix<CompressedMatrix, CompressedMatrix>(transpose, r_matrix);
797  ComputeNonZeroBlocks(transpose, k, matrix_cols_aux);
798  #ifdef KRATOS_DEBUG
799  ComputeNonZeroBlocks(transpose, k, partial_matrix_cols_aux);
800  #endif
801  } else {
802  ComputeNonZeroBlocks(r_matrix, k, matrix_cols_aux);
803  #ifdef KRATOS_DEBUG
804  ComputeNonZeroBlocks(r_matrix, k, partial_matrix_cols_aux);
805  #endif
806  }
807  }
808  #ifdef KRATOS_DEBUG
809  check_non_zero_blocks(i, j) += partial_matrix_cols_aux;
810  #endif
811  }
812  IndexType& r_matrix_ptr_value = matrix_ptr[std::accumulate(row_sizes.begin(), row_sizes.begin() + i, 0) + k + 1];
813  AtomicAdd(r_matrix_ptr_value, matrix_cols_aux);
814 
815  #ifdef KRATOS_DEBUG
816  AtomicAdd(check_non_zero, matrix_cols_aux);
817  #endif
818  }
819  }
820  }
821 
822  // Auxiliar values
823  std::partial_sum(matrix_ptr, matrix_ptr + nrows + 1, matrix_ptr);
824  const SizeType nonzero_values = matrix_ptr[nrows];
825 
826  #ifdef KRATOS_DEBUG
827  SizeType total_nnz = 0;
828  for (int i=0; i<static_cast<int>(number_of_rows_blocks); ++i) {
829  for (int j=0; j<static_cast<int>(number_of_columns_blocks); ++j) {
830  const SizeType block_nnz = rMatricespBlocks(i, j)->nnz();
831  KRATOS_ERROR_IF_NOT(check_non_zero_blocks(i, j) == block_nnz) << "Inconsistent number of non-zero values. Check 0: " << block_nnz << " vs " << check_non_zero_blocks(i, j) << ". Block: " << i << ", " << j << std::endl;
832  total_nnz += block_nnz;
833  }
834  }
835  KRATOS_ERROR_IF_NOT(check_non_zero == total_nnz) << "Inconsistent number of non-zero values. Check 1: " << total_nnz << " vs " << check_non_zero << std::endl;
836  KRATOS_ERROR_IF_NOT(nonzero_values == total_nnz) << "Inconsistent number of non-zero values. Check 2: " << total_nnz << " vs " << nonzero_values << std::endl;
837  #endif
838 
839  // Initialize matrix with the corresponding non-zero values
840  rMatrix = CompressedMatrix(nrows, ncols, nonzero_values);
841 
842  // Fill the new matrix
843  double* Matrix_values = rMatrix.value_data().begin();
844  IndexType* Matrix_index1 = rMatrix.index1_data().begin();
845  IndexType* Matrix_index2 = rMatrix.index2_data().begin();
846 
847  Matrix_index1[0] = 0;
848  for (IndexType i = 0; i < nrows; ++i)
849  Matrix_index1[i+1] = Matrix_index1[i] + (matrix_ptr[i + 1] - matrix_ptr[i]);
850 
851  #pragma omp parallel
852  {
853  #pragma omp for
854  for (int i=0; i<static_cast<int>(number_of_rows_blocks); ++i) {
855  for (int k=0; k<static_cast<int>(row_sizes[i]); ++k) {
856  const IndexType row_beg = matrix_ptr[std::accumulate(row_sizes.begin(), row_sizes.begin() + i, 0) + k];
857  IndexType row_end = row_beg;
858  for (int j=0; j<static_cast<int>(number_of_columns_blocks); ++j) {
859  const SizeType initial_index_column = std::accumulate(column_sizes.begin(), column_sizes.begin() + j, 0);
860 
861  // Skip if empty matrix
862  CompressedMatrix& r_matrix = *rMatricespBlocks(i, j);
863  if (r_matrix.nnz() > 0) {
864  if (TransposeBlocks(i, j)) {
865  // We compute the transposed matrix
866  const SizeType size_system_1 = r_matrix.size1();
867  const SizeType size_system_2 = r_matrix.size2();
868  CompressedMatrix transpose(size_system_2, size_system_1);
869  TransposeMatrix<CompressedMatrix, CompressedMatrix>(transpose, r_matrix);
870  ComputeAuxiliarValuesBlocks(transpose, Matrix_index2, Matrix_values, k, row_end, initial_index_column, ContributionCoefficients(i, j));
871  } else {
872  ComputeAuxiliarValuesBlocks(r_matrix, Matrix_index2, Matrix_values, k, row_end, initial_index_column, ContributionCoefficients(i, j));
873  }
874  }
875  }
876  }
877  }
878  }
879 
880  // Close the matrix
881  rMatrix.set_filled(nrows+1, nonzero_values);
882 
883  // Release memory
884  delete[] matrix_ptr;
885  }
886 
893  static inline void ComputeNonZeroBlocks(
894  const CompressedMatrix& rMatrix,
895  const int CurrentRow,
896  IndexType& rNonZeroColsAux2
897  )
898  {
899  // Get access to aux_K data
900  const IndexType* aux_matrix_index1 = rMatrix.index1_data().begin();
901 
902  const IndexType row_begin = aux_matrix_index1[CurrentRow];
903  const IndexType row_end = aux_matrix_index1[CurrentRow + 1];
904 
905  for (IndexType j=row_begin; j<row_end; j++) {
906  ++rNonZeroColsAux2;
907  }
908  }
909 
919  static inline void ComputeAuxiliarValuesBlocks(
920  const CompressedMatrix& rMatrix,
921  IndexType* AuxIndex2,
922  double* AuxVals,
923  const int CurrentRow,
924  IndexType& RowEnd,
925  const SizeType InitialIndexColumn,
926  const double ContributionCoefficient = 1.0
927  )
928  {
929  // Get access to aux_K data
930  const double* aux_values = rMatrix.value_data().begin();
931  const IndexType* aux_Matrix_index1 = rMatrix.index1_data().begin();
932  const IndexType* aux_Matrix_index2 = rMatrix.index2_data().begin();
933 
934  const IndexType aux_Matrix_row_begin = aux_Matrix_index1[CurrentRow];
935  const IndexType aux_Matrix_row_end = aux_Matrix_index1[CurrentRow + 1];
936 
937  for (IndexType j=aux_Matrix_row_begin; j<aux_Matrix_row_end; j++) {
938  const IndexType col_index = InitialIndexColumn + aux_Matrix_index2[j];
939  AuxIndex2[RowEnd] = col_index;
940  AuxVals[RowEnd] = ContributionCoefficient * aux_values[j];
941  ++RowEnd;
942  }
943  }
944 
948 
952 
956 
958  std::string Info() const
959  {
960  return "SparseMatrixMultiplicationUtility";
961  }
962 
964  void PrintInfo (std::ostream& rOStream) const
965  {
966  rOStream << "SparseMatrixMultiplicationUtility";
967  }
968 
970  void PrintData (std::ostream& rOStream) const
971  {
972  }
973 
977 
979 protected:
982 
986 
990 
994 
998 
1002 
1006 
1008 private:
1011 
1015 
1019 
1023 
1033  template <bool TNeedOut, class TIndex>
1034  static TIndex* MergeRows(
1035  const TIndex* Column1,
1036  const TIndex* Column1End,
1037  const TIndex* Column2,
1038  const TIndex* Column2End,
1039  TIndex* Column3
1040  )
1041  {
1042  while(Column1 != Column1End && Column2 != Column2End) {
1043  TIndex c1 = *Column1;
1044  TIndex c2 = *Column2;
1045 
1046  if (c1 < c2) {
1047  if constexpr (TNeedOut) *Column3 = c1;
1048  ++Column1;
1049  } else if (c1 == c2) {
1050  if constexpr (TNeedOut) *Column3 = c1;
1051  ++Column1;
1052  ++Column2;
1053  } else {
1054  if constexpr (TNeedOut) *Column3 = c2;
1055  ++Column2;
1056  }
1057  ++Column3;
1058  }
1059 
1060  if constexpr (TNeedOut) {
1061  if (Column1 < Column1End) {
1062  return std::copy(Column1, Column1End, Column3);
1063  } else if (Column2 < Column2End) {
1064  return std::copy(Column2, Column2End, Column3);
1065  } else {
1066  return Column3;
1067  }
1068  } else {
1069  return Column3 + (Column1End - Column1) + (Column2End - Column2);
1070  }
1071  }
1072 
1087  template <class TIndex, class TValueType>
1088  static TIndex* MergeRows(
1089  const TValueType &rAlpha1,
1090  const TIndex* Column1,
1091  const TIndex* Column1End,
1092  const TValueType *Value1,
1093  const TValueType &rAlpha2,
1094  const TIndex* Column2,
1095  const TIndex* Column2End,
1096  const TValueType *Value2,
1097  TIndex* Column3,
1098  TValueType *Value3
1099  )
1100  {
1101  while(Column1 != Column1End && Column2 != Column2End) {
1102  TIndex c1 = *Column1;
1103  TIndex c2 = *Column2;
1104 
1105  if (c1 < c2) {
1106  ++Column1;
1107 
1108  *Column3 = c1;
1109  *Value3 = rAlpha1 * (*Value1++);
1110  } else if (c1 == c2) {
1111  ++Column1;
1112  ++Column2;
1113 
1114  *Column3 = c1;
1115  *Value3 = rAlpha1 * (*Value1++) + rAlpha2 * (*Value2++);
1116  } else {
1117  ++Column2;
1118 
1119  *Column3 = c2;
1120  *Value3 = rAlpha2 * (*Value2++);
1121  }
1122 
1123  ++Column3;
1124  ++Value3;
1125  }
1126 
1127  while(Column1 < Column1End) {
1128  *Column3++ = *Column1++;
1129  *Value3++ = rAlpha1 * (*Value1++);
1130  }
1131 
1132  while(Column2 < Column2End) {
1133  *Column3++ = *Column2++;
1134  *Value3++ = rAlpha2 * (*Value2++);
1135  }
1136 
1137  return Column3;
1138  }
1139 
1152  template <class TIndex>
1153  static TIndex ProdRowWidth(
1154  const TIndex* AColumn,
1155  const TIndex* AColumnEnd,
1156  const TIndex* BPtr,
1157  const TIndex* BColumn,
1158  TIndex* Tmp1Column,
1159  TIndex* Tmp2Column,
1160  TIndex* Tmp3Column
1161  )
1162  {
1163  const TIndex nrow = AColumnEnd - AColumn;
1164 
1165  /* No rows to merge, nothing to do */
1166  if (nrow == 0) return 0;
1167 
1168  /* Single row, just copy it to output */
1169  if (nrow == 1) return BPtr[*AColumn + 1] - BPtr[*AColumn];
1170 
1171  /* Two rows, merge them */
1172  if (nrow == 2) {
1173  int a1 = AColumn[0];
1174  int a2 = AColumn[1];
1175 
1176  return MergeRows<false>( BColumn + BPtr[a1], BColumn + BPtr[a1+1], BColumn + BPtr[a2], BColumn + BPtr[a2+1], Tmp1Column) - Tmp1Column;
1177  }
1178 
1179  /* Generic case (more than two rows).
1180  *
1181  * Merge rows by pairs, then merge the results together.
1182  * When merging two rows, the result is always wider (or equal).
1183  * Merging by pairs allows to work with short rows as often as possible.
1184  */
1185  // Merge first two.
1186  TIndex a1 = *AColumn++;
1187  TIndex a2 = *AColumn++;
1188  TIndex c_col1 = MergeRows<true>( BColumn + BPtr[a1], BColumn + BPtr[a1+1], BColumn + BPtr[a2], BColumn + BPtr[a2+1], Tmp1Column ) - Tmp1Column;
1189 
1190  // Go by pairs.
1191  while(AColumn + 1 < AColumnEnd) {
1192  a1 = *AColumn++;
1193  a2 = *AColumn++;
1194 
1195  TIndex c_col2 = MergeRows<true>( BColumn + BPtr[a1], BColumn + BPtr[a1+1], BColumn + BPtr[a2], BColumn + BPtr[a2+1], Tmp2Column ) - Tmp2Column;
1196 
1197  if (AColumn == AColumnEnd) {
1198  return MergeRows<false>( Tmp1Column, Tmp1Column + c_col1, Tmp2Column, Tmp2Column + c_col2, Tmp3Column ) - Tmp3Column;
1199  } else {
1200  c_col1 = MergeRows<true>( Tmp1Column, Tmp1Column + c_col1, Tmp2Column, Tmp2Column + c_col2, Tmp3Column ) - Tmp3Column;
1201 
1202  std::swap(Tmp1Column, Tmp3Column);
1203  }
1204  }
1205 
1206  // Merge the tail.
1207  a2 = *AColumn;
1208  return MergeRows<false>( Tmp1Column, Tmp1Column + c_col1, BColumn + BPtr[a2], BColumn + BPtr[a2+1], Tmp2Column ) - Tmp2Column;
1209  }
1210 
1227  template <class TIndex, class TValueType>
1228  static void ProdRow(
1229  const TIndex* AColumn,
1230  const TIndex* AColumnEnd,
1231  const TValueType *AValue,
1232  const TIndex* BPtr,
1233  const TIndex* BColumn,
1234  const TValueType *BValue,
1235  TIndex* OutColumn,
1236  TValueType *OutValue,
1237  TIndex* Tmp2Column,
1238  TValueType *Tmp2Value,
1239  TIndex* Tmp3Column,
1240  TValueType *Tmp3Value
1241  )
1242  {
1243  const TIndex nrow = AColumnEnd - AColumn;
1244 
1245  /* No rows to merge, nothing to do */
1246  if (nrow == 0) return;
1247 
1248  /* Single row, just copy it to output */
1249  if (nrow == 1) {
1250  TIndex ac = *AColumn;
1251  TValueType av = *AValue;
1252 
1253  const TValueType *bv = BValue + BPtr[ac];
1254  const TIndex* bc = BColumn + BPtr[ac];
1255  const TIndex* be = BColumn + BPtr[ac+1];
1256 
1257  while(bc != be) {
1258  *OutColumn++ = *bc++;
1259  *OutValue++ = av * (*bv++);
1260  }
1261 
1262  return;
1263  }
1264 
1265  /* Two rows, merge them */
1266  if (nrow == 2) {
1267  TIndex ac1 = AColumn[0];
1268  TIndex ac2 = AColumn[1];
1269 
1270  TValueType av1 = AValue[0];
1271  TValueType av2 = AValue[1];
1272 
1273  MergeRows( av1, BColumn + BPtr[ac1], BColumn + BPtr[ac1+1], BValue + BPtr[ac1], av2, BColumn + BPtr[ac2], BColumn + BPtr[ac2+1], BValue + BPtr[ac2], OutColumn, OutValue );
1274  }
1275 
1276  /* Generic case (more than two rows).
1277  *
1278  * Merge rows by pairs, then merge the results together.
1279  * When merging two rows, the result is always wider (or equal).
1280  * Merging by pairs allows to work with short rows as often as possible.
1281  */
1282  // Merge first two.
1283  TIndex ac1 = *AColumn++;
1284  TIndex ac2 = *AColumn++;
1285 
1286  TValueType av1 = *AValue++;
1287  TValueType av2 = *AValue++;
1288 
1289  TIndex* tm1_col = OutColumn;
1290  TValueType *tm1_val = OutValue;
1291 
1292  TIndex c_col1 = MergeRows( av1, BColumn + BPtr[ac1], BColumn + BPtr[ac1+1], BValue + BPtr[ac1], av2, BColumn + BPtr[ac2], BColumn + BPtr[ac2+1], BValue + BPtr[ac2], tm1_col, tm1_val ) - tm1_col;
1293 
1294  // Go by pairs.
1295  while(AColumn + 1 < AColumnEnd) {
1296  ac1 = *AColumn++;
1297  ac2 = *AColumn++;
1298 
1299  av1 = *AValue++;
1300  av2 = *AValue++;
1301 
1302  TIndex c_col2 = MergeRows( av1, BColumn + BPtr[ac1], BColumn + BPtr[ac1+1], BValue + BPtr[ac1], av2, BColumn + BPtr[ac2], BColumn + BPtr[ac2+1], BValue + BPtr[ac2], Tmp2Column, Tmp2Value ) - Tmp2Column;
1303 
1304  c_col1 = MergeRows( amgcl::math::identity<TValueType>(), tm1_col, tm1_col + c_col1, tm1_val, amgcl::math::identity<TValueType>(), Tmp2Column, Tmp2Column + c_col2, Tmp2Value, Tmp3Column, Tmp3Value ) - Tmp3Column;
1305 
1306  std::swap(Tmp3Column, tm1_col);
1307  std::swap(Tmp3Value, tm1_val);
1308  }
1309 
1310  // Merge the tail if there is one.
1311  if (AColumn < AColumnEnd) {
1312  ac2 = *AColumn++;
1313  av2 = *AValue++;
1314 
1315  c_col1 = MergeRows( amgcl::math::identity<TValueType>(), tm1_col, tm1_col + c_col1, tm1_val, av2, BColumn + BPtr[ac2], BColumn + BPtr[ac2+1], BValue + BPtr[ac2], Tmp3Column, Tmp3Value ) - Tmp3Column;
1316 
1317  std::swap(Tmp3Column, tm1_col);
1318  std::swap(Tmp3Value, tm1_val);
1319  }
1320 
1321  // If we are lucky, tm1 now points to out.
1322  // Otherwise, copy the results.
1323  if (tm1_col != OutColumn) {
1324  std::copy(tm1_col, tm1_col + c_col1, OutColumn);
1325  std::copy(tm1_val, tm1_val + c_col1, OutValue);
1326  }
1327 
1328  return;
1329  }
1330 
1334 
1338 
1342 
1344 
1345 }; // Class SparseMatrixMultiplicationUtility
1346 
1348 
1351 
1352 
1356 
1357 // /****************************** INPUT STREAM FUNCTION ******************************/
1358 // /***********************************************************************************/
1359 //
1360 // template<class TPointType, class TPointerType>
1361 // inline std::istream& operator >> (std::istream& rIStream,
1362 // SparseMatrixMultiplicationUtility& rThis);
1363 //
1364 // /***************************** OUTPUT STREAM FUNCTION ******************************/
1365 // /***********************************************************************************/
1366 //
1367 // template<class TPointType, class TPointerType>
1368 // inline std::ostream& operator << (std::ostream& rOStream,
1369 // const SparseMatrixMultiplicationUtility& rThis)
1370 // {
1371 // return rOStream;
1372 // }
1373 
1375 
1376 } // namespace Kratos.
1377 
1378 #endif // KRATOS_TREE_CONTACT_SEARCH_H_INCLUDED defined
Definition: amatrix_interface.h:41
void resize(std::size_t NewSize1, std::size_t NewSize2, bool preserve=0)
Definition: amatrix_interface.h:224
iterator end()
Definition: amatrix_interface.h:243
iterator begin()
Definition: amatrix_interface.h:241
An utility to multiply sparse matrix in Ublas.
Definition: sparse_matrix_multiplication_utility.h:62
static void MatrixAdd(AMatrix &A, const BMatrix &B, const double Factor=1.0)
The first is a method in order to sum to sparse matrices in a efficient way.
Definition: sparse_matrix_multiplication_utility.h:390
virtual ~SparseMatrixMultiplicationUtility()=default
Desctructor.
static void ComputeAuxiliarValuesBlocks(const CompressedMatrix &rMatrix, IndexType *AuxIndex2, double *AuxVals, const int CurrentRow, IndexType &RowEnd, const SizeType InitialIndexColumn, const double ContributionCoefficient=1.0)
This is a method to compute the contribution of the auxiliar blocks.
Definition: sparse_matrix_multiplication_utility.h:919
static void TransposeMatrix(AMatrix &rA, const BMatrix &rB, const double Factor=1.0)
This method computes of the transpose matrix of a given matrix.
Definition: sparse_matrix_multiplication_utility.h:525
SparseMatrixMultiplicationUtility()
Default constructor.
Definition: sparse_matrix_multiplication_utility.h:90
static void AssembleSparseMatrixByBlocks(CompressedMatrix &rMatrix, const DenseMatrix< CompressedMatrix * > &rMatricespBlocks, DenseMatrix< double > ContributionCoefficients=DenseMatrix< double >(0, 0), DenseMatrix< bool > TransposeBlocks=DenseMatrix< bool >(0, 0))
This method assembles several sparse matrices into one large sparse matrix.
Definition: sparse_matrix_multiplication_utility.h:695
std::ptrdiff_t SignedIndexType
The signed index type.
Definition: sparse_matrix_multiplication_utility.h:77
std::size_t SizeType
The size type.
Definition: sparse_matrix_multiplication_utility.h:71
DenseVector< IndexType > IndexVectorType
A vector of indexes.
Definition: sparse_matrix_multiplication_utility.h:80
static void ComputeNonZeroBlocks(const CompressedMatrix &rMatrix, const int CurrentRow, IndexType &rNonZeroColsAux2)
This is a method to check the block containing nonzero values.
Definition: sparse_matrix_multiplication_utility.h:893
void PrintInfo(std::ostream &rOStream) const
Print information about this object.
Definition: sparse_matrix_multiplication_utility.h:964
static void MatrixMultiplicationSaad(const AMatrix &A, const BMatrix &B, CMatrix &C)
The first is an OpenMP-enabled modification of classic algorithm from Saad.
Definition: sparse_matrix_multiplication_utility.h:144
static void CreateSolutionMatrix(CMatrix &C, const TSize NRows, const TSize NCols, const Ptr *CPtr, const IndexType *AuxIndex2C, const ValueType *AuxValC)
This method is designed to create the final solution sparse matrix from the auxiliar values.
Definition: sparse_matrix_multiplication_utility.h:610
static void MatrixMultiplicationRMerge(const AMatrix &A, const BMatrix &B, CMatrix &C)
Row-merge algorithm from Rupp et al.
Definition: sparse_matrix_multiplication_utility.h:264
void PrintData(std::ostream &rOStream) const
Print object's data.
Definition: sparse_matrix_multiplication_utility.h:970
DenseVector< SignedIndexType > SignedIndexVectorType
A vector of indexes (signed)
Definition: sparse_matrix_multiplication_utility.h:83
std::string Info() const
Turn back information as a string.
Definition: sparse_matrix_multiplication_utility.h:958
static void MatrixMultiplication(const AMatrix &rA, const BMatrix &rB, CMatrix &rC)
Matrix-matrix product C = A·B @detail This method uses a template for each matrix.
Definition: sparse_matrix_multiplication_utility.h:117
static void SortRows(const TIndexType *CPtr, const TSize NRows, const TSize NCols, Col *Columns, ValueType *Values)
This method is designed to reorder the rows by columns.
Definition: sparse_matrix_multiplication_utility.h:654
std::size_t IndexType
The index type.
Definition: sparse_matrix_multiplication_utility.h:74
KRATOS_CLASS_POINTER_DEFINITION(SparseMatrixMultiplicationUtility)
Pointer definition of TreeContactSearch.
#define KRATOS_ERROR_IF_NOT(conditional)
Definition: exception.h:163
#define KRATOS_ERROR_IF(conditional)
Definition: exception.h:162
#define KRATOS_DEBUG_ERROR_IF(conditional)
Definition: exception.h:171
ncols
Definition: GenerateCN.py:97
static double max(double a, double b)
Definition: GeometryFunctions.h:79
REF: G. R. Cowper, GAUSSIAN QUADRATURE FORMULAS FOR TRIANGLES.
Definition: mesh_condition.cpp:21
void AtomicAdd(TDataType &target, const TDataType &value)
Definition: atomic_utilities.h:55
boost::numeric::ublas::compressed_matrix< double > CompressedMatrix
Definition: ublas_interface.h:94
marker
Definition: exact_hinsberg_test.py:111
v
Definition: generate_convection_diffusion_explicit_element.py:114
int C
Definition: generate_hyper_elastic_simo_taylor_neo_hookean.py:27
c
Definition: generate_weakly_compressible_navier_stokes_element.py:108
data
Definition: mesh_to_mdpa_converter.py:59
int k
Definition: quadrature.py:595
int j
Definition: quadrature.py:648
A
Definition: sensitivityMatrix.py:70
B
Definition: sensitivityMatrix.py:76
integer i
Definition: TensorModule.f:17
Metafunction that returns value type of a matrix or a vector type.
Definition: sparse_matrix_multiplication_utility.h:105
T::value_type type
Definition: sparse_matrix_multiplication_utility.h:106