use super::super::primitive_symmetry_search::PrimitiveSymmetrySearch;
use super::layer_bravais_group::is_layer_block_form;
use crate::base::{
AngleTolerance, Cell, Lattice, LayerCell, MoyoError, Operation, Operations, Permutation,
Rotation,
};
#[derive(Debug)]
pub struct LayerPrimitiveSymmetrySearch {
pub operations: Operations,
pub permutations: Vec<Permutation>,
}
impl LayerPrimitiveSymmetrySearch {
pub fn new(
primitive_layer_cell: &LayerCell,
symprec: f64,
angle_tolerance: AngleTolerance,
) -> Result<Self, MoyoError> {
let cell = Cell::new(
Lattice {
basis: *primitive_layer_cell.lattice().basis(),
},
primitive_layer_cell.positions().to_vec(),
primitive_layer_cell.numbers().to_vec(),
);
let bulk = PrimitiveSymmetrySearch::new(&cell, symprec, angle_tolerance)?;
let nc = primitive_layer_cell.lattice().basis().column(2).norm();
let z_tol = symprec / nc;
let mut operations = vec![];
let mut permutations = vec![];
for (op, perm) in bulk.operations.iter().zip(bulk.permutations.iter()) {
if !is_layer_block_form(&op.rotation) {
continue;
}
let exact_tz = exact_tz(primitive_layer_cell, &op.rotation, perm);
if op.rotation[(2, 2)] == 1 {
let tz = exact_tz - exact_tz.round();
if tz.abs() > z_tol {
continue;
}
}
let mut translation = op.translation;
translation[2] = exact_tz;
operations.push(Operation::new(op.rotation, translation));
permutations.push(perm.clone());
}
if operations.is_empty() {
return Err(MoyoError::TooSmallToleranceError);
}
Ok(Self {
operations,
permutations,
})
}
}
fn exact_tz(cell: &LayerCell, rotation: &Rotation, permutation: &Permutation) -> f64 {
let n = cell.num_atoms();
debug_assert!(n > 0);
let w22 = rotation[(2, 2)] as f64;
let positions = cell.positions();
let raw_tz = |i: usize| positions[permutation.apply(i)][2] - w22 * positions[i][2];
let reference = raw_tz(0);
let mut sum = reference;
for i in 1..n {
let candidate = raw_tz(i);
sum += candidate - (candidate - reference).round();
}
sum / n as f64
}