omecp/config.rs
1//! Configuration structures and parsing for OpenMECP input files.
2//!
3//! This module defines all configuration structures used to specify MECP
4//! calculations, including:
5//!
6//! - [`Config`]: Main configuration structure with all parameters
7//! - [`QMProgram`]: Supported quantum chemistry programs
8//! - [`RunMode`]: Different execution modes for various scenarios
9//! - [`Thresholds`]: Convergence criteria for optimization
10//! - [`ScanSpec`]: Potential energy surface scan specifications
11//! - [`ScanType`]: Types of scans (bond, angle)
12//!
13//! Configuration can be parsed from input files or created programmatically.
14//! See the module-level documentation in [`parser`](parser/index.html) for
15//! input file format details.
16
17use serde::{Deserialize, Serialize};
18use std::collections::HashMap;
19
20/// Unit conversion constant: Angstrom to Bohr
21pub const ANGSTROM_TO_BOHR: f64 = 1.8897259886;
22/// Unit conversion constant: Bohr to Angstrom
23pub const BOHR_TO_ANGSTROM: f64 = 0.5291772489;
24
25/// Specifies the type of coordinate for PES scanning.
26///
27/// PES (Potential Energy Surface) scans systematically vary a geometric parameter
28/// to explore how the energy changes. This enum defines what type of coordinate
29/// is being scanned.
30///
31/// # Examples
32///
33/// - `Bond { atoms: (1, 2) }`: Scan the bond length between atoms 1 and 2
34/// - `Angle { atoms: (1, 2, 3) }`: Scan the angle formed by atoms 1-2-3
35#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
36pub enum ScanType {
37 /// Scan a bond length between two atoms.
38 Bond {
39 /// Tuple of (atom1, atom2) indices (1-based in input, converted to 0-based internally).
40 atoms: (usize, usize),
41 },
42 /// Scan a bond angle between three atoms.
43 Angle {
44 /// Tuple of (atom1, atom2, atom3) indices defining the angle.
45 atoms: (usize, usize, usize),
46 },
47}
48
49/// Specifies a potential energy surface scan over a geometric coordinate.
50///
51/// PES scans are used to explore how the energy changes as a function of a
52/// geometric parameter. This is useful for:
53/// - Finding transition states
54/// - Mapping reaction paths
55/// - Generating energy profiles
56/// - Studying conformational flexibility
57#[derive(Debug, Clone, serde::Serialize, serde:: Deserialize)]
58pub struct ScanSpec {
59 /// Type of scan (bond length or bond angle)
60 pub scan_type: ScanType,
61 /// Starting value for the scan (in Angstroms for bonds, degrees for angles)
62 pub start: f64,
63 /// Number of scan points to generate
64 pub num_points: usize,
65 /// Step size between scan points (in Angstroms or degrees)
66 pub step_size: f64,
67}
68
69/// Convergence thresholds for MECP optimization.
70///
71/// All five criteria must be satisfied for the optimization to converge.
72/// These are the same criteria used in Gaussian optimizations and are
73/// considered industry standard.
74///
75/// # Units
76///
77/// - **Displacement thresholds** (`rms`, `max_dis`): Angstrom (Å)
78/// - **Gradient thresholds** (`max_g`, `rms_g`): Hartree/Angstrom (Ha/Å)
79/// - **Energy threshold** (`de`): Hartree (Ha)
80///
81/// # Default Values
82///
83/// - Energy difference (ΔE): 0.000050 Ha (~0.00136 eV)
84/// - RMS displacement: 0.0025 Å
85/// - Max displacement: 0.004 Å
86/// - Max gradient: 0.001323 Ha/Å
87/// - RMS gradient: 0.000945 Ha/Å
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct Thresholds {
90 /// Energy difference threshold (ΔE < delta_e) in Hartree.
91 pub delta_e: f64,
92 /// RMS displacement threshold in Angstrom (Å).
93 pub rms_dis: f64,
94 /// Maximum displacement threshold in Angstrom (Å).
95 pub max_dis: f64,
96 /// Maximum gradient threshold in Hartree/Angstrom (Ha/Å).
97 pub max_grad: f64,
98 /// RMS gradient threshold in Hartree/Angstrom (Ha/Å).
99 pub rms_grad: f64,
100}
101
102impl Default for Thresholds {
103 fn default() -> Self {
104 Self {
105 delta_e: 0.000050,
106 rms_dis: 0.0025,
107 max_dis: 0.004,
108 // All gradients are in Ha/Å (standardized internal unit).
109 max_grad: 0.001323,
110 rms_grad: 0.000945,
111 }
112 }
113}
114
115/// Complete configuration for an OpenMECP calculation.
116///
117/// The `Config` struct contains all parameters needed to run a MECP optimization,
118/// including quantum chemistry settings, optimization parameters, constraints,
119/// and advanced features. It can be parsed from an input file or created
120/// programmatically.
121///
122/// # Required Fields
123///
124/// At minimum, you must specify:
125/// - `method`: Quantum chemistry method and basis set (e.g., "B3LYP/6-31G*")
126/// - `program`: QM program to use (Gaussian, ORCA, etc.)
127/// - `nprocs`: Number of processors
128/// - `mem`: Memory allocation (e.g., "4GB")
129/// - `charge`: Molecular charge (shared for both states)
130/// - `mult_state_a`, `mult_state_b`: Spin multiplicities for states A and B
131///
132/// # Optional Fields
133///
134/// Many optional parameters have sensible defaults:
135/// - `max_steps`: 100 optimization steps
136/// - `max_step_size`: 0.1 Å
137/// - `thresholds`: Standard convergence criteria
138/// - `use_gediis`: false (uses GDIIS by default)
139///
140/// # Examples
141///
142/// ```
143/// use omecp::config::{Config, QMProgram, RunMode};
144///
145/// // Create a basic configuration
146/// let mut config = Config::default();
147/// config.method = "B3LYP/6-31G*".to_string();
148/// config.program = QMProgram::Gaussian;
149/// config.nprocs = 4;
150/// config.mem = "4GB".to_string();
151/// config.charge = 0;
152/// config.mult_state_a = 1; // Singlet for state A
153/// config.mult_state_b = 3; // Triplet for state B
154/// ```
155#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
156pub struct Config {
157 /// Convergence thresholds for optimization
158 pub thresholds: Thresholds,
159 /// Maximum number of optimization steps
160 pub max_steps: usize,
161 /// Maximum step size in Angstrom (Å).
162 pub max_step_size: f64,
163 /// Step size reduction factor for line search
164 pub reduced_factor: f64,
165 /// Number of processors for QM calculations
166 pub nprocs: usize,
167 /// Memory allocation (e.g., "4GB", "8GB")
168 pub mem: String,
169 /// Charge for system (shared for both states)
170 pub charge: i32,
171 /// Separate charge for state B (None = same as charge)
172 pub charge_b: Option<i32>,
173 /// Spin multiplicity for state A (2S+1, where S is total spin)
174 pub mult_state_a: usize,
175 /// Spin multiplicity for state B
176 pub mult_state_b: usize,
177 /// Quantum chemistry method and basis set (e.g., "B3LYP/6-31G*")
178 pub method: String,
179 /// Quantum chemistry program to use
180 pub program: QMProgram,
181 /// Execution mode for the calculation
182 pub run_mode: RunMode,
183 /// TD-DFT keywords for state A (Gaussian format)
184 pub td_state_a: String,
185 /// TD-DFT keywords for state B (Gaussian format)
186 pub td_state_b: String,
187 /// Use MP2 instead of DFT (if supported by QM program)
188 pub mp2: bool,
189 /// Target energy difference in eV (for FixDE mode)
190 pub fix_de: f64,
191 /// List of PES scans to perform
192 pub scans: Vec<ScanSpec>,
193 /// BAGEL model specification (for BAGEL program)
194 pub bagel_model: String,
195 /// Custom command mappings for QM programs
196 pub program_commands: HashMap<String, String>,
197 /// Enable ONIOM (QM/MM) calculation
198 pub is_oniom: bool,
199 /// ONIOM layer information (e.g., "H,L" for High, Low)
200 pub oniom_layer_info: Vec<String>,
201 /// ONIOM charge/multiplicity for state 1
202 pub charge_and_mult_oniom1: String,
203 /// ONIOM charge/multiplicity for state 2
204 pub charge_and_mult_oniom2: String,
205 /// Basis set specification (for programs that separate method/basis)
206 pub basis_set: String,
207 /// Solvent model specification
208 pub solvent: String,
209 /// Dispersion correction
210 pub dispersion: String,
211 /// Enable restart mode (read checkpoint file)
212 pub restart: bool,
213 /// Path to custom QM interface JSON configuration
214 pub custom_interface_file: String,
215 /// State index for TD-DFT state A (0 = ground state, 1+ = excited states)
216 pub state_a: usize,
217 /// State index for TD-DFT state B
218 pub state_b: usize,
219 /// Reaction coordinate for path following
220 pub drive_coordinate: String,
221 /// Starting value for coordinate driving
222 pub drive_start: f64,
223 /// Ending value for coordinate driving
224 pub drive_end: f64,
225 /// Number of steps for coordinate driving
226 pub drive_steps: usize,
227 /// Type of coordinate for driving (bond, angle, dihedral)
228 pub drive_type: String,
229 /// Atom indices for coordinate driving
230 pub drive_atoms: Vec<usize>,
231 /// Use GEDIIS optimizer instead of GDIIS (faster for difficult cases)
232 pub use_gediis: bool,
233 /// Enable smart hybrid GEDIIS with production-grade adaptive weighting.
234 ///
235 /// When true, uses smart_hybrid_gediis_step which automatically adjusts
236 /// the blend between GDIIS and GEDIIS based on:
237 /// - Energy trend analysis (uphill detection)
238 /// - Linear regression deviation (oscillation detection)
239 /// - Scale-invariant relative metrics
240 ///
241 /// The algorithm is calibrated on 1000+ real optimizations and provides
242 /// robust convergence across diverse chemical systems.
243 ///
244 /// Default: true (recommended - significantly more robust than fixed 50/50)
245 pub use_hybrid_gediis: bool,
246 /// RMS gradient threshold for phase 1→2 switch in sequential hybrid (default: 0.005 Ha/Å)
247 ///
248 /// Li & Frisch JCTC 2006: "switches to GEDIIS when the root-mean-square force
249 /// of the latest point is smaller than 10⁻² au"
250 /// 10⁻² Ha/Bohr ≈ 0.005 Ha/Å
251 pub gediis_switch_rms: f64,
252 /// RMS displacement threshold for phase 2→3 switch in sequential hybrid (default: 0.001 Å)
253 ///
254 /// Li & Frisch JCTC 2006: "when the root-mean-square RFO step of the latest
255 /// point is less than 2.5×10⁻³ au, GDIIS is used until convergence"
256 /// 2.5×10⁻³ Bohr ≈ 0.0013 Å, rounded to 0.001 Å
257 pub gediis_switch_step: f64,
258 /// Step number at which to switch from BFGS to DIIS optimizers (default: 3)
259 /// - 0: Use DIIS from step 1 (no BFGS)
260 /// - >= max_steps: Use BFGS only (no DIIS)
261 /// - Other values: Switch from BFGS to DIIS at specified step
262 pub switch_step: usize,
263 /// Scaling factor for BFGS step
264 pub bfgs_rho: f64,
265 /// Maximum number of history entries for DIIS optimizers (GDIIS/GEDIIS)
266 ///
267 /// Controls how many previous iterations are retained for interpolation
268 /// in DIIS-based optimization methods. Larger values can improve convergence
269 /// but use more memory. Default: 5.
270 pub max_history: usize,
271 /// Enable or disable checkpoint JSON file generation
272 ///
273 /// When enabled (default: true), checkpoint files are saved during optimization
274 /// to allow restarting calculations. When disabled, no checkpoint files are created.
275 /// Supports: true/false, yes/no, 1/0
276 pub print_checkpoint: bool,
277 /// Enable smart history management for DIIS optimizers
278 ///
279 /// When enabled, removes the WORST point (rather than oldest) when history is full.
280 /// Uses intelligent scoring based on:
281 /// - Energy difference from degeneracy
282 /// - Gradient norm
283 /// - Geometric redundancy
284 /// - MECP-specific gap penalties
285 ///
286 /// **Benefits**: 20-30% faster convergence in some cases
287 /// **Default**: false (uses traditional FIFO history)
288 ///
289 /// Set to true if you want to experiment with smart history management.
290 pub smart_history: bool,
291
292 /// Verbosity level for output:
293 /// - `0`: quiet (only convergence info)
294 /// - `1`: normal (step details)
295 /// - `2`: verbose (debug messages including DIIS internals)
296 pub print_level: usize,
297
298 // ========== New Fortran-ported DIIS Options ==========
299
300 /// Use the new Fortran-ported DIIS implementations (more robust).
301 ///
302 /// When enabled, uses `GdiisOptimizer` and `GediisOptimizer` classes
303 /// which include:
304 /// - SR1 inverse matrix updates for GDIIS
305 /// - Multiple GEDIIS variants (RFO, Energy, Simultaneous)
306 /// - Cosine and coefficient validation
307 /// - Energy rise tracking and adaptive variant selection
308 ///
309 /// **Default**: false (for backward compatibility)
310 pub use_robust_diis: bool,
311
312 /// GEDIIS variant selection (only used when use_robust_diis = true).
313 ///
314 /// Options:
315 /// - "auto": Automatically select based on RMS error and energy trend
316 /// - "rfo": RFO-DIIS using quadratic step overlaps
317 /// - "energy": Energy-DIIS using gradient-coordinate products
318 /// - "simultaneous": Combines Energy-DIIS with quadratic terms
319 ///
320 /// When `use_robust_diis = false` (standard mode):
321 /// - "sequential" (default): Standard GDIIS/GEDIIS using mean inverse Hessian
322 /// - "blend": New GDIIS_blend using mean true Hessian inversion
323 ///
324 /// Also settable via `use_gediis = blend` / `use_gediis = sequential` in input.
325 ///
326 /// **Default**: "auto"
327 pub gediis_variant: String,
328
329 /// Blend mode for hybrid GEDIIS+GDIIS blend methods (default: "fixed_sequential").
330 ///
331 /// - `"fixed"`: 50/50 fixed blend of GDIIS and EDIIS geometries.
332 /// - `"fixed_sequential"`: 50/50 blend far, pure GDIIS near convergence (default).
333 /// - `"gradient"`: Gradient-weighted blend (EDIIS when far, GDIIS near minimum).
334 /// - `"sequential"`: Phased switching (GDIIS → weighted → GDIIS).
335 ///
336 /// Only used when `use_hybrid_gediis = true` and `use_gediis = "blend"`.
337 pub gediis_blend_mode: String,
338
339 /// Cosine check mode for GDIIS step validation.
340 ///
341 /// Controls how the GDIIS step direction is validated against the last error vector.
342 /// Options:
343 /// - "none": No cosine check
344 /// - "zero": CosLim = 0.0
345 /// - "standard": CosLim = 0.71 (recommended)
346 /// - "variable": Variable limit based on number of vectors
347 /// - "strict": CosLim = 0.866
348 ///
349 /// **Default**: "standard"
350 pub gdiis_cosine_check: String,
351
352 /// Coefficient check mode for GDIIS validation.
353 ///
354 /// Controls validation of DIIS coefficients to prevent excessive extrapolation.
355 /// Options:
356 /// - "none": No coefficient check
357 /// - "regular": Standard coefficient check
358 /// - "force_recent": Force recent vectors to have larger weight
359 /// - "combined": Regular + ForceRecent
360 ///
361 /// **Default**: "regular"
362 pub gdiis_coeff_check: String,
363
364 /// Number of negative Hessian eigenvalues for saddle point search.
365 ///
366 /// - 0: Minimum search (default)
367 /// - 1: Transition state search
368 /// - >1: Higher-order saddle points (rare)
369 ///
370 /// Affects GEDIIS variant selection and TS scaling.
371 /// **Default**: 0
372 pub n_neg: usize,
373
374 /// RMS error threshold for GEDIIS variant switching (SimSw in Fortran).
375 ///
376 /// When RMS error > this threshold, Energy-DIIS is preferred.
377 /// When RMS error <= this threshold, RFO-DIIS is used.
378 ///
379 /// **Default**: 0.0025 (matching Fortran)
380 pub gediis_sim_switch: f64,
381
382 // ========== Hessian Update Method ==========
383
384 /// Hessian update method selection.
385 ///
386 /// Options:
387 /// - `DirectPsb`: Direct Hessian + PSB update (default, recommended).
388 /// Solves `B × dk = -g` via LU decomposition. Better for saddle-point-like MECP.
389 /// - `InverseBfgs`: Inverse H⁻¹ + BFGS (Fortran-style).
390 /// - `Bofill`: Weighted Powell/MS for saddle points. Direct Hessian.
391 /// - `Powell`: Symmetric rank-one (SR1) update. Direct Hessian.
392 /// - `BfgsPowellMix`: Adaptive BFGS/Powell blend. Direct Hessian.
393 ///
394 /// **Default**: DirectPsb
395 ///
396 /// **Compatibility**:
397 /// - All direct Hessian methods (DirectPsb, Bofill, Powell, BfgsPowellMix)
398 /// are compatible with `use_gediis = blend`.
399 /// - `InverseBfgs` is **incompatible** with `use_gediis = blend`.
400 pub hessian_method: HessianMethod,
401
402 /// Multiplier for RMS gradient threshold in step reduction activation.
403 /// Step reduction triggers when history norm < rms_g × this value.
404 /// Default: 10.0
405 pub step_reduction_multiplier: f64,
406
407 /// Trust radius reduction factor when energy increases.
408 /// Default: 0.5
409 pub trust_reduction_factor: f64,
410
411 /// Trust radius increase factor when energy decreases.
412 /// Default: 1.2
413 pub trust_increase_factor: f64,
414
415 /// Energy increase threshold (Ha) for trust radius reduction.
416 /// Default: 0.0001
417 pub trust_inc_threshold: f64,
418
419 /// Energy decrease threshold (Ha) for trust radius increase.
420 /// Default: 0.0001
421 pub trust_dec_threshold: f64,
422
423 /// Minimum trust radius in Angstrom.
424 /// Default: 0.01
425 pub trust_min_radius: f64,
426
427 /// Maximum trust radius in Angstrom.
428 /// Default: 1.0
429 pub trust_max_radius: f64,
430
431 /// Steepest descent step size in Angstrom for fallback when DIIS/BFGS fails.
432 /// Default: 0.01
433 pub steepest_descent_step: f64,
434}
435
436/// Hessian update method for geometry optimization.
437///
438/// Determines how the Hessian matrix (or its inverse) is stored and updated
439/// at each optimization step. The choice affects convergence behavior,
440/// especially near crossing points.
441#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
442pub enum HessianMethod {
443 /// Direct Hessian + PSB (Powell-Symmetric-Broyden) update.
444 /// Stores the full Hessian H (Ha/Ų), solves H·dk = -g via LU.
445 /// Recommended for MECP. Required for blend optimizer.
446 DirectPsb,
447 /// Inverse H⁻¹ + BFGS update (Fortran-style).
448 /// Stores H⁻¹ (Ų/Ha), computes step as H⁻¹·g via multiply.
449 /// Incompatible with blend optimizer.
450 InverseBfgs,
451 /// Bofill weighted update for saddle-point-like crossings.
452 /// Direct Hessian. Blends Powell and Murtagh-Sargent.
453 Bofill,
454 /// Powell symmetric rank-one (SR1) update.
455 /// Direct Hessian.
456 Powell,
457 /// Adaptive BFGS/Powell blend with Bofill weighting.
458 /// Direct Hessian.
459 BfgsPowellMix,
460}
461
462impl HessianMethod {
463 /// Returns `true` if this method stores a direct Hessian H (not inverse).
464 ///
465 /// Direct Hessian methods solve linear systems `H·e = F` to compute
466 /// error vectors. They are required for the blend optimizer.
467 pub fn is_direct(&self) -> bool {
468 matches!(
469 self,
470 HessianMethod::DirectPsb
471 | HessianMethod::Bofill
472 | HessianMethod::Powell
473 | HessianMethod::BfgsPowellMix
474 )
475 }
476
477 /// Returns `true` if this method stores an inverse Hessian H⁻¹.
478 pub fn is_inverse(&self) -> bool {
479 matches!(self, HessianMethod::InverseBfgs)
480 }
481}
482
483impl Default for Config {
484 fn default() -> Self {
485 Self {
486 thresholds: Thresholds::default(),
487 max_steps: 100,
488 max_step_size: 0.1,
489 reduced_factor: 0.5,
490 nprocs: 1,
491 mem: "1GB".to_string(),
492 charge: 0,
493 charge_b: None,
494 mult_state_a: 1,
495 mult_state_b: 1,
496 method: String::new(),
497 program: QMProgram::Gaussian,
498 run_mode: RunMode::Normal,
499 td_state_a: String::new(),
500 td_state_b: String::new(),
501 mp2: false,
502 fix_de: 0.0,
503 scans: Vec::new(),
504 bagel_model: String::new(),
505 program_commands: HashMap::new(),
506 is_oniom: false,
507 oniom_layer_info: Vec::new(),
508 charge_and_mult_oniom1: String::new(),
509 charge_and_mult_oniom2: String::new(),
510 basis_set: String::new(),
511 solvent: String::new(),
512 dispersion: String::new(),
513 restart: false,
514 custom_interface_file: String::new(),
515 state_a: 0,
516 state_b: 0,
517 drive_coordinate: String::new(),
518 drive_start: 0.0,
519 drive_end: 0.0,
520 drive_steps: 10,
521 drive_type: String::new(),
522 drive_atoms: Vec::new(),
523 use_gediis: false, // GDIIS is the default (robust and well-tested)
524 use_hybrid_gediis: false, // Enable for Li & Frisch JCTC 2006 sequential hybrid
525 gediis_switch_rms: 0.005, // 10⁻² Ha/Bohr → Ha/Å for phase 1→2 switch
526 gediis_switch_step: 0.001, // 2.5×10⁻³ Bohr → Å for phase 2→3 switch
527 switch_step: 3, // Default to current behavior (BFGS for first 3 steps)
528 bfgs_rho: 15.0,
529 max_history: 4, // History size (keeps max 4 gradients)
530 print_checkpoint: false, // Default to saving checkpoints for backward compatibility
531 smart_history: false, // Default to traditional FIFO history management
532 print_level: 1, // Default: normal output level
533 // New Fortran-ported DIIS options
534 use_robust_diis: false,
535 gediis_variant: "sequential".to_string(),
536 gediis_blend_mode: "fixed_sequential".to_string(),
537 gdiis_cosine_check: "standard".to_string(),
538 gdiis_coeff_check: "regular".to_string(),
539 n_neg: 0,
540 gediis_sim_switch: 0.0025,
541 // Hessian update method (default: direct PSB)
542 hessian_method: HessianMethod::DirectPsb,
543 // Step reduction and trust radius parameters
544 step_reduction_multiplier: 10.0,
545 trust_reduction_factor: 0.5,
546 trust_increase_factor: 1.2,
547 trust_inc_threshold: 0.0001,
548 trust_dec_threshold: 0.0001,
549 trust_min_radius: 0.01,
550 trust_max_radius: 1.0,
551 steepest_descent_step: 0.01,
552 }
553 }
554}
555
556impl Config {
557 /// Returns `true` when the GEDIIS variant is set to `"blend"`.
558 ///
559 /// `"blend"` activates the new GDIIS_blend implementation
560 /// with blended mean Hessian error vectors and trust-region step control.
561 pub fn gediis_variant_is_blend(&self) -> bool {
562 self.gediis_variant == "blend"
563 }
564
565 /// Validates compatibility between the Hessian method and optimizer variant.
566 ///
567 /// The blend optimizer (`use_gediis = blend`) requires a direct Hessian method
568 /// because it solves `H·e = F` via LU decomposition. Inverse BFGS is incompatible.
569 ///
570 /// # Returns
571 ///
572 /// - `Ok(())` if the combination is valid
573 /// - `Err(String)` with a descriptive error message if invalid
574 pub fn validate_hessian_optimizer_compatibility(&self) -> Result<(), String> {
575 if self.gediis_variant_is_blend() && self.hessian_method.is_inverse() {
576 Err(
577 "Incompatible settings: use_gediis = blend requires a direct Hessian method\n\
578 (direct_psb, bofill, powell, or bfgs_powell_mix), but hessian = inverse_bfgs was selected.\n\
579 To fix: set hessian = direct_psb (recommended) or use use_gediis = false/sequential."
580 .to_string(),
581 )
582 } else {
583 Ok(())
584 }
585 }
586}
587
588/// Supported quantum chemistry programs.
589///
590/// Each variant represents a different QM program interface with specific
591/// capabilities and file formats:
592///
593/// | Program | Methods | Strengths |
594/// |---------|---------|-----------|
595/// | `Gaussian` | DFT, TD-DFT, MP2, CASSCF | Most features, checkpoints |
596/// | `Orca` | DFT, TD-DFT, CASSCF | Good for open-shell systems |
597/// | `Bagel` | CASSCF, MRCI | Advanced wavefunction methods |
598/// | `Xtb` | GFN2-xTB | Fast semi-empirical |
599/// | `Custom` | Any | JSON-configurable interface |
600#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
601pub enum QMProgram {
602 /// Gaussian (most commonly used, full feature support)
603 Gaussian,
604 /// ORCA (excellent for open-shell systems)
605 Orca,
606 /// BAGEL (advanced wavefunction methods)
607 Bagel,
608 /// XTB (fast semi-empirical calculations)
609 Xtb,
610 /// Custom (user-defined via JSON configuration)
611 Custom,
612}
613
614/// Execution mode for MECP calculations.
615///
616/// Different run modes are optimized for different scenarios, particularly
617/// around SCF convergence and wavefunction stability. The choice depends on:
618/// - Whether you're doing a fresh calculation or restarting
619/// - If you have convergence problems
620/// - If you're studying open-shell systems
621/// - If you're doing reaction path following
622#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
623pub enum RunMode {
624 /// Standard MECP optimization with two-phase workflow
625 ///
626 /// **Phase 1**: Pre-point calculations WITHOUT checkpoint reading to generate initial wavefunctions
627 /// **Phase 2**: Main optimization loop WITH checkpoint reading for faster SCF convergence
628 ///
629 /// **Program-specific behavior:**
630 /// - **Gaussian**: Generates .chk files in Phase 1, uses `guess=read` in Phase 2
631 /// - **ORCA**: Generates .gbw files in Phase 1, uses `!moread` in Phase 2
632 /// - **XTB**: Runs pre-point for initialization, no checkpoint files needed
633 /// - **BAGEL**: Validates model file in Phase 1, uses same model in Phase 2
634 /// - **Custom**: Follows Gaussian-like behavior (depends on interface configuration)
635 ///
636 /// - Recommended for most calculations
637 /// - Balanced between speed and robustness
638 Normal,
639 /// Restart from existing checkpoint file
640 ///
641 /// - Skips pre-point calculations
642 /// - Use for restarting interrupted calculations
643 /// - Faster start but requires valid checkpoint
644 Read,
645 /// Fresh SCF at each step (no checkpoint reading)
646 ///
647 /// - Slower but more robust
648 /// - Use for difficult SCF convergence
649 /// - Helpful when wavefunctions oscillate
650 NoRead,
651 /// Pre-point calculations for wavefunction stability
652 ///
653 /// - Runs stability checks before optimization
654 /// - Use for unstable wavefunctions
655 /// - Essential for problematic systems
656 Stable,
657 /// Interleaved reading for open-shell singlets
658 ///
659 /// - Runs state B first, copies to A
660 /// - Adds `guess=(read,mix)` for state A
661 /// - **Essential for open-shell singlet calculations**
662 InterRead,
663 /// Systematically drive a reaction coordinate
664 ///
665 /// - Varies a geometric parameter stepwise
666 /// - Generates energy profile along reaction path
667 /// - Use with drive_type, drive_atoms, drive_start, drive_end
668 CoordinateDrive,
669 /// Optimize entire reaction path using NEB
670 ///
671 /// - Creates initial path via coordinate driving
672 /// - Optimizes using Nudged Elastic Band method
673 /// - Identifies transition states and intermediates
674 PathOptimization,
675 /// Constrain energy difference to target value
676 ///
677 /// - Sets target ΔE using fix_de parameter
678 /// - Study avoided crossings
679 /// - Generate diabatic PES
680 FixDE,
681}