deal.II version GIT relicensing-6968-g1957ba3668 2026-10-25 03:50:00+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
solver_idr.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) 2019 - 2025 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_solver_idr_h
14#define dealii_solver_idr_h
15
16
17#include <deal.II/base/config.h>
18
23
27#include <deal.II/lac/solver.h>
29
30#include <Kokkos_Random.hpp>
31
32#include <cmath>
33#include <cstdint>
34#include <random>
35
37
43namespace internal
44{
48 namespace SolverIDRImplementation
49 {
54 template <typename VectorType>
56 {
57 public:
61 TmpVectors(const unsigned int s_param, VectorMemory<VectorType> &vmem);
62
66 ~TmpVectors() = default;
67
72 VectorType &
73 operator[](const unsigned int i) const;
74
81 VectorType &
82 operator()(const unsigned int i, const VectorType &temp);
83
84 private:
89
93 std::vector<typename VectorMemory<VectorType>::Pointer> data;
94 };
95 } // namespace SolverIDRImplementation
96} // namespace internal
97
120template <typename VectorType = Vector<double>>
122class SolverIDR : public SolverBase<VectorType>
123{
124public:
129 {
133 explicit AdditionalData(const unsigned int s = 2)
134 : s(s)
135 {}
136
137 const unsigned int s;
138 };
139
146
153
157 virtual ~SolverIDR() override = default;
158
162 template <typename MatrixType, typename PreconditionerType>
166 void solve(const MatrixType &A,
167 VectorType &x,
168 const VectorType &b,
169 const PreconditionerType &preconditioner);
170
171protected:
177 virtual void
178 print_vectors(const unsigned int step,
179 const VectorType &x,
180 const VectorType &r,
181 const VectorType &d) const;
182
183private:
187 AdditionalData additional_data;
188};
189
191/*------------------------- Implementation ----------------------------*/
192
193#ifndef DOXYGEN
194
195
196namespace internal
197{
198 namespace SolverIDRImplementation
199 {
200 template <typename VectorType>
201 inline TmpVectors<VectorType>::TmpVectors(const unsigned int s_param,
203 : mem(vmem)
204 , data(s_param)
205 {}
206
207
208
209 template <typename VectorType>
210 inline VectorType &
211 TmpVectors<VectorType>::operator[](const unsigned int i) const
212 {
213 AssertIndexRange(i, data.size());
214
215 Assert(data[i] != nullptr, ExcNotInitialized());
216 return *data[i];
217 }
218
219
220
221 template <typename VectorType>
222 inline VectorType &
223 TmpVectors<VectorType>::operator()(const unsigned int i,
224 const VectorType &temp)
225 {
226 AssertIndexRange(i, data.size());
227 if (data[i] == nullptr)
228 {
229 data[i] = std::move(typename VectorMemory<VectorType>::Pointer(mem));
230 data[i]->reinit(temp, true);
231 }
232 return *data[i];
233 }
234
235
236
237 template <typename VectorType,
238 std::enable_if_t<!IsBlockVector<VectorType>::value, VectorType>
239 * = nullptr>
240 unsigned int
241 n_blocks(const VectorType &)
242 {
243 return 1;
244 }
245
246
247
248 template <typename VectorType,
249 std::enable_if_t<IsBlockVector<VectorType>::value, VectorType> * =
250 nullptr>
251 unsigned int
252 n_blocks(const VectorType &vector)
253 {
254 return vector.n_blocks();
255 }
256
257
258
259 template <typename VectorType,
260 std::enable_if_t<!IsBlockVector<VectorType>::value, VectorType>
261 * = nullptr>
262 VectorType &
263 block(VectorType &vector, const unsigned int b)
264 {
265 AssertDimension(b, 0);
266 return vector;
267 }
268
269
270
271 template <typename VectorType,
272 std::enable_if_t<IsBlockVector<VectorType>::value, VectorType> * =
273 nullptr>
274 typename VectorType::BlockType &
275 block(VectorType &vector, const unsigned int b)
276 {
277 return vector.block(b);
278 }
279
280
281
282 template <typename Number>
283 void
284 initialize_random_vector(
286 std::mt19937 &rng,
287 std::normal_distribution<> &)
288 {
289 using ExecutionSpace = Kokkos::DefaultExecutionSpace;
290 std::uniform_int_distribution<std::uint64_t> distribution;
291 const std::uint64_t seed = distribution(rng);
292 Kokkos::Random_XorShift64_Pool<ExecutionSpace> random_pool(seed);
293
294 Number *values = vector.get_values();
295 Kokkos::parallel_for(
296 "SolverIDR initialize random vector",
297 Kokkos::RangePolicy<ExecutionSpace>(0, vector.locally_owned_size()),
298 KOKKOS_LAMBDA(const types::global_dof_index i) {
299 auto random_state = random_pool.get_state();
300 values[i] = random_state.normal();
301 random_pool.free_state(random_state);
302 });
303 ExecutionSpace().fence();
304 }
305
306
307
308 template <typename VectorType>
309 void
310 initialize_random_vector(VectorType &vector,
311 std::mt19937 &rng,
312 std::normal_distribution<> &normal_distribution)
313 {
314 for (const auto index : vector.locally_owned_elements())
315 vector(index) = normal_distribution(rng);
316 }
317
318 } // namespace SolverIDRImplementation
319} // namespace internal
320
321
322
323template <typename VectorType>
327 const AdditionalData &data)
328 : SolverBase<VectorType>(cn, mem)
329 , additional_data(data)
330{}
331
332
333
334template <typename VectorType>
336SolverIDR<VectorType>::SolverIDR(SolverControl &cn, const AdditionalData &data)
338 , additional_data(data)
339{}
340
341
342
343template <typename VectorType>
345void SolverIDR<VectorType>::print_vectors(const unsigned int,
346 const VectorType &,
347 const VectorType &,
348 const VectorType &) const
349{}
350
351
352
353template <typename VectorType>
355template <typename MatrixType, typename PreconditionerType>
359void SolverIDR<VectorType>::solve(const MatrixType &A,
360 VectorType &x,
361 const VectorType &b,
362 const PreconditionerType &preconditioner)
363{
364 LogStream::Prefix prefix("IDR(s)");
365
367 unsigned int step = 0;
368
369 const unsigned int s = additional_data.s;
370
371 // Define temporary vectors which do not do not depend on s
372 typename VectorMemory<VectorType>::Pointer r_pointer(this->memory);
373 typename VectorMemory<VectorType>::Pointer v_pointer(this->memory);
374 typename VectorMemory<VectorType>::Pointer uhat_pointer(this->memory);
375
376 VectorType &r = *r_pointer;
377 VectorType &v = *v_pointer;
378 VectorType &uhat = *uhat_pointer;
379
380 r.reinit(x, true);
381 v.reinit(x, true);
382 uhat.reinit(x, true);
383
384 // Initial residual
385 A.vmult(r, x);
386 r.sadd(-1.0, 1.0, b);
387
388 using value_type = typename VectorType::value_type;
389 using real_type = typename numbers::NumberTraits<value_type>::real_type;
390
391 // Check for convergent initial guess
392 real_type res = r.l2_norm();
393 iteration_state = this->iteration_status(step, res, x);
394 if (iteration_state == SolverControl::success)
395 return;
396
397 // Initialize sets of vectors/matrices whose size dependent on s
402
403 // Random number generator for vector entries of
404 // Q (normal distribution, mean=0 sigma=1)
405 std::mt19937 rng;
406 std::normal_distribution<> normal_distribution(0.0, 1.0);
407 for (unsigned int i = 0; i < s; ++i)
408 {
409 // Initialize vectors
410 G(i, x);
411 U(i, x);
412
413 // Compute random set of s orthonormalized vectors Q
414 // Note: the first vector is chosen to be the initial
415 // residual to match BiCGStab (as is done in comparisons
416 // with BiCGStab in the papers listed in the documentation
417 // of this function)
418 VectorType &tmp_q = Q(i, x);
419 if (i != 0)
420 {
421 for (unsigned int b = 0;
422 b < internal::SolverIDRImplementation::n_blocks(tmp_q);
423 ++b)
424 internal::SolverIDRImplementation::initialize_random_vector(
425 internal::SolverIDRImplementation::block(tmp_q, b),
426 rng,
427 normal_distribution);
428 tmp_q.compress(VectorOperation::insert);
429 }
430 else
431 tmp_q = r;
432
433 for (unsigned int j = 0; j < i; ++j)
434 {
435 v = Q[j];
436 v *= (v * tmp_q) / (tmp_q * tmp_q);
437 tmp_q.add(-1.0, v);
438 }
439
440 if (i != 0)
441 tmp_q *= 1.0 / tmp_q.l2_norm();
442
443 M(i, i) = 1.;
444 }
445
446 value_type omega = 1.;
447
448 bool early_exit = false;
449
450 // Outer iteration
451 while (iteration_state == SolverControl::iterate)
452 {
453 ++step;
454
455 // Compute phi
456 Vector<value_type> phi(s);
457 for (unsigned int i = 0; i < s; ++i)
458 phi(i) = Q[i] * r;
459
460 // Inner iteration over s
461 for (unsigned int k = 0; k < s; ++k)
462 {
463 // Solve M(k:s)*gamma = phi(k:s)
465 {
466 Vector<value_type> phik(s - k);
467 FullMatrix<value_type> Mk(s - k, s - k);
468 std::vector<unsigned int> indices;
469 unsigned int j = 0;
470 for (unsigned int i = k; i < s; ++i, ++j)
471 {
472 indices.push_back(i);
473 phik(j) = phi(i);
474 }
475 Mk.extract_submatrix_from(M, indices, indices);
476
477 FullMatrix<value_type> Mk_inv(s - k, s - k);
478 Mk_inv.invert(Mk);
479 Mk_inv.vmult(gamma, phik);
480 }
481
482 v = r;
483
484 if (step > 1)
485 {
486 for (unsigned int i = k, j = 0; i < s; ++i, ++j)
487 v.add(-gamma(j), G[i]);
488 }
489
490 preconditioner.vmult(uhat, v);
491
492 if (step > 1)
493 {
494 uhat.sadd(omega, gamma(0), U[k]);
495 for (unsigned int i = k + 1, j = 1; i < s; ++i, ++j)
496 uhat.add(gamma(j), U[i]);
497 }
498 else
499 uhat *= omega;
500
501 A.vmult(G[k], uhat);
502
503 // Update G and U
504 // Orthogonalize G[k] to Q0,..,Q_{k-1} and update uhat
505 if (k > 0)
506 {
507 value_type alpha = Q[0] * G[k] / M(0, 0);
508 for (unsigned int i = 1; i < k; ++i)
509 {
510 const value_type alpha_old = alpha;
511 alpha = G[k].add_and_dot(-alpha, G[i - 1], Q[i]) / M(i, i);
512
513 // update uhat every other iteration to reduce vector access
514 if (i % 2 == 1)
515 uhat.add(-alpha_old, U[i - 1], -alpha, U[i]);
516 }
517 M(k, k) = G[k].add_and_dot(-alpha, G[k - 1], Q[k]);
518 if (k % 2 == 1)
519 uhat.add(-alpha, U[k - 1]);
520 }
521 else
522 M(k, k) = G[k] * Q[k];
523
524 U[k].swap(uhat);
525
526 // Update kth column of M
527 for (unsigned int i = k + 1; i < s; ++i)
528 M(i, k) = Q[i] * G[k];
529
530 // Orthogonalize r to Q0,...,Qk, update x
531 {
532 const value_type beta = phi(k) / M(k, k);
533 r.add(-beta, G[k]);
534 x.add(beta, U[k]);
535
536 print_vectors(step, x, r, U[k]);
537
538 // Check for early convergence. If so, store
539 // information in early_exit so that outer iteration
540 // is broken before recomputing the residual
541 res = r.l2_norm();
542 iteration_state = this->iteration_status(step, res, x);
543 if (iteration_state != SolverControl::iterate)
544 {
545 early_exit = true;
546 break;
547 }
548
549 // Update phi
550 if (k + 1 < s)
551 {
552 for (unsigned int i = 0; i < k + 1; ++i)
553 phi(i) = 0.0;
554 for (unsigned int i = k + 1; i < s; ++i)
555 phi(i) -= beta * M(i, k);
556 }
557 }
558 }
559 if (early_exit == true)
560 break;
561
562 // Update r and x
563 preconditioner.vmult(uhat, r);
564 A.vmult(v, uhat);
565
566 omega = (v * r) / (v * v);
567
568 res = std::sqrt(r.add_and_dot(-1.0 * omega, v, r));
569 x.add(omega, uhat);
570
571 print_vectors(step, x, r, uhat);
572
573 // Check for convergence
574 iteration_state = this->iteration_status(step, res, x);
575 if (iteration_state != SolverControl::iterate)
576 break;
577 }
578
579 if (iteration_state != SolverControl::success)
580 AssertThrow(false, SolverControl::NoConvergence(step, res));
581}
582
583
584#endif // DOXYGEN
585
587
588#endif
@ iterate
Continue iteration.
@ success
Stop iteration, goal reached.
virtual void print_vectors(const unsigned int step, const VectorType &x, const VectorType &r, const VectorType &d) const
SolverIDR(SolverControl &cn, const AdditionalData &data=AdditionalData())
void solve(const MatrixType &A, VectorType &x, const VectorType &b, const PreconditionerType &preconditioner)
SolverIDR(SolverControl &cn, VectorMemory< VectorType > &mem, const AdditionalData &data=AdditionalData())
virtual ~SolverIDR() override=default
TmpVectors(const unsigned int s_param, VectorMemory< VectorType > &vmem)
VectorType & operator()(const unsigned int i, const VectorType &temp)
VectorType & operator[](const unsigned int i) const
std::vector< typename VectorMemory< VectorType >::Pointer > data
Definition solver_idr.h:93
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_CXX20_REQUIRES(condition)
Definition config.h:249
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcNotInitialized()
#define AssertThrow(cond, exc)
std::vector< index_type > data
Definition mpi.cc:734
constexpr char U
constexpr char A
Tpetra::Vector< Number, LO, GO, NodeType< MemorySpace > > VectorType
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
long double gamma(const unsigned int n)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
const unsigned int s
Definition solver_idr.h:137
AdditionalData(const unsigned int s=2)
Definition solver_idr.h:133