use ndarray::{Array1, Array2, Array3, Array4};
use crate::error::Result;
use super::validate::validate_dym;
#[derive(Debug, Clone, PartialEq)]
pub enum DymCoordinates {
Cartesian(Array2<f64>),
Reduced {
reduced: Array2<f64>,
cell: Array2<f64>,
},
}
#[derive(Debug, Clone, PartialEq)]
pub struct DymUniqueAtom {
pub atom_type: i32,
pub center_atom_indices: Array1<usize>,
pub weights: Array1<f64>,
pub coordinates: Array2<f64>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct DymType2Metadata {
pub cell_atom_count: usize,
pub unique_atoms: Vec<DymUniqueAtom>,
}
impl DymCoordinates {
#[must_use]
pub fn cartesian_positions(&self) -> Array2<f64> {
match self {
Self::Cartesian(positions) => positions.clone(),
Self::Reduced { reduced, cell } => reduced.dot(cell),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DymData {
pub dym_type: i32,
pub atomic_numbers: Array1<i32>,
pub atomic_masses: Array1<f64>,
pub coordinates: DymCoordinates,
pub force_constants: Array4<f64>,
pub type2_metadata: Option<DymType2Metadata>,
pub dipole_derivatives: Option<Array3<f64>>,
}
impl DymData {
#[must_use]
pub fn atom_count(&self) -> usize {
self.atomic_numbers.len()
}
pub fn mass_weighted_dynamical_matrix(&self) -> Result<Array2<f64>> {
validate_dym(self)?;
let atom_count = self.atom_count();
let mut matrix = Array2::zeros((3 * atom_count, 3 * atom_count));
for i_atom in 0..atom_count {
for j_atom in 0..atom_count {
let scale = (self.atomic_masses[i_atom] * self.atomic_masses[j_atom]).sqrt();
for row in 0..3 {
for column in 0..3 {
matrix[[i_atom + atom_count * row, j_atom + atom_count * column]] =
self.force_constants[[i_atom, j_atom, row, column]] / scale;
}
}
}
}
Ok(matrix)
}
}