use super::layer::{LayerPrimitiveCell, LayerPrimitiveSymmetrySearch};
use super::primitive_cell::{PrimitiveCell, PrimitiveMagneticCell};
use super::primitive_symmetry_search::{PrimitiveMagneticSymmetrySearch, PrimitiveSymmetrySearch};
use crate::base::{
AngleTolerance, Cell, LayerCell, MagneticCell, MagneticMoment, MagneticSymmetryTolerances,
MoyoError, RotationMagneticMomentAction, SymmetryTolerances, ToleranceHandler,
};
use log::debug;
const MAX_SYMMETRY_SEARCH_TRIALS: usize = 16;
const MAX_TOLERANCE_HANDLER_TRIALS: usize = 4;
pub fn iterative_symmetry_search(
cell: &Cell,
symprec: f64,
angle_tolerance: AngleTolerance,
) -> Result<(PrimitiveCell, PrimitiveSymmetrySearch, f64, AngleTolerance), MoyoError> {
let mut tolerances = SymmetryTolerances {
symprec,
angle_tolerance,
};
for _ in 0..MAX_TOLERANCE_HANDLER_TRIALS {
let mut tolerance_handler = ToleranceHandler::new(tolerances);
for _ in 0..MAX_SYMMETRY_SEARCH_TRIALS {
match PrimitiveCell::new(cell, tolerance_handler.tolerances.symprec) {
Ok(prim_cell) => {
match PrimitiveSymmetrySearch::new(
&prim_cell.cell,
tolerance_handler.tolerances.symprec,
tolerance_handler.tolerances.angle_tolerance,
) {
Ok(symmetry_search) => {
return Ok((
prim_cell,
symmetry_search,
tolerance_handler.tolerances.symprec,
tolerance_handler.tolerances.angle_tolerance,
));
}
Err(err) => tolerance_handler.update(err),
}
}
Err(err) => tolerance_handler.update(err),
}
}
tolerances = tolerance_handler.tolerances.clone();
debug!("Restart ToleranceHandler with {:?}", tolerances);
}
debug!("Reach the maximum number of symmetry search trials");
Err(MoyoError::PrimitiveSymmetrySearchError)
}
pub fn iterative_layer_symmetry_search(
cell: &Cell,
symprec: f64,
angle_tolerance: AngleTolerance,
) -> Result<
(
LayerPrimitiveCell,
LayerPrimitiveSymmetrySearch,
f64,
AngleTolerance,
),
MoyoError,
> {
let layer_cell = LayerCell::new(cell.clone(), symprec, angle_tolerance)?;
let mut tolerances = SymmetryTolerances {
symprec,
angle_tolerance,
};
for _ in 0..MAX_TOLERANCE_HANDLER_TRIALS {
let mut tolerance_handler = ToleranceHandler::new(tolerances);
for _ in 0..MAX_SYMMETRY_SEARCH_TRIALS {
match LayerPrimitiveCell::new(&layer_cell, tolerance_handler.tolerances.symprec) {
Ok(prim_layer) => {
match LayerPrimitiveSymmetrySearch::new(
&prim_layer.layer_cell,
tolerance_handler.tolerances.symprec,
tolerance_handler.tolerances.angle_tolerance,
) {
Ok(symmetry_search) => {
return Ok((
prim_layer,
symmetry_search,
tolerance_handler.tolerances.symprec,
tolerance_handler.tolerances.angle_tolerance,
));
}
Err(err) => tolerance_handler.update(err),
}
}
Err(err) => tolerance_handler.update(err),
}
}
tolerances = tolerance_handler.tolerances.clone();
debug!("Restart ToleranceHandler with {:?}", tolerances);
}
debug!("Reach the maximum number of symmetry search trials");
Err(MoyoError::PrimitiveSymmetrySearchError)
}
pub fn iterative_magnetic_symmetry_search<M: MagneticMoment>(
magnetic_cell: &MagneticCell<M>,
symprec: f64,
angle_tolerance: AngleTolerance,
mag_symprec: Option<f64>,
action: RotationMagneticMomentAction,
) -> Result<
(
PrimitiveMagneticCell<M>,
PrimitiveMagneticSymmetrySearch,
f64,
AngleTolerance,
f64,
),
MoyoError,
> {
let mut tolerances = MagneticSymmetryTolerances {
symprec,
angle_tolerance,
mag_symprec: mag_symprec.unwrap_or(symprec),
};
for _ in 0..MAX_TOLERANCE_HANDLER_TRIALS {
let mut tolerance_handler = ToleranceHandler::new(tolerances);
for _ in 0..MAX_SYMMETRY_SEARCH_TRIALS {
match PrimitiveMagneticCell::new(
magnetic_cell,
tolerance_handler.tolerances.symprec,
tolerance_handler.tolerances.mag_symprec,
) {
Ok(prim_mag_cell) => {
match PrimitiveMagneticSymmetrySearch::new(
&prim_mag_cell.magnetic_cell,
tolerance_handler.tolerances.symprec,
tolerance_handler.tolerances.angle_tolerance,
tolerance_handler.tolerances.mag_symprec,
action,
) {
Ok(magnetic_symmetry_search) => {
return Ok((
prim_mag_cell,
magnetic_symmetry_search,
tolerance_handler.tolerances.symprec,
tolerance_handler.tolerances.angle_tolerance,
tolerance_handler.tolerances.mag_symprec,
));
}
Err(err) => tolerance_handler.update(err),
}
}
Err(err) => tolerance_handler.update(err),
}
}
tolerances = tolerance_handler.tolerances.clone();
debug!("Restart ToleranceHandler with {:?}", tolerances);
}
debug!("Reach the maximum number of symmetry search trials");
Err(MoyoError::PrimitiveMagneticSymmetrySearchError)
}
#[cfg(test)]
mod tests {
use nalgebra::{Vector3, matrix};
use super::iterative_layer_symmetry_search;
use crate::base::{AngleTolerance, Cell, Lattice, LayerCell, MoyoError};
use crate::search::layer::LayerPrimitiveCell;
fn unit_square_p1_cell() -> Cell {
Cell::new(
Lattice::new(matrix![
1.0, 0.0, 0.0;
0.0, 1.0, 0.0;
0.0, 0.0, 5.0;
]),
vec![Vector3::new(0.3, 0.4, 0.2)],
vec![1],
)
}
#[test]
fn test_iterative_layer_symmetry_search_passthrough_when_symprec_is_already_safe() {
let cell = unit_square_p1_cell();
let symprec = 1e-4;
let angle = AngleTolerance::Default;
let (_prim, _search, out_symprec, out_angle) =
iterative_layer_symmetry_search(&cell, symprec, angle).unwrap();
assert_eq!(out_symprec, symprec);
match (out_angle, angle) {
(AngleTolerance::Default, AngleTolerance::Default) => {}
other => panic!("angle tolerance changed unexpectedly: {:?}", other),
}
}
#[test]
fn test_iterative_layer_symmetry_search_retries_after_too_large_tolerance() {
let cell = unit_square_p1_cell();
let layer = LayerCell::new(cell.clone(), 0.3, AngleTolerance::Default).unwrap();
assert!(matches!(
LayerPrimitiveCell::new(&layer, 0.3),
Err(MoyoError::TooLargeToleranceError),
));
let (_prim, _search, out_symprec, _out_angle) =
iterative_layer_symmetry_search(&cell, 0.3, AngleTolerance::Default).unwrap();
assert!(
out_symprec < 0.3,
"expected the retry to tighten symprec below the input; got {}",
out_symprec,
);
}
}