Hex  1.0
Hydrogen-electron collision solver
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
clenshawcurtis.h
Go to the documentation of this file.
1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
2  * *
3  * / / / / __ \ \ / / *
4  * / /__ / / / _ \ \ \/ / *
5  * / ___ / | |/_/ / /\ \ *
6  * / / / / \_\ / / \ \ *
7  * *
8  * Jakub Benda (c) 2014 *
9  * Charles University in Prague *
10  * *
11 \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12 
13 #ifndef HEX_CLENSHAWCURTIS
14 #define HEX_CLENSHAWCURTIS
15 
16 #include <cmath>
17 #include <iostream>
18 #include <numeric>
19 #include <vector>
20 
21 #include <fftw3.h>
22 #include <gsl/gsl_sf.h>
23 
24 #include "arrays.h"
25 #include "compact.h"
26 #include "misc.h"
27 
39 template <class Functor, typename FType> class ClenshawCurtis
40 {
41 public:
42 
47  ClenshawCurtis (Functor const & f) : F(f), EpsRel(1e-8), EpsAbs(1e-12),
48  Limit(true), Recurrence(true), NNest(5), NStack(5), L(1.0), Verbose(false),
49  vName("[ClenshawCurtis_ff]"), Throw(true) {}
50 
52  inline double eps() const { return EpsRel; }
53 
55  inline void setEps(double epsrel) { EpsRel = epsrel; }
56 
58  inline double tol() const { return EpsAbs; }
59 
61  inline void setTol(double epsabs) { EpsAbs = epsabs; }
62 
64  inline double getRange() const { return L; }
65 
67  inline void setRange(double l) { L = l; }
68 
70  inline int subdiv() const { return NNest; }
71 
73  inline void setSubdiv(int nlevel) { NNest = nlevel; }
74 
76  inline int stack() const { return NStack; }
77 
79  inline void setStack(int nlevel) { NStack = nlevel; }
80 
82  inline bool lim() const { return Limit; }
83 
85  inline void setLim(bool limit) { Limit = limit; }
86 
88  inline bool Rec() const { return Recurrence; }
89 
91  inline void setRec(bool recurrence) { Recurrence = recurrence; }
92 
94  inline bool verbose() const { return Verbose; }
95 
97  inline void setVerbose(bool verbose, std::string name = "ClenshawCurtis_ff") { Verbose = verbose; vName = name; }
98 
100  inline void setThrowAll(bool t) { Throw = t; }
101 
103  inline bool throwall() const { return Throw; }
104 
116  FType integrate (double x1, double x2, int * n = nullptr) const
117  {
118  if (x1 == x2)
119  return 0.;
120 
121  // if both bounds are infinite, call a specialized function
122  if (not std::isfinite(x1) and not std::isfinite(x2))
123  return integrate_ii(n);
124 
125  // lower bound is infinite
126  if (not std::isfinite(x1))
127  {
128  // the compactified functor
129  CompactIntegrand<decltype(F),FType> G(F, x1, x2, Limit, L);
130 
131  // quadrature system
133  cc_G.setEps(EpsRel);
134  cc_G.setTol(EpsAbs);
135  cc_G.setLim(Limit);
136  cc_G.setRec(Recurrence);
137  cc_G.setSubdiv(NNest);
138  cc_G.setStack(NStack);
139  cc_G.setRange(L);
140 
141  // integrate
142  return -cc_G.integrate_ff(-1., 1., n); // (-∞,x2)->(1,-1)
143  }
144 
145  // upper bound is infinite
146  if (not std::isfinite(x2))
147  {
148  // the compactified functor
149  CompactIntegrand<decltype(F),FType> G(F, x1, x2, Limit, L);
150 
151  // quadrature system
153  cc_G.setEps(EpsRel);
154  cc_G.setTol(EpsAbs);
155  cc_G.setLim(Limit);
156  cc_G.setRec(Recurrence);
157  cc_G.setSubdiv(NNest);
158  cc_G.setStack(NStack);
159  cc_G.setRange(L);
160 
161  // integrate
162  return cc_G.integrate_ff(-1., 1., n); // (x1,+∞)->(-1,1)
163  }
164 
165  // both bounds are finite
166  return integrate_ff(x1, x2, n);
167  }
168 
178  FType integrate_ff (double x1, double x2, int * n = nullptr) const
179  {
180  // check interval bounds
181  if (x1 == x2)
182  {
183  if (n != nullptr)
184  *n = 0;
185  return FType(0);
186  }
187  if (x1 > x2)
188  {
189  return -integrate_ff(x2, x1, n);
190  }
191 
192  // scaled F
193  auto f = [&](double x) -> FType
194  {
195  return F(x1 + 0.5 * (1.0 + x) * (x2 - x1));
196  };
197 
198  // Chebyshev coefficients
199  std::vector<FType> coefs;
200 
201  // evaluated function f and its transformation
202  std::vector<Complex> fvals_prev, fvals, ftraf;
203 
204  // integral approximation
205  FType sum = 0, sum_prev = special::constant::Nan;
206 
207  // get nesting limit
208  int maxN = gsl_sf_pow_int(2,NNest);
209 
210  // convergence loop
211  for (int N = 4; N <= maxN /*or not Recurrence*/; N *= 2)
212  {
213  if (n != nullptr)
214  *n = N;
215 
216  // reserve memory
217  fvals.resize(2*N + 1);
218  ftraf.resize(2*N);
219 
220  // create plan
221  fftw_plan plan = fftw_plan_dft_1d
222  (
223  2*N,
224  reinterpret_cast<fftw_complex*>(&fvals[0]),
225  reinterpret_cast<fftw_complex*>(&ftraf[0]),
226  FFTW_BACKWARD,
227  0
228  );
229 
230  // is this the first iteration?
231  if (coefs.empty())
232  {
233  // evaluate f everywhere
234  double pi_over_N = M_PI / N;
235  for (int k = 0; k < N; k++)
236  {
237  fvals[k] = fvals[2*N-k] = f(cos(k * pi_over_N));
238 
239  if (not std::isfinite(std::abs(fvals[k])))
240  throw exception("%s \"%g\" when evaluating function at %g", vName.c_str(), fvals[k], cos(k * pi_over_N));
241  }
242  fvals[N] = f(-1);
243 
244  if (not std::isfinite(std::abs(fvals[N])))
245  throw exception("%s \"%g\" when evaluating function at -1.", vName.c_str(), fvals[N]);
246  }
247  else
248  {
249  // evaluate just the new half, recycle older evaluations
250  double pi_over_N = M_PI / N;
251  for (int k = 0; k < N; k++)
252  {
253  fvals[k] = fvals[2*N-k] = (k % 2 == 0) ? fvals_prev[k/2] : f(cos(k * pi_over_N));
254 
255  if (not std::isfinite(std::abs(fvals[k])))
256  throw exception("%s \"%g\" when evaluating function.", vName.c_str(), fvals[k]);
257  }
258  fvals[N] = fvals_prev[N/2];
259  }
260 
261  coefs.resize(N + 1);
262 
263  // compute coefficients using FFT/DCT-I
264  fftw_execute(plan);
265  fftw_destroy_plan(plan);
266 
267  // create type-correct pointer
268  FType const * ftraf_ptr = reinterpret_cast<FType*>(&ftraf[0]);
269 
270  // copy result
271  if (typeid(FType) == typeid(Complex))
272  {
273  // copy whole complex numbers
274  for (int i = 0; i <= N; i++)
275  coefs[i] = 0.5 * (*(ftraf_ptr + i));
276  }
277  else if (typeid(FType) == typeid(double))
278  {
279  // copy just real parts
280  for (int i = 0; i <= N; i++)
281  coefs[i] = 0.5 * (*(ftraf_ptr + 2*i));
282  }
283  else
284  {
285  throw exception("%s Can't handle datatype \"%s\".", vName.c_str(), typeid(FType).name());
286  }
287 
288  // sum the quadrature rule
289  sum = 0.5 * (coefs[0] - coefs[N] / (N*N - 1.));
290  for (int twok = 2; twok < N; twok += 2)
291  sum -= coefs[twok] / (twok*twok - 1.);
292 
293  // echo debug information
294  if (Verbose)
295  std::cout << vName << " N = " << N << ", Sum = " << FType(2.*(x2-x1)/N)*sum << "\n";
296 
297  // check for convergence
298  if (std::abs(sum - FType(2.) * sum_prev) <= std::max(EpsRel*std::abs(sum), EpsAbs))
299  {
300  if (Verbose)
301  std::cout << vName << " Convergence for N = " << N << ", sum = " << FType(2. * (x2 - x1) / N) * sum << "\n";
302 
303  return FType(2. * (x2 - x1) / N) * sum;
304  }
305  else if ( std::isfinite(std::abs(sum))
306  and std::isfinite(std::abs(sum_prev))
307  and std::max(std::abs(sum), std::abs(sum_prev)) <= EpsAbs * std::abs(x2-x1) )
308  {
309  if (Verbose)
310  std::cout << vName << " EpsAbs limit matched, " << EpsAbs << " on (" << x1 << "," << x2 << ").\n";
311 
312  return FType(0.);
313  }
314  else
315  {
316  /* do nothing */
317  }
318 
319  // save function evaluations and sum
320  fvals_prev = std::move(fvals);
321  sum_prev = sum;
322  }
323 
324  // At this point the subdivision loop has been exited.
325  // Now we either return, recurr or throw, depending on the settings.
326 
327  if (not Recurrence)
328  {
329  if (Throw)
330  {
331  throw exception("%s Insufficient evaluation limit %d", vName.c_str(), maxN);
332  }
333  else
334  {
335  std::cout << vName << " WARNING: Insufficient evaluation limit " << maxN << ".\n";
336  return FType(2. * (x2 - x1) / maxN) * sum;
337  }
338  }
339 
340  //
341  // no convergence? -> bisect
342  //
343 
344  // cancel bisection if interval too tiny
345  if (std::abs(x2-x1) < EpsAbs)
346  {
347  if (Verbose)
348  std::cout << vName << " Interval smaller than " << EpsAbs << "\n";
349  return 0;
350  }
351 
352  // cancel bisection if stack full
353  if (NStack == 0)
354  {
355  if (Verbose)
356  std::cout << vName << " Bisection inhibited due to internal stack limit.\n";
357  return FType(2. * (x2 - x1) / maxN) * sum;
358  }
359 
360  if (Verbose)
361  {
362  std::cout << vName << " Bisecting to ("
363  << x1 << "," << (x2+x1)/2 << ") and ("
364  << (x2+x1)/2 << "," << x2 << ")\n";
365  }
366 
367  // the actual bisection - also the attributes of the class need to be modified
368 
369  int n1, n2;
370  double this_EpsAbs = EpsAbs;
371 
372  NStack--;
373  EpsAbs = 0.5 * (2. * (x2 - x1) / maxN) * std::abs(sum) * EpsRel;
374 
375  FType i1 = integrate_ff(x1, (x2+x1)/2, &n1);
376  FType i2 = integrate_ff((x2+x1)/2, x2, &n2);
377 
378  NStack++;
379  EpsAbs = this_EpsAbs;
380 
381  if (n != nullptr) *n = n1 + n2;
382  return i1 + i2;
383  }
384 
392  FType integrate_ii (int * n = nullptr) const
393  {
394  // function values, new and previous
395  std::vector<FType> fvals, fvals_prev;
396 
397  // weights, new and previous
398  std::vector<double> weights, weights_prev;
399 
400  // previous integral
401  FType sum_prev = special::constant::Nan;
402 
403  // main loop
404  for (int N = 2; ; N *= 2)
405  {
406  fvals.resize(N);
407  weights.resize(N);
408 
409  // precompute values
410  if (fvals_prev.empty())
411  {
412  // compute all values
413  for (int i = 1; i <= N - 1; i++)
414  {
415  double x = i * M_PI / N;
416  fvals[i] = F(L / tan(x));
417  if (not std::isfinite(std::abs(fvals[i])))
418  fvals[i] = 0;
419  weights[i] = 1. / gsl_sf_pow_int(sin(x), 2);
420  }
421  }
422  else
423  {
424  // compute new values only
425  for (int i = 1; i < N - 1; i++)
426  {
427  if (i % 2 == 0)
428  {
429  fvals[i] = fvals_prev[i/2];
430  weights[i] = weights_prev[i/2];
431  }
432  else
433  {
434  double x = i * M_PI / N;
435  fvals[i] = F(L / tan(x));
436  if (not std::isfinite(std::abs(fvals[i])))
437  fvals[i] = 0;
438  weights[i] = 1. / gsl_sf_pow_int(sin(x), 2);
439  }
440  }
441  }
442 
443  // evaluate the integral
444  FType sum = 0.;
445  for (int i = 1; i <= N - 1; i++)
446  sum += weights[i] * fvals[i];
447  if (std::abs(sum - FType(2.) * sum_prev) < EpsRel * std::abs(sum))
448  {
449  if (n != nullptr) *n = N;
450  return FType(L * M_PI / N) * sum;
451  }
452 
453  // save precomputed values
454  sum_prev = sum;
455  fvals_prev = fvals;
456  weights_prev = weights;
457  }
458  }
459 
460 private:
461 
463  Functor const & F;
464 
466  double EpsRel;
467 
469  mutable double EpsAbs;
470 
480  bool Limit;
481 
488  bool Recurrence;
489 
497  int NNest;
498 
505  mutable int NStack;
506 
508  double L;
509 
511  bool Verbose;
512 
514  std::string vName;
515 
517  bool Throw;
518 };
519 
520 #endif
ClenshawCurtis(Functor const &f)
Definition: clenshawcurtis.h:47
bool lim() const
Get limit flag.
Definition: clenshawcurtis.h:82
void setEps(double epsrel)
Set relative tolerance.
Definition: clenshawcurtis.h:55
void setStack(int nlevel)
Set subdivision level limit.
Definition: clenshawcurtis.h:79
NumberArray< T > cos(NumberArray< T > const &A)
Return per-element cosine.
Definition: arrays.h:1747
FType integrate(double x1, double x2, int *n=nullptr) const
General integration.
Definition: clenshawcurtis.h:116
double getRange() const
Get compacification parameter.
Definition: clenshawcurtis.h:64
Clenshaw-Curtis quadrature.
Definition: clenshawcurtis.h:39
bool verbose() const
Get verbose flag.
Definition: clenshawcurtis.h:94
const double e
Definition: special.h:44
void setLim(bool limit)
Set limit flag.
Definition: clenshawcurtis.h:85
void setThrowAll(bool t)
Set warn flag.
Definition: clenshawcurtis.h:100
FType integrate_ii(int *n=nullptr) const
Improper integration.
Definition: clenshawcurtis.h:392
void setVerbose(bool verbose, std::string name="ClenshawCurtis_ff")
Set verbose flag.
Definition: clenshawcurtis.h:97
T sum(const ArrayView< T > v)
Sum elements in array.
Definition: arrays.h:1770
double tol() const
Get absolute tolerance.
Definition: clenshawcurtis.h:58
NumberArray< T > sin(NumberArray< T > const &A)
Return per-element sine.
Definition: arrays.h:1723
void setRange(double l)
Set compacification parameter.
Definition: clenshawcurtis.h:67
void setSubdiv(int nlevel)
Set 2-log of maximal subdivision interval count.
Definition: clenshawcurtis.h:73
int stack() const
Get subdivision level limit.
Definition: clenshawcurtis.h:76
void setRec(bool recurrence)
Set recurrence flag.
Definition: clenshawcurtis.h:91
CLArrayView exception
void setTol(double epsabs)
Set absolute tolerance.
Definition: clenshawcurtis.h:61
rArray abs(const cArrayView u)
Definition: arrays.cpp:19
std::complex< double > Complex
Definition: complex.h:20
int subdiv() const
Get 2-log of maximal subdivision interval count.
Definition: clenshawcurtis.h:70
bool throwall() const
Get warn flag.
Definition: clenshawcurtis.h:103
T max(const ArrayView< T > a)
Maximal element of array.
Definition: arrays.h:1673
const double Nan
Definition: special.h:68
double eps() const
Get relative tolerance.
Definition: clenshawcurtis.h:52
Compactification multiplied by its jacobian.
Definition: compact.h:311
bool Rec() const
Get recurrence flag.
Definition: clenshawcurtis.h:88
FType integrate_ff(double x1, double x2, int *n=nullptr) const
Finite integration.
Definition: clenshawcurtis.h:178