castep_seeding/seed/
seed_setup.rs

1use castep_cell_io::{
2    cell_document::{
3        BSKpointPathSpacing, CellDocument, CellEntries, ExtEFieldBlock, ExtPressureBlock,
4        FixAllCell, FixCom, IonicConstraintsBlock, KpointMPSpacing, KpointSettings,
5        NCKpointSettings, SpeciesLCAOStatesBlock, SpeciesMassBlock, SpeciesPotBlock,
6    },
7    CastepTask, EnergyCutoff, EnergyCutoffError, InvLengthUnit,
8};
9
10use castep_periodic_table::data::ELEMENT_TABLE;
11use castep_periodic_table::element::LookupElement;
12use std::path::Path;
13
14use crate::SeedingErrors;
15use castep_param_io::param::CastepParam;
16
17pub trait CellBuilding {
18    fn geom_opt_cell_template(template_cell: &CellDocument) -> CellDocument {
19        let elements = template_cell.get_elements();
20        let entries = vec![
21            CellEntries::KpointSettings(KpointSettings::MPSpacing(KpointMPSpacing::default())),
22            CellEntries::FixAllCell(FixAllCell::new(true)),
23            CellEntries::FixCom(FixCom::new(false)),
24            CellEntries::IonicConstraints(IonicConstraintsBlock::default()),
25            CellEntries::ExtEfield(ExtEFieldBlock::default()),
26            CellEntries::ExtPressure(ExtPressureBlock::default()),
27            CellEntries::SpeciesMass(SpeciesMassBlock::from_elements(&elements)),
28            CellEntries::SpeciesPot(SpeciesPotBlock::from_elements(&elements)),
29            CellEntries::SpeciesLCAOStates(SpeciesLCAOStatesBlock::from_elememts(&elements)),
30        ];
31        let mut geom_cell = template_cell.clone();
32        geom_cell.set_entries(Some(entries));
33        geom_cell
34    }
35
36    #[cfg(feature = "template")]
37    fn bs_cell_template(template_cell: &CellDocument) -> CellDocument {
38        let mut bs_cell = template_cell.clone();
39        let elements = template_cell.get_elements();
40        let entries = vec![
41            CellEntries::KpointSettings(KpointSettings::MPSpacing(KpointMPSpacing::default())),
42            CellEntries::NCKpointSettings(NCKpointSettings::PathSpacing(BSKpointPathSpacing::new(
43                InvLengthUnit::Ang,
44                0.07,
45            ))),
46            CellEntries::FixAllCell(FixAllCell::new(true)),
47            CellEntries::FixCom(FixCom::new(false)),
48            CellEntries::IonicConstraints(IonicConstraintsBlock::default()),
49            CellEntries::ExtEfield(ExtEFieldBlock::default()),
50            CellEntries::ExtPressure(ExtPressureBlock::default()),
51            CellEntries::SpeciesMass(SpeciesMassBlock::from_elements(&elements)),
52            CellEntries::SpeciesPot(SpeciesPotBlock::from_elements(&elements)),
53            CellEntries::SpeciesLCAOStates(SpeciesLCAOStatesBlock::from_elememts(&elements)),
54        ];
55        bs_cell.set_entries(Some(entries));
56        bs_cell
57    }
58
59    fn build_cell_for_task(
60        &self,
61        template_cell: &CellDocument,
62        castep_task: CastepTask,
63    ) -> CellDocument {
64        #[cfg(feature = "template")]
65        match castep_task {
66            CastepTask::BandStructure => Self::bs_cell_template(template_cell),
67            CastepTask::GeometryOptimization => Self::geom_opt_cell_template(template_cell),
68        }
69    }
70}
71
72pub trait ParamBuilding {
73    fn cutoff_energy<P: AsRef<Path>>(
74        &self,
75        template_cell: &CellDocument,
76        energy_cutoff: EnergyCutoff,
77        potentials_loc: P,
78    ) -> Result<f64, EnergyCutoffError> {
79        let cutoff_energies = template_cell
80            .get_elements()
81            .iter()
82            .map(|&elm| {
83                let potential_name = ELEMENT_TABLE.get_by_symbol(elm).potential();
84                let potential_file = Path::new(potentials_loc.as_ref()).join(potential_name);
85                energy_cutoff.get_cutoff_energy_from_pot(potential_file)
86            })
87            .collect::<Result<Vec<f64>, EnergyCutoffError>>()?;
88        Ok(cutoff_energies
89            .into_iter()
90            .reduce(|prev, next| if next > prev { next } else { prev })
91            .expect("Error in comparing the largest cutoff energy"))
92    }
93    fn build_param_for_task(
94        &self,
95        template_cell: &CellDocument,
96        castep_task: CastepTask,
97    ) -> Result<CastepParam, SeedingErrors>;
98
99    #[cfg(feature = "template")]
100    fn geom_opt_param_template<P: AsRef<Path>>(
101        &self,
102        template_cell: &CellDocument,
103        energy_cutoff: EnergyCutoff,
104        use_edft: bool,
105        potentials_loc: P,
106    ) -> Result<castep_param_io::param::CastepParam, crate::SeedingErrors> {
107        use castep_periodic_table::element::ElementFamily;
108
109        use castep_param_io::param::{CastepParam, MetalsMethod, NumOccCycles};
110
111        {
112            let use_edft = if use_edft {
113                template_cell.get_elements().iter().any(|elm| {
114                    matches!(
115                        elm.family(),
116                        ElementFamily::RareEarthLa | ElementFamily::RareEarthAc
117                    )
118                })
119            } else {
120                false
121            };
122            let mut param = CastepParam::geom_opt_param_template(
123                self.cutoff_energy(template_cell, energy_cutoff, potentials_loc)?,
124                template_cell.total_spin().into(),
125            )?;
126            if use_edft {
127                if let Some(electro_min) = param.electro_min.as_mut() {
128                    electro_min.metals_method = Some(MetalsMethod::EDFT);
129                    electro_min.num_occ_cycles = Some(NumOccCycles::from(6));
130                }
131            }
132            Ok(param)
133        }
134    }
135
136    #[cfg(feature = "template")]
137    fn dos_param_template<P: AsRef<Path>>(
138        &self,
139        template_cell: &CellDocument,
140        energy_cutoff: EnergyCutoff,
141        use_edft: bool,
142        potentials_loc: P,
143    ) -> Result<castep_param_io::param::CastepParam, crate::SeedingErrors> {
144        use castep_periodic_table::element::ElementFamily;
145
146        use castep_param_io::param::{CastepParam, MetalsMethod, NumOccCycles};
147
148        let use_edft = if use_edft {
149            template_cell.get_elements().iter().any(|elm| {
150                matches!(
151                    elm.family(),
152                    ElementFamily::RareEarthLa | ElementFamily::RareEarthAc
153                )
154            })
155        } else {
156            false
157        };
158
159        let mut param = CastepParam::dos_opt_param_template(
160            self.cutoff_energy(template_cell, energy_cutoff, potentials_loc)?,
161            template_cell.total_spin().into(),
162        )?;
163        if use_edft {
164            if let Some(electro_min) = param.electro_min.as_mut() {
165                electro_min.metals_method = Some(MetalsMethod::EDFT);
166                electro_min.num_occ_cycles = Some(NumOccCycles::from(6));
167            }
168        }
169        Ok(param)
170    }
171}