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