phonopy/c/spglib/spacegroup.c

1526 lines
48 KiB
C

/* Copyright (C) 2010 Atsushi Togo */
/* 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. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cell.h"
#include "delaunay.h"
#include "hall_symbol.h"
#include "mathfunc.h"
#include "niggli.h"
#include "pointgroup.h"
#include "primitive.h"
#include "spacegroup.h"
#include "spg_database.h"
#include "symmetry.h"
#include "debug.h"
#define REDUCE_RATE 0.95
#define NUM_ATTEMPT 100
#define INT_PREC 0.1
static double change_of_basis_monocli[36][3][3] = {
{{ 1, 0, 0 }, /* b first turn; two axes are flipped in second turn */
{ 0, 1, 0 },
{ 0, 0, 1 }},
{{ 0, 0, 1 }, /* b */
{ 0,-1, 0 },
{ 1, 0, 0 }},
{{ 0, 0, 1 }, /* a */
{ 1, 0, 0 },
{ 0, 1, 0 }},
{{ 1, 0, 0 }, /* c */
{ 0, 0, 1 },
{ 0,-1, 0 }},
{{ 0, 1, 0 }, /* c */
{ 0, 0, 1 },
{ 1, 0, 0 }},
{{ 0,-1, 0 }, /* a */
{ 1, 0, 0 },
{ 0, 0, 1 }},
{{-1, 0, 1 }, /* b */
{ 0, 1, 0 },
{-1, 0, 0 }},
{{ 1, 0,-1 }, /* b */
{ 0,-1, 0 },
{ 0, 0,-1 }},
{{ 0, 1,-1 }, /* a */
{ 1, 0, 0 },
{ 0, 0,-1 }},
{{-1,-1, 0 }, /* c */
{ 0, 0, 1 },
{-1, 0, 0 }},
{{ 1,-1, 0 }, /* c */
{ 0, 0, 1 },
{ 0,-1, 0 }},
{{ 0, 1, 1 }, /* a */
{ 1, 0, 0 },
{ 0, 1, 0 }},
{{ 0, 0,-1 }, /* b */
{ 0, 1, 0 },
{ 1, 0,-1 }},
{{-1, 0, 0 }, /* b */
{ 0,-1, 0 },
{-1, 0, 1 }},
{{ 0,-1, 0 }, /* a */
{ 1, 0, 0 },
{ 0,-1, 1 }},
{{ 0, 1, 0 }, /* c */
{ 0, 0, 1 },
{ 1, 1, 0 }},
{{-1, 0, 0 }, /* c */
{ 0, 0, 1 },
{-1, 1, 0 }},
{{ 0, 0,-1 }, /* a */
{ 1, 0, 0 },
{ 0,-1,-1 }},
{{ 1, 0, 0 }, /* b two axes are flipped to look for non-acute axes */
{ 0,-1, 0 },
{ 0, 0,-1 }},
{{ 0, 0,-1 }, /* b */
{ 0, 1, 0 },
{ 1, 0, 0 }},
{{ 0, 0, 1 }, /* a */
{-1, 0, 0 },
{ 0,-1, 0 }},
{{-1, 0, 0 }, /* c */
{ 0, 0,-1 },
{ 0,-1, 0 }},
{{ 0, 1, 0 }, /* c */
{ 0, 0,-1 },
{-1, 0, 0 }},
{{ 0, 1, 0 }, /* a */
{-1, 0, 0 },
{ 0, 0, 1 }},
{{-1, 0,-1 }, /* b */
{ 0,-1, 0 },
{-1, 0, 0 }},
{{ 1, 0, 1 }, /* b */
{ 0, 1, 0 },
{ 0, 0, 1 }},
{{ 0,-1,-1 }, /* a */
{-1, 0, 0 },
{ 0, 0,-1 }},
{{ 1,-1, 0 }, /* c */
{ 0, 0,-1 },
{ 1, 0, 0 }},
{{-1,-1, 0 }, /* c */
{ 0, 0,-1 },
{ 0,-1, 0 }},
{{ 0,-1, 1 }, /* a */
{-1, 0, 0 },
{ 0,-1, 0 }},
{{ 0, 0, 1 }, /* b */
{ 0,-1, 0 },
{ 1, 0, 1 }},
{{-1, 0, 0 }, /* b */
{ 0, 1, 0 },
{-1, 0,-1 }},
{{ 0, 1, 0 }, /* a */
{-1, 0, 0 },
{ 0, 1, 1 }},
{{ 0, 1, 0 }, /* c */
{ 0, 0,-1 },
{-1, 1, 0 }},
{{ 1, 0, 0 }, /* c */
{ 0, 0,-1 },
{ 1, 1, 0 }},
{{ 0, 0,-1 }, /* a */
{-1, 0, 0 },
{ 0, 1,-1 }}};
static Centering change_of_centering_monocli[36] = {
C_FACE, /* first turn */
A_FACE,
B_FACE,
B_FACE,
A_FACE,
C_FACE,
BASE,
BASE,
BASE,
BASE,
BASE,
BASE,
A_FACE,
C_FACE,
C_FACE,
A_FACE,
B_FACE,
B_FACE,
C_FACE, /* second turn */
A_FACE,
B_FACE,
B_FACE,
A_FACE,
C_FACE,
BASE,
BASE,
BASE,
BASE,
BASE,
BASE,
A_FACE,
C_FACE,
C_FACE,
A_FACE,
B_FACE,
B_FACE};
static int change_of_unique_axis_monocli[36] = {
1, 1, 0, 2, 2, 0, 1, 1, 0, 2, 2, 0, 1, 1, 0, 2, 2, 0,
1, 1, 0, 2, 2, 0, 1, 1, 0, 2, 2, 0, 1, 1, 0, 2, 2, 0};
static double change_of_basis_ortho[6][3][3] = {{{ 1, 0, 0 },
{ 0, 1, 0 },
{ 0, 0, 1 }},
{{ 0, 0, 1 },
{ 1, 0, 0 },
{ 0, 1, 0 }},
{{ 0, 1, 0 },
{ 0, 0, 1 },
{ 1, 0, 0 }},
{{ 0, 1, 0 },
{ 1, 0, 0 },
{ 0, 0,-1 }},
{{ 1, 0, 0 },
{ 0, 0, 1 },
{ 0,-1, 0 }},
{{ 0, 0, 1 },
{ 0, 1, 0 },
{-1, 0, 0 }}};
static Centering change_of_centering_ortho[6] = {C_FACE,
B_FACE,
A_FACE,
C_FACE,
B_FACE,
A_FACE};
static int change_of_unique_axis_ortho[6] = {2, 1, 0, 2, 1, 0};
static double hR_to_hP[3][3] = {{ 1, 0, 1 },
{-1, 1, 1 },
{ 0,-1, 1 }};
static double change_of_basis_501[3][3] = {{ 0, 0, 1},
{ 0,-1, 0},
{ 1, 0, 0}};
static int spacegroup_to_hall_number[230] = {
1, 2, 3, 6, 9, 18, 21, 30, 39, 57,
60, 63, 72, 81, 90, 108, 109, 112, 115, 116,
119, 122, 123, 124, 125, 128, 134, 137, 143, 149,
155, 161, 164, 170, 173, 176, 182, 185, 191, 197,
203, 209, 212, 215, 218, 221, 227, 228, 230, 233,
239, 245, 251, 257, 263, 266, 269, 275, 278, 284,
290, 292, 298, 304, 310, 313, 316, 322, 334, 335,
337, 338, 341, 343, 349, 350, 351, 352, 353, 354,
355, 356, 357, 358, 359, 361, 363, 364, 366, 367,
368, 369, 370, 371, 372, 373, 374, 375, 376, 377,
378, 379, 380, 381, 382, 383, 384, 385, 386, 387,
388, 389, 390, 391, 392, 393, 394, 395, 396, 397,
398, 399, 400, 401, 402, 404, 406, 407, 408, 410,
412, 413, 414, 416, 418, 419, 420, 422, 424, 425,
426, 428, 430, 431, 432, 433, 435, 436, 438, 439,
440, 441, 442, 443, 444, 446, 447, 448, 449, 450,
452, 454, 455, 456, 457, 458, 460, 462, 463, 464,
465, 466, 467, 468, 469, 470, 471, 472, 473, 474,
475, 476, 477, 478, 479, 480, 481, 482, 483, 484,
485, 486, 487, 488, 489, 490, 491, 492, 493, 494,
495, 497, 498, 500, 501, 502, 503, 504, 505, 506,
507, 508, 509, 510, 511, 512, 513, 514, 515, 516,
517, 518, 520, 521, 523, 524, 525, 527, 529, 530,
};
static double identity[3][3] = {{ 1, 0, 0 },
{ 0, 1, 0 },
{ 0, 0, 1 }};
static double monocli_i2c[3][3] = {{ 1, 0,-1 },
{ 0, 1, 0 },
{ 1, 0, 0 }};
static double monocli_a2c[3][3] = {{ 0, 0, 1 },
{ 0,-1, 0 },
{ 1, 0, 0 }};
static double rhombo_obverse[3][3] = {{ 2./3,-1./3,-1./3 },
{ 1./3, 1./3,-2./3 },
{ 1./3, 1./3, 1./3 }};
static double rhomb_reverse[3][3] = {{ 1./3,-2./3, 1./3 },
{ 2./3,-1./3,-1./3 },
{ 1./3, 1./3, 1./3 }};
static double a2c[3][3] = {{ 0, 0, 1 },
{ 1, 0, 0 },
{ 0, 1, 0 }};
static double b2c[3][3] = {{ 0, 1, 0 },
{ 0, 0, 1 },
{ 1, 0, 0 }};
static double A_mat[3][3] = {{ 1, 0, 0},
{ 0, 1./2,-1./2},
{ 0, 1./2, 1./2}};
static double C_mat[3][3] = {{ 1./2, 1./2, 0},
{-1./2, 1./2, 0},
{ 0, 0, 1}};
static double R_mat[3][3] = {{ 2./3,-1./3,-1./3 },
{ 1./3, 1./3,-2./3 },
{ 1./3, 1./3, 1./3 }};
static double I_mat[3][3] = {{-1./2, 1./2, 1./2 },
{ 1./2,-1./2, 1./2 },
{ 1./2, 1./2,-1./2 }};
static double F_mat[3][3] = {{ 0, 1./2, 1./2 },
{ 1./2, 0, 1./2 },
{ 1./2, 1./2, 0 }};
static Spacegroup * search_spacegroup_with_symmetry(const Cell * primitive,
const int candidates[],
const int num_candidates,
const Symmetry *symmetry,
const double symprec,
const double angle_tolerance);
static Spacegroup * get_spacegroup(const int hall_number,
const double origin_shift[3],
SPGCONST double conv_lattice[3][3]);
static int iterative_search_hall_number(double origin_shift[3],
double conv_lattice[3][3],
const int candidates[],
const int num_candidates,
const Cell * primitive,
const Symmetry * symmetry,
const double symprec,
const double angle_tolerance);
static int change_basis_tricli(int int_transform_mat[3][3],
SPGCONST double conv_lattice[3][3],
SPGCONST double primitive_lattice[3][3],
const double symprec);
static int change_basis_monocli(int int_transform_mat[3][3],
SPGCONST double conv_lattice[3][3],
SPGCONST double primitive_lattice[3][3],
const double symprec);
static Symmetry *
get_initial_conventional_symmetry(const Centering centering,
SPGCONST double transform_mat[3][3],
const Symmetry * symmetry);
static int search_hall_number(double origin_shift[3],
double conv_lattice[3][3],
const int candidates[],
const int num_candidates,
SPGCONST double primitive_lattice[3][3],
const Symmetry * symmetry,
const double symprec);
static int match_hall_symbol_db(double origin_shift[3],
double lattice[3][3],
const int hall_number,
const int pointgroup_number,
const Holohedry holohedry,
const Centering centering,
const Symmetry *symmetry,
const double symprec);
static int match_hall_symbol_db_monocli(double origin_shift[3],
double lattice[3][3],
const int hall_number,
const int num_hall_types,
const Centering centering,
const Symmetry *symmetry,
const double symprec);
static int match_hall_symbol_db_ortho(double origin_shift[3],
double lattice[3][3],
const int hall_number,
const Centering centering,
const Symmetry *symmetry,
const int num_free_axes,
const double symprec);
static Symmetry * get_conventional_symmetry(SPGCONST double transform_mat[3][3],
const Centering centering,
const Symmetry *primitive_sym);
static Centering get_centering(double correction_mat[3][3],
SPGCONST int transform_mat[3][3],
const Laue laue);
static Centering get_base_center(SPGCONST int transform_mat[3][3]);
static int get_centering_shifts(double shift[3][3],
const Centering centering);
/* Return spacegroup.number = 0 if failed */
Spacegroup * spa_search_spacegroup(const Cell * primitive,
const int hall_number,
const double symprec,
const double angle_tolerance)
{
Spacegroup *spacegroup;
Symmetry *symmetry;
int candidate[1];
debug_print("search_spacegroup (tolerance = %f):\n", symprec);
symmetry = NULL;
spacegroup = NULL;
if ((symmetry = sym_get_operation(primitive, symprec, angle_tolerance)) ==
NULL) {
return NULL;
}
if (hall_number > 0) {
candidate[0] = hall_number;
}
if (hall_number) {
spacegroup = search_spacegroup_with_symmetry(primitive,
candidate,
1,
symmetry,
symprec,
angle_tolerance);
} else {
spacegroup = search_spacegroup_with_symmetry(primitive,
spacegroup_to_hall_number,
230,
symmetry,
symprec,
angle_tolerance);
}
sym_free_symmetry(symmetry);
symmetry = NULL;
return spacegroup;
}
/* Retrun 0 if failed */
int spa_search_spacegroup_with_symmetry(const Symmetry *symmetry,
const double symprec)
{
int i, hall_number;
Spacegroup *spacegroup;
Cell *primitive;
spacegroup = NULL;
primitive = cel_alloc_cell(1);
mat_copy_matrix_d3(primitive->lattice, identity);
for (i = 0; i < 3; i++) {
primitive->position[0][i] = 0;
}
spacegroup = search_spacegroup_with_symmetry(primitive,
spacegroup_to_hall_number,
230,
symmetry,
symprec,
-1.0);
if (spacegroup != NULL) {
hall_number = spacegroup->hall_number;
free(spacegroup);
spacegroup = NULL;
return hall_number;
} else {
return 0;
}
}
/* Return NULL if failed */
Cell * spa_transform_to_primitive(int * mapping_table,
const Cell * cell,
SPGCONST double trans_mat[3][3],
const Centering centering,
const double symprec)
{
double tmat[3][3], tmat_inv[3][3], prim_lat[3][3];
Cell * primitive;
primitive = NULL;
if (!mat_inverse_matrix_d3(tmat_inv, trans_mat, symprec)) {
goto err;
}
switch (centering) {
case PRIMITIVE:
mat_copy_matrix_d3(tmat, tmat_inv);
break;
case A_FACE:
mat_multiply_matrix_d3(tmat, tmat_inv, A_mat);
break;
case C_FACE:
mat_multiply_matrix_d3(tmat, tmat_inv, C_mat);
break;
case FACE:
mat_multiply_matrix_d3(tmat, tmat_inv, F_mat);
break;
case BODY:
mat_multiply_matrix_d3(tmat, tmat_inv, I_mat);
break;
case R_CENTER:
mat_multiply_matrix_d3(tmat, tmat_inv, R_mat);
break;
default:
goto err;
}
mat_multiply_matrix_d3(prim_lat, cell->lattice, tmat);
if ((primitive = cel_trim_cell(mapping_table, prim_lat, cell, symprec))
== NULL) {
warning_print("spglib: cel_trim_cell failed.");
warning_print(" (line %d, %s).\n", __LINE__, __FILE__);
}
return primitive;
err:
return NULL;
}
/* Return NULL if failed */
Cell * spa_transform_from_primitive(const Cell * primitive,
const Centering centering,
const double symprec)
{
int multi, i, j, k, num_atom;
int *mapping_table;
double tmat[3][3], inv_tmat[3][3], shift[3][3];
Cell *std_cell, *trimmed_cell;
mapping_table = NULL;
trimmed_cell = NULL;
std_cell = NULL;
switch (centering) {
case PRIMITIVE:
break;
case A_FACE:
mat_copy_matrix_d3(tmat, A_mat);
mat_inverse_matrix_d3(inv_tmat, A_mat, 0);
break;
case C_FACE:
mat_copy_matrix_d3(tmat, C_mat);
mat_inverse_matrix_d3(inv_tmat, C_mat, 0);
break;
case FACE:
mat_copy_matrix_d3(tmat, F_mat);
mat_inverse_matrix_d3(inv_tmat, F_mat, 0);
break;
case BODY:
mat_copy_matrix_d3(tmat, I_mat);
mat_inverse_matrix_d3(inv_tmat, I_mat, 0);
break;
case R_CENTER:
mat_copy_matrix_d3(tmat, R_mat);
mat_inverse_matrix_d3(inv_tmat, R_mat, 0);
break;
default:
goto ret;
}
multi = get_centering_shifts(shift, centering);
if ((mapping_table = (int*) malloc(sizeof(int) * primitive->size * multi))
== NULL) {
warning_print("spglib: Memory could not be allocated ");
goto ret;
}
if ((std_cell = cel_alloc_cell(primitive->size * multi)) == NULL) {
free(mapping_table);
mapping_table = NULL;
goto ret;
}
mat_multiply_matrix_d3(std_cell->lattice, primitive->lattice, inv_tmat);
num_atom = 0;
for (i = 0; i < primitive->size; i++) {
mat_multiply_matrix_vector_d3(std_cell->position[num_atom],
tmat,
primitive->position[i]);
std_cell->types[num_atom] = primitive->types[i];
num_atom++;
}
for (i = 0; i < multi - 1; i++) {
for (j = 0; j < primitive->size; j++) {
mat_copy_vector_d3(std_cell->position[num_atom],
std_cell->position[j]);
for (k = 0; k < 3; k++) {
std_cell->position[num_atom][k] += shift[i][k];
}
std_cell->types[num_atom] = std_cell->types[j];
num_atom++;
}
}
trimmed_cell = cel_trim_cell(mapping_table,
std_cell->lattice,
std_cell,
symprec);
cel_free_cell(std_cell);
std_cell = NULL;
free(mapping_table);
mapping_table = NULL;
ret:
return trimmed_cell;
}
/* Return NULL if failed */
static Spacegroup * search_spacegroup_with_symmetry(const Cell * primitive,
const int candidates[],
const int num_candidates,
const Symmetry *symmetry,
const double symprec,
const double angle_tolerance)
{
int hall_number;
double conv_lattice[3][3];
double origin_shift[3];
Spacegroup *spacegroup;
PointSymmetry pointsym;
debug_print("search_spacegroup (tolerance = %f):\n", symprec);
spacegroup = NULL;
pointsym = ptg_get_pointsymmetry(symmetry->rot, symmetry->size);
if (pointsym.size < symmetry->size) {
warning_print("spglib: Point symmetry of primitive cell is broken. ");
warning_print("(line %d, %s).\n", __LINE__, __FILE__);
return NULL;
}
hall_number = iterative_search_hall_number(origin_shift,
conv_lattice,
candidates,
num_candidates,
primitive,
symmetry,
symprec,
angle_tolerance);
if (hall_number == 0) {
return NULL;
}
spacegroup = get_spacegroup(hall_number, origin_shift, conv_lattice);
return spacegroup;
}
/* Return spacegroup.number = 0 if failed */
static Spacegroup * get_spacegroup(const int hall_number,
const double origin_shift[3],
SPGCONST double conv_lattice[3][3])
{
Spacegroup *spacegroup;
SpacegroupType spacegroup_type;
spacegroup = NULL;
if ((spacegroup = (Spacegroup*) malloc(sizeof(Spacegroup))) == NULL) {
warning_print("spglib: Memory could not be allocated.");
return NULL;
}
if (0 < hall_number && hall_number < 531) {
spacegroup_type = spgdb_get_spacegroup_type(hall_number);
mat_copy_matrix_d3(spacegroup->bravais_lattice, conv_lattice);
mat_copy_vector_d3(spacegroup->origin_shift, origin_shift);
spacegroup->number = spacegroup_type.number;
spacegroup->hall_number = hall_number;
spacegroup->pointgroup_number = spacegroup_type.pointgroup_number;
strcpy(spacegroup->schoenflies, spacegroup_type.schoenflies);
strcpy(spacegroup->hall_symbol, spacegroup_type.hall_symbol);
strcpy(spacegroup->international, spacegroup_type.international);
strcpy(spacegroup->international_long, spacegroup_type.international_full);
strcpy(spacegroup->international_short,
spacegroup_type.international_short);
strcpy(spacegroup->choice, spacegroup_type.choice);
}
return spacegroup;
}
/* Return 0 if failed */
static int iterative_search_hall_number(double origin_shift[3],
double conv_lattice[3][3],
const int candidates[],
const int num_candidates,
const Cell * primitive,
const Symmetry * symmetry,
const double symprec,
const double angle_tolerance)
{
int attempt, hall_number;
double tolerance;
Symmetry * sym_reduced;
debug_print("iterative_search_hall_number:\n");
hall_number = 0;
sym_reduced = NULL;
hall_number = search_hall_number(origin_shift,
conv_lattice,
candidates,
num_candidates,
primitive->lattice,
symmetry,
symprec);
if (hall_number > 0) {
goto ret;
}
tolerance = symprec;
for (attempt = 0; attempt < NUM_ATTEMPT; attempt++) {
warning_print("spglib: Attempt %d tolerance = %e failed",
attempt, tolerance);
warning_print("(line %d, %s).\n", __LINE__, __FILE__);
tolerance *= REDUCE_RATE;
sym_reduced = sym_reduce_operation(primitive,
symmetry,
tolerance,
angle_tolerance);
hall_number = search_hall_number(origin_shift,
conv_lattice,
candidates,
num_candidates,
primitive->lattice,
sym_reduced,
symprec);
sym_free_symmetry(sym_reduced);
sym_reduced = NULL;
if (hall_number > 0) {
break;
}
}
ret:
return hall_number;
}
/* Return 0 if failed */
static int search_hall_number(double origin_shift[3],
double conv_lattice[3][3],
const int candidates[],
const int num_candidates,
SPGCONST double primitive_lattice[3][3],
const Symmetry * symmetry,
const double symprec)
{
int i, hall_number;
Centering centering;
Pointgroup pointgroup;
Symmetry * conv_symmetry;
int int_transform_mat[3][3];
double correction_mat[3][3], transform_mat[3][3];
debug_print("search_hall_number:\n");
hall_number = 0;
conv_symmetry = NULL;
pointgroup = ptg_get_transformation_matrix(int_transform_mat,
symmetry->rot,
symmetry->size);
if (pointgroup.number == 0) {
goto err;
}
mat_multiply_matrix_di3(conv_lattice, primitive_lattice, int_transform_mat);
if (pointgroup.laue == LAUE1) {
if (! change_basis_tricli(int_transform_mat,
conv_lattice,
primitive_lattice,
symprec)) {
goto err;
}
}
if (pointgroup.laue == LAUE2M) {
if (! change_basis_monocli(int_transform_mat,
conv_lattice,
primitive_lattice,
symprec)) {
goto err;
}
}
if ((centering = get_centering(correction_mat,
int_transform_mat,
pointgroup.laue)) == CENTERING_ERROR) {
goto err;
}
mat_multiply_matrix_id3(transform_mat, int_transform_mat, correction_mat);
mat_multiply_matrix_d3(conv_lattice, primitive_lattice, transform_mat);
if ((conv_symmetry = get_initial_conventional_symmetry(centering,
transform_mat,
symmetry)) == NULL) {
goto err;
}
for (i = 0; i < num_candidates; i++) {
if (match_hall_symbol_db(origin_shift,
conv_lattice, /* <-- modified only matched */
candidates[i],
pointgroup.number,
pointgroup.holohedry,
centering,
conv_symmetry,
symprec)) {
hall_number = candidates[i];
break;
}
}
sym_free_symmetry(conv_symmetry);
conv_symmetry = NULL;
return hall_number;
err:
return 0;
}
/* Triclinic: Niggli cell reduction */
/* Return 0 if failed */
static int change_basis_tricli(int int_transform_mat[3][3],
SPGCONST double conv_lattice[3][3],
SPGCONST double primitive_lattice[3][3],
const double symprec)
{
int i, j;
double niggli_cell[9];
double smallest_lattice[3][3], inv_lattice[3][3], transform_mat[3][3];
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
niggli_cell[i * 3 + j] = conv_lattice[i][j];
}
}
if (! niggli_reduce(niggli_cell, symprec * symprec)) {
return 0;
}
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
smallest_lattice[i][j] = niggli_cell[i * 3 + j];
}
}
if (mat_get_determinant_d3(smallest_lattice) < 0) {
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
smallest_lattice[i][j] = -smallest_lattice[i][j];
}
}
}
mat_inverse_matrix_d3(inv_lattice, primitive_lattice, 0);
mat_multiply_matrix_d3(transform_mat, inv_lattice, smallest_lattice);
mat_cast_matrix_3d_to_3i(int_transform_mat, transform_mat);
return 1;
}
/* Monoclinic: choose shortest a, c lattice vectors (|a| < |c|) */
/* Return 0 if failed */
static int change_basis_monocli(int int_transform_mat[3][3],
SPGCONST double conv_lattice[3][3],
SPGCONST double primitive_lattice[3][3],
const double symprec)
{
double smallest_lattice[3][3], inv_lattice[3][3], transform_mat[3][3];
if (! del_delaunay_reduce_2D(smallest_lattice,
conv_lattice,
1, /* unique axis of b */
symprec)) {
return 0;
}
mat_inverse_matrix_d3(inv_lattice, primitive_lattice, 0);
mat_multiply_matrix_d3(transform_mat, inv_lattice, smallest_lattice);
mat_cast_matrix_3d_to_3i(int_transform_mat, transform_mat);
return 1;
}
/* Return NULL if failed */
static Symmetry *
get_initial_conventional_symmetry(const Centering centering,
SPGCONST double transform_mat[3][3],
const Symmetry * symmetry)
{
Symmetry * conv_symmetry;
debug_print("get_initial_conventional_symmetry\n");
conv_symmetry = NULL;
if (centering == R_CENTER) {
/* hP for rhombohedral */
conv_symmetry = get_conventional_symmetry(transform_mat,
PRIMITIVE,
symmetry);
} else {
conv_symmetry = get_conventional_symmetry(transform_mat,
centering,
symmetry);
}
return conv_symmetry;
}
/* Return 0 if failed */
static int match_hall_symbol_db(double origin_shift[3],
double lattice[3][3],
const int hall_number,
const int pointgroup_number,
const Holohedry holohedry,
const Centering centering,
const Symmetry *symmetry,
const double symprec)
{
int is_found, num_hall_types;
SpacegroupType spacegroup_type;
Symmetry * changed_symmetry;
double changed_lattice[3][3], inv_lattice[3][3], transform_mat[3][3];
changed_symmetry = NULL;
spacegroup_type = spgdb_get_spacegroup_type(hall_number);
num_hall_types = (spacegroup_to_hall_number[spacegroup_type.number] -
spacegroup_to_hall_number[spacegroup_type.number - 1]);
if (pointgroup_number != spacegroup_type.pointgroup_number) {
goto err;
}
switch (holohedry) {
case MONOCLI:
if (match_hall_symbol_db_monocli(origin_shift,
lattice,
hall_number,
num_hall_types,
centering,
symmetry,
symprec)) {return 1;}
break;
case ORTHO:
if (spacegroup_type.number == 48 ||
spacegroup_type.number == 50 ||
spacegroup_type.number == 59 ||
spacegroup_type.number == 68 ||
spacegroup_type.number == 70) { /* uncount origin shift */
num_hall_types /= 2;
}
if (num_hall_types == 1) {
if (match_hall_symbol_db_ortho(origin_shift,
lattice,
hall_number,
centering,
symmetry,
6,
symprec)) {return 1;}
break;
}
if (num_hall_types == 2) {
if (match_hall_symbol_db_ortho(origin_shift,
lattice,
hall_number,
centering,
symmetry,
3,
symprec)) {return 1;}
break;
}
if (num_hall_types == 3) {
mat_copy_matrix_d3(changed_lattice, lattice);
if (! match_hall_symbol_db_ortho
(origin_shift,
changed_lattice,
spacegroup_to_hall_number[spacegroup_type.number - 1],
centering,
symmetry,
0,
symprec)) {break;}
mat_inverse_matrix_d3(inv_lattice, lattice, 0);
mat_multiply_matrix_d3(transform_mat, inv_lattice, changed_lattice);
if ((changed_symmetry = get_conventional_symmetry(transform_mat,
PRIMITIVE,
symmetry)) == NULL) {
goto err;
}
is_found = match_hall_symbol_db_ortho(origin_shift,
changed_lattice,
hall_number,
centering,
changed_symmetry,
2,
symprec);
sym_free_symmetry(changed_symmetry);
changed_symmetry = NULL;
if (is_found) {
mat_copy_matrix_d3(lattice, changed_lattice);
return 1;
}
break;
}
if (num_hall_types == 6) {
if (match_hall_symbol_db_ortho(origin_shift,
lattice,
hall_number,
centering,
symmetry,
1,
symprec)) {return 1;}
break;
}
break;
case CUBIC:
if (hal_match_hall_symbol_db(origin_shift,
lattice,
hall_number,
centering,
symmetry,
symprec)) {return 1;}
if (hall_number == 501) { /* Try another basis for No.205 */
mat_multiply_matrix_d3(changed_lattice,
lattice,
change_of_basis_501);
if ((changed_symmetry = get_conventional_symmetry(change_of_basis_501,
PRIMITIVE,
symmetry)) == NULL) {
goto err;
}
is_found = hal_match_hall_symbol_db(origin_shift,
changed_lattice,
hall_number,
PRIMITIVE,
changed_symmetry,
symprec);
sym_free_symmetry(changed_symmetry);
changed_symmetry = NULL;
if (is_found) {
mat_copy_matrix_d3(lattice, changed_lattice);
return 1;
}
}
break;
case TRIGO:
if (centering == R_CENTER) {
if (hall_number == 433 ||
hall_number == 436 ||
hall_number == 444 ||
hall_number == 450 ||
hall_number == 452 ||
hall_number == 458 ||
hall_number == 460) {
mat_multiply_matrix_d3(changed_lattice, lattice, hR_to_hP);
if ((changed_symmetry =
get_conventional_symmetry(hR_to_hP, R_CENTER, symmetry)) == NULL) {
goto err;
}
is_found = hal_match_hall_symbol_db(origin_shift,
changed_lattice,
hall_number,
centering,
changed_symmetry,
symprec);
sym_free_symmetry(changed_symmetry);
changed_symmetry = NULL;
if (is_found) {
mat_copy_matrix_d3(lattice, changed_lattice);
return 1;
}
} else {
if (hal_match_hall_symbol_db(origin_shift,
lattice,
hall_number,
PRIMITIVE,
symmetry,
symprec)) {
return 1;
}
}
break;
}
/* Do not break for other trigonal cases */
default: /* HEXA, TETRA, TRICLI and rest of TRIGO */
if (hal_match_hall_symbol_db(origin_shift,
lattice,
hall_number,
centering,
symmetry,
symprec)) {
return 1;
}
break;
}
err:
return 0;
}
/* Return 0 if failed */
static int match_hall_symbol_db_monocli(double origin_shift[3],
double lattice[3][3],
const int hall_number,
const int num_hall_types,
const Centering centering,
const Symmetry *symmetry,
const double symprec)
{
int i, j, k, l, is_found;
double vec[2][3], norms[3];
Centering changed_centering;
Symmetry * changed_symmetry;
double changed_lattice[3][3];
changed_symmetry = NULL;
for (i = 0; i < 36; i++) {
/* centring type should be P or C */
if (centering == C_FACE) {
changed_centering = change_of_centering_monocli[i];
} else { /* suppose PRIMITIVE */
changed_centering = centering;
}
mat_multiply_matrix_d3(changed_lattice,
lattice,
change_of_basis_monocli[i]);
/* Make non-acute and length preference */
l = 0;
for (j = 0; j < 3; j++) {
if (j == change_of_unique_axis_monocli[i]) {continue;}
for (k = 0; k < 3; k++) {vec[l][k] = changed_lattice[k][j];}
norms[l] = mat_norm_squared_d3(vec[l]);
l++;
}
/* discard if principal angle is acute. */
if (vec[0][0] * vec[1][0] +
vec[0][1] * vec[1][1] +
vec[0][2] * vec[1][2] > 0) {
continue;
}
/* Choose |a| < |b| < |c| if there are freedom. */
if (num_hall_types == 3) {
if (norms[0] > norms[1]) {continue;}
}
if ((changed_symmetry =
get_conventional_symmetry(change_of_basis_monocli[i],
PRIMITIVE,
symmetry)) == NULL) {
goto err;
}
is_found = hal_match_hall_symbol_db(origin_shift,
changed_lattice,
hall_number,
changed_centering,
changed_symmetry,
symprec);
sym_free_symmetry(changed_symmetry);
changed_symmetry = NULL;
if (is_found) {
mat_copy_matrix_d3(lattice, changed_lattice);
return 1;
}
}
err:
return 0;
}
/* Return 0 if failed */
static int match_hall_symbol_db_ortho(double origin_shift[3],
double lattice[3][3],
const int hall_number,
const Centering centering,
const Symmetry *symmetry,
const int num_free_axes,
const double symprec)
{
int i, j, k, l, is_found;
double vec[3], norms[3];
Centering changed_centering;
Symmetry * changed_symmetry;
double changed_lattice[3][3];
changed_symmetry = NULL;
for (i = 0; i < 6; i++) {
if (centering == C_FACE) {
changed_centering = change_of_centering_ortho[i];
} else {
changed_centering = centering;
}
mat_multiply_matrix_d3(changed_lattice,
lattice,
change_of_basis_ortho[i]);
if (num_free_axes == 2) {
l = 0;
for (j = 0; j < 3; j++) {
if (j == change_of_unique_axis_ortho[i]) {continue;}
for (k = 0; k < 3; k++) {vec[k] = changed_lattice[k][j];}
norms[l] = mat_norm_squared_d3(vec);
l++;
}
if (norms[0] > norms[1]) {continue;}
}
if (num_free_axes == 3) {
for (j = 0; j < 3; j++) {
for (k = 0; k < 3; k++) {vec[k] = changed_lattice[k][j];}
norms[j] = mat_norm_squared_d3(vec);
}
if (norms[0] > norms[1] || norms[0] > norms[2]) {continue;}
}
if (num_free_axes == 6) {
for (j = 0; j < 3; j++) {
for (k = 0; k < 3; k++) {vec[k] = changed_lattice[k][j];}
norms[j] = mat_norm_squared_d3(vec);
}
if (norms[0] > norms[1] || norms[1] > norms[2]) {continue;}
}
if ((changed_symmetry = get_conventional_symmetry(change_of_basis_ortho[i],
PRIMITIVE,
symmetry)) == NULL) {
goto err;
}
is_found = hal_match_hall_symbol_db(origin_shift,
changed_lattice,
hall_number,
changed_centering,
changed_symmetry,
symprec);
sym_free_symmetry(changed_symmetry);
changed_symmetry = NULL;
if (is_found) {
mat_copy_matrix_d3(lattice, changed_lattice);
return 1;
}
}
err:
return 0;
}
/* Return NULL if failed */
static Symmetry * get_conventional_symmetry(SPGCONST double transform_mat[3][3],
const Centering centering,
const Symmetry *primitive_sym)
{
int i, j, k, multi, size;
double inv_tmat[3][3], shift[3][3];
double symmetry_rot_d3[3][3], primitive_sym_rot_d3[3][3];
Symmetry *symmetry;
symmetry = NULL;
size = primitive_sym->size;
switch (centering) {
case FACE:
if ((symmetry = sym_alloc_symmetry(size * 4)) == NULL) {
return NULL;
}
break;
case R_CENTER:
if ((symmetry = sym_alloc_symmetry(size * 3)) == NULL) {
return NULL;
}
break;
case BODY:
case A_FACE:
case B_FACE:
case C_FACE:
if ((symmetry = sym_alloc_symmetry(size * 2)) == NULL) {
return NULL;
}
break;
default:
if ((symmetry = sym_alloc_symmetry(size)) == NULL) {
return NULL;
}
break;
}
for (i = 0; i < size; i++) {
mat_cast_matrix_3i_to_3d(primitive_sym_rot_d3, primitive_sym->rot[i]);
/* C*S*C^-1: recover conventional cell symmetry operation */
mat_get_similar_matrix_d3(symmetry_rot_d3,
primitive_sym_rot_d3,
transform_mat,
0);
mat_cast_matrix_3d_to_3i(symmetry->rot[i], symmetry_rot_d3);
/* translation in conventional cell: C = B^-1*P */
mat_inverse_matrix_d3(inv_tmat, transform_mat, 0);
mat_multiply_matrix_vector_d3(symmetry->trans[i],
inv_tmat,
primitive_sym->trans[i]);
}
multi = 1;
if (centering != PRIMITIVE) {
multi = get_centering_shifts(shift, centering);
for (i = 0; i < multi - 1; i++) {
for (j = 0; j < size; j++) {
mat_copy_matrix_i3(symmetry->rot[(i+1) * size + j],
symmetry->rot[j]);
for (k = 0; k < 3; k++) {
symmetry->trans[(i+1) * size + j][k] =
symmetry->trans[j][k] + shift[i][k];
}
}
}
}
for (i = 0; i < multi; i++) {
for (j = 0; j < size; j++) {
for (k = 0; k < 3; k++) {
symmetry->trans[i * size + j][k] =
mat_Dmod1(symmetry->trans[i * size + j][k]);
}
}
}
return symmetry;
}
/* Return CENTERING_ERROR if failed */
static Centering get_centering(double correction_mat[3][3],
SPGCONST int transform_mat[3][3],
const Laue laue)
{
int det;
double trans_corr_mat[3][3];
Centering centering;
mat_copy_matrix_d3(correction_mat, identity);
det = abs(mat_get_determinant_i3(transform_mat));
debug_print("laue class: %d\n", laue);
debug_print("multiplicity: %d\n", det);
switch (det) {
case 1:
centering = PRIMITIVE;
break;
case 2:
centering = get_base_center(transform_mat);
if (centering == A_FACE) {
if (laue == LAUE2M) {
debug_print("Monocli A to C\n");
mat_copy_matrix_d3(correction_mat, monocli_a2c);
} else {
mat_copy_matrix_d3(correction_mat, a2c);
}
centering = C_FACE;
}
if (centering == B_FACE) {
mat_copy_matrix_d3(correction_mat, b2c);
centering = C_FACE;
}
if (laue == LAUE2M && centering == BODY) {
debug_print("Monocli I to C\n");
mat_copy_matrix_d3(correction_mat, monocli_i2c);
centering = C_FACE;
}
break;
case 3:
/* hP (a=b) but not hR (a=b=c) */
centering = R_CENTER;
mat_multiply_matrix_id3(trans_corr_mat, transform_mat, rhombo_obverse);
if (mat_is_int_matrix(trans_corr_mat, INT_PREC)) {
mat_copy_matrix_d3(correction_mat, rhombo_obverse);
debug_print("R-center observe setting\n");
debug_print_matrix_d3(trans_corr_mat);
}
mat_multiply_matrix_id3(trans_corr_mat, transform_mat, rhomb_reverse);
if (mat_is_int_matrix(trans_corr_mat, INT_PREC)) {
mat_copy_matrix_d3(correction_mat, rhomb_reverse);
debug_print("R-center reverse setting\n");
debug_print_matrix_d3(trans_corr_mat);
}
break;
case 4:
centering = FACE;
break;
default:
centering = CENTERING_ERROR;
break;
}
return centering;
}
static Centering get_base_center(SPGCONST int transform_mat[3][3])
{
int i;
Centering centering = PRIMITIVE;
debug_print("lat_get_base_center\n");
/* C center */
for (i = 0; i < 3; i++) {
if (transform_mat[i][0] == 0 &&
transform_mat[i][1] == 0 &&
abs(transform_mat[i][2]) == 1) {
centering = C_FACE;
goto end;
}
}
/* A center */
for (i = 0; i < 3; i++) {
if (abs(transform_mat[i][0]) == 1 &&
transform_mat[i][1] == 0 &&
transform_mat[i][2] == 0) {
centering = A_FACE;
goto end;
}
}
/* B center */
for (i = 0; i < 3; i++) {
if (transform_mat[i][0] == 0 &&
abs(transform_mat[i][1]) == 1 &&
transform_mat[i][2] == 0) {
centering = B_FACE;
goto end;
}
}
/* body center */
if ((abs(transform_mat[0][0]) +
abs(transform_mat[0][1]) +
abs(transform_mat[0][2]) == 2) &&
(abs(transform_mat[1][0]) +
abs(transform_mat[1][1]) +
abs(transform_mat[1][2]) == 2) &&
(abs(transform_mat[2][0]) +
abs(transform_mat[2][1]) +
abs(transform_mat[2][2]) == 2)) {
centering = BODY;
goto end;
}
/* This should not happen. */
warning_print("spglib: No centring was found (line %d, %s).\n", __LINE__, __FILE__);
return PRIMITIVE;
end:
return centering;
}
static int get_centering_shifts(double shift[3][3],
const Centering centering)
{
int i, j, multi;
multi = 1;
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
shift[i][j] = 0;
}
}
if (centering != PRIMITIVE) {
if (centering != FACE && centering != R_CENTER) {
for (i = 0; i < 3; i++) { shift[0][i] = 0.5; } /* BASE */
if (centering == A_FACE) { shift[0][0] = 0; }
if (centering == B_FACE) { shift[0][1] = 0; }
if (centering == C_FACE) { shift[0][2] = 0; }
multi = 2;
}
if (centering == R_CENTER) {
shift[0][0] = 2. / 3;
shift[0][1] = 1. / 3;
shift[0][2] = 1. / 3;
shift[1][0] = 1. / 3;
shift[1][1] = 2. / 3;
shift[1][2] = 2. / 3;
multi = 3;
}
if (centering == FACE) {
shift[0][0] = 0;
shift[0][1] = 0.5;
shift[0][2] = 0.5;
shift[1][0] = 0.5;
shift[1][1] = 0;
shift[1][2] = 0.5;
shift[2][0] = 0.5;
shift[2][1] = 0.5;
shift[2][2] = 0;
multi = 4;
}
}
return multi;
}