use crate::MagneticType;
use crate::mathfunc::{
mat_cast_matrix_3i_to_3d, mat_check_identity_matrix_i3,
mat_get_determinant_d3, mat_inverse_matrix_d3, mat_multiply_matrix_d3,
mat_multiply_matrix_i3, mat_multiply_matrix_id3,
mat_multiply_matrix_vector_d3, mat_multiply_matrix_vector_id3, mat_nint, Mat3, Mat3I, Vec3,
};
use crate::msg_database::{
msgdb_get_magnetic_spacegroup_type, msgdb_get_spacegroup_operations,
msgdb_get_std_transformations, msgdb_get_uni_candidates,
};
use crate::hall_symbol::hal_match_hall_symbol_db;
use crate::pointgroup::ptg_get_transformation_matrix;
use crate::primitive::prm_get_primitive_symmetry;
use crate::refinement::ref_find_similar_bravais_lattice;
use crate::spacegroup::{
get_centering, get_initial_conventional_symmetry,
spa_search_spacegroup_with_symmetry, Spacegroup,
};
use crate::spg_database::{spgdb_get_spacegroup_type, Centering};
use crate::symmetry::{MagneticSymmetry, Symmetry};
const MAX_DENOMINATOR: f64 = 100.0;
pub struct MagneticDataset {
pub uni_number: usize,
pub msg_type: MagneticType,
pub hall_number: usize,
pub transformation_matrix: Mat3,
pub origin_shift: Vec3,
pub std_rotation_matrix: Mat3,
}
pub fn msg_identify_magnetic_space_group_type(
lattice: &Mat3,
magnetic_symmetry: &MagneticSymmetry,
symprec: f64,
) -> Option<MagneticDataset> {
msg_identify_with_parent_hall(lattice, magnetic_symmetry, None, symprec)
}
pub fn msg_identify_with_parent_hall(
lattice: &Mat3,
magnetic_symmetry: &MagneticSymmetry,
parent_hall_number: Option<usize>,
symprec: f64,
) -> Option<MagneticDataset> {
let prim_sym = reduce_to_primitive_magsym(magnetic_symmetry, symprec);
let (ref_sg, changed_symmetry, mut tmat, mut shift, msgtype_num) =
match get_reference_space_group(&prim_sym, symprec) {
Some(result) => {
result
}
None => {
let hall_number = parent_hall_number?;
let fb = build_fallback_reference(lattice, magnetic_symmetry, hall_number, symprec)?;
fb
}
};
let mut hall_numbers_try = vec![];
if let Some(ph) = parent_hall_number {
hall_numbers_try.push(ph);
}
if !hall_numbers_try.contains(&ref_sg.hall_number) {
hall_numbers_try.push(ref_sg.hall_number);
}
let mut best_uni = 0;
let mut best_msg_type = MagneticType::NonMagnetic;
let mut best_hall_number = 0;
for &hall_number in &hall_numbers_try {
let range = match msgdb_get_uni_candidates(hall_number) {
Some(r) => r,
None => continue,
};
let min_uni = range[0];
let max_uni = range[1];
for uni_number in min_uni..=max_uni {
let msgtype_db = msgdb_get_magnetic_spacegroup_type(uni_number);
if msgtype_db.type_ != msgtype_num {
continue;
}
let msg_uni = match msgdb_get_spacegroup_operations(uni_number, hall_number) {
Some(u) => u,
None => continue,
};
if changed_symmetry.size > msg_uni.size {
continue;
}
let transformations = match msgdb_get_std_transformations(uni_number, hall_number) {
Some(t) => t,
None => continue,
};
let mut same = false;
for trans_idx in 0..transformations.size {
let tmat_cor = transformations.rot[trans_idx];
let shift_cor = transformations.trans[trans_idx];
let tmat_cor_d = mat_cast_matrix_3i_to_3d(&tmat_cor);
let symmetry_cor = get_distinct_changed_magnetic_symmetry(
&tmat_cor_d, &shift_cor, &changed_symmetry,
);
let symmetry_cor = match symmetry_cor {
Some(s) => s,
None => continue,
};
let matched = is_subset(&symmetry_cor, &msg_uni, symprec);
if matched {
same = true;
tmat = mat_multiply_matrix_d3(&tmat_cor_d, &tmat);
let shift_tmp = mat_multiply_matrix_vector_d3(&tmat_cor_d, &shift);
for s in 0..3 {
shift[s] = shift_tmp[s] + shift_cor[s];
}
break;
}
}
if same {
best_uni = uni_number;
best_msg_type = msgtype_db.type_;
best_hall_number = hall_number;
break;
}
}
if best_uni != 0 {
break;
}
}
if best_uni == 0 {
return None;
}
let hall_number = best_hall_number;
let _msgtype = msgdb_get_magnetic_spacegroup_type(best_uni);
let mut ret = MagneticDataset {
uni_number: best_uni,
msg_type: best_msg_type,
hall_number,
transformation_matrix: [[0.0; 3]; 3],
origin_shift: [0.0; 3],
std_rotation_matrix: [[0.0; 3]; 3],
};
get_rigid_rotation(&mut ret.std_rotation_matrix, lattice, &tmat, &ref_sg);
ret.transformation_matrix = tmat;
ret.origin_shift = shift;
Some(ret)
}
fn get_reference_space_group(
magnetic_symmetry: &MagneticSymmetry,
symprec: f64,
) -> Option<(Spacegroup, MagneticSymmetry, Mat3, Vec3, MagneticType)> {
let (mut fsg, sym_fsg) =
match get_family_space_group_with_magnetic_symmetry(magnetic_symmetry, symprec) {
Some(r) => r,
None => {
return None;
}
};
let (mut _xsg, sym_xsg) =
match get_maximal_subspace_group_with_magnetic_symmetry(magnetic_symmetry, symprec) {
Some(r) => r,
None => {
return None;
}
};
let msgtype_num =
get_magnetic_space_group_type(magnetic_symmetry, sym_fsg.size, sym_xsg.size, symprec)?;
let representatives = build_representatives(msgtype_num, magnetic_symmetry)?;
let ref_sg = if msgtype_num == MagneticType::AntiTranslation {
&mut _xsg
} else {
&mut fsg
};
let tmat = ref_sg.bravais_lattice;
let shift = ref_sg.origin_shift;
let changed_symmetry = get_changed_magnetic_symmetry(
&tmat, &shift, &representatives, &sym_xsg,
magnetic_symmetry, symprec,
)?;
let mut ref_sg_copy = Spacegroup::new();
ref_sg_copy = ref_sg.clone();
Some((ref_sg_copy, changed_symmetry, tmat, shift, msgtype_num))
}
fn build_fallback_reference(
lattice: &Mat3,
magnetic_symmetry: &MagneticSymmetry,
parent_hall_number: usize,
symprec: f64,
) -> Option<(Spacegroup, MagneticSymmetry, Mat3, Vec3, MagneticType)> {
let sym_fsg = extract_symmetry(magnetic_symmetry, true, symprec)?;
let sym_xsg = extract_symmetry(magnetic_symmetry, false, symprec)?;
let msgtype_num =
get_magnetic_space_group_type(magnetic_symmetry, sym_fsg.size, sym_xsg.size, symprec)?;
let spg_type = spgdb_get_spacegroup_type(parent_hall_number);
let mut ref_sg = Spacegroup::new();
ref_sg.hall_number = parent_hall_number;
ref_sg.number = spg_type.number;
ref_sg.pointgroup_number = spg_type.pointgroup_number;
ref_sg.schoenflies = spg_type.schoenflies;
ref_sg.hall_symbol = spg_type.hall_symbol;
ref_sg.international = spg_type.international;
ref_sg.international_long = spg_type.international_full;
ref_sg.international_short = spg_type.international_short;
ref_sg.choice = spg_type.choice;
ref_sg.bravais_lattice = *lattice;
let tmat = ref_sg.bravais_lattice;
let shift = ref_sg.origin_shift;
let representatives = build_representatives(msgtype_num, magnetic_symmetry)?;
let changed_symmetry = get_changed_magnetic_symmetry(
&tmat, &shift, &representatives, &sym_xsg,
magnetic_symmetry, symprec,
)?;
let ref_sg_copy = ref_sg.clone();
Some((ref_sg_copy, changed_symmetry, tmat, shift, msgtype_num))
}
pub(crate) fn extract_symmetry(
magnetic_symmetry: &MagneticSymmetry,
ignore_time_reversal: bool,
symprec: f64,
) -> Option<Symmetry> {
let identity: Mat3I = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
let num_sym_msg = magnetic_symmetry.size;
let is_type2 = magnetic_symmetry
.rot
.iter()
.zip(magnetic_symmetry.trans.iter())
.zip(magnetic_symmetry.timerev.iter())
.any(|((rot, trans), &timerev)| {
mat_check_identity_matrix_i3(&identity, rot)
&& trans[0].abs() < symprec
&& trans[1].abs() < symprec
&& trans[2].abs() < symprec
&& timerev
});
let mut sym = Symmetry::new(num_sym_msg);
let mut num_sym = 0;
for i in 0..num_sym_msg {
if !ignore_time_reversal && magnetic_symmetry.timerev[i] {
continue;
}
if is_type2 && magnetic_symmetry.timerev[i] {
continue;
}
sym.rot[num_sym] = magnetic_symmetry.rot[i];
sym.trans[num_sym] = magnetic_symmetry.trans[i];
num_sym += 1;
}
sym.size = num_sym;
if ignore_time_reversal || is_type2 {
let mut dedup = Symmetry::new(num_sym);
let mut n_dedup = 0;
for i in 0..num_sym {
let mut dup = false;
for j in 0..n_dedup {
if !mat_check_identity_matrix_i3(&dedup.rot[j], &sym.rot[i]) {
continue;
}
let mut diff = 0.0;
for k in 0..3 {
let x = dedup.trans[j][k] - sym.trans[i][k];
diff += (x - x.round()).abs();
}
if diff < symprec {
dup = true;
break;
}
}
if !dup {
dedup.rot[n_dedup] = sym.rot[i];
dedup.trans[n_dedup] = sym.trans[i];
n_dedup += 1;
}
}
dedup.size = n_dedup;
sym = dedup;
}
if sym.size == 0 {
None
} else {
Some(sym)
}
}
fn get_family_space_group_with_magnetic_symmetry(
magnetic_symmetry: &MagneticSymmetry,
symprec: f64,
) -> Option<(Spacegroup, Symmetry)> {
get_space_group_with_magnetic_symmetry(magnetic_symmetry, true, symprec)
}
fn get_maximal_subspace_group_with_magnetic_symmetry(
magnetic_symmetry: &MagneticSymmetry,
symprec: f64,
) -> Option<(Spacegroup, Symmetry)> {
get_space_group_with_magnetic_symmetry(magnetic_symmetry, false, symprec)
}
fn get_maximal_subspace_symmetry(
magnetic_symmetry: &MagneticSymmetry,
symprec: f64,
) -> Option<Symmetry> {
extract_symmetry(magnetic_symmetry, false, symprec)
}
fn get_space_group_with_magnetic_symmetry(
magnetic_symmetry: &MagneticSymmetry,
ignore_time_reversal: bool,
symprec: f64,
) -> Option<(Spacegroup, Symmetry)> {
let identity: Mat3I = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
let unit_lat: Mat3 = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]];
let num_sym_msg = magnetic_symmetry.size;
let is_type2 = magnetic_symmetry
.rot
.iter()
.zip(magnetic_symmetry.trans.iter())
.zip(magnetic_symmetry.timerev.iter())
.any(|((rot, trans), &timerev)| {
mat_check_identity_matrix_i3(&identity, rot)
&& trans[0].abs() < symprec
&& trans[1].abs() < symprec
&& trans[2].abs() < symprec
&& timerev
});
let mut sym = Symmetry::new(num_sym_msg);
let mut num_sym = 0;
for i in 0..num_sym_msg {
if !ignore_time_reversal && magnetic_symmetry.timerev[i] {
continue;
}
if is_type2 && magnetic_symmetry.timerev[i] {
continue;
}
sym.rot[num_sym] = magnetic_symmetry.rot[i];
sym.trans[num_sym] = magnetic_symmetry.trans[i];
num_sym += 1;
}
sym.size = num_sym;
if num_sym == 0 {
return None;
}
if ignore_time_reversal || is_type2 {
let mut dedup = Symmetry::new(num_sym);
let mut n_dedup = 0;
for i in 0..num_sym {
let mut dup = false;
for j in 0..n_dedup {
if !mat_check_identity_matrix_i3(&dedup.rot[j], &sym.rot[i]) {
continue;
}
let mut diff = 0.0;
for k in 0..3 {
let x = dedup.trans[j][k] - sym.trans[i][k];
diff += (x - x.round()).abs();
}
if diff < symprec {
dup = true;
break;
}
}
if !dup {
dedup.rot[n_dedup] = sym.rot[i];
dedup.trans[n_dedup] = sym.trans[i];
n_dedup += 1;
}
}
dedup.size = n_dedup;
sym = dedup;
}
let (tmat, prim_sym) = prm_get_primitive_symmetry(&sym, symprec)?;
let mut spacegroup = match spa_search_spacegroup_with_symmetry(&prim_sym, &unit_lat, symprec) {
Some(sg) => sg,
None => {
return find_spacegroup_by_symmetry(&sym, &unit_lat, symprec)
.map(|sg| (sg, sym));
}
};
ref_find_similar_bravais_lattice(&mut spacegroup, symprec);
let inv_tmat = mat_inverse_matrix_d3(&tmat, 0.0).ok()?;
spacegroup.bravais_lattice = mat_multiply_matrix_d3(&inv_tmat, &spacegroup.bravais_lattice);
Some((spacegroup, sym))
}
fn find_spacegroup_by_symmetry(
symmetry: &Symmetry,
lattice: &Mat3,
symprec: f64,
) -> Option<Spacegroup> {
let mut origin_shift = [0.0; 3];
let mut conv_lattice = [[0.0; 3]; 3];
let (tmat_int, pointgroup) = ptg_get_transformation_matrix(&symmetry.rot, None);
if pointgroup.number == 0 {
return None;
}
let mut correction_mat = [[0.0; 3]; 3];
let centering = get_centering(&mut correction_mat, &tmat_int, pointgroup.laue);
if centering == Centering::Error {
return None;
}
let tmat = mat_multiply_matrix_id3(&tmat_int, &correction_mat);
conv_lattice = mat_multiply_matrix_d3(lattice, &tmat);
let conv_symmetry = get_initial_conventional_symmetry(centering, &tmat, symmetry)?;
for hall in 1..=530 {
if hal_match_hall_symbol_db(
&mut origin_shift,
&conv_lattice,
hall,
centering,
&conv_symmetry,
symprec,
) {
let spg_type = spgdb_get_spacegroup_type(hall as usize);
let mut spacegroup = Spacegroup::new();
spacegroup.bravais_lattice = conv_lattice;
spacegroup.origin_shift = origin_shift;
spacegroup.number = spg_type.number;
spacegroup.hall_number = hall as usize;
spacegroup.pointgroup_number = spg_type.pointgroup_number;
spacegroup.schoenflies = spg_type.schoenflies;
spacegroup.hall_symbol = spg_type.hall_symbol;
spacegroup.international = spg_type.international;
spacegroup.international_long = spg_type.international_full;
spacegroup.international_short = spg_type.international_short;
spacegroup.choice = spg_type.choice;
return Some(spacegroup);
}
}
None
}
fn build_representatives(
msgtype: MagneticType,
magnetic_symmetry: &MagneticSymmetry,
) -> Option<MagneticSymmetry> {
let identity: Mat3I = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
match msgtype {
MagneticType::Ordinary => {
let mut rep = MagneticSymmetry::new(1);
rep.rot[0] = identity;
rep.trans[0] = [0.0; 3];
rep.timerev[0] = false;
rep.size = 1;
Some(rep)
}
MagneticType::Grey => {
let mut rep = MagneticSymmetry::new(2);
rep.rot[0] = identity;
rep.trans[0] = [0.0; 3];
rep.timerev[0] = false;
rep.rot[1] = identity;
rep.trans[1] = [0.0; 3];
rep.timerev[1] = true;
rep.size = 2;
Some(rep)
}
MagneticType::BlackWhite | MagneticType::AntiTranslation => {
get_representative(magnetic_symmetry)
}
_ => None,
}
}
fn get_magnetic_space_group_type(
magnetic_symmetry: &MagneticSymmetry,
num_sym_fsg: usize,
num_sym_xsg: usize,
symprec: f64,
) -> Option<MagneticType> {
let identity: Mat3I = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
if num_sym_fsg == num_sym_xsg {
let num_sym_msg = magnetic_symmetry.size;
if num_sym_msg == num_sym_fsg {
Some(MagneticType::Ordinary)
} else if num_sym_msg == 2 * num_sym_fsg {
Some(MagneticType::Grey)
} else {
None
}
} else if num_sym_fsg == 2 * num_sym_xsg {
let has_anti_translation = magnetic_symmetry
.rot
.iter()
.zip(magnetic_symmetry.trans.iter())
.zip(magnetic_symmetry.timerev.iter())
.any(|((rot, trans), &timerev)| {
mat_check_identity_matrix_i3(&identity, rot)
&& trans[0].abs() < symprec
&& trans[1].abs() < symprec
&& trans[2].abs() < symprec
&& timerev
});
if has_anti_translation {
Some(MagneticType::AntiTranslation) } else {
Some(MagneticType::BlackWhite) }
} else {
None
}
}
fn get_representative(magnetic_symmetry: &MagneticSymmetry) -> Option<MagneticSymmetry> {
let identity: Mat3I = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
let mut representative = MagneticSymmetry::new(2);
representative.rot[0] = identity;
representative.trans[0] = [0.0; 3];
representative.timerev[0] = false;
for i in 0..magnetic_symmetry.size {
if mat_check_identity_matrix_i3(&identity, &magnetic_symmetry.rot[i])
&& magnetic_symmetry.timerev[i]
&& magnetic_symmetry.trans[i][0].abs() < 1e-5
&& magnetic_symmetry.trans[i][1].abs() < 1e-5
&& magnetic_symmetry.trans[i][2].abs() < 1e-5
{
representative.rot[1] = magnetic_symmetry.rot[i];
representative.trans[1] = magnetic_symmetry.trans[i];
representative.timerev[1] = true;
representative.size = 2;
return Some(representative);
}
}
for i in 0..magnetic_symmetry.size {
if !magnetic_symmetry.timerev[i] {
continue;
}
if mat_check_identity_matrix_i3(&identity, &magnetic_symmetry.rot[i]) {
continue; }
representative.rot[1] = magnetic_symmetry.rot[i];
representative.trans[1] = magnetic_symmetry.trans[i];
representative.timerev[1] = true;
representative.size = 2;
return Some(representative);
}
representative.size = 1;
Some(representative)
}
fn get_distinct_changed_magnetic_symmetry(
tmat: &Mat3,
shift: &Vec3,
sym_msg: &MagneticSymmetry,
) -> Option<MagneticSymmetry> {
let inv_tmat = mat_inverse_matrix_d3(tmat, 0.0).ok()?;
let mut changed = MagneticSymmetry::new(sym_msg.size);
let mut size = 0usize;
for i in 0..sym_msg.size {
let rot_f64 = mat_cast_matrix_3i_to_3d(&sym_msg.rot[i]);
let tmp = mat_multiply_matrix_d3(&inv_tmat, &rot_f64);
let r_new = mat_multiply_matrix_d3(&tmp, tmat);
let rot_i = [
[mat_nint(r_new[0][0]), mat_nint(r_new[0][1]), mat_nint(r_new[0][2])],
[mat_nint(r_new[1][0]), mat_nint(r_new[1][1]), mat_nint(r_new[1][2])],
[mat_nint(r_new[2][0]), mat_nint(r_new[2][1]), mat_nint(r_new[2][2])],
];
let mut tmp_rot = sym_msg.rot[i];
tmp_rot[0][0] -= 1;
tmp_rot[1][1] -= 1;
tmp_rot[2][2] -= 1;
let shift_term = mat_multiply_matrix_vector_id3(&tmp_rot, shift);
let mut t_new = [0.0; 3];
for j in 0..3 {
t_new[j] = sym_msg.trans[i][j] + shift_term[j];
}
t_new = mat_multiply_matrix_vector_d3(&inv_tmat, &t_new);
let mut is_dup = false;
for j in 0..size {
if mat_check_identity_matrix_i3(&changed.rot[j], &rot_i) {
let mut diff = [0.0; 3];
for k in 0..3 {
diff[k] = changed.trans[j][k] - t_new[k];
diff[k] -= mat_nint(diff[k]) as f64;
}
if diff[0].abs() < 1e-5 && diff[1].abs() < 1e-5 && diff[2].abs() < 1e-5 {
if changed.timerev[j] == sym_msg.timerev[i] {
is_dup = true;
break;
}
}
}
}
if !is_dup {
changed.rot[size] = rot_i;
changed.trans[size] = t_new;
changed.timerev[size] = sym_msg.timerev[i];
size += 1;
}
}
changed.size = size;
Some(changed)
}
fn mat_dmod1(a: f64) -> f64 {
let b = a - a.round();
if b < 0.0 { b + 1.0 } else { b }
}
fn is_contained_mat(a: &Mat3I, sym_msg: &MagneticSymmetry, size: usize) -> bool {
for i in 0..size {
if mat_check_identity_matrix_i3(a, &sym_msg.rot[i]) {
return true;
}
}
false
}
fn is_contained_vec(v: &Vec3, trans: &[Vec3], symprec: f64) -> bool {
for t in trans {
let mut eq = true;
for s in 0..3 {
if (v[s] - t[s]).abs() >= symprec {
eq = false;
break;
}
}
if eq { return true; }
}
false
}
fn get_changed_pure_translations(
tmat: &Mat3,
pure_trans: &[Vec3],
symprec: f64,
) -> Option<Vec<Vec3>> {
let det = mat_get_determinant_d3(tmat);
let size = mat_nint(pure_trans.len() as f64 / det.abs()) as usize;
let mut changed: Vec<Vec3> = Vec::with_capacity(size);
if (det - 1.0).abs() <= symprec {
for pt in pure_trans {
let trans = mat_multiply_matrix_vector_d3(tmat, pt);
changed.push([mat_dmod1(trans[0]), mat_dmod1(trans[1]), mat_dmod1(trans[2])]);
}
} else {
let mut denominator = 1;
loop {
let mut ok = true;
for s in 0..3 {
for t in 0..3 {
if (tmat[s][t] * denominator as f64
- mat_nint(tmat[s][t] * denominator as f64) as f64)
.abs()
> symprec
{
ok = false;
break;
}
}
if !ok { break; }
}
if ok { break; }
denominator += 1;
if denominator as f64 > MAX_DENOMINATOR {
return None;
}
}
for n0 in 0..=denominator {
for n1 in 0..=denominator {
for n2 in 0..=denominator {
for pt in pure_trans {
let shifted = [
pt[0] + n0 as f64,
pt[1] + n1 as f64,
pt[2] + n2 as f64,
];
let trans = mat_multiply_matrix_vector_d3(tmat, &shifted);
let t_mod = [mat_dmod1(trans[0]), mat_dmod1(trans[1]), mat_dmod1(trans[2])];
if !is_contained_vec(&t_mod, &changed, symprec) {
changed.push(t_mod);
}
}
}
}
}
}
if changed.len() != size {
changed.clear();
for pt in pure_trans {
let trans = mat_multiply_matrix_vector_d3(tmat, pt);
let t_mod = [mat_dmod1(trans[0]), mat_dmod1(trans[1]), mat_dmod1(trans[2])];
if !is_contained_vec(&t_mod, &changed, symprec) {
changed.push(t_mod);
}
}
}
Some(changed)
}
fn get_changed_magnetic_symmetry(
tmat: &Mat3,
shift: &Vec3,
representatives: &MagneticSymmetry,
sym_xsg: &Symmetry,
magnetic_symmetry: &MagneticSymmetry,
symprec: f64,
) -> Option<MagneticSymmetry> {
let changed_representatives =
match get_distinct_changed_magnetic_symmetry(tmat, shift, representatives) {
Some(r) => r,
None => return None,
};
let pure_trans = match crate::spin::spn_collect_pure_translations_from_magnetic_symmetry(
magnetic_symmetry,
) {
Some(p) => {
p
}
None => return None,
};
let changed_pure_trans = match get_changed_pure_translations(tmat, &pure_trans, symprec) {
Some(p) => p,
None => return None,
};
let mut factors = MagneticSymmetry::new(sym_xsg.size);
let mut num_factors = 0;
for i in 0..sym_xsg.size {
if is_contained_mat(&sym_xsg.rot[i], &factors, num_factors) {
continue;
}
factors.rot[num_factors] = sym_xsg.rot[i];
factors.trans[num_factors] = sym_xsg.trans[i];
factors.timerev[num_factors] = false;
num_factors += 1;
}
factors.size = num_factors;
let changed_factors =
match get_distinct_changed_magnetic_symmetry(tmat, shift, &factors) {
Some(f) => f,
None => return None,
};
let size = changed_representatives.size * changed_pure_trans.len() * num_factors;
let mut changed = MagneticSymmetry::new(size);
let mut num_sym = 0;
for i in 0..changed_pure_trans.len() {
for j in 0..changed_representatives.size {
for k in 0..num_factors {
changed.rot[num_sym] = mat_multiply_matrix_i3(
&changed_representatives.rot[j],
&changed_factors.rot[k],
);
let mut trans = mat_multiply_matrix_vector_id3(
&changed_representatives.rot[j],
&changed_factors.trans[k],
);
for s in 0..3 {
trans[s] += changed_representatives.trans[j][s]
+ changed_pure_trans[i][s];
trans[s] = mat_dmod1(trans[s]);
}
changed.trans[num_sym] = trans;
changed.timerev[num_sym] =
changed_representatives.timerev[j] != changed_factors.timerev[k];
num_sym += 1;
}
}
}
changed.size = num_sym;
Some(changed)
}
pub(crate) fn reduce_to_primitive_magsym(sym: &MagneticSymmetry, symprec: f64) -> MagneticSymmetry {
let identity: Mat3I = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
let n_in = sym.size;
let mut lat_trans: Vec<(Vec3, bool)> = vec![([0.0; 3], false)];
for i in 0..n_in {
if mat_check_identity_matrix_i3(&identity, &sym.rot[i]) {
let t = sym.trans[i];
let tr = sym.timerev[i];
if tr && t[0].abs() < symprec && t[1].abs() < symprec && t[2].abs() < symprec {
continue;
}
let mut dup = false;
for (lt, ltr) in &lat_trans {
let mut d = 0.0;
for k in 0..3 {
let mut x = t[k] - lt[k];
x -= x.round();
d += x.abs();
}
if d < symprec && *ltr == tr { dup = true; break; }
}
if !dup { lat_trans.push((t, tr)); }
}
}
if lat_trans.len() <= 1 { return sym.clone(); }
let mut out = MagneticSymmetry::new(n_in);
let mut n = 0usize;
'next: for i in 0..n_in {
let rot_i = &sym.rot[i];
let t_i = sym.trans[i];
let tr_i = sym.timerev[i];
let mut best_t = t_i;
let mut best_tr = tr_i;
let mut best_n2 = t_i[0] * t_i[0] + t_i[1] * t_i[1] + t_i[2] * t_i[2];
for (lt, ltr) in &lat_trans {
let cand = [t_i[0] - lt[0], t_i[1] - lt[1], t_i[2] - lt[2]];
let n2 = cand[0] * cand[0] + cand[1] * cand[1] + cand[2] * cand[2];
let new_tr = tr_i != *ltr;
if n2 < best_n2 - 1e-10 || (n2 < best_n2 + 1e-10 && !new_tr && best_tr) {
best_t = cand;
best_n2 = n2;
best_tr = new_tr;
}
}
for j in 0..n {
if out.timerev[j] != best_tr { continue; }
if !mat_check_identity_matrix_i3(&out.rot[j], rot_i) { continue; }
let mut d = 0.0;
for k in 0..3 {
let mut x = out.trans[j][k] - best_t[k];
x -= x.round();
d += x.abs();
}
if d < symprec { continue 'next; }
}
out.rot[n] = *rot_i;
out.trans[n] = best_t;
out.timerev[n] = best_tr;
n += 1;
}
out.size = n;
let mut final_sym = MagneticSymmetry::new(n);
for i in 0..n {
final_sym.rot[i] = out.rot[i];
final_sym.trans[i] = out.trans[i];
final_sym.timerev[i] = out.timerev[i];
}
final_sym
}
fn is_subset(
sym1: &MagneticSymmetry,
sym2: &MagneticSymmetry,
symprec: f64,
) -> bool {
if sym1.size > sym2.size {
return false; }
let mut found = vec![false; sym2.size];
for i in 0..sym1.size {
let mut matched = false;
for j in 0..sym2.size {
if found[j] {
continue;
}
if !mat_check_identity_matrix_i3(&sym1.rot[i], &sym2.rot[j]) {
continue;
}
if sym1.timerev[i] != sym2.timerev[j] {
continue;
}
let mut diff = [0.0; 3];
for k in 0..3 {
diff[k] = sym1.trans[i][k] - sym2.trans[j][k];
diff[k] -= mat_nint(diff[k]) as f64;
}
if diff[0].abs() < symprec && diff[1].abs() < symprec && diff[2].abs() < symprec {
found[j] = true;
matched = true;
break;
}
}
if !matched {
return false;
}
}
true
}
#[allow(dead_code)]
fn is_equal(
sym1: &MagneticSymmetry,
sym2: &MagneticSymmetry,
symprec: f64,
) -> bool {
if sym1.size != sym2.size {
return false;
}
let mut found = vec![false; sym2.size];
for i in 0..sym1.size {
let mut matched = false;
for j in 0..sym2.size {
if found[j] {
continue;
}
if !mat_check_identity_matrix_i3(&sym1.rot[i], &sym2.rot[j]) {
continue;
}
if sym1.timerev[i] != sym2.timerev[j] {
continue;
}
let mut diff = [0.0; 3];
for k in 0..3 {
diff[k] = sym1.trans[i][k] - sym2.trans[j][k];
diff[k] -= mat_nint(diff[k]) as f64;
}
if diff[0].abs() < symprec && diff[1].abs() < symprec && diff[2].abs() < symprec {
found[j] = true;
matched = true;
break;
}
}
if !matched {
return false;
}
}
true
}
fn get_rigid_rotation(
rigid_rot: &mut Mat3,
lattice: &Mat3,
tmat: &Mat3,
ref_sg: &Spacegroup,
) {
let inv_tmat = mat_inverse_matrix_d3(tmat, 0.0).ok();
if let Some(inv) = inv_tmat {
let tmp = mat_multiply_matrix_d3(&ref_sg.bravais_lattice, &inv);
let inv_lat = mat_inverse_matrix_d3(lattice, 0.0).ok();
if let Some(inv_l) = inv_lat {
let result = mat_multiply_matrix_d3(&inv_l, &tmp);
*rigid_rot = result;
}
}
}