637 lines
20 KiB
Rust
637 lines
20 KiB
Rust
//! 氢碰撞速率计算。
|
||
//!
|
||
//! 重构自 TLUSTY `colh.f`
|
||
//!
|
||
//! 计算氢的碰撞电离和碰撞激发速率。
|
||
//! 标准表达式来自 Mihalas, Heasley, and Auer (1975)。
|
||
|
||
use crate::tlusty::math::butler;
|
||
use crate::tlusty::math::ceh12;
|
||
use crate::tlusty::math::cspec;
|
||
use crate::tlusty::math::irc;
|
||
use crate::tlusty::data::{COLH_CCOOL, COLH_CHOT};
|
||
use crate::tlusty::state::constants::{EH, HK, MLEVEL, TWO, UN};
|
||
// f2r_depends: BUTLER, CSPEC, IRC
|
||
|
||
// 物理常量
|
||
const CC0: f64 = 5.465e-11;
|
||
const CEX1: f64 = -30.20581;
|
||
const CEX2: f64 = 3.8608704;
|
||
const CEX3: f64 = 305.63574;
|
||
const ALF0: f64 = 1.8;
|
||
const ALF1: f64 = 0.4;
|
||
const BET0: f64 = 3.0;
|
||
const BET1: f64 = 1.2;
|
||
const O148: f64 = 0.148;
|
||
const CHMI: f64 = 5.59e-15;
|
||
|
||
// 指数积分系数
|
||
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;
|
||
|
||
/// COLH 输入参数
|
||
pub struct ColhParams {
|
||
/// 深度索引 (1-indexed)
|
||
pub id: usize,
|
||
/// 温度 (K)
|
||
pub t: f64,
|
||
/// 碰撞速率选择标志
|
||
pub icolhn: i32,
|
||
}
|
||
|
||
/// COLH 原子数据
|
||
pub struct ColhAtomicData<'a> {
|
||
/// 氢元素索引
|
||
pub ielh: usize,
|
||
/// H- 元素索引 (0 表示没有 H-)
|
||
pub ielhm: usize,
|
||
/// 氢原子索引
|
||
pub iath: usize,
|
||
/// 能级的第一个索引 (nelem)
|
||
pub nfirst: &'a [i32],
|
||
/// 能级的最后一个索引 (nelem)
|
||
pub nlast: &'a [i32],
|
||
/// 电离能级索引 (natom)
|
||
pub nka: &'a [i32],
|
||
/// 主量子数 (nlevel)
|
||
pub nquant: &'a [i32],
|
||
/// 上能级截止 (nelem)
|
||
pub icup: &'a [i32],
|
||
/// 跃迁索引数组 (nlevel × nlevel)
|
||
pub itra: &'a [i32],
|
||
/// 碰撞速率标志 (ntrans)
|
||
pub icol: &'a [i32],
|
||
/// 跃迁频率 (ntrans)
|
||
pub fr0: &'a [f64],
|
||
/// 振子强度 (ntrans)
|
||
pub osc0: &'a [f64],
|
||
/// 碰撞参数 (ntrans)
|
||
pub cpar: &'a [f64],
|
||
/// 电离能 (nlevel)
|
||
pub enion: &'a [f64],
|
||
/// 能级宽度选项 (nlevel)
|
||
pub ifwop: &'a [i32],
|
||
/// WNHINT 数组 (nlmx × nd)
|
||
pub wnhint: &'a [f64],
|
||
/// OSH 振子强度 (10 × 20)
|
||
pub osh: &'a [f64],
|
||
/// 最大谱线数
|
||
pub nlmx: usize,
|
||
}
|
||
|
||
/// COLH 输出
|
||
pub struct ColhOutput<'a> {
|
||
/// 碰撞速率数组 (ntrans)
|
||
pub col: &'a mut [f64],
|
||
}
|
||
|
||
// A 系数数组(用于高 n 碰撞电离)
|
||
static A: [[f64; 10]; 6] = [
|
||
[-86.7633398, 2632.8369, 7478.9556, -4202.8442, -47995.930,
|
||
-120942.89, -202300.81, -261373.03, -266337.91, -192293.20],
|
||
[100.919188, -2738.7485, -8495.4590, 1937.3763, 45825.371,
|
||
122209.39, 211928.67, 285044.75, 309455.47, 258802.22],
|
||
[-45.7813807, 1121.3976, 3794.6826, 340.35764, -16617.055,
|
||
-47390.313, -84973.688, -117833.95, -133243.61, -120363.95],
|
||
[10.1978559, -224.30670, -822.83636, -290.10489, 2905.7393,
|
||
8944.6025, 16556.992, 23544.543, 27419.742, 26002.143],
|
||
[-1.11223557, 21.923729, 86.619110, 48.840523, -246.99014,
|
||
-828.41028, -1581.2722, -2297.9321, -2738.1743, -2686.4087],
|
||
[0.0474198818, -0.83974838, -3.5534720, -2.6097214, 8.1972208,
|
||
30.267115, 59.521984, 88.178680, 107.05288, 107.73775],
|
||
];
|
||
|
||
/// 计算氢的碰撞速率。
|
||
///
|
||
/// # 参数
|
||
///
|
||
/// * `params` - 输入参数
|
||
/// * `atomic` - 原子数据
|
||
/// * `output` - 输出碰撞速率
|
||
pub fn colh(params: &ColhParams, atomic: &ColhAtomicData, output: &mut ColhOutput) {
|
||
let t = params.t;
|
||
let id = params.id;
|
||
let id_idx = id - 1;
|
||
|
||
let hkt = HK / t;
|
||
let ct = CC0 * t.sqrt();
|
||
let tk = hkt / EH;
|
||
let x = t.log10();
|
||
let x2 = x * x;
|
||
let x3 = x * x2;
|
||
let x4 = x2 * x2;
|
||
let x5 = x3 * x2;
|
||
let sqt = t.sqrt();
|
||
|
||
let xtt = [1.0, t, t * t, t * t * t];
|
||
|
||
let n0hn = atomic.nfirst[atomic.ielh] as usize - 1;
|
||
let n1h = atomic.nlast[atomic.ielh] as usize - 1;
|
||
let nkh = atomic.nka[atomic.iath] as usize - 1;
|
||
let n1q = if atomic.icup[atomic.ielh] > 0 {
|
||
atomic.icup[atomic.ielh] as usize
|
||
} else {
|
||
0
|
||
};
|
||
let n1hc = if atomic.ifwop[n1h] < 0 { n1h - 1 } else { n1h };
|
||
let mut nhl = if n1q > 0 {
|
||
n1q
|
||
} else {
|
||
n1h - n0hn + 1
|
||
};
|
||
if atomic.ifwop[n1h] < 0 {
|
||
nhl = atomic.nlmx;
|
||
}
|
||
|
||
for ii in n0hn..=n1h {
|
||
let i = ii - n0hn + 1;
|
||
let it = atomic.itra[ii + MLEVEL * nkh] as usize;
|
||
if it == 0 {
|
||
continue;
|
||
}
|
||
|
||
// 碰撞电离
|
||
if t > 1e6 {
|
||
// 高温使用 XSTAR 公式
|
||
let rno = 16.0;
|
||
let izc = 1;
|
||
let cs = irc(i as i32, t, izc, rno);
|
||
output.col[it - 1] = cs;
|
||
} else {
|
||
let ic = atomic.icol[it - 1];
|
||
let u0 = atomic.fr0[it - 1] * hkt;
|
||
|
||
if ic < 0 {
|
||
// 非标准公式
|
||
output.col[it - 1] = cspec(
|
||
ii as i32, nkh as i32, ic,
|
||
atomic.osc0[it - 1], atomic.cpar[it - 1], u0, t
|
||
);
|
||
} else if atomic.ifwop[ii] < 0 {
|
||
// 合并态电离
|
||
let ehk = EH / tk;
|
||
let n00q = atomic.nquant[n1h - 1] as usize + 1;
|
||
let mut sum1 = 0.0;
|
||
let mut sum2 = 0.0;
|
||
for img in n00q..=atomic.nlmx {
|
||
let xi = img as f64;
|
||
let xii = xi * xi;
|
||
sum1 += xii * xii * xi * atomic.wnhint[(img - 1) + atomic.nlmx * id_idx];
|
||
sum2 += xii * atomic.wnhint[(img - 1) + atomic.nlmx * id_idx] * (ehk / xii).exp();
|
||
}
|
||
output.col[it - 1] = ct * sum1 / sum2;
|
||
} else {
|
||
// 标准公式
|
||
let gam = if i > 10 {
|
||
(i * i * i) as f64
|
||
} else {
|
||
A[0][i - 1] + A[1][i - 1] * x + A[2][i - 1] * x2
|
||
+ A[3][i - 1] * x3 + A[4][i - 1] * x4 + A[5][i - 1] * x5
|
||
};
|
||
output.col[it - 1] = ct * (-u0).exp() * gam;
|
||
}
|
||
}
|
||
|
||
// 碰撞激发
|
||
let i1 = i + 1;
|
||
if i1 > nhl {
|
||
continue;
|
||
}
|
||
|
||
let xi = i as f64;
|
||
let vi = xi * xi;
|
||
let alf = ALF0 - ALF1 / vi;
|
||
let bet = BET0 - BET1 / xi;
|
||
let csca = 8.63e-6 / 2.0 / vi / sqt;
|
||
let mut csum = 0.0;
|
||
|
||
for j in i1..nhl {
|
||
let xj = j as f64;
|
||
let vj = xj * xj;
|
||
let jj = j + n0hn;
|
||
|
||
let (cs, is_lumped) = if jj > n1hc {
|
||
// 非显式能级,归入碰撞电离
|
||
let e = UN / vi - UN / vj;
|
||
let u0 = EH * e * tk;
|
||
let c1 = if j <= 20 {
|
||
atomic.osh[(i - 1) + 20 * (j - 1)]
|
||
} else {
|
||
atomic.osh[(i - 1) + 20 * 19] * ((400.0 - vi) / 20.0 * xj / (vj - vi)).powi(3)
|
||
};
|
||
(compute_collision_rate(params.icolhn, i, j, t, u0, c1, csca, alf, bet, xi, xj, &xtt), true)
|
||
} else {
|
||
let ict = atomic.itra[ii + MLEVEL * (jj - 1)] as usize;
|
||
if ict == 0 {
|
||
continue;
|
||
}
|
||
let ic = atomic.icol[ict - 1];
|
||
let u0 = atomic.fr0[ict - 1] * hkt;
|
||
let c1 = atomic.osc0[ict - 1];
|
||
|
||
let cs = if ic < 0 {
|
||
cspec(ii as i32, jj as i32, ic, c1, atomic.cpar[ict - 1], u0, t)
|
||
} else if ic == 0 {
|
||
compute_collision_rate(params.icolhn, i, j, t, u0, c1, csca, alf, bet, xi, xj, &xtt)
|
||
} else if ic == 1 {
|
||
ct * (-u0).exp() * (CEX1 + CEX2 * x + CEX3 / x / x)
|
||
} else {
|
||
ceh12(t)
|
||
};
|
||
(cs, false)
|
||
};
|
||
|
||
if is_lumped {
|
||
csum += cs;
|
||
} else {
|
||
let ict = atomic.itra[ii + MLEVEL * (jj - 1)] as usize;
|
||
if ict > 0 {
|
||
output.col[ict - 1] = cs;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 添加累积碰撞速率
|
||
let it_main = atomic.itra[ii + MLEVEL * nkh] as usize;
|
||
if it_main > 0 && n1q > 0 {
|
||
output.col[it_main - 1] += csum;
|
||
}
|
||
let ith = atomic.itra[ii + MLEVEL * n1h] as usize;
|
||
if atomic.ifwop[n1h] < 0 && ith > 0 {
|
||
output.col[ith - 1] = csum;
|
||
}
|
||
}
|
||
|
||
// H- 碰撞电离
|
||
if atomic.ielhm > 0 {
|
||
let it_hm = atomic.itra[(atomic.nfirst[atomic.ielhm] as usize - 1) + MLEVEL * n0hn] as usize;
|
||
if it_hm > 0 {
|
||
let ic = atomic.icol[it_hm - 1];
|
||
if ic >= 0 {
|
||
output.col[it_hm - 1] = CHMI * t * t.sqrt();
|
||
} else {
|
||
let u0 = atomic.enion[atomic.nfirst[atomic.ielhm] as usize - 1] * tk;
|
||
output.col[it_hm - 1] = cspec(
|
||
atomic.nfirst[atomic.ielhm] as i32,
|
||
n0hn as i32,
|
||
ic,
|
||
atomic.osc0[it_hm - 1],
|
||
atomic.cpar[it_hm - 1],
|
||
u0,
|
||
t,
|
||
);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 计算碰撞激发速率
|
||
fn compute_collision_rate(
|
||
icolhn: i32,
|
||
i: usize,
|
||
j: usize,
|
||
t: f64,
|
||
u0: f64,
|
||
c1: f64,
|
||
csca: f64,
|
||
alf: f64,
|
||
bet: f64,
|
||
xi: f64,
|
||
xj: f64,
|
||
xtt: &[f64],
|
||
) -> f64 {
|
||
// 使用 Butler 新计算
|
||
if icolhn == 1 && j <= 7 {
|
||
let (cs, ierr) = butler(i as i32, j as i32, t, u0);
|
||
if ierr == 0 {
|
||
return cs;
|
||
}
|
||
}
|
||
|
||
// Giovanardi 公式
|
||
if icolhn == 2 && j <= 15 {
|
||
let cs = if t <= 60000.0 {
|
||
get_ccool(i, j, xtt)
|
||
} else {
|
||
get_chot(i, j, xtt)
|
||
};
|
||
return csca * cs * (-u0).exp();
|
||
}
|
||
|
||
// 标准公式 (Mihalas et al 1975)
|
||
let ct = CC0 * t.sqrt();
|
||
let e = u0 / (HK / t);
|
||
let cs = 4.0 * ct * c1 / (e * e);
|
||
let ex = (-u0).exp();
|
||
|
||
let e1 = if u0 <= UN {
|
||
-u0.ln() + EXPIA1 + u0 * (EXPIA2 + u0 * (EXPIA3 + u0 * (EXPIA4 + u0 * (EXPIA5 + u0 * EXPIA6))))
|
||
} else {
|
||
let u0_inv = 1.0 / u0;
|
||
(-u0).exp() * ((EXPIB1 + u0 * (EXPIB2 + u0 * (EXPIB3 + u0 * EXPIB4)))
|
||
/ (EXPIC1 + u0 * (EXPIC2 + u0 * (EXPIC3 + u0 * EXPIC4))))
|
||
* u0_inv
|
||
};
|
||
|
||
let mut e5 = e1;
|
||
for ix in 1..=4 {
|
||
e5 = (ex - u0 * e5) / ix as f64;
|
||
}
|
||
|
||
let mut cs = cs * u0 * (e1 + O148 * u0 * e5);
|
||
if j - i != 1 {
|
||
cs *= bet + TWO * (alf - bet) / (xj - xi);
|
||
}
|
||
cs
|
||
}
|
||
|
||
/// 获取 CCOOL 系数(从 data.rs 获取)
|
||
/// CCOOL(I, J, K) - I=1..4, J=1..14, K=1..15
|
||
/// Fortran 列优先: idx = (I-1) + 4*((J-1) + 14*(K-1))
|
||
fn get_ccool(i: usize, j: usize, xtt: &[f64]) -> f64 {
|
||
// Giovanardi 公式: CS = sum_{ica=1}^{4} CCOOL(ica, i, j) * XTT(ica)
|
||
let mut cs = 0.0;
|
||
for ica in 0..4 {
|
||
// Fortran: CCOOL(ica+1, i, j)
|
||
// 列优先索引: (ica) + 4*((i-1) + 14*(j-1))
|
||
let idx = ica + 4 * ((i - 1) + 14 * (j - 1));
|
||
if idx < COLH_CCOOL.len() {
|
||
cs += COLH_CCOOL[idx] * xtt[ica];
|
||
}
|
||
}
|
||
cs
|
||
}
|
||
|
||
/// 获取 CHOT 系数(从 data.rs 获取)
|
||
/// CHOT(I, J, K) - I=1..4, J=1..14, K=1..15
|
||
/// Fortran 列优先: idx = (I-1) + 4*((J-1) + 14*(K-1))
|
||
fn get_chot(i: usize, j: usize, xtt: &[f64]) -> f64 {
|
||
// Giovanardi 公式: CS = sum_{ica=1}^{4} CHOT(ica, i, j) * XTT(ica)
|
||
let mut cs = 0.0;
|
||
for ica in 0..4 {
|
||
// Fortran: CHOT(ica+1, i, j)
|
||
// 列优先索引: (ica) + 4*((i-1) + 14*(j-1))
|
||
let idx = ica + 4 * ((i - 1) + 14 * (j - 1));
|
||
if idx < COLH_CHOT.len() {
|
||
cs += COLH_CHOT[idx] * xtt[ica];
|
||
}
|
||
}
|
||
cs
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_colh_constants() {
|
||
assert!((CC0 - 5.465e-11).abs() < 1e-20);
|
||
assert!((ALF0 - 1.8).abs() < 1e-10);
|
||
assert!((BET0 - 3.0).abs() < 1e-10);
|
||
}
|
||
|
||
#[test]
|
||
fn test_get_ccool_returns_data() {
|
||
// 测试 get_ccool 函数是否正确返回数据
|
||
let xtt = [1.0, 10000.0, 1e8, 1e12]; // T=10000K 的幂次
|
||
|
||
// 测试几个有效索引
|
||
for i in 1..=14 {
|
||
for j in 1..=15 {
|
||
let cs = get_ccool(i, j, &xtt);
|
||
// 返回值应该是有限数
|
||
assert!(cs.is_finite(), "get_ccool({}, {}) returned non-finite: {}", i, j, cs);
|
||
}
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_get_chot_returns_data() {
|
||
// 测试 get_chot 函数是否正确返回数据
|
||
let xtt = [1.0, 100000.0, 1e10, 1e15]; // T=100000K 的幂次
|
||
|
||
// 测试几个有效索引
|
||
for i in 1..=14 {
|
||
for j in 1..=15 {
|
||
let cs = get_chot(i, j, &xtt);
|
||
// 返回值应该是有限数
|
||
assert!(cs.is_finite(), "get_chot({}, {}) returned non-finite: {}", i, j, cs);
|
||
}
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_get_ccool_temperature_scaling() {
|
||
// 验证 CCOOL 系数随温度的缩放
|
||
let xtt_low = [1.0, 5000.0, 2.5e7, 1.25e11];
|
||
let xtt_high = [1.0, 50000.0, 2.5e9, 1.25e14];
|
||
|
||
for i in 1..=5 {
|
||
for j in 1..=5 {
|
||
let cs_low = get_ccool(i, j, &xtt_low);
|
||
let cs_high = get_ccool(i, j, &xtt_high);
|
||
|
||
// 高温应该有更大的系数(大部分情况)
|
||
// 这里只验证都是有限数
|
||
assert!(cs_low.is_finite() && cs_high.is_finite());
|
||
}
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_compute_collision_rate_standard() {
|
||
// 测试标准 Mihalas 公式
|
||
let t: f64 = 10000.0;
|
||
let i = 1;
|
||
let j = 2;
|
||
let u0: f64 = 0.5; // 激发能量 / kT
|
||
let c1: f64 = 0.4162; // Lyman-alpha 振子强度
|
||
let csca = 8.63e-6 / 2.0 / 1.0 / t.sqrt();
|
||
let alf = ALF0 - ALF1 / 1.0;
|
||
let bet = BET0 - BET1 / 1.0;
|
||
let xi = 1.0;
|
||
let xj = 2.0;
|
||
let xtt = [1.0, t, t*t, t*t*t];
|
||
|
||
// 使用 icolhn=0 强制使用标准公式
|
||
let cs = compute_collision_rate(0, i, j, t, u0, c1, csca, alf, bet, xi, xj, &xtt);
|
||
|
||
// 碰撞速率应该是有限的(可能很小但应该有效)
|
||
assert!(cs.is_finite(), "Collision rate should be finite");
|
||
}
|
||
|
||
#[test]
|
||
fn test_compute_collision_rate_butler() {
|
||
// 测试 Butler 公式 (icolhn=1, j<=7)
|
||
let t: f64 = 10000.0;
|
||
let i = 1;
|
||
let j = 2;
|
||
let u0: f64 = 0.5;
|
||
let c1: f64 = 0.4162;
|
||
let csca = 8.63e-6 / 2.0 / 1.0 / t.sqrt();
|
||
let alf = ALF0 - ALF1 / 1.0;
|
||
let bet = BET0 - BET1 / 1.0;
|
||
let xi = 1.0;
|
||
let xj = 2.0;
|
||
let xtt = [1.0, t, t*t, t*t*t];
|
||
|
||
let cs = compute_collision_rate(1, i, j, t, u0, c1, csca, alf, bet, xi, xj, &xtt);
|
||
|
||
assert!(cs > 0.0, "Butler collision rate should be positive");
|
||
assert!(cs.is_finite(), "Butler collision rate should be finite");
|
||
}
|
||
|
||
#[test]
|
||
fn test_compute_collision_rate_giovanardi_cool() {
|
||
// 测试 Giovanardi 冷公式 (icolhn=2, j<=15, T<=60000K)
|
||
let t: f64 = 10000.0;
|
||
let i = 1;
|
||
let j = 2;
|
||
let u0: f64 = 0.5;
|
||
let c1: f64 = 0.4162;
|
||
let csca = 8.63e-6 / 2.0 / 1.0 / t.sqrt();
|
||
let alf = ALF0 - ALF1 / 1.0;
|
||
let bet = BET0 - BET1 / 1.0;
|
||
let xi = 1.0;
|
||
let xj = 2.0;
|
||
let xtt = [1.0, t, t*t, t*t*t];
|
||
|
||
let cs = compute_collision_rate(2, i, j, t, u0, c1, csca, alf, bet, xi, xj, &xtt);
|
||
|
||
assert!(cs > 0.0, "Giovanardi cool collision rate should be positive");
|
||
assert!(cs.is_finite(), "Giovanardi cool collision rate should be finite");
|
||
}
|
||
|
||
#[test]
|
||
fn test_compute_collision_rate_giovanardi_hot() {
|
||
// 测试 Giovanardi 热公式 (icolhn=2, j<=15, T>60000K)
|
||
let t: f64 = 100000.0;
|
||
let i = 1;
|
||
let j = 2;
|
||
let u0: f64 = 0.1; // 更小的激发能量比
|
||
let c1: f64 = 0.4162;
|
||
let csca = 8.63e-6 / 2.0 / 1.0 / t.sqrt();
|
||
let alf = ALF0 - ALF1 / 1.0;
|
||
let bet = BET0 - BET1 / 1.0;
|
||
let xi = 1.0;
|
||
let xj = 2.0;
|
||
let xtt = [1.0, t, t*t, t*t*t];
|
||
|
||
let cs = compute_collision_rate(2, i, j, t, u0, c1, csca, alf, bet, xi, xj, &xtt);
|
||
|
||
assert!(cs > 0.0, "Giovanardi hot collision rate should be positive");
|
||
assert!(cs.is_finite(), "Giovanardi hot collision rate should be finite");
|
||
}
|
||
|
||
#[test]
|
||
fn test_collision_rate_temperature_dependence() {
|
||
// 验证碰撞速率随温度的变化
|
||
let i = 1;
|
||
let j = 2;
|
||
let u0_base: f64 = 10.0; // 跃迁能量
|
||
let c1: f64 = 0.4162;
|
||
let alf = ALF0 - ALF1 / 1.0;
|
||
let bet = BET0 - BET1 / 1.0;
|
||
let xi = 1.0;
|
||
let xj = 2.0;
|
||
|
||
let temperatures: [f64; 4] = [5000.0, 10000.0, 20000.0, 50000.0];
|
||
let mut rates = Vec::new();
|
||
|
||
for t in &temperatures {
|
||
let hkt = HK / t;
|
||
let u0 = u0_base * hkt / (HK / 10000.0) * 0.5; // 归一化激发能量
|
||
let csca = 8.63e-6 / 2.0 / 1.0 / t.sqrt();
|
||
let xtt = [1.0, *t, (*t)*(*t), (*t)*(*t)*(*t)];
|
||
|
||
let cs = compute_collision_rate(0, i, j, *t, u0, c1, csca, alf, bet, xi, xj, &xtt);
|
||
rates.push(cs);
|
||
}
|
||
|
||
// 碰撞速率应该是有限的
|
||
for rate in &rates {
|
||
assert!(rate.is_finite(), "Collision rate should be finite");
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_collision_rate_level_dependence() {
|
||
// 验证碰撞速率随能级的变化
|
||
let t: f64 = 10000.0;
|
||
let c1: f64 = 0.1;
|
||
let xtt = [1.0, t, t*t, t*t*t];
|
||
|
||
let mut rates = Vec::new();
|
||
|
||
for j in 2..=10 {
|
||
let i = j - 1;
|
||
let xi = i as f64;
|
||
let xj = j as f64;
|
||
let u0 = 0.5; // 简化
|
||
let csca = 8.63e-6 / 2.0 / ((i*i) as f64) / t.sqrt();
|
||
let alf = ALF0 - ALF1 / ((i*i) as f64);
|
||
let bet = BET0 - BET1 / (i as f64);
|
||
|
||
let cs = compute_collision_rate(0, i, j, t, u0, c1, csca, alf, bet, xi, xj, &xtt);
|
||
rates.push(cs);
|
||
}
|
||
|
||
// 所有速率应该是正的有限数
|
||
for (j, rate) in rates.iter().enumerate() {
|
||
assert!(rate.is_finite() && rate > &0.0,
|
||
"Rate for transition {}->{} should be positive finite", j+1, j+2);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_exponential_integral_approximation() {
|
||
// 验证指数积分近似
|
||
// 小 u0 使用级数展开,大 u0 使用连分式
|
||
let small_u0 = 0.5_f64;
|
||
let large_u0 = 5.0_f64;
|
||
|
||
// 小 u0 的级数展开
|
||
let e1_small = -small_u0.ln() + EXPIA1
|
||
+ small_u0 * (EXPIA2 + small_u0 * (EXPIA3
|
||
+ small_u0 * (EXPIA4 + small_u0 * (EXPIA5 + small_u0 * EXPIA6))));
|
||
|
||
assert!(e1_small > 0.0, "E1 for small u0 should be positive");
|
||
|
||
// 大 u0 的连分式
|
||
let u0_inv = 1.0 / large_u0;
|
||
let e1_large = (-large_u0).exp()
|
||
* ((EXPIB1 + large_u0 * (EXPIB2 + large_u0 * (EXPIB3 + large_u0 * EXPIB4)))
|
||
/ (EXPIC1 + large_u0 * (EXPIC2 + large_u0 * (EXPIC3 + large_u0 * EXPIC4))))
|
||
* u0_inv;
|
||
|
||
assert!(e1_large > 0.0 && e1_large < 1.0, "E1 for large u0 should be between 0 and 1");
|
||
}
|
||
|
||
#[test]
|
||
fn test_hm_ionization_rate() {
|
||
// 验证 H- 碰撞电离速率公式
|
||
let t = 10000.0_f64;
|
||
let rate = CHMI * t * t.sqrt();
|
||
|
||
// H- 电离速率应该在合理范围内
|
||
assert!(rate > 0.0, "H- ionization rate should be positive");
|
||
assert!(rate.is_finite(), "H- ionization rate should be finite");
|
||
|
||
// CHMI = 5.59e-15, 所以 rate ≈ 5.59e-15 * 10000 * 100 = 5.59e-9
|
||
assert!(rate > 1e-12 && rate < 1e-6,
|
||
"H- ionization rate {:.2e} seems unreasonable", rate);
|
||
}
|
||
}
|