//! 氢线系列的 ODF(Opacity Distribution Function)计算。 //! //! 重构自 TLUSTY `odfhyd.f` //! //! 计算氢线系列的不透明度分布函数。 use crate::tlusty::math::divstr; use crate::tlusty::math::indexx; use crate::tlusty::math::odfhst; use crate::tlusty::state::constants::{HALF, TWO, UN}; use crate::tlusty::state::model::StrAux; // 物理常量 /// 玻尔兹曼常数 / 氢质量 const CDOP: f64 = 2.0 * 1.38054e-16 / 1.6726e-24; /// 光速 (Angstrom/s) const CA: f64 = 2.997925e18; /// 转换因子 const CCM: f64 = CA / 1.0e8; /// 氢的 Rydberg 频率 const FRH: f64 = 3.28805e15; /// Rydberg 波长 (Angstrom) const RYDEL: f64 = 911.764; /// 2/3 const TTW: f64 = 2.0 / 3.0; /// Stark 加宽常量 const C00: f64 = 1.25e-9; /// 仪器加宽常量 const CID: f64 = 0.02654; /// ODFHYD 输入参数 pub struct OdfhydParams { /// 深度索引 (1-indexed) pub id: usize, /// 跃迁索引 (1-indexed) pub itr: usize, } /// ODFHYD 配置参数 pub struct OdfhydConfig { /// ODF 采样标志 (0: 标准 ODF, >0: 采样 ODF) pub ispodf: i32, /// 最大谱线数 pub nlmx: usize, /// 光速 (Angstrom/s) pub cas: f64, /// 湍流速度 (cm/s) pub vtb: f64, } /// ODFHYD 原子数据 pub struct OdfhydAtomicData<'a> { /// 下能级索引 (ntrans) pub ilow: &'a [i32], /// 上能级索引 (ntrans) pub iup: &'a [i32], /// 主量子数 (nlevel) pub nquant: &'a [i32], /// ODF 的下量子数 (nlevel) pub nqlodf: &'a [i32], /// 跃迁起始频率索引 (ntrans) pub ifr0: &'a [i32], /// 跃迁结束频率索引 (ntrans) pub ifr1: &'a [i32], /// XKIJ 系数 (ntrans × nlmx) pub xkij: &'a [f64], /// FIJ 系数 (ntrans × nlmx) pub fij: &'a [f64], /// ODF 索引 (ntrans) pub jndodf: &'a [i32], } /// ODFHYD 模型状态 pub struct OdfhydModelState<'a> { /// 温度 (nd) pub temp: &'a [f64], /// 电子密度 (nd) pub elec: &'a [f64], /// WNHINT 数组 (nlmx × nd) pub wnhint: &'a [f64], /// Stark 展宽参数 pub straux: StrAux, } /// ODFHYD ODF 数据 pub struct OdfhydOdfData<'a> { /// ODF 频率数 (ntrans) pub nfrodf: &'a [i32], /// ODF 频率 (mfro × ntrans) pub fros: &'a [f64], /// ODF 频率宽度 (mfro × ntrans) pub wnus: &'a [f64], /// 谱线轮廓 (nd × nfreq 或其他) pub prflin: &'a mut [f64], /// 频率数组 (nfreq) pub freq: &'a [f64], /// KFR0 索引 (ntrans) pub kfr0: &'a [i32], /// INDEXP 标志 (ntrans) pub indexp: &'a [i32], } /// XI2 函数:计算 1/n^2 fn xi2(n: i32) -> f64 { 1.0 / (n as f64 * n as f64) } /// 计算氢线系列的 ODF。 /// /// # 参数 /// /// * `params` - 输入参数(id, itr) /// * `config` - 配置参数 /// * `atomic` - 原子数据 /// * `model` - 模型状态 /// * `odf_data` - ODF 数据 /// /// # 返回值 /// /// 更新后的 PRFLIN 数组 pub fn odfhyd( params: &OdfhydParams, config: &OdfhydConfig, atomic: &OdfhydAtomicData, model: &mut OdfhydModelState, odf_data: &mut OdfhydOdfData, ) { let id = params.id; let id_idx = id - 1; let itr = params.itr; let itr_idx = itr - 1; let jo = atomic.jndodf[itr_idx] as usize - 1; let ispodf = config.ispodf; // 确定频率点数和初始化数组 let nf = if ispodf == 0 { odf_data.nfrodf[jo] as usize } else { (atomic.ifr1[itr_idx] - atomic.ifr0[itr_idx] + 1) as usize }; let mut sig = vec![0.0; nf]; let mut sgt = vec![0.0; nf]; let mut odf = vec![0.0; nf]; let mut iodf = vec![0; nf]; let mut ynus = vec![0.0; nf]; let mut alam = vec![0.0; nf]; // 初始化频率和波长 if ispodf == 0 { for ij in 0..nf { iodf[ij] = 0; sig[ij] = 0.0; odf[ij] = 0.0; ynus[ij] = odf_data.fros[ij + jo * 1000]; // 假设 MFRO = 1000 alam[ij] = config.cas / ynus[ij]; } } else { let ifr0 = atomic.ifr0[itr_idx] as usize - 1; for ij in 0..nf { sig[ij] = 0.0; ynus[ij] = odf_data.freq[ifr0 + ij]; alam[ij] = config.cas / ynus[ij]; } } // 获取能级索引 let ii = atomic.ilow[itr_idx] as usize - 1; let jj = atomic.iup[itr_idx] as usize - 1; // 计算电子密度因子 let anes = (TTW * model.elec[id_idx].ln()).exp(); let f00 = C00 * anes; // 计算跃迁频率 let nquant_ii = atomic.nquant[ii]; let nquant_jj = atomic.nquant[jj]; let fra = FRH * (xi2(nquant_ii) - xi2(nquant_jj)); // 计算 Doppler 宽度 let dopo = fra / CCM * (CDOP * model.temp[id_idx] + config.vtb * config.vtb).sqrt(); // 遍历谱线系列 let nqlodf_ii = atomic.nqlodf[ii] as usize; for j in nqlodf_ii..=config.nlmx { let wl = RYDEL / (xi2(nquant_ii) - xi2(j as i32)); let fxk = f00 * atomic.xkij[jo * config.nlmx + j]; let dbeta = wl * wl / CA / fxk; let betad = dbeta * dopo; let fid = CID * atomic.fij[jo * config.nlmx + j] * dbeta; // 调用 DIVSTR let (adh, divh) = divstr(betad, 1); // 获取 Stark 宽度 let wprob = model.wnhint[j * id + id_idx]; // 更新 straux 中的参数 model.straux.betad = betad; model.straux.adh = adh; model.straux.divh = divh; // 调用 ODFHST odfhst(nf, fxk, fid, wprob, wl, &alam, &model.straux, &mut sgt); // 累加截面 for ij in 0..nf { sig[ij] += sgt[ij]; } } // 后处理(仅对标准 ODF) if ispodf == 0 { // 排序 iodf = indexx(&sig); // 重新排列 ODF for ij in 0..nf { odf[ij] = sig[iodf[ij]]; } // 计算频率网格 let i0 = atomic.ifr0[itr_idx] as usize; let i1 = atomic.ifr1[itr_idx] as usize; if odf_data.indexp[itr_idx].abs() == 2 { ynus[0] = odf_data.freq[i0 - 1]; } let mut iw1 = iodf[0]; for ij in 1..nf { let iw2 = iodf[ij]; if ij > 1 && ij < nf - 1 { ynus[ij] = ynus[ij - 1] - HALF * (odf_data.wnus[iw1 + jo * 1000] + odf_data.wnus[iw2 + jo * 1000]); } else if ij == 1 { ynus[ij] = ynus[ij - 1] - HALF * (TWO * odf_data.wnus[iw1 + jo * 1000] + odf_data.wnus[iw2 + jo * 1000]); } else if ij == nf - 1 { ynus[ij] = ynus[ij - 1] - HALF * (odf_data.wnus[iw1 + jo * 1000] + TWO * odf_data.wnus[iw2 + jo * 1000]); } iw1 = iw2; } // 插值到频率网格 odf_data.prflin[id_idx * 100000 + i1 - 1] = 1e-35; for ij0 in i0..i1 { let mut ji = 1; for ij in 1..nf { ji = ij; if ynus[ij] <= odf_data.freq[ij0 - 1] { break; } } let prfln = if ji > 0 && ji < nf { odf[ji - 1] + (odf[ji] - odf[ji - 1]) * (odf_data.freq[ij0 - 1] - ynus[ji - 1]) / (ynus[ji] - ynus[ji - 1]) } else { 0.0 }; odf_data.prflin[id_idx * 100000 + ij0 - 1] = prfln; } } else { // 采样 ODF 情况 let kfr0 = odf_data.kfr0[itr_idx] as usize; for ij in 0..nf { odf_data.prflin[id_idx * 100000 + kfr0 + ij - 1] = sig[ij]; } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_xi2() { assert!((xi2(1) - 1.0).abs() < 1e-15); assert!((xi2(2) - 0.25).abs() < 1e-15); assert!((xi2(3) - 1.0 / 9.0).abs() < 1e-15); } #[test] fn test_odfhyd_constants() { // CDOP = 2 * BOLK / HMASS = 2 * 1.38054e-16 / 1.6726e-24 ≈ 1.65e8 // 注意:Fortran 中的 BOLK 可能是 1.38054e-16,HMASS 是 1.6726e-24 // CDOP ≈ 1.65e8 cm/s/K^0.5 assert!(CDOP > 1e7 && CDOP < 1e9); assert!((CA - 2.997925e18).abs() < 1e13); assert!((FRH - 3.28805e15).abs() < 1e10); } }