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