castep_seeding/seed/
seed_folder.rs

1use std::{
2    fs::{create_dir, File},
3    io::{BufWriter, Write},
4    path::{Path, PathBuf},
5};
6
7use crate::{error::SeedingErrors, CellBuilding, ParamBuilding};
8use castep_cell_io::{cell_document::CellDocument, CastepTask};
9use castep_periodic_table::{data::ELEMENT_TABLE, element::LookupElement};
10
11/// A trait of how to create seed file folders.
12/// Required psuedopotential files must be copied to the root dir first.
13pub trait SeedFolder {
14    fn seed_name(&self) -> &str;
15    fn root_dir(&self) -> impl AsRef<Path>;
16    fn cell_template(&self) -> &CellDocument;
17    /// Join seed name after the root dir as the seed directory.
18    /// You might implement this by yourself to customize the seed directory naming logic.
19    fn seed_dir(&self) -> impl AsRef<Path> {
20        self.root_dir().as_ref().join(self.seed_name())
21    }
22    fn create_seed_dir(&self) -> Result<PathBuf, SeedingErrors> {
23        let seed_dir = self.seed_dir();
24        if seed_dir.as_ref().exists() {
25            return Ok(seed_dir.as_ref().into());
26        }
27        create_dir(&seed_dir).map_err(SeedingErrors::CreateSeedDir)?;
28        Ok(seed_dir.as_ref().into())
29    }
30    fn soft_link_potentials<P: AsRef<Path>>(&self, potential_src: P) -> Result<(), SeedingErrors> {
31        self.cell_template()
32            .get_elements()
33            .iter()
34            .try_for_each(|&elm| {
35                let potential_file = ELEMENT_TABLE.get_by_symbol(elm).potential();
36                let src_path = potential_src.as_ref().join(potential_file);
37                let dst_path = self.seed_dir().as_ref().join(potential_file);
38                if dst_path.is_symlink() || dst_path.exists() {
39                    Ok(())
40                } else {
41                    #[cfg(target_os = "windows")]
42                    {
43                        std::os::windows::fs::symlink_file(src_path, dst_path)
44                            .map_err(SeedingErrors::SoftlinkError)
45                    }
46                    #[cfg(not(target_os = "windows"))]
47                    std::os::unix::fs::symlink(src_path, dst_path)
48                        .map_err(SeedingErrors::SoftlinkError)
49                }
50            })
51    }
52    fn create_seed_file<P: AsRef<Path>, F: AsRef<[u8]>>(
53        &self,
54        filename: P,
55        file_content: F,
56    ) -> Result<(), SeedingErrors> {
57        let seed_dir = self.seed_dir();
58        let file_path = seed_dir.as_ref().join(filename);
59        let file = File::create(file_path).map_err(SeedingErrors::WriteError)?;
60        let mut f = BufWriter::new(file);
61        f.write_all(file_content.as_ref())
62            .map_err(SeedingErrors::WriteError)?;
63        Ok(())
64    }
65    /// Here generates and writes all files needed.
66    /// Implement by yourself to change the behavior.
67    fn write_files(
68        &self,
69        cell_builder: &impl CellBuilding,
70        param_builder: &impl ParamBuilding,
71    ) -> Result<(), SeedingErrors> {
72        [
73            (
74                format!("{}.param", self.seed_name()),
75                param_builder
76                    .build_param_for_task(self.cell_template(), CastepTask::GeometryOptimization)?
77                    .to_string(),
78            ),
79            (
80                format!("{}_DOS.param", self.seed_name()),
81                param_builder
82                    .build_param_for_task(self.cell_template(), CastepTask::BandStructure)?
83                    .to_string(),
84            ),
85            (
86                format!("{}.cell", self.seed_name()),
87                cell_builder
88                    .build_cell_for_task(self.cell_template(), CastepTask::GeometryOptimization)
89                    .to_string(),
90            ),
91            (
92                format!("{}_DOS.cell", self.seed_name()),
93                cell_builder
94                    .build_cell_for_task(self.cell_template(), CastepTask::BandStructure)
95                    .to_string(),
96            ),
97        ]
98        .iter()
99        .try_for_each(|(filename, file_content)| self.create_seed_file(filename, file_content))?;
100        Ok(())
101    }
102    /// One command to do all
103    fn actions<P: AsRef<Path>>(
104        &self,
105        cell_builder: &impl CellBuilding,
106        param_builder: &impl ParamBuilding,
107        potential_src: P,
108    ) -> Result<(), SeedingErrors> {
109        self.create_seed_dir()?;
110        self.write_files(cell_builder, param_builder)?;
111        self.soft_link_potentials(potential_src)
112    }
113}