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