SIRIUS 7.5.0
Electronic structure library and applications
atom_symmetry_class.cpp
Go to the documentation of this file.
1// Copyright (c) 2013-2022 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 atom_symmetry_class.cpp
21 *
22 * \brief Partial implementation of sirius::Atom_symmetry_class class.
23 */
24
27
28namespace sirius {
29
31 : id_(id__)
32 , atom_type_(atom_type__)
33{
34 if (!atom_type_.initialized()) {
35 RTE_THROW("atom type is not initialized");
36 }
37
38 int nl = atom_type_.indexr().lmax() + 1;
39 int max_order = atom_type_.indexr().max_order();
41
43
45
48
49 o_radial_integrals_ = sddk::mdarray<double, 3>(nl, max_order, max_order);
51
52 so_radial_integrals_ = sddk::mdarray<double, 3>(nl, max_order, max_order);
54
55 if (atom_type_.parameters().valence_relativity() == relativity_t::iora) {
58 }
59
60 /* copy descriptors because enu is different between atom classes */
61 for (int i = 0; i < atom_type_.num_aw_descriptors(); i++) {
62 aw_descriptors_.push_back(atom_type_.aw_descriptor(i));
63 }
64
65 for (int i = 0; i < atom_type_.num_lo_descriptors(); i++) {
66 lo_descriptors_.push_back(atom_type_.lo_descriptor(i));
67 }
68
70 std::fill(ae_core_charge_density_.begin(), ae_core_charge_density_.end(), 0);
71}
72
73void
75{
76 int nmtp = atom_type_.num_mt_points();
77
79
80 #pragma omp parallel default(shared)
81 {
82 Spline<double> s(atom_type_.radial_grid());
83
84 std::vector<double> p;
85 std::vector<double> rdudr;
86 std::array<double, 2> uderiv;
87
88 #pragma omp for schedule(dynamic, 1)
89 for (int l = 0; l < num_aw_descriptors(); l++) {
90 for (int order = 0; order < (int)aw_descriptor(l).size(); order++) {
91 auto rsd = aw_descriptor(l)[order];
92
93 auto idxrf = atom_type_.indexr().index_of(angular_momentum(l), order);
94
95 solver.solve(rel__, rsd.dme, rsd.l, rsd.enu, p, rdudr, uderiv);
96
97 /* normalize */
98 for (int ir = 0; ir < nmtp; ir++) {
99 s(ir) = std::pow(p[ir], 2);
100 }
101 double norm = 1.0 / std::sqrt(s.interpolate().integrate(0));
102
103 for (int ir = 0; ir < nmtp; ir++) {
104 radial_functions_(ir, idxrf, 0) = p[ir] * norm;
105 radial_functions_(ir, idxrf, 1) = rdudr[ir] * norm;
106 }
107 surface_derivatives_(0, idxrf) = norm * p.back() / atom_type_.mt_radius();
108 for (int i : {0, 1}) {
109 surface_derivatives_(i + 1, idxrf) = uderiv[i] * norm;
110 }
111
112 /* orthogonalize to previous radial functions */
113 for (int order1 = 0; order1 < order; order1++) {
114 auto idxrf1 = atom_type_.indexr().index_of(angular_momentum(l), order1);
115
116 for (int ir = 0; ir < nmtp; ir++) {
117 s(ir) = radial_functions_(ir, idxrf, 0) * radial_functions_(ir, idxrf1, 0);
118 }
119
120 /* <u_{\nu'}|u_{\nu}> */
121 double ovlp = s.interpolate().integrate(0);
122
123 for (int ir = 0; ir < nmtp; ir++) {
124 radial_functions_(ir, idxrf, 0) -= radial_functions_(ir, idxrf1, 0) * ovlp;
125 radial_functions_(ir, idxrf, 1) -= radial_functions_(ir, idxrf1, 1) * ovlp;
126 }
127 for (int i : {0, 1, 2}) {
128 surface_derivatives_(i, idxrf) -= surface_derivatives_(i, idxrf1) * ovlp;
129 }
130 }
131
132 /* normalize again */
133 for (int ir = 0; ir < nmtp; ir++) {
134 s(ir) = std::pow(radial_functions_(ir, idxrf, 0), 2);
135 }
136 norm = s.interpolate().integrate(0);
137
138 if (std::abs(norm) < 1e-10) {
139 std::stringstream s;
140 s << "AW radial function for atom " << atom_type_.label() << " is linearly dependent" << std::endl
141 << " order: " << order << std::endl
142 << " l: " << l << std::endl
143 << " dme: " << rsd.dme << std::endl
144 << " enu: " << rsd.enu;
145 RTE_THROW(s);
146 }
147
148 norm = 1.0 / std::sqrt(norm);
149
150 for (int ir = 0; ir < nmtp; ir++) {
151 radial_functions_(ir, idxrf, 0) *= norm;
152 radial_functions_(ir, idxrf, 1) *= norm;
153 }
154 for (int i : {0, 1, 2}) {
155 surface_derivatives_(i, idxrf) *= norm;
156 }
157 } // order
158 /* divide by r */
159 for (int order = 0; order < (int)aw_descriptor(l).size(); order++) {
160 auto idxrf = atom_type_.indexr().index_of(angular_momentum(l), order);
161 for (int ir = 0; ir < nmtp; ir++) {
162 radial_functions_(ir, idxrf, 0) *= atom_type_.radial_grid().x_inv(ir);
163 }
164 }
165 } // l
166 }
167}
168
169void
171{
172 int nmtp = atom_type_.num_mt_points();
173
175
176 #pragma omp parallel for schedule(dynamic, 1)
177 for (int idxlo = 0; idxlo < num_lo_descriptors(); idxlo++) {
178 Spline<double> s(atom_type_.radial_grid());
179 double a[3][3];
180 double rderiv[3][3];
181
182 /* number of radial solutions */
183 int num_rs = static_cast<int>(lo_descriptor(idxlo).rsd_set.size());
184 RTE_ASSERT(num_rs <= 3);
185
186 std::vector<std::vector<double>> p(num_rs);
187 std::vector<std::vector<double>> rdudr(num_rs);
188 std::array<double, 2> uderiv;
189
190 for (int order = 0; order < num_rs; order++) {
191 auto rsd = lo_descriptor(idxlo).rsd_set[order];
192
193 solver.solve(rel__, rsd.dme, rsd.l, rsd.enu, p[order], rdudr[order], uderiv);
194
195 /* find norm of the radial solution */
196 for (int ir = 0; ir < nmtp; ir++) {
197 s(ir) = std::pow(p[order][ir], 2);
198 }
199 double norm = 1.0 / std::sqrt(s.interpolate().integrate(0));
200
201 /* normalize radial solution and divide by r */
202 for (int ir = 0; ir < nmtp; ir++) {
203 /* store u(r) = p(r)/r */
204 p[order][ir] *= (norm * atom_type_.radial_grid().x_inv(ir));
205 /* don't divide rdudr by r */
206 rdudr[order][ir] *= norm;
207 }
208 uderiv[0] *= norm;
209 uderiv[1] *= norm;
210
211 /* matrix of derivatives */
212 a[order][0] = p[order].back();
213 a[order][1] = uderiv[0];
214 a[order][2] = uderiv[1];
215
216 for (int i: {0, 1, 2}) {
217 rderiv[order][i] = a[order][i];
218 }
219 }
220
221 double b[] = {0, 0, 0};
222 b[num_rs - 1] = 1.0;
223
224 int info = la::wrap(la::lib_t::lapack).gesv(num_rs, 1, &a[0][0], 3, b, 3);
225
226 if (info) {
227 std::stringstream s;
228 s << "a[i][j] = ";
229 for (int i = 0; i < num_rs; i++) {
230 for (int j = 0; j < num_rs; j++) {
231 s << rderiv[i][j] << " ";
232 }
233 }
234 s << std::endl;
235 s << "atom: " << atom_type_.label() << std::endl
236 << "zn: " << atom_type_.zn() << std::endl
237 << "l: " << lo_descriptor(idxlo).am.l() << std::endl;
238 s << "gesv returned " << info;
239 RTE_THROW(s);
240 }
241
242 /* index of local orbital radial function */
243 auto idxrf = atom_type_.indexr().index_of(rf_lo_index(idxlo));
244 /* zero surface derivatives */
245 for (int i : {0, 1, 2}) {
246 surface_derivatives_(i, idxrf) = 0;
247 }
248 /* take linear combination of radial solutions */
249 for (int order = 0; order < num_rs; order++) {
250 for (int ir = 0; ir < nmtp; ir++) {
251 /* u(r) function */
252 radial_functions_(ir, idxrf, 0) += b[order] * p[order][ir];
253 /* r(du/dr) function */
254 radial_functions_(ir, idxrf, 1) += b[order] * rdudr[order][ir];
255 }
256 for (int i : {0, 1, 2}) {
257 surface_derivatives_(i, idxrf) += b[order] * rderiv[order][i];
258 }
259 }
260
261 /* find norm of constructed local orbital */
262 for (int ir = 0; ir < nmtp; ir++) {
263 s(ir) = std::pow(radial_functions_(ir, idxrf, 0), 2);
264 }
265 double norm = 1.0 / std::sqrt(s.interpolate().integrate(2));
266
267 /* normalize */
268 for (int ir = 0; ir < nmtp; ir++) {
269 radial_functions_(ir, idxrf, 0) *= norm;
270 radial_functions_(ir, idxrf, 1) *= norm;
271 }
272 for (int i : {0, 1, 2}) {
273 surface_derivatives_(i, idxrf) *= norm;
274 }
275
276 if (std::abs(radial_functions_(nmtp - 1, idxrf, 0)) > 1e-10 || surface_derivatives_(0, idxrf) > 1e-10) {
277 std::stringstream s;
278 s << "local orbital " << idxlo << " is not zero at MT boundary" << std::endl
279 << " atom symmetry class id : " << id() << " (" << atom_type().symbol() << ")" << std::endl
280 << " value : " << radial_functions_(nmtp - 1, idxrf, 0) << std::endl
281 << " number of MT points: " << nmtp << std::endl
282 << " MT radius: " << atom_type_.radial_grid().last() << std::endl
283 << " b_coeffs: ";
284 for (int j = 0; j < num_rs; j++) {
285 s << b[j] << " ";
286 }
287 s << std::endl;
288 s << "surface derivative : " << surface_derivatives_(0, idxrf);
289 RTE_WARNING(s);
290 }
291 }
292
293 if (atom_type_.parameters().cfg().control().verification() > 0 && num_lo_descriptors() > 0) {
295 }
296}
297
298void
300{
301 int nmtp = atom_type().num_mt_points();
302 Spline<double> s(atom_type().radial_grid());
303 /* orthogonalize local orbitals */
304 for (int l = 0; l <= atom_type().indexr().lmax_lo(); l++) {
305 for (int j = 0; j < atom_type().indexr().num_lo(l); j++) {
306 int idxrf = atom_type().indexr().index_of(angular_momentum(l), j + atom_type_.aw_order(l));
307 for (int j1 = 0; j1 < j; j1++) {
308 int idxrf1 = atom_type().indexr().index_of(angular_momentum(l), j1 + atom_type().aw_order(l));
309
310 for (int ir = 0; ir < nmtp; ir++) {
311 s(ir) = radial_functions_(ir, idxrf, 0) * radial_functions_(ir, idxrf1, 0);
312 }
313 /* <u_{j}|u_{j'}> */
314 double ovlp = s.interpolate().integrate(2);
315
316 for (int ir = 0; ir < nmtp; ir++) {
317 radial_functions_(ir, idxrf, 0) -= radial_functions_(ir, idxrf1, 0) * ovlp;
318 radial_functions_(ir, idxrf, 1) -= radial_functions_(ir, idxrf1, 1) * ovlp;
319 }
320 for (int i : {0, 1, 2}) {
321 surface_derivatives_(i, idxrf) -= surface_derivatives_(i, idxrf1) * ovlp;
322 }
323 }
324 /* normalize again */
325 for (int ir = 0; ir < nmtp; ir++) {
326 s(ir) = std::pow(radial_functions_(ir, idxrf, 0), 2);
327 }
328 auto norm = s.interpolate().integrate(2);
329
330 if (std::abs(norm) < 1e-10) {
331 std::stringstream s;
332 s << "LO radial function for atom " << atom_type_.label() << " is linearly dependent" << std::endl
333 << " l : " << l << std::endl
334 << " index for a given l : " << j;
335 }
336
337 norm = 1.0 / std::sqrt(norm);
338
339 for (int ir = 0; ir < nmtp; ir++) {
340 radial_functions_(ir, idxrf, 0) *= norm;
341 radial_functions_(ir, idxrf, 1) *= norm;
342 }
343 for (int i : {0, 1, 2}) {
344 surface_derivatives_(i, idxrf) *= norm;
345 }
346 }
347 }
348}
349
350std::vector<int>
352{
353 int nmtp = atom_type_.num_mt_points();
354
355 Spline<double> s(atom_type_.radial_grid());
356 la::dmatrix<double> loprod(num_lo_descriptors(), num_lo_descriptors());
357 loprod.zero();
358 for (int idxlo1 = 0; idxlo1 < num_lo_descriptors(); idxlo1++) {
359
360 int idxrf1 = atom_type_.indexr().index_of(rf_lo_index(idxlo1));
361
362 for (int idxlo2 = 0; idxlo2 < num_lo_descriptors(); idxlo2++) {
363
364 int idxrf2 = atom_type_.indexr().index_of(rf_lo_index(idxlo2));
365
366 if (lo_descriptor(idxlo1).am == lo_descriptor(idxlo2).am) {
367
368 for (int ir = 0; ir < nmtp; ir++) {
369 s(ir) = radial_functions_(ir, idxrf1, 0) * radial_functions_(ir, idxrf2, 0);
370 }
371 loprod(idxlo1, idxlo2) = s.interpolate().integrate(2);
372 }
373 }
374 }
375
376 sddk::mdarray<double, 2> ovlp(num_lo_descriptors(), num_lo_descriptors());
377 sddk::copy(loprod, ovlp);
378
379 auto stdevp = la::Eigensolver_factory("lapack");
380
381 std::vector<double> loprod_eval(num_lo_descriptors());
382 la::dmatrix<double> loprod_evec(num_lo_descriptors(), num_lo_descriptors());
383
384 stdevp->solve(num_lo_descriptors(), loprod, &loprod_eval[0], loprod_evec);
385
386 if (std::abs(loprod_eval[0]) < tol__) {
387 std::printf("\n");
388 std::printf("local orbitals for atom symmetry class %i are almost linearly dependent\n", id_);
389 std::printf("local orbitals overlap matrix:\n");
390 for (int i = 0; i < num_lo_descriptors(); i++) {
391 for (int j = 0; j < num_lo_descriptors(); j++) {
392 std::printf("%12.6f", ovlp(i, j));
393 }
394 std::printf("\n");
395 }
396 std::printf("overlap matrix eigen-values:\n");
397 for (int i = 0; i < num_lo_descriptors(); i++) {
398 std::printf("%12.6f", loprod_eval[i]);
399 }
400 std::printf("\n");
401 std::printf("smallest eigenvalue: %20.16f\n", loprod_eval[0]);
402 }
403
404 std::vector<int> inc(num_lo_descriptors(), 0);
405
406 /* try all local orbitals */
407 for (int i = 0; i < num_lo_descriptors(); i++) {
408 inc[i] = 1;
409
410 std::vector<int> ilo;
411 for (int j = 0; j < num_lo_descriptors(); j++) {
412 if (inc[j] == 1) {
413 ilo.push_back(j);
414 }
415 }
416
417 std::vector<double> eval(ilo.size());
418 la::dmatrix<double> evec(static_cast<int>(ilo.size()), static_cast<int>(ilo.size()));
419 la::dmatrix<double> tmp(static_cast<int>(ilo.size()), static_cast<int>(ilo.size()));
420 for (int j1 = 0; j1 < (int)ilo.size(); j1++) {
421 for (int j2 = 0; j2 < (int)ilo.size(); j2++) {
422 tmp(j1, j2) = ovlp(ilo[j1], ilo[j2]);
423 }
424 }
425
426 stdevp->solve(static_cast<int>(ilo.size()), tmp, &eval[0], evec);
427
428 if (eval[0] < tol__) {
429 std::printf("local orbital %i can be removed\n", i);
430 inc[i] = 0;
431 }
432 }
433 return inc;
434}
435
436void
438{
439 std::stringstream s;
440 s << "local_orbitals_" << id_ << ".dat";
441 FILE* fout = fopen(s.str().c_str(), "w");
442
443 for (int ir = 0; ir < atom_type_.num_mt_points(); ir++) {
444 fprintf(fout, "%f ", atom_type_.radial_grid(ir));
445 for (int idxlo = 0; idxlo < num_lo_descriptors(); idxlo++) {
446 int idxrf = atom_type_.indexr().index_of(rf_lo_index(idxlo));
447 fprintf(fout, "%f ", radial_functions_(ir, idxrf, 0));
448 }
449 fprintf(fout, "\n");
450 }
451 fclose(fout);
452
453 s.str("");
454 s << "local_orbitals_deriv_" << id_ << ".dat";
455 fout = fopen(s.str().c_str(), "w");
456
457 for (int ir = 0; ir < atom_type_.num_mt_points(); ir++) {
458 fprintf(fout, "%f ", atom_type_.radial_grid(ir));
459 for (int idxlo = 0; idxlo < num_lo_descriptors(); idxlo++) {
460 int idxrf = atom_type_.indexr().index_of(rf_lo_index(idxlo));
461 fprintf(fout, "%f ", radial_functions_(ir, idxrf, 1));
462 }
463 fprintf(fout, "\n");
464 }
465 fclose(fout);
466}
467
468void
469Atom_symmetry_class::set_spherical_potential(std::vector<double> const& vs__)
470{
471 if (atom_type_.num_mt_points() != (int)vs__.size()) {
472 RTE_THROW("wrong size of effective potential array");
473 }
474
476
477 //HDF5_tree fout("mt_potential.h5", true);
478 //fout.write("potential", spherical_potential_);
479
480 ///* write spherical potential */
481 //std::stringstream sstr;
482 //sstr << "mt_spheric_potential_" << id_ << ".dat";
483 //FILE* fout = fopen(sstr.str().c_str(), "w");
484
485 //for (int ir = 0; ir < atom_type_.num_mt_points(); ir++) {
486 // double r = atom_type_.radial_grid(ir);
487 // fprintf(fout, "%20.10f %20.10f \n", r, spherical_potential_[ir] + atom_type_.zn() / r);
488 //}
489 //fclose(fout);
490}
491
492void
494{
495 PROFILE("sirius::Atom_symmetry_class::find_enu");
496
497 std::vector<radial_solution_descriptor*> rs_with_auto_enu;
498
499 /* find which aw functions need auto enu */
500 for (int l = 0; l < num_aw_descriptors(); l++) {
501 for (size_t order = 0; order < aw_descriptor(l).size(); order++) {
502 auto& rsd = aw_descriptor(l)[order];
503 if (rsd.auto_enu) {
504 rs_with_auto_enu.push_back(&rsd);
505 }
506 }
507 }
508
509 /* find which lo functions need auto enu */
510 for (int idxlo = 0; idxlo < num_lo_descriptors(); idxlo++) {
511 /* number of radial solutions */
512 size_t num_rs = lo_descriptor(idxlo).rsd_set.size();
513
514 for (size_t order = 0; order < num_rs; order++) {
515 auto& rsd = lo_descriptor(idxlo).rsd_set[order];
516 if (rsd.auto_enu) {
517 rs_with_auto_enu.push_back(&rsd);
518 }
519 }
520 }
521
522 #pragma omp parallel for
523 for (size_t i = 0; i < rs_with_auto_enu.size(); i++) {
524 auto rsd = rs_with_auto_enu[i];
525 double new_enu = Enu_finder(rel__, atom_type_.zn(), rsd->n, rsd->l, atom_type_.radial_grid(), spherical_potential_, rsd->enu).enu();
526 /* update linearization energy only if its change is above a threshold */
527 if (std::abs(new_enu - rsd->enu) > atom_type_.parameters().cfg().settings().auto_enu_tol()) {
528 rsd->enu = new_enu;
529 rsd->new_enu_found = true;
530 } else {
531 rsd->new_enu_found = false;
532 }
533 }
534}
535
536void
538{
539 PROFILE("sirius::Atom_symmetry_class::generate_radial_functions");
540
542
543 find_enu(rel__);
544
546
548
549 if (atom_type().parameters().cfg().control().ortho_rf()) {
551 }
552
553 //** if (true)
554 //** {
555 //** std::stringstream s;
556 //** s << "radial_functions_" << id_ << ".dat";
557 //** FILE* fout = fopen(s.str().c_str(), "w");
558
559 //** for (int ir = 0; ir <atom_type_.num_mt_points(); ir++)
560 //** {
561 //** fprintf(fout, "%f ", atom_type_.radial_grid(ir));
562 //** for (int idxrf = 0; idxrf < atom_type_.indexr().size(); idxrf++)
563 //** {
564 //** fprintf(fout, "%f ", radial_functions_(ir, idxrf, 0));
565 //** }
566 //** fprintf(fout, "\n");
567 //** }
568 //** fclose(fout);
569 //** }
570 //** STOP();
571}
572
573void
574Atom_symmetry_class::sync_radial_functions(mpi::Communicator const& comm__, int const rank__)
575{
576 /* don't broadcast Hamiltonian radial functions, because they are used locally */
577 int size = (int)(radial_functions_.size(0) * radial_functions_.size(1));
578 comm__.bcast(radial_functions_.at(sddk::memory_t::host), size, rank__);
579 comm__.bcast(surface_derivatives_.at(sddk::memory_t::host), (int)surface_derivatives_.size(), rank__);
580 // TODO: sync enu to pass to Exciting / Elk
581}
582
583void
584Atom_symmetry_class::sync_radial_integrals(mpi::Communicator const& comm__, int const rank__)
585{
586 comm__.bcast(h_spherical_integrals_.at(sddk::memory_t::host), (int)h_spherical_integrals_.size(), rank__);
587 comm__.bcast(o_radial_integrals_.at(sddk::memory_t::host), (int)o_radial_integrals_.size(), rank__);
588 comm__.bcast(so_radial_integrals_.at(sddk::memory_t::host), (int)so_radial_integrals_.size(), rank__);
589 if (atom_type_.parameters().valence_relativity() == relativity_t::iora) {
590 comm__.bcast(o1_radial_integrals_.at(sddk::memory_t::host), (int)o1_radial_integrals_.size(), rank__);
591 }
592}
593
594void
595Atom_symmetry_class::sync_core_charge_density(mpi::Communicator const& comm__, int const rank__)
596{
597 RTE_ASSERT(ae_core_charge_density_.size() != 0);
598
599 comm__.bcast(&ae_core_charge_density_[0], atom_type_.radial_grid().num_points(), rank__);
600 comm__.bcast(&core_leakage_, 1, rank__);
601 comm__.bcast(&core_eval_sum_, 1, rank__);
602}
603
604void
606{
607 PROFILE("sirius::Atom_symmetry_class::generate_radial_integrals");
608
609 int nmtp = atom_type_.num_mt_points();
610
611 double sq_alpha_half = 0.5 * std::pow(speed_of_light, -2);
612 if (rel__ == relativity_t::none) {
613 sq_alpha_half = 0;
614 }
615
617 #pragma omp parallel default(shared)
618 {
619 Spline<double> s(atom_type_.radial_grid());
620 #pragma omp for
621 for (int i1 = 0; i1 < atom_type_.mt_radial_basis_size(); i1++) {
622 for (int i2 = 0; i2 < atom_type_.mt_radial_basis_size(); i2++) {
623 /* for spherical part of potential integrals are diagonal in l */
624 if (atom_type_.indexr(i1).am.l() == atom_type_.indexr(i2).am.l()) {
625 int ll = atom_type_.indexr(i1).am.l() * (atom_type_.indexr(i1).am.l() + 1);
626 for (int ir = 0; ir < nmtp; ir++) {
627 double Minv = 1.0 / (1 - spherical_potential_[ir] * sq_alpha_half);
628 /* u_1(r) * u_2(r) */
629 double t0 = radial_functions_(ir, i1, 0) * radial_functions_(ir, i2, 0);
630 /* r*u'_1(r) * r*u'_2(r) */
631 double t1 = radial_functions_(ir, i1, 1) * radial_functions_(ir, i2, 1);
632 s(ir) = 0.5 * t1 * Minv + t0 * (0.5 * ll * Minv + spherical_potential_[ir] *
633 std::pow(atom_type_.radial_grid(ir), 2));
634 }
635 h_spherical_integrals_(i1, i2) = s.interpolate().integrate(0) / y00;
636 }
637 }
638 }
639 }
640
642 #pragma omp parallel default(shared)
643 {
644 Spline<double> s(atom_type_.radial_grid());
645 #pragma omp for
646 for (int l = 0; l <= atom_type_.indexr().lmax(); l++) {
647 int nrf = atom_type_.indexr().max_order(l);
648
649 for (int order1 = 0; order1 < nrf; order1++) {
650 auto idxrf1 = atom_type_.indexr().index_of(angular_momentum(l), order1);
651 for (int order2 = 0; order2 < nrf; order2++) {
652 auto idxrf2 = atom_type_.indexr().index_of(angular_momentum(l), order2);
653 if (order1 == order2) {
654 o_radial_integrals_(l, order1, order2) = 1.0;
655 } else {
656 for (int ir = 0; ir < nmtp; ir++) {
657 s(ir) = radial_functions_(ir, idxrf1, 0) * radial_functions_(ir, idxrf2, 0);
658 }
659 o_radial_integrals_(l, order1, order2) = s.interpolate().integrate(2);
660 }
661 }
662 }
663 }
664 }
665 if (atom_type_.parameters().valence_relativity() == relativity_t::iora) {
667 #pragma omp parallel for
668 for (int i1 = 0; i1 < atom_type_.mt_radial_basis_size(); i1++) {
669 Spline<double> s(atom_type_.radial_grid());
670 for (int i2 = 0; i2 < atom_type_.mt_radial_basis_size(); i2++) {
671 /* for spherical part of potential integrals are diagonal in l */
672 if (atom_type_.indexr(i1).am.l() == atom_type_.indexr(i2).am.l()) {
673 int ll = atom_type_.indexr(i1).am.l() * (atom_type_.indexr(i1).am.l() + 1);
674 for (int ir = 0; ir < nmtp; ir++) {
675 double Minv = std::pow(1 - spherical_potential_[ir] * sq_alpha_half, -2);
676 /* u_1(r) * u_2(r) */
677 double t0 = radial_functions_(ir, i1, 0) * radial_functions_(ir, i2, 0);
678 /* r*u'_1(r) * r*u'_2(r) */
679 double t1 = radial_functions_(ir, i1, 1) * radial_functions_(ir, i2, 1);
680 s(ir) = sq_alpha_half * 0.5 * Minv * (t1 + t0 * 0.5 * ll);
681 }
682 o1_radial_integrals_(i1, i2) = s.interpolate().integrate(0);
683 }
684 }
685 }
686 }
687
688 if (atom_type_.parameters().so_correction()) {
689 double soc = std::pow(2 * speed_of_light, -2);
690
691 Spline<double> s(atom_type_.radial_grid());
692 Spline<double> s1(atom_type_.radial_grid());
693 Spline<double> ve(atom_type_.radial_grid());
694
695 for (int i = 0; i < nmtp; i++) {
696 ve(i) = spherical_potential_[i] + atom_type_.zn() / atom_type_.radial_grid(i);
697 }
698 ve.interpolate();
699
701 for (int l = 0; l <= atom_type_.indexr().lmax(); l++) {
702 int nrf = atom_type_.indexr().max_order(l);
703
704 for (int order1 = 0; order1 < nrf; order1++) {
705 auto idxrf1 = atom_type_.indexr().index_of(angular_momentum(l), order1);
706 for (int order2 = 0; order2 < nrf; order2++) {
707 auto idxrf2 = atom_type_.indexr().index_of(angular_momentum(l), order2);
708
709 for (int ir = 0; ir < nmtp; ir++) {
710 double M = 1.0 - 2 * soc * spherical_potential_[ir];
711 /* first part <f| dVe / dr |f'> */
712 s(ir) = radial_functions_(ir, idxrf1, 0) * radial_functions_(ir, idxrf2, 0) *
713 soc * ve.deriv(1, ir) / pow(M, 2);
714
715 /* second part <f| d(z/r) / dr |f'> */
716 s1(ir) = radial_functions_(ir, idxrf1, 0) * radial_functions_(ir, idxrf2, 0) *
717 soc * atom_type_.zn() / pow(M, 2);
718 }
719 s.interpolate();
720 s1.interpolate();
721
722 so_radial_integrals_(l, order1, order2) = s.integrate(1) + s1.integrate(-1);
723 }
724 }
725 }
726 }
727}
728
729void
730Atom_symmetry_class::write_enu(mpi::pstdout& pout) const
731{
732 pout << "Atom : " << atom_type_.symbol() << ", class id : " << id_ << std::endl;
733 pout << "augmented waves" << std::endl;
734 for (int l = 0; l < num_aw_descriptors(); l++) {
735 for (size_t order = 0; order < aw_descriptor(l).size(); order++) {
736 auto& rsd = aw_descriptor(l)[order];
737 if (rsd.auto_enu) {
738 pout << rsd; //printf("n = %2i l = %2i order = %i enu = %12.6f", rsd.n, rsd.l, order, rsd.enu);
739 if (rsd.new_enu_found) {
740 pout << " +";
741 }
742 pout << std::endl;
743 }
744 }
745 }
746
747 pout << "local orbitals" << std::endl;
748 for (int idxlo = 0; idxlo < num_lo_descriptors(); idxlo++) {
749 for (size_t order = 0; order < lo_descriptor(idxlo).rsd_set.size(); order++) {
750 auto& rsd = lo_descriptor(idxlo).rsd_set[order];
751 if (rsd.auto_enu) {
752 //pout.printf("n = %2i l = %2i order = %i enu = %12.6f", rsd.n, rsd.l, order, rsd.enu);
753 pout << rsd;
754 if (rsd.new_enu_found) {
755 pout << " +";
756 }
757 pout << std::endl;
758 }
759 }
760 }
761 pout << std::endl;
762}
763
764void
766{
767 PROFILE("sirius::Atom_symmetry_class::generate_core_charge_density");
768
769 /* nothing to do */
770 if (atom_type_.num_core_electrons() == 0.0) {
771 return;
772 }
773
774 int nmtp = atom_type_.num_mt_points();
775
776 std::vector<double> free_atom_grid(nmtp);
777 for (int i = 0; i < nmtp; i++) {
778 free_atom_grid[i] = atom_type_.radial_grid(i);
779 }
780
781 /* extend radial grid */
782 double x = atom_type_.radial_grid(nmtp - 1);
783 double dx = atom_type_.radial_grid().dx(nmtp - 2);
784 while (x < 30.0 + atom_type_.zn() / 4.0) {
785 x += dx;
786 free_atom_grid.push_back(x);
787 dx *= 1.025;
788 }
789 Radial_grid_ext<double> rgrid(static_cast<int>(free_atom_grid.size()), free_atom_grid.data());
790
791 /* interpolate spherical potential inside muffin-tin */
792 Spline<double> svmt(atom_type_.radial_grid());
793 /* remove nucleus contribution from Vmt */
794 for (int ir = 0; ir < nmtp; ir++) {
795 svmt(ir) = spherical_potential_[ir] + atom_type_.zn() * atom_type_.radial_grid().x_inv(ir);
796 }
797 svmt.interpolate();
798 /* fit tail to alpha/r + beta */
799 double alpha = -(std::pow(atom_type_.mt_radius(), 2) * svmt.deriv(1, nmtp - 1) + atom_type_.zn());
800 double beta = svmt(nmtp - 1) - (atom_type_.zn() + alpha) / atom_type_.mt_radius();
801
802 /* cook an effective potential from muffin-tin part and a tail */
803 std::vector<double> veff(rgrid.num_points());
804 for (int ir = 0; ir < nmtp; ir++) {
805 veff[ir] = spherical_potential_[ir];
806 }
807 /* simple tail alpha/r + beta */
808 for (int ir = nmtp; ir < rgrid.num_points(); ir++) {
809 veff[ir] = alpha * rgrid.x_inv(ir) + beta;
810 }
811
812 //== /* write spherical potential */
813 //== std::stringstream sstr;
814 //== sstr << "spheric_potential_" << id_ << ".dat";
815 //== FILE* fout = fopen(sstr.str().c_str(), "w");
816
817 //== for (int ir = 0; ir < rgrid.num_points(); ir++)
818 //== {
819 //== fprintf(fout, "%18.10f %18.10f\n", rgrid[ir], veff[ir]);
820 //== }
821 //== fclose(fout);
822 //== STOP();
823
824 /* charge density */
825 Spline<double> rho(rgrid);
826
827 /* atomic level energies */
828 std::vector<double> level_energy(atom_type_.num_atomic_levels());
829
830 for (int ist = 0; ist < atom_type_.num_atomic_levels(); ist++) {
831 level_energy[ist] = -1.0 * atom_type_.zn() / 2 / std::pow(double(atom_type_.atomic_level(ist).n), 2);
832 }
833
834 sddk::mdarray<double, 2> rho_t(rgrid.num_points(), atom_type_.num_atomic_levels());
835 rho_t.zero();
836 #pragma omp parallel for
837 for (int ist = 0; ist < atom_type_.num_atomic_levels(); ist++) {
838 if (atom_type_.atomic_level(ist).core) {
839 /* serch for the bound state */
840 Bound_state bs(core_rel__, atom_type_.zn(), atom_type_.atomic_level(ist).n, atom_type_.atomic_level(ist).l,
841 atom_type_.atomic_level(ist).k, rgrid, veff, level_energy[ist]);
842
843 auto& rho = bs.rho();
844 for (int i = 0; i < rgrid.num_points(); i++) {
845 rho_t(i, ist) = atom_type_.atomic_level(ist).occupancy * rho(i) / fourpi;
846 }
847
848 level_energy[ist] = bs.enu();
849 }
850 }
851 for (int ist = 0; ist < atom_type_.num_atomic_levels(); ist++) {
852 if (atom_type_.atomic_level(ist).core) {
853 for (int i = 0; i < rgrid.num_points(); i++) {
854 rho(i) += rho_t(i, ist);
855 }
856 }
857 }
858
859 //#pragma omp parallel default(shared)
860 //{
861 // std::vector<double> rho_t(rho.num_points());
862 // std::memset(&rho_t[0], 0, rho.num_points() * sizeof(double));
863
864 // #pragma omp for
865 // for (int ist = 0; ist < atom_type_.num_atomic_levels(); ist++) {
866 // if (atom_type_.atomic_level(ist).core) {
867 // Bound_state bs(core_rel__, atom_type_.zn(), atom_type_.atomic_level(ist).n, atom_type_.atomic_level(ist).l,
868 // atom_type_.atomic_level(ist).k, rgrid, veff, level_energy[ist]);
869
870 // auto& rho = bs.rho();
871 // for (int i = 0; i < rgrid.num_points(); i++) {
872 // rho_t[i] += atom_type_.atomic_level(ist).occupancy * rho(i) / fourpi;
873 // }
874
875 // level_energy[ist] = bs.enu();
876 // }
877 // }
878
879 // #pragma omp critical
880 // for (int i = 0; i < rho.num_points(); i++) {
881 // rho(i) += rho_t[i];
882 // }
883 //}
884
885 for (int ir = 0; ir < atom_type_.num_mt_points(); ir++) {
886 ae_core_charge_density_[ir] = rho(ir);
887 }
888
889 /* interpolate muffin-tin part of core density */
891
892 /* compute core leakage */
893 core_leakage_ = fourpi * (rho.interpolate().integrate(2) - rho_mt.integrate(2));
894
895 /* compute eigen-value sum of core states */
896 core_eval_sum_ = 0.0;
897 for (int ist = 0; ist < atom_type_.num_atomic_levels(); ist++) {
898 if (atom_type_.atomic_level(ist).core) {
899 core_eval_sum_ += level_energy[ist] * atom_type_.atomic_level(ist).occupancy;
900 }
901 }
902}
903
904} // namespace sirius
905
Contains declaration and partial implementation of sirius::Atom_symmetry_class class.
void generate_aw_radial_functions(relativity_t rel__)
Generate radial functions for augmented waves.
void generate_radial_functions(relativity_t rel__)
Generate APW and LO radial functions.
Atom_symmetry_class(int id_, Atom_type const &atom_type_)
Constructor.
void set_spherical_potential(std::vector< double > const &vs__)
Set the spherical component of the potential.
double core_eval_sum_
Core eigen-value sum.
void generate_lo_radial_functions(relativity_t rel__)
Generate local orbital raidal functions.
void dump_lo()
Dump local orbitals to the file for debug purposes.
Atom_type const & atom_type_
Pointer to atom type.
std::vector< double > ae_core_charge_density_
Core charge density.
std::vector< local_orbital_descriptor > lo_descriptors_
list of radial descriptor sets used to construct local orbitals
void generate_core_charge_density(relativity_t core_rel__)
Find core states and generate core density.
sddk::mdarray< double, 2 > o1_radial_integrals_
Overlap integrals for IORA relativistic treatment.
int id() const
Return symmetry class id.
std::vector< double > spherical_potential_
Spherical part of the effective potential.
sddk::mdarray< double, 3 > so_radial_integrals_
Spin-orbit interaction integrals.
void find_enu(relativity_t rel__)
Find linearization energy.
sddk::mdarray< double, 2 > h_spherical_integrals_
Spherical part of radial integral.
sddk::mdarray< double, 3 > o_radial_integrals_
Overlap integrals.
int id_
Symmetry class id in the range [0, N_class).
sddk::mdarray< double, 3 > radial_functions_
List of radial functions for the LAPW basis.
std::vector< radial_solution_descriptor_set > aw_descriptors_
list of radial descriptor sets used to construct augmented waves
sddk::mdarray< double, 2 > surface_derivatives_
Surface derivatives of AW radial functions.
void generate_radial_integrals(relativity_t rel__)
Generate radial overlap and SO integrals.
void orthogonalize_radial_functions()
Orthogonalize the radial functions.
std::vector< int > check_lo_linear_independence(double etol__)
Check if local orbitals are linearly independent.
Defines the properties of atom type.
Definition: atom_type.hpp:93
int num_mt_points() const
Return number of muffin-tin radial grid points.
Definition: atom_type.hpp:718
auto const & indexr() const
Return const reference to the index of radial functions.
Definition: atom_type.hpp:817
int zn() const
Return ionic charge (as positive integer).
Definition: atom_type.hpp:681
double mt_radius() const
Return muffin-tin radius.
Definition: atom_type.hpp:712
int mt_radial_basis_size() const
Total number of radial basis functions.
Definition: atom_type.hpp:886
int aw_order(int l__) const
Order of augmented wave radial functions for a given l.
Definition: atom_type.hpp:810
Spline< double > const & rho() const
Return charge density, corresponding to a radial solution.
External radial grid provided as a list of points.
T last() const
Last point of the grid.
T dx(const int i) const
Return .
T x_inv(const int i) const
Return .
int num_points() const
Number of grid points.
Finds a solution to radial Schrodinger, Koelling-Harmon or Dirac equation.
T integrate(std::vector< T > &g__, int m__) const
Integrate spline with r^m prefactor.
Definition: spline.hpp:422
Spline< T, U > & interpolate()
Definition: spline.hpp:275
Angular momentum quantum number.
auto l() const
Get orbital quantum number l.
Distributed matrix.
Definition: dmatrix.hpp:56
int gesv(ftn_int n, ftn_int nrhs, T *A, ftn_int lda, T *B, ftn_int ldb) const
Compute the solution to system of linear equations A * X = B for general matrix.
MPI communicator wrapper.
void bcast(T *buffer__, int count__, int root__) const
Perform buffer broadcast.
Parallel standard output.
Definition: pstdout.hpp:42
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
Contains definition of eigensolver factory.
void copy(T *target__, T const *source__, size_t n__)
Copy memory inside a device.
Definition: acc.hpp:320
@ lapack
CPU LAPACK.
int lmax(int lmmax__)
Get maximum orbital quantum number by the maximum lm index.
Definition: specfunc.hpp:56
Namespace of the SIRIUS library.
Definition: sirius.f90:5
relativity_t
Type of relativity treatment in the case of LAPW.
Definition: typedefs.hpp:95
strong_type< int, struct __rf_lo_index_tag > rf_lo_index
Local orbital radial function index.
const double y00
First spherical harmonic .
Definition: constants.hpp:51
const double speed_of_light
NIST value for the inverse fine structure (http://physics.nist.gov/cuu/Constants/index....
Definition: constants.hpp:33
const double fourpi
Definition: constants.hpp:48
bool core
True if this is a core level.
Definition: atomic_data.hpp:29
int k
Quantum number k.
Definition: atomic_data.hpp:23
int n
Principal quantum number.
Definition: atomic_data.hpp:17
double occupancy
Level occupancy.
Definition: atomic_data.hpp:26
int l
Angular momentum quantum number.
Definition: atomic_data.hpp:20
radial_solution_descriptor_set rsd_set
Set of radial solution descriptors.
Definition: atom_type.hpp:51
angular_momentum am
Orbital quantum number .
Definition: atom_type.hpp:46