Nektar++
Loading...
Searching...
No Matches
NekLinSysIterGMRESLoc.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: NekLinSysIterGMRESLoc.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// License for the specific language governing rights and limitations under
14// Permission is hereby granted, free of charge, to any person obtaining a
15// copy of this software and associated documentation files (the "Software"),
16// to deal in the Software without restriction, including without limitation
17// the rights to use, copy, modify, merge, publish, distribute, sublicense,
18// and/or sell copies of the Software, and to permit persons to whom the
19// Software is furnished to do so, subject to the following conditions:
20//
21// The above copyright notice and this permission notice shall be included
22// in all copies or substantial portions of the Software.
23//
24// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30// DEALINGS IN THE SOFTWARE.
31//
32// Description: NekLinSysIterGMRESLoc definition
33//
34///////////////////////////////////////////////////////////////////////////////
35
38
39using namespace std;
40
42{
43/**
44 * @class NekLinSysIterGMRESLoc
45 *
46 * Solves a linear system using iterative methods using local storage rather
47 * than global
48 */
52 "NekLinSysIterGMRES local storage solver.");
53
56 const LibUtilities::CommSharedPtr &vRowComm, const int nDimen,
57 const NekSysKey &pKey)
58 : NekLinSysIter(pSession, vRowComm, nDimen, pKey)
59{
62
64
68
70
71 // LGMRES parameter
72 // Reference:
73 // Baker, Allison H., Elizabeth R. Jessup, and Thomas Manteuffel. "A
74 // technique for accelerating the convergence of restarted GMRES." SIAM
75 // Journal on Matrix Analysis and Applications 26, no. 4 (2005):
76 // 962-984.
77 m_GMRESDeltaDirection = pSession->DefinesParameter("GMRESDeltaDirection")
78 ? pSession->GetParameter("GMRESDeltaDirection")
79 : 0;
80
81 m_isLocal = true;
82
83 m_flexible = pSession->DefinesParameter("FlexibleGMRES")
84 ? pSession->GetParameter("FlexibleGMRES")
85 : false;
86
87 // Allocate array storage of coefficients
88 // Hessenburg matrix
90 for (size_t nd = 0; nd < m_LinSysMaxStorage; nd++)
91 {
93 }
94 // Hesseburg matrix after rotation
96 for (size_t nd = 0; nd < m_LinSysMaxStorage; nd++)
97 {
99 }
100 // Total search directions
103}
104
109
110/**
111 *
112 */
114 const int nLocal, const Array<OneD, const NekDouble> &pInput,
115 Array<OneD, NekDouble> &pOutput, [[maybe_unused]] const int nDir)
116{
117 int niterations = DoGMRES(nLocal, pInput, pOutput);
118
119 return niterations;
120}
121
123 const Array<OneD, NekDouble> &rhs,
125 [[maybe_unused]] const int nDir,
126 NekDouble &err, int &iter)
127{
128 iter = DoGMRES(nGlobal, rhs, x);
129 err = m_finalError;
130}
131
132/**  
133 * Solve a global linear system using the Gmres 
134 * We solve only for the non-Dirichlet modes. The operator is evaluated  
135 * using an auxiliary function v_DoMatrixMultiply defined by the  
136 * specific solver. Distributed math routines are used to support  
137 * parallel execution of the solver.  
138 *  
139 * @param pInput Input residual in local format.
140 * @param pOutput Solution vector in local format but with continuous
141 * values
142 */
143
145 const Array<OneD, const NekDouble> &pInput,
146 Array<OneD, NekDouble> &pOutput)
147{
150 {
151 Set_Rhs_Magnitude(pInput);
152 }
153
154 NekDouble eps = 0.0;
156
158 m_converged = false;
159
160 bool truncted = false;
161
163 {
164 truncted = true;
165 }
166
167 for (int nrestart = 0; nrestart < m_maxrestart; ++nrestart)
168 {
169 eps = DoGmresRestart(nrestart, truncted, nLocal, pInput, pOutput);
170
171 if (m_converged)
172 {
173 break;
174 }
175 }
176
177 // Verbose print error, iteration count, tolerance, ..
178 if (m_verbose)
179 {
180 Array<OneD, NekDouble> r0(nLocal);
181 Array<OneD, NekDouble> wk(nLocal);
182
183 // calculate difference in residual of solution
185
186 // Note this is finding the difference between the whole
187 // residual not jsut the non-Dirichlet values.
188 // Probably OK since just an monitoring output?
189 Vmath::Vsub(nLocal, pInput, 1, r0, 1, r0, 1);
190
191 m_operator.DoAssembleLoc(r0, wk, true);
192 NekDouble vExchange = Vmath::Dot(nLocal, wk, r0);
193
194 m_rowComm->AllReduce(vExchange, LibUtilities::ReduceSum);
195 NekDouble eps1 = vExchange;
196
197 if (m_root)
198 {
199 cout << "GMRES iterations made = " << m_totalIterations
200 << " using tolerance of " << m_NekLinSysTolerance
201 << " (error = " << m_finalError
202 << ", rhs_mag = " << sqrt(m_rhs_magnitude)
203 << " with (GMRES eps = " << eps << " REAL eps= " << eps1
204 << ")";
205
206 // Append appropriate message when finalising GMRES
207 if (m_converged)
208 {
209 cout << " CONVERGED" << endl;
210 }
211 else
212 {
213 cout << " WARNING: Exceeded maxIt" << endl;
214 }
215 }
216 }
217
218 if (m_FlagWarnings)
219 {
220 WARNINGL1(m_converged, "GMRES did not converge.");
221 }
222 return m_totalIterations;
223}
224
226 const unsigned int nrestart, const bool truncted, const int nLocal,
228{
229 // Allocate array storage of coefficients
230 // Residual
232 // Givens rotation c
234 // Givens rotation s
236 // Total coefficients, just for check
238 // Search direction order
242 // Temporary variables
243 int idtem;
244 int starttem;
245 int endtem;
246
247 NekDouble eps;
248 NekDouble beta, alpha;
249 NekDouble vExchange = 0;
250 // Temporary Array
251 Array<OneD, NekDouble> w(nLocal, 0.0);
252 Array<OneD, NekDouble> wk(nLocal, 0.0);
253 Array<OneD, NekDouble> r0(nLocal, 0.0);
259
260 if (nrestart)
261 {
262 // This is A*x
264
265 // The first search direction
266 beta = -1.0;
267
268 // This is r0 = b-A*x
269 Vmath::Svtvp(nLocal, beta, r0, 1, pInput, 1, r0, 1);
270 }
271 else
272 {
273 // If not restarted, x0 should be zero
274 Vmath::Vcopy(nLocal, pInput, 1, r0, 1);
275 }
276
278 {
280 }
281
282 // Norm of (r0)
283 // The m_map tells how to connect
284 m_operator.DoAssembleLoc(r0, wk, true);
285 vExchange = Vmath::Dot(nLocal, wk, r0);
286 m_rowComm->AllReduce(vExchange, LibUtilities::ReduceSum);
287 eps = vExchange;
288
289 if (!nrestart)
290 {
292 {
294 {
295 // Evaluate initial residual error for exit check
296 ASSERTL0(false, "Need to set up/debugging");
297
298 m_operator.DoAssembleLoc(pInput, wk, true);
299 vExchange = Vmath::Dot(nLocal, wk, pInput);
300 m_rowComm->AllReduce(vExchange, LibUtilities::ReduceSum);
301 m_prec_factor = vExchange / eps;
302 }
303 else
304 {
305 m_prec_factor = 1.0;
306 }
307 }
308 }
309
310 Vmath::Smul(nLocal, sqrt(m_prec_factor), r0, 1, r0, 1);
311 eps = eps * m_prec_factor;
312 eta[0] = sqrt(eps);
313
314 // Give an order for the entries in Hessenburg matrix
315 for (int nd = 0; nd < m_LinSysMaxStorage; ++nd)
316 {
317 id[nd] = nd;
318 id_end[nd] = nd + 1;
319 starttem = id_end[nd] - m_KrylovMaxHessMatBand;
320 if (truncted && (starttem) > 0)
321 {
322 id_start[nd] = starttem;
323 }
324 else
325 {
326 id_start[nd] = 0;
327 }
328 }
329
330 // Normlized by r0 norm V(:,1)=r0/norm(r0)
331 alpha = 1.0 / eta[0];
332
333 // Scalar multiplication
334 if (m_V_total[0].size() == 0)
335 {
336 m_V_total[0] = Array<OneD, NekDouble>(nLocal, 0.0);
337 m_Z_total[0] = Array<OneD, NekDouble>(nLocal, 0.0);
338 // Set storage of LGMRES
339 for (unsigned int dir = 0; dir < m_GMRESDeltaDirection; dir++)
340 {
341 m_delta.push_back(Array<OneD, NekDouble>(nLocal, 0.0));
342 }
343 }
344 Vmath::Smul(nLocal, alpha, r0, 1, m_V_total[0], 1);
345
346 // Restarted Gmres(m) process
347 int nswp = 0;
348 for (int nd = 0; nd < m_LinSysMaxStorage; ++nd)
349 {
350 if (m_V_total[nd + 1].size() == 0)
351 {
352 m_V_total[nd + 1] = Array<OneD, NekDouble>(nLocal, 0.0);
353 if (m_flexible)
354 {
355 m_Z_total[nd + 1] = Array<OneD, NekDouble>(nLocal, 0.0);
356 }
357 }
358 Vmath::Zero(nLocal, m_V_total[nd + 1], 1);
360
361 // For LGMRES use m_delta for the last m_GMRESDeltaDirection
362 // iterations.
363 bool cond = nd >= (m_LinSysMaxStorage - m_GMRESDeltaDirection) &&
364 nrestart >= m_GMRESDeltaDirection;
365 unsigned int index = nd - (m_LinSysMaxStorage - m_GMRESDeltaDirection);
366 auto &V1 = (cond) ? m_delta[index] : m_V_total[nd];
367 auto &Z1 =
368 (m_NekLinSysRightPrecon) ? m_Z_total[(m_flexible) ? nd : 0] : V1;
369 V2 = m_V_total[nd + 1];
370 h1 = m_hes[nd];
371
373 {
374 m_operator.DoNekSysPrecon(V1, Z1, true);
375 }
376
377 // w here is no need to add nDir due to temporary Array
378 idtem = id[nd];
379 starttem = id_start[idtem];
380 endtem = id_end[idtem];
381
382 DoArnoldi(starttem, endtem, nLocal, w, wk, Z1, V2, h1);
383
384 if (starttem > 0)
385 {
386 starttem = starttem - 1;
387 }
388
389 h2 = m_Upper[nd];
390 Vmath::Vcopy(m_LinSysMaxStorage + 1, &h1[0], 1, &h2[0], 1);
391 DoGivensRotation(starttem, endtem, cs, sn, h2, eta);
392
393 eps = eta[nd + 1] * eta[nd + 1];
394
395 // This Gmres merge truncted Gmres to accelerate.
396 // If truncted, cannot jump out because
397 // the last term of eta is not residual
398 if ((!truncted) || (nd < m_KrylovMaxHessMatBand))
399 {
400 if ((eps <
402 {
403 m_converged = true;
404 }
405 }
406
407 nswp++;
409
410 if (m_converged)
411 {
412 break;
413 }
414 }
415
416 DoBackward(nswp, m_Upper, eta, y_total);
417
418 // Calculate output V_total * y_total or Z_total * y_total (flexible).
419 auto &Z_total = (m_flexible) ? m_Z_total : m_V_total;
420 Array<OneD, NekDouble> solution(nLocal, 0.0);
421 for (int i = 0; i < nswp; ++i)
422 {
423 // For LGMRES use m_delta for the last m_GMRESDeltaDirection
424 // iterations.
425 bool cond = i >= (m_LinSysMaxStorage - m_GMRESDeltaDirection) &&
426 nrestart >= m_GMRESDeltaDirection;
427 if (cond)
428 {
429 unsigned int index =
431 Vmath::Svtvp(nLocal, y_total[i], m_delta[index], 1, solution, 1,
432 solution, 1);
433 }
434 else
435 {
436 Vmath::Svtvp(nLocal, y_total[i], Z_total[i], 1, solution, 1,
437 solution, 1);
438 }
439 }
440
441 // Store last m_GMRESDeltaDirection delta for LGMRES.
443 {
444 auto last = std::move(m_delta.back());
445 Vmath::Vcopy(nLocal, solution, 1, last, 1);
446 m_delta.pop_back();
447 m_delta.push_front(std::move(last));
448 }
449
451 {
452 m_operator.DoNekSysPrecon(solution, solution, true);
453 }
454
455 // Update output.
456 Vmath::Vadd(nLocal, solution, 1, pOutput, 1, pOutput, 1);
457
458 return eps;
459}
460
461// Arnoldi Subroutine
462void NekLinSysIterGMRESLoc::DoArnoldi(const int starttem, const int endtem,
463 const int nLocal,
469{
470 NekDouble alpha, beta, vExchange = 0.0;
472 timer.Start();
474 timer.Stop();
475 timer.AccumulateRegion("NekSysOperators::DoNekSysLhsEval", 10);
476
478 {
480 }
481
482 Vmath::Smul(nLocal, sqrt(m_prec_factor), w, 1, w, 1);
483
484 // Modified Gram-Schmidt
485 for (int i = starttem; i < endtem; ++i)
486 {
487 // Do inner product on equivalent of global values excluding
488 // Diriclet conditions. To do this need to assmble (and
489 // scatter back vector and then zero Dirichlet conditions.
490 m_operator.DoAssembleLoc(m_V_total[i], wk, true);
491 vExchange = Vmath::Dot(nLocal, wk, w);
492 m_rowComm->AllReduce(vExchange, LibUtilities::ReduceSum);
493
494 h[i] = vExchange;
495
496 beta = -1.0 * vExchange;
497 Vmath::Svtvp(nLocal, beta, m_V_total[i], 1, w, 1, w, 1);
498 }
499 // end of Modified Gram-Schmidt
500
501 // calculate the L2 norm and normalize
502 m_operator.DoAssembleLoc(w, wk, true);
503 vExchange = Vmath::Dot(nLocal, wk, w);
504 m_rowComm->AllReduce(vExchange, LibUtilities::ReduceSum);
505
506 h[endtem] = sqrt(vExchange);
507
508 alpha = 1.0 / h[endtem];
509 Vmath::Smul(nLocal, alpha, w, 1, V2, 1);
510}
511
512// QR factorization through Givens rotation -> Put into a helper class
514 const int endtem,
519{
520 NekDouble dbl;
521 NekDouble dd;
522 NekDouble hh;
523 int idtem = endtem - 1;
524 // The starttem and endtem are beginning and ending order of Givens rotation
525 // They usually equal to the beginning position and ending position of
526 // Hessenburg matrix But sometimes starttem will change, like if it is
527 // initial 0 and becomes nonzero because previous Givens rotation See Yu
528 // Pan's User Guide
529 for (int i = starttem; i < idtem; ++i)
530 {
531 dbl = c[i] * h[i] - s[i] * h[i + 1];
532 h[i + 1] = s[i] * h[i] + c[i] * h[i + 1];
533 h[i] = dbl;
534 }
535 dd = h[idtem];
536 hh = h[endtem];
537 if (hh == 0.0)
538 {
539 c[idtem] = 1.0;
540 s[idtem] = 0.0;
541 }
542 else if (abs(hh) > abs(dd))
543 {
544 dbl = -dd / hh;
545 s[idtem] = 1.0 / sqrt(1.0 + dbl * dbl);
546 c[idtem] = dbl * s[idtem];
547 }
548 else
549 {
550 dbl = -hh / dd;
551 c[idtem] = 1.0 / sqrt(1.0 + dbl * dbl);
552 s[idtem] = dbl * c[idtem];
553 }
554
555 h[idtem] = c[idtem] * h[idtem] - s[idtem] * h[endtem];
556 h[endtem] = 0.0;
557
558 dbl = c[idtem] * eta[idtem] - s[idtem] * eta[endtem];
559 eta[endtem] = s[idtem] * eta[idtem] + c[idtem] * eta[endtem];
560 eta[idtem] = dbl;
561}
562
563// Backward calculation
564// To notice, Hesssenburg matrix's column
565// and row changes due to use Array<OneD,Array<OneD,NekDouble>>
570{
571 // Number is the entry number
572 // but C++'s order need to be one smaller
573 int maxid = number - 1;
574 NekDouble sum;
575 y[maxid] = b[maxid] / A[maxid][maxid];
576 for (int i = maxid - 1; i > -1; --i)
577 {
578 sum = b[i];
579 for (int j = i + 1; j < number; ++j)
580 {
581 // i and j changes due to use Array<OneD,Array<OneD,NekDouble>>
582 sum -= y[j] * A[j][i];
583 }
584 y[i] = sum / A[i][i];
585 }
586}
587} // namespace Nektar::LibUtilities
#define WARNINGL1(condition, msg)
#define ASSERTL0(condition, msg)
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
static NekLinSysIterSharedPtr create(const LibUtilities::SessionReaderSharedPtr &pSession, const LibUtilities::CommSharedPtr &vRowComm, const int nDimen, const NekSysKey &pKey)
Array< OneD, Array< OneD, NekDouble > > m_V_total
void DoArnoldi(const int starttem, const int endtem, const int nLocal, Array< OneD, NekDouble > &w, Array< OneD, NekDouble > &wk, Array< OneD, NekDouble > &V1, Array< OneD, NekDouble > &V2, Array< OneD, NekDouble > &h)
void DoBackward(const int number, Array< OneD, Array< OneD, NekDouble > > &A, const Array< OneD, const NekDouble > &b, Array< OneD, NekDouble > &y)
std::deque< Array< OneD, NekDouble > > m_delta
NekDouble DoGmresRestart(const unsigned int nrestart, const bool truncted, const int nLocal, const Array< OneD, const NekDouble > &pInput, Array< OneD, NekDouble > &pOutput)
Actual iterative gmres solver for one restart.
void DoGivensRotation(const int starttem, const int endtem, Array< OneD, NekDouble > &c, Array< OneD, NekDouble > &s, Array< OneD, NekDouble > &h, Array< OneD, NekDouble > &eta)
Array< OneD, Array< OneD, NekDouble > > m_Z_total
void v_DoIterate(const int nGlobal, const Array< OneD, NekDouble > &rhs, Array< OneD, NekDouble > &x, const int nDir, NekDouble &err, int &iter) override
int v_SolveSystem(const int nLocal, const Array< OneD, const NekDouble > &pInput, Array< OneD, NekDouble > &pOutput, const int nDir) override
NekLinSysIterGMRESLoc(const LibUtilities::SessionReaderSharedPtr &pSession, const LibUtilities::CommSharedPtr &vRowComm, const int nDimen, const NekSysKey &pKey=NekSysKey())
Array< OneD, Array< OneD, NekDouble > > m_hes
int DoGMRES(const int pNumRows, const Array< OneD, const NekDouble > &pInput, Array< OneD, NekDouble > &pOutput)
Actual iterative solve-GMRES.
Array< OneD, Array< OneD, NekDouble > > m_Upper
void Set_Rhs_Magnitude(const Array< OneD, NekDouble > &pIn)
LibUtilities::CommSharedPtr m_rowComm
Definition NekSys.h:299
NekSysOperators m_operator
Definition NekSys.h:306
void DoAssembleLoc(InArrayType &xn, OutArrayType &xn1, const bool &flag=false) const
Definition NekSys.h:168
void DoNekSysPrecon(InArrayType &inarray, OutArrayType &outarray, const bool &flag=false) const
Definition NekSys.h:155
void DoNekSysLhsEval(InArrayType &inarray, OutArrayType &outarray, const bool &flag=false) const
Definition NekSys.h:148
void AccumulateRegion(std::string, int iolevel=0)
Accumulate elapsed time for a region.
Definition Timer.cpp:70
std::shared_ptr< SessionReader > SessionReaderSharedPtr
NekLinSysIterFactory & GetNekLinSysIterFactory()
@ beta
Gauss Radau pinned at x=-1,.
Definition PointsType.h:59
std::shared_ptr< Comm > CommSharedPtr
Pointer to a Communicator object.
Definition Comm.h:55
static const NekDouble kNekUnsetDouble
void Svtvp(int n, const T alpha, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Svtvp (scalar times vector plus vector): z = alpha*x + y.
Definition Vmath.hpp:396
T Dot(int n, const T *w, const T *x)
dot product
Definition Vmath.hpp:761
void Vadd(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Add vector z = x+y.
Definition Vmath.hpp:180
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
void Vsub(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Subtract vector z = x-y.
Definition Vmath.hpp:220
STL namespace.
scalarT< T > abs(scalarT< T > in)
Definition scalar.hpp:295
scalarT< T > min(scalarT< T > lhs, scalarT< T > rhs)
Definition scalar.hpp:300
scalarT< T > sqrt(scalarT< T > in)
Definition scalar.hpp:290