Nektar++
GlobalLinSysIterative.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: GlobalLinSysIterative.cpp
4//
5// For more information, please see: http://www.nektar.info
6//
7// The MIT License
8//
9// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10// Department of Aeronautics, Imperial College London (UK), and Scientific
11// Computing and Imaging Institute, University of Utah (USA).
12//
13// Permission is hereby granted, free of charge, to any person obtaining a
14// copy of this software and associated documentation files (the "Software"),
15// to deal in the Software without restriction, including without limitation
16// the rights to use, copy, modify, merge, publish, distribute, sublicense,
17// and/or sell copies of the Software, and to permit persons to whom the
18// Software is furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included
21// in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29// DEALINGS IN THE SOFTWARE.
30//
31// Description: GlobalLinSysIterative definition
32//
33///////////////////////////////////////////////////////////////////////////////
34
36
37using namespace std;
38
39namespace Nektar
40{
41namespace MultiRegions
42{
45 "LinSysIterSolver", "ConjugateGradient",
48 "LinSysIterSolver", "ConjugateGradientLoc",
50 LibUtilities::SessionReader::RegisterEnumValue("LinSysIterSolver", "GMRES",
53 "LinSysIterSolver", "GMRESLoc", MultiRegions::eGMRESLoc),
54};
55
58 "ConjugateGradient");
59
60/**
61 * @class GlobalLinSysIterative
62 *
63 * Solves a linear system using iterative methods.
64 */
65
66/// Constructor for full iterative matrix solve.
68 const GlobalLinSysKey &pKey, const std::weak_ptr<ExpList> &pExpList,
69 const std::shared_ptr<AssemblyMap> &pLocToGloMap)
70 : GlobalLinSys(pKey, pExpList, pLocToGloMap),
71 m_rhs_magnitude(NekConstants::kNekUnsetDouble), m_rhs_mag_sm(0.9),
72 m_precon(NullPreconditionerSharedPtr), m_totalIterations(0),
73 m_useProjection(false), m_numPrevSols(0)
74{
75 m_tolerance = pLocToGloMap->GetIterativeTolerance();
76 m_isAbsoluteTolerance = pLocToGloMap->IsAbsoluteTolerance();
77 m_maxiter = pLocToGloMap->GetMaxIterations();
78 m_linSysIterSolver = pLocToGloMap->GetLinSysIterSolver();
79
81 m_expList.lock()->GetComm()->GetRowComm();
82 m_root = (vComm->GetRank()) ? false : true;
83
84 m_numSuccessiveRHS = pLocToGloMap->GetSuccessiveRHS();
88
89 // Check for advection matrix and switch to GMRES, if not already used
92 m_matrixType.find("AdvectionDiffusionReaction") != string::npos;
93
95 !m_linSysIterSolver.compare("ConjugateGradient"))
96 {
97 m_linSysIterSolver = "GMRES";
99 false,
100 "Detected ConjugateGradient solver and a "
101 "Advection-Diffusion-Reaction matrix. "
102 "Switchted to a GMRES solver for this non-symmetric matrix type. "
103 "Change LinSysIterSolver to GMRES in the session file to suppress "
104 "this warning.");
105 }
106
107 if (m_isAconjugate && m_linSysIterSolver.compare("GMRES") == 0 &&
108 m_linSysIterSolver.compare("GMRESLoc") == 0)
109 {
110 WARNINGL0(false, "To use A-conjugate projection, the matrix "
111 "should be symmetric positive definite.");
112 }
113
115 this);
117 this);
119 this);
120}
121
123{
124}
125
126/**
127 * This method implements projection techniques
128 * in order to speed up successive linear solves with
129 * right-hand sides arising from time-dependent discretisations.
130 * (P.F.Fischer, Comput. Methods Appl. Mech. Engrg. 163, 1998)
131 */
133 const int nGlobal, const Array<OneD, const NekDouble> &pInput,
134 Array<OneD, NekDouble> &pOutput, const int nDir, const NekDouble tol,
135 const bool isAconjugate)
136{
137 int numIterations = 0;
138 if (0 == m_numPrevSols)
139 {
140 // no previous solutions found
141 numIterations =
142 m_linsol->SolveSystem(nGlobal, pInput, pOutput, nDir, tol);
143 }
144 else
145 {
146 // Get the communicator for performing data exchanges
148 m_expList.lock()->GetComm()->GetRowComm();
149
150 // Get vector sizes
151 int nNonDir = nGlobal - nDir;
152
153 // check the input vector (rhs) is not zero
155
156 NekDouble rhsNorm =
157 Vmath::Dot2(nNonDir, pInput + nDir, pInput + nDir, m_map + nDir);
158
159 vComm->AllReduce(rhsNorm, Nektar::LibUtilities::ReduceSum);
160
161 if (rhsNorm < tol * tol * m_rhs_magnitude)
162 {
163 Vmath::Zero(nNonDir, tmp = pOutput + nDir, 1);
164 if (m_verbose && m_root)
165 {
166 cout << "No iterations made"
167 << " using tolerance of " << tol
168 << " (error = " << sqrt(rhsNorm / m_rhs_magnitude)
169 << ", rhs_mag = " << sqrt(m_rhs_magnitude) << ")" << endl;
170 }
171 return;
172 }
173
174 // Create NekVector wrappers for linear algebra operations
175 NekVector<NekDouble> b(nNonDir, pInput + nDir, eWrapper);
176 NekVector<NekDouble> x(nNonDir, tmp = pOutput + nDir, eWrapper);
177 // Allocate array storage
178 Array<OneD, NekDouble> px_s(nGlobal, 0.0);
179 Array<OneD, NekDouble> pb_s(nGlobal, 0.0);
180 Array<OneD, NekDouble> tmpAx_s(nGlobal, 0.0);
181 Array<OneD, NekDouble> tmpx_s(nGlobal, 0.0);
182
183 NekVector<NekDouble> pb(nNonDir, tmp = pb_s + nDir, eWrapper);
184 NekVector<NekDouble> px(nNonDir, tmp = px_s + nDir, eWrapper);
185 NekVector<NekDouble> tmpAx(nNonDir, tmp = tmpAx_s + nDir, eWrapper);
186 NekVector<NekDouble> tmpx(nNonDir, tmp = tmpx_s + nDir, eWrapper);
187
188 // notation follows the paper cited:
189 // \alpha_i = \tilda{x_i}^T b^n
190 // projected x, px = \sum \alpha_i \tilda{x_i}
191
192 Array<OneD, NekDouble> alpha(m_prevBasis.size(), 0.0);
193 Array<OneD, NekDouble> alphaback(m_prevBasis.size(), 0.0);
194 for (int i = 0; i < m_prevBasis.size(); i++)
195 {
196 alpha[i] = Vmath::Dot2(nNonDir, m_prevBasis[i], pInput + nDir,
197 m_map + nDir);
198 }
199 vComm->AllReduce(alpha, Nektar::LibUtilities::ReduceSum);
200 int n = m_prevBasis.size(), info = -1;
201 Vmath::Vcopy(m_prevBasis.size(), alpha, 1, alphaback, 1);
202 Lapack::Dsptrs('U', n, 1, m_coeffMatrixFactor.get(), m_ipivot.get(),
203 alpha.get(), n, info);
204 if (info != 0)
205 {
206 // Dsptrs fails, only keep the latest solution
207 int latest = ResetKnownSolutionsToLatestOne();
208 alpha[0] = alphaback[latest];
209 }
210 for (int i = 0; i < m_prevBasis.size(); ++i)
211 {
213 px += alpha[i] * xi;
214 }
215
216 // pb = b^n - A px
217 Vmath::Vcopy(nNonDir, pInput.get() + nDir, 1, pb_s.get() + nDir, 1);
218
219 DoMatrixMultiplyFlag(px_s, tmpAx_s, false);
220
221 pb -= tmpAx;
222
223 if (m_verbose)
224 {
225 if (m_root)
226 cout << "SuccessiveRHS: " << m_prevBasis.size()
227 << "-bases projection reduces L2-norm of RHS from "
228 << std::sqrt(rhsNorm) << " to ";
229 NekDouble tmprhsNorm =
230 Vmath::Dot2(nNonDir, pb_s + nDir, pb_s + nDir, m_map + nDir);
231 vComm->AllReduce(tmprhsNorm, Nektar::LibUtilities::ReduceSum);
232 if (m_root)
233 cout << std::sqrt(tmprhsNorm) << endl;
234 }
235
236 // solve the system with projected rhs
237 numIterations = m_linsol->SolveSystem(nGlobal, pb_s, tmpx_s, nDir, tol);
238
239 // remainder solution + projection of previous solutions
240 x = tmpx + px;
241 }
242 // save the auxiliary solution to prev. known solutions
243 if (numIterations)
244 {
245 UpdateKnownSolutions(nGlobal, pOutput, nDir, isAconjugate);
246 }
247}
248
250{
251 if (m_numPrevSols == 0)
252 {
253 return -1;
254 }
258 m_prevBasis.clear();
259 m_prevLinSol.clear();
260 m_prevBasis.push_back(b);
261 m_prevLinSol.push_back(x);
262 m_numPrevSols = 1;
263 return latest;
264}
265
266/**
267 * Updates the storage of previously known solutions.
268 * Performs normalisation of input vector wrt A-norm.
269 */
271 const int nGlobal, const Array<OneD, const NekDouble> &newX, const int nDir,
272 const bool isAconjugate)
273{
274 // Get vector sizes
275 int nNonDir = nGlobal - nDir;
276 int insertLocation = m_numPrevSols % m_numSuccessiveRHS;
277 int fullbuffer = (m_prevBasis.size() == m_numSuccessiveRHS);
278
279 // Get the communicator for performing data exchanges
281 m_expList.lock()->GetComm()->GetRowComm();
282
283 Array<OneD, NekDouble> tmpAx_s(nGlobal, 0.0);
284 Array<OneD, NekDouble> y_s(m_prevBasis.size() - fullbuffer, 0.0);
285 Array<OneD, NekDouble> invMy_s(y_s.size(), 0.0);
287 Array<OneD, NekDouble> tmp, newBasis;
288
289 DoMatrixMultiplyFlag(newX, tmpAx_s, false);
290
291 if (isAconjugate)
292 {
293 newBasis = newX + nDir;
294 }
295 else
296 {
297 newBasis = tmpAx_s + nDir;
298 }
299
300 // Check the solution is non-zero
301 NekDouble solNorm =
302 Vmath::Dot2(nNonDir, newBasis, tmpAx_s + nDir, m_map + nDir);
303 vComm->AllReduce(solNorm, Nektar::LibUtilities::ReduceSum);
304
305 if (solNorm < 22.2 * NekConstants::kNekSparseNonZeroTol)
306 {
307 return;
308 }
309
310 // normalisation of A x
311 Vmath::Smul(nNonDir, 1.0 / sqrt(solNorm), tmpAx_s + nDir, 1,
312 tmp = tmpAx_s + nDir, 1);
313
314 for (int i = 0; i < m_prevBasis.size(); ++i)
315 {
316 if (i == insertLocation)
317 continue;
318 int skip = i > insertLocation;
319 y_s[i - skip] =
320 Vmath::Dot2(nNonDir, m_prevBasis[i], tmpAx_s + nDir, m_map + nDir);
321 }
322 vComm->AllReduce(y_s, Nektar::LibUtilities::ReduceSum);
323
324 // check if linearly dependent
325 DNekMatSharedPtr tilCoeffMatrix;
326 if (fullbuffer && m_numSuccessiveRHS > 1)
327 {
330 for (int i = 0; i < m_numSuccessiveRHS; ++i)
331 {
332 if (i == insertLocation)
333 continue;
334 int iskip = i > insertLocation;
335 for (int j = i; j < m_numSuccessiveRHS; ++j)
336 {
337 if (j == insertLocation)
338 continue;
339 int jskip = j > insertLocation;
340 tilCoeffMatrix->SetValue(i - iskip, j - jskip,
341 m_coeffMatrix->GetValue(i, j));
342 }
343 }
344 }
345 else if (!fullbuffer && m_prevBasis.size())
346 {
348 m_prevBasis.size(), m_prevBasis.size(), 0.0, eSYMMETRIC);
349 Vmath::Vcopy(tilCoeffMatrix->GetStorageSize(), m_coeffMatrix->GetPtr(),
350 1, tilCoeffMatrix->GetPtr(), 1);
351 }
352
353 int n, info1 = 0, info2 = 0, info3 = 0;
354 if (y_s.size())
355 {
356 n = tilCoeffMatrix->GetRows();
357 Array<OneD, NekDouble> tilCoeffMatrixFactor(
358 tilCoeffMatrix->GetStorageSize());
359 Vmath::Vcopy(tilCoeffMatrix->GetStorageSize(), tilCoeffMatrix->GetPtr(),
360 1, tilCoeffMatrixFactor, 1);
361 Lapack::Dsptrf('U', n, tilCoeffMatrixFactor.get(), ipivot.get(), info1);
362 if (info1 == 0)
363 {
364 Vmath::Vcopy(n, y_s, 1, invMy_s, 1);
365 Lapack::Dsptrs('U', n, 1, tilCoeffMatrixFactor.get(), ipivot.get(),
366 invMy_s.get(), n, info2);
367 }
368 }
369 if (info1 || info2)
370 {
371 int latest = ResetKnownSolutionsToLatestOne();
372 y_s[0] = y_s[latest - (latest > insertLocation)];
373 invMy_s[0] = y_s[0];
374 insertLocation = m_numPrevSols % m_numSuccessiveRHS;
375 fullbuffer = (m_prevBasis.size() == m_numSuccessiveRHS);
376 }
377 NekDouble residual = 1.;
378 NekDouble epsilon = 10. * NekConstants::kNekZeroTol;
379 for (int i = 0; i < m_prevBasis.size() - fullbuffer; i++)
380 {
381 residual -= y_s[i] * invMy_s[i];
382 }
383 if (m_verbose && m_root)
384 cout << "SuccessiveRHS: residual " << residual;
385 if (residual < epsilon)
386 {
387 if (m_verbose && m_root)
388 cout << " < " << epsilon << ", reject" << endl;
389 return;
390 }
391
392 // calculate new coefficient matrix and its factor
393 DNekMatSharedPtr newCoeffMatrix;
394 if (fullbuffer)
395 {
398 Vmath::Vcopy(m_coeffMatrix->GetStorageSize(), m_coeffMatrix->GetPtr(),
399 1, newCoeffMatrix->GetPtr(), 1);
400 newCoeffMatrix->SetValue(insertLocation, insertLocation, 1.);
401 for (int i = 0; i < m_numSuccessiveRHS; ++i)
402 {
403 if (i == insertLocation)
404 continue;
405 int iskip = i > insertLocation;
406 newCoeffMatrix->SetValue(insertLocation, i, y_s[i - iskip]);
407 }
408 }
409 else
410 {
412 m_prevBasis.size() + 1, m_prevBasis.size() + 1, 0.0, eSYMMETRIC);
413 newCoeffMatrix->SetValue(insertLocation, insertLocation, 1.);
414 for (int i = 0; i < m_prevBasis.size(); ++i)
415 {
416 newCoeffMatrix->SetValue(insertLocation, i, y_s[i]);
417 for (int j = i; j < m_prevBasis.size(); ++j)
418 {
419 newCoeffMatrix->SetValue(i, j, m_coeffMatrix->GetValue(i, j));
420 }
421 }
422 }
423 n = newCoeffMatrix->GetRows();
424 Array<OneD, NekDouble> coeffMatrixFactor(newCoeffMatrix->GetStorageSize());
425 Vmath::Vcopy(newCoeffMatrix->GetStorageSize(), newCoeffMatrix->GetPtr(), 1,
426 coeffMatrixFactor, 1);
427 Lapack::Dsptrf('U', n, coeffMatrixFactor.get(), ipivot.get(), info3);
428 if (info3)
429 {
430 if (m_verbose && m_root)
431 cout << " >= " << epsilon << ", reject (Dsptrf fails)" << endl;
432 return;
433 }
434 if (m_verbose && m_root)
435 cout << " >= " << epsilon << ", accept" << endl;
436
437 // if success, update basis, rhs, coefficient matrix, and its factor
438 if (m_prevBasis.size() < m_numSuccessiveRHS)
439 {
440 m_prevBasis.push_back(tmp = tmpAx_s + nDir);
441 if (isAconjugate)
442 {
443 m_prevLinSol.push_back(tmp);
444 }
445 else
446 {
447 Array<OneD, NekDouble> solution(nNonDir, 0.0);
448 m_prevLinSol.push_back(solution);
449 }
450 }
451 Vmath::Smul(nNonDir, 1. / sqrt(solNorm), tmp = newX + nDir, 1,
452 m_prevLinSol[insertLocation], 1);
453 if (!isAconjugate)
454 {
455 m_prevBasis[insertLocation] = tmpAx_s + nDir;
456 }
457 m_coeffMatrix = newCoeffMatrix;
458 m_coeffMatrixFactor = coeffMatrixFactor;
459 m_ipivot = ipivot;
461}
462
464{
466 {
467 m_rhs_magnitude = 1.0;
468 return;
469 }
470
471 NekDouble vExchange(0.0);
472 if (m_map.size() > 0)
473 {
474 vExchange =
475 Vmath::Dot2(pIn.GetDimension(), &pIn[0], &pIn[0], &m_map[0]);
476 }
477
478 m_expList.lock()->GetComm()->GetRowComm()->AllReduce(
480
481 // To ensure that very different rhs values are not being
482 // used in subsequent solvers such as the velocit solve in
483 // INC NS. If this works we then need to work out a better
484 // way to control this.
485 NekDouble new_rhs_mag = (vExchange > 1e-6) ? vExchange : 1.0;
486
488 {
489 m_rhs_magnitude = new_rhs_mag;
490 }
491 else
492 {
494 (1.0 - m_rhs_mag_sm) * new_rhs_mag);
495 }
496}
497
498} // namespace MultiRegions
499} // namespace Nektar
#define WARNINGL0(condition, msg)
Definition: ErrorUtil.hpp:222
void DefineAssembleLoc(FuncPointerT func, ObjectPointerT obj)
Definition: NekSys.h:122
void DefineNekSysLhsEval(FuncPointerT func, ObjectPointerT obj)
Definition: NekSys.h:108
void DefineNekSysPrecon(FuncPointerT func, ObjectPointerT obj)
Definition: NekSys.h:115
static std::string RegisterEnumValue(std::string pEnum, std::string pString, int pEnumValue)
Registers an enumeration value.
static std::string RegisterDefaultSolverInfo(const std::string &pName, const std::string &pValue)
Registers the default string value of a solver info property.
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
A global linear system.
Definition: GlobalLinSys.h:72
const std::weak_ptr< ExpList > m_expList
Local Matrix System.
Definition: GlobalLinSys.h:124
GlobalLinSysIterative(const GlobalLinSysKey &pKey, const std::weak_ptr< ExpList > &pExpList, const std::shared_ptr< AssemblyMap > &pLocToGloMap)
Constructor for full direct matrix solve.
std::vector< Array< OneD, NekDouble > > m_prevBasis
LibUtilities::NekLinSysIterSharedPtr m_linsol
void DoProjection(const int pNumRows, const Array< OneD, const NekDouble > &pInput, Array< OneD, NekDouble > &pOutput, const int pNumDir, const NekDouble tol, const bool isAconjugate)
projection technique
void UpdateKnownSolutions(const int pGlobalBndDofs, const Array< OneD, const NekDouble > &pSolution, const int pNumDirBndDofs, const bool isAconjugate)
void DoAssembleLocFlag(const Array< OneD, NekDouble > &pInput, Array< OneD, NekDouble > &pOutput, const bool &ZeroDir)
void DoPreconditionerFlag(const Array< OneD, NekDouble > &pInput, Array< OneD, NekDouble > &pOutput, const bool &isLocal=false)
std::vector< Array< OneD, NekDouble > > m_prevLinSol
Storage for solutions to previous linear problems.
void DoMatrixMultiplyFlag(const Array< OneD, NekDouble > &pInput, Array< OneD, NekDouble > &pOutput, const bool &controlFlag)
bool m_useProjection
Whether to apply projection technique.
NekDouble m_tolerance
Tolerance of iterative solver.
Array< OneD, int > m_map
Global to universal unique map.
NekDouble m_rhs_magnitude
dot product of rhs to normalise stopping criterion
std::string m_linSysIterSolver
Iterative solver: Conjugate Gradient, GMRES.
void Set_Rhs_Magnitude(const NekVector< NekDouble > &pIn)
NekDouble m_rhs_mag_sm
cnt to how many times rhs_magnitude is called
StdRegions::MatrixType GetMatrixType() const
Return the matrix type.
unsigned int GetDimension() const
Returns the number of dimensions for the point.
Definition: NekVector.cpp:201
static void Dsptrs(const char &uplo, const int &n, const int &nrhs, const double *ap, const int *ipiv, double *b, const int &ldb, int &info)
Solve a real packed-symmetric matrix problem using Bunch-Kaufman pivoting.
Definition: Lapack.hpp:154
static void Dsptrf(const char &uplo, const int &n, double *ap, int *ipiv, int &info)
factor a real packed-symmetric matrix using Bunch-Kaufman pivoting.
Definition: Lapack.hpp:128
std::shared_ptr< Comm > CommSharedPtr
Pointer to a Communicator object.
Definition: Comm.h:57
static PreconditionerSharedPtr NullPreconditionerSharedPtr
@ eGMRESLoc
GMRES in Local storage.
@ eConjugateGradient
Conjugate Gradient.
static const NekDouble kNekUnsetDouble
static const NekDouble kNekSparseNonZeroTol
static const NekDouble kNekZeroTol
const char *const MatrixTypeMap[]
Definition: StdRegions.hpp:145
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:2
std::shared_ptr< DNekMat > DNekMatSharedPtr
Definition: NekTypeDefs.hpp:75
double NekDouble
T Dot2(int n, const T *w, const T *x, const int *y)
dot2 (vector times vector times vector): z = w*x*y
Definition: Vmath.cpp:1142
void Smul(int n, const T alpha, const T *x, const int incx, T *y, const int incy)
Scalar multiply y = alpha*x.
Definition: Vmath.cpp:245
void Zero(int n, T *x, const int incx)
Zero vector.
Definition: Vmath.cpp:487
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition: Vmath.cpp:1191
scalarT< T > abs(scalarT< T > in)
Definition: scalar.hpp:298
scalarT< T > sqrt(scalarT< T > in)
Definition: scalar.hpp:294