Skip to content

Commit 3dba507

Browse files
pcarruscageorbay-metuEzgi Orbay Akcengizbigfooted
authored
Cleanup nested preconditioner implementation (#2659)
* Add nested preconditioner: FGMRES with BiCGSTAB (#2566) * Add nested preconditioner: FGMRES with BiCGSTAB * Fix style: replace tab with spaces in CConfig.cpp * Update CConfig.cpp * Add files via upload * Nested preconditioner test cases * Remove nested .git from TestCases/TestCases and add nested preconditioner test scripts * Apply clang-format style to CSysSolve nested solver * Add nested FGMRES inner BiCGSTAB options and logging * Add nested FGMRES inner BiCGSTAB test case * Fix formatting for nested FGMRES changes * Remove duplicated TestCases/TestCases directory * Add nested FGMRES with BCGSTAB test case cfg --------- Co-authored-by: Ezgi Orbay Akcengiz <[email protected]> Co-authored-by: Nijso <[email protected]> Co-authored-by: Pedro Gomes <[email protected]> * cleanup * apply to NK * modify a test * authors and restore some comments * update tests --------- Co-authored-by: Ezgi Orbay Akcengiz <[email protected]> Co-authored-by: Ezgi Orbay Akcengiz <[email protected]> Co-authored-by: Nijso <[email protected]>
1 parent bd4d182 commit 3dba507

File tree

13 files changed

+138
-30
lines changed

13 files changed

+138
-30
lines changed

AUTHORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ Eduardo Molina
7171
Edwin van der Weide
7272
Eitan Aberman
7373
Ethan Alan Hereth
74+
Ezgi Orbay Akcengiz
7475
Florian Dittmann
7576
Filip Hahs
7677
Francesco Poli

Common/include/CConfig.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,7 @@ class CConfig {
515515
Kind_SlopeLimit_AdjFlow, /*!< \brief Slope limiter for the adjoint equation.*/
516516
Kind_SlopeLimit_Heat, /*!< \brief Slope limiter for the adjoint equation.*/
517517
Kind_SlopeLimit_Species; /*!< \brief Slope limiter for the species equation.*/
518+
LINEAR_SOLVER_INNER Kind_Linear_Solver_Inner; /*!< \brief Inner solver used in nested Krylov schemes. */
518519
unsigned short Kind_FluidModel, /*!< \brief Kind of the Fluid Model: Ideal, van der Waals, etc. */
519520
Kind_InitOption, /*!< \brief Kind of Init option to choose if initializing with Reynolds number or with thermodynamic conditions */
520521
Kind_GridMovement, /*!< \brief Kind of the static mesh movement. */
@@ -4288,6 +4289,10 @@ class CConfig {
42884289
*/
42894290
unsigned short GetKind_Linear_Solver(void) const { return Kind_Linear_Solver; }
42904291

4292+
/*!
4293+
* \brief Get the inner linear solver used in nested Krylov linear solvers.
4294+
*/
4295+
LINEAR_SOLVER_INNER GetKind_Linear_Solver_Inner(void) const { return Kind_Linear_Solver_Inner; }
42914296

42924297
/*!
42934298
* \brief Get the kind of preconditioner for the implicit solver.

Common/include/linear_algebra/CPreconditioner.hpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#pragma once
3030

31+
#include <functional>
3132
#include "../CConfig.hpp"
3233
#include "../geometry/CGeometry.hpp"
3334
#include "CSysVector.hpp"
@@ -305,6 +306,25 @@ class CPastixPreconditioner final : public CPreconditioner<ScalarType> {
305306
inline void Build() override { sparse_matrix.BuildPastixPreconditioner(geometry, config, kind_fact); }
306307
};
307308

309+
/*!
310+
* \class CAbstractPreconditioner
311+
* \brief Applies a std::function as the preconditioning operation.
312+
* \note This can be used to treat almost anything as a preconditioner.
313+
*/
314+
template <class ScalarType>
315+
class CAbstractPreconditioner final : public CPreconditioner<ScalarType> {
316+
private:
317+
std::function<void(const CSysVector<ScalarType>&, CSysVector<ScalarType>&)> impl;
318+
319+
public:
320+
CAbstractPreconditioner() = delete;
321+
322+
template <class F>
323+
explicit CAbstractPreconditioner(const F& function) : impl(function) {}
324+
325+
inline void operator()(const CSysVector<ScalarType>& u, CSysVector<ScalarType>& v) const override { impl(u, v); }
326+
};
327+
308328
template <class ScalarType>
309329
CPreconditioner<ScalarType>* CPreconditioner<ScalarType>::Create(ENUM_LINEAR_SOLVER_PREC kind,
310330
CSysMatrix<ScalarType>& jacobian, CGeometry* geometry,

Common/include/linear_algebra/CSysSolve.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "../containers/C2DContainer.hpp"
3232

3333
#include <cmath>
34+
#include <memory>
3435
#include <vector>
3536
#include <iostream>
3637
#include <cstdlib>
@@ -115,6 +116,9 @@ class CSysSolve {
115116
bool recomputeRes = false; /*!< \brief Recompute the residual after inner iterations, if monitoring. */
116117
unsigned long monitorFreq = 10; /*!< \brief Monitoring frequency. */
117118

119+
/*!< \brief Inner solver for nested preconditioning. */
120+
std::unique_ptr<CSysSolve<ScalarType>> inner_solver;
121+
118122
/*!
119123
* \brief sign transfer function
120124
* \param[in] x - value having sign prescribed

Common/include/option_structure.hpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2369,6 +2369,19 @@ static const MapType<std::string, ENUM_LINEAR_SOLVER> Linear_Solver_Map = {
23692369
MakePair("PASTIX_LU", PASTIX_LU)
23702370
};
23712371

2372+
/*!
2373+
* \brief Inner solver for nested linear solver, only compatible with "flexible" linear solvers.
2374+
*/
2375+
enum class LINEAR_SOLVER_INNER {
2376+
NONE, /*!< \brief Do not use a nested linear solver. */
2377+
BCGSTAB, /*!< \brief Use BCGSTAB as the preconditioning linear solver. */
2378+
};
2379+
static const MapType<std::string, LINEAR_SOLVER_INNER> Inner_Linear_Solver_Map = {
2380+
MakePair("NONE", LINEAR_SOLVER_INNER::NONE)
2381+
MakePair("BCGSTAB", LINEAR_SOLVER_INNER::BCGSTAB)
2382+
};
2383+
2384+
23722385
/*!
23732386
* \brief Types surface continuity at the intersection with the FFD
23742387
*/

Common/src/CConfig.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1887,6 +1887,8 @@ void CConfig::SetConfig_Options() {
18871887
addDoubleOption("LINEAR_SOLVER_SMOOTHER_RELAXATION", Linear_Solver_Smoother_Relaxation, 1.0);
18881888
/* DESCRIPTION: Custom number of threads used for additive domain decomposition for ILU and LU_SGS (0 is "auto"). */
18891889
addUnsignedLongOption("LINEAR_SOLVER_PREC_THREADS", Linear_Solver_Prec_Threads, 0);
1890+
/* DESCRIPTION: Use an inner linear solver. */
1891+
addEnumOption("LINEAR_SOLVER_INNER", Kind_Linear_Solver_Inner, Inner_Linear_Solver_Map, LINEAR_SOLVER_INNER::NONE);
18901892
/* DESCRIPTION: Relaxation factor for updates of adjoint variables. */
18911893
addDoubleOption("RELAXATION_FACTOR_ADJOINT", Relaxation_Factor_Adjoint, 1.0);
18921894
/* DESCRIPTION: Relaxation of the CHT coupling */
@@ -1907,7 +1909,6 @@ void CConfig::SetConfig_Options() {
19071909
addEnumOption("DISCADJ_LIN_SOLVER", Kind_DiscAdj_Linear_Solver, Linear_Solver_Map, FGMRES);
19081910
/* DESCRIPTION: Preconditioner for the discrete adjoint Krylov linear solvers */
19091911
addEnumOption("DISCADJ_LIN_PREC", Kind_DiscAdj_Linear_Prec, Linear_Solver_Prec_Map, ILU);
1910-
/* DESCRIPTION: Linear solver for the discete adjoint systems */
19111912

19121913
/* DESCRIPTION: Maximum update ratio value for flow density and energy variables */
19131914
addDoubleOption("MAX_UPDATE_FLOW", MaxUpdateFlow, 0.2);
@@ -7260,10 +7261,15 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) {
72607261
case BCGSTAB:
72617262
case FGMRES:
72627263
case RESTARTED_FGMRES:
7263-
if (Kind_Linear_Solver == BCGSTAB)
7264+
if (Kind_Linear_Solver == BCGSTAB) {
72647265
cout << "BCGSTAB is used for solving the linear system." << endl;
7265-
else
7266-
cout << "FGMRES is used for solving the linear system." << endl;
7266+
} else {
7267+
if (Kind_Linear_Solver_Inner == LINEAR_SOLVER_INNER::BCGSTAB){
7268+
cout << "Nested FGMRES (FGMRES with inner BiCGSTAB) is used for solving the linear system." << endl;
7269+
} else {
7270+
cout << "FGMRES is used for solving the linear system." << endl;
7271+
}
7272+
}
72677273
switch (Kind_Linear_Solver_Prec) {
72687274
case ILU: cout << "Using a ILU("<< Linear_Solver_ILU_n <<") preconditioning."<< endl; break;
72697275
case LINELET: cout << "Using a linelet preconditioning."<< endl; break;
@@ -7310,6 +7316,8 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) {
73107316
cout << "FGMRES is used for solving the linear system." << endl;
73117317
cout << "Convergence criteria of the linear solver: "<< Linear_Solver_Error <<"."<< endl;
73127318
cout << "Max number of iterations: "<< Linear_Solver_Iter <<"."<< endl;
7319+
if (Kind_Linear_Solver_Inner == LINEAR_SOLVER_INNER::BCGSTAB)
7320+
cout << "Nested BiCGSTAB is used as the inner solver." << endl;
73137321
break;
73147322
case CONJUGATE_GRADIENT:
73157323
cout << "A Conjugate Gradient method is used for solving the linear system." << endl;

Common/src/linear_algebra/CSysSolve.cpp

Lines changed: 66 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "../../include/linear_algebra/CPreconditioner.hpp"
3636

3737
#include <limits>
38+
#include <memory>
3839

3940
/*!
4041
* \brief Epsilon used in CSysSolve depending on datatype to
@@ -110,7 +111,7 @@ void CSysSolve<ScalarType>::SolveReduced(int n, const su2matrix<ScalarType>& Hsb
110111

111112
template <class ScalarType>
112113
void CSysSolve<ScalarType>::ModGramSchmidt(bool shared_hsbg, int i, su2matrix<ScalarType>& Hsbg,
113-
vector<CSysVector<ScalarType> >& w) const {
114+
vector<CSysVector<ScalarType>>& w) const {
114115
const auto thread = omp_get_thread_num();
115116

116117
/*--- If Hsbg is shared by multiple threads calling this function, only one
@@ -902,9 +903,8 @@ unsigned long CSysSolve<ScalarType>::Solve(CSysMatrix<ScalarType>& Jacobian, con
902903
break;
903904
}
904905

905-
/*--- Normal mode
906-
* assumes that 'lin_sol_mode==LINEAR_SOLVER_MODE::STANDARD', but does not enforce it to avoid compiler warning.
907-
* ---*/
906+
/*--- Normal mode assumes that 'lin_sol_mode==LINEAR_SOLVER_MODE::STANDARD',
907+
* but does not enforce it to avoid compiler warning. ---*/
908908
default: {
909909
KindSolver = config->GetKind_Linear_Solver();
910910
KindPrecond = config->GetKind_Linear_Solver_Prec();
@@ -915,6 +915,17 @@ unsigned long CSysSolve<ScalarType>::Solve(CSysMatrix<ScalarType>& Jacobian, con
915915
}
916916
}
917917

918+
const bool nested = (KindSolver == FGMRES || KindSolver == RESTARTED_FGMRES || KindSolver == SMOOTHER) &&
919+
config->GetKind_Linear_Solver_Inner() != LINEAR_SOLVER_INNER::NONE;
920+
921+
if (nested && !inner_solver) {
922+
BEGIN_SU2_OMP_SAFE_GLOBAL_ACCESS {
923+
inner_solver = std::make_unique<CSysSolve<ScalarType>>(LINEAR_SOLVER_MODE::STANDARD);
924+
inner_solver->SetxIsZero(true);
925+
}
926+
END_SU2_OMP_SAFE_GLOBAL_ACCESS
927+
}
928+
918929
/*--- Stop the recording for the linear solver ---*/
919930
bool TapeActive = NO;
920931

@@ -948,13 +959,25 @@ unsigned long CSysSolve<ScalarType>::Solve(CSysMatrix<ScalarType>& Jacobian, con
948959

949960
auto mat_vec = CSysMatrixVectorProduct<ScalarType>(Jacobian, geometry, config);
950961

951-
const auto kindPrec = static_cast<ENUM_LINEAR_SOLVER_PREC>(KindPrecond);
952-
953-
auto precond = CPreconditioner<ScalarType>::Create(kindPrec, Jacobian, geometry, config);
954-
955962
/*--- Build preconditioner. ---*/
956963

957-
precond->Build();
964+
const auto kindPrec = static_cast<ENUM_LINEAR_SOLVER_PREC>(KindPrecond);
965+
auto* normal_prec = CPreconditioner<ScalarType>::Create(kindPrec, Jacobian, geometry, config);
966+
normal_prec->Build();
967+
968+
CPreconditioner<ScalarType>* nested_prec = nullptr;
969+
if (nested) {
970+
nested_prec = new CAbstractPreconditioner<ScalarType>([&](const CSysVector<ScalarType>& u,
971+
CSysVector<ScalarType>& v) {
972+
/*--- Initialize to 0 to be safe. ---*/
973+
v = ScalarType{};
974+
ScalarType unused{};
975+
/*--- Handle other types here if desired but do not call Solve because
976+
* that will create issues with the AD external function. ---*/
977+
(void)inner_solver->BCGSTAB_LinSolver(u, v, mat_vec, *normal_prec, SolverTol, MaxIter, unused, false, config);
978+
});
979+
}
980+
const auto* precond = nested ? nested_prec : normal_prec;
958981

959982
/*--- Solve system. ---*/
960983

@@ -1000,7 +1023,8 @@ unsigned long CSysSolve<ScalarType>::Solve(CSysMatrix<ScalarType>& Jacobian, con
10001023

10011024
HandleTemporariesOut(LinSysSol);
10021025

1003-
delete precond;
1026+
delete normal_prec;
1027+
delete nested_prec;
10041028

10051029
if (TapeActive) {
10061030
/*--- To keep the behavior of SU2_DOT, but not strictly required since jacobian is symmetric(?). ---*/
@@ -1085,9 +1109,8 @@ unsigned long CSysSolve<ScalarType>::Solve_b(CSysMatrix<ScalarType>& Jacobian, c
10851109
break;
10861110
}
10871111

1088-
/*--- Normal mode
1089-
* assumes that 'lin_sol_mode==LINEAR_SOLVER_MODE::STANDARD', but does not enforce it to avoid compiler warning.
1090-
* ---*/
1112+
/*--- Normal mode assumes that 'lin_sol_mode==LINEAR_SOLVER_MODE::STANDARD',
1113+
* but does not enforce it to avoid compiler warning. ---*/
10911114
default: {
10921115
KindSolver = config->GetKind_Linear_Solver();
10931116
KindPrecond = config->GetKind_Linear_Solver_Prec();
@@ -1098,19 +1121,43 @@ unsigned long CSysSolve<ScalarType>::Solve_b(CSysMatrix<ScalarType>& Jacobian, c
10981121
}
10991122
}
11001123

1124+
const bool nested = (KindSolver == FGMRES || KindSolver == RESTARTED_FGMRES || KindSolver == SMOOTHER) &&
1125+
config->GetKind_Linear_Solver_Inner() != LINEAR_SOLVER_INNER::NONE;
1126+
1127+
if (nested && !inner_solver) {
1128+
BEGIN_SU2_OMP_SAFE_GLOBAL_ACCESS {
1129+
inner_solver = std::make_unique<CSysSolve<ScalarType>>(LINEAR_SOLVER_MODE::STANDARD);
1130+
inner_solver->SetxIsZero(true);
1131+
}
1132+
END_SU2_OMP_SAFE_GLOBAL_ACCESS
1133+
}
1134+
11011135
/*--- Set up preconditioner and matrix-vector product ---*/
11021136

1103-
const auto kindPrec = static_cast<ENUM_LINEAR_SOLVER_PREC>(KindPrecond);
1137+
auto mat_vec = CSysMatrixVectorProduct<ScalarType>(Jacobian, geometry, config);
11041138

1105-
auto precond = CPreconditioner<ScalarType>::Create(kindPrec, Jacobian, geometry, config);
1139+
const auto kindPrec = static_cast<ENUM_LINEAR_SOLVER_PREC>(KindPrecond);
1140+
auto* normal_prec = CPreconditioner<ScalarType>::Create(kindPrec, Jacobian, geometry, config);
11061141

11071142
/*--- If there was no call to solve first the preconditioner needs to be built here. ---*/
11081143
if (directCall) {
11091144
Jacobian.TransposeInPlace();
1110-
precond->Build();
1145+
normal_prec->Build();
11111146
}
11121147

1113-
auto mat_vec = CSysMatrixVectorProduct<ScalarType>(Jacobian, geometry, config);
1148+
CPreconditioner<ScalarType>* nested_prec = nullptr;
1149+
if (nested) {
1150+
nested_prec =
1151+
new CAbstractPreconditioner<ScalarType>([&](const CSysVector<ScalarType>& u, CSysVector<ScalarType>& v) {
1152+
/*--- Initialize to 0 to be safe. ---*/
1153+
v = ScalarType{};
1154+
ScalarType unused{};
1155+
/*--- Handle other types here if desired but do not call Solve because
1156+
* that will create issues with the AD external function. ---*/
1157+
(void)inner_solver->BCGSTAB_LinSolver(u, v, mat_vec, *normal_prec, SolverTol, MaxIter, unused, false, config);
1158+
});
1159+
}
1160+
const auto* precond = nested ? nested_prec : normal_prec;
11141161

11151162
/*--- Solve the system ---*/
11161163

@@ -1154,7 +1201,8 @@ unsigned long CSysSolve<ScalarType>::Solve_b(CSysMatrix<ScalarType>& Jacobian, c
11541201

11551202
HandleTemporariesOut(LinSysSol);
11561203

1157-
delete precond;
1204+
delete normal_prec;
1205+
delete nested_prec;
11581206

11591207
SU2_OMP_MASTER {
11601208
Residual = residual;

SU2_CFD/include/integration/CNewtonIntegration.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,11 @@ class CNewtonIntegration final : public CIntegration {
158158
auto product = CSysMatrixVectorProduct<MixedScalar>(solvers[FLOW_SOL]->Jacobian, geometry, config);
159159
v = MixedScalar(0.0);
160160
MixedScalar eps_t = eps;
161-
iters = solvers[FLOW_SOL]->System.FGMRES_LinSolver(u, v, product, *preconditioner, eps, iters, eps_t, false, config);
161+
if (config->GetKind_Linear_Solver_Inner() == LINEAR_SOLVER_INNER::NONE) {
162+
iters = solvers[FLOW_SOL]->System.FGMRES_LinSolver(u, v, product, *preconditioner, eps, iters, eps_t, false, config);
163+
} else {
164+
iters = solvers[FLOW_SOL]->System.BCGSTAB_LinSolver(u, v, product, *preconditioner, eps, iters, eps_t, false, config);
165+
}
162166
eps = eps_t;
163167
return iters;
164168
}

TestCases/hybrid_regression.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ def main():
404404
inc_turb_naca0012.cfg_dir = "incomp_rans/naca0012"
405405
inc_turb_naca0012.cfg_file = "naca0012.cfg"
406406
inc_turb_naca0012.test_iter = 20
407-
inc_turb_naca0012.test_vals = [-4.788405, -11.040877, 0.000008, 0.309505]
407+
inc_turb_naca0012.test_vals = [-4.758062, -10.974496, -0.000005, -0.028654, 4, -5.397415, 2, -6.426845]
408408
test_list.append(inc_turb_naca0012)
409409

410410
# NACA0012, SST_SUST

TestCases/incomp_rans/naca0012/naca0012.cfg

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ MARKER_MONITORING= ( airfoil )
5050
%
5151
NUM_METHOD_GRAD= WEIGHTED_LEAST_SQUARES
5252
CFL_NUMBER= 10.0
53-
CFL_ADAPT= NO
54-
CFL_ADAPT_PARAM= ( 1.5, 0.5, 1.0, 100.0 )
53+
CFL_ADAPT= YES
54+
CFL_ADAPT_PARAM= ( 0.8, 1.1, 1.0, 100.0 )
5555
ITER= 2500
5656

5757
% ----------------------- SLOPE LIMITER DEFINITION ----------------------------%
@@ -64,6 +64,7 @@ SENS_REMOVE_SHARP= NO
6464
% ------------------------ LINEAR SOLVER DEFINITION ---------------------------%
6565
%
6666
LINEAR_SOLVER= FGMRES
67+
LINEAR_SOLVER_INNER= BCGSTAB
6768
LINEAR_SOLVER_PREC= LU_SGS
6869
LINEAR_SOLVER_ERROR= 1E-4
6970
LINEAR_SOLVER_ITER= 5
@@ -107,4 +108,4 @@ GRAD_OBJFUNC_FILENAME= of_grad
107108
SURFACE_FILENAME= surface_flow
108109
SURFACE_ADJ_FILENAME= surface_adjoint
109110
OUTPUT_WRT_FREQ= 100
110-
SCREEN_OUTPUT= (INNER_ITER, RMS_PRESSURE, RMS_NU_TILDE, LIFT, DRAG, TOTAL_HEATFLUX)
111+
SCREEN_OUTPUT= (INNER_ITER, RMS_PRESSURE, RMS_NU_TILDE, LIFT, DRAG, LINSOL)

0 commit comments

Comments
 (0)