phono3py/c/imag_self_energy_with_g.c

469 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 "imag_self_energy_with_g.h"
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include "funcs.h"
#include "lagrid.h"
#include "phonoc_array.h"
#include "triplet.h"
static long set_g_pos_frequency_point(long (*g_pos)[4], const long num_band0,
const long num_band, const char *g_zero);
static void detailed_imag_self_energy_at_triplet(
double *detailed_imag_self_energy, double *imag_self_energy,
const long num_band0, const long num_band, const double *fc3_normal_squared,
const double *frequencies, const long triplet[3], const double *g1,
const double *g2_3, const char *g_zero, const double *temperatures,
const long num_temps, const double cutoff_frequency);
static double collect_detailed_imag_self_energy(
double *imag_self_energy, const long num_band,
const double *fc3_normal_squared, const double *n1, const double *n2,
const double *g1, const double *g2_3, const char *g_zero);
static double collect_detailed_imag_self_energy_0K(
double *imag_self_energy, const long num_band,
const double *fc3_normal_squared, const double *n1, const double *n2,
const double *g, const char *g_zero);
static void set_occupations(double *n1, double *n2, const long num_band,
const double temperature, const long triplet[3],
const double *frequencies,
const double cutoff_frequency);
void ise_get_imag_self_energy_with_g(
double *imag_self_energy, const Darray *fc3_normal_squared,
const double *frequencies, const long (*triplets)[3],
const long *triplet_weights, const double *g, const char *g_zero,
const double temperature, const double cutoff_frequency,
const long num_frequency_points, const long frequency_point_index) {
long i, j, num_triplets, num_band0, num_band, num_band_prod;
long num_g_pos, g_index_dims, g_index_shift;
long(*g_pos)[4];
double *ise;
long at_a_frequency_point;
g_pos = NULL;
ise = NULL;
num_triplets = fc3_normal_squared->dims[0];
num_band0 = fc3_normal_squared->dims[1];
num_band = fc3_normal_squared->dims[2];
num_band_prod = num_band0 * num_band * num_band;
ise = (double *)malloc(sizeof(double) * num_triplets * num_band0);
/**
* g has the shape of
* (num_triplets, num_band0 or num_frequency_points, num_band, num_band)
*
* g_index_dims: Stride for 1st dim
* g_index_shift: Fixed index shift for 2nd dim
*/
if (frequency_point_index < 0) {
/* frequency_points == frequencies at bands */
at_a_frequency_point = 0;
g_index_dims = num_band_prod;
g_index_shift = 0;
} else {
/* At an arbitrary frequency point. */
at_a_frequency_point = 1;
g_index_dims = num_frequency_points * num_band * num_band;
g_index_shift = frequency_point_index * num_band * num_band;
}
#ifdef _OPENMP
#pragma omp parallel for schedule(guided) private(num_g_pos, g_pos)
#endif
for (i = 0; i < num_triplets; i++) {
/**
* g_pos contains the indices of g that are known non-zeros in series.
*
* ise_set_g_pos works for frquency points as bands.
* set_g_pos_frequency_point works for frequency sampling mode.
*/
g_pos = (long(*)[4])malloc(sizeof(long[4]) * num_band_prod);
if (at_a_frequency_point) {
num_g_pos = set_g_pos_frequency_point(
g_pos, num_band0, num_band,
g_zero + i * g_index_dims + g_index_shift);
} else {
num_g_pos = ise_set_g_pos(g_pos, num_band0, num_band,
g_zero + i * g_index_dims);
}
ise_imag_self_energy_at_triplet(
ise + i * num_band0, num_band0, num_band,
fc3_normal_squared->data + i * num_band_prod, frequencies,
triplets[i], triplet_weights[i],
g + i * g_index_dims + g_index_shift,
g + (i + num_triplets) * g_index_dims + g_index_shift, g_pos,
num_g_pos, &temperature, 1, cutoff_frequency, 0,
at_a_frequency_point);
free(g_pos);
g_pos = NULL;
}
for (i = 0; i < num_band0; i++) {
imag_self_energy[i] = 0;
}
for (i = 0; i < num_triplets; i++) {
for (j = 0; j < num_band0; j++) {
imag_self_energy[j] += ise[i * num_band0 + j];
}
}
free(ise);
ise = NULL;
}
void ise_get_detailed_imag_self_energy_with_g(
double *detailed_imag_self_energy, double *imag_self_energy_N,
double *imag_self_energy_U, const Darray *fc3_normal_squared,
const double *frequencies, const long (*triplets)[3],
const long *triplet_weights, const long (*bz_grid_addresses)[3],
const double *g, const char *g_zero, const double temperature,
const double cutoff_frequency) {
double *ise;
long i, j, num_triplets, num_band0, num_band, num_band_prod;
long *is_N;
double ise_tmp, N, U;
ise = NULL;
is_N = NULL;
num_triplets = fc3_normal_squared->dims[0];
num_band0 = fc3_normal_squared->dims[1];
num_band = fc3_normal_squared->dims[2];
num_band_prod = num_band0 * num_band * num_band;
ise = (double *)malloc(sizeof(double) * num_triplets * num_band0);
/* detailed_imag_self_energy has the same shape as fc3_normal_squared. */
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (i = 0; i < num_triplets; i++) {
detailed_imag_self_energy_at_triplet(
detailed_imag_self_energy + i * num_band_prod, ise + i * num_band0,
num_band0, num_band, fc3_normal_squared->data + i * num_band_prod,
frequencies, triplets[i], g + i * num_band_prod,
g + (i + num_triplets) * num_band_prod, g_zero + i * num_band_prod,
&temperature, 1, cutoff_frequency);
}
is_N = (long *)malloc(sizeof(long) * num_triplets);
for (i = 0; i < num_triplets; i++) {
is_N[i] = tpl_is_N(triplets[i], bz_grid_addresses);
}
for (i = 0; i < num_band0; i++) {
N = 0;
U = 0;
/* #ifdef _OPENMP */
/* #pragma omp parallel for private(ise_tmp) reduction(+:N,U) */
/* #endif */
for (j = 0; j < num_triplets; j++) {
ise_tmp = ise[j * num_band0 + i] * triplet_weights[j];
if (is_N[j]) {
N += ise_tmp;
} else {
U += ise_tmp;
}
}
imag_self_energy_N[i] = N;
imag_self_energy_U[i] = U;
}
free(is_N);
is_N = NULL;
free(ise);
ise = NULL;
}
void ise_imag_self_energy_at_triplet(
double *imag_self_energy, const long num_band0, const long num_band,
const double *fc3_normal_squared, const double *frequencies,
const long triplet[3], const long triplet_weight, const double *g1,
const double *g2_3, const long (*g_pos)[4], const long num_g_pos,
const double *temperatures, const long num_temps,
const double cutoff_frequency, const long openmp_per_triplets,
const long at_a_frequency_point) {
long i, j;
double *n1, *n2, *ise_at_g_pos;
long g_pos_3;
n1 = (double *)malloc(sizeof(double) * num_temps * num_band);
n2 = (double *)malloc(sizeof(double) * num_temps * num_band);
ise_at_g_pos = (double *)malloc(sizeof(double) * num_g_pos * num_temps);
for (i = 0; i < num_temps; i++) {
set_occupations(n1 + i * num_band, n2 + i * num_band, num_band,
temperatures[i], triplet, frequencies,
cutoff_frequency);
}
#ifdef _OPENMP
#pragma omp parallel for private(j, g_pos_3) if (!openmp_per_triplets)
#endif
for (i = 0; i < num_g_pos; i++) {
if (at_a_frequency_point) {
/** At a frequency point:
* g_pos[i][3] is for Phi with the shape of (band0, band, band).
* g_pos_3 is for g at-freq-point with the shape of (bands, bands).
*/
g_pos_3 = g_pos[i][3] % (num_band * num_band);
} else {
g_pos_3 = g_pos[i][3];
}
for (j = 0; j < num_temps; j++) {
if (n1[j * num_band + g_pos[i][1]] < 0 ||
n2[j * num_band + g_pos[i][2]] < 0) {
ise_at_g_pos[i * num_temps + j] = 0;
} else {
if (temperatures[j] > 0) {
ise_at_g_pos[i * num_temps + j] =
((n1[j * num_band + g_pos[i][1]] +
n2[j * num_band + g_pos[i][2]] + 1) *
g1[g_pos_3] +
(n1[j * num_band + g_pos[i][1]] -
n2[j * num_band + g_pos[i][2]]) *
g2_3[g_pos_3]) *
fc3_normal_squared[g_pos[i][3]] * triplet_weight;
} else {
ise_at_g_pos[i * num_temps + j] =
g1[g_pos_3] * fc3_normal_squared[g_pos[i][3]] *
triplet_weight;
}
}
}
}
for (i = 0; i < num_temps * num_band0; i++) {
imag_self_energy[i] = 0;
}
for (i = 0; i < num_g_pos; i++) {
for (j = 0; j < num_temps; j++) {
imag_self_energy[j * num_band0 + g_pos[i][0]] +=
ise_at_g_pos[i * num_temps + j];
}
}
free(ise_at_g_pos);
ise_at_g_pos = NULL;
free(n1);
n1 = NULL;
free(n2);
n2 = NULL;
}
long ise_set_g_pos(long (*g_pos)[4], const long num_band0, const long num_band,
const char *g_zero) {
long num_g_pos, j, k, l, jkl;
num_g_pos = 0;
jkl = 0;
for (j = 0; j < num_band0; j++) {
for (k = 0; k < num_band; k++) {
for (l = 0; l < num_band; l++) {
if (!g_zero[jkl]) {
g_pos[num_g_pos][0] = j;
g_pos[num_g_pos][1] = k;
g_pos[num_g_pos][2] = l;
g_pos[num_g_pos][3] = jkl;
num_g_pos++;
}
jkl++;
}
}
}
return num_g_pos;
}
/**
* @brief
*
* @param g_pos
* @param num_band0
* @param num_band
* @param g_zero
* @return long
*/
static long set_g_pos_frequency_point(long (*g_pos)[4], const long num_band0,
const long num_band, const char *g_zero) {
long num_g_pos, j, k, l, kl, jkl;
num_g_pos = 0;
jkl = 0;
for (j = 0; j < num_band0; j++) {
kl = 0;
for (k = 0; k < num_band; k++) {
for (l = 0; l < num_band; l++) {
if (!g_zero[kl]) {
g_pos[num_g_pos][0] = j;
g_pos[num_g_pos][1] = k;
g_pos[num_g_pos][2] = l;
g_pos[num_g_pos][3] = jkl;
num_g_pos++;
}
jkl++;
kl++;
}
}
}
return num_g_pos;
}
static void detailed_imag_self_energy_at_triplet(
double *detailed_imag_self_energy, double *imag_self_energy,
const long num_band0, const long num_band, const double *fc3_normal_squared,
const double *frequencies, const long triplet[3], const double *g1,
const double *g2_3, const char *g_zero, const double *temperatures,
const long num_temps, const double cutoff_frequency) {
long i, j, adrs_shift;
double *n1, *n2;
n1 = NULL;
n2 = NULL;
n1 = (double *)malloc(sizeof(double) * num_band);
n2 = (double *)malloc(sizeof(double) * num_band);
for (i = 0; i < num_temps; i++) {
set_occupations(n1, n2, num_band, temperatures[i], triplet, frequencies,
cutoff_frequency);
for (j = 0; j < num_band0; j++) {
adrs_shift = j * num_band * num_band;
if (temperatures[i] > 0) {
imag_self_energy[i * num_band0 + j] =
collect_detailed_imag_self_energy(
detailed_imag_self_energy + adrs_shift, num_band,
fc3_normal_squared + adrs_shift, n1, n2,
g1 + adrs_shift, g2_3 + adrs_shift,
g_zero + adrs_shift);
} else {
imag_self_energy[i * num_band0 + j] =
collect_detailed_imag_self_energy_0K(
detailed_imag_self_energy + adrs_shift, num_band,
fc3_normal_squared + adrs_shift, n1, n2,
g1 + adrs_shift, g_zero + adrs_shift);
}
}
}
free(n1);
n1 = NULL;
free(n2);
n2 = NULL;
}
static double collect_detailed_imag_self_energy(
double *imag_self_energy, const long num_band,
const double *fc3_normal_squared, const double *n1, const double *n2,
const double *g1, const double *g2_3, const char *g_zero) {
long ij, i, j;
double sum_g;
sum_g = 0;
for (ij = 0; ij < num_band * num_band; ij++) {
imag_self_energy[ij] = 0;
if (g_zero[ij]) {
continue;
}
i = ij / num_band;
j = ij % num_band;
if (n1[i] < 0 || n2[j] < 0) {
continue;
}
imag_self_energy[ij] =
(((n1[i] + n2[j] + 1) * g1[ij] + (n1[i] - n2[j]) * g2_3[ij]) *
fc3_normal_squared[ij]);
sum_g += imag_self_energy[ij];
}
return sum_g;
}
static double collect_detailed_imag_self_energy_0K(
double *imag_self_energy, const long num_band,
const double *fc3_normal_squared, const double *n1, const double *n2,
const double *g1, const char *g_zero) {
long ij, i, j;
double sum_g;
sum_g = 0;
for (ij = 0; ij < num_band * num_band; ij++) {
imag_self_energy[ij] = 0;
if (g_zero[ij]) {
continue;
}
i = ij / num_band;
j = ij % num_band;
if (n1[i] < 0 || n2[j] < 0) {
continue;
}
imag_self_energy[ij] = g1[ij] * fc3_normal_squared[ij];
sum_g += imag_self_energy[ij];
}
return sum_g;
}
static void set_occupations(double *n1, double *n2, const long num_band,
const double temperature, const long triplet[3],
const double *frequencies,
const double cutoff_frequency) {
long j;
double f1, f2;
for (j = 0; j < num_band; j++) {
f1 = frequencies[triplet[1] * num_band + j];
f2 = frequencies[triplet[2] * num_band + j];
if (f1 > cutoff_frequency) {
n1[j] = funcs_bose_einstein(f1, temperature);
} else {
n1[j] = -1;
}
if (f2 > cutoff_frequency) {
n2[j] = funcs_bose_einstein(f2, temperature);
} else {
n2[j] = -1;
}
}
}