mirror of https://github.com/phonopy/phono3py.git
414 lines
15 KiB
C
414 lines
15 KiB
C
/* Copyright (C) 2020 Atsushi Togo */
|
|
/* All rights reserved. */
|
|
|
|
/* This file is part of kspclib. */
|
|
|
|
/* 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 kspclib 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 "grgrid.h"
|
|
|
|
#include <assert.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
|
|
#include "lagrid.h"
|
|
#include "snf3x3.h"
|
|
|
|
#define IDENTITY_TOL 1e-5
|
|
|
|
static void reduce_grid_address(long address[3], const long D_diag[3]);
|
|
static long get_double_grid_index(const long address_double[3],
|
|
const long D_diag[3], const long PS[3]);
|
|
static long get_grid_index_from_address(const long address[3],
|
|
const long D_diag[3]);
|
|
static void get_all_grid_addresses(long grid_address[][3],
|
|
const long D_diag[3]);
|
|
static void get_grid_address_from_index(long address[3], const long grid_index,
|
|
const long D_diag[3]);
|
|
static void get_grid_address(long address[3], const long address_double[3],
|
|
const long PS[3]);
|
|
static void get_double_grid_address(long address_double[3],
|
|
const long address[3], const long PS[3]);
|
|
static long rotate_grid_index(const long grid_index, const long rotation[3][3],
|
|
const long D_diag[3], const long PS[3]);
|
|
static void get_ir_grid_map(long *ir_grid_map, const long (*rotations)[3][3],
|
|
const long num_rot, const long D_diag[3],
|
|
const long PS[3]);
|
|
|
|
long grg_get_snf3x3(long D_diag[3], long P[3][3], long Q[3][3],
|
|
const long A[3][3]) {
|
|
long i, j, succeeded;
|
|
long D[3][3];
|
|
|
|
succeeded = 0;
|
|
|
|
if (lagmat_get_determinant_l3(A) == 0) {
|
|
goto err;
|
|
}
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
for (j = 0; j < 3; j++) {
|
|
D[i][j] = A[i][j];
|
|
}
|
|
}
|
|
|
|
succeeded = snf3x3(D, P, Q);
|
|
for (i = 0; i < 3; i++) {
|
|
D_diag[i] = D[i][i];
|
|
}
|
|
|
|
err:
|
|
return succeeded;
|
|
}
|
|
|
|
/*----------------------------------------*/
|
|
/* Transform rotations by D(Q^-1)RQ(D^-1) */
|
|
/*----------------------------------------*/
|
|
/* transformed_rots : D(Q^-1)RQ(D^-1) */
|
|
/* rotations : [num_rot][3][3] */
|
|
/* Defined as q' = Rq where q is in the reciprocal primitive basis */
|
|
/* vectors. */
|
|
/* num_rot : Number of rotations */
|
|
long grg_transform_rotations(long (*transformed_rots)[3][3],
|
|
const long (*rotations)[3][3], const long num_rot,
|
|
const long D_diag[3], const long Q[3][3]) {
|
|
long i, j, k;
|
|
double r[3][3], Q_double[3][3];
|
|
|
|
/* Compute D(Q^-1)RQ(D^-1) by three steps */
|
|
/* It is assumed that |det(Q)|=1 and Q^-1 has relatively small round-off */
|
|
/* error, and we want to divide by D carefully. */
|
|
/* 1. Compute (Q^-1)RQ */
|
|
/* 2. Compute D(Q^-1)RQ */
|
|
/* 3. Compute D(Q^-1)RQ(D^-1) */
|
|
lagmat_cast_matrix_3l_to_3d(Q_double, Q);
|
|
for (i = 0; i < num_rot; i++) {
|
|
lagmat_get_similar_matrix_ld3(r, rotations[i], Q_double, 0);
|
|
for (j = 0; j < 3; j++) {
|
|
for (k = 0; k < 3; k++) {
|
|
r[j][k] *= D_diag[j];
|
|
r[j][k] /= D_diag[k];
|
|
}
|
|
}
|
|
lagmat_cast_matrix_3d_to_3l(transformed_rots[i], r);
|
|
if (!lagmat_check_identity_matrix_ld3(transformed_rots[i], r,
|
|
IDENTITY_TOL)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* -------------------------------*/
|
|
/* Get all address in single grid */
|
|
/* -------------------------------*/
|
|
/* address : Single grid address. */
|
|
/* D_diag : Diagnal elements of D. */
|
|
void grg_get_all_grid_addresses(long (*grid_address)[3], const long D_diag[3]) {
|
|
get_all_grid_addresses(grid_address, D_diag);
|
|
}
|
|
|
|
/* -------------------------------------------------------*/
|
|
/* Get address in double grid from address in single grid */
|
|
/* -------------------------------------------------------*/
|
|
/* This function doubles single-grid address and shifts it by PS. */
|
|
/* No modulo operation is applied to returned double-grid address. */
|
|
/* address_double : Double grid address. */
|
|
/* address : Single grid address. */
|
|
/* PS : Shifts transformed by P. s_i is 0 or 1. */
|
|
void grg_get_double_grid_address(long address_double[3], const long address[3],
|
|
const long PS[3]) {
|
|
get_double_grid_address(address_double, address, PS);
|
|
}
|
|
|
|
/* -------------------------------------------------------*/
|
|
/* Get address in single grid from address in double grid */
|
|
/* -------------------------------------------------------*/
|
|
/* This function shifts double-grid adress by PS and divides it by 2. */
|
|
/* No modulo operation is applied to returned single-grid address. */
|
|
/* address : Single grid address. */
|
|
/* address_double : Double grid address. */
|
|
/* PS : Shifts transformed by P. s_i is 0 or 1. */
|
|
void grg_get_grid_address(long address[3], const long address_double[3],
|
|
const long PS[3]) {
|
|
get_grid_address(address, address_double, PS);
|
|
}
|
|
|
|
/* -------------------------------------------------*/
|
|
/* Get grid point index from address in double grid */
|
|
/* -------------------------------------------------*/
|
|
/* address_double : Double grid address. */
|
|
/* D_diag : Diagnal elements of D. */
|
|
/* PS : Shifts transformed by P. s_i is 0 or 1. */
|
|
long grg_get_double_grid_index(const long address_double[3],
|
|
const long D_diag[3], const long PS[3]) {
|
|
return get_double_grid_index(address_double, D_diag, PS);
|
|
}
|
|
|
|
/* -------------------------------------------------*/
|
|
/* Get grid point index from address in single grid */
|
|
/* -------------------------------------------------*/
|
|
/* address : Single grid address. */
|
|
/* D_diag : Diagnal elements of D. */
|
|
long grg_get_grid_index(const long address[3], const long D_diag[3]) {
|
|
long red_adrs[3];
|
|
|
|
lagmat_copy_vector_l3(red_adrs, address);
|
|
reduce_grid_address(red_adrs, D_diag);
|
|
return get_grid_index_from_address(red_adrs, D_diag);
|
|
}
|
|
|
|
/* ---------------------------------------*/
|
|
/* Get grid address from grid point index */
|
|
/* ---------------------------------------*/
|
|
/* address : Single grid address. */
|
|
/* D_diag : Diagnal elements of D. */
|
|
void grg_get_grid_address_from_index(long address[3], const long grid_index,
|
|
const long D_diag[3]) {
|
|
get_grid_address_from_index(address, grid_index, D_diag);
|
|
}
|
|
|
|
/* ---------------------------*/
|
|
/* Rotate grid point by index */
|
|
/* ---------------------------*/
|
|
long grg_rotate_grid_index(const long grid_index, const long rotation[3][3],
|
|
const long D_diag[3], const long PS[3]) {
|
|
return rotate_grid_index(grid_index, rotation, D_diag, PS);
|
|
}
|
|
|
|
/* -----------------------------*/
|
|
/* Find irreducible grid points */
|
|
/* -----------------------------*/
|
|
void grg_get_ir_grid_map(long *ir_grid_map, const long (*rotations)[3][3],
|
|
const long num_rot, const long D_diag[3],
|
|
const long PS[3]) {
|
|
get_ir_grid_map(ir_grid_map, rotations, num_rot, D_diag, PS);
|
|
}
|
|
|
|
/* Unique reciprocal rotations are collected from input rotations. */
|
|
/* is_transpose == 0 : Input rotations are considered those for */
|
|
/* reciprocal space. */
|
|
/* is_transpose != 0 : Input rotations are considered those for */
|
|
/* direct space, i.e., the rotation matrices are transposed. */
|
|
/* is_time_reversal controls if inversion is added in the group of */
|
|
/* reciprocal space rotations. */
|
|
/* Return 0 if failed */
|
|
long grg_get_reciprocal_point_group(long rec_rotations[48][3][3],
|
|
const long (*rotations)[3][3],
|
|
const long num_rot,
|
|
const long is_time_reversal,
|
|
const long is_transpose)
|
|
|
|
{
|
|
long i, j, num_rot_ret, inv_exist;
|
|
const long inversion[3][3] = {{-1, 0, 0}, {0, -1, 0}, {0, 0, -1}};
|
|
|
|
/* Collect unique rotations */
|
|
num_rot_ret = 0;
|
|
for (i = 0; i < num_rot; i++) {
|
|
for (j = 0; j < num_rot_ret; j++) {
|
|
if (lagmat_check_identity_matrix_l3(rotations[i],
|
|
rec_rotations[j])) {
|
|
goto escape;
|
|
}
|
|
}
|
|
if (num_rot_ret == 48) {
|
|
goto err;
|
|
}
|
|
lagmat_copy_matrix_l3(rec_rotations[num_rot_ret], rotations[i]);
|
|
num_rot_ret++;
|
|
escape:;
|
|
}
|
|
|
|
if (is_transpose) {
|
|
for (i = 0; i < num_rot_ret; i++) {
|
|
lagmat_transpose_matrix_l3(rec_rotations[i], rec_rotations[i]);
|
|
}
|
|
}
|
|
|
|
if (is_time_reversal) {
|
|
inv_exist = 0;
|
|
for (i = 0; i < num_rot_ret; i++) {
|
|
if (lagmat_check_identity_matrix_l3(inversion, rec_rotations[i])) {
|
|
inv_exist = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!inv_exist) {
|
|
if (num_rot_ret > 24) {
|
|
goto err;
|
|
}
|
|
|
|
for (i = 0; i < num_rot_ret; i++) {
|
|
lagmat_multiply_matrix_l3(rec_rotations[num_rot_ret + i],
|
|
inversion, rec_rotations[i]);
|
|
}
|
|
num_rot_ret *= 2;
|
|
}
|
|
}
|
|
|
|
return num_rot_ret;
|
|
err:
|
|
return 0;
|
|
}
|
|
|
|
static void reduce_grid_address(long address[3], const long D_diag[3]) {
|
|
long i;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
address[i] = lagmat_modulo_l(address[i], D_diag[i]);
|
|
}
|
|
}
|
|
|
|
static long get_double_grid_index(const long address_double[3],
|
|
const long D_diag[3], const long PS[3]) {
|
|
long address[3];
|
|
|
|
get_grid_address(address, address_double, PS);
|
|
reduce_grid_address(address, D_diag);
|
|
return get_grid_index_from_address(address, D_diag);
|
|
}
|
|
|
|
/* Here address elements have to be zero or positive. */
|
|
/* Therefore reduction to interval [0, D_diag[i]) has to be */
|
|
/* done outside of this function. */
|
|
/* See kgrid.h about GRID_ORDER_XYZ information. */
|
|
static long get_grid_index_from_address(const long address[3],
|
|
const long D_diag[3]) {
|
|
#ifndef GRID_ORDER_XYZ
|
|
return (address[2] * D_diag[0] * D_diag[1] + address[1] * D_diag[0] +
|
|
address[0]);
|
|
#else
|
|
return (address[0] * D_diag[1] * D_diag[2] + address[1] * D_diag[2] +
|
|
address[2]);
|
|
#endif
|
|
}
|
|
|
|
static void get_all_grid_addresses(long grid_address[][3],
|
|
const long D_diag[3]) {
|
|
long i, j, k, grid_index;
|
|
long address[3];
|
|
|
|
for (i = 0; i < D_diag[0]; i++) {
|
|
address[0] = i;
|
|
for (j = 0; j < D_diag[1]; j++) {
|
|
address[1] = j;
|
|
for (k = 0; k < D_diag[2]; k++) {
|
|
address[2] = k;
|
|
grid_index = get_grid_index_from_address(address, D_diag);
|
|
lagmat_copy_vector_l3(grid_address[grid_index], address);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* See grg_get_grid_address_from_index */
|
|
static void get_grid_address_from_index(long address[3], const long grid_index,
|
|
const long D_diag[3]) {
|
|
long nn;
|
|
|
|
#ifndef GRID_ORDER_XYZ
|
|
nn = D_diag[0] * D_diag[1];
|
|
address[0] = grid_index % D_diag[0];
|
|
address[2] = grid_index / nn;
|
|
address[1] = (grid_index - address[2] * nn) / D_diag[0];
|
|
#else
|
|
nn = D_diag[1] * D_diag[2];
|
|
address[2] = grid_index % D_diag[2];
|
|
address[0] = grid_index / nn;
|
|
address[1] = (grid_index - address[0] * nn) / D_diag[2];
|
|
#endif
|
|
}
|
|
|
|
/* Usually address has to be reduced to [0, D_diag[i]) */
|
|
/* by calling reduce_grid_address after this operation. */
|
|
static void get_grid_address(long address[3], const long address_double[3],
|
|
const long PS[3]) {
|
|
long i;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
address[i] = (address_double[i] - PS[i]) / 2;
|
|
}
|
|
}
|
|
|
|
/* Usually address_double has to be reduced to [0, 2*D_diag[i]) */
|
|
/* by calling reduce_double_grid_address after this operation. */
|
|
static void get_double_grid_address(long address_double[3],
|
|
const long address[3], const long PS[3]) {
|
|
long i;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
address_double[i] = address[i] * 2 + PS[i];
|
|
}
|
|
}
|
|
|
|
static long rotate_grid_index(const long grid_index, const long rotation[3][3],
|
|
const long D_diag[3], const long PS[3]) {
|
|
long adrs[3], dadrs[3], dadrs_rot[3];
|
|
|
|
get_grid_address_from_index(adrs, grid_index, D_diag);
|
|
get_double_grid_address(dadrs, adrs, PS);
|
|
lagmat_multiply_matrix_vector_l3(dadrs_rot, rotation, dadrs);
|
|
return get_double_grid_index(dadrs_rot, D_diag, PS);
|
|
}
|
|
|
|
/* Find ir-grid points. */
|
|
/* This algorithm relies on the ir-grid index is always smallest */
|
|
/* number among symmetrically equivalent grid points. */
|
|
static void get_ir_grid_map(long *ir_grid_map, const long (*rotations)[3][3],
|
|
const long num_rot, const long D_diag[3],
|
|
const long PS[3]) {
|
|
long gp, num_gp, r_gp;
|
|
long i;
|
|
|
|
num_gp = D_diag[0] * D_diag[1] * D_diag[2];
|
|
|
|
for (gp = 0; gp < num_gp; gp++) {
|
|
ir_grid_map[gp] = num_gp;
|
|
}
|
|
|
|
/* Do not simply multithreaded this for-loop. */
|
|
/* This algorithm contains race condition in different gp's. */
|
|
for (gp = 0; gp < num_gp; gp++) {
|
|
for (i = 0; i < num_rot; i++) {
|
|
r_gp = rotate_grid_index(gp, rotations[i], D_diag, PS);
|
|
if (r_gp < gp) {
|
|
ir_grid_map[gp] = ir_grid_map[r_gp];
|
|
break;
|
|
}
|
|
}
|
|
if (ir_grid_map[gp] == num_gp) {
|
|
ir_grid_map[gp] = gp;
|
|
}
|
|
}
|
|
}
|