spectrafit_models/
harmonic_ir.rs1use crate::Model;
2
3pub struct HarmonicIr;
9
10impl Model for HarmonicIr {
11 fn eval(&self, x: &[f64], params: &[f64]) -> f64 {
12 let (a, c, sigma) = (params[0], params[1], params[2]);
13 let d = c * c - x[0] * x[0];
14 a / (d * d + (sigma * x[0]) * (sigma * x[0]))
15 }
16
17 fn jacobian(&self, x: &[f64], params: &[f64]) -> Vec<f64> {
18 let mut p = params.to_vec();
19 (0..params.len())
20 .map(|i| {
21 let h = 1e-7_f64 * params[i].abs().max(1e-7);
22 p[i] = params[i] + h;
23 let f_plus = self.eval(x, &p);
24 p[i] = params[i] - h;
25 let f_minus = self.eval(x, &p);
26 p[i] = params[i];
27 (f_plus - f_minus) / (2.0 * h)
28 })
29 .collect()
30 }
31
32 fn param_names(&self) -> Vec<std::borrow::Cow<'static, str>> {
33 vec!["amplitude".into(), "center".into(), "sigma".into()]
34 }
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40 use approx::assert_relative_eq;
41
42 #[test]
43 fn eval_at_resonance() {
44 assert_relative_eq!(
46 HarmonicIr.eval(&[2.0], &[1.0, 2.0, 0.5]),
47 1.0,
48 epsilon = 1e-12
49 );
50 }
51
52 #[test]
53 fn param_names_and_jacobian() {
54 assert_eq!(
55 HarmonicIr
56 .param_names()
57 .iter()
58 .map(|c| c.as_ref())
59 .collect::<Vec<_>>(),
60 &["amplitude", "center", "sigma"]
61 );
62 assert_eq!(HarmonicIr.jacobian(&[1.0], &[1.0, 2.0, 0.5]).len(), 3);
63 }
64
65 #[test]
76 fn sigma_zero_undamped_matches_closed_form() {
77 let m = HarmonicIr;
78 let a = 2.0_f64;
79 let c = 1.5_f64;
80 let p = [a, c, 0.0]; for &xi in &[0.5_f64, 1.0, 2.0, 3.0, 5.0] {
82 let d = c * c - xi * xi;
83 let expected = a / (d * d);
84 assert_relative_eq!(m.eval(&[xi], &p), expected, epsilon = 1e-12);
85 }
86 }
87}