deal.II version GIT relicensing-6834-g5b78e6bcdf 2026-10-01 11:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
matrix_tools.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 1998 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
17
21
22#include <deal.II/fe/fe.h>
24
26
31#include <deal.II/lac/vector.h>
32
34
35#ifdef DEAL_II_WITH_PETSC
39#endif
40
41#ifdef DEAL_II_WITH_TRILINOS
46#endif
47
48#include <algorithm>
49#include <cmath>
50#include <complex>
51
52
54
55
56
57namespace MatrixTools
58{
59 // TODO:[WB] I don't think that the optimized storage of diagonals is needed
60 // (GK)
61 template <typename number>
62 void
64 const std::map<types::global_dof_index, number> &boundary_values,
66 Vector<number> &solution,
67 Vector<number> &right_hand_side,
68 const bool eliminate_columns)
69 {
70 Assert(matrix.n() == right_hand_side.size(),
71 ExcDimensionMismatch(matrix.n(), right_hand_side.size()));
72 Assert(matrix.n() == solution.size(),
73 ExcDimensionMismatch(matrix.n(), solution.size()));
74 Assert(matrix.n() == matrix.m(),
75 ExcDimensionMismatch(matrix.n(), matrix.m()));
76
77 // if no boundary values are to be applied
78 // simply return
79 if (boundary_values.empty())
80 return;
81
82
83 const types::global_dof_index n_dofs = matrix.m();
84
85 // if a diagonal entry is zero
86 // later, then we use another
87 // number instead. take it to be
88 // the first nonzero diagonal
89 // element of the matrix, or 1 if
90 // there is no such thing
91 number first_nonzero_diagonal_entry = 1;
92 for (types::global_dof_index i = 0; i < n_dofs; ++i)
93 if (matrix.diag_element(i) != number())
94 {
95 first_nonzero_diagonal_entry = matrix.diag_element(i);
96 break;
97 }
98
99
100 typename std::map<types::global_dof_index, number>::const_iterator
101 dof = boundary_values.begin(),
102 endd = boundary_values.end();
103 for (; dof != endd; ++dof)
104 {
105 Assert(dof->first < n_dofs, ExcInternalError());
106
107 const types::global_dof_index dof_number = dof->first;
108 // for each boundary dof:
109
110 // set entries of this line to zero except for the diagonal
111 // entry
112 for (typename SparseMatrix<number>::iterator p =
113 matrix.begin(dof_number);
114 p != matrix.end(dof_number);
115 ++p)
116 if (p->column() != dof_number)
117 p->value() = 0.;
118
119 // set right hand side to
120 // wanted value: if main diagonal
121 // entry nonzero, don't touch it
122 // and scale rhs accordingly. If
123 // zero, take the first main
124 // diagonal entry we can find, or
125 // one if no nonzero main diagonal
126 // element exists. Normally, however,
127 // the main diagonal entry should
128 // not be zero.
129 //
130 // store the new rhs entry to make
131 // the gauss step more efficient
132 number new_rhs;
133 if (matrix.diag_element(dof_number) != number())
134 {
135 new_rhs = dof->second * matrix.diag_element(dof_number);
136 right_hand_side(dof_number) = new_rhs;
137 }
138 else
139 {
140 matrix.set(dof_number, dof_number, first_nonzero_diagonal_entry);
141 new_rhs = dof->second * first_nonzero_diagonal_entry;
142 right_hand_side(dof_number) = new_rhs;
143 }
144
145
146 // if the user wants to have
147 // the symmetry of the matrix
148 // preserved, and if the
149 // sparsity pattern is
150 // symmetric, then do a Gauss
151 // elimination step with the
152 // present row
153 if (eliminate_columns)
154 {
155 // store the only nonzero entry
156 // of this line for the Gauss
157 // elimination step
158 const number diagonal_entry = matrix.diag_element(dof_number);
159
160 // we have to loop over all rows of the matrix which have
161 // a nonzero entry in the column which we work in
162 // presently. if the sparsity pattern is symmetric, then
163 // we can get the positions of these rows cheaply by
164 // looking at the nonzero column numbers of the present
165 // row. we need not look at the first entry of each row,
166 // since that is the diagonal element and thus the present
167 // row
168 for (typename SparseMatrix<number>::iterator q =
169 matrix.begin(dof_number) + 1;
170 q != matrix.end(dof_number);
171 ++q)
172 {
173 const types::global_dof_index row = q->column();
174
175 // find the position of element (row,dof_number)
176 const typename SparseMatrix<number>::iterator p =
177 Utilities::lower_bound(matrix.begin(row) + 1,
178 matrix.end(row),
179 dof_number,
180 [](const auto &a, const auto &b) {
181 return a.column() < b;
182 });
183
184 // check whether this line has an entry in the
185 // regarding column (check for ==dof_number and !=
186 // next_row, since if row==dof_number-1, *p is a
187 // past-the-end pointer but points to dof_number
188 // anyway...)
189 //
190 // there should be such an entry! we know this because
191 // we have assumed that the sparsity pattern is
192 // symmetric and we only walk over those rows for
193 // which the current row has a column entry
194 Assert((p != matrix.end(row)) && (p->column() == dof_number),
196 "This function is trying to access an element of the "
197 "matrix that doesn't seem to exist. Are you using a "
198 "nonsymmetric sparsity pattern? If so, you are not "
199 "allowed to set the eliminate_column argument of this "
200 "function, see the documentation."));
201
202 // correct right hand side
203 right_hand_side(row) -=
204 static_cast<number>(p->value()) / diagonal_entry * new_rhs;
205
206 // set matrix entry to zero
207 p->value() = 0.;
208 }
209 }
210
211 // preset solution vector
212 solution(dof_number) = dof->second;
213 }
214 }
215
216
217
218 template <typename number>
219 void
221 const std::map<types::global_dof_index, number> &boundary_values,
223 BlockVector<number> &solution,
224 BlockVector<number> &right_hand_side,
225 const bool eliminate_columns)
226 {
227 const unsigned int blocks = matrix.n_block_rows();
228
229 Assert(matrix.n() == right_hand_side.size(),
230 ExcDimensionMismatch(matrix.n(), right_hand_side.size()));
231 Assert(matrix.n() == solution.size(),
232 ExcDimensionMismatch(matrix.n(), solution.size()));
233 Assert(matrix.n_block_rows() == matrix.n_block_cols(), ExcNotQuadratic());
234 Assert(matrix.get_sparsity_pattern().get_row_indices() ==
235 matrix.get_sparsity_pattern().get_column_indices(),
237 Assert(matrix.get_sparsity_pattern().get_column_indices() ==
238 solution.get_block_indices(),
240 Assert(matrix.get_sparsity_pattern().get_row_indices() ==
241 right_hand_side.get_block_indices(),
243
244 // if no boundary values are to be applied
245 // simply return
246 if (boundary_values.empty())
247 return;
248
249
250 const types::global_dof_index n_dofs = matrix.m();
251
252 // if a diagonal entry is zero
253 // later, then we use another
254 // number instead. take it to be
255 // the first nonzero diagonal
256 // element of the matrix, or 1 if
257 // there is no such thing
258 number first_nonzero_diagonal_entry = 0;
259 for (unsigned int diag_block = 0; diag_block < blocks; ++diag_block)
260 {
261 for (types::global_dof_index i = 0;
262 i < matrix.block(diag_block, diag_block).n();
263 ++i)
264 if (matrix.block(diag_block, diag_block).diag_element(i) != number{})
265 {
266 first_nonzero_diagonal_entry =
267 matrix.block(diag_block, diag_block).diag_element(i);
268 break;
269 }
270 // check whether we have found
271 // something in the present
272 // block
273 if (first_nonzero_diagonal_entry != number{})
274 break;
275 }
276 // nothing found on all diagonal
277 // blocks? if so, use 1.0 instead
278 if (first_nonzero_diagonal_entry == number{})
279 first_nonzero_diagonal_entry = 1;
280
281
282 typename std::map<types::global_dof_index, number>::const_iterator
283 dof = boundary_values.begin(),
284 endd = boundary_values.end();
285 const BlockSparsityPattern &sparsity_pattern =
286 matrix.get_sparsity_pattern();
287
288 // pointer to the mapping between
289 // global and block indices. since
290 // the row and column mappings are
291 // equal, store a pointer on only
292 // one of them
293 const BlockIndices &index_mapping = sparsity_pattern.get_column_indices();
294
295 // now loop over all boundary dofs
296 for (; dof != endd; ++dof)
297 {
298 Assert(dof->first < n_dofs, ExcInternalError());
299
300 // get global index and index
301 // in the block in which this
302 // dof is located
303 const types::global_dof_index dof_number = dof->first;
304 const std::pair<unsigned int, types::global_dof_index> block_index =
305 index_mapping.global_to_local(dof_number);
306
307 // for each boundary dof:
308
309 // set entries of this line
310 // to zero except for the diagonal
311 // entry. Note that the diagonal
312 // entry is always the first one
313 // in a row for square matrices
314 for (unsigned int block_col = 0; block_col < blocks; ++block_col)
315 for (typename SparseMatrix<number>::iterator p =
316 (block_col == block_index.first ?
317 matrix.block(block_index.first, block_col)
318 .begin(block_index.second) +
319 1 :
320 matrix.block(block_index.first, block_col)
321 .begin(block_index.second));
322 p != matrix.block(block_index.first, block_col)
323 .end(block_index.second);
324 ++p)
325 p->value() = 0;
326
327 // set right hand side to
328 // wanted value: if main diagonal
329 // entry nonzero, don't touch it
330 // and scale rhs accordingly. If
331 // zero, take the first main
332 // diagonal entry we can find, or
333 // one if no nonzero main diagonal
334 // element exists. Normally, however,
335 // the main diagonal entry should
336 // not be zero.
337 //
338 // store the new rhs entry to make
339 // the gauss step more efficient
340 number new_rhs;
341 if (matrix.block(block_index.first, block_index.first)
342 .diag_element(block_index.second) != number{})
343 new_rhs =
344 dof->second * matrix.block(block_index.first, block_index.first)
345 .diag_element(block_index.second);
346 else
347 {
348 matrix.block(block_index.first, block_index.first)
349 .diag_element(block_index.second) = first_nonzero_diagonal_entry;
350 new_rhs = dof->second * first_nonzero_diagonal_entry;
351 }
352 right_hand_side.block(block_index.first)(block_index.second) = new_rhs;
353
354
355 // if the user wants to have
356 // the symmetry of the matrix
357 // preserved, and if the
358 // sparsity pattern is
359 // symmetric, then do a Gauss
360 // elimination step with the
361 // present row. this is a
362 // little more complicated for
363 // block matrices.
364 if (eliminate_columns)
365 {
366 // store the only nonzero entry
367 // of this line for the Gauss
368 // elimination step
369 const number diagonal_entry =
370 matrix.block(block_index.first, block_index.first)
371 .diag_element(block_index.second);
372
373 // we have to loop over all
374 // rows of the matrix which
375 // have a nonzero entry in
376 // the column which we work
377 // in presently. if the
378 // sparsity pattern is
379 // symmetric, then we can
380 // get the positions of
381 // these rows cheaply by
382 // looking at the nonzero
383 // column numbers of the
384 // present row.
385 //
386 // note that if we check
387 // whether row @p{row} in
388 // block (r,c) is non-zero,
389 // then we have to check
390 // for the existence of
391 // column @p{row} in block
392 // (c,r), i.e. of the
393 // transpose block
394 for (unsigned int block_row = 0; block_row < blocks; ++block_row)
395 {
396 // get pointers to the sparsity patterns of this block and of
397 // the transpose one
398 const SparsityPattern &this_sparsity =
399 sparsity_pattern.block(block_row, block_index.first);
400
401 SparseMatrix<number> &this_matrix =
402 matrix.block(block_row, block_index.first);
403 SparseMatrix<number> &transpose_matrix =
404 matrix.block(block_index.first, block_row);
405
406 // traverse the row of the transpose block to find the
407 // interesting rows in the present block. don't use the
408 // diagonal element of the diagonal block
409 for (typename SparseMatrix<number>::iterator q =
410 (block_index.first == block_row ?
411 transpose_matrix.begin(block_index.second) + 1 :
412 transpose_matrix.begin(block_index.second));
413 q != transpose_matrix.end(block_index.second);
414 ++q)
415 {
416 // get the number of the column in this row in which a
417 // nonzero entry is. this is also the row of the transpose
418 // block which has an entry in the interesting row
419 const types::global_dof_index row = q->column();
420
421 // find the position of element (row,dof_number) in this
422 // block (not in the transpose one). note that we have to
423 // take care of special cases with square sub-matrices
425 this_matrix.end();
426
427 if (this_sparsity.n_rows() == this_sparsity.n_cols())
428 {
429 if (this_matrix.begin(row)->column() ==
430 block_index.second)
431 p = this_matrix.begin(row);
432 else
433 p = Utilities::lower_bound(this_matrix.begin(row) + 1,
434 this_matrix.end(row),
435 block_index.second,
436 [](const auto &a,
437 const auto &b) {
438 return a.column() < b;
439 });
440 }
441 else
442 p = Utilities::lower_bound(this_matrix.begin(row),
443 this_matrix.end(row),
444 block_index.second,
445 [](const auto &a,
446 const auto &b) {
447 return a.column() < b;
448 });
449
450 // check whether this line has an entry in the
451 // regarding column (check for ==dof_number and !=
452 // next_row, since if row==dof_number-1, *p is a
453 // past-the-end pointer but points to dof_number
454 // anyway...)
455 //
456 // there should be such an entry! we know this because
457 // we have assumed that the sparsity pattern is
458 // symmetric and we only walk over those rows for
459 // which the current row has a column entry
460 Assert((p->column() == block_index.second) &&
461 (p != this_matrix.end(row)),
463
464 // correct right hand side
465 right_hand_side.block(block_row)(row) -=
466 number(p->value()) / diagonal_entry * new_rhs;
467
468 // set matrix entry to zero
469 p->value() = 0.;
470 }
471 }
472 }
473
474 // preset solution vector
475 solution.block(block_index.first)(block_index.second) = dof->second;
476 }
477 }
478
479
480
481 template <typename number>
482 void
484 const std::map<types::global_dof_index, number> &boundary_values,
485 const std::vector<types::global_dof_index> &local_dof_indices,
486 FullMatrix<number> &local_matrix,
487 Vector<number> &local_rhs,
488 const bool eliminate_columns)
489 {
490 Assert(local_dof_indices.size() == local_matrix.m(),
491 ExcDimensionMismatch(local_dof_indices.size(), local_matrix.m()));
492 Assert(local_dof_indices.size() == local_matrix.n(),
493 ExcDimensionMismatch(local_dof_indices.size(), local_matrix.n()));
494 Assert(local_dof_indices.size() == local_rhs.size(),
495 ExcDimensionMismatch(local_dof_indices.size(), local_rhs.size()));
496
497 // if there is nothing to do, then exit
498 // right away
499 if (boundary_values.empty())
500 return;
501
502 // otherwise traverse all the dofs used in
503 // the local matrices and vectors and see
504 // what's there to do
505
506 // if we need to treat an entry, then we
507 // set the diagonal entry to its absolute
508 // value. if it is zero, we used to set it
509 // to one, which is a really terrible
510 // choice that can lead to hours of
511 // searching for bugs in programs (I
512 // experienced this :-( ) if the matrix
513 // entries are otherwise very large. this
514 // is so since iterative solvers would
515 // simply not correct boundary nodes for
516 // their correct values since the residual
517 // contributions of their rows of the
518 // linear system is almost zero if the
519 // diagonal entry is one. thus, set it to
520 // the average absolute value of the
521 // nonzero diagonal elements.
522 //
523 // we only compute this value lazily the
524 // first time we need it.
525 number average_diagonal = 0;
526 const unsigned int n_local_dofs = local_dof_indices.size();
527 for (unsigned int i = 0; i < n_local_dofs; ++i)
528 {
529 const typename std::map<types::global_dof_index, number>::const_iterator
530 boundary_value = boundary_values.find(local_dof_indices[i]);
531 if (boundary_value != boundary_values.end())
532 {
533 // remove this row, except for the
534 // diagonal element
535 for (unsigned int j = 0; j < n_local_dofs; ++j)
536 if (i != j)
537 local_matrix(i, j) = 0;
538
539 // replace diagonal entry by its
540 // absolute value to make sure that
541 // everything remains positive, or
542 // by the average diagonal value if
543 // zero
544 if (local_matrix(i, i) == number{})
545 {
546 // if average diagonal hasn't
547 // yet been computed, do so now
548 if (average_diagonal == number{})
549 {
550 unsigned int nonzero_diagonals = 0;
551 for (unsigned int k = 0; k < n_local_dofs; ++k)
552 if (local_matrix(k, k) != number{})
553 {
554 average_diagonal += std::abs(local_matrix(k, k));
555 ++nonzero_diagonals;
556 }
557 if (nonzero_diagonals != 0)
558 average_diagonal /= nonzero_diagonals;
559 else
560 average_diagonal = 0;
561 }
562
563 // only if all diagonal entries
564 // are zero, then resort to the
565 // last measure: choose one
566 if (average_diagonal == number{})
567 average_diagonal = 1.;
568
569 local_matrix(i, i) = average_diagonal;
570 }
571 else
572 local_matrix(i, i) = std::abs(local_matrix(i, i));
573
574 // and replace rhs entry by correct
575 // value
576 local_rhs(i) = local_matrix(i, i) * boundary_value->second;
577
578 // finally do the elimination step
579 // if requested
580 if (eliminate_columns == true)
581 {
582 for (unsigned int row = 0; row < n_local_dofs; ++row)
583 if (row != i)
584 {
585 local_rhs(row) -=
586 local_matrix(row, i) * boundary_value->second;
587 local_matrix(row, i) = 0;
588 }
589 }
590 }
591 }
592 }
593} // namespace MatrixTools
594
595
596
597// explicit instantiations
598#include "numerics/matrix_tools.inst"
599
600
std::pair< unsigned int, size_type > global_to_local(const size_type i) const
SparsityPatternType & block(const size_type row, const size_type column)
const BlockIndices & get_column_indices() const
virtual size_type size() const override
BlockType & block(const unsigned int i)
const BlockIndices & get_block_indices() const
size_type n() const
size_type m() const
const_iterator end() const
const_iterator begin() const
size_type n_rows() const
size_type n_cols() const
virtual size_type size() const override
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
static ::ExceptionBase & ExcBlocksDontMatch()
#define Assert(cond, exc)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
static ::ExceptionBase & ExcNotQuadratic()
static ::ExceptionBase & ExcMessage(std::string arg1)
void apply_boundary_values(const std::map< types::global_dof_index, number > &boundary_values, SparseMatrix< number > &matrix, Vector< number > &solution, Vector< number > &right_hand_side, const bool eliminate_columns=true)
void local_apply_boundary_values(const std::map< types::global_dof_index, number > &boundary_values, const std::vector< types::global_dof_index > &local_dof_indices, FullMatrix< number > &local_matrix, Vector< number > &local_rhs, const bool eliminate_columns)
Iterator lower_bound(Iterator first, Iterator last, const T &val)
Definition utilities.h:1003
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)