Nektar++
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
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 
115  std::string variant, int order)
116  {
117  phase->m_schemeType = eExponential;
118  phase->m_variant = variant;
119  phase->m_order = order;
120  phase->m_name = variant + std::string("EulerExponentialOrder") +
121  std::to_string(phase->m_order);
122 
123  // Parameters for the compact 1 step implementation.
124  phase->m_numstages = 1;
125  phase->m_numsteps = phase->m_order; // Okay up to 4th order.
126 
127  phase->m_A = Array<OneD, Array<TwoD, NekDouble>>(1);
128  phase->m_B = Array<OneD, Array<TwoD, NekDouble>>(1);
129 
130  phase->m_A[0] =
131  Array<TwoD, NekDouble>(phase->m_numstages, phase->m_numstages, 0.0);
132  phase->m_B[0] =
133  Array<TwoD, NekDouble>(phase->m_numsteps, phase->m_numstages, 0.0);
134 
135  phase->m_U =
136  Array<TwoD, NekDouble>(phase->m_numstages, phase->m_numsteps, 0.0);
137  phase->m_V =
138  Array<TwoD, NekDouble>(phase->m_numsteps, phase->m_numsteps, 0.0);
139 
140  // Coefficients
141 
142  // When multiple steps are taken B[0][0] and V[0][1...s] must be
143  // weighted so the time contribution is correct.
144 
145  // B Phi function for first row first column
146  phase->m_B[0][0][0] = 1.0 / phase->m_order; // phi_func(0 or 1)
147 
148  // B evaluation value shuffling second row first column
149  if (phase->m_order > 1)
150  {
151  phase->m_B[0][1][0] = 1.0; // constant 1
152  }
153 
154  // U Curent time step evaluation first row first column
155  phase->m_U[0][0] = 1.0; // constant 1
156 
157  // V Phi function for first row first column
158  phase->m_V[0][0] = 1.0; // phi_func(0)
159 
160  // V Phi function for first row additional columns
161  for (int n = 1; n < phase->m_order; ++n)
162  {
163  phase->m_V[0][n] = 1.0 / phase->m_order; // phi_func(n+1)
164  }
165 
166  // V evaluation value shuffling row n column n-1
167  for (int n = 2; n < phase->m_order; ++n)
168  {
169  phase->m_V[n][n - 1] = 1.0; // constant 1
170  }
171 
172  phase->m_numMultiStepValues = 1;
173  phase->m_numMultiStepImplicitDerivs = 0;
174  phase->m_numMultiStepDerivs = phase->m_order - 1;
175  phase->m_timeLevelOffset = Array<OneD, unsigned int>(phase->m_numsteps);
176  phase->m_timeLevelOffset[0] = 0;
177 
178  // For order > 1 derivatives are needed.
179  for (int n = 1; n < phase->m_order; ++n)
180  {
181  phase->m_timeLevelOffset[n] = n;
182  }
183 
184  phase->CheckAndVerify();
185  }
186 
188  Array<OneD, std::complex<NekDouble>> &Lambda)
189  {
190  ASSERTL0(!m_integration_phases.empty(), "No scheme")
191 
192  /**
193  * \brief Lambda Matrix Assumption, parameter Lambda.
194  *
195  * The one-dimensional Lambda matrix is a diagonal
196  * matrix thus values are non zero if and only i=j. As such,
197  * the diagonal Lambda values are stored in an array of
198  * complex numbers.
199  */
200 
201  // Assume that each phase is an exponential integrator.
202  for (int i = 0; i < m_integration_phases.size(); i++)
203  {
204  m_integration_phases[i]->m_L = Lambda;
205 
206  // Anytime the coefficents are updated reset the nVars to
207  // be assured that the exponential matrices are
208  // recalculated (e.g. the number of variables may remain
209  // the same but the coefficients have changed).
210  m_integration_phases[i]->m_lastNVars = 0;
211  }
212  }
213 
214 protected:
215  LUE virtual std::string v_GetName() const override
216  {
217  return std::string("EulerExponential");
218  }
219 
220  LUE virtual std::string v_GetFullName() const override
221  {
222  return GetVariant() + GetName() + "Order" + std::to_string(GetOrder());
223  }
224 
225  LUE virtual NekDouble v_GetTimeStability() const override
226  {
227  return 1.0;
228  }
229 
231  NekDouble deltaT) const override
232  {
233  /**
234  * \brief Lambda Matrix Assumption, member variable phase->m_L
235  *
236  * The one-dimensional Lambda matrix is a diagonal
237  * matrix thus values are non zero if and only i=j. As such,
238  * the diagonal Lambda values are stored in an array of
239  * complex numbers.
240  */
241 
242  ASSERTL1(phase->m_nvars == phase->m_L.size(),
243  "The number of variables does not match "
244  "the number of exponential coefficents.");
245 
250 
252 
253  for (unsigned int k = 0; k < phase->m_nvars; ++k)
254  {
255  // B Phi function for first row first column
256  if (phase->m_variant == "Lawson")
257  {
258  phi[0] = phi_function(0, deltaT * phase->m_L[k]).real();
259  }
260  else if (phase->m_variant == "Norsett")
261  {
262  phi[0] = phi_function(1, deltaT * phase->m_L[k]).real();
263  }
264  else
265  {
266  ASSERTL1(false, "Cannot call EulerExponential directly "
267  "use the variant 'Lawson' or 'Norsett'.");
268  }
269 
270  // Set up for multiple steps. For multiple steps one needs
271  // to weight the phi functions in much the same way there
272  // are weights for multi-step Adams-Bashfort methods.
273 
274  // For order N the weights are an N x N matrix with
275  // values: W[j][i] = std::pow(i, j) and phi_func = W phi.
276  // There are other possible wieghting schemes.
277  if (phase->m_order == 1)
278  {
279  // Nothing to do as the value is set above and the
280  // weight is just 1.
281  }
282  else if (phase->m_order == 2)
283  {
284  // For Order 2 the weights are simply : 1 1
285  // 0 1
286 
287  // If one were to solve the system of equations it
288  // simply results in subtracting the second order
289  // value from the first order value.
290  phi[1] = phi_function(2, deltaT * phase->m_L[k]).real();
291 
292  phi[0] -= phi[1];
293  }
294  else if (phase->m_order == 3)
295  {
296  Array<OneD, NekDouble> phi_func =
298 
299  phi_func[0] = phi[0];
300 
301  for (unsigned int m = 1; m < phase->m_order; ++m)
302  {
303  phi_func[m] =
304  phi_function(m + 1, deltaT * phase->m_L[k]).real();
305  }
306 
307  NekDouble W[3][3];
308 
309  // Set up the wieghts and calculate the determinant.
310  for (unsigned int j = 0; j < phase->m_order; ++j)
311  {
312  for (unsigned int i = 0; i < phase->m_order; ++i)
313  {
314  W[j][i] = std::pow(i, j);
315  }
316  }
317 
318  NekDouble W_det = Determinant<3>(W);
319 
320  // Solve the series of equations using Cramer's rule.
321  for (unsigned int m = 0; m < phase->m_order; ++m)
322  {
323  // Assemble the working matrix for this solution.
324  for (unsigned int j = 0; j < phase->m_order; ++j)
325  {
326  for (unsigned int i = 0; i < phase->m_order; ++i)
327  {
328  // Fill in the mth column for the mth
329  // solution using the phi function value
330  // otherwise utilize the weights.
331  W[i][j] = (j == m) ? phi_func[i] : std::pow(j, i);
332  }
333  }
334 
335  // Get the mth solutiion.
336  phi[m] = Determinant<3>(W) / W_det;
337  }
338  }
339  else if (phase->m_order == 4)
340  {
341  Array<OneD, NekDouble> phi_func =
343 
344  phi_func[0] = phi[0];
345 
346  for (unsigned int m = 1; m < phase->m_order; ++m)
347  {
348  phi_func[m] =
349  phi_function(m + 1, deltaT * phase->m_L[k]).real();
350  }
351 
352  NekDouble W[4][4];
353 
354  // Set up the weights and calculate the determinant.
355  for (unsigned int j = 0; j < phase->m_order; ++j)
356  {
357  for (unsigned int i = 0; i < phase->m_order; ++i)
358  {
359  W[j][i] = std::pow(i, j);
360  }
361  }
362 
363  NekDouble W_det = Determinant<4>(W);
364 
365  // Solve the series of equations using Cramer's rule.
366  for (unsigned int m = 0; m < phase->m_order; ++m)
367  {
368  // Assemble the working matrix for this solution.
369  for (unsigned int j = 0; j < phase->m_order; ++j)
370  {
371  for (unsigned int i = 0; i < phase->m_order; ++i)
372  {
373  // Fill in the mth column for the mth
374  // solution using the phi function value
375  // otherwise utilize the weights.
376  W[i][j] = (j == m) ? phi_func[i] : std::pow(j, i);
377  }
378  }
379 
380  // Get the mth solutiion.
381  phi[m] = Determinant<4>(W) / W_det;
382  }
383  }
384  else
385  {
386  ASSERTL1(false, "Not set up for more than 4th Order.");
387  }
388 
389  // Create the phi based Butcher tableau matrices. Note
390  // these matrices are set up using a general formational
391  // based on the number of steps (i.e. the order).
392  phase->m_A_phi[k] = Array<TwoD, NekDouble>(phase->m_numstages,
393  phase->m_numstages, 0.0);
394  phase->m_B_phi[k] = Array<TwoD, NekDouble>(phase->m_numsteps,
395  phase->m_numstages, 0.0);
396  phase->m_U_phi[k] = Array<TwoD, NekDouble>(phase->m_numstages,
397  phase->m_numsteps, 0.0);
398  phase->m_V_phi[k] = Array<TwoD, NekDouble>(phase->m_numsteps,
399  phase->m_numsteps, 0.0);
400 
401  // B Phi function for first row first column.
402  phase->m_B_phi[k][0][0] = phi[0];
403 
404  // B evaluation value shuffling second row first column.
405  if (phase->m_order > 1)
406  phase->m_B_phi[k][1][0] = 1.0; // constant 1
407 
408  // U Curent time step evaluation first row first column.
409  phase->m_U_phi[k][0][0] = 1.0; // constant 1
410 
411  // V Phi function for first row first column.
412  phase->m_V_phi[k][0][0] =
413  phi_function(0, deltaT * phase->m_L[k]).real();
414 
415  // V Phi function for first row additional columns.
416  for (int n = 1; n < phase->m_order; ++n)
417  {
418  phase->m_V_phi[k][0][n] = phi[n];
419  }
420 
421  // V evaluation value shuffling row n column n-1.
422  for (int n = 2; n < phase->m_order; ++n)
423  {
424  phase->m_V_phi[k][n][n - 1] = 1.0; // constant 1
425  }
426  }
427  }
428 
429 private:
430  inline NekDouble factorial(unsigned int n) const
431  {
432  return (n == 1 || n == 0) ? 1 : n * factorial(n - 1);
433  }
434 
435  std::complex<NekDouble> phi_function(const unsigned int order,
436  const std::complex<NekDouble> z) const
437  {
438  /**
439  * \brief Phi function
440  *
441  * Central to the implementation of exponential integrators is
442  * the evaluation of exponential-like functions, commonly
443  * denoted by phi (φ) functions. It is convenient to define
444  * then as φ0(z) = e^z, in which case the functions obey the
445  * recurrence relation.
446 
447  * 0: exp(z);
448  * 1: (exp(z) - 1.0) / (z);
449  * 2: (exp(z) - z - 1.0) / (z * z);
450  */
451 
452  if (z == 0.0)
453  {
454  return 1.0 / factorial(order);
455  }
456 
457  if (order == 0)
458  {
459  return exp(z);
460  }
461  else
462  {
463  return (phi_function(order - 1, z) - 1.0 / factorial(order - 1)) /
464  z;
465  }
466 
467  return 0;
468  }
469 
470 }; // end class EulerExponentialTimeIntegrator
471 
472 } // end namespace LibUtilities
473 } // end namespace Nektar
474 
475 #endif
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:215
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode....
Definition: ErrorUtil.hpp:249
virtual void v_InitializeSecondaryData(TimeIntegrationAlgorithmGLM *phase, NekDouble deltaT) const override
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:2
double NekDouble