castep_cell_io/cell_document/
mod.rs

1#[allow(unused_imports)]
2mod chemrust_impl;
3pub mod params;
4pub mod sections;
5pub mod units;
6
7use std::fs::read_to_string;
8use std::path::PathBuf;
9use std::{fmt::Display, fs, io::Error, path::Path};
10
11use castep_periodic_table::data::ELEMENT_TABLE;
12use castep_periodic_table::element::{ElementSymbol, LookupElement};
13use chemrust_core::data::atom::CoreAtomData;
14use chemrust_core::data::lattice::{CrystalModel, UnitCellParameters};
15use chemrust_core::data::symmetry::SymmetryInfo;
16
17pub use sections::constraints::{FixAllCell, FixCom, IonicConstraintsBlock};
18pub use sections::external_fields::{ExtEFieldBlock, ExtPressureBlock};
19pub use sections::ionic_positions::{IonicPosition, IonicPositionBlock, Mixture};
20pub use sections::kpoint_settings::*;
21pub use sections::lattice_parameters::{LatticeABC, LatticeCart, LatticeParam, LatticeParamBlock};
22pub use sections::species_characters::{
23    LCAOBasis, SpeciesLCAOStatesBlock, SpeciesMass, SpeciesMassBlock, SpeciesPot, SpeciesPotBlock,
24};
25
26pub use chemrust_impl::to_cell_document;
27pub use sections::CellEntries;
28pub use sections::CellEssentials;
29
30use crate::{CellParseError, CellParser};
31
32/// A structure to represent the `.cell` file.
33#[derive(Debug, Clone)]
34pub struct CellDocument {
35    model_description: CellEssentials,
36    other_entries: Option<Vec<CellEntries>>,
37}
38
39impl CrystalModel for CellDocument {
40    fn get_cell_parameters(&self) -> &impl UnitCellParameters {
41        self.model_description().lattice_block()
42    }
43
44    fn get_atom_data(&self) -> &impl CoreAtomData {
45        self.model_description().ionic_pos_block()
46    }
47
48    fn get_cell_parameters_mut(&mut self) -> &mut impl UnitCellParameters {
49        self.model_description_mut().lattice_block_mut()
50    }
51
52    fn get_atom_data_mut(&mut self) -> &mut impl CoreAtomData {
53        self.model_description_mut().ionic_pos_block_mut()
54    }
55}
56
57impl SymmetryInfo for CellDocument {
58    fn get_space_group_it_num(&self) -> u8 {
59        if matches!(
60            self.other_entries().and_then(|entries| {
61                entries
62                    .iter()
63                    .find(|entry| matches!(entry, CellEntries::SymmetryOps(_)))
64            }),
65            Some(CellEntries::SymmetryOps(..))
66        ) {
67            todo!()
68        } else {
69            1_u8
70        }
71    }
72
73    fn make_symmetry(&self) -> bool {
74        matches!(
75            self.other_entries().and_then(|entries| {
76                entries
77                    .iter()
78                    .find(|entry| matches!(entry, CellEntries::SymmetryOps(_)))
79            }),
80            Some(CellEntries::SymmetryOps(..))
81        )
82    }
83}
84
85impl CellDocument {
86    pub fn new(model_description: CellEssentials) -> Self {
87        Self {
88            model_description,
89            other_entries: None,
90        }
91    }
92
93    pub fn parse_from_file<P: AsRef<Path>>(&self, file_path: P) -> Result<Self, CellParseError> {
94        let content = read_to_string(file_path).map_err(|_| CellParseError::FileReadingFailure)?;
95        CellParser::from(&content).parse()
96    }
97
98    pub fn write_out<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
99        fs::write(path, self.to_string())
100    }
101
102    pub fn other_entries(&self) -> Option<&Vec<CellEntries>> {
103        self.other_entries.as_ref()
104    }
105
106    pub fn other_entries_mut(&mut self) -> &mut Option<Vec<CellEntries>> {
107        &mut self.other_entries
108    }
109
110    pub fn set_entries(&mut self, entries: Option<Vec<CellEntries>>) {
111        self.other_entries = entries;
112    }
113    pub fn get_elements(&self) -> Vec<ElementSymbol> {
114        let mut symbols: Vec<ElementSymbol> = self
115            .model_description()
116            .ionic_pos_block()
117            .positions()
118            .iter()
119            .map(|pos| pos.symbol())
120            .collect();
121        symbols.sort();
122        symbols.dedup();
123        symbols
124    }
125
126    /// Get total spins of the species in cell
127    /// Change! Accumulate all spins of species
128    pub fn total_spin(&self) -> u32 {
129        self.model_description()
130            .ionic_pos_block()
131            .positions()
132            .iter()
133            .map(|&pos| ELEMENT_TABLE.get_by_symbol(pos.symbol()).spin() as u32)
134            .sum::<u32>()
135    }
136
137    pub fn model_description(&self) -> &CellEssentials {
138        &self.model_description
139    }
140
141    pub fn model_description_mut(&mut self) -> &mut CellEssentials {
142        &mut self.model_description
143    }
144
145    pub fn set_model_description(&mut self, model_description: CellEssentials) {
146        self.model_description = model_description;
147    }
148
149    pub fn get_potential_paths<P: AsRef<Path>>(&self, potentials_loc: P) -> Vec<PathBuf> {
150        self.get_elements()
151            .iter()
152            .map(|&elm| {
153                let potential_file = ELEMENT_TABLE.get_by_symbol(elm).potential();
154                potentials_loc.as_ref().join(potential_file)
155            })
156            .collect()
157    }
158}
159
160impl Display for CellDocument {
161    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162        let entries = self
163            .other_entries()
164            .map(|e| {
165                e.iter()
166                    .map(|item| format!("{}", item))
167                    .collect::<Vec<String>>()
168                    .join("\n")
169            })
170            .unwrap_or_default();
171        let content = [
172            format!("{}", self.model_description().lattice_block()),
173            format!("{}", self.model_description().ionic_pos_block()),
174            entries,
175        ]
176        .concat();
177        write!(f, "{}", content)
178    }
179}