Quantum 3xplorer (Q3)
An AI-native quantum simulation engine
Loading...
Searching...
No Matches
pseudo_atomic_orbital_set.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * PlanewavePseudoAtomicOrbitalSet — the pseudopotential's own atomic
5 * wavefunctions (UPF PP_CHI / PP_PSWFC), rendered in plane waves.
6 *
7 * Every orbital the pseudopotential file carries, on every atom of that
8 * species, in the enumeration order atomic_orbital_builder.hpp defines: the
9 * same set the PDOS / Loewdin population analysis projects onto, and the
10 * default initial guess of a Wannier construction. The radial part is
11 * tabulated in q once at construction; each k-point's rendering then costs
12 * an interpolation per G-vector plus structure factors.
13 *
14 * `trial_shells` narrows it to the shells it names per species, as 0-based
15 * indices in the order the file lists them; a species it does not name keeps
16 * all of them. The whole set is still rendered and the named rows kept, so the
17 * orbitals are exactly the corresponding rows of the full set.
18 *
19 * All atom-centred by construction. For a material whose Wannier functions
20 * are not -- silicon's valence functions sit on the bonds -- this set spans
21 * the wrong subspace and the explicit-centre PlanewaveHydrogenicOrbitalSet
22 * is the right choice; see that class.
23 */
24
25#include <cstddef>
26#include <map>
27#include <stdexcept>
28#include <string>
29#include <utility>
30#include <vector>
31
36
37namespace q3 {
38
40 public:
42 const Crystal& crystal_structure,
43 const PseudopotentialSet& pseudopotential_set,
44 const std::map<std::string, std::vector<int>>& trial_shells = {})
45 : pseudopotential_set_(pseudopotential_set) {
46 const std::vector<std::vector<bool>> kept =
47 kept_shells(crystal_structure, pseudopotential_set, trial_shells);
48 /// One label per rendered orbital, in the order build_atomic_orbitals_g_space fills them.
49 const std::vector<AtomicOrbitalLabel> every_label =
50 enumerate_atomic_orbitals(crystal_structure, pseudopotential_set);
51 for (std::size_t row = 0; row < every_label.size(); row++) {
52 const AtomicOrbitalLabel& label = every_label[row];
53 if (kept[static_cast<std::size_t>(label.species_type_index)]
54 [static_cast<std::size_t>(label.shell_index)]) {
55 kept_rows_.push_back(static_cast<int>(row));
56 orbital_labels_.push_back(label);
57 }
58 }
59 number_of_rendered_orbitals_ = static_cast<int>(every_label.size());
60 centers_crystal_.reserve(orbital_labels_.size());
61 for (const AtomicOrbitalLabel& label : orbital_labels_) {
62 centers_crystal_.push_back(crystal_structure.fractional_position(label.atom_index));
63 }
64 atomic_wavefunction_table_ = tabulate_atomic_wavefunctions_in_q_space(
65 pseudopotential_set, crystal_structure.cell_volume(), q_spacing_, number_of_q_points_);
66 }
67
68 [[nodiscard]] int number_of_orbitals() const override {
69 return static_cast<int>(orbital_labels_.size());
70 }
71
72 [[nodiscard]] const std::vector<AtomicOrbitalLabel>& orbital_labels() const {
73 return orbital_labels_;
74 }
75
76 void build(int kpoint_index,
77 int number_of_plane_waves,
78 const std::vector<int>& g_vector_indices,
79 const Crystal& crystal_structure,
80 const KPointsGrid& kpoint_grid,
81 const PlanewaveBasis& planewave_basis,
82 std::vector<std::vector<Complex>>& orbitals) const override {
83 const bool every_shell = number_of_orbitals() == number_of_rendered_orbitals_;
84 std::vector<std::vector<Complex>> rendered;
86 number_of_plane_waves,
87 g_vector_indices,
88 crystal_structure,
89 kpoint_grid,
90 planewave_basis,
91 pseudopotential_set_,
92 atomic_wavefunction_table_,
93 q_spacing_,
94 number_of_q_points_,
95 every_shell ? orbitals : rendered);
96 if (every_shell) {
97 return;
98 }
99 orbitals.resize(kept_rows_.size());
100 for (std::size_t orbital = 0; orbital < kept_rows_.size(); orbital++) {
101 orbitals[orbital] = std::move(rendered[kept_rows_[orbital]]);
102 }
103 }
104
105 /// The atom each orbital is centred on; atom-centred by construction.
106 [[nodiscard]] std::vector<Vector3D> centers_crystal() const override {
107 return centers_crystal_;
108 }
109
110 [[nodiscard]] std::string description() const override {
111 std::string text =
112 std::to_string(number_of_orbitals()) +
113 " pseudo-atomic orbitals from the pseudopotentials' atomic wavefunctions";
114 if (number_of_orbitals() != number_of_rendered_orbitals_) {
115 text += ", " + std::to_string(number_of_rendered_orbitals_ - number_of_orbitals()) +
116 " left out by control.interpolation.trial_shells";
117 }
118 return text;
119 }
120
121 private:
122 /**
123 * kept[type][shell]: whether that shell of that species is a trial orbital.
124 *
125 * Refuses a species the material does not have, a shell index past the
126 * file's list, and a shell the reader marked not to be used -- a negative
127 * occupation, or a zero-norm radial function, which the reader records as
128 * one -- each a selection that cannot mean what it says.
129 */
130 [[nodiscard]] static std::vector<std::vector<bool>>
131 kept_shells(const Crystal& crystal_structure,
132 const PseudopotentialSet& pseudopotential_set,
133 const std::map<std::string, std::vector<int>>& trial_shells) {
134 std::vector<std::vector<bool>> kept(pseudopotential_set.size());
135 std::map<std::string, int> type_of_species;
136 for (int atom_index = 0; atom_index < crystal_structure.number_of_atoms(); atom_index++) {
137 type_of_species.emplace(crystal_structure.element(atom_index),
138 crystal_structure.species_type_index(atom_index));
139 }
140 for (std::size_t type_index = 0; type_index < kept.size(); type_index++) {
141 kept[type_index].assign(pseudopotential_set[type_index].wavefunctions_atomic.size(),
142 true);
143 }
144 for (const auto& [species, shells] : trial_shells) {
145 const auto found = type_of_species.find(species);
146 if (found == type_of_species.end()) {
147 throw std::invalid_argument("control.interpolation.trial_shells names " + species +
148 ", which is not a species of this material.");
149 }
150 const auto& wavefunctions = pseudopotential_set[found->second].wavefunctions_atomic;
151 std::vector<bool> named(wavefunctions.size(), false);
152 for (const int shell : shells) {
153 if (shell < 0 || shell >= static_cast<int>(wavefunctions.size())) {
154 throw std::invalid_argument(
155 "control.interpolation.trial_shells." + species + " lists shell " +
156 std::to_string(shell) + ", but its pseudopotential lists " +
157 std::to_string(wavefunctions.size()) + " atomic wavefunctions.");
158 }
159 if (wavefunctions[shell].occupations < 0.0) {
160 throw std::invalid_argument("control.interpolation.trial_shells." + species +
161 " lists shell " + std::to_string(shell) +
162 ", which its pseudopotential marks not to be used "
163 "as an orbital (a negative "
164 "occupation, or a radial function of zero norm).");
165 }
166 named[shell] = true;
167 }
168 kept[found->second] = named;
169 }
170 return kept;
171 }
172
173 const PseudopotentialSet& pseudopotential_set_;
174 std::vector<AtomicOrbitalLabel> orbital_labels_;
175 /// The rows of the full rendering this set keeps, in order.
176 std::vector<int> kept_rows_;
177 /// How many orbitals the full rendering holds, trial shells or not.
178 int number_of_rendered_orbitals_ = 0;
179 std::vector<std::vector<std::vector<Real>>> atomic_wavefunction_table_;
180 std::vector<Vector3D> centers_crystal_;
181 Real q_spacing_ = 0.01;
182 int number_of_q_points_ = 900;
183};
184
185} // namespace q3
Real cell_volume() const
Definition crystal.hpp:193
Vector3D fractional_position(int atom_index) const
Definition crystal.hpp:269
const std::string & element(int atom_index) const
Definition crystal.hpp:346
int species_type_index(int atom_index) const
Definition crystal.hpp:273
int number_of_atoms() const
Definition crystal.hpp:209
const std::vector< AtomicOrbitalLabel > & orbital_labels() const
std::string description() const override
One line for logs, naming the source and the count.
std::vector< Vector3D > centers_crystal() const override
The atom each orbital is centred on; atom-centred by construction.
void build(int kpoint_index, int number_of_plane_waves, const std::vector< int > &g_vector_indices, const Crystal &crystal_structure, const KPointsGrid &kpoint_grid, const PlanewaveBasis &planewave_basis, std::vector< std::vector< Complex > > &orbitals) const override
PlanewavePseudoAtomicOrbitalSet(const Crystal &crystal_structure, const PseudopotentialSet &pseudopotential_set, const std::map< std::string, std::vector< int > > &trial_shells={})
int size() const
Definition set.hpp:98
void build_atomic_orbitals_g_space(int kpoint_index, int number_of_plane_waves, const std::vector< int > &g_vector_indices, const Crystal &crystal_structure, const KPointsGrid &kpoint_grid, const PlanewaveBasis &planewave_basis, const PseudopotentialSet &pseudopotential_set, const std::vector< std::vector< std::vector< Real > > > &atomic_wavefunction_table, Real q_spacing, int number_of_q_points, std::vector< std::vector< Complex > > &orbitals)
double Real
std::vector< AtomicOrbitalLabel > enumerate_atomic_orbitals(const Crystal &crystal_structure, const PseudopotentialSet &pseudopotential_set)
std::vector< std::vector< std::vector< Real > > > tabulate_atomic_wavefunctions_in_q_space(const PseudopotentialSet &pseudopotential_set, Real cell_volume, Real q_spacing, int number_of_q_points)
Identifies one atomic orbital in the ordering produced by build_atomic_orbitals_g_space().