11#pragma once
22
33#include < stdexcept>
4+ #include < string>
45
56#include " p3a_dynamic_array.hpp"
67#include " p3a_reduce.hpp"
78
89namespace p3a {
910
11+ class convergence_failure : public std ::runtime_error {
12+ public:
13+ convergence_failure (std::string const & msg)
14+ :std::runtime_error(msg)
15+ {
16+ }
17+ };
18+
1019template <
1120 class T ,
1221 class Allocator = host_allocator<T>,
@@ -20,6 +29,8 @@ class preconditioned_conjugate_gradient {
2029 array_type m_p;
2130 array_type m_scratch;
2231 associative_sum<T, Allocator, ExecutionPolicy> m_adder;
32+ T m_relative_tolerance = 1.0e-6 ;
33+ int m_maximum_iterations = 1'000'000 ;
2334 public:
2435 using M_inv_action_type = std::function<
2536 void (array_type const &, array_type&)>;
@@ -31,12 +42,19 @@ class preconditioned_conjugate_gradient {
3142 preconditioned_conjugate_gradient (mpicpp::comm&& comm_arg)
3243 :m_adder(std::move(comm_arg))
3344 {}
45+ void set_relative_tolerance (T const & arg)
46+ {
47+ m_relative_tolerance = arg;
48+ }
49+ void set_maximum_iterations (int arg)
50+ {
51+ m_maximum_iterations = arg;
52+ }
3453 P3A_NEVER_INLINE int solve (
3554 M_inv_action_type const & M_inv_action,
3655 A_action_type const & A_action,
3756 b_filler_type const & b_filler,
38- array_type& x,
39- T const & relative_tolerance);
57+ array_type& x);
4058};
4159
4260template <
@@ -90,8 +108,7 @@ int preconditioned_conjugate_gradient<T, Allocator, ExecutionPolicy>::solve(
90108 M_inv_action_type const & M_inv_action,
91109 A_action_type const & A_action,
92110 b_filler_type const & b_filler,
93- array_type& x,
94- T const & relative_tolerance)
111+ array_type& x)
95112{
96113 this ->m_r .resize (x.size ());
97114 this ->m_z .resize (x.size ());
@@ -107,9 +124,9 @@ int preconditioned_conjugate_gradient<T, Allocator, ExecutionPolicy>::solve(
107124 T const b_dot_b = dot_product (m_adder, b, b);
108125 T const b_magnitude = p3a::sqrt (b_dot_b);
109126 if (b_magnitude == T (0 )) {
110- throw std::runtime_error ( " p3a::preconditioned_conjugate_gradient: b_magnitude = 0 " );
127+ throw std::invalid_argument ( " P3A CG solver: the magnitude of the right hand side vector is zero " );
111128 }
112- T const absolute_tolerance = b_magnitude * relative_tolerance ;
129+ T const absolute_tolerance = b_magnitude * m_relative_tolerance ;
113130 A_action (x, Ax); // Ax = A * x
114131 axpy (T (-1 ), Ax, b, r); // r = A * x - b
115132 T residual_magnitude = p3a::sqrt (dot_product (m_adder, r, r));
@@ -127,6 +144,14 @@ int preconditioned_conjugate_gradient<T, Allocator, ExecutionPolicy>::solve(
127144 if (residual_magnitude <= absolute_tolerance) {
128145 return k;
129146 }
147+ if (k == m_maximum_iterations) {
148+ throw convergence_failure (
149+ " P3A CG solver failed to converge in " + std::to_string (m_maximum_iterations) +
150+ " iterations. relative tolerance was " + std::to_string (m_relative_tolerance) +
151+ " , right hand side magnitude was " + std::to_string (b_magnitude) +
152+ " , absolute_tolerance was " + std::to_string (absolute_tolerance) +
153+ " , and final residual magnitude was " + std::to_string (residual_magnitude));
154+ }
130155 M_inv_action (r, z); // z = M^-1 r
131156 T const r_dot_z_new = dot_product (m_adder, r, z);
132157 T const beta = r_dot_z_new / r_dot_z_old;
0 commit comments