Nektar++
TimeIntegrationSchemeFIT.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: TimeIntegrationSchemeFIT.cpp
4//
5// For more information, please see: http://www.nektar.info
6//
7// The MIT License
8//
9// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10// Department of Aeronautics, Imperial College London (UK), and Scientific
11// Computing and Imaging Institute, University of Utah (USA).
12//
13// Permission is hereby granted, free of charge, to any person obtaining a
14// copy of this software and associated documentation files (the "Software"),
15// to deal in the Software without restriction, including without limitation
16// the rights to use, copy, modify, merge, publish, distribute, sublicense,
17// and/or sell copies of the Software, and to permit persons to whom the
18// Software is furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included
21// in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29// DEALINGS IN THE SOFTWARE.
30//
31// Description: implementation of time integration scheme FIT class
32//
33///////////////////////////////////////////////////////////////////////////////
34
35// Note: The file is named TimeIntegrationSchemeFIT to parallel the
36// TimeIntegrationSchemeGLM file but the class is named
37// FractionalInTimeIntegrationScheme so keep with the factory naming
38// convention.
39
41
42namespace Nektar
43{
44namespace LibUtilities
45{
46/**
47 * @class FractionalInTimeIntegrationScheme
48 *
49 * A fast convolution algorithm for computing solutions to (Caputo)
50 * time-fractional differential equations. This is an explicit solver
51 * that expresses the solution as an integral over a Talbot curve,
52 * which is discretized with quadrature. First-order quadrature is
53 * currently implemented (Soon be expanded to forth order).
54 */
56 std::string variant, size_t order, std::vector<NekDouble> freeParams)
57 : TimeIntegrationScheme(variant, order, freeParams),
58 m_name("FractionalInTime")
59{
60 m_variant = variant;
61 m_order = order;
62 m_freeParams = freeParams;
63
64 // Currently up to 4th order is implemented.
65 ASSERTL1(1 <= order && order <= 4,
66 "FractionalInTime Time integration scheme bad order: " +
67 std::to_string(order));
68
69 ASSERTL1(freeParams.size() == 0 || // Use defaults
70 freeParams.size() == 1 || // Alpha
71 freeParams.size() == 2 || // Base
72 freeParams.size() == 6, // Talbot quadrature rule
73 "FractionalInTime Time integration scheme invalid number "
74 "of free parameters, expected zero, one <alpha>, "
75 "two <alpha, base>, or "
76 "six <alpha, base, nQuadPts, sigma, mu0, nu> received " +
77 std::to_string(freeParams.size()));
78
79 if (freeParams.size() >= 1)
80 {
81 m_alpha = freeParams[0]; // Value for exp integration.
82 }
83
84 if (freeParams.size() >= 2)
85 {
86 m_base = freeParams[1]; // "Base" of the algorithm.
87 }
88
89 if (freeParams.size() == 6)
90 {
91 m_nQuadPts = freeParams[2]; // Number of Talbot quadrature rule points
92 m_sigma = freeParams[3];
93 m_mu0 = freeParams[4];
94 m_nu = freeParams[5];
95 }
96}
97
98/**
99 * @brief Worker method to initialize the integration scheme.
100 */
102 const NekDouble deltaT, ConstDoubleArray &y_0, const NekDouble time,
104
105{
106 m_op = op;
107 m_nvars = y_0.size();
108 m_npoints = y_0[0].size();
109
110 m_deltaT = deltaT;
111
112 m_T = time; // Finial time;
114
115 // The +2 below is a buffer, and keeps +2 extra rectangle groups
116 // in case T needs to be increased later.
118
119 // Demarcation integers - one array that is re-used
120 m_qml = Array<OneD, size_t>(m_Lmax - 1, size_t(0));
121
122 // Demarcation interval markers - one array that is re-used
123 m_taus = Array<OneD, size_t>(m_Lmax + 1, size_t(0));
124
125 // Storage of the initial values.
126 m_u0 = y_0;
127
128 // Storage for the exponential factor in the integral
129 // contribution. One array that is re-used
131
132 // Storage of previous states and associated timesteps.
133 m_u = TripleArray(m_order + 1);
134
135 for (size_t m = 0; m <= m_order; ++m)
136 {
137 m_u[m] = DoubleArray(m_nvars);
138
139 for (size_t i = 0; i < m_nvars; ++i)
140 {
141 m_u[m][i] = SingleArray(m_npoints, 0.0);
142
143 for (size_t j = 0; j < m_npoints; ++j)
144 {
145 // Store the initial values as the first previous state.
146 if (m == 0)
147 m_u[m][i][j] = m_u0[i][j];
148 else
149 m_u[m][i][j] = 0;
150 }
151 }
152 }
153
154 // Storage for the stage derivative as the data will be re-used to
155 // update the solution.
157 // Storage of the next solution from the final increment.
159 // Storage for the integral contribution.
161
162 for (size_t i = 0; i < m_nvars; ++i)
163 {
164 m_F[i] = SingleArray(m_npoints, 0.0);
165 m_uNext[i] = SingleArray(m_npoints, 0.0);
167 }
168
169 // J
170 m_J = SingleArray(m_order, 0.0);
171
172 m_J[0] = pow(m_deltaT, m_alpha) / tgamma(m_alpha + 1.);
173
174 for (size_t m = 1, m_1 = 0; m < m_order; ++m, ++m_1)
175 {
176 m_J[m] = m_J[m_1] * NekDouble(m) / (NekDouble(m) + m_alpha);
177 }
178
179 // Ahat array, one for each order.
180 // These are elements in a multi-step exponential integrator tableau
182
183 for (size_t m = 1; m <= m_order; ++m)
184 {
185 m_Ahats[m] = DoubleArray(m);
186
187 for (size_t n = 0; n < m; ++n)
188 {
189 m_Ahats[m][n] = SingleArray(m, 0.0);
190 }
191
192 switch (m)
193 {
194 case 1:
195 m_Ahats[m][0][0] = 1.;
196 break;
197
198 case 2:
199 m_Ahats[m][0][0] = 1.;
200 m_Ahats[m][0][1] = 0.;
201 m_Ahats[m][1][0] = 1.;
202 m_Ahats[m][1][1] = -1.;
203 break;
204
205 case 3:
206 m_Ahats[m][0][0] = 1.;
207 m_Ahats[m][0][1] = 0.;
208 m_Ahats[m][0][2] = 0;
209 m_Ahats[m][1][0] = 3. / 2.;
210 m_Ahats[m][1][1] = -2.;
211 m_Ahats[m][1][2] = 1. / 2.;
212 m_Ahats[m][2][0] = 1. / 2.;
213 m_Ahats[m][2][1] = -1.;
214 m_Ahats[m][2][2] = 1. / 2.;
215 break;
216
217 case 4:
218 m_Ahats[m][0][0] = 1.;
219 m_Ahats[m][0][1] = 0.;
220 m_Ahats[m][0][2] = 0.;
221 m_Ahats[m][0][3] = 0.;
222
223 m_Ahats[m][1][0] = 11. / 6.;
224 m_Ahats[m][1][1] = -3;
225 m_Ahats[m][1][2] = 3. / 2.;
226 m_Ahats[m][1][3] = -1. / 3.;
227
228 m_Ahats[m][2][0] = 1.;
229 m_Ahats[m][2][1] = -5. / 2.;
230 m_Ahats[m][2][2] = 2.;
231 m_Ahats[m][2][3] = -1. / 2.;
232
233 m_Ahats[m][3][0] = 1. / 6.;
234 m_Ahats[m][3][1] = -1. / 2.;
235 m_Ahats[m][3][2] = 1. / 2.;
236 m_Ahats[m][3][3] = -1. / 6.;
237 break;
238
239 default:
240
241 m_Ahats[m][0][0] = 1;
242
243 for (size_t j = 2; j <= m; ++j)
244 {
245 for (size_t i = 0; i < m; ++i)
246 {
247 m_Ahats[m][j - 1][i] = pow((1 - j), i);
248 }
249 }
250
251 ASSERTL1(false, "No matrix inverse.");
252
253 // Future code: m_Ahats[m] = inv(m_Ahats[m]);
254
255 break;
256 }
257 }
258
259 // Mulitply the last Ahat array, transposed, by J
261
262 for (size_t i = 0; i < m_order; ++i)
263 {
264 for (size_t j = 0; j < m_order; ++j)
265 {
266 m_AhattJ[i] += m_Ahats[m_order][j][i] * m_J[j];
267 }
268 }
269
271
272 for (size_t l = 0; l < m_Lmax; ++l)
273 {
275 }
276}
277
278/**
279 * @brief Worker method that performs the time integration.
280 */
282 const size_t timestep, const NekDouble delta_t)
283{
284 boost::ignore_unused(delta_t);
285
286 ASSERTL1(delta_t == m_deltaT,
287 "Delta T has changed which is not permitted.");
288
289 // The Fractional in Time works via the logical? time step value.
290 size_t timeStep = timestep + 1;
291
292 // Update the storage and counters for integral classes. Performs
293 // staging for updating u.
294 for (size_t l = 0; l < m_Lmax; ++l)
295 {
296 updateStage(timeStep, m_integral_classes[l]);
297 }
298
299 // Compute u update to time timeStep * m_deltaT. Stored in
300 // m_uNext.
301 finalIncrement(timeStep);
302
303 // Contributions to the current integral
304 size_t L = computeTaus(m_base, timeStep);
305
306 for (size_t l = 0; l < L; ++l)
307 {
308 // Integral contribution over [taus(i+1) taus(i)]. Stored in
309 // m_uInt.
311
312 for (size_t i = 0; i < m_nvars; ++i)
313 {
314 for (size_t j = 0; j < m_npoints; ++j)
315 {
316 m_uNext[i][j] += m_uInt[i][j].real();
317 }
318 }
319 }
320
321 // Shuffle the previous solutions back one in the history.
322 for (size_t m = m_order; m > 0; --m)
323 {
324 for (size_t i = 0; i < m_nvars; ++i)
325 {
326 for (size_t j = 0; j < m_npoints; ++j)
327 {
328 m_u[m][i][j] = m_u[m - 1][i][j];
329 }
330 }
331 }
332
333 // Get the current solution.
334 for (size_t i = 0; i < m_nvars; ++i)
335 {
336 for (size_t j = 0; j < m_npoints; ++j)
337 {
338 m_u[0][i][j] = m_uNext[i][j] + m_u0[i][j];
339
340 m_uNext[i][j] = 0; // Zero out for the next itereation.
341 }
342 }
343
344 // Update the storage and counters for integral classes to
345 // time timeStep * m_deltaT. Also time-steps the sandboxes and stashes.
346 for (size_t i = 0; i < m_Lmax; ++i)
347 {
349 }
350
351 return m_u[0];
352}
353
354/**
355 * @brief Method that increments the counter then performs mod
356 * calculation.
357 */
359 const size_t base) const
360{
361 return (counter + 1) % base;
362}
363
364/**
365 * @brief Method to compute the smallest integer L such that base < 2
366 * * base^l.
367 */
369 const size_t l) const
370{
371 size_t L = ceil(log(l / 2.0) / log(base));
372
373 if (l % (size_t)(2 * pow(base, L)) == 0)
374 {
375 ++L;
376 }
377
378 return L;
379}
380
381/**
382 * @brief Method to compute the demarcation integers q_{m, ell}.
383 *
384 * Returns a length-(L-1) vector qml such that h*taus are interval
385 * boundaries for a partition of [0, m h]. The value of h is not
386 * needed to compute this vector.
387 */
389 const size_t m)
390{
391 size_t L = computeL(base, m);
392
393 // m_qml is set in InitializeScheme to be the largest length expected.
394 // qml = Array<OneD, size_t>( L-1, 0 );
395
396 for (size_t i = 0; i < L - 1; ++i)
397 {
398 m_qml[i] = floor(m / pow(base, i + 1)) - 1;
399 }
400
401 return L;
402}
403
404/**
405 * @brief Method to compute the demarcation interval marker tau_{m, ell}.
406 *
407 * Returns a length-(L+1) vector tauml such that h*taus are interval
408 * boundaries for a partition of [0, m h]. The value of h is not
409 * needed to compute this vector.
410 */
412 const size_t m)
413{
414 if (m == 1)
415 {
416 m_taus[0] = 0;
417
418 return 0;
419 }
420 else
421 {
422 size_t L = computeQML(base, m);
423
424 // m_taus is set in InitializeScheme to be the largest length
425 // expected.
426
427 m_taus[0] = m - 1;
428
429 for (size_t i = 1; i < L; ++i)
430 {
431 m_taus[i] = m_qml[i - 1] * pow(base, i);
432 }
433
434 m_taus[L] = 0;
435
436 return L;
437 }
438}
439
440/**
441 * @brief Method to compute the quadrature rule over Tablot contour
442 *
443 * Returns a quadrature rule over the Tablot contour defined by the
444 * parameterization.
445 *
446 * gamma(th) = sigma + mu * ( th*cot(th) + i*nu*th ), -pi < th < pi
447 *
448 * An N-point rule is returned, equidistant in the parameter theta. The
449 * returned quadrature rule approximes an integral over the contour.
450 */
452 const size_t nQuadPts, const NekDouble mu, const NekDouble nu,
453 const NekDouble sigma, ComplexSingleArray &lamb,
454 ComplexSingleArray &w) const
455{
456 lamb = ComplexSingleArray(nQuadPts, 0.0);
457 w = ComplexSingleArray(nQuadPts, 0.0);
458
459 for (size_t q = 0; q < nQuadPts; ++q)
460 {
461 NekDouble th =
462 (NekDouble(q) + 0.5) / NekDouble(nQuadPts) * 2.0 * M_PI - M_PI;
463
464 lamb[q] = sigma + mu * th * std::complex<NekDouble>(1. / tan(th), nu);
465
466 w[q] = std::complex<NekDouble>(0, -1. / NekDouble(nQuadPts)) * mu *
467 std::complex<NekDouble>(1. / tan(th) - th / (sin(th) * sin(th)),
468 nu);
469 }
470
471 // Special case for th = 0 which happens when there is an odd
472 // number of quadrature points.
473 if (nQuadPts % 2 == 1)
474 {
475 size_t q = (nQuadPts + 1) / 2;
476
477 lamb[q] = std::complex<NekDouble>(sigma + mu, 0);
478
479 w[q] = std::complex<NekDouble>(nu * mu / nQuadPts, 0);
480 }
481}
482
483/**
484 * @brief Method to initialize the integral class
485 */
487 const size_t index, Instance &instance) const
488{
489 /**
490 * /brief
491 *
492 * This object stores information for performing integration over
493 * an interval [a, b]. (Defined by taus in the parent calling
494 * function.)
495 *
496 * The "main" object stores information about [a,b]. In
497 * particular, main.ind identifies [a,b] via multiples of h.
498 *
499 * Periodically the values of [a,b] need to be incremented. The
500 * necessary background storage to accomplish this increment
501 * depends whether a or b is being incremented.
502 *
503 * The objects with "f" ("Floor") modifiers are associated with
504 * increments of the interval floor a.
505 *
506 * The objects with "c" ("Ceiling") modifiers are associated with
507 * increments of the interval ceiling b.
508 *
509 * Items on the "stage" are stored for use in computing u at the
510 * current time. Items in the "stash" are stored for use for
511 * future staging. Items in the "sandbox" are being actively
512 * updated at the current time for future stashing. Only items in
513 * the sandbox are time-stepped. the stage and stash locations are
514 * for storage only.
515 *
516 * This is the same for all integral classes, so there's probably
517 * a better way to engineer this. And technically, all that's
518 * needed is the array K(instance.z) anyway.
519 */
520
521 instance.base = m_base;
522 instance.index = index; // Index of this instance
523 instance.active = false; // Used to determine if active
524 instance.activecounter = 0; // Counter used to flip active bit
525 instance.activebase = 2. * pow(m_base, (index - 1));
526
527 // Storage for values of y currently used to update u
533
534 for (size_t q = 0; q < m_nvars; ++q)
535 {
541
542 for (size_t i = 0; i < m_npoints; ++i)
543 {
544 instance.stage_y[q][i] = ComplexSingleArray(m_nQuadPts, 0.0);
545 instance.cstash_y[q][i] = ComplexSingleArray(m_nQuadPts, 0.0);
546 instance.csandbox_y[q][i] = ComplexSingleArray(m_nQuadPts, 0.0);
547 instance.fstash_y[q][i] = ComplexSingleArray(m_nQuadPts, 0.0);
548 instance.fsandbox_y[q][i] = ComplexSingleArray(m_nQuadPts, 0.0);
549 }
550 }
551
552 // Major storage for auxilliary ODE solutions.
553 instance.stage_ind =
554 std::pair<size_t, size_t>(0, 0); // Time-step counters
555 // indicating the interval
556 // ymain is associated with
557
558 // Staging allocation
559 instance.stage_active = false;
560 instance.stage_ccounter = 0;
561 instance.stage_cbase = pow(m_base, index - 1); // This base is halved
562 // after the first cycle
563 instance.stage_fcounter = 0;
564 instance.stage_fbase = pow(m_base, index); // This base is halved
565 // after the first cycle
566
567 // Ceiling stash allocation
568 instance.cstash_counter = 0; // Counter used to determine
569 // when to stash
570
571 instance.cstash_base = pow(m_base, index - 1); // base for counter ind(1)
572 instance.cstash_ind =
573 std::pair<size_t, size_t>(0, 0); // is never used: it always
574 // matches main.ind(1)
575
576 // Ceiling sandbox allocation
577 instance.csandbox_active = false; // Flag to determine when stash 2
578 // is utilized
579 instance.csandbox_counter = 0;
580 instance.csandbox_ind = std::pair<size_t, size_t>(0, 0);
581
582 // Floor stash
583 instance.fstash_base = 2 * pow(m_base, index);
584 instance.fstash_ind = std::pair<size_t, size_t>(0, 0);
585
586 // Floor sandbox
587 instance.fsandbox_active = false;
588 instance.fsandbox_activebase = pow(m_base, index);
589 instance.fsandbox_stashincrement = (m_base - 1) * pow(m_base, index - 1);
590 instance.fsandbox_ind = std::pair<size_t, size_t>(0, 0);
591
592 // Defining parameters of the Talbot contour quadrature rule
593 NekDouble Tl =
594 m_deltaT * (2. * pow(m_base, index) - 1. - pow(m_base, index - 1));
595 NekDouble mu = m_mu0 / Tl;
596
597 // Talbot quadrature rule
598 talbotQuadrature(m_nQuadPts, mu, m_nu, m_sigma, instance.z, instance.w);
599
600 /**
601 * /brief
602 *
603 * With sigma == 0, the dependence of z and w on index is just a
604 * multiplicative scaling factor (mu). So technically we'll only
605 * need one instance of this N-point rule and can scale it
606 * accordingly inside each integral_class instance. Not sure if
607 * this optimization is worth it. Cumulative memory savings would
608 * only be about 4*N*Lmax floats.
609
610 * Below: precomputation for time integration of auxiliary
611 * variables. Everything below here is independent of the
612 * instance index index. Therefore, we could actually just
613 * generate and store one copy of this stuff and use it
614 * everywhere.
615 */
616
617 // 'As' array - one for each order.
618 TripleArray &As = instance.As;
619
620 As = TripleArray(m_order + 2);
621
622 for (size_t m = 1; m <= m_order + 1; ++m)
623 {
624 As[m] = DoubleArray(m);
625
626 for (size_t n = 0; n < m; ++n)
627 {
628 As[m][n] = SingleArray(m, 0.0);
629 }
630
631 switch (m)
632 {
633 case 1:
634 As[m][0][0] = 1.;
635 break;
636
637 case 2:
638 As[m][0][0] = 0.;
639 As[m][0][1] = 1.;
640 As[m][1][0] = 1.;
641 As[m][1][1] = -1.;
642 break;
643
644 case 3:
645 As[m][0][0] = 0.;
646 As[m][0][1] = 1.;
647 As[m][0][2] = 0;
648 As[m][1][0] = 1. / 2.;
649 As[m][1][1] = 0.;
650 As[m][1][2] = -1. / 2.;
651 As[m][2][0] = 1. / 2.;
652 As[m][2][1] = -1.;
653 As[m][2][2] = 1. / 2.;
654 break;
655
656 case 4:
657 As[m][0][0] = 0.;
658 As[m][0][1] = 1.;
659 As[m][0][2] = 0.;
660 As[m][0][3] = 0.;
661
662 As[m][1][0] = 1. / 3.;
663 As[m][1][1] = 1. / 2.;
664 As[m][1][2] = -1.;
665 As[m][1][3] = 1. / 6.;
666
667 As[m][2][0] = 1. / 2.;
668 As[m][2][1] = -1.;
669 As[m][2][2] = 1. / 2.;
670 As[m][2][3] = 0.;
671
672 As[m][3][0] = 1. / 6.;
673 As[m][3][1] = -1. / 2.;
674 As[m][3][2] = 1. / 2.;
675 As[m][3][3] = -1. / 6.;
676 break;
677
678 case 5:
679 As[m][0][0] = 0.;
680 As[m][0][1] = 1.;
681 As[m][0][2] = 0.;
682 As[m][0][3] = 0.;
683 As[m][0][4] = 0.;
684
685 As[m][1][0] = 1. / 4.;
686 As[m][1][1] = 5. / 6.;
687 As[m][1][2] = -3. / 2.;
688 As[m][1][3] = 1. / 2.;
689 As[m][1][4] = -1. / 12.;
690
691 As[m][2][0] = 11. / 24.;
692 As[m][2][1] = -5. / 6.;
693 As[m][2][2] = 1. / 4.;
694 As[m][2][3] = 1. / 6.;
695 As[m][2][4] = -1. / 24.;
696
697 As[m][3][0] = 1. / 4.;
698 As[m][3][1] = -5. / 6.;
699 As[m][3][2] = 1.;
700 As[m][3][3] = -1. / 2.;
701 As[m][3][4] = 1. / 12.;
702
703 As[m][4][0] = 1. / 24.;
704 As[m][4][1] = -1. / 6.;
705 As[m][4][2] = 1. / 4.;
706 As[m][4][3] = -1. / 6.;
707 As[m][4][4] = 1. / 24.;
708 break;
709
710 // The default is a general formula, but the matrix inversion
711 // involved is ill-conditioned, so the special cases above are
712 // epxlicitly given to combat roundoff error in the most-used
713 // scenarios.
714 default:
715 ASSERTL1(false, "No matrix inverse.");
716 break;
717 }
718 }
719
720 // Initialize the exponenetial integrators.
721 instance.E = ComplexSingleArray(m_nQuadPts, 0.0);
722
723 for (size_t q = 0; q < m_nQuadPts; ++q)
724 {
725 instance.E[q] = exp(instance.z[q] * m_deltaT);
726 }
727
728 instance.Eh = ComplexDoubleArray(m_order + 1);
729
730 for (size_t m = 0; m < m_order + 1; ++m)
731 {
732 instance.Eh[m] = ComplexSingleArray(m_nQuadPts, 0.0);
733
734 for (size_t q = 0; q < m_nQuadPts; ++q)
735 {
736 if (m == 0)
737 instance.Eh[0][q] =
738 1. / instance.z[q] * (exp(instance.z[q] * m_deltaT) - 1.0);
739 else
740 instance.Eh[m][q] = -1. / instance.z[q] +
741 NekDouble(m) / (instance.z[q] * m_deltaT) *
742 instance.Eh[m - 1][q];
743 }
744 }
745
746 // 'AtEh' is set for the primary order. If a lower order method is
747 // needed for initializing it will be changed in time_advance then
748 // restored.
749 instance.AtEh = ComplexDoubleArray(m_order + 1);
750
751 for (size_t m = 0; m <= m_order; ++m)
752 {
753 instance.AtEh[m] = ComplexSingleArray(m_nQuadPts, 0.0);
754
755 for (size_t q = 0; q < m_nQuadPts; ++q)
756 {
757 for (size_t i = 0; i <= m_order; ++i)
758 {
759 instance.AtEh[m][q] +=
760 instance.As[m_order + 1][m][i] * instance.Eh[i][q];
761 }
762 }
763 }
764}
765
766/**
767 * @brief Method to rearrange of staging/stashing for current time
768 *
769 * (1) activates ceiling staging
770 * (2) moves ceiling stash ---> stage
771 * (3) moves floor stash --> stage (+ updates all ceiling data)
772 */
774 Instance &instance)
775{
776 // Counter to flip active bit
777 if (!instance.active)
778 {
779 instance.active = (timeStep % instance.activebase == 0);
780 }
781
782 // Determine if staging is necessary
783 if (instance.active)
784 {
785 // Floor staging superscedes ceiling staging
786 if (timeStep % instance.fstash_base == 0)
787 {
788 // Here a swap of the contents can be done because values
789 // will copied into the stash and the f sandbox values will
790 // cleared next.
791 std::swap(instance.stage_y, instance.fstash_y);
792 instance.stage_ind = instance.fstash_ind;
793
794 std::swap(instance.csandbox_y, instance.fsandbox_y);
795 instance.csandbox_ind = instance.fsandbox_ind;
796
797 // After floor staging happens once, new base is base^index
798 instance.fstash_base = pow(instance.base, instance.index);
799
800 // Restart floor sandbox
801 instance.fsandbox_ind = std::pair<size_t, size_t>(0, 0);
802 instance.fsandbox_active = false;
803
804 // Clear the floor sandbox values.
805 for (size_t i = 0; i < m_nvars; ++i)
806 {
807 for (size_t j = 0; j < m_npoints; ++j)
808 {
809 for (size_t q = 0; q < m_nQuadPts; ++q)
810 {
811 instance.fsandbox_y[i][j][q] = 0;
812 }
813 }
814 }
815 }
816
817 // Check for ceiling staging
818 else if (timeStep % instance.stage_cbase == 0)
819 {
820 instance.stage_ind = instance.cstash_ind;
821
822 // A swap of the contents can be done because values will
823 // copied into the stash.
824 std::swap(instance.stage_y, instance.cstash_y);
825 }
826 }
827}
828
829/**
830 * @brief Method to approximate the integral
831 *
832 * \int_{(m-1) h}^{m h} k(m*h -s) f(u, s) dx{s}
833 *
834 * Using a time-stepping scheme of a particular order. Here, k depends
835 * on alpha, the derivative order.
836 */
838{
839 // Note: m_uNext is initialized to zero and then reset to zero
840 // after it is used to update the current solution in TimeIntegrate.
841 for (size_t m = 0; m < m_order; ++m)
842 {
843 m_op.DoOdeRhs(m_u[m], m_F, m_deltaT * (timeStep - m));
844
845 for (size_t i = 0; i < m_nvars; ++i)
846 {
847 for (size_t j = 0; j < m_npoints; ++j)
848 {
849 m_uNext[i][j] += m_F[i][j] * m_AhattJ[m];
850 }
851 }
852 }
853}
854
855/**
856 * @brief Method to get the integral contribution over [taus(i+1)
857 * taus(i)]. Stored in m_uInt.
858 */
860 const size_t timeStep, const size_t tauml, const Instance &instance)
861{
862 // Assume y has already been updated to time level m
863 for (size_t q = 0; q < m_nQuadPts; ++q)
864 {
865 m_expFactor[q] =
866 exp(instance.z[q] * m_deltaT * NekDouble(timeStep - tauml)) *
867 pow(instance.z[q], -m_alpha) * instance.w[q];
868 }
869
870 for (size_t i = 0; i < m_nvars; ++i)
871 {
872 for (size_t j = 0; j < m_npoints; ++j)
873 {
874 m_uInt[i][j] = 0;
875
876 for (size_t q = 0; q < m_nQuadPts; ++q)
877 {
878 m_uInt[i][j] += instance.stage_y[i][j][q] * m_expFactor[q];
879 }
880
881 if (m_uInt[i][j].real() < 1e8)
882 {
883 m_uInt[i][j] = m_uInt[i][j].real();
884 }
885 }
886 }
887}
888
889/**
890 * @brief Method to get the solution to y' = z*y + f(u), using an
891 * exponential integrator with implicit order (m_order + 1)
892 * interpolation of the f(u) term.
893 */
895 Instance &instance,
897{
898 size_t order;
899
900 // Try automated high-order method.
901 if (timeStep <= m_order)
902 {
903 // Not enough history. For now, demote to lower-order method.
904 // TODO: use multi-stage method.
905 order = timeStep;
906
907 // Prep for the time step.
908 for (size_t m = 0; m <= order; ++m)
909 {
910 for (size_t q = 0; q < m_nQuadPts; ++q)
911 {
912 instance.AtEh[m][q] = 0;
913
914 for (size_t i = 0; i <= order; ++i)
915 {
916 instance.AtEh[m][q] +=
917 instance.As[order + 1][m][i] * instance.Eh[i][q];
918 }
919 }
920 }
921 }
922 else
923 {
924 order = m_order;
925 }
926
927 // y = y * instance.E + F * instance.AtEh;
928 for (size_t m = 0; m <= order; ++m)
929 {
930 m_op.DoOdeRhs(m_u[m], m_F, m_deltaT * (timeStep - m));
931
932 for (size_t i = 0; i < m_nvars; ++i)
933 {
934 for (size_t j = 0; j < m_npoints; ++j)
935 {
936 for (size_t q = 0; q < m_nQuadPts; ++q)
937 {
938 // y * instance.E
939 if (m == 0)
940 y[i][j][q] *= instance.E[q];
941
942 // F * instance.AtEh
943 y[i][j][q] += m_F[i][j] * instance.AtEh[m][q];
944 }
945 }
946 }
947 }
948}
949
950/**
951 * @brief Method to update sandboxes to the current time.
952 *
953 * (1) advances ceiling sandbox
954 * (2) moves ceiling sandbox ---> stash
955 * (3) activates floor sandboxing
956 * (4) advances floor sandbox
957 * (5) moves floor sandbox ---> stash
958 */
960 Instance &instance)
961{
962 // (1)
963 // update(instance.csandbox.y)
964 timeAdvance(timeStep, instance, instance.csandbox_y);
965 instance.csandbox_ind.second = timeStep;
966
967 // (2)
968 // Determine if ceiling stashing is necessary
969 instance.cstash_counter =
970 modIncrement(instance.cstash_counter, instance.cstash_base);
971
972 if (timeStep % instance.cstash_base == 0)
973 {
974 // Then need to stash
975 // instance.cstash_y = instance.csandbox_y;
976 instance.cstash_ind = instance.csandbox_ind;
977
978 // Stash the c sandbox value. This step has to be a deep copy
979 // because the values in the sandbox are still needed for the
980 // time advance.
981 for (size_t i = 0; i < m_nvars; ++i)
982 {
983 for (size_t j = 0; j < m_npoints; ++j)
984 {
985 for (size_t q = 0; q < m_nQuadPts; ++q)
986 {
987 instance.cstash_y[i][j][q] = instance.csandbox_y[i][j][q];
988 }
989 }
990 }
991 }
992
993 if (instance.fsandbox_active)
994 {
995 // (4)
996 timeAdvance(timeStep, instance, instance.fsandbox_y);
997
998 instance.fsandbox_ind.second = timeStep;
999
1000 // (5) Move floor sandbox to stash
1001 if ((instance.fsandbox_ind.second - instance.fsandbox_ind.first) %
1002 instance.fsandbox_stashincrement ==
1003 0)
1004 {
1005 // instance.fstash_y = instance.fsandbox_y;
1006 instance.fstash_ind = instance.fsandbox_ind;
1007
1008 // Stash the f sandbox values. This step has to be a deep
1009 // copy because the values in the sandbox are still needed
1010 // for the time advance.
1011 for (size_t i = 0; i < m_nvars; ++i)
1012 {
1013 for (size_t j = 0; j < m_npoints; ++j)
1014 {
1015 for (size_t q = 0; q < m_nQuadPts; ++q)
1016 {
1017 instance.fstash_y[i][j][q] =
1018 instance.fsandbox_y[i][j][q];
1019 }
1020 }
1021 }
1022 }
1023 }
1024 else // Determine if advancing floor sandbox is necessary at next time
1025 {
1026 // (3)
1027 if (timeStep % instance.fsandbox_activebase == 0)
1028 {
1029 instance.fsandbox_active = true;
1030 instance.fsandbox_ind =
1031 std::pair<size_t, size_t>(timeStep, timeStep);
1032 }
1033 }
1034}
1035
1036/**
1037 * @brief Worker method to print details on the integration scheme
1038 */
1040{
1041 os << "Time Integration Scheme: " << GetFullName() << std::endl
1042 << " Alpha " << m_alpha << std::endl
1043 << " Base " << m_base << std::endl
1044 << " Number of instances " << m_Lmax << std::endl
1045 << " Number of quadature points " << m_nQuadPts << std::endl
1046 << " Talbot Parameter: sigma " << m_sigma << std::endl
1047 << " Talbot Parameter: mu0 " << m_mu0 << std::endl
1048 << " Talbot Parameter: nu " << m_nu << std::endl;
1049}
1050
1052{
1053 os << "Time Integration Scheme: " << GetFullName() << std::endl
1054 << " Alpha " << m_alpha << std::endl
1055 << " Base " << m_base << std::endl
1056 << " Number of instances " << m_Lmax << std::endl
1057 << " Number of quadature points " << m_nQuadPts << std::endl
1058 << " Talbot Parameter: sigma " << m_sigma << std::endl
1059 << " Talbot Parameter: mu0 " << m_mu0 << std::endl
1060 << " Talbot Parameter: nu " << m_nu << std::endl;
1061}
1062
1063// Friend Operators
1064std::ostream &operator<<(std::ostream &os,
1066{
1067 rhs.print(os);
1068
1069 return os;
1070}
1071
1072std::ostream &operator<<(std::ostream &os,
1074{
1075 os << *rhs.get();
1076
1077 return os;
1078}
1079
1080} // end namespace LibUtilities
1081} // namespace Nektar
NekDouble L
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode....
Definition: ErrorUtil.hpp:249
size_t computeTaus(const size_t base, const size_t m)
Method to compute the demarcation interval marker tau_{m, ell}.
virtual LUE void v_InitializeScheme(const NekDouble deltaT, ConstDoubleArray &y_0, const NekDouble time, const TimeIntegrationSchemeOperators &op) override
Worker method to initialize the integration scheme.
void timeAdvance(const size_t timeStep, Instance &instance, ComplexTripleArray &y)
Method to get the solution to y' = z*y + f(u), using an exponential integrator with implicit order (m...
void updateStage(const size_t timeStep, Instance &instance)
Method to rearrange of staging/stashing for current time.
void advanceSandbox(const size_t timeStep, Instance &instance)
Method to update sandboxes to the current time.
void integralClassInitialize(const size_t index, Instance &instance) const
Method to initialize the integral class.
virtual LUE void v_print(std::ostream &os) const override
Worker method to print details on the integration scheme.
size_t modIncrement(const size_t counter, const size_t base) const
Method that increments the counter then performs mod calculation.
void finalIncrement(const size_t timeStep)
Method to approximate the integral.
void integralContribution(const size_t timeStep, const size_t tauml, const Instance &instance)
Method to get the integral contribution over [taus(i+1) taus(i)]. Stored in m_uInt.
void talbotQuadrature(const size_t nQuadPts, const NekDouble mu, const NekDouble nu, const NekDouble sigma, ComplexSingleArray &lamb, ComplexSingleArray &w) const
Method to compute the quadrature rule over Tablot contour.
size_t computeL(const size_t base, const size_t m) const
Method to compute the smallest integer L such that base < 2.
virtual LUE void v_printFull(std::ostream &os) const override
virtual LUE ConstDoubleArray & v_TimeIntegrate(const size_t timestep, const NekDouble delta_t) override
Worker method that performs the time integration.
FractionalInTimeIntegrationScheme(std::string variant, size_t order, std::vector< NekDouble > freeParams)
Constructor.
size_t computeQML(const size_t base, const size_t m)
Method to compute the demarcation integers q_{m, ell}.
Base class for time integration schemes.
Binds a set of functions for use by time integration schemes.
void DoOdeRhs(InArrayType &inarray, OutArrayType &outarray, const NekDouble time) const
AT< AT< NekDouble > > DoubleArray
AT< AT< std::complex< NekDouble > > > ComplexDoubleArray
AT< std::complex< NekDouble > > ComplexSingleArray
AT< AT< AT< NekDouble > > > TripleArray
std::shared_ptr< FractionalInTimeIntegrationScheme > FractionalInTimeIntegrationSchemeSharedPtr
AT< AT< AT< std::complex< NekDouble > > > > ComplexTripleArray
std::ostream & operator<<(std::ostream &os, const BasisKey &rhs)
std::vector< double > w(NPUPPER)
std::vector< double > q(NPUPPER *NPUPPER)
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:2
double NekDouble
scalarT< T > log(scalarT< T > in)
Definition: scalar.hpp:303