320 lines
9.4 KiB
Rust
320 lines
9.4 KiB
Rust
//! Doppler 宽度和 Voigt 阻尼参数计算。
|
||
//!
|
||
//! 重构自 TLUSTY `dopgam.f`
|
||
//!
|
||
//! 计算谱线的 Doppler 宽度和总阻尼参数。
|
||
|
||
use crate::math::gamsp;
|
||
|
||
const BOL2: f64 = 2.76108e-16;
|
||
const CIN: f64 = 1.0 / 2.997925e10;
|
||
const R02: f64 = 2.5;
|
||
const R12: f64 = 45.0;
|
||
const OP4: f64 = 0.4;
|
||
const VW0: f64 = 4.5e-9;
|
||
const EH: f64 = 2.17853041e-11;
|
||
|
||
/// Doppler 宽度和 Voigt 阻尼参数计算。
|
||
///
|
||
/// 计算给定谱线的 Doppler 宽度和总阻尼参数(以 Doppler 宽度为单位)。
|
||
///
|
||
/// # 参数
|
||
///
|
||
/// * `itr` - 跃迁索引 (1-based)
|
||
/// * `id` - 深度索引 (1-based)
|
||
/// * `t` - 温度
|
||
/// * `iup` - 上能级索引数组
|
||
/// * `iatm` - 原子索引数组
|
||
/// * `fr0` - 跃迁频率数组
|
||
/// * `iel` - 元素索引数组
|
||
/// * `amass` - 原子质量数组
|
||
/// * `iprof` - 轮廓类型数组
|
||
/// * `itra` - 跃迁索引矩阵 [MTRANS][MLEVEL]
|
||
/// * `ilow` - 下能级索引数组
|
||
/// * `gamar` - 辐射阻尼参数数组
|
||
/// * `iz` - 电离度数组
|
||
/// * `enion` - 电离能数组
|
||
/// * `stark1` - Stark 参数 1
|
||
/// * `stark2` - Stark 参数 2
|
||
/// * `stark3` - Stark 参数 3
|
||
/// * `vdwh` - Van der Waals 参数
|
||
/// * `ielh` - 氢元素索引
|
||
/// * `nfirst` - 第一个能级索引数组
|
||
/// ` `iathe` - 氦原子索引
|
||
/// * `ielhe1` - He I 元素索引
|
||
/// * `vturbs` - 微湍流速度数组
|
||
/// * `elec` - 电子密度数组
|
||
/// * `dens` - 密度数组
|
||
/// * `wmm` - 平均分子量数组
|
||
/// * `ytot` - 总粒子数数组
|
||
/// * `abndd` - 丰度数组 [MABUND][MDEPTH]
|
||
/// * `popul` - 布居数数组 [MLEVEL][MDEPTH]
|
||
/// * `abund` - 原子丰度数组 [MATOM][MDEPTH]
|
||
/// * `mtrans` - 最大跃迁数
|
||
///
|
||
/// # 返回值
|
||
///
|
||
/// 返回 (dop, agam):
|
||
/// - `dop` - Doppler 宽度
|
||
/// - `agam` - 总阻尼参数(以 Doppler 宽度为单位)
|
||
pub fn dopgam(
|
||
itr: i32,
|
||
id: usize,
|
||
t: f64,
|
||
iup: &[i32],
|
||
iatm: &[i32],
|
||
fr0: &[f64],
|
||
iel: &[i32],
|
||
amass: &[f64],
|
||
iprof: &[i32],
|
||
itra: &[i32],
|
||
ilow: &[i32],
|
||
gamar: &[f64],
|
||
iz: &[i32],
|
||
enion: &[f64],
|
||
stark1: &[f64],
|
||
stark2: &[f64],
|
||
stark3: &[f64],
|
||
vdwh: &[f64],
|
||
ielh: i32,
|
||
nfirst: &[i32],
|
||
iathe: i32,
|
||
ielhe1: i32,
|
||
vturbs: &[f64],
|
||
elec: &[f64],
|
||
dens: &[f64],
|
||
wmm: &[f64],
|
||
ytot: &[f64],
|
||
abndd: &[Vec<f64>],
|
||
popul: &[Vec<f64>],
|
||
abund: &[Vec<f64>],
|
||
mtrans: usize,
|
||
) -> (f64, f64) {
|
||
// 获取跃迁参数
|
||
let itr_idx = (itr - 1) as usize;
|
||
let j = iup[itr_idx] as usize;
|
||
let iat = iatm[j - 1] as usize;
|
||
let fr = fr0[itr_idx];
|
||
let ie = iel[j - 1] as usize;
|
||
|
||
// 温度相关的热运动项
|
||
let am = BOL2 / amass[iat - 1] * t;
|
||
let mut agam = 0.0;
|
||
|
||
// Doppler 宽度
|
||
let dop = fr * CIN * (am + vturbs[id - 1] * vturbs[id - 1]).sqrt();
|
||
|
||
// 阻尼参数 - 仅对 IPROF = 1 计算
|
||
if iprof[itr_idx].abs() != 1 {
|
||
return (dop, agam);
|
||
}
|
||
|
||
let ilow_idx = ilow[itr_idx] as usize - 1;
|
||
let ip = itra[(j - 1) + mtrans * ilow_idx] as usize;
|
||
let ane = elec[id - 1];
|
||
|
||
// 自然(辐射)致宽
|
||
if gamar[ip - 1] > 0.0 {
|
||
agam = gamar[ip - 1];
|
||
} else if gamar[ip - 1] == 0.0 {
|
||
agam = 2.47342e-22 * fr * fr;
|
||
} else {
|
||
// 非标准表达式 - 用于总阻尼参数,不仅是辐射阻尼
|
||
agam = gamsp(itr, t, ane);
|
||
}
|
||
|
||
// Stark 致宽
|
||
let z = iz[ie - 1] as f64;
|
||
let anff = z * z * EH / enion[j - 1];
|
||
|
||
if stark1[ip - 1] == 0.0 {
|
||
agam += 1e-8 * anff.powf(2.5) * ane;
|
||
} else if stark1[ip - 1] > 0.0 {
|
||
agam += ane * (stark1[ip - 1] * t.powf(stark2[ip - 1]) + stark3[ip - 1]);
|
||
}
|
||
|
||
// Van der Waals 致宽
|
||
let ah = if ielh > 0 {
|
||
popul[nfirst[ielh as usize - 1] as usize - 1][id - 1]
|
||
} else {
|
||
dens[id - 1] / wmm[id - 1] / ytot[id - 1]
|
||
};
|
||
|
||
let ahe = if ielhe1 != 0 {
|
||
ah * abund[iathe as usize - 1][id - 1]
|
||
} else {
|
||
ah * abndd[1][id - 1]
|
||
};
|
||
|
||
let vdwc = (t * 1e-4).powf(0.3) * (ah + 0.42 * ahe);
|
||
|
||
if vdwh[ip - 1] >= 0.0 {
|
||
let r2 = if iat < 21 {
|
||
R02 * (anff / z).powi(2)
|
||
} else if iat < 45 {
|
||
(R12 - iat as f64) / z
|
||
} else {
|
||
0.5
|
||
};
|
||
let gw0 = vdwc * VW0 * r2.powf(OP4);
|
||
agam += gw0;
|
||
} else {
|
||
let gw0 = vdwc * (2.3025851_f64 * vdwh[ip - 1]).exp();
|
||
agam += gw0;
|
||
}
|
||
|
||
// 总阻尼参数(以 Doppler 宽度为单位)
|
||
agam /= dop * 12.566370614;
|
||
|
||
(dop, agam)
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
const MDEPTH: usize = 100;
|
||
const MLEVEL: usize = 100;
|
||
const MTRANS: usize = 200;
|
||
const MATOM: usize = 30;
|
||
const MABUND: usize = 10;
|
||
|
||
fn create_test_arrays() -> (
|
||
Vec<i32>, Vec<i32>, Vec<f64>, Vec<i32>, Vec<f64>, Vec<i32>,
|
||
Vec<i32>, Vec<i32>, Vec<f64>, Vec<i32>, Vec<f64>, Vec<f64>,
|
||
Vec<f64>, Vec<f64>, Vec<f64>, Vec<f64>, Vec<f64>, Vec<f64>,
|
||
Vec<f64>, Vec<f64>, Vec<Vec<f64>>, Vec<Vec<f64>>, Vec<Vec<f64>>,
|
||
) {
|
||
let mut iup = vec![0i32; MTRANS];
|
||
let mut iatm = vec![0i32; MLEVEL];
|
||
let mut fr0 = vec![0.0f64; MTRANS];
|
||
let mut iel = vec![0i32; MLEVEL];
|
||
let mut amass = vec![0.0f64; MATOM];
|
||
let mut iprof = vec![0i32; MTRANS];
|
||
let mut ilow = vec![0i32; MTRANS];
|
||
let mut gamar = vec![0.0f64; MTRANS];
|
||
let mut iz = vec![0i32; MLEVEL];
|
||
let mut enion = vec![0.0f64; MLEVEL];
|
||
let mut stark1 = vec![0.0f64; MTRANS];
|
||
let mut stark2 = vec![0.0f64; MTRANS];
|
||
let mut stark3 = vec![0.0f64; MTRANS];
|
||
let mut vdwh = vec![0.0f64; MTRANS];
|
||
let vturbs = vec![2e5f64; MDEPTH];
|
||
let elec = vec![1e13f64; MDEPTH];
|
||
let dens = vec![1e-7f64; MDEPTH];
|
||
let wmm = vec![1.0f64; MDEPTH];
|
||
let ytot = vec![1.0f64; MDEPTH];
|
||
let abndd = vec![vec![0.1f64; MDEPTH]; MABUND];
|
||
let popul = vec![vec![1e10f64; MDEPTH]; MLEVEL];
|
||
let abund = vec![vec![0.1f64; MDEPTH]; MATOM];
|
||
|
||
// 设置跃迁 1 的参数
|
||
iup[0] = 2; // 上能级 2
|
||
iatm[1] = 1; // 原子 1 (氢)
|
||
fr0[0] = 2.466e15; // Lyman alpha 频率
|
||
iel[1] = 1; // 元素 1
|
||
amass[0] = 1.0; // 氢原子质量
|
||
iprof[0] = 1; // Voigt 轮廓
|
||
ilow[0] = 1; // 下能级
|
||
gamar[0] = 0.0; // 经典自然阻尼
|
||
iz[0] = 1; // 电离度
|
||
enion[1] = 13.6 * EH; // 氢电离能
|
||
stark1[0] = 0.0; // Stark 忽略
|
||
vdwh[0] = 0.0; // Van der Waals 忽略
|
||
|
||
// 设置 ITRA 矩阵
|
||
let mut itra = vec![0i32; MTRANS * MLEVEL];
|
||
itra[1 + 0 * MTRANS] = 1; // ITRA(2,1) = 1
|
||
|
||
(
|
||
iup, iatm, fr0, iel, amass, iprof,
|
||
itra, ilow, gamar, iz, enion, stark1,
|
||
stark2, stark3, vdwh, vturbs, elec, dens, wmm, ytot,
|
||
abndd, popul, abund,
|
||
)
|
||
}
|
||
|
||
#[test]
|
||
fn test_dopgam_basic() {
|
||
let (
|
||
iup, iatm, fr0, iel, amass, iprof,
|
||
itra, ilow, gamar, iz, enion, stark1,
|
||
stark2, stark3, vdwh, vturbs, elec, dens, wmm, ytot,
|
||
abndd, popul, abund,
|
||
) = create_test_arrays();
|
||
|
||
let (dop, agam) = dopgam(
|
||
1, 1, 10000.0,
|
||
&iup, &iatm, &fr0, &iel, &amass, &iprof,
|
||
&itra, &ilow, &gamar, &iz, &enion, &stark1,
|
||
&stark2, &stark3, &vdwh,
|
||
0, &vec![1], 0, 0, // ielh, nfirst, iathe, ielhe1
|
||
&vturbs, &elec, &dens, &wmm, &ytot,
|
||
&abndd, &popul, &abund,
|
||
MTRANS,
|
||
);
|
||
|
||
// Doppler 宽度应该是正值
|
||
assert!(dop > 0.0);
|
||
// 阻尼参数应该非负
|
||
assert!(agam >= 0.0);
|
||
}
|
||
|
||
#[test]
|
||
fn test_dopgam_no_voigt() {
|
||
let (
|
||
mut iup, iatm, mut fr0, iel, amass, mut iprof,
|
||
itra, ilow, gamar, iz, enion, stark1,
|
||
stark2, stark3, vdwh, vturbs, elec, dens, wmm, ytot,
|
||
abndd, popul, abund,
|
||
) = create_test_arrays();
|
||
|
||
iprof[0] = 0; // 非 Voigt 轮廓
|
||
|
||
let (dop, agam) = dopgam(
|
||
1, 1, 10000.0,
|
||
&iup, &iatm, &fr0, &iel, &amass, &iprof,
|
||
&itra, &ilow, &gamar, &iz, &enion, &stark1,
|
||
&stark2, &stark3, &vdwh,
|
||
0, &vec![1], 0, 0,
|
||
&vturbs, &elec, &dens, &wmm, &ytot,
|
||
&abndd, &popul, &abund,
|
||
MTRANS,
|
||
);
|
||
|
||
// Doppler 宽度应该是正值
|
||
assert!(dop > 0.0);
|
||
// 阻尼参数应该为 0(非 Voigt 轮廓)
|
||
assert!((agam - 0.0).abs() < 1e-30);
|
||
}
|
||
|
||
#[test]
|
||
fn test_dopgam_classical_damping() {
|
||
let (
|
||
iup, iatm, fr0, iel, amass, iprof,
|
||
itra, ilow, gamar, iz, enion, stark1,
|
||
stark2, stark3, vdwh, vturbs, elec, dens, wmm, ytot,
|
||
abndd, popul, abund,
|
||
) = create_test_arrays();
|
||
|
||
let (dop, agam) = dopgam(
|
||
1, 1, 10000.0,
|
||
&iup, &iatm, &fr0, &iel, &amass, &iprof,
|
||
&itra, &ilow, &gamar, &iz, &enion, &stark1,
|
||
&stark2, &stark3, &vdwh,
|
||
0, &vec![1], 0, 0,
|
||
&vturbs, &elec, &dens, &wmm, &ytot,
|
||
&abndd, &popul, &abund,
|
||
MTRANS,
|
||
);
|
||
|
||
// 使用经典自然阻尼,agam 应该有值
|
||
assert!(dop > 0.0);
|
||
// 经典阻尼 = 2.47342e-22 * fr^2
|
||
let classical_gam = 2.47342e-22 * fr0[0] * fr0[0];
|
||
let expected_agam = classical_gam / dop / 12.566370614;
|
||
// 由于 Stark 致宽也有贡献,所以 agam 应该 >= expected_agam
|
||
assert!(agam >= expected_agam * 0.9);
|
||
}
|
||
}
|