SIRIUS 7.5.0
Electronic structure library and applications
symmetrize_pw_function.hpp
Go to the documentation of this file.
1// Copyright (c) 2013-2023 Anton Kozhevnikov, Thomas Schulthess
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without modification, are permitted provided that
5// the following conditions are met:
6//
7// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
8// following disclaimer.
9// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
10// and the following disclaimer in the documentation and/or other materials provided with the distribution.
11//
12// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
13// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
14// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
15// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
16// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
17// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
18// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19
20/** \file symmetrize_pw_function.hpp
21 *
22 * \brief Symmetrize plane-wave coefficients of regular-grid function.
23 */
24
25#ifndef __SYMMETRIZE_PW_FUNCTION_HPP__
26#define __SYMMETRIZE_PW_FUNCTION_HPP__
27
30
31namespace sirius {
32
33/// Symmetrize scalar or vector function.
34/** The following operation is performed:
35 \f[
36 f_{\mathrm{sym}}({\bf r}) = \frac{1}{N_{\mathrm{sym}}}
37 \sum_{\hat{\bf S}\hat{\bf P}} \hat {\bf S} \hat {\bf P}f({\bf r})
38 \f]
39 where \f$ f({\bf r}) \f$ has to be understood as an unsymmetrized scalar or vector function.
40 In the case of a scalar function \f$ \hat {\bf S} = 1 \f$. In the case of vector function
41 \f$ \hat {\bf S} \f$ is rotation matrix acting on the Cartesian components of the function.
42 \f$ \hat {\bf P} = \{{\bf R}|{\bf t}\} \f$ is a spacial part of the full magentic symmetry operatoin acting
43 on the real-space coordinates.
44
45 For the function expanded in plane-waves we have:
46 \f[
47 f_{\mathrm{sym}}({\bf r}) = \frac{1}{N_{\mathrm{sym}}}
48 \sum_{\hat{\bf S}\hat{\bf P}} \hat {\bf S} \hat {\bf P}f({\bf r}) =
49 \frac{1}{N_{\mathrm{sym}}} \sum_{\hat{\bf S}\hat{\bf P}} \hat {\bf S} \sum_{\bf G}
50 f({\bf G}) e^{i{\bf G}\hat{\bf P}^{-1}{\bf r}} = \\
51 \frac{1}{N_{\mathrm{sym}}} \sum_{\hat{\bf S}\hat{\bf P}} \sum_{\bf G} \hat {\bf S} f({\bf G})
52 e^{i{\bf G}({\bf R}^{-1}{\bf r} - {\bf R}^{-1}{\bf t})} =
53 \frac{1}{N_{\mathrm{sym}}} \sum_{\hat{\bf S}\hat{\bf P}} \sum_{\bf G} \hat {\bf S} f({\bf G})
54 e^{i{\bf G}'{\bf r}} e^{-i{\bf G}'{\bf t}}
55 \f]
56 where \f$ {\bf G}' = {\bf G}{\bf R}^{-1} = {\bf R}^{-T}{\bf G} \f$. The last expression establishes the link
57 between unsymmetrized plane-wave coefficient at <b>G</b>-vector and symmetrized coefficient at <b>G</b>'. We will
58 rewrite the expression using inverse relation \f$ {\bf G} = {\bf R}^{T}{\bf G'} \f$ and summing over <b>G</b>'
59 (which is just a permutaion of <b>G</b>):
60 \f[
61 f_{\mathrm{sym}}({\bf r}) =
62 \sum_{\bf G'} e^{i{\bf G}'{\bf r}} \frac{1}{N_{\mathrm{sym}}} \sum_{\hat{\bf S}\hat{\bf P}}
63 \hat {\bf S} f({\bf R}^{T}{\bf G'}) e^{-i{\bf G}'{\bf t}}
64 \f]
65 That gives an expression for the symmetrized plane-wave coefficient at <b>G</b>':
66 \f[
67 f_{\mathrm{sym}}({\bf G}') = \frac{1}{N_{\mathrm{sym}}} \sum_{\hat{\bf S}\hat{\bf P}}
68 \hat {\bf S} f({\bf R}^{T}{\bf G'}) e^{-i{\bf G}'{\bf t}}
69 \f]
70
71 Once \f$ f_{\mathrm{sym}}({\bf G}) \f$ has been calculated for a single <b>G</b>, its values at a
72 star of <b>G</b> can be calculated using the following relation:
73 \f[
74 f_{\mathrm{sym}}({\bf r}) = \hat{\bf S}\hat{\bf P} f_{\mathrm{sym}}({\bf r}) =
75 \hat{\bf S} f_{\mathrm{sym}}(\hat {\bf P}^{-1}{\bf r})
76 \f]
77 which leads to the following relation for the plane-wave coefficient:
78 \f[
79 \sum_{\bf G} f_{\mathrm{sym}}({\bf G})e^{i{\bf G}{\bf r}} =
80 \sum_{\bf G} \hat{\bf S}f_{\mathrm{sym}}({\bf G})e^{i{\bf G}\hat{\bf P}^{-1}{\bf r}} =
81 \sum_{\bf G} \hat{\bf S}f_{\mathrm{sym}}({\bf G})e^{i{\bf G}{\bf R}^{-1}{\bf r}}
82 e^{-i{\bf G}{\bf R}^{-1}{\bf t}} =
83 \sum_{\bf G'} \hat{\bf S}f_{\mathrm{sym}}({\bf G})e^{i{\bf G}'{\bf r}}
84 e^{-i{\bf G}'{\bf t}} =
85 \sum_{\bf G'} \hat{\bf S}f_{\mathrm{sym}}({\bf G'})e^{i{\bf G}'{\bf r}}
86 \f]
87 and so
88 \f[
89 f_{\mathrm{sym}}({\bf G}') = \hat{\bf S}f_{\mathrm{sym}}({\bf G})e^{-i{\bf G'}{\bf t}}
90 \f]
91
92 \param [in] sym Description of the crystal symmetry.
93 \param [in] gvec_shells Description of the G-vector shells.
94 \param [in] sym_phase_factors Phase factors associated with fractional translations.
95 \param [in] num_mag_dims Number of magnetic dimensions.
96 \param [inout] frg Array of pointers to scalar and vector parts of the filed being symmetrized.
97 */
98inline void
99symmetrize_pw_function(Crystal_symmetry const& sym__, fft::Gvec_shells const& gvec_shells__,
100 sddk::mdarray<std::complex<double>, 3> const& sym_phase_factors__,
101 int num_mag_dims__, std::vector<Smooth_periodic_function<double>*> frg__)
102{
103 PROFILE("sirius::symmetrize_pw_function");
104
105 std::array<std::vector<std::complex<double>>, 4> fpw_remapped;
106 std::array<std::vector<std::complex<double>>, 4> fpw_sym;
107
108 /* local number of G-vectors in a distribution with complete G-vector shells */
109 int ngv = gvec_shells__.gvec_count_remapped();
110
111 for (int j = 0; j < num_mag_dims__ + 1; j++) {
112 fpw_remapped[j] = gvec_shells__.remap_forward(&frg__[j]->f_pw_local(0));
113 fpw_sym[j] = std::vector<std::complex<double>>(ngv, 0);
114 }
115
116 std::vector<bool> is_done(ngv, false);
117
118 double norm = 1 / double(sym__.size());
119
120 auto phase_factor = [&](int isym, r3::vector<int> G) {
121 return sym_phase_factors__(0, G[0], isym) * sym_phase_factors__(1, G[1], isym) *
122 sym_phase_factors__(2, G[2], isym);
123 };
124
125 double const eps{1e-9};
126
127 PROFILE_START("sirius::symmetrize|fpw|local");
128
129 #pragma omp parallel
130 {
131 int nt = omp_get_max_threads();
132 int tid = omp_get_thread_num();
133
134 for (int igloc = 0; igloc < gvec_shells__.gvec_count_remapped(); igloc++) {
135 auto G = gvec_shells__.gvec_remapped(igloc);
136
137 int igsh = gvec_shells__.gvec_shell_remapped(igloc);
138
139#if !defined(NDEBUG)
140 if (igsh != gvec_shells__.gvec().shell(G)) {
141 std::stringstream s;
142 s << "wrong index of G-shell" << std::endl
143 << " G-vector : " << G << std::endl
144 << " igsh in the remapped set : " << igsh << std::endl
145 << " igsh in the original set : " << gvec_shells__.gvec().shell(G);
146 RTE_THROW(s);
147 }
148#endif
149 /* each thread is working on full shell of G-vectors */
150 if (igsh % nt == tid && !is_done[igloc]) {
151
152 std::complex<double> symf(0, 0);
153 std::complex<double> symx(0, 0);
154 std::complex<double> symy(0, 0);
155 std::complex<double> symz(0, 0);
156
157 /* find the symmetrized PW coefficient */
158
159 for (int i = 0; i < sym__.size(); i++) {
160 auto G1 = r3::dot(G, sym__[i].spg_op.R);
161
162 auto S = sym__[i].spin_rotation;
163
164 auto phase = std::conj(phase_factor(i, G));
165
166 /* local index of a rotated G-vector */
167 int ig1 = gvec_shells__.index_by_gvec(G1);
168
169 bool conj_coeff{false};
170
171 /* check the reduced G-vector */
172 if (ig1 == -1) {
173 G1 = G1 * (-1);
174 conj_coeff = true;
175 }
176 auto do_conj = (conj_coeff) ? [](std::complex<double> z){return std::conj(z);} :
177 [](std::complex<double> z){return z;};
178#if !defined(NDEBUG)
179 if (igsh != gvec_shells__.gvec().shell(G1)) {
180 std::stringstream s;
181 s << "wrong index of G-shell" << std::endl
182 << " index of G-shell : " << igsh << std::endl
183 << " symmetry operation : " << sym__[i].spg_op.R << std::endl
184 << " G-vector : " << G << std::endl
185 << " rotated G-vector : " << G1 << std::endl
186 << " G-vector index : " << gvec_shells__.index_by_gvec(G1) << std::endl
187 << " index of rotated G-vector shell : " << gvec_shells__.gvec().shell(G1);
188 RTE_THROW(s);
189 }
190#endif
191 ig1 = gvec_shells__.index_by_gvec(G1);
192 RTE_ASSERT(ig1 >= 0 && ig1 < ngv);
193
194 if (frg__[0]) {
195 symf += do_conj(fpw_remapped[0][ig1]) * phase;
196 }
197 if (num_mag_dims__ == 1 && frg__[1]) {
198 symz += do_conj(fpw_remapped[1][ig1]) * phase * S(2, 2);
199 }
200 if (num_mag_dims__ == 3) {
201 auto v = r3::dot(S, r3::vector<std::complex<double>>({fpw_remapped[1][ig1],
202 fpw_remapped[2][ig1], fpw_remapped[3][ig1]}));
203 symx += do_conj(v[0]) * phase;
204 symy += do_conj(v[1]) * phase;
205 symz += do_conj(v[2]) * phase;
206 }
207 } /* loop over symmetries */
208
209 symf *= norm;
210 symx *= norm;
211 symy *= norm;
212 symz *= norm;
213
214 /* apply symmetry operation and get all other plane-wave coefficients */
215
216 for (int isym = 0; isym < sym__.size(); isym++) {
217 auto S = sym__[isym].spin_rotation;
218
219 /* get rotated G-vector */
220 auto G1 = r3::dot(sym__[isym].spg_op.invRT, G);
221 /* index of a rotated G-vector */
222 int ig1 = gvec_shells__.index_by_gvec(G1);
223
224 if (ig1 != -1) {
225 RTE_ASSERT(ig1 >= 0 && ig1 < ngv);
226 auto phase = std::conj(phase_factor(isym, G1));
227 std::complex<double> symf1, symx1, symy1, symz1;
228 if (frg__[0]) {
229 symf1 = symf * phase;
230 }
231 if (num_mag_dims__ == 1 && frg__[1]) {
232 symz1 = symz * phase * S(2, 2);
233 }
234 if (num_mag_dims__ == 3) {
235 auto v = r3::dot(S, r3::vector<std::complex<double>>({symx, symy, symz}));
236 symx1 = v[0] * phase;
237 symy1 = v[1] * phase;
238 symz1 = v[2] * phase;
239 }
240
241 if (is_done[ig1]) {
242 /* run checks */
243 if (frg__[0]) {
244 /* check that another symmetry operation leads to the same coefficient */
245 if (abs_diff(fpw_sym[0][ig1], symf1) > eps) {
246 std::stringstream s;
247 s << "inconsistent symmetry operation" << std::endl
248 << " existing value : " << fpw_sym[0][ig1] << std::endl
249 << " computed value : " << symf1 << std::endl
250 << " difference: " << std::abs(fpw_sym[0][ig1] - symf1) << std::endl;
251 RTE_THROW(s);
252 }
253 }
254 if (num_mag_dims__ == 1 && frg__[1]) {
255 if (abs_diff(fpw_sym[1][ig1], symz1) > eps) {
256 std::stringstream s;
257 s << "inconsistent symmetry operation" << std::endl
258 << " existing value : " << fpw_sym[1][ig1] << std::endl
259 << " computed value : " << symz1 << std::endl
260 << " difference: " << std::abs(fpw_sym[1][ig1] - symz1) << std::endl;
261 RTE_THROW(s);
262 }
263 }
264 if (num_mag_dims__ == 3) {
265 if (abs_diff(fpw_sym[1][ig1], symx1) > eps ||
266 abs_diff(fpw_sym[2][ig1], symy1) > eps ||
267 abs_diff(fpw_sym[3][ig1], symz1) > eps) {
268
269 RTE_THROW("inconsistent symmetry operation");
270 }
271 }
272 } else {
273 if (frg__[0]) {
274 fpw_sym[0][ig1] = symf1;
275 }
276 if (num_mag_dims__ == 1 && frg__[1]) {
277 fpw_sym[1][ig1] = symz1;
278 }
279 if (num_mag_dims__ == 3) {
280 fpw_sym[1][ig1] = symx1;
281 fpw_sym[2][ig1] = symy1;
282 fpw_sym[3][ig1] = symz1;
283 }
284 is_done[ig1] = true;
285 }
286 }
287 } /* loop over symmetries */
288 }
289 } /* loop over igloc */
290 }
291 PROFILE_STOP("sirius::symmetrize|fpw|local");
292
293#if !defined(NDEBUG)
294 for (int igloc = 0; igloc < gvec_shells__.gvec_count_remapped(); igloc++) {
295 auto G = gvec_shells__.gvec_remapped(igloc);
296 for (int isym = 0; isym < sym__.size(); isym++) {
297 auto S = sym__[isym].spin_rotation;
298 auto gv_rot = r3::dot(sym__[isym].spg_op.invRT, G);
299 /* index of a rotated G-vector */
300 int ig_rot = gvec_shells__.index_by_gvec(gv_rot);
301 std::complex<double> phase = std::conj(phase_factor(isym, gv_rot));
302
303 if (frg__[0] && ig_rot != -1) {
304 auto diff = abs_diff(fpw_sym[0][ig_rot], fpw_sym[0][igloc] * phase);
305 if (diff > eps) {
306 std::stringstream s;
307 s << "check of symmetrized PW coefficients failed" << std::endl
308 << "difference : " << diff;
309 RTE_THROW(s);
310 }
311 }
312 if (num_mag_dims__ == 1 && frg__[1] && ig_rot != -1) {
313 auto diff = abs_diff(fpw_sym[1][ig_rot], fpw_sym[1][igloc] * phase * S(2, 2));
314 if (diff > eps) {
315 std::stringstream s;
316 s << "check of symmetrized PW coefficients failed" << std::endl
317 << "difference : " << diff;
318 RTE_THROW(s);
319 }
320 }
321 }
322 }
323#endif
324
325 for (int j = 0; j < num_mag_dims__ + 1; j++) {
326 gvec_shells__.remap_backward(fpw_sym[j], &frg__[j]->f_pw_local(0));
327 }
328}
329
330}
331
332#endif
Representation of the crystal symmetry.
int size() const
Number of symmetries including the magnetic configuration.
Representation of a smooth (Fourier-transformable) periodic function.
Helper class to manage G-vector shells and redistribute G-vectors for symmetrization.
Definition: gvec.hpp:916
int gvec_count_remapped() const
Local number of G-vectors in the remapped distribution with complete shells on each rank.
Definition: gvec.hpp:964
r3::vector< int > gvec_remapped(int igloc__) const
G-vector by local index (in the remapped set).
Definition: gvec.hpp:970
int gvec_shell_remapped(int igloc__) const
Index of the G-vector shell by the local G-vector index (in the remapped set).
Definition: gvec.hpp:986
int index_by_gvec(r3::vector< int > G__) const
Return local index of the G-vector in the remapped set.
Definition: gvec.hpp:976
int shell(int ig__) const
Return index of the G-vector shell by the G-vector index.
Definition: gvec.hpp:608
Simple implementation of 3d vector.
Definition: r3.hpp:45
Multidimensional array with the column-major (Fortran) order.
Definition: memory.hpp:660
Namespace of the SIRIUS library.
Definition: sirius.f90:5
void symmetrize_pw_function(Crystal_symmetry const &sym__, fft::Gvec_shells const &gvec_shells__, sddk::mdarray< std::complex< double >, 3 > const &sym_phase_factors__, int num_mag_dims__, std::vector< Smooth_periodic_function< double > * > frg__)
Symmetrize scalar or vector function.
auto conj(double x__)
Return complex conjugate of a number. For a real value this is the number itself.
Definition: math_tools.hpp:165
Contains declaration and implementation of sirius::Smooth_periodic_function and sirius::Smooth_period...
Contains declaration and implementation of sirius::Spheric_function and sirius::Spheric_function_grad...