chemrust_core/systems/crystal_model/
mod.rs

1use nalgebra::{Matrix3, Rotation3};
2
3use crate::data::{atom::CoreAtomData, geom::coordinates::CoordData, lattice::UnitCellParameters};
4
5/// Convert fractional coordinates to cartesian by reference to the unit cell
6/// parameters.
7/// # Return
8/// - If the input data is mix of fractional coordinate and cartesian coordinate
9/// It should be better to refuse doing anything and return `None` to indicate.
10pub fn frac_to_cart_coords<T: UnitCellParameters, U: CoreAtomData>(
11    lattice_parameters: T,
12    atoms_data: U,
13) -> Option<Vec<CoordData>> {
14    let cell_tensor = lattice_parameters.lattice_bases();
15    let all_is_frac = atoms_data
16        .coords_repr()
17        .iter()
18        .all(|coord| coord.is_fractional());
19    if !all_is_frac {
20        None
21    } else {
22        Some(
23            atoms_data
24                .coords_repr()
25                .iter()
26                .map(|coord| CoordData::Fractional(cell_tensor * coord.raw_data()))
27                .collect(),
28        )
29    }
30}
31
32pub fn rotated_lattice_tensor<T: UnitCellParameters>(
33    lattice_parameters: &T,
34    rotation: Rotation3<f64>,
35) -> Matrix3<f64> {
36    rotation.matrix() * lattice_parameters.lattice_bases()
37}
38
39#[cfg(test)]
40mod test {
41    use std::f64::consts::FRAC_PI_4;
42
43    use nalgebra::{Matrix3, Point3, Rotation3, Vector3};
44
45    use crate::data::{
46        geom::coordinates::CoordData,
47        lattice::{LatticeVectors, UnitCellParameters},
48    };
49
50    #[test]
51    fn test_rotation_of_frac_coord() {
52        let p = CoordData::Fractional(Point3::new(
53            0.0756034347004260,
54            0.0756034355668187,
55            0.5000000004346841,
56        ));
57        #[allow(clippy::excessive_precision)]
58        let lattice_vector = LatticeVectors::new(Matrix3::from_columns(&[
59            Vector3::new(
60                18.931530020488704480_f64,
61                -0.000000000000003553_f64,
62                0.000000000000000000_f64,
63            ),
64            Vector3::new(
65                -9.465765010246645517_f64,
66                16.395185930251127360_f64,
67                0.000000000000000000_f64,
68            ),
69            Vector3::new(
70                0.000000000000000000_f64,
71                0.000000000000000000_f64,
72                9.999213039981000861_f64,
73            ),
74        ]));
75        let rotation = Rotation3::from_axis_angle(&Vector3::z_axis(), FRAC_PI_4);
76        let cart_p = lattice_vector.lattice_bases() * p.raw_data();
77        let rot_cart_p = rotation.matrix() * cart_p;
78        let back_to_frac_p = lattice_vector.lattice_bases().try_inverse().unwrap() * rot_cart_p;
79        let rot = lattice_vector.lattice_bases().try_inverse().unwrap()
80            * rotation.matrix()
81            * lattice_vector.lattice_bases();
82        let rot_frac_p = rot * p.raw_data();
83        println!(
84            "Direct rotate frac_p: {:.3}, cart rotate back to frac_p: {:.3}",
85            rot_frac_p, back_to_frac_p
86        );
87    }
88}