atomicrex  0.1
An advanced atomistic model building tool
NumericalDerivative.h
1 //
3 // Copyright (C) 2017, Alexander Stukowski and Paul Erhart
4 //
5 // This file is part of atomicrex.
6 //
7 // Atomicrex is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // Atomicrex is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 //
21 
22 #pragma once
23 
24 #include "../Atomicrex.h"
25 
26 namespace atomicrex {
27 
33 {
34 public:
35 
38  double compute(double x, double& error, double epsilon = 1e-5) {
39 
40  double round, trunc;
41  double r_0 = centralDerivative(x, epsilon, round, trunc);
42  error = round + trunc;
43  if(round < trunc && (round > 0 && trunc > 0)) {
44  // Compute an optimized step size to minimize the total error,
45  // using the scaling of the truncation error (O(h^2)) and
46  // rounding error (O(1/h)).
47  double h_opt = epsilon * pow(round / (2.0 * trunc), 1.0 / 3.0);
48  double round_opt, trunc_opt;
49  double r_opt = centralDerivative(x, h_opt, round_opt, trunc_opt);
50  double error_opt = round_opt + trunc_opt;
51 
52  // Check that the new error is smaller, and that the new derivative
53  // is consistent with the error bounds of the original estimate.
54  if(error_opt < error && fabs(r_opt - r_0) < 4.0 * error) {
55  r_0 = r_opt;
56  error = error_opt;
57  }
58  }
59 
60  return r_0;
61  }
62 
63 protected:
64 
67  virtual double evaluate(double x) = 0;
68 
71  double centralDerivative(double x, double h, double& abserr_trunc, double& abserr_round) {
72 
73  // Compute the error using the difference between the 5-point and
74  // the 3-point rule (-h,0,+h). Again the central point is not used.
75 
76  double fm1 = evaluate(x - h);
77  double fp1 = evaluate(x + h);
78 
79  double fmh = evaluate(x - h/2);
80  double fph = evaluate(x + h/2);
81 
82  double r3 = 0.5 * (fp1 - fm1);
83  double r5 = (4.0 / 3.0) * (fph - fmh) - (1.0 / 3.0) * r3;
84 
85  const double CENTRAL_DBL_EPSILON = 2.2204460492503131e-16;
86  double e3 = (fabs(fp1) + fabs(fm1)) * CENTRAL_DBL_EPSILON;
87  double e5 = 2.0 * (fabs(fph) + fabs(fmh)) * CENTRAL_DBL_EPSILON + e3;
88 
89  // The truncation error in the r5 approximation itself is O(h^4).
90  // However, for safety, we estimate the error from r5-r3, which is
91  // O(h^2). By scaling h we will minimize this estimated error, not
92  // the actual truncation error in r5.
93 
94  double result = r5 / h;
95  abserr_trunc = fabs((r5 - r3) / h); // Estimated truncation error O(h^2)
96  abserr_round = fabs(e5 / h); // Rounding error (cancellations)
97 
98  return result;
99  }
100 };
101 
102 } // End of namespace
This file collects the definition of classes that define various simple crystal structures.
Definition: Atomicrex.h:67
double centralDerivative(double x, double h, double &abserr_trunc, double &abserr_round)
Definition: NumericalDerivative.h:71
Definition: NumericalDerivative.h:32
double compute(double x, double &error, double epsilon=1e-5)
Definition: NumericalDerivative.h:38
virtual double evaluate(double x)=0