121 throw std::runtime_error(
"PlanewaveKohnShamSCFIterator::execution_policy(): null.");
138 log::debug(
"Planewave preload: Copying states from preloaded quantum system.");
143 log::debug(
"Planewave preload: preloaded_quantum_system_ is null.");
154 const auto grid_points =
160 if (spin_density.size() != grid_points) {
169 Real integrated_electrons = 0.0;
171 for (
const Real value : spin_density) {
172 integrated_electrons += value;
175 integrated_electrons *=
177 if (integrated_electrons <= 0.0) {
182 for (
Real& value : spin_density) {
192 log::debug(
"Planewave preload: Transforming real charge density to G-space.");
194 std::vector<Real> total_charge_density_real(grid_points, 0.0);
196 for (
int grid_index = 0; grid_index < grid_points; ++grid_index) {
197 total_charge_density_real[grid_index] +=
251 std::vector<Real> global_data =
258 eigenvalues_in_global_order.
raw() = std::move(global_data);
259 return eigenvalues_in_global_order;
418 std::optional<KPointsGrid> owned_kpoint_grid_;
476 bool use_gamma_only =
false;
480 if (grid_dims[0] == 1 && grid_dims[1] == 1 && grid_dims[2] == 1 &&
481 grid_shifts[0] == 0 && grid_shifts[1] == 0 && grid_shifts[2] == 0) {
482 use_gamma_only =
true;
483 log::info(
" Gamma-only optimization enabled (half-sphere G-vectors)");
493 effective_charge_density_cutoff,
498 log::info(
" Gamma-only disabled: this FFT layout separates a G-vector from its "
499 "negative, so the half sphere cannot be rebuilt locally");
500 use_gamma_only =
false;
504 effective_charge_density_cutoff,
533 if (saved_preloaded !=
nullptr) {
561 std::vector<Complex>& density_g) {
564 environment.
to_real_space = [
this](
const std::vector<Complex>& density_g,
565 std::vector<Real>& density_real) {
570 Real two_pi_over_lattice_parameter_squared =
581 environment.
global_reduce = [
this](std::vector<Real>& values) {
600 preconditioner_environment.
g_squared.assign(number_of_g_vectors, 0.0);
601 for (
int g_index = 0; g_index < number_of_g_vectors; ++g_index) {
602 preconditioner_environment.
g_squared[g_index] =
604 two_pi_over_lattice_parameter_squared;
622 if (dense_cutoff > smooth_cutoff + 1.0e-6) {
627 Real smooth_g_squared_max = smooth_cutoff / two_pi_over_lattice_parameter_squared;
628 int smooth_count = 0;
629 for (
int g_index = 0; g_index < number_of_g_vectors; ++g_index) {
631 smooth_g_squared_max) {
632 smooth_count = g_index + 1;
644 for (
int g_index = smooth_count; g_index < number_of_g_vectors; ++g_index) {
647 log::info(
" Dual-grid mixing: {} smooth / {} total G-vectors "
648 "(smooth cutoff = {:.0f} Ry, dense cutoff = {:.0f} Ry)",
659 mixer_->initialize(environment);
679 if (number_of_grid_points > 0) {
680 std::vector<Real> spin_averaged_potential(number_of_grid_points, 0.0);
683 for (
int grid_index = 0; grid_index < number_of_grid_points; ++grid_index) {
684 spin_averaged_potential[grid_index] +=
685 spin_potential[grid_index] / number_of_spin_comp;
696 for (
int spin_index = 0; spin_index < number_of_spin_comp; ++spin_index)
709 if (!local_ionic_potential.empty()) {
837 const std::vector<std::vector<Real>>& density_out)
const {
842 return density_residual;
857 const std::vector<std::vector<Real>>& density_out)
const {
863 Real density_residual = 0.0;
867 std::vector<Complex> input_density_g;
868 std::vector<Complex> output_density_g;
871 for (
int g_vector_index = first_nonzero_g_index; g_vector_index < residual_g_count;
873 Complex delta = output_density_g[g_vector_index] - input_density_g[g_vector_index];
875 density_residual += std::norm(delta) / g_squared * gamma_factor;
888 std::vector<Complex> input_up_g, input_down_g, output_up_g, output_down_g;
894 Real magnetization_residual = 0.0;
895 for (
int g_vector_index = 0; g_vector_index < residual_g_count; ++g_vector_index) {
896 Complex delta_up = output_up_g[g_vector_index] - input_up_g[g_vector_index];
897 Complex delta_down = output_down_g[g_vector_index] - input_down_g[g_vector_index];
898 Complex delta_total = delta_up + delta_down;
899 Complex delta_magnetization = delta_up - delta_down;
901 Real g_factor = (g_vector_index < first_nonzero_g_index) ? 1.0 : gamma_factor;
902 if (g_vector_index >= first_nonzero_g_index) {
904 density_residual += std::norm(delta_total) / g_squared * g_factor;
906 magnetization_residual += std::norm(delta_magnetization) * g_factor;
934 Real double_counting_correction = 0.0;
937 for (
int grid_index = 0; grid_index < local_grid_points; ++grid_index) {
943 double_counting_correction *= volume_per_grid_point;
947 return double_counting_correction;
959 Real delta_escf = 0.0;
967 for (
int grid_index = 0; grid_index < local_grid_points; ++grid_index) {
974 delta_escf *= volume_per_grid_point;
1008 Real total_magnetization_sum = 0.0;
1009 Real absolute_magnetization_sum = 0.0;
1011 for (
int grid_index = 0; grid_index < local_grid_points; ++grid_index) {
1014 total_magnetization_sum += spin_density;
1015 absolute_magnetization_sum += std::abs(spin_density);
1021 Real volume_element =
1055 const int local_number_of_kpoint_states =
1057 std::vector<Real> local_state_data(
static_cast<std::size_t
>(local_number_of_kpoint_states) *
1058 number_of_values_per_state,
1060 for (
int kpoint_index_state = 0; kpoint_index_state < local_number_of_kpoint_states;
1061 ++kpoint_index_state) {
1062 const int local_physical_kpoint_index =
1063 (local_number_of_kpoints > 0) ? (kpoint_index_state % local_number_of_kpoints) : 0;
1064 const std::size_t state_offset =
1065 static_cast<std::size_t
>(kpoint_index_state) * number_of_values_per_state;
1067 local_state_data[state_offset +
static_cast<std::size_t
>(band_index)] =
1070 const auto& kpoint =
kpoint_grid()[local_physical_kpoint_index];
1071 local_state_data[state_offset +
static_cast<std::size_t
>(
number_of_bands_)] =
1072 kpoint.coordinates.x;
1073 local_state_data[state_offset +
static_cast<std::size_t
>(
number_of_bands_) + 1] =
1074 kpoint.coordinates.y;
1075 local_state_data[state_offset +
static_cast<std::size_t
>(
number_of_bands_) + 2] =
1076 kpoint.coordinates.z;
1079 std::vector<Real> global_state_data = local_state_data;
1083 local_number_of_kpoints,
1085 number_of_values_per_state);
1088 const int global_number_of_kpoint_states =
1089 static_cast<int>(global_state_data.size()) / number_of_values_per_state;
1093 : global_number_of_kpoint_states;
1094 summary.
eigenvalues.resize(
static_cast<std::size_t
>(global_number_of_kpoint_states));
1096 for (
int kpoint_index_state = 0; kpoint_index_state < global_number_of_kpoint_states;
1097 ++kpoint_index_state) {
1098 const std::size_t state_offset =
1099 static_cast<std::size_t
>(kpoint_index_state) * number_of_values_per_state;
1100 summary.
eigenvalues[
static_cast<std::size_t
>(kpoint_index_state)].assign(
1101 global_state_data.begin() +
static_cast<std::ptrdiff_t
>(state_offset),
1102 global_state_data.begin() +
1104 const int global_physical_kpoint_index =
1107 summary.
kpoints[
static_cast<std::size_t
>(global_physical_kpoint_index)].coordinates = {
1108 global_state_data[state_offset +
static_cast<std::size_t
>(
number_of_bands_)],
1109 global_state_data[state_offset +
static_cast<std::size_t
>(
number_of_bands_) + 1],
1110 global_state_data[state_offset +
static_cast<std::size_t
>(
number_of_bands_) + 2]};
1206 log::warn(
"number_of_bands ({}) equals the number of occupied states ({}). "
1207 "This may cause SCF convergence issues. Consider adding extra empty "
1208 "bands (e.g., number_of_bands: {}).",
1210 number_of_occupied_bands,
1211 number_of_occupied_bands + 4);
1219 number_of_occupied_bands + 4);
1221 return number_of_occupied_bands + 4;
1241 int local_minimum_plane_waves =
1246 int global_minimum_plane_waves = local_minimum_plane_waves;
1248 if (global_minimum_plane_waves == 0) {
1249 log::warn(
"Band parallelism: some ranks received 0 plane waves. "
1250 "Falling back to replicated G-vectors.");
1315 const std::vector<Real> kpoint_state_probe(
1316 static_cast<std::size_t
>(local_kpoint_states), 0.0);
1317 const std::size_t global_kpoint_states =
1330 number_of_plane_waves_vector.empty()
1332 : *std::max_element(number_of_plane_waves_vector.begin(),
1333 number_of_plane_waves_vector.end());
1384 Real total_charge = 0.0;
1386 for (
int grid_index = 0; grid_index < local_grid_points; ++grid_index) {
1393 log::warn(
" Total charge {:.4f} deviates from target {} by >{:.1f}e",
Real two_pi_over_lattice_parameter_squared() const
Real valence_charge(int species_index) const
void compute_derived_quantities()
int number_of_atoms() const
Real lattice_parameter() const
virtual int total_grid_points() const =0
virtual int grid_dimension_1() const =0
virtual bool co_locates_mirrors() const
virtual int grid_dimension_3() const =0
virtual int grid_dimension_2() const =0
virtual int local_grid_points() const =0
int number_of_kpoints() const
const std::array< int, 3 > & grid_dimensions() const
const std::array< int, 3 > & grid_shifts() const
virtual void prepare_hamiltonian()
const KohnShamSCFIteratorConfig & config() const
std::unique_ptr< XCFunctional > exchange_correlation_
Real diagonalization_threshold() const
std::vector< Complex > total_potential_g_
std::unique_ptr< Mixer > mixer_
std::unique_ptr< SmearingScheme > smearing_scheme_
Crystal & crystal_structure() const
Real diagonalizer_tolerance_
std::vector< std::vector< Real > > charge_density_real_
virtual const KohnShamEnergies & energies() const =0
bool has_preloaded_effective_potential_
std::vector< Complex > charge_density_g_
KohnShamSCFIteratorConfig config_
virtual KohnShamEnergies & energies_mutable()=0
std::vector< std::vector< Real > > real_space_potential_
std::string mixing_preconditioning_
std::vector< std::vector< Complex > > total_potential_g_by_spin_
std::vector< T > & raw()
Returns raw vector (useful for MPI calls that need contiguous block)
int number_of_g_vectors() const
void rho_g_to_r(const std::vector< Complex > &charge_density_g_space, std::vector< Real > &charge_density_real_space) const
const std::vector< GVector > & g_vectors() const
void initialize_kpoint_data(const Crystal &crystal_structure, const KPointsGrid &kpoint_grid, int band_size=1)
void rho_r_to_g(const std::vector< Real > &charge_density_real_space, std::vector< Complex > &charge_density_g_space) const
int first_nonzero_g_index() const
const std::vector< int > & g_vector_indices_by_kpoint_index(int kpoint_index) const
const std::vector< std::vector< int > > & kpoint_g_vector_indices() const
int number_of_plane_waves_by_kpoint_index(int kpoint_index) const
void generate(const FFTGridFactory &fft_grid_factory, const Crystal &crystal_structure, Real wavefunction_energy_cutoff, Real charge_density_energy_cutoff, bool is_gamma_only=false, bool use_measured_fft_planning=true, bool use_exact_wavefunction_grid=false)
const std::vector< std::vector< Real > > & kpoint_kinetic_energy_squared() const
const std::vector< int > & kpoint_number_of_plane_waves() const
bool is_gamma_only() const
int number_of_shells() const
void symmetrize(const PlanewaveBasis &planewave_basis, std::vector< std::vector< Real > > &charge_density_real, int number_of_spin_components) const
void build_shells(const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis)
Precompute the symmetry operations and G-vector shells for the given cell/basis.
int number_of_symmetry_operations() const
bool is_initialized() const
Planewave Fermi energy calculator — operates on PlanewaveKohnShamQuantumSystem.
Real calculate_occupations() override
Compute occupations and return the Fermi energy.
void copy_quantum_states_from(const QuantumSystem< PlanewaveKohnShamQuantumState > &source) override
Real spin_degeneracy() const
Complex wavefunction(int kpoint_index, int band_index, int g_vector_index) const
Matrix< Real > eigenvalues_matrix() const
Real occupation_by_kpoint_state_index_and_band_index(int kpoint_state_index, int band_index) const
void allocate_electronic_states(const std::vector< int > &number_of_plane_waves_per_kpoint)
int number_of_occupied_bands_per_spin() const
Real eigenvalue(int kpoint_state_index, int band_index) const
PlanewaveKohnShamEnergies & planewave_energies_mutable()
const std::vector< std::vector< int > > & g_vector_indices() const
const std::optional< Magnetization > & magnetization() const
PlanewaveBasis planewave_basis_
const PlanewaveKohnShamQuantumSystem & quantum_system() const
const KPointsGrid & kpoint_grid() const
Real compute_density_residual(const std::vector< std::vector< Real > > &input_density_for_mixing) const override
virtual void release_scf_workspace()
Real number_of_electrons() const override
void initiate_mixer() override
std::string diagonalization_method_
int number_of_kpoint_states() const override
PlanewaveKohnShamQuantumSystem quantum_system_
Real charge_density_energy_cutoff_
void set_owned_execution_policy(std::unique_ptr< execution::PlanewaveExecutionPolicy > policy)
const KPointsGrid * kpoint_grid_
void restore_density_at_convergence() override
Real effective_charge_density_energy_cutoff() const
Matrix< Real > eigenvalues() const
std::unique_ptr< PlanewaveChargeDensityAccumulator > charge_density_accumulator_
Accumulates real-space charge density from wavefunctions each SCF iteration.
void set_diagonalization_tolerance(int iteration) override
void build_kpoint_plane_wave_data()
const execution::PlanewaveExecutionPolicy & execution_policy() const
void set_diagonalizer(std::unique_ptr< PlanewaveDiagonalizer > diagonalizer)
void compute_energy_components() override
Real hartree_metric_prefactor() const
void update_occupations() override
std::unique_ptr< execution::PlanewaveExecutionPolicy > owned_execution_policy_
const std::vector< std::vector< Real > > & kinetic_energy_squared() const
Real compute_double_counting_correction() const override
void warn_if_total_charge_deviates() const
void allocate_electronic_states()
Real magnetization_metric_weight() const
PlanewaveFermiEnergyCalculator fermi_energy_calculator_
~PlanewaveKohnShamSCFIterator() override=default
bool replicated_plane_wave_fallback_active_
Real compute_residual_between(const std::vector< std::vector< Real > > &density_in, const std::vector< std::vector< Real > > &density_out) const
void set_diagonalizer_tolerance(Real tolerance) override
void construct_density_from_states() override
void scale_loaded_charge_density_to_electron_count()
Rescale the loaded density so it integrates to this system's electron count.
std::unique_ptr< PlanewaveDiagonalizer > diagonalizer_
virtual void on_charge_density_initialized()
bool report_unconverged_bands_
std::vector< std::vector< Real > > input_density_for_mixing() const override
int number_of_occupied_bands_per_spin() const
const std::vector< int > & number_of_plane_waves() const
const PlanewaveKohnShamEnergies & planewave_energies() const
std::unique_ptr< PlanewaveKohnShamEnergyCalculator > energy_calculator_
Computes band energy and smearing entropy from the occupied Kohn-Sham states.
virtual void compute_effective_potential_from_density()=0
const PlanewaveBasis & planewave_basis() const
std::optional< int > nscf_override_maximum_iterations_
int number_of_plane_waves(int kpoint_index) const
PlanewaveKohnShamSCFIterator(KohnShamSCFIteratorConfig config)
void log_representation_summary() const
virtual NormConservingPlanewaveKohnShamSCFState result() const =0
int number_of_smooth_g_vectors_
std::optional< Real > nscf_override_tolerance_
void mix_density(const std::vector< std::vector< Real > > &input_density_for_mixing) override
std::vector< std::vector< Real > > convergence_output_density_
Converged output density snapshot, restored once self-consistency is reached.
std::optional< Magnetization > magnetization_
Set by report_convergence_details(); see magnetization() above.
void symmetrize_density() override
Matrix< Real > global_eigenvalues() const
Real compute_delta_escf_correction() const override
bool loaded_charge_density_fits_grid() const
Real number_of_electrons_
void update_effective_potential() override
void load_quantum_system(const PlanewaveKohnShamQuantumSystem &system)
std::vector< Real > cached_local_ionic_potential_real_
Cached real-space ionic local potential (constant during SCF, avoids repeated FFTs).
virtual const std::vector< Complex > & local_ionic_potential_g() const =0
PlanewaveChargeDensitySymmetrizer density_symmetrizer_
void preload_single_particle_states()
void set_owned_kpoint_grid(KPointsGrid kpoint_grid)
Transfer ownership of a KPointsGrid into this iterator.
Real eigenvalue(int kpoint_index, int band_index) const
void report_convergence_details() override
void initialize_representation() override
execution::PlanewaveExecutionPolicy * execution_policy_
Complex wavefunction(int kpoint_index, int band_index, int g_vector_index) const
Real wavefunction_energy_cutoff_
virtual const std::vector< Complex > & core_charge_density_g() const =0
void report_first_iteration_details() override
Real spin_degeneracy() const
int resolve_number_of_bands() const
void preload_charge_density()
int number_of_bands() const override
Real compute_number_of_electrons() const
const std::vector< int > & g_vector_indices(int kpoint_index) const
Real occupation(int kpoint_index, int band_index) const
Real compute_local_residual_between(const std::vector< std::vector< Real > > &density_in, const std::vector< std::vector< Real > > &density_out) const
void snapshot_output_density() override
static void report_convergence_summary(const PlanewaveKohnShamSCFConvergenceSummary &summary)
static void report_first_iteration(Real total_energy, const PlanewaveKohnShamEnergies &energies)
static void report_setup_summary(const PlanewaveKohnShamSCFSetupSummary &summary)
const QuantumSystem< QuantumStateType > * preloaded_quantum_system_
void load_quantum_system(const QuantumSystem< QuantumStateType > &system)
int number_of_spin_components() const
virtual int number_of_processes() const =0
virtual int band_distribution_size() const
virtual bool is_root() const =0
virtual void reduce_within_group(ComplexValue *data, int number_of_elements) const =0
virtual void disable_band_distribution()
virtual void reduce_scalar_max(int &value) const =0
virtual void reduce_band_group_scalar_min(int &value) const =0
virtual void reduce_charge_density(std::vector< std::vector< Real > > &charge_density) const =0
virtual void reduce_within_pool_vector(std::vector< Real > &values) const =0
virtual int number_of_pools() const =0
virtual std::vector< Real > assemble_global_kpoint_state_data(const std::vector< Real > &local_data, int local_number_of_physical_kpoints, int number_of_spin_components, int number_of_values_per_state) const =0
virtual void reduce_within_pool_scalar(Real &value) const =0
virtual void reduce_scalar(Real &value) const =0
std::unique_ptr< Preconditioner > create(const std::string &name, const PreconditionerEnvironment &environment)
Create a residual preconditioner by name.
constexpr Real electron_charge_squared_rydberg
void debug(spdlog::format_string_t< Args... > fmt, Args &&... args)
void warn(spdlog::format_string_t< Args... > fmt, Args &&... args)
void info(spdlog::format_string_t< Args... > fmt, Args &&... args)
constexpr double smearing_width_threshold
constexpr double initial_eigenvalue_tolerance
Real compute_ewald(const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis)
std::complex< double > Complex
std::unique_ptr< PlanewaveDiagonalizer > create_planewave_diagonalizer(const std::string &name, const execution::ExecutionPolicy &execution_policy=PlanewaveDavidsonDiagonalizer::default_serial_execution_policy)
const KPointsGrid * kpoint_grid
int davidson_basis_expansion_factor
Real wavefunction_energy_cutoff
std::string diagonalization_method
bool use_exact_wavefunction_grid
Real charge_density_energy_cutoff
const execution::ExecutionPolicy * execution_policy
bool use_deterministic_fft_planning
int first_nonzero_g_index
Real magnetization_metric_weight
std::function< void(const std::vector< Complex > &, std::vector< Real > &)> to_real_space
int number_of_smooth_g_vectors
std::shared_ptr< Preconditioner > preconditioner
std::function< void(const std::vector< Real > &, std::vector< Complex > &)> to_reciprocal_space
std::function< void(std::vector< Real > &)> global_reduce
std::vector< Real > charge_metric_weights
Real smearing_entropy_contribution
int number_of_kpoints
The reporting rank's pool share under MPI, not the irreducible total.
std::vector< std::vector< Real > > eigenvalues
eigenvalues[kpoint_state_index][band_index] in Rydberg.
const PlanewaveKohnShamEnergies * energies
Real total_magnetization
Globally integrated spin-density difference in Bohr magnetons per cell.
Real absolute_magnetization
Globally integrated absolute spin-density difference in Bohr magnetons per cell.
int number_of_spin_components
int number_of_kpoint_pools
Set from the execution policy so the report can say whose k-points it lists.
std::vector< KPoint > kpoints
k-points in the same order as eigenvalues rows.
int fft_rho_grid_dimension_2
std::string diagonalizer_name
int davidson_basis_expansion_factor
Davidson working basis as a multiple of the band count; 0 when not iterative.
bool bands_are_distributed
True when those ranks divide the bands rather than the reciprocal-space coefficients.
int fft_rho_grid_dimension_3
int max_plane_waves_per_kpoint
int fft_rho_grid_dimension_1
int number_of_spin_components
int band_group_size
Ranks sharing a k-point pool, however that pool divides its work.
Real charge_density_energy_cutoff
bool density_symmetrization_active
int fft_wave_grid_dimension_3
int number_of_kpoint_pools
int number_of_symmetry_operations
int fft_wave_grid_dimension_2
std::string xc_functional_name
int fft_wave_grid_dimension_1
Real wavefunction_energy_cutoff
bool replicated_plane_wave_fallback_active
Everything any preconditioner needs from the surrounding representation.
int first_nonzero_g_index
Real q0_squared
Screening wavevector squared / vacuum floor, in bohr⁻².
std::vector< Real > g_squared
|G|² per G-vector, in bohr⁻².
std::function< void(const std::vector< Real > &, std::vector< Complex > &)> to_reciprocal_space
std::function< void(std::vector< Real > &)> global_reduce
Sums real-space integrals over the ranks sharing this pool's grid.
std::function< void(const std::vector< Complex > &, std::vector< Real > &)> to_real_space
void log_resident_checkpoint(const std::string &label)
static ProfilingCounters & instance()
void release_unused_heap(const std::string &label)