omecp/naming.rs
1//! Dynamic file naming based on input file basename
2//!
3//! This module provides a centralized system for generating file names dynamically
4//! based on the input file basename. This allows multiple jobs to run in the same
5//! directory without file conflicts.
6//!
7//! # Example
8//!
9//! ```
10//! use std::path::Path;
11//! use omecp::naming::FileNaming;
12//!
13//! let input_path = Path::new("compound_xyz_123.input");
14//! let naming = FileNaming::new(input_path);
15//!
16//! // Generate dynamic file names
17//! assert_eq!(naming.state_a_chk(), "compound_xyz_123_state_A.chk");
18//! assert_eq!(naming.pre_a("job_dir", "inp"), "job_dir/compound_xyz_123_pre_A.inp");
19//! assert_eq!(naming.step_state_a("job_dir", 5, "gjf"), "job_dir/compound_xyz_123_5_state_A.gjf");
20//! ```
21
22use std::path::Path;
23
24/// Manages dynamic file naming based on input file basename
25///
26/// All file names are prefixed with the basename extracted from the input file,
27/// ensuring unique file names when multiple jobs run in the same directory.
28#[derive(Debug, Clone)]
29pub struct FileNaming {
30 basename: String,
31}
32
33impl FileNaming {
34 /// Creates a new FileNaming instance from an input file path
35 ///
36 /// Extracts the file stem (filename without extension) to use as the basename
37 /// for all generated file names.
38 ///
39 /// # Arguments
40 ///
41 /// * `input_path` - Path to the input file
42 ///
43 /// # Example
44 ///
45 /// ```
46 /// use std::path::Path;
47 /// use omecp::naming::FileNaming;
48 ///
49 /// let naming = FileNaming::new(Path::new("compound_xyz_123.input"));
50 /// assert_eq!(naming.basename(), "compound_xyz_123");
51 /// ```
52 pub fn new(input_path: &Path) -> Self {
53 let basename = input_path
54 .file_stem()
55 .and_then(|s| s.to_str())
56 .unwrap_or("mecp_job")
57 .to_string();
58
59 Self { basename }
60 }
61
62 /// Returns the basename used for file naming
63 pub fn basename(&self) -> &str {
64 &self.basename
65 }
66
67 // Checkpoint files (Gaussian)
68
69 /// Returns the state A checkpoint file name
70 ///
71 /// Format: `{basename}_state_A.chk`
72 pub fn state_a_chk(&self) -> String {
73 format!("{}_state_A.chk", self.basename)
74 }
75
76 /// Returns the state B checkpoint file name
77 ///
78 /// Format: `{basename}_state_B.chk`
79 pub fn state_b_chk(&self) -> String {
80 format!("{}_state_B.chk", self.basename)
81 }
82
83 /// Returns the 'a' checkpoint file name
84 ///
85 /// Format: `{basename}_a.chk`
86 pub fn a_chk(&self) -> String {
87 format!("{}_a.chk", self.basename)
88 }
89
90 /// Returns the 'b' checkpoint file name
91 ///
92 /// Format: `{basename}_b.chk`
93 pub fn b_chk(&self) -> String {
94 format!("{}_b.chk", self.basename)
95 }
96
97 /// Returns the state A checkpoint file path in a job directory
98 ///
99 /// Format: `{job_dir}/{basename}_state_A.chk`
100 pub fn state_a_chk_path(&self, job_dir: &str) -> String {
101 format!("{}/{}", job_dir, self.state_a_chk())
102 }
103
104 /// Returns the state B checkpoint file path in a job directory
105 ///
106 /// Format: `{job_dir}/{basename}_state_B.chk`
107 pub fn state_b_chk_path(&self, job_dir: &str) -> String {
108 format!("{}/{}", job_dir, self.state_b_chk())
109 }
110
111 /// Returns the 'a' checkpoint file path in a job directory
112 ///
113 /// Format: `{job_dir}/{basename}_a.chk`
114 pub fn a_chk_path(&self, job_dir: &str) -> String {
115 format!("{}/{}", job_dir, self.a_chk())
116 }
117
118 /// Returns the 'b' checkpoint file path in a job directory
119 ///
120 /// Format: `{job_dir}/{basename}_b.chk`
121 pub fn b_chk_path(&self, job_dir: &str) -> String {
122 format!("{}/{}", job_dir, self.b_chk())
123 }
124
125 // Wavefunction files (ORCA)
126
127 /// Returns the state A wavefunction file path
128 ///
129 /// Format: `{job_dir}/{basename}_state_A.gbw`
130 pub fn state_a_gbw(&self, job_dir: &str) -> String {
131 format!("{}/{}_state_A.gbw", job_dir, self.basename)
132 }
133
134 /// Returns the state B wavefunction file path
135 ///
136 /// Format: `{job_dir}/{basename}_state_B.gbw`
137 pub fn state_b_gbw(&self, job_dir: &str) -> String {
138 format!("{}/{}_state_B.gbw", job_dir, self.basename)
139 }
140
141 /// Returns the 'a' wavefunction file path
142 ///
143 /// Format: `{job_dir}/{basename}_a.gbw`
144 pub fn a_gbw(&self, job_dir: &str) -> String {
145 format!("{}/{}_a.gbw", job_dir, self.basename)
146 }
147
148 /// Returns the 'b' wavefunction file path
149 ///
150 /// Format: `{job_dir}/{basename}_b.gbw`
151 pub fn b_gbw(&self, job_dir: &str) -> String {
152 format!("{}/{}_b.gbw", job_dir, self.basename)
153 }
154
155 // Pre-point calculation files
156
157 /// Returns the pre-point A input file path
158 ///
159 /// Format: `{job_dir}/{basename}_pre_A.{ext}`
160 pub fn pre_a(&self, job_dir: &str, ext: &str) -> String {
161 format!("{}/{}_pre_A.{}", job_dir, self.basename, ext)
162 }
163
164 /// Returns the pre-point B input file path
165 ///
166 /// Format: `{job_dir}/{basename}_pre_B.{ext}`
167 pub fn pre_b(&self, job_dir: &str, ext: &str) -> String {
168 format!("{}/{}_pre_B.{}", job_dir, self.basename, ext)
169 }
170
171 /// Returns the pre-point A checkpoint file path
172 ///
173 /// Format: `{job_dir}/{basename}_pre_A.chk`
174 pub fn pre_a_chk(&self, job_dir: &str) -> String {
175 format!("{}/{}_pre_A.chk", job_dir, self.basename)
176 }
177
178 /// Returns the pre-point B checkpoint file path
179 ///
180 /// Format: `{job_dir}/{basename}_pre_B.chk`
181 pub fn pre_b_chk(&self, job_dir: &str) -> String {
182 format!("{}/{}_pre_B.chk", job_dir, self.basename)
183 }
184
185 /// Returns the pre-point A wavefunction file path
186 ///
187 /// Format: `{job_dir}/{basename}_pre_A.gbw`
188 pub fn pre_a_gbw(&self, job_dir: &str) -> String {
189 format!("{}/{}_pre_A.gbw", job_dir, self.basename)
190 }
191
192 /// Returns the pre-point B wavefunction file path
193 ///
194 /// Format: `{job_dir}/{basename}_pre_B.gbw`
195 pub fn pre_b_gbw(&self, job_dir: &str) -> String {
196 format!("{}/{}_pre_B.gbw", job_dir, self.basename)
197 }
198
199 // Optimization step files
200
201 /// Returns the state A input file path for a given step
202 ///
203 /// Format: `{job_dir}/{basename}_{step}_state_A.{ext}`
204 pub fn step_state_a(&self, job_dir: &str, step: usize, ext: &str) -> String {
205 format!("{}/{}_{}_state_A.{}", job_dir, self.basename, step, ext)
206 }
207
208 /// Returns the state B input file path for a given step
209 ///
210 /// Format: `{job_dir}/{basename}_{step}_state_B.{ext}`
211 pub fn step_state_b(&self, job_dir: &str, step: usize, ext: &str) -> String {
212 format!("{}/{}_{}_state_B.{}", job_dir, self.basename, step, ext)
213 }
214
215 /// Returns the state A wavefunction file path for a given step
216 ///
217 /// Format: `{job_dir}/{basename}_{step}_state_A.gbw`
218 pub fn step_state_a_gbw(&self, job_dir: &str, step: usize) -> String {
219 format!("{}/{}_{}_state_A.gbw", job_dir, self.basename, step)
220 }
221
222 /// Returns the state B wavefunction file path for a given step
223 ///
224 /// Format: `{job_dir}/{basename}_{step}_state_B.gbw`
225 pub fn step_state_b_gbw(&self, job_dir: &str, step: usize) -> String {
226 format!("{}/{}_{}_state_B.gbw", job_dir, self.basename, step)
227 }
228
229 /// Returns the state A engrad file path for a given step
230 ///
231 /// Format: `{job_dir}/{basename}_{step}_state_A.engrad`
232 pub fn step_state_a_engrad(&self, job_dir: &str, step: usize) -> String {
233 format!("{}/{}_{}_state_A.engrad", job_dir, self.basename, step)
234 }
235
236 /// Returns the state B engrad file path for a given step
237 ///
238 /// Format: `{job_dir}/{basename}_{step}_state_B.engrad`
239 pub fn step_state_b_engrad(&self, job_dir: &str, step: usize) -> String {
240 format!("{}/{}_{}_state_B.engrad", job_dir, self.basename, step)
241 }
242
243 // Alternative naming for PES analysis modes
244
245 /// Returns the 'A' input file path for a given step (PES analysis)
246 ///
247 /// Format: `{job_dir}/{basename}_{step}_A.{ext}`
248 pub fn step_a(&self, job_dir: &str, step: usize, ext: &str) -> String {
249 format!("{}/{}_{}_A.{}", job_dir, self.basename, step, ext)
250 }
251
252 /// Returns the 'B' input file path for a given step (PES analysis)
253 ///
254 /// Format: `{job_dir}/{basename}_{step}_B.{ext}`
255 pub fn step_b(&self, job_dir: &str, step: usize, ext: &str) -> String {
256 format!("{}/{}_{}_B.{}", job_dir, self.basename, step, ext)
257 }
258
259 // Special files for specific modes
260
261 /// Returns a drive calculation file path
262 ///
263 /// Format: `{job_dir}/{basename}_drive_{step}_{state}.{ext}`
264 pub fn drive_file(&self, job_dir: &str, step: usize, state: &str, ext: &str) -> String {
265 format!(
266 "{}/{}_drive_{}_{}.{}",
267 job_dir, self.basename, step, state, ext
268 )
269 }
270
271 /// Returns a NEB calculation file path
272 ///
273 /// Format: `{job_dir}/{basename}_neb_{step}_{state}.{ext}`
274 pub fn neb_file(&self, job_dir: &str, step: usize, state: &str, ext: &str) -> String {
275 format!(
276 "{}/{}_neb_{}_{}.{}",
277 job_dir, self.basename, step, state, ext
278 )
279 }
280
281 /// Returns the basename for ORCA gbw file references in input headers
282 ///
283 /// This is used in ORCA input files where the gbw path needs to be specified.
284 /// Format: `{job_dir}/{basename}`
285 pub fn orca_basename(&self, job_dir: &str) -> String {
286 format!("{}/{}", job_dir, self.basename)
287 }
288
289 // Final output files
290
291 /// Returns the final MECP geometry output file name
292 ///
293 /// Format: `{basename}_mecp.xyz`
294 ///
295 /// # Example
296 ///
297 /// ```
298 /// use std::path::Path;
299 /// use omecp::naming::FileNaming;
300 ///
301 /// let naming = FileNaming::new(Path::new("compound_xyz_123.input"));
302 /// assert_eq!(naming.final_mecp_xyz(), "compound_xyz_123_mecp.xyz");
303 /// ```
304 pub fn final_mecp_xyz(&self) -> String {
305 format!("{}_mecp.xyz", self.basename)
306 }
307}