//! 氢原子碰撞激发速率。 //! //! 重构自 TLUSTY `butler.f` //! //! 基于 Przybilla & Butler (2004, ApJ) 表3的插值。 use std::sync::OnceLock; /// Butler 数据。 struct ButlerData { tref: [f64; 16], colstr: [[f64; 21]; 16], } static BUTLER_DATA: OnceLock = OnceLock::new(); fn get_butler_data() -> &'static ButlerData { BUTLER_DATA.get_or_init(|| { // 参考温度 (K) let tref = [ 2.5e3, 5e3, 7.5e3, 1e4, 1.5e4, 2e4, 2.5e4, 3e4, 4e4, 5e4, 6e4, 8e4, 1e5, 1.5e5, 2e5, 2.5e5, ]; // 碰撞强度数据 (16个温度 x 21个跃迁) // J=1,21 对应 (NI,NJ)={(1,2),(1,3),...,(1,7),(2,3),...,(6,7)} // I=1,16 对应 T={2.5e3,...,2.5e5} let colstr = [ // T = 2500 K [ 6.40e-1, 2.20e-1, 9.93e-2, 4.92e-2, 2.97e-2, 5.03e-2, 2.35e1, 1.07e1, 5.22e0, 2.91e0, 5.25e0, 1.50e2, 7.89e1, 4.13e1, 7.60e1, 5.90e2, 2.94e2, 4.79e2, 1.93e3, 1.95e3, 6.81e3, ], // T = 5000 K [ 6.98e-1, 2.40e-1, 1.02e-1, 5.84e-2, 4.66e-2, 6.72e-2, 2.78e1, 1.15e1, 5.90e0, 4.53e0, 7.26e0, 1.90e2, 9.01e1, 6.11e1, 1.07e2, 8.17e2, 4.21e2, 7.06e2, 2.91e3, 3.24e3, 1.17e4, ], // T = 7500 K [ 7.57e-1, 2.50e-1, 1.10e-1, 7.17e-2, 6.28e-2, 7.86e-2, 3.09e1, 1.23e1, 6.96e0, 6.06e0, 8.47e0, 2.28e2, 1.07e2, 8.21e1, 1.25e2, 1.07e3, 5.78e2, 8.56e2, 4.00e3, 4.20e3, 1.50e4, ], // T = 10000 K [ 8.09e-1, 2.61e-1, 1.22e-1, 8.58e-2, 7.68e-2, 8.74e-2, 3.38e1, 1.34e1, 8.15e0, 7.32e0, 9.27e0, 2.70e2, 1.26e2, 1.01e2, 1.37e2, 1.35e3, 7.36e2, 9.66e2, 5.04e3, 4.95e3, 1.73e4, ], // T = 15000 K [ 8.97e-1, 2.88e-1, 1.51e-1, 1.12e-1, 9.82e-2, 1.00e-1, 4.01e1, 1.62e1, 1.04e1, 9.17e0, 1.03e1, 3.64e2, 1.66e2, 1.31e2, 1.52e2, 1.93e3, 1.02e3, 1.11e3, 6.81e3, 6.02e3, 2.03e4, ], // T = 20000 K [ 9.78e-1, 3.22e-1, 1.80e-1, 1.33e-1, 1.14e-1, 1.10e-1, 4.71e1, 1.90e1, 1.23e1, 1.05e1, 1.08e1, 4.66e2, 2.03e2, 1.54e2, 1.61e2, 2.47e3, 1.26e3, 1.21e3, 8.20e3, 6.76e3, 2.21e4, ], // T = 25000 K [ 1.06e0, 3.59e-1, 2.06e-1, 1.50e-1, 1.25e-1, 1.16e-1, 5.45e1, 2.18e1, 1.39e1, 1.14e1, 1.12e1, 5.70e2, 2.37e2, 1.72e2, 1.68e2, 2.96e3, 1.46e3, 1.29e3, 9.29e3, 7.29e3, 2.33e4, ], // T = 30000 K [ 1.15e0, 3.96e-1, 2.28e-1, 1.64e-1, 1.33e-1, 1.21e-1, 6.20e1, 2.44e1, 1.52e1, 1.21e1, 1.14e1, 6.72e2, 2.68e2, 1.86e2, 1.72e2, 3.40e3, 1.64e3, 1.34e3, 1.02e4, 7.70e3, 2.41e4, ], // T = 40000 K [ 1.32e0, 4.64e-1, 2.66e-1, 1.85e-1, 1.45e-1, 1.27e-1, 7.71e1, 2.89e1, 1.74e1, 1.31e1, 1.17e1, 8.66e2, 3.19e2, 2.08e2, 1.78e2, 4.14e3, 1.92e3, 1.41e3, 1.15e4, 8.26e3, 2.52e4, ], // T = 50000 K [ 1.51e0, 5.26e-1, 2.95e-1, 2.01e-1, 1.53e-1, 1.31e-1, 9.14e1, 3.27e1, 1.90e1, 1.38e1, 1.18e1, 1.04e3, 3.62e2, 2.24e2, 1.81e2, 4.75e3, 2.15e3, 1.46e3, 1.26e4, 8.63e3, 2.60e4, ], // T = 60000 K [ 1.68e0, 5.79e-1, 3.18e-1, 2.12e-1, 1.58e-1, 1.34e-1, 1.05e2, 3.60e1, 2.03e1, 1.44e1, 1.19e1, 1.19e3, 3.98e2, 2.36e2, 1.83e2, 5.25e3, 2.33e3, 1.50e3, 1.34e4, 8.88e3, 2.69e4, ], // T = 80000 K [ 2.02e0, 6.70e-1, 3.55e-1, 2.29e-1, 1.65e-1, 1.35e-1, 1.29e2, 4.14e1, 2.23e1, 1.51e1, 1.19e1, 1.46e3, 4.53e2, 2.53e2, 1.85e2, 6.08e3, 2.61e3, 1.55e3, 1.49e4, 9.21e3, 2.90e4, ], // T = 100000 K [ 2.33e0, 7.43e-1, 3.83e-1, 2.39e-1, 1.70e-1, 1.37e-1, 1.51e2, 4.56e1, 2.37e1, 1.56e1, 1.20e1, 1.67e3, 4.95e2, 2.65e2, 1.86e2, 6.76e3, 2.81e3, 1.57e3, 1.63e4, 9.43e3, 3.17e4, ], // T = 150000 K [ 2.97e0, 8.80e-1, 4.30e-1, 2.59e-1, 1.77e-1, 1.39e-1, 1.93e2, 5.31e1, 2.61e1, 1.63e1, 1.19e1, 2.08e3, 5.68e2, 2.83e2, 1.87e2, 8.08e3, 3.15e3, 1.61e3, 1.97e4, 9.78e3, 3.94e4, ], // T = 200000 K [ 3.50e0, 9.79e-1, 4.63e-1, 2.71e-1, 1.82e-1, 1.39e-1, 2.26e2, 5.83e1, 2.78e1, 1.68e1, 1.19e1, 2.39e3, 6.16e2, 2.94e2, 1.86e2, 9.13e3, 3.36e3, 1.62e3, 2.27e4, 1.00e4, 4.73e4, ], // T = 250000 K (not used, for completeness) [ 3.95e0, 1.06e0, 4.88e-1, 2.81e-1, 1.85e-1, 1.40e-1, 2.52e2, 6.23e1, 2.89e1, 1.71e1, 1.19e1, 2.62e3, 6.51e2, 3.02e2, 1.87e2, 1.00e4, 3.51e3, 1.63e3, 2.54e4, 1.02e4, 5.50e4, ], ]; ButlerData { tref, colstr } }) } /// 氢原子碰撞激发速率。 /// /// 基于 Przybilla & Butler (2004, ApJ) 表3的插值计算电子碰撞激发速率。 /// /// # 参数 /// /// * `ni` - 下能级主量子数 (1-6) /// * `nj` - 上能级主量子数 (2-7) /// * `t` - 温度 (K) /// * `u0` - hν/kT /// /// # 返回值 /// /// `(col, ierr)` 元组: /// - `col`: 碰撞速率 (cm³/s) /// - `ierr`: 错误码 (0=正常, 1=温度超出范围, 2=量子数超出范围) /// /// # 备注 /// /// 基于 Przybilla & Butler (2004, ApJ) 表3。 pub fn butler(ni: i32, nj: i32, t: f64, u0: f64) -> (f64, i32) { const NL: i32 = 7; let mut ierr = 0; let mut col = 0.0; // 检查温度范围 if t < 2.5e3 || t >= 2.5e5 { ierr = 1; } // 检查量子数范围 if ni < 1 || ni > NL - 1 || nj < 2 || nj > NL { ierr = 2; } if ierr == 0 { // 计算 J 索引 (跃迁索引) let mut j: usize = 0; for _i in 1..ni { j += (NL - _i) as usize; } for _k in (ni + 1)..=nj { j += 1; } let data = get_butler_data(); // 找到最近的温度点 let mut ilow: usize = 0; while ilow < 15 && t >= data.tref[ilow + 1] { ilow += 1; } let mut ihig: usize = 15; while ihig > 0 && t < data.tref[ihig - 1] { ihig -= 1; } if ihig == ilow { ihig += 1; } // 对数-对数线性插值碰撞强度 let sl = (data.colstr[ihig][j - 1].log10() - data.colstr[ilow][j - 1].log10()) / (data.tref[ihig].log10() - data.tref[ilow].log10()); let or = data.colstr[ihig][j - 1].log10() - sl * data.tref[ihig].log10(); col = 10f64.powf(t.log10() * sl + or); // 计算速率 col = 8.631e-6 / (2.0 * (ni as f64).powi(2)) / t.sqrt() * (-u0).exp() * col; } (col, ierr) } #[cfg(test)] mod tests { use super::*; #[test] fn test_butler_lyman_alpha() { // Lyman-α: n=1 → n=2 let (col, ierr) = butler(1, 2, 10000.0, 11.87); assert_eq!(ierr, 0); assert!(col > 0.0); } #[test] fn test_butler_balmer_alpha() { // Balmer-α: n=2 → n=3 let (col, ierr) = butler(2, 3, 10000.0, 1.89); assert_eq!(ierr, 0); assert!(col > 0.0); } #[test] fn test_butler_low_temp() { // 低温边界 let (col, ierr) = butler(1, 2, 3000.0, 10.0); assert_eq!(ierr, 0); assert!(col > 0.0); } #[test] fn test_butler_high_temp() { // 高温边界 let (col, ierr) = butler(1, 2, 200000.0, 0.5); assert_eq!(ierr, 0); assert!(col > 0.0); } #[test] fn test_butler_temp_out_of_range_low() { // 温度过低 let (_, ierr) = butler(1, 2, 1000.0, 10.0); assert_eq!(ierr, 1); } #[test] fn test_butler_temp_out_of_range_high() { // 温度过高 let (_, ierr) = butler(1, 2, 300000.0, 0.5); assert_eq!(ierr, 1); } #[test] fn test_butler_quantum_out_of_range() { // 量子数超出范围 let (_, ierr) = butler(7, 8, 10000.0, 1.0); assert_eq!(ierr, 2); } #[test] fn test_butler_n6_to_n7() { // n=6 → n=7 跃迁 let (col, ierr) = butler(6, 7, 10000.0, 0.1); assert_eq!(ierr, 0); assert!(col > 0.0); } }