//! Griem Stark 阻尼参数计算。 //! //! 重构自 SYNSPEC `griem.f` //! //! 从输入的 Stark 宽度值计算 Stark 阻尼参数 (GAM)。 //! 输入值对应 T=5000, 10000, 20000, 40000 K, //! 以及 NE=1e16 (中性原子) 或 NE=1e17 (离子)。 /// Griem 计算所需的模型层参数。 #[derive(Debug, Clone)] pub struct GriemParams { /// 温度插值索引 (JT) pub jt: usize, /// 温度插值系数 0 pub ti0: f64, /// 温度插值系数 1 pub ti1: f64, /// 温度插值系数 2 pub ti2: f64, } /// 计算 Griem Stark 阻尼参数 GAM。 /// /// # 参数 /// /// * `t` - 温度 (K),如果 <= 0 则返回 0 /// * `ane` - 电子数密度 /// * `ion` - 电离级数 (1=中性, 2=一次电离, ...) /// * `fr` - 频率相关因子 /// * `wgr` - Stark 宽度数组 [W@5000K, W@10000K, W@20000K, W@40000K] /// * `params` - 模型层插值参数 /// /// # 返回值 /// /// Stark 阻尼参数 GAM /// /// # 算法 /// /// ```text /// GAM = (插值后的WGR) * ANE * 1e-10 * FR * 1e-10 * FR * 4.2e-14 /// 如果 ION > 1: GAM = GAM * 0.1 /// 如果 GAM < 0: GAM = 0 /// ``` pub fn griem(t: f64, ane: f64, ion: i32, fr: f64, wgr: &[f64; 4], params: &GriemParams) -> f64 { // 温度检查 if t <= 0.0 { return 0.0; } // 温度插值 let jt_idx = params.jt; // 已是 0-indexed // 插值计算 Stark 宽度 let w_interp = params.ti0 * wgr[jt_idx] + params.ti1 * wgr[jt_idx - 1] + params.ti2 * wgr[jt_idx - 2]; // 计算 GAM let mut gam = w_interp * ane * 1e-10 * fr * 1e-10 * fr * 4.2e-14; // 离子的修正 if ion > 1 { gam *= 0.1; } // 确保非负 gam.max(0.0) } #[cfg(test)] mod tests { use super::*; use approx::assert_relative_eq; fn make_params(jt: usize) -> GriemParams { GriemParams { jt, ti0: 1.0, ti1: 0.0, ti2: 0.0, } } #[test] fn test_basic() { let params = make_params(2); let wgr = [1.0, 1.0, 1.0, 1.0]; let gam = griem(10000.0, 1e13, 1, 1.0, &wgr, ¶ms); assert!(gam >= 0.0); assert!(gam.is_finite()); } #[test] fn test_zero_temperature() { let params = make_params(2); let wgr = [1.0, 1.0, 1.0, 1.0]; let gam = griem(0.0, 1e13, 1, 1.0, &wgr, ¶ms); assert_relative_eq!(gam, 0.0); } #[test] fn test_negative_temperature() { let params = make_params(2); let wgr = [1.0, 1.0, 1.0, 1.0]; let gam = griem(-100.0, 1e13, 1, 1.0, &wgr, ¶ms); assert_relative_eq!(gam, 0.0); } #[test] fn test_ion_correction() { let params = make_params(2); let wgr = [1.0, 1.0, 1.0, 1.0]; let gam_neutral = griem(10000.0, 1e13, 1, 1.0, &wgr, ¶ms); let gam_ion = griem(10000.0, 1e13, 2, 1.0, &wgr, ¶ms); // 离子的 GAM 应该是中性的 0.1 倍 assert_relative_eq!(gam_ion, 0.1 * gam_neutral, epsilon = 1e-10); } #[test] fn test_density_scaling() { let params = make_params(2); let wgr = [1.0, 1.0, 1.0, 1.0]; let gam1 = griem(10000.0, 1e13, 1, 1.0, &wgr, ¶ms); let gam2 = griem(10000.0, 2e13, 1, 1.0, &wgr, ¶ms); // 密度翻倍,GAM 应该翻倍 assert_relative_eq!(gam2, 2.0 * gam1, epsilon = 1e-10); } #[test] fn test_fr_scaling() { let params = make_params(2); let wgr = [1.0, 1.0, 1.0, 1.0]; let gam1 = griem(10000.0, 1e13, 1, 1.0, &wgr, ¶ms); let gam2 = griem(10000.0, 1e13, 1, 2.0, &wgr, ¶ms); // FR 翻倍,GAM 应该是 4 倍 (因为 FR^2) assert_relative_eq!(gam2, 4.0 * gam1, epsilon = 1e-10); } #[test] fn test_temperature_interpolation() { let params = make_params(2); let wgr = [1.0, 2.0, 4.0, 8.0]; // 不同的温度宽度 let gam = griem(10000.0, 1e13, 1, 1.0, &wgr, ¶ms); // jt=2 使用 wgr[2]=4.0 assert!(gam > 0.0); } #[test] fn test_negative_result_clamped() { let params = GriemParams { jt: 2, ti0: -1.0, // 负系数 ti1: 0.0, ti2: 0.0, }; let wgr = [1.0, 1.0, 1.0, 1.0]; let gam = griem(10000.0, 1e13, 1, 1.0, &wgr, ¶ms); // 负值应该被钳制为 0 assert_relative_eq!(gam, 0.0); } }