156 const std::vector<Real>& core_density,
157 const std::vector<Real>& radial_grid,
158 const std::vector<Real>& radial_grid_derivative,
160 const std::vector<AngularPoint>& angular_grid,
161 const std::vector<std::vector<Real>>& harmonics,
164 std::vector<std::vector<Real>>& potential_lm) {
165 int number_of_lm =
static_cast<int>(density_channels.size());
166 int number_of_angles =
static_cast<int>(angular_grid.size());
168 potential_lm.assign(number_of_lm, std::vector<Real>(mesh_size, 0.0));
171 std::vector<Real> density_at_angles(number_of_angles);
172 std::vector<Real> xc_potential(number_of_angles);
173 std::vector<Real> xc_energy_density(number_of_angles);
174 std::vector<Real> radial_energy(mesh_size, 0.0);
175 std::vector<std::vector<Real>> radial_potential(number_of_lm,
176 std::vector<Real>(mesh_size, 0.0));
178 for (
int point = 0; point < mesh_size; point++) {
179 Real radius = radial_grid[point];
180 Real inverse_r_squared = 1.0 / (radius * radius);
181 Real core = core_density.empty() ? 0.0 : core_density[point];
183 for (
int angle = 0; angle < number_of_angles; angle++) {
185 for (
int lm = 0; lm < number_of_lm; lm++)
186 value += density_channels[lm][point] * harmonics[angle][lm];
187 density_at_angles[angle] = value * inverse_r_squared + core;
190 functional.
compute(density_at_angles, xc_potential, xc_energy_density);
196 Real energy_at_radius = 0.0;
197 for (
int angle = 0; angle < number_of_angles; angle++) {
199 angular_grid[angle].weight * xc_energy_density[angle] * density_at_angles[angle];
201 radial_energy[point] = energy_at_radius * radius * radius;
203 for (
int lm = 0; lm < number_of_lm; lm++) {
204 Real projected = 0.0;
205 for (
int angle = 0; angle < number_of_angles; angle++) {
207 angular_grid[angle].weight * xc_potential[angle] * harmonics[angle][lm];
209 radial_potential[lm][point] = projected;
214 energy = cumulative[mesh_size - 1];
215 potential_lm = radial_potential;
227 const std::vector<std::vector<Real>>& density_channels,
228 const std::vector<Real>& core_density,
229 const std::vector<Real>& radial_grid,
230 const std::vector<Real>& radial_grid_derivative,
232 const std::vector<AngularPoint>& angular_grid,
233 const std::vector<std::vector<Real>>& harmonics,
234 const std::vector<std::vector<std::array<Real, 3>>>& harmonic_gradients,
237 std::vector<std::vector<Real>>& potential_lm,
238 std::vector<std::vector<Real>>& radial_weight_lm,
239 std::vector<std::vector<Real>>& angular_weight_lm) {
240 int number_of_lm =
static_cast<int>(density_channels.size());
241 int number_of_angles =
static_cast<int>(angular_grid.size());
248 std::vector<std::vector<Real>> channel_values(number_of_lm, std::vector<Real>(mesh_size, 0.0));
249 for (
int lm = 0; lm < number_of_lm; lm++) {
250 for (
int point = 0; point < mesh_size; point++) {
251 Real radius = radial_grid[point];
252 channel_values[lm][point] = density_channels[lm][point] / (radius * radius);
255 if (!core_density.empty()) {
257 for (
int point = 0; point < mesh_size; point++)
258 channel_values[0][point] += core_density[point] * inverse_y00;
261 std::vector<std::vector<Real>> channel_derivatives(number_of_lm);
262 for (
int lm = 0; lm < number_of_lm; lm++)
263 channel_derivatives[lm] =
266 potential_lm.assign(number_of_lm, std::vector<Real>(mesh_size, 0.0));
267 radial_weight_lm.assign(number_of_lm, std::vector<Real>(mesh_size, 0.0));
268 angular_weight_lm.assign(number_of_lm, std::vector<Real>(mesh_size, 0.0));
270 std::vector<Real> density_at_angles(number_of_angles);
271 std::vector<Real> xc_potential(number_of_angles);
272 std::vector<Real> gradient_correction(number_of_angles);
273 std::vector<Real> xc_energy_density(number_of_angles);
274 std::vector<Real> radial_energy(mesh_size, 0.0);
276 std::vector<Real> values_at_radius(number_of_lm);
277 std::vector<Real> derivatives_at_radius(number_of_lm);
279 for (
int point = 0; point < mesh_size; point++) {
280 Real radius = radial_grid[point];
281 for (
int lm = 0; lm < number_of_lm; lm++) {
282 values_at_radius[lm] = channel_values[lm][point];
283 derivatives_at_radius[lm] = channel_derivatives[lm][point];
287 values_at_radius, derivatives_at_radius, radius, harmonics, harmonic_gradients);
289 for (
int angle = 0; angle < number_of_angles; angle++) {
291 for (
int lm = 0; lm < number_of_lm; lm++)
292 value += values_at_radius[lm] * harmonics[angle][lm];
293 density_at_angles[angle] = value;
297 gradient.gradient_squared,
302 Real energy_at_radius = 0.0;
303 for (
int angle = 0; angle < number_of_angles; angle++) {
305 angular_grid[angle].weight * xc_energy_density[angle] * density_at_angles[angle];
307 radial_energy[point] = energy_at_radius * radius * radius;
309 for (
int lm = 0; lm < number_of_lm; lm++) {
311 Real radial_weight = 0.0;
312 Real angular_weight = 0.0;
313 for (
int angle = 0; angle < number_of_angles; angle++) {
314 Real weight = angular_grid[angle].weight;
315 local += weight * xc_potential[angle] * harmonics[angle][lm];
317 Real two_v_sigma = 2.0 * gradient_correction[angle];
319 weight * two_v_sigma * gradient.radial_component[angle] * harmonics[angle][lm];
321 Real tangential_dot = 0.0;
322 for (
int axis = 0; axis < 3; axis++) {
324 gradient.tangential[angle][axis] * harmonic_gradients[angle][lm][axis];
334 angular_weight += weight * two_v_sigma * tangential_dot * radius;
336 potential_lm[lm][point] = local;
337 radial_weight_lm[lm][point] = radial_weight;
338 angular_weight_lm[lm][point] = angular_weight;
343 energy = cumulative[mesh_size - 1];
359 const std::vector<std::vector<std::vector<Real>>>& density_channels_by_spin,
360 const std::vector<Real>& core_density,
361 const std::vector<Real>& radial_grid,
362 const std::vector<Real>& radial_grid_derivative,
364 const std::vector<AngularPoint>& angular_grid,
365 const std::vector<std::vector<Real>>& harmonics,
368 std::vector<std::vector<std::vector<Real>>>& potential_lm_by_spin) {
369 int number_of_lm =
static_cast<int>(density_channels_by_spin[0].size());
370 int number_of_angles =
static_cast<int>(angular_grid.size());
372 potential_lm_by_spin.assign(
373 2, std::vector<std::vector<Real>>(number_of_lm, std::vector<Real>(mesh_size, 0.0)));
376 std::vector<Real> density_up(number_of_angles), density_down(number_of_angles);
377 std::vector<Real> potential_up(number_of_angles), potential_down(number_of_angles);
378 std::vector<Real> xc_energy_density(number_of_angles);
379 std::vector<Real> radial_energy(mesh_size, 0.0);
381 for (
int point = 0; point < mesh_size; point++) {
382 Real radius = radial_grid[point];
383 Real inverse_r_squared = 1.0 / (radius * radius);
384 Real half_core = core_density.empty() ? 0.0 : 0.5 * core_density[point];
386 for (
int angle = 0; angle < number_of_angles; angle++) {
389 for (
int lm = 0; lm < number_of_lm; lm++) {
390 up += density_channels_by_spin[0][lm][point] * harmonics[angle][lm];
391 down += density_channels_by_spin[1][lm][point] * harmonics[angle][lm];
393 density_up[angle] = up * inverse_r_squared + half_core;
394 density_down[angle] = down * inverse_r_squared + half_core;
398 density_up, density_down, potential_up, potential_down, xc_energy_density);
404 Real energy_at_radius = 0.0;
405 for (
int angle = 0; angle < number_of_angles; angle++) {
406 energy_at_radius += angular_grid[angle].weight * xc_energy_density[angle] *
407 (density_up[angle] + density_down[angle]);
409 radial_energy[point] = energy_at_radius * radius * radius;
411 for (
int lm = 0; lm < number_of_lm; lm++) {
412 Real projected_up = 0.0;
413 Real projected_down = 0.0;
414 for (
int angle = 0; angle < number_of_angles; angle++) {
415 Real weight = angular_grid[angle].weight * harmonics[angle][lm];
416 projected_up += weight * potential_up[angle];
417 projected_down += weight * potential_down[angle];
419 potential_lm_by_spin[0][lm][point] = projected_up;
420 potential_lm_by_spin[1][lm][point] = projected_down;
425 energy = cumulative[mesh_size - 1];
446 const std::vector<std::vector<std::vector<Real>>>& density_channels_by_spin,
447 const std::vector<Real>& core_density,
448 const std::vector<Real>& radial_grid,
449 const std::vector<Real>& radial_grid_derivative,
451 const std::vector<AngularPoint>& angular_grid,
452 const std::vector<std::vector<Real>>& harmonics,
453 const std::vector<std::vector<std::array<Real, 3>>>& harmonic_gradients,
456 std::vector<std::vector<std::vector<Real>>>& potential_lm_by_spin,
457 std::vector<std::vector<std::vector<Real>>>& radial_weight_lm_by_spin,
458 std::vector<std::vector<std::vector<Real>>>& angular_weight_lm_by_spin) {
459 int number_of_lm =
static_cast<int>(density_channels_by_spin[0].size());
460 int number_of_angles =
static_cast<int>(angular_grid.size());
463 std::vector<std::vector<std::vector<Real>>> channel_values(
464 2, std::vector<std::vector<Real>>(number_of_lm, std::vector<Real>(mesh_size, 0.0)));
465 for (
int spin = 0; spin < 2; spin++) {
466 for (
int lm = 0; lm < number_of_lm; lm++) {
467 for (
int point = 0; point < mesh_size; point++) {
468 Real radius = radial_grid[point];
469 channel_values[spin][lm][point] =
470 density_channels_by_spin[spin][lm][point] / (radius * radius);
473 if (!core_density.empty()) {
475 for (
int point = 0; point < mesh_size; point++)
476 channel_values[spin][0][point] += 0.5 * core_density[point] * inverse_y00;
480 std::vector<std::vector<std::vector<Real>>> channel_derivatives(
481 2, std::vector<std::vector<Real>>(number_of_lm));
482 for (
int spin = 0; spin < 2; spin++)
483 for (
int lm = 0; lm < number_of_lm; lm++)
484 channel_derivatives[spin][lm] =
487 auto empty_by_spin = [&]() {
488 return std::vector<std::vector<std::vector<Real>>>(
489 2, std::vector<std::vector<Real>>(number_of_lm, std::vector<Real>(mesh_size, 0.0)));
491 potential_lm_by_spin = empty_by_spin();
492 radial_weight_lm_by_spin = empty_by_spin();
493 angular_weight_lm_by_spin = empty_by_spin();
495 std::vector<Real> density_up(number_of_angles), density_down(number_of_angles);
496 std::vector<Real> sigma_uu(number_of_angles), sigma_ud(number_of_angles),
497 sigma_dd(number_of_angles);
498 std::vector<Real> potential_up(number_of_angles), potential_down(number_of_angles);
499 std::vector<Real> gradient_uu(number_of_angles), gradient_ud(number_of_angles),
500 gradient_dd(number_of_angles);
501 std::vector<Real> xc_energy_density(number_of_angles);
502 std::vector<Real> radial_energy(mesh_size, 0.0);
504 std::vector<Real> values_at_radius(number_of_lm), derivatives_at_radius(number_of_lm);
506 for (
int point = 0; point < mesh_size; point++) {
507 Real radius = radial_grid[point];
509 std::vector<GradientAtRadius> gradient(2);
510 for (
int spin = 0; spin < 2; spin++) {
511 for (
int lm = 0; lm < number_of_lm; lm++) {
512 values_at_radius[lm] = channel_values[spin][lm][point];
513 derivatives_at_radius[lm] = channel_derivatives[spin][lm][point];
516 values_at_radius, derivatives_at_radius, radius, harmonics, harmonic_gradients);
519 for (
int angle = 0; angle < number_of_angles; angle++) {
522 for (
int lm = 0; lm < number_of_lm; lm++) {
523 up += channel_values[0][lm][point] * harmonics[angle][lm];
524 down += channel_values[1][lm][point] * harmonics[angle][lm];
526 density_up[angle] = up;
527 density_down[angle] = down;
529 sigma_uu[angle] = gradient[0].gradient_squared[angle];
530 sigma_dd[angle] = gradient[1].gradient_squared[angle];
531 Real cross = gradient[0].radial_component[angle] * gradient[1].radial_component[angle];
532 for (
int axis = 0; axis < 3; axis++)
533 cross += gradient[0].tangential[angle][axis] * gradient[1].tangential[angle][axis];
534 sigma_ud[angle] = cross;
549 Real energy_at_radius = 0.0;
550 for (
int angle = 0; angle < number_of_angles; angle++) {
551 energy_at_radius += angular_grid[angle].weight * xc_energy_density[angle] *
552 (density_up[angle] + density_down[angle]);
554 radial_energy[point] = energy_at_radius * radius * radius;
556 for (
int spin = 0; spin < 2; spin++) {
557 const auto& potential = spin == 0 ? potential_up : potential_down;
558 const auto& same_spin_gradient = spin == 0 ? gradient_uu : gradient_dd;
559 const auto& own = gradient[spin];
560 const auto& other = gradient[1 - spin];
562 for (
int lm = 0; lm < number_of_lm; lm++) {
564 Real radial_weight = 0.0;
565 Real angular_weight = 0.0;
566 for (
int angle = 0; angle < number_of_angles; angle++) {
567 Real weight = angular_grid[angle].weight;
568 local += weight * potential[angle] * harmonics[angle][lm];
571 Real conjugate_radial =
572 2.0 * same_spin_gradient[angle] * own.radial_component[angle] +
573 gradient_ud[angle] * other.radial_component[angle];
574 radial_weight += weight * conjugate_radial * harmonics[angle][lm];
576 Real tangential_dot = 0.0;
577 for (
int axis = 0; axis < 3; axis++) {
578 Real conjugate_tangential =
579 2.0 * same_spin_gradient[angle] * own.tangential[angle][axis] +
580 gradient_ud[angle] * other.tangential[angle][axis];
582 conjugate_tangential * harmonic_gradients[angle][lm][axis];
590 angular_weight += weight * tangential_dot * radius;
592 potential_lm_by_spin[spin][lm][point] = local;
593 radial_weight_lm_by_spin[spin][lm][point] = radial_weight;
594 angular_weight_lm_by_spin[spin][lm][point] = angular_weight;
600 energy = cumulative[mesh_size - 1];
610 const std::vector<AngularPoint>& angular_grid,
611 const std::vector<std::vector<Real>>& harmonics,
613 const std::vector<std::vector<std::array<Real, 3>>>* harmonic_gradients =
nullptr,
615 OneCenterEnergy result;
616 int mesh_size = pseudopotential.
mesh_size;
619 auto build_branch = [&](
const std::vector<std::vector<Real>>& channels,
620 const std::vector<Real>& core_density,
621 bool subtract_core_exchange_correlation) {
622 OneCenterBranch branch;
624 std::vector<std::vector<Real>> hartree_potential(number_of_lm);
625 for (
int lm = 0; lm < number_of_lm; lm++) {
631 hartree_potential[lm]);
636 std::vector<std::vector<Real>> xc_potential;
637 if (harmonic_gradients !=
nullptr) {
649 branch.gradient_radial_weight_lm,
650 branch.gradient_angular_weight_lm);
670 if (subtract_core_exchange_correlation && !core_density.empty()) {
671 std::vector<std::vector<Real>> no_valence(number_of_lm,
672 std::vector<Real>(mesh_size, 0.0));
673 std::vector<std::vector<Real>> discarded_potential;
674 Real core_only_energy = 0.0;
675 if (harmonic_gradients !=
nullptr) {
676 std::vector<std::vector<Real>> discarded_radial_weight;
677 std::vector<std::vector<Real>> discarded_angular_weight;
689 discarded_radial_weight,
690 discarded_angular_weight);
701 discarded_potential);
703 branch.xc_energy -= core_only_energy;
706 branch.potential_lm.assign(number_of_lm, std::vector<Real>(mesh_size, 0.0));
707 for (
int lm = 0; lm < number_of_lm; lm++) {
708 for (
int point = 0; point < mesh_size; point++) {
709 branch.potential_lm[lm][point] =
710 hartree_potential[lm][point] + xc_potential[lm][point];
716 result.all_electron = build_branch(densities.
all_electron,
718 options.subtract_core_exchange_correlation);
743 const std::vector<OneCenterDensities>& densities_by_spin,
744 const std::vector<AngularPoint>& angular_grid,
745 const std::vector<std::vector<Real>>& harmonics,
747 const std::vector<std::vector<std::array<Real, 3>>>* harmonic_gradients =
nullptr,
749 std::vector<OneCenterEnergy> result(2);
750 int mesh_size = pseudopotential.
mesh_size;
751 int number_of_lm = densities_by_spin[0].number_of_lm;
753 auto build_branch = [&](
const std::vector<std::vector<Real>>& channels_up,
754 const std::vector<std::vector<Real>>& channels_down,
755 const std::vector<Real>& core_density,
756 bool subtract_core_exchange_correlation,
757 OneCenterBranch& branch_up,
758 OneCenterBranch& branch_down) {
759 std::vector<std::vector<Real>> total(number_of_lm, std::vector<Real>(mesh_size, 0.0));
760 for (
int lm = 0; lm < number_of_lm; lm++)
761 for (
int point = 0; point < mesh_size; point++)
762 total[lm][point] = channels_up[lm][point] + channels_down[lm][point];
764 std::vector<std::vector<Real>> hartree_potential(number_of_lm);
765 for (
int lm = 0; lm < number_of_lm; lm++) {
771 hartree_potential[lm]);
775 branch_down.hartree_energy = 0.0;
777 std::vector<std::vector<std::vector<Real>>> density_by_spin{channels_up, channels_down};
778 std::vector<std::vector<std::vector<Real>>> xc_potential_by_spin;
779 Real xc_energy = 0.0;
780 if (harmonic_gradients !=
nullptr) {
781 std::vector<std::vector<std::vector<Real>>> radial_weight_by_spin;
782 std::vector<std::vector<std::vector<Real>>> angular_weight_by_spin;
793 xc_potential_by_spin,
794 radial_weight_by_spin,
795 angular_weight_by_spin);
796 branch_up.gradient_radial_weight_lm = radial_weight_by_spin[0];
797 branch_up.gradient_angular_weight_lm = angular_weight_by_spin[0];
798 branch_down.gradient_radial_weight_lm = radial_weight_by_spin[1];
799 branch_down.gradient_angular_weight_lm = angular_weight_by_spin[1];
810 xc_potential_by_spin);
812 branch_up.xc_energy = xc_energy;
813 branch_down.xc_energy = 0.0;
825 if (subtract_core_exchange_correlation && !core_density.empty()) {
826 std::vector<std::vector<std::vector<Real>>> no_valence(
827 2, std::vector<std::vector<Real>>(number_of_lm, std::vector<Real>(mesh_size, 0.0)));
828 std::vector<std::vector<std::vector<Real>>> discarded_potential;
829 Real core_only_energy = 0.0;
830 if (harmonic_gradients !=
nullptr) {
831 std::vector<std::vector<std::vector<Real>>> discarded_radial_weight;
832 std::vector<std::vector<std::vector<Real>>> discarded_angular_weight;
845 discarded_radial_weight,
846 discarded_angular_weight);
857 discarded_potential);
859 branch_up.xc_energy -= core_only_energy;
862 for (
int spin = 0; spin < 2; spin++) {
863 OneCenterBranch& branch = spin == 0 ? branch_up : branch_down;
864 branch.potential_lm.assign(number_of_lm, std::vector<Real>(mesh_size, 0.0));
865 for (
int lm = 0; lm < number_of_lm; lm++)
866 for (
int point = 0; point < mesh_size; point++)
867 branch.potential_lm[lm][point] =
868 hartree_potential[lm][point] + xc_potential_by_spin[spin][lm][point];
872 build_branch(densities_by_spin[0].all_electron,
873 densities_by_spin[1].all_electron,
875 options.subtract_core_exchange_correlation,
876 result[0].all_electron,
877 result[1].all_electron);
878 build_branch(densities_by_spin[0].pseudo,
879 densities_by_spin[1].pseudo,