deal.II version GIT relicensing-6842-g793a97d2aa 2026-10-02 14:00: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
utilities.h
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) 2017 - 2024 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
13#ifndef dealii_lac_utilities_h
14#define dealii_lac_utilities_h
15
16#include <deal.II/base/config.h>
17
20
23
24#include <array>
25#include <complex>
26#include <limits>
27
29
30namespace Utilities
31{
35 namespace LinearAlgebra
36 {
61 template <typename NumberType>
62 std::array<NumberType, 3>
63 givens_rotation(const NumberType &x, const NumberType &y);
64
91 template <typename NumberType>
92 std::array<NumberType, 3>
93 hyperbolic_rotation(const NumberType &x, const NumberType &y);
94
118 template <typename OperatorType, typename VectorType>
119 double
120 lanczos_largest_eigenvalue(const OperatorType &H,
121 const VectorType &v0,
122 const unsigned int k,
123 VectorMemory<VectorType> &vector_memory,
124 std::vector<double> *eigenvalues = nullptr);
125
162 template <typename OperatorType, typename VectorType>
163 void
164 chebyshev_filter(VectorType &x,
165 const OperatorType &H,
166 const unsigned int n,
167 const std::pair<double, double> unwanted_spectrum,
168 const double tau,
169 VectorMemory<VectorType> &vector_memory);
170
171 } // namespace LinearAlgebra
172
173} // namespace Utilities
174
175
176/*------------------------- Implementation ----------------------------*/
177
178#ifndef DOXYGEN
179
180namespace internal
181{
182 namespace UtilitiesImplementation
183 {
184 // We want to avoid including our own LAPACK wrapper header in any external
185 // headers to avoid possible conflicts with other packages that may define
186 // their own such header. At the same time we want to be able to call some
187 // LAPACK functions from the template functions below. To resolve both
188 // problems define some extra wrappers here that can be in the header:
189 template <typename Number>
190 void
191 call_stev(const char jobz,
192 const types::blas_int n,
193 Number *d,
194 Number *e,
195 Number *z,
196 const types::blas_int ldz,
197 Number *work,
198 types::blas_int *info);
199 } // namespace UtilitiesImplementation
200} // namespace internal
201
202namespace Utilities
203{
204 namespace LinearAlgebra
205 {
206 template <typename NumberType>
207 std::array<std::complex<NumberType>, 3>
208 hyperbolic_rotation(const std::complex<NumberType> & /*f*/,
209 const std::complex<NumberType> & /*g*/)
210 {
212 std::array<NumberType, 3> res;
213 return res;
214 }
215
216
217
218 template <typename NumberType>
219 std::array<NumberType, 3>
220 hyperbolic_rotation(const NumberType &f, const NumberType &g)
221 {
222 Assert(f != 0, ExcDivideByZero());
223 const NumberType tau = g / f;
224 AssertThrow(std::abs(tau) < 1.,
226 "real-valued Hyperbolic rotation does not exist for (" +
227 std::to_string(f) + "," + std::to_string(g) + ")"));
228 const NumberType u =
229 std::copysign(std::sqrt((1. - tau) * (1. + tau)),
230 f); // <-- more stable than std::sqrt(1.-tau*tau)
231 std::array<NumberType, 3> csr;
232 csr[0] = 1. / u; // c
233 csr[1] = csr[0] * tau; // s
234 csr[2] = f * u; // r
235 return csr;
236 }
237
238
239
240 template <typename NumberType>
241 std::array<std::complex<NumberType>, 3>
242 givens_rotation(const std::complex<NumberType> & /*f*/,
243 const std::complex<NumberType> & /*g*/)
244 {
246 std::array<NumberType, 3> res;
247 return res;
248 }
249
250
251
252 template <typename NumberType>
253 std::array<NumberType, 3>
254 givens_rotation(const NumberType &f, const NumberType &g)
255 {
256 std::array<NumberType, 3> res;
257 // naive calculation for "r" may overflow or underflow:
258 // c = x / \sqrt{x^2+y^2}
259 // s = -y / \sqrt{x^2+y^2}
260
261 // See Golub 2013, Matrix computations, Chapter 5.1.8
262 // Algorithm 5.1.3
263 // and
264 // Anderson (2000),
265 // Discontinuous Plane Rotations and the Symmetric Eigenvalue Problem.
266 // LAPACK Working Note 150, University of Tennessee, UT-CS-00-454,
267 // December 4, 2000.
268 // Algorithm 4
269 // We implement the latter below:
270 if (g == NumberType())
271 {
272 res[0] = std::copysign(1., f);
273 res[1] = NumberType();
274 res[2] = std::abs(f);
275 }
276 else if (f == NumberType())
277 {
278 res[0] = NumberType();
279 res[1] = std::copysign(1., g);
280 res[2] = std::abs(g);
281 }
282 else if (std::abs(f) > std::abs(g))
283 {
284 const NumberType tau = g / f;
285 const NumberType u = std::copysign(std::sqrt(1. + tau * tau), f);
286 res[0] = 1. / u; // c
287 res[1] = res[0] * tau; // s
288 res[2] = f * u; // r
289 }
290 else
291 {
292 const NumberType tau = f / g;
293 const NumberType u = std::copysign(std::sqrt(1. + tau * tau), g);
294 res[1] = 1. / u; // s
295 res[0] = res[1] * tau; // c
296 res[2] = g * u; // r
297 }
298
299 return res;
300 }
301
302
303
304 template <typename OperatorType, typename VectorType>
305 double
306 lanczos_largest_eigenvalue(const OperatorType &H,
307 const VectorType &v0_,
308 const unsigned int k,
309 VectorMemory<VectorType> &vector_memory,
310 std::vector<double> *eigenvalues)
311 {
312 // Do k-step Lanczos:
313
314 typename VectorMemory<VectorType>::Pointer v(vector_memory);
315 typename VectorMemory<VectorType>::Pointer v0(vector_memory);
316 typename VectorMemory<VectorType>::Pointer f(vector_memory);
317
318 v->reinit(v0_);
319 v0->reinit(v0_);
320 f->reinit(v0_);
321
322 // two vectors to store diagonal and subdiagonal of the Lanczos
323 // matrix
324 std::vector<double> diagonal;
325 std::vector<double> subdiagonal;
326
327 // 1. Normalize input vector
328 (*v) = v0_;
329 double a = v->l2_norm();
330 Assert(a != 0, ExcDivideByZero());
331 (*v) *= 1. / a;
332
333 // 2. Compute f = Hv; a = f*v; f <- f - av; T(0,0)=a;
334 H.vmult(*f, *v);
335 a = (*f) * (*v);
336 f->add(-a, *v);
337 diagonal.push_back(a);
338
339 // 3. Loop over steps
340 for (unsigned int i = 1; i < k; ++i)
341 {
342 // 4. L2 norm of f
343 const double b = f->l2_norm();
344 Assert(b != 0, ExcDivideByZero());
345 // 5. v0 <- v; v <- f/b
346 *v0 = *v;
347 *v = *f;
348 (*v) *= 1. / b;
349 // 6. f = Hv; f <- f - b v0;
350 H.vmult(*f, *v);
351 f->add(-b, *v0);
352 // 7. a = f*v; f <- f - a v;
353 a = (*f) * (*v);
354 f->add(-a, *v);
355 // 8. T(i,i-1) = T(i-1,i) = b; T(i,i) = a;
356 diagonal.push_back(a);
357 subdiagonal.push_back(b);
358 }
359
360 Assert(diagonal.size() == k, ExcInternalError());
361 Assert(subdiagonal.size() == k - 1, ExcInternalError());
362
363 // Use Lapack dstev to get ||T||_2 norm, i.e. the largest eigenvalue
364 // of T
365 const types::blas_int n = k;
366 std::vector<double> Z; // unused for eigenvalues-only ("N") job
367 const types::blas_int ldz = 1; // ^^ (>=1)
368 std::vector<double> work; // ^^
369 types::blas_int info;
370 // call lapack_templates.h wrapper:
372 n,
373 diagonal.data(),
374 subdiagonal.data(),
375 Z.data(),
376 ldz,
377 work.data(),
378 &info);
379
380 Assert(info == 0, LAPACKSupport::ExcErrorCode("dstev", info));
381
382 if (eigenvalues != nullptr)
383 {
384 eigenvalues->resize(diagonal.size());
385 std::copy(diagonal.begin(), diagonal.end(), eigenvalues->begin());
386 }
387
388 // note that the largest eigenvalue of T is below the largest
389 // eigenvalue of the operator.
390 // return ||T||_2 + ||f||_2, although it is not guaranteed to be an upper
391 // bound.
392 return diagonal[k - 1] + f->l2_norm();
393 }
394
395
396 template <typename OperatorType, typename VectorType>
397 void
398 chebyshev_filter(VectorType &x,
399 const OperatorType &op,
400 const unsigned int degree,
401 const std::pair<double, double> unwanted_spectrum,
402 const double a_L,
403 VectorMemory<VectorType> &vector_memory)
404 {
405 const double a = unwanted_spectrum.first;
406 const double b = unwanted_spectrum.second;
407 Assert(degree > 0, ExcMessage("Only positive degrees make sense."));
408
409 const bool scale = numbers::is_finite(a_L);
410 Assert(
411 a < b,
413 "Lower bound of the unwanted spectrum should be smaller than the upper bound."));
414
415 Assert(a_L <= a || a_L >= b || !scale,
417 "Scaling point should be outside of the unwanted spectrum."));
418
419 // Setup auxiliary vectors:
420 typename VectorMemory<VectorType>::Pointer p_y(vector_memory);
421 typename VectorMemory<VectorType>::Pointer p_yn(vector_memory);
422
423 p_y->reinit(x);
424 p_yn->reinit(x);
425
426 // convenience to avoid pointers
427 VectorType &y = *p_y;
428 VectorType &yn = *p_yn;
429
430 // Below is an implementation of
431 // Algorithm 3.2 in Zhou et al, Journal of Computational Physics 274
432 // (2014) 770-782 with **a bugfix for sigma1**. Here is the original
433 // algorithm verbatim:
434 //
435 // [Y]=chebyshev_filter_scaled(X, m, a, b, aL).
436 // e=(b-a)/2; c=(a+b)/2; σ=e/(c-aL); τ=2/σ;
437 // Y=(H∗X-c∗X)∗(σ/e);
438 // for i=2 to m do
439 // σnew =1/(τ - σ);
440 // Yt =(H∗Y - c∗Y)∗(2∗σnew/e)-(σ∗σnew)∗X;
441 // X =Y; Y =Yt; σ =σnew;
442
443 const double e = (b - a) / 2.;
444 const double c = (a + b) / 2.;
445 const double alpha = 1. / e;
446 const double beta = -c / e;
447
448 const double sigma1 =
449 e / (a_L - c); // BUGFIX which is relevant for odd degrees
450 double sigma = scale ? sigma1 : 1.;
451 const double tau = 2. / sigma;
452 op.vmult(y, x);
453 y.sadd(alpha * sigma, beta * sigma, x);
454
455 for (unsigned int i = 2; i <= degree; ++i)
456 {
457 const double sigma_new = scale ? 1. / (tau - sigma) : 1.;
458 op.vmult(yn, y);
459 yn.sadd(2. * alpha * sigma_new, 2. * beta * sigma_new, y);
460 yn.add(-sigma * sigma_new, x);
461 x.swap(y);
462 y.swap(yn);
463 sigma = sigma_new;
464 }
465
466 x.swap(y);
467 }
468
469 } // namespace LinearAlgebra
470} // namespace Utilities
471
472#endif
473
474
475
477
478
479#endif
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
const unsigned int v0
static ::ExceptionBase & ExcNotImplemented()
static ::ExceptionBase & ExcErrorCode(std::string arg1, types::blas_int arg2)
#define Assert(cond, exc)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcDivideByZero()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
void scale(const double scaling_factor, Triangulation< dim, spacedim > &triangulation)
@ diagonal
Matrix is diagonal.
Tpetra::Vector< Number, LO, GO, NodeType< MemorySpace > > VectorType
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
void chebyshev_filter(VectorType &x, const OperatorType &H, const unsigned int n, const std::pair< double, double > unwanted_spectrum, const double tau, VectorMemory< VectorType > &vector_memory)
std::array< NumberType, 3 > givens_rotation(const NumberType &x, const NumberType &y)
std::array< NumberType, 3 > hyperbolic_rotation(const NumberType &x, const NumberType &y)
double lanczos_largest_eigenvalue(const OperatorType &H, const VectorType &v0, const unsigned int k, VectorMemory< VectorType > &vector_memory, std::vector< double > *eigenvalues=nullptr)
void call_stev(const char jobz, const types::blas_int n, Number *d, Number *e, Number *z, const types::blas_int ldz, Number *work, types::blas_int *info)
Definition utilities.cc:29
bool is_finite(const double x)
Definition numbers.h:508
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
std::array< Number, 1 > eigenvalues(const SymmetricTensor< 2, 1, Number > &T)