25#ifndef __SYMMETRIZE_OCCUPATION_MATRIX_HPP__
26#define __SYMMETRIZE_OCCUPATION_MATRIX_HPP__
33symmetrize_occupation_matrix(Occupation_matrix& om__)
35 auto& ctx = om__.ctx();
36 auto& uc = ctx.unit_cell();
38 if (!ctx.hubbard_correction()) {
42 auto& sym = uc.symmetry();
43 const double f = 1.0 / sym.size();
44 std::vector<sddk::mdarray<std::complex<double>, 3>> local_tmp;
46 local_tmp.resize(om__.local().size());
48 for (
int at_lvl = 0; at_lvl < static_cast<int>(om__.local().size()); at_lvl++) {
49 const int ia = om__.atomic_orbitals(at_lvl).first;
50 auto const& atom_type = uc.atom(ia).type();
53 if (atom_type.lo_descriptor_hub(om__.atomic_orbitals(at_lvl).second).use_for_calculation()) {
55 sddk::mdarray<std::complex<double>, 3>(om__.local(at_lvl).size(0), om__.local(at_lvl).size(1), 4);
56 copy(om__.local(at_lvl), local_tmp[at_lvl]);
60 for (
int at_lvl = 0; at_lvl < static_cast<int>(om__.local().size()); at_lvl++) {
61 const int ia = om__.atomic_orbitals(at_lvl).first;
62 const auto& atom = uc.atom(ia);
63 om__.local(at_lvl).zero();
66 if (atom.type().lo_descriptor_hub(om__.atomic_orbitals(at_lvl).second).use_for_calculation()) {
67 const int il = atom.type().lo_descriptor_hub(om__.atomic_orbitals(at_lvl).second).l();
68 const int lmmax_at = 2 * il + 1;
70 sddk::mdarray<std::complex<double>, 3> dm_ia(lmmax_at, lmmax_at, 4);
71 for (
int isym = 0; isym < sym.size(); isym++) {
72 int pr = sym[isym].spg_op.proper;
73 auto eang = sym[isym].spg_op.euler_angles;
74 auto rotm = sht::rotation_matrix<double>(4, eang, pr);
77 int iap = sym[isym].spg_op.inv_sym_atom[ia];
81 om__.find_orbital_index(iap, atom.type().lo_descriptor_hub(om__.atomic_orbitals(at_lvl).second).n(),
82 atom.type().lo_descriptor_hub(om__.atomic_orbitals(at_lvl).second).l());
84 for (
int ispn = 0; ispn < (ctx.num_mag_dims() == 3 ? 4 : ctx.num_spins()); ispn++) {
85 for (
int m1 = 0; m1 < lmmax_at; m1++) {
86 for (
int m2 = 0; m2 < lmmax_at; m2++) {
87 for (
int m1p = 0; m1p < lmmax_at; m1p++) {
88 for (
int m2p = 0; m2p < lmmax_at; m2p++) {
89 dm_ia(m1, m2, ispn) +=
90 rotm[il](m1, m1p) * rotm[il](m2, m2p) * local_tmp[at_lvl1](m1p, m2p, ispn) * f;
97 if (ctx.num_mag_dims() == 0) {
98 for (
int m1 = 0; m1 < lmmax_at; m1++) {
99 for (
int m2 = 0; m2 < lmmax_at; m2++) {
100 om__.local(at_lvl)(m1, m2, 0) += dm_ia(m1, m2, 0);
105 if (ctx.num_mag_dims() == 1) {
106 int const map_s[3][2] = {{0, 0}, {1, 1}, {0, 1}};
107 for (
int j = 0; j < 2; j++) {
108 int s1 = map_s[j][0];
109 int s2 = map_s[j][1];
111 for (
int m1 = 0; m1 < lmmax_at; m1++) {
112 for (
int m2 = 0; m2 < lmmax_at; m2++) {
113 std::complex<double> dm[2][2] = {{dm_ia(m1, m2, 0), 0}, {0, dm_ia(m1, m2, 1)}};
115 for (
int s1p = 0; s1p < 2; s1p++) {
116 for (
int s2p = 0; s2p < 2; s2p++) {
117 om__.local(at_lvl)(m1, m2, j) +=
118 dm[s1p][s2p] * spin_rot_su2(s1, s1p) *
std::conj(spin_rot_su2(s2, s2p));
126 if (ctx.num_mag_dims() == 3) {
127 int s_idx[2][2] = {{0, 3}, {2, 1}};
128 for (
int m1 = 0; m1 < lmmax_at; m1++) {
129 for (
int m2 = 0; m2 < lmmax_at; m2++) {
131 std::complex<double> dm[2][2];
132 std::complex<double> dm1[2][2] = {{0.0, 0.0}, {0.0, 0.0}};
133 for (
int s1 = 0; s1 < ctx.num_spins(); s1++) {
134 for (
int s2 = 0; s2 < ctx.num_spins(); s2++) {
135 dm[s1][s2] = dm_ia(m1, m2, s_idx[s1][s2]);
139 for (
int i = 0; i < 2; i++) {
140 for (
int j = 0; j < 2; j++) {
141 for (
int s1p = 0; s1p < 2; s1p++) {
142 for (
int s2p = 0; s2p < 2; s2p++) {
144 dm[s1p][s2p] * spin_rot_su2(i, s1p) *
std::conj(spin_rot_su2(j, s2p));
150 for (
int s1 = 0; s1 < ctx.num_spins(); s1++) {
151 for (
int s2 = 0; s2 < ctx.num_spins(); s2++) {
152 om__.local(at_lvl)(m1, m2, s_idx[s1][s2]) += dm1[s1][s2];
162 if (ctx.cfg().hubbard().nonlocal().size() && ctx.num_mag_dims() == 3) {
163 RTE_THROW(
"non-collinear nonlocal occupancy symmetrization is not implemented");
167 auto r = uc.num_hubbard_wf();
169 for (
int i = 0; i < static_cast<int>(ctx.cfg().hubbard().nonlocal().size()); i++) {
170 auto nl = ctx.cfg().hubbard().nonlocal(i);
171 int ia = nl.atom_pair()[0];
172 int ja = nl.atom_pair()[1];
180 om__.nonlocal(i).zero();
181 for (
int isym = 0; isym < sym.size(); isym++) {
182 int pr = sym[isym].spg_op.proper;
183 auto eang = sym[isym].spg_op.euler_angles;
184 auto rotm = sht::rotation_matrix<double>(4, eang, pr);
187 int iap = sym[isym].spg_op.inv_sym_atom[ia];
188 int jap = sym[isym].spg_op.inv_sym_atom[ja];
190 auto Ttot = sym[isym].spg_op.inv_sym_atom_T[ja] - sym[isym].spg_op.inv_sym_atom_T[ia] +
191 dot(sym[isym].spg_op.invR, r3::vector<int>(T));
197 int at1_lvl = om__.find_orbital_index(iap, n1, il);
198 int at2_lvl = om__.find_orbital_index(jap, n2, jl);
199 auto const& occ_mtrx = om__.occ_mtrx_T(Ttot);
201 sddk::mdarray<std::complex<double>, 3> dm_ia_ja(2 * il + 1, 2 * jl + 1, ctx.num_spins());
204 for (
int ispn = 0; ispn < ctx.num_spins(); ispn++) {
205 for (
int m1 = 0; m1 < ib; m1++) {
206 for (
int m2 = 0; m2 < jb; m2++) {
207 for (
int m1p = 0; m1p < ib; m1p++) {
208 for (
int m2p = 0; m2p < jb; m2p++) {
209 dm_ia_ja(m1, m2, ispn) +=
210 rotm[il](m1, m1p) * rotm[jl](m2, m2p) *
211 occ_mtrx(om__.offset(at1_lvl) + m1p, om__.offset(at2_lvl) + m2p, ispn) * f;
218 if (ctx.num_mag_dims() == 0) {
219 for (
int m1 = 0; m1 < ib; m1++) {
220 for (
int m2 = 0; m2 < jb; m2++) {
221 om__.nonlocal(i)(m1, m2, 0) += dm_ia_ja(m1, m2, 0);
225 if (ctx.num_mag_dims() == 1) {
226 int const map_s[3][2] = {{0, 0}, {1, 1}, {0, 1}};
227 for (
int j = 0; j < 2; j++) {
228 int s1 = map_s[j][0];
229 int s2 = map_s[j][1];
231 for (
int m1 = 0; m1 < ib; m1++) {
232 for (
int m2 = 0; m2 < jb; m2++) {
233 std::complex<double> dm[2][2] = {{dm_ia_ja(m1, m2, 0), 0}, {0, dm_ia_ja(m1, m2, 1)}};
235 for (
int s1p = 0; s1p < 2; s1p++) {
236 for (
int s2p = 0; s2p < 2; s2p++) {
237 om__.nonlocal(i)(m1, m2, j) +=
238 dm[s1p][s2p] * spin_rot_su2(s1, s1p) *
std::conj(spin_rot_su2(s2, s2p));
void copy(T *target__, T const *source__, size_t n__)
Copy memory inside a device.
Namespace of the SIRIUS library.
auto rotation_matrix_su2(std::array< double, 3 > u__, double theta__)
Generate SU(2) rotation matrix from the axes and angle.
auto conj(double x__)
Return complex conjugate of a number. For a real value this is the number itself.
Occupation matrix of the LDA+U method.