MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
ginkgo.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12#include "../config/config.hpp"
13
14#ifdef MFEM_USE_GINKGO
15
16#include "ginkgo.hpp"
17#include "sparsemat.hpp"
19#include "../general/error.hpp"
20#include <iostream>
21#include <iomanip>
22#include <algorithm>
23#include <cmath>
24#include <cstring>
25
26namespace mfem
27{
28
29namespace Ginkgo
30{
31
32// Create a GinkgoExecutor of type exec_type.
34{
35#if defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP)
36 gko::version_info gko_version = gko::version_info::get();
37 bool gko_with_omp_support = (strcmp(gko_version.omp_version.tag,
38 "not compiled") != 0);
39#endif
40 switch (exec_type)
41 {
43 {
44 executor = gko::ReferenceExecutor::create();
45 break;
46 }
48 {
49 executor = gko::OmpExecutor::create();
50 break;
51 }
53 {
54 if (gko::CudaExecutor::get_num_devices() > 0)
55 {
56#ifdef MFEM_USE_CUDA
57 int current_device = 0;
58 MFEM_GPU_CHECK(cudaGetDevice(&current_device));
59#ifndef MFEM_USE_MPI
60 if (gko_with_omp_support)
61 {
62 executor = gko::CudaExecutor::create(current_device,
63 gko::OmpExecutor::create());
64 }
65 else
66#endif // with MPI, always use Reference for host Executor
67 {
68 executor = gko::CudaExecutor::create(current_device,
69 gko::ReferenceExecutor::create());
70 }
71#endif
72 }
73 else
74 {
75 MFEM_ABORT("gko::CudaExecutor::get_num_devices() did not report "
76 "any valid devices.");
77 }
78 break;
79 }
81 {
82 if (gko::HipExecutor::get_num_devices() > 0)
83 {
84#ifdef MFEM_USE_HIP
85 int current_device = 0;
86 MFEM_GPU_CHECK(hipGetDevice(&current_device));
87#ifndef MFEM_USE_MPI
88 if (gko_with_omp_support)
89 {
90 executor = gko::HipExecutor::create(current_device,
91 gko::OmpExecutor::create());
92 }
93 else
94#endif // with MPI, always use Reference for host Executor
95 {
96 executor = gko::HipExecutor::create(current_device,
97 gko::ReferenceExecutor::create());
98 }
99#endif
100 }
101 else
102 {
103 MFEM_ABORT("gko::HipExecutor::get_num_devices() did not report "
104 "any valid devices.");
105 }
106 break;
107 }
108 default:
109 MFEM_ABORT("Invalid ExecType specified");
110 }
111}
112
113// Create a GinkgoExecutor of type exec_type, with host_exec_type for the
114// related CPU Executor (only applicable to GPU backends).
116{
117 switch (exec_type)
118 {
120 {
121 MFEM_WARNING("Parameter host_exec_type ignored for CPU GinkgoExecutor.");
122 executor = gko::ReferenceExecutor::create();
123 break;
124 }
126 {
127 MFEM_WARNING("Parameter host_exec_type ignored for CPU GinkgoExecutor.");
128 executor = gko::OmpExecutor::create();
129 break;
130 }
132 {
133 if (gko::CudaExecutor::get_num_devices() > 0)
134 {
135#ifdef MFEM_USE_CUDA
136 int current_device = 0;
137 MFEM_GPU_CHECK(cudaGetDevice(&current_device));
138 if (host_exec_type == GinkgoExecutor::OMP)
139 {
140 executor = gko::CudaExecutor::create(current_device,
141 gko::OmpExecutor::create());
142 }
143 else
144 {
145 executor = gko::CudaExecutor::create(current_device,
146 gko::ReferenceExecutor::create());
147 }
148#endif
149 }
150 else
151 {
152 MFEM_ABORT("gko::CudaExecutor::get_num_devices() did not report "
153 "any valid devices.");
154 }
155 break;
156 }
158 {
159 if (gko::HipExecutor::get_num_devices() > 0)
160 {
161#ifdef MFEM_USE_HIP
162 int current_device = 0;
163 MFEM_GPU_CHECK(hipGetDevice(&current_device));
164 if (host_exec_type == GinkgoExecutor::OMP)
165 {
166 executor = gko::HipExecutor::create(current_device,
167 gko::OmpExecutor::create());
168 }
169 else
170 {
171 executor = gko::HipExecutor::create(current_device,
172 gko::ReferenceExecutor::create());
173 }
174#endif
175 }
176 else
177 {
178 MFEM_ABORT("gko::HipExecutor::get_num_devices() did not report "
179 "any valid devices.");
180 }
181 break;
182 }
183 default:
184 MFEM_ABORT("Invalid ExecType specified");
185 }
186}
187
188// Create a GinkgoExecutor to match MFEM's device configuration.
190{
191 gko::version_info gko_version = gko::version_info::get();
192 bool gko_with_omp_support = (strcmp(gko_version.omp_version.tag,
193 "not compiled") != 0);
194 if (mfem_device.Allows(Backend::CUDA_MASK))
195 {
196 if (gko::CudaExecutor::get_num_devices() > 0)
197 {
198#ifdef MFEM_USE_CUDA
199 int current_device = 0;
200 MFEM_GPU_CHECK(cudaGetDevice(&current_device));
201#ifndef MFEM_USE_MPI
202 if (gko_with_omp_support)
203 {
204 executor = gko::CudaExecutor::create(current_device,
205 gko::OmpExecutor::create());
206 }
207 else
208#endif // with MPI, always use Reference for host Executor
209 {
210 executor = gko::CudaExecutor::create(current_device,
211 gko::ReferenceExecutor::create());
212 }
213#endif
214 }
215 else
216 {
217 MFEM_ABORT("gko::CudaExecutor::get_num_devices() did not report "
218 "any valid devices.");
219 }
220 }
221 else if (mfem_device.Allows(Backend::HIP_MASK))
222 {
223 if (gko::HipExecutor::get_num_devices() > 0)
224 {
225#ifdef MFEM_USE_HIP
226 int current_device = 0;
227 MFEM_GPU_CHECK(hipGetDevice(&current_device));
228#ifndef MFEM_USE_MPI
229 if (gko_with_omp_support)
230 {
231 executor = gko::HipExecutor::create(current_device,
232 gko::OmpExecutor::create());
233 }
234 else
235#endif // with MPI, always use Reference for host Executor
236 {
237 executor = gko::HipExecutor::create(current_device,
238 gko::ReferenceExecutor::create());
239 }
240#endif
241 }
242 else
243 {
244 MFEM_ABORT("gko::HipExecutor::get_num_devices() did not report "
245 "any valid devices.");
246 }
247 }
248 else
249 {
250 if (mfem_device.Allows(Backend::OMP_MASK))
251 {
252 // Also use OpenMP for Ginkgo, if Ginkgo supports it
253 if (gko_with_omp_support)
254 {
255 executor = gko::OmpExecutor::create();
256 }
257 else
258 {
259 executor = gko::ReferenceExecutor::create();
260 }
261 }
262 else
263 {
264 executor = gko::ReferenceExecutor::create();
265 }
266 }
267}
268
269// Create a GinkgoExecutor to match MFEM's device configuration, with
270// a specific host_exec_type for the associated CPU Executor (only
271// applicable to GPU backends).
272GinkgoExecutor::GinkgoExecutor(Device &mfem_device, ExecType host_exec_type)
273{
274
275 if (mfem_device.Allows(Backend::CUDA_MASK))
276 {
277 if (gko::CudaExecutor::get_num_devices() > 0)
278 {
279#ifdef MFEM_USE_CUDA
280 int current_device = 0;
281 MFEM_GPU_CHECK(cudaGetDevice(&current_device));
282 if (host_exec_type == GinkgoExecutor::OMP)
283 {
284 executor = gko::CudaExecutor::create(current_device,
285 gko::OmpExecutor::create());
286 }
287 else
288 {
289 executor = gko::CudaExecutor::create(current_device,
290 gko::ReferenceExecutor::create());
291 }
292#endif
293 }
294 else
295 {
296 MFEM_ABORT("gko::CudaExecutor::get_num_devices() did not report "
297 "any valid devices.");
298 }
299 }
300 else if (mfem_device.Allows(Backend::HIP_MASK))
301 {
302 if (gko::HipExecutor::get_num_devices() > 0)
303 {
304#ifdef MFEM_USE_HIP
305 int current_device = 0;
306 MFEM_GPU_CHECK(hipGetDevice(&current_device));
307 if (host_exec_type == GinkgoExecutor::OMP)
308 {
309 executor = gko::HipExecutor::create(current_device,
310 gko::OmpExecutor::create());
311 }
312 else
313 {
314 executor = gko::HipExecutor::create(current_device,
315 gko::ReferenceExecutor::create());
316 }
317#endif
318 }
319 else
320 {
321 MFEM_ABORT("gko::HipExecutor::get_num_devices() did not report "
322 "any valid devices.");
323 }
324 }
325 else
326 {
327 MFEM_WARNING("Parameter host_exec_type ignored for CPU GinkgoExecutor.");
328 if (mfem_device.Allows(Backend::OMP_MASK))
329 {
330 // Also use OpenMP for Ginkgo, if Ginkgo supports it
331 gko::version_info gko_version = gko::version_info::get();
332 bool gko_with_omp_support = (strcmp(gko_version.omp_version.tag,
333 "not compiled") != 0);
334 if (gko_with_omp_support)
335 {
336 executor = gko::OmpExecutor::create();
337 }
338 else
339 {
340 executor = gko::ReferenceExecutor::create();
341 }
342 }
343 else
344 {
345 executor = gko::ReferenceExecutor::create();
346 }
347 }
348}
349
351 bool use_implicit_res_norm)
352 : Solver(),
353#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
354 gko_comm(NULL),
355#endif
356 use_implicit_res_norm(use_implicit_res_norm)
357{
358 executor = exec.GetExecutor();
359 print_level = -1;
360
361 // Build default stopping criterion factory
362 max_iter = 10;
363 rel_tol = 0.0;
364 abs_tol = 0.0;
365 this->update_stop_factory();
366
367 needs_wrapped_vecs = false;
369}
370
371#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
373 MPI_Comm comm,
374 bool use_implicit_res_norm)
375 : Solver(),
376 use_implicit_res_norm(use_implicit_res_norm),
377 needs_sorted_diagonal_mat(false)
378{
379 executor = exec.GetExecutor();
380 gko_comm = std::shared_ptr<gko::experimental::mpi::communicator>(
381 new gko::experimental::mpi::communicator(comm));
382 print_level = -1;
383
384 // Build default stopping criterion factory
385 max_iter = 10;
386 rel_tol = 0.0;
387 abs_tol = 0.0;
388 this->update_stop_factory();
389
390 // Distributed solvers always need wrapped vectors
391 needs_wrapped_vecs = true;
393}
394#endif
395
397{
398 using ResidualCriterionFactory = gko::stop::ResidualNorm<real_t>;
399 using ImplicitResidualCriterionFactory =
400 gko::stop::ImplicitResidualNorm<real_t>;
401
403 {
404 imp_rel_criterion = ImplicitResidualCriterionFactory::build()
405 .with_reduction_factor(rel_tol)
406 .with_baseline(gko::stop::mode::initial_resnorm)
407 .on(executor);
408 imp_abs_criterion = ImplicitResidualCriterionFactory::build()
409 .with_reduction_factor(abs_tol)
410 .with_baseline(gko::stop::mode::absolute)
411 .on(executor);
413 gko::stop::Combined::build()
414 .with_criteria(imp_rel_criterion,
416 gko::stop::Iteration::build()
417 .with_max_iters(static_cast<unsigned long>(max_iter))
418 .on(executor))
419 .on(executor);
420 }
421 else
422 {
423 rel_criterion = ResidualCriterionFactory::build()
424 .with_reduction_factor(rel_tol)
425 .with_baseline(gko::stop::mode::initial_resnorm)
426 .on(executor);
427 abs_criterion = ResidualCriterionFactory::build()
428 .with_reduction_factor(abs_tol)
429 .with_baseline(gko::stop::mode::absolute)
430 .on(executor);
432 gko::stop::Combined::build()
433 .with_criteria(rel_criterion,
435 gko::stop::Iteration::build()
436 .with_max_iters(static_cast<unsigned long>(max_iter))
437 .on(executor))
438 .on(executor);
439 }
440}
441
442void
443GinkgoIterativeSolver::initialize_ginkgo_log(gko::matrix::Dense<real_t>* b)
444const
445{
446 // Add the logger object. See the different masks available in Ginkgo's
447 // documentation
449 std::make_shared<EnableConvergenceLogger<gko::matrix::Dense<real_t>>>
450 (executor,
451 system_oper.get(),b);
453 std::make_shared<EnableResidualLogger<gko::matrix::Dense<real_t>>>
454 (executor,
455 system_oper.get(),b);
456
457}
458
459#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
460void
461GinkgoIterativeSolver::initialize_ginkgo_log(ParallelVectorWrapper* b)
462const
463{
464 // Add the logger object. See the different masks available in Ginkgo's
465 // documentation
467 std::make_shared<EnableConvergenceLogger<gko::experimental::distributed::Vector<real_t>>>
468 (executor,
469 system_oper.get(),b);
471 std::make_shared<EnableResidualLogger<gko::experimental::distributed::Vector<real_t>>>
472 (executor,
473 system_oper.get(),b);
474}
475#endif
476
477void OperatorWrapper::apply_impl(const gko::LinOp *b, gko::LinOp *x) const
478{
479
480 // Cast to VectorWrapper; only accept this type for this impl
481 const VectorWrapper *mfem_b = gko::as<const VectorWrapper>(b);
482 VectorWrapper *mfem_x = gko::as<VectorWrapper>(x);
483
484 this->wrapped_oper->Mult(mfem_b->get_mfem_vec_const_ref(),
485 mfem_x->get_mfem_vec_ref());
486}
487void OperatorWrapper::apply_impl(const gko::LinOp *alpha,
488 const gko::LinOp *b,
489 const gko::LinOp *beta,
490 gko::LinOp *x) const
491{
492 // x = alpha * op (b) + beta * x
493 // Cast to VectorWrapper; only accept this type for this impl
494 const VectorWrapper *mfem_b = gko::as<const VectorWrapper>(b);
495 VectorWrapper *mfem_x = gko::as<VectorWrapper>(x);
496
497 // Check that alpha and beta are Dense<real_t> of size (1,1):
498 if (alpha->get_size()[0] > 1 || alpha->get_size()[1] > 1)
499 {
500 throw gko::BadDimension(
501 __FILE__, __LINE__, __func__, "alpha", alpha->get_size()[0],
502 alpha->get_size()[1],
503 "Expected an object of size [1 x 1] for scaling "
504 " in this operator's apply_impl");
505 }
506 if (beta->get_size()[0] > 1 || beta->get_size()[1] > 1)
507 {
508 throw gko::BadDimension(
509 __FILE__, __LINE__, __func__, "beta", beta->get_size()[0],
510 beta->get_size()[1],
511 "Expected an object of size [1 x 1] for scaling "
512 " in this operator's apply_impl");
513 }
514 real_t alpha_f;
515 real_t beta_f;
516
517 if (alpha->get_executor() == alpha->get_executor()->get_master())
518 {
519 // Access value directly
520 alpha_f = gko::as<gko::matrix::Dense<real_t>>(alpha)->at(0, 0);
521 }
522 else
523 {
524 // Copy from device to host
525 this->get_executor()->get_master().get()->copy_from(
526 this->get_executor().get(),
527 1, gko::as<gko::matrix::Dense<real_t>>(alpha)->get_const_values(),
528 &alpha_f);
529 }
530 if (beta->get_executor() == beta->get_executor()->get_master())
531 {
532 // Access value directly
533 beta_f = gko::as<gko::matrix::Dense<real_t>>(beta)->at(0, 0);
534 }
535 else
536 {
537 // Copy from device to host
538 this->get_executor()->get_master().get()->copy_from(
539 this->get_executor().get(),
540 1, gko::as<gko::matrix::Dense<real_t>>(beta)->get_const_values(),
541 &beta_f);
542 }
543 // Scale x by beta
544 mfem_x->get_mfem_vec_ref() *= beta_f;
545 // Multiply operator with b and store in tmp
546 mfem::Vector mfem_tmp =
547 mfem::Vector(mfem_x->get_size()[0],
549 // Set UseDevice flag to match mfem_x (not automatically done through
550 // MemoryType)
551 mfem_tmp.UseDevice(mfem_x->get_mfem_vec_ref().UseDevice());
552
553 // Apply the operator
554 this->wrapped_oper->Mult(mfem_b->get_mfem_vec_const_ref(), mfem_tmp);
555 // Scale tmp by alpha and add
556 mfem_x->get_mfem_vec_ref().Add(alpha_f, mfem_tmp);
557
558 mfem_tmp.Destroy();
559}
560
561#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
563 gko::LinOp *x) const
564{
565 // Cast local vector to VectorWrapper
566 const mfem::Ginkgo::VectorWrapper *mfem_b =
567 gko::as<const mfem::Ginkgo::VectorWrapper>(
568 (gko::as<const ParallelVectorWrapper>(b))->get_local_wrapped_vec_const());
569 mfem::Ginkgo::VectorWrapper *mfem_x = gko::as< mfem::Ginkgo::VectorWrapper>(
570 (gko::as<ParallelVectorWrapper>(x))->get_local_wrapped_vec());
571 this->wrapped_oper->Mult(mfem_b->get_mfem_vec_const_ref(),
572 mfem_x->get_mfem_vec_ref());
573}
574
576 const gko::LinOp *b,
577 const gko::LinOp *beta,
578 gko::LinOp *x) const
579{
580 // x = alpha * op (b) + beta * x
581 // Cast local vector to mfem::Ginkgo::VectorWrapper; only accept this type for this impl
582 const mfem::Ginkgo::VectorWrapper *mfem_b =
583 gko::as<const mfem::Ginkgo::VectorWrapper>(
584 (gko::as<const ParallelVectorWrapper>(b))->get_local_wrapped_vec_const());
585 mfem::Ginkgo::VectorWrapper *mfem_x = gko::as< mfem::Ginkgo::VectorWrapper>(
586 (gko::as<ParallelVectorWrapper>(x))->get_local_wrapped_vec());
587
588 // Check that alpha and beta are Dense<double> of size (1,1):
589 if (alpha->get_size()[0] > 1 || alpha->get_size()[1] > 1)
590 {
591 throw gko::BadDimension(
592 __FILE__, __LINE__, __func__, "alpha", alpha->get_size()[0],
593 alpha->get_size()[1],
594 "Expected an object of size [1 x 1] for scaling "
595 " in this operator's apply_impl");
596 }
597 if (beta->get_size()[0] > 1 || beta->get_size()[1] > 1)
598 {
599 throw gko::BadDimension(
600 __FILE__, __LINE__, __func__, "beta", beta->get_size()[0],
601 beta->get_size()[1],
602 "Expected an object of size [1 x 1] for scaling "
603 " in this operator's apply_impl");
604 }
605 double alpha_f;
606 double beta_f;
607 if (alpha->get_executor() == alpha->get_executor()->get_master())
608 {
609 // Access value directly
610 alpha_f = gko::as<gko::matrix::Dense<double>>(alpha)->at(0, 0);
611 }
612 else
613 {
614 // Copy from device to host
615 this->get_executor()->get_master().get()->copy_from(
616 this->get_executor().get(),
617 1, gko::as<gko::matrix::Dense<double>>(alpha)->get_const_values(),
618 &alpha_f);
619 }
620 if (beta->get_executor() == beta->get_executor()->get_master())
621 {
622 // Access value directly
623 beta_f = gko::as<gko::matrix::Dense<double>>(beta)->at(0, 0);
624 }
625 else
626 {
627 // Copy from device to host
628 this->get_executor()->get_master().get()->copy_from(
629 this->get_executor().get(),
630 1, gko::as<gko::matrix::Dense<double>>(beta)->get_const_values(),
631 &beta_f);
632 }
633 // Scale x by beta
634 mfem_x->get_mfem_vec_ref() *= beta_f;
635 // Multiply operator with b and store in tmp
636 mfem::Vector mfem_tmp =
639 // Set UseDevice flag to match mfem_x (not automatically done through
640 // MemoryType)
641 mfem_tmp.UseDevice(mfem_x->get_mfem_vec_ref().UseDevice());
642
643 // Apply the operator
644 this->wrapped_oper->Mult(mfem_b->get_mfem_vec_const_ref(), mfem_tmp);
645 // Scale tmp by alpha and add
646 mfem_x->get_mfem_vec_ref().Add(alpha_f, mfem_tmp);
647
648 mfem_tmp.Destroy();
649}
650#endif
651
652void
654{
655
656 MFEM_VERIFY(system_oper, "System matrix or operator not initialized");
657 MFEM_VERIFY(executor, "executor is not initialized");
658 MFEM_VERIFY(y.Size() == x.Size(),
659 "Mismatching sizes for rhs and solution");
660
661 using vec = gko::matrix::Dense<real_t>;
662 if (!iterative_mode)
663 {
664 y = 0.0;
665 }
666
667 // Create x and y vectors in Ginkgo's format. Wrap MFEM's data directly,
668 // on CPU or GPU.
669 bool on_device = false;
670 if (executor != executor->get_master())
671 {
672 on_device = true;
673 }
674 std::unique_ptr<gko::LinOp> gko_x;
675 std::unique_ptr<gko::LinOp> gko_y;
676
677 // If we do not have an OperatorWrapper for the system operator or
678 // preconditioner, or have an inner solver using VectorWrappers (as
679 // for IR), then directly create Ginkgo vectors from MFEM's data.
681 {
682 gko_x = vec::create(executor, gko::dim<2>(x.Size(), 1),
684 x.Size(), const_cast<real_t *>(
685 x.Read(on_device))), 1);
686 gko_y = vec::create(executor, gko::dim<2>(y.Size(), 1),
688 y.Size(),
689 y.ReadWrite(on_device)), 1);
690 initialize_ginkgo_log(gko::as<vec>(gko_x.get()));
691 }
692 else // We have at least one wrapped MFEM operator or a distributed matrix; need wrapped vectors
693 {
694#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
695 if (gko_comm)
696 {
697 using par_vec = gko::experimental::distributed::Vector<real_t>;
698 auto local_gko_x = new VectorWrapper(executor, x.Size(),
699 const_cast<Vector *>(&x), false);
700 auto local_gko_y = new VectorWrapper(executor, y.Size(), &y,
701 false);
702 gko_x = std::unique_ptr<ParallelVectorWrapper>(
704 local_gko_x,
705 system_oper->get_size()[0], 1));
706 gko_y = std::unique_ptr<ParallelVectorWrapper>(
708 local_gko_y,
709 system_oper->get_size()[0], 1));
710 // Create the logger object to log some data from the solvers to confirm
711 // convergence.
712 initialize_ginkgo_log(gko::as<ParallelVectorWrapper>(gko_x.get()));
713 }
714 else
715#endif
716 {
717 gko_x = std::unique_ptr<vec>(
718 new VectorWrapper(executor, x.Size(),
719 const_cast<Vector *>(&x), false));
720 gko_y = std::unique_ptr<vec>(
721 new VectorWrapper(executor, y.Size(), &y,
722 false));
723 initialize_ginkgo_log(gko::as<vec>(gko_x.get()));
724 }
725 }
726
727 solver->clear_loggers(); // Clear any loggers from previous Mult() calls
728 MFEM_VERIFY(convergence_logger, "convergence logger not initialized" );
729 solver->add_logger(convergence_logger);
730
731 if (print_level==1)
732 {
733 MFEM_VERIFY(residual_logger, "residual logger not initialized" );
734 solver->add_logger(residual_logger);
735 }
736
737 // Finally, apply the solver to x and get the solution in y.
738 solver->apply(gko_x, gko_y);
739
740 // Get the final stats for the solver.
741 real_t final_res_norm = 0.0;
742 converged = 0;
743 final_iter = convergence_logger->get_num_iterations();
744 final_res_norm = convergence_logger->get_final_residual_norm();
745 if (convergence_logger->has_converged())
746 {
747 converged = 1;
748 }
749
750#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
751 if ((gko_comm && (gko_comm->rank() == 0)) || !gko_comm)
752 {
753#endif
754 if (print_level == 1)
755 {
756 residual_logger->write();
757 }
758 if (converged == 0)
759 {
760 mfem::err << "No convergence!" << '\n';
761 }
762 if (print_level >=2 && converged==1 )
763 {
764 mfem::out << "Converged in " << final_iter <<
765 " iterations with final residual norm "
766 << final_res_norm << '\n';
767 }
768#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
769 }
770#endif
771}
772
773#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
774std::unique_ptr<gko::experimental::distributed::Matrix<real_t, gko_hypre_int, gko_hypre_bigint>>
776 std::shared_ptr<gko::Executor> executor,
777 std::shared_ptr<gko::experimental::mpi::communicator> gko_comm, bool sort_diag)
778{
779 // Needs to be a square matrix
780 MFEM_VERIFY(par_mat->Height() == par_mat->Width(),
781 "System matrix is not square");
782
784 if (executor != executor->get_master())
785 {
787 }
788
789 using local_mtx = gko::matrix::Csr<real_t, gko_hypre_int>;
790 using global_mtx =
791 gko::experimental::distributed::Matrix<real_t, gko_hypre_int, gko_hypre_bigint>;
792 using gko_partition =
793 gko::experimental::distributed::Partition<gko_hypre_int, gko_hypre_bigint>;
794 using idx_map =
795 gko::experimental::distributed::index_map<gko_hypre_int, gko_hypre_bigint>;
796 // Create Ginkgo column partition from Hypre col_starts information
797 // Collect the col_starts on every rank, via host memory
798 auto mpi_exec = executor->get_master();
799 gko::array<gko_hypre_bigint> col_ranges(mpi_exec, gko_comm->size() + 1);
800 col_ranges.fill(gko::zero<gko_hypre_bigint>());
801 // Gather the "ends" of the ranges such that col_ranges[i] contains the starting index for the
802 // ith part of the partition
803 gko_comm->all_gather(mpi_exec, par_mat->GetColStarts() + 1, 1,
804 col_ranges.get_data() + 1, 1);
805 // Move to device, if necessary
806 col_ranges.set_executor(executor);
807 auto col_part = gko::share(gko_partition::build_from_contiguous(executor,
808 col_ranges, {}));
809
810 // Create Ginkgo off-process index mapping from Hypre's col_map_offd
811 HYPRE_Int num_offd_cols;
812 HYPRE_BigInt *cmap;
813 par_mat->GetOffdColMap(cmap, num_offd_cols);
815 mpi_exec,
816 num_offd_cols, reinterpret_cast<gko_hypre_bigint*>(cmap));
817 // Move to device, if necessary
818 recv_indices.set_executor(executor);
819 idx_map imap(executor, col_part, gko_comm->rank(), recv_indices);
820
821 // Create local diag and off-diag matrices that share memory with the HypreParMat
822 HYPRE_Int local_diag_nnz = par_mat->GetDiagMemoryData().Capacity();
823 std::shared_ptr<local_mtx> diag_mat = local_mtx::create(
824 executor, gko::dim<2>(par_mat->GetNumRows(), par_mat->GetNumCols()),
825 gko_array<real_t>::view(executor, local_diag_nnz,
826 par_mat->GetDiagMemoryData().ReadWrite(mc, local_diag_nnz)),
827 gko_array<gko_hypre_int>::view(executor, local_diag_nnz,
828 reinterpret_cast<gko_hypre_int*>(par_mat->GetDiagMemoryJ().ReadWrite(mc,
829 local_diag_nnz))),
830 gko_array<gko_hypre_int>::view(executor, par_mat->GetNumRows() + 1,
831 reinterpret_cast<gko_hypre_int*>(par_mat->GetDiagMemoryI().ReadWrite(mc,
832 par_mat->GetNumRows() + 1)))
833 );
834 if (sort_diag == true)
835 {
836 diag_mat->sort_by_column_index();
837 }
838 HYPRE_Int local_offd_nnz = par_mat->GetOffdMemoryData().Capacity();
839 std::shared_ptr<local_mtx> off_diag_mat = local_mtx::create(
840 executor, gko::dim<2>(par_mat->GetNumRows(), num_offd_cols),
841 gko_array<real_t>::view(executor, local_offd_nnz,
842 par_mat->GetOffdMemoryData().ReadWrite(mc, local_offd_nnz)),
843 gko_array<gko_hypre_int>::view(executor, local_offd_nnz,
844 reinterpret_cast<gko_hypre_int*>(par_mat->GetOffdMemoryJ().ReadWrite(mc,
845 local_offd_nnz))),
846 gko_array<gko_hypre_int>::view(executor, par_mat->GetNumRows() + 1,
847 reinterpret_cast<gko_hypre_int*>(par_mat->GetOffdMemoryI().ReadWrite(mc,
848 par_mat->GetNumRows() + 1)))
849 );
850 // Finally, create Ginkgo distributed matrix point to Hypre data
851 return global_mtx::create(executor, *(gko_comm.get()), imap, diag_mat,
852 off_diag_mat);
853}
854#endif
855
857{
858
859 if (system_oper)
860 {
861 // If the solver currently needs VectorWrappers, but not due to a
862 // "sub-operator" (preconditioner or inner solver), then it's
863 // because the current system_oper needs them. Reset the property
864 // to false in case the new op is a SparseMatrix.
865 if (needs_wrapped_vecs == true && sub_op_needs_wrapped_vecs == false)
866 {
867 needs_wrapped_vecs = false;
868 }
869 // Reset the pointer
870 system_oper.reset();
871 // Reset the solver generated for the previous operator
872 solver.reset();
873 }
874
875 // Needs to be square
876 MFEM_VERIFY(op.Height() == op.Width(),
877 "System operator is not square");
878
879#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
880 // Check for HypreParMatrix:
881 HypreParMatrix *par_op_mat = const_cast<HypreParMatrix*>(
882 dynamic_cast<const HypreParMatrix*>(&op));
883 if (par_op_mat != NULL)
884 {
885 system_oper = GinkgoWrapHypreParMatrix(par_op_mat, executor, gko_comm,
887 // We always need wrapped vectors for the parallel case, even with a matrix,
888 // because the MFEM Vector passed to Mult() will be local-only, and Ginkgo needs
889 // a distributed vector.
890 needs_wrapped_vecs = true;
891 }
892 else
893#endif
894 {
895 // Check for SparseMatrix:
896 SparseMatrix *op_mat = const_cast<SparseMatrix*>(
897 dynamic_cast<const SparseMatrix*>(&op));
898 if (op_mat != NULL)
899 {
900
901 bool on_device = false;
902 if (executor != executor->get_master())
903 {
904 on_device = true;
905 }
906
907 using mtx = gko::matrix::Csr<real_t, int>;
908 const int nnz = op_mat->GetMemoryData().Capacity();
909 system_oper = mtx::create(
910 executor, gko::dim<2>(op_mat->Height(), op_mat->Width()),
912 nnz,
913 op_mat->ReadWriteData(on_device)),
915 nnz,
916 op_mat->ReadWriteJ(on_device)),
917 gko_array<int>::view(executor, op_mat->Height() + 1,
918 op_mat->ReadWriteI(on_device)));
919
920 }
921 else // We don't have a HypreParMatrix or a SparseMatrix; need an operator wrapper
922 {
923 needs_wrapped_vecs = true;
924#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
925 if (gko_comm)
926 {
927 HYPRE_BigInt global_size = op.Height();
928 auto mpi_exec = executor->get_master();
929 gko_comm->all_reduce(mpi_exec, &global_size, 1, MPI_SUM);
930 system_oper = std::shared_ptr<ParallelOperatorWrapper>(
931 new ParallelOperatorWrapper(executor, *(gko_comm.get()), global_size, &op));
932 }
933 else
934#endif
935 {
936 system_oper = std::shared_ptr<OperatorWrapper>(
937 new OperatorWrapper(executor, op.Height(), &op));
938 }
939 }
940 }
941
942 // Set MFEM Solver size values
943 height = op.Height();
944 width = op.Width();
945
946 // Generate the solver from the solver using the system matrix or operator.
947 solver = solver_gen->generate(system_oper);
948}
949
950/* ---------------------- CGSolver ------------------------ */
952 : EnableGinkgoSolver(exec, true)
953{
954 using cg = gko::solver::Cg<real_t>;
955 this->solver_gen =
956 cg::build().with_criteria(this->combined_factory).on(this->executor);
957}
958
959#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
961 : EnableGinkgoSolver(exec, comm, true)
962{
963 using cg = gko::solver::Cg<real_t>;
964 this->solver_gen =
965 cg::build().with_criteria(this->combined_factory).on(this->executor);
966}
967#endif
968
970 const GinkgoPreconditioner &preconditioner)
971 : EnableGinkgoSolver(exec, true)
972{
973 using cg = gko::solver::Cg<real_t>;
974 // Check for a previously-generated preconditioner (for a specific matrix)
975 if (preconditioner.HasGeneratedPreconditioner())
976 {
977 this->solver_gen = cg::build()
978 .with_criteria(this->combined_factory)
979 .with_generated_preconditioner(
980 preconditioner.GetGeneratedPreconditioner())
981 .on(this->executor);
982 if (dynamic_cast<const OperatorWrapper*>(preconditioner.
983 GetGeneratedPreconditioner().get()))
984 {
985 this->sub_op_needs_wrapped_vecs = true;
986 this->needs_wrapped_vecs = true;
987 }
988 }
989 else // Pass a preconditioner factory (will use same matrix as the solver)
990 {
991 this->solver_gen = cg::build()
992 .with_criteria(this->combined_factory)
993 .with_preconditioner(preconditioner.GetFactory())
994 .on(this->executor);
995 }
996}
997
998#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
1000 MPI_Comm comm,
1001 const GinkgoPreconditioner &preconditioner)
1002 : EnableGinkgoSolver(exec, comm, true)
1003{
1004 using cg = gko::solver::Cg<real_t>;
1005 this->needs_wrapped_vecs = true;
1006 // Check for a previously-generated preconditioner (for a specific matrix)
1007 if (preconditioner.HasGeneratedPreconditioner())
1008 {
1009 this->solver_gen = cg::build()
1010 .with_criteria(this->combined_factory)
1011 .with_generated_preconditioner(
1012 preconditioner.GetGeneratedPreconditioner())
1013 .on(this->executor);
1014 }
1015 else // Pass a preconditioner factory (will use same matrix as the solver)
1016 {
1017 this->solver_gen = cg::build()
1018 .with_criteria(this->combined_factory)
1019 .with_preconditioner(preconditioner.GetFactory())
1020 .on(this->executor);
1022 }
1023}
1024#endif
1025
1026/* ---------------------- BICGSTABSolver ------------------------ */
1028 : EnableGinkgoSolver(exec, true)
1029{
1030 using bicgstab = gko::solver::Bicgstab<real_t>;
1031 this->solver_gen = bicgstab::build()
1032 .with_criteria(this->combined_factory)
1033 .on(this->executor);
1034}
1035
1036#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
1038 : EnableGinkgoSolver(exec, comm, true)
1039{
1040 using bicgstab = gko::solver::Bicgstab<real_t>;
1041 this->solver_gen = bicgstab::build()
1042 .with_criteria(this->combined_factory)
1043 .on(this->executor);
1044}
1045#endif
1046
1048 const GinkgoPreconditioner &preconditioner)
1049 : EnableGinkgoSolver(exec, true)
1050{
1051 using bicgstab = gko::solver::Bicgstab<real_t>;
1052 if (preconditioner.HasGeneratedPreconditioner())
1053 {
1054 this->solver_gen = bicgstab::build()
1055 .with_criteria(this->combined_factory)
1056 .with_generated_preconditioner(
1057 preconditioner.GetGeneratedPreconditioner())
1058 .on(this->executor);
1059 if (dynamic_cast<const OperatorWrapper*>(preconditioner.
1060 GetGeneratedPreconditioner().get()))
1061 {
1062 this->sub_op_needs_wrapped_vecs = true;
1063 this->needs_wrapped_vecs = true;
1064 }
1065 }
1066 else
1067 {
1068 this->solver_gen = bicgstab::build()
1069 .with_criteria(this->combined_factory)
1070 .with_preconditioner(preconditioner.GetFactory())
1071 .on(this->executor);
1072 }
1073}
1074
1075#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
1077 MPI_Comm comm,
1078 const GinkgoPreconditioner &preconditioner)
1079 : EnableGinkgoSolver(exec, comm, true)
1080{
1081 using bicgstab = gko::solver::Bicgstab<real_t>;
1082 if (preconditioner.HasGeneratedPreconditioner())
1083 {
1084 this->solver_gen = bicgstab::build()
1085 .with_criteria(this->combined_factory)
1086 .with_generated_preconditioner(
1087 preconditioner.GetGeneratedPreconditioner())
1088 .on(this->executor);
1089 }
1090 else
1091 {
1092 this->solver_gen = bicgstab::build()
1093 .with_criteria(this->combined_factory)
1094 .with_preconditioner(preconditioner.GetFactory())
1095 .on(this->executor);
1097 }
1098}
1099#endif
1100
1101/* ---------------------- CGSSolver ------------------------ */
1103 : EnableGinkgoSolver(exec, true)
1104{
1105 using cgs = gko::solver::Cgs<real_t>;
1106 this->solver_gen =
1107 cgs::build().with_criteria(this->combined_factory).on(this->executor);
1108}
1109
1110#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
1112 : EnableGinkgoSolver(exec, comm, true)
1113{
1114 using cgs = gko::solver::Cgs<real_t>;
1115 this->solver_gen =
1116 cgs::build().with_criteria(this->combined_factory).on(this->executor);
1117}
1118#endif
1119
1121 const GinkgoPreconditioner &preconditioner)
1122 : EnableGinkgoSolver(exec, true)
1123{
1124 using cgs = gko::solver::Cgs<real_t>;
1125 if (preconditioner.HasGeneratedPreconditioner())
1126 {
1127 this->solver_gen = cgs::build()
1128 .with_criteria(this->combined_factory)
1129 .with_generated_preconditioner(
1130 preconditioner.GetGeneratedPreconditioner())
1131 .on(this->executor);
1132 if (dynamic_cast<const OperatorWrapper*>(preconditioner.
1133 GetGeneratedPreconditioner().get()))
1134 {
1135 this->sub_op_needs_wrapped_vecs = true;
1136 this->needs_wrapped_vecs = true;
1137 }
1138 }
1139 else
1140 {
1141 this->solver_gen = cgs::build()
1142 .with_criteria(this->combined_factory)
1143 .with_preconditioner(preconditioner.GetFactory())
1144 .on(this->executor);
1145 }
1146}
1147
1148#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
1150 const GinkgoPreconditioner &preconditioner)
1151 : EnableGinkgoSolver(exec, comm, true)
1152{
1153 using cgs = gko::solver::Cgs<real_t>;
1154 if (preconditioner.HasGeneratedPreconditioner())
1155 {
1156 this->solver_gen = cgs::build()
1157 .with_criteria(this->combined_factory)
1158 .with_generated_preconditioner(
1159 preconditioner.GetGeneratedPreconditioner())
1160 .on(this->executor);
1161 }
1162 else
1163 {
1164 this->solver_gen = cgs::build()
1165 .with_criteria(this->combined_factory)
1166 .with_preconditioner(preconditioner.GetFactory())
1167 .on(this->executor);
1169 }
1170}
1171#endif
1172
1173/* ---------------------- FCGSolver ------------------------ */
1175 : EnableGinkgoSolver(exec, true)
1176{
1177 using fcg = gko::solver::Fcg<real_t>;
1178 this->solver_gen =
1179 fcg::build().with_criteria(this->combined_factory).on(this->executor);
1180}
1181
1182#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
1184 : EnableGinkgoSolver(exec, comm, true)
1185{
1186 using fcg = gko::solver::Fcg<real_t>;
1187 this->solver_gen =
1188 fcg::build().with_criteria(this->combined_factory).on(this->executor);
1189}
1190#endif
1191
1193 const GinkgoPreconditioner &preconditioner)
1194 : EnableGinkgoSolver(exec, true)
1195{
1196 using fcg = gko::solver::Fcg<real_t>;
1197 if (preconditioner.HasGeneratedPreconditioner())
1198 {
1199 this->solver_gen = fcg::build()
1200 .with_criteria(this->combined_factory)
1201 .with_generated_preconditioner(
1202 preconditioner.GetGeneratedPreconditioner())
1203 .on(this->executor);
1204 if (dynamic_cast<const OperatorWrapper*>(preconditioner.
1205 GetGeneratedPreconditioner().get()))
1206 {
1207 this->sub_op_needs_wrapped_vecs = true;
1208 this->needs_wrapped_vecs = true;
1209 }
1210 }
1211 else
1212 {
1213 this->solver_gen = fcg::build()
1214 .with_criteria(this->combined_factory)
1215 .with_preconditioner(preconditioner.GetFactory())
1216 .on(this->executor);
1217 }
1218}
1219
1220#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
1222 const GinkgoPreconditioner &preconditioner)
1223 : EnableGinkgoSolver(exec, comm, true)
1224{
1225 using fcg = gko::solver::Fcg<real_t>;
1226 if (preconditioner.HasGeneratedPreconditioner())
1227 {
1228 this->solver_gen = fcg::build()
1229 .with_criteria(this->combined_factory)
1230 .with_generated_preconditioner(
1231 preconditioner.GetGeneratedPreconditioner())
1232 .on(this->executor);
1233 }
1234 else
1235 {
1236 this->solver_gen = fcg::build()
1237 .with_criteria(this->combined_factory)
1238 .with_preconditioner(preconditioner.GetFactory())
1239 .on(this->executor);
1241 }
1242}
1243#endif
1244
1245/* ---------------------- GMRESSolver ------------------------ */
1247 : EnableGinkgoSolver(exec, false),
1248 m{dim}
1249{
1250 using gmres = gko::solver::Gmres<real_t>;
1251 if (this->m == 0) // Don't set a dimension, but let Ginkgo use its default
1252 {
1253 this->solver_gen = gmres::build()
1254 .with_criteria(this->combined_factory)
1255 .on(this->executor);
1256 }
1257 else
1258 {
1259 this->solver_gen = gmres::build()
1260 .with_krylov_dim(static_cast<unsigned long>(m))
1261 .with_criteria(this->combined_factory)
1262 .on(this->executor);
1263 }
1264}
1265
1266#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
1268 : EnableGinkgoSolver(exec, comm, false),
1269 m{dim}
1270{
1271 using gmres = gko::solver::Gmres<real_t>;
1272 this->needs_wrapped_vecs = true;
1273 if (this->m == 0) // Don't set a dimension, but let Ginkgo use its default
1274 {
1275 this->solver_gen = gmres::build()
1276 .with_criteria(this->combined_factory)
1277 .on(this->executor);
1278 }
1279 else
1280 {
1281 this->solver_gen = gmres::build()
1282 .with_krylov_dim(static_cast<unsigned long>(m))
1283 .with_criteria(this->combined_factory)
1284 .on(this->executor);
1285 }
1286}
1287#endif
1288
1290 const GinkgoPreconditioner &preconditioner, int dim)
1291 : EnableGinkgoSolver(exec, false),
1292 m{dim}
1293{
1294 using gmres = gko::solver::Gmres<real_t>;
1295 // Check for a previously-generated preconditioner (for a specific matrix)
1296 if (this->m == 0) // Don't set a dimension, but let Ginkgo use its default
1297 {
1298 if (preconditioner.HasGeneratedPreconditioner())
1299 {
1300 this->solver_gen = gmres::build()
1301 .with_criteria(this->combined_factory)
1302 .with_generated_preconditioner(
1303 preconditioner.GetGeneratedPreconditioner())
1304 .on(this->executor);
1305 if (dynamic_cast<const OperatorWrapper*>(preconditioner.
1306 GetGeneratedPreconditioner().get()))
1307 {
1308 this->sub_op_needs_wrapped_vecs = true;
1309 this->needs_wrapped_vecs = true;
1310 }
1311 }
1312 else
1313 {
1314 this->solver_gen = gmres::build()
1315 .with_criteria(this->combined_factory)
1316 .with_preconditioner(preconditioner.GetFactory())
1317 .on(this->executor);
1318 }
1319 }
1320 else
1321 {
1322 if (preconditioner.HasGeneratedPreconditioner())
1323 {
1324 this->solver_gen = gmres::build()
1325 .with_krylov_dim(static_cast<unsigned long>(m))
1326 .with_criteria(this->combined_factory)
1327 .with_generated_preconditioner(
1328 preconditioner.GetGeneratedPreconditioner())
1329 .on(this->executor);
1330 if (dynamic_cast<const OperatorWrapper*>(preconditioner.
1331 GetGeneratedPreconditioner().get()))
1332 {
1333 this->sub_op_needs_wrapped_vecs = true;
1334 this->needs_wrapped_vecs = true;
1335 }
1336 }
1337 else
1338 {
1339 this->solver_gen = gmres::build()
1340 .with_krylov_dim(static_cast<unsigned long>(m))
1341 .with_criteria(this->combined_factory)
1342 .with_preconditioner(preconditioner.GetFactory())
1343 .on(this->executor);
1344 }
1345 }
1346}
1347
1348#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
1350 const GinkgoPreconditioner &preconditioner, int dim)
1351 : EnableGinkgoSolver(exec, comm, false),
1352 m{dim}
1353{
1354 using gmres = gko::solver::Gmres<real_t>;
1355 this->needs_wrapped_vecs = true;
1356 // Check for a previously-generated preconditioner (for a specific matrix)
1357 if (this->m == 0) // Don't set a dimension, but let Ginkgo use its default
1358 {
1359 if (preconditioner.HasGeneratedPreconditioner())
1360 {
1361 this->solver_gen = gmres::build()
1362 .with_criteria(this->combined_factory)
1363 .with_generated_preconditioner(
1364 preconditioner.GetGeneratedPreconditioner())
1365 .on(this->executor);
1366 }
1367 else
1368 {
1369 this->solver_gen = gmres::build()
1370 .with_criteria(this->combined_factory)
1371 .with_preconditioner(preconditioner.GetFactory())
1372 .on(this->executor);
1373 }
1374 }
1375 else
1376 {
1377 if (preconditioner.HasGeneratedPreconditioner())
1378 {
1379 this->solver_gen = gmres::build()
1380 .with_krylov_dim(static_cast<unsigned long>(m))
1381 .with_criteria(this->combined_factory)
1382 .with_generated_preconditioner(
1383 preconditioner.GetGeneratedPreconditioner())
1384 .on(this->executor);
1385 }
1386 else
1387 {
1388 this->solver_gen = gmres::build()
1389 .with_krylov_dim(static_cast<unsigned long>(m))
1390 .with_criteria(this->combined_factory)
1391 .with_preconditioner(preconditioner.GetFactory())
1392 .on(this->executor);
1394 }
1395 }
1396}
1397#endif
1398
1400{
1401 m = dim;
1402 using gmres = gko::solver::Gmres<real_t>;
1403 // Create new solver factory with other parameters the same, but new value for krylov_dim
1404 auto current_params = gko::as<gmres::Factory>(solver_gen)->get_parameters();
1405 this->solver_gen = current_params.with_krylov_dim(static_cast<unsigned long>(m))
1406 .on(this->executor);
1407 if (solver)
1408 {
1409 gko::as<gmres>(solver)->set_krylov_dim(static_cast<unsigned long>(m));
1410 }
1411}
1412
1413/* ---------------------- CBGMRESSolver ------------------------ */
1415 storage_precision prec)
1416 : EnableGinkgoSolver(exec, false),
1417 m{dim}
1418{
1419 using gmres = gko::solver::CbGmres<real_t>;
1420 if (this->m == 0) // Don't set a dimension, but let Ginkgo use its default
1421 {
1422 this->solver_gen = gmres::build()
1423 .with_criteria(this->combined_factory)
1424 .with_storage_precision(prec)
1425 .on(this->executor);
1426 }
1427 else
1428 {
1429 this->solver_gen = gmres::build()
1430 .with_krylov_dim(static_cast<unsigned long>(m))
1431 .with_criteria(this->combined_factory)
1432 .with_storage_precision(prec)
1433 .on(this->executor);
1434 }
1435}
1436
1438 const GinkgoPreconditioner &preconditioner,
1439 int dim, storage_precision prec)
1440 : EnableGinkgoSolver(exec, false),
1441 m{dim}
1442{
1443 using gmres = gko::solver::CbGmres<real_t>;
1444 // Check for a previously-generated preconditioner (for a specific matrix)
1445 if (this->m == 0) // Don't set a dimension, but let Ginkgo use its default
1446 {
1447 if (preconditioner.HasGeneratedPreconditioner())
1448 {
1449 this->solver_gen = gmres::build()
1450 .with_criteria(this->combined_factory)
1451 .with_storage_precision(prec)
1452 .with_generated_preconditioner(
1453 preconditioner.GetGeneratedPreconditioner())
1454 .on(this->executor);
1455 if (dynamic_cast<const OperatorWrapper*>(preconditioner.
1456 GetGeneratedPreconditioner().get()))
1457 {
1458 this->sub_op_needs_wrapped_vecs = true;
1459 this->needs_wrapped_vecs = true;
1460 }
1461 }
1462 else
1463 {
1464 this->solver_gen = gmres::build()
1465 .with_criteria(this->combined_factory)
1466 .with_storage_precision(prec)
1467 .with_preconditioner(preconditioner.GetFactory())
1468 .on(this->executor);
1469 }
1470 }
1471 else
1472 {
1473 if (preconditioner.HasGeneratedPreconditioner())
1474 {
1475 this->solver_gen = gmres::build()
1476 .with_krylov_dim(static_cast<unsigned long>(m))
1477 .with_criteria(this->combined_factory)
1478 .with_storage_precision(prec)
1479 .with_generated_preconditioner(
1480 preconditioner.GetGeneratedPreconditioner())
1481 .on(this->executor);
1482 if (dynamic_cast<const OperatorWrapper*>(preconditioner.
1483 GetGeneratedPreconditioner().get()))
1484 {
1485 this->sub_op_needs_wrapped_vecs = true;
1486 this->needs_wrapped_vecs = true;
1487 }
1488 }
1489 else
1490 {
1491 this->solver_gen = gmres::build()
1492 .with_krylov_dim(static_cast<unsigned long>(m))
1493 .with_criteria(this->combined_factory)
1494 .with_storage_precision(prec)
1495 .with_preconditioner(preconditioner.GetFactory())
1496 .on(this->executor);
1497 }
1498 }
1499}
1500
1502{
1503 m = dim;
1504 using gmres = gko::solver::CbGmres<real_t>;
1505 // Create new solver factory with other parameters the same, but new value for krylov_dim
1506 auto current_params = gko::as<gmres::Factory>(solver_gen)->get_parameters();
1507 this->solver_gen = current_params.with_krylov_dim(static_cast<unsigned long>(m))
1508 .on(this->executor);
1509 if (solver)
1510 {
1511 gko::as<gmres>(solver)->set_krylov_dim(static_cast<unsigned long>(m));
1512 }
1513}
1514
1515/* ---------------------- IRSolver ------------------------ */
1517 : EnableGinkgoSolver(exec, false)
1518{
1519 using ir = gko::solver::Ir<real_t>;
1520 this->solver_gen =
1521 ir::build().with_criteria(this->combined_factory).on(this->executor);
1522}
1523
1524#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
1526 : EnableGinkgoSolver(exec, comm, false)
1527{
1528 using ir = gko::solver::Ir<real_t>;
1529 this->solver_gen =
1530 ir::build().with_criteria(this->combined_factory).on(this->executor);
1531}
1532#endif
1533
1535 const GinkgoIterativeSolver &inner_solver)
1536 : EnableGinkgoSolver(exec, false)
1537{
1538 using ir = gko::solver::Ir<real_t>;
1539 this->solver_gen = ir::build()
1540 .with_criteria(this->combined_factory)
1541 .with_solver(inner_solver.GetFactory())
1542 .on(this->executor);
1543 if (inner_solver.UsesVectorWrappers())
1544 {
1545 this->sub_op_needs_wrapped_vecs = true;
1546 this->needs_wrapped_vecs = true;
1547 }
1548}
1549
1550#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
1552 const GinkgoIterativeSolver &inner_solver)
1553 : EnableGinkgoSolver(exec, comm, false)
1554{
1555 using ir = gko::solver::Ir<real_t>;
1556 this->solver_gen = ir::build()
1557 .with_criteria(this->combined_factory)
1558 .with_solver(inner_solver.GetFactory())
1559 .on(this->executor);
1560}
1561#endif
1562
1563/* --------------------------------------------------------------- */
1564/* ---------------------- Preconditioners ------------------------ */
1566 GinkgoExecutor &exec)
1567 : Solver(),
1568#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
1569 gko_comm(NULL),
1570#endif
1571 has_generated_precond(false)
1572{
1573 executor = exec.GetExecutor();
1574}
1575
1576#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
1578 GinkgoExecutor &exec,
1579 MPI_Comm comm)
1580 : Solver(),
1581 has_generated_precond(false)
1582{
1583 executor = exec.GetExecutor();
1584 gko_comm = std::shared_ptr<gko::experimental::mpi::communicator>(
1585 new gko::experimental::mpi::communicator(comm));
1586}
1587#endif
1588
1589void
1591{
1592
1593 MFEM_VERIFY(generated_precond, "Preconditioner not initialized");
1594 MFEM_VERIFY(executor, "executor is not initialized");
1595
1596 using vec = gko::matrix::Dense<real_t>;
1597 if (!iterative_mode)
1598 {
1599 y = 0.0;
1600 }
1601
1602 std::unique_ptr<gko::LinOp> gko_x;
1603 std::unique_ptr<gko::LinOp> gko_y;
1604
1605#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
1606 if (gko_comm)
1607 {
1608 using par_vec = gko::experimental::distributed::Vector<real_t>;
1609 auto local_gko_x = new VectorWrapper(executor, x.Size(),
1610 const_cast<Vector *>(&x), false);
1611 auto local_gko_y = new VectorWrapper(executor, y.Size(), &y,
1612 false);
1613 gko_x = std::unique_ptr<ParallelVectorWrapper>(
1615 local_gko_x,
1616 generated_precond->get_size()[0], 1));
1617 gko_y = std::unique_ptr<ParallelVectorWrapper>(
1619 local_gko_y,
1620 generated_precond->get_size()[0], 1));
1621 }
1622 else
1623#endif
1624 {
1625 // Create x and y vectors in Ginkgo's format. Wrap MFEM's data directly,
1626 // on CPU or GPU.
1627 bool on_device = false;
1628 if (executor != executor->get_master())
1629 {
1630 on_device = true;
1631 }
1632 gko_x = vec::create(executor, gko::dim<2>(x.Size(), 1),
1634 x.Size(), const_cast<real_t *>(
1635 x.Read(on_device))), 1);
1636 gko_y = vec::create(executor, gko::dim<2>(y.Size(), 1),
1638 y.Size(),
1639 y.ReadWrite(on_device)), 1);
1640 }
1641 generated_precond.get()->apply(gko_x, gko_y);
1642}
1643
1645{
1646
1648 {
1649 generated_precond.reset();
1650 has_generated_precond = false;
1651 }
1652
1653 // Needs to be square
1654 MFEM_VERIFY(op.Height() == op.Width(),
1655 "System operator is not square");
1656#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
1657 // Check for HypreParMatrix:
1658 HypreParMatrix *par_op_mat = const_cast<HypreParMatrix*>(
1659 dynamic_cast<const HypreParMatrix*>(&op));
1660 if (par_op_mat != NULL)
1661 {
1662 // Finally, create Ginkgo distributed matrix point to Hypre data
1663 auto gko_matrix = GinkgoWrapHypreParMatrix(par_op_mat, executor, gko_comm,
1664 false);
1665 generated_precond = precond_gen->generate(gko::give(gko_matrix));
1666 has_generated_precond = true;
1667
1668 // Set MFEM Solver size values
1669 height = op.Height();
1670 width = op.Width();
1671 }
1672 else
1673#endif
1674 {
1675 // Only accept SparseMatrix for this type.
1676 SparseMatrix *op_mat = const_cast<SparseMatrix*>(
1677 dynamic_cast<const SparseMatrix*>(&op));
1678 MFEM_VERIFY(op_mat != NULL,
1679 "GinkgoPreconditioner::SetOperator : not a SparseMatrix or HypreParMatrix!");
1680
1681 bool on_device = false;
1682 if (executor != executor->get_master())
1683 {
1684 on_device = true;
1685 }
1686
1687 using mtx = gko::matrix::Csr<real_t, int>;
1688 const int nnz = op_mat->GetMemoryData().Capacity();
1689 auto gko_matrix = mtx::create(
1690 executor, gko::dim<2>(op_mat->Height(), op_mat->Width()),
1692 nnz,
1693 op_mat->ReadWriteData(on_device)),
1695 nnz,
1696 op_mat->ReadWriteJ(on_device)),
1697 gko_array<int>::view(executor, op_mat->Height() + 1,
1698 op_mat->ReadWriteI(on_device)));
1699
1700 generated_precond = precond_gen->generate(gko::give(gko_matrix));
1701 has_generated_precond = true;
1702
1703 // Set MFEM Solver size values
1704 height = op.Height();
1705 width = op.Width();
1706 }
1707}
1708
1709
1710/* ---------------------- JacobiPreconditioner ------------------------ */
1712 GinkgoExecutor &exec,
1713 const std::string &storage_opt,
1714 const real_t accuracy,
1715 const int max_block_size
1716)
1717 : GinkgoPreconditioner(exec)
1718{
1719
1720 if (storage_opt == "auto")
1721 {
1722 precond_gen = gko::preconditioner::Jacobi<real_t, int>::build()
1723 .with_storage_optimization(
1724 gko::precision_reduction::autodetect())
1725 .with_accuracy(accuracy)
1726 .with_max_block_size(static_cast<unsigned int>(max_block_size))
1727 .on(executor);
1728 }
1729 else
1730 {
1731 precond_gen = gko::preconditioner::Jacobi<real_t, int>::build()
1732 .with_storage_optimization(
1733 gko::precision_reduction(0, 0))
1734 .with_accuracy(accuracy)
1735 .with_max_block_size(static_cast<unsigned int>(max_block_size))
1736 .on(executor);
1737 }
1738
1739}
1740
1741/* ---------------------- Ilu/IluIsaiPreconditioner ------------------------ */
1743 GinkgoExecutor &exec,
1744 const std::string &factorization_type,
1745 const int sweeps,
1746 const bool skip_sort
1747)
1748 : GinkgoPreconditioner(exec)
1749{
1750 if (factorization_type == "exact")
1751 {
1752 using ilu_fact_type = gko::factorization::Ilu<real_t, int>;
1753 std::shared_ptr<ilu_fact_type::Factory> fact_factory =
1754 ilu_fact_type::build()
1755 .with_skip_sorting(skip_sort)
1756 .on(executor);
1757 precond_gen = gko::preconditioner::Ilu<>::build()
1758 .with_factorization(fact_factory)
1759 .on(executor);
1760 }
1761 else
1762 {
1763 using ilu_fact_type = gko::factorization::ParIlu<real_t, int>;
1764 std::shared_ptr<ilu_fact_type::Factory> fact_factory =
1765 ilu_fact_type::build()
1766 .with_iterations(static_cast<unsigned long>(sweeps))
1767 .with_skip_sorting(skip_sort)
1768 .on(executor);
1769 precond_gen = gko::preconditioner::Ilu<>::build()
1770 .with_factorization(fact_factory)
1771 .on(executor);
1772 }
1773
1774}
1775
1777 GinkgoExecutor &exec,
1778 const std::string &factorization_type,
1779 const int sweeps,
1780 const int sparsity_power,
1781 const bool skip_sort
1782)
1783 : GinkgoPreconditioner(exec)
1784{
1785 using l_solver_type = gko::preconditioner::LowerIsai<>;
1786 using u_solver_type = gko::preconditioner::UpperIsai<>;
1787
1788 std::shared_ptr<l_solver_type::Factory> l_solver_factory =
1789 l_solver_type::build()
1790 .with_sparsity_power(sparsity_power)
1791 .on(executor);
1792 std::shared_ptr<u_solver_type::Factory> u_solver_factory =
1793 u_solver_type::build()
1794 .with_sparsity_power(sparsity_power)
1795 .on(executor);
1796
1797
1798
1799 if (factorization_type == "exact")
1800 {
1801 using ilu_fact_type = gko::factorization::Ilu<real_t, int>;
1802 std::shared_ptr<ilu_fact_type::Factory> fact_factory =
1803 ilu_fact_type::build()
1804 .with_skip_sorting(skip_sort)
1805 .on(executor);
1806 precond_gen = gko::preconditioner::Ilu<l_solver_type,
1807 u_solver_type>::build()
1808 .with_factorization(fact_factory)
1809 .with_l_solver(l_solver_factory)
1810 .with_u_solver(u_solver_factory)
1811 .on(executor);
1812
1813 }
1814 else
1815 {
1816 using ilu_fact_type = gko::factorization::ParIlu<real_t, int>;
1817 std::shared_ptr<ilu_fact_type::Factory> fact_factory =
1818 ilu_fact_type::build()
1819 .with_iterations(static_cast<unsigned long>(sweeps))
1820 .with_skip_sorting(skip_sort)
1821 .on(executor);
1822 precond_gen = gko::preconditioner::Ilu<l_solver_type,
1823 u_solver_type>::build()
1824 .with_factorization(fact_factory)
1825 .with_l_solver(l_solver_factory)
1826 .with_u_solver(u_solver_factory)
1827 .on(executor);
1828 }
1829}
1830
1831
1832/* ---------------------- Ic/IcIsaiPreconditioner ------------------------ */
1834 GinkgoExecutor &exec,
1835 const std::string &factorization_type,
1836 const int sweeps,
1837 const bool skip_sort
1838)
1839 : GinkgoPreconditioner(exec)
1840{
1841
1842 if (factorization_type == "exact")
1843 {
1844 using ic_fact_type = gko::factorization::Ic<real_t, int>;
1845 std::shared_ptr<ic_fact_type::Factory> fact_factory =
1846 ic_fact_type::build()
1847 .with_both_factors(false)
1848 .with_skip_sorting(skip_sort)
1849 .on(executor);
1850 precond_gen = gko::preconditioner::Ic<>::build()
1851 .with_factorization(fact_factory)
1852 .on(executor);
1853 }
1854 else
1855 {
1856 using ic_fact_type = gko::factorization::ParIc<real_t, int>;
1857 std::shared_ptr<ic_fact_type::Factory> fact_factory =
1858 ic_fact_type::build()
1859 .with_both_factors(false)
1860 .with_iterations(static_cast<unsigned long>(sweeps))
1861 .with_skip_sorting(skip_sort)
1862 .on(executor);
1863 precond_gen = gko::preconditioner::Ic<>::build()
1864 .with_factorization(fact_factory)
1865 .on(executor);
1866 }
1867}
1868
1870 GinkgoExecutor &exec,
1871 const std::string &factorization_type,
1872 const int sweeps,
1873 const int sparsity_power,
1874 const bool skip_sort
1875)
1876 : GinkgoPreconditioner(exec)
1877{
1878
1879 using l_solver_type = gko::preconditioner::LowerIsai<>;
1880 std::shared_ptr<l_solver_type::Factory> l_solver_factory =
1881 l_solver_type::build()
1882 .with_sparsity_power(sparsity_power)
1883 .on(executor);
1884 if (factorization_type == "exact")
1885 {
1886 using ic_fact_type = gko::factorization::Ic<real_t, int>;
1887 std::shared_ptr<ic_fact_type::Factory> fact_factory =
1888 ic_fact_type::build()
1889 .with_both_factors(false)
1890 .with_skip_sorting(skip_sort)
1891 .on(executor);
1892 precond_gen = gko::preconditioner::Ic<l_solver_type>::build()
1893 .with_factorization(fact_factory)
1894 .with_l_solver(l_solver_factory)
1895 .on(executor);
1896 }
1897 else
1898 {
1899 using ic_fact_type = gko::factorization::ParIc<real_t, int>;
1900 std::shared_ptr<ic_fact_type::Factory> fact_factory =
1901 ic_fact_type::build()
1902 .with_both_factors(false)
1903 .with_iterations(static_cast<unsigned long>(sweeps))
1904 .with_skip_sorting(skip_sort)
1905 .on(executor);
1906 precond_gen = gko::preconditioner::Ic<l_solver_type>::build()
1907 .with_factorization(fact_factory)
1908 .with_l_solver(l_solver_factory)
1909 .on(executor);
1910 }
1911}
1912
1913/* ---------------------- SchwarzPreconditioner ------------------------ */
1914#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
1916 GinkgoExecutor &exec,
1917 MPI_Comm comm,
1918 Solver &local_solver,
1919 const bool l1_smoother
1920)
1921 : GinkgoPreconditioner(exec, comm)
1922{
1923 using schwarz =
1924 gko::experimental::distributed::preconditioner::Schwarz<real_t, int, gko_hypre_bigint>;
1925 GinkgoIterativeSolver *local_gko_solver = dynamic_cast<GinkgoIterativeSolver*>
1926 (&local_solver);
1927 if (local_gko_solver != NULL)
1928 {
1929 precond_gen = schwarz::build()
1930 .with_local_solver(local_gko_solver->GetFactory())
1931 .with_l1_smoother(l1_smoother)
1932 .on(executor);
1933 }
1934 else
1935 {
1936 MFEMPreconditioner *local_mfem_precond = dynamic_cast<MFEMPreconditioner*>
1937 (&local_solver);
1938 MFEM_VERIFY(local_mfem_precond == NULL,
1939 "Ginkgo::SchwarzPreconditioner cannot use an MFEMPreconditioner "
1940 "for the local solver.");
1941
1942 GinkgoPreconditioner *local_gko_precond = dynamic_cast<GinkgoPreconditioner*>
1943 (&local_solver);
1944 if (local_gko_precond != NULL)
1945 {
1946 if (local_gko_precond->HasGeneratedPreconditioner())
1947 {
1948 MFEM_VERIFY(l1_smoother == false,
1949 "L1 smoother not available for pre-generated local solvers");
1950 precond_gen = schwarz::build()
1951 .with_generated_local_solver(local_gko_precond->GetGeneratedPreconditioner())
1952 .on(executor);
1953 }
1954 else
1955 {
1956 precond_gen = schwarz::build()
1957 .with_local_solver(local_gko_precond->GetFactory())
1958 .with_l1_smoother(l1_smoother)
1959 .on(executor);
1960 }
1961 }
1962 else
1963 {
1964 MFEM_ABORT("Ginkgo::SchwarzPreconditioner must take a GinkgoIterativeSolver or GinkgoPreconditioner object "
1965 "for the local solver.");
1966 }
1967 }
1968}
1969
1970// Custom version of SetOperator that will sort the diagonal matrix if using
1971// L1 smoothing. This should be fixed in a future version of Ginkgo.
1973{
1975 {
1976 generated_precond.reset();
1977 has_generated_precond = false;
1978 }
1979
1980 // Needs to be square
1981 MFEM_VERIFY(op.Height() == op.Width(),
1982 "System operator is not square");
1983
1984 // Check for HypreParMatrix:
1985 HypreParMatrix *par_op_mat = const_cast<HypreParMatrix*>(
1986 dynamic_cast<const HypreParMatrix*>(&op));
1987 MFEM_VERIFY(par_op_mat != NULL,
1988 "GinkgoPreconditioner::SetOperator : not a HypreParMatrix!");
1989
1990 // Needs to be a square matrix
1991 MFEM_VERIFY(par_op_mat->Height() == par_op_mat->Width(),
1992 "System matrix is not square");
1993
1994 using schwarz =
1995 gko::experimental::distributed::preconditioner::Schwarz<real_t, int, gko_hypre_bigint>;
1996 auto factory_params = gko::as<typename schwarz::Factory>
1997 (precond_gen)->get_parameters();
1998 bool sort_diag = false;
1999 if (factory_params.l1_smoother == true)
2000 {
2001 sort_diag = true;
2002 }
2003 auto gko_matrix = GinkgoWrapHypreParMatrix(par_op_mat, executor, gko_comm,
2004 sort_diag);
2005 generated_precond = precond_gen->generate(gko::give(gko_matrix));
2006 has_generated_precond = true;
2007
2008 // Set MFEM Solver size values
2009 height = op.Height();
2010 width = op.Width();
2011}
2012#endif
2013
2014/* ---------------------- MFEMPreconditioner ------------------------ */
2016 GinkgoExecutor &exec,
2017 const Solver &mfem_precond
2018)
2019 : GinkgoPreconditioner(exec)
2020{
2021 generated_precond = std::shared_ptr<OperatorWrapper>(
2023 mfem_precond.Height(), &mfem_precond));
2024 has_generated_precond = true;
2025}
2026
2027#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
2029 GinkgoExecutor &exec,
2030 const Solver &mfem_precond,
2031 MPI_Comm comm
2032)
2033 : GinkgoPreconditioner(exec, comm)
2034{
2035 // Get global size (the MFEM preconditioner's Height() will be local)
2036 auto mpi_exec = executor->get_master();
2037 HYPRE_BigInt global_size = mfem_precond.Height();
2038 gko_comm->all_reduce(mpi_exec, &global_size, 1, MPI_SUM);
2039 generated_precond = std::shared_ptr<ParallelOperatorWrapper>(
2040 new ParallelOperatorWrapper(executor, *(this->gko_comm.get()),
2041 global_size, &mfem_precond));
2042 has_generated_precond = true;
2043}
2044#endif
2045
2046} // namespace Ginkgo
2047
2048} // namespace mfem
2049
2050#endif // MFEM_USE_GINKGO
The MFEM Device class abstracts hardware devices such as GPUs, as well as programming models such as ...
Definition device.hpp:129
static bool Allows(unsigned long b_mask)
Return true if any of the backends in the backend mask, b_mask, are allowed.
Definition device.hpp:271
BICGSTABSolver(GinkgoExecutor &exec)
Definition ginkgo.cpp:1027
CBGMRESSolver(GinkgoExecutor &exec, int dim=0, storage_precision prec=storage_precision::reduce1)
Definition ginkgo.cpp:1414
CGSSolver(GinkgoExecutor &exec)
Definition ginkgo.cpp:1102
FCGSolver(GinkgoExecutor &exec)
Definition ginkgo.cpp:1174
std::shared_ptr< gko::Executor > GetExecutor() const
Definition ginkgo.hpp:927
GinkgoExecutor(ExecType exec_type)
Definition ginkgo.cpp:33
@ CUDA
CUDA GPU Executor.
Definition ginkgo.hpp:881
@ HIP
HIP GPU Executor.
Definition ginkgo.hpp:883
@ REFERENCE
Reference CPU Executor.
Definition ginkgo.hpp:877
@ OMP
OpenMP CPU Executor.
Definition ginkgo.hpp:879
void SetOperator(const Operator &op) override
Definition ginkgo.cpp:856
void Mult(const Vector &x, Vector &y) const override
Definition ginkgo.cpp:653
std::shared_ptr< gko::stop::Combined::Factory > combined_factory
Definition ginkgo.hpp:1202
std::shared_ptr< gko::experimental::mpi::communicator > gko_comm
Definition ginkgo.hpp:1215
std::shared_ptr< gko::stop::ResidualNorm< real_t >::Factory > abs_criterion
Definition ginkgo.hpp:1172
std::shared_ptr< gko::stop::ImplicitResidualNorm< real_t >::Factory > imp_abs_criterion
Definition ginkgo.hpp:1186
std::shared_ptr< ConvergenceLogger > convergence_logger
Definition ginkgo.hpp:1191
std::shared_ptr< gko::stop::ResidualNorm< real_t >::Factory > rel_criterion
Definition ginkgo.hpp:1165
const std::shared_ptr< gko::LinOpFactory > GetFactory() const
Definition ginkgo.hpp:1077
std::shared_ptr< ResidualLogger > residual_logger
Definition ginkgo.hpp:1196
GinkgoIterativeSolver(GinkgoExecutor &exec, bool use_implicit_res_norm)
Definition ginkgo.cpp:350
std::shared_ptr< gko::stop::ImplicitResidualNorm< real_t >::Factory > imp_rel_criterion
Definition ginkgo.hpp:1179
std::shared_ptr< gko::LinOpFactory > solver_gen
Definition ginkgo.hpp:1153
std::shared_ptr< gko::Executor > executor
Definition ginkgo.hpp:1209
std::shared_ptr< gko::LinOp > solver
Definition ginkgo.hpp:1158
void SetOperator(const Operator &op) override
Definition ginkgo.cpp:1644
std::shared_ptr< gko::Executor > executor
Definition ginkgo.hpp:1043
const std::shared_ptr< gko::LinOpFactory > GetFactory() const
Definition ginkgo.hpp:1002
void Mult(const Vector &x, Vector &y) const override
Definition ginkgo.cpp:1590
std::shared_ptr< gko::LinOp > generated_precond
Definition ginkgo.hpp:1036
std::shared_ptr< gko::experimental::mpi::communicator > gko_comm
Definition ginkgo.hpp:1049
GinkgoPreconditioner(GinkgoExecutor &exec)
Definition ginkgo.cpp:1565
std::shared_ptr< gko::LinOpFactory > precond_gen
Definition ginkgo.hpp:1029
const std::shared_ptr< gko::LinOp > GetGeneratedPreconditioner() const
Definition ginkgo.hpp:1011
IRSolver(GinkgoExecutor &exec)
Definition ginkgo.cpp:1516
IcIsaiPreconditioner(GinkgoExecutor &exec, const std::string &factorization_type="exact", const int sweeps=0, const int sparsity_power=1, const bool skip_sort=false)
Definition ginkgo.cpp:1869
IcPreconditioner(GinkgoExecutor &exec, const std::string &factorization_type="exact", const int sweeps=0, const bool skip_sort=false)
Definition ginkgo.cpp:1833
IluIsaiPreconditioner(GinkgoExecutor &exec, const std::string &factorization_type="exact", const int sweeps=0, const int sparsity_power=1, const bool skip_sort=false)
Definition ginkgo.cpp:1776
IluPreconditioner(GinkgoExecutor &exec, const std::string &factorization_type="exact", const int sweeps=0, const bool skip_sort=false)
Definition ginkgo.cpp:1742
JacobiPreconditioner(GinkgoExecutor &exec, const std::string &storage_opt="none", const real_t accuracy=1.e-1, const int max_block_size=32)
Definition ginkgo.cpp:1711
MFEMPreconditioner(GinkgoExecutor &exec, const Solver &mfem_precond)
Definition ginkgo.cpp:2015
void apply_impl(const gko::LinOp *b, gko::LinOp *x) const override
Definition ginkgo.cpp:477
void apply_impl(const gko::LinOp *b, gko::LinOp *x) const override
Definition ginkgo.cpp:562
void SetOperator(const Operator &op) override
Definition ginkgo.cpp:1972
SchwarzPreconditioner(GinkgoExecutor &exec, MPI_Comm comm, Solver &local_solver, const bool l1_smoother=false)
Definition ginkgo.cpp:1915
const Vector & get_mfem_vec_const_ref() const
Definition ginkgo.hpp:137
Wrapper for hypre's ParCSR matrix class.
Definition hypre.hpp:419
Memory< HYPRE_Int > & GetOffdMemoryI()
Definition hypre.hpp:965
Memory< HYPRE_Int > & GetDiagMemoryI()
Definition hypre.hpp:957
int GetNumCols() const
Returns the number of columns in the diagonal block of the ParCSRMatrix.
Definition hypre.hpp:706
int GetNumRows() const
Returns the number of rows in the diagonal block of the ParCSRMatrix.
Definition hypre.hpp:699
Memory< HYPRE_Int > & GetDiagMemoryJ()
Definition hypre.hpp:958
Memory< HYPRE_Int > & GetOffdMemoryJ()
Definition hypre.hpp:966
Memory< real_t > & GetOffdMemoryData()
Definition hypre.hpp:967
Memory< real_t > & GetDiagMemoryData()
Definition hypre.hpp:959
HYPRE_BigInt * GetColStarts() const
Return the parallel column partitioning array.
Definition hypre.hpp:728
void GetOffdColMap(HYPRE_BigInt *&cmap, HYPRE_Int &num_cols) const
Get the global column mapping for the local off-diagonal block.
Definition hypre.cpp:1687
int Capacity() const
Return the size of the allocated memory.
T * ReadWrite(MemoryClass mc, int size)
Get read-write access to the memory with the given MemoryClass.
MemoryType GetMemoryType() const
Return a MemoryType that is currently valid. If both the host and the device pointers are currently v...
Abstract operator.
Definition operator.hpp:27
int width
Dimension of the input / number of columns in the matrix.
Definition operator.hpp:30
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition operator.hpp:68
int height
Dimension of the output / number of rows in the matrix.
Definition operator.hpp:29
virtual void Mult(const Vector &x, Vector &y) const =0
Operator application: y=A(x).
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition operator.hpp:74
Base class for solvers.
Definition operator.hpp:855
bool iterative_mode
If true, use the second argument of Mult() as an initial guess.
Definition operator.hpp:858
Data type sparse matrix.
Definition sparsemat.hpp:51
int * ReadWriteI(bool on_dev=true)
real_t * ReadWriteData(bool on_dev=true)
int * ReadWriteJ(bool on_dev=true)
Memory< real_t > & GetMemoryData()
Vector data type.
Definition vector.hpp:82
virtual const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:520
virtual real_t * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:536
Memory< real_t > & GetMemory()
Return a reference to the Memory object used by the Vector.
Definition vector.hpp:265
void Destroy()
Destroy a vector.
Definition vector.hpp:722
int Size() const
Returns the size of the vector.
Definition vector.hpp:234
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
Definition vector.hpp:145
Vector & Add(const real_t a, const Vector &Va)
(*this) += a * Va
Definition vector.cpp:326
const real_t alpha
Definition ex15.cpp:369
int dim
Definition ex24.cpp:53
HYPRE_Int HYPRE_BigInt
real_t b
Definition lissajous.cpp:42
std::conditional_t< sizeof(HYPRE_BigInt)==sizeof(std::int32_t), std::int32_t, std::conditional_t< sizeof(HYPRE_BigInt)==sizeof(std::int64_t), std::int64_t, void > > gko_hypre_bigint
Definition ginkgo.hpp:47
std::conditional_t< sizeof(HYPRE_Int)==sizeof(std::int32_t), std::int32_t, std::conditional_t< sizeof(HYPRE_Int)==sizeof(std::int64_t), std::int64_t, void > > gko_hypre_int
Definition ginkgo.hpp:44
gko::array< T > gko_array
Definition ginkgo.hpp:41
std::unique_ptr< gko::experimental::distributed::Matrix< real_t, gko_hypre_int, gko_hypre_bigint > > GinkgoWrapHypreParMatrix(HypreParMatrix *par_mat, std::shared_ptr< gko::Executor > executor, std::shared_ptr< gko::experimental::mpi::communicator > gko_comm, bool sort_diag)
Definition ginkgo.cpp:775
if(k >=N)
Definition forall.hpp:738
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition globals.hpp:66
MemoryClass
Memory classes identify sets of memory types.
OutStream err(std::cerr)
Global stream used by the library for standard error output. Initially it uses the same std::streambu...
Definition globals.hpp:71
float real_t
Definition config.hpp:46
struct schwarz_common schwarz
@ HIP_MASK
Biwise-OR of all HIP backends.
Definition device.hpp:98
@ CUDA_MASK
Biwise-OR of all CUDA backends.
Definition device.hpp:96
@ OMP_MASK
Biwise-OR of all OpenMP backends.
Definition device.hpp:100