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
40{
43 "LinSysIterSolver", "ConjugateGradient",
46 "LinSysIterSolver", "ConjugateGradientLoc",
48 LibUtilities::SessionReader::RegisterEnumValue("LinSysIterSolver", "GMRES",
51 "LinSysIterSolver", "GMRESLoc", MultiRegions::eGMRESLoc),
52};
53
56 "ConjugateGradient");
57
58/**
59 * @class GlobalLinSysIterative
60 *
61 * Solves a linear system using iterative methods.
62 */
63
64/// Constructor for full iterative matrix solve.
66 const GlobalLinSysKey &pKey, const std::weak_ptr<ExpList> &pExpList,
67 const std::shared_ptr<AssemblyMap> &pLocToGloMap)
68 : GlobalLinSys(pKey, pExpList, pLocToGloMap),
69 m_rhs_magnitude(NekConstants::kNekUnsetDouble), m_rhs_mag_sm(0.9),
70 m_precon(NullPreconditionerSharedPtr), m_totalIterations(0),
71 m_useProjection(false), m_numPrevSols(0)
72{
73 m_tolerance = pLocToGloMap->GetIterativeTolerance();
74 m_isAbsoluteTolerance = pLocToGloMap->IsAbsoluteTolerance();
75 m_linSysIterSolver = pLocToGloMap->GetLinSysIterSolver();
76
78 m_expList.lock()->GetComm()->GetRowComm();
79 m_root = (vComm->GetRank()) ? false : true;
80
81 m_numSuccessiveRHS = pLocToGloMap->GetSuccessiveRHS();
85
86 // Check for advection matrix and switch to GMRES, if not already used
89 m_matrixType.find("AdvectionDiffusionReaction") != string::npos;
90
92 !m_linSysIterSolver.compare("ConjugateGradient"))
93 {
94 m_linSysIterSolver = "GMRES";
96 false,
97 "Detected ConjugateGradient solver and a "
98 "Advection-Diffusion-Reaction matrix. "
99 "Switchted to a GMRES solver for this non-symmetric matrix type. "
100 "Change LinSysIterSolver to GMRES in the session file to suppress "
101 "this warning.");
102 }
103
104 if (m_isAconjugate && m_linSysIterSolver.compare("GMRES") == 0 &&
105 m_linSysIterSolver.compare("GMRESLoc") == 0)
106 {
107 WARNINGL0(false, "To use A-conjugate projection, the matrix "
108 "should be symmetric positive definite.");
109 }
110
112 this);
114 this);
116 this);
117}
118
120{
121}
122
123/**
124 * This method implements projection techniques
125 * in order to speed up successive linear solves with
126 * right-hand sides arising from time-dependent discretisations.
127 * (P.F.Fischer, Comput. Methods Appl. Mech. Engrg. 163, 1998)
128 */
130 const int nGlobal, const Array<OneD, const NekDouble> &pInput,
131 Array<OneD, NekDouble> &pOutput, const int nDir, const NekDouble tol,
132 const bool isAconjugate)
133{
134 int numIterations = 0;
135 if (0 == m_numPrevSols)
136 {
137 // no previous solutions found
138 numIterations =
139 m_linsol->SolveSystem(nGlobal, pInput, pOutput, nDir, tol);
140 }
141 else
142 {
143 // Get the communicator for performing data exchanges
145 m_expList.lock()->GetComm()->GetRowComm();
146
147 // Get vector sizes
148 int nNonDir = nGlobal - nDir;
149
150 // check the input vector (rhs) is not zero
152
153 NekDouble rhsNorm =
154 Vmath::Dot2(nNonDir, pInput + nDir, pInput + nDir, m_map + nDir);
155
156 vComm->AllReduce(rhsNorm, Nektar::LibUtilities::ReduceSum);
157
158 if (rhsNorm < tol * tol * m_rhs_magnitude)
159 {
160 Vmath::Zero(nNonDir, tmp = pOutput + nDir, 1);
161 if (m_verbose && m_root)
162 {
163 cout << "No iterations made"
164 << " using tolerance of " << tol
165 << " (error = " << sqrt(rhsNorm / m_rhs_magnitude)
166 << ", rhs_mag = " << sqrt(m_rhs_magnitude) << ")" << endl;
167 }
168 return;
169 }
170
171 // Create NekVector wrappers for linear algebra operations
172 NekVector<NekDouble> b(nNonDir, pInput + nDir, eWrapper);
173 NekVector<NekDouble> x(nNonDir, tmp = pOutput + nDir, eWrapper);
174 // Allocate array storage
175 Array<OneD, NekDouble> px_s(nGlobal, 0.0);
176 Array<OneD, NekDouble> pb_s(nGlobal, 0.0);
177 Array<OneD, NekDouble> tmpAx_s(nGlobal, 0.0);
178 Array<OneD, NekDouble> tmpx_s(nGlobal, 0.0);
179
180 NekVector<NekDouble> pb(nNonDir, tmp = pb_s + nDir, eWrapper);
181 NekVector<NekDouble> px(nNonDir, tmp = px_s + nDir, eWrapper);
182 NekVector<NekDouble> tmpAx(nNonDir, tmp = tmpAx_s + nDir, eWrapper);
183 NekVector<NekDouble> tmpx(nNonDir, tmp = tmpx_s + nDir, eWrapper);
184
185 // notation follows the paper cited:
186 // \alpha_i = \tilda{x_i}^T b^n
187 // projected x, px = \sum \alpha_i \tilda{x_i}
188
189 Array<OneD, NekDouble> alpha(m_prevBasis.size(), 0.0);
190 Array<OneD, NekDouble> alphaback(m_prevBasis.size(), 0.0);
191 for (int i = 0; i < m_prevBasis.size(); i++)
192 {
193 alpha[i] = Vmath::Dot2(nNonDir, m_prevBasis[i], pInput + nDir,
194 m_map + nDir);
195 }
196 vComm->AllReduce(alpha, Nektar::LibUtilities::ReduceSum);
197 int n = m_prevBasis.size(), info = -1;
198 Vmath::Vcopy(m_prevBasis.size(), alpha, 1, alphaback, 1);
199 Lapack::Dsptrs('U', n, 1, m_coeffMatrixFactor.get(), m_ipivot.get(),
200 alpha.get(), n, info);
201 if (info != 0)
202 {
203 // Dsptrs fails, only keep the latest solution
204 int latest = ResetKnownSolutionsToLatestOne();
205 alpha[0] = alphaback[latest];
206 }
207 for (int i = 0; i < m_prevBasis.size(); ++i)
208 {
210 px += alpha[i] * xi;
211 }
212
213 // pb = b^n - A px
214 Vmath::Vcopy(nNonDir, pInput.get() + nDir, 1, pb_s.get() + nDir, 1);
215
216 DoMatrixMultiplyFlag(px_s, tmpAx_s, false);
217
218 pb -= tmpAx;
219
220 if (m_verbose)
221 {
222 if (m_root)
223 {
224 cout << "SuccessiveRHS: " << m_prevBasis.size()
225 << "-bases projection reduces L2-norm of RHS from "
226 << std::sqrt(rhsNorm) << " to ";
227 }
228 NekDouble tmprhsNorm =
229 Vmath::Dot2(nNonDir, pb_s + nDir, pb_s + nDir, m_map + nDir);
230 vComm->AllReduce(tmprhsNorm, Nektar::LibUtilities::ReduceSum);
231 if (m_root)
232 {
233 cout << std::sqrt(tmprhsNorm) << endl;
234 }
235 }
236
237 // solve the system with projected rhs
238 numIterations = m_linsol->SolveSystem(nGlobal, pb_s, tmpx_s, nDir, tol);
239
240 // remainder solution + projection of previous solutions
241 x = tmpx + px;
242 }
243 // save the auxiliary solution to prev. known solutions
244 if (numIterations)
245 {
246 UpdateKnownSolutions(nGlobal, pOutput, nDir, isAconjugate);
247 }
248}
249
251{
252 if (m_numPrevSols == 0)
253 {
254 return -1;
255 }
259 m_prevBasis.clear();
260 m_prevLinSol.clear();
261 m_prevBasis.push_back(b);
262 m_prevLinSol.push_back(x);
263 m_numPrevSols = 1;
264 return latest;
265}
266
267/**
268 * Updates the storage of previously known solutions.
269 * Performs normalisation of input vector wrt A-norm.
270 */
272 const int nGlobal, const Array<OneD, const NekDouble> &newX, const int nDir,
273 const bool isAconjugate)
274{
275 // Get vector sizes
276 int nNonDir = nGlobal - nDir;
277 int insertLocation = m_numPrevSols % m_numSuccessiveRHS;
278 int fullbuffer = (m_prevBasis.size() == m_numSuccessiveRHS);
279
280 // Get the communicator for performing data exchanges
282 m_expList.lock()->GetComm()->GetRowComm();
283
284 Array<OneD, NekDouble> tmpAx_s(nGlobal, 0.0);
285 Array<OneD, NekDouble> y_s(m_prevBasis.size() - fullbuffer, 0.0);
286 Array<OneD, NekDouble> invMy_s(y_s.size(), 0.0);
288 Array<OneD, NekDouble> tmp, newBasis;
289
290 DoMatrixMultiplyFlag(newX, tmpAx_s, false);
291
292 if (isAconjugate)
293 {
294 newBasis = newX + nDir;
295 }
296 else
297 {
298 newBasis = tmpAx_s + nDir;
299 }
300
301 // Check the solution is non-zero
302 NekDouble solNorm =
303 Vmath::Dot2(nNonDir, newBasis, tmpAx_s + nDir, m_map + nDir);
304 vComm->AllReduce(solNorm, Nektar::LibUtilities::ReduceSum);
305
307 {
308 return;
309 }
310
311 // normalisation of A x
312 Vmath::Smul(nNonDir, 1.0 / sqrt(solNorm), tmpAx_s + nDir, 1,
313 tmp = tmpAx_s + nDir, 1);
314
315 for (int i = 0; i < m_prevBasis.size(); ++i)
316 {
317 if (i == insertLocation)
318 {
319 continue;
320 }
321 int skip = i > insertLocation;
322 y_s[i - skip] =
323 Vmath::Dot2(nNonDir, m_prevBasis[i], tmpAx_s + nDir, m_map + nDir);
324 }
325 vComm->AllReduce(y_s, Nektar::LibUtilities::ReduceSum);
326
327 // check if linearly dependent
328 DNekMatSharedPtr tilCoeffMatrix;
329 if (fullbuffer && m_numSuccessiveRHS > 1)
330 {
333 for (int i = 0; i < m_numSuccessiveRHS; ++i)
334 {
335 if (i == insertLocation)
336 {
337 continue;
338 }
339 int iskip = i > insertLocation;
340 for (int j = i; j < m_numSuccessiveRHS; ++j)
341 {
342 if (j == insertLocation)
343 {
344 continue;
345 }
346 int jskip = j > insertLocation;
347 tilCoeffMatrix->SetValue(i - iskip, j - jskip,
348 m_coeffMatrix->GetValue(i, j));
349 }
350 }
351 }
352 else if (!fullbuffer && m_prevBasis.size())
353 {
355 m_prevBasis.size(), m_prevBasis.size(), 0.0, eSYMMETRIC);
356 Vmath::Vcopy(tilCoeffMatrix->GetStorageSize(), m_coeffMatrix->GetPtr(),
357 1, tilCoeffMatrix->GetPtr(), 1);
358 }
359
360 int n, info1 = 0, info2 = 0, info3 = 0;
361 if (y_s.size())
362 {
363 n = tilCoeffMatrix->GetRows();
364 Array<OneD, NekDouble> tilCoeffMatrixFactor(
365 tilCoeffMatrix->GetStorageSize());
366 Vmath::Vcopy(tilCoeffMatrix->GetStorageSize(), tilCoeffMatrix->GetPtr(),
367 1, tilCoeffMatrixFactor, 1);
368 Lapack::Dsptrf('U', n, tilCoeffMatrixFactor.get(), ipivot.get(), info1);
369 if (info1 == 0)
370 {
371 Vmath::Vcopy(n, y_s, 1, invMy_s, 1);
372 Lapack::Dsptrs('U', n, 1, tilCoeffMatrixFactor.get(), ipivot.get(),
373 invMy_s.get(), n, info2);
374 }
375 }
376 if (info1 || info2)
377 {
378 int latest = ResetKnownSolutionsToLatestOne();
379 y_s[0] = y_s[latest - (latest > insertLocation)];
380 invMy_s[0] = y_s[0];
381 insertLocation = m_numPrevSols % m_numSuccessiveRHS;
382 fullbuffer = (m_prevBasis.size() == m_numSuccessiveRHS);
383 }
384 NekDouble residual = 1.;
385 NekDouble epsilon = 10. * NekConstants::kNekZeroTol;
386 for (int i = 0; i < m_prevBasis.size() - fullbuffer; i++)
387 {
388 residual -= y_s[i] * invMy_s[i];
389 }
390 if (m_verbose && m_root)
391 {
392 cout << "SuccessiveRHS: residual " << residual;
393 }
394 if (residual < epsilon)
395 {
396 if (m_verbose && m_root)
397 {
398 cout << " < " << epsilon << ", reject" << endl;
399 }
400 return;
401 }
402
403 // calculate new coefficient matrix and its factor
404 DNekMatSharedPtr newCoeffMatrix;
405 if (fullbuffer)
406 {
409 Vmath::Vcopy(m_coeffMatrix->GetStorageSize(), m_coeffMatrix->GetPtr(),
410 1, newCoeffMatrix->GetPtr(), 1);
411 newCoeffMatrix->SetValue(insertLocation, insertLocation, 1.);
412 for (int i = 0; i < m_numSuccessiveRHS; ++i)
413 {
414 if (i == insertLocation)
415 {
416 continue;
417 }
418 int iskip = i > insertLocation;
419 newCoeffMatrix->SetValue(insertLocation, i, y_s[i - iskip]);
420 }
421 }
422 else
423 {
425 m_prevBasis.size() + 1, m_prevBasis.size() + 1, 0.0, eSYMMETRIC);
426 newCoeffMatrix->SetValue(insertLocation, insertLocation, 1.);
427 for (int i = 0; i < m_prevBasis.size(); ++i)
428 {
429 newCoeffMatrix->SetValue(insertLocation, i, y_s[i]);
430 for (int j = i; j < m_prevBasis.size(); ++j)
431 {
432 newCoeffMatrix->SetValue(i, j, m_coeffMatrix->GetValue(i, j));
433 }
434 }
435 }
436 n = newCoeffMatrix->GetRows();
437 Array<OneD, NekDouble> coeffMatrixFactor(newCoeffMatrix->GetStorageSize());
438 Vmath::Vcopy(newCoeffMatrix->GetStorageSize(), newCoeffMatrix->GetPtr(), 1,
439 coeffMatrixFactor, 1);
440 Lapack::Dsptrf('U', n, coeffMatrixFactor.get(), ipivot.get(), info3);
441 if (info3)
442 {
443 if (m_verbose && m_root)
444 {
445 cout << " >= " << epsilon << ", reject (Dsptrf fails)" << endl;
446 }
447 return;
448 }
449 if (m_verbose && m_root)
450 {
451 cout << " >= " << epsilon << ", accept" << endl;
452 }
453
454 // if success, update basis, rhs, coefficient matrix, and its factor
455 if (m_prevBasis.size() < m_numSuccessiveRHS)
456 {
457 m_prevBasis.push_back(tmp = tmpAx_s + nDir);
458 if (isAconjugate)
459 {
460 m_prevLinSol.push_back(tmp);
461 }
462 else
463 {
464 Array<OneD, NekDouble> solution(nNonDir, 0.0);
465 m_prevLinSol.push_back(solution);
466 }
467 }
468 Vmath::Smul(nNonDir, 1. / sqrt(solNorm), tmp = newX + nDir, 1,
469 m_prevLinSol[insertLocation], 1);
470 if (!isAconjugate)
471 {
472 m_prevBasis[insertLocation] = tmpAx_s + nDir;
473 }
474 m_coeffMatrix = newCoeffMatrix;
475 m_coeffMatrixFactor = coeffMatrixFactor;
476 m_ipivot = ipivot;
478}
479
480} // namespace Nektar::MultiRegions
#define WARNINGL0(condition, msg)
Definition: ErrorUtil.hpp:215
void DefineAssembleLoc(FuncPointerT func, ObjectPointerT obj)
Definition: NekSys.h:120
void DefineNekSysLhsEval(FuncPointerT func, ObjectPointerT obj)
Definition: NekSys.h:106
void DefineNekSysPrecon(FuncPointerT func, ObjectPointerT obj)
Definition: NekSys.h:113
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:70
const std::weak_ptr< ExpList > m_expList
Local Matrix System.
Definition: GlobalLinSys.h:122
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.
StdRegions::MatrixType GetMatrixType() const
Return the matrix type.
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:55
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:139
std::shared_ptr< DNekMat > DNekMatSharedPtr
Definition: NekTypeDefs.hpp:75
double NekDouble
T Dot2(int n, const T *w, const T *x, const int *y)
dot product
Definition: Vmath.hpp:790
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.hpp:100
void Zero(int n, T *x, const int incx)
Zero vector.
Definition: Vmath.hpp:273
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition: Vmath.hpp:825
scalarT< T > abs(scalarT< T > in)
Definition: scalar.hpp:298
scalarT< T > sqrt(scalarT< T > in)
Definition: scalar.hpp:294