SpectraRust/src/math/dopgam.rs
2026-03-21 09:12:18 +08:00

320 lines
9.4 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 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);
}
}