//! 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], popul: &[Vec], abund: &[Vec], 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, Vec, Vec, Vec, Vec, Vec, Vec, Vec, Vec, Vec, Vec, Vec, Vec, Vec, Vec, Vec, Vec, Vec, Vec, Vec, Vec>, Vec>, Vec>, ) { 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); } }