27 #ifndef OOMPH_COMPLEX_SMOOTHER_HEADER
28 #define OOMPH_COMPLEX_SMOOTHER_HEADER
32 #include <oomph-lib-config.h>
75 if (matrices_pt.size() != 2)
78 std::ostringstream error_message_stream;
81 error_message_stream <<
"Can only deal with two matrices. You have "
82 << matrices_pt.size() <<
" matrices." << std::endl;
86 OOMPH_CURRENT_FUNCTION,
87 OOMPH_EXCEPTION_LOCATION);
93 std::ostringstream error_message_stream;
97 <<
"Can only deal with two input vectors. You have " << x.size()
98 <<
" vectors." << std::endl;
102 OOMPH_CURRENT_FUNCTION,
103 OOMPH_EXCEPTION_LOCATION);
106 if (soln.size() != 2)
109 std::ostringstream error_message_stream;
113 <<
"Can only deal with two output vectors. You have " << soln.size()
114 <<
" output vectors." << std::endl;
118 OOMPH_CURRENT_FUNCTION,
119 OOMPH_EXCEPTION_LOCATION);
140 matrices_pt[0]->multiply(x[0], soln[0]);
143 matrices_pt[0]->multiply(x[1], soln[1]);
149 matrices_pt[1]->multiply(x[1], temp);
155 matrices_pt[1]->multiply(x[0], temp);
163 template<
typename MATRIX>
169 const double& n_dof);
184 template<
typename MATRIX>
193 unsigned n_dof_types = 2;
199 matrix_storage_pt[0] = real_matrix_pt;
202 matrix_storage_pt[1] = imag_matrix_pt;
205 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
210 matrix_storage_pt[dof_type]) != 0)
213 matrix_storage_pt[dof_type])
216 std::ostringstream error_message_stream;
217 error_message_stream <<
"The matrix must not be distributed.";
219 OOMPH_CURRENT_FUNCTION,
220 OOMPH_EXCEPTION_LOCATION);
224 if (!(rhs[dof_type].built()))
226 std::ostringstream error_message_stream;
227 error_message_stream <<
"The rhs vector distribution must be setup.";
229 OOMPH_CURRENT_FUNCTION,
230 OOMPH_EXCEPTION_LOCATION);
233 if (rhs[dof_type].
nrow() != n_dof)
235 std::ostringstream error_message_stream;
236 error_message_stream <<
"RHS does not have the same dimension as the "
239 OOMPH_CURRENT_FUNCTION,
240 OOMPH_EXCEPTION_LOCATION);
245 std::ostringstream error_message_stream;
246 error_message_stream <<
"The rhs vector must not be distributed.";
248 OOMPH_CURRENT_FUNCTION,
249 OOMPH_EXCEPTION_LOCATION);
253 if (solution[dof_type].built())
258 std::ostringstream error_message_stream;
259 error_message_stream <<
"If the result distribution is setup then it "
260 <<
"must be the same as the rhs distribution";
262 OOMPH_CURRENT_FUNCTION,
263 OOMPH_EXCEPTION_LOCATION);
280 template<
typename MATRIX>
342 double omega = (12.0 - 4.0 * pow(kh, 2.0)) / (18.0 - 3.0 * pow(kh, 2.0));
360 else if ((k > pi) && (kh > 2 * cos(pi * h / 2)))
363 double omega_2 = (2.0 - pow(kh, 2.0));
366 omega_2 /= (2.0 * pow(sin(pi * h / 2), 2.0) - 0.5 * pow(kh, 2.0));
369 if (omega_2 > (2.0 / 3.0))
418 std::ostringstream error_message_stream;
419 error_message_stream <<
"Incompatible real and complex matrix sizes.";
421 OOMPH_CURRENT_FUNCTION,
422 OOMPH_EXCEPTION_LOCATION);
454 for (
unsigned i = 0;
i < n_dof;
i++)
464 1.0 / (dummy_r * dummy_r + dummy_c * dummy_c);
531 template<
typename MATRIX>
536 unsigned n_dof = Matrix_real_pt->nrow();
548 this->check_validity_of_solve_helper_inputs<MATRIX>(
549 tmp_rmatrix_pt, tmp_imatrix_pt, rhs, solution, n_dof);
559 if ((!solution[0].distribution_pt()->built()) ||
560 (!solution[1].distribution_pt()->built()))
562 solution[0].build(rhs[0].distribution_pt(), 0.0);
563 solution[1].build(rhs[1].distribution_pt(), 0.0);
578 DoubleVector constant_term_real(this->distribution_pt(), 0.0);
579 DoubleVector constant_term_imag(this->distribution_pt(), 0.0);
589 double res_real_norm = 0.0;
590 double res_imag_norm = 0.0;
594 double norm_res = 0.0;
612 for (
unsigned i = 0;
i < n_dof;
i++)
616 constant_term_real[
i] = (Matrix_diagonal_real[
i] * rhs_real[
i] +
617 Matrix_diagonal_imag[
i] * rhs_imag[
i]);
620 constant_term_real[
i] *= Matrix_diagonal_inv_sq[
i];
623 constant_term_real[
i] *= Omega;
627 constant_term_imag[
i] = (Matrix_diagonal_real[
i] * rhs_imag[
i] -
628 Matrix_diagonal_imag[
i] * rhs_real[
i]);
631 constant_term_imag[
i] *= Matrix_diagonal_inv_sq[
i];
634 constant_term_imag[
i] *= Omega;
648 Matrix_real_pt->multiply(x_real, temp_vec_rr);
649 Matrix_imag_pt->multiply(x_imag, temp_vec_cc);
650 Matrix_real_pt->multiply(x_imag, temp_vec_rc);
651 Matrix_imag_pt->multiply(x_real, temp_vec_cr);
665 if (!Use_as_smoother)
668 residual_real.
build(this->distribution_pt(), 0.0);
669 residual_imag.
build(this->distribution_pt(), 0.0);
672 residual_real = rhs_real;
673 residual_real -= temp_vec_rr;
674 residual_real += temp_vec_cc;
677 residual_imag = rhs_imag;
678 residual_imag -= temp_vec_rc;
679 residual_imag -= temp_vec_cr;
687 double res_real_norm = residual_real.
norm();
688 double res_imag_norm = residual_imag.
norm();
690 sqrt(res_real_norm * res_real_norm + res_imag_norm * res_imag_norm);
694 if (Doc_convergence_history)
696 if (!Output_file_stream.is_open())
698 oomph_info << Iterations <<
" " << norm_res << std::endl;
702 Output_file_stream << Iterations <<
" " << norm_res << std::endl;
709 DoubleVector temp_vec_real(this->distribution_pt(), 0.0);
710 DoubleVector temp_vec_imag(this->distribution_pt(), 0.0);
713 temp_vec_real = temp_vec_rr;
714 temp_vec_real -= temp_vec_cc;
717 temp_vec_imag = temp_vec_cr;
718 temp_vec_imag += temp_vec_rc;
721 for (
unsigned iter_num = 0; iter_num <
Max_iter; iter_num++)
725 for (
unsigned i = 0;
i < n_dof;
i++)
728 double dummy_r = 0.0;
729 double dummy_c = 0.0;
732 dummy_r = (Matrix_diagonal_real[
i] * temp_vec_real[
i] +
733 Matrix_diagonal_imag[
i] * temp_vec_imag[
i]);
736 dummy_c = (Matrix_diagonal_real[
i] * temp_vec_imag[
i] -
737 Matrix_diagonal_imag[
i] * temp_vec_real[
i]);
740 dummy_r *= Omega * Matrix_diagonal_inv_sq[
i];
741 dummy_c *= Omega * Matrix_diagonal_inv_sq[
i];
744 x_real[
i] -= dummy_r;
745 x_imag[
i] -= dummy_c;
749 x_real += constant_term_real;
750 x_imag += constant_term_imag;
755 Matrix_real_pt->multiply(x_real, temp_vec_rr);
756 Matrix_imag_pt->multiply(x_imag, temp_vec_cc);
757 Matrix_real_pt->multiply(x_imag, temp_vec_rc);
758 Matrix_imag_pt->multiply(x_real, temp_vec_cr);
761 temp_vec_real = temp_vec_rr;
762 temp_vec_real -= temp_vec_cc;
765 temp_vec_imag = temp_vec_cr;
766 temp_vec_imag += temp_vec_rc;
769 if (!Use_as_smoother)
772 residual_real = rhs_real;
773 residual_real -= temp_vec_rr;
774 residual_real += temp_vec_cc;
777 residual_imag = rhs_imag;
778 residual_imag -= temp_vec_rc;
779 residual_imag -= temp_vec_cr;
782 res_real_norm = residual_real.
norm();
783 res_imag_norm = residual_imag.
norm();
785 sqrt(res_real_norm * res_real_norm + res_imag_norm * res_imag_norm) /
790 if (Doc_convergence_history)
792 if (!Output_file_stream.is_open())
794 oomph_info << Iterations <<
" " << norm_res << std::endl;
798 Output_file_stream << Iterations <<
" " << norm_res << std::endl;
803 if (norm_res < Tolerance)
812 if (!Use_as_smoother)
817 oomph_info <<
"\n(Complex) damped Jacobi converged. Residual norm: "
819 <<
"\nNumber of iterations to convergence: " << Iterations
826 solution[0] = x_real;
827 solution[1] = x_imag;
831 Solution_time = t_end - t_start;
834 oomph_info <<
"Time for solve with (complex) damped Jacobi [sec]: "
835 << Solution_time << std::endl;
840 if ((Iterations >
Max_iter - 1) && (Throw_error_after_max_iter))
843 "Solver failed to converge and you requested ";
844 error_message +=
"an error on convergence failures.";
846 error_message, OOMPH_EXCEPTION_LOCATION, OOMPH_CURRENT_FUNCTION);
853 template<
typename MATRIX>
895 std::string error_message =
"Solve function for class\n\n";
896 error_message +=
"ComplexGMRES\n\n";
897 error_message +=
"is deliberately broken to avoid the accidental \n";
898 error_message +=
"use of the inappropriate C++ default. If you \n";
899 error_message +=
"really need one for this class, write it yourself...\n";
903 error_message, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
936 if (helmholtz_matrix_pt.size() != 2)
938 std::ostringstream error_message_stream;
939 error_message_stream <<
"Can only deal with two matrices. You have "
940 << helmholtz_matrix_pt.size() <<
" matrices."
945 OOMPH_CURRENT_FUNCTION,
946 OOMPH_EXCEPTION_LOCATION);
964 std::ostringstream error_message_stream;
965 error_message_stream <<
"Incompatible real and imag. matrix sizes.";
967 OOMPH_CURRENT_FUNCTION,
968 OOMPH_EXCEPTION_LOCATION);
974 std::ostringstream error_message_stream;
975 error_message_stream <<
"Incompatible real and imag. matrix sizes.";
977 OOMPH_CURRENT_FUNCTION,
978 OOMPH_EXCEPTION_LOCATION);
1030 const Vector<
Vector<std::complex<double>>>& hessenberg,
1031 const Vector<std::complex<double>>&
s,
1053 for (
int i =
int(k);
i >= 0;
i--)
1056 y[
i] /= hessenberg[
i][
i];
1059 for (
int j =
i - 1; j >= 0; j--)
1062 y[j] -= hessenberg[
i][j] * y[
i];
1068 unsigned n_row = x[0].nrow();
1073 for (
unsigned j = 0; j <= k; j++)
1076 const double* vj_r_pt = v[j][0].values_pt();
1079 const double* vj_c_pt = v[j][1].values_pt();
1082 for (
unsigned i = 0;
i < n_row;
i++)
1085 x[0][
i] += (vj_r_pt[
i] * y[j].real()) - (vj_c_pt[
i] * y[j].imag());
1088 x[1][
i] += (vj_c_pt[
i] * y[j].real()) + (vj_r_pt[
i] * y[j].imag());
1107 std::complex<double>& dy,
1108 std::complex<double>& cs,
1109 std::complex<double>& sn)
1125 else if (std::abs(dy) > std::abs(dx))
1128 std::complex<double> temp = dx / dy;
1133 sn = (dy / std::abs(dy)) / sqrt(1.0 + pow(std::abs(temp), 2.0));
1146 std::complex<double> temp = dy / dx;
1151 cs = (dx / std::abs(dx)) / sqrt(1.0 + pow(std::abs(temp), 2.0));
1164 if ((std::fabs(sn.imag()) >
tolerance) || (sn.real() < 0))
1167 std::ostringstream error_message_stream;
1170 error_message_stream <<
"The value of sin(theta) is not real "
1171 <<
"and/or nonnegative. Value is: " << sn
1176 OOMPH_CURRENT_FUNCTION,
1177 OOMPH_EXCEPTION_LOCATION);
1186 std::complex<double>& dy,
1187 std::complex<double>& cs,
1188 std::complex<double>& sn)
1191 std::complex<double> temp = std::conj(cs) * dx + std::conj(sn) * dy;
1194 dy = -sn * dx + cs * dy;
1218 template<
typename MATRIX>
1223 unsigned n_dof_types = 2;
1228 unsigned n_row = Matrices_storage_pt[0]->nrow();
1235 std::ostringstream error_message_stream;
1238 error_message_stream <<
"The maximum number of iterations cannot exceed "
1239 <<
"the number of rows in the problem."
1240 <<
"\nMaximum number of iterations: " <<
Max_iter
1241 <<
"\nNumber of rows: " << n_row << std::endl;
1245 OOMPH_CURRENT_FUNCTION,
1246 OOMPH_EXCEPTION_LOCATION);
1250 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
1256 Matrices_storage_pt[dof_type]) != 0)
1259 Matrices_storage_pt[dof_type])
1262 std::ostringstream error_message_stream;
1263 error_message_stream <<
"The matrix must not be distributed.";
1265 OOMPH_CURRENT_FUNCTION,
1266 OOMPH_EXCEPTION_LOCATION);
1270 if (!rhs[dof_type].built())
1272 std::ostringstream error_message_stream;
1273 error_message_stream <<
"The rhs vector distribution must be setup.";
1275 OOMPH_CURRENT_FUNCTION,
1276 OOMPH_EXCEPTION_LOCATION);
1279 if (rhs[dof_type].nrow() != n_row)
1281 std::ostringstream error_message_stream;
1282 error_message_stream <<
"RHS does not have the same dimension as the"
1283 <<
" linear system";
1285 OOMPH_CURRENT_FUNCTION,
1286 OOMPH_EXCEPTION_LOCATION);
1289 if (rhs[dof_type].distribution_pt()->distributed())
1291 std::ostringstream error_message_stream;
1292 error_message_stream <<
"The rhs vector must not be distributed.";
1294 OOMPH_CURRENT_FUNCTION,
1295 OOMPH_EXCEPTION_LOCATION);
1299 if (solution[dof_type].built())
1301 if (!(*rhs[dof_type].distribution_pt() ==
1302 *solution[dof_type].distribution_pt()))
1304 std::ostringstream error_message_stream;
1305 error_message_stream <<
"If the result distribution is setup then it "
1306 <<
"must be the same as the rhs distribution";
1308 OOMPH_CURRENT_FUNCTION,
1309 OOMPH_EXCEPTION_LOCATION);
1314 if (!solution[dof_type].built())
1317 solution[dof_type].build(this->distribution_pt(), 0.0);
1352 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
1355 w[dof_type].build(this->distribution_pt(), 0.0);
1363 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
1366 r[dof_type].build(this->distribution_pt(), 0.0);
1370 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
1373 r[dof_type] = rhs[dof_type];
1377 double norm_r = r[0].norm();
1380 double norm_c = r[1].norm();
1383 double normb = sqrt(pow(norm_r, 2.0) + pow(norm_c, 2.0));
1386 double beta = normb;
1401 resid = beta / normb;
1405 if (Doc_convergence_history)
1408 if (!Output_file_stream.is_open())
1411 oomph_info << 0 <<
" " << resid << std::endl;
1417 Output_file_stream << 0 <<
" " << resid << std::endl;
1422 if (resid <= Tolerance)
1428 oomph_info <<
"GMRES converged immediately. Normalised residual norm: "
1429 << resid << std::endl;
1440 v.resize(n_row + 1);
1444 for (
unsigned dof_type = 0; dof_type < n_row + 1; dof_type++)
1447 v[dof_type].resize(n_dof_types);
1458 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
1462 v[0][dof_type].build(this->distribution_pt(), 0.0);
1466 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
1469 double* v0_pt = v[0][dof_type].values_pt();
1472 const double* r_pt = r[dof_type].values_pt();
1475 for (
unsigned i = 0;
i < n_row;
i++)
1478 v0_pt[
i] = r_pt[
i] / beta;
1488 for (
unsigned j = 0; j <
Max_iter; j++)
1491 hessenberg[j].resize(j + 2, std::complex<double>(0.0, 0.0));
1495 complex_matrix_multiplication(Matrices_storage_pt, v[j], w);
1498 double* w_r_pt = w[0].values_pt();
1501 double* w_c_pt = w[1].values_pt();
1507 for (
unsigned i = 0;
i < j + 1;
i++)
1510 const double* vi_r_pt = v[
i][0].values_pt();
1513 const double* vi_c_pt = v[
i][1].values_pt();
1516 for (
unsigned k = 0; k < n_row; k++)
1519 std::complex<double> complex_v(vi_r_pt[k], vi_c_pt[k]);
1522 std::complex<double> complex_w(w_r_pt[k], w_c_pt[k]);
1526 hessenberg[j][
i] += std::conj(complex_v) * complex_w;
1531 for (
unsigned k = 0; k < n_row; k++)
1534 w_r_pt[k] -= (hessenberg[j][
i].real() * vi_r_pt[k] -
1535 hessenberg[j][
i].imag() * vi_c_pt[k]);
1538 w_c_pt[k] -= (hessenberg[j][
i].real() * vi_c_pt[k] +
1539 hessenberg[j][
i].imag() * vi_r_pt[k]);
1544 norm_r = w[0].norm();
1547 norm_c = w[1].norm();
1551 hessenberg[j][j + 1] = sqrt(pow(norm_r, 2.0) + pow(norm_c, 2.0));
1554 v[j + 1][0].build(this->distribution_pt(), 0.0);
1557 v[j + 1][1].build(this->distribution_pt(), 0.0);
1561 if (hessenberg[j][j + 1] != 0.0)
1564 double* v_r_pt = v[j + 1][0].values_pt();
1567 double* v_c_pt = v[j + 1][1].values_pt();
1570 const double* w_r_pt = w[0].values_pt();
1573 const double* w_c_pt = w[1].values_pt();
1580 double h_subdiag_val = hessenberg[j][j + 1].real();
1583 for (
unsigned k = 0; k < n_row; k++)
1586 v_r_pt[k] = w_r_pt[k] / h_subdiag_val;
1589 v_c_pt[k] = w_c_pt[k] / h_subdiag_val;
1601 oomph_info <<
"Subdiagonal Hessenberg entry is zero."
1602 <<
"Do something here..." << std::endl;
1607 for (
unsigned k = 0; k < j; k++)
1611 apply_plane_rotation(
1612 hessenberg[j][k], hessenberg[j][k + 1], cs[k], sn[k]);
1616 generate_plane_rotation(
1617 hessenberg[j][j], hessenberg[j][j + 1], cs[j], sn[j]);
1620 apply_plane_rotation(
1621 hessenberg[j][j], hessenberg[j][j + 1], cs[j], sn[j]);
1625 apply_plane_rotation(
s[j],
s[j + 1], cs[j], sn[j]);
1630 beta = std::abs(
s[j + 1]);
1633 resid = beta / normb;
1637 if (Doc_convergence_history)
1640 if (!Output_file_stream.is_open())
1643 oomph_info << j + 1 <<
" " << resid << std::endl;
1649 Output_file_stream << j + 1 <<
" " << resid << std::endl;
1654 if (resid < Tolerance)
1661 update(j, hessenberg,
s, v, solution);
1667 oomph_info <<
"\nGMRES converged (1). Normalised residual norm: "
1668 << resid << std::endl;
1671 oomph_info <<
"Number of iterations to convergence: " << j + 1 <<
"\n"
1679 Solution_time = t_end - t_start;
1685 oomph_info <<
"Time for solve with GMRES [sec]: " << Solution_time
1703 update(
Max_iter - 1, hessenberg,
s, v, solution);
1710 Solution_time = t_end - t_start;
1716 oomph_info <<
"Time for solve with GMRES [sec]: " << Solution_time
1733 template<
typename MATRIX>
1773 std::ostringstream error_message_stream;
1776 error_message_stream <<
"Preconditioner_solve(...) is broken. "
1777 <<
"HelmholtzGMRESMG is only meant to be used as "
1778 <<
"a linear solver.\n";
1782 OOMPH_CURRENT_FUNCTION,
1783 OOMPH_EXCEPTION_LOCATION);
1791 std::ostringstream error_message_stream;
1794 error_message_stream <<
"This function is broken. HelmholtzGMRESMG is "
1795 <<
"only meant to be used as a linear solver.\n";
1799 OOMPH_CURRENT_FUNCTION,
1800 OOMPH_EXCEPTION_LOCATION);
1808 #ifdef OOMPH_HAS_MPI
1814 OomphLibWarning(
"Can't guarantee the MG solver will work in parallel!",
1815 OOMPH_CURRENT_FUNCTION,
1816 OOMPH_EXCEPTION_LOCATION);
1821 unsigned n_dof = problem_pt->
ndof();
1875 bool allow_different_element_types_in_mesh =
true;
1877 0, problem_pt->
mesh_pt(), allow_different_element_types_in_mesh);
1887 if (nblock_types != 2)
1890 std::stringstream tmp;
1891 tmp <<
"There are supposed to be two block types.\nYours has "
1896 tmp.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
1922 oomph_info <<
"\nTime for setup of block Jacobian [sec]: "
1968 std::ostringstream error_message_stream;
1971 error_message_stream
1972 <<
"This function is broken. The block preconditioner "
1973 <<
"needs access to the underlying mesh.\n";
1977 OOMPH_CURRENT_FUNCTION,
1978 OOMPH_EXCEPTION_LOCATION);
2004 throw OomphLibError(
"No matrix was stored -- cannot re-solve",
2005 OOMPH_CURRENT_FUNCTION,
2006 OOMPH_EXCEPTION_LOCATION);
2093 if (matrices_pt.size() != 2)
2096 std::ostringstream error_message_stream;
2099 error_message_stream <<
"Can only deal with two matrices. You have "
2100 << matrices_pt.size() <<
" matrices." << std::endl;
2104 OOMPH_CURRENT_FUNCTION,
2105 OOMPH_EXCEPTION_LOCATION);
2111 std::ostringstream error_message_stream;
2114 error_message_stream
2115 <<
"Can only deal with two input vectors. You have " << x.size()
2116 <<
" vectors." << std::endl;
2120 OOMPH_CURRENT_FUNCTION,
2121 OOMPH_EXCEPTION_LOCATION);
2124 if (soln.size() != 2)
2127 std::ostringstream error_message_stream;
2130 error_message_stream
2131 <<
"Can only deal with two output vectors. You have " << soln.size()
2132 <<
" output vectors." << std::endl;
2136 OOMPH_CURRENT_FUNCTION,
2137 OOMPH_EXCEPTION_LOCATION);
2162 matrices_pt[0]->multiply(x[0], soln[0]);
2165 matrices_pt[0]->multiply(x[1], soln[1]);
2171 matrices_pt[1]->multiply(x[1], temp);
2177 matrices_pt[1]->multiply(x[0], temp);
2185 const Vector<
Vector<std::complex<double>>>& hessenberg,
2186 const Vector<std::complex<double>>&
s,
2209 for (
int i =
int(k);
i >= 0;
i--)
2212 y[
i] /= hessenberg[
i][
i];
2215 for (
int j =
i - 1; j >= 0; j--)
2218 y[j] -= hessenberg[
i][j] * y[
i];
2224 unsigned n_row = x[0].nrow();
2233 for (
unsigned dof_type = 0; dof_type < 2; dof_type++)
2243 double* block_temp_r_pt = block_temp[0].values_pt();
2246 double* block_temp_c_pt = block_temp[1].values_pt();
2249 for (
unsigned j = 0; j <= k; j++)
2252 const double* vj_r_pt = v[j][0].values_pt();
2255 const double* vj_c_pt = v[j][1].values_pt();
2258 for (
unsigned i = 0;
i < n_row;
i++)
2261 block_temp_r_pt[
i] +=
2262 (vj_r_pt[
i] * y[j].real()) - (vj_c_pt[
i] * y[j].imag());
2265 block_temp_c_pt[
i] +=
2266 (vj_c_pt[
i] * y[j].real()) + (vj_r_pt[
i] * y[j].imag());
2275 for (
unsigned dof_type = 0; dof_type < 2; dof_type++)
2278 x[dof_type] += block_temp[dof_type];
2306 for (
unsigned dof_type = 0; dof_type < 2; dof_type++)
2309 x[dof_type] += block_z[dof_type];
2328 std::complex<double>& dy,
2329 std::complex<double>& cs,
2330 std::complex<double>& sn)
2346 else if (std::abs(dy) > std::abs(dx))
2349 std::complex<double> temp = dx / dy;
2354 sn = (dy / std::abs(dy)) / sqrt(1.0 + pow(std::abs(temp), 2.0));
2367 std::complex<double> temp = dy / dx;
2372 cs = (dx / std::abs(dx)) / sqrt(1.0 + pow(std::abs(temp), 2.0));
2385 if ((std::fabs(sn.imag()) >
tolerance) || (sn.real() < 0))
2388 std::ostringstream error_message_stream;
2391 error_message_stream <<
"The value of sin(theta) is not real "
2392 <<
"and/or nonnegative. Value is: " << sn
2397 OOMPH_CURRENT_FUNCTION,
2398 OOMPH_EXCEPTION_LOCATION);
2407 std::complex<double>& dy,
2408 std::complex<double>& cs,
2409 std::complex<double>& sn)
2412 std::complex<double> temp = std::conj(cs) * dx + std::conj(sn) * dy;
2415 dy = -sn * dx + cs * dy;
2452 template<
typename MATRIX>
2459 unsigned n_dof_types = this->ndof_types();
2463 if (n_dof_types != 2)
2466 std::stringstream error_message_stream;
2469 error_message_stream <<
"This preconditioner only works for problems "
2470 <<
"with 2 dof types\nYours has " << n_dof_types;
2474 OOMPH_CURRENT_FUNCTION,
2475 OOMPH_EXCEPTION_LOCATION);
2482 unsigned n_row = Matrices_storage_pt[0]->nrow();
2489 std::ostringstream error_message_stream;
2492 error_message_stream <<
"The maximum number of iterations cannot exceed "
2493 <<
"the number of rows in the problem."
2494 <<
"\nMaximum number of iterations: " <<
Max_iter
2495 <<
"\nNumber of rows: " << n_row << std::endl;
2499 OOMPH_CURRENT_FUNCTION,
2500 OOMPH_EXCEPTION_LOCATION);
2505 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
2510 Matrices_storage_pt[dof_type]) != 0)
2513 Matrices_storage_pt[dof_type])
2516 std::ostringstream error_message_stream;
2517 error_message_stream <<
"The matrix must not be distributed.";
2519 OOMPH_CURRENT_FUNCTION,
2520 OOMPH_EXCEPTION_LOCATION);
2527 std::ostringstream error_message_stream;
2528 error_message_stream <<
"The rhs vector distribution must be setup.";
2530 OOMPH_CURRENT_FUNCTION,
2531 OOMPH_EXCEPTION_LOCATION);
2534 if (rhs.
nrow() != 2 * n_row)
2536 std::ostringstream error_message_stream;
2537 error_message_stream <<
"RHS does not have the same dimension as the"
2538 <<
" linear system";
2540 OOMPH_CURRENT_FUNCTION,
2541 OOMPH_EXCEPTION_LOCATION);
2546 std::ostringstream error_message_stream;
2547 error_message_stream <<
"The rhs vector must not be distributed.";
2549 OOMPH_CURRENT_FUNCTION,
2550 OOMPH_EXCEPTION_LOCATION);
2554 if (solution.
built())
2558 std::ostringstream error_message_stream;
2559 error_message_stream <<
"If the result distribution is setup then it "
2560 <<
"must be the same as the rhs distribution";
2562 OOMPH_CURRENT_FUNCTION,
2563 OOMPH_EXCEPTION_LOCATION);
2569 if (!solution.
built())
2590 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
2593 block_solution[dof_type].build(this->block_distribution_pt(dof_type),
2597 block_rhs[dof_type].build(this->block_distribution_pt(dof_type), 0.0);
2601 this->get_block_vectors(solution, block_solution);
2604 this->get_block_vectors(rhs, block_rhs);
2631 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
2634 block_w[dof_type].build(this->block_distribution_pt(dof_type), 0.0);
2641 if (Setup_preconditioner_before_solve)
2647 preconditioner_pt()->setup(
dynamic_cast<MATRIX*
>(matrix_pt));
2651 Preconditioner_setup_time = t_end_prec - t_start_prec;
2657 oomph_info <<
"Time for setup of preconditioner [sec]: "
2658 << Preconditioner_setup_time << std::endl;
2668 oomph_info <<
"Setup of preconditioner is bypassed in resolve mode"
2678 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
2681 block_r[dof_type].build(this->block_distribution_pt(dof_type), 0.0);
2686 if (Preconditioner_LHS)
2692 this->return_block_vectors(block_r, r);
2695 preconditioner_pt()->preconditioner_solve(rhs, r);
2698 this->get_block_vectors(r, block_r);
2703 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
2706 block_r[dof_type] = block_rhs[dof_type];
2711 double norm_r = block_r[0].norm();
2714 double norm_c = block_r[1].norm();
2717 double normb = sqrt(pow(norm_r, 2.0) + pow(norm_c, 2.0));
2720 double beta = normb;
2735 resid = beta / normb;
2739 if (Doc_convergence_history)
2742 if (!Output_file_stream.is_open())
2745 oomph_info << 0 <<
" " << resid << std::endl;
2751 Output_file_stream << 0 <<
" " << resid << std::endl;
2756 if (resid <= Tolerance)
2762 oomph_info <<
"GMRES converged immediately. Normalised residual norm: "
2763 << resid << std::endl;
2774 block_v.resize(n_row + 1);
2778 for (
unsigned dof_type = 0; dof_type < n_row + 1; dof_type++)
2781 block_v[dof_type].resize(n_dof_types);
2792 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
2796 block_v[0][dof_type].build(this->block_distribution_pt(dof_type), 0.0);
2800 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
2803 double* v0_pt = block_v[0][dof_type].values_pt();
2806 const double* block_r_pt = block_r[dof_type].values_pt();
2809 for (
unsigned i = 0;
i < n_row;
i++)
2812 v0_pt[
i] = block_r_pt[
i] / beta;
2822 for (
unsigned j = 0; j <
Max_iter; j++)
2825 hessenberg[j].resize(j + 2, std::complex<double>(0.0, 0.0));
2842 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
2844 block_temp[dof_type].build(this->block_distribution_pt(dof_type),
2849 if (Preconditioner_LHS)
2853 complex_matrix_multiplication(
2854 Matrices_storage_pt, block_v[j], block_temp);
2857 this->return_block_vectors(block_temp, temp);
2860 this->return_block_vectors(block_w, w);
2863 this->preconditioner_pt()->preconditioner_solve(temp, w);
2866 this->get_block_vectors(w, block_w);
2872 this->return_block_vectors(block_v[j], vj);
2875 this->preconditioner_pt()->preconditioner_solve(vj, temp);
2878 this->get_block_vectors(temp, block_temp);
2882 complex_matrix_multiplication(
2883 Matrices_storage_pt, block_temp, block_w);
2888 double* block_w_r_pt = block_w[0].values_pt();
2891 double* block_w_c_pt = block_w[1].values_pt();
2897 for (
unsigned i = 0;
i < j + 1;
i++)
2900 const double* vi_r_pt = block_v[
i][0].values_pt();
2903 const double* vi_c_pt = block_v[
i][1].values_pt();
2906 for (
unsigned k = 0; k < n_row; k++)
2909 std::complex<double> complex_v(vi_r_pt[k], vi_c_pt[k]);
2912 std::complex<double> complex_w(block_w_r_pt[k], block_w_c_pt[k]);
2916 hessenberg[j][
i] += std::conj(complex_v) * complex_w;
2921 for (
unsigned k = 0; k < n_row; k++)
2924 block_w_r_pt[k] -= (hessenberg[j][
i].real() * vi_r_pt[k] -
2925 hessenberg[j][
i].imag() * vi_c_pt[k]);
2928 block_w_c_pt[k] -= (hessenberg[j][
i].real() * vi_c_pt[k] +
2929 hessenberg[j][
i].imag() * vi_r_pt[k]);
2934 norm_r = block_w[0].norm();
2937 norm_c = block_w[1].norm();
2941 hessenberg[j][j + 1] = sqrt(pow(norm_r, 2.0) + pow(norm_c, 2.0));
2944 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
2948 block_v[j + 1][dof_type].build(this->block_distribution_pt(dof_type),
2954 if (hessenberg[j][j + 1] != 0.0)
2957 double* v_r_pt = block_v[j + 1][0].values_pt();
2960 double* v_c_pt = block_v[j + 1][1].values_pt();
2963 const double* block_w_r_pt = block_w[0].values_pt();
2966 const double* block_w_c_pt = block_w[1].values_pt();
2973 double h_subdiag_val = hessenberg[j][j + 1].real();
2976 for (
unsigned k = 0; k < n_row; k++)
2979 v_r_pt[k] = block_w_r_pt[k] / h_subdiag_val;
2982 v_c_pt[k] = block_w_c_pt[k] / h_subdiag_val;
2994 oomph_info <<
"Subdiagonal Hessenberg entry is zero. "
2995 <<
"Do something here..." << std::endl;
3000 for (
unsigned k = 0; k < j; k++)
3004 apply_plane_rotation(
3005 hessenberg[j][k], hessenberg[j][k + 1], cs[k], sn[k]);
3009 generate_plane_rotation(
3010 hessenberg[j][j], hessenberg[j][j + 1], cs[j], sn[j]);
3013 apply_plane_rotation(
3014 hessenberg[j][j], hessenberg[j][j + 1], cs[j], sn[j]);
3018 apply_plane_rotation(
s[j],
s[j + 1], cs[j], sn[j]);
3023 beta = std::abs(
s[j + 1]);
3026 resid = beta / normb;
3030 if (Doc_convergence_history)
3033 if (!Output_file_stream.is_open())
3036 oomph_info << j + 1 <<
" " << resid << std::endl;
3042 Output_file_stream << j + 1 <<
" " << resid << std::endl;
3047 if (resid < Tolerance)
3054 update(j, hessenberg,
s, block_v, block_solution);
3057 this->return_block_vectors(block_solution, solution);
3063 oomph_info <<
"\nGMRES converged (1). Normalised residual norm: "
3064 << resid << std::endl;
3067 oomph_info <<
"Number of iterations to convergence: " << j + 1 <<
"\n"
3075 Solution_time = t_end - t_start;
3081 oomph_info <<
"Time for solve with GMRES [sec]: " << Solution_time
3099 update(
Max_iter - 1, hessenberg,
s, block_v, block_solution);
3102 this->return_block_vectors(block_solution, solution);
3111 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
3114 block_temp[dof_type].build(this->block_distribution_pt(dof_type), 0.0);
3118 complex_matrix_multiplication(
3119 Matrices_storage_pt, block_solution, block_temp);
3122 double* block_temp_r_pt = block_temp[0].values_pt();
3125 double* block_temp_c_pt = block_temp[1].values_pt();
3128 const double* block_rhs_r_pt = block_rhs[0].values_pt();
3131 const double* block_rhs_c_pt = block_rhs[1].values_pt();
3134 for (
unsigned i = 0;
i < n_row;
i++)
3137 block_temp_r_pt[
i] = block_rhs_r_pt[
i] - block_temp_r_pt[
i];
3140 block_temp_c_pt[
i] = block_rhs_c_pt[
i] - block_temp_c_pt[
i];
3144 if (Preconditioner_LHS)
3153 this->return_block_vectors(block_temp, temp);
3156 this->return_block_vectors(block_r, r);
3159 preconditioner_pt()->preconditioner_solve(temp, r);
3167 norm_r = block_r[0].norm();
3170 norm_c = block_r[1].norm();
3173 beta = sqrt(pow(norm_r, 2.0) + pow(norm_c, 2.0));
3176 resid = beta / normb;
3179 if (resid < Tolerance)
3185 oomph_info <<
"\nGMRES converged (2). Normalised residual norm: "
3187 <<
"\nNumber of iterations to convergence: " << Iterations
3196 Solution_time = t_end - t_start;
3201 oomph_info <<
"Time for solve with GMRES [sec]: " << Solution_time
3208 oomph_info <<
"\nGMRES did not converge to required tolerance! "
3209 <<
"\nReturning with normalised residual norm: " << resid
3210 <<
"\nafter " <<
Max_iter <<
" iterations.\n"
3214 if (Throw_error_after_max_iter)
3216 std::string err =
"Solver failed to converge and you requested an error";
3217 err +=
" on convergence failures.";
3219 err, OOMPH_EXCEPTION_LOCATION, OOMPH_CURRENT_FUNCTION);
3239 template<
typename MATRIX>
3268 std::ostringstream error_message_stream;
3271 error_message_stream <<
"FGMRES cannot use left preconditioning. It is "
3272 <<
"only capable of using right preconditioning."
3277 OOMPH_CURRENT_FUNCTION,
3278 OOMPH_EXCEPTION_LOCATION);
3286 #ifdef OOMPH_HAS_MPI
3292 OomphLibWarning(
"Can't guarantee the MG solver will work in parallel!",
3293 OOMPH_CURRENT_FUNCTION,
3294 OOMPH_EXCEPTION_LOCATION);
3299 unsigned n_dof = problem_pt->
ndof();
3353 bool allow_different_element_types_in_mesh =
true;
3355 0, problem_pt->
mesh_pt(), allow_different_element_types_in_mesh);
3365 if (nblock_types != 2)
3368 std::stringstream tmp;
3369 tmp <<
"There are supposed to be two block types.\nYours has "
3374 tmp.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3400 oomph_info <<
"\nTime for setup of block Jacobian [sec]: "
3447 const Vector<
Vector<std::complex<double>>>& hessenberg,
3448 const Vector<std::complex<double>>&
s,
3471 for (
int i =
int(k);
i >= 0;
i--)
3474 y[
i] /= hessenberg[
i][
i];
3477 for (
int j =
i - 1; j >= 0; j--)
3480 y[j] -= hessenberg[
i][j] * y[
i];
3486 unsigned n_row = x[0].nrow();
3492 for (
unsigned dof_type = 0; dof_type < 2; dof_type++)
3499 double* block_update_r_pt = block_update[0].values_pt();
3502 double* block_update_c_pt = block_update[1].values_pt();
3505 for (
unsigned j = 0; j <= k; j++)
3508 const double* z_mj_r_pt = z_m[j][0].values_pt();
3511 const double* z_mj_c_pt = z_m[j][1].values_pt();
3514 for (
unsigned i = 0;
i < n_row;
i++)
3517 block_update_r_pt[
i] +=
3518 (z_mj_r_pt[
i] * y[j].real()) - (z_mj_c_pt[
i] * y[j].imag());
3521 block_update_c_pt[
i] +=
3522 (z_mj_c_pt[
i] * y[j].real()) + (z_mj_r_pt[
i] * y[j].imag());
3528 for (
unsigned dof_type = 0; dof_type < 2; dof_type++)
3531 x[dof_type] += block_update[dof_type];
3550 template<
typename MATRIX>
3557 unsigned n_dof_types = this->ndof_types();
3561 if (n_dof_types != 2)
3564 std::stringstream error_message_stream;
3567 error_message_stream <<
"This preconditioner only works for problems "
3568 <<
"with 2 dof types\nYours has " << n_dof_types;
3572 OOMPH_CURRENT_FUNCTION,
3573 OOMPH_EXCEPTION_LOCATION);
3580 unsigned n_row = this->Matrices_storage_pt[0]->nrow();
3587 std::ostringstream error_message_stream;
3590 error_message_stream <<
"The maximum number of iterations cannot exceed "
3591 <<
"the number of rows in the problem."
3592 <<
"\nMaximum number of iterations: "
3593 << this->
Max_iter <<
"\nNumber of rows: " << n_row
3598 OOMPH_CURRENT_FUNCTION,
3599 OOMPH_EXCEPTION_LOCATION);
3604 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
3609 this->Matrices_storage_pt[dof_type]) != 0)
3612 this->Matrices_storage_pt[dof_type])
3615 std::ostringstream error_message_stream;
3616 error_message_stream <<
"The matrix must not be distributed.";
3618 OOMPH_CURRENT_FUNCTION,
3619 OOMPH_EXCEPTION_LOCATION);
3626 std::ostringstream error_message_stream;
3627 error_message_stream <<
"The rhs vector distribution must be setup.";
3629 OOMPH_CURRENT_FUNCTION,
3630 OOMPH_EXCEPTION_LOCATION);
3633 if (rhs.
nrow() != 2 * n_row)
3635 std::ostringstream error_message_stream;
3636 error_message_stream <<
"RHS does not have the same dimension as the"
3637 <<
" linear system";
3639 OOMPH_CURRENT_FUNCTION,
3640 OOMPH_EXCEPTION_LOCATION);
3645 std::ostringstream error_message_stream;
3646 error_message_stream <<
"The rhs vector must not be distributed.";
3648 OOMPH_CURRENT_FUNCTION,
3649 OOMPH_EXCEPTION_LOCATION);
3653 if (solution.
built())
3657 std::ostringstream error_message_stream;
3658 error_message_stream <<
"If the result distribution is setup then it "
3659 <<
"must be the same as the rhs distribution";
3661 OOMPH_CURRENT_FUNCTION,
3662 OOMPH_EXCEPTION_LOCATION);
3668 if (!solution.
built())
3689 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
3692 block_solution[dof_type].build(this->block_distribution_pt(dof_type),
3696 block_rhs[dof_type].build(this->block_distribution_pt(dof_type), 0.0);
3700 this->get_block_vectors(solution, block_solution);
3703 this->get_block_vectors(rhs, block_rhs);
3730 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
3733 block_w[dof_type].build(this->block_distribution_pt(dof_type), 0.0);
3737 if (!(this->Resolving))
3740 if (this->Setup_preconditioner_before_solve)
3746 this->preconditioner_pt()->setup(
dynamic_cast<MATRIX*
>(matrix_pt));
3750 this->Preconditioner_setup_time = t_end_prec - t_start_prec;
3756 oomph_info <<
"Time for setup of preconditioner [sec]: "
3757 << this->Preconditioner_setup_time << std::endl;
3767 oomph_info <<
"Setup of preconditioner is bypassed in resolve mode"
3777 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
3780 block_r[dof_type].build(this->block_distribution_pt(dof_type), 0.0);
3784 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
3787 block_r[dof_type] = block_rhs[dof_type];
3791 double norm_r = block_r[0].norm();
3794 double norm_c = block_r[1].norm();
3797 double normb = sqrt(pow(norm_r, 2.0) + pow(norm_c, 2.0));
3800 double beta = normb;
3815 resid = beta / normb;
3819 if (this->Doc_convergence_history)
3822 if (!(this->Output_file_stream.is_open()))
3825 oomph_info << 0 <<
" " << resid << std::endl;
3831 this->Output_file_stream << 0 <<
" " << resid << std::endl;
3836 if (resid <= this->Tolerance)
3842 oomph_info <<
"FGMRES converged immediately. Normalised residual norm: "
3843 << resid << std::endl;
3854 block_v.resize(n_row + 1);
3860 block_z.resize(n_row + 1);
3864 for (
unsigned i = 0;
i < n_row + 1;
i++)
3867 block_v[
i].resize(n_dof_types);
3870 block_z[
i].resize(n_dof_types);
3881 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
3885 block_v[0][dof_type].build(this->block_distribution_pt(dof_type), 0.0);
3889 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
3892 double* block_v0_pt = block_v[0][dof_type].values_pt();
3895 const double* block_r_pt = block_r[dof_type].values_pt();
3898 for (
unsigned i = 0;
i < n_row;
i++)
3901 block_v0_pt[
i] = block_r_pt[
i] / beta;
3911 for (
unsigned j = 0; j < this->
Max_iter; j++)
3914 hessenberg[j].resize(j + 2, std::complex<double>(0.0, 0.0));
3928 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
3931 block_z[j][dof_type].build(this->block_distribution_pt(dof_type),
3936 this->return_block_vectors(block_v[j], vj);
3939 this->preconditioner_pt()->preconditioner_solve(vj, zj);
3942 this->get_block_vectors(zj, block_z[j]);
3946 this->complex_matrix_multiplication(
3947 this->Matrices_storage_pt, block_z[j], block_w);
3951 double* block_w_r_pt = block_w[0].values_pt();
3954 double* block_w_c_pt = block_w[1].values_pt();
3960 for (
unsigned i = 0;
i < j + 1;
i++)
3963 const double* block_vi_r_pt = block_v[
i][0].values_pt();
3966 const double* block_vi_c_pt = block_v[
i][1].values_pt();
3969 for (
unsigned k = 0; k < n_row; k++)
3972 std::complex<double> complex_v(block_vi_r_pt[k], block_vi_c_pt[k]);
3975 std::complex<double> complex_w(block_w_r_pt[k], block_w_c_pt[k]);
3979 hessenberg[j][
i] += std::conj(complex_v) * complex_w;
3984 for (
unsigned k = 0; k < n_row; k++)
3987 block_w_r_pt[k] -= (hessenberg[j][
i].real() * block_vi_r_pt[k] -
3988 hessenberg[j][
i].imag() * block_vi_c_pt[k]);
3991 block_w_c_pt[k] -= (hessenberg[j][
i].real() * block_vi_c_pt[k] +
3992 hessenberg[j][
i].imag() * block_vi_r_pt[k]);
3997 norm_r = block_w[0].norm();
4000 norm_c = block_w[1].norm();
4004 hessenberg[j][j + 1] = sqrt(pow(norm_r, 2.0) + pow(norm_c, 2.0));
4007 for (
unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
4011 block_v[j + 1][dof_type].build(this->block_distribution_pt(dof_type),
4017 if (hessenberg[j][j + 1] != 0.0)
4020 double* block_v_r_pt = block_v[j + 1][0].values_pt();
4023 double* block_v_c_pt = block_v[j + 1][1].values_pt();
4026 const double* block_w_r_pt = block_w[0].values_pt();
4029 const double* block_w_c_pt = block_w[1].values_pt();
4036 double h_subdiag_val = hessenberg[j][j + 1].real();
4039 for (
unsigned k = 0; k < n_row; k++)
4042 block_v_r_pt[k] = block_w_r_pt[k] / h_subdiag_val;
4045 block_v_c_pt[k] = block_w_c_pt[k] / h_subdiag_val;
4057 oomph_info <<
"Subdiagonal Hessenberg entry is zero. "
4058 <<
"Do something here..." << std::endl;
4063 for (
unsigned k = 0; k < j; k++)
4067 this->apply_plane_rotation(
4068 hessenberg[j][k], hessenberg[j][k + 1], cs[k], sn[k]);
4072 this->generate_plane_rotation(
4073 hessenberg[j][j], hessenberg[j][j + 1], cs[j], sn[j]);
4076 this->apply_plane_rotation(
4077 hessenberg[j][j], hessenberg[j][j + 1], cs[j], sn[j]);
4081 this->apply_plane_rotation(
s[j],
s[j + 1], cs[j], sn[j]);
4086 beta = std::abs(
s[j + 1]);
4089 resid = beta / normb;
4093 if (this->Doc_convergence_history)
4096 if (!(this->Output_file_stream.is_open()))
4099 oomph_info << j + 1 <<
" " << resid << std::endl;
4105 this->Output_file_stream << j + 1 <<
" " << resid << std::endl;
4110 if (resid < this->Tolerance)
4113 this->Iterations = j + 1;
4117 update(j, hessenberg,
s, block_z, block_solution);
4120 this->return_block_vectors(block_solution, solution);
4126 oomph_info <<
"\nFGMRES converged (1). Normalised residual norm: "
4127 << resid << std::endl;
4130 oomph_info <<
"Number of iterations to convergence: " << j + 1 <<
"\n"
4138 this->Solution_time = t_end - t_start;
4144 oomph_info <<
"Time for solve with FGMRES [sec]: "
4145 << this->Solution_time << std::endl;
4158 if (this->Max_iter > 0)
4162 update(this->Max_iter - 1, hessenberg,
s, block_z, block_solution);
4165 this->return_block_vectors(block_solution, solution);
4172 norm_r = block_r[0].norm();
4175 norm_c = block_r[1].norm();
4178 beta = sqrt(pow(norm_r, 2.0) + pow(norm_c, 2.0));
4181 resid = beta / normb;
4184 if (resid < this->Tolerance)
4190 oomph_info <<
"\nFGMRES converged (2). Normalised residual norm: "
4191 << resid <<
"\nNumber of iterations to convergence: "
4192 << this->Iterations <<
"\n"
4200 this->Solution_time = t_end - t_start;
4205 oomph_info <<
"Time for solve with FGMRES [sec]: "
4206 << this->Solution_time << std::endl;
4212 oomph_info <<
"\nFGMRES did not converge to required tolerance! "
4213 <<
"\nReturning with normalised residual norm: " << resid
4214 <<
"\nafter " << this->Max_iter <<
" iterations.\n"
4218 if (this->Throw_error_after_max_iter)
4220 std::string err =
"Solver failed to converge and you requested an error";
4221 err +=
" on convergence failures.";
4223 err, OOMPH_EXCEPTION_LOCATION, OOMPH_CURRENT_FUNCTION);
Block Preconditioner base class. The block structure of the overall problem is determined from the Me...
void return_block_vectors(const Vector< unsigned > &block_vec_number, const Vector< DoubleVector > &s, DoubleVector &v) const
Takes the vector of block vectors, s, and copies its entries into the naturally ordered vector,...
void get_block(const unsigned &i, const unsigned &j, MATRIX &output_matrix, const bool &ignore_replacement_block=false) const
Put block (i,j) into output_matrix. This block accounts for any coarsening of dof types and any repla...
unsigned nblock_types() const
Return the number of block types.
MATRIX * matrix_pt() const
Access function to matrix_pt. If this is the master then cast the matrix pointer to MATRIX*,...
void get_block_vectors(const Vector< unsigned > &block_vec_number, const DoubleVector &v, Vector< DoubleVector > &s) const
Takes the naturally ordered vector and rearranges it into a vector of sub vectors corresponding to th...
void set_nmesh(const unsigned &n)
Specify the number of meshes required by this block preconditioner. Note: elements in different meshe...
virtual void block_setup()
Determine the size of the matrix blocks and setup the lookup schemes relating the global degrees of f...
void set_mesh(const unsigned &i, const Mesh *const mesh_pt, const bool &allow_multiple_element_type_in_mesh=false)
Set the i-th mesh for this block preconditioner. Note: The method set_nmesh(...) must be called befor...
A class for compressed row matrices. This is a distributable object.
unsigned long nrow() const
Return the number of rows of the matrix.
Vector< double > diagonal_entries() const
returns a Vector of diagonal entries of this matrix. This only works with square matrices....
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
void complex_solve_helper(const Vector< DoubleVector > &rhs, Vector< DoubleVector > &solution)
This is where the actual work is done.
~ComplexDampedJacobi()
Empty destructor.
void operator=(const ComplexDampedJacobi &)=delete
Broken assignment operator.
unsigned iterations() const
Number of iterations taken.
bool Matrix_can_be_deleted
Boolean flag to indicate if the matrices pointed to by Matrix_real_pt and Matrix_imag_pt can be delet...
void complex_smoother_solve(const Vector< DoubleVector > &rhs, Vector< DoubleVector > &solution)
The smoother_solve function performs fixed number of iterations on the system A*result=rhs....
void complex_smoother_setup(Vector< CRDoubleMatrix * > helmholtz_matrix_pt)
Setup: Pass pointer to the matrix and store in cast form.
void clean_up_memory()
Cleanup data that's stored for resolve (if any has been stored)
Vector< double > Matrix_diagonal_inv_sq
Vector whose j'th entry is given by 1/(A_r(j,j)^2 + A_c(j,j)^2)
ComplexDampedJacobi(const double &omega=0.5)
Constructor (empty)
Vector< double > Matrix_diagonal_real
Vector containing the diagonal entries of A_r (real(A))
Vector< double > Matrix_diagonal_imag
Vector containing the diagonal entries of A_c (imag(A))
CRDoubleMatrix * Matrix_imag_pt
Pointer to the real part of the system matrix.
double & omega()
Get access to the value of Omega (lvalue)
double Omega
Damping factor.
CRDoubleMatrix * Matrix_real_pt
Pointer to the real part of the system matrix.
void solve(Problem *const &problem_pt, DoubleVector &result)
Use damped Jacobi iteration as an IterativeLinearSolver: This obtains the Jacobian matrix J and the r...
ComplexDampedJacobi(const ComplexDampedJacobi &)=delete
Broken copy constructor.
unsigned Iterations
Number of iterations taken.
void calculate_omega(const double &k, const double &h)
Function to calculate the value of Omega by passing in the value of k and h [see Elman et al....
The GMRES method rewritten for complex matrices.
void generate_plane_rotation(std::complex< double > &dx, std::complex< double > &dy, std::complex< double > &cs, std::complex< double > &sn)
Helper function: Generate a plane rotation. This is done by finding the value of (i....
void disable_resolve()
Overload disable resolve so that it cleans up memory too.
unsigned iterations() const
Number of iterations taken.
void complex_smoother_setup(Vector< CRDoubleMatrix * > helmholtz_matrix_pt)
Setup: Pass pointer to the matrix and store in cast form.
unsigned Iterations
Number of iterations taken.
bool Resolving
Boolean flag to indicate if the solve is done in re-solve mode, bypassing setup of matrix and precond...
ComplexGMRES(const ComplexGMRES &)=delete
Broken copy constructor.
void update(const unsigned &k, const Vector< Vector< std::complex< double >>> &hessenberg, const Vector< std::complex< double >> &s, const Vector< Vector< DoubleVector >> &v, Vector< DoubleVector > &x)
Helper function to update the result vector.
void solve(Problem *const &problem_pt, DoubleVector &result)
Solver: Takes pointer to problem and returns the results vector which contains the solution of the li...
void complex_solve_helper(const Vector< DoubleVector > &rhs, Vector< DoubleVector > &solution)
This is where the actual work is done.
void complex_smoother_solve(const Vector< DoubleVector > &rhs, Vector< DoubleVector > &solution)
The smoother_solve function performs fixed number of iterations on the system A*result=rhs....
void apply_plane_rotation(std::complex< double > &dx, std::complex< double > &dy, std::complex< double > &cs, std::complex< double > &sn)
Helper function: Apply plane rotation. This is done using the update:
ComplexGMRES()
Constructor.
void clean_up_memory()
Cleanup data that's stored for resolve (if any has been stored)
void solve(DoubleMatrixBase *const &matrix_pt, const Vector< double > &rhs, Vector< double > &result)
Linear-algebra-type solver: Takes pointer to a matrix and rhs vector and returns the solution of the ...
bool Matrix_can_be_deleted
Boolean flag to indicate if the real and imaginary system matrices can be deleted.
void operator=(const ComplexGMRES &)=delete
Broken assignment operator.
~ComplexGMRES()
Empty destructor.
Vector< CRDoubleMatrix * > Matrices_storage_pt
Vector of pointers to the real and imaginary part of the system matrix.
Base class for any linear algebra object that is distributable. Just contains storage for the LinearA...
bool distributed() const
distribution is serial or distributed
LinearAlgebraDistribution * distribution_pt() const
access to the LinearAlgebraDistribution
unsigned nrow() const
access function to the number of global rows.
void build_distribution(const LinearAlgebraDistribution *const dist_pt)
setup the distribution of this distributable linear algebra object
Abstract base class for matrices of doubles – adds abstract interfaces for solving,...
A vector in the mathematical sense, initially developed for linear algebra type applications....
void initialise(const double &v)
initialise the whole vector with value v
void build(const DoubleVector &old_vector)
Just copys the argument DoubleVector.
void redistribute(const LinearAlgebraDistribution *const &dist_pt)
The contents of the vector are redistributed to match the new distribution. In a non-MPI rebuild this...
double norm() const
compute the 2 norm of this vector
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
void update(const unsigned &k, const Vector< Vector< std::complex< double >>> &hessenberg, const Vector< std::complex< double >> &s, const Vector< Vector< DoubleVector >> &z_m, Vector< DoubleVector > &x)
Helper function to update the result vector.
HelmholtzFGMRESMG(const HelmholtzFGMRESMG &)=delete
Broken copy constructor.
void solve(Problem *const &problem_pt, DoubleVector &result)
Solver: Takes pointer to problem and returns the results vector which contains the solution of the li...
virtual ~HelmholtzFGMRESMG()
Destructor (cleanup storage)
void solve_helper(DoubleMatrixBase *const &matrix_pt, const DoubleVector &rhs, DoubleVector &solution)
General interface to solve function.
HelmholtzFGMRESMG()
Constructor (empty)
void operator=(const HelmholtzFGMRESMG &)=delete
Broken assignment operator.
void set_preconditioner_LHS()
Overloaded function to let the user know that left preconditioning is not possible with FGMRES,...
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
void solve(DoubleMatrixBase *const &matrix_pt, const Vector< double > &rhs, Vector< double > &result)
Linear-algebra-type solver: Takes pointer to a matrix and rhs vector and returns the solution of the ...
bool Matrix_can_be_deleted
Boolean flag to indicate if the matrix pointed to be Matrix_pt can be deleted.
void solve_helper(DoubleMatrixBase *const &matrix_pt, const DoubleVector &rhs, DoubleVector &solution)
General interface to solve function.
void solve(DoubleMatrixBase *const &matrix_pt, const DoubleVector &rhs, DoubleVector &solution)
Linear-algebra-type solver: Takes pointer to a matrix and rhs vector and returns the solution of the ...
void solve(Problem *const &problem_pt, DoubleVector &result)
Solver: Takes pointer to problem and returns the results vector which contains the solution of the li...
Vector< CRDoubleMatrix * > Matrices_storage_pt
Vector of pointers to the real and imaginary part of the system matrix.
void preconditioner_solve(const DoubleVector &r, DoubleVector &z)
Implementation of the pure virtual base class function. The function has been broken because this is ...
void apply_plane_rotation(std::complex< double > &dx, std::complex< double > &dy, std::complex< double > &cs, std::complex< double > &sn)
Helper function: Apply plane rotation. This is done using the update:
HelmholtzGMRESMG(const HelmholtzGMRESMG &)=delete
Broken copy constructor.
void update(const unsigned &k, const Vector< Vector< std::complex< double >>> &hessenberg, const Vector< std::complex< double >> &s, const Vector< Vector< DoubleVector >> &v, Vector< DoubleVector > &x)
Helper function to update the result vector.
bool Preconditioner_LHS
boolean indicating use of left hand preconditioning (if true) or right hand preconditioning (if false...
void clean_up_memory()
Cleanup data that's stored for resolve (if any has been stored)
void disable_resolve()
Overload disable resolve so that it cleans up memory too.
void set_preconditioner_LHS()
Set left preconditioning (the default)
virtual ~HelmholtzGMRESMG()
Destructor (cleanup storage)
void complex_matrix_multiplication(Vector< CRDoubleMatrix * > const matrices_pt, const Vector< DoubleVector > &x, Vector< DoubleVector > &soln)
Helper function to calculate a complex matrix-vector product. Assumes the matrix has been provided as...
void set_preconditioner_RHS()
Enable right preconditioning.
HelmholtzGMRESMG()
Constructor.
bool Resolving
Boolean flag to indicate if the solve is done in re-solve mode, bypassing setup of matrix and precond...
unsigned iterations() const
Number of iterations taken.
void resolve(const DoubleVector &rhs, DoubleVector &result)
Re-solve the system defined by the last assembled Jacobian and the rhs vector specified here....
unsigned Iterations
Number of iterations taken.
void setup()
Implementation of the pure virtual base class function. This accompanies the preconditioner_solve fun...
void operator=(const HelmholtzGMRESMG &)=delete
Broken assignment operator.
void generate_plane_rotation(std::complex< double > &dx, std::complex< double > &dy, std::complex< double > &cs, std::complex< double > &sn)
Helper function: Generate a plane rotation. This is done by finding the value of (i....
Helmholtz smoother class: The smoother class is designed for the Helmholtz equation to be used in con...
void check_validity_of_solve_helper_inputs(CRDoubleMatrix *const &real_matrix_pt, CRDoubleMatrix *const &imag_matrix_pt, const Vector< DoubleVector > &rhs, Vector< DoubleVector > &solution, const double &n_dof)
Self-test to check that all the dimensions of the inputs to solve helper are consistent and everythin...
bool Use_as_smoother
When a derived class object is being used as a smoother in the MG algorithm the residual norm does no...
virtual void complex_smoother_setup(Vector< CRDoubleMatrix * > matrix_pt)=0
Setup the smoother for the matrix specified by the pointer.
HelmholtzSmoother()
Empty constructor.
virtual void complex_smoother_solve(const Vector< DoubleVector > &rhs, Vector< DoubleVector > &result)=0
The smoother_solve function performs fixed number of iterations on the system A*result=rhs....
virtual ~HelmholtzSmoother()
Virtual empty destructor.
void complex_matrix_multiplication(Vector< CRDoubleMatrix * > matrices_pt, const Vector< DoubleVector > &x, Vector< DoubleVector > &soln)
Helper function to calculate a complex matrix-vector product. Assumes the matrix has been provided as...
Base class for all linear iterative solvers. This merely defines standard interfaces for linear itera...
Preconditioner *& preconditioner_pt()
Access function to preconditioner.
double Jacobian_setup_time
Jacobian setup time.
double & tolerance()
Access to convergence tolerance.
Describes the distribution of a distributable linear algebra type object. Typically this is a contain...
bool distributed() const
access function to the distributed - indicates whether the distribution is serial or distributed
virtual void solve(Problem *const &problem_pt, DoubleVector &result)=0
Solver: Takes pointer to problem and returns the results vector which contains the solution of the li...
bool Doc_time
Boolean flag that indicates whether the time taken.
bool Enable_resolve
Boolean that indicates whether the matrix (or its factors, in the case of direct solver) should be st...
virtual void disable_resolve()
Disable resolve (i.e. store matrix and/or LU decomposition, say) This function simply resets an inter...
static OomphCommunicator * communicator_pt()
access to global communicator. This is the oomph-lib equivalent of MPI_COMM_WORLD
An OomphLibError object which should be thrown when an run-time error is encountered....
An OomphLibWarning object which should be created as a temporary object to issue a warning....
virtual void set_comm_pt(const OomphCommunicator *const comm_pt)
Set the communicator pointer.
virtual void preconditioner_solve(const DoubleVector &r, DoubleVector &z)=0
Apply the preconditioner. Pure virtual generic interface function. This method should apply the preco...
virtual void set_matrix_pt(DoubleMatrixBase *matrix_pt)
Set the matrix pointer.
////////////////////////////////////////////////////////////////// //////////////////////////////////...
unsigned long ndof() const
Return the number of dofs.
virtual void get_jacobian(DoubleVector &residuals, DenseDoubleMatrix &jacobian)
Return the fully-assembled Jacobian and residuals for the problem Interface for the case when the Jac...
OomphCommunicator * communicator_pt()
access function to the oomph-lib communicator
Mesh *& mesh_pt()
Return a pointer to the global mesh.
A slight extension to the standard template vector class so that we can include "graceful" array rang...
void initialise(const _Tp &__value)
Iterate over all values and set to the desired value.
unsigned Max_iter
Max. # of Newton iterations.
void broken_assign(const std::string &class_name)
Issue error message and terminate execution.
std::string string(const unsigned &i)
Return the i-th string or "" if the relevant string hasn't been defined.
const double Pi
50 digits from maple
double timer()
returns the time in seconds after some point in past
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
OomphInfo oomph_info
Single (global) instantiation of the OomphInfo object – this is used throughout the library as a "rep...