63 return augmentation_q_table_;
70 return spherical_harmonics_table_;
91 species_type_index, projector_pair_index, q_value);
141 int channel_pair_index,
143 int number_of_g_vectors)
const {
144 return q_functions_g_[species_type_index]
145 [channel_pair_index * number_of_g_vectors + g_index];
155 int channel_pair_index,
156 int number_of_g_vectors)
const {
157 return &q_functions_g_[species_type_index][channel_pair_index * number_of_g_vectors];
164 return species_type_index < static_cast<int>(q_functions_g_.size()) &&
165 !q_functions_g_[species_type_index].empty();
179 const Crystal& crystal_structure,
181 std::vector<Complex>& charge_density_g,
182 const std::vector<std::vector<Complex>>* cached_structure_factors =
nullptr)
const {
186 for (
int atom_index = 0; atom_index < crystal_structure.
number_of_atoms(); atom_index++) {
218 std::vector<Complex> augmentation_g(number_of_g_vectors,
Complex(0.0, 0.0));
224 int channel_pair_index =
226 Real diagonal_projector_overlap_coefficient =
227 projector_overlap_coefficients(atom_index, channel_pair_index);
228 if (std::abs(diagonal_projector_overlap_coefficient) >= 1e-15) {
230 species_type_index, channel_pair_index, number_of_g_vectors);
231 for (
int g_index = 0; g_index < number_of_g_vectors; ++g_index) {
232 augmentation_g[g_index] +=
233 diagonal_projector_overlap_coefficient * q_g_data[g_index];
239 for (
int channel_index_j = channel_index_i + 1;
244 Real combined_projector_overlap_coefficient =
245 projector_overlap_coefficients(atom_index, pair_ij) +
246 projector_overlap_coefficients(atom_index, pair_ji);
247 if (std::abs(combined_projector_overlap_coefficient) < 1e-15)
252 for (
int g_index = 0; g_index < number_of_g_vectors; ++g_index) {
253 augmentation_g[g_index] +=
254 combined_projector_overlap_coefficient * q_g_data[g_index];
260 if (cached_structure_factors !=
nullptr) {
261 const auto& structure_factor = (*cached_structure_factors)[atom_index];
262 for (
int g_index = 0; g_index < number_of_g_vectors; ++g_index) {
263 charge_density_g[g_index] +=
264 augmentation_g[g_index] * structure_factor[g_index];
269 for (
int g_index = 0; g_index < number_of_g_vectors; ++g_index) {
272 charge_density_g[g_index] +=
289 int projector_index_i,
290 int projector_index_j,
292 Real two_pi_over_lattice_parameter)
const {
302 Real g_norm = g_cartesian.
norm() * two_pi_over_lattice_parameter;
320 int combined_lm_index_i =
322 int combined_lm_index_j =
324 int angular_momentum_i =
326 int angular_momentum_j =
331 int maximum_angular_momentum_q_plus_one =
333 int number_of_lm_projectors =
334 maximum_angular_momentum_q_plus_one * maximum_angular_momentum_q_plus_one;
335 if (combined_lm_index_i >= number_of_lm_projectors ||
336 combined_lm_index_j >= number_of_lm_projectors)
339 .number_of_nonzero_product_indices[combined_lm_index_i][combined_lm_index_j] == 0)
342 Complex sum_integral(0.0, 0.0);
343 for (
int gaunt_index = 0;
345 [combined_lm_index_j];
347 int product_lm_index =
350 Real gaunt_coefficient_value =
352 .
coefficients[product_lm_index][combined_lm_index_i][combined_lm_index_j];
353 int angular_momentum =
354 static_cast<int>(std::sqrt(
static_cast<double>(product_lm_index)));
355 if (angular_momentum < std::abs(angular_momentum_i - angular_momentum_j) ||
356 angular_momentum > angular_momentum_i + angular_momentum_j ||
357 (angular_momentum + angular_momentum_i + angular_momentum_j) % 2 != 0)
360 Real augmentation_function_value =
362 species_type_index, projector_pair_index, angular_momentum, g_norm);
363 int offset_in_shell = product_lm_index - angular_momentum * angular_momentum;
364 int magnetic_projection = (offset_in_shell == 0)
366 : ((offset_in_shell % 2 == 1) ? (offset_in_shell + 1) / 2
367 : -(offset_in_shell / 2));
368 Real spherical_harmonic_value =
370 Real integrand_contribution = augmentation_function_value * spherical_harmonic_value;
372 switch (angular_momentum % 4) {
374 sum_integral += gaunt_coefficient_value * integrand_contribution;
377 sum_integral +=
Complex(0.0, -gaunt_coefficient_value * integrand_contribution);
380 sum_integral -= gaunt_coefficient_value * integrand_contribution;
383 sum_integral +=
Complex(0.0, gaunt_coefficient_value * integrand_contribution);
400 spherical_harmonics_table_.
init(
403 log::info(
" Ultrasoft Q functions tabulated (L-resolved, lmaxq={})",
441 [[nodiscard]]
static int
443 int maximum_angular_momentum_q) {
444 int projector_degree = 0;
446 species_type_index++) {
447 projector_degree = std::max(
450 if (maximum_angular_momentum_q > 2 * projector_degree) {
451 throw std::invalid_argument(
452 "Gaunt table: the augmentation charges declare angular momenta up to L = " +
453 std::to_string(maximum_angular_momentum_q) +
454 ", above the 2 * l_max = " + std::to_string(2 * projector_degree) +
455 " that the projector channels of this dataset can produce. The table is "
456 "built from the projector degree, so such an expansion would be indexed "
459 return projector_degree;
482 int maximum_angular_momentum_q_plus_one =
484 int number_of_lm_projectors =
485 maximum_angular_momentum_q_plus_one * maximum_angular_momentum_q_plus_one;
487 q_functions_g_.resize(number_of_species);
489 for (
int species_type_index = 0; species_type_index < number_of_species;
490 ++species_type_index) {
502 int number_of_beta_functions =
506 q_functions_g_[species_type_index].assign(total_pairs * number_of_g_vectors,
510 struct GauntTermForPair {
511 int product_lm_index;
512 int angular_momentum;
513 int projector_pair_index;
514 Real gaunt_coefficient_value;
517 struct ChannelPairInfo {
518 int linear_pair_index;
519 std::vector<GauntTermForPair> gaunt_terms;
522 std::vector<ChannelPairInfo> active_pairs;
529 species_type_index, channel_index_i);
531 species_type_index, channel_index_j);
533 species_type_index, channel_index_i);
535 species_type_index, channel_index_j);
540 int projector_pair_index =
541 beta_index_i * number_of_beta_functions + beta_index_j;
543 if (combined_lm_index_i >= number_of_lm_projectors ||
544 combined_lm_index_j >= number_of_lm_projectors) {
548 [combined_lm_index_j] == 0) {
552 std::vector<GauntTermForPair> terms;
553 for (
int gaunt_index = 0;
556 [combined_lm_index_j];
558 int product_lm_index =
560 [combined_lm_index_j][gaunt_index];
561 Real gaunt_coefficient_value =
562 gaunt_table_.
coefficients[product_lm_index][combined_lm_index_i]
563 [combined_lm_index_j];
564 int angular_momentum =
565 static_cast<int>(std::sqrt(
static_cast<double>(product_lm_index)));
566 if (angular_momentum < std::abs(angular_momentum_i - angular_momentum_j) ||
567 angular_momentum > angular_momentum_i + angular_momentum_j ||
568 (angular_momentum + angular_momentum_i + angular_momentum_j) % 2 != 0) {
571 terms.push_back({product_lm_index,
573 projector_pair_index,
574 gaunt_coefficient_value});
577 if (!terms.empty()) {
578 int linear_pair_index =
580 active_pairs.push_back({linear_pair_index, std::move(terms)});
586 for (
int g_index = 0; g_index < number_of_g_vectors; ++g_index) {
587 Real g_magnitude = g_vector_magnitudes[g_index];
589 for (
const auto& pair : active_pairs) {
593 for (
const auto& term : pair.gaunt_terms) {
594 Real augmentation_function_value =
597 term.projector_pair_index,
598 term.angular_momentum,
600 Real spherical_harmonic_value =
601 spherical_harmonics_table_.
value(g_index, term.product_lm_index);
602 Real integrand_contribution =
603 augmentation_function_value * spherical_harmonic_value;
605 switch (term.angular_momentum % 4) {
607 q_g_real += term.gaunt_coefficient_value * integrand_contribution;
610 q_g_imag -= term.gaunt_coefficient_value * integrand_contribution;
613 q_g_real -= term.gaunt_coefficient_value * integrand_contribution;
616 q_g_imag += term.gaunt_coefficient_value * integrand_contribution;
621 q_functions_g_[species_type_index]
622 [pair.linear_pair_index * number_of_g_vectors + g_index] =
628 " Pre-computed Q(G) table for species {} ({} channel pairs, {} G-vectors)",
630 static_cast<int>(active_pairs.size()),
631 number_of_g_vectors);
647 std::vector<std::vector<Complex>> q_functions_g_;
Real interpolate_qfunc(int species_type_index, int projector_pair_index, Real q_value) const override
int maximum_angular_momentum_q() const override
void init(const PseudopotentialSet &pseudopotential_set, const std::vector< int > &mesh_sizes, int number_of_q_points, Real q_spacing, Real cell_volume)
Real interpolate_augmentation_function(int species_type_index, int projector_pair_index, int angular_momentum, Real q_value) const override
Vector3D convert_from_crystal_to_cartesian(int atom_index) const
int species_type_index(int atom_index) const
int number_of_atoms() const
std::unique_ptr< BetaFunctionTable > beta_function_table_
std::vector< int > mesh_sizes_
ProjectorMappingTable projector_mapping_table_
int number_of_projectors(int species_type) const
PseudopotentialSet pseudopotential_set_
const PlanewaveBasis & planewave_basis() const
const PseudopotentialSet & pseudopotential_set() const
int number_of_projector_channels(int species_type) const
virtual void init(const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, const PseudopotentialSet &pseudopotential_set)
const PlanewaveBasis * planewave_basis_ptr_
int number_of_g_vectors() const
const std::vector< GVector > & g_vectors() const
const std::vector< Real > & g_vector_magnitudes() const
int number_of_channels(int species_type) const
int to_beta_index(int species_type, int channel_index) const
int angular_momentum(int species_type, int channel_index) const
int combined_lm_index(int species_type, int channel_index) const
const UltrasoftPseudopotential & as_ultrasoft(int index) const
void init(const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, int maximum_angular_momentum)
Real value(int g_index, int combined_lm_index) const
void add_augmentation_charges(const Crystal &crystal_structure, const Matrix< Real > &projector_overlap_coefficients, std::vector< Complex > &charge_density_g, const std::vector< std::vector< Complex > > *cached_structure_factors=nullptr) const
const SphericalHarmonicsTable & spherical_harmonics_table() const
void init(const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, const PseudopotentialSet &pseudopotential_set) override
const GauntCoefficientTable & gaunt_table() const
const Complex * q_function_g_data(int species_type_index, int channel_pair_index, int number_of_g_vectors) const
Complex compute_q_g_single(int species_type_index, int projector_index_i, int projector_index_j, const Vector3D &g_cartesian, Real two_pi_over_lattice_parameter) const
static int projector_degree_for_gaunt_table(const PseudopotentialSet &pseudopotential_set, int maximum_angular_momentum_q)
~UltrasoftPlanewaveBasis() override=default
void init_ultrasoft(const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis)
Real augmentation_overlap_integral(int species_type, int projector_a, int projector_b) const
bool has_q_functions_g(int species_type_index) const
Real interpolate_qfunc(int species_type_index, int projector_pair_index, Real q_value) const
const AugmentationQTable & augmentation_q_table() const
bool is_ultrasoft() const override
void precompute_q_functions_g(const PlanewaveBasis &planewave_basis)
Complex q_function_g_value(int species_type_index, int channel_pair_index, int g_index, int number_of_g_vectors) const
UltrasoftPlanewaveBasis()=default
std::vector< std::vector< Real > > augmentation_overlap_integrals
void info(spdlog::format_string_t< Args... > fmt, Args &&... args)
constexpr double zero_vector_magnitude_threshold
Real compute_real_spherical_harmonic(int degree, int order, const Vector3D &direction)
Complex compute_phase_factor(Real phase)
Compute e^{-iφ} = cos(φ) - i·sin(φ) (standard structure factor phase).
std::complex< double > Complex
GauntCoefficientTable generate_gaunt_coefficients(int max_degree)
std::vector< std::vector< std::vector< Real > > > coefficients
std::vector< std::vector< std::vector< int > > > nonzero_product_indices
std::vector< std::vector< int > > number_of_nonzero_product_indices