Loading...
Searching...
No Matches
reciprocal_projector_resampling.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * Resample a dataset-supplied β(q) onto Q3's q-grid.
5 *
6 * Most pseudopotential formats give projectors radially, as r·β(r), and Q3
7 * obtains β(q) by spherical Bessel transform. Some datasets give β(q)
8 * directly, in the same convention Q3's own transform produces
9 * (β(q) = 4π ∫ β(r) j_l(qr) r² dr), so the only work is moving it from the
10 * dataset's uniform q-grid onto Q3's.
11 *
12 * Doing that instead of inverting to r and transforming back is not merely
13 * cheaper. The dataset's table is finite, so an inversion truncates, and the
14 * truncation shows up as a projector that no
15 * longer integrates to unity against its own partial wave. Resampling touches
16 * only q values below the plane-wave cutoff, well inside the tabulated range,
17 * and inherits no such error.
18 *
19 * Interpolation is the Catmull-Rom cubic through the four surrounding samples,
20 * which is local and continuous in value and slope. Beyond the
21 * tabulated range the projector is zero: the dataset's q_max is far above any
22 * cutoff a calculation would use, so reaching past it means the basis exceeded
23 * what the dataset describes, and extrapolating a projector there would invent
24 * structure the dataset never claimed.
25 */
26
27#include <cstddef>
28#include <vector>
29
31
32namespace q3 {
33
34/**
35 * Interpolate a uniformly tabulated function onto arbitrary q values.
36 *
37 * The table starts at q = 0, so the cubic's first interval needs a sample at
38 * q = −spacing that no dataset stores. It follows from parity rather than
39 * being invented: β_l(q) is even in q for even l and odd for odd l, so the
40 * point below zero is ±β(+spacing), signed by the parity of l. The choice
41 * matters: clamping instead
42 * costs an order of magnitude in accuracy over the first interval, which is
43 * the long-wavelength end where projectors carry the most weight.
44 *
45 * @param samples f(q_i) at q_i = i · spacing, i = 0 … samples.size()-1
46 * @param spacing grid spacing of the supplied table
47 * @param angular_momentum l, which fixes the parity of the below-zero sample
48 * @param q_values q values to evaluate at
49 * @param result [out] f(q), resized to q_values.size()
50 */
51inline void resample_uniform_reciprocal_table(const std::vector<Real>& samples,
52 Real spacing,
53 int angular_momentum,
54 const std::vector<Real>& q_values,
55 std::vector<Real>& result) {
56 result.assign(q_values.size(), 0.0);
57 if (samples.size() < 2 || spacing <= 0.0)
58 return;
59
60 const Real parity_sign = (angular_momentum % 2 == 0) ? 1.0 : -1.0;
61 auto sample_at = [&samples, parity_sign](long index) -> Real {
62 if (index < 0)
63 return parity_sign * samples[static_cast<size_t>(-index)];
64 if (index >= static_cast<long>(samples.size()))
65 return 0.0;
66 return samples[static_cast<size_t>(index)];
67 };
68
69 const Real last_q = static_cast<Real>(samples.size() - 1) * spacing;
70 for (size_t point = 0; point < q_values.size(); point++) {
71 Real q_value = q_values[point];
72 if (q_value < 0.0 || q_value > last_q)
73 continue; // outside what the dataset describes
74
75 Real scaled = q_value / spacing;
76 auto lower = static_cast<long>(scaled);
77 if (lower >= static_cast<long>(samples.size()) - 1)
78 lower = static_cast<long>(samples.size()) - 2;
79 Real fraction = scaled - static_cast<Real>(lower);
80
81 Real previous = sample_at(lower - 1);
82 Real current = sample_at(lower);
83 Real next = sample_at(lower + 1);
84 Real after = sample_at(lower + 2);
85
86 // Catmull-Rom.
87 Real a = -0.5 * previous + 1.5 * current - 1.5 * next + 0.5 * after;
88 Real b = previous - 2.5 * current + 2.0 * next - 0.5 * after;
89 Real c = -0.5 * previous + 0.5 * next;
90 result[point] = ((a * fraction + b) * fraction + c) * fraction + current;
91 }
92}
93
94} // namespace q3
double Real
void resample_uniform_reciprocal_table(const std::vector< Real > &samples, Real spacing, int angular_momentum, const std::vector< Real > &q_values, std::vector< Real > &result)