phonopy/c/spglib/site_symmetry.c

385 lines
13 KiB
C
Raw Normal View History

2012-12-11 16:15:20 +08:00
/* Copyright (C) 2011 Atsushi Togo */
2015-06-25 21:04:21 +08:00
/* All rights reserved. */
/* This file is part of spglib. */
/* 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. */
2012-12-11 16:15:20 +08:00
#include <stdio.h>
#include <stdlib.h>
#include "cell.h"
#include "mathfunc.h"
#include "symmetry.h"
2016-11-25 16:13:33 +08:00
#include "site_symmetry.h"
2012-12-11 16:15:20 +08:00
#include "sitesym_database.h"
#include "debug.h"
2016-10-11 13:33:45 +08:00
#define INCREASE_RATE 1.05
2018-11-13 16:29:58 +08:00
#define NUM_ATTEMPT 5
2016-10-11 13:33:45 +08:00
2018-11-13 16:29:58 +08:00
static VecDBL * get_exact_positions(int * equiv_atoms,
const Cell * conv_prim,
const Symmetry * conv_sym,
const double symprec);
static void set_exact_location(double position[3],
const Symmetry * conv_sym,
2018-11-13 16:29:58 +08:00
SPGCONST double bravais_lattice[3][3],
const double symprec);
2016-10-11 13:33:45 +08:00
static int set_equivalent_atom(VecDBL *positions,
int * equiv_atoms,
const int i,
const int num_indep_atoms,
const int *indep_atoms,
const Cell * conv_prim,
const Symmetry * conv_sym,
const double symprec);
2016-10-11 13:33:45 +08:00
static int set_Wyckoffs_labels(int * wyckoffs,
const VecDBL *positions,
const int * equiv_atoms,
const Cell * conv_prim,
const Symmetry * conv_sym,
const int hall_number,
const double symprec);
2018-11-13 16:29:58 +08:00
static int get_Wyckoff_notation(const double position[3],
const Symmetry * conv_sym,
SPGCONST double bravais_lattice[3][3],
const int hall_number,
const double symprec);
2012-12-11 16:15:20 +08:00
2018-11-13 16:29:58 +08:00
/* Return NULL if failed */
2012-12-11 16:15:20 +08:00
VecDBL * ssm_get_exact_positions(int *wyckoffs,
int *equiv_atoms,
const Cell * conv_prim,
const Symmetry * conv_sym,
const int hall_number,
const double symprec)
2012-12-11 16:15:20 +08:00
{
2018-11-13 16:29:58 +08:00
int i;
2016-10-11 13:33:45 +08:00
double tolerance;
VecDBL *positions;
positions = NULL;
2018-11-13 16:29:58 +08:00
/* Attempt slightly loosen tolerance */
2016-10-11 13:33:45 +08:00
tolerance = symprec;
2018-11-13 16:29:58 +08:00
for (i = 0; i < NUM_ATTEMPT; i++) {
if ((positions = get_exact_positions(equiv_atoms,
conv_prim,
conv_sym,
tolerance)) == NULL) {
return NULL;
2016-10-11 13:33:45 +08:00
}
2018-11-13 16:29:58 +08:00
if (set_Wyckoffs_labels(wyckoffs,
positions,
equiv_atoms,
conv_prim,
conv_sym,
hall_number,
symprec)) {
2018-11-13 16:29:58 +08:00
break;
} else {
mat_free_VecDBL(positions);
positions = NULL;
tolerance *= INCREASE_RATE;
}
2016-10-11 13:33:45 +08:00
}
return positions;
2012-12-11 16:15:20 +08:00
}
2018-11-13 16:29:58 +08:00
/* Return 0: succeeded, 1: tolerance too large, -1: tolerance too short */
static VecDBL * get_exact_positions(int * equiv_atoms,
const Cell * conv_prim,
const Symmetry * conv_sym,
const double symprec)
2012-12-11 16:15:20 +08:00
{
2018-11-13 16:29:58 +08:00
int i, num_indep_atoms;
2012-12-11 16:15:20 +08:00
int *indep_atoms;
2018-11-13 16:29:58 +08:00
VecDBL *positions;
2012-12-11 16:15:20 +08:00
2015-06-17 14:06:47 +08:00
debug_print("get_exact_positions\n");
2018-11-13 16:29:58 +08:00
positions = NULL;
2015-06-17 14:06:47 +08:00
indep_atoms = NULL;
2018-11-13 16:29:58 +08:00
if ((positions = mat_alloc_VecDBL(conv_prim->size)) == NULL) {
return NULL;
}
2016-10-11 13:33:45 +08:00
if ((indep_atoms = (int*) malloc(sizeof(int) * conv_prim->size)) == NULL) {
2015-06-17 14:06:47 +08:00
warning_print("spglib: Memory could not be allocated ");
2018-11-13 16:29:58 +08:00
mat_free_VecDBL(positions);
return NULL;
2015-06-17 14:06:47 +08:00
}
2012-12-11 16:15:20 +08:00
num_indep_atoms = 0;
2016-10-11 13:33:45 +08:00
for (i = 0; i < conv_prim->size; i++) {
2012-12-11 16:15:20 +08:00
/* Check if atom_i overlap to an atom already set at the exact position. */
2016-10-11 13:33:45 +08:00
if (! set_equivalent_atom(positions,
equiv_atoms,
i,
num_indep_atoms,
indep_atoms,
conv_prim,
conv_sym,
symprec)) {
2016-10-11 13:33:45 +08:00
/* No equivalent atom was found. */
2018-11-13 16:29:58 +08:00
equiv_atoms[i] = i;
2016-10-11 13:33:45 +08:00
indep_atoms[num_indep_atoms] = i;
mat_copy_vector_d3(positions->vec[i], conv_prim->position[i]);
2018-11-13 16:29:58 +08:00
set_exact_location(positions->vec[i],
conv_sym,
conv_prim->lattice,
symprec);
num_indep_atoms++;
2012-12-11 16:15:20 +08:00
}
}
free(indep_atoms);
indep_atoms = NULL;
2018-11-13 16:29:58 +08:00
return positions;
2016-10-11 13:33:45 +08:00
}
static int set_equivalent_atom(VecDBL *positions,
int * equiv_atoms,
const int i,
const int num_indep_atoms,
const int *indep_atoms,
const Cell * conv_prim,
const Symmetry * conv_sym,
const double symprec)
2016-10-11 13:33:45 +08:00
{
int j, k, l;
double pos[3];
for (j = 0; j < num_indep_atoms; j++) {
for (k = 0; k < conv_sym->size; k++) {
mat_multiply_matrix_vector_id3(pos,
conv_sym->rot[k],
positions->vec[indep_atoms[j]]);
2016-10-11 13:33:45 +08:00
for (l = 0; l < 3; l++) {
pos[l] += conv_sym->trans[k][l];
2016-10-11 13:33:45 +08:00
}
2016-11-25 16:13:33 +08:00
if (cel_is_overlap_with_same_type(pos,
conv_prim->position[i],
conv_prim->types[indep_atoms[j]],
conv_prim->types[i],
conv_prim->lattice,
symprec)) {
for (l = 0; l < 3; l++) {
positions->vec[i][l] = mat_Dmod1(pos[l]);
}
equiv_atoms[i] = indep_atoms[j];
return 1;
2016-10-11 13:33:45 +08:00
}
2015-06-17 14:06:47 +08:00
}
}
2016-10-11 13:33:45 +08:00
return 0;
2012-12-11 16:15:20 +08:00
}
/* Site-symmetry is used to determine exact location of an atom */
/* R. W. Grosse-Kunstleve and P. D. Adams */
/* Acta Cryst. (2002). A58, 60-65 */
2018-11-13 16:29:58 +08:00
static void set_exact_location(double position[3],
const Symmetry * conv_sym,
SPGCONST double bravais_lattice[3][3],
const double symprec)
2012-12-11 16:15:20 +08:00
{
2018-11-13 16:29:58 +08:00
int i, j, k, num_site_sym;
2012-12-11 16:15:20 +08:00
double sum_rot[3][3];
double pos[3], sum_trans[3];
2017-08-21 11:01:00 +08:00
debug_print("set_exact_location\n");
2015-06-17 14:06:47 +08:00
2018-11-13 16:29:58 +08:00
num_site_sym = 0;
2012-12-11 16:15:20 +08:00
for (i = 0; i < 3; i++) {
sum_trans[i] = 0.0;
for (j = 0; j < 3; j++) {
sum_rot[i][j] = 0;
}
}
2016-10-11 13:33:45 +08:00
2012-12-11 16:15:20 +08:00
for (i = 0; i < conv_sym->size; i++) {
2018-11-13 16:29:58 +08:00
mat_multiply_matrix_vector_id3(pos,
conv_sym->rot[i],
position);
2012-12-11 16:15:20 +08:00
for (j = 0; j < 3; j++) {
pos[j] += conv_sym->trans[i][j];
}
if (cel_is_overlap(pos,
position,
bravais_lattice,
symprec)) {
2012-12-11 16:15:20 +08:00
for (j = 0; j < 3; j++) {
for (k = 0; k < 3; k++) {
sum_rot[j][k] += conv_sym->rot[i][j][k];
}
sum_trans[j] +=
conv_sym->trans[i][j] - mat_Nint(pos[j] - position[j]);
2012-12-11 16:15:20 +08:00
}
2018-11-13 16:29:58 +08:00
num_site_sym++;
2012-12-11 16:15:20 +08:00
}
}
for (i = 0; i < 3; i++) {
2018-11-13 16:29:58 +08:00
sum_trans[i] /= num_site_sym;
2012-12-11 16:15:20 +08:00
for (j = 0; j < 3; j++) {
2018-11-13 16:29:58 +08:00
sum_rot[i][j] /= num_site_sym;
2012-12-11 16:15:20 +08:00
}
}
/* (sum_rot|sum_trans) is the special-position operator. */
/* Elements of sum_rot can be fractional values. */
mat_multiply_matrix_vector_d3(position,
sum_rot,
position);
2012-12-11 16:15:20 +08:00
for (i = 0; i < 3; i++) {
position[i] += sum_trans[i];
}
2016-10-11 13:33:45 +08:00
}
static int set_Wyckoffs_labels(int *wyckoffs,
const VecDBL *positions,
const int * equiv_atoms,
const Cell * conv_prim,
const Symmetry * conv_sym,
const int hall_number,
const double symprec)
2016-10-11 13:33:45 +08:00
{
int i, w;
for (i = 0; i < conv_prim->size; i++) {
if (i == equiv_atoms[i]) {
w = get_Wyckoff_notation(positions->vec[i],
conv_sym,
conv_prim->lattice,
hall_number,
symprec);
2016-10-11 13:33:45 +08:00
if (w < 0) {
goto err;
2016-10-11 13:33:45 +08:00
} else {
wyckoffs[i] = w;
2016-10-11 13:33:45 +08:00
}
}
}
for (i = 0; i < conv_prim->size; i++) {
if (i != equiv_atoms[i]) {
wyckoffs[i] = wyckoffs[equiv_atoms[i]];
}
}
return 1;
err:
return 0;
2012-12-11 16:15:20 +08:00
}
2015-06-17 14:06:47 +08:00
/* Return -1 if failed */
2018-11-13 16:29:58 +08:00
static int get_Wyckoff_notation(const double position[3],
const Symmetry * conv_sym,
SPGCONST double bravais_lattice[3][3],
const int hall_number,
const double symprec)
2012-12-11 16:15:20 +08:00
{
2018-11-13 16:29:58 +08:00
int i, j, k, l, num_sitesym, multiplicity, wyckoff_letter;
2012-12-11 16:15:20 +08:00
int indices_wyc[2];
int rot[3][3];
double trans[3], orbit[3];
VecDBL *pos_rot;
2015-06-17 14:06:47 +08:00
debug_print("get_Wyckoff_notation\n");
wyckoff_letter = -1;
pos_rot = NULL;
if ((pos_rot = mat_alloc_VecDBL(conv_sym->size)) == NULL) {
return -1;
}
2012-12-11 16:15:20 +08:00
for (i = 0; i < conv_sym->size; i++) {
mat_multiply_matrix_vector_id3(pos_rot->vec[i], conv_sym->rot[i], position);
for (j = 0; j < 3; j++) {
pos_rot->vec[i][j] += conv_sym->trans[i][j];
}
}
ssmdb_get_wyckoff_indices(indices_wyc, hall_number);
for (i = 0; i < indices_wyc[1]; i++) {
2018-11-13 16:29:58 +08:00
/* (rot, trans) gives the first element of each Wyckoff position */
/* of the 'Coordinates' in ITA */
/* Example: (x,1/4,1/2) */
/* rot trans */
/* [1, 0, 0] [0, 1/4, 1/2] */
/* [0, 0, 0] */
/* [0, 0, 0] */
multiplicity = ssmdb_get_coordinate(rot, trans, i + indices_wyc[0]);
/* Effectively this iteration runs over all 'Coordinates' of each */
/* Wyckoff position, i.e., works as looking for the first element. */
2012-12-11 16:15:20 +08:00
for (j = 0; j < pos_rot->size; j++) {
2018-11-13 16:29:58 +08:00
num_sitesym = 0;
2012-12-11 16:15:20 +08:00
for (k = 0; k < pos_rot->size; k++) {
if (cel_is_overlap(pos_rot->vec[j],
pos_rot->vec[k],
bravais_lattice,
symprec)) {
mat_multiply_matrix_vector_id3(orbit, rot, pos_rot->vec[k]);
for (l = 0; l < 3; l++) {
orbit[l] += trans[l];
}
if (cel_is_overlap(pos_rot->vec[k],
orbit,
bravais_lattice,
symprec)) {
2018-11-13 16:29:58 +08:00
num_sitesym++;
}
}
2012-12-11 16:15:20 +08:00
}
2018-11-13 16:29:58 +08:00
if (num_sitesym * multiplicity == conv_sym->size) {
/* Database is made reversed order, e.g., gfedcba. */
/* wyckoff is set 0 1 2 3 4... for a b c d e..., respectively. */
wyckoff_letter = indices_wyc[1] - i - 1;
goto end;
2012-12-11 16:15:20 +08:00
}
}
}
end:
mat_free_VecDBL(pos_rot);
2016-04-22 09:41:52 +08:00
pos_rot = NULL;
2012-12-11 16:15:20 +08:00
return wyckoff_letter;
}