mirror of https://github.com/phonopy/phono3py.git
397 lines
16 KiB
C
397 lines
16 KiB
C
/* Copyright (C) 2015 Atsushi Togo */
|
|
/* All rights reserved. */
|
|
|
|
/* This file is part of phonopy. */
|
|
|
|
/* Redistribution and use in source and binary forms, with or without */
|
|
/* modification, are permitted provided that the following conditions */
|
|
/* are met: */
|
|
|
|
/* * Redistributions of source code must retain the above copyright */
|
|
/* notice, this list of conditions and the following disclaimer. */
|
|
|
|
/* * Redistributions in binary form must reproduce the above copyright */
|
|
/* notice, this list of conditions and the following disclaimer in */
|
|
/* the documentation and/or other materials provided with the */
|
|
/* distribution. */
|
|
|
|
/* * Neither the name of the phonopy project nor the names of its */
|
|
/* contributors may be used to endorse or promote products derived */
|
|
/* from this software without specific prior written permission. */
|
|
|
|
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
|
|
/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
|
|
/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
|
|
/* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE */
|
|
/* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, */
|
|
/* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, */
|
|
/* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; */
|
|
/* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER */
|
|
/* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT */
|
|
/* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN */
|
|
/* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE */
|
|
/* POSSIBILITY OF SUCH DAMAGE. */
|
|
|
|
#include "real_to_reciprocal.h"
|
|
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "lapack_wrapper.h"
|
|
#include "phonoc_array.h"
|
|
#include "phonoc_const.h"
|
|
|
|
static void real_to_reciprocal_legacy(
|
|
lapack_complex_double *fc3_reciprocal,
|
|
const lapack_complex_double *pre_phase_factors,
|
|
const lapack_complex_double *phase_factor1,
|
|
const lapack_complex_double *phase_factor2, const double *fc3,
|
|
const long is_compact_fc3, const AtomTriplets *atom_triplets,
|
|
const long openmp_per_triplets);
|
|
static void real_to_reciprocal_r0_average(
|
|
lapack_complex_double *fc3_reciprocal,
|
|
const lapack_complex_double *pre_phase_factors,
|
|
const lapack_complex_double *phase_factor0,
|
|
const lapack_complex_double *phase_factor1,
|
|
const lapack_complex_double *phase_factor2, const double *fc3,
|
|
const long is_compact_fc3, const AtomTriplets *atom_triplets,
|
|
const long openmp_per_triplets);
|
|
static void real_to_reciprocal_elements(
|
|
lapack_complex_double *fc3_rec_elem,
|
|
const lapack_complex_double *phase_factor1,
|
|
const lapack_complex_double *phase_factor2, const double *fc3,
|
|
const long is_compact_fc3, const AtomTriplets *atom_triplets,
|
|
const long pi0, const long pi1, const long pi2, const long leg_index);
|
|
static lapack_complex_double get_phase_factor(const double q[3],
|
|
const double (*svecs)[3],
|
|
const long multi[2]);
|
|
static lapack_complex_double get_pre_phase_factor(
|
|
const long i_patom, const double q_vecs[3][3],
|
|
const AtomTriplets *atom_triplets);
|
|
static lapack_complex_double sum_lapack_complex_double(lapack_complex_double a,
|
|
lapack_complex_double b);
|
|
|
|
/* fc3_reciprocal[num_patom, num_patom, num_patom, 3, 3, 3] */
|
|
void r2r_real_to_reciprocal(lapack_complex_double *fc3_reciprocal,
|
|
const double q_vecs[3][3], const double *fc3,
|
|
const long is_compact_fc3,
|
|
const AtomTriplets *atom_triplets,
|
|
const long openmp_per_triplets) {
|
|
long i, j, num_band, num_patom, num_satom, adrs_vec;
|
|
lapack_complex_double *pre_phase_factors, *phase_factors, *phase_factor0,
|
|
*phase_factor1, *phase_factor2;
|
|
|
|
num_patom = atom_triplets->multi_dims[1];
|
|
num_satom = atom_triplets->multi_dims[0];
|
|
|
|
pre_phase_factors = (lapack_complex_double *)malloc(
|
|
sizeof(lapack_complex_double) * num_patom);
|
|
for (i = 0; i < num_patom; i++) {
|
|
pre_phase_factors[i] = get_pre_phase_factor(i, q_vecs, atom_triplets);
|
|
}
|
|
|
|
phase_factors = (lapack_complex_double *)malloc(
|
|
sizeof(lapack_complex_double) * 3 * num_patom * num_satom);
|
|
phase_factor0 = phase_factors;
|
|
phase_factor1 = phase_factors + num_patom * num_satom;
|
|
phase_factor2 = phase_factors + 2 * num_patom * num_satom;
|
|
for (i = 0; i < num_patom; i++) {
|
|
for (j = 0; j < num_satom; j++) {
|
|
adrs_vec = j * atom_triplets->multi_dims[1] + i;
|
|
phase_factor0[i * num_satom + j] =
|
|
get_phase_factor(q_vecs[0], atom_triplets->svecs,
|
|
atom_triplets->multiplicity[adrs_vec]);
|
|
phase_factor1[i * num_satom + j] =
|
|
get_phase_factor(q_vecs[1], atom_triplets->svecs,
|
|
atom_triplets->multiplicity[adrs_vec]);
|
|
phase_factor2[i * num_satom + j] =
|
|
get_phase_factor(q_vecs[2], atom_triplets->svecs,
|
|
atom_triplets->multiplicity[adrs_vec]);
|
|
}
|
|
}
|
|
|
|
if (atom_triplets->make_r0_average) {
|
|
real_to_reciprocal_r0_average(fc3_reciprocal, pre_phase_factors,
|
|
phase_factor0, phase_factor1,
|
|
phase_factor2, fc3, is_compact_fc3,
|
|
atom_triplets, openmp_per_triplets);
|
|
num_band = atom_triplets->multi_dims[1] * 3;
|
|
for (i = 0; i < num_band * num_band * num_band; i++) {
|
|
fc3_reciprocal[i] = lapack_make_complex_double(
|
|
lapack_complex_double_real(fc3_reciprocal[i]) / 3,
|
|
lapack_complex_double_imag(fc3_reciprocal[i]) / 3);
|
|
}
|
|
} else {
|
|
real_to_reciprocal_legacy(
|
|
fc3_reciprocal, pre_phase_factors, phase_factor1, phase_factor2,
|
|
fc3, is_compact_fc3, atom_triplets, openmp_per_triplets);
|
|
}
|
|
|
|
free(pre_phase_factors);
|
|
pre_phase_factors = NULL;
|
|
free(phase_factors);
|
|
phase_factors = NULL;
|
|
phase_factor0 = NULL;
|
|
phase_factor1 = NULL;
|
|
phase_factor2 = NULL;
|
|
}
|
|
|
|
static void real_to_reciprocal_legacy(
|
|
lapack_complex_double *fc3_reciprocal,
|
|
const lapack_complex_double *pre_phase_factors,
|
|
const lapack_complex_double *phase_factor1,
|
|
const lapack_complex_double *phase_factor2, const double *fc3,
|
|
const long is_compact_fc3, const AtomTriplets *atom_triplets,
|
|
const long openmp_per_triplets) {
|
|
long i, j, k, l, m, n, ijk;
|
|
long num_patom, num_satom, num_band;
|
|
lapack_complex_double fc3_rec_elem[27];
|
|
|
|
num_patom = atom_triplets->multi_dims[1];
|
|
num_satom = atom_triplets->multi_dims[0];
|
|
num_band = num_patom * 3;
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel for private(i, j, k, l, m, n, \
|
|
fc3_rec_elem) if (!openmp_per_triplets)
|
|
#endif
|
|
for (ijk = 0; ijk < num_patom * num_patom * num_patom; ijk++) {
|
|
i = ijk / (num_patom * num_patom);
|
|
j = (ijk - (i * num_patom * num_patom)) / num_patom;
|
|
k = ijk % num_patom;
|
|
|
|
real_to_reciprocal_elements(fc3_rec_elem, phase_factor1 + i * num_satom,
|
|
phase_factor2 + i * num_satom, fc3,
|
|
is_compact_fc3, atom_triplets, i, j, k, 0);
|
|
for (l = 0; l < 3; l++) {
|
|
for (m = 0; m < 3; m++) {
|
|
for (n = 0; n < 3; n++) {
|
|
fc3_reciprocal[(i * 3 + l) * num_band * num_band +
|
|
(j * 3 + m) * num_band + k * 3 + n] =
|
|
phonoc_complex_prod(fc3_rec_elem[l * 9 + m * 3 + n],
|
|
pre_phase_factors[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Summations are performed with respect to three different lattice reference
|
|
// point for the index of real space fc3 when make_r0_average=True. For cubic
|
|
// case, these three are roughly equivalent but small difference comes from the
|
|
// q-points in triplets used for summation implemented in
|
|
// real_to_reciprocal_elements().
|
|
// --sym-fc3q makes them almost equivalent.
|
|
static void real_to_reciprocal_r0_average(
|
|
lapack_complex_double *fc3_reciprocal,
|
|
const lapack_complex_double *pre_phase_factors,
|
|
const lapack_complex_double *phase_factor0,
|
|
const lapack_complex_double *phase_factor1,
|
|
const lapack_complex_double *phase_factor2, const double *fc3,
|
|
const long is_compact_fc3, const AtomTriplets *atom_triplets,
|
|
const long openmp_per_triplets) {
|
|
long i, j, k, l, m, n, ijk;
|
|
long num_patom, num_satom, num_band;
|
|
lapack_complex_double fc3_rec_elem[27], fc3_rec;
|
|
|
|
num_patom = atom_triplets->multi_dims[1];
|
|
num_satom = atom_triplets->multi_dims[0];
|
|
num_band = num_patom * 3;
|
|
|
|
#ifdef _OPENMP
|
|
#pragma omp parallel for private(i, j, k, l, m, n, fc3_rec_elem, \
|
|
fc3_rec) if (!openmp_per_triplets)
|
|
#endif
|
|
for (ijk = 0; ijk < num_patom * num_patom * num_patom; ijk++) {
|
|
i = ijk / (num_patom * num_patom);
|
|
j = (ijk - (i * num_patom * num_patom)) / num_patom;
|
|
k = ijk % num_patom;
|
|
|
|
real_to_reciprocal_elements(fc3_rec_elem, phase_factor1 + i * num_satom,
|
|
phase_factor2 + i * num_satom, fc3,
|
|
is_compact_fc3, atom_triplets, i, j, k, 1);
|
|
for (l = 0; l < 3; l++) {
|
|
for (m = 0; m < 3; m++) {
|
|
for (n = 0; n < 3; n++) {
|
|
fc3_rec = phonoc_complex_prod(
|
|
fc3_rec_elem[l * 9 + m * 3 + n], pre_phase_factors[i]);
|
|
fc3_reciprocal[(i * 3 + l) * num_band * num_band +
|
|
(j * 3 + m) * num_band + k * 3 + n] =
|
|
sum_lapack_complex_double(
|
|
fc3_reciprocal[(i * 3 + l) * num_band * num_band +
|
|
(j * 3 + m) * num_band + k * 3 + n],
|
|
fc3_rec);
|
|
}
|
|
}
|
|
}
|
|
|
|
// fc3_rec is stored in a way swapping jm <-> il.
|
|
real_to_reciprocal_elements(fc3_rec_elem, phase_factor0 + j * num_satom,
|
|
phase_factor2 + j * num_satom, fc3,
|
|
is_compact_fc3, atom_triplets, j, i, k, 2);
|
|
for (l = 0; l < 3; l++) {
|
|
for (m = 0; m < 3; m++) {
|
|
for (n = 0; n < 3; n++) {
|
|
fc3_rec = phonoc_complex_prod(
|
|
fc3_rec_elem[m * 9 + l * 3 + n], pre_phase_factors[j]);
|
|
fc3_reciprocal[(i * 3 + l) * num_band * num_band +
|
|
(j * 3 + m) * num_band + k * 3 + n] =
|
|
sum_lapack_complex_double(
|
|
fc3_reciprocal[(i * 3 + l) * num_band * num_band +
|
|
(j * 3 + m) * num_band + k * 3 + n],
|
|
fc3_rec);
|
|
}
|
|
}
|
|
}
|
|
|
|
// fc3_rec is stored in a way swapping kn <-> il.
|
|
real_to_reciprocal_elements(fc3_rec_elem, phase_factor1 + k * num_satom,
|
|
phase_factor0 + k * num_satom, fc3,
|
|
is_compact_fc3, atom_triplets, k, j, i, 3);
|
|
for (l = 0; l < 3; l++) {
|
|
for (m = 0; m < 3; m++) {
|
|
for (n = 0; n < 3; n++) {
|
|
fc3_rec = phonoc_complex_prod(
|
|
fc3_rec_elem[n * 9 + m * 3 + l], pre_phase_factors[k]);
|
|
fc3_reciprocal[(i * 3 + l) * num_band * num_band +
|
|
(j * 3 + m) * num_band + k * 3 + n] =
|
|
sum_lapack_complex_double(
|
|
fc3_reciprocal[(i * 3 + l) * num_band * num_band +
|
|
(j * 3 + m) * num_band + k * 3 + n],
|
|
fc3_rec);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void real_to_reciprocal_elements(
|
|
lapack_complex_double *fc3_rec_elem,
|
|
const lapack_complex_double *phase_factor1,
|
|
const lapack_complex_double *phase_factor2, const double *fc3,
|
|
const long is_compact_fc3, const AtomTriplets *atom_triplets,
|
|
const long pi0, const long pi1, const long pi2, const long leg_index) {
|
|
long i, j, k, l;
|
|
long num_satom, adrs_shift;
|
|
lapack_complex_double phase_factor;
|
|
double fc3_rec_real[27], fc3_rec_imag[27];
|
|
|
|
num_satom = atom_triplets->multi_dims[0];
|
|
|
|
for (i = 0; i < 27; i++) {
|
|
fc3_rec_real[i] = 0;
|
|
fc3_rec_imag[i] = 0;
|
|
}
|
|
|
|
if (is_compact_fc3) {
|
|
i = pi0;
|
|
} else {
|
|
i = atom_triplets->p2s_map[pi0];
|
|
}
|
|
|
|
for (j = 0; j < num_satom; j++) {
|
|
if (atom_triplets->s2p_map[j] != atom_triplets->p2s_map[pi1]) {
|
|
continue;
|
|
}
|
|
|
|
for (k = 0; k < num_satom; k++) {
|
|
if (atom_triplets->s2p_map[k] != atom_triplets->p2s_map[pi2]) {
|
|
continue;
|
|
}
|
|
if (leg_index > 1) {
|
|
if (atom_triplets->all_shortest[pi0 * num_satom * num_satom +
|
|
j * num_satom + k]) {
|
|
continue;
|
|
}
|
|
}
|
|
adrs_shift =
|
|
i * 27 * num_satom * num_satom + j * 27 * num_satom + k * 27;
|
|
phase_factor =
|
|
phonoc_complex_prod(phase_factor1[j], phase_factor2[k]);
|
|
|
|
if ((leg_index == 1) &&
|
|
(atom_triplets->all_shortest[pi0 * num_satom * num_satom +
|
|
j * num_satom + k])) {
|
|
for (l = 0; l < 27; l++) {
|
|
fc3_rec_real[l] +=
|
|
lapack_complex_double_real(phase_factor) *
|
|
fc3[adrs_shift + l] * 3;
|
|
fc3_rec_imag[l] +=
|
|
lapack_complex_double_imag(phase_factor) *
|
|
fc3[adrs_shift + l] * 3;
|
|
}
|
|
} else {
|
|
for (l = 0; l < 27; l++) {
|
|
fc3_rec_real[l] +=
|
|
lapack_complex_double_real(phase_factor) *
|
|
fc3[adrs_shift + l];
|
|
fc3_rec_imag[l] +=
|
|
lapack_complex_double_imag(phase_factor) *
|
|
fc3[adrs_shift + l];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < 27; i++) {
|
|
fc3_rec_elem[i] =
|
|
lapack_make_complex_double(fc3_rec_real[i], fc3_rec_imag[i]);
|
|
}
|
|
}
|
|
|
|
// This function doesn't need to think about position +
|
|
// lattice-translation because q+q'+q''=G.
|
|
static lapack_complex_double get_pre_phase_factor(
|
|
const long i_patom, const double q_vecs[3][3],
|
|
const AtomTriplets *atom_triplets) {
|
|
long j, svecs_adrs;
|
|
double pre_phase;
|
|
lapack_complex_double pre_phase_factor;
|
|
|
|
svecs_adrs = atom_triplets->p2s_map[i_patom] * atom_triplets->multi_dims[1];
|
|
pre_phase = 0;
|
|
for (j = 0; j < 3; j++) {
|
|
pre_phase +=
|
|
atom_triplets
|
|
->svecs[atom_triplets->multiplicity[svecs_adrs][1]][j] *
|
|
(q_vecs[0][j] + q_vecs[1][j] + q_vecs[2][j]);
|
|
}
|
|
pre_phase *= M_2PI;
|
|
pre_phase_factor =
|
|
lapack_make_complex_double(cos(pre_phase), sin(pre_phase));
|
|
return pre_phase_factor;
|
|
}
|
|
|
|
static lapack_complex_double get_phase_factor(const double q[3],
|
|
const double (*svecs)[3],
|
|
const long multi[2]) {
|
|
long i, j;
|
|
double sum_real, sum_imag, phase;
|
|
|
|
sum_real = 0;
|
|
sum_imag = 0;
|
|
for (i = 0; i < multi[0]; i++) {
|
|
phase = 0;
|
|
for (j = 0; j < 3; j++) {
|
|
phase += q[j] * svecs[multi[1] + i][j];
|
|
}
|
|
phase *= M_2PI;
|
|
sum_real += cos(phase);
|
|
sum_imag += sin(phase);
|
|
}
|
|
sum_real /= multi[0];
|
|
sum_imag /= multi[0];
|
|
|
|
return lapack_make_complex_double(sum_real, sum_imag);
|
|
}
|
|
|
|
static lapack_complex_double sum_lapack_complex_double(
|
|
lapack_complex_double a, lapack_complex_double b) {
|
|
double v_real, v_imag;
|
|
v_real = lapack_complex_double_real(a) + lapack_complex_double_real(b);
|
|
v_imag = lapack_complex_double_imag(a) + lapack_complex_double_imag(b);
|
|
return lapack_make_complex_double(v_real, v_imag);
|
|
}
|