66 int one_based_eigenvalue_index,
67 int one_based_spin_index,
68 Real target_occupation,
69 const std::string& context) {
70 if (one_based_eigenvalue_index < 1)
71 throw std::runtime_error(context +
72 " eigenvalue index must be >= 1 (1-based, ascending), got " +
73 std::to_string(one_based_eigenvalue_index));
74 if (one_based_spin_index != 1 && one_based_spin_index != 2)
75 throw std::runtime_error(context +
" spin must be 1 or 2, got " +
76 std::to_string(one_based_spin_index));
77 if (target_occupation < 0.0 || target_occupation > 1.0)
78 throw std::runtime_error(context +
" value must be in [0, 1], got " +
79 std::to_string(target_occupation));
80 return {species_symbol,
81 one_based_eigenvalue_index - 1,
82 one_based_spin_index - 1,
113 auto upper = [](std::string text) {
114 for (
char& character : text)
115 character =
static_cast<char>(std::toupper(
static_cast<unsigned char>(character)));
118 std::string wanted = upper(specification);
121 if (wanted.size() != 1)
123 static const std::string orbital_letters =
"SPDFGH";
124 size_t position = orbital_letters.find(wanted[0]);
125 return position != std::string::npos &&
static_cast<int>(position) == label.
angular_momentum;
132[[nodiscard]]
inline std::vector<HubbardManifold>
134 const std::vector<AtomicOrbitalLabel>& orbital_labels,
135 const std::vector<HubbardSpecification>& specifications) {
136 std::vector<HubbardManifold> manifolds;
137 for (
const auto& specification : specifications) {
138 bool species_found =
false;
139 for (
int atom_index = 0; atom_index < crystal_structure.
number_of_atoms(); atom_index++) {
140 if (crystal_structure.
element(atom_index) != specification.species_symbol) {
143 species_found =
true;
155 std::vector<std::vector<int>> matched_shells;
156 std::vector<Real> matched_occupations;
157 int angular_momentum = -1;
158 for (
int label_index = 0; label_index < static_cast<int>(orbital_labels.size());
160 const auto& label = orbital_labels[label_index];
161 if (label.atom_index != atom_index ||
164 if (label.magnetic_quantum_number == 0) {
165 matched_shells.emplace_back();
166 matched_occupations.push_back(label.shell_occupation);
168 if (!matched_shells.empty())
169 matched_shells.back().push_back(label_index);
170 angular_momentum = label.angular_momentum;
173 std::vector<int> orbital_indices;
174 if (!matched_shells.empty()) {
176 for (
size_t candidate = 1; candidate < matched_shells.size(); candidate++)
177 if (matched_occupations[candidate] > matched_occupations[best])
180 for (
Real occupation : matched_occupations)
181 if (occupation == matched_occupations[best])
183 if (matched_shells.size() > 1 && ties > 1)
184 throw std::runtime_error(
185 "Hubbard: '" + specification.orbital_label +
"' matches " +
186 std::to_string(matched_shells.size()) +
" shells on atom " +
187 std::to_string(atom_index) +
188 " with equal occupancy, so the intended one cannot be inferred. "
189 "Name the shell explicitly.");
190 orbital_indices = matched_shells[best];
192 if (orbital_indices.empty()) {
193 std::string available;
194 for (
const auto& label : orbital_labels) {
195 if (label.atom_index != atom_index || label.magnetic_quantum_number != 0)
198 (available.empty() ?
"" :
", ") +
199 (label.shell_label.empty()
200 ?
"(unlabelled, l=" + std::to_string(label.angular_momentum) +
")"
201 : label.shell_label);
203 throw std::runtime_error(
204 "Hubbard: orbital shell '" + specification.orbital_label +
205 "' not found in the pseudopotential for species '" +
206 specification.species_symbol +
"' (atom " + std::to_string(atom_index) +
207 "). Available: " + (available.empty() ?
"none" : available) +
208 ". A bare orbital letter such as 'd' selects by angular momentum, which "
209 "is what unlabelled datasets need.");
216 if (
static_cast<int>(orbital_indices.size()) != 2 * angular_momentum + 1) {
217 throw std::runtime_error(
218 "Hubbard: '" + specification.orbital_label +
"' matched " +
219 std::to_string(orbital_indices.size()) +
" orbitals on atom " +
220 std::to_string(atom_index) +
221 ", but a shell with l=" + std::to_string(angular_momentum) +
" has " +
222 std::to_string(2 * angular_momentum + 1) +
223 ". Name the shell explicitly to disambiguate.");
226 {atom_index, specification.u_rydberg, angular_momentum, orbital_indices});
228 if (!species_found) {
229 throw std::runtime_error(
"Hubbard: species '" + specification.species_symbol +
230 "' specified in the hubbard input list is not present in "
257[[nodiscard]]
inline Real
262 int channel_column) {
305 int total_hubbard_orbitals,
307 const Complex* packed_orbitals,
309 std::vector<Complex>& overlaps) {
310 overlaps.assign(
static_cast<size_t>(total_hubbard_orbitals) * number_of_bands,
312 if (number_of_bands == 0 || total_hubbard_orbitals == 0 || number_of_plane_waves == 0) {
320 &total_hubbard_orbitals,
322 &number_of_plane_waves,
323 reinterpret_cast<const std::complex<double>*
>(&alpha_blas),
324 reinterpret_cast<const std::complex<double>*
>(packed_orbitals),
325 &number_of_plane_waves,
326 reinterpret_cast<const std::complex<double>*
>(band_functions),
327 &number_of_plane_waves,
328 reinterpret_cast<const std::complex<double>*
>(&beta_blas),
329 reinterpret_cast<std::complex<double>*
>(overlaps.data()),
330 &total_hubbard_orbitals);
353 Real g_zero_product) {
354 return {2.0 * half_sphere_sum.real() - g_zero_product, 0.0};
365 const std::vector<int>& plane_wave_indices,
366 int number_of_plane_waves) {
367 for (
int g_index = 0; g_index < number_of_plane_waves; g_index++) {
368 if (planewave_basis.
g_vectors()[plane_wave_indices[
static_cast<size_t>(g_index)]]
393 int total_hubbard_orbitals,
395 const Complex* packed_orbitals,
398 bool band_functions_carry_g_component,
399 std::vector<Complex>& overlaps) {
400 for (
int orbital_index = 0; orbital_index < total_hubbard_orbitals; orbital_index++) {
401 for (
int band_index = 0; band_index < number_of_bands; band_index++) {
402 Real g_zero_product = 0.0;
403 if (!band_functions_carry_g_component && g_zero_index >= 0) {
405 (std::conj(packed_orbitals[
static_cast<size_t>(orbital_index) *
406 number_of_plane_waves +
408 band_functions[
static_cast<size_t>(band_index) * number_of_plane_waves +
413 static_cast<size_t>(band_index) * total_hubbard_orbitals + orbital_index;
414 overlaps[flat_index] =
437 const std::vector<Real>& band_occupations,
439 std::vector<Complex>& occupation_matrix) {
440 for (
int band_index = 0; band_index < number_of_bands; band_index++) {
441 Real weight = kpoint_weight * band_occupations[band_index];
445 for (
int m = 0; m < manifold_size; m++) {
446 Complex overlap_m = overlaps(
static_cast<size_t>(band_index),
static_cast<size_t>(m));
447 for (
int m_prime = 0; m_prime < manifold_size; m_prime++) {
449 overlaps(
static_cast<size_t>(band_index),
static_cast<size_t>(m_prime));
450 occupation_matrix[
static_cast<size_t>(m * manifold_size + m_prime)] +=
451 weight * std::conj(overlap_m) * overlap_m_prime;
488[[nodiscard]]
inline Real
493 const std::vector<Real>& band_occupations,
495 const std::vector<Complex>& d_matrix) {
496 Real force_contribution = 0.0;
497 for (
int band_index = 0; band_index < number_of_bands; band_index++) {
498 Real weight = kpoint_weight * band_occupations[band_index];
503 for (
int m = 0; m < manifold_size; m++) {
504 Complex derivative_overlap_m = direction_derivative_overlaps(
505 static_cast<size_t>(band_index),
static_cast<size_t>(m));
506 Complex derivative_overlap_m_conj = std::conj(derivative_overlap_m);
507 for (
int m_prime = 0; m_prime < manifold_size; m_prime++) {
509 overlaps(
static_cast<size_t>(band_index),
static_cast<size_t>(m_prime));
510 band_sum += d_matrix[
static_cast<size_t>(m * manifold_size + m_prime)] *
511 derivative_overlap_m_conj * overlap_m_prime;
514 force_contribution += weight * 2.0 * band_sum.real();
516 return force_contribution;
524 int manifold_size,
Real u_rydberg,
const std::vector<Complex>& occupation_matrix) {
526 result.
d_matrix.assign(
static_cast<size_t>(manifold_size) * manifold_size,
Complex(0.0, 0.0));
529 Real trace_n_squared = 0.0;
530 for (
int m = 0; m < manifold_size; m++) {
531 trace_n += occupation_matrix[
static_cast<size_t>(m * manifold_size + m)].real();
532 for (
int m_prime = 0; m_prime < manifold_size; m_prime++) {
533 Complex element = occupation_matrix[
static_cast<size_t>(m * manifold_size + m_prime)];
534 trace_n_squared += std::norm(element);
537 result.
energy = 0.5 * u_rydberg * (trace_n - trace_n_squared);
539 for (
int m = 0; m < manifold_size; m++) {
540 for (
int m_prime = 0; m_prime < manifold_size; m_prime++) {
541 Real kronecker_delta = (m == m_prime) ? 1.0 : 0.0;
547 occupation_matrix[
static_cast<size_t>(m_prime * manifold_size + m)];
548 result.
d_matrix[
static_cast<size_t>(m * manifold_size + m_prime)] =
549 u_rydberg * (0.5 * kronecker_delta - occupation_m_prime_m);
const std::string & element(int atom_index) const
int number_of_atoms() const
int projector_combined_lm_index(int species_type, int projector_index) const
const PseudopotentialSet & pseudopotential_set() const
int projector_to_beta_index(int species_type, int projector_index) const
const std::vector< GVector > & g_vectors() const
bool has_augmentation_charges() const
Real augmentation_overlap_integral(int species_type, int projector_a, int projector_b) const
void zgemm_(const char *transa, const char *transb, const int *m, const int *n, const int *k, const std::complex< double > *alpha, const std::complex< double > *a, const int *lda, const std::complex< double > *b, const int *ldb, const std::complex< double > *beta, std::complex< double > *c, const int *ldc)
constexpr double numerical_zero_threshold
void accumulate_hubbard_occupation_matrix(int number_of_bands, int manifold_size, const Matrix< Complex > &overlaps, const std::vector< Real > &band_occupations, Real kpoint_weight, std::vector< Complex > &occupation_matrix)
Real hubbard_augmentation_overlap(const UltrasoftPlanewaveBasis &ultrasoft_basis, const NormConservingPlanewaveBasis &pseudopotential_basis, int species_index, int channel_row, int channel_column)
void compute_hubbard_projector_overlaps(int number_of_plane_waves, int total_hubbard_orbitals, int number_of_bands, const Complex *packed_orbitals, const Complex *band_functions, std::vector< Complex > &overlaps)
void convert_gamma_only_projector_overlaps_to_full_sphere(int number_of_plane_waves, int total_hubbard_orbitals, int number_of_bands, const Complex *packed_orbitals, const Complex *band_functions, int g_zero_index, bool band_functions_carry_g_component, std::vector< Complex > &overlaps)
HubbardStartingEigenvalue make_hubbard_starting_eigenvalue(const std::string &species_symbol, int one_based_eigenvalue_index, int one_based_spin_index, Real target_occupation, const std::string &context)
HubbardEnergyAndPotential compute_hubbard_energy_and_potential(int manifold_size, Real u_rydberg, const std::vector< Complex > &occupation_matrix)
Real accumulate_hubbard_force_gradient(int number_of_bands, int manifold_size, const Matrix< Complex > &overlaps, const Matrix< Complex > &direction_derivative_overlaps, const std::vector< Real > &band_occupations, Real kpoint_weight, const std::vector< Complex > &d_matrix)
bool shell_specification_matches(const std::string &specification, const AtomicOrbitalLabel &label)
int find_g_zero_plane_wave_index(const PlanewaveBasis &planewave_basis, const std::vector< int > &plane_wave_indices, int number_of_plane_waves)
Complex gamma_only_full_sphere_overlap(Complex half_sphere_sum, Real g_zero_product)
std::complex< double > Complex
std::vector< HubbardManifold > resolve_hubbard_manifolds(const Crystal &crystal_structure, const std::vector< AtomicOrbitalLabel > &orbital_labels, const std::vector< HubbardSpecification > &specifications)
Identifies one atomic orbital in the ordering produced by build_atomic_orbitals_g_space().
Result of the Dudarev energy/potential formula for one manifold, one spin channel.
std::vector< Complex > d_matrix
std::vector< int > orbital_indices
One user-specified Hubbard-active shell (input.hubbard[i]).
std::string orbital_label
std::string species_symbol
std::string species_symbol
Real target_occupation
[0, 1] in the per-spin convention.
int eigenvalue_index
0-based position in the ascending eigenvalue list.
int spin_index
0-based spin channel.