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 
42 namespace Nektar
43 {
44 namespace 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, unsigned int 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(0 < 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  boost::ignore_unused(op);
107 
108  m_nvars = y_0.size();
109  m_npoints = y_0[0].size();
110 
111  m_deltaT = deltaT;
112 
113  m_T = time; // Finial time;
115 
116  // The +2 below is a buffer, and keeps +2 extra rectangle groups
117  // in case T needs to be increased later.
119 
120  // Demarcation integers - one array that is re-used
121  m_qml = Array<OneD, int>(m_Lmax - 1, 0);
122  // Demarcation interval markers - one array that is re-used
123  m_taus = Array<OneD, int>(m_Lmax + 1, 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 (unsigned int m = 0; m <= m_order; ++m)
136  {
137  m_u[m] = DoubleArray(m_nvars);
138 
139  for (unsigned int i = 0; i < m_nvars; ++i)
140  {
141  m_u[m][i] = SingleArray(m_npoints, 0.0);
142 
143  for (unsigned int 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 (unsigned int 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 (unsigned int 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
181  m_Ahats = TripleArray(m_order + 1);
182 
183  for (unsigned int m = 1; m <= m_order; ++m)
184  {
185  m_Ahats[m] = DoubleArray(m);
186 
187  for (unsigned int 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 (unsigned int j = 2; j <= m; ++j)
244  {
245  for (unsigned int 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
260  m_AhattJ = SingleArray(m_order, 0.0);
261 
262  for (unsigned int i = 0; i < m_order; ++i)
263  {
264  for (unsigned int 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 (int l = 0; l < m_Lmax; ++l)
273  {
275  }
276 }
277 
278 /**
279  * @brief Worker method that performs the time integration.
280  */
282  const int timestep, const NekDouble delta_t,
284 {
285  boost::ignore_unused(delta_t);
286 
287  ASSERTL1(delta_t == m_deltaT,
288  "Delta T has changed which is not permitted.");
289 
290  // The Fractional in Time works via the logical? time step value.
291  int timeStep = timestep + 1;
292 
293  // Update the storage and counters for integral classes. Performs
294  // staging for updating u.
295  for (int l = 0; l < m_Lmax; ++l)
296  {
297  updateStage(timeStep, m_integral_classes[l]);
298  }
299 
300  // Compute u update to time timeStep * m_deltaT. Stored in
301  // m_uNext.
302  finalIncrement(timeStep, op);
303 
304  // Contributions to the current integral
305  int L = computeTaus(m_base, timeStep);
306 
307  for (int l = 0; l < L; ++l)
308  {
309  // Integral contribution over [taus(i+1) taus(i)]. Stored in
310  // m_uInt.
312 
313  for (int i = 0; i < m_nvars; ++i)
314  {
315  for (int j = 0; j < m_npoints; ++j)
316  {
317  m_uNext[i][j] += m_uInt[i][j].real();
318  }
319  }
320  }
321 
322  // Shuffle the previous solutions back one in the history.
323  for (int m = m_order; m > 0; --m)
324  {
325  for (int i = 0; i < m_nvars; ++i)
326  {
327  for (int j = 0; j < m_npoints; ++j)
328  {
329  m_u[m][i][j] = m_u[m - 1][i][j];
330  }
331  }
332  }
333 
334  // Get the current solution.
335  for (int i = 0; i < m_nvars; ++i)
336  {
337  for (int j = 0; j < m_npoints; ++j)
338  {
339  m_u[0][i][j] = m_uNext[i][j] + m_u0[i][j];
340 
341  m_uNext[i][j] = 0; // Zero out for the next itereation.
342  }
343  }
344 
345  // Dump the current solution.
346  // std::cout << "timeStep " << timeStep << std::endl;
347  // for( int j=0; j<m_npoints; ++j )
348  // {
349  // for( int i=0; i<m_nvars; ++i )
350  // {
351  // for( int m=0; m<m_order+1; ++m )
352  // {
353  // std::cout << m_u[m][i][j] << " ";
354  // }
355  // std::cout << std::endl;
356  // }
357  // std::cout << std::endl;
358  // }
359  // std::cout << std::endl;
360 
361  // Update the storage and counters for integral classes to
362  // time timeStep * m_deltaT. Also time-steps the sandboxes and stashes.
363  for (int i = 0; i < m_Lmax; ++i)
364  {
365  advanceSandbox(timeStep, op, m_integral_classes[i]);
366  }
367 
368  return m_u[0];
369 }
370 
371 /**
372  * @brief Method that increments the counter then performs mod
373  * calculation.
374  */
376  const int unsigned counter, const int unsigned base) const
377 {
378  return (counter + 1) % base;
379 }
380 
381 /**
382  * @brief Method to compute the smallest integer L such that base < 2
383  * * base^l.
384  */
386  const unsigned int base, const unsigned int l) const
387 {
388  unsigned int L = ceil(log(l / 2.0) / log(base));
389 
390  if (l % (unsigned int)(2 * pow(base, L)) == 0)
391  {
392  ++L;
393  }
394 
395  return L;
396 }
397 
398 /**
399  * @brief Method to compute the demarcation integers q_{m, ell}.
400  *
401  * Returns a length-(L-1) vector qml such that h*taus are interval
402  * boundaries for a partition of [0, m h]. The value of h is not
403  * needed to compute this vector.
404  */
406  const unsigned int base, const unsigned int m)
407 {
408  int L = computeL(base, m);
409 
410  // m_qml is set in InitializeScheme to be the largest length expected.
411  // qml = Array<OneD, int>( L-1, 0 );
412 
413  for (unsigned int i = 0; i < L - 1; ++i)
414  {
415  m_qml[i] = floor(m / pow(base, i + 1)) - 1;
416  }
417 
418  return L;
419 }
420 
421 /**
422  * @brief Method to compute the demarcation interval marker tau_{m, ell}.
423  *
424  * Returns a length-(L+1) vector tauml such that h*taus are interval
425  * boundaries for a partition of [0, m h]. The value of h is not
426  * needed to compute this vector.
427  */
429  const unsigned int base, const unsigned int m)
430 {
431  if (m == 1)
432  {
433  m_taus[0] = 0;
434 
435  return 0;
436  }
437  else
438  {
439  unsigned int L = computeQML(base, m);
440 
441  // m_taus is set in InitializeScheme to be the largest length
442  // expected.
443 
444  m_taus[0] = m - 1;
445 
446  for (unsigned int i = 1; i < L; ++i)
447  {
448  m_taus[i] = m_qml[i - 1] * pow(base, i);
449  }
450 
451  m_taus[L] = 0;
452 
453  return L;
454  }
455 }
456 
457 /**
458  * @brief Method to compute the quadrature rule over Tablot contour
459  *
460  * Returns a quadrature rule over the Tablot contour defined by the
461  * parameterization.
462  *
463  * gamma(th) = sigma + mu * ( th*cot(th) + i*nu*th ), -pi < th < pi
464  *
465  * An N-point rule is returned, equidistant in the parameter theta. The
466  * returned quadrature rule approximes an integral over the contour.
467  */
469  const unsigned int nQuadPts, const NekDouble mu, const NekDouble nu,
470  const NekDouble sigma, ComplexSingleArray &lamb,
471  ComplexSingleArray &w) const
472 {
473  lamb = ComplexSingleArray(nQuadPts, 0.0);
474  w = ComplexSingleArray(nQuadPts, 0.0);
475 
476  for (unsigned int q = 0; q < nQuadPts; ++q)
477  {
478  NekDouble th =
479  (NekDouble(q) + 0.5) / NekDouble(nQuadPts) * 2.0 * M_PI - M_PI;
480 
481  lamb[q] = sigma + mu * th * std::complex<NekDouble>(1. / tan(th), nu);
482 
483  w[q] = std::complex<NekDouble>(0, -1. / NekDouble(nQuadPts)) * mu *
484  std::complex<NekDouble>(1. / tan(th) - th / (sin(th) * sin(th)),
485  nu);
486  }
487 
488  // Special case for th = 0 which happens when there is an odd
489  // number of quadrature points.
490  if (nQuadPts % 2 == 1)
491  {
492  unsigned int q = (nQuadPts + 1) / 2;
493 
494  lamb[q] = std::complex<NekDouble>(sigma + mu, 0);
495 
496  w[q] = std::complex<NekDouble>(nu * mu / nQuadPts, 0);
497  }
498 }
499 
500 /**
501  * @brief Method to initialize the integral class
502  */
504  const unsigned int index, Instance &instance) const
505 {
506  /**
507  * /brief
508  *
509  * This object stores information for performing integration over
510  * an interval [a, b]. (Defined by taus in the parent calling
511  * function.)
512  *
513  * The "main" object stores information about [a,b]. In
514  * particular, main.ind identifies [a,b] via multiples of h.
515  *
516  * Periodically the values of [a,b] need to be incremented. The
517  * necessary background storage to accomplish this increment
518  * depends whether a or b is being incremented.
519  *
520  * The objects with "f" ("Floor") modifiers are associated with
521  * increments of the interval floor a.
522  *
523  * The objects with "c" ("Ceiling") modifiers are associated with
524  * increments of the interval ceiling b.
525  *
526  * Items on the "stage" are stored for use in computing u at the
527  * current time. Items in the "stash" are stored for use for
528  * future staging. Items in the "sandbox" are being actively
529  * updated at the current time for future stashing. Only items in
530  * the sandbox are time-stepped. the stage and stash locations are
531  * for storage only.
532  *
533  * This is the same for all integral classes, so there's probably
534  * a better way to engineer this. And technically, all that's
535  * needed is the array K(instance.z) anyway.
536  */
537 
538  instance.base = m_base;
539  instance.index = index; // Index of this instance
540  instance.active = false; // Used to determine if active
541  instance.activecounter = 0; // Counter used to flip active bit
542  instance.activebase = 2. * pow(m_base, (index - 1));
543 
544  // Storage for values of y currently used to update u
545  instance.stage_y = ComplexTripleArray(m_nvars);
546  instance.cstash_y = ComplexTripleArray(m_nvars);
548  instance.fstash_y = ComplexTripleArray(m_nvars);
550 
551  for (unsigned int q = 0; q < m_nvars; ++q)
552  {
553  instance.stage_y[q] = ComplexDoubleArray(m_npoints);
554  instance.cstash_y[q] = ComplexDoubleArray(m_npoints);
555  instance.csandbox_y[q] = ComplexDoubleArray(m_npoints);
556  instance.fstash_y[q] = ComplexDoubleArray(m_npoints);
557  instance.fsandbox_y[q] = ComplexDoubleArray(m_npoints);
558 
559  for (unsigned int i = 0; i < m_npoints; ++i)
560  {
561  instance.stage_y[q][i] = ComplexSingleArray(m_nQuadPts, 0.0);
562  instance.cstash_y[q][i] = ComplexSingleArray(m_nQuadPts, 0.0);
563  instance.csandbox_y[q][i] = ComplexSingleArray(m_nQuadPts, 0.0);
564  instance.fstash_y[q][i] = ComplexSingleArray(m_nQuadPts, 0.0);
565  instance.fsandbox_y[q][i] = ComplexSingleArray(m_nQuadPts, 0.0);
566  }
567  }
568 
569  // Major storage for auxilliary ODE solutions.
570  instance.stage_ind = std::pair<int, int>(0, 0); // Time-step counters
571  // indicating the interval
572  // ymain is associated with
573 
574  // Staging allocation
575  instance.stage_active = false;
576  instance.stage_ccounter = 0;
577  instance.stage_cbase = pow(m_base, index - 1); // This base is halved
578  // after the first cycle
579  instance.stage_fcounter = 0;
580  instance.stage_fbase = pow(m_base, index); // This base is halved
581  // after the first cycle
582 
583  // Ceiling stash allocation
584  instance.cstash_counter = 0; // Counter used to determine
585  // when to stash
586 
587  instance.cstash_base = pow(m_base, index - 1); // base for counter ind(1)
588  instance.cstash_ind = std::pair<int, int>(0, 0); // is never used: it always
589  // matches main.ind(1)
590 
591  // Ceiling sandbox allocation
592  instance.csandbox_active = false; // Flag to determine when stash 2
593  // is utilized
594  instance.csandbox_counter = 0;
595  instance.csandbox_ind = std::pair<int, int>(0, 0);
596 
597  // Floor stash
598  instance.fstash_base = 2 * pow(m_base, index);
599  instance.fstash_ind = std::pair<int, int>(0, 0);
600 
601  // Floor sandbox
602  instance.fsandbox_active = false;
603  instance.fsandbox_activebase = pow(m_base, index);
604  instance.fsandbox_stashincrement = (m_base - 1) * pow(m_base, index - 1);
605  instance.fsandbox_ind = std::pair<int, int>(0, 0);
606 
607  // Defining parameters of the Talbot contour quadrature rule
608  NekDouble Tl =
609  m_deltaT * (2. * pow(m_base, index) - 1. - pow(m_base, index - 1));
610  NekDouble mu = m_mu0 / Tl;
611 
612  // Talbot quadrature rule
613  talbotQuadrature(m_nQuadPts, mu, m_nu, m_sigma, instance.z, instance.w);
614 
615  /**
616  * /brief
617  *
618  * With sigma == 0, the dependence of z and w on index is just a
619  * multiplicative scaling factor (mu). So technically we'll only
620  * need one instance of this N-point rule and can scale it
621  * accordingly inside each integral_class instance. Not sure if
622  * this optimization is worth it. Cumulative memory savings would
623  * only be about 4*N*Lmax floats.
624 
625  * Below: precomputation for time integration of auxiliary
626  * variables. Everything below here is independent of the
627  * instance index index. Therefore, we could actually just
628  * generate and store one copy of this stuff and use it
629  * everywhere.
630  */
631 
632  // 'As' array - one for each order.
633  TripleArray &As = instance.As;
634 
635  As = TripleArray(m_order + 2);
636 
637  for (unsigned int m = 1; m <= m_order + 1; ++m)
638  {
639  As[m] = DoubleArray(m);
640 
641  for (unsigned int n = 0; n < m; ++n)
642  {
643  As[m][n] = SingleArray(m, 0.0);
644  }
645 
646  switch (m)
647  {
648  case 1:
649  As[m][0][0] = 1.;
650  break;
651 
652  case 2:
653  As[m][0][0] = 0.;
654  As[m][0][1] = 1.;
655  As[m][1][0] = 1.;
656  As[m][1][1] = -1.;
657  break;
658 
659  case 3:
660  As[m][0][0] = 0.;
661  As[m][0][1] = 1.;
662  As[m][0][2] = 0;
663  As[m][1][0] = 1. / 2.;
664  As[m][1][1] = 0.;
665  As[m][1][2] = -1. / 2.;
666  As[m][2][0] = 1. / 2.;
667  As[m][2][1] = -1.;
668  As[m][2][2] = 1. / 2.;
669  break;
670 
671  case 4:
672  As[m][0][0] = 0.;
673  As[m][0][1] = 1.;
674  As[m][0][2] = 0.;
675  As[m][0][3] = 0.;
676 
677  As[m][1][0] = 1. / 3.;
678  As[m][1][1] = 1. / 2.;
679  As[m][1][2] = -1.;
680  As[m][1][3] = 1. / 6.;
681 
682  As[m][2][0] = 1. / 2.;
683  As[m][2][1] = -1.;
684  As[m][2][2] = 1. / 2.;
685  As[m][2][3] = 0.;
686 
687  As[m][3][0] = 1. / 6.;
688  As[m][3][1] = -1. / 2.;
689  As[m][3][2] = 1. / 2.;
690  As[m][3][3] = -1. / 6.;
691  break;
692 
693  case 5:
694  As[m][0][0] = 0.;
695  As[m][0][1] = 1.;
696  As[m][0][2] = 0.;
697  As[m][0][3] = 0.;
698  As[m][0][4] = 0.;
699 
700  As[m][1][0] = 1. / 4.;
701  As[m][1][1] = 5. / 6.;
702  As[m][1][2] = -3. / 2.;
703  As[m][1][3] = 1. / 2.;
704  As[m][1][4] = -1. / 12.;
705 
706  As[m][2][0] = 11. / 24.;
707  As[m][2][1] = -5. / 6.;
708  As[m][2][2] = 1. / 4.;
709  As[m][2][3] = 1. / 6.;
710  As[m][2][4] = -1. / 24.;
711 
712  As[m][3][0] = 1. / 4.;
713  As[m][3][1] = -5. / 6.;
714  As[m][3][2] = 1.;
715  As[m][3][3] = -1. / 2.;
716  As[m][3][4] = 1. / 12.;
717 
718  As[m][4][0] = 1. / 24.;
719  As[m][4][1] = -1. / 6.;
720  As[m][4][2] = 1. / 4.;
721  As[m][4][3] = -1. / 6.;
722  As[m][4][4] = 1. / 24.;
723  break;
724 
725  // The default is a general formula, but the matrix inversion
726  // involved is ill-conditioned, so the special cases above are
727  // epxlicitly given to combat roundoff error in the most-used
728  // scenarios.
729  default:
730  ASSERTL1(false, "No matrix inverse.");
731 
732  // Ainv = zeros(counter);
733  // Ainv(1,:) = 1;
734  // Ainv(2,1) = 1;
735 
736  // for( unsigned int j = 3; j<=counter; ++j )
737  // {
738  // Ainv(j,:) = pow((j-2)., (0:counter));
739  // Ainv(j,:) = Ainv(j,:). * pow((-1)., (0:counter));
740  // }
741 
742  // As[m] = inv(Ainv);
743  break;
744  }
745  }
746 
747  // Initialize the exponenetial integrators.
748  instance.E = ComplexSingleArray(m_nQuadPts, 0.0);
749 
750  for (unsigned int q = 0; q < m_nQuadPts; ++q)
751  {
752  instance.E[q] = exp(instance.z[q] * m_deltaT);
753  }
754 
755  instance.Eh = ComplexDoubleArray(m_order + 1);
756 
757  for (unsigned int m = 0; m < m_order + 1; ++m)
758  {
759  instance.Eh[m] = ComplexSingleArray(m_nQuadPts, 0.0);
760 
761  for (unsigned int q = 0; q < m_nQuadPts; ++q)
762  {
763  if (m == 0)
764  instance.Eh[0][q] =
765  1. / instance.z[q] * (exp(instance.z[q] * m_deltaT) - 1.0);
766  else
767  instance.Eh[m][q] = -1. / instance.z[q] +
768  NekDouble(m) / (instance.z[q] * m_deltaT) *
769  instance.Eh[m - 1][q];
770  }
771  }
772 
773  // 'AtEh' is set for the primary order. If a lower order method is
774  // needed for initializing it will be changed in time_advance then
775  // restored.
776  instance.AtEh = ComplexDoubleArray(m_order + 1);
777 
778  for (unsigned int m = 0; m <= m_order; ++m)
779  {
780  instance.AtEh[m] = ComplexSingleArray(m_nQuadPts, 0.0);
781 
782  for (unsigned int q = 0; q < m_nQuadPts; ++q)
783  {
784  for (unsigned int i = 0; i <= m_order; ++i)
785  {
786  instance.AtEh[m][q] +=
787  instance.As[m_order + 1][m][i] * instance.Eh[i][q];
788  }
789  }
790  }
791 }
792 
793 /**
794  * @brief Method to rearrange of staging/stashing for current time
795  *
796  * (1) activates ceiling staging
797  * (2) moves ceiling stash ---> stage
798  * (3) moves floor stash --> stage (+ updates all ceiling data)
799  */
800 void FractionalInTimeIntegrationScheme::updateStage(const unsigned int timeStep,
801  Instance &instance)
802 {
803  // Counter to flip active bit
804  if (!instance.active)
805  {
806  instance.active = (timeStep % instance.activebase == 0);
807  }
808 
809  // Determine if staging is necessary
810  if (instance.active)
811  {
812  // Floor staging superscedes ceiling staging
813  if (timeStep % instance.fstash_base == 0)
814  {
815  // Here a swap of the contents can be done because values
816  // will copied into the stash and the f sandbox values will
817  // cleared next.
818  std::swap(instance.stage_y, instance.fstash_y);
819  instance.stage_ind = instance.fstash_ind;
820 
821  std::swap(instance.csandbox_y, instance.fsandbox_y);
822  instance.csandbox_ind = instance.fsandbox_ind;
823 
824  // After floor staging happens once, new base is base^index
825  instance.fstash_base = pow(instance.base, instance.index);
826 
827  // Restart floor sandbox
828  instance.fsandbox_ind = std::pair<int, int>(0, 0);
829  instance.fsandbox_active = false;
830 
831  // Clear the floor sandbox values.
832  for (unsigned int i = 0; i < m_nvars; ++i)
833  {
834  for (unsigned int j = 0; j < m_npoints; ++j)
835  {
836  for (unsigned int q = 0; q < m_nQuadPts; ++q)
837  {
838  instance.fsandbox_y[i][j][q] = 0;
839  }
840  }
841  }
842  }
843 
844  // Check for ceiling staging
845  else if (timeStep % instance.stage_cbase == 0)
846  {
847  instance.stage_ind = instance.cstash_ind;
848 
849  // A swap of the contents can be done because values will
850  // copied into the stash.
851  std::swap(instance.stage_y, instance.cstash_y);
852  }
853  }
854 }
855 
856 /**
857  * @brief Method to approximate the integral
858  *
859  * \int_{(m-1) h}^{m h} k(m*h -s) f(u, s) dx{s}
860  *
861  * Using a time-stepping scheme of a particular order. Here, k depends
862  * on alpha, the derivative order.
863  */
865  const unsigned int timeStep, const TimeIntegrationSchemeOperators &op)
866 {
867  // Note: m_uNext is initialized to zero and then reset to zero
868  // after it is used to update the current solution in TimeIntegrate.
869  for (unsigned int m = 0; m < m_order; ++m)
870  {
871  op.DoOdeRhs(m_u[m], m_F, m_deltaT * (timeStep - m));
872 
873  for (unsigned int i = 0; i < m_nvars; ++i)
874  {
875  for (unsigned int j = 0; j < m_npoints; ++j)
876  {
877  m_uNext[i][j] += m_F[i][j] * m_AhattJ[m];
878  }
879  }
880  }
881 }
882 
883 /**
884  * @brief Method to get the integral contribution over [taus(i+1)
885  * taus(i)]. Stored in m_uInt.
886  */
888  const unsigned int timeStep, const unsigned int tauml,
889  const Instance &instance)
890 {
891  // Assume y has already been updated to time level m
892  for (unsigned int q = 0; q < m_nQuadPts; ++q)
893  {
894  m_expFactor[q] =
895  exp(instance.z[q] * m_deltaT * NekDouble(timeStep - tauml)) *
896  pow(instance.z[q], -m_alpha) * instance.w[q];
897  }
898 
899  for (unsigned int i = 0; i < m_nvars; ++i)
900  {
901  for (unsigned int j = 0; j < m_npoints; ++j)
902  {
903  m_uInt[i][j] = 0;
904 
905  for (unsigned int q = 0; q < m_nQuadPts; ++q)
906  {
907  m_uInt[i][j] += instance.stage_y[i][j][q] * m_expFactor[q];
908  }
909 
910  if (m_uInt[i][j].real() < 1e8)
911  {
912  m_uInt[i][j] = m_uInt[i][j].real();
913  }
914  }
915  }
916 }
917 
918 /**
919  * @brief Method to get the solution to y' = z*y + f(u), using an
920  * exponential integrator with implicit order (m_order + 1)
921  * interpolation of the f(u) term.
922  */
924  const unsigned int timeStep, const TimeIntegrationSchemeOperators &op,
925  Instance &instance, ComplexTripleArray &y)
926 {
927  int order;
928 
929  // Try automated high-order method.
930  if (timeStep <= m_order)
931  {
932  // Not enough history. For now, demote to lower-order method.
933  // TODO: use multi-stage method.
934  order = timeStep;
935 
936  // Prep for the time step.
937  for (unsigned int m = 0; m <= order; ++m)
938  {
939  for (unsigned int q = 0; q < m_nQuadPts; ++q)
940  {
941  instance.AtEh[m][q] = 0;
942 
943  for (unsigned int i = 0; i <= order; ++i)
944  {
945  instance.AtEh[m][q] +=
946  instance.As[order + 1][m][i] * instance.Eh[i][q];
947  }
948  }
949  }
950  }
951  else
952  {
953  order = m_order;
954  }
955 
956  // y = y * instance.E + F * instance.AtEh;
957  for (unsigned int m = 0; m <= order; ++m)
958  {
959  op.DoOdeRhs(m_u[m], m_F, m_deltaT * (timeStep - m));
960 
961  for (unsigned int i = 0; i < m_nvars; ++i)
962  {
963  for (unsigned int j = 0; j < m_npoints; ++j)
964  {
965  for (unsigned int q = 0; q < m_nQuadPts; ++q)
966  {
967  // y * instance.E
968  if (m == 0)
969  y[i][j][q] *= instance.E[q];
970 
971  // F * instance.AtEh
972  y[i][j][q] += m_F[i][j] * instance.AtEh[m][q];
973  }
974  }
975  }
976  }
977 }
978 
979 /**
980  * @brief Method to update sandboxes to the current time.
981  *
982  * (1) advances ceiling sandbox
983  * (2) moves ceiling sandbox ---> stash
984  * (3) activates floor sandboxing
985  * (4) advances floor sandbox
986  * (5) moves floor sandbox ---> stash
987  */
989  const unsigned int timeStep, const TimeIntegrationSchemeOperators &op,
990  Instance &instance)
991 {
992  // (1)
993  // update(instance.csandbox.y)
994  timeAdvance(timeStep, op, instance, instance.csandbox_y);
995  instance.csandbox_ind.second = timeStep;
996 
997  // (2)
998  // Determine if ceiling stashing is necessary
999  instance.cstash_counter =
1000  modIncrement(instance.cstash_counter, instance.cstash_base);
1001 
1002  if (timeStep % instance.cstash_base == 0)
1003  {
1004  // Then need to stash
1005  // instance.cstash_y = instance.csandbox_y;
1006  instance.cstash_ind = instance.csandbox_ind;
1007 
1008  // Stash the c sandbox value. This step has to be a deep copy
1009  // because the values in the sandbox are still needed for the
1010  // time advance.
1011  for (unsigned int i = 0; i < m_nvars; ++i)
1012  {
1013  for (unsigned int j = 0; j < m_npoints; ++j)
1014  {
1015  for (unsigned int q = 0; q < m_nQuadPts; ++q)
1016  {
1017  instance.cstash_y[i][j][q] = instance.csandbox_y[i][j][q];
1018  }
1019  }
1020  }
1021  }
1022 
1023  if (instance.fsandbox_active)
1024  {
1025  // (4)
1026  timeAdvance(timeStep, op, instance, instance.fsandbox_y);
1027 
1028  instance.fsandbox_ind.second = timeStep;
1029 
1030  // (5) Move floor sandbox to stash
1031  if ((instance.fsandbox_ind.second - instance.fsandbox_ind.first) %
1032  instance.fsandbox_stashincrement ==
1033  0)
1034  {
1035  // instance.fstash_y = instance.fsandbox_y;
1036  instance.fstash_ind = instance.fsandbox_ind;
1037 
1038  // Stash the f sandbox values. This step has to be a deep
1039  // copy because the values in the sandbox are still needed
1040  // for the time advance.
1041  for (unsigned int i = 0; i < m_nvars; ++i)
1042  {
1043  for (unsigned int j = 0; j < m_npoints; ++j)
1044  {
1045  for (unsigned int q = 0; q < m_nQuadPts; ++q)
1046  {
1047  instance.fstash_y[i][j][q] =
1048  instance.fsandbox_y[i][j][q];
1049  }
1050  }
1051  }
1052  }
1053  }
1054  else // Determine if advancing floor sandbox is necessary at next time
1055  {
1056  // (3)
1057  if (timeStep % instance.fsandbox_activebase == 0)
1058  {
1059  instance.fsandbox_active = true;
1060  instance.fsandbox_ind = std::pair<int, int>(timeStep, timeStep);
1061  }
1062  }
1063 }
1064 
1065 /**
1066  * @brief Worker method to print details on the integration scheme
1067  */
1069 {
1070  os << "Time Integration Scheme: " << GetFullName() << std::endl
1071  << " Alpha " << m_alpha << std::endl
1072  << " Base " << m_base << std::endl
1073  << " Number of instances " << m_Lmax << std::endl
1074  << " Number of quadature points " << m_nQuadPts << std::endl
1075  << " Talbot Parameter: sigma " << m_sigma << std::endl
1076  << " Talbot Parameter: mu0 " << m_mu0 << std::endl
1077  << " Talbot Parameter: nu " << m_nu << std::endl;
1078 }
1079 
1081 {
1082  os << "Time Integration Scheme: " << GetFullName() << std::endl
1083  << " Alpha " << m_alpha << std::endl
1084  << " Base " << m_base << std::endl
1085  << " Number of instances " << m_Lmax << std::endl
1086  << " Number of quadature points " << m_nQuadPts << std::endl
1087  << " Talbot Parameter: sigma " << m_sigma << std::endl
1088  << " Talbot Parameter: mu0 " << m_mu0 << std::endl
1089  << " Talbot Parameter: nu " << m_nu << std::endl;
1090 }
1091 
1092 // Friend Operators
1093 std::ostream &operator<<(std::ostream &os,
1095 {
1096  rhs.print(os);
1097 
1098  return os;
1099 }
1100 
1101 std::ostream &operator<<(std::ostream &os,
1103 {
1104  os << *rhs.get();
1105 
1106  return os;
1107 }
1108 
1109 } // end namespace LibUtilities
1110 } // 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
unsigned int modIncrement(const unsigned int counter, const unsigned int base) const
Method that increments the counter then performs mod calculation.
void updateStage(const unsigned int timeStep, Instance &instance)
Method to rearrange of staging/stashing for current time.
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 integralClassInitialize(const unsigned int index, Instance &instance) const
Method to initialize the integral class.
virtual LUE ConstDoubleArray & v_TimeIntegrate(const int timestep, const NekDouble delta_t, const TimeIntegrationSchemeOperators &op) override
Worker method that performs the time integration.
unsigned int computeQML(const unsigned int base, const unsigned int m)
Method to compute the demarcation integers q_{m, ell}.
unsigned int computeL(const unsigned int base, const unsigned int m) const
Method to compute the smallest integer L such that base < 2.
void timeAdvance(const unsigned int timeStep, const TimeIntegrationSchemeOperators &op, Instance &instance, ComplexTripleArray &y)
Method to get the solution to y' = z*y + f(u), using an exponential integrator with implicit order (m...
virtual LUE void v_print(std::ostream &os) const override
Worker method to print details on the integration scheme.
unsigned int computeTaus(const unsigned int base, const unsigned int m)
Method to compute the demarcation interval marker tau_{m, ell}.
void talbotQuadrature(const unsigned int nQuadPts, const NekDouble mu, const NekDouble nu, const NekDouble sigma, ComplexSingleArray &lamb, ComplexSingleArray &w) const
Method to compute the quadrature rule over Tablot contour.
void advanceSandbox(const unsigned int timeStep, const TimeIntegrationSchemeOperators &op, Instance &instance)
Method to update sandboxes to the current time.
virtual LUE void v_printFull(std::ostream &os) const override
FractionalInTimeIntegrationScheme(std::string variant, unsigned int order, std::vector< NekDouble > freeParams)
Constructor.
void finalIncrement(const unsigned int timeStep, const TimeIntegrationSchemeOperators &op)
Method to approximate the integral.
void integralContribution(const unsigned int timeStep, const unsigned int tauml, const Instance &instance)
Method to get the integral contribution over [taus(i+1) taus(i)]. Stored in m_uInt.
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)
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