56namespace paw_one_center {
72 if (pseudopotential.
projectors[channel].angular_momentum !=
117 int angular_order = 0) {
118 functional_ = &functional;
119 int number_of_species = pseudopotential_set.
size();
122 for (
int species_index = 0; species_index < number_of_species; species_index++) {
128 throw std::runtime_error(
129 "PAW dataset for " + paw->element +
130 " pairs its projectors and partial waves in different orders, so the "
131 "projector overlap coefficients cannot be read as on-site occupancies");
133 auto& context = contexts_[species_index];
134 context.pseudopotential = paw;
137 int maximum_angular_momentum = 0;
138 for (
const auto& wave : paw->partial_waves_pseudo)
139 maximum_angular_momentum =
140 std::max(maximum_angular_momentum, wave.angular_momentum);
154 int density_order = 2 * maximum_angular_momentum;
155 int functional_order = density_order + (context.gradient_corrected ? 1 : 0);
156 int order = std::max(angular_order, functional_order);
160 if (context.gradient_corrected) {
161 context.harmonic_gradients =
174 inputs.
gaunt = &context.gaunt;
178 context.gradient_corrected ? &context.harmonic_gradients :
nullptr;
180 context.convention->prepare(inputs);
181 log::info(
" PAW: {} one-center convention for {} (atomic Hxc {:.9f} Ry, "
182 "reference double counting {:.9f} Ry)",
183 context.convention->name(),
185 context.convention->reference_one_center_energy(),
186 context.convention->reference_double_counting_energy());
190 d_correction_by_spin_.assign(1, std::vector<std::vector<Real>>(number_of_atoms));
195 for (
const auto& context : contexts_) {
196 if (context.pseudopotential !=
nullptr)
214 update_by_spin(crystal_structure, {&projector_overlap_coefficients}, stride);
231 const std::vector<
const Matrix<Real>*>& projector_overlap_coefficients_by_spin,
233 int number_of_spins =
static_cast<int>(projector_overlap_coefficients_by_spin.size());
237 d_correction_by_spin_.assign(number_of_spins,
238 std::vector<std::vector<Real>>(number_of_atoms));
240 for (
int atom_index = 0; atom_index < number_of_atoms; atom_index++) {
242 const auto& context = contexts_[species_index];
243 if (context.pseudopotential ==
nullptr)
260 throw std::runtime_error(
"PAW one-center channel count exceeds the allocated "
261 "projector overlap row width");
264 for (
int spin = 0; spin < number_of_spins; spin++) {
265 const auto& coefficients_for_spin = *projector_overlap_coefficients_by_spin[spin];
268 occupancies.at(spin, first, second) =
272 const auto& paw = *context.pseudopotential;
273 if (number_of_spins == 1) {
275 paw, context.expansion, occupancies, context.gaunt, 0);
279 context.angular_grid,
282 context.gradient_corrected ? &context.harmonic_gradients :
nullptr,
283 context.convention->options());
284 accumulate_atom(context, paw, occupancies,
energy, atom_index, 0);
288 if (number_of_spins > 2)
289 throw std::runtime_error(
290 "PAW one-center terms support at most two spin channels; " +
291 std::to_string(number_of_spins) +
" were given");
293 std::vector<OneCenterDensities> densities_by_spin;
294 densities_by_spin.reserve(number_of_spins);
295 for (
int spin = 0; spin < number_of_spins; spin++)
297 paw, context.expansion, occupancies, context.gaunt, spin));
301 context.angular_grid,
304 context.gradient_corrected ? &context.harmonic_gradients :
nullptr,
305 context.convention->options());
306 for (
int spin = 0; spin < number_of_spins; spin++)
307 accumulate_atom(context, paw, occupancies, energies[spin], atom_index, spin);
326 energy_ +=
energy.total();
328 energy_ += context.
convention->energy_correction(occupancies);
333 d_correction_by_spin_[spin][atom_index] =
335 context.
convention->correct_d_matrix(d_correction_by_spin_[spin][atom_index]);
365 return branch_totals_;
381 [[nodiscard]]
const std::vector<Real>&
d_correction(
int atom_index,
int spin = 0)
const {
382 int channel = spin < static_cast<int>(d_correction_by_spin_.size()) ? spin : 0;
383 return d_correction_by_spin_[channel][atom_index];
387 return contexts_[species_index].number_of_channels();
391 std::vector<SpeciesOneCenterContext> contexts_;
393 std::vector<std::vector<std::vector<Real>>> d_correction_by_spin_;
396 BranchTotals branch_totals_;
int species_type_index(int atom_index) const
int number_of_atoms() const
PAW pseudopotential — extends ultrasoft with one-center (on-site) data.
std::vector< PartialWave > partial_waves_pseudo
Pseudo partial waves φ̃_i(r), one per projector channel.
std::vector< Projector > projectors
Abstract base class for XC functionals.
virtual bool requires_density_gradient() const
Whether this is a GGA functional (needs gradients).
const std::vector< Real > & d_correction(int atom_index, int spin=0) const
const BranchTotals & branch_totals() const
int number_of_channels(int species_index) const
void update(const Crystal &crystal_structure, const Matrix< Real > &projector_overlap_coefficients, int stride)
Real energy() const
Σ_a (E¹_a − Ẽ¹_a), in Rydberg.
void build(const Crystal &crystal_structure, const PseudopotentialSet &pseudopotential_set, const XCFunctional &functional, int angular_order=0)
void update_by_spin(const Crystal &crystal_structure, const std::vector< const Matrix< Real > * > &projector_overlap_coefficients_by_spin, int row_capacity)
void info(spdlog::format_string_t< Args... > fmt, Args &&... args)
std::vector< std::vector< Real > > tabulate_harmonics(const std::vector< AngularPoint > &angular_grid, int number_of_lm)
Real spherical harmonics at every angular grid point, [point][lm].
std::vector< std::vector< std::array< Real, 3 > > > tabulate_harmonic_gradients(const std::vector< AngularPoint > &angular_grid, int number_of_lm)
std::unique_ptr< PawOneCenterConvention > create_one_center_convention(const PawPseudopotential &pseudopotential)
std::vector< OneCenterEnergy > compute_one_center_energy_spin(const PawPseudopotential &pseudopotential, const std::vector< OneCenterDensities > &densities_by_spin, const std::vector< AngularPoint > &angular_grid, const std::vector< std::vector< Real > > &harmonics, const XCFunctional &functional, const std::vector< std::vector< std::array< Real, 3 > > > *harmonic_gradients=nullptr, const OneCenterOptions &options={})
OneCenterDensities build_one_center_densities(const PawPseudopotential &pseudopotential, const ChannelExpansion &expansion, const OnSiteOccupancies &occupancies, const GauntCoefficientTable &gaunt, int spin)
std::vector< AngularPoint > build_angular_grid(int maximum_angular_momentum)
ChannelExpansion expand_channels(const PawPseudopotential &pseudopotential)
std::vector< Real > compute_one_center_d_matrix(const PawPseudopotential &pseudopotential, const ChannelExpansion &expansion, const GauntCoefficientTable &gaunt, const OneCenterEnergy &energy)
OneCenterEnergy compute_one_center_energy_either_mode(const PawPseudopotential &pseudopotential, const OneCenterDensities &densities, const std::vector< AngularPoint > &angular_grid, const std::vector< std::vector< Real > > &harmonics, const XCFunctional &functional, const std::vector< std::vector< std::array< Real, 3 > > > *harmonic_gradients=nullptr, const OneCenterOptions &options={})
int density_lm_count(const PawPseudopotential &pseudopotential)
Number of lm channels a density needs: products of two l_max harmonics.
bool channel_expansion_matches_projectors(const PawPseudopotential &pseudopotential)
GauntCoefficientTable generate_gaunt_coefficients(int max_degree)
static OnSiteOccupancies zeros(int number_of_channels, int number_of_spin_components=1)
Both branches, kept separate so a gate failure can be localized.
Real all_electron_hartree
Everything reusable across SCF steps for one species.
std::shared_ptr< PawOneCenterConvention > convention
ChannelExpansion expansion
GauntCoefficientTable gaunt
std::vector< std::vector< Real > > harmonics
std::vector< AngularPoint > angular_grid
std::vector< std::vector< std::array< Real, 3 > > > harmonic_gradients
int number_of_channels() const
const PawPseudopotential * pseudopotential