405 lines
11 KiB
Rust
405 lines
11 KiB
Rust
//! 氦原子碰撞速率计算。
|
||
//!
|
||
//! 重构自 TLUSTY `COLHE` 子程序。
|
||
//!
|
||
//! # 功能
|
||
//!
|
||
//! - 计算中性氦(He I)和电离氦(He II)的碰撞速率
|
||
//! - 支持多种碰撞速率公式(ICOL = 0, 1, 2, 3)
|
||
//! - 包含碰撞电离和碰撞激发
|
||
|
||
use crate::tlusty::state::constants::{HK, H, UN};
|
||
|
||
// ============================================================================
|
||
// 常量和数据
|
||
// ============================================================================
|
||
|
||
/// 指数积分展开系数
|
||
const EXPIA1: f64 = -0.57721566;
|
||
const EXPIA2: f64 = 0.99999193;
|
||
const EXPIA3: f64 = -0.24991055;
|
||
const EXPIA4: f64 = 0.05519968;
|
||
const EXPIA5: f64 = -0.00976004;
|
||
const EXPIA6: f64 = 0.00107857;
|
||
|
||
const EXPIB1: f64 = 0.2677734343;
|
||
const EXPIB2: f64 = 8.6347608925;
|
||
const EXPIB3: f64 = 18.059016973;
|
||
const EXPIB4: f64 = 8.5733287401;
|
||
|
||
const EXPIC1: f64 = 3.9584969228;
|
||
const EXPIC2: f64 = 21.0996530827;
|
||
const EXPIC3: f64 = 25.6329561486;
|
||
const EXPIC4: f64 = 9.5733223454;
|
||
|
||
/// He I 从基态到 n=2-17 的振子强度
|
||
static FHE1: [f64; 16] = [
|
||
0.0, 2.75e-1, 7.29e-2, 2.96e-2, 1.48e-2, 8.5e-3, 5.3e-3,
|
||
3.5e-3, 2.5e-3, 1.8e-3, 1.5e-3, 1.2e-3, 9.4e-4, 7.5e-4,
|
||
6.1e-4, 5.3e-4,
|
||
];
|
||
|
||
/// He II 碰撞电离系数(低能级)
|
||
static G0: [f64; 3] = [7.3399521e-2, 1.7252867, 8.6335087];
|
||
static G1: [f64; 3] = [-1.4592763e-7, 2.0944117e-6, 2.7575544e-5];
|
||
static G2: [f64; 3] = [7.6621299e5, 5.4254879e6, 6.6395519e6];
|
||
static G3: [f64; 3] = [2.3775439e2, 2.2177891e3, 5.20725e3];
|
||
|
||
/// He II 碰撞电离系数(高能级)
|
||
static A: [[f64; 10]; 6] = [
|
||
[-8.5931587, 85.014091, 923.64099, 2018.6470, 1551.5061,
|
||
-2327.4819, -10701.481, -27619.789, -41099.602, -61599.023],
|
||
[9.3868790, -78.834488, -969.18451, -2243.1768, -2059.9768,
|
||
1546.7107, 9834.3447, 27067.436, 41421.254, 63594.133],
|
||
[-4.0027571, 28.360615, 401.23965, 983.83374, 1051.4103,
|
||
-204.82320, -3335.4211, -10100.119, -15863.257, -24949.125],
|
||
[0.83941799, -4.7963457, -81.122566, -209.86169, -251.30855,
|
||
-43.175175, 530.37292, 1826.1049, 2941.6460, 4740.8364],
|
||
[-8.6396709e-2, 0.37385577, 8.0078983, 21.757591, 28.375637,
|
||
11.890312, -39.536087, -161.52513, -266.86011, -440.88257],
|
||
[3.4853835e-3, -1.0401310e-2, -0.30957383, -0.87988985, -1.2254572,
|
||
-0.72724497, 1.0879648, 5.6239786, 9.5323009, 16.150818],
|
||
];
|
||
|
||
// ============================================================================
|
||
// 辅助函数
|
||
// ============================================================================
|
||
|
||
/// 计算指数积分 E1(x) 的近似值。
|
||
///
|
||
/// 使用 Abramowitz-Stegun 公式。
|
||
fn expi_approx(u0: f64) -> f64 {
|
||
if u0 <= UN {
|
||
// 小参数展开
|
||
-u0.ln() + EXPIA1 + u0 * (EXPIA2 + u0 * (EXPIA3 + u0 * (EXPIA4 + u0 * (EXPIA5 + u0 * EXPIA6))))
|
||
} else {
|
||
// 大参数渐近展开
|
||
let eu0 = (-u0).exp();
|
||
eu0 * ((EXPIB1 + u0 * (EXPIB2 + u0 * (EXPIB3 + u0 * (EXPIB4 + u0))))
|
||
/ (EXPIC1 + u0 * (EXPIC2 + u0 * (EXPIC3 + u0 * (EXPIC4 + u0))))) / u0
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 输入/输出结构体
|
||
// ============================================================================
|
||
|
||
/// COLHE 输入参数(简化版)。
|
||
pub struct ColheParams {
|
||
/// 温度 (K)
|
||
pub temp: f64,
|
||
/// 能级数(中性氦)
|
||
pub nlevel_he1: usize,
|
||
/// 能级数(电离氦)
|
||
pub nlevel_he2: usize,
|
||
}
|
||
|
||
/// COLHE 输出结果。
|
||
#[derive(Debug, Clone)]
|
||
pub struct ColheOutput {
|
||
/// 碰撞速率数组(简化版,仅示例)
|
||
pub col_rates: Vec<f64>,
|
||
}
|
||
|
||
// ============================================================================
|
||
// 核心计算函数
|
||
// ============================================================================
|
||
|
||
/// 计算 He I 碰撞电离速率。
|
||
///
|
||
/// # 参数
|
||
/// - `t`: 温度 (K)
|
||
/// - `enion`: 电离能 (erg)
|
||
/// - `osc0`: 振子强度
|
||
///
|
||
/// # 返回
|
||
/// 碰撞电离速率
|
||
pub fn colhe1_ionization(t: f64, enion: f64, osc0: f64) -> f64 {
|
||
let srt = t.sqrt();
|
||
let ct = 5.465e-11 * srt;
|
||
let tk = HK / H / t;
|
||
let u0 = enion * tk;
|
||
|
||
let u1 = u0 + 0.27;
|
||
let u2 = (u0 + 3.43) / (u0 + 1.43).powi(3);
|
||
|
||
let expiu0 = expi_approx(u0);
|
||
let expiu1 = expi_approx(u1);
|
||
|
||
ct * osc0 * u0 * (expiu0 - u0 * (0.728 * expiu1 / u1 + 0.189 * (-u0).exp() * u2))
|
||
}
|
||
|
||
/// 计算 He I 碰撞激发速率(从基态)。
|
||
///
|
||
/// # 参数
|
||
/// - `t`: 温度 (K)
|
||
/// - `u0`: 激发能量 / kT
|
||
/// - `osc0`: 振子强度
|
||
///
|
||
/// # 返回
|
||
/// 碰撞激发速率
|
||
pub fn colhe1_excitation_ground(t: f64, u0: f64, osc0: f64) -> f64 {
|
||
let srt = t.sqrt();
|
||
let ct1 = 5.4499487 / t / srt;
|
||
let ex = expi_approx(u0);
|
||
|
||
ct1 * ex / u0 * osc0
|
||
}
|
||
|
||
/// 计算 He I 碰撞激发速率(激发态之间)。
|
||
///
|
||
/// # 参数
|
||
/// - `t`: 温度 (K)
|
||
/// - `u0`: 激发能量 / kT
|
||
/// - `osc0`: 振子强度
|
||
///
|
||
/// # 返回
|
||
/// 碰撞激发速率
|
||
pub fn colhe1_excitation_excited(t: f64, u0: f64, osc0: f64) -> f64 {
|
||
let srt = t.sqrt();
|
||
let ct1 = 5.4499487 / t / srt;
|
||
|
||
let u1 = u0 + 0.2;
|
||
let ex = expi_approx(u0);
|
||
let expiu1 = expi_approx(u1);
|
||
|
||
ct1 / u0 * (ex - u0 / u1 * 0.81873 * expiu1) * osc0
|
||
}
|
||
|
||
/// 计算 He II 碰撞电离速率。
|
||
///
|
||
/// # 参数
|
||
/// - `t`: 温度 (K)
|
||
/// - `level_index`: 能级索引 (1-based, 1-10)
|
||
/// - `u0`: 电离能量 / kT
|
||
///
|
||
/// # 返回
|
||
/// 碰撞电离速率
|
||
pub fn colhe2_ionization(t: f64, level_index: usize, u0: f64) -> f64 {
|
||
let srt = t.sqrt();
|
||
let ct = 5.465e-11 * srt;
|
||
|
||
let x = t.log10();
|
||
let x2 = x * x;
|
||
let x3 = x2 * x;
|
||
let x4 = x3 * x;
|
||
let x5 = x4 * x;
|
||
|
||
let gam = if level_index <= 3 {
|
||
let i = level_index - 1;
|
||
G0[i] - G1[i] * t + (G2[i] / t - G3[i]) / t
|
||
} else if level_index == 4 {
|
||
-95.23828 + (62.656249 - 8.1454078 * x) * x
|
||
} else if level_index == 5 {
|
||
472.99219 - 74.144287 * x - 1869.6562 / x2
|
||
} else if level_index == 6 {
|
||
825.17186 - 134.23096 * x - 2739.4375 / x2
|
||
} else if level_index == 7 {
|
||
1181.3516 - 200.71191 * x - 2810.7812 / x2
|
||
} else if level_index == 8 {
|
||
1440.1016 - 259.75781 * x - 1283.5625 / x2
|
||
} else if level_index == 9 {
|
||
2492.1250 - 624.84375 * x + 30.101562 * x2
|
||
} else if level_index == 10 {
|
||
4663.3129 - 1390.1250 * x + 97.671874 * x2
|
||
} else {
|
||
// IC >= 1: 使用多项式拟合
|
||
let i = level_index - 1;
|
||
if i < 10 {
|
||
A[0][i] + A[1][i] * x + A[2][i] * x2 + A[3][i] * x3 + A[4][i] * x4 + A[5][i] * x5
|
||
} else {
|
||
(level_index * level_index * level_index) as f64
|
||
}
|
||
};
|
||
|
||
ct * (-u0).exp() * gam
|
||
}
|
||
|
||
/// 计算 He II 碰撞激发速率。
|
||
///
|
||
/// # 参数
|
||
/// - `t`: 温度 (K)
|
||
/// - `i`: 下能级主量子数
|
||
/// - `j`: 上能级主量子数
|
||
/// - `u0`: 激发能量 / kT
|
||
/// - `osh`: 振子强度因子
|
||
///
|
||
/// # 返回
|
||
/// 碰撞激发速率
|
||
pub fn colhe2_excitation(t: f64, i: usize, j: usize, u0: f64, osh: f64) -> f64 {
|
||
let srt = t.sqrt();
|
||
let ct2 = 3.7036489 / t / srt;
|
||
|
||
let xi = i as f64;
|
||
let xj = j as f64;
|
||
|
||
// 振子强度
|
||
let c1 = if j <= 20 { osh } else { osh * (20.0 / xj).powi(3) };
|
||
|
||
// Gaunt 因子
|
||
let mut gam = xi - (xi - 1.0) / (xj - xi);
|
||
if gam > xj - xi {
|
||
gam = xj - xi;
|
||
}
|
||
if i > 1 {
|
||
gam *= 1.1;
|
||
}
|
||
|
||
let expiu0 = expi_approx(u0);
|
||
|
||
ct2 / u0 * c1 * (0.693 * (-u0).exp() + expiu0) * gam
|
||
}
|
||
|
||
/// 执行 COLHE 主计算(简化版)。
|
||
///
|
||
/// # 参数
|
||
/// - `params`: 输入参数
|
||
///
|
||
/// # 返回
|
||
/// 碰撞速率结果
|
||
pub fn colhe(params: &ColheParams) -> ColheOutput {
|
||
let t = params.temp;
|
||
let srt = t.sqrt();
|
||
let hkt = HK / t;
|
||
let tk = hkt / H;
|
||
|
||
// 初始化输出
|
||
let mut col_rates = Vec::new();
|
||
|
||
// He I 碰撞电离示例(从基态)
|
||
let enion_he1 = 24.587 * 1.602e-12; // eV -> erg
|
||
let osc0 = 1.0;
|
||
let col_ion_he1 = colhe1_ionization(t, enion_he1, osc0);
|
||
col_rates.push(col_ion_he1);
|
||
|
||
// He II 碰撞电离示例(从 n=1)
|
||
let u0_he2 = 4.0 * 13.6 * 1.602e-12 * tk; // He II 电离能 = 4 * H
|
||
let col_ion_he2 = colhe2_ionization(t, 1, u0_he2);
|
||
col_rates.push(col_ion_he2);
|
||
|
||
ColheOutput { col_rates }
|
||
}
|
||
|
||
// ============================================================================
|
||
// 测试
|
||
// ============================================================================
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_expi_approx_small() {
|
||
// 小参数
|
||
let result = expi_approx(0.5);
|
||
assert!(result > 0.0);
|
||
assert!(result < 2.0); // E1(0.5) ≈ 0.56
|
||
}
|
||
|
||
#[test]
|
||
fn test_expi_approx_large() {
|
||
// 大参数
|
||
let result = expi_approx(5.0);
|
||
assert!(result > 0.0);
|
||
assert!(result < 0.01); // E1(5) 很小
|
||
}
|
||
|
||
#[test]
|
||
fn test_colhe1_ionization() {
|
||
let t = 10000.0;
|
||
let enion = 24.587 * 1.602e-12; // He I 电离能
|
||
let osc0 = 1.0;
|
||
|
||
let result = colhe1_ionization(t, enion, osc0);
|
||
assert!(result > 0.0);
|
||
assert!(result.is_finite());
|
||
}
|
||
|
||
#[test]
|
||
fn test_colhe1_excitation_ground() {
|
||
let t = 10000.0;
|
||
let u0 = 20.0; // 典型激发能量
|
||
let osc0 = 0.1;
|
||
|
||
let result = colhe1_excitation_ground(t, u0, osc0);
|
||
assert!(result > 0.0);
|
||
assert!(result.is_finite());
|
||
}
|
||
|
||
#[test]
|
||
fn test_colhe1_excitation_excited() {
|
||
let t = 10000.0;
|
||
let u0 = 5.0; // 激发态之间的跃迁
|
||
let osc0 = 0.5;
|
||
|
||
let result = colhe1_excitation_excited(t, u0, osc0);
|
||
assert!(result > 0.0);
|
||
assert!(result.is_finite());
|
||
}
|
||
|
||
#[test]
|
||
fn test_colhe2_ionization() {
|
||
let t = 20000.0;
|
||
let tk = HK / H / t;
|
||
let u0 = 4.0 * 13.6 * 1.602e-12 * tk;
|
||
|
||
for level in 1..=10 {
|
||
let result = colhe2_ionization(t, level, u0);
|
||
assert!(result > 0.0);
|
||
assert!(result.is_finite());
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_colhe2_excitation() {
|
||
let t = 20000.0;
|
||
let tk = HK / H / t;
|
||
let u0 = 3.0; // 典型值
|
||
let osh = 1.0;
|
||
|
||
let result = colhe2_excitation(t, 1, 2, u0, osh);
|
||
assert!(result > 0.0);
|
||
assert!(result.is_finite());
|
||
}
|
||
|
||
#[test]
|
||
fn test_colhe_basic() {
|
||
let params = ColheParams {
|
||
temp: 15000.0,
|
||
nlevel_he1: 19,
|
||
nlevel_he2: 10,
|
||
};
|
||
|
||
let result = colhe(¶ms);
|
||
assert_eq!(result.col_rates.len(), 2);
|
||
assert!(result.col_rates[0] > 0.0); // He I
|
||
assert!(result.col_rates[1] > 0.0); // He II
|
||
}
|
||
|
||
#[test]
|
||
fn test_temperature_dependence() {
|
||
let enion = 24.587 * 1.602e-12;
|
||
let osc0 = 1.0;
|
||
|
||
let col_low = colhe1_ionization(5000.0, enion, osc0);
|
||
let col_high = colhe1_ionization(20000.0, enion, osc0);
|
||
|
||
// 较高温度应该有更高的碰撞速率
|
||
assert!(col_high > col_low);
|
||
}
|
||
|
||
#[test]
|
||
fn test_colhe2_level_dependence() {
|
||
let t = 20000.0;
|
||
let tk = HK / H / t;
|
||
let u0_base = 4.0 * 13.6 * 1.602e-12 * tk;
|
||
|
||
// 不同能级应该有不同的速率
|
||
let col_n1 = colhe2_ionization(t, 1, u0_base);
|
||
let col_n2 = colhe2_ionization(t, 2, u0_base / 4.0); // n=2 电离能是 n=1 的 1/4
|
||
|
||
assert!(col_n1 > 0.0);
|
||
assert!(col_n2 > 0.0);
|
||
}
|
||
}
|