Loading...
Searching...
No Matches
capture.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * capture_interpolation_record — the NSCF-side half of the interpolation
5 * umbrella for the plane-wave representation: from a converged plane-wave
6 * state to an InterpolationRecord, dispatching on the method the input asked
7 * for.
8 *
9 * This is the only place that knows both the interpolation specification and
10 * the plane-wave iterator, so the flavor that calls it stays free of plane
11 * waves, k-point grids and pseudopotential types -- it hands over its
12 * iterator and gets a record back, the way it hands its state to
13 * build_mean_field_record(). Everything representation-specific about a
14 * method's construction -- which trial orbitals, whether the pseudopotentials
15 * carry augmentation charges, the topology it can run under -- is decided
16 * here or in the method's own plane-wave builder.
17 *
18 * Refusals, each with its reason:
19 *
20 * - **Topology.** M_mn(k,b) couples each k-point to its neighbours, and
21 * k-point pools hold only their own k-points, so a neighbour generally
22 * lives on another rank and nothing exchanges it yet. This cannot fire
23 * today -- main_mpi.cpp constructs only SelfConsistentFieldFlavor, and the
24 * serial and GPU policies report that one process owns everything -- and is
25 * insurance on the reasoning the mean-field export states: one code path,
26 * guarded now. The condition reads world size, not this rank's share, so
27 * every rank decides alike.
28 * - **Method.** Only "wannier" has a plane-wave builder today; a method the
29 * parser accepted but nothing here can build is refused by name rather than
30 * silently producing a Wannier record.
31 * - **Augmentation.** Ultrasoft and PAW need the S metric in A_mn(k) and the
32 * augmentation term in M_mn(k,b); the norm-conserving path would silently
33 * omit both and write complete-looking, wrong matrices. The dispatch is on
34 * the pseudopotential set, and the ultrasoft quantum system it requires is
35 * reached by a checked downcast -- the same object the iterator factory
36 * built for this run, which the ultrasoft iterator itself downcasts to.
37 */
38
39#include <array>
40#include <memory>
41#include <stdexcept>
42#include <string>
43
56
57namespace q3 {
58
59namespace internal {
60
61/// The crystal's lattice in the record's units: Cartesian bohr, and inverse bohr with the 2 pi.
62inline void record_lattice(const Crystal& crystal_structure, InterpolationRecord& record) {
63 const Real lattice_parameter_bohr = crystal_structure.lattice_parameter();
64 for (int row = 0; row < 3; row++) {
65 record.real_lattice_bohr[row] =
66 crystal_structure.lattice_vectors()[row] * lattice_parameter_bohr;
68 crystal_structure.reciprocal_lattice_vectors()[row] *
69 (constants::two_pi / lattice_parameter_bohr);
70 }
71}
72
73/// The Wannier method's record: interface matrices on the full k-grid.
74[[nodiscard]] inline WannierInputs
77 int number_of_bands) {
78 const std::array<int, 3>& grid_dimensions = specification.grid;
79 /**
80 * Without a range the model is built from the lowest bands, which is what
81 * an isolated valence manifold needs. With one, the range says where the
82 * manifold starts -- semicore states from the pseudopotential sit below
83 * it -- and how many bands it holds.
84 */
85 const int first_band_index = specification.first_band_index();
86 const int selected_bands = specification.has_band_selection()
87 ? specification.number_of_selected_bands()
88 : number_of_bands;
89 if (specification.has_band_selection()) {
90 log::info("NSCF: building the model from bands {} to {} of {}",
91 specification.bands[0],
92 specification.bands[1],
93 number_of_bands);
94 }
95 const Crystal& crystal_structure = iterator.crystal_structure();
96 const PseudopotentialSet& pseudopotential_set = iterator.pseudopotential_set();
97 std::unique_ptr<PlanewaveLocalOrbitalSet> trial_orbitals =
98 create_planewave_local_orbital_set(crystal_structure,
99 pseudopotential_set,
100 specification.projection_centers,
101 specification.projection_decay_bohr,
102 specification.trial_shells);
103 log::info("NSCF: building Wannier interface matrices on the {}x{}x{} grid ({} k-points) "
104 "from {}",
105 grid_dimensions[0],
106 grid_dimensions[1],
107 grid_dimensions[2],
108 iterator.kpoint_grid().number_of_kpoints(),
109 trial_orbitals->description());
110
112 crystal_structure, iterator.planewave_basis(), pseudopotential_set);
113 if (pseudopotential_set.has_augmentation_charges()) {
114 /**
115 * The augmentation tables and the electronic state are in two
116 * different objects, and the snapshot is the one to ask.
117 * `iterator.quantum_system()` returns a by-value member declared as
118 * the base PlanewaveKohnShamQuantumSystem, so its dynamic type is
119 * always exactly that and a cast of it can never succeed, for any
120 * dataset. The ultrasoft ITERATOR's snapshot is the
121 * augmentation-aware quantum system.
122 *
123 * The snapshot carries the pseudopotential data but only the
124 * electronic state of its last refresh, and the builder reads
125 * wavefunctions, eigenvalues and the Fermi energy out of the same
126 * object it reads ultrasoft_basis() from, so refresh it first.
127 */
129 const auto* ultrasoft_system = dynamic_cast<const UltrasoftPlanewaveKohnShamQuantumSystem*>(
131 if (ultrasoft_system == nullptr) {
132 throw std::logic_error(
133 "control.interpolation: the pseudopotentials carry augmentation charges but the "
134 "quantum-system snapshot is not the ultrasoft one; the iterator factory and this "
135 "builder disagree about the run's pseudopotential type.");
136 }
137 log::info("NSCF: ultrasoft/PAW pseudopotentials -- including the augmentation term in "
138 "M_mn(k,b) and the S metric in A_mn(k)");
139 return builder.build_ultrasoft(iterator.kpoint_grid(),
140 grid_dimensions[0],
141 grid_dimensions[1],
142 grid_dimensions[2],
143 selected_bands,
144 iterator.g_vector_indices(),
145 *ultrasoft_system,
146 *trial_orbitals,
147 iterator.execution_policy(),
148 first_band_index);
149 }
150 return builder.build(iterator.kpoint_grid(),
151 grid_dimensions[0],
152 grid_dimensions[1],
153 grid_dimensions[2],
154 selected_bands,
155 iterator.g_vector_indices(),
156 iterator.quantum_system(),
157 *trial_orbitals,
158 first_band_index);
159}
160
161} // namespace internal
162
163[[nodiscard]] inline InterpolationRecord
166 int number_of_bands) {
168 throw std::runtime_error(
169 "control.interpolation is not supported under MPI: building the model needs each "
170 "k-point's neighbours, and k-point pools hold only their own k-points, so no rank "
171 "can reach the states it would have to overlap. Nothing exchanges them across pools "
172 "yet. Run the NSCF that builds the model with a serial binary; evaluating the model "
173 "afterwards is cheap and serial anyway.");
174 }
175
176 InterpolationRecord record;
177 record.method = specification.method;
179
180 if (specification.method == "wannier") {
181 record.wannier =
182 internal::build_wannier_interface_record(specification, iterator, number_of_bands);
185 /**
186 * Not a defect: a window wider than the orbital count is what
187 * disentanglement is for, and the record deliberately keeps the
188 * raw window so the choice belongs to the evaluation step. It is
189 * still worth saying, because the one thing that will NOT work is
190 * evaluating this record with the projection gauge alone.
191 */
192 log::info("NSCF: the record holds {} bands and {} trial orbitals, so it needs a "
193 "subspace choice: evaluate it with "
194 "control.interpolation.disentanglement: projectability, or export it "
195 "(export_formats: [wannier90]) for a code that disentangles. The one-shot "
196 "projection gauge on its own needs an isolated manifold with as many bands "
197 "as Wannier functions.",
200 }
201 return record;
202 }
203 throw std::runtime_error("control.interpolation.method '" + specification.method +
204 "' has no plane-wave builder; supported: wannier.");
205}
206
207} // namespace q3
const Matrix3x3 & reciprocal_lattice_vectors() const
Definition crystal.hpp:205
const Matrix3x3 & lattice_vectors() const
Definition crystal.hpp:202
Real lattice_parameter() const
Definition crystal.hpp:180
int number_of_kpoints() const
Crystal & crystal_structure() const
Definition init.hpp:219
const std::vector< std::vector< int > > & g_vector_indices() const
Definition init.hpp:280
const PlanewaveKohnShamQuantumSystem & quantum_system() const
Definition init.hpp:223
const KPointsGrid & kpoint_grid() const
Definition init.hpp:209
const execution::PlanewaveExecutionPolicy & execution_policy() const
Definition init.hpp:119
const PlanewaveBasis & planewave_basis() const
Definition init.hpp:216
const NormConservingPlanewaveKohnShamQuantumSystem & norm_conserving_quantum_system() const
Definition init.hpp:234
const PseudopotentialSet & pseudopotential_set() const
Definition init.hpp:226
WannierInputs build_ultrasoft(const KPointsGrid &kpoint_grid, int grid_dimension_x, int grid_dimension_y, int grid_dimension_z, int number_of_bands, const std::vector< std::vector< int > > &g_vector_indices_by_kpoint, const UltrasoftPlanewaveKohnShamQuantumSystem &quantum_system, const PlanewaveLocalOrbitalSet &trial_orbitals, const execution::PlanewaveExecutionPolicy &execution_policy, int first_band_index=0) const
Definition init.hpp:124
WannierInputs build(const KPointsGrid &kpoint_grid, int grid_dimension_x, int grid_dimension_y, int grid_dimension_z, int number_of_bands, const std::vector< std::vector< int > > &g_vector_indices_by_kpoint, const PlanewaveKohnShamQuantumSystem &quantum_system, const PlanewaveLocalOrbitalSet &trial_orbitals, int first_band_index=0) const
Definition init.hpp:98
bool has_augmentation_charges() const
Definition set.hpp:129
virtual bool one_process_owns_complete_state() const =0
constexpr Real two_pi
Definition constants.hpp:25
void record_lattice(const Crystal &crystal_structure, InterpolationRecord &record)
The crystal's lattice in the record's units: Cartesian bohr, and inverse bohr with the 2 pi.
Definition capture.hpp:62
WannierInputs build_wannier_interface_record(const InterpolationSpecification &specification, PlanewavePseudopotentialKohnShamSCFIterator &iterator, int number_of_bands)
The Wannier method's record: interface matrices on the full k-grid.
Definition capture.hpp:75
void info(spdlog::format_string_t< Args... > fmt, Args &&... args)
Definition logger.hpp:183
double Real
std::unique_ptr< PlanewaveLocalOrbitalSet > create_planewave_local_orbital_set(const Crystal &crystal_structure, const PseudopotentialSet &pseudopotential_set, const std::vector< Vector3D > &explicit_centers_crystal, Real decay_length_bohr, const std::map< std::string, std::vector< int > > &trial_shells={})
Definition factory.hpp:33
InterpolationRecord capture_interpolation_record(const InterpolationSpecification &specification, PlanewavePseudopotentialKohnShamSCFIterator &iterator, int number_of_bands)
Definition capture.hpp:164
WannierInputs wannier
Payload for method == "wannier": interface matrices on the full k-grid.
Definition record.hpp:46
Matrix3x3 real_lattice_bohr
Rows a1, a2, a3, Cartesian, in bohr.
Definition record.hpp:34
std::string method
The method that built the payload: "wannier".
Definition record.hpp:31
Matrix3x3 reciprocal_lattice_inverse_bohr
Rows b1, b2, b3, Cartesian, in inverse bohr, with the 2 pi included.
Definition record.hpp:36
std::map< std::string, std::vector< int > > trial_shells
int first_band_index() const
0-based index of the first selected band; 0 when no range was given.
int number_of_selected_bands() const
How many bands the range holds; 0 when no range was given.
std::string method
"" when no interpolation was asked for; otherwise the method name ("wannier").
std::array< int, 3 > grid
The full k-point grid control.kpoints_list enumerates; {0,0,0} = not stated.
Real projection_decay_bohr
Wannier: decay length of those orbitals in bohr (Wannier90's zona is 1/a).
std::vector< Vector3D > projection_centers
Real fermi_energy_rydberg
Definition inputs.hpp:77
int number_of_trial_orbitals
Definition inputs.hpp:72