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