seulex.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2013-2018 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "seulex.H"
28 
29 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
30 
31 namespace Foam
32 {
33  defineTypeNameAndDebug(seulex, 0);
34 
35  addToRunTimeSelectionTable(ODESolver, seulex, dictionary);
36 
37  const scalar
38  seulex::stepFactor1_ = 0.6,
39  seulex::stepFactor2_ = 0.93,
40  seulex::stepFactor3_ = 0.1,
41  seulex::stepFactor4_ = 4,
42  seulex::stepFactor5_ = 0.5,
43  seulex::kFactor1_ = 0.7,
44  seulex::kFactor2_ = 0.9;
45 }
46 
47 
48 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
49 
51 :
52  ODESolver(ode, dict),
53  jacRedo_(min(1e-4, min(relTol_))),
54  nSeq_(iMaxx_),
55  cpu_(iMaxx_),
56  coeff_(iMaxx_, iMaxx_),
57  theta_(2*jacRedo_),
58  table_(kMaxx_, n_),
59  dfdx_(n_),
60  dfdy_(n_),
61  a_(n_),
62  pivotIndices_(n_),
63  dxOpt_(iMaxx_),
64  temp_(iMaxx_),
65  y0_(n_),
66  ySequence_(n_),
67  scale_(n_),
68  dy_(n_),
69  yTemp_(n_),
70  dydx_(n_)
71 {
72  // The CPU time factors for the major parts of the algorithm
73  const scalar cpuFunc = 1, cpuJac = 5, cpuLU = 1, cpuSolve = 1;
74 
75  nSeq_[0] = 2;
76  nSeq_[1] = 3;
77 
78  for (int i=2; i<iMaxx_; i++)
79  {
80  nSeq_[i] = 2*nSeq_[i-2];
81  }
82  cpu_[0] = cpuJac + cpuLU + nSeq_[0]*(cpuFunc + cpuSolve);
83 
84  for (int k=0; k<kMaxx_; k++)
85  {
86  cpu_[k+1] = cpu_[k] + (nSeq_[k+1]-1)*(cpuFunc + cpuSolve) + cpuLU;
87  }
88 
89  // Set the extrapolation coefficients array
90  for (int k=0; k<iMaxx_; k++)
91  {
92  for (int l=0; l<k; l++)
93  {
94  scalar ratio = scalar(nSeq_[k])/nSeq_[l];
95  coeff_(k, l) = 1/(ratio - 1);
96  }
97  }
98 }
99 
100 
101 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
102 
103 bool Foam::seulex::seul
104 (
105  const scalar x0,
106  const scalarField& y0,
107  const scalar dxTot,
108  const label k,
109  scalarField& y,
110  const scalarField& scale
111 ) const
112 {
113  label nSteps = nSeq_[k];
114  scalar dx = dxTot/nSteps;
115 
116  for (label i=0; i<n_; i++)
117  {
118  for (label j=0; j<n_; j++)
119  {
120  a_(i, j) = -dfdy_(i, j);
121  }
122 
123  a_(i, i) += 1/dx;
124  }
125 
126  LUDecompose(a_, pivotIndices_);
127 
128  scalar xnew = x0 + dx;
129  odes_.derivatives(xnew, y0, dy_);
130  LUBacksubstitute(a_, pivotIndices_, dy_);
131 
132  yTemp_ = y0;
133 
134  for (label nn=1; nn<nSteps; nn++)
135  {
136  yTemp_ += dy_;
137  xnew += dx;
138 
139  if (nn == 1 && k<=1)
140  {
141  scalar dy1 = 0;
142  for (label i=0; i<n_; i++)
143  {
144  dy1 += sqr(dy_[i]/scale[i]);
145  }
146  dy1 = sqrt(dy1);
147 
148  odes_.derivatives(x0 + dx, yTemp_, dydx_);
149  for (label i=0; i<n_; i++)
150  {
151  dy_[i] = dydx_[i] - dy_[i]/dx;
152  }
153 
154  LUBacksubstitute(a_, pivotIndices_, dy_);
155 
156  // This form from the original paper is unreliable
157  // step size underflow for some cases
158  // const scalar denom = max(1, dy1);
159 
160  // This form is reliable but limits how large the step size can be
161  const scalar denom = min(1, dy1 + small);
162 
163  scalar dy2 = 0;
164  for (label i=0; i<n_; i++)
165  {
166  // Test of dy_[i] to avoid overflow
167  if (mag(dy_[i]) > scale[i]*denom)
168  {
169  theta_ = 1;
170  return false;
171  }
172 
173  dy2 += sqr(dy_[i]/scale[i]);
174  }
175  dy2 = sqrt(dy2);
176  theta_ = dy2/denom;
177 
178  if (theta_ > 1)
179  {
180  return false;
181  }
182  }
183 
184  odes_.derivatives(xnew, yTemp_, dy_);
185  LUBacksubstitute(a_, pivotIndices_, dy_);
186  }
187 
188  for (label i=0; i<n_; i++)
189  {
190  y[i] = yTemp_[i] + dy_[i];
191  }
192 
193  return true;
194 }
195 
196 
197 void Foam::seulex::extrapolate
198 (
199  const label k,
201  scalarField& y
202 ) const
203 {
204  for (int j=k-1; j>0; j--)
205  {
206  for (label i=0; i<n_; i++)
207  {
208  table[j-1][i] =
209  table(j, i) + coeff_(k, j)*(table(j, i) - table[j-1][i]);
210  }
211  }
212 
213  for (int i=0; i<n_; i++)
214  {
215  y[i] = table(0, i) + coeff_(k, 0)*(table(0, i) - y[i]);
216  }
217 }
218 
219 
221 {
222  if (ODESolver::resize())
223  {
224  table_.shallowResize(kMaxx_, n_);
225  resizeField(dfdx_);
226  resizeMatrix(dfdy_);
227  resizeMatrix(a_);
228  resizeField(pivotIndices_);
229  resizeField(y0_);
230  resizeField(ySequence_);
231  resizeField(scale_);
232  resizeField(dy_);
233  resizeField(yTemp_);
234  resizeField(dydx_);
235 
236  return true;
237  }
238  else
239  {
240  return false;
241  }
242 }
243 
244 
246 (
247  scalar& x,
248  scalarField& y,
249  stepState& step
250 ) const
251 {
252  temp_[0] = great;
253  scalar dx = step.dxTry;
254  y0_ = y;
255  dxOpt_[0] = mag(0.1*dx);
256 
257  if (step.first || step.prevReject)
258  {
259  theta_ = 2*jacRedo_;
260  }
261 
262  if (step.first)
263  {
264  // NOTE: the first element of relTol_ and absTol_ are used here.
265  scalar logTol = -log10(relTol_[0] + absTol_[0])*0.6 + 0.5;
266  kTarg_ = max(1, min(kMaxx_ - 1, int(logTol)));
267  }
268 
269  forAll(scale_, i)
270  {
271  scale_[i] = absTol_[i] + relTol_[i]*mag(y[i]);
272  }
273 
274  bool jacUpdated = false;
275 
276  if (theta_ > jacRedo_)
277  {
278  odes_.jacobian(x, y, dfdx_, dfdy_);
279  jacUpdated = true;
280  }
281 
282  int k;
283  scalar dxNew = mag(dx);
284  bool firstk = true;
285 
286  while (firstk || step.reject)
287  {
288  dx = step.forward ? dxNew : -dxNew;
289  firstk = false;
290  step.reject = false;
291 
292  if (mag(dx) <= mag(x)*sqr(small))
293  {
295  << "step size underflow :" << dx << endl;
296  }
297 
298  scalar errOld = 0;
299 
300  for (k=0; k<=kTarg_+1; k++)
301  {
302  bool success = seul(x, y0_, dx, k, ySequence_, scale_);
303 
304  if (!success)
305  {
306  step.reject = true;
307  dxNew = mag(dx)*stepFactor5_;
308  break;
309  }
310 
311  if (k == 0)
312  {
313  y = ySequence_;
314  }
315  else
316  {
317  forAll(ySequence_, i)
318  {
319  table_[k-1][i] = ySequence_[i];
320  }
321  }
322 
323  if (k != 0)
324  {
325  extrapolate(k, table_, y);
326  scalar err = 0;
327  forAll(scale_, i)
328  {
329  scale_[i] = absTol_[i] + relTol_[i]*mag(y0_[i]);
330  err += sqr((y[i] - table_(0, i))/scale_[i]);
331  }
332  err = sqrt(err/n_);
333  if (err > 1/small || (k > 1 && err >= errOld))
334  {
335  step.reject = true;
336  dxNew = mag(dx)*stepFactor5_;
337  break;
338  }
339  errOld = min(4*err, 1);
340  scalar expo = 1.0/(k + 1);
341  scalar facmin = pow(stepFactor3_, expo);
342  scalar fac;
343  if (err == 0)
344  {
345  fac = 1/facmin;
346  }
347  else
348  {
349  fac = stepFactor2_/pow(err/stepFactor1_, expo);
350  fac = max(facmin/stepFactor4_, min(1/facmin, fac));
351  }
352  dxOpt_[k] = mag(dx*fac);
353  temp_[k] = cpu_[k]/dxOpt_[k];
354 
355  if ((step.first || step.last) && err <= 1)
356  {
357  break;
358  }
359 
360  if
361  (
362  k == kTarg_ - 1
363  && !step.prevReject
364  && !step.first && !step.last
365  )
366  {
367  if (err <= 1)
368  {
369  break;
370  }
371  else if (err > nSeq_[kTarg_]*nSeq_[kTarg_ + 1]*4)
372  {
373  step.reject = true;
374  kTarg_ = k;
375  if (kTarg_>1 && temp_[k-1] < kFactor1_*temp_[k])
376  {
377  kTarg_--;
378  }
379  dxNew = dxOpt_[kTarg_];
380  break;
381  }
382  }
383 
384  if (k == kTarg_)
385  {
386  if (err <= 1)
387  {
388  break;
389  }
390  else if (err > nSeq_[k + 1]*2)
391  {
392  step.reject = true;
393  if (kTarg_>1 && temp_[k-1] < kFactor1_*temp_[k])
394  {
395  kTarg_--;
396  }
397  dxNew = dxOpt_[kTarg_];
398  break;
399  }
400  }
401 
402  if (k == kTarg_+1)
403  {
404  if (err > 1)
405  {
406  step.reject = true;
407  if
408  (
409  kTarg_ > 1
410  && temp_[kTarg_-1] < kFactor1_*temp_[kTarg_]
411  )
412  {
413  kTarg_--;
414  }
415  dxNew = dxOpt_[kTarg_];
416  }
417  break;
418  }
419  }
420  }
421  if (step.reject)
422  {
423  step.prevReject = true;
424  if (!jacUpdated)
425  {
426  theta_ = 2*jacRedo_;
427 
428  if (theta_ > jacRedo_ && !jacUpdated)
429  {
430  odes_.jacobian(x, y, dfdx_, dfdy_);
431  jacUpdated = true;
432  }
433  }
434  }
435  }
436 
437  jacUpdated = false;
438 
439  step.dxDid = dx;
440  x += dx;
441 
442  label kopt;
443  if (k == 1)
444  {
445  kopt = 2;
446  }
447  else if (k <= kTarg_)
448  {
449  kopt=k;
450  if (temp_[k-1] < kFactor1_*temp_[k])
451  {
452  kopt = k - 1;
453  }
454  else if (temp_[k] < kFactor2_*temp_[k - 1])
455  {
456  kopt = min(k + 1, kMaxx_ - 1);
457  }
458  }
459  else
460  {
461  kopt = k - 1;
462  if (k > 2 && temp_[k-2] < kFactor1_*temp_[k - 1])
463  {
464  kopt = k - 2;
465  }
466  if (temp_[k] < kFactor2_*temp_[kopt])
467  {
468  kopt = min(k, kMaxx_ - 1);
469  }
470  }
471 
472  if (step.prevReject)
473  {
474  kTarg_ = min(kopt, k);
475  dxNew = min(mag(dx), dxOpt_[kTarg_]);
476  step.prevReject = false;
477  }
478  else
479  {
480  if (kopt <= k)
481  {
482  dxNew = dxOpt_[kopt];
483  }
484  else
485  {
486  if (k < kTarg_ && temp_[k] < kFactor2_*temp_[k - 1])
487  {
488  dxNew = dxOpt_[k]*cpu_[kopt + 1]/cpu_[k];
489  }
490  else
491  {
492  dxNew = dxOpt_[k]*cpu_[kopt]/cpu_[k];
493  }
494  }
495  kTarg_ = kopt;
496  }
497 
498  step.dxTry = step.forward ? dxNew : -dxNew;
499 }
500 
501 
502 // ************************************************************************* //
void shallowResize(const label m, const label n)
Resize the matrix without reallocating storage (unsafe)
Definition: MatrixI.H:284
dictionary dict
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
Abstract base class for the systems of ordinary differential equations.
Definition: ODESystem.H:46
virtual bool resize()=0
Resize the ODE solver.
Definition: ODESolver.C:89
A list of keyword definitions, which are a keyword followed by any number of values (e...
Definition: dictionary.H:158
dimensioned< Type > max(const dimensioned< Type > &, const dimensioned< Type > &)
void LUDecompose(scalarSquareMatrix &matrix, labelList &pivotIndices)
LU decompose the matrix with pivoting.
dimensionedSymmTensor sqr(const dimensionedVector &dv)
dimensionedScalar sqrt(const dimensionedScalar &ds)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:256
dimensionedScalar y0(const dimensionedScalar &ds)
label k
Boltzmann constant.
virtual void solve(scalar &x, scalarField &y, stepState &step) const
Solve the ODE system and the update the state.
Definition: seulex.C:246
An ODE solver for chemistry.
Definition: ode.H:50
Macros for easy insertion into run-time selection tables.
virtual void jacobian(const scalar x, const scalarField &y, scalarField &dfdx, scalarSquareMatrix &dfdy) const =0
Calculate the Jacobian of the system.
scalar y
virtual bool resize()
Resize the ODE solver.
Definition: seulex.C:220
void resizeMatrix(scalarSquareMatrix &m) const
Definition: ODESolverI.H:61
const ODESystem & odes_
Reference to ODESystem.
Definition: ODESolver.H:58
addToRunTimeSelectionTable(ensightPart, ensightPartCells, istream)
bool success
defineTypeNameAndDebug(combustionModel, 0)
dimensioned< Type > min(const dimensioned< Type > &, const dimensioned< Type > &)
dimensionedScalar pow(const dimensionedScalar &ds, const dimensionedScalar &expt)
#define WarningInFunction
Report a warning using Foam::Warning.
scalarField relTol_
Relative convergence tolerance per step.
Definition: ODESolver.H:70
Abstract base-class for ODE system solvers.
Definition: ODESolver.H:50
scalarField absTol_
Absolute convergence tolerance per step.
Definition: ODESolver.H:67
seulex(const ODESystem &ode, const dictionary &dict)
Construct from ODESystem.
Definition: seulex.C:50
dimensioned< scalar > mag(const dimensioned< Type > &)
static void resizeField(UList< Type > &f, const label n)
Definition: ODESolverI.H:48
const doubleScalar e
Elementary charge.
Definition: doubleScalar.H:98
label n_
Size of the ODESystem (adjustable)
Definition: ODESolver.H:64
dimensionedScalar log10(const dimensionedScalar &ds)
virtual void derivatives(const scalar x, const scalarField &y, scalarField &dydx) const =0
Calculate the derivatives in dydx.
Namespace for OpenFOAM.
void LUBacksubstitute(const scalarSquareMatrix &luMmatrix, const labelList &pivotIndices, List< Type > &source)
LU back-substitution with given source, returning the solution.