SIRIUS 7.5.0
Electronic structure library and applications
initialize_subspace.hpp
Go to the documentation of this file.
1// Copyright (c) 2013-2023 Anton Kozhevnikov, Thomas Schulthess
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without modification, are permitted provided that
5// the following conditions are met:
6//
7// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
8// following disclaimer.
9// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
10// and the following disclaimer in the documentation and/or other materials provided with the distribution.
11//
12// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
13// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
14// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
15// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
16// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
17// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
18// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19
20/** \file initialize_subspace.hpp
21 *
22 * \brief Create intial subspace from atomic-like wave-functions
23 */
24
25#ifndef __INITIALIZE_SUBSPACE_HPP__
26#define __INITIALIZE_SUBSPACE_HPP__
27
29#include "diagonalize_pp.hpp"
30
31namespace sirius {
32
33/// Initialize the wave-functions subspace at a given k-point.
34/** If the number of atomic orbitals is smaller than the number of bands, the rest of the initial wave-functions
35 * are created from the random numbers. */
36template <typename T, typename F>
37inline void
38initialize_subspace(Hamiltonian_k<T> const& Hk__, K_point<T>& kp__, int num_ao__)
39{
40 PROFILE("sirius::initialize_subspace|kp");
41
42 auto& ctx = Hk__.H0().ctx();
43
44 if (ctx.cfg().control().verification() >= 2) {
45 auto eval = diag_S_davidson<T, F>(Hk__, kp__);
46 if (eval[0] <= 0) {
47 std::stringstream s;
48 s << "S-operator matrix is not positive definite\n"
49 << " lowest eigen-value: " << eval[0];
50 RTE_WARNING(s);
51 } else {
52 std::stringstream s;
53 s << "S-matrix is OK! Minimum eigen-value : " << eval[0];
54 RTE_OUT(ctx.out(1)) << s.str() << std::endl;
55 }
56 }
57
58 auto pcs = env::print_checksum();
59
60 /* number of non-zero spin components */
61 const int num_sc = (ctx.num_mag_dims() == 3) ? 2 : 1;
62
63 /* short notation for number of target wave-functions */
64 int num_bands = ctx.num_bands();
65
66 /* number of basis functions */
67 int num_phi = std::max(num_ao__, num_bands / num_sc);
68
69 int num_phi_tot = num_phi * num_sc;
70
71 auto& mp = get_memory_pool(ctx.host_memory_t());
72
73 print_memory_usage(ctx.out(), FILE_LINE);
74
75 /* initial basis functions */
76 wf::Wave_functions<T> phi(kp__.gkvec_sptr(), wf::num_mag_dims(ctx.num_mag_dims() == 3 ? 3 : 0),
77 wf::num_bands(num_phi_tot), sddk::memory_t::host);
78
79 for (int ispn = 0; ispn < num_sc; ispn++) {
80 phi.zero(sddk::memory_t::host, wf::spin_index(ispn), wf::band_range(0, num_phi_tot));
81 }
82
83 /* generate the initial atomic wavefunctions */
84 std::vector<int> atoms(ctx.unit_cell().num_atoms());
85 std::iota(atoms.begin(), atoms.end(), 0);
87 atoms, [&](int iat) { return &ctx.unit_cell().atom_type(iat).indexb_wfs(); }, *ctx.ri().ps_atomic_wf_, phi);
88
89 /* generate some random noise */
90 std::vector<T> tmp(4096);
91 random_uint32(true);
92 for (int i = 0; i < 4096; i++) {
93 tmp[i] = 1e-5 * random<T>();
94 }
95 PROFILE_START("sirius::initialize_subspace|kp|wf");
96 /* fill remaining wave-functions with pseudo-random guess */
97 RTE_ASSERT(kp__.num_gkvec() > num_phi + 10);
98 #pragma omp parallel
99 {
100 for (int i = 0; i < num_phi - num_ao__; i++) {
101 #pragma omp for schedule(static) nowait
102 for (int igk_loc = 0; igk_loc < kp__.num_gkvec_loc(); igk_loc++) {
103 /* global index of G+k vector */
104 int igk = kp__.gkvec().offset() + igk_loc; // Hk__.kp().idxgk(igk_loc);
105 if (igk == i + 1) {
106 phi.pw_coeffs(igk_loc, wf::spin_index(0), wf::band_index(num_ao__ + i)) = 1.0;
107 }
108 if (igk == i + 2) {
109 phi.pw_coeffs(igk_loc, wf::spin_index(0), wf::band_index(num_ao__ + i)) = 0.5;
110 }
111 if (igk == i + 3) {
112 phi.pw_coeffs(igk_loc, wf::spin_index(0), wf::band_index(num_ao__ + i)) = 0.25;
113 }
114 }
115 }
116 /* add random noise */
117 for (int i = 0; i < num_phi; i++) {
118 #pragma omp for schedule(static) nowait
119 for (int igk_loc = kp__.gkvec().skip_g0(); igk_loc < kp__.num_gkvec_loc(); igk_loc++) {
120 /* global index of G+k vector */
121 int igk = kp__.gkvec().offset() + igk_loc;
122 phi.pw_coeffs(igk_loc, wf::spin_index(0), wf::band_index(i)) += tmp[igk & 0xFFF];
123 }
124 }
125 }
126
127 if (ctx.num_mag_dims() == 3) {
128 /* make pure spinor up- and dn- wave functions */
129 wf::copy(sddk::memory_t::host, phi, wf::spin_index(0), wf::band_range(0, num_phi), phi, wf::spin_index(1),
130 wf::band_range(num_phi, num_phi_tot));
131 }
132 PROFILE_STOP("sirius::initialize_subspace|kp|wf");
133
134 /* allocate wave-functions */
135 wf::Wave_functions<T> hphi(kp__.gkvec_sptr(), wf::num_mag_dims(ctx.num_mag_dims() == 3 ? 3 : 0),
136 wf::num_bands(num_phi_tot), sddk::memory_t::host);
137 wf::Wave_functions<T> ophi(kp__.gkvec_sptr(), wf::num_mag_dims(ctx.num_mag_dims() == 3 ? 3 : 0),
138 wf::num_bands(num_phi_tot), sddk::memory_t::host);
139 /* temporary wave-functions required as a storage during orthogonalization */
140 wf::Wave_functions<T> wf_tmp(kp__.gkvec_sptr(), wf::num_mag_dims(ctx.num_mag_dims() == 3 ? 3 : 0),
141 wf::num_bands(num_phi_tot), sddk::memory_t::host);
142
143 int bs = ctx.cyclic_block_size();
144
145 auto& gen_solver = ctx.gen_evp_solver();
146
147 la::dmatrix<F> hmlt(num_phi_tot, num_phi_tot, ctx.blacs_grid(), bs, bs, mp);
148 la::dmatrix<F> ovlp(num_phi_tot, num_phi_tot, ctx.blacs_grid(), bs, bs, mp);
149 la::dmatrix<F> evec(num_phi_tot, num_phi_tot, ctx.blacs_grid(), bs, bs, mp);
150
151 std::vector<real_type<F>> eval(num_bands);
152
153 print_memory_usage(ctx.out(), FILE_LINE);
154
155 auto mem = ctx.processing_unit() == sddk::device_t::CPU ? sddk::memory_t::host : sddk::memory_t::device;
156
157 std::vector<wf::device_memory_guard> mg;
158 mg.emplace_back(kp__.spinor_wave_functions().memory_guard(mem, wf::copy_to::host));
159 mg.emplace_back(phi.memory_guard(mem, wf::copy_to::device));
160 mg.emplace_back(hphi.memory_guard(mem));
161 mg.emplace_back(ophi.memory_guard(mem));
162 mg.emplace_back(wf_tmp.memory_guard(mem));
163
164 if (is_device_memory(mem)) {
165 auto& mpd = get_memory_pool(mem);
166 evec.allocate(mpd);
167 hmlt.allocate(mpd);
168 ovlp.allocate(mpd);
169 }
170
171 print_memory_usage(ctx.out(), FILE_LINE);
172
173 if (pcs) {
174 for (int ispn = 0; ispn < num_sc; ispn++) {
175 auto cs = phi.checksum(mem, wf::spin_index(ispn), wf::band_range(0, num_phi_tot));
176 if (kp__.comm().rank() == 0) {
177 std::stringstream s;
178 s << "initial_phi" << ispn;
179 print_checksum(s.str(), cs, RTE_OUT(std::cout));
180 }
181 }
182 }
183
184 for (int ispn_step = 0; ispn_step < ctx.num_spinors(); ispn_step++) {
185 /* apply Hamiltonian and overlap operators to the new basis functions */
186 Hk__.template apply_h_s<F>(ctx.num_mag_dims() == 3 ? wf::spin_range(0, 2) : wf::spin_range(ispn_step),
187 wf::band_range(0, num_phi_tot), phi, &hphi, &ophi);
188
189 /* do some checks */
190 // if (ctx_.cfg().control().verification() >= 1) {
191
192 // set_subspace_mtrx<T>(0, num_phi_tot, 0, phi, ophi, ovlp);
193 // if (ctx_.cfg().control().verification() >= 2 && ctx_.verbosity() >= 2) {
194 // auto s = ovlp.serialize("overlap", num_phi_tot, num_phi_tot);
195 // if (Hk__.kp().comm().rank() == 0) {
196 // ctx_.out() << s.str() << std::endl;
197 // }
198 // }
199
200 // double max_diff = check_hermitian(ovlp, num_phi_tot);
201 // if (max_diff > 1e-12) {
202 // std::stringstream s;
203 // s << "overlap matrix is not hermitian, max_err = " << max_diff;
204 // RTE_WARNING(s);
205 // }
206 // std::vector<real_type<T>> eo(num_phi_tot);
207 // auto& std_solver = ctx_.std_evp_solver();
208 // if (std_solver.solve(num_phi_tot, num_phi_tot, ovlp, eo.data(), evec)) {
209 // std::stringstream s;
210 // s << "error in diagonalization";
211 // RTE_WARNING(s);
212 // }
213 // Hk__.kp().message(1, __function_name__, "minimum eigen-value of the overlap matrix: %18.12f\n",
214 // eo[0]); if (eo[0] < 0) {
215 // RTE_WARNING("overlap matrix is not positively defined");
216 // }
217 // }
218
219 /* setup eigen-value problem */
220 generate_subspace_matrix(ctx, 0, num_phi_tot, 0, phi, hphi, hmlt);
221 generate_subspace_matrix(ctx, 0, num_phi_tot, 0, phi, ophi, ovlp);
222
223 if (pcs) {
224 auto cs1 = hmlt.checksum(num_phi_tot, num_phi_tot);
225 auto cs2 = ovlp.checksum(num_phi_tot, num_phi_tot);
226 if (kp__.comm().rank() == 0) {
227 print_checksum("hmlt", cs1, RTE_OUT(std::cout));
228 print_checksum("ovlp", cs2, RTE_OUT(std::cout));
229 }
230 }
231
232 // if (ctx_.cfg().control().verification() >= 2 && ctx_.verbosity() >= 2) {
233 // auto s1 = hmlt.serialize("hmlt", num_phi_tot, num_phi_tot);
234 // auto s2 = hmlt.serialize("ovlp", num_phi_tot, num_phi_tot);
235 // if (Hk__.kp().comm().rank() == 0) {
236 // ctx_.out() << s1.str() << std::endl << s2.str() << std::endl;
237 // }
238 // }
239
240 /* solve generalized eigen-value problem with the size N and get lowest num_bands eigen-vectors */
241 if (gen_solver.solve(num_phi_tot, num_bands, hmlt, ovlp, eval.data(), evec)) {
242 RTE_THROW("error in diagonalization");
243 }
244
245 // if (ctx_.print_checksum()) {
246 // auto cs = evec.checksum(num_phi_tot, num_bands);
247 // real_type<T> cs1{0};
248 // for (int i = 0; i < num_bands; i++) {
249 // cs1 += eval[i];
250 // }
251 // if (Hk__.kp().comm().rank() == 0) {
252 // utils::print_checksum("evec", cs);
253 // utils::print_checksum("eval", cs1);
254 // }
255 // }
256 {
257 rte::ostream out(kp__.out(3), std::string(__func__));
258 for (int i = 0; i < num_bands; i++) {
259 out << "eval[" << i << "]=" << eval[i] << std::endl;
260 }
261 }
262
263 /* compute wave-functions */
264 /* \Psi_{i} = \sum_{mu} \phi_{mu} * Z_{mu, i} */
265 for (int ispn = 0; ispn < num_sc; ispn++) {
266 wf::transform(ctx.spla_context(), mem, evec, 0, 0, 1.0, phi, wf::spin_index(num_sc == 2 ? ispn : 0),
267 wf::band_range(0, num_phi_tot), 0.0, kp__.spinor_wave_functions(),
268 wf::spin_index(num_sc == 2 ? ispn : ispn_step), wf::band_range(0, num_bands));
269 }
270
271 for (int j = 0; j < num_bands; j++) {
272 kp__.band_energy(j, ispn_step, eval[j]);
273 }
274 }
275
276 if (pcs) {
277 for (int ispn = 0; ispn < ctx.num_spins(); ispn++) {
278 auto cs = kp__.spinor_wave_functions().checksum(mem, wf::spin_index(ispn), wf::band_range(0, num_bands));
279 std::stringstream s;
280 s << "initial_spinor_wave_functions_" << ispn;
281 if (kp__.comm().rank() == 0) {
282 print_checksum(s.str(), cs, RTE_OUT(std::cout));
283 }
284 }
285 }
286}
287
288template <typename T>
289void
290initialize_subspace(K_point_set& kset__, Hamiltonian0<T>& H0__)
291{
292 PROFILE("sirius::initialize_subspace_kset");
293
294 int N{0};
295
296 auto& ctx = H0__.ctx();
297
298 if (ctx.cfg().iterative_solver().init_subspace() == "lcao") {
299 /* get the total number of atomic-centered orbitals */
300 N = ctx.unit_cell().num_ps_atomic_wf().first;
301 }
302
303 for (auto it : kset__.spl_num_kpoints()) {
304 auto kp = kset__.get<T>(it.i);
305 auto Hk = H0__(*kp);
306 if (ctx.gamma_point() && (ctx.so_correction() == false)) {
307 initialize_subspace<T, T>(Hk, *kp, N);
308 } else {
309 initialize_subspace<T, std::complex<T>>(Hk, *kp, N);
310 }
311 }
312
313 /* reset the energies for the iterative solver to do at least two steps */
314 for (int ik = 0; ik < kset__.num_kpoints(); ik++) {
315 for (int ispn = 0; ispn < ctx.num_spinors(); ispn++) {
316 for (int i = 0; i < ctx.num_bands(); i++) {
317 kset__.get<T>(ik)->band_energy(i, ispn, 0);
318 kset__.get<T>(ik)->band_occupancy(i, ispn, ctx.max_occupancy());
319 }
320 }
321 }
322}
323
324} // namespace sirius
325
326#endif
Representation of Kohn-Sham Hamiltonian.
K-point related variables and methods.
Definition: k_point.hpp:44
void generate_atomic_wave_functions(std::vector< int > atoms__, std::function< basis_functions_index const *(int)> indexb__, Radial_integrals_atomic_wf< false > const &ri__, wf::Wave_functions< T > &wf__)
Generate plane-wave coefficients of the atomic wave-functions.
Definition: k_point.cpp:678
auto gkvec_sptr() const
Return shared pointer to gkvec object.
Definition: k_point.hpp:381
std::ostream & out(int level__) const
Return stdout stream for high verbosity output.
Definition: k_point.hpp:729
double band_energy(int j__, int ispn__) const
Get band energy.
Definition: k_point.hpp:413
int num_gkvec() const
Total number of G+k vectors within the cutoff distance.
Definition: k_point.hpp:387
int num_gkvec_loc() const
Local number of G+k vectors in case of flat distribution.
Definition: k_point.hpp:393
Distributed matrix.
Definition: dmatrix.hpp:56
mdarray< T, N > & allocate(memory_t memory__)
Allocate memory for array.
Definition: memory.hpp:1057
auto memory_guard(sddk::memory_t mem__, wf::copy_to copy_to__=copy_to::none) const
Return an instance of the memory guard.
void zero(sddk::memory_t mem__, spin_index s__, band_range br__)
Zero a spin component of the wave-functions in a band range.
Wave-functions representation.
auto & pw_coeffs(int ig__, spin_index ispn__, band_index i__)
Return reference to the plane-wave coefficient for a given plane-wave, spin and band indices.
Describe a range of bands.
Describe a range of spins.
Diagonalize pseudo-potential Hamiltonian.
Contains declaration and partial implementation of sirius::K_point_set class.
bool is_device_memory(memory_t mem__)
Check if this is a valid device memory (memory, accessible by the device).
Definition: memory.hpp:93
std::enable_if_t< std::is_same< T, real_type< F > >::value, void > transform(::spla::Context &spla_ctx__, sddk::memory_t mem__, la::dmatrix< F > const &M__, int irow0__, int jcol0__, real_type< F > alpha__, Wave_functions< T > const &wf_in__, spin_index s_in__, band_range br_in__, real_type< F > beta__, Wave_functions< T > &wf_out__, spin_index s_out__, band_range br_out__)
Apply linear transformation to the wave-functions.
void copy(sddk::memory_t mem__, Wave_functions< T > const &in__, wf::spin_index s_in__, wf::band_range br_in__, Wave_functions< F > &out__, wf::spin_index s_out__, wf::band_range br_out__)
Copy wave-functions.
Namespace of the SIRIUS library.
Definition: sirius.f90:5
uint32_t random_uint32(bool reset=false)
Simple random number generator.
Definition: math_tools.hpp:105
void initialize_subspace(Hamiltonian_k< T > const &Hk__, K_point< T > &kp__, int num_ao__)
Initialize the wave-functions subspace at a given k-point.
void generate_subspace_matrix(Simulation_context &ctx__, int N__, int n__, int num_locked__, wf::Wave_functions< T > &phi__, wf::Wave_functions< T > &op_phi__, la::dmatrix< F > &mtrx__, la::dmatrix< F > *mtrx_old__=nullptr)
Generate subspace matrix for the iterative diagonalization.