Skip to content

Commit d2481dd

Browse files
authored
Merge pull request #2581 from su2code/pedro/nk_relaxation
Newton Krylov improvements
2 parents 82ba477 + a5f5513 commit d2481dd

File tree

22 files changed

+209
-112
lines changed

22 files changed

+209
-112
lines changed

Common/include/CConfig.hpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,8 @@ class CConfig {
431431
bool UseVectorization; /*!< \brief Whether to use vectorized numerics schemes. */
432432
bool NewtonKrylov; /*!< \brief Use a coupled Newton method to solve the flow equations. */
433433
array<unsigned short,3> NK_IntParam{{20, 3, 2}}; /*!< \brief Integer parameters for NK method. */
434-
array<su2double,4> NK_DblParam{{-2.0, 0.1, -3.0, 1e-4}}; /*!< \brief Floating-point parameters for NK method. */
434+
array<su2double,5> NK_DblParam{{-2.0, 0.1, -3.0, 1e-4, 1.0}}; /*!< \brief Floating-point parameters for NK method. */
435+
su2double NK_Relaxation = 1.0;
435436

436437
unsigned short nMGLevels; /*!< \brief Number of multigrid levels (coarse levels). */
437438
unsigned short nCFL; /*!< \brief Number of CFL, one for each multigrid level. */
@@ -1332,9 +1333,9 @@ class CConfig {
13321333
template <class Tenum, class Tfield>
13331334
void addEnumListOption(const string name, unsigned short& input_size, Tfield*& option_field, const map<string,Tenum>& enum_map);
13341335

1335-
void addDoubleArrayOption(const string& name, const int size, su2double* option_field);
1336+
void addDoubleArrayOption(const string& name, int size, bool allow_fewer, su2double* option_field);
13361337

1337-
void addUShortArrayOption(const string& name, const int size, unsigned short* option_field);
1338+
void addUShortArrayOption(const string& name, int size, bool allow_fewer, unsigned short* option_field);
13381339

13391340
void addDoubleListOption(const string& name, unsigned short & size, su2double * & option_field);
13401341

@@ -4375,12 +4376,22 @@ class CConfig {
43754376
/*!
43764377
* \brief Get Newton-Krylov integer parameters.
43774378
*/
4378-
array<unsigned short,3> GetNewtonKrylovIntParam(void) const { return NK_IntParam; }
4379+
array<unsigned short,3> GetNewtonKrylovIntParam() const { return NK_IntParam; }
43794380

43804381
/*!
43814382
* \brief Get Newton-Krylov floating-point parameters.
43824383
*/
4383-
array<su2double,4> GetNewtonKrylovDblParam(void) const { return NK_DblParam; }
4384+
array<su2double,5> GetNewtonKrylovDblParam() const { return NK_DblParam; }
4385+
4386+
/*!
4387+
* \brief Get the Newton-Krylov relaxation.
4388+
*/
4389+
su2double GetNewtonKrylovRelaxation() const { return NK_Relaxation; }
4390+
4391+
/*!
4392+
* \brief Set the Newton-Krylov relaxation.
4393+
*/
4394+
void SetNewtonKrylovRelaxation(const su2double& relaxation) { NK_Relaxation = relaxation; }
43844395

43854396
/*!
43864397
* \brief Returns the Roe kappa (multipler of the dissipation term).

Common/include/option_structure.inl

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -233,18 +233,19 @@ class COptionEnumList final : public COptionBase {
233233

234234
template <class Type>
235235
class COptionArray final : public COptionBase {
236-
string name; // Identifier for the option
237-
const int size; // Number of elements
238-
Type* field; // Reference to the field
236+
string name; // Identifier for the option
237+
const int size; // Number of elements
238+
const bool allow_fewer; // Allow smaller size
239+
Type* field; // Reference to the field
239240

240241
public:
241-
COptionArray(string option_field_name, const int list_size, Type* option_field)
242-
: name(option_field_name), size(list_size), field(option_field) {}
242+
COptionArray(string option_field_name, const int list_size, const bool allow_fewer, Type* option_field)
243+
: name(std::move(option_field_name)), size(list_size), allow_fewer(allow_fewer), field(option_field) {}
243244

244245
string SetValue(const vector<string>& option_value) override {
245246
COptionBase::SetValue(option_value);
246247
// Check that the size is correct
247-
if (option_value.size() != (unsigned long)this->size) {
248+
if ((option_value.size() < size_t(size) && !allow_fewer) || option_value.size() > size_t(size)) {
248249
string newstring;
249250
newstring.append(this->name);
250251
newstring.append(": wrong number of arguments: ");
@@ -258,7 +259,7 @@ class COptionArray final : public COptionBase {
258259
newstring.append(" found");
259260
return newstring;
260261
}
261-
for (int i = 0; i < this->size; i++) {
262+
for (size_t i = 0; i < option_value.size(); i++) {
262263
istringstream is(option_value[i]);
263264
if (!(is >> field[i])) {
264265
return badValue(" array", this->name);

Common/src/CConfig.cpp

Lines changed: 46 additions & 46 deletions
Large diffs are not rendered by default.

Common/src/linear_algebra/CSysSolve.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ constexpr T linSolEpsilon() {
4747
}
4848
template <>
4949
constexpr float linSolEpsilon<float>() {
50-
return 1e-12;
50+
return 1e-14;
5151
}
5252
} // namespace
5353

SU2_CFD/include/integration/CNewtonIntegration.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,10 @@ class CNewtonIntegration final : public CIntegration {
6767
enum class ResEvalType {EXPLICIT, DEFAULT};
6868

6969
bool setup = false;
70+
bool autoRelaxation = false;
7071
Scalar finDiffStepND = 0.0;
7172
Scalar finDiffStep = 0.0; /*!< \brief Based on RMS(solution), used in matrix-free products. */
73+
Scalar nkRelaxation = 1.0;
7274
unsigned long omp_chunk_size; /*!< \brief Chunk size used in light point loops. */
7375

7476
/*--- Number of iterations and tolerance for the linear preconditioner,
@@ -81,7 +83,7 @@ class CNewtonIntegration final : public CIntegration {
8183
* criteria are zero, or the solver does not provide a linear
8284
* preconditioner, there is no startup phase. ---*/
8385
bool startupPeriod = false;
84-
unsigned short startupIters = 0;
86+
unsigned short startupIters = 0, iter = 0;
8587
su2double startupResidual = 0.0;
8688
su2double firstResidual = -20.0;
8789

@@ -96,8 +98,9 @@ class CNewtonIntegration final : public CIntegration {
9698
CNumerics*** numerics = nullptr;
9799

98100
/*--- Residual and linear solver. ---*/
99-
CSysVector<Scalar> LinSysRes;
101+
CSysVector<Scalar> LinSysRes, LinSysResRelax;
100102
CSysSolve<Scalar> LinSolver;
103+
const CSysVector<Scalar>* LinSysRes0 = nullptr;
101104

102105
/*--- If possible the solution vector of the solver is re-used, otherwise this temporary is used. ---*/
103106
CSysVector<Scalar> LinSysSol;

SU2_CFD/include/numerics_simd/flow/convection/common.hpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ FORCEINLINE CPair<ReconVarType> reconstructPrimitives(Int iEdge, Int iPoint, Int
114114
const CPair<PrimVarType>& V1st,
115115
const VectorDbl<nDim>& vector_ij,
116116
const VariableType& solution) {
117-
static_assert(ReconVarType::nVar <= PrimVarType::nVar,"");
117+
static_assert(ReconVarType::nVar <= PrimVarType::nVar);
118118

119119
const auto& gradients = solution.GetGradient_Reconstruction();
120120
const auto& limiters = solution.GetLimiter_Primitive();
@@ -129,7 +129,6 @@ FORCEINLINE CPair<ReconVarType> reconstructPrimitives(Int iEdge, Int iPoint, Int
129129
if (muscl) {
130130
/*--- Recompute density and enthalpy instead of reconstructing. ---*/
131131
constexpr auto nVarGrad = ReconVarType::nVar - 2;
132-
133132
switch (limiterType) {
134133
case LIMITER::NONE:
135134
musclUnlimited<nVarGrad>(iPoint, vector_ij, 0.5, gradients, V.i.all);

SU2_CFD/include/numerics_simd/flow/convection/roe.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ class CRoeBase : public Base {
9696
AD::StartPreacc();
9797

9898
const bool implicit = (config.GetKind_TimeIntScheme() == EULER_IMPLICIT);
99+
const auto nkRelax = config.GetNewtonKrylovRelaxation();
99100
const auto& solution = static_cast<const CEulerVariable&>(solution_);
100101

101102
const auto iPoint = geometry.edges->GetNode(iEdge,0);
@@ -118,8 +119,13 @@ class CRoeBase : public Base {
118119
V1st.i.all = gatherVariables<nPrimVar>(iPoint, solution.GetPrimitive());
119120
V1st.j.all = gatherVariables<nPrimVar>(jPoint, solution.GetPrimitive());
120121

122+
VectorDbl<nDim> mod_vector_ij;
123+
for (size_t iDim = 0; iDim < nDim; ++iDim) {
124+
mod_vector_ij(iDim) = nkRelax * vector_ij(iDim);
125+
}
126+
/*--- Recompute density and enthalpy instead of reconstructing. ---*/
121127
auto V = reconstructPrimitives<CCompressiblePrimitives<nDim,nPrimVarGrad> >(
122-
iEdge, iPoint, jPoint, gamma, gasConst, muscl, typeLimiter, V1st, vector_ij, solution);
128+
iEdge, iPoint, jPoint, gamma, gasConst, muscl, typeLimiter, V1st, mod_vector_ij, solution);
123129

124130
/*--- Compute conservative variables. ---*/
125131

SU2_CFD/include/variables/CEulerVariable.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@
3030
#include <limits>
3131
#include "CFlowVariable.hpp"
3232

33+
/*!
34+
* \brief Returns the number of primitive variables for which to compute gradients.
35+
*/
36+
unsigned long EulerNPrimVarGrad(const CConfig *config, unsigned long ndim);
37+
3338
/*!
3439
* \class CEulerVariable
3540
* \brief Class for defining the variables of the compressible Euler solver.

SU2_CFD/src/integration/CNewtonIntegration.cpp

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,18 @@ void CNewtonIntegration::Setup() {
6666
auto iparam = config->GetNewtonKrylovIntParam();
6767
auto dparam = config->GetNewtonKrylovDblParam();
6868

69-
startupIters = iparam[0];
69+
startupIters = iter = iparam[0];
7070
startupResidual = dparam[0];
7171
precondIters = iparam[1];
7272
precondTol = dparam[1];
7373
tolRelaxFactor = iparam[2];
7474
fullTolResidual = dparam[2];
7575
finDiffStepND = SU2_TYPE::GetValue(dparam[3]);
76+
nkRelaxation = fmin(SU2_TYPE::GetValue(dparam[4]), 1);
77+
if (nkRelaxation < 0) {
78+
autoRelaxation = true;
79+
nkRelaxation = 1;
80+
}
7681

7782
const auto nVar = solvers[FLOW_SOL]->GetnVar();
7883
const auto nPoint = geometry->GetnPoint();
@@ -83,6 +88,9 @@ void CNewtonIntegration::Setup() {
8388
LinSolver.SetxIsZero(true);
8489

8590
LinSysRes.Initialize(nPoint, nPointDomain, nVar, 0.0);
91+
if (autoRelaxation || nkRelaxation < 1) {
92+
LinSysResRelax.Initialize(nPoint, nPointDomain, nVar, 0.0);
93+
}
8694

8795
if (!std::is_same<Scalar,su2double>::value) {
8896
LinSysSol.Initialize(nPoint, nPointDomain, nVar, nullptr);
@@ -175,6 +183,17 @@ void CNewtonIntegration::MultiGrid_Iteration(CGeometry ****geometry_, CSolver **
175183

176184
if (!setup) { Setup(); setup = true; }
177185

186+
// Ramp from 1st to 2nd order during the startup.
187+
su2double baseNkRelaxation = 1;
188+
if (startupPeriod && startupIters > 0 && !config->GetRestart()) {
189+
baseNkRelaxation = su2double(startupIters - iter) / startupIters;
190+
}
191+
config->SetNewtonKrylovRelaxation(baseNkRelaxation);
192+
193+
// When using NK relaxation (not fully 2nd order Jacobian products) we need an additional
194+
// residual evaluation that is used as the reference for finite differences.
195+
LinSysRes0 = (!startupPeriod && nkRelaxation < 1) ? &LinSysResRelax : &LinSysRes;
196+
178197
SU2_OMP_PARALLEL_(if(solvers[FLOW_SOL]->GetHasHybridParallel())) {
179198

180199
/*--- Save the current solution to be able to perturb it. ---*/
@@ -191,10 +210,20 @@ void CNewtonIntegration::MultiGrid_Iteration(CGeometry ****geometry_, CSolver **
191210

192211
if (preconditioner) preconditioner->Build();
193212

194-
SU2_OMP_FOR_STAT(omp_chunk_size)
195-
for (auto i = 0ul; i < LinSysRes.GetNElmDomain(); ++i)
196-
LinSysRes[i] = SU2_TYPE::GetValue(solvers[FLOW_SOL]->LinSysRes[i]);
197-
END_SU2_OMP_FOR
213+
auto CopyLinSysRes = [&](int sign, auto& dst) {
214+
SU2_OMP_FOR_STAT(omp_chunk_size)
215+
for (auto i = 0ul; i < dst.GetNElmDomain(); ++i)
216+
dst[i] = sign * SU2_TYPE::GetValue(solvers[FLOW_SOL]->LinSysRes[i]);
217+
END_SU2_OMP_FOR
218+
};
219+
CopyLinSysRes(1, LinSysRes);
220+
221+
if (!startupPeriod && nkRelaxation < 1) {
222+
SU2_OMP_SAFE_GLOBAL_ACCESS(config->SetNewtonKrylovRelaxation(nkRelaxation);)
223+
ComputeResiduals(ResEvalType::EXPLICIT);
224+
// Here the sign was not flipped by PrepareImplicitIteration.
225+
CopyLinSysRes(-1, LinSysResRelax);
226+
}
198227

199228
su2double residual = 0.0;
200229
for (auto iVar = 0ul; iVar < LinSysRes.GetNVar(); ++iVar)
@@ -207,10 +236,10 @@ void CNewtonIntegration::MultiGrid_Iteration(CGeometry ****geometry_, CSolver **
207236
if (startupPeriod) {
208237
BEGIN_SU2_OMP_SAFE_GLOBAL_ACCESS {
209238
firstResidual = max(firstResidual, residual);
210-
if (startupIters) startupIters -= 1;
239+
if (iter) iter -= 1;
211240
}
212241
END_SU2_OMP_SAFE_GLOBAL_ACCESS
213-
endStartup = (startupIters == 0) && (residual - firstResidual < startupResidual);
242+
endStartup = (iter == 0) && (residual - firstResidual < startupResidual);
214243
}
215244

216245
/*--- The NK solves are expensive, the tolerance is relaxed while the residuals are high. ---*/
@@ -232,8 +261,7 @@ void CNewtonIntegration::MultiGrid_Iteration(CGeometry ****geometry_, CSolver **
232261

233262
if (startupPeriod) {
234263
iter = Preconditioner_impl(LinSysRes, linSysSol, iter, eps);
235-
}
236-
else {
264+
} else {
237265
ComputeFinDiffStep();
238266

239267
eps *= toleranceFactor;
@@ -247,6 +275,15 @@ void CNewtonIntegration::MultiGrid_Iteration(CGeometry ****geometry_, CSolver **
247275
BEGIN_SU2_OMP_SAFE_GLOBAL_ACCESS {
248276
solvers[FLOW_SOL]->SetIterLinSolver(iter);
249277
solvers[FLOW_SOL]->SetResLinSolver(eps);
278+
279+
if (!startupPeriod && autoRelaxation) {
280+
const su2double adaptTol = config->GetCFL_Adapt() ? config->GetCFL_AdaptParam(4) : 0;
281+
if (eps > fmax(config->GetLinear_Solver_Error(), adaptTol)) {
282+
nkRelaxation *= 0.9;
283+
} else if (eps < 0.9 * fmax(config->GetLinear_Solver_Error(), adaptTol)) {
284+
nkRelaxation = fmin(nkRelaxation * 1.05, 1);
285+
}
286+
}
250287
}
251288
END_SU2_OMP_SAFE_GLOBAL_ACCESS
252289

@@ -303,11 +340,11 @@ void CNewtonIntegration::MatrixFreeProduct(const CSysVector<Scalar>& u, CSysVect
303340
su2double delta = (geometry->nodes->GetVolume(iPoint) + geometry->nodes->GetPeriodicVolume(iPoint)) /
304341
max(EPS, solvers[FLOW_SOL]->GetNodes()->GetDelta_Time(iPoint));
305342
SU2_OMP_SIMD
306-
for (auto iVar = 0ul; iVar < LinSysRes.GetNVar(); ++iVar) {
343+
for (auto iVar = 0ul; iVar < LinSysRes0->GetNVar(); ++iVar) {
307344
Scalar perturbRes = SU2_TYPE::GetValue(solvers[FLOW_SOL]->LinSysRes(iPoint,iVar));
308345

309346
/*--- The global residual had its sign flipped, so we add to get the difference. ---*/
310-
v(iPoint,iVar) = (perturbRes + LinSysRes(iPoint,iVar)) * factor;
347+
v(iPoint,iVar) = (perturbRes + (*LinSysRes0)(iPoint,iVar)) * factor;
311348

312349
/*--- Pseudotime term of the true Jacobian. ---*/
313350
v(iPoint,iVar) += SU2_TYPE::GetValue(delta) * u(iPoint,iVar);

SU2_CFD/src/output/CFlowCompOutput.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ void CFlowCompOutput::SetVolumeOutputFields(CConfig *config){
277277
SetVolumeOutputFieldsScalarResidual(config);
278278

279279
if (config->GetKind_SlopeLimit_Flow() != LIMITER::NONE && config->GetKind_SlopeLimit_Flow() != LIMITER::VAN_ALBADA_EDGE) {
280+
AddVolumeOutput("LIMITER_TEMPERATURE", "Limiter_Temperature", "LIMITER", "Limiter value of the temperature");
280281
AddVolumeOutput("LIMITER_VELOCITY-X", "Limiter_Velocity_x", "LIMITER", "Limiter value of the x-velocity");
281282
AddVolumeOutput("LIMITER_VELOCITY-Y", "Limiter_Velocity_y", "LIMITER", "Limiter value of the y-velocity");
282283
if (nDim == 3) {
@@ -364,14 +365,17 @@ void CFlowCompOutput::LoadVolumeData(CConfig *config, CGeometry *geometry, CSolv
364365
}
365366

366367
if (config->GetKind_SlopeLimit_Flow() != LIMITER::NONE && config->GetKind_SlopeLimit_Flow() != LIMITER::VAN_ALBADA_EDGE) {
368+
SetVolumeOutputValue("LIMITER_TEMPERATURE", iPoint, Node_Flow->GetLimiter_Primitive(iPoint, 0));
367369
SetVolumeOutputValue("LIMITER_VELOCITY-X", iPoint, Node_Flow->GetLimiter_Primitive(iPoint, 1));
368370
SetVolumeOutputValue("LIMITER_VELOCITY-Y", iPoint, Node_Flow->GetLimiter_Primitive(iPoint, 2));
369371
if (nDim == 3){
370372
SetVolumeOutputValue("LIMITER_VELOCITY-Z", iPoint, Node_Flow->GetLimiter_Primitive(iPoint, 3));
371373
}
372374
SetVolumeOutputValue("LIMITER_PRESSURE", iPoint, Node_Flow->GetLimiter_Primitive(iPoint, nDim+1));
373-
SetVolumeOutputValue("LIMITER_DENSITY", iPoint, Node_Flow->GetLimiter_Primitive(iPoint, nDim+2));
374-
SetVolumeOutputValue("LIMITER_ENTHALPY", iPoint, Node_Flow->GetLimiter_Primitive(iPoint, nDim+3));
375+
if (solver[FLOW_SOL]->GetnPrimVarGrad() > nDim + 2) {
376+
SetVolumeOutputValue("LIMITER_DENSITY", iPoint, Node_Flow->GetLimiter_Primitive(iPoint, nDim+2));
377+
SetVolumeOutputValue("LIMITER_ENTHALPY", iPoint, Node_Flow->GetLimiter_Primitive(iPoint, nDim+3));
378+
}
375379
}
376380

377381
if (config->GetKind_RoeLowDiss() != NO_ROELOWDISS){

0 commit comments

Comments
 (0)