Skip to main content

castep_cell_io/param/geometry_optimization/
geom_method.rs

1use castep_cell_fmt::{Cell, CellValue, ToCell, ToCellValue};
2use castep_cell_fmt::parse::{FromCellValue, FromKeyValue};
3use castep_cell_fmt::{CResult, Error};
4use castep_cell_fmt::query::value_as_str;
5use serde::{Deserialize, Serialize};
6
7/// Determines the method used for geometry optimization.
8///
9/// Keyword type: String
10///
11/// Default: GeomMethod::Bfgs
12///
13/// Example:
14/// GEOM_METHOD : DampedMD
15#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
16#[serde(rename = "GEOM_METHOD")]
17#[derive(Default)]
18pub enum GeomMethod {
19    /// BFGS minimization
20    #[serde(alias = "bfgs", alias = "BFGS")]
21    #[default]
22    Bfgs,
23    /// Low-memory BFGS minimization
24    #[serde(alias = "lbfgs", alias = "LBFGS")]
25    Lbfgs,
26    /// BFGS minimization using delocalized internal coordinates
27    #[serde(rename = "Delocalized", alias = "delocalized", alias = "DELOCALIZED")]
28    #[serde(alias = "Delocalised", alias = "delocalised", alias = "DELOCALISED")]
29    // Alternative spelling
30    Delocalized,
31    /// Damped molecular dynamics
32    #[serde(alias = "dampedmd", alias = "DAMPEDMD", alias = "DampedMD")]
33    DampedMd,
34    /// Two-point steepest descent
35    #[serde(alias = "tpsd", alias = "TPSD")]
36    Tpsd,
37}
38
39impl FromCellValue for GeomMethod {
40    fn from_cell_value(value: &CellValue<'_>) -> CResult<Self> {
41        match value_as_str(value)?.to_ascii_lowercase().as_str() {
42            "bfgs" => Ok(Self::Bfgs),
43            "lbfgs" => Ok(Self::Lbfgs),
44            "delocalized" | "delocalised" => Ok(Self::Delocalized),
45            "dampedmd" => Ok(Self::DampedMd),
46            "tpsd" => Ok(Self::Tpsd),
47            other => Err(Error::Message(format!("unknown GeomMethod: {other}"))),
48        }
49    }
50}
51
52impl FromKeyValue for GeomMethod {
53    const KEY_NAME: &'static str = "GEOM_METHOD";
54
55    fn from_cell_value_kv(value: &CellValue<'_>) -> CResult<Self> {
56        Self::from_cell_value(value)
57    }
58}
59
60
61impl ToCell for GeomMethod {
62    fn to_cell(&self) -> Cell<'_> {
63        Cell::KeyValue("GEOM_METHOD", self.to_cell_value())
64    }
65}
66
67impl ToCellValue for GeomMethod {
68    fn to_cell_value(&self) -> CellValue<'_> {
69        CellValue::String(
70            match self {
71                GeomMethod::Bfgs => "BFGS",
72                GeomMethod::Lbfgs => "LBFGS",
73                GeomMethod::Delocalized => "Delocalized",
74                GeomMethod::DampedMd => "DampedMD",
75                GeomMethod::Tpsd => "TPSD",
76            }
77            .to_string(),
78        )
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85    use castep_cell_fmt::CellValue;
86
87    #[test]
88    fn test_case_insensitive() {
89        assert_eq!(GeomMethod::from_cell_value(&CellValue::Str("bfgs")).unwrap(), GeomMethod::Bfgs);
90        assert_eq!(GeomMethod::from_cell_value(&CellValue::Str("BFGS")).unwrap(), GeomMethod::Bfgs);
91        assert_eq!(GeomMethod::from_cell_value(&CellValue::Str("lbfgs")).unwrap(), GeomMethod::Lbfgs);
92    }
93
94    #[test]
95    fn test_all_variants() {
96        assert_eq!(GeomMethod::from_cell_value(&CellValue::Str("delocalized")).unwrap(), GeomMethod::Delocalized);
97        assert_eq!(GeomMethod::from_cell_value(&CellValue::Str("delocalised")).unwrap(), GeomMethod::Delocalized);
98        assert_eq!(GeomMethod::from_cell_value(&CellValue::Str("dampedmd")).unwrap(), GeomMethod::DampedMd);
99        assert_eq!(GeomMethod::from_cell_value(&CellValue::Str("tpsd")).unwrap(), GeomMethod::Tpsd);
100    }
101
102    #[test]
103    fn test_invalid() {
104        assert!(GeomMethod::from_cell_value(&CellValue::Str("invalid")).is_err());
105    }
106
107    #[test]
108    fn test_key_name() {
109        assert_eq!(GeomMethod::KEY_NAME, "GEOM_METHOD");
110    }
111}
112