use crate::debug;
use crate::kgrid;
use crate::mathfunc::{
MatINT, mat_check_identity_matrix_i3, mat_dabs,
mat_multiply_matrix_i3, mat_multiply_matrix_vector_d3,
mat_multiply_matrix_vector_i3, mat_multiply_matrix_vector_id3, mat_nint, mat_norm_squared_d3,
mat_transpose_matrix_i3,
};
#[cfg(feature = "parallel")]
use rayon::prelude::*;
const KPT_NUM_BZ_SEARCH_SPACE: usize = 125;
static BZ_SEARCH_SPACE: [[i32; 3]; KPT_NUM_BZ_SEARCH_SPACE] = [
[0, 0, 0],
[0, 0, 1],
[0, 0, 2],
[0, 0, -2],
[0, 0, -1],
[0, 1, 0],
[0, 1, 1],
[0, 1, 2],
[0, 1, -2],
[0, 1, -1],
[0, 2, 0],
[0, 2, 1],
[0, 2, 2],
[0, 2, -2],
[0, 2, -1],
[0, -2, 0],
[0, -2, 1],
[0, -2, 2],
[0, -2, -2],
[0, -2, -1],
[0, -1, 0],
[0, -1, 1],
[0, -1, 2],
[0, -1, -2],
[0, -1, -1],
[1, 0, 0],
[1, 0, 1],
[1, 0, 2],
[1, 0, -2],
[1, 0, -1],
[1, 1, 0],
[1, 1, 1],
[1, 1, 2],
[1, 1, -2],
[1, 1, -1],
[1, 2, 0],
[1, 2, 1],
[1, 2, 2],
[1, 2, -2],
[1, 2, -1],
[1, -2, 0],
[1, -2, 1],
[1, -2, 2],
[1, -2, -2],
[1, -2, -1],
[1, -1, 0],
[1, -1, 1],
[1, -1, 2],
[1, -1, -2],
[1, -1, -1],
[2, 0, 0],
[2, 0, 1],
[2, 0, 2],
[2, 0, -2],
[2, 0, -1],
[2, 1, 0],
[2, 1, 1],
[2, 1, 2],
[2, 1, -2],
[2, 1, -1],
[2, 2, 0],
[2, 2, 1],
[2, 2, 2],
[2, 2, -2],
[2, 2, -1],
[2, -2, 0],
[2, -2, 1],
[2, -2, 2],
[2, -2, -2],
[2, -2, -1],
[2, -1, 0],
[2, -1, 1],
[2, -1, 2],
[2, -1, -2],
[2, -1, -1],
[-2, 0, 0],
[-2, 0, 1],
[-2, 0, 2],
[-2, 0, -2],
[-2, 0, -1],
[-2, 1, 0],
[-2, 1, 1],
[-2, 1, 2],
[-2, 1, -2],
[-2, 1, -1],
[-2, 2, 0],
[-2, 2, 1],
[-2, 2, 2],
[-2, 2, -2],
[-2, 2, -1],
[-2, -2, 0],
[-2, -2, 1],
[-2, -2, 2],
[-2, -2, -2],
[-2, -2, -1],
[-2, -1, 0],
[-2, -1, 1],
[-2, -1, 2],
[-2, -1, -2],
[-2, -1, -1],
[-1, 0, 0],
[-1, 0, 1],
[-1, 0, 2],
[-1, 0, -2],
[-1, 0, -1],
[-1, 1, 0],
[-1, 1, 1],
[-1, 1, 2],
[-1, 1, -2],
[-1, 1, -1],
[-1, 2, 0],
[-1, 2, 1],
[-1, 2, 2],
[-1, 2, -2],
[-1, 2, -1],
[-1, -2, 0],
[-1, -2, 1],
[-1, -2, 2],
[-1, -2, -2],
[-1, -2, -1],
[-1, -1, 0],
[-1, -1, 1],
[-1, -1, 2],
[-1, -1, -2],
[-1, -1, -1],
];
pub fn kpt_get_irreducible_reciprocal_mesh(
grid_address: &mut [[i32; 3]],
ir_mapping_table: &mut [usize],
mesh: &[i32; 3],
is_shift: &[i32; 3],
rot_reciprocal: &MatINT,
) -> usize {
kpt_get_dense_irreducible_reciprocal_mesh(
grid_address,
ir_mapping_table,
mesh,
is_shift,
rot_reciprocal,
)
}
pub fn kpt_get_dense_irreducible_reciprocal_mesh(
grid_address: &mut [[i32; 3]],
ir_mapping_table: &mut [usize],
mesh: &[i32; 3],
is_shift: &[i32; 3],
rot_reciprocal: &MatINT,
) -> usize {
get_dense_ir_reciprocal_mesh(
grid_address,
ir_mapping_table,
mesh,
is_shift,
rot_reciprocal,
)
}
pub fn kpt_get_stabilized_reciprocal_mesh(
grid_address: &mut [[i32; 3]],
ir_mapping_table: &mut [usize],
mesh: &[i32; 3],
is_shift: &[i32; 3],
is_time_reversal: i32,
rotations: &MatINT,
qpoints: &[[f64; 3]],
) -> usize {
kpt_get_dense_stabilized_reciprocal_mesh(
grid_address,
ir_mapping_table,
mesh,
is_shift,
is_time_reversal,
rotations,
qpoints,
)
}
pub fn kpt_get_dense_stabilized_reciprocal_mesh(
grid_address: &mut [[i32; 3]],
ir_mapping_table: &mut [usize],
mesh: &[i32; 3],
is_shift: &[i32; 3],
is_time_reversal: i32,
rotations: &MatINT,
qpoints: &[[f64; 3]],
) -> usize {
let rot_reciprocal = get_point_group_reciprocal(rotations, is_time_reversal)
.expect("Failed to allocate rot_reciprocal");
let tolerance = 0.01 / (mesh[0] + mesh[1] + mesh[2]) as f64;
let rot_reciprocal_q = get_point_group_reciprocal_with_q(&rot_reciprocal, tolerance, qpoints)
.expect("Failed to allocate rot_reciprocal_q");
let num_ir = get_dense_ir_reciprocal_mesh(
grid_address,
ir_mapping_table,
mesh,
is_shift,
&rot_reciprocal_q,
);
num_ir
}
pub fn kpt_relocate_bz_grid_address(
bz_grid_address: &mut [[i32; 3]],
bz_map: &mut [usize],
grid_address: &[[i32; 3]],
mesh: &[i32; 3],
rec_lattice: &[[f64; 3]; 3],
is_shift: &[i32; 3],
) -> usize {
let num_bz_map = (mesh[0] * mesh[1] * mesh[2]) as usize * 8;
let mut dense_bz_map = vec![0; num_bz_map];
let num_bzgp = relocate_dense_bz_grid_address(
bz_grid_address,
&mut dense_bz_map,
grid_address,
mesh,
rec_lattice,
is_shift,
);
for i in 0..num_bz_map {
if dense_bz_map[i] == num_bz_map {
bz_map[i] = usize::MAX; } else {
bz_map[i] = dense_bz_map[i];
}
}
num_bzgp
}
pub fn kpt_get_dense_grid_points_by_rotations(
rot_grid_points: &mut [usize],
address_orig: &[i32; 3],
rot_reciprocal: &MatINT,
mesh: &[i32; 3],
is_shift: &[i32; 3],
) {
let mut address_double_orig = [0i32; 3];
for i in 0..3 {
address_double_orig[i] = address_orig[i] * 2 + is_shift[i];
}
for i in 0..rot_reciprocal.size {
let address_double =
mat_multiply_matrix_vector_i3(&rot_reciprocal.mat[i], &address_double_orig);
rot_grid_points[i] =
kgrid::kgd_get_dense_grid_point_double_mesh(&address_double, mesh);
}
}
pub fn kpt_get_dense_BZ_grid_points_by_rotations(
rot_grid_points: &mut [usize],
address_orig: &[i32; 3],
rot_reciprocal: &MatINT,
mesh: &[i32; 3],
is_shift: &[i32; 3],
bz_map: &[usize],
) {
let mut address_double_orig = [0i32; 3];
let mut bzmesh = [0i32; 3];
for i in 0..3 {
bzmesh[i] = mesh[i] * 2;
address_double_orig[i] = address_orig[i] * 2 + is_shift[i];
}
for i in 0..rot_reciprocal.size {
let address_double =
mat_multiply_matrix_vector_i3(&rot_reciprocal.mat[i], &address_double_orig);
rot_grid_points[i] = bz_map[kgrid::kgd_get_dense_grid_point_double_mesh(
&address_double,
&bzmesh,
)];
}
}
pub fn kpt_get_point_group_reciprocal(
rotations: &MatINT,
is_time_reversal: i32,
) -> Option<MatINT> {
get_point_group_reciprocal(rotations, is_time_reversal)
}
pub fn kpt_get_point_group_reciprocal_with_q(
rot_reciprocal: &MatINT,
symprec: f64,
qpoints: &[[f64; 3]],
) -> Option<MatINT> {
get_point_group_reciprocal_with_q(rot_reciprocal, symprec, qpoints)
}
pub fn kpt_relocate_dense_BZ_grid_address(
bz_grid_address: &mut [[i32; 3]],
bz_map: &mut [usize],
grid_address: &[[i32; 3]],
mesh: &[i32; 3],
rec_lattice: &[[f64; 3]; 3],
is_shift: &[i32; 3],
) -> usize {
relocate_dense_bz_grid_address(bz_grid_address, bz_map, grid_address, mesh, rec_lattice, is_shift)
}
fn get_point_group_reciprocal(rotations: &MatINT, is_time_reversal: i32) -> Option<MatINT> {
let inversion = [[-1, 0, 0], [0, -1, 0], [0, 0, -1]];
let size = if is_time_reversal != 0 {
rotations.size * 2
} else {
rotations.size
};
let mut rot_reciprocal = MatINT::new(size);
let mut unique_rot = vec![-1; size];
for i in 0..rotations.size {
let t = mat_transpose_matrix_i3(&rotations.mat[i]);
rot_reciprocal.mat[i] = t;
if is_time_reversal != 0 {
let inv_rot = mat_multiply_matrix_i3(&inversion, &rot_reciprocal.mat[i]);
rot_reciprocal.mat[rotations.size + i] = inv_rot;
}
}
let mut num_rot = 0;
for i in 0..rot_reciprocal.size {
let mut is_unique = true;
for j in 0..num_rot {
if mat_check_identity_matrix_i3(
&rot_reciprocal.mat[unique_rot[j] as usize],
&rot_reciprocal.mat[i],
) {
is_unique = false;
break;
}
}
if is_unique {
unique_rot[num_rot] = i as i32;
num_rot += 1;
}
}
let mut rot_return = MatINT::new(num_rot);
for i in 0..num_rot {
rot_return.mat[i] = rot_reciprocal.mat[unique_rot[i] as usize];
}
Some(rot_return)
}
fn get_point_group_reciprocal_with_q(
rot_reciprocal: &MatINT,
symprec: f64,
qpoints: &[[f64; 3]],
) -> Option<MatINT> {
let mut ir_rot = vec![-1; rot_reciprocal.size];
let mut num_rot = 0;
for i in 0..rot_reciprocal.size {
let mut is_all_ok = true;
for j in 0..qpoints.len() {
let q_rot = mat_multiply_matrix_vector_id3(&rot_reciprocal.mat[i], &qpoints[j]);
let mut found_diff = false;
for k in 0..qpoints.len() {
let mut diff = [0.0; 3];
for l in 0..3 {
diff[l] = q_rot[l] - qpoints[k][l];
diff[l] -= mat_nint(diff[l]) as f64;
}
if mat_dabs(diff[0]) < symprec
&& mat_dabs(diff[1]) < symprec
&& mat_dabs(diff[2]) < symprec
{
found_diff = true;
break;
}
}
if !found_diff {
is_all_ok = false;
break;
}
}
if is_all_ok {
ir_rot[num_rot] = i as i32;
num_rot += 1;
}
}
let mut rot_reciprocal_q = MatINT::new(num_rot);
for i in 0..num_rot {
rot_reciprocal_q.mat[i] = rot_reciprocal.mat[ir_rot[i] as usize];
}
Some(rot_reciprocal_q)
}
fn get_dense_ir_reciprocal_mesh(
grid_address: &mut [[i32; 3]],
ir_mapping_table: &mut [usize],
mesh: &[i32; 3],
is_shift: &[i32; 3],
rot_reciprocal: &MatINT,
) -> usize {
if check_mesh_symmetry(mesh, is_shift, rot_reciprocal) {
get_dense_ir_reciprocal_mesh_normal(
grid_address,
ir_mapping_table,
mesh,
is_shift,
rot_reciprocal,
)
} else {
get_dense_ir_reciprocal_mesh_distortion(
grid_address,
ir_mapping_table,
mesh,
is_shift,
rot_reciprocal,
)
}
}
fn get_dense_ir_reciprocal_mesh_normal(
grid_address: &mut [[i32; 3]],
ir_mapping_table: &mut [usize],
mesh: &[i32; 3],
is_shift: &[i32; 3],
rot_reciprocal: &MatINT,
) -> usize {
kgrid::kgd_get_all_grid_addresses(grid_address, mesh);
let total_pts = (mesh[0] * mesh[1] * mesh[2]) as usize;
#[cfg(feature = "parallel")]
let iter = (0..total_pts).into_par_iter();
#[cfg(not(feature = "parallel"))]
let iter = 0..total_pts;
for i in 0..total_pts {
let mut address_double = [0; 3];
kgrid::kgd_get_grid_address_double_mesh(
&mut address_double,
&grid_address[i],
mesh,
is_shift,
);
ir_mapping_table[i] = i;
for j in 0..rot_reciprocal.size {
let address_double_rot =
mat_multiply_matrix_vector_i3(&rot_reciprocal.mat[j], &address_double);
let grid_point_rot =
kgrid::kgd_get_dense_grid_point_double_mesh(&address_double_rot, mesh);
if grid_point_rot < ir_mapping_table[i] {
ir_mapping_table[i] = grid_point_rot;
#[cfg(not(feature = "parallel"))]
break;
}
}
}
get_dense_num_ir(ir_mapping_table, mesh)
}
fn get_dense_ir_reciprocal_mesh_distortion(
grid_address: &mut [[i32; 3]],
ir_mapping_table: &mut [usize],
mesh: &[i32; 3],
is_shift: &[i32; 3],
rot_reciprocal: &MatINT,
) -> usize {
kgrid::kgd_get_all_grid_addresses(grid_address, mesh);
let divisor = [
(mesh[1] * mesh[2]) as i64,
(mesh[2] * mesh[0]) as i64,
(mesh[0] * mesh[1]) as i64,
];
let total_pts = (mesh[0] * mesh[1] * mesh[2]) as usize;
for i in 0..total_pts {
let mut address_double = [0; 3];
kgrid::kgd_get_grid_address_double_mesh(
&mut address_double,
&grid_address[i],
mesh,
is_shift,
);
let mut long_address_double = [0i64; 3];
for j in 0..3 {
long_address_double[j] = address_double[j] as i64 * divisor[j];
}
ir_mapping_table[i] = i;
for j in 0..rot_reciprocal.size {
let mut long_address_double_rot = [0i64; 3];
for k in 0..3 {
long_address_double_rot[k] = rot_reciprocal.mat[j][k][0] as i64
* long_address_double[0]
+ rot_reciprocal.mat[j][k][1] as i64 * long_address_double[1]
+ rot_reciprocal.mat[j][k][2] as i64 * long_address_double[2];
}
let mut indivisible = false;
let mut address_double_rot = [0; 3];
for k in 0..3 {
if long_address_double_rot[k] % divisor[k] != 0 {
indivisible = true;
break;
}
address_double_rot[k] = (long_address_double_rot[k] / divisor[k]) as i32;
if (address_double_rot[k] % 2 != 0 && is_shift[k] == 0)
|| (address_double_rot[k] % 2 == 0 && is_shift[k] == 1)
{
indivisible = true;
break;
}
}
if indivisible {
continue;
}
let grid_point_rot =
kgrid::kgd_get_dense_grid_point_double_mesh(&address_double_rot, mesh);
if grid_point_rot < ir_mapping_table[i] {
ir_mapping_table[i] = grid_point_rot;
#[cfg(not(feature = "parallel"))]
break;
}
}
}
get_dense_num_ir(ir_mapping_table, mesh)
}
fn get_dense_num_ir(ir_mapping_table: &mut [usize], mesh: &[i32; 3]) -> usize {
let total_pts = (mesh[0] * mesh[1] * mesh[2]) as usize;
let mut num_ir = 0;
for i in 0..total_pts {
if ir_mapping_table[i] == i {
num_ir += 1;
}
}
for i in 0..total_pts {
ir_mapping_table[i] = ir_mapping_table[ir_mapping_table[i]];
}
num_ir
}
fn relocate_dense_bz_grid_address(
bz_grid_address: &mut [[i32; 3]],
bz_map: &mut [usize],
grid_address: &[[i32; 3]],
mesh: &[i32; 3],
rec_lattice: &[[f64; 3]; 3],
is_shift: &[i32; 3],
) -> usize {
let tolerance = get_tolerance_for_bz_reduction(rec_lattice, mesh);
let bzmesh = [mesh[0] * 2, mesh[1] * 2, mesh[2] * 2];
let num_bzmesh = (bzmesh[0] * bzmesh[1] * bzmesh[2]) as usize;
for i in 0..num_bzmesh {
bz_map[i] = num_bzmesh;
}
let mut boundary_num_gp = 0;
let total_num_gp = (mesh[0] * mesh[1] * mesh[2]) as usize;
for i in 0..total_num_gp {
let mut distance = [0.0; KPT_NUM_BZ_SEARCH_SPACE];
for j in 0..KPT_NUM_BZ_SEARCH_SPACE {
let mut q_vector = [0.0; 3];
for k in 0..3 {
q_vector[k] = ((grid_address[i][k] + BZ_SEARCH_SPACE[j][k] * mesh[k]) * 2
+ is_shift[k]) as f64
/ (mesh[k] as f64)
/ 2.0;
}
let q_vec_rec = mat_multiply_matrix_vector_d3(rec_lattice, &q_vector);
distance[j] = mat_norm_squared_d3(&q_vec_rec);
}
let mut min_distance = distance[0];
let mut min_index = 0;
for j in 1..KPT_NUM_BZ_SEARCH_SPACE {
if distance[j] < min_distance {
min_distance = distance[j];
min_index = j;
}
}
for j in 0..KPT_NUM_BZ_SEARCH_SPACE {
if distance[j] < min_distance + tolerance {
let gp = if j == min_index {
i
} else {
boundary_num_gp + total_num_gp
};
let mut bz_address_double = [0; 3];
for k in 0..3 {
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];
}
let bzgp = kgrid::kgd_get_dense_grid_point_double_mesh(&bz_address_double, &bzmesh);
bz_map[bzgp] = gp;
if j != min_index {
boundary_num_gp += 1;
}
}
}
}
boundary_num_gp + total_num_gp
}
fn get_tolerance_for_bz_reduction(rec_lattice: &[[f64; 3]; 3], mesh: &[i32; 3]) -> f64 {
let mut length = [0.0; 3];
for i in 0..3 {
for j in 0..3 {
length[i] += rec_lattice[j][i] * rec_lattice[j][i];
}
length[i] /= (mesh[i] * mesh[i]) as f64;
}
let mut tolerance = length[0];
for i in 1..3 {
if tolerance < length[i] {
tolerance = length[i];
}
}
tolerance * 0.01
}
fn check_mesh_symmetry(mesh: &[i32; 3], is_shift: &[i32; 3], rot_reciprocal: &MatINT) -> bool {
let mut eq = [false; 3];
for i in 0..rot_reciprocal.size {
let mut sum = 0;
for j in 0..3 {
for k in 0..3 {
sum += rot_reciprocal.mat[i][j][k].abs();
}
}
if sum > 3 {
return false;
}
}
for i in 0..rot_reciprocal.size {
if rot_reciprocal.mat[i][0][0] == 0
&& rot_reciprocal.mat[i][1][0] == 1
&& rot_reciprocal.mat[i][2][0] == 0
{
eq[0] = true;
}
if rot_reciprocal.mat[i][0][1] == 0
&& rot_reciprocal.mat[i][1][1] == 0
&& rot_reciprocal.mat[i][2][1] == 1
{
eq[1] = true;
}
if rot_reciprocal.mat[i][0][2] == 1
&& rot_reciprocal.mat[i][1][2] == 0
&& rot_reciprocal.mat[i][2][2] == 0
{
eq[2] = true;
}
}
let cond1 = (eq[0] && mesh[0] == mesh[1] && is_shift[0] == is_shift[1]) || !eq[0];
let cond2 = (eq[1] && mesh[1] == mesh[2] && is_shift[1] == is_shift[2]) || !eq[1];
let cond3 = (eq[2] && mesh[2] == mesh[0] && is_shift[2] == is_shift[0]) || !eq[2];
cond1 && cond2 && cond3
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mathfunc::MatINT;
#[test]
fn test_check_mesh_symmetry() {
let mesh = [4, 4, 4];
let shift = [0, 0, 0];
let mut rot = MatINT::new(1);
rot.mat[0] = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
assert!(check_mesh_symmetry(&mesh, &shift, &rot));
}
#[test]
fn test_kpt_get_irreducible_reciprocal_mesh_simple() {
let mesh = [2, 2, 2];
let shift = [0, 0, 0];
let mut rot = MatINT::new(1);
rot.mat[0] = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
let mut grid_address = vec![[0; 3]; 8];
let mut map = vec![0; 8];
let num_ir = kpt_get_irreducible_reciprocal_mesh(
&mut grid_address,
&mut map,
&mesh,
&shift,
&rot,
);
assert_eq!(num_ir, 8);
for i in 0..8 {
assert_eq!(map[i], i);
}
}
}