static Cell * get_cell_with_smallest_lattice( SPGCONST Cell * cell, const double symprec ) { int i, j; double min_lat[3][3], trans_mat[3][3], inv_lat[3][3]; Cell * smallest_cell; if ( lat_smallest_lattice_vector( min_lat, cell->lattice, symprec ) ) { mat_inverse_matrix_d3( inv_lat, min_lat, 0 ); mat_multiply_matrix_d3( trans_mat, inv_lat, cell->lattice ); smallest_cell = cel_alloc_cell( cell->size ); mat_copy_matrix_d3( smallest_cell->lattice, min_lat ); for ( i = 0; i < cell->size; i++ ) { smallest_cell->types[i] = cell->types[i]; mat_multiply_matrix_vector_d3( smallest_cell->position[i], trans_mat, cell->position[i] ); for ( j = 0; j < 3; j++ ) { cell->position[i][j] -= mat_Nint( cell->position[i][j] ); } } } else { smallest_cell = cel_alloc_cell( -1 ); } return smallest_cell; }
/* Return NULL if failed */ static VecDBL * translate_atoms_in_trimmed_lattice(const Cell * cell, SPGCONST double trimmed_lattice[3][3]) { int i, j; double tmp_matrix[3][3], axis_inv[3][3]; VecDBL * position; position = NULL; if ((position = mat_alloc_VecDBL(cell->size)) == NULL) { return NULL; } mat_inverse_matrix_d3(tmp_matrix, trimmed_lattice, 0); mat_multiply_matrix_d3(axis_inv, tmp_matrix, cell->lattice); /* Send atoms into the trimmed cell */ for (i = 0; i < cell->size; i++) { mat_multiply_matrix_vector_d3(position->vec[i], axis_inv, cell->position[i]); for (j = 0; j < 3; j++) { position->vec[i][j] = mat_Dmod1(position->vec[i][j]); } } return position; }
/* Acta Cryst. (2002). A58, 60-65 */ static void get_exact_location(double position[3], SPGCONST Symmetry * conv_sym, SPGCONST double bravais_lattice[3][3], const double symprec) { int i, j, k, num_sum; double sum_rot[3][3]; double pos[3], sum_trans[3]; debug_print("get_exact_location\n"); num_sum = 0; for (i = 0; i < 3; i++) { sum_trans[i] = 0.0; for (j = 0; j < 3; j++) { sum_rot[i][j] = 0; } } for (i = 0; i < conv_sym->size; i++) { mat_multiply_matrix_vector_id3(pos, conv_sym->rot[i], position); for (j = 0; j < 3; j++) { pos[j] += conv_sym->trans[i][j]; } if (cel_is_overlap(pos, position, bravais_lattice, symprec)) { for (j = 0; j < 3; j++) { sum_trans[j] += conv_sym->trans[i][j] - mat_Nint(pos[j] - position[j]); for (k = 0; k < 3; k++) { sum_rot[j][k] += conv_sym->rot[i][j][k]; } } num_sum++; } } for (i = 0; i < 3; i++) { sum_trans[i] /= num_sum; for (j = 0; j < 3; j++) { sum_rot[i][j] /= num_sum; } } /* (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); for (i = 0; i < 3; i++) { position[i] += sum_trans[i]; } }
static VecDBL * get_positions_primitive( SPGCONST Cell * cell, SPGCONST double prim_lat[3][3] ) { int i, j; double tmp_matrix[3][3], axis_inv[3][3]; VecDBL * position; position = mat_alloc_VecDBL( cell->size ); mat_inverse_matrix_d3(tmp_matrix, prim_lat, 0); mat_multiply_matrix_d3(axis_inv, tmp_matrix, cell->lattice); /* Send atoms into the primitive cell */ debug_print("Positions in new axes reduced to primitive cell\n"); for (i = 0; i < cell->size; i++) { mat_multiply_matrix_vector_d3( position->vec[i], axis_inv, cell->position[i] ); for (j = 0; j < 3; j++) { position->vec[i][j] -= mat_Nint( position->vec[i][j] ); } debug_print("%d: %f %f %f\n", i + 1, position->vec[i][0], position->vec[i][1], position->vec[i][2]); } return position; }
/* Return NULL if failed */ static VecDBL * get_positions_primitive(SPGCONST Cell * cell, SPGCONST double prim_lat[3][3]) { int i, j; double tmp_matrix[3][3], axis_inv[3][3]; VecDBL * position; position = NULL; if ((position = mat_alloc_VecDBL(cell->size)) == NULL) { return NULL; } mat_inverse_matrix_d3(tmp_matrix, prim_lat, 0); mat_multiply_matrix_d3(axis_inv, tmp_matrix, cell->lattice); /* Send atoms into the primitive cell */ for (i = 0; i < cell->size; i++) { mat_multiply_matrix_vector_d3(position->vec[i], axis_inv, cell->position[i]); for (j = 0; j < 3; j++) { position->vec[i][j] -= mat_Nint(position->vec[i][j]); } } return position; }
static Cell * get_conventional_primitive(SPGCONST Spacegroup * spacegroup, SPGCONST Cell * primitive) { int i, j; double inv_brv[3][3], trans_mat[3][3]; Cell * conv_prim; conv_prim = cel_alloc_cell(primitive->size); mat_inverse_matrix_d3(inv_brv, spacegroup->bravais_lattice, 0); mat_multiply_matrix_d3(trans_mat, inv_brv, primitive->lattice); for (i = 0; i < primitive->size; i++) { conv_prim->types[i] = primitive->types[i]; mat_multiply_matrix_vector_d3(conv_prim->position[i], trans_mat, primitive->position[i]); for (j = 0; j < 3; j++) { conv_prim->position[i][j] -= spacegroup->origin_shift[j]; conv_prim->position[i][j] -= mat_Nint(conv_prim->position[i][j]); } } return conv_prim; }
static int get_operation_supercell( int rot[][3][3], double trans[][3], const int num_sym, const VecDBL * pure_trans, SPGCONST Cell *cell, SPGCONST Cell *primitive ) { int i, j, k, multi; double inv_prim_lat[3][3], drot[3][3], trans_mat[3][3], trans_mat_inv[3][3]; MatINT *rot_prim; VecDBL *trans_prim; rot_prim = mat_alloc_MatINT( num_sym ); trans_prim = mat_alloc_VecDBL( num_sym ); multi = pure_trans->size; debug_print("get_operation_supercell\n"); mat_inverse_matrix_d3( inv_prim_lat, primitive->lattice, 0 ); mat_multiply_matrix_d3( trans_mat, inv_prim_lat, cell->lattice ); mat_inverse_matrix_d3( trans_mat_inv, trans_mat, 0 ); for( i = 0; i < num_sym; i++) { /* Translations */ mat_multiply_matrix_vector_d3( trans[i], trans_mat_inv, trans[i] ); /* Rotations */ mat_cast_matrix_3i_to_3d( drot, rot[i] ); mat_get_similar_matrix_d3( drot, drot, trans_mat, 0 ); mat_cast_matrix_3d_to_3i( rot[i], drot ); } for( i = 0; i < num_sym; i++ ) { mat_copy_matrix_i3( rot_prim->mat[i], rot[i] ); for( j = 0; j < 3; j++ ) trans_prim->vec[i][j] = trans[i][j]; } /* Rotations and translations are copied with the set of */ /* pure translations. */ for( i = 0; i < num_sym; i++ ) { for( j = 0; j < multi; j++ ) { mat_copy_matrix_i3( rot[ i * multi + j ], rot_prim->mat[i] ); for ( k = 0; k < 3; k++ ) { trans[i * multi + j][k] = mat_Dmod1( trans_prim->vec[i][k] + pure_trans->vec[j][k] ); } } } mat_free_MatINT( rot_prim ); mat_free_VecDBL( trans_prim ); /* return number of symmetry operation of supercell */ return num_sym * multi; }
static int is_overlap_all_atoms(const double trans[3], SPGCONST int rot[3][3], SPGCONST Cell * cell, const double symprec, const int is_identity) { int i, j, k, is_found; double symprec2; double pos_rot[3], d[3]; symprec2 = symprec*symprec; for (i = 0; i < cell->size; i++) { if (is_identity) { /* Identity matrix is treated as special for speed. */ for (j = 0; j < 3; j++) { pos_rot[j] = cell->position[i][j] + trans[j]; } } else { mat_multiply_matrix_vector_id3(pos_rot, rot, cell->position[i]); for (j = 0; j < 3; j++) { pos_rot[j] += trans[j]; } } is_found = 0; for (j = 0; j < cell->size; j++) { if (cell->types[i] == cell->types[j]) { /* here cel_is_overlap can be used, but for the tuning */ /* purpose, write it again */ for (k = 0; k < 3; k++) { d[k] = pos_rot[k] - cell->position[j][k]; d[k] -= mat_Nint(d[k]); } mat_multiply_matrix_vector_d3(d, cell->lattice, d); if (d[0]*d[0]+d[1]*d[1]+d[2]*d[2] < symprec2) { is_found = 1; break; } } } if (! is_found) { goto not_found; } } return 1; /* found */ not_found: return 0; }
static Symmetry * get_primitive_db_symmetry(SPGCONST double t_mat[3][3], const Symmetry *conv_sym, const double symprec) { int i, j, num_op; double inv_mat[3][3], tmp_mat[3][3]; MatINT *r_prim; VecDBL *t_prim; Symmetry *prim_sym; r_prim = mat_alloc_MatINT(conv_sym->size); t_prim = mat_alloc_VecDBL(conv_sym->size); mat_inverse_matrix_d3(inv_mat, t_mat, symprec); num_op = 0; for (i = 0; i < conv_sym->size; i++) { for (j = 0; j < i; j++) { if (mat_check_identity_matrix_i3(conv_sym->rot[i], conv_sym->rot[j])) { goto pass; } } /* R' = T*R*T^-1 */ mat_multiply_matrix_di3(tmp_mat, t_mat, conv_sym->rot[i]); mat_multiply_matrix_d3(tmp_mat, tmp_mat, inv_mat); mat_cast_matrix_3d_to_3i(r_prim->mat[ num_op ], tmp_mat); /* t' = T*t */ mat_multiply_matrix_vector_d3(t_prim->vec[ num_op ], t_mat, conv_sym->trans[ i ]); num_op++; pass: ; } prim_sym = sym_alloc_symmetry(num_op); for (i = 0; i < num_op; i++) { mat_copy_matrix_i3(prim_sym->rot[i], r_prim->mat[i]); for (j = 0; j < 3; j++) { prim_sym->trans[i][j] = t_prim->vec[i][j] - mat_Nint(t_prim->vec[i][j]); } } mat_free_MatINT(r_prim); mat_free_VecDBL(t_prim); return prim_sym; }
static Symmetry * recover_operations_original(SPGCONST Symmetry *symmetry, const VecDBL * pure_trans, SPGCONST Cell *cell, SPGCONST Cell *primitive) { int i, j, k, multi; double inv_prim_lat[3][3], drot[3][3], trans_mat[3][3], trans_mat_inv[3][3]; Symmetry *symmetry_orig, *sym_tmp; debug_print("recover_operations_original:\n"); multi = pure_trans->size; sym_tmp = sym_alloc_symmetry(symmetry->size); symmetry_orig = sym_alloc_symmetry(symmetry->size * multi); mat_inverse_matrix_d3(inv_prim_lat, primitive->lattice, 0); mat_multiply_matrix_d3(trans_mat, inv_prim_lat, cell->lattice); mat_inverse_matrix_d3(trans_mat_inv, trans_mat, 0); for(i = 0; i < symmetry->size; i++) { mat_copy_matrix_i3(sym_tmp->rot[i], symmetry->rot[i]); mat_copy_vector_d3(sym_tmp->trans[i], symmetry->trans[i]); } for(i = 0; i < symmetry->size; i++) { mat_cast_matrix_3i_to_3d(drot, sym_tmp->rot[i]); mat_get_similar_matrix_d3(drot, drot, trans_mat, 0); mat_cast_matrix_3d_to_3i(sym_tmp->rot[i], drot); mat_multiply_matrix_vector_d3(sym_tmp->trans[i], trans_mat_inv, sym_tmp->trans[i]); } for(i = 0; i < symmetry->size; i++) { for(j = 0; j < multi; j++) { mat_copy_matrix_i3(symmetry_orig->rot[i * multi + j], sym_tmp->rot[i]); for (k = 0; k < 3; k++) { symmetry_orig->trans[i * multi + j][k] = sym_tmp->trans[i][k] + pure_trans->vec[j][k]; } } } sym_free_symmetry(sym_tmp); return symmetry_orig; }
int cel_is_overlap( const double a[3], const double b[3], SPGCONST double lattice[3][3], const double symprec ) { int i; double v_diff[3]; for ( i = 0; i < 3; i++ ) { v_diff[i] = a[i] - b[i]; v_diff[i] -= mat_Nint( v_diff[i] ); } mat_multiply_matrix_vector_d3( v_diff, lattice, v_diff ); if ( mat_norm_squared_d3( v_diff ) < symprec*symprec ) { return 1; } else { return 0; } }
/* Return NULL if failed */ static Cell * get_cell_with_smallest_lattice(SPGCONST Cell * cell, const double symprec) { int i, j; double min_lat[3][3], trans_mat[3][3], inv_lat[3][3]; Cell * smallest_cell; debug_print("get_cell_with_smallest_lattice:\n"); smallest_cell = NULL; if (!lat_smallest_lattice_vector(min_lat, cell->lattice, symprec)) { goto err; } mat_inverse_matrix_d3(inv_lat, min_lat, 0); mat_multiply_matrix_d3(trans_mat, inv_lat, cell->lattice); if ((smallest_cell = cel_alloc_cell(cell->size)) == NULL) { goto err; } mat_copy_matrix_d3(smallest_cell->lattice, min_lat); for (i = 0; i < cell->size; i++) { smallest_cell->types[i] = cell->types[i]; mat_multiply_matrix_vector_d3(smallest_cell->position[i], trans_mat, cell->position[i]); for (j = 0; j < 3; j++) { cell->position[i][j] = mat_Dmod1(cell->position[i][j]); } } return smallest_cell; err: return NULL; }
/* Return NULL if failed */ static VecDBL * get_lattice_translations(const int frame[3], SPGCONST double inv_tmat[3][3]) { int i, j, k, l, num_trans; VecDBL * lattice_trans; lattice_trans = NULL; if ((lattice_trans = mat_alloc_VecDBL(frame[0] * frame[1] * frame[2])) == NULL) { return NULL; } num_trans = 0; for (i = 0; i < frame[0]; i++) { for (j = 0; j < frame[1]; j++) { for (k = 0; k < frame[2]; k++) { lattice_trans->vec[num_trans][0] = i; lattice_trans->vec[num_trans][1] = j; lattice_trans->vec[num_trans][2] = k; /* t' = T^-1*t */ mat_multiply_matrix_vector_d3(lattice_trans->vec[num_trans], inv_tmat, lattice_trans->vec[num_trans]); for (l = 0; l < 3; l++) { lattice_trans->vec[num_trans][l] = mat_Dmod1(lattice_trans->vec[num_trans][l]); } num_trans++; } } } return lattice_trans; }
static int get_ir_reciprocal_mesh(int grid_address[][3], int map[], const int mesh[3], const int is_shift[3], const MatINT *rot_reciprocal) { /* In the following loop, mesh is doubled. */ /* Even and odd mesh numbers correspond to */ /* is_shift[i] are 0 or 1, respectively. */ /* is_shift = [0,0,0] gives Gamma center mesh. */ /* grid: reducible grid points */ /* map: the mapping from each point to ir-point. */ int i, j, k, l, grid_point, grid_point_rot, num_ir = 0; int address[3], address_double[3], address_double_rot[3]; /* "-1" means the element is not touched yet. */ for (i = 0; i < mesh[0] * mesh[1] * mesh[2]; i++) { map[i] = -1; } #ifndef GRID_ORDER_XYZ for (i = 0; i < mesh[2]; i++) { for (j = 0; j < mesh[1]; j++) { for (k = 0; k < mesh[0]; k++) { address[0] = k; address[1] = j; address[2] = i; #else for (i = 0; i < mesh[0]; i++) { for (j = 0; j < mesh[1]; j++) { for (k = 0; k < mesh[2]; k++) { address[0] = i; address[1] = j; address[2] = k; #endif for (l = 0; l < 3; l++) { address_double[l] = address[l] * 2 + is_shift[l]; } grid_point = get_grid_point_double_mesh(address_double, mesh); reduce_grid_address(grid_address[grid_point], address, mesh); for (l = 0; l < rot_reciprocal->size; l++) { mat_multiply_matrix_vector_i3(address_double_rot, rot_reciprocal->mat[l], address_double); grid_point_rot = get_grid_point_double_mesh(address_double_rot, mesh); if (grid_point_rot > -1) { /* Invalid if even --> odd or odd --> even */ if (map[grid_point_rot] > -1) { map[grid_point] = map[grid_point_rot]; break; } } } if (map[grid_point] == -1) { map[grid_point] = grid_point; num_ir++; } } } } return num_ir; } static int get_ir_reciprocal_mesh_openmp(int grid_address[][3], int map[], const int mesh[3], const int is_shift[3], const MatINT * rot_reciprocal) { int i, j, grid_point, grid_point_rot, num_ir; int address[3], address_double[3], address_double_rot[3]; #pragma omp parallel for private(j, grid_point, grid_point_rot, address, address_double, address_double_rot) for (i = 0; i < mesh[0] * mesh[1] * mesh[2]; i++) { #ifndef GRID_ORDER_XYZ /* address[2] * mesh[0] * mesh[1] + address[1] * mesh[0] + address[0]; */ address[0] = i % mesh[0]; address[2] = i / (mesh[0] * mesh[1]); address[1] = (i - address[2] * mesh[0] * mesh[1]) / mesh[0]; #else /* address[0] * mesh[1] * mesh[2] + address[1] * mesh[2] + address[2]; */ address[2] = i % mesh[2]; address[0] = i / (mesh[1] * mesh[2]); address[1] = (i - address[0] * mesh[1] * mesh[2]) / mesh[2]; #endif for (j = 0; j < 3; j++) { address_double[j] = address[j] * 2 + is_shift[j]; } grid_point = get_grid_point_double_mesh(address_double, mesh); map[grid_point] = grid_point; reduce_grid_address(grid_address[grid_point], address, mesh); for (j = 0; j < rot_reciprocal->size; j++) { mat_multiply_matrix_vector_i3(address_double_rot, rot_reciprocal->mat[j], address_double); grid_point_rot = get_grid_point_double_mesh(address_double_rot, mesh); if (grid_point_rot > -1) { /* Invalid if even --> odd or odd --> even */ if (grid_point_rot < map[grid_point]) { map[grid_point] = grid_point_rot; } } } } num_ir = 0; #pragma omp parallel for reduction(+:num_ir) for (i = 0; i < mesh[0] * mesh[1] * mesh[2]; i++) { if (map[i] == i) { num_ir++; } } return num_ir; } /* Relocate grid addresses to first Brillouin zone */ /* bz_grid_address[prod(mesh + 1)][3] */ /* bz_map[prod(mesh * 2)] */ static int relocate_BZ_grid_address(int bz_grid_address[][3], int bz_map[], SPGCONST int grid_address[][3], const int mesh[3], SPGCONST double rec_lattice[3][3], const int is_shift[3]) { double tolerance, min_distance; double q_vector[3], distance[KPT_NUM_BZ_SEARCH_SPACE]; int bzmesh[3], bz_address_double[3]; int i, j, k, min_index, boundary_num_gp, total_num_gp, bzgp, gp; tolerance = get_tolerance_for_BZ_reduction(rec_lattice, mesh); for (i = 0; i < 3; i++) { bzmesh[i] = mesh[i] * 2; } for (i = 0; i < bzmesh[0] * bzmesh[1] * bzmesh[2]; i++) { bz_map[i] = -1; } boundary_num_gp = 0; total_num_gp = mesh[0] * mesh[1] * mesh[2]; for (i = 0; i < total_num_gp; i++) { for (j = 0; j < KPT_NUM_BZ_SEARCH_SPACE; j++) { for (k = 0; k < 3; k++) { q_vector[k] = ((grid_address[i][k] + kpt_bz_search_space[j][k] * mesh[k]) * 2 + is_shift[k]) / ((double)mesh[k]) / 2; } mat_multiply_matrix_vector_d3(q_vector, rec_lattice, q_vector); distance[j] = mat_norm_squared_d3(q_vector); } min_distance = distance[0]; min_index = 0; for (j = 1; j < KPT_NUM_BZ_SEARCH_SPACE; j++) { if (distance[j] < min_distance) { min_distance = distance[j]; min_index = j; } } for (j = 0; j < KPT_NUM_BZ_SEARCH_SPACE; j++) { if (distance[j] < min_distance + tolerance) { if (j == min_index) { gp = i; } else { gp = boundary_num_gp + total_num_gp; } for (k = 0; k < 3; k++) { bz_grid_address[gp][k] = grid_address[i][k] + kpt_bz_search_space[j][k] * mesh[k]; bz_address_double[k] = bz_grid_address[gp][k] * 2 + is_shift[k]; } bzgp = get_grid_point_double_mesh(bz_address_double, bzmesh); bz_map[bzgp] = gp; if (j != min_index) { boundary_num_gp++; } } } } return boundary_num_gp + total_num_gp; } static double get_tolerance_for_BZ_reduction(SPGCONST double rec_lattice[3][3], const int mesh[3]) { int i, j; double tolerance; double length[3]; for (i = 0; i < 3; i++) { length[i] = 0; for (j = 0; j < 3; j++) { length[i] += rec_lattice[j][i] * rec_lattice[j][i]; } length[i] /= mesh[i] * mesh[i]; } tolerance = length[0]; for (i = 1; i < 3; i++) { if (tolerance < length[i]) { tolerance = length[i]; } } tolerance *= 0.01; return tolerance; }
/* Acta Cryst. (2002). A58, 60-65 */ static int set_exact_location(double position[3], const Symmetry * conv_sym, SPGCONST double bravais_lattice[3][3], const double symprec) { int i, j, k, num_sum, multi, num_pure_trans; double sum_rot[3][3]; double pos[3], sum_trans[3]; debug_print("get_exact_location\n"); num_sum = 0; for (i = 0; i < 3; i++) { sum_trans[i] = 0.0; for (j = 0; j < 3; j++) { sum_rot[i][j] = 0; } } num_pure_trans = 0; for (i = 0; i < conv_sym->size; i++) { if (mat_check_identity_matrix_i3(identity, conv_sym->rot[i])) { num_pure_trans++; for (j = 0; j < 3; j++) { pos[j] = position[j]; } } else { mat_multiply_matrix_vector_id3(pos, conv_sym->rot[i], position); } for (j = 0; j < 3; j++) { pos[j] += conv_sym->trans[i][j]; } if (cel_is_overlap(pos, position, bravais_lattice, symprec)) { 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]); } num_sum++; } } for (i = 0; i < 3; i++) { sum_trans[i] /= num_sum; for (j = 0; j < 3; j++) { sum_rot[i][j] /= num_sum; } } /* (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); for (i = 0; i < 3; i++) { position[i] += sum_trans[i]; } multi = conv_sym->size / num_pure_trans / num_sum; if (multi * num_sum * num_pure_trans == conv_sym->size) { return multi; } else { return 0; } }
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 tmp_trans; double tmp_matrix_d3[3][3], shift[4][3]; double symmetry_rot_d3[3][3], primitive_sym_rot_d3[3][3]; Symmetry *symmetry; size = primitive_sym->size; if (centering == FACE) { symmetry = sym_alloc_symmetry(size * 4); } else { if (centering) { symmetry = sym_alloc_symmetry(size * 2); } else { symmetry = sym_alloc_symmetry(size); } } 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(tmp_matrix_d3, transform_mat, 0); mat_multiply_matrix_vector_d3(symmetry->trans[i], tmp_matrix_d3, primitive_sym->trans[i]); } multi = 1; if (centering) { if (! (centering == FACE)) { for (i = 0; i < 3; i++) { shift[0][i] = 0.5; } 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 == 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; } 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++) { tmp_trans = symmetry->trans[j][k] + shift[i][k]; symmetry->trans[(i+1) * size + j][k] = tmp_trans; } } } } /* Reduce translations into -0 < trans < 1.0 */ for (i = 0; i < multi; i++) { for (j = 0; j < size; j++) { for (k = 0; k < 3; k++) { tmp_trans = symmetry->trans[i * size + j][k]; tmp_trans -= mat_Nint(tmp_trans); if (tmp_trans < 0) { tmp_trans += 1.0; } symmetry->trans[i * size + j][k] = tmp_trans; } } } return symmetry; }
/* bz_map[prod(mesh * 2)] */ static int relocate_BZ_grid_address(int bz_grid_address[][3], int bz_map[], SPGCONST int grid_address[][3], const int mesh[3], SPGCONST double rec_lattice[3][3], const int is_shift[3]) { double tolerance, min_distance; double q_vector[3], distance[KPT_NUM_BZ_SEARCH_SPACE]; int bzmesh[3], bz_address_double[3]; int i, j, k, min_index, boundary_num_gp, total_num_gp, bzgp, gp; tolerance = get_tolerance_for_BZ_reduction(rec_lattice, mesh); for (i = 0; i < 3; i++) { bzmesh[i] = mesh[i] * 2; } for (i = 0; i < bzmesh[0] * bzmesh[1] * bzmesh[2]; i++) { bz_map[i] = -1; } boundary_num_gp = 0; total_num_gp = mesh[0] * mesh[1] * mesh[2]; /* Multithreading doesn't work for this loop since gp calculated */ /* with boundary_num_gp is unstable to store bz_grid_address. */ for (i = 0; i < total_num_gp; i++) { for (j = 0; j < KPT_NUM_BZ_SEARCH_SPACE; j++) { for (k = 0; k < 3; k++) { q_vector[k] = ((grid_address[i][k] + bz_search_space[j][k] * mesh[k]) * 2 + is_shift[k]) / ((double)mesh[k]) / 2; } mat_multiply_matrix_vector_d3(q_vector, rec_lattice, q_vector); distance[j] = mat_norm_squared_d3(q_vector); } min_distance = distance[0]; min_index = 0; for (j = 1; j < KPT_NUM_BZ_SEARCH_SPACE; j++) { if (distance[j] < min_distance) { min_distance = distance[j]; min_index = j; } } for (j = 0; j < KPT_NUM_BZ_SEARCH_SPACE; j++) { if (distance[j] < min_distance + tolerance) { if (j == min_index) { gp = i; } else { gp = boundary_num_gp + total_num_gp; } for (k = 0; k < 3; k++) { bz_grid_address[gp][k] = grid_address[i][k] + bz_search_space[j][k] * mesh[k]; bz_address_double[k] = bz_grid_address[gp][k] * 2 + is_shift[k]; } bzgp = kgd_get_grid_point_double_mesh(bz_address_double, bzmesh); bz_map[bzgp] = gp; if (j != min_index) { boundary_num_gp++; } } } } return boundary_num_gp + total_num_gp; }
static Symmetry * reduce_symmetry_in_frame(const int frame[3], SPGCONST Symmetry *prim_sym, SPGCONST int t_mat[3][3], SPGCONST double lattice[3][3], const int multiplicity, const double symprec) { int i, j, k, l, num_trans, size_sym_orig; Symmetry *symmetry, *t_sym; double inv_tmat[3][3], tmp_mat[3][3], tmp_rot_d[3][3], tmp_lat_d[3][3], tmp_lat_i[3][3]; int tmp_rot_i[3][3]; VecDBL *pure_trans, *lattice_trans; mat_cast_matrix_3i_to_3d(tmp_mat, t_mat); mat_inverse_matrix_d3(inv_tmat, tmp_mat, symprec); /* transformed lattice points */ lattice_trans = mat_alloc_VecDBL(frame[0]*frame[1]*frame[2]); num_trans = 0; for (i = 0; i < frame[0]; i++) { for (j = 0; j < frame[1]; j++) { for (k = 0; k < frame[2]; k++) { lattice_trans->vec[num_trans][0] = i; lattice_trans->vec[num_trans][1] = j; lattice_trans->vec[num_trans][2] = k; mat_multiply_matrix_vector_d3(lattice_trans->vec[num_trans], inv_tmat, lattice_trans->vec[num_trans]); for (l = 0; l < 3; l++) { /* t' = T^-1*t */ lattice_trans->vec[num_trans][l] = \ mat_Dmod1(lattice_trans->vec[num_trans][l]); } num_trans++; } } } /* transformed symmetry operations of primitive cell */ t_sym = sym_alloc_symmetry(prim_sym->size); size_sym_orig = 0; for (i = 0; i < prim_sym->size; i++) { /* R' = T^-1*R*T */ mat_multiply_matrix_di3(tmp_mat, inv_tmat, prim_sym->rot[i]); mat_multiply_matrix_di3(tmp_rot_d, tmp_mat, t_mat); mat_cast_matrix_3d_to_3i(tmp_rot_i, tmp_rot_d); mat_multiply_matrix_di3(tmp_lat_i, lattice, tmp_rot_i); mat_multiply_matrix_d3(tmp_lat_d, lattice, tmp_rot_d); if (mat_check_identity_matrix_d3(tmp_lat_i, tmp_lat_d, symprec)) { mat_copy_matrix_i3(t_sym->rot[size_sym_orig], tmp_rot_i); /* t' = T^-1*t */ mat_multiply_matrix_vector_d3(t_sym->trans[size_sym_orig], inv_tmat, prim_sym->trans[i]); size_sym_orig++; } } /* reduce lattice points */ pure_trans = reduce_lattice_points(lattice, lattice_trans, symprec); if (! (pure_trans->size == multiplicity)) { symmetry = sym_alloc_symmetry(0); goto ret; } /* copy symmetry operations upon lattice points */ symmetry = sym_alloc_symmetry(pure_trans->size * size_sym_orig); for (i = 0; i < pure_trans->size; i++) { for (j = 0; j < size_sym_orig; j++) { mat_copy_matrix_i3(symmetry->rot[size_sym_orig * i + j], t_sym->rot[j]); mat_copy_vector_d3(symmetry->trans[size_sym_orig * i + j], t_sym->trans[j]); for (k = 0; k < 3; k++) { symmetry->trans[size_sym_orig * i + j][k] += pure_trans->vec[i][k]; symmetry->trans[size_sym_orig * i + j][k] = \ mat_Dmod1(symmetry->trans[size_sym_orig * i + j][k]); } } } ret: mat_free_VecDBL(lattice_trans); mat_free_VecDBL(pure_trans); sym_free_symmetry(t_sym); return symmetry; }
static int get_ir_reciprocal_mesh(int grid_address[][3], int map[], const int mesh[3], const int is_shift[3], const MatINT *rot_reciprocal) { /* In the following loop, mesh is doubled. */ /* Even and odd mesh numbers correspond to */ /* is_shift[i] are 0 or 1, respectively. */ /* is_shift = [0,0,0] gives Gamma center mesh. */ /* grid: reducible grid points */ /* map: the mapping from each point to ir-point. */ int i, j, k, l, grid_point, grid_point_rot, num_ir = 0; int address_double[3], address_rot[3], mesh_double[3]; for (i = 0; i < 3; i++) { mesh_double[i] = mesh[i] * 2; } /* "-1" means the element is not touched yet. */ for (i = 0; i < mesh[0] * mesh[1] * mesh[2]; i++) { map[i] = -1; } #ifndef GRID_ORDER_XYZ for (i = 0; i < mesh[2]; i++) { for (j = 0; j < mesh[1]; j++) { for (k = 0; k < mesh[0]; k++) { address_double[0] = k * 2 + is_shift[0]; address_double[1] = j * 2 + is_shift[1]; address_double[2] = i * 2 + is_shift[2]; #else for (i = 0; i < mesh[0]; i++) { for (j = 0; j < mesh[1]; j++) { for (k = 0; k < mesh[2]; k++) { address_double[0] = i * 2 + is_shift[0]; address_double[1] = j * 2 + is_shift[1]; address_double[2] = k * 2 + is_shift[2]; #endif grid_point = get_grid_point_double_mesh(address_double, mesh); get_grid_address(grid_address[grid_point], address_double, mesh); for (l = 0; l < rot_reciprocal->size; l++) { mat_multiply_matrix_vector_i3(address_rot, rot_reciprocal->mat[l], address_double); get_vector_modulo(address_rot, mesh_double); grid_point_rot = get_grid_point_double_mesh(address_rot, mesh); if (grid_point_rot > -1) { /* Invalid if even --> odd or odd --> even */ if (map[grid_point_rot] > -1) { map[grid_point] = map[grid_point_rot]; break; } } } if (map[grid_point] == -1) { map[grid_point] = grid_point; num_ir++; } } } } return num_ir; } static int get_ir_reciprocal_mesh_openmp(int grid_address[][3], int map[], const int mesh[3], const int is_shift[3], const MatINT * rot_reciprocal) { int i, j, k, l, grid_point, grid_point_rot, num_ir; int address_double[3], address_rot[3], mesh_double[3]; for (i = 0; i < 3; i++) { mesh_double[i] = mesh[i] * 2; } #ifndef GRID_ORDER_XYZ #pragma omp parallel for private(j, k, l, grid_point, grid_point_rot, address_double, address_rot) for (i = 0; i < mesh[2]; i++) { for (j = 0; j < mesh[1]; j++) { for (k = 0; k < mesh[0]; k++) { address_double[0] = k * 2 + is_shift[0]; address_double[1] = j * 2 + is_shift[1]; address_double[2] = i * 2 + is_shift[2]; #else #pragma omp parallel for private(j, k, l, grid_point, grid_point_rot, address_double, address_rot) for (i = 0; i < mesh[0]; i++) { for (j = 0; j < mesh[1]; j++) { for (k = 0; k < mesh[2]; k++) { address_double[0] = i * 2 + is_shift[0]; address_double[1] = j * 2 + is_shift[1]; address_double[2] = k * 2 + is_shift[2]; #endif grid_point = get_grid_point_double_mesh(address_double, mesh); map[grid_point] = grid_point; get_grid_address(grid_address[grid_point], address_double, mesh); for (l = 0; l < rot_reciprocal->size; l++) { mat_multiply_matrix_vector_i3(address_rot, rot_reciprocal->mat[l], address_double); get_vector_modulo(address_rot, mesh_double); grid_point_rot = get_grid_point_double_mesh(address_rot, mesh); if (grid_point_rot > -1) { /* Invalid if even --> odd or odd --> even */ if (grid_point_rot < map[grid_point]) { map[grid_point] = grid_point_rot; } } } } } } num_ir = 0; #pragma omp parallel for reduction(+:num_ir) for (i = 0; i < mesh[0] * mesh[1] * mesh[2]; i++) { if (map[i] == i) { num_ir++; } } return num_ir; } /* Relocate grid addresses to first Brillouin zone */ /* bz_grid_address[prod(mesh + 1)][3] */ /* bz_map[prod(mesh * 2)] */ static int relocate_BZ_grid_address(int bz_grid_address[][3], int bz_map[], SPGCONST int grid_address[][3], const int mesh[3], SPGCONST double rec_lattice[3][3], const int is_shift[3]) { double tolerance, min_distance; double q_vector[3], distance[NUM_DIM_SEARCH]; int bzmesh[3], bzmesh_double[3], bz_address_double[3]; int i, j, k, min_index, boundary_num_gp, total_num_gp, bzgp, gp; tolerance = get_tolerance_for_BZ_reduction(rec_lattice, mesh); for (i = 0; i < 3; i++) { bzmesh[i] = mesh[i] * 2; bzmesh_double[i] = bzmesh[i] * 2; } for (i = 0; i < bzmesh[0] * bzmesh[1] * bzmesh[2]; i++) { bz_map[i] = -1; } boundary_num_gp = 0; total_num_gp = mesh[0] * mesh[1] * mesh[2]; for (i = 0; i < total_num_gp; i++) { for (j = 0; j < NUM_DIM_SEARCH; j++) { for (k = 0; k < 3; k++) { q_vector[k] = ((grid_address[i][k] + search_space[j][k] * mesh[k]) * 2 + is_shift[k]) / ((double)mesh[k]) / 2; } mat_multiply_matrix_vector_d3(q_vector, rec_lattice, q_vector); distance[j] = mat_norm_squared_d3(q_vector); } min_distance = distance[0]; min_index = 0; for (j = 1; j < NUM_DIM_SEARCH; j++) { if (distance[j] < min_distance) { min_distance = distance[j]; min_index = j; } } for (j = 0; j < NUM_DIM_SEARCH; j++) { if (distance[j] < min_distance + tolerance) { if (j == min_index) { gp = i; } else { gp = boundary_num_gp + total_num_gp; } for (k = 0; k < 3; k++) { bz_grid_address[gp][k] = grid_address[i][k] + search_space[j][k] * mesh[k]; bz_address_double[k] = bz_grid_address[gp][k] * 2 + is_shift[k]; } get_vector_modulo(bz_address_double, bzmesh_double); bzgp = get_grid_point_double_mesh(bz_address_double, bzmesh); bz_map[bzgp] = gp; if (j != min_index) { boundary_num_gp++; } } } } return boundary_num_gp + total_num_gp; } static double get_tolerance_for_BZ_reduction(SPGCONST double rec_lattice[3][3], const int mesh[3]) { int i, j; double tolerance; double length[3]; for (i = 0; i < 3; i++) { length[i] = 0; for (j = 0; j < 3; j++) { length[i] += rec_lattice[j][i] * rec_lattice[j][i]; } length[i] /= mesh[i] * mesh[i]; } tolerance = length[0]; for (i = 1; i < 3; i++) { if (tolerance < length[i]) { tolerance = length[i]; } } tolerance *= 0.01; return tolerance; } static int get_ir_triplets_at_q(int map_triplets[], int map_q[], int grid_address[][3], const int grid_point, const int mesh[3], const MatINT * rot_reciprocal) { int i, j, num_grid, q_2, num_ir_q, num_ir_triplets, ir_grid_point; int mesh_double[3], is_shift[3]; int address_double0[3], address_double1[3], address_double2[3]; int *ir_grid_points, *third_q; double tolerance; double stabilizer_q[1][3]; MatINT *rot_reciprocal_q; tolerance = 0.01 / (mesh[0] + mesh[1] + mesh[2]); num_grid = mesh[0] * mesh[1] * mesh[2]; for (i = 0; i < 3; i++) { /* Only consider the gamma-point */ is_shift[i] = 0; mesh_double[i] = mesh[i] * 2; } /* Search irreducible q-points (map_q) with a stabilizer */ /* q */ grid_point_to_address_double(address_double0, grid_point, mesh, is_shift); for (i = 0; i < 3; i++) { stabilizer_q[0][i] = (double)address_double0[i] / mesh_double[i] - (address_double0[i] > mesh[i]); } rot_reciprocal_q = get_point_group_reciprocal_with_q(rot_reciprocal, tolerance, 1, stabilizer_q); #ifdef _OPENMP num_ir_q = get_ir_reciprocal_mesh_openmp(grid_address, map_q, mesh, is_shift, rot_reciprocal_q); #else num_ir_q = get_ir_reciprocal_mesh(grid_address, map_q, mesh, is_shift, rot_reciprocal_q); #endif mat_free_MatINT(rot_reciprocal_q); third_q = (int*) malloc(sizeof(int) * num_ir_q); ir_grid_points = (int*) malloc(sizeof(int) * num_ir_q); num_ir_q = 0; for (i = 0; i < num_grid; i++) { if (map_q[i] == i) { ir_grid_points[num_ir_q] = i; num_ir_q++; } map_triplets[i] = -1; } #pragma omp parallel for private(j, address_double1, address_double2) for (i = 0; i < num_ir_q; i++) { grid_point_to_address_double(address_double1, ir_grid_points[i], mesh, is_shift); /* q' */ for (j = 0; j < 3; j++) { /* q'' */ address_double2[j] = - address_double0[j] - address_double1[j]; } get_vector_modulo(address_double2, mesh_double); third_q[i] = get_grid_point_double_mesh(address_double2, mesh); } num_ir_triplets = 0; for (i = 0; i < num_ir_q; i++) { ir_grid_point = ir_grid_points[i]; q_2 = third_q[i]; if (map_triplets[map_q[q_2]] > -1) { map_triplets[ir_grid_point] = map_q[q_2]; } else { map_triplets[ir_grid_point] = ir_grid_point; num_ir_triplets++; } } #pragma omp parallel for for (i = 0; i < num_grid; i++) { map_triplets[i] = map_triplets[map_q[i]]; } free(third_q); third_q = NULL; free(ir_grid_points); ir_grid_points = NULL; return num_ir_triplets; } static int get_BZ_triplets_at_q(int triplets[][3], const int grid_point, SPGCONST int bz_grid_address[][3], const int bz_map[], const int map_triplets[], const int num_map_triplets, const int mesh[3]) { int i, j, k, num_ir; int bz_address[3][3], bz_address_double[3], bzmesh[3], bzmesh_double[3]; int *ir_grid_points; for (i = 0; i < 3; i++) { bzmesh[i] = mesh[i] * 2; bzmesh_double[i] = bzmesh[i] * 2; } num_ir = 0; ir_grid_points = (int*) malloc(sizeof(int) * num_map_triplets); for (i = 0; i < num_map_triplets; i++) { if (map_triplets[i] == i) { ir_grid_points[num_ir] = i; num_ir++; } } #pragma omp parallel for private(j, k, bz_address, bz_address_double) for (i = 0; i < num_ir; i++) { for (j = 0; j < 3; j++) { bz_address[0][j] = bz_grid_address[grid_point][j]; bz_address[1][j] = bz_grid_address[ir_grid_points[i]][j]; bz_address[2][j] = - bz_address[0][j] - bz_address[1][j]; } for (j = 2; j > -1; j--) { if (get_third_q_of_triplets_at_q(bz_address, j, bz_map, mesh, bzmesh, bzmesh_double) == 0) { break; } } for (j = 0; j < 3; j++) { for (k = 0; k < 3; k++) { bz_address_double[k] = bz_address[j][k] * 2; } get_vector_modulo(bz_address_double, bzmesh_double); triplets[i][j] = bz_map[get_grid_point_double_mesh(bz_address_double, bzmesh)]; } } free(ir_grid_points); return num_ir; } static int get_third_q_of_triplets_at_q(int bz_address[3][3], const int q_index, const int bz_map[], const int mesh[3], const int bzmesh[3], const int bzmesh_double[3]) { int i, j, smallest_g, smallest_index, sum_g, delta_g[3]; int bzgp[NUM_DIM_SEARCH], bz_address_double[3]; get_vector_modulo(bz_address[q_index], mesh); for (i = 0; i < 3; i++) { delta_g[i] = 0; for (j = 0; j < 3; j++) { delta_g[i] += bz_address[j][i]; } delta_g[i] /= mesh[i]; } for (i = 0; i < NUM_DIM_SEARCH; i++) { for (j = 0; j < 3; j++) { bz_address_double[j] = (bz_address[q_index][j] + search_space[i][j] * mesh[j]) * 2; } for (j = 0; j < 3; j++) { if (bz_address_double[j] < 0) { bz_address_double[j] += bzmesh_double[j]; } } get_vector_modulo(bz_address_double, bzmesh_double); bzgp[i] = bz_map[get_grid_point_double_mesh(bz_address_double, bzmesh)]; } for (i = 0; i < NUM_DIM_SEARCH; i++) { if (bzgp[i] != -1) { goto escape; } } warning_print("******* Warning *******\n"); warning_print(" No third-q was found.\n"); warning_print("******* Warning *******\n"); escape: smallest_g = 4; smallest_index = 0; for (i = 0; i < NUM_DIM_SEARCH; i++) { if (bzgp[i] > -1) { /* q'' is in BZ */ sum_g = (abs(delta_g[0] + search_space[i][0]) + abs(delta_g[1] + search_space[i][1]) + abs(delta_g[2] + search_space[i][2])); if (sum_g < smallest_g) { smallest_index = i; smallest_g = sum_g; } } } for (i = 0; i < 3; i++) { bz_address[q_index][i] += search_space[smallest_index][i] * mesh[i]; } return smallest_g; }
/* Return NULL if failed */ static Symmetry * get_symmetry_in_original_cell(SPGCONST int t_mat[3][3], SPGCONST double inv_tmat[3][3], SPGCONST double lattice[3][3], SPGCONST Symmetry *prim_sym, const double symprec) { int i, size_sym_orig; double tmp_rot_d[3][3], tmp_lat_d[3][3], tmp_lat_i[3][3], tmp_mat[3][3]; int tmp_rot_i[3][3]; Symmetry *t_sym, *t_red_sym; t_sym = NULL; t_red_sym = NULL; if ((t_sym = sym_alloc_symmetry(prim_sym->size)) == NULL) { return NULL; } /* transform symmetry operations of primitive cell to those of original */ size_sym_orig = 0; for (i = 0; i < prim_sym->size; i++) { /* R' = T^-1*R*T */ mat_multiply_matrix_di3(tmp_mat, inv_tmat, prim_sym->rot[i]); mat_multiply_matrix_di3(tmp_rot_d, tmp_mat, t_mat); /* In spglib, symmetry of supercell is defined by the set of symmetry */ /* operations that are searched among supercell lattice point group */ /* operations. The supercell lattice may be made by breaking the */ /* unit cell lattice symmetry. In this case, a part of symmetry */ /* operations is discarded. */ mat_cast_matrix_3d_to_3i(tmp_rot_i, tmp_rot_d); mat_multiply_matrix_di3(tmp_lat_i, lattice, tmp_rot_i); mat_multiply_matrix_d3(tmp_lat_d, lattice, tmp_rot_d); if (mat_check_identity_matrix_d3(tmp_lat_i, tmp_lat_d, symprec)) { mat_copy_matrix_i3(t_sym->rot[size_sym_orig], tmp_rot_i); /* t' = T^-1*t */ mat_multiply_matrix_vector_d3(t_sym->trans[size_sym_orig], inv_tmat, prim_sym->trans[i]); size_sym_orig++; } } /* Broken symmetry due to supercell multiplicity */ if (size_sym_orig != prim_sym->size) { if ((t_red_sym = sym_alloc_symmetry(size_sym_orig)) == NULL) { sym_free_symmetry(t_sym); t_sym = NULL; return NULL; } for (i = 0; i < size_sym_orig; i++) { mat_copy_matrix_i3(t_red_sym->rot[i], t_sym->rot[i]); mat_copy_vector_d3(t_red_sym->trans[i], t_sym->trans[i]); } sym_free_symmetry(t_sym); t_sym = NULL; t_sym = t_red_sym; t_red_sym = NULL; } return t_sym; }
static Symmetry * reduce_symmetry_in_frame( const int frame[3], SPGCONST Symmetry *prim_sym, SPGCONST int t_mat[3][3], SPGCONST double lattice[3][3], const double symprec ) { int i, j, k, l, n, num_op, is_found; Symmetry *symmetry, *t_sym; double inv_mat[3][3], tmp_mat[3][3]; VecDBL *t, *lattice_trans; mat_cast_matrix_3i_to_3d( tmp_mat, t_mat ); mat_inverse_matrix_d3( inv_mat, tmp_mat, symprec ); /* transformed lattice points */ lattice_trans = mat_alloc_VecDBL( frame[0]*frame[1]*frame[2] ); n = 0; for ( i = 0; i < frame[0]; i++ ) { for ( j = 0; j < frame[1]; j++ ) { for ( k = 0; k < frame[2]; k++ ) { lattice_trans->vec[n][0] = i; lattice_trans->vec[n][1] = j; lattice_trans->vec[n][2] = k; mat_multiply_matrix_vector_d3( lattice_trans->vec[n], inv_mat, lattice_trans->vec[n] ); for ( l = 0; l < 3; l++ ) { /* t' = T^-1*t */ lattice_trans->vec[n][l] = mat_Dmod1( lattice_trans->vec[n][l] ); } n++; } } } /* transformed symmetry operations of primitive cell */ t_sym = sym_alloc_symmetry( prim_sym->size ); for ( i = 0; i < prim_sym->size; i++ ) { /* R' = T^-1*R*T */ mat_multiply_matrix_di3( tmp_mat, inv_mat, prim_sym->rot[i] ); mat_multiply_matrix_di3( tmp_mat, tmp_mat, t_mat ); mat_cast_matrix_3d_to_3i( t_sym->rot[i], tmp_mat ); /* t' = T^-1*t */ mat_multiply_matrix_vector_d3( t_sym->trans[i], inv_mat, prim_sym->trans[ i ] ); } /* reduce lattice points */ num_op = 0; t = mat_alloc_VecDBL( lattice_trans->size ); for ( i = 0; i < lattice_trans->size; i++ ) { is_found = 0; for ( j = 0; j < num_op; j++ ) { if ( cel_is_overlap( lattice_trans->vec[i], t->vec[j], lattice, symprec ) ) { is_found = 1; break; } } if ( ! is_found ) { mat_copy_vector_d3( t->vec[num_op], lattice_trans->vec[i] ); num_op++; } } /* copy symmetry operations upon lattice points */ symmetry = sym_alloc_symmetry( num_op * t_sym->size ); for ( i = 0; i < num_op; i++ ) { for ( j = 0; j < t_sym->size; j++ ) { mat_copy_matrix_i3( symmetry->rot[ t_sym->size * i + j ], t_sym->rot[j] ); mat_copy_vector_d3( symmetry->trans[ t_sym->size * i + j ], t_sym->trans[j] ); for ( k = 0; k < 3; k++ ) { symmetry->trans[ t_sym->size * i + j ][k] += t->vec[i][k]; symmetry->trans[ t_sym->size * i + j ][k] = \ mat_Dmod1( symmetry->trans[ t_sym->size * i + j ][k] ); } } } mat_free_VecDBL( t ); sym_free_symmetry( t_sym ); return symmetry; }
/* Return 0 if failed */ static int get_primitive_lattice_vectors(double prim_lattice[3][3], const VecDBL * vectors, SPGCONST Cell * cell, const double symprec) { int i, j, k, size; double initial_volume, volume; double relative_lattice[3][3], min_vectors[3][3], tmp_lattice[3][3]; double inv_mat_dbl[3][3]; int inv_mat_int[3][3]; debug_print("get_primitive_lattice_vectors:\n"); size = vectors->size; initial_volume = mat_Dabs(mat_get_determinant_d3(cell->lattice)); /* check volumes of all possible lattices, find smallest volume */ for (i = 0; i < size; i++) { for (j = i + 1; j < size; j++) { for (k = j + 1; k < size; k++) { mat_multiply_matrix_vector_d3(tmp_lattice[0], cell->lattice, vectors->vec[i]); mat_multiply_matrix_vector_d3(tmp_lattice[1], cell->lattice, vectors->vec[j]); mat_multiply_matrix_vector_d3(tmp_lattice[2], cell->lattice, vectors->vec[k]); volume = mat_Dabs(mat_get_determinant_d3(tmp_lattice)); if (volume > symprec) { if (mat_Nint(initial_volume / volume) == size-2) { mat_copy_vector_d3(min_vectors[0], vectors->vec[i]); mat_copy_vector_d3(min_vectors[1], vectors->vec[j]); mat_copy_vector_d3(min_vectors[2], vectors->vec[k]); goto ret; } } } } } /* Not found */ warning_print("spglib: Primitive lattice vectors cound not be found "); warning_print("(line %d, %s).\n", __LINE__, __FILE__); return 0; /* Found */ ret: for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { relative_lattice[j][i] = min_vectors[i][j]; } } mat_inverse_matrix_d3(inv_mat_dbl, relative_lattice, 0); mat_cast_matrix_3d_to_3i(inv_mat_int, inv_mat_dbl); if (abs(mat_get_determinant_i3(inv_mat_int)) == size-2) { mat_cast_matrix_3i_to_3d(inv_mat_dbl, inv_mat_int); mat_inverse_matrix_d3(relative_lattice, inv_mat_dbl, 0); } else { warning_print("spglib: Primitive lattice cleaning is incomplete "); warning_print("(line %d, %s).\n", __LINE__, __FILE__); } mat_multiply_matrix_d3(prim_lattice, cell->lattice, relative_lattice); return 1; }
/* 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[4][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) { 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; } 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; }