ampsci
High-precision calculations for one- and two-valence atomic systems
Loading...
Searching...
No Matches
SphericalBessel.hpp
1#pragma once
2#include "LinAlg/Matrix.hpp"
3#include "qip/Maths.hpp"
4#include "qip/Vector.hpp"
5#include <cmath>
6#include <gsl/gsl_sf_bessel.h>
7#include <vector>
8//
9#include <gsl/gsl_errno.h>
10#include <iostream>
11
16namespace SphericalBessel {
17
18//==============================================================================
21template <typename T>
22T JL(const int L, const T x) {
23
24 // Low x expansion for first few Ls. Accurate to better than ~ 1e-15
25 if (std::abs(x) < 0.1) {
26 if (L == 0) {
27 const T x2 = x * x;
28 const T x4 = x2 * x2;
29 return 1.0 - 0.166666666666667 * x2 + 0.0083333333333333 * x4;
30 } else if (L == 1) {
31 const T x2 = x * x;
32 const T x3 = x2 * x;
33 const T x5 = x3 * x2;
34 return 0.333333333333 * x - 0.0333333333333 * x3 + 0.001190476190 * x5;
35 } else if (L == 2) {
36 const T x2 = x * x;
37 const T x4 = x2 * x2;
38 const T x6 = x4 * x2;
39 return 0.06666666667 * x2 - 0.004761904762 * x4 + 0.0001322751323 * x6;
40 } else if (L == 3) {
41 const T x2 = x * x;
42 const T x3 = x2 * x;
43 const T x5 = x3 * x2;
44 const T x7 = x5 * x2;
45 return 0.009523809524 * x3 - 0.0005291005291 * x5 + 0.00001202501203 * x7;
46 } else if (L == 4) {
47 const T x2 = x * x;
48 const T x4 = x2 * x2;
49 const T x6 = x4 * x2;
50 return 0.001058201058 * x4 - 0.00004810004810 * x6;
51 } else if (L == 5) {
52 const T x2 = x * x;
53 const T x3 = x2 * x;
54 const T x5 = x3 * x2;
55 const T x7 = x5 * x2;
56 return 0.00009620009620 * x5 - 3.700003700e-6 * x7;
57 } else if (L == 6) {
58 const T x2 = x * x;
59 const T x6 = x2 * x2 * x2;
60 return 7.400007400e-6 * x6;
61 } else if (L == 7) {
62 const T x2 = x * x;
63 const T x7 = x2 * x2 * x2 * x;
64 return 4.933338267e-7 * x7;
65 }
66 }
67
68 // If none of above apply, use GSL to calc. accurately
69 return (T)gsl_sf_bessel_jl(L, (double)x);
70}
71
72//==============================================================================
73template <typename T>
74T exactGSL_JL(int L, T x) {
75 return (T)gsl_sf_bessel_jl(L, (double)x);
76}
77
78//==============================================================================
80template <typename T>
81std::vector<T> fillBesselVec(const int l, const std::vector<T> &xvec) {
82 std::vector<T> Jl_vec;
83 Jl_vec.reserve(xvec.size());
84 for (const auto &x : xvec) {
85 // Jl_vec.push_back(exactGSL_JL(l, x));
86 Jl_vec.push_back(JL(l, x));
87 }
88 return Jl_vec;
89}
90
91template <typename T>
92std::vector<T> fillBesselVec_kr(const int l, const double k,
93 const std::vector<T> &rvec) {
94 std::vector<T> Jl_vec;
95 Jl_vec.reserve(rvec.size());
96 for (const auto &r : rvec) {
97 Jl_vec.push_back(JL(l, k * r));
98 }
99 return Jl_vec;
100}
101
102template <typename T>
103void fillBesselVec_kr(int l, double k, const std::vector<T> &r,
104 std::vector<T> *jl) {
105 jl->resize(r.size());
106#pragma omp parallel for
107 for (std::size_t i = 0; i < r.size(); ++i) {
108 (*jl)[i] = JL(l, k * r[i]);
109 }
110}
111
112//==============================================================================
114
120class JL_table {
121public:
122 JL_table() {}
123 JL_table(int max_L, const std::vector<double> &q,
124 const std::vector<double> &r) {
125 fill(max_L, q, r);
126 }
127
129 void fill(int max_L, const std::vector<double> &q,
130 const std::vector<double> &r) {
131 m_q = q;
132 m_J_L_q.resize(std::size_t(max_L + 1), q.size());
133 m_J_L_q_on_qr.resize(std::size_t(max_L + 1), q.size());
134 using namespace qip::overloads;
135#pragma omp parallel for collapse(2)
136 for (auto L = 0ul; L <= std::size_t(max_L); ++L) {
137 for (auto iq = 0ul; iq < m_J_L_q.cols(); ++iq) {
138 const auto tq = q[iq];
139 // Fill jL(qr)
140 m_J_L_q[L][iq] = fillBesselVec_kr(int(L), tq, r);
141 // Store jL(qr)/qr
142 m_J_L_q_on_qr[L][iq] = m_J_L_q[L][iq] / (tq * r);
143 }
144 }
145 }
146
147 //----------------------------------------------------------------------------
149 const std::vector<double> &at(std::size_t L, std::size_t iq) const {
150 return m_J_L_q.atc(L, iq);
151 }
152
153 //----------------------------------------------------------------------------
155 const std::vector<double> &jL(int L, double q) const {
156 // The '-1' here ensures we always get a valid index, even if q > m_q.back()
157 const auto it = std::lower_bound(m_q.begin(), m_q.end() - 1, q);
158 const auto iq = std::size_t(std::distance(m_q.begin(), it));
159 return m_J_L_q.atc(std::size_t(L), iq);
160 }
161
162 //----------------------------------------------------------------------------
164 const std::vector<double> &jL_nearest(int L, double q) const {
165
166 // Clamp
167 if (q <= m_q.front())
168 return m_J_L_q.atc(std::size_t(L), 0);
169 if (q >= m_q.back())
170 return m_J_L_q.atc(std::size_t(L), m_q.size() - 1);
171
172 // Find i such that m_q[i] <= q < m_q[i+1]
173 const auto it = std::lower_bound(m_q.begin() + 1, m_q.end() - 1, q);
174 const auto i = std::size_t(std::distance(m_q.begin(), it)) - 1;
175
176 // Pick nearest
177 const auto iq = (q - m_q[i] < m_q[i + 1] - q) ? i : i + 1;
178
179 return m_J_L_q.atc(std::size_t(L), iq);
180 }
181
182 //----------------------------------------------------------------------------
184 const std::vector<double> &jL_on_qr_nearest(int L, double q) const {
185
186 // Clamp
187 if (q <= m_q.front())
188 return m_J_L_q_on_qr.atc(std::size_t(L), 0);
189 if (q >= m_q.back())
190 return m_J_L_q_on_qr.atc(std::size_t(L), m_q.size() - 1);
191
192 // Find i such that m_q[i] <= q < m_q[i+1]
193 const auto it = std::lower_bound(m_q.begin() + 1, m_q.end() - 1, q);
194 const auto i = std::size_t(std::distance(m_q.begin(), it)) - 1;
195
196 // Pick nearest
197 const auto iq = (q - m_q[i] < m_q[i + 1] - q) ? i : i + 1;
198
199 return m_J_L_q_on_qr.atc(std::size_t(L), iq);
200 }
201
202 //----------------------------------------------------------------------------
205 std::vector<double> jL_interp(int L, double q) const {
206
207 // Clamp q to the tabulated range
208 if (q <= m_q.front())
209 return m_J_L_q.atc(std::size_t(L), 0);
210 if (q >= m_q.back())
211 return m_J_L_q.atc(std::size_t(L), m_q.size() - 1);
212
213 // Find i such that m_q[i] <= q < m_q[i+1]
214 const auto it = std::lower_bound(m_q.begin() + 1, m_q.end() - 1, q);
215 const auto i = std::size_t(std::distance(m_q.begin(), it)) - 1;
216
217 // Linear interpolation weight
218 // const double t = (q - m_q[i]) / (m_q[i + 1] - m_q[i]);
219
220 const double logq = std::log10(q);
221 const double logq0 = std::log10(m_q[i]);
222 const double logq1 = std::log10(m_q[i + 1]);
223 const double t = (logq - logq0) / (logq1 - logq0);
224
225 const auto &v0 = m_J_L_q.atc(std::size_t(L), i);
226 const auto &v1 = m_J_L_q.atc(std::size_t(L), i + 1);
227
228 using namespace qip::overloads;
229 return (1.0 - t) * v0 + t * v1;
230 }
231
232 //----------------------------------------------------------------------------
233private:
235 LinAlg::Matrix<std::vector<double>> m_J_L_q_on_qr{};
236 std::vector<double> m_q{};
237};
238
239} // namespace SphericalBessel
Matrix class; row-major.
Definition Matrix.hpp:35
const T & atc(std::size_t row_i, std::size_t col_j) const
const ref () index access (with range checking). (i,j) ith row, jth col
Definition Matrix.hpp:139
std::size_t cols() const
Return columns [minor index size].
Definition Matrix.hpp:110
void resize(std::size_t rows, std::size_t cols)
Resizes matrix to new dimension; all values reset to default.
Definition Matrix.hpp:92
Spherical Bessel Lookup table; in the form j_L(qr) = J[L][q][r].
Definition SphericalBessel.hpp:120
const std::vector< double > & jL_nearest(int L, double q) const
Returns jL(q_i*r) for the grid point q_i nearest to the requested q.
Definition SphericalBessel.hpp:164
const std::vector< double > & at(std::size_t L, std::size_t iq) const
Direct access to jL(q*r) for specific q grid index.
Definition SphericalBessel.hpp:149
const std::vector< double > & jL_on_qr_nearest(int L, double q) const
Returns jL(q_i*r)/(q_i*r) for the grid point q_i nearest to the requested q.
Definition SphericalBessel.hpp:184
const std::vector< double > & jL(int L, double q) const
Returns jL(q_i*r) for the first grid point q_i such that q_i >= q.
Definition SphericalBessel.hpp:155
std::vector< double > jL_interp(int L, double q) const
Returns jL(q*r) interpolated linearly between grid points. nb: assumes q grid dense enough that linea...
Definition SphericalBessel.hpp:205
void fill(int max_L, const std::vector< double > &q, const std::vector< double > &r)
If derivatives required for degree L, max_L = L+1.
Definition SphericalBessel.hpp:129
Wrappers for returning Spherical Bessel functions.
Definition SphericalBessel.hpp:16
T JL(const int L, const T x)
Spherical Bessel function. For x<0.1 and K<=7, uses expansion accurate to delta~1e-15,...
Definition SphericalBessel.hpp:22
std::vector< T > fillBesselVec(const int l, const std::vector< T > &xvec)
Creates a vector of Jl(r) for given r.
Definition SphericalBessel.hpp:81
namespace qip::overloads provides operator overloads for std::vector
Definition Vector.hpp:451