1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2008 - 2019 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15 
16 #ifndef dealii_chunk_sparse_matrix_templates_h
17 #define dealii_chunk_sparse_matrix_templates_h
18 
19 
20 #include <deal.II/base/config.h>
21 
22 #include <deal.II/base/parallel.h>
23 #include <deal.II/base/template_constraints.h>
24 
25 #include <deal.II/lac/chunk_sparse_matrix.h>
26 #include <deal.II/lac/full_matrix.h>
27 #include <deal.II/lac/vector.h>
28 
29 #include <algorithm>
30 #include <cmath>
31 #include <functional>
32 #include <iomanip>
33 #include <numeric>
34 #include <ostream>
35 #include <vector>
36 
37 DEAL_II_NAMESPACE_OPEN
38 
39 
40 namespace internal
41 {
42   // TODO: the goal of the ChunkSparseMatrix class is to stream data and use
43   // the vectorization features of modern processors. to make this happen,
44   // we will have to vectorize the functions in the following namespace, either
45   // by hand or by using, for example, optimized BLAS versions for them.
46   namespace ChunkSparseMatrixImplementation
47   {
48     /**
49      * Declare type for container size.
50      */
51     using size_type = types::global_dof_index;
52 
53     /**
54      * Add the result of multiplying a chunk of size chunk_size times
55      * chunk_size by a source vector fragment of size chunk_size to the
56      * destination vector fragment.
57      */
58     template <typename MatrixIterator,
59               typename SrcIterator,
60               typename DstIterator>
61     inline void
chunk_vmult_add(const size_type chunk_size,const MatrixIterator matrix,const SrcIterator src,DstIterator dst)62     chunk_vmult_add(const size_type      chunk_size,
63                     const MatrixIterator matrix,
64                     const SrcIterator    src,
65                     DstIterator          dst)
66     {
67       MatrixIterator matrix_row = matrix;
68 
69       for (size_type i = 0; i < chunk_size; ++i, matrix_row += chunk_size)
70         {
71           typename std::iterator_traits<DstIterator>::value_type sum = 0;
72 
73           for (size_type j = 0; j < chunk_size; ++j)
74             sum += matrix_row[j] * src[j];
75 
76           dst[i] += sum;
77         }
78     }
79 
80 
81 
82     /**
83      * Like the previous function, but subtract. We need this for computing
84      * the residual.
85      */
86     template <typename MatrixIterator,
87               typename SrcIterator,
88               typename DstIterator>
89     inline void
chunk_vmult_subtract(const size_type chunk_size,const MatrixIterator matrix,const SrcIterator src,DstIterator dst)90     chunk_vmult_subtract(const size_type      chunk_size,
91                          const MatrixIterator matrix,
92                          const SrcIterator    src,
93                          DstIterator          dst)
94     {
95       MatrixIterator matrix_row = matrix;
96 
97       for (size_type i = 0; i < chunk_size; ++i, matrix_row += chunk_size)
98         {
99           typename std::iterator_traits<DstIterator>::value_type sum = 0;
100 
101           for (size_type j = 0; j < chunk_size; ++j)
102             sum += matrix_row[j] * src[j];
103 
104           dst[i] -= sum;
105         }
106     }
107 
108 
109     /**
110      * Add the result of multiplying the transpose of a chunk of size
111      * chunk_size times chunk_size by a source vector fragment of size
112      * chunk_size to the destination vector fragment.
113      */
114     template <typename MatrixIterator,
115               typename SrcIterator,
116               typename DstIterator>
117     inline void
chunk_Tvmult_add(const size_type chunk_size,const MatrixIterator matrix,const SrcIterator src,DstIterator dst)118     chunk_Tvmult_add(const size_type      chunk_size,
119                      const MatrixIterator matrix,
120                      const SrcIterator    src,
121                      DstIterator          dst)
122     {
123       for (size_type i = 0; i < chunk_size; ++i)
124         {
125           typename std::iterator_traits<DstIterator>::value_type sum = 0;
126 
127           for (size_type j = 0; j < chunk_size; ++j)
128             sum += matrix[j * chunk_size + i] * src[j];
129 
130           dst[i] += sum;
131         }
132     }
133 
134 
135     /**
136      * Produce the result of the matrix scalar product $u^TMv$ for an
137      * individual chunk.
138      */
139     template <typename result_type,
140               typename MatrixIterator,
141               typename SrcIterator1,
142               typename SrcIterator2>
143     inline result_type
chunk_matrix_scalar_product(const size_type chunk_size,const MatrixIterator matrix,const SrcIterator1 u,const SrcIterator2 v)144     chunk_matrix_scalar_product(const size_type      chunk_size,
145                                 const MatrixIterator matrix,
146                                 const SrcIterator1   u,
147                                 const SrcIterator2   v)
148     {
149       result_type result = 0;
150 
151       MatrixIterator matrix_row = matrix;
152 
153       for (size_type i = 0; i < chunk_size; ++i, matrix_row += chunk_size)
154         {
155           typename std::iterator_traits<SrcIterator2>::value_type sum = 0;
156 
157           for (size_type j = 0; j < chunk_size; ++j)
158             sum += matrix_row[j] * v[j];
159 
160           result += u[i] * sum;
161         }
162 
163       return result;
164     }
165 
166 
167 
168     /**
169      * Perform a vmult_add using the ChunkSparseMatrix data structures, but
170      * only using a subinterval of the matrix rows.
171      *
172      * In the sequential case, this function is called on all rows, in the
173      * parallel case it may be called on a subrange, at the discretion of the
174      * task scheduler.
175      */
176     template <typename number, typename InVector, typename OutVector>
177     void
vmult_add_on_subrange(const ChunkSparsityPattern & cols,const unsigned int begin_row,const unsigned int end_row,const number * values,const std::size_t * rowstart,const size_type * colnums,const InVector & src,OutVector & dst)178     vmult_add_on_subrange(const ChunkSparsityPattern &cols,
179                           const unsigned int          begin_row,
180                           const unsigned int          end_row,
181                           const number *              values,
182                           const std::size_t *         rowstart,
183                           const size_type *           colnums,
184                           const InVector &            src,
185                           OutVector &                 dst)
186     {
187       const size_type m          = cols.n_rows();
188       const size_type n          = cols.n_cols();
189       const size_type chunk_size = cols.get_chunk_size();
190 
191       // loop over all chunks. note that we need to treat the last chunk row
192       // and column differently if they have padding elements
193       const size_type n_filled_last_rows = m % chunk_size;
194       const size_type n_filled_last_cols = n % chunk_size;
195 
196       const size_type last_regular_row =
197         n_filled_last_rows > 0 ?
198           std::min(m / chunk_size, static_cast<size_type>(end_row)) :
199           end_row;
200       const size_type irregular_col = n / chunk_size;
201 
202       typename OutVector::iterator dst_ptr =
203         dst.begin() + chunk_size * begin_row;
204       const number *val_ptr =
205         &values[rowstart[begin_row] * chunk_size * chunk_size];
206       const size_type *colnum_ptr = &colnums[rowstart[begin_row]];
207       for (unsigned int chunk_row = begin_row; chunk_row < last_regular_row;
208            ++chunk_row)
209         {
210           const number *const val_end_of_row =
211             &values[rowstart[chunk_row + 1] * chunk_size * chunk_size];
212           while (val_ptr != val_end_of_row)
213             {
214               if (*colnum_ptr != irregular_col)
215                 chunk_vmult_add(chunk_size,
216                                 val_ptr,
217                                 src.begin() + *colnum_ptr * chunk_size,
218                                 dst_ptr);
219               else
220                 // we're at a chunk column that has padding
221                 for (size_type r = 0; r < chunk_size; ++r)
222                   for (size_type c = 0; c < n_filled_last_cols; ++c)
223                     dst_ptr[r] += (val_ptr[r * chunk_size + c] *
224                                    src(*colnum_ptr * chunk_size + c));
225 
226               ++colnum_ptr;
227               val_ptr += chunk_size * chunk_size;
228             }
229 
230           dst_ptr += chunk_size;
231         }
232 
233       // now deal with last chunk row if necessary
234       if (n_filled_last_rows > 0 && end_row == (m / chunk_size + 1))
235         {
236           const size_type chunk_row = last_regular_row;
237 
238           const number *const val_end_of_row =
239             &values[rowstart[chunk_row + 1] * chunk_size * chunk_size];
240           while (val_ptr != val_end_of_row)
241             {
242               if (*colnum_ptr != irregular_col)
243                 {
244                   // we're at a chunk row but not column that has padding
245                   for (size_type r = 0; r < n_filled_last_rows; ++r)
246                     for (size_type c = 0; c < chunk_size; ++c)
247                       dst_ptr[r] += (val_ptr[r * chunk_size + c] *
248                                      src(*colnum_ptr * chunk_size + c));
249                 }
250               else
251                 // we're at a chunk row and column that has padding
252                 for (size_type r = 0; r < n_filled_last_rows; ++r)
253                   for (size_type c = 0; c < n_filled_last_cols; ++c)
254                     dst_ptr[r] += (val_ptr[r * chunk_size + c] *
255                                    src(*colnum_ptr * chunk_size + c));
256 
257               ++colnum_ptr;
258               val_ptr += chunk_size * chunk_size;
259             }
260         }
261       Assert(std::size_t(colnum_ptr - colnums) == rowstart[end_row],
262              ExcInternalError());
263       Assert(std::size_t(val_ptr - values) ==
264                rowstart[end_row] * chunk_size * chunk_size,
265              ExcInternalError());
266     }
267   } // namespace ChunkSparseMatrixImplementation
268 } // namespace internal
269 
270 
271 
272 template <typename number>
ChunkSparseMatrix()273 ChunkSparseMatrix<number>::ChunkSparseMatrix()
274   : cols(nullptr, "ChunkSparseMatrix")
275   , val(nullptr)
276   , max_len(0)
277 {}
278 
279 
280 
281 template <typename number>
ChunkSparseMatrix(const ChunkSparseMatrix & m)282 ChunkSparseMatrix<number>::ChunkSparseMatrix(const ChunkSparseMatrix &m)
283   : Subscriptor(m)
284   , cols(nullptr, "ChunkSparseMatrix")
285   , val(nullptr)
286   , max_len(0)
287 {
288   Assert(m.cols == nullptr && m.val == nullptr && m.max_len == 0,
289          ExcMessage(
290            "This constructor can only be called if the provided argument "
291            "is an empty matrix. This constructor can not be used to "
292            "copy-construct a non-empty matrix. Use the "
293            "ChunkSparseMatrix::copy_from() function for that purpose."));
294 }
295 
296 
297 
298 template <typename number>
299 ChunkSparseMatrix<number> &
300 ChunkSparseMatrix<number>::operator=(const ChunkSparseMatrix<number> &m)
301 {
302   (void)m;
303   Assert(m.cols == nullptr && m.val == nullptr && m.max_len == 0,
304          ExcMessage(
305            "This operator can only be called if the provided right "
306            "hand side is an empty matrix. This operator can not be "
307            "used to copy a non-empty matrix. Use the "
308            "ChunkSparseMatrix::copy_from() function for that purpose."));
309 
310   return *this;
311 }
312 
313 
314 
315 template <typename number>
ChunkSparseMatrix(const ChunkSparsityPattern & c)316 ChunkSparseMatrix<number>::ChunkSparseMatrix(const ChunkSparsityPattern &c)
317   : cols(nullptr, "ChunkSparseMatrix")
318   , val(nullptr)
319   , max_len(0)
320 {
321   // virtual functions called in constructors and destructors never use the
322   // override in a derived class
323   // for clarity be explicit on which function is called
324   ChunkSparseMatrix<number>::reinit(c);
325 }
326 
327 
328 
329 template <typename number>
ChunkSparseMatrix(const ChunkSparsityPattern & c,const IdentityMatrix & id)330 ChunkSparseMatrix<number>::ChunkSparseMatrix(const ChunkSparsityPattern &c,
331                                              const IdentityMatrix &      id)
332   : cols(nullptr, "ChunkSparseMatrix")
333   , val(nullptr)
334   , max_len(0)
335 {
336   (void)id;
337   Assert(c.n_rows() == id.m(), ExcDimensionMismatch(c.n_rows(), id.m()));
338   Assert(c.n_cols() == id.n(), ExcDimensionMismatch(c.n_cols(), id.n()));
339 
340   // virtual functions called in constructors and destructors never use the
341   // override in a derived class
342   // for clarity be explicit on which function is called
343   ChunkSparseMatrix<number>::reinit(c);
344   for (size_type i = 0; i < n(); ++i)
345     this->set(i, i, 1.);
346 }
347 
348 
349 
350 template <typename number>
~ChunkSparseMatrix()351 ChunkSparseMatrix<number>::~ChunkSparseMatrix()
352 {
353   cols = nullptr;
354 }
355 
356 
357 
358 namespace internal
359 {
360   namespace ChunkSparseMatrixImplementation
361   {
362     template <typename T>
363     void
zero_subrange(const unsigned int begin,const unsigned int end,T * dst)364     zero_subrange(const unsigned int begin, const unsigned int end, T *dst)
365     {
366       std::memset(dst + begin, 0, (end - begin) * sizeof(T));
367     }
368   } // namespace ChunkSparseMatrixImplementation
369 } // namespace internal
370 
371 
372 
373 template <typename number>
374 ChunkSparseMatrix<number> &
375 ChunkSparseMatrix<number>::operator=(const double d)
376 {
377   (void)d;
378   Assert(d == 0, ExcScalarAssignmentOnlyForZeroValue());
379 
380   Assert(cols != nullptr, ExcNotInitialized());
381   Assert(cols->sparsity_pattern.compressed || cols->empty(),
382          ChunkSparsityPattern::ExcNotCompressed());
383 
384   // do initial zeroing of elements in parallel. Try to achieve a similar
385   // layout as when doing matrix-vector products, as on some NUMA systems, a
386   // memory block is assigned to memory banks where the first access is
387   // generated. For sparse matrices, the first operations is usually the
388   // operator=. The grain size is chosen to reflect the number of rows in
389   // minimum_parallel_grain_size, weighted by the number of nonzero entries
390   // per row on average.
391   const unsigned int matrix_size = cols->sparsity_pattern.n_nonzero_elements() *
392                                    cols->chunk_size * cols->chunk_size;
393   const unsigned int grain_size =
394     internal::SparseMatrixImplementation::minimum_parallel_grain_size *
395     (matrix_size + m()) / m();
396   if (matrix_size > grain_size)
397     parallel::apply_to_subranges(
398       0U,
399       matrix_size,
400       [this](const unsigned int begin, const unsigned int end) {
401         internal::ChunkSparseMatrixImplementation::zero_subrange(begin,
402                                                                  end,
403                                                                  val.get());
404       },
405       grain_size);
406   else if (matrix_size > 0)
407     std::memset(val.get(), 0, matrix_size * sizeof(number));
408 
409   return *this;
410 }
411 
412 
413 
414 template <typename number>
415 ChunkSparseMatrix<number> &
416 ChunkSparseMatrix<number>::operator=(const IdentityMatrix &id)
417 {
418   (void)id;
419   Assert(cols->n_rows() == id.m(),
420          ExcDimensionMismatch(cols->n_rows(), id.m()));
421   Assert(cols->n_cols() == id.n(),
422          ExcDimensionMismatch(cols->n_cols(), id.n()));
423 
424   *this = 0;
425   for (size_type i = 0; i < n(); ++i)
426     this->set(i, i, 1.);
427 
428   return *this;
429 }
430 
431 
432 
433 template <typename number>
434 void
reinit(const ChunkSparsityPattern & sparsity)435 ChunkSparseMatrix<number>::reinit(const ChunkSparsityPattern &sparsity)
436 {
437   cols = &sparsity;
438 
439   if (cols->empty())
440     {
441       val.reset();
442       max_len = 0;
443       return;
444     }
445 
446   // allocate not just m() * n() elements but enough so that we can store full
447   // chunks. this entails some padding elements
448   const size_type chunk_size = cols->get_chunk_size();
449   const size_type N =
450     cols->sparsity_pattern.n_nonzero_elements() * chunk_size * chunk_size;
451   if (N > max_len || max_len == 0)
452     {
453       val     = std::make_unique<number[]>(N);
454       max_len = N;
455     }
456 
457   // fill with zeros. do not just fill N elements but all that we allocated to
458   // ensure that also the padding elements are zero and not left at previous
459   // values
460   this->operator=(0.);
461 }
462 
463 
464 
465 template <typename number>
466 void
clear()467 ChunkSparseMatrix<number>::clear()
468 {
469   cols = nullptr;
470   val.reset();
471   max_len = 0;
472 }
473 
474 
475 
476 template <typename number>
477 bool
empty()478 ChunkSparseMatrix<number>::empty() const
479 {
480   if (cols == nullptr)
481     return true;
482   else
483     return cols->empty();
484 }
485 
486 
487 
488 template <typename number>
489 typename ChunkSparseMatrix<number>::size_type
n_nonzero_elements()490 ChunkSparseMatrix<number>::n_nonzero_elements() const
491 {
492   Assert(cols != nullptr, ExcNotInitialized());
493   return cols->n_nonzero_elements();
494 }
495 
496 
497 
498 template <typename number>
499 typename ChunkSparseMatrix<number>::size_type
n_actually_nonzero_elements()500 ChunkSparseMatrix<number>::n_actually_nonzero_elements() const
501 {
502   Assert(cols != nullptr, ExcNotInitialized());
503 
504   // count those elements that are nonzero, even if they lie in the padding
505   // around the matrix. since we have the invariant that padding elements are
506   // zero, nothing bad can happen here
507   const size_type chunk_size = cols->get_chunk_size();
508   return std::count_if(val.get(),
509                        val.get() + cols->sparsity_pattern.n_nonzero_elements() *
510                                      chunk_size * chunk_size,
511                        [](const double element) { return element != 0.; });
512 }
513 
514 
515 
516 template <typename number>
517 void
symmetrize()518 ChunkSparseMatrix<number>::symmetrize()
519 {
520   Assert(cols != nullptr, ExcNotInitialized());
521   Assert(cols->rows == cols->cols, ExcNotQuadratic());
522 
523   Assert(false, ExcNotImplemented());
524 }
525 
526 
527 
528 template <typename number>
529 template <typename somenumber>
530 ChunkSparseMatrix<number> &
copy_from(const ChunkSparseMatrix<somenumber> & matrix)531 ChunkSparseMatrix<number>::copy_from(
532   const ChunkSparseMatrix<somenumber> &matrix)
533 {
534   Assert(cols != nullptr, ExcNotInitialized());
535   Assert(val != nullptr, ExcNotInitialized());
536   Assert(cols == matrix.cols, ExcDifferentChunkSparsityPatterns());
537 
538   // copy everything, including padding elements
539   const size_type chunk_size = cols->get_chunk_size();
540   std::copy(matrix.val.get(),
541             matrix.val.get() + cols->sparsity_pattern.n_nonzero_elements() *
542                                  chunk_size * chunk_size,
543             val.get());
544 
545   return *this;
546 }
547 
548 
549 
550 template <typename number>
551 template <typename somenumber>
552 void
copy_from(const FullMatrix<somenumber> & matrix)553 ChunkSparseMatrix<number>::copy_from(const FullMatrix<somenumber> &matrix)
554 {
555   // first delete previous content
556   *this = 0;
557 
558   // then copy old matrix
559   for (size_type row = 0; row < matrix.m(); ++row)
560     for (size_type col = 0; col < matrix.n(); ++col)
561       if (matrix(row, col) != 0)
562         set(row, col, matrix(row, col));
563 }
564 
565 
566 
567 template <typename number>
568 template <typename somenumber>
569 void
add(const number factor,const ChunkSparseMatrix<somenumber> & matrix)570 ChunkSparseMatrix<number>::add(const number                         factor,
571                                const ChunkSparseMatrix<somenumber> &matrix)
572 {
573   Assert(cols != nullptr, ExcNotInitialized());
574   Assert(val != nullptr, ExcNotInitialized());
575   Assert(cols == matrix.cols, ExcDifferentChunkSparsityPatterns());
576 
577   // add everything, including padding elements
578   const size_type     chunk_size = cols->get_chunk_size();
579   number *            val_ptr    = val.get();
580   const somenumber *  matrix_ptr = matrix.val.get();
581   const number *const end_ptr =
582     val.get() +
583     cols->sparsity_pattern.n_nonzero_elements() * chunk_size * chunk_size;
584 
585   while (val_ptr != end_ptr)
586     *val_ptr++ += factor * *matrix_ptr++;
587 }
588 
589 
590 
591 template <typename number>
592 void
extract_row_copy(const size_type row,const size_type array_length,size_type & row_length,size_type * column_indices,number * values)593 ChunkSparseMatrix<number>::extract_row_copy(const size_type row,
594                                             const size_type array_length,
595                                             size_type &     row_length,
596                                             size_type *     column_indices,
597                                             number *        values) const
598 {
599   (void)array_length;
600   AssertIndexRange(cols->row_length(row), array_length + 1);
601   AssertIndexRange(row, m());
602   const unsigned int chunk_size  = cols->get_chunk_size();
603   const size_type    reduced_row = row / chunk_size;
604 
605   SparsityPattern::iterator it    = cols->sparsity_pattern.begin(reduced_row),
606                             itend = cols->sparsity_pattern.end(reduced_row);
607   const number *val_ptr =
608     &val[(it - cols->sparsity_pattern.begin(0)) * chunk_size * chunk_size +
609          (row % chunk_size) * chunk_size];
610 
611   // find out if we did padding and if this row is affected by it
612   if (cols->n_cols() % chunk_size == 0)
613     {
614       for (; it < itend; ++it)
615         {
616           for (unsigned int c = 0; c < chunk_size; ++c)
617             {
618               *values++         = val_ptr[c];
619               *column_indices++ = it->column() * chunk_size + c;
620             }
621           val_ptr += chunk_size * chunk_size;
622         }
623       row_length =
624         chunk_size * (cols->sparsity_pattern.row_length(reduced_row));
625     }
626   else
627     {
628       const unsigned int last_chunk_size = cols->n_cols() % chunk_size;
629       row_length                         = 0;
630       for (; it < itend; ++it)
631         {
632           const unsigned int next_chunk_size =
633             (it->column() == cols->sparsity_pattern.n_cols() - 1) ?
634               last_chunk_size :
635               chunk_size;
636           for (unsigned int c = 0; c < next_chunk_size; ++c, ++row_length)
637             {
638               *values++         = val_ptr[c];
639               *column_indices++ = it->column() * chunk_size + c;
640             }
641           val_ptr += chunk_size * chunk_size;
642         }
643     }
644 }
645 
646 
647 
648 template <typename number>
649 template <class OutVector, class InVector>
650 void
vmult(OutVector & dst,const InVector & src)651 ChunkSparseMatrix<number>::vmult(OutVector &dst, const InVector &src) const
652 {
653   Assert(cols != nullptr, ExcNotInitialized());
654   Assert(val != nullptr, ExcNotInitialized());
655   Assert(m() == dst.size(), ExcDimensionMismatch(m(), dst.size()));
656   Assert(n() == src.size(), ExcDimensionMismatch(n(), src.size()));
657 
658   Assert(!PointerComparison::equal(&src, &dst), ExcSourceEqualsDestination());
659 
660   // set the output vector to zero and then add to it the contributions of
661   // vmults from individual chunks. this is what vmult_add does
662   dst = 0;
663   vmult_add(dst, src);
664 }
665 
666 
667 
668 template <typename number>
669 template <class OutVector, class InVector>
670 void
Tvmult(OutVector & dst,const InVector & src)671 ChunkSparseMatrix<number>::Tvmult(OutVector &dst, const InVector &src) const
672 {
673   Assert(val != nullptr, ExcNotInitialized());
674   Assert(cols != nullptr, ExcNotInitialized());
675   Assert(n() == dst.size(), ExcDimensionMismatch(n(), dst.size()));
676   Assert(m() == src.size(), ExcDimensionMismatch(m(), src.size()));
677 
678   Assert(!PointerComparison::equal(&src, &dst), ExcSourceEqualsDestination());
679 
680   Assert(cols != nullptr, ExcNotInitialized());
681   Assert(val != nullptr, ExcNotInitialized());
682   Assert(m() == dst.size(), ExcDimensionMismatch(m(), dst.size()));
683   Assert(n() == src.size(), ExcDimensionMismatch(n(), src.size()));
684 
685   Assert(!PointerComparison::equal(&src, &dst), ExcSourceEqualsDestination());
686 
687   // set the output vector to zero and then add to it the contributions of
688   // vmults from individual chunks. this is what vmult_add does
689   dst = 0;
690   Tvmult_add(dst, src);
691 }
692 
693 
694 
695 template <typename number>
696 template <class OutVector, class InVector>
697 void
vmult_add(OutVector & dst,const InVector & src)698 ChunkSparseMatrix<number>::vmult_add(OutVector &dst, const InVector &src) const
699 {
700   Assert(cols != nullptr, ExcNotInitialized());
701   Assert(val != nullptr, ExcNotInitialized());
702   Assert(m() == dst.size(), ExcDimensionMismatch(m(), dst.size()));
703   Assert(n() == src.size(), ExcDimensionMismatch(n(), src.size()));
704 
705   Assert(!PointerComparison::equal(&src, &dst), ExcSourceEqualsDestination());
706   parallel::apply_to_subranges(
707     0U,
708     cols->sparsity_pattern.n_rows(),
709     [this, &src, &dst](const unsigned int begin_row,
710                        const unsigned int end_row) {
711       internal::ChunkSparseMatrixImplementation::vmult_add_on_subrange(
712         *cols,
713         begin_row,
714         end_row,
715         val.get(),
716         cols->sparsity_pattern.rowstart.get(),
717         cols->sparsity_pattern.colnums.get(),
718         src,
719         dst);
720     },
721     internal::SparseMatrixImplementation::minimum_parallel_grain_size /
722         cols->chunk_size +
723       1);
724 }
725 
726 
727 template <typename number>
728 template <class OutVector, class InVector>
729 void
Tvmult_add(OutVector & dst,const InVector & src)730 ChunkSparseMatrix<number>::Tvmult_add(OutVector &dst, const InVector &src) const
731 {
732   Assert(cols != nullptr, ExcNotInitialized());
733   Assert(val != nullptr, ExcNotInitialized());
734   Assert(m() == dst.size(), ExcDimensionMismatch(m(), dst.size()));
735   Assert(n() == src.size(), ExcDimensionMismatch(n(), src.size()));
736 
737   Assert(!PointerComparison::equal(&src, &dst), ExcSourceEqualsDestination());
738 
739   const size_type n_chunk_rows = cols->sparsity_pattern.n_rows();
740 
741   // loop over all chunks. note that we need to treat the last chunk row and
742   // column differently if they have padding elements
743   const bool rows_have_padding = (m() % cols->chunk_size != 0),
744              cols_have_padding = (n() % cols->chunk_size != 0);
745 
746   const size_type n_regular_chunk_rows =
747     (rows_have_padding ? n_chunk_rows - 1 : n_chunk_rows);
748 
749   // like in vmult_add, but don't keep an iterator into dst around since we're
750   // not traversing it sequentially this time
751   const number *   val_ptr    = val.get();
752   const size_type *colnum_ptr = cols->sparsity_pattern.colnums.get();
753 
754   for (size_type chunk_row = 0; chunk_row < n_regular_chunk_rows; ++chunk_row)
755     {
756       const number *const val_end_of_row =
757         &val[cols->sparsity_pattern.rowstart[chunk_row + 1] * cols->chunk_size *
758              cols->chunk_size];
759       while (val_ptr != val_end_of_row)
760         {
761           if ((cols_have_padding == false) ||
762               (*colnum_ptr != cols->sparsity_pattern.n_cols() - 1))
763             internal::ChunkSparseMatrixImplementation::chunk_Tvmult_add(
764               cols->chunk_size,
765               val_ptr,
766               src.begin() + chunk_row * cols->chunk_size,
767               dst.begin() + *colnum_ptr * cols->chunk_size);
768           else
769             // we're at a chunk column that has padding
770             for (size_type r = 0; r < cols->chunk_size; ++r)
771               for (size_type c = 0; c < n() % cols->chunk_size; ++c)
772                 dst(*colnum_ptr * cols->chunk_size + c) +=
773                   (val_ptr[r * cols->chunk_size + c] *
774                    src(chunk_row * cols->chunk_size + r));
775 
776           ++colnum_ptr;
777           val_ptr += cols->chunk_size * cols->chunk_size;
778         }
779     }
780 
781   // now deal with last chunk row if necessary
782   if (rows_have_padding)
783     {
784       const size_type chunk_row = n_chunk_rows - 1;
785 
786       const number *const val_end_of_row =
787         &val[cols->sparsity_pattern.rowstart[chunk_row + 1] * cols->chunk_size *
788              cols->chunk_size];
789       while (val_ptr != val_end_of_row)
790         {
791           if ((cols_have_padding == false) ||
792               (*colnum_ptr != cols->sparsity_pattern.n_cols() - 1))
793             {
794               // we're at a chunk row but not column that has padding
795               for (size_type r = 0; r < m() % cols->chunk_size; ++r)
796                 for (size_type c = 0; c < cols->chunk_size; ++c)
797                   dst(*colnum_ptr * cols->chunk_size + c) +=
798                     (val_ptr[r * cols->chunk_size + c] *
799                      src(chunk_row * cols->chunk_size + r));
800             }
801           else
802             // we're at a chunk row and column that has padding
803             for (size_type r = 0; r < m() % cols->chunk_size; ++r)
804               for (size_type c = 0; c < n() % cols->chunk_size; ++c)
805                 dst(*colnum_ptr * cols->chunk_size + c) +=
806                   (val_ptr[r * cols->chunk_size + c] *
807                    src(chunk_row * cols->chunk_size + r));
808 
809           ++colnum_ptr;
810           val_ptr += cols->chunk_size * cols->chunk_size;
811         }
812     }
813 }
814 
815 
816 template <typename number>
817 template <typename somenumber>
818 somenumber
matrix_norm_square(const Vector<somenumber> & v)819 ChunkSparseMatrix<number>::matrix_norm_square(const Vector<somenumber> &v) const
820 {
821   Assert(cols != nullptr, ExcNotInitialized());
822   Assert(val != nullptr, ExcNotInitialized());
823   Assert(m() == v.size(), ExcDimensionMismatch(m(), v.size()));
824   Assert(n() == v.size(), ExcDimensionMismatch(n(), v.size()));
825 
826   somenumber result = 0;
827 
828   ////////////////
829   // like matrix_scalar_product, except that the two vectors are now the same
830 
831   const size_type n_chunk_rows = cols->sparsity_pattern.n_rows();
832 
833   // loop over all chunks. note that we need to treat the last chunk row and
834   // column differently if they have padding elements
835   const bool rows_have_padding = (m() % cols->chunk_size != 0),
836              cols_have_padding = (n() % cols->chunk_size != 0);
837 
838   const size_type n_regular_chunk_rows =
839     (rows_have_padding ? n_chunk_rows - 1 : n_chunk_rows);
840 
841   const number *   val_ptr    = val.get();
842   const size_type *colnum_ptr = cols->sparsity_pattern.colnums.get();
843   typename Vector<somenumber>::const_iterator v_ptr = v.begin();
844 
845   for (size_type chunk_row = 0; chunk_row < n_regular_chunk_rows; ++chunk_row)
846     {
847       const number *const val_end_of_row =
848         &val[cols->sparsity_pattern.rowstart[chunk_row + 1] * cols->chunk_size *
849              cols->chunk_size];
850       while (val_ptr != val_end_of_row)
851         {
852           if ((cols_have_padding == false) ||
853               (*colnum_ptr != cols->sparsity_pattern.n_cols() - 1))
854             result += internal::ChunkSparseMatrixImplementation::
855               chunk_matrix_scalar_product<somenumber>(cols->chunk_size,
856                                                       val_ptr,
857                                                       v_ptr,
858                                                       v.begin() +
859                                                         *colnum_ptr *
860                                                           cols->chunk_size);
861           else
862             // we're at a chunk column that has padding
863             for (size_type r = 0; r < cols->chunk_size; ++r)
864               for (size_type c = 0; c < n() % cols->chunk_size; ++c)
865                 result += v(chunk_row * cols->chunk_size + r) *
866                           (val_ptr[r * cols->chunk_size + c] *
867                            v(*colnum_ptr * cols->chunk_size + c));
868 
869           ++colnum_ptr;
870           val_ptr += cols->chunk_size * cols->chunk_size;
871         }
872 
873 
874       v_ptr += cols->chunk_size;
875     }
876 
877   // now deal with last chunk row if necessary
878   if (rows_have_padding)
879     {
880       const size_type chunk_row = n_chunk_rows - 1;
881 
882       const number *const val_end_of_row =
883         &val[cols->sparsity_pattern.rowstart[chunk_row + 1] * cols->chunk_size *
884              cols->chunk_size];
885       while (val_ptr != val_end_of_row)
886         {
887           if ((cols_have_padding == false) ||
888               (*colnum_ptr != cols->sparsity_pattern.n_cols() - 1))
889             {
890               // we're at a chunk row but not column that has padding
891               for (size_type r = 0; r < m() % cols->chunk_size; ++r)
892                 for (size_type c = 0; c < cols->chunk_size; ++c)
893                   result += v(chunk_row * cols->chunk_size + r) *
894                             (val_ptr[r * cols->chunk_size + c] *
895                              v(*colnum_ptr * cols->chunk_size + c));
896             }
897           else
898             // we're at a chunk row and column that has padding
899             for (size_type r = 0; r < m() % cols->chunk_size; ++r)
900               for (size_type c = 0; c < n() % cols->chunk_size; ++c)
901                 result += v(chunk_row * cols->chunk_size + r) *
902                           (val_ptr[r * cols->chunk_size + c] *
903                            v(*colnum_ptr * cols->chunk_size + c));
904 
905           ++colnum_ptr;
906           val_ptr += cols->chunk_size * cols->chunk_size;
907         }
908     }
909 
910   return result;
911 }
912 
913 
914 
915 template <typename number>
916 template <typename somenumber>
917 somenumber
matrix_scalar_product(const Vector<somenumber> & u,const Vector<somenumber> & v)918 ChunkSparseMatrix<number>::matrix_scalar_product(
919   const Vector<somenumber> &u,
920   const Vector<somenumber> &v) const
921 {
922   Assert(cols != nullptr, ExcNotInitialized());
923   Assert(val != nullptr, ExcNotInitialized());
924   Assert(m() == u.size(), ExcDimensionMismatch(m(), u.size()));
925   Assert(n() == v.size(), ExcDimensionMismatch(n(), v.size()));
926 
927   // the following works like the vmult_add function
928   somenumber result = 0;
929 
930   const size_type n_chunk_rows = cols->sparsity_pattern.n_rows();
931 
932   // loop over all chunks. note that we need to treat the last chunk row and
933   // column differently if they have padding elements
934   const bool rows_have_padding = (m() % cols->chunk_size != 0),
935              cols_have_padding = (n() % cols->chunk_size != 0);
936 
937   const size_type n_regular_chunk_rows =
938     (rows_have_padding ? n_chunk_rows - 1 : n_chunk_rows);
939 
940   const number *   val_ptr    = val.get();
941   const size_type *colnum_ptr = cols->sparsity_pattern.colnums.get();
942   typename Vector<somenumber>::const_iterator u_ptr = u.begin();
943 
944   for (size_type chunk_row = 0; chunk_row < n_regular_chunk_rows; ++chunk_row)
945     {
946       const number *const val_end_of_row =
947         &val[cols->sparsity_pattern.rowstart[chunk_row + 1] * cols->chunk_size *
948              cols->chunk_size];
949       while (val_ptr != val_end_of_row)
950         {
951           if ((cols_have_padding == false) ||
952               (*colnum_ptr != cols->sparsity_pattern.n_cols() - 1))
953             result += internal::ChunkSparseMatrixImplementation::
954               chunk_matrix_scalar_product<somenumber>(cols->chunk_size,
955                                                       val_ptr,
956                                                       u_ptr,
957                                                       v.begin() +
958                                                         *colnum_ptr *
959                                                           cols->chunk_size);
960           else
961             // we're at a chunk column that has padding
962             for (size_type r = 0; r < cols->chunk_size; ++r)
963               for (size_type c = 0; c < n() % cols->chunk_size; ++c)
964                 result += u(chunk_row * cols->chunk_size + r) *
965                           (val_ptr[r * cols->chunk_size + c] *
966                            v(*colnum_ptr * cols->chunk_size + c));
967 
968           ++colnum_ptr;
969           val_ptr += cols->chunk_size * cols->chunk_size;
970         }
971 
972 
973       u_ptr += cols->chunk_size;
974     }
975 
976   // now deal with last chunk row if necessary
977   if (rows_have_padding)
978     {
979       const size_type chunk_row = n_chunk_rows - 1;
980 
981       const number *const val_end_of_row =
982         &val[cols->sparsity_pattern.rowstart[chunk_row + 1] * cols->chunk_size *
983              cols->chunk_size];
984       while (val_ptr != val_end_of_row)
985         {
986           if ((cols_have_padding == false) ||
987               (*colnum_ptr != cols->sparsity_pattern.n_cols() - 1))
988             {
989               // we're at a chunk row but not column that has padding
990               for (size_type r = 0; r < m() % cols->chunk_size; ++r)
991                 for (size_type c = 0; c < cols->chunk_size; ++c)
992                   result += u(chunk_row * cols->chunk_size + r) *
993                             (val_ptr[r * cols->chunk_size + c] *
994                              v(*colnum_ptr * cols->chunk_size + c));
995             }
996           else
997             // we're at a chunk row and column that has padding
998             for (size_type r = 0; r < m() % cols->chunk_size; ++r)
999               for (size_type c = 0; c < n() % cols->chunk_size; ++c)
1000                 result += u(chunk_row * cols->chunk_size + r) *
1001                           (val_ptr[r * cols->chunk_size + c] *
1002                            v(*colnum_ptr * cols->chunk_size + c));
1003 
1004           ++colnum_ptr;
1005           val_ptr += cols->chunk_size * cols->chunk_size;
1006         }
1007     }
1008 
1009   return result;
1010 }
1011 
1012 
1013 
1014 template <typename number>
1015 typename ChunkSparseMatrix<number>::real_type
l1_norm()1016 ChunkSparseMatrix<number>::l1_norm() const
1017 {
1018   Assert(cols != nullptr, ExcNotInitialized());
1019   Assert(val != nullptr, ExcNotInitialized());
1020 
1021   const size_type n_chunk_rows = cols->sparsity_pattern.n_rows();
1022 
1023   // loop over all rows and columns; it is safe to also loop over the padding
1024   // elements (they are zero) if we make sure that the vector into which we
1025   // sum column sums is large enough
1026   Vector<real_type> column_sums(cols->sparsity_pattern.n_cols() *
1027                                 cols->chunk_size);
1028 
1029   for (size_type chunk_row = 0; chunk_row < n_chunk_rows; ++chunk_row)
1030     for (size_type j = cols->sparsity_pattern.rowstart[chunk_row];
1031          j < cols->sparsity_pattern.rowstart[chunk_row + 1];
1032          ++j)
1033       for (size_type r = 0; r < cols->chunk_size; ++r)
1034         for (size_type s = 0; s < cols->chunk_size; ++s)
1035           column_sums(cols->sparsity_pattern.colnums[j] * cols->chunk_size +
1036                       s) +=
1037             numbers::NumberTraits<number>::abs(
1038               val[j * cols->chunk_size * cols->chunk_size +
1039                   r * cols->chunk_size + s]);
1040 
1041   return column_sums.linfty_norm();
1042 }
1043 
1044 
1045 
1046 template <typename number>
1047 typename ChunkSparseMatrix<number>::real_type
linfty_norm()1048 ChunkSparseMatrix<number>::linfty_norm() const
1049 {
1050   Assert(cols != nullptr, ExcNotInitialized());
1051   Assert(val != nullptr, ExcNotInitialized());
1052 
1053   // this function works like l1_norm(). it can be made more efficient
1054   // (without allocating a temporary vector) as is done in the SparseMatrix
1055   // class but since it is rarely called in time critical places it is
1056   // probably not worth it
1057   const size_type n_chunk_rows = cols->sparsity_pattern.n_rows();
1058 
1059   // loop over all rows and columns; it is safe to also loop over the padding
1060   // elements (they are zero) if we make sure that the vector into which we
1061   // sum column sums is large enough
1062   Vector<real_type> row_sums(cols->sparsity_pattern.n_rows() *
1063                              cols->chunk_size);
1064 
1065   for (size_type chunk_row = 0; chunk_row < n_chunk_rows; ++chunk_row)
1066     for (size_type j = cols->sparsity_pattern.rowstart[chunk_row];
1067          j < cols->sparsity_pattern.rowstart[chunk_row + 1];
1068          ++j)
1069       for (size_type r = 0; r < cols->chunk_size; ++r)
1070         for (size_type s = 0; s < cols->chunk_size; ++s)
1071           row_sums(chunk_row * cols->chunk_size + r) +=
1072             numbers::NumberTraits<number>::abs(
1073               val[j * cols->chunk_size * cols->chunk_size +
1074                   r * cols->chunk_size + s]);
1075 
1076   return row_sums.linfty_norm();
1077 }
1078 
1079 
1080 
1081 template <typename number>
1082 typename ChunkSparseMatrix<number>::real_type
frobenius_norm()1083 ChunkSparseMatrix<number>::frobenius_norm() const
1084 {
1085   // simply add up all entries in the sparsity pattern, without taking any
1086   // reference to rows or columns
1087   //
1088   // padding elements are zero, so we can add them up as well
1089   real_type norm_sqr = 0;
1090   for (const number *ptr = val.get(); ptr != val.get() + max_len; ++ptr)
1091     norm_sqr += numbers::NumberTraits<number>::abs_square(*ptr);
1092 
1093   return std::sqrt(norm_sqr);
1094 }
1095 
1096 
1097 
1098 template <typename number>
1099 template <typename somenumber>
1100 somenumber
residual(Vector<somenumber> & dst,const Vector<somenumber> & u,const Vector<somenumber> & b)1101 ChunkSparseMatrix<number>::residual(Vector<somenumber> &      dst,
1102                                     const Vector<somenumber> &u,
1103                                     const Vector<somenumber> &b) const
1104 {
1105   Assert(cols != nullptr, ExcNotInitialized());
1106   Assert(val != nullptr, ExcNotInitialized());
1107   Assert(m() == dst.size(), ExcDimensionMismatch(m(), dst.size()));
1108   Assert(m() == b.size(), ExcDimensionMismatch(m(), b.size()));
1109   Assert(n() == u.size(), ExcDimensionMismatch(n(), u.size()));
1110 
1111   Assert(&u != &dst, ExcSourceEqualsDestination());
1112 
1113   // set dst=b, then subtract the result of A*u from it. since the purpose of
1114   // the current class is to promote streaming of data rather than more random
1115   // access patterns, breaking things up into two loops may be reasonable
1116   dst = b;
1117 
1118   /////////
1119   // the rest of this function is like vmult_add, except that we subtract
1120   // rather than add A*u
1121   /////////
1122   const size_type n_chunk_rows = cols->sparsity_pattern.n_rows();
1123 
1124   // loop over all chunks. note that we need to treat the last chunk row and
1125   // column differently if they have padding elements
1126   const bool rows_have_padding = (m() % cols->chunk_size != 0),
1127              cols_have_padding = (n() % cols->chunk_size != 0);
1128 
1129   const size_type n_regular_chunk_rows =
1130     (rows_have_padding ? n_chunk_rows - 1 : n_chunk_rows);
1131 
1132   const number *   val_ptr    = val.get();
1133   const size_type *colnum_ptr = cols->sparsity_pattern.colnums.get();
1134   typename Vector<somenumber>::iterator dst_ptr = dst.begin();
1135 
1136   for (size_type chunk_row = 0; chunk_row < n_regular_chunk_rows; ++chunk_row)
1137     {
1138       const number *const val_end_of_row =
1139         &val[cols->sparsity_pattern.rowstart[chunk_row + 1] * cols->chunk_size *
1140              cols->chunk_size];
1141       while (val_ptr != val_end_of_row)
1142         {
1143           if ((cols_have_padding == false) ||
1144               (*colnum_ptr != cols->sparsity_pattern.n_cols() - 1))
1145             internal::ChunkSparseMatrixImplementation::chunk_vmult_subtract(
1146               cols->chunk_size,
1147               val_ptr,
1148               u.begin() + *colnum_ptr * cols->chunk_size,
1149               dst_ptr);
1150           else
1151             // we're at a chunk column that has padding
1152             for (size_type r = 0; r < cols->chunk_size; ++r)
1153               for (size_type c = 0; c < n() % cols->chunk_size; ++c)
1154                 dst(chunk_row * cols->chunk_size + r) -=
1155                   (val_ptr[r * cols->chunk_size + c] *
1156                    u(*colnum_ptr * cols->chunk_size + c));
1157 
1158           ++colnum_ptr;
1159           val_ptr += cols->chunk_size * cols->chunk_size;
1160         }
1161 
1162 
1163       dst_ptr += cols->chunk_size;
1164     }
1165 
1166   // now deal with last chunk row if necessary
1167   if (rows_have_padding)
1168     {
1169       const size_type chunk_row = n_chunk_rows - 1;
1170 
1171       const number *const val_end_of_row =
1172         &val[cols->sparsity_pattern.rowstart[chunk_row + 1] * cols->chunk_size *
1173              cols->chunk_size];
1174       while (val_ptr != val_end_of_row)
1175         {
1176           if ((cols_have_padding == false) ||
1177               (*colnum_ptr != cols->sparsity_pattern.n_cols() - 1))
1178             {
1179               // we're at a chunk row but not column that has padding
1180               for (size_type r = 0; r < m() % cols->chunk_size; ++r)
1181                 for (size_type c = 0; c < cols->chunk_size; ++c)
1182                   dst(chunk_row * cols->chunk_size + r) -=
1183                     (val_ptr[r * cols->chunk_size + c] *
1184                      u(*colnum_ptr * cols->chunk_size + c));
1185             }
1186           else
1187             // we're at a chunk row and column that has padding
1188             for (size_type r = 0; r < m() % cols->chunk_size; ++r)
1189               for (size_type c = 0; c < n() % cols->chunk_size; ++c)
1190                 dst(chunk_row * cols->chunk_size + r) -=
1191                   (val_ptr[r * cols->chunk_size + c] *
1192                    u(*colnum_ptr * cols->chunk_size + c));
1193 
1194           ++colnum_ptr;
1195           val_ptr += cols->chunk_size * cols->chunk_size;
1196         }
1197 
1198 
1199       dst_ptr += cols->chunk_size;
1200     }
1201 
1202   // finally compute the norm
1203   return dst.l2_norm();
1204 }
1205 
1206 
1207 
1208 template <typename number>
1209 template <typename somenumber>
1210 void
precondition_Jacobi(Vector<somenumber> & dst,const Vector<somenumber> & src,const number)1211 ChunkSparseMatrix<number>::precondition_Jacobi(Vector<somenumber> &      dst,
1212                                                const Vector<somenumber> &src,
1213                                                const number /*om*/) const
1214 {
1215   (void)dst;
1216   (void)src;
1217   Assert(cols != nullptr, ExcNotInitialized());
1218   Assert(val != nullptr, ExcNotInitialized());
1219   Assert(m() == n(),
1220          ExcMessage("This operation is only valid on square matrices."));
1221 
1222   Assert(dst.size() == n(), ExcDimensionMismatch(dst.size(), n()));
1223   Assert(src.size() == n(), ExcDimensionMismatch(src.size(), n()));
1224 
1225   Assert(false, ExcNotImplemented());
1226 }
1227 
1228 
1229 
1230 template <typename number>
1231 template <typename somenumber>
1232 void
precondition_SSOR(Vector<somenumber> & dst,const Vector<somenumber> & src,const number)1233 ChunkSparseMatrix<number>::precondition_SSOR(Vector<somenumber> &      dst,
1234                                              const Vector<somenumber> &src,
1235                                              const number /*om*/) const
1236 {
1237   // to understand how this function works you may want to take a look at the
1238   // CVS archives to see the original version which is much clearer...
1239   (void)dst;
1240   (void)src;
1241   Assert(cols != nullptr, ExcNotInitialized());
1242   Assert(val != nullptr, ExcNotInitialized());
1243   Assert(m() == n(),
1244          ExcMessage("This operation is only valid on square matrices."));
1245 
1246   Assert(dst.size() == n(), ExcDimensionMismatch(dst.size(), n()));
1247   Assert(src.size() == n(), ExcDimensionMismatch(src.size(), n()));
1248 
1249   Assert(false, ExcNotImplemented());
1250 }
1251 
1252 
1253 template <typename number>
1254 template <typename somenumber>
1255 void
precondition_SOR(Vector<somenumber> & dst,const Vector<somenumber> & src,const number om)1256 ChunkSparseMatrix<number>::precondition_SOR(Vector<somenumber> &      dst,
1257                                             const Vector<somenumber> &src,
1258                                             const number              om) const
1259 {
1260   Assert(cols != nullptr, ExcNotInitialized());
1261   Assert(val != nullptr, ExcNotInitialized());
1262   Assert(m() == n(),
1263          ExcMessage("This operation is only valid on square matrices."));
1264 
1265 
1266   dst = src;
1267   SOR(dst, om);
1268 }
1269 
1270 
1271 template <typename number>
1272 template <typename somenumber>
1273 void
precondition_TSOR(Vector<somenumber> & dst,const Vector<somenumber> & src,const number om)1274 ChunkSparseMatrix<number>::precondition_TSOR(Vector<somenumber> &      dst,
1275                                              const Vector<somenumber> &src,
1276                                              const number              om) const
1277 {
1278   Assert(cols != nullptr, ExcNotInitialized());
1279   Assert(val != nullptr, ExcNotInitialized());
1280   Assert(m() == n(),
1281          ExcMessage("This operation is only valid on square matrices."));
1282 
1283 
1284   dst = src;
1285   TSOR(dst, om);
1286 }
1287 
1288 
1289 template <typename number>
1290 template <typename somenumber>
1291 void
SOR(Vector<somenumber> & dst,const number)1292 ChunkSparseMatrix<number>::SOR(Vector<somenumber> &dst,
1293                                const number /*om*/) const
1294 {
1295   (void)dst;
1296   Assert(cols != nullptr, ExcNotInitialized());
1297   Assert(val != nullptr, ExcNotInitialized());
1298   Assert(m() == n(),
1299          ExcMessage("This operation is only valid on square matrices."));
1300   Assert(m() == dst.size(), ExcDimensionMismatch(m(), dst.size()));
1301 
1302   Assert(false, ExcNotImplemented());
1303 }
1304 
1305 
1306 template <typename number>
1307 template <typename somenumber>
1308 void
TSOR(Vector<somenumber> & dst,const number)1309 ChunkSparseMatrix<number>::TSOR(Vector<somenumber> &dst,
1310                                 const number /*om*/) const
1311 {
1312   (void)dst;
1313   Assert(cols != nullptr, ExcNotInitialized());
1314   Assert(val != nullptr, ExcNotInitialized());
1315   Assert(m() == n(),
1316          ExcMessage("This operation is only valid on square matrices."));
1317   Assert(m() == dst.size(), ExcDimensionMismatch(m(), dst.size()));
1318 
1319   Assert(false, ExcNotImplemented());
1320 }
1321 
1322 
1323 template <typename number>
1324 template <typename somenumber>
1325 void
PSOR(Vector<somenumber> & dst,const std::vector<size_type> & permutation,const std::vector<size_type> & inverse_permutation,const number)1326 ChunkSparseMatrix<number>::PSOR(
1327   Vector<somenumber> &          dst,
1328   const std::vector<size_type> &permutation,
1329   const std::vector<size_type> &inverse_permutation,
1330   const number /*om*/) const
1331 {
1332   (void)dst;
1333   (void)permutation;
1334   (void)inverse_permutation;
1335   Assert(cols != nullptr, ExcNotInitialized());
1336   Assert(val != nullptr, ExcNotInitialized());
1337   Assert(m() == n(),
1338          ExcMessage("This operation is only valid on square matrices."));
1339 
1340   Assert(m() == dst.size(), ExcDimensionMismatch(m(), dst.size()));
1341   Assert(m() == permutation.size(),
1342          ExcDimensionMismatch(m(), permutation.size()));
1343   Assert(m() == inverse_permutation.size(),
1344          ExcDimensionMismatch(m(), inverse_permutation.size()));
1345 
1346   Assert(false, ExcNotImplemented());
1347 }
1348 
1349 
1350 template <typename number>
1351 template <typename somenumber>
1352 void
TPSOR(Vector<somenumber> & dst,const std::vector<size_type> & permutation,const std::vector<size_type> & inverse_permutation,const number)1353 ChunkSparseMatrix<number>::TPSOR(
1354   Vector<somenumber> &          dst,
1355   const std::vector<size_type> &permutation,
1356   const std::vector<size_type> &inverse_permutation,
1357   const number /*om*/) const
1358 {
1359   (void)dst;
1360   (void)permutation;
1361   (void)inverse_permutation;
1362   Assert(cols != nullptr, ExcNotInitialized());
1363   Assert(val != nullptr, ExcNotInitialized());
1364   Assert(m() == n(),
1365          ExcMessage("This operation is only valid on square matrices."));
1366 
1367   Assert(m() == dst.size(), ExcDimensionMismatch(m(), dst.size()));
1368   Assert(m() == permutation.size(),
1369          ExcDimensionMismatch(m(), permutation.size()));
1370   Assert(m() == inverse_permutation.size(),
1371          ExcDimensionMismatch(m(), inverse_permutation.size()));
1372 
1373   Assert(false, ExcNotImplemented());
1374 }
1375 
1376 
1377 
1378 template <typename number>
1379 template <typename somenumber>
1380 void
SOR_step(Vector<somenumber> & v,const Vector<somenumber> & b,const number)1381 ChunkSparseMatrix<number>::SOR_step(Vector<somenumber> &      v,
1382                                     const Vector<somenumber> &b,
1383                                     const number /*om*/) const
1384 {
1385   (void)v;
1386   (void)b;
1387   Assert(cols != nullptr, ExcNotInitialized());
1388   Assert(val != nullptr, ExcNotInitialized());
1389   Assert(m() == n(),
1390          ExcMessage("This operation is only valid on square matrices."));
1391 
1392   Assert(m() == v.size(), ExcDimensionMismatch(m(), v.size()));
1393   Assert(m() == b.size(), ExcDimensionMismatch(m(), b.size()));
1394 
1395   Assert(false, ExcNotImplemented());
1396 }
1397 
1398 
1399 
1400 template <typename number>
1401 template <typename somenumber>
1402 void
TSOR_step(Vector<somenumber> & v,const Vector<somenumber> & b,const number)1403 ChunkSparseMatrix<number>::TSOR_step(Vector<somenumber> &      v,
1404                                      const Vector<somenumber> &b,
1405                                      const number /*om*/) const
1406 {
1407   (void)v;
1408   (void)b;
1409   Assert(cols != nullptr, ExcNotInitialized());
1410   Assert(val != nullptr, ExcNotInitialized());
1411   Assert(m() == n(),
1412          ExcMessage("This operation is only valid on square matrices."));
1413 
1414   Assert(m() == v.size(), ExcDimensionMismatch(m(), v.size()));
1415   Assert(m() == b.size(), ExcDimensionMismatch(m(), b.size()));
1416 
1417   Assert(false, ExcNotImplemented());
1418 }
1419 
1420 
1421 
1422 template <typename number>
1423 template <typename somenumber>
1424 void
SSOR_step(Vector<somenumber> & v,const Vector<somenumber> & b,const number om)1425 ChunkSparseMatrix<number>::SSOR_step(Vector<somenumber> &      v,
1426                                      const Vector<somenumber> &b,
1427                                      const number              om) const
1428 {
1429   SOR_step(v, b, om);
1430   TSOR_step(v, b, om);
1431 }
1432 
1433 
1434 
1435 template <typename number>
1436 template <typename somenumber>
1437 void
SSOR(Vector<somenumber> & dst,const number)1438 ChunkSparseMatrix<number>::SSOR(Vector<somenumber> &dst,
1439                                 const number /*om*/) const
1440 {
1441   (void)dst;
1442   Assert(cols != nullptr, ExcNotInitialized());
1443   Assert(val != nullptr, ExcNotInitialized());
1444   Assert(m() == n(),
1445          ExcMessage("This operation is only valid on square matrices."));
1446 
1447   Assert(m() == dst.size(), ExcDimensionMismatch(m(), dst.size()));
1448 
1449   Assert(false, ExcNotImplemented());
1450 }
1451 
1452 
1453 
1454 template <typename number>
1455 void
print(std::ostream & out)1456 ChunkSparseMatrix<number>::print(std::ostream &out) const
1457 {
1458   AssertThrow(out, ExcIO());
1459 
1460   Assert(cols != nullptr, ExcNotInitialized());
1461   Assert(val != nullptr, ExcNotInitialized());
1462 
1463   Assert(false, ExcNotImplemented());
1464 
1465   AssertThrow(out, ExcIO());
1466 }
1467 
1468 
1469 template <typename number>
1470 void
print_formatted(std::ostream & out,const unsigned int precision,const bool scientific,const unsigned int width_,const char * zero_string,const double denominator)1471 ChunkSparseMatrix<number>::print_formatted(std::ostream &     out,
1472                                            const unsigned int precision,
1473                                            const bool         scientific,
1474                                            const unsigned int width_,
1475                                            const char *       zero_string,
1476                                            const double       denominator) const
1477 {
1478   AssertThrow(out, ExcIO());
1479 
1480   Assert(cols != nullptr, ExcNotInitialized());
1481   Assert(val != nullptr, ExcNotInitialized());
1482 
1483   unsigned int width = width_;
1484 
1485   Assert(false, ExcNotImplemented());
1486 
1487   std::ios::fmtflags old_flags     = out.flags();
1488   unsigned int       old_precision = out.precision(precision);
1489 
1490   if (scientific)
1491     {
1492       out.setf(std::ios::scientific, std::ios::floatfield);
1493       if (!width)
1494         width = precision + 7;
1495     }
1496   else
1497     {
1498       out.setf(std::ios::fixed, std::ios::floatfield);
1499       if (!width)
1500         width = precision + 2;
1501     }
1502 
1503   for (size_type i = 0; i < m(); ++i)
1504     {
1505       for (size_type j = 0; j < n(); ++j)
1506         if (cols->sparsity_pattern(i, j) != SparsityPattern::invalid_entry)
1507           out << std::setw(width)
1508               << val[cols->sparsity_pattern(i, j)] * denominator << ' ';
1509         else
1510           out << std::setw(width) << zero_string << ' ';
1511       out << std::endl;
1512     };
1513   AssertThrow(out, ExcIO());
1514 
1515   // reset output format
1516   out.precision(old_precision);
1517   out.flags(old_flags);
1518 }
1519 
1520 
1521 
1522 template <typename number>
1523 void
print_pattern(std::ostream & out,const double threshold)1524 ChunkSparseMatrix<number>::print_pattern(std::ostream &out,
1525                                          const double  threshold) const
1526 {
1527   AssertThrow(out, ExcIO());
1528 
1529   Assert(cols != nullptr, ExcNotInitialized());
1530   Assert(val != nullptr, ExcNotInitialized());
1531 
1532   const size_type chunk_size = cols->get_chunk_size();
1533 
1534   // loop over all chunk rows and columns, and each time we find something
1535   // repeat it chunk_size times in both directions
1536   for (size_type i = 0; i < cols->sparsity_pattern.n_rows(); ++i)
1537     {
1538       for (size_type d = 0; d < chunk_size; ++d)
1539         for (size_type j = 0; j < cols->sparsity_pattern.n_cols(); ++j)
1540           if (cols->sparsity_pattern(i, j) == SparsityPattern::invalid_entry)
1541             {
1542               for (size_type e = 0; e < chunk_size; ++e)
1543                 out << '.';
1544             }
1545           else if (std::fabs(val[cols->sparsity_pattern(i, j)]) > threshold)
1546             {
1547               for (size_type e = 0; e < chunk_size; ++e)
1548                 out << '*';
1549             }
1550           else
1551             {
1552               for (size_type e = 0; e < chunk_size; ++e)
1553                 out << ':';
1554             }
1555       out << std::endl;
1556     }
1557 
1558   AssertThrow(out, ExcIO());
1559 }
1560 
1561 
1562 
1563 template <typename number>
1564 void
block_write(std::ostream & out)1565 ChunkSparseMatrix<number>::block_write(std::ostream &out) const
1566 {
1567   AssertThrow(out, ExcIO());
1568 
1569   // first the simple objects, bracketed in [...]
1570   out << '[' << max_len << "][";
1571   // then write out real data
1572   out.write(reinterpret_cast<const char *>(val.get()),
1573             reinterpret_cast<const char *>(val.get() + max_len) -
1574               reinterpret_cast<const char *>(val.get()));
1575   out << ']';
1576 
1577   AssertThrow(out, ExcIO());
1578 }
1579 
1580 
1581 
1582 template <typename number>
1583 void
block_read(std::istream & in)1584 ChunkSparseMatrix<number>::block_read(std::istream &in)
1585 {
1586   AssertThrow(in, ExcIO());
1587 
1588   char c;
1589 
1590   // first read in simple data
1591   in >> c;
1592   AssertThrow(c == '[', ExcIO());
1593   in >> max_len;
1594 
1595   in >> c;
1596   AssertThrow(c == ']', ExcIO());
1597   in >> c;
1598   AssertThrow(c == '[', ExcIO());
1599 
1600   // reallocate space
1601   val = std::make_unique<number[]>(max_len);
1602 
1603   // then read data
1604   in.read(reinterpret_cast<char *>(val.get()),
1605           reinterpret_cast<char *>(val.get() + max_len) -
1606             reinterpret_cast<char *>(val.get()));
1607   in >> c;
1608   AssertThrow(c == ']', ExcIO());
1609 }
1610 
1611 
1612 
1613 template <typename number>
1614 std::size_t
memory_consumption()1615 ChunkSparseMatrix<number>::memory_consumption() const
1616 {
1617   return sizeof(*this) + max_len * sizeof(number);
1618 }
1619 
1620 
1621 DEAL_II_NAMESPACE_CLOSE
1622 
1623 #endif
1624