43 : quantum_system_(&quantum_system)
45 , execution_policy_(execution_policy) {}
48 if (quantum_system_ ==
nullptr) {
49 throw std::logic_error(
50 "PlanewaveFermiEnergyCalculator::calculate_occupations() called before the "
51 "quantum system was assigned. The calculator is wired after construction, so "
52 "there is a window in which it is not yet usable; this reports that window "
53 "instead of dereferencing null, which is what the assert it replaces did in a "
57 if (!kpoint_weights_sample_the_brillouin_zone()) {
60 calculate_smeared_occupations();
62 calculate_fixed_occupations();
103 [[nodiscard]]
bool kpoint_weights_sample_the_brillouin_zone()
const {
105 Real total_weight = 0.0;
106 for (
int kpoint_index = 0; kpoint_index < grid.
number_of_kpoints(); kpoint_index++) {
107 total_weight += grid[kpoint_index].weight;
117 if (execution_policy_ !=
nullptr && execution_policy_->
number_of_pools() > 1) {
124 warn_once(
"Fermi energy: the {} k-point weights sum to {:.6f}, not the {:.1f} a "
125 "Brillouin-zone sampling carries, so this k-point set is not one this "
126 "calculation can integrate over -- a band path is the usual reason, since it "
127 "traces lines through the zone rather than filling it. No Fermi energy is "
128 "determined from it; the value stays at {:.8f} Ry. Take the Fermi energy from "
129 "the self-consistent run that produced the density.",
158 void calculate_fixed_occupations()
const {
168 const bool has_occupied_band = occupied_bands > 0 && occupied_bands <= number_of_bands;
169 const bool has_unoccupied_band = occupied_bands < number_of_bands;
172 Real highest_occupied_molecular_orbital = -1e30;
173 Real lowest_unoccupied_molecular_orbital = 1e30;
174 for (
int kpoint_state_index = 0; kpoint_state_index < number_of_kpoint_states;
175 kpoint_state_index++) {
176 if (has_occupied_band) {
177 highest_occupied_molecular_orbital =
178 std::max(highest_occupied_molecular_orbital,
179 quantum_system_->
eigenvalue(kpoint_state_index, occupied_bands - 1));
181 if (has_unoccupied_band) {
182 lowest_unoccupied_molecular_orbital =
183 std::min(lowest_unoccupied_molecular_orbital,
184 quantum_system_->
eigenvalue(kpoint_state_index, occupied_bands));
192 if (execution_policy_ !=
nullptr && execution_policy_->
number_of_pools() > 1) {
197 if (has_occupied_band && has_unoccupied_band) {
199 0.5 * (highest_occupied_molecular_orbital + lowest_unoccupied_molecular_orbital));
203 if (has_occupied_band) {
204 warn_once(
"Fermi energy: {} band(s) requested for {} occupied band(s), so there is no "
205 "unoccupied level to bound the gap from above; reporting the highest "
206 "occupied level ({:.8f} Ry) instead of a mid-gap Fermi energy.",
209 highest_occupied_molecular_orbital);
214 if (has_unoccupied_band) {
215 warn_once(
"Fermi energy: no occupied bands ({} electron(s)), so there is no occupied "
216 "level to bound the gap from below; reporting the lowest unoccupied level "
219 lowest_unoccupied_molecular_orbital);
230 warn_once(
"Fermi energy: no bands to report one from; leaving it at {:.8f} Ry.",
241 template <
typename... Arguments>
242 void warn_once(
const char* message, Arguments&&... arguments)
const {
243 if (has_warned_about_missing_band_edge_) {
246 has_warned_about_missing_band_edge_ =
true;
247 log::warn(message, std::forward<Arguments>(arguments)...);
254 void calculate_smeared_occupations()
const {
276 const std::vector<Real> state_table = global_kpoint_state_table();
277 const int values_per_state = number_of_bands + 1;
278 const int number_of_global_states =
static_cast<int>(state_table.size()) / values_per_state;
279 const auto weight_of = [&](
int state_index) {
280 return state_table[
static_cast<std::size_t
>(state_index) * values_per_state];
282 const auto eigenvalue_of = [&](
int state_index,
int band_index) {
283 return state_table[
static_cast<std::size_t
>(state_index) * values_per_state + 1 +
288 Real eigenvalue_minimum = 1e30, eigenvalue_maximum = -1e30;
289 for (
int state_index = 0; state_index < number_of_global_states; state_index++) {
290 for (
int band_index = 0; band_index < number_of_bands; band_index++) {
291 Real energy = eigenvalue_of(state_index, band_index);
292 eigenvalue_minimum = std::min(eigenvalue_minimum,
energy);
293 eigenvalue_maximum = std::max(eigenvalue_maximum,
energy);
297 eigenvalue_minimum -= 1.0;
298 eigenvalue_maximum += 1.0;
307 auto count_electrons = [&](
Real trial_fermi_energy) {
308 Real total_electrons = 0.0;
309 for (
int state_index = 0; state_index < number_of_global_states; state_index++) {
310 Real weight = weight_of(state_index);
311 for (
int band_index = 0; band_index < number_of_bands; band_index++) {
312 Real energy_difference_ratio =
313 (eigenvalue_of(state_index, band_index) - trial_fermi_energy) /
315 total_electrons += weight * smearing_->
occupation(energy_difference_ratio);
318 return total_electrons;
329 auto bisect_for_fermi_energy = [&](
PlateauEdge edge) {
331 Real lower_bound = eigenvalue_minimum;
332 Real upper_bound = eigenvalue_maximum;
333 Real trial_fermi_energy = 0.0;
334 for (
int iteration = 0; iteration < 100; iteration++) {
335 trial_fermi_energy = 0.5 * (lower_bound + upper_bound);
336 Real total_electrons = count_electrons(trial_fermi_energy);
348 : total_electrons < number_of_electrons;
350 lower_bound = trial_fermi_energy;
352 upper_bound = trial_fermi_energy;
353 if (std::abs(upper_bound - lower_bound) <
357 return trial_fermi_energy;
402 if (fermi_energy_plateau_top - fermi_energy > smearing_width)
403 fermi_energy = 0.5 * (fermi_energy + fermi_energy_plateau_top);
408 for (
int kpoint_state_index = 0; kpoint_state_index < number_of_kpoint_states;
409 kpoint_state_index++) {
410 for (
int band_index = 0; band_index < number_of_bands; band_index++) {
411 Real energy_difference_ratio =
412 (quantum_system_->
eigenvalue(kpoint_state_index, band_index) - fermi_energy) /
415 kpoint_state_index, band_index, smearing_->
occupation(energy_difference_ratio));
425 [[nodiscard]] std::vector<Real> global_kpoint_state_table()
const {
428 const int values_per_state = number_of_bands + 1;
429 const KPointsGrid& grid = quantum_system_->
kpoint_grid();
431 std::vector<Real> table(
static_cast<std::size_t
>(number_of_kpoint_states) *
433 if (local_number_of_kpoints == 0) {
436 for (
int kpoint_state_index = 0;
437 local_number_of_kpoints > 0 && kpoint_state_index < number_of_kpoint_states;
438 kpoint_state_index++) {
439 const std::size_t row =
static_cast<std::size_t
>(kpoint_state_index) * values_per_state;
440 table[row] = grid[kpoint_state_index % local_number_of_kpoints].weight;
441 for (
int band_index = 0; band_index < number_of_bands; band_index++) {
442 table[row + 1 + band_index] =
443 quantum_system_->
eigenvalue(kpoint_state_index, band_index);
446 if (execution_policy_ ==
nullptr || execution_policy_->
number_of_pools() <= 1) {
456 table, local_number_of_kpoints, number_of_spin_blocks, values_per_state);
459 mutable bool has_warned_about_missing_band_edge_ =
false;
460 PlanewaveKohnShamQuantumSystem* quantum_system_ =
nullptr;
461 const SmearingScheme* smearing_ =
nullptr;
462 const execution::PlanewaveExecutionPolicy* execution_policy_ =
nullptr;