forked from dftlibs/xcfun
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbr.cpp
120 lines (106 loc) · 2.44 KB
/
br.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include <iostream>
#include <cassert>
#include <cmath>
#define TAYLOR_CXXIO
#include "taylor.h"
/*
Initial tests for the Becke Roussel functionals.
This shows how to taylor expand the implicitly
defined quantity x, see BR() below.
by ulfek
*/
using namespace std;
// This is the function we want to find roots for
template<class T>
T BR_y(const T &x)
{
return x*exp(-2.0/3.0*x)/(x-2);
}
// Determine x as a function of y = BR_y(x), using first
// a guess and then root polishing.
// We need about 3-5 NR iterations depending on the y value.
double BR(double y)
{
double d;
taylor<double,1,2> x(0,0),fx;
// More or less clever starting guesses
if (y < 0)
{
if (y > -0.65)
x[0] = -2*y;
else
x[0] = (6*exp(4.0/3.0)*y + 8)/(3*exp(4.0/3.0)*y+1);
}
else
{
if (y > 0.1)
{
x[0] = (6*exp(4.0/3.0)*y + 8)/(3*exp(4.0/3.0)*y+1);
if (y < 0.7)
x[0] -= 0.7*exp(-5*y); // Pragmatic correction
}
else
{
x[0] = -3.0/2.0*log(y) + y*(47 + y*(-850 + y*4800));
}
}
//Polish root with Newton-Raphson (or Halley's method)
int niter = 0;
do
{
fx = BR_y(x);
// Newton:
d = (y-fx[0])/fx[1];
// Halley:
double yn = fx[0] - y;
//d = -yn*fx[1]/(fx[1]*fx[1] - yn*fx[2]);
x[0] += d;
if (++niter > 100)
{
cerr << niter << " iterations reached, giving up. y = " << y << endl;
return 0;
}
}
while (fabs(d)>1e-12);
cerr << "niter = " << niter << endl;
return x[0];
}
// Obtain the Taylor expansion of x(y), which is the
// inverse of BR_y. Use linear method for simplicity.
template<class T, int Ndeg>
void BR_taylor(const T &y0, taylor<T,1,Ndeg> &t)
{
taylor<T,1,Ndeg> f,d;
t = 0;
t[0] = BR(y0);
t[1] = 1;
f = BR_y(t);
t[1] = 1/f[1];
// Linear method, for quadratic see i.e. Brent & Kung ~197x
for (int i=2;i<=Ndeg;i++)
{
f = BR_y(t);
t[i] = -f[i]*t[1];
}
}
/* This is a fully differentiable solver for Eq.(21) in
Becke and Roussel, PRA 39, 1989. t is the right hand
side value, x is returned.
*/
template<class T,int Nvar, int Ndeg>
static taylor<T,Nvar,Ndeg> BR(const taylor<T,Nvar,Ndeg> &t)
{
taylor<T,1,Ndeg> tmp;
BR_taylor(t[0],tmp);
taylor<T,Nvar,Ndeg> res;
t.compose(res,tmp);
return res;
}
int main()
{
taylor<double,1,5> seed(3,0);
for (double y = -100;y<100;y+= 5.001)
cout << y << " " << BR(y) << endl;
cout << "# taylor expansion coeffs at y = 3: " << BR(seed) << endl;
return 0;
}