use bon::Builder;
use castep_cell_fmt::{
CResult, Cell, CellValue, Error, FromKeyValue, ToCell, ToCellFile,
parse::{FromBlock, FromCellFile},
query::{find_block, find_block_any, has_flag},
};
use crate::cell::{
bz_sampling_kpoints::{
BSKpointList, BsKpointPath, BsKpointPathSpacing, KpointsList, KpointsMpGrid,
KpointsMpOffset, KpointsMpSpacing, MagresKpointsList, OpticsKpointsList,
SpectralKpointPath, SpectralKpointsList, SpectralKpointPathSpacing,
SpectralKpointsMpGrid, SpectralKpointsMpSpacing, SpectralKpointsMpOffset,
},
constraints::{
CellConstraints, FixAllCell, FixAllIons, FixCOM, FixVOL, IonicConstraints,
NonlinearConstraints,
},
external_fields::{ExternalEfield, ExternalPressure},
lattice_param::{LatticeABC, LatticeCart},
phonon::{
PhononFineKpointList, PhononFineKpointPath, PhononFineKpointPathSpacing,
PhononFineKpointsMpGrid, PhononFineKpointsMpOffset, PhononFineKpointsMpSpacing,
PhononGammaDirections, PhononKpointList, PhononKpointPath,
PhononKpointsMpGrid, PhononKpointsMpOffset, PhononKpointsMpSpacing,
PhononSupercellMatrix, SupercellKpointListCastep,
},
positions::{PositionsAbs, PositionsFrac},
species::{HubbardU, SedcCustomParams, SpeciesLcaoStates, SpeciesMass, SpeciesPot, SpeciesQ},
symmetry::{SymmetryGenerate, SymmetryOps, SymmetryTol},
velocities::IonicVelocities,
};
use cell_document_builder::IsComplete;
#[derive(Debug, Clone)]
pub enum Lattice {
Cart(LatticeCart),
Abc(LatticeABC),
}
impl ToCell for Lattice {
fn to_cell(&self) -> Cell<'_> {
match self {
Lattice::Cart(cart) => cart.to_cell(),
Lattice::Abc(abc) => abc.to_cell(),
}
}
}
impl From<LatticeCart> for Lattice {
fn from(v: LatticeCart) -> Self {
Lattice::Cart(v)
}
}
impl From<LatticeABC> for Lattice {
fn from(v: LatticeABC) -> Self {
Lattice::Abc(v)
}
}
#[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), finish_fn(vis = "", name = build_internal))]
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 kpoints_mp_grid: Option<KpointsMpGrid>,
pub kpoints_mp_spacing: Option<KpointsMpSpacing>,
pub kpoints_mp_offset: Option<KpointsMpOffset>,
pub bs_kpoint_path_spacing: Option<BsKpointPathSpacing>,
pub spectral_kpoint_path: Option<SpectralKpointPath>,
pub spectral_kpoints_list: Option<SpectralKpointsList>,
pub spectral_kpoint_path_spacing: Option<SpectralKpointPathSpacing>,
pub spectral_kpoints_mp_grid: Option<SpectralKpointsMpGrid>,
pub spectral_kpoints_mp_spacing: Option<SpectralKpointsMpSpacing>,
pub spectral_kpoints_mp_offset: Option<SpectralKpointsMpOffset>,
pub symmetry_ops: Option<SymmetryOps>,
pub symmetry_tol: Option<SymmetryTol>,
pub symmetry_generate: Option<SymmetryGenerate>,
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 fix_vol: Option<FixVOL>,
pub cell_constraints: Option<CellConstraints>,
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_kpoints_mp_grid: Option<PhononKpointsMpGrid>,
pub phonon_kpoints_mp_spacing: Option<PhononKpointsMpSpacing>,
pub phonon_kpoints_mp_offset: Option<PhononKpointsMpOffset>,
pub phonon_fine_kpoint_path: Option<PhononFineKpointPath>,
pub phonon_fine_kpoint_path_spacing: Option<PhononFineKpointPathSpacing>,
pub phonon_fine_kpoints_mp_grid: Option<PhononFineKpointsMpGrid>,
pub phonon_fine_kpoints_mp_spacing: Option<PhononFineKpointsMpSpacing>,
pub phonon_fine_kpoints_mp_offset: Option<PhononFineKpointsMpOffset>,
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<S: cell_document_builder::IsComplete> CellDocumentBuilder<S> {
pub fn build(self) -> CResult<CellDocument> {
let doc = self.build_internal();
let kpoint_count = [doc.kpoints_list.is_some(), doc.kpoints_mp_grid.is_some(), doc.kpoints_mp_spacing.is_some()]
.iter().filter(|&&x| x).count();
if kpoint_count > 1 {
return Err(Error::Message("At most one of kpoints_list, kpoints_mp_grid, kpoints_mp_spacing may be specified".into()));
}
let spectral_count = [
doc.spectral_kpoint_path.is_some(),
doc.spectral_kpoints_mp_grid.is_some(),
doc.spectral_kpoints_mp_spacing.is_some(),
doc.spectral_kpoints_list.is_some(),
doc.bs_kpoint_path.is_some(),
doc.bs_kpoints_list.is_some(),
].iter().filter(|&&x| x).count();
if spectral_count > 1 {
return Err(Error::Message("At most one of spectral_kpoint_path, spectral_kpoints_mp_grid, spectral_kpoints_mp_spacing, spectral_kpoints_list, bs_kpoint_path, bs_kpoints_list may be specified".into()));
}
let phonon_count = [doc.phonon_kpoint_path.is_some(), doc.phonon_kpoint_list.is_some()]
.iter().filter(|&&x| x).count();
if phonon_count > 1 {
return Err(Error::Message("At most one of phonon_kpoint_path, phonon_kpoint_list may be specified".into()));
}
let symmetry_count = [doc.symmetry_generate.is_some(), doc.symmetry_ops.is_some()]
.iter().filter(|&&x| x).count();
if symmetry_count > 1 {
return Err(Error::Message("At most one of symmetry_generate, symmetry_ops may be specified".into()));
}
Ok(doc)
}
}
impl FromCellFile for CellDocument {
fn from_cell_file(cells: &[Cell<'_>]) -> CResult<Self> {
let has_lattice_cart = find_block(cells, "LATTICE_CART").is_ok();
let has_lattice_abc = find_block(cells, "LATTICE_ABC").is_ok();
if has_lattice_cart && has_lattice_abc {
return Err(Error::Message(
"Both LATTICE_CART and LATTICE_ABC are specified. Only one lattice specification is allowed."
.into(),
));
}
let lattice = if has_lattice_cart {
Lattice::Cart(LatticeCart::from_block_rows(find_block(
cells,
"LATTICE_CART",
)?)?)
} else {
let rows = find_block(cells, "LATTICE_ABC")?;
Lattice::Abc(LatticeABC::from_block_rows(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_any(cells, &["KPOINT_LIST", "KPOINTS_LIST"])
.ok()
.map(|rows| KpointsList::from_block_rows(rows))
.transpose()?;
let optics_kpoints_list = find_block_any(cells, &["OPTICS_KPOINT_LIST", "OPTICS_KPOINTS_LIST"])
.ok()
.map(|rows| OpticsKpointsList::from_block_rows(rows))
.transpose()?;
let magres_kpoints_list = find_block_any(cells, &["MAGRES_KPOINT_LIST", "MAGRES_KPOINTS_LIST"])
.ok()
.map(|rows| MagresKpointsList::from_block_rows(rows))
.transpose()?;
let spectral_kpoint_path = find_block_any(
cells,
&["SPECTRAL_KPOINT_PATH", "SPECTRAL_KPOINTS_PATH", "BS_KPOINT_PATH", "BS_KPOINTS_PATH"],
)
.ok()
.map(|rows| SpectralKpointPath::from_block_rows(rows))
.transpose()?;
let spectral_kpoints_list = find_block_any(
cells,
&["SPECTRAL_KPOINT_LIST", "SPECTRAL_KPOINTS_LIST", "BS_KPOINT_LIST", "BS_KPOINTS_LIST"],
)
.ok()
.map(|rows| SpectralKpointsList::from_block_rows(rows))
.transpose()?;
let bs_kpoint_path = if spectral_kpoint_path.is_some() {
None
} else {
find_block_any(cells, &["BS_KPOINT_PATH", "BS_KPOINTS_PATH"])
.ok()
.map(|rows| BsKpointPath::from_block_rows(rows))
.transpose()?
};
let bs_kpoints_list = if spectral_kpoints_list.is_some() {
None
} else {
find_block_any(cells, &["BS_KPOINT_LIST", "BS_KPOINTS_LIST"])
.ok()
.map(|rows| BSKpointList::from_block_rows(rows))
.transpose()?
};
let bs_kpoint_path_spacing = BsKpointPathSpacing::from_cells(cells)?;
let kpoints_mp_grid = KpointsMpGrid::from_cells(cells)?;
let kpoints_mp_spacing = KpointsMpSpacing::from_cells(cells)?;
let kpoints_mp_offset = KpointsMpOffset::from_cells(cells)?;
let spectral_kpoint_path_spacing = SpectralKpointPathSpacing::from_cells(cells)?;
let spectral_kpoints_mp_grid = SpectralKpointsMpGrid::from_cells(cells)?;
let spectral_kpoints_mp_spacing = SpectralKpointsMpSpacing::from_cells(cells)?;
let spectral_kpoints_mp_offset = SpectralKpointsMpOffset::from_cells(cells)?;
let symmetry_ops = find_block(cells, "SYMMETRY_OPS")
.ok()
.map(|rows| SymmetryOps::from_block_rows(rows))
.transpose()?;
let symmetry_tol = SymmetryTol::from_cells(cells)?;
let symmetry_generate = if has_flag(cells, "SYMMETRY_GENERATE") {
Some(SymmetryGenerate)
} else {
None
};
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 fix_vol = FixVOL::from_cells(cells)?;
let cell_constraints = find_block(cells, "CELL_CONSTRAINTS")
.ok()
.map(|rows| CellConstraints::from_block_rows(rows))
.transpose()?;
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_any(cells, &["PHONON_KPOINT_LIST", "PHONON_KPOINTS_LIST"])
.ok()
.map(|rows| PhononKpointList::from_block_rows(rows))
.transpose()?;
let phonon_kpoint_path = find_block_any(cells, &["PHONON_KPOINT_PATH", "PHONON_KPOINTS_PATH"])
.ok()
.map(|rows| PhononKpointPath::from_block_rows(rows))
.transpose()?;
let phonon_kpoints_mp_grid = PhononKpointsMpGrid::from_cells(cells)?;
let phonon_kpoints_mp_spacing = PhononKpointsMpSpacing::from_cells(cells)?;
let phonon_kpoints_mp_offset = PhononKpointsMpOffset::from_cells(cells)?;
let phonon_fine_kpoint_path = find_block_any(
cells,
&["PHONON_FINE_KPOINT_PATH", "PHONON_FINE_KPOINTS_PATH"],
)
.ok()
.map(|rows| PhononFineKpointPath::from_block_rows(rows))
.transpose()?;
let phonon_fine_kpoint_path_spacing = PhononFineKpointPathSpacing::from_cells(cells)?;
let phonon_fine_kpoints_mp_grid = PhononFineKpointsMpGrid::from_cells(cells)?;
let phonon_fine_kpoints_mp_spacing = PhononFineKpointsMpSpacing::from_cells(cells)?;
let phonon_fine_kpoints_mp_offset = PhononFineKpointsMpOffset::from_cells(cells)?;
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_any(cells, &["PHONON_FINE_KPOINT_LIST", "PHONON_FINE_KPOINTS_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_any(cells, &["SUPERCELL_KPOINT_LIST", "SUPERCELL_KPOINTS_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()?;
CellDocument::builder()
.lattice(lattice)
.positions(positions)
.maybe_kpoints_list(kpoints_list)
.maybe_bs_kpoint_path(bs_kpoint_path)
.maybe_bs_kpoints_list(bs_kpoints_list)
.maybe_optics_kpoints_list(optics_kpoints_list)
.maybe_magres_kpoints_list(magres_kpoints_list)
.maybe_bs_kpoint_path_spacing(bs_kpoint_path_spacing)
.maybe_kpoints_mp_grid(kpoints_mp_grid)
.maybe_kpoints_mp_spacing(kpoints_mp_spacing)
.maybe_kpoints_mp_offset(kpoints_mp_offset)
.maybe_spectral_kpoint_path(spectral_kpoint_path)
.maybe_spectral_kpoints_list(spectral_kpoints_list)
.maybe_spectral_kpoint_path_spacing(spectral_kpoint_path_spacing)
.maybe_spectral_kpoints_mp_grid(spectral_kpoints_mp_grid)
.maybe_spectral_kpoints_mp_spacing(spectral_kpoints_mp_spacing)
.maybe_spectral_kpoints_mp_offset(spectral_kpoints_mp_offset)
.maybe_symmetry_ops(symmetry_ops)
.maybe_symmetry_tol(symmetry_tol)
.maybe_symmetry_generate(symmetry_generate)
.maybe_fix_com(fix_com)
.maybe_ionic_constraints(ionic_constraints)
.maybe_nonlinear_constraints(nonlinear_constraints)
.maybe_fix_all_ions(fix_all_ions)
.maybe_fix_all_cell(fix_all_cell)
.maybe_cell_constraints(cell_constraints)
.maybe_fix_vol(fix_vol)
.maybe_external_efield(external_efield)
.maybe_external_pressure(external_pressure)
.maybe_species_mass(species_mass)
.maybe_species_pot(species_pot)
.maybe_species_lcao_states(species_lcao_states)
.maybe_species_q(species_q)
.maybe_hubbard_u(hubbard_u)
.maybe_sedc_custom_params(sedc_custom_params)
.maybe_phonon_kpoint_list(phonon_kpoint_list)
.maybe_phonon_kpoint_path(phonon_kpoint_path)
.maybe_phonon_kpoints_mp_grid(phonon_kpoints_mp_grid)
.maybe_phonon_kpoints_mp_spacing(phonon_kpoints_mp_spacing)
.maybe_phonon_kpoints_mp_offset(phonon_kpoints_mp_offset)
.maybe_phonon_fine_kpoint_path(phonon_fine_kpoint_path)
.maybe_phonon_fine_kpoint_path_spacing(phonon_fine_kpoint_path_spacing)
.maybe_phonon_fine_kpoints_mp_grid(phonon_fine_kpoints_mp_grid)
.maybe_phonon_fine_kpoints_mp_spacing(phonon_fine_kpoints_mp_spacing)
.maybe_phonon_fine_kpoints_mp_offset(phonon_fine_kpoints_mp_offset)
.maybe_phonon_gamma_directions(phonon_gamma_directions)
.maybe_phonon_fine_kpoint_list(phonon_fine_kpoint_list)
.maybe_phonon_supercell_matrix(phonon_supercell_matrix)
.maybe_supercell_kpoint_list(supercell_kpoint_list)
.maybe_ionic_velocities(ionic_velocities)
.build()
}
}
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(sp) = &self.spectral_kpoint_path {
cells.push(sp.to_cell());
}
if let Some(sl) = &self.spectral_kpoints_list {
cells.push(sl.to_cell());
}
if let Some(sps) = &self.spectral_kpoint_path_spacing {
cells.push(sps.to_cell());
}
if let Some(smg) = &self.spectral_kpoints_mp_grid {
cells.push(smg.to_cell());
}
if let Some(sms) = &self.spectral_kpoints_mp_spacing {
cells.push(sms.to_cell());
}
if let Some(smo) = &self.spectral_kpoints_mp_offset {
cells.push(smo.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(kmg) = &self.kpoints_mp_grid {
cells.push(kmg.to_cell());
}
if let Some(kms) = &self.kpoints_mp_spacing {
cells.push(kms.to_cell());
}
if let Some(kmo) = &self.kpoints_mp_offset {
cells.push(kmo.to_cell());
}
if let Some(bps) = &self.bs_kpoint_path_spacing {
cells.push(bps.to_cell());
}
if let Some(sym) = &self.symmetry_ops {
cells.push(sym.to_cell());
}
if let Some(st) = &self.symmetry_tol {
cells.push(st.to_cell());
}
if let Some(_sg) = &self.symmetry_generate {
cells.push(Cell::Flag("SYMMETRY_GENERATE"));
}
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(fv) = &self.fix_vol {
cells.push(fv.to_cell());
}
if let Some(cc) = &self.cell_constraints {
cells.push(cc.to_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(pmg) = &self.phonon_kpoints_mp_grid {
cells.push(pmg.to_cell());
}
if let Some(pms) = &self.phonon_kpoints_mp_spacing {
cells.push(pms.to_cell());
}
if let Some(pmo) = &self.phonon_kpoints_mp_offset {
cells.push(pmo.to_cell());
}
if let Some(pfp) = &self.phonon_fine_kpoint_path {
cells.push(pfp.to_cell());
}
if let Some(pfps) = &self.phonon_fine_kpoint_path_spacing {
cells.push(pfps.to_cell());
}
if let Some(pfmg) = &self.phonon_fine_kpoints_mp_grid {
cells.push(pfmg.to_cell());
}
if let Some(pfms) = &self.phonon_fine_kpoints_mp_spacing {
cells.push(pfms.to_cell());
}
if let Some(pfmo) = &self.phonon_fine_kpoints_mp_offset {
cells.push(pfmo.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::*;
use crate::cell::species::Species;
use crate::cell::positions::PositionFracEntry;
use crate::cell::bz_sampling_kpoints::{Kpoint, SpectralKpointPathEntry, BsKpointPathEntry};
use crate::cell::phonon::{PhononKpointPathEntry, PhononKpointListEntry};
use crate::cell::symmetry::SymmetryOp;
#[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());
}
fn minimal_lattice() -> Lattice {
Lattice::Cart(LatticeCart {
unit: None,
a: [10.0, 0.0, 0.0],
b: [0.0, 10.0, 0.0],
c: [0.0, 0.0, 10.0],
})
}
fn minimal_positions() -> Positions {
Positions::Frac(PositionsFrac {
positions: vec![PositionFracEntry {
species: Species::Symbol("Si".to_string()),
coord: [0.0, 0.0, 0.0],
spin: None,
mixture: None,
}],
})
}
#[test]
fn build_rejects_multiple_kpoint_specs() {
let result = CellDocument::builder()
.lattice(minimal_lattice())
.positions(minimal_positions())
.maybe_kpoints_list(Some(KpointsList::builder()
.kpts(vec![Kpoint::builder().coord([0.0, 0.0, 0.0]).weight(1.0).build()])
.build()))
.maybe_kpoints_mp_grid(Some(KpointsMpGrid([2, 2, 2])))
.build();
assert!(result.is_err());
}
#[test]
fn build_rejects_multiple_spectral_specs() {
let result = CellDocument::builder()
.lattice(minimal_lattice())
.positions(minimal_positions())
.maybe_spectral_kpoint_path(Some(SpectralKpointPath::builder()
.points(vec![SpectralKpointPathEntry { coord: [0.0, 0.0, 0.0] }])
.build()))
.maybe_spectral_kpoints_mp_grid(Some(SpectralKpointsMpGrid([2, 2, 2])))
.build();
assert!(result.is_err());
}
#[test]
fn build_rejects_spectral_and_bs_duplication() {
let result = CellDocument::builder()
.lattice(minimal_lattice())
.positions(minimal_positions())
.maybe_spectral_kpoint_path(Some(SpectralKpointPath::builder()
.points(vec![SpectralKpointPathEntry { coord: [0.0, 0.0, 0.0] }])
.build()))
.maybe_bs_kpoint_path(Some(BsKpointPath::builder()
.points(vec![BsKpointPathEntry { coord: [0.0, 0.0, 0.0] }])
.build()))
.build();
assert!(result.is_err());
}
#[test]
fn build_rejects_multiple_phonon_specs() {
let result = CellDocument::builder()
.lattice(minimal_lattice())
.positions(minimal_positions())
.maybe_phonon_kpoint_path(Some(PhononKpointPath {
points: vec![PhononKpointPathEntry { coord: [0.0, 0.0, 0.0] }],
}))
.maybe_phonon_kpoint_list(Some(PhononKpointList::builder()
.kpoints(vec![PhononKpointListEntry { coord: [0.0, 0.0, 0.0], weight: 1.0 }])
.build()))
.build();
assert!(result.is_err());
}
#[test]
fn build_rejects_symmetry_generate_and_ops() {
let result = CellDocument::builder()
.lattice(minimal_lattice())
.positions(minimal_positions())
.maybe_symmetry_generate(Some(SymmetryGenerate))
.maybe_symmetry_ops(Some(SymmetryOps::builder()
.ops(vec![SymmetryOp::builder()
.rotation([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
.translation([0.0, 0.0, 0.0])
.build()])
.build()))
.build();
assert!(result.is_err());
}
#[test]
fn build_allows_mp_offset_with_grid() {
let result = CellDocument::builder()
.lattice(minimal_lattice())
.positions(minimal_positions())
.maybe_kpoints_mp_grid(Some(KpointsMpGrid([2, 2, 2])))
.maybe_kpoints_mp_offset(Some(KpointsMpOffset([0.0, 0.0, 0.0])))
.build();
assert!(result.is_ok());
}
#[test]
fn build_allows_spectral_mp_offset_with_grid() {
let result = CellDocument::builder()
.lattice(minimal_lattice())
.positions(minimal_positions())
.maybe_spectral_kpoints_mp_grid(Some(SpectralKpointsMpGrid([2, 2, 2])))
.maybe_spectral_kpoints_mp_offset(Some(SpectralKpointsMpOffset([0.0, 0.0, 0.0])))
.build();
assert!(result.is_ok());
}
#[test]
fn build_allows_single_spec_each_category() {
let r1 = CellDocument::builder()
.lattice(minimal_lattice())
.positions(minimal_positions())
.maybe_kpoints_mp_grid(Some(KpointsMpGrid([2, 2, 2])))
.build();
assert!(r1.is_ok());
let r2 = CellDocument::builder()
.lattice(minimal_lattice())
.positions(minimal_positions())
.maybe_spectral_kpoint_path(Some(SpectralKpointPath::builder()
.points(vec![SpectralKpointPathEntry { coord: [0.0, 0.0, 0.0] }])
.build()))
.build();
assert!(r2.is_ok());
let r3 = CellDocument::builder()
.lattice(minimal_lattice())
.positions(minimal_positions())
.maybe_phonon_kpoint_path(Some(PhononKpointPath {
points: vec![PhononKpointPathEntry { coord: [0.0, 0.0, 0.0] }],
}))
.build();
assert!(r3.is_ok());
let r4 = CellDocument::builder()
.lattice(minimal_lattice())
.positions(minimal_positions())
.maybe_symmetry_ops(Some(SymmetryOps::builder()
.ops(vec![SymmetryOp::builder()
.rotation([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
.translation([0.0, 0.0, 0.0])
.build()])
.build()))
.build();
assert!(r4.is_ok());
}
#[test]
fn build_rejects_all_three_kpoint_specs() {
let result = CellDocument::builder()
.lattice(minimal_lattice())
.positions(minimal_positions())
.maybe_kpoints_list(Some(KpointsList::builder()
.kpts(vec![Kpoint::builder().coord([0.0, 0.0, 0.0]).weight(1.0).build()])
.build()))
.maybe_kpoints_mp_grid(Some(KpointsMpGrid([2, 2, 2])))
.maybe_kpoints_mp_spacing(Some(KpointsMpSpacing { value: 0.05, unit: None }))
.build();
assert!(result.is_err());
}
#[test]
fn build_allows_empty_document() {
let result = CellDocument::builder()
.lattice(minimal_lattice())
.positions(minimal_positions())
.build();
assert!(result.is_ok());
}
}