Nektar++
EulerExponentialTimeIntegrationSchemes.h
Go to the documentation of this file.
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 // File: EulerExponentialTimeIntegrationSchemes.h
4 //
5 // For more information, please see: http://www.nektar.info
6 //
7 // The MIT License
8 //
9 // Copyright (c) 2018 Division of Applied Mathematics, Brown University (USA),
10 // Department of Aeronautics, Imperial College London (UK), and Scientific
11 // Computing and Imaging Institute, University of Utah (USA).
12 //
13 // License for the specific language governing rights and limitations under
14 // Permission is hereby granted, free of charge, to any person obtaining a
15 // copy of this software and associated documentation files (the "Software"),
16 // to deal in the Software without restriction, including without limitation
17 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 // and/or sell copies of the Software, and to permit persons to whom the
19 // Software is furnished to do so, subject to the following conditions:
20 //
21 // The above copyright notice and this permission notice shall be included
22 // in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 // DEALINGS IN THE SOFTWARE.
31 //
32 // Description: Combined header file for all basic Euler exponential
33 // based time integration schemes.
34 //
35 ///////////////////////////////////////////////////////////////////////////////
36 
37 // Note : If adding a new integrator be sure to register the
38 // integrator with the Time Integration Scheme Facatory in
39 // SchemeInitializor.cpp.
40 
41 #ifndef NEKTAR_LIB_UTILITIES_TIME_INTEGRATION_EULER_EXP_TIME_INTEGRATION_SCHEME
42 #define NEKTAR_LIB_UTILITIES_TIME_INTEGRATION_EULER_EXP_TIME_INTEGRATION_SCHEME
43 
44 #define LUE LIB_UTILITIES_EXPORT
45 
48 
49 #include <NekMesh/Module/ProcessModules/ProcessVarOpti/Evaluator.hxx>
50 
51 namespace Nektar
52 {
53 
54 using namespace NekMesh;
55 
56 namespace LibUtilities
57 {
58 
59 ///////////////////////////////////////////////////////////////////////////////
60 // EulerExponential
61 
63 {
64 public:
66  unsigned int order,
67  std::vector<NekDouble> freeParams)
68  : TimeIntegrationSchemeGLM(variant, order, freeParams)
69  {
70  // Currently up to 4th order is implemented because the number
71  // of steps is the same as the order.
72  // Currently up to 4th order is implemented.
73  ASSERTL1(variant == "Lawson" || variant == "Norsett",
74  "EulerExponential Time integration scheme unknown variant: " +
75  variant + ". Must be 'Lawson' or 'Norsett'.");
76 
77  ASSERTL1(((variant == "Lawson" && 1 <= order && order <= 1) ||
78  (variant == "Norsett" && 1 <= order && order <= 4)),
79  "EulerExponential Time integration scheme bad order, "
80  "Lawson (1) or Norsett (1-4): " +
81  std::to_string(order));
82 
83  m_integration_phases = TimeIntegrationAlgorithmGLMVector(order);
84 
85  // Currently the next lowest order is used to seed the current
86  // order. This is not correct but is an okay approximation.
87  for (unsigned int n = 0; n < order; ++n)
88  {
89  m_integration_phases[n] = TimeIntegrationAlgorithmGLMSharedPtr(
90  new TimeIntegrationAlgorithmGLM(this));
91 
93  m_integration_phases[n], variant, n + 1);
94  }
95  }
96 
98  {
99  }
100 
102  std::string variant, unsigned int order,
103  std::vector<NekDouble> freeParams)
104  {
107  AllocateSharedPtr(variant, order, freeParams);
108 
109  return p;
110  }
111 
112  static std::string className;
113 
114  LUE virtual std::string GetName() const
115  {
116  return std::string("EulerExponential");
117  }
118 
119  LUE virtual std::string GetFullName() const
120  {
121  return GetVariant() + GetName() + "Order" + std::to_string(GetOrder());
122  }
123 
125  {
126  return 1.0;
127  }
128 
130  std::string variant, int order)
131  {
132  phase->m_schemeType = eExponential;
133  phase->m_variant = variant;
134  phase->m_order = order;
135  phase->m_name = variant + std::string("EulerExponentialOrder") +
136  std::to_string(phase->m_order);
137 
138  // Parameters for the compact 1 step implementation.
139  phase->m_numstages = 1;
140  phase->m_numsteps = phase->m_order; // Okay up to 4th order.
141 
142  phase->m_A = Array<OneD, Array<TwoD, NekDouble>>(1);
143  phase->m_B = Array<OneD, Array<TwoD, NekDouble>>(1);
144 
145  phase->m_A[0] =
146  Array<TwoD, NekDouble>(phase->m_numstages, phase->m_numstages, 0.0);
147  phase->m_B[0] =
148  Array<TwoD, NekDouble>(phase->m_numsteps, phase->m_numstages, 0.0);
149 
150  phase->m_U =
151  Array<TwoD, NekDouble>(phase->m_numstages, phase->m_numsteps, 0.0);
152  phase->m_V =
153  Array<TwoD, NekDouble>(phase->m_numsteps, phase->m_numsteps, 0.0);
154 
155  // Coefficients
156 
157  // When multiple steps are taken B[0][0] and V[0][1...s] must be
158  // weighted so the time contribution is correct.
159 
160  // B Phi function for first row first column
161  phase->m_B[0][0][0] = 1.0 / phase->m_order; // phi_func(0 or 1)
162 
163  // B evaluation value shuffling second row first column
164  if (phase->m_order > 1)
165  {
166  phase->m_B[0][1][0] = 1.0; // constant 1
167  }
168 
169  // U Curent time step evaluation first row first column
170  phase->m_U[0][0] = 1.0; // constant 1
171 
172  // V Phi function for first row first column
173  phase->m_V[0][0] = 1.0; // phi_func(0)
174 
175  // V Phi function for first row additional columns
176  for (int n = 1; n < phase->m_order; ++n)
177  {
178  phase->m_V[0][n] = 1.0 / phase->m_order; // phi_func(n+1)
179  }
180 
181  // V evaluation value shuffling row n column n-1
182  for (int n = 2; n < phase->m_order; ++n)
183  {
184  phase->m_V[n][n - 1] = 1.0; // constant 1
185  }
186 
187  phase->m_numMultiStepValues = 1;
188  phase->m_numMultiStepDerivs = phase->m_order - 1;
189  phase->m_timeLevelOffset = Array<OneD, unsigned int>(phase->m_numsteps);
190  phase->m_timeLevelOffset[0] = 0;
191 
192  // For order > 1 derivatives are needed.
193  for (int n = 1; n < phase->m_order; ++n)
194  {
195  phase->m_timeLevelOffset[n] = n;
196  }
197 
198  phase->CheckAndVerify();
199  }
200 
202  NekDouble deltaT) const
203  {
204  /**
205  * \brief Lambda Matrix Assumption, member variable phase->m_L
206  *
207  * The one-dimensional Lambda matrix is a diagonal
208  * matrix thus values are non zero if and only i=j. As such,
209  * the diagonal Lambda values are stored in an array of
210  * complex numbers.
211  */
212 
213  ASSERTL1(phase->m_nvars == phase->m_L.size(),
214  "The number of variables does not match "
215  "the number of exponential coefficents.");
216 
221 
223 
224  for (unsigned int k = 0; k < phase->m_nvars; ++k)
225  {
226  // B Phi function for first row first column
227  if (phase->m_variant == "Lawson")
228  {
229  phi[0] = phi_function(0, deltaT * phase->m_L[k]).real();
230  }
231  else if (phase->m_variant == "Norsett")
232  {
233  phi[0] = phi_function(1, deltaT * phase->m_L[k]).real();
234  }
235  else
236  {
237  ASSERTL1(false, "Cannot call EulerExponential directly "
238  "use the variant 'Lawson' or 'Norsett'.");
239  }
240 
241  // Set up for multiple steps. For multiple steps one needs
242  // to weight the phi functions in much the same way there
243  // are weights for multi-step Adams-Bashfort methods.
244 
245  // For order N the weights are an N x N matrix with
246  // values: W[j][i] = std::pow(i, j) and phi_func = W phi.
247  // There are other possible wieghting schemes.
248  if (phase->m_order == 1)
249  {
250  // Nothing to do as the value is set above and the
251  // weight is just 1.
252  }
253  else if (phase->m_order == 2)
254  {
255  // For Order 2 the weights are simply : 1 1
256  // 0 1
257 
258  // If one were to solve the system of equations it
259  // simply results in subtracting the second order
260  // value from the first order value.
261  phi[1] = phi_function(2, deltaT * phase->m_L[k]).real();
262 
263  phi[0] -= phi[1];
264  }
265  else if (phase->m_order == 3)
266  {
267  Array<OneD, NekDouble> phi_func =
269 
270  phi_func[0] = phi[0];
271 
272  for (unsigned int m = 1; m < phase->m_order; ++m)
273  {
274  phi_func[m] =
275  phi_function(m + 1, deltaT * phase->m_L[k]).real();
276  }
277 
278  NekDouble W[3][3];
279 
280  // Set up the wieghts and calculate the determinant.
281  for (unsigned int j = 0; j < phase->m_order; ++j)
282  {
283  for (unsigned int i = 0; i < phase->m_order; ++i)
284  {
285  W[j][i] = std::pow(i, j);
286  }
287  }
288 
289  NekDouble W_det = Determinant<3>(W);
290 
291  // Solve the series of equations using Cramer's rule.
292  for (unsigned int m = 0; m < phase->m_order; ++m)
293  {
294  // Assemble the working matrix for this solution.
295  for (unsigned int j = 0; j < phase->m_order; ++j)
296  {
297  for (unsigned int i = 0; i < phase->m_order; ++i)
298  {
299  // Fill in the mth column for the mth
300  // solution using the phi function value
301  // otherwise utilize the weights.
302  W[i][j] = (j == m) ? phi_func[i] : std::pow(j, i);
303  }
304  }
305 
306  // Get the mth solutiion.
307  phi[m] = Determinant<3>(W) / W_det;
308  }
309  }
310  else if (phase->m_order == 4)
311  {
312  Array<OneD, NekDouble> phi_func =
314 
315  phi_func[0] = phi[0];
316 
317  for (unsigned int m = 1; m < phase->m_order; ++m)
318  {
319  phi_func[m] =
320  phi_function(m + 1, deltaT * phase->m_L[k]).real();
321  }
322 
323  NekDouble W[4][4];
324 
325  // Set up the weights and calculate the determinant.
326  for (unsigned int j = 0; j < phase->m_order; ++j)
327  {
328  for (unsigned int i = 0; i < phase->m_order; ++i)
329  {
330  W[j][i] = std::pow(i, j);
331  }
332  }
333 
334  NekDouble W_det = Determinant<4>(W);
335 
336  // Solve the series of equations using Cramer's rule.
337  for (unsigned int m = 0; m < phase->m_order; ++m)
338  {
339  // Assemble the working matrix for this solution.
340  for (unsigned int j = 0; j < phase->m_order; ++j)
341  {
342  for (unsigned int i = 0; i < phase->m_order; ++i)
343  {
344  // Fill in the mth column for the mth
345  // solution using the phi function value
346  // otherwise utilize the weights.
347  W[i][j] = (j == m) ? phi_func[i] : std::pow(j, i);
348  }
349  }
350 
351  // Get the mth solutiion.
352  phi[m] = Determinant<4>(W) / W_det;
353  }
354  }
355  else
356  {
357  ASSERTL1(false, "Not set up for more than 4th Order.");
358  }
359 
360  // Create the phi based Butcher tableau matrices. Note
361  // these matrices are set up using a general formational
362  // based on the number of steps (i.e. the order).
363  phase->m_A_phi[k] = Array<TwoD, NekDouble>(phase->m_numstages,
364  phase->m_numstages, 0.0);
365  phase->m_B_phi[k] = Array<TwoD, NekDouble>(phase->m_numsteps,
366  phase->m_numstages, 0.0);
367  phase->m_U_phi[k] = Array<TwoD, NekDouble>(phase->m_numstages,
368  phase->m_numsteps, 0.0);
369  phase->m_V_phi[k] = Array<TwoD, NekDouble>(phase->m_numsteps,
370  phase->m_numsteps, 0.0);
371 
372  // B Phi function for first row first column.
373  phase->m_B_phi[k][0][0] = phi[0];
374 
375  // B evaluation value shuffling second row first column.
376  if (phase->m_order > 1)
377  phase->m_B_phi[k][1][0] = 1.0; // constant 1
378 
379  // U Curent time step evaluation first row first column.
380  phase->m_U_phi[k][0][0] = 1.0; // constant 1
381 
382  // V Phi function for first row first column.
383  phase->m_V_phi[k][0][0] =
384  phi_function(0, deltaT * phase->m_L[k]).real();
385 
386  // V Phi function for first row additional columns.
387  for (int n = 1; n < phase->m_order; ++n)
388  {
389  phase->m_V_phi[k][0][n] = phi[n];
390  }
391 
392  // V evaluation value shuffling row n column n-1.
393  for (int n = 2; n < phase->m_order; ++n)
394  {
395  phase->m_V_phi[k][n][n - 1] = 1.0; // constant 1
396  }
397  }
398  }
399 
401  Array<OneD, std::complex<NekDouble>> &Lambda)
402  {
403  ASSERTL0(!m_integration_phases.empty(), "No scheme")
404 
405  /**
406  * \brief Lambda Matrix Assumption, parameter Lambda.
407  *
408  * The one-dimensional Lambda matrix is a diagonal
409  * matrix thus values are non zero if and only i=j. As such,
410  * the diagonal Lambda values are stored in an array of
411  * complex numbers.
412  */
413 
414  // Assume that each phase is an exponential integrator.
415  for (int i = 0; i < m_integration_phases.size(); i++)
416  {
417  m_integration_phases[i]->m_L = Lambda;
418 
419  // Anytime the coefficents are updated reset the nVars to
420  // be assured that the exponential matrices are
421  // recalculated (e.g. the number of variables may remain
422  // the same but the coefficients have changed).
423  m_integration_phases[i]->m_lastNVars = 0;
424  }
425  }
426 
427 private:
428  inline NekDouble factorial(unsigned int n) const
429  {
430  return (n == 1 || n == 0) ? 1 : n * factorial(n - 1);
431  }
432 
433  std::complex<NekDouble> phi_function(const unsigned int order,
434  const std::complex<NekDouble> z) const
435  {
436  /**
437  * \brief Phi function
438  *
439  * Central to the implementation of exponential integrators is
440  * the evaluation of exponential-like functions, commonly
441  * denoted by phi (φ) functions. It is convenient to define
442  * then as φ0(z) = e^z, in which case the functions obey the
443  * recurrence relation.
444 
445  * 0: exp(z);
446  * 1: (exp(z) - 1.0) / (z);
447  * 2: (exp(z) - z - 1.0) / (z * z);
448  */
449 
450  if (z == 0.0)
451  {
452  return 1.0 / factorial(order);
453  }
454 
455  if (order == 0)
456  {
457  return exp(z);
458  }
459  else
460  {
461  return (phi_function(order - 1, z) - 1.0 / factorial(order - 1)) /
462  z;
463  }
464 
465  return 0;
466  }
467 
468 }; // end class EulerExponentialTimeIntegrator
469 
470 } // end namespace LibUtilities
471 } // end namespace Nektar
472 
473 #endif
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:216
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode....
Definition: ErrorUtil.hpp:250
virtual void InitializeSecondaryData(TimeIntegrationAlgorithmGLM *phase, NekDouble deltaT) const
static TimeIntegrationSchemeSharedPtr create(std::string variant, unsigned int order, std::vector< NekDouble > freeParams)
EulerExponentialTimeIntegrationScheme(std::string variant, unsigned int order, std::vector< NekDouble > freeParams)
std::complex< NekDouble > phi_function(const unsigned int order, const std::complex< NekDouble > z) const
static LUE void SetupSchemeData(TimeIntegrationAlgorithmGLMSharedPtr &phase, std::string variant, int order)
LUE void SetExponentialCoefficients(Array< OneD, std::complex< NekDouble >> &Lambda)
Base class for GLM time integration schemes.
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
std::shared_ptr< TimeIntegrationAlgorithmGLM > TimeIntegrationAlgorithmGLMSharedPtr
@ eExponential
Exponential scheme.
std::vector< TimeIntegrationAlgorithmGLMSharedPtr > TimeIntegrationAlgorithmGLMVector
std::shared_ptr< TimeIntegrationScheme > TimeIntegrationSchemeSharedPtr
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:1
double NekDouble