Nektar++
TimeIntegrationAlgorithmGLM.cpp
Go to the documentation of this file.
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 // File TimeIntegrationAlgorithmGLM.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: Implementation of time integration scheme data class.
33 //
34 ///////////////////////////////////////////////////////////////////////////////
35 
38 
43 
44 #include <iostream>
45 
46 #include <boost/core/ignore_unused.hpp>
47 
48 #include <cmath>
49 
50 namespace Nektar
51 {
52 namespace LibUtilities
53 {
54 
55 ////////////////////////////////////////////////////////////////////////////////
58  const NekDouble deltaT, ConstDoubleArray &y_0, const NekDouble time,
60 {
61  // create a TimeIntegrationSolutionGLM object based upon the
62  // initial value. Initialise all other multi-step values
63  // and derivatives to zero
64 
67  time, deltaT);
68 
70  {
71  // ensure initial solution is in correct space
72  op.DoProjection(y_0, y_out->UpdateSolution(), time);
73  }
74 
75  // calculate the initial derivative, if is part of the
76  // solution vector of the current scheme
77  if (GetNmultiStepDerivs() > 0)
78  {
80 
81  if (offsets[GetNmultiStepValues()] == 0)
82  {
83  int i;
84  int nvar = y_0.size();
85  int npoints = y_0[0].size();
86  DoubleArray f_y_0(nvar);
87  for (i = 0; i < nvar; i++)
88  {
89  f_y_0[i] = Array<OneD, NekDouble>(npoints);
90  }
91  // calculate the derivative of the initial value
92  op.DoOdeRhs(y_0, f_y_0, time);
93 
94  // multiply by the step size
95  for (i = 0; i < nvar; i++)
96  {
97  Blas::Dscal(npoints, deltaT, f_y_0[i].get(), 1);
98  }
99  y_out->SetDerivative(0, f_y_0, deltaT);
100  }
101  }
102 
103  return y_out;
104 }
105 
107 TimeIntegrate(const NekDouble deltaT,
110 {
111  // ASSERTL1( !(m_parent->GetIntegrationSchemeType() == eImplicit), "Fully
112  // Implicit integration scheme cannot be handled by this routine." );
113 
114  int nvar = solvector->GetFirstDim();
115  int npoints = solvector->GetSecondDim();
116 
117  if (solvector->GetIntegrationSchemeData() !=
118  this) // FIMXE ... verify that this is correct...
119  {
120  // This branch will be taken when the solution vector
121  // (solvector) is set up for a different scheme than
122  // the object this method is called from. (typically
123  // needed to calculate the first time-levels of a
124  // multi-step scheme)
125 
126  // To do this kind of 'non-matching' integration, we
127  // perform the following three steps:
128  //
129  // 1: copy the required input information from the
130  // solution vector of the master scheme to the
131  // input solution vector of the current scheme
132  //
133  // 2: time-integrate for one step using the current
134  // scheme
135  //
136  // 3: copy the information contained in the output
137  // vector of the current scheme to the solution
138  // vector of the master scheme
139 
140  // STEP 1: copy the required input information from
141  // the solution vector of the master scheme
142  // to the input solution vector of the
143  // current scheme
144 
145  // 1.1 Determine which information is required for the
146  // current scheme
147 
148  DoubleArray y_n;
149  NekDouble t_n = 0;
150  DoubleArray dtFy_n;
151 
152  unsigned int nCurSchemeVals =
153  GetNmultiStepValues(); // number of required values of the current
154  // scheme
155  unsigned int nCurSchemeDers =
156  GetNmultiStepDerivs(); // number of required derivs of the current
157  // scheme
158  unsigned int nCurSchemeSteps =
159  GetNsteps(); // number of steps in the current scheme // FIMXE...
160  // what does it mean by "current scheme"... is this now
161  // a SchemeData issue?
162 
163  unsigned int nMasterSchemeVals =
164  solvector->GetNvalues(); // number of values of the master scheme
165  unsigned int nMasterSchemeDers =
166  solvector->GetNderivs(); // number of derivs of the master scheme
167 
168  // The arrays below contains information to which time-level
169  // the values and derivatives of the schemes belong
170  const Array<OneD, const unsigned int> &curTimeLevels =
172  const Array<OneD, const unsigned int> &masterTimeLevels =
173  solvector->GetTimeLevelOffset();
174 
175  // 1.2 Copy the required information from the master
176  // solution vector to the input solution vector of
177  // the current scheme
180  this); // input solution vector of the current scheme
181 
182  for (int n = 0; n < nCurSchemeVals; n++)
183  {
184  // Get the required value out of the master solution vector
185  // DoubleArray& y_n = solvector->GetValue ( curTimeLevels[n] );
186  // NekDouble t_n = solvector->GetValueTime( curTimeLevels[n] );
187 
188  y_n = solvector->GetValue(curTimeLevels[n]);
189  t_n = solvector->GetValueTime(curTimeLevels[n]);
190 
191  // Set the required value in the input solution
192  // vector of the current scheme
193  solvector_in->SetValue(curTimeLevels[n], y_n, t_n);
194  }
195 
196  for (int n = nCurSchemeVals; n < nCurSchemeSteps; n++)
197  {
198  // Get the required derivative out of the master
199  // solution vector
200  // DoubleArray& dtFy_n = solvector->GetDerivative (
201  // curTimeLevels[n] );
202  dtFy_n = solvector->GetDerivative(curTimeLevels[n]);
203 
204  // Set the required derivative in the input
205  // solution vector of the current scheme
206  solvector_in->SetDerivative(curTimeLevels[n], dtFy_n, deltaT);
207  }
208 
209  // STEP 2: time-integrate for one step using the
210  // current scheme
213  this, nvar,
214  npoints); // output solution vector of the current scheme
215 
216  // Integrate one step
217  TimeIntegrate(deltaT,
218  solvector_in->GetSolutionVector(),
219  solvector_in->GetTimeVector(),
220  solvector_out->UpdateSolutionVector(),
221  solvector_out->UpdateTimeVector(), op);
222 
223  // STEP 3: copy the information contained in the
224  // output vector of the current scheme to the
225  // solution vector of the master scheme
226 
227  // 3.1 Check whether the current time scheme updates
228  // the most recent derivative that should be
229  // updated in the master scheme. If not,
230  // calculate the derivative. This can be done
231  // based upon the corresponding value and the
232  // DoOdeRhs operator.
233  bool CalcNewDeriv = false; // This flag inidicates whether
234  // the new derivative is availble
235  // in the output of the current
236  // scheme or whether it should be
237  // calculated.
238 
239  if (nMasterSchemeDers > 0)
240  {
241  if (nCurSchemeDers == 0)
242  {
243  CalcNewDeriv = true;
244  }
245  else
246  {
247  if (masterTimeLevels[nMasterSchemeVals] <
248  curTimeLevels[nCurSchemeVals])
249  {
250  CalcNewDeriv = true;
251  }
252  }
253  }
254 
255  if (CalcNewDeriv)
256  {
257  int newDerivTimeLevel =
258  masterTimeLevels[nMasterSchemeVals]; // Contains the
259  // time level at
260  // which the
261  // derivative of
262  // the master
263  // scheme is
264  // known.
265  // DoubleArray y_n;
266  // NekDouble t_n;
267  // If the time level corresponds to 0, calculate the
268  // derivative based upon the solution value at the new
269  // time-level
270  if (newDerivTimeLevel == 0)
271  {
272  y_n = solvector_out->GetValue(0);
273  t_n = solvector_out->GetValueTime(0);
274  }
275  // If the time level corresponds to 1, calculate the
276  // derivative based upon the solution value at the new
277  // old-level.
278  else if (newDerivTimeLevel == 1)
279  {
280  y_n = solvector->GetValue(0);
281  t_n = solvector->GetValueTime(0);
282  }
283  else
284  {
285  ASSERTL1(false, "Problems with initialising scheme");
286  }
287 
288  DoubleArray f_n(nvar);
289  for (int j = 0; j < nvar; j++)
290  {
291  f_n[j] = Array<OneD, NekDouble>(npoints);
292  }
293 
294  // Calculate the derivative
295  op.DoOdeRhs(y_n, f_n, t_n);
296 
297  // Multiply by dt (as required by the General Linear Method
298  // framework)
299  for (int j = 0; j < nvar; j++)
300  {
301  Vmath::Smul(npoints, deltaT, f_n[j], 1, f_n[j], 1);
302  }
303 
304  // Rotate the solution vector (i.e. updating without
305  // calculating/inserting new values).
306  solvector->RotateSolutionVector();
307 
308  // Set the calculated derivative in the master solution
309  // vector.
310  solvector->SetDerivative(newDerivTimeLevel, f_n, deltaT);
311  }
312  else
313  {
314  // Rotate the solution vector (i.e. updating without
315  // calculating/inserting new values).
316  solvector->RotateSolutionVector();
317  }
318 
319  // 1.2 Copy the information calculated using the
320  // current scheme from the output solution vector
321  // to the master solution vector
322  for (int n = 0; n < nCurSchemeVals; n++)
323  {
324  // Get the calculated value out of the output
325  // solution vector of the current scheme
326  // DoubleArray& y_n = solvector_out->GetValue ( curTimeLevels[n]
327  // );
328  // NekDouble t_n = solvector_out->GetValueTime( curTimeLevels[n]
329  // );
330  y_n = solvector_out->GetValue(curTimeLevels[n]);
331  t_n = solvector_out->GetValueTime(curTimeLevels[n]);
332 
333  // Set the calculated value in the master solution vector
334  solvector->SetValue(curTimeLevels[n], y_n, t_n);
335  }
336 
337  for (int n = nCurSchemeVals; n < nCurSchemeSteps; n++)
338  {
339  // Get the calculated derivative out of the output
340  // solution vector of the current scheme.
341  // DoubleArray& dtFy_n =
342  // solvector_out->GetDerivative (curTimeLevels[n]);
343  dtFy_n = solvector_out->GetDerivative(curTimeLevels[n]);
344 
345  // Set the calculated derivative in the master
346  // solution vector.
347  solvector->SetDerivative(curTimeLevels[n], dtFy_n, deltaT);
348  }
349  }
350  else
351  {
354  this, nvar, npoints);
355 
356  TimeIntegrate(deltaT,
357  solvector->GetSolutionVector(),
358  solvector->GetTimeVector(),
359  solvector_new->UpdateSolutionVector(),
360  solvector_new->UpdateTimeVector(), op);
361 
362  solvector = solvector_new;
363  }
364 
365  return solvector->GetSolution();
366 
367 } // end TimeIntegrate()
368 
369 
370 // Does the actual multi-stage multi-step integration.
371 void
373  ConstTripleArray &y_old,
374  ConstSingleArray &t_old,
375  TripleArray &y_new,
376  SingleArray &t_new,
378 {
379  ASSERTL1(
380  CheckTimeIntegrateArguments(/*deltaT,*/ y_old, t_old, y_new, t_new, op),
381  "Arguments not well defined");
382 
384 
385  // Check if storage has already been initialised.
386  // If so, we just zero the temporary storage.
387  if (m_initialised && m_nvars == GetFirstDim(y_old) &&
388  m_npoints == GetSecondDim(y_old))
389  {
390  for (int j = 0; j < m_nvars; j++)
391  {
392  Vmath::Zero(m_npoints, m_tmp[j], 1);
393  }
394  }
395  else
396  {
397  m_nvars = GetFirstDim(y_old);
398  m_npoints = GetSecondDim(y_old);
399 
400  // First, calculate the various stage values and stage
401  // derivatives (this is the multi-stage part of the method)
402  // - m_Y corresponds to the stage values
403  // - m_F corresponds to the stage derivatives
404  // - m_T corresponds to the time at the different stages
405  // - m_tmp corresponds to the explicit right hand side of
406  // each stage equation
407  // (for explicit schemes, this correspond to m_Y)
408 
409  // Allocate memory for the arrays m_Y and m_F and m_tmp. The same
410  // storage will be used for every stage -> m_Y is a
411  // DoubleArray
413  for (int j = 0; j < m_nvars; j++)
414  {
416  }
417 
418  // The same storage will be used for every stage -> m_tmp is a
419  // DoubleArray
420  if (type == eExplicit || m_schemeType == eExponential)
421  {
422  m_Y = m_tmp;
423  }
424  else
425  {
427  for (int j = 0; j < m_nvars; j++)
428  {
430  }
431  }
432 
433  // Different storage for every stage derivative as the data
434  // will be re-used to update the solution -> m_F is a TripleArray
436  for (int i = 0; i < m_numstages; ++i)
437  {
438  m_F[i] = DoubleArray(m_nvars);
439  for (int j = 0; j < m_nvars; j++)
440  {
441  m_F[i][j] = Array<OneD, NekDouble>(m_npoints, 0.0);
442  }
443  }
444 
445  if (type == eIMEX)
446  {
448  for (int i = 0; i < m_numstages; ++i)
449  {
451  for (int j = 0; j < m_nvars; j++)
452  {
454  }
455  }
456  }
457 
458  // Finally, flag that the memory has been initialised.
459  m_initialised = true;
460  } // end else
461 
462  // For an exponential integrator if the time increment or the
463  // number of variables has changed then the exponenial matrices
464  // must be recomputed.
465  if (type == eExponential)
466  {
467  if (m_lastDeltaT != deltaT || m_lastNVars != GetFirstDim(y_old) )
468  {
470  InitializeSecondaryData( this, deltaT );
471 
472  m_lastDeltaT = deltaT;
473  m_lastNVars = GetFirstDim(y_old);
474  }
475  }
476 
477  // The loop below calculates the stage values and derivatives
478  for (int stage = 0; stage < m_numstages; stage++)
479  {
480  if ((stage == 0) && m_firstStageEqualsOldSolution)
481  {
482  for (int k = 0; k < m_nvars; k++)
483  {
484  Vmath::Vcopy(m_npoints, y_old[0][k], 1, m_Y[k], 1);
485  }
486 
487  m_T = t_old[0];
488  }
489  else
490  {
491  // The stage values m_Y are a linear combination of:
492  // 1: The stage derivatives:
493  if (stage != 0)
494  {
495  for (int k = 0; k < m_nvars; k++)
496  {
497  if (type == eExponential)
498  {
499  Vmath::Smul(m_npoints, deltaT * m_A_phi[k][stage][0],
500  m_F[0][k], 1, m_tmp[k], 1);
501  }
502  else
503  {
504  Vmath::Smul(m_npoints, deltaT * A(stage, 0),
505  m_F[0][k], 1, m_tmp[k], 1);
506  }
507 
508  if (type == eIMEX)
509  {
510  Vmath::Svtvp(m_npoints, deltaT * A_IMEX(stage, 0),
511  m_F_IMEX[0][k], 1, m_tmp[k], 1,
512  m_tmp[k], 1);
513  }
514  }
515  }
516 
517  m_T = A(stage, 0) * deltaT;
518 
519  for (int j = 1; j < stage; j++)
520  {
521  for (int k = 0; k < m_nvars; k++)
522  {
523  if (type == eExponential)
524  {
525  Vmath::Svtvp(m_npoints, deltaT * m_A_phi[k][stage][j],
526  m_F[j][k], 1, m_tmp[k], 1, m_tmp[k], 1);
527  }
528  else
529  {
530  Vmath::Svtvp(m_npoints, deltaT * A(stage, j),
531  m_F[j][k], 1, m_tmp[k], 1, m_tmp[k], 1);
532  }
533 
534  if (type == eIMEX)
535  {
536  Vmath::Svtvp(m_npoints, deltaT * A_IMEX(stage, j),
537  m_F_IMEX[j][k], 1, m_tmp[k], 1,
538  m_tmp[k], 1);
539  }
540  }
541 
542  m_T += A(stage, j) * deltaT;
543  }
544 
545  // 2: The imported multi-step solution of the previous time level:
546  for (int j = 0; j < m_numsteps; j++)
547  {
548  for (int k = 0; k < m_nvars; k++)
549  {
550  if (type == eExponential)
551  {
552  Vmath::Svtvp(m_npoints, m_U_phi[k][stage][j],
553  y_old[j][k], 1, m_tmp[k], 1, m_tmp[k], 1);
554  }
555  else
556  {
557  Vmath::Svtvp(m_npoints, U(stage, j),
558  y_old[j][k], 1, m_tmp[k], 1, m_tmp[k], 1);
559  }
560  }
561 
562  m_T += U(stage, j) * t_old[j];
563  }
564  } // end else
565 
566  // Calculate the stage derivative based upon the stage value
567  if (type == eDiagonallyImplicit)
568  {
569  if( (stage == 0) && m_firstStageEqualsOldSolution )
570  {
571  op.DoOdeRhs(m_Y, m_F[stage], m_T);
572  }
573  else
574  {
575  if (m_numstages == 1)
576  {
577  m_T = t_old[0] + deltaT;
578  }
579  else
580  {
581  m_T = t_old[0];
582  for (int j = 0; j <= stage; ++j)
583  {
584  m_T += A(stage, j) * deltaT;
585  }
586  }
587 
588  op.DoImplicitSolve(m_tmp, m_Y, m_T, A(stage, stage) * deltaT);
589 
590  for (int k = 0; k < m_nvars; ++k)
591  {
592  Vmath::Vsub(m_npoints, m_Y[k], 1, m_tmp[k], 1,
593  m_F[stage][k], 1);
594  Vmath::Smul(m_npoints, 1.0 / (A(stage, stage) * deltaT),
595  m_F[stage][k], 1, m_F[stage][k], 1);
596  }
597  }
598  }
599  else if (type == eIMEX)
600  {
601  if (m_numstages == 1)
602  {
603  m_T = t_old[0] + deltaT;
604  }
605  else
606  {
607  m_T = t_old[0];
608  for (int j = 0; j <= stage; ++j)
609  {
610  m_T += A(stage, j) * deltaT;
611  }
612  }
613 
614  if (fabs(A(stage, stage)) > NekConstants::kNekZeroTol)
615  {
616  op.DoImplicitSolve(m_tmp, m_Y, m_T, A(stage, stage) * deltaT);
617 
618  for (int k = 0; k < m_nvars; k++)
619  {
620  Vmath::Vsub(m_npoints, m_Y[k], 1, m_tmp[k], 1,
621  m_F[stage][k], 1);
622  Vmath::Smul(m_npoints, 1.0 / (A(stage, stage) * deltaT),
623  m_F[stage][k], 1, m_F[stage][k], 1);
624  }
625  }
626  op.DoOdeRhs(m_Y, m_F_IMEX[stage], m_T);
627  }
628  else if (type == eExplicit || m_schemeType == eExponential)
629  {
630  // Avoid projecting the same solution twice
631  if (!((stage == 0) && m_firstStageEqualsOldSolution))
632  {
633  // ensure solution is in correct space
634  op.DoProjection(m_Y, m_Y, m_T);
635  }
636 
637  op.DoOdeRhs(m_Y, m_F[stage], m_T);
638  }
639  }
640 
641  // Next, the solution vector y at the new time level will
642  // be calculated.
643  //
644  // For multi-step methods, this includes updating the
645  // values of the auxiliary parameters
646  //
647  // The loop below calculates the solution at the new time
648  // level
649  //
650  // If last stage equals the new solution, the new solution
651  // needs not be calculated explicitly but can simply be
652  // copied. This saves a solve.
653  int i_start = 0;
655  {
656  for (int k = 0; k < m_nvars; k++)
657  {
658  Vmath::Vcopy(m_npoints, m_Y[k], 1, y_new[0][k], 1);
659  }
660 
661  if (m_numstages == 1 && type == eIMEX)
662  {
663  t_new[0] = t_old[0] + deltaT;
664  }
665  else
666  {
667  t_new[0] = B(0, 0) * deltaT;
668 
669  for (int j = 1; j < m_numstages; j++)
670  {
671  t_new[0] += B(0, j) * deltaT;
672  }
673 
674  for (int j = 0; j < m_numsteps; j++)
675  {
676  t_new[0] += V(0, j) * t_old[j];
677  }
678  }
679 
680  i_start = 1;
681  }
682 
683  for (int i = i_start; i < m_numsteps; i++)
684  {
685  // The solution at the new time level is a linear
686  // combination of:
687  // 1: the stage derivatives
688  for (int k = 0; k < m_nvars; k++)
689  {
690  if (type == eExponential)
691  {
692  Vmath::Smul(m_npoints, deltaT * m_B_phi[k][i][0],
693  m_F[0][k], 1, y_new[i][k], 1);
694  }
695  else
696  {
697  Vmath::Smul(m_npoints, deltaT * B(i, 0),
698  m_F[0][k], 1, y_new[i][k], 1);
699  }
700 
701  if (type == eIMEX)
702  {
703  Vmath::Svtvp(m_npoints, deltaT * B_IMEX(i, 0),
704  m_F_IMEX[0][k], 1, y_new[i][k], 1, y_new[i][k], 1);
705  }
706  }
707 
708  if (m_numstages != 1 || type != eIMEX)
709  {
710  t_new[i] = B(i, 0) * deltaT;
711  }
712 
713  for (int j = 1; j < m_numstages; j++)
714  {
715  for (int k = 0; k < m_nvars; k++)
716  {
717  if (type == eExponential)
718  {
719  Vmath::Svtvp(m_npoints, deltaT * m_B_phi[k][i][j],
720  m_F[j][k], 1, y_new[i][k], 1, y_new[i][k], 1);
721  }
722  else
723  {
724  Vmath::Svtvp(m_npoints, deltaT * B(i, j),
725  m_F[j][k], 1, y_new[i][k], 1, y_new[i][k], 1);
726  }
727 
728  if (type == eIMEX)
729  {
730  Vmath::Svtvp(m_npoints, deltaT * B_IMEX(i, j),
731  m_F_IMEX[j][k], 1, y_new[i][k], 1,
732  y_new[i][k], 1);
733  }
734  }
735 
736  if (m_numstages != 1 || type != eIMEX)
737  {
738  t_new[i] += B(i, j) * deltaT;
739  }
740  }
741 
742  // 2: the imported multi-step solution of the previous
743  // time level
744  for (int j = 0; j < m_numsteps; j++)
745  {
746  for (int k = 0; k < m_nvars; k++)
747  {
748  if (type == eExponential)
749  {
750  Vmath::Svtvp(m_npoints, m_V_phi[k][i][j], y_old[j][k], 1,
751  y_new[i][k], 1, y_new[i][k], 1);
752  }
753  else
754  {
755  Vmath::Svtvp(m_npoints, V(i, j), y_old[j][k], 1,
756  y_new[i][k], 1, y_new[i][k], 1);
757  }
758  }
759 
760  if (m_numstages != 1 || type != eIMEX)
761  {
762  t_new[i] += V(i, j) * t_old[j];
763  }
764  }
765  }
766 
767  // Ensure that the new solution is projected if necessary
768  if (type == eExplicit || m_schemeType == eExponential)
769  {
770  op.DoProjection(y_new[0], y_new[0], t_new[0]);
771  }
772 
773 } // end TimeIntegrate()
774 
776 {
777  // First stage equals old solution if:
778  // 1. the first row of the coefficient matrix A consists of zeros
779  // 2. U[0][0] is equal to one and all other first row entries of U are zero
780 
781  // 1. Check first condition
782  for (int m = 0; m < m_A.size(); m++)
783  {
784  for (int i = 0; i < m_numstages; i++)
785  {
786  if (fabs(m_A[m][0][i]) > NekConstants::kNekZeroTol)
787  {
789  return;
790  }
791  }
792  }
793 
794  // 2. Check second condition
795  if (fabs(m_U[0][0] - 1.0) > NekConstants::kNekZeroTol)
796  {
798  return;
799  }
800 
801  for (int i = 1; i < m_numsteps; i++)
802  {
803  if (fabs(m_U[0][i]) > NekConstants::kNekZeroTol)
804  {
806  return;
807  }
808  }
809 
811 }
812 
814 {
815  // Last stage equals new solution if:
816  // 1. the last row of the coefficient matrix A is equal to the first row of
817  // matrix B
818  // 2. the last row of the coefficient matrix U is equal to the first row of
819  // matrix V
820 
821  // 1. Check first condition
822  for (int m = 0; m < m_A.size(); m++)
823  {
824  for (int i = 0; i < m_numstages; i++)
825  {
826  if (fabs(m_A[m][m_numstages-1][i] - m_B[m][0][i]) >
828  {
830  return;
831  }
832  }
833  }
834 
835  // 2. Check second condition
836  for (int i = 0; i < m_numsteps; i++)
837  {
838  if (fabs(m_U[m_numstages-1][i] - m_V[0][i]) > NekConstants::kNekZeroTol)
839  {
841  return;
842  }
843  }
844 
846 }
847 
849 {
850 #ifdef DEBUG
851  int IMEXdim = m_A.size();
852  int dim = m_A[0].GetRows();
853 
855 
856  if (m_schemeType == eExponential)
857  vertype[0] = eExponential;
858 
859  for (int m = 0; m < IMEXdim; m++)
860  {
861  for (int i = 0; i < dim; i++)
862  {
863  if (fabs(m_A[m][i][i]) > NekConstants::kNekZeroTol)
864  {
865  vertype[m] = eDiagonallyImplicit;
866  }
867  }
868 
869  for (int i = 0; i < dim; i++)
870  {
871  for (int j = i + 1; j < dim; j++)
872  {
873  if (fabs(m_A[m][i][j]) > NekConstants::kNekZeroTol)
874  {
875  vertype[m] = eImplicit;
876  ASSERTL1(false, "Fully Implicit schemes cannnot be handled "
877  "by the TimeIntegrationScheme class");
878  }
879  }
880  }
881  }
882 
883  if (IMEXdim == 2)
884  {
885  ASSERTL1(m_B.size() == 2, "Coefficient Matrix B should have an "
886  "implicit and explicit part for IMEX schemes");
887 
888  if ((vertype[0] == eDiagonallyImplicit) && (vertype[1] == eExplicit))
889  {
890  vertype[0] = eIMEX;
891  }
892  else
893  {
894  ASSERTL1(false, "This is not a proper IMEX scheme");
895  }
896  }
897 
898  ASSERTL1(vertype[0] == m_schemeType,
899  "Time integration scheme coefficients do not match its type");
900 #endif
901 }
902 
904  ConstTripleArray &y_old,
905  ConstSingleArray &t_old,
906  TripleArray &y_new,
907  SingleArray &t_new,
908  const TimeIntegrationSchemeOperators &op) const
909 {
910 #ifdef DEBUG
911  boost::ignore_unused(op);
912 #else
913  boost::ignore_unused(y_old, t_old, y_new, t_new, op);
914 #endif
915 
916  // Check if arrays are all of consistent size
917 
918  ASSERTL1(y_old.size() == m_numsteps,
919  "Non-matching number of steps.");
920  ASSERTL1(y_new.size() == m_numsteps,
921  "Non-matching number of steps.");
922 
923  ASSERTL1(y_old[0].size() == y_new[0].size(),
924  "Non-matching number of variables.");
925  ASSERTL1(y_old[0][0].size() == y_new[0][0].size(),
926  "Non-matching number of coefficients.");
927 
928  ASSERTL1(t_old.size() == m_numsteps,
929  "Non-matching number of steps.");
930  ASSERTL1(t_new.size() == m_numsteps,
931  "Non-matching number of steps.");
932 
933  return true;
934 }
935 
936 // Friend Operators
937 std::ostream &operator<<(std::ostream &os,
939 {
940  return operator << (os, *rhs);
941 }
942 
943 std::ostream &operator<<(std::ostream &os,
944  const TimeIntegrationAlgorithmGLM &rhs)
945 {
946  int r = rhs.m_numsteps;
947  int s = rhs.m_numstages;
949 
950  int oswidth = 9;
951  int osprecision = 6;
952 
953  os << "Time Integration Scheme (Master): "
954  << rhs.m_parent->GetFullName() << "\n"
955  << "Time Integration Phase : " << rhs.m_name << "\n"
956  << "- number of steps: " << r << "\n"
957  << "- number of stages: " << s << "\n"
958  << "General linear method tableau:\n";
959 
960  for (int i = 0; i < s; i++)
961  {
962  for (int j = 0; j < s; j++)
963  {
964  os.width(oswidth);
965  os.precision(osprecision);
966  os << std::right << rhs.A(i, j) << " ";
967  }
968  if (type == eIMEX)
969  {
970  os << " '";
971  for (int j = 0; j < s; j++)
972  {
973  os.width(oswidth);
974  os.precision(osprecision);
975  os << std::right << rhs.A_IMEX(i, j) << " ";
976  }
977  }
978 
979  os << " |";
980 
981  for (int j = 0; j < r; j++)
982  {
983  os.width(oswidth);
984  os.precision(osprecision);
985  os << std::right << rhs.U(i, j);
986  }
987  os << std::endl;
988  }
989 
990  int imexflag = (type == eIMEX) ? 2 : 1;
991  for (int i = 0; i < (r + imexflag * s) * (oswidth + 1) + imexflag * 2 - 1;
992  i++)
993  {
994  os << "-";
995  }
996  os << std::endl;
997 
998  for (int i = 0; i < r; i++)
999  {
1000  for (int j = 0; j < s; j++)
1001  {
1002  os.width(oswidth);
1003  os.precision(osprecision);
1004  os << std::right << rhs.B(i, j) << " ";
1005  }
1006  if (type == eIMEX)
1007  {
1008  os << " '";
1009  for (int j = 0; j < s; j++)
1010  {
1011  os.width(oswidth);
1012  os.precision(osprecision);
1013  os << std::right << rhs.B_IMEX(i, j) << " ";
1014  }
1015  }
1016 
1017  os << " |";
1018 
1019  for (int j = 0; j < r; j++)
1020  {
1021  os.width(oswidth);
1022  os.precision(osprecision);
1023  os << std::right << rhs.V(i, j);
1024  }
1025 
1026  os << " |";
1027 
1028  os.width(oswidth);
1029  os.precision(osprecision);
1030  os << std::right << rhs.m_timeLevelOffset[i];
1031 
1032  if( i < rhs.m_numMultiStepValues )
1033  {
1034  os << std::right << " value";
1035  }
1036  else
1037  {
1038  os << std::right << " derivative";
1039  }
1040 
1041  os << std::endl;
1042  }
1043 
1044  if( type == eExponential )
1045  {
1046  for (int k = 0; k < rhs.m_nvars; k++)
1047  {
1048  os << std::endl
1049  << "General linear method exponential tableau for variable "
1050  << k << ":\n";
1051 
1052  for (int i = 0; i < s; i++)
1053  {
1054  for (int j = 0; j < s; j++)
1055  {
1056  os.width(oswidth);
1057  os.precision(osprecision);
1058  os << std::right << rhs.m_A_phi[k][i][j] << " ";
1059  }
1060 
1061  os << " |";
1062 
1063  for (int j = 0; j < r; j++)
1064  {
1065  os.width(oswidth);
1066  os.precision(osprecision);
1067  os << std::right << rhs.m_U_phi[k][i][j];
1068  }
1069  os << std::endl;
1070  }
1071 
1072  int imexflag = (type == eIMEX) ? 2 : 1;
1073  for (int i = 0; i < (r + imexflag * s) * (oswidth + 1) + imexflag * 2 - 1;
1074  i++)
1075  {
1076  os << "-";
1077  }
1078  os << std::endl;
1079 
1080  for (int i = 0; i < r; i++)
1081  {
1082  for (int j = 0; j < s; j++)
1083  {
1084  os.width(oswidth);
1085  os.precision(osprecision);
1086  os << std::right << rhs.m_B_phi[k][i][j] << " ";
1087  }
1088 
1089  os << " |";
1090 
1091  for (int j = 0; j < r; j++)
1092  {
1093  os.width(oswidth);
1094  os.precision(osprecision);
1095  os << std::right << rhs.m_V_phi[k][i][j];
1096  }
1097  os << std::endl;
1098  }
1099  }
1100  }
1101 
1102  return os;
1103 } // end function operator<<
1104 
1105 } // end namespace LibUtilities
1106 } // end namespace NekTar
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode....
Definition: ErrorUtil.hpp:250
NekDouble A(const unsigned int i, const unsigned int j) const
TripleArray m_F
Explicit RHS of each stage equation.
bool CheckTimeIntegrateArguments(ConstTripleArray &y_old, ConstSingleArray &t_old, TripleArray &y_new, SingleArray &t_new, const TimeIntegrationSchemeOperators &op) const
int m_npoints
The size of inner data which is stored for reuse.
DoubleArray m_tmp
Array containing the stage values.
NekDouble B_IMEX(const unsigned int i, const unsigned int j) const
NekDouble V(const unsigned int i, const unsigned int j) const
const TimeIntegrationScheme * m_parent
Parent scheme object.
NekDouble m_T
Used to store the Explicit stage derivative of IMEX schemes.
const Array< OneD, const unsigned int > & GetTimeLevelOffset() const
NekDouble A_IMEX(const unsigned int i, const unsigned int j) const
NekDouble B(const unsigned int i, const unsigned int j) const
LUE ConstDoubleArray & TimeIntegrate(const NekDouble deltaT, TimeIntegrationSolutionGLMSharedPtr &y, const TimeIntegrationSchemeOperators &op)
Explicit integration of an ODE.
LUE TimeIntegrationSolutionGLMSharedPtr InitializeData(const NekDouble deltaT, ConstDoubleArray &y_0, const NekDouble time, const TimeIntegrationSchemeOperators &op)
This function initialises the time integration scheme.
NekDouble m_lastDeltaT
bool to identify array initialization
NekDouble U(const unsigned int i, const unsigned int j) const
TripleArray m_F_IMEX
Array corresponding to the stage Derivatives.
Base class for GLM time integration schemes.
Binds a set of functions for use by time integration schemes.
void DoImplicitSolve(InArrayType &inarray, OutArrayType &outarray, const NekDouble time, const NekDouble lambda) const
void DoProjection(InArrayType &inarray, OutArrayType &outarray, const NekDouble time) const
void DoOdeRhs(InArrayType &inarray, OutArrayType &outarray, const NekDouble time) const
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
static void Dscal(const int &n, const double &alpha, double *x, const int &incx)
BLAS level 1: x = alpha x.
Definition: Blas.hpp:182
AT< AT< NekDouble > > DoubleArray
std::shared_ptr< TimeIntegrationAlgorithmGLM > TimeIntegrationAlgorithmGLMSharedPtr
@ eImplicit
Fully implicit scheme.
@ eExplicit
Formally explicit scheme.
@ eExponential
Exponential scheme.
@ eDiagonallyImplicit
Diagonally implicit scheme (e.g. the DIRK schemes)
@ eIMEX
Implicit Explicit General Linear Method.
std::shared_ptr< TimeIntegrationSolutionGLM > TimeIntegrationSolutionGLMSharedPtr
AT< AT< AT< NekDouble > > > TripleArray
std::ostream & operator<<(std::ostream &os, const BasisKey &rhs)
static const NekDouble kNekZeroTol
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:1
double NekDouble
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.cpp:565
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
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.cpp:372