void CSysMatrix::MatrixVectorProduct(const CSysVector & vec, CSysVector & prod, CGeometry *geometry, CConfig *config) { unsigned long prod_begin, vec_begin, mat_begin, index, iVar, jVar, row_i; /*--- Some checks for consistency between CSysMatrix and the CSysVectors ---*/ if ( (nVar != vec.GetNVar()) || (nVar != prod.GetNVar()) ) { cerr << "CSysMatrix::MatrixVectorProduct(const CSysVector&, CSysVector): " << "nVar values incompatible." << endl; throw(-1); } if ( (nPoint != vec.GetNBlk()) || (nPoint != prod.GetNBlk()) ) { cerr << "CSysMatrix::MatrixVectorProduct(const CSysVector&, CSysVector): " << "nPoint and nBlk values incompatible." << endl; throw(-1); } prod = 0.0; // set all entries of prod to zero for (row_i = 0; row_i < nPointDomain; row_i++) { prod_begin = row_i*nVar; // offset to beginning of block row_i for (index = row_ptr[row_i]; index < row_ptr[row_i+1]; index++) { vec_begin = col_ind[index]*nVar; // offset to beginning of block col_ind[index] mat_begin = (index*nVar*nVar); // offset to beginning of matrix block[row_i][col_ind[indx]] for (iVar = 0; iVar < nVar; iVar++) { for (jVar = 0; jVar < nVar; jVar++) { prod[(const unsigned int)(prod_begin+iVar)] += matrix[(const unsigned int)(mat_begin+iVar*nVar+jVar)]*vec[(const unsigned int)(vec_begin+jVar)]; } } } } /*--- MPI Parallelization ---*/ SendReceive_Solution(prod, geometry, config); }
void CSysSolve::SetExternalSolve(CSysMatrix & Jacobian, CSysVector & LinSysRes, CSysVector & LinSysSol, CGeometry *geometry, CConfig *config){ #ifdef CODI_REVERSE_TYPE unsigned long size = LinSysRes.GetLocSize(); unsigned long i, nBlk = LinSysRes.GetNBlk(), nVar = LinSysRes.GetNVar(), nBlkDomain = LinSysRes.GetNBlkDomain(); /*--- Arrays to store the indices of the input/output of the linear solver. * Note: They will be deleted in the CSysSolve_b::Delete_b routine. ---*/ int *LinSysRes_Indices = new int[size]; int *LinSysSol_Indices = new int[size]; for (i = 0; i < size; i++){ /*--- Register the solution of the linear system (could already be registered when using multigrid) ---*/ if (LinSysSol[i].getGradientData() == 0){ AD::globalTape.registerInput(LinSysSol[i]); } /*--- Store the indices ---*/ LinSysRes_Indices[i] = LinSysRes[i].getGradientData(); LinSysSol_Indices[i] = LinSysSol[i].getGradientData(); } /*--- Push the data to the checkpoint handler for access in the reverse sweep ---*/ AD::CheckpointHandler* dataHandler = new AD::CheckpointHandler; dataHandler->addData(LinSysRes_Indices); dataHandler->addData(LinSysSol_Indices); dataHandler->addData(size); dataHandler->addData(nBlk); dataHandler->addData(nVar); dataHandler->addData(nBlkDomain); dataHandler->addData(&Jacobian); dataHandler->addData(geometry); dataHandler->addData(config); /*--- Build preconditioner for the transposed Jacobian ---*/ switch(config->GetKind_DiscAdj_Linear_Prec()){ case ILU: Jacobian.BuildILUPreconditioner(true); break; case JACOBI: Jacobian.BuildJacobiPreconditioner(true); break; default: cout << "The specified preconditioner is not yet implemented for the discrete adjoint method." << endl; exit(EXIT_FAILURE); } /*--- Push the external function to the AD tape ---*/ AD::globalTape.pushExternalFunction(&CSysSolve_b::Solve_b, dataHandler, &CSysSolve_b::Delete_b); #endif }
unsigned long CSysSolve::Solve(CSysMatrix & Jacobian, CSysVector & LinSysRes, CSysVector & LinSysSol, CGeometry *geometry, CConfig *config) { su2double SolverTol = config->GetLinear_Solver_Error(), Residual; unsigned long MaxIter = config->GetLinear_Solver_Iter(); unsigned long IterLinSol = 0; CMatrixVectorProduct *mat_vec; bool TapeActive = NO; if (config->GetDiscrete_Adjoint()){ #ifdef CODI_REVERSE_TYPE /*--- Check whether the tape is active, i.e. if it is recording and store the status ---*/ TapeActive = AD::globalTape.isActive(); /*--- Stop the recording for the linear solver ---*/ AD::StopRecording(); #endif } /*--- Solve the linear system using a Krylov subspace method ---*/ if (config->GetKind_Linear_Solver() == BCGSTAB || config->GetKind_Linear_Solver() == FGMRES || config->GetKind_Linear_Solver() == RESTARTED_FGMRES) { mat_vec = new CSysMatrixVectorProduct(Jacobian, geometry, config); CPreconditioner* precond = NULL; switch (config->GetKind_Linear_Solver_Prec()) { case JACOBI: Jacobian.BuildJacobiPreconditioner(); precond = new CJacobiPreconditioner(Jacobian, geometry, config); break; case ILU: Jacobian.BuildILUPreconditioner(); precond = new CILUPreconditioner(Jacobian, geometry, config); break; case LU_SGS: precond = new CLU_SGSPreconditioner(Jacobian, geometry, config); break; case LINELET: Jacobian.BuildJacobiPreconditioner(); precond = new CLineletPreconditioner(Jacobian, geometry, config); break; default: Jacobian.BuildJacobiPreconditioner(); precond = new CJacobiPreconditioner(Jacobian, geometry, config); break; } switch (config->GetKind_Linear_Solver()) { case BCGSTAB: IterLinSol = BCGSTAB_LinSolver(LinSysRes, LinSysSol, *mat_vec, *precond, SolverTol, MaxIter, &Residual, false); break; case FGMRES: IterLinSol = FGMRES_LinSolver(LinSysRes, LinSysSol, *mat_vec, *precond, SolverTol, MaxIter, &Residual, false); break; case RESTARTED_FGMRES: IterLinSol = 0; while (IterLinSol < config->GetLinear_Solver_Iter()) { if (IterLinSol + config->GetLinear_Solver_Restart_Frequency() > config->GetLinear_Solver_Iter()) MaxIter = config->GetLinear_Solver_Iter() - IterLinSol; IterLinSol += FGMRES_LinSolver(LinSysRes, LinSysSol, *mat_vec, *precond, SolverTol, MaxIter, &Residual, false); if (LinSysRes.norm() < SolverTol) break; SolverTol = SolverTol*(1.0/LinSysRes.norm()); } break; } /*--- Dealocate memory of the Krylov subspace method ---*/ delete mat_vec; delete precond; } /*--- Smooth the linear system. ---*/ else { switch (config->GetKind_Linear_Solver()) { case SMOOTHER_LUSGS: mat_vec = new CSysMatrixVectorProduct(Jacobian, geometry, config); IterLinSol = Jacobian.LU_SGS_Smoother(LinSysRes, LinSysSol, *mat_vec, SolverTol, MaxIter, &Residual, false, geometry, config); delete mat_vec; break; case SMOOTHER_JACOBI: mat_vec = new CSysMatrixVectorProduct(Jacobian, geometry, config); Jacobian.BuildJacobiPreconditioner(); IterLinSol = Jacobian.Jacobi_Smoother(LinSysRes, LinSysSol, *mat_vec, SolverTol, MaxIter, &Residual, false, geometry, config); delete mat_vec; break; case SMOOTHER_ILU: mat_vec = new CSysMatrixVectorProduct(Jacobian, geometry, config); Jacobian.BuildILUPreconditioner(); IterLinSol = Jacobian.ILU0_Smoother(LinSysRes, LinSysSol, *mat_vec, SolverTol, MaxIter, &Residual, false, geometry, config); delete mat_vec; break; case SMOOTHER_LINELET: Jacobian.BuildJacobiPreconditioner(); Jacobian.ComputeLineletPreconditioner(LinSysRes, LinSysSol, geometry, config); IterLinSol = 1; break; } } if(TapeActive){ /*--- Prepare the externally differentiated linear solver ---*/ SetExternalSolve(Jacobian, LinSysRes, LinSysSol, geometry, config); /*--- Start recording if it was stopped for the linear solver ---*/ AD::StartRecording(); } return IterLinSol; }
unsigned long CSysSolve::BCGSTAB_LinSolver(const CSysVector & b, CSysVector & x, CMatrixVectorProduct & mat_vec, CPreconditioner & precond, su2double tol, unsigned long m, su2double *residual, bool monitoring) { int rank = 0; #ifdef HAVE_MPI MPI_Comm_rank(MPI_COMM_WORLD, &rank); #endif /*--- Check the subspace size ---*/ if (m < 1) { if (rank == MASTER_NODE) cerr << "CSysSolve::BCGSTAB: illegal value for subspace size, m = " << m << endl; #ifndef HAVE_MPI exit(EXIT_FAILURE); #else MPI_Abort(MPI_COMM_WORLD,1); MPI_Finalize(); #endif } CSysVector r(b); CSysVector r_0(b); CSysVector p(b); CSysVector v(b); CSysVector s(b); CSysVector t(b); CSysVector phat(b); CSysVector shat(b); CSysVector A_x(b); /*--- Calculate the initial residual, compute norm, and check if system is already solved ---*/ mat_vec(x, A_x); r -= A_x; r_0 = r; // recall, r holds b initially su2double norm_r = r.norm(); su2double norm0 = b.norm(); if ( (norm_r < tol*norm0) || (norm_r < eps) ) { if (rank == MASTER_NODE) cout << "CSysSolve::BCGSTAB(): system solved by initial guess." << endl; return 0; } /*--- Initialization ---*/ su2double alpha = 1.0, beta = 1.0, omega = 1.0, rho = 1.0, rho_prime = 1.0; /*--- Set the norm to the initial initial residual value ---*/ norm0 = norm_r; /*--- Output header information including initial residual ---*/ int i = 0; if ((monitoring) && (rank == MASTER_NODE)) { WriteHeader("BCGSTAB", tol, norm_r); WriteHistory(i, norm_r, norm0); } /*--- Loop over all search directions ---*/ for (i = 0; i < (int)m; i++) { /*--- Compute rho_prime ---*/ rho_prime = rho; /*--- Compute rho_i ---*/ rho = dotProd(r, r_0); /*--- Compute beta ---*/ beta = (rho / rho_prime) * (alpha /omega); /*--- p_{i} = r_{i-1} + beta * p_{i-1} - beta * omega * v_{i-1} ---*/ su2double beta_omega = -beta*omega; p.Equals_AX_Plus_BY(beta, p, beta_omega, v); p.Plus_AX(1.0, r); /*--- Preconditioning step ---*/ precond(p, phat); mat_vec(phat, v); /*--- Calculate step-length alpha ---*/ su2double r_0_v = dotProd(r_0, v); alpha = rho / r_0_v; /*--- s_{i} = r_{i-1} - alpha * v_{i} ---*/ s.Equals_AX_Plus_BY(1.0, r, -alpha, v); /*--- Preconditioning step ---*/ precond(s, shat); mat_vec(shat, t); /*--- Calculate step-length omega ---*/ omega = dotProd(t, s) / dotProd(t, t); /*--- Update solution and residual: ---*/ x.Plus_AX(alpha, phat); x.Plus_AX(omega, shat); r.Equals_AX_Plus_BY(1.0, s, -omega, t); /*--- Check if solution has converged, else output the relative residual if necessary ---*/ norm_r = r.norm(); if (norm_r < tol*norm0) break; if (((monitoring) && (rank == MASTER_NODE)) && ((i+1) % 50 == 0) && (rank == MASTER_NODE)) WriteHistory(i+1, norm_r, norm0); } if ((monitoring) && (rank == MASTER_NODE)) { cout << "# BCGSTAB final (true) residual:" << endl; cout << "# Iteration = " << i << ": |res|/|res0| = " << norm_r/norm0 << ".\n" << endl; } // /*--- Recalculate final residual (this should be optional) ---*/ // mat_vec(x, A_x); // r = b; r -= A_x; // su2double true_res = r.norm(); // // if ((fabs(true_res - norm_r) > tol*10.0) && (rank == MASTER_NODE)) { // cout << "# WARNING in CSysSolve::BCGSTAB(): " << endl; // cout << "# true residual norm and calculated residual norm do not agree." << endl; // cout << "# true_res - calc_res = " << true_res <<" "<< norm_r << endl; // } (*residual) = norm_r; return i; }
unsigned long CSysSolve::FGMRES_LinSolver(const CSysVector & b, CSysVector & x, CMatrixVectorProduct & mat_vec, CPreconditioner & precond, su2double tol, unsigned long m, su2double *residual, bool monitoring) { int rank = 0; #ifdef HAVE_MPI MPI_Comm_rank(MPI_COMM_WORLD, &rank); #endif /*--- Check the subspace size ---*/ if (m < 1) { if (rank == MASTER_NODE) cerr << "CSysSolve::FGMRES: illegal value for subspace size, m = " << m << endl; #ifndef HAVE_MPI exit(EXIT_FAILURE); #else MPI_Abort(MPI_COMM_WORLD,1); MPI_Finalize(); #endif } /*--- Check the subspace size ---*/ if (m > 1000) { if (rank == MASTER_NODE) cerr << "CSysSolve::FGMRES: illegal value for subspace size (too high), m = " << m << endl; #ifndef HAVE_MPI exit(EXIT_FAILURE); #else MPI_Abort(MPI_COMM_WORLD,1); MPI_Finalize(); #endif } /*--- Define various arrays Note: elements in w and z are initialized to x to avoid creating a temporary CSysVector object for the copy constructor ---*/ vector<CSysVector> w(m+1, x); vector<CSysVector> z(m+1, x); vector<su2double> g(m+1, 0.0); vector<su2double> sn(m+1, 0.0); vector<su2double> cs(m+1, 0.0); vector<su2double> y(m, 0.0); vector<vector<su2double> > H(m+1, vector<su2double>(m, 0.0)); /*--- Calculate the norm of the rhs vector ---*/ su2double norm0 = b.norm(); /*--- Calculate the initial residual (actually the negative residual) and compute its norm ---*/ mat_vec(x, w[0]); w[0] -= b; su2double beta = w[0].norm(); if ( (beta < tol*norm0) || (beta < eps) ) { /*--- System is already solved ---*/ if (rank == MASTER_NODE) cout << "CSysSolve::FGMRES(): system solved by initial guess." << endl; return 0; } /*--- Normalize residual to get w_{0} (the negative sign is because w[0] holds the negative residual, as mentioned above) ---*/ w[0] /= -beta; /*--- Initialize the RHS of the reduced system ---*/ g[0] = beta; /*--- Set the norm to the initial residual value ---*/ norm0 = beta; /*--- Output header information including initial residual ---*/ int i = 0; if ((monitoring) && (rank == MASTER_NODE)) { WriteHeader("FGMRES", tol, beta); WriteHistory(i, beta, norm0); } /*--- Loop over all search directions ---*/ for (i = 0; i < (int)m; i++) { /*--- Check if solution has converged ---*/ if (beta < tol*norm0) break; /*--- Precondition the CSysVector w[i] and store result in z[i] ---*/ precond(w[i], z[i]); /*--- Add to Krylov subspace ---*/ mat_vec(z[i], w[i+1]); /*--- Modified Gram-Schmidt orthogonalization ---*/ ModGramSchmidt(i, H, w); /*--- Apply old Givens rotations to new column of the Hessenberg matrix then generate the new Givens rotation matrix and apply it to the last two elements of H[:][i] and g ---*/ for (int k = 0; k < i; k++) ApplyGivens(sn[k], cs[k], H[k][i], H[k+1][i]); GenerateGivens(H[i][i], H[i+1][i], sn[i], cs[i]); ApplyGivens(sn[i], cs[i], g[i], g[i+1]); /*--- Set L2 norm of residual and check if solution has converged ---*/ beta = fabs(g[i+1]); /*--- Output the relative residual if necessary ---*/ if ((((monitoring) && (rank == MASTER_NODE)) && ((i+1) % 50 == 0)) && (rank == MASTER_NODE)) WriteHistory(i+1, beta, norm0); } /*--- Solve the least-squares system and update solution ---*/ SolveReduced(i, H, g, y); for (int k = 0; k < i; k++) { x.Plus_AX(y[k], z[k]); } if ((monitoring) && (rank == MASTER_NODE)) { cout << "# FGMRES final (true) residual:" << endl; cout << "# Iteration = " << i << ": |res|/|res0| = " << beta/norm0 << ".\n" << endl; } // /*--- Recalculate final (neg.) residual (this should be optional) ---*/ // mat_vec(x, w[0]); // w[0] -= b; // su2double res = w[0].norm(); // // if (fabs(res - beta) > tol*10) { // if (rank == MASTER_NODE) { // cout << "# WARNING in CSysSolve::FGMRES(): " << endl; // cout << "# true residual norm and calculated residual norm do not agree." << endl; // cout << "# res - beta = " << res - beta << endl; // } // } (*residual) = beta; return i; }
unsigned long CSysSolve::CG_LinSolver(const CSysVector & b, CSysVector & x, CMatrixVectorProduct & mat_vec, CPreconditioner & precond, su2double tol, unsigned long m, bool monitoring) { int rank = 0; #ifdef HAVE_MPI MPI_Comm_rank(MPI_COMM_WORLD, &rank); #endif /*--- Check the subspace size ---*/ if (m < 1) { if (rank == MASTER_NODE) cerr << "CSysSolve::ConjugateGradient: illegal value for subspace size, m = " << m << endl; #ifndef HAVE_MPI exit(EXIT_FAILURE); #else MPI_Abort(MPI_COMM_WORLD,1); MPI_Finalize(); #endif } CSysVector r(b); CSysVector A_p(b); /*--- Calculate the initial residual, compute norm, and check if system is already solved ---*/ mat_vec(x, A_p); r -= A_p; // recall, r holds b initially su2double norm_r = r.norm(); su2double norm0 = b.norm(); if ( (norm_r < tol*norm0) || (norm_r < eps) ) { if (rank == MASTER_NODE) cout << "CSysSolve::ConjugateGradient(): system solved by initial guess." << endl; return 0; } su2double alpha, beta, r_dot_z; CSysVector z(r); precond(r, z); CSysVector p(z); /*--- Set the norm to the initial initial residual value ---*/ norm0 = norm_r; /*--- Output header information including initial residual ---*/ int i = 0; if ((monitoring) && (rank == MASTER_NODE)) { WriteHeader("CG", tol, norm_r); WriteHistory(i, norm_r, norm0); } /*--- Loop over all search directions ---*/ for (i = 0; i < (int)m; i++) { /*--- Apply matrix to p to build Krylov subspace ---*/ mat_vec(p, A_p); /*--- Calculate step-length alpha ---*/ r_dot_z = dotProd(r, z); alpha = dotProd(A_p, p); alpha = r_dot_z / alpha; /*--- Update solution and residual: ---*/ x.Plus_AX(alpha, p); r.Plus_AX(-alpha, A_p); /*--- Check if solution has converged, else output the relative residual if necessary ---*/ norm_r = r.norm(); if (norm_r < tol*norm0) break; if (((monitoring) && (rank == MASTER_NODE)) && ((i+1) % 5 == 0)) WriteHistory(i+1, norm_r, norm0); precond(r, z); /*--- Calculate Gram-Schmidt coefficient beta, beta = dotProd(r_{i+1}, z_{i+1}) / dotProd(r_{i}, z_{i}) ---*/ beta = 1.0 / r_dot_z; r_dot_z = dotProd(r, z); beta *= r_dot_z; /*--- Gram-Schmidt orthogonalization; p = beta *p + z ---*/ p.Equals_AX_Plus_BY(beta, p, 1.0, z); } if ((monitoring) && (rank == MASTER_NODE)) { cout << "# Conjugate Gradient final (true) residual:" << endl; cout << "# Iteration = " << i << ": |res|/|res0| = " << norm_r/norm0 << ".\n" << endl; } // /*--- Recalculate final residual (this should be optional) ---*/ // mat_vec(x, A_p); // r = b; // r -= A_p; // su2double true_res = r.norm(); // // if (fabs(true_res - norm_r) > tol*10.0) { // if (rank == MASTER_NODE) { // cout << "# WARNING in CSysSolve::ConjugateGradient(): " << endl; // cout << "# true residual norm and calculated residual norm do not agree." << endl; // cout << "# true_res - calc_res = " << true_res - norm_r << endl; // } // } return i; }