Nektar++
Loading...
Searching...
No Matches
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
87 m_isNonSymmetricLinSys = m_matrixType.find("Advection") != string::npos;
88
90 !m_linSysIterSolver.compare("ConjugateGradient"))
91 {
92 m_linSysIterSolver = "GMRES";
94 false,
95 "Detected ConjugateGradient solver and a "
96 "Advection-type matrix. "
97 "Switchted to a GMRES solver for this non-symmetric matrix type. "
98 "Change SolverInfo 'LinSysIterSolver' to GMRES in the session file "
99 "to suppress this warning.");
100 }
101
102 if (m_isAconjugate && m_linSysIterSolver.compare("GMRES") == 0 &&
103 m_linSysIterSolver.compare("GMRESLoc") == 0)
104 {
105 WARNINGL0(false, "To use A-conjugate projection, the matrix "
106 "should be symmetric positive definite.");
107 }
108
110 this);
112 this);
114 this);
115}
116
120
121/**
122 * This method implements projection techniques
123 * in order to speed up successive linear solves with
124 * right-hand sides arising from time-dependent discretisations.
125 * (P.F.Fischer, Comput. Methods Appl. Mech. Engrg. 163, 1998)
126 */
128 const int nGlobal, const Array<OneD, const NekDouble> &pInput,
129 Array<OneD, NekDouble> &pOutput, const int nDir, const bool isAconjugate)
130{
131 int numIterations = 0;
132 if (m_numPrevSols == 0)
133 {
134 // no previous solutions found
135 numIterations = m_linsol->SolveSystem(nGlobal, pInput, pOutput, nDir);
136 }
137 else
138 {
139 // Get the communicator for performing data exchanges
141 m_expList.lock()->GetComm()->GetRowComm();
142
143 // Get vector sizes
144 int nNonDir = nGlobal - nDir;
145
146 // check the input vector (rhs) is not zero
148
149 NekDouble rhsNorm =
150 Vmath::Dot2(nNonDir, pInput + nDir, pInput + nDir, m_map + nDir);
151
152 vComm->AllReduce(rhsNorm, Nektar::LibUtilities::ReduceSum);
153
154 NekDouble tol = m_linsol->GetNekLinSysTolerance();
155 if (rhsNorm < tol * tol * m_rhs_magnitude)
156 {
157 Vmath::Zero(nNonDir, tmp = pOutput + nDir, 1);
158 if (m_verbose && m_root)
159 {
160 cout << "No iterations made"
161 << " using tolerance of " << tol
162 << " (error = " << sqrt(rhsNorm / m_rhs_magnitude)
163 << ", rhs_mag = " << sqrt(m_rhs_magnitude) << ")" << endl;
164 }
165 return;
166 }
167
168 // Create NekVector wrappers for linear algebra operations
169 NekVector<NekDouble> b(nNonDir, pInput + nDir, eWrapper);
170 NekVector<NekDouble> x(nNonDir, tmp = pOutput + nDir, eWrapper);
171 // Allocate array storage
172 Array<OneD, NekDouble> px_s(nGlobal, 0.0);
173 Array<OneD, NekDouble> pb_s(nGlobal, 0.0);
174 Array<OneD, NekDouble> tmpAx_s(nGlobal, 0.0);
175 Array<OneD, NekDouble> tmpx_s(nGlobal, 0.0);
176
177 NekVector<NekDouble> pb(nNonDir, tmp = pb_s + nDir, eWrapper);
178 NekVector<NekDouble> px(nNonDir, tmp = px_s + nDir, eWrapper);
179 NekVector<NekDouble> tmpAx(nNonDir, tmp = tmpAx_s + nDir, eWrapper);
180 NekVector<NekDouble> tmpx(nNonDir, tmp = tmpx_s + nDir, eWrapper);
181
182 // notation follows the paper cited:
183 // \alpha_i = \tilda{x_i}^T b^n
184 // projected x, px = \sum \alpha_i \tilda{x_i}
185
186 Array<OneD, NekDouble> alpha(m_prevBasis.size(), 0.0);
187 Array<OneD, NekDouble> alphaback(m_prevBasis.size(), 0.0);
188 for (int i = 0; i < m_prevBasis.size(); i++)
189 {
190 alpha[i] = Vmath::Dot2(nNonDir, m_prevBasis[i], pInput + nDir,
191 m_map + nDir);
192 }
193 vComm->AllReduce(alpha, Nektar::LibUtilities::ReduceSum);
194 int n = m_prevBasis.size(), info = -1;
195 Vmath::Vcopy(m_prevBasis.size(), alpha, 1, alphaback, 1);
196 Lapack::Dsptrs('U', n, 1, m_coeffMatrixFactor.data(), m_ipivot.data(),
197 alpha.data(), n, info);
198 if (info != 0)
199 {
200 // Dsptrs fails, only keep the latest solution
201 int latest = ResetKnownSolutionsToLatestOne();
202 alpha[0] = alphaback[latest];
203 }
204 for (int i = 0; i < m_prevBasis.size(); ++i)
205 {
207 px += alpha[i] * xi;
208 }
209
210 // pb = b^n - A px
211 Vmath::Vcopy(nNonDir, pInput.data() + nDir, 1, pb_s.data() + nDir, 1);
212
213 DoMatrixMultiplyFlag(px_s, tmpAx_s, false);
214
215 pb -= tmpAx;
216
217 if (m_verbose)
218 {
219 if (m_root)
220 {
221 cout << "SuccessiveRHS: " << m_prevBasis.size()
222 << "-bases projection reduces L2-norm of RHS from "
223 << std::sqrt(rhsNorm) << " to ";
224 }
225 NekDouble tmprhsNorm =
226 Vmath::Dot2(nNonDir, pb_s + nDir, pb_s + nDir, m_map + nDir);
227 vComm->AllReduce(tmprhsNorm, Nektar::LibUtilities::ReduceSum);
228 if (m_root)
229 {
230 cout << std::sqrt(tmprhsNorm) << endl;
231 }
232 }
233
234 // solve the system with projected rhs
235 numIterations = m_linsol->SolveSystem(nGlobal, pb_s, tmpx_s, nDir);
236
237 // remainder solution + projection of previous solutions
238 x = tmpx + px;
239 }
240 // save the auxiliary solution to prev. known solutions
241 if (numIterations)
242 {
243 UpdateKnownSolutions(nGlobal, pOutput, nDir, isAconjugate);
244 }
245}
246
248{
249 if (m_numPrevSols == 0)
250 {
251 return -1;
252 }
256 m_prevBasis.clear();
257 m_prevLinSol.clear();
258 m_prevBasis.push_back(b);
259 m_prevLinSol.push_back(x);
260 m_numPrevSols = 1;
261 return latest;
262}
263
264/**
265 * Updates the storage of previously known solutions.
266 * Performs normalisation of input vector wrt A-norm.
267 */
269 const int nGlobal, const Array<OneD, const NekDouble> &newX, const int nDir,
270 const bool isAconjugate)
271{
272 // Get vector sizes
273 int nNonDir = nGlobal - nDir;
274 int insertLocation = m_numPrevSols % m_numSuccessiveRHS;
275 int fullbuffer = (m_prevBasis.size() == m_numSuccessiveRHS);
276
277 // Get the communicator for performing data exchanges
279 m_expList.lock()->GetComm()->GetRowComm();
280
281 Array<OneD, NekDouble> tmpAx_s(nGlobal, 0.0);
282 Array<OneD, NekDouble> y_s(m_prevBasis.size() - fullbuffer, 0.0);
283 Array<OneD, NekDouble> invMy_s(y_s.size(), 0.0);
285 Array<OneD, NekDouble> tmp, newBasis;
286
287 DoMatrixMultiplyFlag(newX, tmpAx_s, false);
288
289 if (isAconjugate)
290 {
291 newBasis = newX + nDir;
292 }
293 else
294 {
295 newBasis = tmpAx_s + nDir;
296 }
297
298 // Check the solution is non-zero
299 NekDouble solNorm =
300 Vmath::Dot2(nNonDir, newBasis, tmpAx_s + nDir, m_map + nDir);
301 vComm->AllReduce(solNorm, Nektar::LibUtilities::ReduceSum);
302
304 {
305 return;
306 }
307
308 // normalisation of A x
309 Vmath::Smul(nNonDir, 1.0 / sqrt(solNorm), tmpAx_s + nDir, 1,
310 tmp = tmpAx_s + nDir, 1);
311
312 for (int i = 0; i < m_prevBasis.size(); ++i)
313 {
314 if (i == insertLocation)
315 {
316 continue;
317 }
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 {
334 continue;
335 }
336 int iskip = i > insertLocation;
337 for (int j = i; j < m_numSuccessiveRHS; ++j)
338 {
339 if (j == insertLocation)
340 {
341 continue;
342 }
343 int jskip = j > insertLocation;
344 tilCoeffMatrix->SetValue(i - iskip, j - jskip,
345 m_coeffMatrix->GetValue(i, j));
346 }
347 }
348 }
349 else if (!fullbuffer && m_prevBasis.size())
350 {
352 m_prevBasis.size(), m_prevBasis.size(), 0.0, eSYMMETRIC);
353 Vmath::Vcopy(tilCoeffMatrix->GetStorageSize(), m_coeffMatrix->GetPtr(),
354 1, tilCoeffMatrix->GetPtr(), 1);
355 }
356
357 int n, info1 = 0, info2 = 0, info3 = 0;
358 if (y_s.size())
359 {
360 n = tilCoeffMatrix->GetRows();
361 Array<OneD, NekDouble> tilCoeffMatrixFactor(
362 tilCoeffMatrix->GetStorageSize());
363 Vmath::Vcopy(tilCoeffMatrix->GetStorageSize(), tilCoeffMatrix->GetPtr(),
364 1, tilCoeffMatrixFactor, 1);
365 Lapack::Dsptrf('U', n, tilCoeffMatrixFactor.data(), ipivot.data(),
366 info1);
367 if (info1 == 0)
368 {
369 Vmath::Vcopy(n, y_s, 1, invMy_s, 1);
370 Lapack::Dsptrs('U', n, 1, tilCoeffMatrixFactor.data(),
371 ipivot.data(), invMy_s.data(), 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.data(), ipivot.data(), 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)
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.
const std::weak_ptr< ExpList > m_expList
Local Matrix System.
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 kNekSparseNonZeroTol
static const NekDouble kNekZeroTol
const char *const MatrixTypeMap[]
std::shared_ptr< DNekMat > DNekMatSharedPtr
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 > sqrt(scalarT< T > in)
Definition scalar.hpp:290