Quantum 3xplorer (Q3)
An AI-native quantum simulation engine
Loading...
Searching...
No Matches
wavefunction_atomic.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * Planewave representation of atomic wavefunctions.
5 *
6 * Provides the Bessel-transformed atomic wavefunction χ(q) interpolation table
7 * for wavefunction initialization. This is the canonical location for the
8 * radial Fourier transform of pseudopotential atomic wavefunctions onto a
9 * uniform q-grid, which is then interpolated at |k+G| values.
10 *
11 * The tabulated quantity is the radial part of the Rayleigh expansion of a
12 * plane wave in the cell-normalised basis:
13 * table[species_type][wavefunction_index][q_index]
14 * = 4π/√Ω ∫ χ(r) j_l(q·r) r dr
15 * with the 4π and j_l from the expansion, the 1/√Ω from the plane-wave
16 * normalisation, and the single power of r because UPF stores r·R_nl(r).
17 *
18 * The q axis is innermost so the 4-point interpolation stencil reads four
19 * consecutive entries of one contiguous vector; species is outermost because
20 * the shell count varies per species.
21 */
22
23#include <algorithm>
24#include <cmath>
25#include <vector>
26
32
33namespace q3 {
34
35/**
36 * Tabulate atomic wavefunction radial Fourier transforms on a uniform q-grid.
37 *
38 * For each species type and each atomic wavefunction, computes:
39 * table[species_type][wavefunction_index][q_index]
40 * = (4π / √Ω) ∫₀^∞ χ(r) · j_l(q·r) · r · dr
41 * using Simpson's rule on the radial mesh (truncated at the radial cutoff).
42 *
43 * @param pseudopotential_set The pseudopotential set (one per species type).
44 * @param cell_volume Unit cell volume Ω in Bohr³.
45 * @param q_spacing Spacing between q-grid points.
46 * @param number_of_q_points Number of q-grid points.
47 * @return table[species_type][wavefunction_index][q_index]
48 */
49inline std::vector<std::vector<std::vector<Real>>>
51 Real cell_volume,
52 Real q_spacing,
53 int number_of_q_points) {
54
55 int number_of_species = static_cast<int>(pseudopotential_set.size());
56 Real prefactor = constants::four_pi / std::sqrt(cell_volume);
57
58 // Compute mesh truncation sizes for each species
59 std::vector<int> mesh_truncation_sizes(number_of_species);
60 for (int species_index = 0; species_index < number_of_species; species_index++) {
61 const auto& pseudopotential = pseudopotential_set[species_index];
63 mesh_truncation_sizes[species_index] = pseudopotential.mesh_size;
64 for (int radial_index = pseudopotential.mesh_size - 1; radial_index >= 0;
65 radial_index--) {
66 if (pseudopotential.radial_grid[radial_index] <
68 mesh_truncation_sizes[species_index] = radial_index + 1;
69 if (mesh_truncation_sizes[species_index] % 2 == 0)
70 mesh_truncation_sizes[species_index]++; // force odd for Simpson
71 break;
72 }
73 }
74 if (mesh_truncation_sizes[species_index] > pseudopotential.mesh_size) {
75 mesh_truncation_sizes[species_index] = pseudopotential.mesh_size;
76 if (mesh_truncation_sizes[species_index] % 2 == 0) {
77 mesh_truncation_sizes[species_index]--;
78 }
79 }
80 } else {
81 mesh_truncation_sizes[species_index] = pseudopotential.mesh_size;
82 if (mesh_truncation_sizes[species_index] % 2 == 0) {
83 mesh_truncation_sizes[species_index]--;
84 }
85 }
86 }
87
88 // Build tabulation
89 std::vector<std::vector<std::vector<Real>>> table(number_of_species);
90 for (int type_index = 0; type_index < number_of_species; type_index++) {
91 const auto& pseudopotential = pseudopotential_set[type_index];
92 table[type_index].resize(pseudopotential.wavefunctions_atomic.size());
93
94 for (int wavefunction_index = 0;
95 wavefunction_index < static_cast<int>(pseudopotential.wavefunctions_atomic.size());
96 wavefunction_index++) {
97 table[type_index][wavefunction_index].resize(number_of_q_points, 0.0);
98
99 if (pseudopotential.wavefunctions_atomic[wavefunction_index].occupations < 0.0)
100 continue; // skip unoccupied
101
102 int angular_momentum =
103 pseudopotential.wavefunctions_atomic[wavefunction_index].angular_momentum;
104
105 for (int q_index = 0; q_index < number_of_q_points; q_index++) {
106 Real q_value = q_spacing * q_index;
107 // Compute ∫ χ(r) * j_l(q*r) * r * dr using Simpson
108 std::vector<Real> integrand(mesh_truncation_sizes[type_index]);
109 for (int radial_index = 0; radial_index < mesh_truncation_sizes[type_index];
110 radial_index++) {
111 Real bessel_value = spherical_bessel(
112 angular_momentum, q_value * pseudopotential.radial_grid[radial_index]);
113 integrand[radial_index] =
114 pseudopotential.wavefunctions_atomic[wavefunction_index]
115 .radial_values[radial_index] *
116 bessel_value * pseudopotential.radial_grid[radial_index];
117 }
118 table[type_index][wavefunction_index][q_index] =
119 prefactor * simpson_rule(integrand,
120 pseudopotential.radial_grid_derivative,
121 mesh_truncation_sizes[type_index]);
122 }
123 }
124 }
125
126 return table;
127}
128
129} // namespace q3
int size() const
Definition set.hpp:98
constexpr Real four_pi
Definition constants.hpp:26
constexpr bool use_radial_integration_truncation
constexpr double radial_integration_truncation_radius
Real simpson_rule(const std::vector< Real > &function_values, const std::vector< Real > &integration_factors, int number_of_grid_points)
Definition simpson.hpp:23
double Real
std::vector< std::vector< std::vector< Real > > > tabulate_atomic_wavefunctions_in_q_space(const PseudopotentialSet &pseudopotential_set, Real cell_volume, Real q_spacing, int number_of_q_points)
Real spherical_bessel(int degree, Real argument)
Definition bessel.hpp:30