mirror of https://github.com/phonopy/phono3py.git
554 lines
14 KiB
C
554 lines
14 KiB
C
/* Copyright (C) 2015 Atsushi Togo */
|
|
/* All rights reserved. */
|
|
|
|
/* This file is part of niggli. */
|
|
|
|
/* 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 niggli 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 "niggli.h"
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define NIGGLI_MAX_NUM_LOOP 100
|
|
|
|
typedef struct {
|
|
double A;
|
|
double B;
|
|
double C;
|
|
double eta;
|
|
double xi;
|
|
double zeta;
|
|
double eps;
|
|
int l;
|
|
int m;
|
|
int n;
|
|
double *tmat;
|
|
double *lattice;
|
|
} NiggliParams;
|
|
|
|
static NiggliParams *initialize(const double *lattice_, const double eps_);
|
|
static void finalize(double *lattice_, NiggliParams *p);
|
|
static int reset(NiggliParams *p);
|
|
static int step1(NiggliParams *p);
|
|
static int step2(NiggliParams *p);
|
|
static int step3(NiggliParams *p);
|
|
static int step4(NiggliParams *p);
|
|
static int step5(NiggliParams *p);
|
|
static int step6(NiggliParams *p);
|
|
static int step7(NiggliParams *p);
|
|
static int step8(NiggliParams *p);
|
|
static int set_parameters(NiggliParams *p);
|
|
static void set_angle_types(NiggliParams *p);
|
|
static double *get_transpose(const double *M);
|
|
static double *get_metric(const double *M);
|
|
static double *multiply_matrices(const double *A, const double *B);
|
|
static int layer_swap_axis(NiggliParams *p, const int aperiodic_axis);
|
|
static int step2_for_layer(NiggliParams *p);
|
|
|
|
#ifdef NIGGLI_DEBUG
|
|
#define debug_print(...) printf(__VA_ARGS__)
|
|
static void debug_show(const int j, const NiggliParams *p);
|
|
static void debug_show(const int j, const NiggliParams *p) {
|
|
/* int i; */
|
|
|
|
if (j < 0) {
|
|
printf("Finish: ");
|
|
} else {
|
|
printf("Step %d: ", j);
|
|
}
|
|
printf("%f %f %f %f %f %f\n", p->A, p->B, p->C, p->xi, p->eta, p->zeta);
|
|
|
|
/* printf("%d %d %d\n", p->l, p->m, p->n); */
|
|
/* for (i = 0; i < 3; i++) { */
|
|
/* printf("%f %f %f\n", */
|
|
/* p->lattice[i * 3], p->lattice[i * 3 + 1], p->lattice[i * 3 + 2]);
|
|
*/
|
|
/* } */
|
|
}
|
|
#else
|
|
#define debug_print(...)
|
|
#define debug_show(...)
|
|
#endif
|
|
|
|
#ifdef NIGGLI_WARNING
|
|
#include <stdio.h>
|
|
#define warning_print(...) fprintf(stderr, __VA_ARGS__)
|
|
#else
|
|
#define warning_print(...)
|
|
#endif
|
|
|
|
/*--------------------------------------------*/
|
|
/* Version: niggli-[major].[minor].[micro] */
|
|
/*--------------------------------------------*/
|
|
int niggli_get_major_version(void) { return NIGGLI_MAJOR_VERSION; }
|
|
|
|
int niggli_get_minor_version(void) { return NIGGLI_MINOR_VERSION; }
|
|
|
|
int niggli_get_micro_version(void) { return NIGGLI_MICRO_VERSION; }
|
|
|
|
/* return 0 if failed */
|
|
int niggli_reduce(double *lattice_, const double eps_) {
|
|
return niggli_reduce_periodic(lattice_, eps_, -1);
|
|
}
|
|
|
|
int niggli_reduce_periodic(double *lattice_, const double eps_,
|
|
const int aperiodic_axis) {
|
|
int i, j, succeeded;
|
|
NiggliParams *p;
|
|
int (*steps[8])(NiggliParams * p) = {step1, step2, step3, step4,
|
|
step5, step6, step7, step8};
|
|
|
|
if (aperiodic_axis != -1) {
|
|
steps[1] = step2_for_layer;
|
|
}
|
|
|
|
p = NULL;
|
|
succeeded = 0;
|
|
|
|
if ((p = initialize(lattice_, eps_)) == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
/* Step 0 */
|
|
if (!(((aperiodic_axis == 0 || aperiodic_axis == 1) &&
|
|
layer_swap_axis(p, aperiodic_axis)) ||
|
|
((aperiodic_axis == -1 || aperiodic_axis == 2) &&
|
|
set_parameters(p)))) {
|
|
goto ret;
|
|
}
|
|
|
|
for (i = 0; i < NIGGLI_MAX_NUM_LOOP; i++) {
|
|
for (j = 0; j < 8; j++) {
|
|
if ((*steps[j])(p)) {
|
|
debug_show(j + 1, p);
|
|
if (!reset(p)) {
|
|
goto ret;
|
|
}
|
|
if (j == 1 || j == 4 || j == 5 || j == 6 || j == 7) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (j == 8) {
|
|
succeeded = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
debug_show(-1, p);
|
|
|
|
ret:
|
|
finalize(lattice_, p);
|
|
return succeeded;
|
|
}
|
|
|
|
static NiggliParams *initialize(const double *lattice_, const double eps_) {
|
|
NiggliParams *p;
|
|
|
|
p = NULL;
|
|
|
|
if ((p = (NiggliParams *)malloc(sizeof(NiggliParams))) == NULL) {
|
|
warning_print("niggli: Memory could not be allocated.");
|
|
return NULL;
|
|
}
|
|
|
|
p->A = 0;
|
|
p->B = 0;
|
|
p->C = 0;
|
|
p->eta = 0;
|
|
p->xi = 0;
|
|
p->zeta = 0;
|
|
p->eps = 0;
|
|
p->l = 0;
|
|
p->m = 0;
|
|
p->n = 0;
|
|
p->tmat = NULL;
|
|
p->lattice = NULL;
|
|
|
|
if ((p->tmat = (double *)malloc(sizeof(double) * 9)) == NULL) {
|
|
warning_print("niggli: Memory could not be allocated.");
|
|
free(p);
|
|
p = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
p->eps = eps_;
|
|
if ((p->lattice = (double *)malloc(sizeof(double) * 9)) == NULL) {
|
|
warning_print("niggli: Memory could not be allocated.");
|
|
free(p->tmat);
|
|
p->tmat = NULL;
|
|
free(p);
|
|
p = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(p->lattice, lattice_, sizeof(double) * 9);
|
|
|
|
return p;
|
|
}
|
|
|
|
/* move aperiodic axis to c. */
|
|
static int layer_swap_axis(NiggliParams *p, const int aperiodic_axis) {
|
|
if (aperiodic_axis == 0) {
|
|
p->tmat[0] = 0, p->tmat[1] = 0, p->tmat[2] = -1;
|
|
p->tmat[3] = 0, p->tmat[4] = -1, p->tmat[5] = 0;
|
|
p->tmat[6] = -1, p->tmat[7] = 0, p->tmat[8] = 0;
|
|
} else if (aperiodic_axis == 1) {
|
|
p->tmat[0] = -1, p->tmat[1] = 0, p->tmat[2] = 0;
|
|
p->tmat[3] = 0, p->tmat[4] = 0, p->tmat[5] = -1;
|
|
p->tmat[6] = 0, p->tmat[7] = -1, p->tmat[8] = 0;
|
|
}
|
|
|
|
return reset(p);
|
|
}
|
|
|
|
static void finalize(double *lattice_, NiggliParams *p) {
|
|
free(p->tmat);
|
|
p->tmat = NULL;
|
|
memcpy(lattice_, p->lattice, sizeof(double) * 9);
|
|
free(p->lattice);
|
|
p->lattice = NULL;
|
|
free(p);
|
|
p = NULL;
|
|
}
|
|
|
|
static int reset(NiggliParams *p) {
|
|
double *lat_tmp;
|
|
|
|
lat_tmp = NULL;
|
|
|
|
if ((lat_tmp = multiply_matrices(p->lattice, p->tmat)) == NULL) {
|
|
return 0;
|
|
}
|
|
memcpy(p->lattice, lat_tmp, sizeof(double) * 9);
|
|
free(lat_tmp);
|
|
lat_tmp = NULL;
|
|
|
|
return set_parameters(p);
|
|
}
|
|
|
|
static int set_parameters(NiggliParams *p) {
|
|
double *G;
|
|
|
|
G = NULL;
|
|
|
|
if ((G = get_metric(p->lattice)) == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
p->A = G[0];
|
|
p->B = G[4];
|
|
p->C = G[8];
|
|
p->xi = G[5] * 2;
|
|
p->eta = G[2] * 2;
|
|
p->zeta = G[1] * 2;
|
|
|
|
free(G);
|
|
G = NULL;
|
|
|
|
set_angle_types(p);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void set_angle_types(NiggliParams *p) {
|
|
p->l = 0;
|
|
p->m = 0;
|
|
p->n = 0;
|
|
if (p->xi < -p->eps) {
|
|
p->l = -1;
|
|
}
|
|
if (p->xi > p->eps) {
|
|
p->l = 1;
|
|
}
|
|
if (p->eta < -p->eps) {
|
|
p->m = -1;
|
|
}
|
|
if (p->eta > p->eps) {
|
|
p->m = 1;
|
|
}
|
|
if (p->zeta < -p->eps) {
|
|
p->n = -1;
|
|
}
|
|
if (p->zeta > p->eps) {
|
|
p->n = 1;
|
|
}
|
|
}
|
|
|
|
static int step1(NiggliParams *p) {
|
|
if (p->A > p->B + p->eps || (!(fabs(p->A - p->B) > p->eps) &&
|
|
fabs(p->xi) > fabs(p->eta) + p->eps)) {
|
|
p->tmat[0] = 0, p->tmat[1] = -1, p->tmat[2] = 0;
|
|
p->tmat[3] = -1, p->tmat[4] = 0, p->tmat[5] = 0;
|
|
p->tmat[6] = 0, p->tmat[7] = 0, p->tmat[8] = -1;
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int step2(NiggliParams *p) {
|
|
if (p->B > p->C + p->eps || (!(fabs(p->B - p->C) > p->eps) &&
|
|
fabs(p->eta) > fabs(p->zeta) + p->eps)) {
|
|
p->tmat[0] = -1, p->tmat[1] = 0, p->tmat[2] = 0;
|
|
p->tmat[3] = 0, p->tmat[4] = 0, p->tmat[5] = -1;
|
|
p->tmat[6] = 0, p->tmat[7] = -1, p->tmat[8] = 0;
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Aperiodic axis is fixed to C, the output may not be a standard Niggli cell */
|
|
/* Nothing else should be affected, so only a warning */
|
|
static int step2_for_layer(NiggliParams *p) {
|
|
if (p->B > p->C + p->eps || (!(fabs(p->B - p->C) > p->eps) &&
|
|
fabs(p->eta) > fabs(p->zeta) + p->eps)) {
|
|
warning_print(
|
|
"niggli: B > C or B = C and |eta| > |zeta|. Please elongate the "
|
|
"aperiodic axis.");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int step3(NiggliParams *p) {
|
|
int i, j, k;
|
|
if (p->l * p->m * p->n == 1) {
|
|
if (p->l == -1) {
|
|
i = -1;
|
|
} else {
|
|
i = 1;
|
|
}
|
|
if (p->m == -1) {
|
|
j = -1;
|
|
} else {
|
|
j = 1;
|
|
}
|
|
if (p->n == -1) {
|
|
k = -1;
|
|
} else {
|
|
k = 1;
|
|
}
|
|
p->tmat[0] = i, p->tmat[1] = 0, p->tmat[2] = 0;
|
|
p->tmat[3] = 0, p->tmat[4] = j, p->tmat[5] = 0;
|
|
p->tmat[6] = 0, p->tmat[7] = 0, p->tmat[8] = k;
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int step4(NiggliParams *p) {
|
|
int i, j, k, r;
|
|
|
|
if (p->l == -1 && p->m == -1 && p->n == -1) {
|
|
return 0;
|
|
}
|
|
|
|
if (p->l * p->m * p->n == 0 || p->l * p->m * p->n == -1) {
|
|
i = 1;
|
|
j = 1;
|
|
k = 1;
|
|
r = -1; /* 0: i, 1: j, 2: k */
|
|
if (p->l == 1) {
|
|
i = -1;
|
|
}
|
|
if (p->l == 0) {
|
|
r = 0;
|
|
}
|
|
if (p->m == 1) {
|
|
j = -1;
|
|
}
|
|
if (p->m == 0) {
|
|
r = 1;
|
|
}
|
|
if (p->n == 1) {
|
|
k = -1;
|
|
}
|
|
if (p->n == 0) {
|
|
r = 2;
|
|
}
|
|
|
|
if (i * j * k == -1) {
|
|
if (r == 0) {
|
|
i = -1;
|
|
}
|
|
if (r == 1) {
|
|
j = -1;
|
|
}
|
|
if (r == 2) {
|
|
k = -1;
|
|
}
|
|
}
|
|
|
|
p->tmat[0] = i, p->tmat[1] = 0, p->tmat[2] = 0;
|
|
p->tmat[3] = 0, p->tmat[4] = j, p->tmat[5] = 0;
|
|
p->tmat[6] = 0, p->tmat[7] = 0, p->tmat[8] = k;
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int step5(NiggliParams *p) {
|
|
if (fabs(p->xi) > p->B + p->eps ||
|
|
(!(fabs(p->B - p->xi) > p->eps) && 2 * p->eta < p->zeta - p->eps) ||
|
|
(!(fabs(p->B + p->xi) > p->eps) && p->zeta < -p->eps)) {
|
|
p->tmat[0] = 1, p->tmat[1] = 0, p->tmat[2] = 0;
|
|
p->tmat[3] = 0, p->tmat[4] = 1, p->tmat[5] = 0;
|
|
p->tmat[6] = 0, p->tmat[7] = 0, p->tmat[8] = 1;
|
|
if (p->xi > 0) {
|
|
p->tmat[5] = -1;
|
|
}
|
|
if (p->xi < 0) {
|
|
p->tmat[5] = 1;
|
|
}
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int step6(NiggliParams *p) {
|
|
if (fabs(p->eta) > p->A + p->eps ||
|
|
(!(fabs(p->A - p->eta) > p->eps) && 2 * p->xi < p->zeta - p->eps) ||
|
|
(!(fabs(p->A + p->eta) > p->eps) && p->zeta < -p->eps)) {
|
|
p->tmat[0] = 1, p->tmat[1] = 0, p->tmat[2] = 0;
|
|
p->tmat[3] = 0, p->tmat[4] = 1, p->tmat[5] = 0;
|
|
p->tmat[6] = 0, p->tmat[7] = 0, p->tmat[8] = 1;
|
|
if (p->eta > 0) {
|
|
p->tmat[2] = -1;
|
|
}
|
|
if (p->eta < 0) {
|
|
p->tmat[2] = 1;
|
|
}
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int step7(NiggliParams *p) {
|
|
if (fabs(p->zeta) > p->A + p->eps ||
|
|
(!(fabs(p->A - p->zeta) > p->eps) && 2 * p->xi < p->eta - p->eps) ||
|
|
(!(fabs(p->A + p->zeta) > p->eps) && p->eta < -p->eps)) {
|
|
p->tmat[0] = 1, p->tmat[1] = 0, p->tmat[2] = 0;
|
|
p->tmat[3] = 0, p->tmat[4] = 1, p->tmat[5] = 0;
|
|
p->tmat[6] = 0, p->tmat[7] = 0, p->tmat[8] = 1;
|
|
if (p->zeta > 0) {
|
|
p->tmat[1] = -1;
|
|
}
|
|
if (p->zeta < 0) {
|
|
p->tmat[1] = 1;
|
|
}
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int step8(NiggliParams *p) {
|
|
if (p->xi + p->eta + p->zeta + p->A + p->B < -p->eps ||
|
|
(!(fabs(p->xi + p->eta + p->zeta + p->A + p->B) > p->eps) &&
|
|
2 * (p->A + p->eta) + p->zeta > p->eps)) {
|
|
p->tmat[0] = 1, p->tmat[1] = 0, p->tmat[2] = 1;
|
|
p->tmat[3] = 0, p->tmat[4] = 1, p->tmat[5] = 1;
|
|
p->tmat[6] = 0, p->tmat[7] = 0, p->tmat[8] = 1;
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static double *get_transpose(const double *M) {
|
|
int i, j;
|
|
double *M_T;
|
|
|
|
M_T = NULL;
|
|
|
|
if ((M_T = (double *)malloc(sizeof(double) * 9)) == NULL) {
|
|
warning_print("niggli: Memory could not be allocated.");
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
for (j = 0; j < 3; j++) {
|
|
M_T[i * 3 + j] = M[j * 3 + i];
|
|
}
|
|
}
|
|
|
|
return M_T;
|
|
}
|
|
|
|
static double *get_metric(const double *M) {
|
|
double *G, *M_T;
|
|
|
|
G = NULL;
|
|
M_T = NULL;
|
|
|
|
if ((M_T = get_transpose(M)) == NULL) {
|
|
return NULL;
|
|
}
|
|
if ((G = multiply_matrices(M_T, M)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
free(M_T);
|
|
M_T = NULL;
|
|
|
|
return G;
|
|
}
|
|
|
|
static double *multiply_matrices(const double *L, const double *R) {
|
|
int i, j, k;
|
|
double *M;
|
|
|
|
M = NULL;
|
|
|
|
if ((M = (double *)malloc(sizeof(double) * 9)) == NULL) {
|
|
warning_print("niggli: Memory could not be allocated.");
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
for (j = 0; j < 3; j++) {
|
|
M[i * 3 + j] = 0;
|
|
for (k = 0; k < 3; k++) {
|
|
M[i * 3 + j] += L[i * 3 + k] * R[k * 3 + j];
|
|
}
|
|
}
|
|
}
|
|
|
|
return M;
|
|
}
|