//! 重叠谱线系列极限的 ODF (不透明度分布函数) 计算。 //! //! 重构自 TLUSTY `odf1.f` //! //! # 功能 //! //! 计算重叠谱线在系列极限附近的 opacity distribution function (ODF)。 //! 谱线收敛到 (IL - IU) 连续跃迁的边缘。 //! //! # 参数 //! //! - `IL` - 下能级索引 //! - `IU` - 上能级索引 (通常是下一个离子的基态) //! - `ID` - 深度索引 //! - `ODF` - 输出:插值到显式频率集的不透明度分布函数 use crate::tlusty::state::atomic::AtomicData; use crate::tlusty::state::config::InpPar; use crate::tlusty::state::constants::{CAS, H, HALF, UN}; use crate::tlusty::state::model::{ModPar, StrAux}; use crate::tlusty::state::odfpar::{OdfCtr, OdfMod, OdfStk, OdfFrq}; use crate::tlusty::state::{MFREQ, MFRO, NLMX, MDEPTH}; use crate::tlusty::math::divstr; use crate::tlusty::math::dwnfr; use crate::tlusty::math::odfhst; use crate::tlusty::math::{sigk, SigkParams}; use crate::tlusty::math::OpData; // ============================================================================ // 常量 // ============================================================================ /// Rydberg 常数 (Hz) const FRH: f64 = 3.28805e15; /// 多普勒宽度常数 const CQT: f64 = 1.284523e12; /// C00 常数 const C00: f64 = 1.25e-9; /// CID 常数 const CID: f64 = 0.02654; // ============================================================================ // 输入参数结构体 // ============================================================================ /// ODF1 输入参数。 pub struct Odf1Params<'a> { /// 模式: 0 = 首次调用, >0 = 后续调用 pub imode: i32, /// 下能级索引 (0-indexed) pub il: usize, /// 上能级索引 (0-indexed) pub iu: usize, /// 深度索引 (0-indexed) pub id: usize, /// 频率数组 pub freq: &'a [f64], /// 原子数据 pub atomic: &'a AtomicData, /// 模型参数 pub modpar: &'a ModPar, /// Stark 辅助参数 pub straux: &'a StrAux, /// WNH 积分 [NLMX][MDEPTH] pub wnhint: &'a [Vec], /// ODF 控制参数 pub odfctr: &'a OdfCtr, /// ODF 模型数据 pub odfmod: &'a OdfMod, /// ODF Stark 数据 pub odfstk: &'a OdfStk, /// ODF 频率数据 pub odffrq: &'a OdfFrq, /// 输入参数 pub inppar: &'a InpPar, /// Opacity Project 数据 pub opdata: &'a OpData, } /// ODF1 输出。 pub struct Odf1Output { /// ODF 值 (插值到显式频率) pub odf: Vec, /// 首次 ODF 索引 pub i1odf: i32, /// 末次 ODF 索引 pub i2odf: i32, } /// ODF1 内部缓存 (用于 IMODE 调用间保存状态) #[derive(Debug, Clone)] pub struct Odf1Cache { /// 频率数组 pub fro: Vec, /// 截面数组 pub sgfr: Vec, /// ODF0 数组 pub odf0: Vec, /// ODF 索引 pub iodf: Vec, } impl Default for Odf1Cache { fn default() -> Self { Self { fro: vec![0.0; MFRO], sgfr: vec![0.0; MFRO], odf0: vec![0.0; MFRO], iodf: vec![0; MFRO], } } } // ============================================================================ // 主函数 // ============================================================================ /// 计算 ODF1 (纯计算函数)。 /// /// # 参数 /// /// * `params` - 输入参数 /// * `cache` - 内部缓存 (用于 IMODE 调用间保存状态) /// /// # 返回值 /// /// ODF1 输出结构 pub fn odf1(params: &Odf1Params, cache: &mut Odf1Cache) -> Odf1Output { let il = params.il; let iu = params.iu; let id = params.id; let imode = params.imode; // 获取能级参数 let nquant_il = params.atomic.levpar.nquant[il]; let kl_idx = (params.odfctr.indodf[il] - 1) as usize; let ielo = params.atomic.levpar.iel[il]; let ielo_idx = (ielo - 1) as usize; let n1h = params.atomic.ionpar.nlast[ielo_idx]; let nq1 = params.odfmod.nqlodf[il]; let fre = params.atomic.levpar.enion[il] / H; let t = params.modpar.temp[id]; let sqt = t.sqrt(); let ane = params.modpar.elec[id]; let anes = ane.powf(UN / 6.0); let f00 = C00 * anes * anes * anes * anes; let dop0 = CQT * sqt; let qz = params.atomic.ionpar.iz[ielo_idx] as f64; // 跃迁索引 let itr = (params.atomic.trapar.itra[il][iu] - 1) as usize; let nfr0 = params.odfctr.nfrodf[kl_idx] as usize; // 临时数组 let mut abs0 = vec![0.0; MFRO]; let mut alam = vec![0.0; MFRO]; let mut frod = vec![0.0; MFRO]; let mut sgt = vec![0.0; MFRO]; let mut dwf = vec![0.0; MFRO]; let mut iodr = vec![0i32; MFRO]; // 伪连续不透明度 (所有频率非零) // 通过溶解分数表述 if imode == 0 { for ij in 0..nfr0 { cache.fro[ij] = params.odffrq.fros[ij][kl_idx]; let sigk_params = SigkParams { fr: cache.fro[ij], itr, mode: 1, atomic: params.atomic, opdata: params.opdata, }; cache.sgfr[ij] = sigk(&sigk_params); alam[ij] = CAS / cache.fro[ij]; } } // 溶解分数 D(nu) dwnfr( 1, nfr0, fre, 0.0, // acor 参数,需要从其他地方获取 ane, qz, &cache.fro[..nfr0], params.inppar, &mut dwf[..nfr0], ); for ij in 0..nfr0 { abs0[ij] = cache.sgfr[ij] * dwf[ij]; } // 对各条谱线求和 for j in nq1 as usize..NLMX { let xj = j as f64; let fxk = f00 * params.odfstk.xkij[kl_idx][j]; let wl0 = params.odfstk.wl0[kl_idx][j]; let dop = dop0 / wl0; let dbeta = wl0 * wl0 / CAS / fxk; let betad = dop * dbeta; let fid = CID * params.odfstk.fij[kl_idx][j] * dbeta; // 计算 Stark 辅助参数 let (adh, divh) = divstr(betad, 1); // 更新 straux (通过重新计算) let straux_local = StrAux { betad, adh, divh, ..Default::default() }; let wprob = params.wnhint[j][id]; odfhst( nfr0, fxk, fid, wprob, wl0, &alam[..nfr0], &straux_local, &mut sgt[..nfr0], ); for ij in 0..nfr0 { abs0[ij] += sgt[ij]; } } // 内部频率集的不透明度分布函数 if imode == 0 { cache.odf0[0] = abs0[0]; cache.iodf[0] = 1; for ij in 1..nfr0 { cache.odf0[ij] = abs0[ij]; cache.iodf[ij] = (ij + 1) as i32; // 插入排序保持单调递增 if cache.odf0[ij] < cache.odf0[ij - 1] { let ab = cache.odf0[ij]; let ijodf = cache.iodf[ij]; for ij0 in 1..=ij { let ij1 = ij - ij0 + 1; if cache.odf0[ij1] >= cache.odf0[ij1 - 1] { break; } cache.odf0[ij1] = cache.odf0[ij1 - 1]; cache.odf0[ij1 - 1] = ab; cache.iodf[ij1] = cache.iodf[ij1 - 1]; cache.iodf[ij1 - 1] = ijodf; } } // WRITE(6,603) IJ,ID,ODF0(IJ) -- FORMAT(' ij,id,odf0',2I5,1PD10.3) if cache.odf0[ij] > 0.001 { eprintln!(" ij,id,odf0{:5}{:5}{:10.3}", ij + 1, id + 1, cache.odf0[ij]); } } } else { cache.odf0[0] = abs0[(cache.iodf[0] - 1) as usize]; iodr[0] = cache.iodf[0]; for ij in 1..nfr0 { cache.odf0[ij] = abs0[(cache.iodf[ij] - 1) as usize]; iodr[ij] = cache.iodf[ij]; if cache.odf0[ij] < cache.odf0[ij - 1] { let ab = cache.odf0[ij]; let ijodf = iodr[ij]; for ij0 in 1..=ij { let ij1 = ij - ij0 + 1; if cache.odf0[ij1] >= cache.odf0[ij1 - 1] { break; } cache.odf0[ij1] = cache.odf0[ij1 - 1]; cache.odf0[ij1 - 1] = ab; iodr[ij1] = iodr[ij1 - 1]; iodr[ij1 - 1] = ijodf; } } // WRITE(6,603) IJ,ID,ODF0(IJ) -- FORMAT(' ij,id,odf0',2I5,1PD10.3) if cache.odf0[ij] > 0.001 { eprintln!(" ij,id,odf0{:5}{:5}{:10.3}", ij + 1, id + 1, cache.odf0[ij]); } } for ij in 0..nfr0 { cache.iodf[ij] = iodr[ij]; } } // 内部频率集的重新初始化 frod[0] = cache.fro[0]; let mut iw = cache.iodf[0] as usize; let w1 = if iw > 1 && iw < nfr0 { cache.fro[iw - 2] - cache.fro[iw] } else if iw == 1 { cache.fro[0] - cache.fro[1] } else { cache.fro[nfr0 - 2] - cache.fro[nfr0 - 1] }; for ij in 1..nfr0 - 1 { iw = cache.iodf[ij] as usize; let w2 = if iw > 1 && iw < nfr0 { HALF * (cache.fro[iw - 2] - cache.fro[iw]) } else if iw == 1 { HALF * (cache.fro[0] - cache.fro[1]) } else { HALF * (cache.fro[nfr0 - 2] - cache.fro[nfr0 - 1]) }; frod[ij] = frod[ij - 1] - HALF * (w1 + w2); } iw = cache.iodf[nfr0 - 1] as usize; let w2 = if iw > 1 && iw < nfr0 { cache.fro[iw - 2] - cache.fro[iw] } else if iw == 1 { cache.fro[0] - cache.fro[1] } else { cache.fro[nfr0 - 2] - cache.fro[nfr0 - 1] }; frod[nfr0 - 1] = frod[nfr0 - 2] - HALF * (w1 + w2); // 插值到显式频率 let nfreq = params.freq.len(); let mut odf = vec![0.0; nfreq]; let mut i1odf: i32 = 0; let mut i2odf: i32 = 0; for ij in 1..nfreq { // 检查频率是否单调递减 if params.freq[ij] > params.freq[ij - 1] { break; } odf[ij] = 0.0; // 检查频率是否在范围内 if params.freq[ij] > frod[0] || params.freq[ij] < frod[nfr0 - 1] { continue; } if id == 0 { if params.freq[ij - 1] > frod[0] { i1odf = ij as i32; } i2odf = ij as i32; } // 找到插值位置 let mut ij0 = 1; for ij1 in 2..nfr0 { ij0 = ij1; if params.freq[ij] >= frod[ij1] { break; } } // 线性插值 let frod_ij0 = frod[ij0]; let frod_ij0_1 = frod[ij0 - 1]; let denom = frod_ij0 - frod_ij0_1; if denom.abs() > 1e-30 { odf[ij] = cache.odf0[ij0 - 1] + (cache.odf0[ij0] - cache.odf0[ij0 - 1]) / denom * (params.freq[ij] - frod_ij0_1); } } Odf1Output { odf, i1odf, i2odf, } } #[cfg(test)] mod tests { use super::*; use crate::tlusty::state::atomic::AtomicData; use crate::tlusty::state::config::InpPar; use crate::tlusty::state::model::{ModPar, StrAux}; use crate::tlusty::state::odfpar::{OdfCtr, OdfMod, OdfStk, OdfFrq}; use crate::tlusty::math::OpData; fn create_test_params<'a>( atomic: &'a AtomicData, modpar: &'a ModPar, straux: &'a StrAux, wnhint: &'a [Vec], odfctr: &'a OdfCtr, odfmod: &'a OdfMod, odfstk: &'a OdfStk, odffrq: &'a OdfFrq, inppar: &'a InpPar, opdata: &'a OpData, freq: &'a [f64], ) -> Odf1Params<'a> { Odf1Params { imode: 0, il: 0, iu: 1, id: 0, freq, atomic, modpar, straux, wnhint, odfctr, odfmod, odfstk, odffrq, inppar, opdata, } } fn create_test_wnhint() -> Vec> { vec![vec![0.5; MDEPTH]; NLMX] } #[test] fn test_odf1_basic() { let mut atomic = AtomicData::new(); let mut modpar = ModPar::default(); let straux = StrAux::default(); let wnhint = create_test_wnhint(); let mut odfctr = OdfCtr::new(); let mut odfmod = OdfMod::new(); let mut odfstk = OdfStk::new(NLMX); let mut odffrq = OdfFrq::new(); let inppar = InpPar::default(); let opdata = OpData::default(); // 设置基本测试数据 atomic.levpar.nquant[0] = 1; atomic.levpar.iel[0] = 1; atomic.levpar.enion[0] = 13.6 * 1.2398e-4; // 氢电离能 (erg) atomic.ionpar.nlast[0] = 10; atomic.ionpar.iz[0] = 1; atomic.trapar.itra[0][1] = 1; atomic.trapar.indexp[0] = 1; atomic.trapar.itrcon[0] = 1; // 连续跃迁索引 (1-indexed) atomic.trapar.ilow[0] = 1; // 下能级索引 (1-indexed) atomic.trapar.fr0[0] = 3.29e15; // 阈值频率 // 设置光致电离截面模式 atomic.phoset.ibf[0] = 0; // 氢原子截面模式 modpar.temp[0] = 10000.0; modpar.elec[0] = 1e12; // ODF 参数 (1-indexed) odfctr.indodf[0] = 1; // ODF 索引 odfctr.nfrodf[0] = 5; // 频率点数 odfmod.nqlodf[0] = 1; // 起始量子数 // 设置 ODF 频率数据 for ij in 0..5 { odffrq.fros[ij][0] = 3.0e15 + (ij as f64) * 0.1e15; } // 设置 ODF Stark 数据 for j in 0..NLMX { odfstk.xkij[0][j] = 1e-10; odfstk.wl0[0][j] = 1215.0; odfstk.fij[0][j] = 0.1; } let freq = vec![3.5e15, 3.4e15, 3.3e15, 3.2e15, 3.1e15]; let params = create_test_params( &atomic, &modpar, &straux, &wnhint, &odfctr, &odfmod, &odfstk, &odffrq, &inppar, &opdata, &freq, ); let mut cache = Odf1Cache::default(); let result = odf1(¶ms, &mut cache); // 验证输出数组大小 assert_eq!(result.odf.len(), freq.len()); } #[test] fn test_odf1_cache_default() { let cache = Odf1Cache::default(); assert_eq!(cache.fro.len(), MFRO); assert_eq!(cache.sgfr.len(), MFRO); assert_eq!(cache.odf0.len(), MFRO); assert_eq!(cache.iodf.len(), MFRO); } #[test] fn test_odf1_frequency_monotonic_check() { // 测试频率单调性检查 let freq_increasing = vec![3.0e15, 3.5e15, 4.0e15]; // 递增,应该提前返回 let freq_decreasing = vec![4.0e15, 3.5e15, 3.0e15]; // 递减,应该正常处理 let mut atomic = AtomicData::new(); let mut modpar = ModPar::default(); let straux = StrAux::default(); let wnhint = create_test_wnhint(); let mut odfctr = OdfCtr::new(); let mut odfmod = OdfMod::new(); let mut odfstk = OdfStk::new(NLMX); let mut odffrq = OdfFrq::new(); let inppar = InpPar::default(); let opdata = OpData::default(); // 设置基本测试数据 atomic.levpar.nquant[0] = 1; atomic.levpar.iel[0] = 1; atomic.levpar.enion[0] = 13.6 * 1.2398e-4; atomic.ionpar.nlast[0] = 10; atomic.ionpar.iz[0] = 1; atomic.trapar.itra[0][1] = 1; atomic.trapar.indexp[0] = 1; atomic.trapar.itrcon[0] = 1; // 连续跃迁索引 atomic.trapar.ilow[0] = 1; // 下能级索引 atomic.trapar.fr0[0] = 3.29e15; // 阈值频率 // 设置光致电离截面模式 atomic.phoset.ibf[0] = 0; // 氢原子截面模式 modpar.temp[0] = 10000.0; modpar.elec[0] = 1e12; // ODF 参数 (1-indexed) odfctr.indodf[0] = 1; odfctr.nfrodf[0] = 5; odfmod.nqlodf[0] = 1; // 设置 ODF 频率数据 for ij in 0..5 { odffrq.fros[ij][0] = 3.0e15 + (ij as f64) * 0.1e15; } // 设置 ODF Stark 数据 for j in 0..NLMX { odfstk.xkij[0][j] = 1e-10; odfstk.wl0[0][j] = 1215.0; odfstk.fij[0][j] = 0.1; } // 递增频率应该在第 1 个元素后就停止 let params_inc = create_test_params( &atomic, &modpar, &straux, &wnhint, &odfctr, &odfmod, &odfstk, &odffrq, &inppar, &opdata, &freq_increasing, ); let mut cache = Odf1Cache::default(); let result_inc = odf1(¶ms_inc, &mut cache); // 递减频率应该处理所有元素 let params_dec = create_test_params( &atomic, &modpar, &straux, &wnhint, &odfctr, &odfmod, &odfstk, &odffrq, &inppar, &opdata, &freq_decreasing, ); let mut cache2 = Odf1Cache::default(); let result_dec = odf1(¶ms_dec, &mut cache2); // 验证输出大小 assert_eq!(result_inc.odf.len(), 3); assert_eq!(result_dec.odf.len(), 3); } }