45 : basis_(basis), kpoint_grid_(kpoint_grid), quantum_system_(quantum_system) {}
65 int number_of_spin_components,
66 Real inverse_cell_volume,
67 std::vector<std::vector<Real>>& charge_density_out)
const {
83 const bool density_grid_is_split =
88 if (use_smooth_grid) {
89 accumulate_on_smooth_grid(number_of_bands,
90 number_of_spin_components,
94 accumulate_on_rho_grid(number_of_bands,
95 number_of_spin_components,
111 [[nodiscard]]
int first_mirrored_index()
const {
121 void scatter_wavefunction_to_grid(
const Complex* band_data,
123 const std::vector<int>& g_to_mapping,
124 const std::vector<int>& minus_g_to_mapping,
125 bool scatter_minus_g,
126 std::vector<Complex>& workspace)
const {
127 std::fill(workspace.begin(), workspace.end(),
Complex(0.0, 0.0));
128 for (
int g_index = 0; g_index < plane_waves; ++g_index) {
129 int fft_index = g_to_mapping[g_index];
130 if (fft_index >= 0) {
131 workspace[
static_cast<size_t>(fft_index)] = band_data[g_index];
134 if (scatter_minus_g) {
135 for (
int g_index = first_mirrored_index(); g_index < plane_waves; ++g_index) {
136 int minus_fft_index = minus_g_to_mapping[g_index];
137 if (minus_fft_index >= 0) {
138 workspace[
static_cast<size_t>(minus_fft_index)] = std::conj(band_data[g_index]);
152 void scatter_two_bands_gamma(
const Complex* band_data_1,
155 const std::vector<int>& g_to_mapping,
156 const std::vector<int>& minus_g_to_mapping,
157 std::vector<Complex>& workspace)
const {
158 std::fill(workspace.begin(), workspace.end(),
Complex(0.0, 0.0));
159 if (band_data_2 !=
nullptr) {
160 for (
int g_index = 0; g_index < plane_waves; ++g_index) {
161 int fft_index = g_to_mapping[g_index];
162 if (fft_index >= 0) {
163 workspace[
static_cast<size_t>(fft_index)] =
164 band_data_1[g_index] +
Complex(0.0, 1.0) * band_data_2[g_index];
167 for (
int g_index = first_mirrored_index(); g_index < plane_waves; ++g_index) {
168 int minus_fft_index = minus_g_to_mapping[g_index];
169 if (minus_fft_index >= 0) {
170 workspace[
static_cast<size_t>(minus_fft_index)] =
171 std::conj(band_data_1[g_index]) +
172 Complex(0.0, 1.0) * std::conj(band_data_2[g_index]);
177 for (
int g_index = 0; g_index < plane_waves; ++g_index) {
178 int fft_index = g_to_mapping[g_index];
179 if (fft_index >= 0) {
180 workspace[
static_cast<size_t>(fft_index)] = band_data_1[g_index];
183 for (
int g_index = first_mirrored_index(); g_index < plane_waves; ++g_index) {
184 int minus_fft_index = minus_g_to_mapping[g_index];
185 if (minus_fft_index >= 0) {
186 workspace[
static_cast<size_t>(minus_fft_index)] =
187 std::conj(band_data_1[g_index]);
220 template <
typename BandIterationBody>
221 void accumulate_bands_into(
int number_of_iterations,
222 const FFTGrid& fft_grid,
223 int workspace_points,
224 int local_grid_points,
226 BandIterationBody accumulate_band_iteration)
const {
227 int transform_slots = fft_grid.concurrent_transform_slots();
229 (transform_slots < 2)
231 : std::min(transform_slots,
234 std::vector<std::vector<Real>> partial_densities;
235 if (band_threads > 1) {
236 partial_densities.assign(
237 static_cast<std::size_t
>(band_threads - 1),
238 std::vector<Real>(
static_cast<std::size_t
>(local_grid_points), 0.0));
241#pragma omp parallel num_threads(band_threads) if (band_threads > 1)
244 Real* density_target =
245 (transform_slot == 0)
247 : partial_densities[static_cast<std::size_t>(transform_slot - 1)].data();
248 std::vector<Complex> workspace(
static_cast<std::size_t
>(workspace_points));
249 long long first_iteration = 0;
250 long long last_iteration = number_of_iterations;
256 for (
long long iteration = first_iteration; iteration < last_iteration; ++iteration) {
257 accumulate_band_iteration(
258 static_cast<int>(iteration), workspace, density_target, transform_slot);
267 for (
const std::vector<Real>& partial_density : partial_densities) {
268 for (
int grid_index = 0; grid_index < local_grid_points; ++grid_index) {
269 density_out[grid_index] += partial_density[
static_cast<std::size_t
>(grid_index)];
280 void accumulate_on_rho_grid(
int number_of_bands,
281 int number_of_spin_components,
282 Real inverse_cell_volume,
283 std::vector<std::vector<Real>>& charge_density_out)
const {
286 int number_of_kpoint_states = number_of_kpoints * number_of_spin_components;
288 charge_density_out.resize(
static_cast<std::size_t
>(number_of_spin_components));
289 for (
int spin_index = 0; spin_index < number_of_spin_components; ++spin_index) {
290 charge_density_out[
static_cast<std::size_t
>(spin_index)].assign(
291 static_cast<std::size_t
>(local_grid_points), 0.0);
305 int fft_workspace_points =
308 for (
int kpoint_index_state = 0; kpoint_index_state < number_of_kpoint_states;
309 ++kpoint_index_state) {
310 int spin_index = kpoint_index_state / number_of_kpoints;
311 int kpoint_index = kpoint_index_state % number_of_kpoints;
312 Real kpoint_weight = kpoint_grid_[kpoint_index].weight;
317 std::vector<int> g_to_fft_mapping(plane_waves_at_kpoint, -1);
318 for (
int g_index = 0; g_index < plane_waves_at_kpoint; ++g_index) {
319 const auto& g_vector =
320 basis_.
g_vectors()[kpoint_g_indices[
static_cast<std::size_t
>(g_index)]];
321 int miller_x =
static_cast<int>(std::round(g_vector.miller.x));
322 int miller_y =
static_cast<int>(std::round(g_vector.miller.y));
323 int miller_z =
static_cast<int>(std::round(g_vector.miller.z));
324 if (std::abs(miller_x) <= fft_dimension_1 / 2 &&
325 std::abs(miller_y) <= fft_dimension_2 / 2 &&
326 std::abs(miller_z) <= fft_dimension_3 / 2) {
327 g_to_fft_mapping[g_index] =
333 std::vector<int> minus_g_to_fft_mapping;
335 minus_g_to_fft_mapping.resize(plane_waves_at_kpoint, -1);
336 for (
int g_index = 0; g_index < plane_waves_at_kpoint; ++g_index) {
337 const auto& g_vector =
338 basis_.
g_vectors()[kpoint_g_indices[
static_cast<std::size_t
>(g_index)]];
339 int miller_x =
static_cast<int>(std::round(g_vector.miller.x));
340 int miller_y =
static_cast<int>(std::round(g_vector.miller.y));
341 int miller_z =
static_cast<int>(std::round(g_vector.miller.z));
343 -miller_x, -miller_y, -miller_z);
347 auto* density_out_ptr = charge_density_out[
static_cast<std::size_t
>(spin_index)].data();
354 std::vector<int> occupied_band_indices;
355 for (
int band_index = 0; band_index < number_of_bands; ++band_index) {
358 kpoint_index_state, band_index);
360 occupied_band_indices.push_back(band_index);
364 int number_of_band_pairs =
static_cast<int>((occupied_band_indices.size() + 1) / 2);
365 accumulate_bands_into(
366 number_of_band_pairs,
368 fft_workspace_points,
372 std::vector<Complex>& fft_workspace,
373 Real* density_target,
374 int transform_slot) {
375 std::size_t pair_start = static_cast<std::size_t>(pair_index) * 2;
376 int band_index_1 = occupied_band_indices[pair_start];
377 bool has_second_band = (pair_start + 1 < occupied_band_indices.size());
379 has_second_band ? occupied_band_indices[pair_start + 1] : -1;
381 const Complex* band_data_1 = quantum_system_.wavefunction_band_data(
382 kpoint_index_state, band_index_1);
383 const Complex* band_data_2 = has_second_band
384 ? quantum_system_.wavefunction_band_data(
385 kpoint_index_state, band_index_2)
389 quantum_system_.occupation_by_kpoint_state_index_and_band_index(
390 kpoint_index_state, band_index_1);
393 ? quantum_system_.occupation_by_kpoint_state_index_and_band_index(
394 kpoint_index_state, band_index_2)
397 scatter_two_bands_gamma(band_data_1,
399 plane_waves_at_kpoint,
401 minus_g_to_fft_mapping,
403 basis_.fft_rho().inverse_raw_on_slot(fft_workspace.data(), transform_slot);
406 Real weight_1 = kpoint_weight * occupation_1 * inverse_cell_volume;
407 Real weight_2 = kpoint_weight * occupation_2 * inverse_cell_volume;
408 for (int grid_index = 0; grid_index < local_grid_points; ++grid_index) {
409 Real psi1 = fft_workspace[static_cast<size_t>(grid_index)].real();
410 Real psi2 = fft_workspace[static_cast<size_t>(grid_index)].imag();
411 density_target[grid_index] +=
412 weight_1 * psi1 * psi1 + weight_2 * psi2 * psi2;
417 accumulate_bands_into(
420 fft_workspace_points,
424 std::vector<Complex>& fft_workspace,
425 Real* density_target,
426 int transform_slot) {
428 quantum_system_.occupation_by_kpoint_state_index_and_band_index(
429 kpoint_index_state, band_index);
430 if (occupation < math::tolerances::occupation_threshold) {
436 scatter_wavefunction_to_grid(band_data,
437 plane_waves_at_kpoint,
439 minus_g_to_fft_mapping,
444 Real weight_factor = kpoint_weight * occupation * inverse_cell_volume;
445 for (
int grid_index = 0; grid_index < local_grid_points; ++grid_index) {
446 density_target[grid_index] +=
448 std::norm(fft_workspace[
static_cast<std::size_t
>(grid_index)]);
468 void accumulate_on_smooth_grid(
int number_of_bands,
469 int number_of_spin_components,
470 Real inverse_cell_volume,
471 std::vector<std::vector<Real>>& charge_density_out)
const {
477 int number_of_kpoint_states = number_of_kpoints * number_of_spin_components;
485 std::vector<std::vector<Real>> smooth_density(
486 static_cast<std::size_t
>(number_of_spin_components));
487 for (
int spin_index = 0; spin_index < number_of_spin_components; ++spin_index) {
488 smooth_density[
static_cast<std::size_t
>(spin_index)].assign(
489 static_cast<std::size_t
>(smooth_local_points), 0.0);
493 int fft_workspace_points =
496 for (
int kpoint_index_state = 0; kpoint_index_state < number_of_kpoint_states;
497 ++kpoint_index_state) {
498 int spin_index = kpoint_index_state / number_of_kpoints;
499 int kpoint_index = kpoint_index_state % number_of_kpoints;
500 Real kpoint_weight = kpoint_grid_[kpoint_index].weight;
505 std::vector<int> g_to_smooth_mapping(plane_waves_at_kpoint, -1);
506 for (
int g_index = 0; g_index < plane_waves_at_kpoint; ++g_index) {
507 const auto& g_vector =
508 basis_.
g_vectors()[kpoint_g_indices[
static_cast<std::size_t
>(g_index)]];
509 int miller_x =
static_cast<int>(std::round(g_vector.miller.x));
510 int miller_y =
static_cast<int>(std::round(g_vector.miller.y));
511 int miller_z =
static_cast<int>(std::round(g_vector.miller.z));
512 if (std::abs(miller_x) <= wave_dim_1 / 2 && std::abs(miller_y) <= wave_dim_2 / 2 &&
513 std::abs(miller_z) <= wave_dim_3 / 2) {
514 g_to_smooth_mapping[g_index] =
520 std::vector<int> minus_g_to_smooth_mapping;
522 minus_g_to_smooth_mapping.resize(plane_waves_at_kpoint, -1);
523 for (
int g_index = 0; g_index < plane_waves_at_kpoint; ++g_index) {
524 const auto& g_vector =
525 basis_.
g_vectors()[kpoint_g_indices[
static_cast<std::size_t
>(g_index)]];
526 int miller_x =
static_cast<int>(std::round(g_vector.miller.x));
527 int miller_y =
static_cast<int>(std::round(g_vector.miller.y));
528 int miller_z =
static_cast<int>(std::round(g_vector.miller.z));
529 if (std::abs(miller_x) <= wave_dim_1 / 2 &&
530 std::abs(miller_y) <= wave_dim_2 / 2 &&
531 std::abs(miller_z) <= wave_dim_3 / 2) {
532 minus_g_to_smooth_mapping[g_index] =
534 -miller_x, -miller_y, -miller_z);
539 auto* smooth_density_ptr = smooth_density[
static_cast<std::size_t
>(spin_index)].data();
543 std::vector<int> occupied_band_indices;
544 for (
int band_index = 0; band_index < number_of_bands; ++band_index) {
547 kpoint_index_state, band_index);
548 if (occupation >= math::tolerances::occupation_threshold) {
549 occupied_band_indices.push_back(band_index);
552 int number_of_band_pairs =
static_cast<int>((occupied_band_indices.size() + 1) / 2);
553 accumulate_bands_into(
554 number_of_band_pairs,
556 fft_workspace_points,
560 std::vector<Complex>& fft_workspace,
561 Real* density_target,
562 int transform_slot) {
563 std::size_t pair_start = static_cast<std::size_t>(pair_index) * 2;
564 int band_index_1 = occupied_band_indices[pair_start];
565 bool has_second_band = (pair_start + 1 < occupied_band_indices.size());
567 has_second_band ? occupied_band_indices[pair_start + 1] : -1;
569 const Complex* band_data_1 = quantum_system_.wavefunction_band_data(
570 kpoint_index_state, band_index_1);
571 const Complex* band_data_2 = has_second_band
572 ? quantum_system_.wavefunction_band_data(
573 kpoint_index_state, band_index_2)
577 quantum_system_.occupation_by_kpoint_state_index_and_band_index(
578 kpoint_index_state, band_index_1);
581 ? quantum_system_.occupation_by_kpoint_state_index_and_band_index(
582 kpoint_index_state, band_index_2)
585 scatter_two_bands_gamma(band_data_1,
587 plane_waves_at_kpoint,
589 minus_g_to_smooth_mapping,
591 basis_.fft_wave().inverse_raw_on_slot(fft_workspace.data(), transform_slot);
593 Real weight_1 = kpoint_weight * occupation_1 * inverse_cell_volume;
594 Real weight_2 = kpoint_weight * occupation_2 * inverse_cell_volume;
595 for (int grid_index = 0; grid_index < smooth_local_points; ++grid_index) {
596 Real psi1 = fft_workspace[static_cast<size_t>(grid_index)].real();
597 Real psi2 = fft_workspace[static_cast<size_t>(grid_index)].imag();
598 density_target[grid_index] +=
599 weight_1 * psi1 * psi1 + weight_2 * psi2 * psi2;
604 accumulate_bands_into(
607 fft_workspace_points,
611 std::vector<Complex>& fft_workspace,
612 Real* density_target,
613 int transform_slot) {
615 quantum_system_.occupation_by_kpoint_state_index_and_band_index(
616 kpoint_index_state, band_index);
617 if (occupation < math::tolerances::occupation_threshold) {
620 const Complex* band_data =
623 scatter_wavefunction_to_grid(band_data,
624 plane_waves_at_kpoint,
626 minus_g_to_smooth_mapping,
631 Real weight_factor = kpoint_weight * occupation * inverse_cell_volume;
632 for (
int grid_index = 0; grid_index < smooth_local_points; ++grid_index) {
633 density_target[grid_index] +=
635 std::norm(fft_workspace[
static_cast<std::size_t
>(grid_index)]);
642 charge_density_out.resize(
static_cast<std::size_t
>(number_of_spin_components));
643 for (
int spin_index = 0; spin_index < number_of_spin_components; ++spin_index) {
645 std::vector<Complex> density_g_smooth(
646 static_cast<std::size_t
>(
649 for (
int grid_index = 0; grid_index < smooth_local_points; ++grid_index) {
650 density_g_smooth[
static_cast<std::size_t
>(grid_index)] =
651 Complex(smooth_density[
static_cast<std::size_t
>(spin_index)]
652 [
static_cast<std::size_t
>(grid_index)],
658 Real inverse_smooth = 1.0 / smooth_total_points;
659 for (
auto& value : density_g_smooth) {
660 value *= inverse_smooth;
668 std::vector<Complex> density_g_smooth_redundant(smooth_total_points,
Complex(0.0, 0.0));
670 density_g_smooth_redundant.data());
672 smooth_total_points);
678 std::vector<Complex> density_g_rho(
679 static_cast<std::size_t
>(
682 for (
int m1 = -(wave_dim_1 / 2); m1 <= wave_dim_1 / 2; ++m1) {
683 for (
int m2 = -(wave_dim_2 / 2); m2 <= wave_dim_2 / 2; ++m2) {
684 for (
int m3 = -(wave_dim_3 / 2); m3 <= wave_dim_3 / 2; ++m3) {
685 int smooth_index_global =
686 (m1 < 0 ? m1 + wave_dim_1 : m1) * wave_dim_2 * wave_dim_3 +
687 (m2 < 0 ? m2 + wave_dim_2 : m2) * wave_dim_3 +
688 (m3 < 0 ? m3 + wave_dim_3 : m3);
689 int rho_index_local =
692 if (rho_index_local >= 0) {
693 density_g_rho[
static_cast<std::size_t
>(rho_index_local)] =
694 density_g_smooth_redundant[
static_cast<std::size_t
>(
695 smooth_index_global)];
705 charge_density_out[
static_cast<std::size_t
>(spin_index)].resize(
706 static_cast<std::size_t
>(rho_local_points));
707 for (
int grid_index = 0; grid_index < rho_local_points; ++grid_index) {
708 charge_density_out[
static_cast<std::size_t
>(spin_index)]
709 [
static_cast<std::size_t
>(grid_index)] =
710 density_g_rho[
static_cast<std::size_t
>(grid_index)].real();