@@ -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);
0 commit comments