Loading...
Searching...
No Matches
charge_core.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * NLCC core charge density tabulation free functions.
5 *
6 * Pure functions that compute the non-linear core correction (NLCC)
7 * charge density in G-space via spherical Bessel transform, and evaluate
8 * the core charge density at a single q value via direct radial integration.
9 */
10
11#include <cmath>
12#include <complex>
13#include <vector>
14
28
29namespace q3 {
30
31/**
32 * Compute core charge density at a single q value via direct radial integration.
33 *
34 * Returns 4π ∫ ρ_core(r) * r² * j₀(qr) dr.
35 */
37 int mesh_size,
38 Real q_value) {
39 /**
40 * A dataset that supplies ñ_c reciprocally means that table for the
41 * plane-wave grid, not a radial pseudized core density it may also carry.
42 * The two are different functions carrying different amounts of charge, so
43 * this is a branch on what the dataset holds.
44 */
45 const auto* paw = dynamic_cast<const PawPseudopotential*>(&pseudopotential);
46 if (paw != nullptr && paw->has_reciprocal_core_charge()) {
47 std::vector<Real> single_q{q_value};
48 std::vector<Real> tabulated;
50 paw->charge_core_reciprocal, paw->reciprocal_core_grid_spacing, 0, single_q, tabulated);
51 return tabulated[0];
52 }
53
54 std::vector<Real> integrand(mesh_size);
55 for (int radial_index = 0; radial_index < mesh_size; radial_index++) {
56 Real bessel_value =
57 spherical_bessel(0, q_value * pseudopotential.radial_grid[radial_index]);
58 integrand[radial_index] = pseudopotential.charge_core.radial_values[radial_index] *
59 pseudopotential.radial_grid[radial_index] *
60 pseudopotential.radial_grid[radial_index] * bessel_value;
61 }
62 Real integrated_sum =
63 simpson_rule(integrand, pseudopotential.radial_grid_derivative, mesh_size);
64 return constants::four_pi * integrated_sum;
65}
66
67/**
68 * Compute the derivative of the core charge density on the radial grid.
69 *
70 * Uses the 3-point asymmetric formula from gradient_nonuniform to compute
71 * d(ρ_core)/dr on the non-uniform radial grid. This is needed for
72 * GGA exchange-correlation with nonlinear core correction.
73 */
75 int mesh_size,
76 std::vector<Real>& core_charge_derivative) {
77 core_charge_derivative.resize(mesh_size, 0.0);
78 gradient_nonuniform(pseudopotential.charge_core.radial_values.data(),
79 core_charge_derivative.data(),
80 pseudopotential.radial_grid.data(),
81 mesh_size);
82}
83
84/// Result of core charge density tabulation.
87 std::vector<std::vector<Real>> interpolation_table; ///< [species_type][q_index]
88 std::vector<Complex> core_charge_density_reciprocal; ///< ρ_core(G) with structure factors
89};
90
91/**
92 * Tabulate core charge density in G-space for all species.
93 *
94 * Checks whether any species has NLCC; if so, builds the q-grid interpolation
95 * table via spherical Bessel transform, then computes ρ_core(G) with structure factors.
96 */
99 const PlanewaveBasis& planewave_basis,
100 const PseudopotentialSet& pseudopotential_set,
101 const std::vector<int>& mesh_sizes,
102 const std::vector<Real>& g_vector_magnitudes,
103 Real cell_volume,
104 Real q_spacing,
105 int number_of_q_points) {
106
107 int number_of_species_types = static_cast<int>(pseudopotential_set.size());
108 int number_of_g_vectors = static_cast<int>(g_vector_magnitudes.size());
109
111
112 // Check if any species has NLCC
113 for (int type_index = 0; type_index < number_of_species_types; type_index++) {
114 if (pseudopotential_set[type_index].has_nonlinear_core_correction) {
115 result.has_nonlinear_core_correction = true;
116 break;
117 }
118 }
119
121 return result;
122
123 // Build q-grid
124 std::vector<Real> q_point_values(number_of_q_points);
125 for (int q_index = 0; q_index < number_of_q_points; q_index++) {
126 q_point_values[q_index] = q_index * q_spacing;
127 }
128
129 // Build interpolation table for each species
130 result.interpolation_table.resize(number_of_species_types);
131 for (int element_type = 0; element_type < number_of_species_types; element_type++) {
132 const auto& pseudopotential = pseudopotential_set[element_type];
133 if (!pseudopotential.has_nonlinear_core_correction) {
134 result.interpolation_table[element_type].assign(number_of_q_points, 0.0);
135 continue;
136 }
137 int radial_mesh_size = mesh_sizes[element_type];
138 std::vector<Real> radial_function(pseudopotential.mesh_size, 0.0);
139 for (int radial_index = 0; radial_index < pseudopotential.mesh_size; radial_index++) {
140 radial_function[radial_index] =
141 pseudopotential.charge_core.radial_values[radial_index] *
142 pseudopotential.radial_grid[radial_index];
143 }
144 transform_spherical_bessel(pseudopotential.radial_grid,
145 pseudopotential.radial_grid_derivative,
146 radial_function,
147 0,
148 radial_mesh_size,
149 q_point_values,
150 result.interpolation_table[element_type]);
151 }
152
153 // Compute core charge density in reciprocal space with structure factors
154 result.core_charge_density_reciprocal.assign(number_of_g_vectors, Complex(0.0, 0.0));
155 for (int g_vector_index = 0; g_vector_index < number_of_g_vectors; g_vector_index++) {
156 for (int element_type = 0; element_type < number_of_species_types; element_type++) {
157 if (!pseudopotential_set[element_type].has_nonlinear_core_correction)
158 continue;
159 Real q_value = g_vector_magnitudes[g_vector_index];
160 Real core_charge_density_value = calculate_core_charge_density_at_q(
161 pseudopotential_set[element_type], mesh_sizes[element_type], q_value);
162
163 Complex structure_factor(0.0, 0.0);
164 for (int atom_index = 0; atom_index < crystal_structure.number_of_atoms();
165 atom_index++) {
166 if (crystal_structure.species_type_index(atom_index) == element_type) {
167 Vector3D atomic_position =
168 crystal_structure.convert_from_crystal_to_cartesian(atom_index);
169 const auto& g_vector = planewave_basis.g_vectors()[g_vector_index];
170 Real phase = constants::two_pi * g_vector.g_cart.dot(atomic_position);
171 structure_factor += compute_phase_factor(phase);
172 }
173 }
174 result.core_charge_density_reciprocal[g_vector_index] +=
175 core_charge_density_value * structure_factor / cell_volume;
176 }
177 }
178
179 return result;
180}
181
182} // namespace q3
Vector3D convert_from_crystal_to_cartesian(int atom_index) const
Definition crystal.hpp:225
int species_type_index(int atom_index) const
Definition crystal.hpp:273
int number_of_atoms() const
Definition crystal.hpp:209
PAW pseudopotential — extends ultrasoft with one-center (on-site) data.
Definition init.hpp:33
const std::vector< GVector > & g_vectors() const
Definition init.hpp:148
int size() const
Definition set.hpp:98
Base class for all pseudopotential types.
Definition init.hpp:35
std::vector< Real > radial_grid_derivative
Definition init.hpp:77
std::vector< Real > radial_grid
Definition init.hpp:76
ChargeCore charge_core
Definition init.hpp:84
constexpr Real two_pi
Definition constants.hpp:25
constexpr Real four_pi
Definition constants.hpp:26
Real simpson_rule(const std::vector< Real > &function_values, const std::vector< Real > &integration_factors, int number_of_grid_points)
Definition simpson.hpp:23
Real calculate_core_charge_density_at_q(const Pseudopotential &pseudopotential, int mesh_size, Real q_value)
void gradient_nonuniform(const Real *function_values, Real *gradient, const Real *grid, int mesh_size)
Definition gradient.hpp:34
double Real
void calculate_core_charge_density_derivative(const Pseudopotential &pseudopotential, int mesh_size, std::vector< Real > &core_charge_derivative)
CoreChargeTabulationResult tabulate_core_charge_density_in_g_space(const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, const PseudopotentialSet &pseudopotential_set, const std::vector< int > &mesh_sizes, const std::vector< Real > &g_vector_magnitudes, Real cell_volume, Real q_spacing, int number_of_q_points)
void transform_spherical_bessel(const std::vector< Real > &grid, const std::vector< Real > &integration_factors, const std::vector< Real > &function, int degree, int mesh_size, const std::vector< Real > &q_points, std::vector< Real > &fourier_transform)
Complex compute_phase_factor(Real phase)
Compute e^{-iφ} = cos(φ) - i·sin(φ) (standard structure factor phase).
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)
std::complex< double > Complex
Real spherical_bessel(int degree, Real argument)
Definition bessel.hpp:30
std::vector< Real > radial_values
Result of core charge density tabulation.
std::vector< std::vector< Real > > interpolation_table
[species_type][q_index]
std::vector< Complex > core_charge_density_reciprocal
ρ_core(G) with structure factors