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