use std::path::PathBuf;
use ndarray::{Array2, ShapeBuilder};
use refeff_core::RhorrpDensityGridInput;
use crate::{IoError, Result};
use super::common::FEFF_BOHR_ANGSTROM;
use super::parser::ControlParser;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct BandInput {
pub mband: i32,
pub energy_mesh: BandEnergyMesh,
pub nkp: i32,
pub ikpath: i32,
pub freeprop: bool,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct BandEnergyMesh {
pub emin: f64,
pub emax: f64,
pub estep: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct DensityInput {
pub grids: Vec<DensityGrid>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct DensityGrid {
pub kind: DensityGridKind,
pub filename: String,
pub origin: [f64; 3],
pub core: bool,
pub axes: Vec<DensityAxis>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct DensityGridBohr {
pub kind: DensityGridKind,
pub filename: String,
pub origin: [f64; 3],
pub core: bool,
pub axes: Array2<f64>,
pub points_per_axis: Vec<usize>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DensityGridKind {
Line,
Plane,
Volume,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DensityAxis {
pub vector: [f64; 3],
pub points: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FullSpectrumInput {
pub m_full_spectrum: i32,
}
#[derive(Debug, Clone, PartialEq)]
pub struct OpconsInput {
pub run_opcons: bool,
pub print_eps: bool,
pub number_densities: Vec<f64>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ReciprocalInput {
pub ispace: i32,
pub cell: Option<ReciprocalCell>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ReciprocalCell {
pub lattice_vectors: [[f64; 3]; 3],
pub volume_scale: f64,
pub imaginary_energy: f64,
pub core_hole_strength: f64,
pub lattice_name: String,
pub space_group_hm: String,
pub space_group: i32,
pub atom_count: usize,
pub absorber: i32,
pub core_hole: i32,
pub k_mesh: ReciprocalKMesh,
pub positions: Vec<[f64; 3]>,
pub potentials: Vec<i32>,
pub labels: Vec<String>,
pub stretch: [f64; 3],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ReciprocalKMesh {
pub total: i32,
pub x: i32,
pub y: i32,
pub z: i32,
pub kind: i32,
pub use_symmetry: bool,
}
impl BandInput {
pub fn parse_str(source: impl Into<PathBuf>, text: &str) -> Result<Self> {
let mut parser = ControlParser::new(source.into(), text);
parser.parse_band()
}
}
impl DensityInput {
pub fn parse_str(source: impl Into<PathBuf>, text: &str) -> Result<Self> {
let mut parser = ControlParser::new(source.into(), text);
parser.parse_density()
}
pub fn to_bohr_grids(&self) -> Result<Vec<DensityGridBohr>> {
self.grids.iter().map(DensityGrid::to_bohr_grid).collect()
}
}
impl FullSpectrumInput {
pub fn parse_str(source: impl Into<PathBuf>, text: &str) -> Result<Self> {
let mut parser = ControlParser::new(source.into(), text);
parser.parse_fullspectrum()
}
}
impl OpconsInput {
pub fn parse_str(source: impl Into<PathBuf>, text: &str) -> Result<Self> {
let mut parser = ControlParser::new(source.into(), text);
parser.parse_opcons()
}
}
impl ReciprocalInput {
pub fn parse_str(source: impl Into<PathBuf>, text: &str) -> Result<Self> {
let mut parser = ControlParser::new(source.into(), text);
parser.parse_reciprocal()
}
}
impl DensityGrid {
pub fn to_bohr_grid(&self) -> Result<DensityGridBohr> {
let dimensions = self.kind.dimensions();
if self.axes.len() != dimensions {
return Err(IoError::Parse {
path: "density.inp".into(),
line: 0,
message: format!(
"density grid {:?} requires {dimensions} axis row(s), got {}",
self.kind,
self.axes.len()
),
});
}
let mut axes = Array2::zeros((3, dimensions).f());
let mut points_per_axis = Vec::with_capacity(dimensions);
for (dimension, axis) in self.axes.iter().enumerate() {
for coordinate in 0..3 {
axes[(coordinate, dimension)] = axis.vector[coordinate] / FEFF_BOHR_ANGSTROM;
}
points_per_axis.push(axis.points);
}
Ok(DensityGridBohr {
kind: self.kind,
filename: self.filename.clone(),
origin: [
self.origin[0] / FEFF_BOHR_ANGSTROM,
self.origin[1] / FEFF_BOHR_ANGSTROM,
self.origin[2] / FEFF_BOHR_ANGSTROM,
],
core: self.core,
axes,
points_per_axis,
})
}
}
impl DensityGridBohr {
#[must_use]
pub fn as_rhorrp_input(&self) -> RhorrpDensityGridInput<'_> {
RhorrpDensityGridInput {
origin: self.origin,
axes: self.axes.view(),
points_per_axis: &self.points_per_axis,
}
}
}
impl DensityGridKind {
pub(super) fn as_command(self) -> &'static str {
match self {
DensityGridKind::Line => "line",
DensityGridKind::Plane => "plane",
DensityGridKind::Volume => "volume",
}
}
pub(super) fn dimensions(self) -> usize {
match self {
DensityGridKind::Line => 1,
DensityGridKind::Plane => 2,
DensityGridKind::Volume => 3,
}
}
}