SIRIUS 7.5.0
Electronic structure library and applications
k_point_set.cpp
1// Copyright (c) 2013-2021 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#include <limits>
21#include "dft/smearing.hpp"
22#include "k_point/k_point.hpp"
25#include <iomanip>
26
27namespace sirius {
28
29template <typename T, sync_band_t what>
31{
32 PROFILE("sirius::K_point_set::sync_band");
33
35 get_memory_pool(sddk::memory_t::host), "K_point_set::sync_band.data");
36 data.zero();
37
38 int nb = ctx_.num_bands() * ctx_.num_spinors();
39 #pragma omp parallel
40 for (auto it : spl_num_kpoints_) {
41 auto kp = this->get<T>(it.i);
42 switch (what) {
43 case sync_band_t::energy: {
44 std::copy(&kp->band_energies_(0, 0), &kp->band_energies_(0, 0) + nb, &data(0, 0, it.i));
45 break;
46 }
47 case sync_band_t::occupancy: {
48 std::copy(&kp->band_occupancies_(0, 0), &kp->band_occupancies_(0, 0) + nb, &data(0, 0, it.i));
49 break;
50 }
51 }
52 }
53
54 comm().allreduce(data.at(sddk::memory_t::host), static_cast<int>(data.size()));
55
56 #pragma omp parallel for
57 for (int ik = 0; ik < num_kpoints(); ik++) {
58 auto kp = this->get<T>(ik);
59 switch (what) {
60 case sync_band_t::energy: {
61 std::copy(&data(0, 0, ik), &data(0, 0, ik) + nb, &kp->band_energies_(0, 0));
62 break;
63 }
64 case sync_band_t::occupancy: {
65 std::copy(&data(0, 0, ik), &data(0, 0, ik) + nb, &kp->band_occupancies_(0, 0));
66 break;
67 }
68 }
69 }
70}
71
72template
73void
74K_point_set::sync_band<double, sync_band_t::energy>();
75
76template
77void
78K_point_set::sync_band<double, sync_band_t::occupancy>();
79
80#if defined(SIRIUS_USE_FP32)
81template
82void
83K_point_set::sync_band<float, sync_band_t::energy>();
84
85template
86void
87K_point_set::sync_band<float, sync_band_t::occupancy>();
88#endif
89
90void K_point_set::create_k_mesh(r3::vector<int> k_grid__, r3::vector<int> k_shift__, int use_symmetry__)
91{
92 PROFILE("sirius::K_point_set::create_k_mesh");
93
94 int nk;
96 std::vector<double> wk;
97 if (use_symmetry__) {
98 auto result = get_irreducible_reciprocal_mesh(ctx_.unit_cell().symmetry(), k_grid__, k_shift__);
99 nk = std::get<0>(result);
100 wk = std::get<1>(result);
101 auto tmp = std::get<2>(result);
102 kp = sddk::mdarray<double, 2>(3, nk);
103 for (int i = 0; i < nk; i++) {
104 for (int x : {0, 1, 2}) {
105 kp(x, i) = tmp[i][x];
106 }
107 }
108 } else {
109 nk = k_grid__[0] * k_grid__[1] * k_grid__[2];
110 wk = std::vector<double>(nk, 1.0 / nk);
111 kp = sddk::mdarray<double, 2>(3, nk);
112
113 int ik = 0;
114 for (int i0 = 0; i0 < k_grid__[0]; i0++) {
115 for (int i1 = 0; i1 < k_grid__[1]; i1++) {
116 for (int i2 = 0; i2 < k_grid__[2]; i2++) {
117 kp(0, ik) = double(i0 + k_shift__[0] / 2.0) / k_grid__[0];
118 kp(1, ik) = double(i1 + k_shift__[1] / 2.0) / k_grid__[1];
119 kp(2, ik) = double(i2 + k_shift__[2] / 2.0) / k_grid__[2];
120 ik++;
121 }
122 }
123 }
124 }
125
126 for (int ik = 0; ik < nk; ik++) {
127 add_kpoint(&kp(0, ik), wk[ik]);
128 }
129
130 initialize();
131}
132
133void K_point_set::initialize(std::vector<int> const& counts)
134{
135 if (this->initialized_) {
136 RTE_THROW("K-point set is already initialized");
137 }
138 PROFILE("sirius::K_point_set::initialize");
139 /* distribute k-points along the 1-st dimension of the MPI grid */
140 if (counts.empty()) {
141 splindex_block<> spl_tmp(num_kpoints(), n_blocks(comm().size()), block_id(comm().rank()));
143 block_id(comm().rank()), spl_tmp.counts());
144 } else {
146 block_id(comm().rank()), counts);
147 }
148
149 for (auto it : spl_num_kpoints_) {
150 kpoints_[it.i]->initialize();
151#if defined(SIRIUS_USE_FP32)
152 kpoints_float_[it.i]->initialize();
153#endif
154 }
155
156 if (ctx_.verbosity() > 0) {
157 this->print_info();
158 }
159 print_memory_usage(ctx_.out(), FILE_LINE);
160 this->initialized_ = true;
161}
162
163
164template<class F>
165double bisection_search(F&& f, double a, double b, double tol, int maxstep=1000)
166{
167 double x = (a+b)/2;
168 double fi = f(x);
169 int step{0};
170 /* compute fermy energy */
171 while (std::abs(fi) >= tol) {
172 /* compute total number of electrons */
173
174 if (fi > 0) {
175 b = x;
176 } else {
177 a = x;
178 }
179
180 x = (a + b) / 2.0;
181 fi = f(x);
182
183 if (step > maxstep) {
184 std::stringstream s;
185 s << "search of band occupancies failed after 10000 steps";
186 RTE_THROW(s);
187 }
188 step++;
189 }
190
191 return x;
192}
193
194/**
195 * Newton minimization to determine the chemical potential.
196 *
197 * \param N number of electrons as a function of \f$\mu\f$
198 * \param dN \f$\partial_\mu N(\mu)\f$
199 * \param ddN \f$\partial^2_\mu N(\mu)\f$
200 * \param mu0 initial guess
201 * \param ne target number of electrons
202 * \param tol tolerance
203 * \param maxstep max number of Newton iterations
204 */
205template <class Nt, class DNt, class D2Nt>
206auto
207newton_minimization_chemical_potential(Nt&& N, DNt&& dN, D2Nt&& ddN, double mu0, double ne, double tol, int maxstep = 1000)
208{
209 // Newton finds the minimum, not necessarily N(mu) == ne, tolerate up to `tol_ne` difference in number of electrons
210 // if |N(mu_0) -ne| > tol_ne an error is thrown.
211 const double tol_ne = 1e-2;
212
213 struct {
214 double mu; // chemical potential
215 int iter{0}; // newton information
216 std::vector<double> ys; // newton history
217 } res;
218
219 double mu = mu0;
220 double alpha{1.0}; // Newton damping
221 int iter{0};
222
223 if ( std::abs(N(mu) - ne) < tol) {
224 res.mu = mu;
225 res.iter = iter;
226 res.ys = {};
227 return res;
228 }
229
230 while (true) {
231 // compute
232 double Nf = N(mu);
233 double dNf = dN(mu);
234 double ddNf = ddN(mu);
235 /* minimize (N(mu) - ne)^2 */
236 //double F = (Nf - ne) * (Nf - ne);
237 double dF = 2 * (Nf - ne) * dNf;
238 double ddF = 2 * dNf * dNf + 2 * (Nf - ne) * ddNf;
239 double step = alpha * dF / std::abs(ddF);
240 mu = mu - step;
241
242 res.ys.push_back(mu);
243
244 if (std::abs(ddF) < 1e-30) {
245 std::stringstream s;
246 s << "Newton minimization (Fermi energy) failed because 2nd derivative too close to zero!"
247 << std::setprecision(8) << std::abs(Nf - ne) << "\n";
248 RTE_THROW(s);
249 }
250
251 if (std::abs(step) < tol || std::abs(Nf - ne) < tol) {
252 if (std::abs(Nf - ne) > tol_ne) {
253 std::stringstream s;
254 s << "Newton minimization (Fermi energy) got stuck in a local minimum. Fallback to bisection search." << "\n";
255 RTE_THROW(s);
256 }
257
258 res.iter = iter;
259 res.mu = mu;
260 return res;
261 }
262
263 iter++;
264 if (iter > maxstep) {
265 std::stringstream s;
266 s << "Newton minimization (chemical potential) failed after " << maxstep << " steps!" << std::endl
267 << "target number of electrons : " << ne << std::endl
268 << "initial guess for chemical potential : " << mu0 << std::endl
269 << "current value of chemical potential : " << mu;
270 RTE_THROW(s);
271 }
272 }
273}
274
275template <typename T>
277{
278 PROFILE("sirius::K_point_set::find_band_occupancies");
279
280 double tol{1e-11};
281
282 auto band_occ_callback = ctx_.band_occ_callback();
283 if (band_occ_callback) {
284 band_occ_callback();
285 return;
286 }
287
288 /* target number of electrons */
289 const double ne_target = ctx_.unit_cell().num_valence_electrons() - ctx_.cfg().parameters().extra_charge();
290
291 /* this is a special case when there are no empty states */
292 if (ctx_.num_mag_dims() != 1 && std::abs(ctx_.num_bands() * ctx_.max_occupancy() - ne_target) < 1e-10) {
293 /* this is an insulator, skip search for band occupancies */
294 this->band_gap_ = 0;
295
296 /* determine fermi energy as max occupied band energy. */
297 energy_fermi_ = std::numeric_limits<double>::lowest();
298 for (int ik = 0; ik < num_kpoints(); ik++) {
299 for (int ispn = 0; ispn < ctx_.num_spinors(); ispn++) {
300 for (int j = 0; j < ctx_.num_bands(); j++) {
301 energy_fermi_ = std::max(energy_fermi_, this->get<T>(ik)->band_energy(j, ispn));
302 }
303 }
304 }
305 for (auto it : spl_num_kpoints_) {
306 for (int ispn = 0; ispn < ctx_.num_spinors(); ispn++) {
307 #pragma omp parallel for
308 for (int j = 0; j < ctx_.num_bands(); j++) {
309 this->get<T>(it.i)->band_occupancy(j, ispn, ctx_.max_occupancy());
310 }
311 }
312 }
313
314 this->sync_band<T, sync_band_t::occupancy>();
315 return;
316 }
317
318 if (ctx_.smearing_width() == 0) {
319 RTE_THROW("zero smearing width");
320 }
321
322 /* get minimum and maximum band energies */
323
324 auto emin = std::numeric_limits<double>::max();
325 auto emax = std::numeric_limits<double>::lowest();
326
327 #pragma omp parallel for reduction(min:emin) reduction(max:emax)
328 for (auto it : spl_num_kpoints_) {
329 for (int ispn = 0; ispn < ctx_.num_spinors(); ispn++) {
330 emin = std::min(emin, this->get<T>(it.i)->band_energy(0, ispn));
331 emax = std::max(emax, this->get<T>(it.i)->band_energy(ctx_.num_bands() - 1, ispn));
332 }
333 }
334 comm().allreduce<double, mpi::op_t::min>(&emin, 1);
335 comm().allreduce<double, mpi::op_t::max>(&emax, 1);
336
338
339 /* computes N(ef; f) = \sum_{i,k} f(ef - e_{k,i}) */
340 auto compute_ne = [&](double ef, auto&& f) {
341 double ne{0};
342 for (auto it : spl_num_kpoints_) {
343 double tmp{0};
344 #pragma omp parallel reduction(+ : tmp)
345 for (int ispn = 0; ispn < ctx_.num_spinors(); ispn++) {
346 #pragma omp for
347 for (int j = 0; j < splb.local_size(); j++) {
348 tmp += f(ef - this->get<T>(it.i)->band_energy(splb.global_index(j), ispn)) * ctx_.max_occupancy();
349 }
350 }
351 ne += tmp * kpoints_[it.i]->weight();
352 }
353 ctx_.comm().allreduce(&ne, 1);
354 return ne;
355 };
356
357 /* smearing function */
358 std::function<double(double)> f;
359 if (ctx_.smearing() == smearing::smearing_t::cold || ctx_.smearing() == smearing::smearing_t::methfessel_paxton) {
360 // obtain initial guess for non-monotous smearing with Gaussian
361 f = [&](double x) { return smearing::gaussian::occupancy(x, ctx_.smearing_width()); };
362 } else {
363 f = smearing::occupancy(ctx_.smearing(), ctx_.smearing_width());
364 }
365
366 try {
367 auto F = [&compute_ne, ne_target, &f](double x) { return compute_ne(x, f) - ne_target; };
368 energy_fermi_ = bisection_search(F, emin, emax, 1e-11);
369
370 /* for cold and Methfessel Paxton smearing start newton minimization */
371 if (ctx_.smearing() == smearing::smearing_t::cold || ctx_.smearing() == smearing::smearing_t::methfessel_paxton) {
372 f = smearing::occupancy(ctx_.smearing(), ctx_.smearing_width());
373 auto df = smearing::delta(ctx_.smearing(), ctx_.smearing_width());
374 auto ddf = smearing::dxdelta(ctx_.smearing(), ctx_.smearing_width());
375 auto N = [&](double mu) { return compute_ne(mu, f); };
376 auto dN = [&](double mu) { return compute_ne(mu, df); };
377 auto ddN = [&](double mu) { return compute_ne(mu, ddf); };
378 auto res_newton = newton_minimization_chemical_potential(N, dN, ddN, energy_fermi_, ne_target, tol, 1000);
379 energy_fermi_ = res_newton.mu;
380 if (ctx_.verbosity() >= 2) {
381 RTE_OUT(ctx_.out()) << "newton iteration converged after " << res_newton.iter << " steps\n";
382 }
383 }
384 } catch(std::exception const& e) {
385 if (ctx_.verbosity() >= 2) {
386 RTE_OUT(ctx_.out()) << e.what() << std::endl
387 << "fallback to bisection search" << std::endl;
388 }
389 f = smearing::occupancy(ctx_.smearing(), ctx_.smearing_width());
390 auto F = [&compute_ne, ne_target, &f](double x) { return compute_ne(x, f) - ne_target; };
391 energy_fermi_ = bisection_search(F, emin, emax, tol);
392 }
393
394 for (auto it : spl_num_kpoints_) {
395 for (int ispn = 0; ispn < ctx_.num_spinors(); ispn++) {
396 #pragma omp parallel for
397 for (int j = 0; j < ctx_.num_bands(); j++) {
398 auto o = f(energy_fermi_ - this->get<T>(it.i)->band_energy(j, ispn)) * ctx_.max_occupancy();
399 this->get<T>(it.i)->band_occupancy(j, ispn, o);
400 }
401 }
402 }
403
404 this->sync_band<T, sync_band_t::occupancy>();
405
406 band_gap_ = 0.0;
407
408 int nve = static_cast<int>(ne_target + 1e-12);
409 if (ctx_.num_spins() == 2 || (std::abs(nve - ne_target) < 1e-12 && nve % 2 == 0)) {
410 /* find band gap */
411 std::vector<std::pair<double, double>> eband(ctx_.num_bands() * ctx_.num_spinors());
412
413 for (int ispn = 0; ispn < ctx_.num_spinors(); ispn++) {
414 #pragma omp for
415 for (int j = 0; j < ctx_.num_bands(); j++) {
416 std::pair<double, double> eminmax;
417 eminmax.first = std::numeric_limits<double>::max();
418 eminmax.second = std::numeric_limits<double>::lowest();
419
420 for (int ik = 0; ik < num_kpoints(); ik++) {
421 eminmax.first = std::min(eminmax.first, this->get<T>(ik)->band_energy(j, ispn));
422 eminmax.second = std::max(eminmax.second, this->get<T>(ik)->band_energy(j, ispn));
423 }
424
425 eband[j + ispn * ctx_.num_bands()] = eminmax;
426 }
427 }
428
429 std::sort(eband.begin(), eband.end());
430
431 int ist = nve;
432 if (ctx_.num_spins() == 1) {
433 ist /= 2;
434 }
435
436 if (eband[ist].first > eband[ist - 1].second) {
437 band_gap_ = eband[ist].first - eband[ist - 1].second;
438 }
439 }
440}
441
442template
443void K_point_set::find_band_occupancies<double>();
444#if defined(SIRIUS_USE_FP32)
445template
446void K_point_set::find_band_occupancies<float>();
447#endif
448
449template <typename T>
451{
452 double eval_sum{0};
453
455
456 for (auto it : spl_num_kpoints_) {
457 auto const& kp = this->get<T>(it.i);
458 double tmp{0};
459 #pragma omp parallel for reduction(+:tmp)
460 for (int j = 0; j < splb.local_size(); j++) {
461 for (int ispn = 0; ispn < ctx_.num_spinors(); ispn++) {
462 tmp += kp->band_energy(splb.global_index(j), ispn) * kp->band_occupancy(splb.global_index(j), ispn);
463 }
464 }
465 eval_sum += kp->weight() * tmp;
466 }
468
469 return eval_sum;
470}
471
473{
474 if (ctx_.cfg().parameters().precision_wf() == "fp32") {
475#if defined(SIRIUS_USE_FP32)
476 return this->valence_eval_sum<float>();
477#else
478 RTE_THROW("not compiled with FP32 support");
479 return 0; // make compiled happy
480#endif
481 } else {
482 return this->valence_eval_sum<double>();
483 }
484}
485
486template <typename T>
488{
489 double s_sum{0};
490
491 double ne_target = ctx_.unit_cell().num_valence_electrons() - ctx_.cfg().parameters().extra_charge();
492
493 bool only_occ = (ctx_.num_mag_dims() != 1 &&
494 std::abs(ctx_.num_bands() * ctx_.max_occupancy() - ne_target) < 1e-10);
495
496 if (only_occ) {
497 return 0;
498 }
499
500 auto f = smearing::entropy(ctx_.smearing(), ctx_.smearing_width());
501
503
504 for (auto it : spl_num_kpoints_) {
505 auto const& kp = this->get<T>(it.i);
506 double tmp{0};
507 #pragma omp parallel for reduction(+:tmp)
508 for (int j = 0; j < splb.local_size(); j++) {
509 for (int ispn = 0; ispn < ctx_.num_spinors(); ispn++) {
510 tmp += ctx_.max_occupancy() * f(energy_fermi_ - kp->band_energy(splb.global_index(j), ispn));
511 }
512 }
513 s_sum += kp->weight() * tmp;
514 }
515 ctx_.comm().allreduce(&s_sum, 1);
516
517 return s_sum;
518}
519
521{
522 if (ctx_.cfg().parameters().precision_wf() == "fp32") {
523#if defined(SIRIUS_USE_FP32)
524 return this->entropy_sum<float>();
525#else
526 RTE_THROW("not compiled with FP32 support");
527 return 0; // make compiler happy
528#endif
529 } else {
530 return this->entropy_sum<double>();
531 }
532}
533
535{
536 mpi::pstdout pout(this->comm());
537
538 if (ctx_.comm().rank() == 0) {
539 pout << std::endl;
540 pout << "total number of k-points : " << num_kpoints() << std::endl;
541 pout << hbar(80, '-') << std::endl;
542 pout << std::endl;
543 pout << " ik vk weight num_gkvec";
544 if (ctx_.full_potential()) {
545 pout << " gklo_basis_size";
546 }
547 pout << std::endl << hbar(80, '-') << std::endl;
548 }
549
550 for (auto it : spl_num_kpoints()) {
551 int ik = it.i;
552 pout << std::setw(4) << ik << ffmt(9, 4) << kpoints_[ik]->vk()[0] << ffmt(9, 4)
553 << kpoints_[ik]->vk()[1] << ffmt(9, 4) << kpoints_[ik]->vk()[2] << ffmt(17, 6)
554 << kpoints_[ik]->weight() << std::setw(11) << kpoints_[ik]->num_gkvec();
555
556 if (ctx_.full_potential()) {
557 pout << std::setw(18) << kpoints_[ik]->gklo_basis_size();
558 }
559 pout << std::endl;
560 }
561 RTE_OUT(ctx_.out()) << pout.flush(0);
562}
563
564void K_point_set::save(std::string const& name__) const
565{
566 if (ctx_.comm().rank() == 0) {
567 if (!file_exists(name__)) {
568 HDF5_tree(name__, hdf5_access_t::truncate);
569 }
570 HDF5_tree fout(name__, hdf5_access_t::read_write);
571 fout.create_node("K_point_set");
572 fout["K_point_set"].write("num_kpoints", num_kpoints());
573 }
574 ctx_.comm().barrier();
575 for (int ik = 0; ik < num_kpoints(); ik++) {
576 /* check if this ranks stores the k-point */
577 if (ctx_.comm_k().rank() == spl_num_kpoints_.location(typename kp_index_t::global(ik)).ib) {
578 this->get<double>(ik)->save(name__, ik);
579 }
580 /* wait for all */
581 ctx_.comm().barrier();
582 }
583}
584
585/// \todo check parameters of saved data in a separate function
587{
588 RTE_THROW("not implemented");
589
590 //== HDF5_tree fin(storage_file_name, false);
591
592 //== int num_kpoints_in;
593 //== fin["K_point_set"].read("num_kpoints", &num_kpoints_in);
594
595 //== std::vector<int> ikidx(num_kpoints(), -1);
596 //== // read available k-points
597 //== double vk_in[3];
598 //== for (int jk = 0; jk < num_kpoints_in; jk++)
599 //== {
600 //== fin["K_point_set"][jk].read("coordinates", vk_in, 3);
601 //== for (int ik = 0; ik < num_kpoints(); ik++)
602 //== {
603 //== r3::vector<double> dvk;
604 //== for (int x = 0; x < 3; x++) dvk[x] = vk_in[x] - kpoints_[ik]->vk(x);
605 //== if (dvk.length() < 1e-12)
606 //== {
607 //== ikidx[ik] = jk;
608 //== break;
609 //== }
610 //== }
611 //== }
612
613 //== for (int ik = 0; ik < num_kpoints(); ik++)
614 //== {
615 //== int rank = spl_num_kpoints_.local_rank(ik);
616 //==
617 //== if (comm_.rank() == rank) kpoints_[ik]->load(fin["K_point_set"], ikidx[ik]);
618 //== }
619}
620
621//== void K_point_set::save_wave_functions()
622//== {
623//== if (Platform::mpi_rank() == 0)
624//== {
625//== HDF5_tree fout(storage_file_name, false);
626//== fout["parameters"].write("num_kpoints", num_kpoints());
627//== fout["parameters"].write("num_bands", ctx_.num_bands());
628//== fout["parameters"].write("num_spins", ctx_.num_spins());
629//== }
630//==
631//== if (ctx_.mpi_grid().side(1 << _dim_k_ | 1 << _dim_col_))
632//== {
633//== for (int ik = 0; ik < num_kpoints(); ik++)
634//== {
635//== int rank = spl_num_kpoints_.location(_splindex_rank_, ik);
636//==
637//== if (ctx_.mpi_grid().coordinate(_dim_k_) == rank) kpoints_[ik]->save_wave_functions(ik);
638//==
639//== ctx_.mpi_grid().barrier(1 << _dim_k_ | 1 << _dim_col_);
640//== }
641//== }
642//== }
643//==
644//== void K_point_set::load_wave_functions()
645//== {
646//== HDF5_tree fin(storage_file_name, false);
647//== int num_spins;
648//== fin["parameters"].read("num_spins", &num_spins);
649//== if (num_spins != ctx_.num_spins()) error_local(__FILE__, __LINE__, "wrong number of spins");
650//==
651//== int num_bands;
652//== fin["parameters"].read("num_bands", &num_bands);
653//== if (num_bands != ctx_.num_bands()) error_local(__FILE__, __LINE__, "wrong number of bands");
654//==
655//== int num_kpoints_in;
656//== fin["parameters"].read("num_kpoints", &num_kpoints_in);
657//==
658//== // ==================================================================
659//== // index of current k-points in the hdf5 file, which (in general) may
660//== // contain a different set of k-points
661//== // ==================================================================
662//== std::vector<int> ikidx(num_kpoints(), -1);
663//== // read available k-points
664//== double vk_in[3];
665//== for (int jk = 0; jk < num_kpoints_in; jk++)
666//== {
667//== fin["kpoints"][jk].read("coordinates", vk_in, 3);
668//== for (int ik = 0; ik < num_kpoints(); ik++)
669//== {
670//== r3::vector<double> dvk;
671//== for (int x = 0; x < 3; x++) dvk[x] = vk_in[x] - kpoints_[ik]->vk(x);
672//== if (dvk.length() < 1e-12)
673//== {
674//== ikidx[ik] = jk;
675//== break;
676//== }
677//== }
678//== }
679//==
680//== for (int ik = 0; ik < num_kpoints(); ik++)
681//== {
682//== int rank = spl_num_kpoints_.location(_splindex_rank_, ik);
683//==
684//== if (ctx_.mpi_grid().coordinate(0) == rank) kpoints_[ik]->load_wave_functions(ikidx[ik]);
685//== }
686//== }
687
688//== void K_point_set::fixed_band_occupancies()
689//== {
690//== Timer t("sirius::K_point_set::fixed_band_occupancies");
691//==
692//== if (ctx_.num_mag_dims() != 1) error_local(__FILE__, __LINE__, "works only for collinear magnetism");
693//==
694//== double n_up = (ctx_.num_valence_electrons() + ctx_.fixed_moment()) / 2.0;
695//== double n_dn = (ctx_.num_valence_electrons() - ctx_.fixed_moment()) / 2.0;
696//==
697//== mdarray<double, 2> bnd_occ(ctx_.num_bands(), num_kpoints());
698//== bnd_occ.zero();
699//==
700//== int j = 0;
701//== while (n_up > 0)
702//== {
703//== for (int ik = 0; ik < num_kpoints(); ik++) bnd_occ(j, ik) = std::min(double(ctx_.max_occupancy()), n_up);
704//== j++;
705//== n_up -= ctx_.max_occupancy();
706//== }
707//==
708//== j = ctx_.num_fv_states();
709//== while (n_dn > 0)
710//== {
711//== for (int ik = 0; ik < num_kpoints(); ik++) bnd_occ(j, ik) = std::min(double(ctx_.max_occupancy()), n_dn);
712//== j++;
713//== n_dn -= ctx_.max_occupancy();
714//== }
715//==
716//== for (int ik = 0; ik < num_kpoints(); ik++) kpoints_[ik]->set_band_occupancies(&bnd_occ(0, ik));
717//==
718//== double gap = 0.0;
719//==
720//== int nve = int(ctx_.num_valence_electrons() + 1e-12);
721//== if ((ctx_.num_spins() == 2) ||
722//== ((fabs(nve - ctx_.num_valence_electrons()) < 1e-12) && nve % 2 == 0))
723//== {
724//== // find band gap
725//== std::vector< std::pair<double, double> > eband;
726//== std::pair<double, double> eminmax;
727//==
728//== for (int j = 0; j < ctx_.num_bands(); j++)
729//== {
730//== eminmax.first = 1e10;
731//== eminmax.second = -1e10;
732//==
733//== for (int ik = 0; ik < num_kpoints(); ik++)
734//== {
735//== eminmax.first = std::min(eminmax.first, kpoints_[ik]->band_energy(j));
736//== eminmax.second = std::max(eminmax.second, kpoints_[ik]->band_energy(j));
737//== }
738//==
739//== eband.push_back(eminmax);
740//== }
741//==
742//== std::sort(eband.begin(), eband.end());
743//==
744//== int ist = nve;
745//== if (ctx_.num_spins() == 1) ist /= 2;
746//==
747//== if (eband[ist].first > eband[ist - 1].second) gap = eband[ist].first - eband[ist - 1].second;
748//==
749//== band_gap_ = gap;
750//== }
751//== }
752
753} // namespace sirius
Interface to the HDF5 library.
Definition: hdf5_tree.hpp:86
HDF5_tree create_node(int idx)
Create node by integer index.
Definition: hdf5_tree.hpp:424
void write(const std::string &name, T const *data, std::vector< int > const &dims)
Write a multidimensional array.
Definition: hdf5_tree.hpp:301
int num_kpoints() const
Return total number of k-points.
double entropy_sum() const
Return entropy contribution from smearing store in Kpoint<T>.
double energy_fermi_
Fermi energy which is searched in find_band_occupancies().
Definition: k_point_set.hpp:58
void add_kpoint(r3::vector< double > vk__, double weight__)
Add k-point to the set.
std::vector< std::unique_ptr< K_point< double > > > kpoints_
List of k-points.
Definition: k_point_set.hpp:47
void initialize(std::vector< int > const &counts={})
Initialize the k-point set.
Simulation_context & ctx_
Context of a simulation.
Definition: k_point_set.hpp:44
double valence_eval_sum() const
Return sum of valence eigen-values store in Kpoint<T>.
void sync_band()
Sync band energies or occupancies between all MPI ranks.
Definition: k_point_set.cpp:30
void print_info()
Print basic info to the standard output.
void find_band_occupancies()
Find Fermi energy and band occupation numbers.
void create_k_mesh(r3::vector< int > k_grid__, r3::vector< int > k_shift__, int use_symmetry__)
Create regular grid of k-points.
Definition: k_point_set.cpp:90
double band_gap_
Band gap found by find_band_occupancies().
Definition: k_point_set.hpp:61
void save(std::string const &name__) const
Save k-point set to HDF5 file.
splindex_chunk< kp_index_t > spl_num_kpoints_
Split index of k-points.
Definition: k_point_set.hpp:55
auto const & comm_band() const
Band parallelization communicator.
auto const & comm_k() const
Communicator between k-points.
std::ostream & out() const
Return output stream.
mpi::Communicator const & comm() const
Total communicator of the simulation.
int num_spins() const
Number of spin components.
int num_spinors() const
Number of spinor wave-functions labeled by a sinlge band index.
int max_occupancy() const
Maximum band occupancy.
int num_bands(int num_bands__)
Set the number of bands.
int num_mag_dims() const
Number of dimensions in the magnetization vector.
Floating-point formatting (precision and width).
Horisontal bar.
void allreduce(T *buffer__, int count__) const
Perform the in-place (the output buffer is used as the input buffer) all-to-all reduction.
int rank() const
Rank of MPI process inside communicator.
Parallel standard output.
Definition: pstdout.hpp:42
Simple implementation of 3d vector.
Definition: r3.hpp:45
void zero(memory_t mem__, size_t idx0__, size_t n__)
Zero n elements starting from idx0.
Definition: memory.hpp:1316
size_t size() const
Return total size (number of elements) of the array.
Definition: memory.hpp:1207
value_type local_size(block_id block_id__) const
Return local size of the split index for a given block.
Definition: splindex.hpp:302
Index_t::global global_index(typename Index_t::local idxloc__, block_id block_id__) const
Return global index of an element by local index and block id.
Definition: splindex.hpp:332
Externally defined block distribution.
Definition: splindex.hpp:444
Find the irriducible k-points of the Brillouin zone.
Contains definition of sirius::K_point class.
Contains declaration and partial implementation of sirius::K_point_set class.
void copy(T *target__, T const *source__, size_t n__)
Copy memory inside a device.
Definition: acc.hpp:320
Namespace of the SIRIUS library.
Definition: sirius.f90:5
double eval_sum(Unit_cell const &unit_cell, K_point_set const &kset)
TODO doc.
Definition: energy.cpp:135
strong_type< int, struct __block_id_tag > block_id
ID of the block.
Definition: splindex.hpp:108
strong_type< int, struct __n_blocks_tag > n_blocks
Number of blocks to which the global index is split.
Definition: splindex.hpp:105
auto newton_minimization_chemical_potential(Nt &&N, DNt &&dN, D2Nt &&ddN, double mu0, double ne, double tol, int maxstep=1000)
bool file_exists(std::string file_name)
Check if file exists.
Smearing functions used in finding the band occupancies.