Quantum 3xplorer (Q3)
An AI-native quantum simulation engine
Loading...
Searching...
No Matches
orbital_metric.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * An orbital set measured in the S metric: S applied to each orbital, and the
5 * Gram matrix O_ij = ⟨φ_i|S|φ_j⟩ that Löwdin orthogonalization needs when the
6 * basis is not orthonormal.
7 *
8 * Every consumer of a non-orthonormal orbital set -- Wannier trial orbitals,
9 * the PDOS / Löwdin population projectors -- needs the same two operations, so
10 * they live beside the operator they apply rather than inside whichever
11 * consumer happened to need them first. `overlap_operator` may be null, which
12 * is the norm-conserving S = I case: the orbitals come back unchanged and the
13 * Gram matrix is the plain ⟨φ_i|φ_j⟩ of
14 * compute_orbital_overlap_matrix(), element for element.
15 *
16 * Ultrasoft and PAW pseudopotentials are what make the metric non-trivial:
17 * both relax norm conservation, so the pseudo-orbitals are orthonormal with
18 * respect to S = 1 + Σ_ij q_ij |β_i⟩⟨β_j| rather than to the identity
19 * (Vanderbilt, PRB 41, 7892 (1990); Blöchl, PRB 50, 17953 (1994); Kresse &
20 * Joubert, PRB 59, 1758 (1999)).
21 */
22
23#include <complex>
24#include <cstring>
25#include <vector>
26
29
30namespace q3 {
31
32/**
33 * Apply `overlap_operator` to every orbital in `orbitals`, batched through
34 * one apply_batch() call. Returns `orbitals` unchanged (a copy) when
35 * `overlap_operator` is null -- the norm-conserving S = I case, kept as an
36 * explicit branch here rather than pushed onto every caller.
37 */
38[[nodiscard]] inline std::vector<std::vector<Complex>>
39apply_overlap_operator_to_orbitals(const std::vector<std::vector<Complex>>& orbitals,
40 const OverlapOperator* overlap_operator) {
41 if (overlap_operator == nullptr) {
42 return orbitals;
43 }
44
45 int number_of_orbitals = static_cast<int>(orbitals.size());
46 if (number_of_orbitals == 0) {
47 return {};
48 }
49 int number_of_plane_waves = static_cast<int>(orbitals[0].size());
50
51 std::vector<Complex> flattened_orbitals(static_cast<std::size_t>(number_of_orbitals) *
52 number_of_plane_waves);
53 for (int orbital_index = 0; orbital_index < number_of_orbitals; orbital_index++) {
54 std::memcpy(flattened_orbitals.data() +
55 static_cast<std::size_t>(orbital_index) * number_of_plane_waves,
56 orbitals[orbital_index].data(),
57 static_cast<std::size_t>(number_of_plane_waves) * sizeof(Complex));
58 }
59
60 std::vector<Complex> flattened_s_applied(flattened_orbitals.size());
61 overlap_operator->apply_batch(number_of_plane_waves,
62 number_of_orbitals,
63 flattened_orbitals.data(),
64 flattened_s_applied.data());
65
66 std::vector<std::vector<Complex>> s_applied_orbitals(
67 number_of_orbitals, std::vector<Complex>(number_of_plane_waves));
68 for (int orbital_index = 0; orbital_index < number_of_orbitals; orbital_index++) {
69 std::memcpy(s_applied_orbitals[orbital_index].data(),
70 flattened_s_applied.data() +
71 static_cast<std::size_t>(orbital_index) * number_of_plane_waves,
72 static_cast<std::size_t>(number_of_plane_waves) * sizeof(Complex));
73 }
74 return s_applied_orbitals;
75}
76
77/**
78 * The orbital overlap matrix O_ij = ⟨φ_i|S|φ_j⟩, column-major, in the same
79 * layout compute_orbital_overlap_matrix() (the S = I case) uses -- so both
80 * feed the same lowdin_orthogonalize_orbitals(orbitals, overlap) entry point
81 * unchanged. `overlap_operator` may be null (S = I).
82 */
83[[nodiscard]] inline std::vector<Complex>
84compute_orbital_overlap_matrix_in_s_metric(const std::vector<std::vector<Complex>>& orbitals,
85 const OverlapOperator* overlap_operator) {
86 int number_of_orbitals = static_cast<int>(orbitals.size());
87 if (number_of_orbitals == 0) {
88 return {};
89 }
90 int number_of_plane_waves = static_cast<int>(orbitals[0].size());
91 std::vector<std::vector<Complex>> s_applied_orbitals =
92 apply_overlap_operator_to_orbitals(orbitals, overlap_operator);
93
94 std::vector<Complex> overlap_column_major(static_cast<std::size_t>(number_of_orbitals) *
95 number_of_orbitals);
96 for (int column = 0; column < number_of_orbitals; column++) {
97 for (int row = 0; row < number_of_orbitals; row++) {
98 Complex value(0.0, 0.0);
99 for (int g_index = 0; g_index < number_of_plane_waves; g_index++) {
100 value += std::conj(orbitals[row][g_index]) * s_applied_orbitals[column][g_index];
101 }
102 overlap_column_major[static_cast<std::size_t>(row) + column * number_of_orbitals] =
103 value;
104 }
105 }
106 return overlap_column_major;
107}
108
109} // namespace q3
virtual void apply_batch(int number_of_plane_waves, int number_of_vectors, const Complex *wavefunctions, Complex *overlap_wavefunctions) const
Definition init.hpp:39
std::vector< std::vector< Complex > > apply_overlap_operator_to_orbitals(const std::vector< std::vector< Complex > > &orbitals, const OverlapOperator *overlap_operator)
std::vector< Complex > compute_orbital_overlap_matrix_in_s_metric(const std::vector< std::vector< Complex > > &orbitals, const OverlapOperator *overlap_operator)
std::complex< double > Complex