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 
37 using namespace std;
38 
39 namespace Nektar
40 {
41 namespace MultiRegions
42 {
43 std::string GlobalLinSysIterative::IteratSolverlookupIds[2] = {
44  LibUtilities::SessionReader::RegisterEnumValue(
45  "LinSysIterSolver", "ConjugateGradient",
47  LibUtilities::SessionReader::RegisterEnumValue("LinSysIterSolver", "GMRES",
49 };
50 
51 std::string GlobalLinSysIterative::IteratSolverdef =
52  LibUtilities::SessionReader::RegisterDefaultSolverInfo("LinSysIterSolver",
53  "ConjugateGradient");
54 
55 /**
56  * @class GlobalLinSysIterative
57  *
58  * Solves a linear system using iterative methods.
59  */
60 
61 /// Constructor for full direct matrix solve.
62 GlobalLinSysIterative::GlobalLinSysIterative(
63  const GlobalLinSysKey &pKey, const std::weak_ptr<ExpList> &pExpList,
64  const std::shared_ptr<AssemblyMap> &pLocToGloMap)
65  : GlobalLinSys(pKey, pExpList, pLocToGloMap),
66  m_rhs_magnitude(NekConstants::kNekUnsetDouble), m_rhs_mag_sm(0.9),
67  m_precon(NullPreconditionerSharedPtr), m_totalIterations(0),
68  m_useProjection(false), m_numPrevSols(0)
69 {
70  m_tolerance = pLocToGloMap->GetIterativeTolerance();
71  m_maxiter = pLocToGloMap->GetMaxIterations();
72  m_linSysIterSolver = pLocToGloMap->GetLinSysIterSolver();
73 
75  m_expList.lock()->GetComm()->GetRowComm();
76  m_root = (vComm->GetRank()) ? false : true;
77 
78  m_numSuccessiveRHS = pLocToGloMap->GetSuccessiveRHS();
82 
83  if (m_isAconjugate && 0 == m_linSysIterSolver.compare("GMRES"))
84  {
85  WARNINGL0(false, "To use A-conjugate projection, the matrix should be "
86  "symmetric positive definite.");
87  }
88 }
89 
91 {
92 }
93 
94 /**
95  *
96  */
98  const int nGlobal, const Array<OneD, const NekDouble> &pInput,
99  Array<OneD, NekDouble> &pOutput, const AssemblyMapSharedPtr &plocToGloMap,
100  const int nDir)
101 {
102  if (!m_linsol)
103  {
105  m_expList.lock()->GetComm()->GetRowComm();
107  m_expList.lock()->GetSession();
108 
109  // Check such a module exists for this equation.
112  "NekLinSysIter '" + m_linSysIterSolver +
113  "' is not defined.\n");
115  m_linSysIterSolver, pSession, v_Comm, nGlobal - nDir,
117 
122  m_linsol->SetSysOperators(m_NekSysOp);
123  v_UniqueMap();
124  m_linsol->setUniversalUniqueMap(m_map);
125  }
126 
127  if (!m_precon)
128  {
129  m_precon = CreatePrecon(plocToGloMap);
130  m_precon->BuildPreconditioner();
131  }
132 
133  m_linsol->setRhsMagnitude(m_rhs_magnitude);
134  if (m_useProjection)
135  {
136  DoProjection(nGlobal, pInput, pOutput, nDir, m_tolerance,
138  }
139  else
140  {
141  m_linsol->SolveSystem(nGlobal, pInput, pOutput, nDir, m_tolerance);
142  }
143 }
144 
145 /**
146  * This method implements projection techniques
147  * in order to speed up successive linear solves with
148  * right-hand sides arising from time-dependent discretisations.
149  * (P.F.Fischer, Comput. Methods Appl. Mech. Engrg. 163, 1998)
150  */
152  const int nGlobal, const Array<OneD, const NekDouble> &pInput,
153  Array<OneD, NekDouble> &pOutput, const int nDir, const NekDouble tol,
154  const bool isAconjugate)
155 {
156  int numIterations = 0;
157  if (0 == m_numPrevSols)
158  {
159  // no previous solutions found
160  numIterations =
161  m_linsol->SolveSystem(nGlobal, pInput, pOutput, nDir, tol);
162  }
163  else
164  {
165  // Get the communicator for performing data exchanges
167  m_expList.lock()->GetComm()->GetRowComm();
168 
169  // Get vector sizes
170  int nNonDir = nGlobal - nDir;
171 
172  // check the input vector (rhs) is not zero
174 
175  NekDouble rhsNorm =
176  Vmath::Dot2(nNonDir, pInput + nDir, pInput + nDir, m_map + nDir);
177 
178  vComm->AllReduce(rhsNorm, Nektar::LibUtilities::ReduceSum);
179 
180  if (rhsNorm < tol * tol * m_rhs_magnitude)
181  {
182  Vmath::Zero(nNonDir, tmp = pOutput + nDir, 1);
183  if (m_verbose && m_root)
184  {
185  cout << "No iterations made"
186  << " using tolerance of " << tol
187  << " (error = " << sqrt(rhsNorm / m_rhs_magnitude)
188  << ", rhs_mag = " << sqrt(m_rhs_magnitude) << ")" << endl;
189  }
190  return;
191  }
192 
193  // Create NekVector wrappers for linear algebra operations
194  NekVector<NekDouble> b(nNonDir, pInput + nDir, eWrapper);
195  NekVector<NekDouble> x(nNonDir, tmp = pOutput + nDir, eWrapper);
196  // Allocate array storage
197  Array<OneD, NekDouble> px_s(nGlobal, 0.0);
198  Array<OneD, NekDouble> pb_s(nGlobal, 0.0);
199  Array<OneD, NekDouble> tmpAx_s(nGlobal, 0.0);
200  Array<OneD, NekDouble> tmpx_s(nGlobal, 0.0);
201 
202  NekVector<NekDouble> pb(nNonDir, tmp = pb_s + nDir, eWrapper);
203  NekVector<NekDouble> px(nNonDir, tmp = px_s + nDir, eWrapper);
204  NekVector<NekDouble> tmpAx(nNonDir, tmp = tmpAx_s + nDir, eWrapper);
205  NekVector<NekDouble> tmpx(nNonDir, tmp = tmpx_s + nDir, eWrapper);
206 
207  // notation follows the paper cited:
208  // \alpha_i = \tilda{x_i}^T b^n
209  // projected x, px = \sum \alpha_i \tilda{x_i}
210 
211  Array<OneD, NekDouble> alpha(m_prevBasis.size(), 0.0);
212  Array<OneD, NekDouble> alphaback(m_prevBasis.size(), 0.0);
213  for (int i = 0; i < m_prevBasis.size(); i++)
214  {
215  alpha[i] = Vmath::Dot2(nNonDir, m_prevBasis[i], pInput + nDir,
216  m_map + nDir);
217  }
218  vComm->AllReduce(alpha, Nektar::LibUtilities::ReduceSum);
219  int n = m_prevBasis.size(), info = -1;
220  Vmath::Vcopy(m_prevBasis.size(), alpha, 1, alphaback, 1);
221  Lapack::Dsptrs('U', n, 1, m_coeffMatrixFactor.get(), m_ipivot.get(),
222  alpha.get(), n, info);
223  if (info != 0)
224  {
225  // Dsptrs fails, only keep the latest solution
226  int latest = ResetKnownSolutionsToLatestOne();
227  alpha[0] = alphaback[latest];
228  }
229  for (int i = 0; i < m_prevBasis.size(); ++i)
230  {
231  NekVector<NekDouble> xi(nNonDir, m_prevLinSol[i], eWrapper);
232  px += alpha[i] * xi;
233  }
234 
235  // pb = b^n - A px
236  Vmath::Vcopy(nNonDir, pInput.get() + nDir, 1, pb_s.get() + nDir, 1);
237 
238  DoMatrixMultiplyFlag(px_s, tmpAx_s, false);
239 
240  pb -= tmpAx;
241 
242  if (m_verbose)
243  {
244  if (m_root)
245  cout << "SuccessiveRHS: " << m_prevBasis.size()
246  << "-bases projection reduces L2-norm of RHS from "
247  << std::sqrt(rhsNorm) << " to ";
248  NekDouble tmprhsNorm =
249  Vmath::Dot2(nNonDir, pb_s + nDir, pb_s + nDir, m_map + nDir);
250  vComm->AllReduce(tmprhsNorm, Nektar::LibUtilities::ReduceSum);
251  if (m_root)
252  cout << std::sqrt(tmprhsNorm) << endl;
253  }
254 
255  // solve the system with projected rhs
256  numIterations = m_linsol->SolveSystem(nGlobal, pb_s, tmpx_s, nDir, tol);
257 
258  // remainder solution + projection of previous solutions
259  x = tmpx + px;
260  }
261  // save the auxiliary solution to prev. known solutions
262  if (numIterations)
263  {
264  UpdateKnownSolutions(nGlobal, pOutput, nDir, isAconjugate);
265  }
266 }
267 
269 {
270  if (m_numPrevSols == 0)
271  {
272  return -1;
273  }
274  int latest = (m_numPrevSols - 1 + m_numSuccessiveRHS) % m_numSuccessiveRHS;
277  m_prevBasis.clear();
278  m_prevLinSol.clear();
279  m_prevBasis.push_back(b);
280  m_prevLinSol.push_back(x);
281  m_numPrevSols = 1;
282  return latest;
283 }
284 
285 /**
286  * Updates the storage of previously known solutions.
287  * Performs normalisation of input vector wrt A-norm.
288  */
290  const int nGlobal, const Array<OneD, const NekDouble> &newX, const int nDir,
291  const bool isAconjugate)
292 {
293  // Get vector sizes
294  int nNonDir = nGlobal - nDir;
295  int insertLocation = m_numPrevSols % m_numSuccessiveRHS;
296  int fullbuffer = (m_prevBasis.size() == m_numSuccessiveRHS);
297 
298  // Get the communicator for performing data exchanges
300  m_expList.lock()->GetComm()->GetRowComm();
301 
302  Array<OneD, NekDouble> tmpAx_s(nGlobal, 0.0);
303  Array<OneD, NekDouble> y_s(m_prevBasis.size() - fullbuffer, 0.0);
304  Array<OneD, NekDouble> invMy_s(y_s.size(), 0.0);
306  Array<OneD, NekDouble> tmp, newBasis;
307 
308  DoMatrixMultiplyFlag(newX, tmpAx_s, false);
309 
310  if (isAconjugate)
311  {
312  newBasis = newX + nDir;
313  }
314  else
315  {
316  newBasis = tmpAx_s + nDir;
317  }
318 
319  // Check the solution is non-zero
320  NekDouble solNorm =
321  Vmath::Dot2(nNonDir, newBasis, tmpAx_s + nDir, m_map + nDir);
322  vComm->AllReduce(solNorm, Nektar::LibUtilities::ReduceSum);
323 
324  if (solNorm < 22.2 * NekConstants::kNekSparseNonZeroTol)
325  {
326  return;
327  }
328 
329  // normalisation of A x
330  Vmath::Smul(nNonDir, 1.0 / sqrt(solNorm), tmpAx_s + nDir, 1,
331  tmp = tmpAx_s + nDir, 1);
332 
333  for (int i = 0; i < m_prevBasis.size(); ++i)
334  {
335  if (i == insertLocation)
336  continue;
337  int skip = i > insertLocation;
338  y_s[i - skip] =
339  Vmath::Dot2(nNonDir, m_prevBasis[i], tmpAx_s + nDir, m_map + nDir);
340  }
341  vComm->AllReduce(y_s, Nektar::LibUtilities::ReduceSum);
342 
343  // check if linearly dependent
344  DNekMatSharedPtr tilCoeffMatrix;
345  if (fullbuffer && m_numSuccessiveRHS > 1)
346  {
349  for (int i = 0; i < m_numSuccessiveRHS; ++i)
350  {
351  if (i == insertLocation)
352  continue;
353  int iskip = i > insertLocation;
354  for (int j = i; j < m_numSuccessiveRHS; ++j)
355  {
356  if (j == insertLocation)
357  continue;
358  int jskip = j > insertLocation;
359  tilCoeffMatrix->SetValue(i - iskip, j - jskip,
360  m_coeffMatrix->GetValue(i, j));
361  }
362  }
363  }
364  else if (!fullbuffer && m_prevBasis.size())
365  {
367  m_prevBasis.size(), m_prevBasis.size(), 0.0, eSYMMETRIC);
368  Vmath::Vcopy(tilCoeffMatrix->GetStorageSize(), m_coeffMatrix->GetPtr(),
369  1, tilCoeffMatrix->GetPtr(), 1);
370  }
371 
372  int n, info1 = 0, info2 = 0, info3 = 0;
373  if (y_s.size())
374  {
375  n = tilCoeffMatrix->GetRows();
376  Array<OneD, NekDouble> tilCoeffMatrixFactor(
377  tilCoeffMatrix->GetStorageSize());
378  Vmath::Vcopy(tilCoeffMatrix->GetStorageSize(), tilCoeffMatrix->GetPtr(),
379  1, tilCoeffMatrixFactor, 1);
380  Lapack::Dsptrf('U', n, tilCoeffMatrixFactor.get(), ipivot.get(), info1);
381  if (info1 == 0)
382  {
383  Vmath::Vcopy(n, y_s, 1, invMy_s, 1);
384  Lapack::Dsptrs('U', n, 1, tilCoeffMatrixFactor.get(), ipivot.get(),
385  invMy_s.get(), n, info2);
386  }
387  }
388  if (info1 || info2)
389  {
390  int latest = ResetKnownSolutionsToLatestOne();
391  y_s[0] = y_s[latest - (latest > insertLocation)];
392  invMy_s[0] = y_s[0];
393  insertLocation = m_numPrevSols % m_numSuccessiveRHS;
394  fullbuffer = (m_prevBasis.size() == m_numSuccessiveRHS);
395  }
396  NekDouble residual = 1.;
397  NekDouble epsilon = 10. * NekConstants::kNekZeroTol;
398  for (int i = 0; i < m_prevBasis.size() - fullbuffer; i++)
399  {
400  residual -= y_s[i] * invMy_s[i];
401  }
402  if (m_verbose && m_root)
403  cout << "SuccessiveRHS: residual " << residual;
404  if (residual < epsilon)
405  {
406  if (m_verbose && m_root)
407  cout << " < " << epsilon << ", reject" << endl;
408  return;
409  }
410 
411  // calculate new coefficient matrix and its factor
412  DNekMatSharedPtr newCoeffMatrix;
413  if (fullbuffer)
414  {
417  Vmath::Vcopy(m_coeffMatrix->GetStorageSize(), m_coeffMatrix->GetPtr(),
418  1, newCoeffMatrix->GetPtr(), 1);
419  newCoeffMatrix->SetValue(insertLocation, insertLocation, 1.);
420  for (int i = 0; i < m_numSuccessiveRHS; ++i)
421  {
422  if (i == insertLocation)
423  continue;
424  int iskip = i > insertLocation;
425  newCoeffMatrix->SetValue(insertLocation, i, y_s[i - iskip]);
426  }
427  }
428  else
429  {
431  m_prevBasis.size() + 1, m_prevBasis.size() + 1, 0.0, eSYMMETRIC);
432  newCoeffMatrix->SetValue(insertLocation, insertLocation, 1.);
433  for (int i = 0; i < m_prevBasis.size(); ++i)
434  {
435  newCoeffMatrix->SetValue(insertLocation, i, y_s[i]);
436  for (int j = i; j < m_prevBasis.size(); ++j)
437  {
438  newCoeffMatrix->SetValue(i, j, m_coeffMatrix->GetValue(i, j));
439  }
440  }
441  }
442  n = newCoeffMatrix->GetRows();
443  Array<OneD, NekDouble> coeffMatrixFactor(newCoeffMatrix->GetStorageSize());
444  Vmath::Vcopy(newCoeffMatrix->GetStorageSize(), newCoeffMatrix->GetPtr(), 1,
445  coeffMatrixFactor, 1);
446  Lapack::Dsptrf('U', n, coeffMatrixFactor.get(), ipivot.get(), info3);
447  if (info3)
448  {
449  if (m_verbose && m_root)
450  cout << " >= " << epsilon << ", reject (Dsptrf fails)" << endl;
451  return;
452  }
453  if (m_verbose && m_root)
454  cout << " >= " << epsilon << ", accept" << endl;
455 
456  // if success, update basis, rhs, coefficient matrix, and its factor
457  if (m_prevBasis.size() < m_numSuccessiveRHS)
458  {
459  m_prevBasis.push_back(tmp = tmpAx_s + nDir);
460  if (isAconjugate)
461  {
462  m_prevLinSol.push_back(tmp);
463  }
464  else
465  {
466  Array<OneD, NekDouble> solution(nNonDir, 0.0);
467  m_prevLinSol.push_back(solution);
468  }
469  }
470  Vmath::Smul(nNonDir, 1. / sqrt(solNorm), tmp = newX + nDir, 1,
471  m_prevLinSol[insertLocation], 1);
472  if (!isAconjugate)
473  {
474  m_prevBasis[insertLocation] = tmpAx_s + nDir;
475  }
476  m_coeffMatrix = newCoeffMatrix;
477  m_coeffMatrixFactor = coeffMatrixFactor;
478  m_ipivot = ipivot;
479  ++m_numPrevSols;
480 }
481 
483 {
484  Array<OneD, NekDouble> vExchange(1, 0.0);
485  if (m_map.size() > 0)
486  {
487  vExchange[0] =
488  Vmath::Dot2(pIn.GetDimension(), &pIn[0], &pIn[0], &m_map[0]);
489  }
490 
491  m_expList.lock()->GetComm()->GetRowComm()->AllReduce(
493 
494  // To ensure that very different rhs values are not being
495  // used in subsequent solvers such as the velocit solve in
496  // INC NS. If this works we then need to work out a better
497  // way to control this.
498  NekDouble new_rhs_mag = (vExchange[0] > 1e-6) ? vExchange[0] : 1.0;
499 
501  {
502  m_rhs_magnitude = new_rhs_mag;
503  }
504  else
505  {
507  (1.0 - m_rhs_mag_sm) * new_rhs_mag);
508  }
509 }
510 
511 } // namespace MultiRegions
512 } // namespace Nektar
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:215
#define WARNINGL0(condition, msg)
Definition: ErrorUtil.hpp:222
tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
Create an instance of the class referred to by idKey.
Definition: NekFactory.hpp:144
void DefineNekSysLhsEval(FuncPointerT func, ObjectPointerT obj)
Definition: NekSys.h:107
void DefineNekSysPrecon(FuncPointerT func, ObjectPointerT obj)
Definition: NekSys.h:114
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
PreconditionerSharedPtr CreatePrecon(AssemblyMapSharedPtr asmMap)
Create a preconditioner object from the parameters defined in the supplied assembly map.
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)
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.
virtual void v_SolveLinearSystem(const int pNumRows, const Array< OneD, const NekDouble > &pInput, Array< OneD, NekDouble > &pOutput, const AssemblyMapSharedPtr &locToGloMap, const int pNumDir) override
Solve the matrix system.
NekDouble m_tolerance
Tolerance of iterative solver.
void DoPreconditionerFlag(const Array< OneD, NekDouble > &pInput, Array< OneD, NekDouble > &pOutput, const bool &controlFlag)
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
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 symmetric matrix problem using Bunch-Kaufman pivoting.
Definition: Lapack.hpp:191
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:165
std::shared_ptr< SessionReader > SessionReaderSharedPtr
NekLinSysIterFactory & GetNekLinSysIterFactory()
std::shared_ptr< Comm > CommSharedPtr
Pointer to a Communicator object.
Definition: Comm.h:54
static PreconditionerSharedPtr NullPreconditionerSharedPtr
std::shared_ptr< AssemblyMap > AssemblyMapSharedPtr
Definition: AssemblyMap.h:51
static const NekDouble kNekUnsetDouble
static const NekDouble kNekSparseNonZeroTol
static const NekDouble kNekZeroTol
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:1147
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:248
void Zero(int n, T *x, const int incx)
Zero vector.
Definition: Vmath.cpp:492
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition: Vmath.cpp:1255
scalarT< T > abs(scalarT< T > in)
Definition: scalar.hpp:298
scalarT< T > sqrt(scalarT< T > in)
Definition: scalar.hpp:294