272 lines
8.5 KiB
Rust
272 lines
8.5 KiB
Rust
//! 氢原子碰撞激发速率。
|
||
//!
|
||
//! 重构自 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<ButlerData> = 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);
|
||
}
|
||
}
|