use bon::Builder;
use castep_cell_fmt::{
CResult, Cell, CellValue, Error, ToCell, ToCellFile,
parse::{FromBlock, FromCellFile},
query::find_block,
};
use crate::cell::{
bz_sampling_kpoints::{
BSKpointList, BsKpointPath, KpointsList, MagresKpointsList, OpticsKpointsList,
},
constraints::{FixAllCell, FixAllIons, FixCOM, IonicConstraints, NonlinearConstraints},
external_fields::{ExternalEfield, ExternalPressure},
lattice_param::LatticeCart,
phonon::{
PhononFineKpointList, PhononGammaDirections, PhononKpointList, PhononKpointPath,
PhononSupercellMatrix, SupercellKpointListCastep,
},
positions::{PositionsAbs, PositionsFrac},
species::{HubbardU, SedcCustomParams, SpeciesLcaoStates, SpeciesMass, SpeciesPot, SpeciesQ},
symmetry::SymmetryOps,
velocities::IonicVelocities,
};
#[derive(Debug, Clone)]
pub enum Lattice {
Cart(LatticeCart),
}
impl ToCell for Lattice {
fn to_cell(&self) -> Cell<'_> {
match self {
Lattice::Cart(cart) => cart.to_cell(),
}
}
}
#[derive(Debug, Clone)]
pub enum Positions {
Frac(PositionsFrac),
Abs(PositionsAbs),
}
impl ToCell for Positions {
fn to_cell(&self) -> Cell<'_> {
match self {
Positions::Frac(frac) => frac.to_cell(),
Positions::Abs(abs) => abs.to_cell(),
}
}
}
#[allow(clippy::duplicated_attributes)]
#[derive(Debug, Clone, Builder)]
#[builder(on(Lattice, into), on(Positions, into))]
pub struct CellDocument {
pub lattice: Lattice,
pub positions: Positions,
pub kpoints_list: Option<KpointsList>,
pub bs_kpoint_path: Option<BsKpointPath>,
pub bs_kpoints_list: Option<BSKpointList>,
pub optics_kpoints_list: Option<OpticsKpointsList>,
pub magres_kpoints_list: Option<MagresKpointsList>,
pub symmetry_ops: Option<SymmetryOps>,
pub fix_com: Option<FixCOM>,
pub ionic_constraints: Option<IonicConstraints>,
pub nonlinear_constraints: Option<NonlinearConstraints>,
pub fix_all_ions: Option<FixAllIons>,
pub fix_all_cell: Option<FixAllCell>,
pub external_efield: Option<ExternalEfield>,
pub external_pressure: Option<ExternalPressure>,
pub species_mass: Option<SpeciesMass>,
pub species_pot: Option<SpeciesPot>,
pub species_lcao_states: Option<SpeciesLcaoStates>,
pub species_q: Option<SpeciesQ>,
pub hubbard_u: Option<HubbardU>,
pub sedc_custom_params: Option<SedcCustomParams>,
pub phonon_kpoint_list: Option<PhononKpointList>,
pub phonon_kpoint_path: Option<PhononKpointPath>,
pub phonon_gamma_directions: Option<PhononGammaDirections>,
pub phonon_fine_kpoint_list: Option<PhononFineKpointList>,
pub phonon_supercell_matrix: Option<PhononSupercellMatrix>,
pub supercell_kpoint_list: Option<SupercellKpointListCastep>,
pub ionic_velocities: Option<IonicVelocities>,
}
impl FromCellFile for CellDocument {
fn from_cell_file(cells: &[Cell<'_>]) -> CResult<Self> {
let lattice_rows = find_block(cells, "LATTICE_CART")?;
let lattice = Lattice::Cart(LatticeCart::from_block_rows(lattice_rows)?);
let positions = if find_block(cells, "POSITIONS_FRAC").is_ok() {
Positions::Frac(PositionsFrac::from_block_rows(find_block(
cells,
"POSITIONS_FRAC",
)?)?)
} else {
Positions::Abs(PositionsAbs::from_block_rows(find_block(
cells,
"POSITIONS_ABS",
)?)?)
};
let kpoints_list = find_block(cells, "KPOINTS_LIST")
.ok()
.map(|rows| KpointsList::from_block_rows(rows))
.transpose()?;
let bs_kpoint_path = find_block(cells, "BS_KPOINT_PATH")
.ok()
.map(|rows| BsKpointPath::from_block_rows(rows))
.transpose()?;
let bs_kpoints_list = find_block(cells, "BS_KPOINTS_LIST")
.ok()
.map(|rows| BSKpointList::from_block_rows(rows))
.transpose()?;
let optics_kpoints_list = find_block(cells, "OPTICS_KPOINTS_LIST")
.ok()
.map(|rows| OpticsKpointsList::from_block_rows(rows))
.transpose()?;
let magres_kpoints_list = find_block(cells, "MAGRES_KPOINTS_LIST")
.ok()
.map(|rows| MagresKpointsList::from_block_rows(rows))
.transpose()?;
let symmetry_ops = find_block(cells, "SYMMETRY_OPS")
.ok()
.map(|rows| SymmetryOps::from_block_rows(rows))
.transpose()?;
let fix_com = cells.iter().find_map(|c| {
if let Cell::KeyValue(k, _v) = c
&& k.eq_ignore_ascii_case("FIX_COM")
{
return Some(FixCOM(true));
}
None
});
let ionic_constraints = find_block(cells, "IONIC_CONSTRAINTS")
.ok()
.map(|rows| IonicConstraints::from_block_rows(rows))
.transpose()?;
let nonlinear_constraints = find_block(cells, "NONLINEAR_CONSTRAINTS")
.ok()
.map(|rows| NonlinearConstraints::from_block_rows(rows))
.transpose()?;
let fix_all_ions = cells.iter().find_map(|c| {
if let Cell::KeyValue(k, _v) = c
&& k.eq_ignore_ascii_case("FIX_ALL_IONS")
{
return Some(FixAllIons(true));
}
None
});
let fix_all_cell = cells.iter().find_map(|c| {
if let Cell::KeyValue(k, _v) = c
&& k.eq_ignore_ascii_case("FIX_ALL_CELL")
{
return Some(FixAllCell(true));
}
None
});
let external_efield = find_block(cells, "EXTERNAL_EFIELD")
.ok()
.map(|rows| ExternalEfield::from_block_rows(rows))
.transpose()?;
let external_pressure = find_block(cells, "EXTERNAL_PRESSURE")
.ok()
.map(|rows| ExternalPressure::from_block_rows(rows))
.transpose()?;
let species_mass = find_block(cells, "SPECIES_MASS")
.ok()
.map(|rows| SpeciesMass::from_block_rows(rows))
.transpose()?;
let species_pot = find_block(cells, "SPECIES_POT")
.ok()
.map(|rows| SpeciesPot::from_block_rows(rows))
.transpose()?;
let species_lcao_states = find_block(cells, "SPECIES_LCAO_STATES")
.ok()
.map(|rows| SpeciesLcaoStates::from_block_rows(rows))
.transpose()?;
let species_q = find_block(cells, "SPECIES_Q")
.ok()
.map(|rows| SpeciesQ::from_block_rows(rows))
.transpose()?;
let hubbard_u = find_block(cells, "HUBBARD_U")
.ok()
.map(|rows| HubbardU::from_block_rows(rows))
.transpose()?;
let sedc_custom_params = find_block(cells, "SEDC_CUSTOM_PARAMS")
.ok()
.map(|rows| SedcCustomParams::from_block_rows(rows))
.transpose()?;
let phonon_kpoint_list = find_block(cells, "PHONON_KPOINT_LIST")
.ok()
.map(|rows| PhononKpointList::from_block_rows(rows))
.transpose()?;
let phonon_kpoint_path = find_block(cells, "PHONON_KPOINT_PATH")
.ok()
.map(|rows| PhononKpointPath::from_block_rows(rows))
.transpose()?;
let phonon_gamma_directions = find_block(cells, "PHONON_GAMMA_DIRECTIONS")
.ok()
.map(|rows| PhononGammaDirections::from_block_rows(rows))
.transpose()?;
let phonon_fine_kpoint_list = find_block(cells, "PHONON_FINE_KPOINT_LIST")
.ok()
.map(|rows| PhononFineKpointList::from_block_rows(rows))
.transpose()?;
let phonon_supercell_matrix = find_block(cells, "PHONON_SUPERCELL_MATRIX")
.ok()
.map(|rows| PhononSupercellMatrix::from_block_rows(rows))
.transpose()?;
let supercell_kpoint_list = find_block(cells, "SUPERCELL_KPOINT_LIST")
.ok()
.map(|rows| SupercellKpointListCastep::from_block_rows(rows))
.transpose()?;
let ionic_velocities = find_block(cells, "IONIC_VELOCITIES")
.ok()
.map(|rows| IonicVelocities::from_block_rows(rows))
.transpose()?;
Ok(CellDocument {
lattice,
positions,
kpoints_list,
bs_kpoint_path,
bs_kpoints_list,
optics_kpoints_list,
magres_kpoints_list,
symmetry_ops,
fix_com,
ionic_constraints,
nonlinear_constraints,
fix_all_ions,
fix_all_cell,
external_efield,
external_pressure,
species_mass,
species_pot,
species_lcao_states,
species_q,
hubbard_u,
sedc_custom_params,
phonon_kpoint_list,
phonon_kpoint_path,
phonon_gamma_directions,
phonon_fine_kpoint_list,
phonon_supercell_matrix,
supercell_kpoint_list,
ionic_velocities,
})
}
}
impl ToCellFile for CellDocument {
fn to_cell_file(&self) -> Vec<Cell<'_>> {
let mut cells = vec![self.lattice.to_cell(), self.positions.to_cell()];
if let Some(kp) = &self.kpoints_list {
cells.push(kp.to_cell());
}
if let Some(bp) = &self.bs_kpoint_path {
cells.push(bp.to_cell());
}
if let Some(bk) = &self.bs_kpoints_list {
cells.push(bk.to_cell());
}
if let Some(ok) = &self.optics_kpoints_list {
cells.push(ok.to_cell());
}
if let Some(mk) = &self.magres_kpoints_list {
cells.push(mk.to_cell());
}
if let Some(sym) = &self.symmetry_ops {
cells.push(sym.to_cell());
}
if let Some(_fc) = &self.fix_com {
cells.push(Cell::Flag("FIX_COM"));
}
if let Some(ic) = &self.ionic_constraints {
cells.push(ic.to_cell());
}
if let Some(nc) = &self.nonlinear_constraints {
cells.push(nc.to_cell());
}
if let Some(_fi) = &self.fix_all_ions {
cells.push(Cell::Flag("FIX_ALL_IONS"));
}
if let Some(_fc) = &self.fix_all_cell {
cells.push(Cell::Flag("FIX_ALL_CELL"));
}
if let Some(ef) = &self.external_efield {
cells.push(ef.to_cell());
}
if let Some(ep) = &self.external_pressure {
cells.push(ep.to_cell());
}
if let Some(sm) = &self.species_mass {
cells.push(sm.to_cell());
}
if let Some(sp) = &self.species_pot {
cells.push(sp.to_cell());
}
if let Some(sl) = &self.species_lcao_states {
cells.push(sl.to_cell());
}
if let Some(sq) = &self.species_q {
cells.push(sq.to_cell());
}
if let Some(hu) = &self.hubbard_u {
cells.push(hu.to_cell());
}
if let Some(sc) = &self.sedc_custom_params {
cells.push(sc.to_cell());
}
if let Some(pk) = &self.phonon_kpoint_list {
cells.push(pk.to_cell());
}
if let Some(pp) = &self.phonon_kpoint_path {
cells.push(pp.to_cell());
}
if let Some(pg) = &self.phonon_gamma_directions {
cells.push(pg.to_cell());
}
if let Some(pf) = &self.phonon_fine_kpoint_list {
cells.push(pf.to_cell());
}
if let Some(pm) = &self.phonon_supercell_matrix {
cells.push(pm.to_cell());
}
if let Some(sk) = &self.supercell_kpoint_list {
cells.push(sk.to_cell());
}
if let Some(iv) = &self.ionic_velocities {
cells.push(iv.to_cell());
}
cells
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore]
fn test_parse_mg2sio4_cell() {
let input = "";
let doc = castep_cell_fmt::parse::<CellDocument>(input).expect("Failed to parse");
assert!(matches!(doc.lattice, Lattice::Cart(_)));
assert!(matches!(doc.positions, Positions::Frac(_)));
assert!(doc.kpoints_list.is_some());
assert!(doc.symmetry_ops.is_some());
assert!(doc.fix_com.is_some());
assert!(doc.ionic_constraints.is_some());
assert!(doc.external_efield.is_some());
assert!(doc.species_mass.is_some());
assert!(doc.species_pot.is_some());
assert!(doc.species_lcao_states.is_some());
}
}