SIRIUS 7.5.0
Electronic structure library and applications
unit_cell.cpp
Go to the documentation of this file.
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/** \file unit_cell.cpp
21 *
22 * \brief Contains implementation of sirius::Unit_cell class.
23 */
24
25#include <iomanip>
26#include "unit_cell.hpp"
29
30namespace sirius {
31
32Unit_cell::Unit_cell(Simulation_parameters const& parameters__, mpi::Communicator const& comm__)
33 : parameters_(parameters__)
34 , comm_(comm__)
35{
36}
37
38Unit_cell::~Unit_cell() = default;
39
40std::vector<double>
41Unit_cell::find_mt_radii(int auto_rmt__, bool inflate__)
42{
43 if (nearest_neighbours_.size() == 0) {
44 RTE_THROW("array of nearest neighbours is empty");
45 }
46
47 std::vector<double> Rmt(num_atom_types(), 1e10);
48
49 if (auto_rmt__ == 1) {
50 for (int ia = 0; ia < num_atoms(); ia++) {
51 int id1 = atom(ia).type_id();
52 if (nearest_neighbours_[ia].size() > 1) {
53 int ja = nearest_neighbours_[ia][1].atom_id;
54 int id2 = atom(ja).type_id();
55 /* don't allow spheres to touch: take a smaller value than half a distance */
56 double R = std::min(parameters_.rmt_max(), 0.95 * nearest_neighbours_[ia][1].distance / 2);
57 /* take minimal R for the given atom type */
58 Rmt[id1] = std::min(R, Rmt[id1]);
59 Rmt[id2] = std::min(R, Rmt[id2]);
60 } else {
61 Rmt[id1] = parameters_.rmt_max();
62 }
63 }
64 }
65
66 if (auto_rmt__ == 2) {
67 std::vector<double> scale(num_atom_types(), 1e10);
68
69 for (int ia = 0; ia < num_atoms(); ia++) {
70 int id1 = atom(ia).type_id();
71 if (nearest_neighbours_[ia].size() > 1) {
72 int ja = nearest_neighbours_[ia][1].atom_id;
73 int id2 = atom(ja).type_id();
74
75 double d = nearest_neighbours_[ia][1].distance;
76 double s = 0.95 * d / (atom_type(id1).mt_radius() + atom_type(id2).mt_radius());
77 scale[id1] = std::min(s, scale[id1]);
78 scale[id2] = std::min(s, scale[id2]);
79 } else {
80 scale[id1] = parameters_.rmt_max() / atom_type(id1).mt_radius();
81 }
82 }
83
84 for (int iat = 0; iat < num_atom_types(); iat++) {
85 Rmt[iat] = std::min(parameters_.rmt_max(), atom_type(iat).mt_radius() * scale[iat]);
86 }
87 }
88
89 /* Suppose we have 3 different atoms. First we determint Rmt between 1st and 2nd atom,
90 * then we determine Rmt between (let's say) 2nd and 3rd atom and at this point we reduce
91 * the Rmt of the 2nd atom. This means that the 1st atom gets a possibility to expand if
92 * it is far from the 3rd atom. */
93 if (inflate__) {
94 std::vector<bool> scale_Rmt(num_atom_types(), true);
95 for (int ia = 0; ia < num_atoms(); ia++) {
96 int id1 = atom(ia).type_id();
97
98 if (nearest_neighbours_[ia].size() > 1) {
99 int ja = nearest_neighbours_[ia][1].atom_id;
100 int id2 = atom(ja).type_id();
101 double dist = nearest_neighbours_[ia][1].distance;
102
103 if (Rmt[id1] + Rmt[id2] > dist * 0.94) {
104 scale_Rmt[id1] = false;
105 scale_Rmt[id2] = false;
106 }
107 }
108 }
109
110 std::vector<double> Rmt_infl(num_atom_types(), 1e10);
111
112 for (int ia = 0; ia < num_atoms(); ia++) {
113 int id1 = atom(ia).type_id();
114 if (nearest_neighbours_[ia].size() > 1) {
115 int ja = nearest_neighbours_[ia][1].atom_id;
116 int id2 = atom(ja).type_id();
117 double dist = nearest_neighbours_[ia][1].distance;
118
119 if (scale_Rmt[id1] && !scale_Rmt[id2]) {
120 Rmt_infl[id1] = std::min(Rmt_infl[id1], std::min(parameters_.rmt_max(), 0.95 * (dist - Rmt[id2])));
121 } else {
122 Rmt_infl[id1] = Rmt[id1];
123 }
124 }
125 }
126 for (int iat = 0; iat < num_atom_types(); iat++) {
127 Rmt[iat] = Rmt_infl[iat];
128 }
129 }
130
131 for (int i = 0; i < num_atom_types(); i++) {
132 if (Rmt[i] < 0.3) {
133 std::stringstream s;
134 s << "muffin-tin radius for atom type " << i << " (" << atom_type(i).label()
135 << ") is too small: " << Rmt[i];
136 RTE_THROW(s);
137 }
138 }
139
140 return Rmt;
141}
142
143bool
144Unit_cell::check_mt_overlap(int& ia__, int& ja__)
145{
146 if (num_atoms() != 0 && nearest_neighbours_.size() == 0) {
147 RTE_THROW("array of nearest neighbours is empty");
148 }
149
150 for (int ia = 0; ia < num_atoms(); ia++) {
151 /* first atom is always the central one itself */
152 if (nearest_neighbours_[ia].size() <= 1) {
153 continue;
154 }
155
156 int ja = nearest_neighbours_[ia][1].atom_id;
157 double dist = nearest_neighbours_[ia][1].distance;
158
159 if (atom(ia).mt_radius() + atom(ja).mt_radius() >= dist) {
160 ia__ = ia;
161 ja__ = ja;
162 return true;
163 }
164 }
165
166 return false;
167}
168
169void
170Unit_cell::print_info(std::ostream& out__, int verbosity__) const
171{
172 out__ << "lattice vectors" << std::endl;
173 for (int i = 0; i < 3; i++) {
174 out__ << " a" << i + 1 << " : ";
175 for (int x: {0, 1, 2}) {
176 out__ << ffmt(18, 10) << lattice_vectors_(x, i);
177 }
178 out__ << std::endl;
179 }
180 out__ << "reciprocal lattice vectors" << std::endl;
181 for (int i = 0; i < 3; i++) {
182 out__ << " b" << i + 1 << " : ";
183 for (int x: {0, 1, 2}) {
184 out__ << ffmt(18, 10) << reciprocal_lattice_vectors_(x, i);
185 }
186 out__ << std::endl;
187 }
188 out__ << std::endl
189 << "unit cell volume : " << ffmt(18, 8) << omega() << " [a.u.^3]" << std::endl
190 << "1/sqrt(omega) : " << ffmt(18, 8) << 1.0 / sqrt(omega()) << std::endl
191 << "MT volume : " << ffmt(18, 8) << volume_mt()
192 << " (" << ffmt(5, 2) << volume_mt() * 100 / omega() << "%)" << std::endl
193 << "IT volume : " << ffmt(18, 8) << volume_it()
194 << " (" << ffmt(5, 2) << volume_it() * 100 / omega() << "%)" << std::endl
195 << std::endl
196 << "number of atom types : " << num_atom_types() << std::endl;
197 for (int i = 0; i < num_atom_types(); i++) {
198 int id = atom_type(i).id();
199 out__ << "type id : " << id << " symbol : " << std::setw(2) << atom_type(i).symbol() << " mt_radius : "
200 << ffmt(10, 6) << atom_type(i).mt_radius() << " num_atoms : " << atom_type(i).num_atoms() << std::endl;
201 }
202
203 out__ << "total number of atoms : " << num_atoms() << std::endl
204 << "number of symmetry classes : " << num_atom_symmetry_classes() << std::endl;
205 if (!parameters_.full_potential()) {
206 out__ << "number of PAW atoms : " << num_paw_atoms() << std::endl;
207 if (num_paw_atoms() != 0) {
208 out__ << "PAW atoms :";
209 for (auto ia : paw_atom_index_) {
210 out__ << " " << ia;
211 }
212 out__ << std::endl;
213 }
214 }
215 if (verbosity__ >= 2) {
216 out__ << std::endl
217 << "atom id type id class id position vector_field" << std::endl
218 << hbar(90, '-') << std::endl;
219 for (int i = 0; i < num_atoms(); i++) {
220 auto pos = atom(i).position();
221 auto vf = atom(i).vector_field();
222 out__ << std::setw(6) << i
223 << std::setw(9) << atom(i).type_id()
224 << std::setw(9) << atom(i).symmetry_class_id()
225 << " ";
226 for (int x: {0, 1, 2}) {
227 out__ << ffmt(10, 5) << pos[x];
228 }
229 out__ << " ";
230 for (int x: {0, 1, 2}) {
231 out__ << ffmt(10, 5) << vf[x];
232 }
233 out__ << std::endl;
234 }
235 out__ << std::endl
236 << "atom id position (Cartesian, a.u.)" << std::endl
237 << hbar(45, '-') << std::endl;
238 for (int i = 0; i < num_atoms(); i++) {
239 auto pos = atom(i).position();
240 auto vc = get_cartesian_coordinates(pos);
241 out__ << std::setw(6) << i << " ";
242 for (int x: {0, 1 ,2}) {
243 out__ << ffmt(12, 6) << vc[x];
244 }
245 out__ << std::endl;
246 }
247 out__ << std::endl;
248 for (int ic = 0; ic < num_atom_symmetry_classes(); ic++) {
249 out__ << "class id : " << ic << " atom id : ";
250 for (int i = 0; i < atom_symmetry_class(ic).num_atoms(); i++) {
251 out__ << atom_symmetry_class(ic).atom_id(i) << " ";
252 }
253 out__ << std::endl;
254 }
255 }
256 out__ << std::endl
257 << "minimum bond length: " << ffmt(12, 6) << min_bond_length() << std::endl;
258 if (!parameters_.full_potential()) {
259 out__ << std::endl
260 << "total number of pseudo wave-functions: " << this->num_ps_atomic_wf().first << std::endl;
261 }
262 out__ << std::endl;
263}
264
265void
266Unit_cell::print_geometry_info(std::ostream& out__, int verbosity__) const
267{
268 if (verbosity__ >= 1) {
269 out__ << std::endl
270 << "lattice vectors" << std::endl;
271 for (int i = 0; i < 3; i++) {
272 out__ << " a" << i + 1 << " : ";
273 for (int x: {0, 1, 2}) {
274 out__ << ffmt(18, 10) << lattice_vectors_(x, i);
275 }
276 out__ << std::endl;
277 }
278 out__ << std::endl
279 << "unit cell volume : " << ffmt(18, 8) << omega() << " [a.u.^3]" << std::endl;
280 }
281
282 if (verbosity__ >= 2) {
283 out__ << std::endl
284 << "atom id type id class id position vector_field" << std::endl
285 << hbar(90, '-') << std::endl;
286 for (int i = 0; i < num_atoms(); i++) {
287 auto pos = atom(i).position();
288 auto vf = atom(i).vector_field();
289 out__ << std::setw(6) << i
290 << std::setw(9) << atom(i).type_id()
291 << std::setw(9) << atom(i).symmetry_class_id()
292 << " ";
293 for (int x: {0, 1, 2}) {
294 out__ << ffmt(10, 5) << pos[x];
295 }
296 out__ << " ";
297 for (int x: {0, 1, 2}) {
298 out__ << ffmt(10, 5) << vf[x];
299 }
300 out__ << std::endl;
301 }
302 out__ << std::endl
303 << "atom id position (Cartesian, a.u.)" << std::endl
304 << hbar(45, '-') << std::endl;
305 for (int i = 0; i < num_atoms(); i++) {
306 auto pos = atom(i).position();
307 auto vc = get_cartesian_coordinates(pos);
308 out__ << std::setw(6) << i << " ";
309 for (int x: {0, 1 ,2}) {
310 out__ << ffmt(12, 6) << vc[x];
311 }
312 out__ << std::endl;
313 }
314 }
315 if (verbosity__ >= 1) {
316 out__ << std::endl
317 << "minimum bond length: " << ffmt(12, 6) << min_bond_length() << std::endl;
318 }
319}
320
321unit_cell_parameters_descriptor
322Unit_cell::unit_cell_parameters()
323{
324 unit_cell_parameters_descriptor d;
325
326 r3::vector<double> v0(lattice_vectors_(0, 0), lattice_vectors_(1, 0), lattice_vectors_(2, 0));
327 r3::vector<double> v1(lattice_vectors_(0, 1), lattice_vectors_(1, 1), lattice_vectors_(2, 1));
328 r3::vector<double> v2(lattice_vectors_(0, 2), lattice_vectors_(1, 2), lattice_vectors_(2, 2));
329
330 d.a = v0.length();
331 d.b = v1.length();
332 d.c = v2.length();
333
334 d.alpha = std::acos(dot(v1, v2) / d.b / d.c) * 180 / pi;
335 d.beta = std::acos(dot(v0, v2) / d.a / d.c) * 180 / pi;
336 d.gamma = std::acos(dot(v0, v1) / d.a / d.b) * 180 / pi;
337
338 return d;
339}
340
341void
342Unit_cell::write_cif()
343{
344 if (comm_.rank() == 0) {
345 FILE* fout = fopen("unit_cell.cif", "w");
346
347 auto d = unit_cell_parameters();
348
349 fprintf(fout, "_cell_length_a %f\n", d.a);
350 fprintf(fout, "_cell_length_b %f\n", d.b);
351 fprintf(fout, "_cell_length_c %f\n", d.c);
352 fprintf(fout, "_cell_angle_alpha %f\n", d.alpha);
353 fprintf(fout, "_cell_angle_beta %f\n", d.beta);
354 fprintf(fout, "_cell_angle_gamma %f\n", d.gamma);
355
356 // fprintf(fout, "loop_\n");
357 // fprintf(fout, "_symmetry_equiv_pos_as_xyz\n");
358
359 fprintf(fout, "loop_\n");
360 fprintf(fout, "_atom_site_label\n");
361 fprintf(fout, "_atom_type_symbol\n");
362 fprintf(fout, "_atom_site_fract_x\n");
363 fprintf(fout, "_atom_site_fract_y\n");
364 fprintf(fout, "_atom_site_fract_z\n");
365 for (int ia = 0; ia < num_atoms(); ia++) {
366 auto pos = atom(ia).position();
367 fprintf(fout, "%i %s %f %f %f\n", ia + 1, atom(ia).type().label().c_str(), pos[0], pos[1], pos[2]);
368 }
369 fclose(fout);
370 }
371}
372
373json
374Unit_cell::serialize(bool cart_pos__) const
375{
376 json dict;
377
378 dict["lattice_vectors"] = {{lattice_vectors_(0, 0), lattice_vectors_(1, 0), lattice_vectors_(2, 0)},
379 {lattice_vectors_(0, 1), lattice_vectors_(1, 1), lattice_vectors_(2, 1)},
380 {lattice_vectors_(0, 2), lattice_vectors_(1, 2), lattice_vectors_(2, 2)}};
381 dict["lattice_vectors_scale"] = 1.0;
382 if (cart_pos__) {
383 dict["atom_coordinate_units"] = "au";
384 } else {
385 dict["atom_coordinate_units"] = "lattice";
386 }
387 dict["atom_types"] = json::array();
388 for (int iat = 0; iat < num_atom_types(); iat++) {
389 dict["atom_types"].push_back(atom_type(iat).label());
390 }
391 dict["atom_files"] = json::object();
392 for (int iat = 0; iat < num_atom_types(); iat++) {
393 dict["atom_files"][atom_type(iat).label()] = atom_type(iat).file_name();
394 }
395 dict["atoms"] = json::object();
396 for (int iat = 0; iat < num_atom_types(); iat++) {
397 dict["atoms"][atom_type(iat).label()] = json::array();
398 for (int i = 0; i < atom_type(iat).num_atoms(); i++) {
399 int ia = atom_type(iat).atom_id(i);
400 auto v = atom(ia).position();
401 /* convert to Cartesian coordinates */
402 if (cart_pos__) {
403 v = dot(lattice_vectors_, v);
404 }
405 dict["atoms"][atom_type(iat).label()].push_back({v[0], v[1], v[2]});
406 }
407 }
408 return dict;
409}
410
411void
412Unit_cell::find_nearest_neighbours(double cluster_radius)
413{
414 PROFILE("sirius::Unit_cell::find_nearest_neighbours");
415
416 auto max_frac_coord = find_translations(cluster_radius, lattice_vectors_);
417
418 nearest_neighbours_.clear();
419 nearest_neighbours_.resize(num_atoms());
420
421 #pragma omp parallel for default(shared)
422 for (int ia = 0; ia < num_atoms(); ia++) {
423
424 std::vector<nearest_neighbour_descriptor> nn;
425
426 std::vector<std::pair<double, int>> nn_sort;
427
428 for (int i0 = -max_frac_coord[0]; i0 <= max_frac_coord[0]; i0++) {
429 for (int i1 = -max_frac_coord[1]; i1 <= max_frac_coord[1]; i1++) {
430 for (int i2 = -max_frac_coord[2]; i2 <= max_frac_coord[2]; i2++) {
432 nnd.translation[0] = i0;
433 nnd.translation[1] = i1;
434 nnd.translation[2] = i2;
435
436 for (int ja = 0; ja < num_atoms(); ja++) {
437 auto v1 = atom(ja).position() + r3::vector<int>(nnd.translation) - atom(ia).position();
438 auto rc = get_cartesian_coordinates(v1);
439
440 nnd.atom_id = ja;
441 nnd.rc = rc;
442 nnd.distance = rc.length();
443
444 if (nnd.distance <= cluster_radius) {
445 nn.push_back(nnd);
446
447 nn_sort.push_back(std::pair<double, int>(nnd.distance, (int)nn.size() - 1));
448 }
449 }
450 }
451 }
452 }
453
454 std::sort(nn_sort.begin(), nn_sort.end());
455 nearest_neighbours_[ia].resize(nn.size());
456 for (int i = 0; i < (int)nn.size(); i++) {
457 nearest_neighbours_[ia][i] = nn[nn_sort[i].second];
458 }
459 }
460}
461
462void
463Unit_cell::print_nearest_neighbours(std::ostream& out__) const
464{
465 out__ << "Nearest neighbors" << std::endl
466 << hbar(17, '-') << std::endl;
467 for (int ia = 0; ia < num_atoms(); ia++) {
468 out__ << "Central atom: " << atom(ia).type().symbol() << "(" << ia << ")" << std::endl
469 << hbar(80, '-') << std::endl;
470 out__ << "atom (ia) D [a.u.] T r_local" << std::endl;
471 for (int i = 0; i < (int)nearest_neighbours_[ia].size(); i++) {
472 int ja = nearest_neighbours_[ia][i].atom_id;
473 auto ja_symbol = atom(ja).type().symbol();
474 auto ja_d = nearest_neighbours_[ia][i].distance;
475 auto T = nearest_neighbours_[ia][i].translation;
476 auto r_loc = nearest_neighbours_[ia][i].rc;
477 out__ << std::setw(4) << ja_symbol << " (" << std::setw(5) << ja << ")"
478 << std::setw(12) << std::setprecision(5) << ja_d
479 << std::setw(5) << T[0] << std::setw(5) << T[1] << std::setw(5) << T[2]
480 << std::setw(13) << std::setprecision(5) << std::fixed << r_loc[0]
481 << std::setw(10) << std::setprecision(5) << std::fixed << r_loc[1]
482 << std::setw(10) << std::setprecision(5) << std::fixed << r_loc[2] << std::endl;
483 }
484 }
485 out__ << std::endl;
486}
487
488bool
489Unit_cell::is_point_in_mt(r3::vector<double> vc, int& ja, int& jr, double& dr, double tp[2]) const
490{
491 /* reduce coordinates to the primitive unit cell */
492 auto vr = reduce_coordinates(get_fractional_coordinates(vc));
493
494 for (int ia = 0; ia < num_atoms(); ia++) {
495 for (int i0 = -1; i0 <= 1; i0++) {
496 for (int i1 = -1; i1 <= 1; i1++) {
497 for (int i2 = -1; i2 <= 1; i2++) {
498 /* atom position */
499 r3::vector<double> posf = r3::vector<double>(i0, i1, i2) + atom(ia).position();
500
501 /* vector connecting center of atom and reduced point */
502 r3::vector<double> vf = vr.first - posf;
503
504 /* convert to spherical coordinates */
505 auto vs = r3::spherical_coordinates(get_cartesian_coordinates(vf));
506
507 if (vs[0] < atom(ia).mt_radius()) {
508 ja = ia;
509 tp[0] = vs[1]; // theta
510 tp[1] = vs[2]; // phi
511
512 if (vs[0] < atom(ia).type().radial_grid(0)) {
513 jr = 0;
514 dr = 0.0;
515 } else {
516 for (int ir = 0; ir < atom(ia).num_mt_points() - 1; ir++) {
517 if (vs[0] >= atom(ia).type().radial_grid(ir) &&
518 vs[0] < atom(ia).type().radial_grid(ir + 1)) {
519 jr = ir;
520 dr = vs[0] - atom(ia).type().radial_grid(ir);
521 break;
522 }
523 }
524 }
525
526 return true;
527 }
528 }
529 }
530 }
531 }
532 ja = -1;
533 jr = -1;
534 return false;
535}
536
537void
538Unit_cell::generate_radial_functions(std::ostream& out__)
539{
540 PROFILE("sirius::Unit_cell::generate_radial_functions");
541
542 for (auto it : spl_num_atom_symmetry_classes()) {
543 atom_symmetry_class(it.i).generate_radial_functions(parameters_.valence_relativity());
544 }
545
546 for (int ic = 0; ic < num_atom_symmetry_classes(); ic++) {
547 int rank = spl_num_atom_symmetry_classes().location(typename atom_symmetry_class_index_t::global(ic)).ib;
548 atom_symmetry_class(ic).sync_radial_functions(comm_, rank);
549 }
550
551 if (parameters_.verbosity() >= 2) {
552 mpi::pstdout pout(comm_);
553 if (comm_.rank() == 0) {
554 pout << std::endl << "Linearization energies" << std::endl;
555 }
556
557 for (auto it : spl_num_atom_symmetry_classes()) {
558 atom_symmetry_class(it.i).write_enu(pout);
559 }
560 RTE_OUT(out__) << pout.flush(0);
561 }
562 if (parameters_.verbosity() >= 4 && comm_.rank() == 0) {
563 for (int ic = 0; ic < num_atom_symmetry_classes(); ic++) {
564 atom_symmetry_class(ic).dump_lo();
565 }
566 }
567}
568
569void
570Unit_cell::generate_radial_integrals()
571{
572 PROFILE("sirius::Unit_cell::generate_radial_integrals");
573
574 try {
575 for (auto it : spl_num_atom_symmetry_classes()) {
576 atom_symmetry_class(it.i).generate_radial_integrals(parameters_.valence_relativity());
577 }
578
579 for (int ic = 0; ic < num_atom_symmetry_classes(); ic++) {
580 int rank = spl_num_atom_symmetry_classes().location(typename atom_symmetry_class_index_t::global(ic)).ib;
581 atom_symmetry_class(ic).sync_radial_integrals(comm_, rank);
582 }
583 } catch(std::exception const& e) {
584 std::stringstream s;
585 s << e.what() << std::endl;
586 s << "Error in generating atom_symmetry_class radial integrals";
587 RTE_THROW(s);
588 }
589
590 try {
591 for (auto it : spl_num_atoms_) {
592 atom(it.i).generate_radial_integrals(parameters_.processing_unit(), mpi::Communicator::self());
593 }
594
595 for (int ia = 0; ia < num_atoms(); ia++) {
596 int rank = spl_num_atoms().location(typename atom_index_t::global(ia)).ib;
597 atom(ia).sync_radial_integrals(comm_, rank);
598 }
599 } catch(std::exception const& e) {
600 std::stringstream s;
601 s << e.what() << std::endl;
602 s << "Error in generating atom radial integrals";
603 RTE_THROW(s);
604 }
605}
606
607std::string
608Unit_cell::chemical_formula()
609{
610 std::string name;
611 for (int iat = 0; iat < num_atom_types(); iat++) {
612 name += atom_type(iat).symbol();
613 int n = 0;
614 for (int ia = 0; ia < num_atoms(); ia++) {
615 if (atom(ia).type_id() == atom_type(iat).id())
616 n++;
617 }
618 if (n != 1) {
619 std::stringstream s;
620 s << n;
621 name = (name + s.str());
622 }
623 }
624
625 return name;
626}
627
629Unit_cell::add_atom_type(const std::string label__, const std::string file_name__)
630{
631 int id = next_atom_type_id(label__);
632 atom_types_.push_back(std::shared_ptr<Atom_type>(new Atom_type(parameters_, id, label__, file_name__)));
633 return *atom_types_.back();
634}
635
636void
637Unit_cell::add_atom(const std::string label, r3::vector<double> position, r3::vector<double> vector_field)
638{
639 if (atom_type_id_map_.count(label) == 0) {
640 std::stringstream s;
641 s << "atom type with label " << label << " is not found";
642 RTE_THROW(s);
643 }
644 if (atom_id_by_position(position) >= 0) {
645 std::stringstream s;
646 s << "atom with the same position is already in list" << std::endl
647 << " position : " << position[0] << " " << position[1] << " " << position[2];
648 RTE_THROW(s);
649 }
650
651 atoms_.push_back(std::shared_ptr<Atom>(new Atom(atom_type(label), position, vector_field)));
652 atom_type(label).add_atom_id(static_cast<int>(atoms_.size()) - 1);
653}
654
655double
656Unit_cell::min_bond_length() const
657{
658 double len{1e10};
659
660 for (int ia = 0; ia < num_atoms(); ia++) {
661 if (nearest_neighbours_[ia].size() > 1) {
662 len = std::min(len, nearest_neighbours_[ia][1].distance);
663 }
664 }
665 return len;
666}
667
668void
670{
671 PROFILE("sirius::Unit_cell::initialize");
672
673 /* split number of atom between all MPI ranks */
674 spl_num_atoms_ = splindex_block<atom_index_t>(num_atoms(), n_blocks(comm_.size()), block_id(comm_.rank()));
675
676 /* initialize atom types */
677 for (int iat = 0; iat < num_atom_types(); iat++) {
678 atom_type(iat).init();
679 }
680
681 /* initialize atoms */
682 for (int ia = 0; ia < num_atoms(); ia++) {
683 atom(ia).init();
684 }
685
686 init_paw();
687
688 for (int iat = 0; iat < num_atom_types(); iat++) {
689 int nat = atom_type(iat).num_atoms();
690 if (nat > 0) {
691 atom_coord_.push_back(sddk::mdarray<double, 2>(nat, 3, sddk::memory_t::host));
692 if (parameters_.processing_unit() == sddk::device_t::GPU) {
693 atom_coord_.back().allocate(sddk::memory_t::device);
694 }
695 } else {
696 atom_coord_.push_back(sddk::mdarray<double, 2>());
697 }
698 }
699
700 std::vector<int> offs(this->num_atoms(), -1);
701 int counter{0};
702
703 /* we loop over atoms to check which atom has hubbard orbitals and then
704 compute the number of Hubbard orbitals associated to it */
705 for (auto ia = 0; ia < this->num_atoms(); ia++) {
706 auto& atom = this->atom(ia);
707 if (atom.type().hubbard_correction()) {
708 offs[ia] = counter;
709 counter += atom.type().indexb_hub().size();
710 }
711 }
712 num_hubbard_wf_ = std::make_pair(counter, offs);
713
714 update();
715
716
717 //== write_cif();
718
719 //== if (comm().rank() == 0) {
720 //== std::ofstream ofs(std::string("unit_cell.json"), std::ofstream::out | std::ofstream::trunc);
721 //== ofs << serialize().dump(4);
722 //== }
723}
724
725void
726Unit_cell::get_symmetry()
727{
728 PROFILE("sirius::Unit_cell::get_symmetry");
729
730 if (num_atoms() == 0) {
731 return;
732 }
733
734 if (atom_symmetry_classes_.size() != 0) {
735 atom_symmetry_classes_.clear();
736 for (int ia = 0; ia < num_atoms(); ia++) {
737 atom(ia).set_symmetry_class(nullptr);
738 }
739 }
740
741 sddk::mdarray<double, 2> positions(3, num_atoms());
742 sddk::mdarray<double, 2> spins(3, num_atoms());
743 std::vector<int> types(num_atoms());
744 for (int ia = 0; ia < num_atoms(); ia++) {
745 auto vp = atom(ia).position();
746 auto vf = atom(ia).vector_field();
747 for (int x : {0, 1, 2}) {
748 positions(x, ia) = vp[x];
749 spins(x, ia) = vf[x];
750 }
751 types[ia] = atom(ia).type_id();
752 }
753
754 symmetry_ = std::unique_ptr<Crystal_symmetry>(
755 new Crystal_symmetry(lattice_vectors_, num_atoms(), num_atom_types(), types, positions, spins,
756 parameters_.so_correction(), parameters_.spglib_tolerance(), parameters_.use_symmetry()));
757
758 int atom_class_id{-1};
759 std::vector<int> asc(num_atoms(), -1);
760 for (int i = 0; i < num_atoms(); i++) {
761 /* if symmetry class is not assigned to this atom */
762 if (asc[i] == -1) {
763 /* take next id */
764 atom_class_id++;
765 atom_symmetry_classes_.push_back(std::shared_ptr<Atom_symmetry_class>(
766 new Atom_symmetry_class(atom_class_id, atom(i).type())));
767
768 /* scan all atoms */
769 for (int j = 0; j < num_atoms(); j++) {
770 bool is_equal = (equivalent_atoms_.size())
771 ? (equivalent_atoms_[j] == equivalent_atoms_[i])
772 : (symmetry_->atom_symmetry_class(j) == symmetry_->atom_symmetry_class(i));
773 /* assign new class id for all equivalent atoms */
774 if (is_equal) {
775 asc[j] = atom_class_id;
776 atom_symmetry_classes_.back()->add_atom_id(j);
777 }
778 }
779 }
780 }
781
782 for (auto e : atom_symmetry_classes_) {
783 for (int i = 0; i < e->num_atoms(); i++) {
784 int ia = e->atom_id(i);
785 atom(ia).set_symmetry_class(e);
786 }
787 }
788
789 assert(num_atom_symmetry_classes() != 0);
790}
791
792void
793Unit_cell::import(config_t::unit_cell_t const &inp__)
794{
795 auto lv = r3::matrix<double>(inp__.lattice_vectors());
796 lv *= inp__.lattice_vectors_scale();
797 set_lattice_vectors(r3::vector<double>(lv(0, 0), lv(0, 1), lv(0, 2)),
798 r3::vector<double>(lv(1, 0), lv(1, 1), lv(1, 2)),
799 r3::vector<double>(lv(2, 0), lv(2, 1), lv(2, 2)));
800 /* here lv are copied from the JSON dictionary as three row vectors; however
801 in the code the lattice vectors are stored as three column vectors, so
802 transposition is needed here */
803 auto ilvT = transpose(inverse(lv));
804
805 auto units = inp__.atom_coordinate_units();
806
807 /* first, load all types */
808 for (auto label: inp__.atom_types()) {
809 auto fname = inp__.atom_files(label);
810 add_atom_type(label, fname);
811 }
812 for (auto label : inp__.atom_types()) {
813 for (auto v : inp__.atoms(label)) {
814 r3::vector<double> p(v[0], v[1], v[2]);
816 if (v.size() == 6) {
817 f = r3::vector<double>(v[3], v[4], v[5]);
818 }
819 /* convert to atomic units */
820 if (units == "A") {
821 for (int x : {0, 1, 2}) {
822 p[x] /= bohr_radius;
823 }
824 }
825 /* convert from Cartesian to lattice coordinates */
826 if (units == "au" || units == "A") {
827 p = dot(ilvT, p);
828 auto rc = reduce_coordinates(p);
829 for (int x : {0, 1, 2}) {
830 p[x] = rc.first[x];
831 }
832 }
833 add_atom(label, p, f);
834 }
835 }
836}
837
838void
839Unit_cell::update()
840{
841 PROFILE("sirius::Unit_cell::update");
842
843 auto v0 = lattice_vector(0);
844 auto v1 = lattice_vector(1);
845 auto v2 = lattice_vector(2);
846
847 double r{0};
848 if (parameters_.cfg().parameters().nn_radius() < 0) {
849 r = std::max(v0.length(), std::max(v1.length(), v2.length()));
850 } else {
851 r = parameters_.cfg().parameters().nn_radius();
852 }
853
854 find_nearest_neighbours(r);
855
856 if (parameters_.full_potential()) {
857 /* find new MT radii and initialize radial grid */
858 if (parameters_.auto_rmt()) {
859 auto rg = get_radial_grid_t(parameters_.cfg().settings().radial_grid());
860 auto Rmt = find_mt_radii(parameters_.auto_rmt(), true);
861 for (int iat = 0; iat < num_atom_types(); iat++) {
862 double r0 = atom_type(iat).radial_grid().first();
863
864 atom_type(iat).set_radial_grid(rg.first, atom_type(iat).num_mt_points(), r0, Rmt[iat], rg.second);
865 }
866 }
867
868 int ia, ja;
869 if (check_mt_overlap(ia, ja)) {
870 std::stringstream s;
871 s << "overlaping muffin-tin spheres for atoms " << ia << "(" << atom(ia).type().symbol() << ")"
872 << " and " << ja << "(" << atom(ja).type().symbol() << ")" << std::endl
873 << " radius of atom " << ia << " : " << atom(ia).mt_radius() << std::endl
874 << " radius of atom " << ja << " : " << atom(ja).mt_radius() << std::endl
875 << " distance : " << nearest_neighbours_[ia][1].distance << " " << nearest_neighbours_[ja][1].distance;
876 RTE_THROW(s);
877 }
878 }
879
880 get_symmetry();
881
882 spl_num_atom_symmetry_classes_ = splindex_block<atom_symmetry_class_index_t>(num_atom_symmetry_classes(),
883 n_blocks(comm_.size()), block_id(comm_.rank()));
884
885 volume_mt_ = 0.0;
886 if (parameters_.full_potential()) {
887 for (int ia = 0; ia < num_atoms(); ia++) {
888 volume_mt_ += fourpi * std::pow(atom(ia).mt_radius(), 3) / 3.0;
889 }
890 }
891
892 volume_it_ = omega() - volume_mt_;
893
894 for (int iat = 0; iat < num_atom_types(); iat++) {
895 int nat = atom_type(iat).num_atoms();
896 if (nat > 0) {
897 for (int i = 0; i < nat; i++) {
898 int ia = atom_type(iat).atom_id(i);
899 for (int x: {0, 1, 2}) {
900 atom_coord_[iat](i, x) = atom(ia).position()[x];
901 }
902 }
903 if (parameters_.processing_unit() == sddk::device_t::GPU) {
904 atom_coord_[iat].copy_to(sddk::memory_t::device);
905 }
906 }
907 }
908}
909
910void
911Unit_cell::set_lattice_vectors(r3::matrix<double> lattice_vectors__)
912{
913 lattice_vectors_ = lattice_vectors__;
914 inverse_lattice_vectors_ = inverse(lattice_vectors_);
915 omega_ = std::abs(lattice_vectors_.det());
916 reciprocal_lattice_vectors_ = transpose(inverse(lattice_vectors_)) * twopi;
917}
918
919void
920Unit_cell::set_lattice_vectors(r3::vector<double> a0__, r3::vector<double> a1__, r3::vector<double> a2__)
921{
923 for (int x : {0, 1, 2}) {
924 lv(x, 0) = a0__[x];
925 lv(x, 1) = a1__[x];
926 lv(x, 2) = a2__[x];
927 }
928 set_lattice_vectors(lv);
929}
930
931int
932Unit_cell::atom_id_by_position(r3::vector<double> position__)
933{
934 for (int ia = 0; ia < num_atoms(); ia++) {
935 auto vd = atom(ia).position() - position__;
936 if (vd.length() < 1e-10) {
937 return ia;
938 }
939 }
940 return -1;
941}
942
943int
944Unit_cell::next_atom_type_id(std::string label__)
945{
946 /* check if the label was already added */
947 if (atom_type_id_map_.count(label__) != 0) {
948 std::stringstream s;
949 s << "atom type with label " << label__ << " is already in list";
950 RTE_THROW(s);
951 }
952 /* take text id */
953 atom_type_id_map_[label__] = static_cast<int>(atom_types_.size());
954 return atom_type_id_map_[label__];
955}
956
957void
958Unit_cell::init_paw()
959{
960 for (int ia = 0; ia < num_atoms(); ia++) {
961 if (atom(ia).type().is_paw()) {
962 paw_atom_index_.push_back(ia);
963 }
964 }
965
966 spl_num_paw_atoms_ = splindex_block<paw_atom_index_t>(num_paw_atoms(), n_blocks(comm_.size()),
967 block_id(comm_.rank()));
968}
969
970std::pair<int, std::vector<int>>
971Unit_cell::num_ps_atomic_wf() const
972{
973 std::vector<int> offs(this->num_atoms(), -1);
974 int counter{0};
975 for (auto ia = 0; ia < this->num_atoms(); ia++) {
976 auto& atom = this->atom(ia);
977 offs[ia] = counter;
978 counter += atom.type().indexb_wfs().size();
979 }
980 return std::make_pair(counter, offs);
981}
982
983} // namespace sirius
static JSON_HEDLEY_WARN_UNUSED_RESULT basic_json object(initializer_list_t init={})
explicitly create an object from an initializer list
array_t * array
array (stored with pointer to save storage)
Data and methods specific to the symmetry class of the atom.
Defines the properties of atom type.
Definition: atom_type.hpp:93
Data and methods specific to the actual atom in the unit cell.
Definition: atom.hpp:37
Representation of the crystal symmetry.
Unit cell representation.
Definition: config.hpp:336
auto lattice_vectors_scale() const
Scaling factor for the lattice vectors.
Definition: config.hpp:358
auto atom_coordinate_units() const
Type of atomic coordinates: lattice, atomic units or Angstroms.
Definition: config.hpp:370
auto atom_files(std::string label__) const
Mapping between atom type labels and atomic files.
Definition: config.hpp:394
auto atom_types() const
List of atom type labels.
Definition: config.hpp:382
auto lattice_vectors() const
Three non-collinear vectors of the primitive unit cell.
Definition: config.hpp:343
auto atoms(std::string label__) const
Atomic coordinates.
Definition: config.hpp:400
Floating-point formatting (precision and width).
Horisontal bar.
Simple implementation of 3d vector.
Definition: r3.hpp:45
Contains definition and partial implementation of sirius::Crystal_symmetry class.
auto transpose(matrix< T > src)
Return transpose of the matrix.
Definition: r3.hpp:445
auto find_translations(double radius__, matrix< double > const &lattice_vectors__)
Find supercell that circumscribes the sphere with a given radius.
Definition: r3.hpp:572
auto inverse(matrix< int > src)
Return inverse of the integer matrix.
Definition: r3.hpp:475
auto spherical_coordinates(vector< double > vc)
Transform Cartesian coordinates [x,y,z] to spherical coordinates [r,theta,phi].
Definition: r3.hpp:590
auto reduce_coordinates(vector< double > coord__)
Reduce the coordinates to the first unit cell.
Definition: r3.hpp:523
Namespace of the SIRIUS library.
Definition: sirius.f90:5
const double bohr_radius
Bohr radius in angstroms.
Definition: constants.hpp:39
void initialize(bool call_mpi_init__=true)
Initialize the library.
Definition: sirius.hpp:82
const double pi
Definition: constants.hpp:42
const double fourpi
Definition: constants.hpp:48
strong_type< int, struct __n_blocks_tag > n_blocks
Number of blocks to which the global index is split.
Definition: splindex.hpp:105
Output stream tools.
Descriptor of an atom in a list of nearest neighbours for each atom.
Definition: typedefs.hpp:160
std::array< double, 3 > rc
Vector connecting central atom with the neighbour in Cartesian coordinates.
Definition: typedefs.hpp:171
double distance
Distance from the central atom.
Definition: typedefs.hpp:168
std::array< int, 3 > translation
Translation in fractional coordinates.
Definition: typedefs.hpp:165
int atom_id
Index of the neighbouring atom.
Definition: typedefs.hpp:162
Contains definition and partial implementation of sirius::Unit_cell class.