1 /* Copyright (C) 2015 Atsushi Togo */
2 /* All rights reserved. */
3 
4 /* This file is part of phonopy. */
5 
6 /* Redistribution and use in source and binary forms, with or without */
7 /* modification, are permitted provided that the following conditions */
8 /* are met: */
9 
10 /* * Redistributions of source code must retain the above copyright */
11 /*   notice, this list of conditions and the following disclaimer. */
12 
13 /* * Redistributions in binary form must reproduce the above copyright */
14 /*   notice, this list of conditions and the following disclaimer in */
15 /*   the documentation and/or other materials provided with the */
16 /*   distribution. */
17 
18 /* * Neither the name of the phonopy project nor the names of its */
19 /*   contributors may be used to endorse or promote products derived */
20 /*   from this software without specific prior written permission. */
21 
22 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
23 /* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
24 /* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
25 /* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE */
26 /* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, */
27 /* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, */
28 /* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; */
29 /* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER */
30 /* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT */
31 /* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN */
32 /* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE */
33 /* POSSIBILITY OF SUCH DAMAGE. */
34 
35 #include <math.h>
36 #include <stdlib.h>
37 #include "dynmat.h"
38 #define PI 3.14159265358979323846
39 
40 static void get_dynmat_ij(double *dynamical_matrix,
41                           const long num_patom,
42                           const long num_satom,
43                           const double *fc,
44                           const double q[3],
45                           const double (*svecs)[3],
46                           const long (*multi)[2],
47                           const double *mass,
48                           const long *s2p_map,
49                           const long *p2s_map,
50                           const double (*charge_sum)[3][3],
51                           const long i,
52                           const long j);
53 static void get_dm(double dm_real[3][3],
54                    double dm_imag[3][3],
55                    const long num_patom,
56                    const long num_satom,
57                    const double *fc,
58                    const double q[3],
59                    const double (*svecs)[3],
60                    const long (*multi)[2],
61                    const long *p2s_map,
62                    const double (*charge_sum)[3][3],
63                    const long i,
64                    const long j,
65                    const long k);
66 static double get_dielectric_part(const double q_cart[3],
67                                   const double dielectric[3][3]);
68 static void get_KK(double *dd_part, /* [natom, 3, natom, 3, (real,imag)] */
69                    const double (*G_list)[3], /* [num_G, 3] */
70                    const long num_G,
71                    const long num_patom,
72                    const double q_cart[3],
73                    const double *q_direction_cart,
74                    const double dielectric[3][3],
75                    const double (*pos)[3], /* [num_patom, 3] */
76                    const double lambda,
77                    const double tolerance);
78 static void make_Hermitian(double *mat, const long num_band);
79 static void multiply_borns(double *dd,
80                            const double *dd_in,
81                            const long num_patom,
82                            const double (*born)[3][3]);
83 
dym_get_dynamical_matrix_at_q(double * dynamical_matrix,const long num_patom,const long num_satom,const double * fc,const double q[3],const double (* svecs)[3],const long (* multi)[2],const double * mass,const long * s2p_map,const long * p2s_map,const double (* charge_sum)[3][3],const long with_openmp)84 long dym_get_dynamical_matrix_at_q(double *dynamical_matrix,
85                                    const long num_patom,
86                                    const long num_satom,
87                                    const double *fc,
88                                    const double q[3],
89                                    const double (*svecs)[3],
90                                    const long (*multi)[2],
91                                    const double *mass,
92                                    const long *s2p_map,
93                                    const long *p2s_map,
94                                    const double (*charge_sum)[3][3],
95                                    const long with_openmp)
96 {
97   long i, j, ij;
98 
99   if (with_openmp) {
100 #ifdef PHPYOPENMP
101 #pragma omp parallel for
102 #endif
103     for (ij = 0; ij < num_patom * num_patom ; ij++) {
104       get_dynmat_ij(dynamical_matrix,
105                     num_patom,
106                     num_satom,
107                     fc,
108                     q,
109                     svecs,
110                     multi,
111                     mass,
112                     s2p_map,
113                     p2s_map,
114                     charge_sum,
115                     ij / num_patom,  /* i */
116                     ij % num_patom); /* j */
117     }
118   } else {
119     for (i = 0; i < num_patom; i++) {
120       for (j = 0; j < num_patom; j++) {
121         get_dynmat_ij(dynamical_matrix,
122                       num_patom,
123                       num_satom,
124                       fc,
125                       q,
126                       svecs,
127                       multi,
128                       mass,
129                       s2p_map,
130                       p2s_map,
131                       charge_sum,
132                       i,
133                       j);
134       }
135     }
136   }
137 
138   make_Hermitian(dynamical_matrix, num_patom * 3);
139 
140   return 0;
141 }
142 
dym_get_recip_dipole_dipole(double * dd,const double * dd_q0,const double (* G_list)[3],const long num_G,const long num_patom,const double q_cart[3],const double * q_direction_cart,const double (* born)[3][3],const double dielectric[3][3],const double (* pos)[3],const double factor,const double lambda,const double tolerance)143 void dym_get_recip_dipole_dipole(double *dd, /* [natom, 3, natom, 3, (real,imag)] */
144                                  const double *dd_q0, /* [natom, 3, 3, (real,imag)] */
145                                  const double (*G_list)[3], /* [num_G, 3] */
146                                  const long num_G,
147                                  const long num_patom,
148                                  const double q_cart[3],
149                                  const double *q_direction_cart, /* must be pointer */
150                                  const double (*born)[3][3],
151                                  const double dielectric[3][3],
152                                  const double (*pos)[3], /* [num_patom, 3] */
153                                  const double factor, /* 4pi/V*unit-conv */
154                                  const double lambda,
155                                  const double tolerance)
156 {
157   long i, k, l, adrs, adrs_sum;
158   double *dd_tmp;
159 
160   dd_tmp = NULL;
161   dd_tmp = (double*) malloc(sizeof(double) * num_patom * num_patom * 18);
162 
163   for (i = 0; i < num_patom * num_patom * 18; i++) {
164     dd[i] = 0;
165     dd_tmp[i] = 0;
166   }
167 
168   get_KK(dd_tmp,
169          G_list,
170          num_G,
171          num_patom,
172          q_cart,
173          q_direction_cart,
174          dielectric,
175          pos,
176          lambda,
177          tolerance);
178 
179   multiply_borns(dd, dd_tmp, num_patom, born);
180 
181   for (i = 0; i < num_patom; i++) {
182     for (k = 0; k < 3; k++) {   /* alpha */
183       for (l = 0; l < 3; l++) { /* beta */
184         adrs = i * num_patom * 9 + k * num_patom * 3 + i * 3 + l;
185         adrs_sum = i * 9 + k * 3 + l;
186         dd[adrs * 2] -= dd_q0[adrs_sum * 2];
187         dd[adrs * 2 + 1] -= dd_q0[adrs_sum * 2 + 1];
188       }
189     }
190   }
191 
192   for (i = 0; i < num_patom * num_patom * 18; i++) {
193     dd[i] *= factor;
194   }
195 
196   /* This may not be necessary. */
197   /* make_Hermitian(dd, num_patom * 3); */
198 
199   free(dd_tmp);
200   dd_tmp = NULL;
201 }
202 
dym_get_recip_dipole_dipole_q0(double * dd_q0,const double (* G_list)[3],const long num_G,const long num_patom,const double (* born)[3][3],const double dielectric[3][3],const double (* pos)[3],const double lambda,const double tolerance)203 void dym_get_recip_dipole_dipole_q0(double *dd_q0, /* [natom, 3, 3, (real,imag)] */
204                                     const double (*G_list)[3], /* [num_G, 3] */
205                                     const long num_G,
206                                     const long num_patom,
207                                     const double (*born)[3][3],
208                                     const double dielectric[3][3],
209                                     const double (*pos)[3], /* [num_patom, 3] */
210                                     const double lambda,
211                                     const double tolerance)
212 {
213   long i, j, k, l, adrs_tmp, adrs, adrsT;
214   double zero_vec[3];
215   double *dd_tmp1, *dd_tmp2;
216 
217   dd_tmp1 = NULL;
218   dd_tmp1 = (double*) malloc(sizeof(double) * num_patom * num_patom * 18);
219   dd_tmp2 = NULL;
220   dd_tmp2 = (double*) malloc(sizeof(double) * num_patom * num_patom * 18);
221 
222   for (i = 0; i < num_patom * num_patom * 18; i++) {
223     dd_tmp1[i] = 0;
224     dd_tmp2[i] = 0;
225   }
226 
227   zero_vec[0] = 0;
228   zero_vec[1] = 0;
229   zero_vec[2] = 0;
230 
231   get_KK(dd_tmp1,
232          G_list,
233          num_G,
234          num_patom,
235          zero_vec,
236          NULL,
237          dielectric,
238          pos,
239          lambda,
240          tolerance);
241 
242   multiply_borns(dd_tmp2, dd_tmp1, num_patom, born);
243 
244   for (i = 0; i < num_patom * 18; i++) {
245     dd_q0[i] = 0;
246   }
247 
248   for (i = 0; i < num_patom; i++) {
249     for (k = 0; k < 3; k++) {   /* alpha */
250       for (l = 0; l < 3; l++) { /* beta */
251         adrs = i * 9 + k * 3 + l;
252         for (j = 0; j < num_patom; j++) {
253           adrs_tmp = i * num_patom * 9 + k * num_patom * 3 + j * 3 + l ;
254           dd_q0[adrs * 2] += dd_tmp2[adrs_tmp * 2];
255           dd_q0[adrs * 2 + 1] += dd_tmp2[adrs_tmp * 2 + 1];
256         }
257       }
258     }
259   }
260 
261   /* Summation over another atomic index */
262   /* for (j = 0; j < num_patom; j++) { */
263   /*   for (k = 0; k < 3; k++) {   /\* alpha *\/ */
264   /*     for (l = 0; l < 3; l++) { /\* beta *\/ */
265   /*       adrs = j * 9 + k * 3 + l; */
266   /*       for (i = 0; i < num_patom; i++) { */
267   /*         adrs_tmp = i * num_patom * 9 + k * num_patom * 3 + j * 3 + l ; */
268   /*         dd_q0[adrs * 2] += dd_tmp2[adrs_tmp * 2]; */
269   /*         dd_q0[adrs * 2 + 1] += dd_tmp2[adrs_tmp * 2 + 1]; */
270   /*       } */
271   /*     } */
272   /*   } */
273   /* } */
274 
275   for (i = 0; i < num_patom; i++) {
276     for (k = 0; k < 3; k++) {   /* alpha */
277       for (l = 0; l < 3; l++) { /* beta */
278         adrs = i * 9 + k * 3 + l;
279         adrsT = i * 9 + l * 3 + k;
280         dd_q0[adrs * 2] += dd_q0[adrsT * 2];
281         dd_q0[adrs * 2] /= 2;
282         dd_q0[adrsT * 2] = dd_q0[adrs * 2];
283         dd_q0[adrs * 2 + 1] -= dd_q0[adrsT * 2 + 1];
284         dd_q0[adrs * 2 + 1] /= 2;
285         dd_q0[adrsT * 2 + 1] = -dd_q0[adrs * 2 + 1];
286       }
287     }
288   }
289 
290   free(dd_tmp1);
291   dd_tmp1 = NULL;
292   free(dd_tmp2);
293   dd_tmp2 = NULL;
294 }
295 
dym_get_charge_sum(double (* charge_sum)[3][3],const long num_patom,const double factor,const double q_cart[3],const double (* born)[3][3])296 void dym_get_charge_sum(double (*charge_sum)[3][3],
297                         const long num_patom,
298                         const double factor, /* 4pi/V*unit-conv and denominator */
299                         const double q_cart[3],
300                         const double (*born)[3][3])
301 {
302   long i, j, k, a, b;
303   double (*q_born)[3];
304 
305   q_born = (double (*)[3]) malloc(sizeof(double[3]) * num_patom);
306   for (i = 0; i < num_patom; i++) {
307     for (j = 0; j < 3; j++) {
308       q_born[i][j] = 0;
309     }
310   }
311 
312   for (i = 0; i < num_patom; i++) {
313     for (j = 0; j < 3; j++) {
314       for (k = 0; k < 3; k++) {
315         q_born[i][j] += q_cart[k] * born[i][k][j];
316       }
317     }
318   }
319 
320   for (i = 0; i < num_patom; i++) {
321     for (j = 0; j < num_patom; j++) {
322       for (a = 0; a < 3; a++) {
323         for (b = 0; b < 3; b++) {
324           charge_sum[i * num_patom + j][a][b] =
325             q_born[i][a] * q_born[j][b] * factor;
326         }
327       }
328     }
329   }
330 
331   free(q_born);
332   q_born = NULL;
333 }
334 
335 /* fc[num_patom, num_satom, 3, 3] */
336 /* dm[num_comm_points, num_patom * 3, num_patom *3] */
337 /* comm_points[num_satom / num_patom, 3] */
338 /* shortest_vectors[:, 3] */
339 /* multiplicities[num_satom, num_patom, 2] */
dym_transform_dynmat_to_fc(double * fc,const double * dm,const double (* comm_points)[3],const double (* svecs)[3],const long (* multi)[2],const double * masses,const long * s2pp_map,const long * fc_index_map,const long num_patom,const long num_satom)340 void dym_transform_dynmat_to_fc(double *fc,
341                                 const double *dm,
342                                 const double (*comm_points)[3],
343                                 const double (*svecs)[3],
344                                 const long (*multi)[2],
345                                 const double *masses,
346                                 const long *s2pp_map,
347                                 const long *fc_index_map,
348                                 const long num_patom,
349                                 const long num_satom)
350 {
351   long i, j, k, l, m, N, adrs, m_pair, i_pair, svecs_adrs;
352   double coef, phase, cos_phase, sin_phase;
353 
354   N = num_satom / num_patom;
355   for (i = 0; i < num_patom * num_satom * 9; i++) {
356     fc[i] = 0;
357   }
358 
359   for (i = 0; i < num_patom; i++) {
360     for (j = 0; j < num_satom; j++) {
361       i_pair = j * num_patom + i;
362       m_pair = multi[i_pair][0];
363       svecs_adrs = multi[i_pair][1];
364       coef = sqrt(masses[i] * masses[s2pp_map[j]]) / N;
365       for (k = 0; k < N; k++) {
366         cos_phase = 0;
367         sin_phase = 0;
368         for (l = 0; l < m_pair; l++) {
369           phase = 0;
370           for (m = 0; m < 3; m++) {
371             phase -= comm_points[k][m] * svecs[svecs_adrs + l][m];
372           }
373           cos_phase += cos(phase * 2 * PI);
374           sin_phase += sin(phase * 2 * PI);
375         }
376         cos_phase /=  m_pair;
377         sin_phase /=  m_pair;
378         for (l = 0; l < 3; l++) {
379           for (m = 0; m < 3; m++) {
380             adrs = k * num_patom * num_patom * 18 + i * num_patom * 18 +
381               l * num_patom * 6 + s2pp_map[j] * 6 + m * 2;
382             fc[fc_index_map[i] * num_satom * 9 + j * 9 + l * 3 + m] +=
383               (dm[adrs] * cos_phase - dm[adrs + 1] * sin_phase) * coef;
384           }
385         }
386       }
387     }
388   }
389 }
390 
391 
get_dynmat_ij(double * dynamical_matrix,const long num_patom,const long num_satom,const double * fc,const double q[3],const double (* svecs)[3],const long (* multi)[2],const double * mass,const long * s2p_map,const long * p2s_map,const double (* charge_sum)[3][3],const long i,const long j)392 static void get_dynmat_ij(double *dynamical_matrix,
393                           const long num_patom,
394                           const long num_satom,
395                           const double *fc,
396                           const double q[3],
397                           const double (*svecs)[3],
398                           const long (*multi)[2],
399                           const double *mass,
400                           const long *s2p_map,
401                           const long *p2s_map,
402                           const double (*charge_sum)[3][3],
403                           const long i,
404                           const long j)
405 {
406   long k, l, adrs;
407   double mass_sqrt;
408   double dm_real[3][3], dm_imag[3][3];
409 
410   mass_sqrt = sqrt(mass[i] * mass[j]);
411 
412   for (k = 0; k < 3; k++) {
413     for (l = 0; l < 3; l++) {
414       dm_real[k][l] = 0;
415       dm_imag[k][l] = 0;
416     }
417   }
418 
419   for (k = 0; k < num_satom; k++) { /* Lattice points of right index of fc */
420     if (s2p_map[k] != p2s_map[j]) {
421       continue;
422     }
423     get_dm(dm_real,
424            dm_imag,
425            num_patom,
426            num_satom,
427            fc,
428            q,
429            svecs,
430            multi,
431            p2s_map,
432            charge_sum,
433            i,
434            j,
435            k);
436   }
437 
438   for (k = 0; k < 3; k++) {
439     for (l = 0; l < 3; l++) {
440       adrs = (i * 3 + k) * num_patom * 3 + j * 3 + l;
441       dynamical_matrix[adrs * 2] = dm_real[k][l] / mass_sqrt;
442       dynamical_matrix[adrs * 2 + 1] = dm_imag[k][l] / mass_sqrt;
443     }
444   }
445 }
446 
get_dm(double dm_real[3][3],double dm_imag[3][3],const long num_patom,const long num_satom,const double * fc,const double q[3],const double (* svecs)[3],const long (* multi)[2],const long * p2s_map,const double (* charge_sum)[3][3],const long i,const long j,const long k)447 static void get_dm(double dm_real[3][3],
448                    double dm_imag[3][3],
449                    const long num_patom,
450                    const long num_satom,
451                    const double *fc,
452                    const double q[3],
453                    const double (*svecs)[3],
454                    const long (*multi)[2],
455                    const long *p2s_map,
456                    const double (*charge_sum)[3][3],
457                    const long i,
458                    const long j,
459                    const long k)
460 {
461   long l, m, i_pair, m_pair, adrs;
462   double phase, cos_phase, sin_phase, fc_elem;
463 
464   cos_phase = 0;
465   sin_phase = 0;
466 
467   i_pair = k * num_patom + i;
468   m_pair = multi[i_pair][0];
469   adrs = multi[i_pair][1];
470 
471   for (l = 0; l < m_pair; l++) {
472     phase = 0;
473     for (m = 0; m < 3; m++) {
474       phase += q[m] * svecs[adrs + l][m];
475     }
476     cos_phase += cos(phase * 2 * PI) / m_pair;
477     sin_phase += sin(phase * 2 * PI) / m_pair;
478   }
479 
480   for (l = 0; l < 3; l++) {
481     for (m = 0; m < 3; m++) {
482       if (charge_sum) {
483         fc_elem = (fc[p2s_map[i] * num_satom * 9 + k * 9 + l * 3 + m] +
484                    charge_sum[i * num_patom + j][l][m]);
485       } else {
486         fc_elem = fc[p2s_map[i] * num_satom * 9 + k * 9 + l * 3 + m];
487       }
488       dm_real[l][m] += fc_elem * cos_phase;
489       dm_imag[l][m] += fc_elem * sin_phase;
490     }
491   }
492 }
493 
get_dielectric_part(const double q_cart[3],const double dielectric[3][3])494 static double get_dielectric_part(const double q_cart[3],
495                                   const double dielectric[3][3])
496 {
497   long i, j;
498   double x[3];
499   double sum;
500 
501   for (i = 0; i < 3; i++) {
502     x[i] = 0;
503     for (j = 0; j < 3; j++) {
504       x[i] += dielectric[i][j] * q_cart[j];
505     }
506   }
507 
508   sum = 0;
509   for (i = 0; i < 3; i++) {
510     sum += q_cart[i] * x[i];
511   }
512 
513   return sum;
514 }
515 
get_KK(double * dd_part,const double (* G_list)[3],const long num_G,const long num_patom,const double q_cart[3],const double * q_direction_cart,const double dielectric[3][3],const double (* pos)[3],const double lambda,const double tolerance)516 static void get_KK(double *dd_part, /* [natom, 3, natom, 3, (real,imag)] */
517                    const double (*G_list)[3], /* [num_G, 3] */
518                    const long num_G,
519                    const long num_patom,
520                    const double q_cart[3],
521                    const double *q_direction_cart,
522                    const double dielectric[3][3],
523                    const double (*pos)[3], /* [num_patom, 3] */
524                    const double lambda,
525                    const double tolerance)
526 {
527   long i, j, k, l, g, adrs;
528   double q_K[3];
529   double norm, cos_phase, sin_phase, phase, dielectric_part, exp_damp, L2;
530   double KK[3][3];
531 
532   L2 = 4 * lambda * lambda;
533 
534   /* sum over K = G + q and over G (i.e. q=0) */
535   /* q_direction has values for summation over K at Gamma point. */
536   /* q_direction is NULL for summation over G */
537   for (g = 0; g < num_G; g++) {
538     norm = 0;
539     for (i = 0; i < 3; i++) {
540       q_K[i] = G_list[g][i] + q_cart[i];
541       norm += q_K[i] * q_K[i];
542     }
543 
544     if (sqrt(norm) < tolerance) {
545       if (!q_direction_cart) {
546         continue;
547       } else {
548         dielectric_part = get_dielectric_part(q_direction_cart, dielectric);
549         for (i = 0; i < 3; i++) {
550           for (j = 0; j < 3; j++) {
551             KK[i][j] =
552               q_direction_cart[i] * q_direction_cart[j] / dielectric_part;
553           }
554         }
555       }
556     } else {
557       dielectric_part = get_dielectric_part(q_K, dielectric);
558       exp_damp = exp(-dielectric_part / L2);
559       for (i = 0; i < 3; i++) {
560         for (j = 0; j < 3; j++) {
561           KK[i][j] = q_K[i] * q_K[j] / dielectric_part * exp_damp;
562         }
563       }
564     }
565 
566     for (i = 0; i < num_patom; i++) {
567       for (j = 0; j < num_patom; j++) {
568         phase = 0;
569         for (k = 0; k < 3; k++) {
570           /* For D-type dynamical matrix */
571           /* phase += (pos[i][k] - pos[j][k]) * q_K[k]; */
572           /* For C-type dynamical matrix */
573           phase += (pos[i][k] - pos[j][k]) * G_list[g][k];
574         }
575         phase *= 2 * PI;
576         cos_phase = cos(phase);
577         sin_phase = sin(phase);
578         for (k = 0; k < 3; k++) {
579           for (l = 0; l < 3; l++) {
580             adrs = i * num_patom * 9 + k * num_patom * 3 + j * 3 + l;
581             dd_part[adrs * 2] += KK[k][l] * cos_phase;
582             dd_part[adrs * 2 + 1] += KK[k][l] * sin_phase;
583           }
584         }
585       }
586     }
587   }
588 }
589 
make_Hermitian(double * mat,const long num_band)590 static void make_Hermitian(double *mat, const long num_band)
591 {
592   long i, j, adrs, adrsT;
593 
594   for (i = 0; i < num_band; i++) {
595     for (j = i; j < num_band; j++) {
596       adrs = i * num_band + j * 1;
597       adrs *= 2;
598       adrsT = j * num_band + i * 1;
599       adrsT *= 2;
600       /* real part */
601       mat[adrs] += mat[adrsT];
602       mat[adrs] /= 2;
603       /* imaginary part */
604       mat[adrs + 1] -= mat[adrsT+ 1];
605       mat[adrs + 1] /= 2;
606       /* store */
607       mat[adrsT] = mat[adrs];
608       mat[adrsT + 1] = -mat[adrs + 1];
609     }
610   }
611 }
612 
multiply_borns(double * dd,const double * dd_in,const long num_patom,const double (* born)[3][3])613 static void multiply_borns(double *dd,
614                            const double *dd_in,
615                            const long num_patom,
616                            const double (*born)[3][3])
617 {
618   long i, j, k, l, m, n, adrs, adrs_in;
619   double zz;
620 
621   for (i = 0; i < num_patom; i++) {
622     for (j = 0; j < num_patom; j++) {
623       for (k = 0; k < 3; k++) {   /* alpha */
624         for (l = 0; l < 3; l++) { /* beta */
625           adrs = i * num_patom * 9 + k * num_patom * 3 + j * 3 + l;
626           for (m = 0; m < 3; m++) { /* alpha' */
627             for (n = 0; n < 3; n++) { /* beta' */
628               adrs_in = i * num_patom * 9 + m * num_patom * 3 + j * 3 + n ;
629               zz = born[i][m][k] * born[j][n][l];
630               dd[adrs * 2] += dd_in[adrs_in * 2] * zz;
631               dd[adrs * 2 + 1] += dd_in[adrs_in * 2 + 1] * zz;
632             }
633           }
634         }
635       }
636     }
637   }
638 }
639