24#include "generated/esse/properties/scalar/magnetization_absolute.hpp"
25#include "generated/esse/properties/scalar/magnetization_total.hpp"
26#include "generated/esse/properties/scalar/pressure.hpp"
27#include "generated/esse/properties/workflow/convergence_electronic.hpp"
51 : iterator_factory_(std::move(iterator_factory)) {}
55 std::unique_ptr<PlanewaveKohnShamSCFIterator> owned_iterator =
58 auto* planewave_iterator =
60 if (!planewave_iterator) {
61 throw std::runtime_error(
62 "SelfConsistentFieldFlavor: expected a planewave pseudopotential iterator.");
65 offer_saved_charge_density(*iterator, input);
66 offer_saved_wavefunctions(*planewave_iterator, input);
71 auto scf_state = iterator->
result();
90 planewave_iterator->execution_policy(),
91 [&scf_state, &input, planewave_iterator] {
92 auto record = build_mean_field_record(scf_state);
93 record.exchange_correlation_potential_g =
94 planewave_iterator->valence_exchange_correlation_potential_g_of(
95 scf_state.charge_density_real);
96 if (input.mean_field_export_kleinman_bylander_projectors)
97 add_kleinman_bylander_projectors_to_mean_field_record(record, scf_state);
104 generated::Results results =
new_results(
"self_consistent_field");
105 results.runtime_log.converged = converged;
106 results.runtime_log.self_consistent_field_iterations = iterator->
iteration();
122 nlohmann::json timings = counters.davidson_timing.as_map();
123 timings[
"fft_calls"] =
static_cast<double>(counters.fft_call_count.load());
124 timings[
"davidson_iterations"] =
125 static_cast<double>(counters.davidson_iteration_count.load());
126 timings[
"hamiltonian_applications"] =
127 static_cast<double>(counters.hamiltonian_application_count.load());
128 if (!timings.empty()) {
129 results.runtime_log.timings_ms = timings;
137 const auto& energies = scf_state.energies();
139 set_fermi_energy(results, planewave_iterator->quantum_system().fermi_energy());
150 results, planewave_iterator->hubbard_occupation_traces(), scf_state.hubbard_manifolds);
159 auto correction_calculator =
161 auto correction = correction_calculator->compute(scf_state);
162 if (correction.applied) {
163 generated::ResultsIsolatedSystemCorrection block;
164 block.method = correction.method;
165 for (
const auto& [label, value_in_ry] : correction.terms_in_ry) {
166 block.terms[label] =
energy(label, value_in_ry);
168 block.total =
energy(
"total", correction.total_in_ry());
169 block.corrected_total_energy =
170 energy(
"corrected_total_energy", energies.total() + correction.total_in_ry());
171 results.properties.isolated_system_correction = block;
172 log::info(
"{} isolated-system correction:", correction.method);
173 for (
const auto& [label, value] : correction.terms_in_ry) {
174 log::info(
" {:<24} = {:.8f} Ry", label, value);
177 "corrected total energy",
178 energies.total() + correction.total_in_ry());
187 planewave_iterator->release_scf_workspace();
191 auto timer_forces_start = std::chrono::high_resolution_clock::now();
194 auto timer_forces_end = std::chrono::high_resolution_clock::now();
212 "no DFT+U stress term is implemented for Hubbard projector type '" +
213 scf_state.hubbard_projector_type +
"'");
223 auto timer_stress_start = std::chrono::high_resolution_clock::now();
225 auto [stress_total, stress_contributions] =
234 " Stress tensor skipped (not yet supported with G-vector distribution)");
237 "stress is not implemented when wavefunctions are distributed across an "
238 "MPI band group, so the reported zero tensor is not a physical result");
240 auto timer_stress_end = std::chrono::high_resolution_clock::now();
244 " forces: {:.1f} ms",
245 std::chrono::duration<double, std::milli>(timer_forces_end - timer_forces_start)
248 " stress: {:.1f} ms",
249 std::chrono::duration<double, std::milli>(timer_stress_end - timer_stress_start)
253 "SelfConsistentFieldFlavor: skipping forces and stress — SCF did not converge");
275 template <
typename History>
277 if (history.empty()) {
280 std::vector<double> energies_in_ry;
281 std::vector<double> density_residuals;
282 for (
const auto& iteration : history) {
283 energies_in_ry.push_back(iteration.total_energy_in_ry);
284 density_residuals.push_back(iteration.density_residual);
287 generated::ConvergenceElectronic{
288 generated::ConvergenceElectronicNameEnum::CONVERGENCE_ELECTRONIC,
289 generated::ConvergenceElectronicUnitsEnum::RY,
291 results.runtime_log.density_residuals = density_residuals;
304 const std::optional<Magnetization>& magnetization) {
305 if (!magnetization) {
309 generated::MagnetizationTotal{
310 generated::MagnetizationTotalNameEnum::MAGNETIZATION_TOTAL,
311 generated::MagnetizationTotalUnitsEnum::UB,
312 magnetization->total_in_bohr_magneton});
314 generated::MagnetizationAbsolute{
315 generated::MagnetizationAbsoluteNameEnum::MAGNETIZATION_ABSOLUTE,
316 generated::MagnetizationAbsoluteUnitsEnum::UB,
317 magnetization->absolute_in_bohr_magneton});
326 template <
typename Manifolds>
328 const std::vector<std::vector<Real>>& traces,
329 const Manifolds& manifolds) {
330 std::vector<generated::ResultsPropertiesHubbardOccupationsItem> occupations;
331 for (std::size_t manifold_index = 0;
332 manifold_index < traces.size() && manifold_index < manifolds.size();
334 occupations.push_back({manifolds[manifold_index].atom_index,
335 std::vector<double>(traces[manifold_index].begin(),
336 traces[manifold_index].end())});
338 if (!occupations.empty()) {
339 results.properties.hubbard_occupations = occupations;
345 const std::vector<Vector3D>& total_forces,
348 results.properties.force_contributions = std::map<std::string, generated::AtomicForces>{
369 generated::Pressure{generated::PressureNameEnum::PRESSURE,
370 generated::PressureUnitsEnum::KBAR,
372 results.properties.stress_contributions = std::map<std::string, generated::StressTensor>{
393 auto& incomplete = results.runtime_log.incomplete;
395 incomplete.emplace();
397 incomplete->stress = incomplete->stress ? *incomplete->stress +
"; " + reason : reason;
409 const std::string checkpoint_path =
414 log::info(
" No saved density at '{}'; starting from overlapping atoms",
418 log::info(
" Starting from the density saved at '{}'", checkpoint_path);
429 static void offer_saved_wavefunctions(PlanewavePseudopotentialKohnShamSCFIterator& iterator,
430 const InputData& input) {
431 iterator.offer_starting_wavefunctions(input.output_directory +
"/" + input.prefix +
static bool supports(const NormConservingPlanewaveKohnShamSCFState &scf_state)
void offer_starting_charge_density(std::vector< std::vector< Real > > charge_density)
const std::vector< ScfIterationRecord > & scf_history() const
const execution::PlanewaveExecutionPolicy & execution_policy() const
virtual NormConservingPlanewaveKohnShamSCFState result() const =0
bool load(const std::string &filename, KohnShamQuantumSystemSnapshot &snapshot) const override
bool is_converged() const
static void record_stress(generated::Results &results, const StressTensorTotal &total, const StressTensorContributions &contributions)
generated::Results run_calculation(const InputData &input) override
static void record_incomplete_stress(generated::Results &results, const std::string &reason)
static void record_convergence(generated::Results &results, const History &history)
static void record_forces(generated::Results &results, const std::vector< Vector3D > &total_forces, const AtomicForcesContributions &contributions)
ESSE's atomic_forces record, and beside it the share of each mechanism, in Ry/bohr.
static void record_hubbard_occupations(generated::Results &results, const std::vector< std::vector< Real > > &traces, const Manifolds &manifolds)
SelfConsistentFieldFlavor(IteratorFactory iterator_factory={})
static void record_magnetization(generated::Results &results, const std::optional< Magnetization > &magnetization)
generated::ResultsExecution execution_record() const
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)
generated::EigenvaluesAtKpoints assemble_global_eigenvalues_at_kpoints(const PlanewavePseudopotentialKohnShamSCFIterator &iterator)
void set_record(generated::Results &results, const Record &record)
List record in the results, in place of any record of the same name.
generated::Results new_results(const std::string &flavor)
std::pair< StressTensorTotal, StressTensorContributions > compute_stress(const NormConservingPlanewaveKohnShamSCFState &scf_state, const std::string &nonlocal_stress_derivative_method="analytical", const std::string &dispersion_correction="none", bool disable_symmetry=false)
void export_and_record_mean_field_under_topology(const execution::PlanewaveExecutionPolicy &execution_policy, RecordBuilder &&build_record, const InputData &input, const std::string &directory)
generated::AtomicForces atomic_forces(const std::vector< Vector3D > &values_in_ry_per_bohr)
Forces in Ry/bohr as ESSE's atomic_forces, atoms by 1-based id: the total or one contribution.
generated::Energy energy(const std::string &name, Real value_in_ry)
One of Q3's energies that ESSE has no property for, as an ESSE energy in rydberg.
void set_fermi_energy(generated::Results &results, Real fermi_energy_in_ry)
ESSE's fermi_energy record, in rydberg.
std::unique_ptr< IsolatedSystemCorrectionCalculator > create_isolated_system_correction_calculator(const std::string &isolated_system_correction)
std::function< std::unique_ptr< PlanewaveKohnShamSCFIterator >(const InputData &)> IteratorFactory
generated::StressTensor stress_tensor(const Real(&values_in_kbar)[3][3])
A stress in kbar as ESSE's stress_tensor: the total or one contribution to it.
std::unique_ptr< PlanewaveKohnShamSCFIterator > create_iterator(const InputData &input)
void set_energy_records(generated::Results &results, Real total_energy_in_ry, const PlanewaveKohnShamEnergies &energies)
Per-contribution force breakdown (Ry/bohr).
std::vector< Vector3D > nonlocal
std::vector< Vector3D > local
std::vector< Vector3D > augmentation
std::vector< Vector3D > dispersion
std::vector< Vector3D > hubbard
std::vector< Vector3D > core_correction
std::vector< Vector3D > ewald
static std::pair< std::vector< Vector3D >, AtomicForcesContributions > compute_forces(const NormConservingPlanewaveKohnShamSCFState &scf_state, const std::string &dispersion_correction="none", bool disable_symmetry=false)
std::vector< std::vector< Real > > charge_density_real
static ProfilingCounters & instance()
Per-contribution stress tensor breakdown (kbar after conversion).
Real core_correction[3][3]
Real exchange_correlation[3][3]