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