新增模块: - count_words: 字符串单词计数工具 - divhe2: He II Stark 轮廓除数参数计算 - extprf: 谱线轮廓波长外推 (Cooper 公式) - feautr: Lyman-α Stark 加宽 (Feautrier 方法) - gamhe: 中性氦 Stark 加宽参数 - griem: Griem Stark 阻尼参数计算 - intrp: 二分法高效插值程序 - partdv: 配分函数计算 (含压力效应) - sffhmi_old: H- 自由-自由截面 (Kurucz 公式) 改进: - 修复 fortran-analyzer 注释行误匹配问题 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
264 lines
7.7 KiB
Rust
264 lines
7.7 KiB
Rust
//! 中性氦 Stark 加宽参数计算。
|
||
//!
|
||
//! 重构自 SYNSPEC `gamhe.f`
|
||
//!
|
||
//! 基于 Dimitrijevic 和 Sahal-Brechot (1984, J.Q.S.R.T. 31, 301)
|
||
//! 或 Freudenstein 和 Cooper (1978, AP.J. 224, 1079) 的数据。
|
||
|
||
/// Gamhe 计算所需的模型层参数。
|
||
#[derive(Debug, Clone)]
|
||
pub struct GamheParams {
|
||
/// 温度插值索引 (JT)
|
||
pub jt: usize,
|
||
/// 温度插值系数 0
|
||
pub ti0: f64,
|
||
/// 温度插值系数 1
|
||
pub ti1: f64,
|
||
/// 温度插值系数 2
|
||
pub ti2: f64,
|
||
}
|
||
|
||
/// 中性氦 Stark 加宽参数。
|
||
///
|
||
/// 包含电子和质子贡献的加宽数据。
|
||
pub struct GamheData {
|
||
/// W 数组: [电子加宽 @ 5000K, @ 10000K, @ 20000K, @ 40000K, 波长(Å)]
|
||
/// 按谱线索引 (1-20) 存储
|
||
pub w: [[f64; 5]; 20],
|
||
/// V 数组: 质子加宽 @ 5000K, @ 10000K, @ 20000K, @ 40000K
|
||
pub v: [[f64; 4]; 20],
|
||
/// C 数组: 备用系数 (当 W=0 时使用)
|
||
pub c: [f64; 20],
|
||
}
|
||
|
||
impl GamheData {
|
||
/// 创建默认的 GamheData,包含所有谱线数据。
|
||
pub fn new() -> Self {
|
||
// W 数组: 电子加宽参数 + 波长
|
||
// Fortran DATA 是列优先,需要转置
|
||
const W_DATA: [[f64; 5]; 20] = [
|
||
[5.990, 6.650, 6.610, 6.210, 3819.60],
|
||
[2.950, 3.130, 3.230, 3.300, 3867.50],
|
||
[0.000, 0.000, 0.000, 0.000, 3871.79],
|
||
[0.142, 0.166, 0.182, 0.190, 3888.65],
|
||
[0.000, 0.000, 0.000, 0.000, 3926.53],
|
||
[1.540, 1.480, 1.400, 1.290, 3964.73],
|
||
[41.600, 50.500, 57.400, 65.800, 4009.27],
|
||
[1.320, 1.350, 1.380, 1.460, 4120.80],
|
||
[7.830, 8.750, 8.690, 8.040, 4143.76],
|
||
[5.830, 6.370, 6.820, 6.990, 4168.97],
|
||
[0.000, 0.000, 0.000, 0.000, 4437.55],
|
||
[1.630, 1.610, 1.490, 1.350, 4471.50],
|
||
[0.588, 0.620, 0.641, 0.659, 4713.20],
|
||
[2.600, 2.480, 2.240, 1.960, 4921.93],
|
||
[0.627, 0.597, 0.568, 0.532, 5015.68],
|
||
[1.050, 1.090, 1.110, 1.140, 5047.74],
|
||
[0.277, 0.298, 0.296, 0.293, 5875.70],
|
||
[0.714, 0.666, 0.602, 0.538, 6678.15],
|
||
[3.490, 3.630, 3.470, 3.190, 4026.20],
|
||
[4.970, 5.100, 4.810, 4.310, 4387.93],
|
||
];
|
||
|
||
// V 数组: 质子加宽参数
|
||
const V_DATA: [[f64; 4]; 20] = [
|
||
[1.520, 4.540, 9.140, 10.200],
|
||
[0.607, 0.710, 0.802, 0.901],
|
||
[0.000, 0.000, 0.000, 0.000],
|
||
[0.0396, 0.0434, 0.0476, 0.0526],
|
||
[0.000, 0.000, 0.000, 0.000],
|
||
[0.507, 0.585, 0.665, 0.762],
|
||
[0.930, 1.710, 13.600, 27.200],
|
||
[0.288, 0.325, 0.365, 0.410],
|
||
[1.330, 6.800, 12.900, 14.300],
|
||
[1.100, 1.370, 1.560, 1.760],
|
||
[0.000, 0.000, 0.000, 0.000],
|
||
[1.340, 1.690, 1.820, 1.630],
|
||
[0.128, 0.143, 0.161, 0.181],
|
||
[2.040, 2.740, 2.950, 2.740],
|
||
[0.187, 0.210, 0.237, 0.270],
|
||
[0.231, 0.260, 0.291, 0.327],
|
||
[0.0591, 0.0650, 0.0719, 0.0799],
|
||
[0.231, 0.260, 0.295, 0.339],
|
||
[2.180, 3.760, 4.790, 4.560],
|
||
[1.860, 5.320, 7.070, 7.150],
|
||
];
|
||
|
||
// C 数组: 备用系数
|
||
// DATA C /2*0.,1.83E-4,0.,1.13E-4,5*0.,1.6E-4,9*0./
|
||
const C_DATA: [f64; 20] = [
|
||
0.0, 0.0, 1.83e-4, 0.0, 1.13e-4,
|
||
0.0, 0.0, 0.0, 0.0, 0.0,
|
||
1.6e-4, 0.0, 0.0, 0.0, 0.0,
|
||
0.0, 0.0, 0.0, 0.0, 0.0,
|
||
];
|
||
|
||
Self {
|
||
w: W_DATA,
|
||
v: V_DATA,
|
||
c: C_DATA,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl Default for GamheData {
|
||
fn default() -> Self {
|
||
Self::new()
|
||
}
|
||
}
|
||
|
||
/// 计算中性氦 Stark 加宽参数 GAM。
|
||
///
|
||
/// # 参数
|
||
///
|
||
/// * `ind` - 谱线索引 (1-20, 1-indexed)
|
||
/// * `t` - 温度 (K)
|
||
/// * `ane` - 电子数密度
|
||
/// * `anp` - 质子数密度
|
||
/// * `params` - 模型层插值参数
|
||
/// * `data` - Stark 加宽数据表
|
||
///
|
||
/// # 返回值
|
||
///
|
||
/// Stark 加宽参数 GAM (Å)
|
||
///
|
||
/// # 算法
|
||
///
|
||
/// 如果 W(1,IND) != 0:
|
||
/// GAM = (电子贡献 * ANE + 质子贡献 * ANP) * 1.884e3 / 波长^2
|
||
/// 否则:
|
||
/// GAM = C(IND) * T^0.16667 * ANE
|
||
pub fn gamhe(ind: usize, t: f64, ane: f64, anp: f64, params: &GamheParams, data: &GamheData) -> f64 {
|
||
// 转换为 0-indexed
|
||
let ind_idx = ind - 1;
|
||
|
||
// 检查是否有有效的电子加宽数据
|
||
if data.w[ind_idx][0] == 0.0 {
|
||
// 使用备用公式
|
||
let gam = data.c[ind_idx] * t.powf(0.16667) * ane;
|
||
return gam.max(0.0);
|
||
}
|
||
|
||
// 温度插值
|
||
let jt_idx = params.jt; // 已是 0-indexed
|
||
|
||
// 电子加宽贡献
|
||
let w_electron = params.ti0 * data.w[ind_idx][jt_idx]
|
||
+ params.ti1 * data.w[ind_idx][jt_idx - 1]
|
||
+ params.ti2 * data.w[ind_idx][jt_idx - 2];
|
||
|
||
// 质子加宽贡献
|
||
let v_proton = params.ti0 * data.v[ind_idx][jt_idx]
|
||
+ params.ti1 * data.v[ind_idx][jt_idx - 1]
|
||
+ params.ti2 * data.v[ind_idx][jt_idx - 2];
|
||
|
||
// 波长 (Å)
|
||
let wavelength = data.w[ind_idx][4];
|
||
|
||
// 计算 GAM
|
||
let gam = (w_electron * ane + v_proton * anp) * 1.884e3 / (wavelength * wavelength);
|
||
|
||
gam.max(0.0)
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
use approx::assert_relative_eq;
|
||
|
||
fn make_params(jt: usize) -> GamheParams {
|
||
GamheParams {
|
||
jt,
|
||
ti0: 1.0,
|
||
ti1: 0.0,
|
||
ti2: 0.0,
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_basic() {
|
||
let data = GamheData::new();
|
||
let params = make_params(2); // jt >= 2 以访问 jt-2
|
||
|
||
// 使用谱线 1 (3819.60 Å)
|
||
let gam = gamhe(1, 10000.0, 1e13, 1e12, ¶ms, &data);
|
||
|
||
assert!(gam >= 0.0);
|
||
assert!(gam.is_finite());
|
||
}
|
||
|
||
#[test]
|
||
fn test_zero_w_case() {
|
||
let data = GamheData::new();
|
||
let params = make_params(2);
|
||
|
||
// 谱线 3 的 W(1,3) = 0,使用备用公式
|
||
let gam = gamhe(3, 10000.0, 1e13, 1e12, ¶ms, &data);
|
||
|
||
// C(3) = 1.83e-4
|
||
let expected = 1.83e-4 * 10000.0_f64.powf(0.16667) * 1e13;
|
||
assert_relative_eq!(gam, expected, epsilon = 1e-6);
|
||
}
|
||
|
||
#[test]
|
||
fn test_electron_contribution() {
|
||
let data = GamheData::new();
|
||
let params = make_params(2);
|
||
|
||
// 纯电子贡献 (ANP = 0)
|
||
let gam_e = gamhe(1, 10000.0, 1e13, 0.0, ¶ms, &data);
|
||
assert!(gam_e > 0.0);
|
||
}
|
||
|
||
#[test]
|
||
fn test_proton_contribution() {
|
||
let data = GamheData::new();
|
||
let params = make_params(2);
|
||
|
||
// 纯质子贡献 (ANE = 0)
|
||
let gam_p = gamhe(1, 10000.0, 0.0, 1e13, ¶ms, &data);
|
||
assert!(gam_p > 0.0);
|
||
}
|
||
|
||
#[test]
|
||
fn test_density_scaling() {
|
||
let data = GamheData::new();
|
||
let params = make_params(2);
|
||
|
||
let gam1 = gamhe(1, 10000.0, 1e13, 1e12, ¶ms, &data);
|
||
let gam2 = gamhe(1, 10000.0, 2e13, 2e12, ¶ms, &data);
|
||
|
||
// 密度翻倍,GAM 应该翻倍
|
||
assert_relative_eq!(gam2, 2.0 * gam1, epsilon = 1e-10);
|
||
}
|
||
|
||
#[test]
|
||
fn test_negative_result_clamped() {
|
||
let data = GamheData::new();
|
||
let params = GamheParams {
|
||
jt: 2,
|
||
ti0: -1.0, // 负系数可能导致负 GAM
|
||
ti1: 0.0,
|
||
ti2: 0.0,
|
||
};
|
||
|
||
let gam = gamhe(1, 10000.0, 1e13, 1e12, ¶ms, &data);
|
||
|
||
// 负值应该被钳制为 0
|
||
assert!(gam >= 0.0);
|
||
}
|
||
|
||
#[test]
|
||
fn test_wavelength_scaling() {
|
||
let data = GamheData::new();
|
||
let params = make_params(2);
|
||
|
||
// 谱线 1: 3819.60 Å, 谱线 2: 3867.50 Å
|
||
let gam1 = gamhe(1, 10000.0, 1e13, 0.0, ¶ms, &data);
|
||
let gam2 = gamhe(2, 10000.0, 1e13, 0.0, ¶ms, &data);
|
||
|
||
// GAM ∝ 1/波长^2,较长波长应该有较小的 GAM
|
||
// (假设其他参数相似)
|
||
assert!(gam1.is_finite() && gam2.is_finite());
|
||
}
|
||
}
|