//! 所有深度点的吸收、发射和散射系数计算 (含离子贡献)。 //! //! 重构自 TLUSTY `opacfa.f` //! //! 对于给定频率点,计算所有深度点的吸收、发射和散射系数, //! 并保存每个离子的贡献(用于计算冷却和加热率)。 //! //! # 算法流程 //! //! 1. 初始化电子散射贡献 //! 2. 计算频率和深度相关的基础量 (XKF, XKFB 等) //! 3. 计算束缚-自由 (bound-free) 贡献 //! 4. 计算自由-自由 (free-free) 贡献 //! 5. 计算附加不透明度 (OPADD) //! 6. 计算谱线贡献 (如果 icoolp != 0) //! 7. 最终不透明度计算 use crate::tlusty::state::constants::{HK, UN}; // 物理常数 const C14: f64 = 2.99793e14; const CFF1: f64 = 1.3727e-25; // ============================================================================ // 参数结构体 // ============================================================================ /// OPACFA 输入参数 #[derive(Debug)] pub struct OpacfaParams<'a> { /// 频率索引 (1-indexed) pub ij: usize, // 控制参数 /// Compton 散射标志 (>0: 计算) pub icompt: i32, /// 冷却率标志 (0: 跳过谱线贡献) pub icoolp: i32, /// ODF 采样标志 (0: 标准模式, >0: ODF 采样) pub ispodf: i32, /// 双电子复合标志 (0: 无, >0: 有) pub ifdiel: i32, /// 附加不透明度标志 (0: 无, !=0: 有) pub iopadd: i32, /// PRD 标志 (>0: 调用 PRD) pub ifprd: i32, /// 密度缩放标志 (0: 已缩放, >0: 需缩放) pub izscal: i32, // 频率数据 /// 频率数组 (nfreq) pub freq: &'a [f64], /// Planck 函数 (nfreq) pub bnue: &'a [f64], // 深度数据 /// 深度点数 pub nd: usize, /// 温度 (nd) pub temp: &'a [f64], /// 电子密度 (nd) pub elec: &'a [f64], /// 密度倒数 (nd) - 用于 izscal > 0 时 pub dens1: &'a [f64], /// 电子散射系数 (nd) - 输入/输出 pub elscat: &'a [f64], /// 电子散射截面 (nfreq) pub sigec: &'a [f64], // 表格频率阈值 /// 表格最大频率 pub frtabm: f64, // 工作数组 (输入/输出) /// HKT1 (nd) - HK/T pub hkt1: &'a mut [f64], /// XKF (nd) pub xkf: &'a mut [f64], /// XKF1 (nd) pub xkf1: &'a mut [f64], /// XKFB (nd) pub xkfb: &'a mut [f64], } /// OPACFA 输出状态 #[derive(Debug)] pub struct OpacfaOutput<'a> { /// 吸收系数 (nd) pub abso1: &'a mut [f64], /// 发射系数 (nd) pub emis1: &'a mut [f64], /// 散射系数 (nd) pub scat1: &'a mut [f64], /// 累积吸收系数 (nd) pub absot: &'a mut [f64], /// 连续谱吸收系数 (nd) - 不含谱线 pub absoc1: &'a mut [f64], /// 连续谱发射系数 (nd) - 不含谱线 pub emisc1: &'a mut [f64], /// 离子吸收贡献 (mion × nd) pub absoti: &'a mut [f64], /// 离子发射贡献 (mion × nd) pub emisti: &'a mut [f64], } /// OPACFA 配置结构体 - 包含所有需要的状态数据 #[derive(Debug)] pub struct OpacfaState<'a> { /// 离子数 pub nion: usize, /// 束缚-自由跃迁数 pub ntranc: usize, // 跃迁相关 /// 束缚-自由跃迁索引 (ntranc), 1-indexed pub itrbf: &'a [i32], /// 低能级索引 (mtrans), 1-indexed pub ilow: &'a [i32], /// 高能级索引 (mtrans), 1-indexed pub iup: &'a [i32], /// 频率阈值索引 (mtrans), 1-indexed pub ifr0: &'a [i32], /// 频率终点索引 (mtrans), 1-indexed pub ifr1: &'a [i32], /// Macfarlane 下沉修正索引 (mtrans), <= 0 表示无 pub mcdw: &'a [i32], /// 阈值频率 (mtrans) pub fr0: &'a [f64], /// Mermerges 处理标志 (mlevel), < 0 表示需要特殊处理 pub ifwop: &'a [i32], /// Mermerges 索引 (mlevel) pub imrg: &'a [i32], // 离子相关 /// 离子对应的下一个能级索引 (mion), 1-indexed pub nnext: &'a [i32], /// 自由-自由阈值频率 (mion) pub ff: &'a [f64], /// 电荷² (mion) pub charg2: &'a [i32], /// 自由-自由系数 SFF2 (mion × nd) pub sff2: &'a [f64], /// 自由-自由系数 SFF3 (mion × nd) pub sff3: &'a [f64], /// 自由-自由类型 (mion): 1=氢型(Gaunt=1), 2=精确Gaunt, 3=H⁻, <0=特殊 pub itype_ff: &'a [i32], // 能级相关 /// 能级对应的元素索引 (mlevel), 1-indexed pub iel: &'a [i32], /// 原子操作标志 (matom), 0=正常, >0=特殊 pub iadop: &'a [i32], /// 能级对应的原子索引 (mlevel), 1-indexed pub iatm: &'a [i32], // 跃迁吸收/发射系数 /// 吸收系数 (mtrans × nd) pub abtra: &'a [f64], /// 发射系数 (mtrans × nd) pub emtra: &'a [f64], // 谱线相关 /// 主谱线索引 (nfreq), 0 表示无, 1-indexed pub ijlin: &'a [i32], /// 重叠谱线数 (nfreq) pub nlines: &'a [i32], /// 谱线展开标志 (mtrans), true=展开 pub linexp: &'a [bool], /// 谱线轮廓 (nd × nfreql 或 nd × nfreq) pub prflin: &'a [f64], // 截面数据 /// 束缚-自由截面 (mcross × nfreq) pub cross_bf: &'a [f64], /// 双电子复合截面 (mcross × nfreq × nd) pub cross_di: &'a [f64], } // ============================================================================ // 主函数 // ============================================================================ /// 计算所有深度点的吸收、发射和散射系数。 /// /// 这是 OPACFA 的简化版本,只实现核心逻辑框架。 /// 完整实现需要传入更多状态参数。 /// /// # 参数 /// /// * `params` - 基本输入参数 /// * `output` - 输出数组 pub fn opacfa(params: &mut OpacfaParams, output: &mut OpacfaOutput) { let ij = params.ij; let ij_idx = ij - 1; // 转换为 0-indexed let nd = params.nd; // ======================================================================== // 1. 初始化 // ======================================================================== // Compton 散射初始化 if params.icompt > 0 { for id in 0..nd { let sigec_val = if ij_idx < params.sigec.len() { params.sigec[ij_idx] } else { 0.0 }; // ELSCAT(ID) = ELEC(ID) * SIGEC(IJ) // 注意: elscat 是输入,这里只是使用它 } } // 初始化输出数组 for id in 0..nd { output.abso1[id] = params.elscat[id]; output.emis1[id] = 0.0; output.scat1[id] = params.elscat[id]; output.absoc1[id] = output.abso1[id]; output.emisc1[id] = 0.0; // 初始化离子贡献 for ion in 0..(output.absoti.len() / nd) { let idx = ion * nd + id; output.absoti[idx] = 0.0; output.emisti[idx] = 0.0; } } // ======================================================================== // 2. 计算频率和深度相关的基础量 // ======================================================================== let fr = if ij_idx < params.freq.len() { params.freq[ij_idx] } else { return; // 频率索引越界 }; let frinv = UN / fr; let fr3inv = frinv * frinv * frinv; let lfre = fr > params.frtabm; for id in 0..nd { params.hkt1[id] = HK / params.temp[id]; params.xkf[id] = (-params.hkt1[id] * fr).exp(); params.xkf1[id] = UN - params.xkf[id]; params.xkfb[id] = params.xkf[id] * params.bnue[ij_idx]; } // ======================================================================== // 3. 束缚-自由贡献 (简化版) // ======================================================================== // 完整实现需要: // - 遍历 NTRANC 个束缚-自由跃迁 // - 调用 CROSS 或 CROSSD 获取截面 // - 调用 DWNFR1 处理 Macfarlane 下沉修正 // - 调用 SGMER1 处理 Mermerges 能级 // 此处留作框架,实际计算在完整版本中实现 // ======================================================================== // 4. 自由-自由贡献 (简化版) // ======================================================================== // 完整实现需要: // - 遍历 NION 个离子 // - 根据 ITYPE_FF 选择计算方式 // - 调用 SFFHMI (H⁻ 自由-自由) // - 调用 FFCROS (特殊截面) // ======================================================================== // 5. 附加不透明度 (OPADD) // ======================================================================== // 完整实现需要调用 OPADD // ======================================================================== // 6. 保存连续谱系数 // ======================================================================== for id in 0..nd { output.absoc1[id] = output.abso1[id]; output.emisc1[id] = output.emis1[id]; } // ======================================================================== // 7. 谱线贡献 (如果 icoolp != 0) // ======================================================================== // 完整实现需要: // - 主谱线处理 // - 重叠谱线处理 // - ODF 采样模式处理 if params.icoolp == 0 { // 跳过谱线贡献 finalize_opacities(params, output, nd); return; } // ======================================================================== // 8. 最终不透明度计算 // ======================================================================== finalize_opacities(params, output, nd); } /// 最终不透明度计算 fn finalize_opacities( params: &OpacfaParams, output: &mut OpacfaOutput, nd: usize, ) { let nion = output.absoti.len() / nd; for id in 0..nd { // 总不透明度 = 吸收 - 发射 × 激发因子 output.abso1[id] = output.abso1[id] - output.emis1[id] * params.xkf[id]; output.absoc1[id] = output.absoc1[id] - output.emisc1[id] * params.xkf[id]; // 离子贡献 for ion in 0..nion { let idx = ion * nd + id; output.absoti[idx] = output.absoti[idx] - output.emisti[idx] * params.xkf[id]; } // 发射系数 × Planck 因子 output.emis1[id] = output.emis1[id] * params.xkfb[id]; output.emisc1[id] = output.emisc1[id] * params.xkfb[id]; for ion in 0..nion { let idx = ion * nd + id; output.emisti[idx] = output.emisti[idx] * params.xkfb[id]; } // 累积吸收系数 output.absot[id] = output.abso1[id]; // 密度缩放 if params.izscal == 0 { output.absot[id] = output.abso1[id] * params.dens1[id]; } } } // ============================================================================ // 测试 // ============================================================================ #[cfg(test)] mod tests { use super::*; use approx::assert_relative_eq; fn create_test_params<'a>( freq: &'a [f64], bnue: &'a [f64], temp: &'a [f64], elec: &'a [f64], dens1: &'a [f64], elscat: &'a [f64], sigec: &'a [f64], hkt1: &'a mut [f64], xkf: &'a mut [f64], xkf1: &'a mut [f64], xkfb: &'a mut [f64], ) -> OpacfaParams<'a> { OpacfaParams { ij: 3, icompt: 0, icoolp: 0, ispodf: 0, ifdiel: 0, iopadd: 0, ifprd: 0, izscal: 1, freq, bnue, nd: temp.len(), temp, elec, dens1, elscat, sigec, frtabm: 1e16, hkt1, xkf, xkf1, xkfb, } } fn create_test_output<'a>( abso1: &'a mut [f64], emis1: &'a mut [f64], scat1: &'a mut [f64], absot: &'a mut [f64], absoc1: &'a mut [f64], emisc1: &'a mut [f64], absoti: &'a mut [f64], emisti: &'a mut [f64], ) -> OpacfaOutput<'a> { OpacfaOutput { abso1, emis1, scat1, absot, absoc1, emisc1, absoti, emisti, } } #[test] fn test_opacfa_initialization() { let nd = 3; let nfreq = 5; let nion = 2; let freq = vec![1e14, 2e14, 3e14, 4e14, 5e14]; let bnue = vec![1e-10, 2e-10, 3e-10, 4e-10, 5e-10]; let temp = vec![5000.0, 6000.0, 7000.0]; let elec = vec![1e10, 2e10, 3e10]; let dens1 = vec![1e-15, 1e-15, 1e-15]; let elscat = vec![1e-20, 2e-20, 3e-20]; let sigec = vec![1e-24; nfreq]; let mut hkt1 = vec![0.0; nd]; let mut xkf = vec![0.0; nd]; let mut xkf1 = vec![0.0; nd]; let mut xkfb = vec![0.0; nd]; let mut abso1 = vec![0.0; nd]; let mut emis1 = vec![0.0; nd]; let mut scat1 = vec![0.0; nd]; let mut absot = vec![0.0; nd]; let mut absoc1 = vec![0.0; nd]; let mut emisc1 = vec![0.0; nd]; let mut absoti = vec![0.0; nion * nd]; let mut emisti = vec![0.0; nion * nd]; let mut params = create_test_params( &freq, &bnue, &temp, &elec, &dens1, &elscat, &sigec, &mut hkt1, &mut xkf, &mut xkf1, &mut xkfb, ); let mut output = create_test_output( &mut abso1, &mut emis1, &mut scat1, &mut absot, &mut absoc1, &mut emisc1, &mut absoti, &mut emisti, ); opacfa(&mut params, &mut output); // 验证初始化 for id in 0..nd { assert_relative_eq!(output.abso1[id], elscat[id], epsilon = 1e-30); assert_relative_eq!(output.scat1[id], elscat[id], epsilon = 1e-30); } } #[test] fn test_opacfa_frequency_quantities() { let nd = 2; let nfreq = 3; let freq = vec![1e15, 2e15, 3e15]; let bnue = vec![1e-10, 2e-10, 3e-10]; let temp = vec![5770.0, 6000.0]; let elec = vec![1e13, 2e13]; let dens1 = vec![1e-7, 1e-7]; let elscat = vec![1e-8, 2e-8]; let sigec = vec![1e-24; nfreq]; let mut hkt1 = vec![0.0; nd]; let mut xkf = vec![0.0; nd]; let mut xkf1 = vec![0.0; nd]; let mut xkfb = vec![0.0; nd]; let nion = 1; let mut abso1 = vec![0.0; nd]; let mut emis1 = vec![0.0; nd]; let mut scat1 = vec![0.0; nd]; let mut absot = vec![0.0; nd]; let mut absoc1 = vec![0.0; nd]; let mut emisc1 = vec![0.0; nd]; let mut absoti = vec![0.0; nion * nd]; let mut emisti = vec![0.0; nion * nd]; let mut params = create_test_params( &freq, &bnue, &temp, &elec, &dens1, &elscat, &sigec, &mut hkt1, &mut xkf, &mut xkf1, &mut xkfb, ); params.ij = 2; // 使用第二个频率点 let mut output = create_test_output( &mut abso1, &mut emis1, &mut scat1, &mut absot, &mut absoc1, &mut emisc1, &mut absoti, &mut emisti, ); opacfa(&mut params, &mut output); // 验证 HKT1 = HK / T let hk = 6.626176e-27 / 1.380662e-16; // HK constant for id in 0..nd { let expected_hkt1 = hk / temp[id]; assert_relative_eq!(params.hkt1[id], expected_hkt1, epsilon = 1e-10); } // 验证 XKF1 = 1 - XKF for id in 0..nd { assert_relative_eq!(params.xkf1[id], 1.0 - params.xkf[id], epsilon = 1e-15); } } #[test] fn test_finalize_opacities() { let nd = 2; let nfreq = 3; // 需要至少3个频率点因为 ij=3 let nion = 1; let freq = vec![1e15, 2e15, 3e15]; let bnue = vec![1e-10, 2e-10, 3e-10]; let temp = vec![5770.0, 6000.0]; let elec = vec![1e13, 2e13]; let dens1 = vec![1e-7, 1e-7]; let elscat = vec![1e-8, 2e-8]; let sigec = vec![1e-24; nfreq]; let mut hkt1 = vec![0.0; nd]; let mut xkf = vec![0.0; nd]; let mut xkf1 = vec![0.0; nd]; let mut xkfb = vec![0.0; nd]; let mut abso1 = vec![0.0; nd]; let mut emis1 = vec![0.0; nd]; let mut scat1 = vec![0.0; nd]; let mut absot = vec![0.0; nd]; let mut absoc1 = vec![0.0; nd]; let mut emisc1 = vec![0.0; nd]; let mut absoti = vec![0.0; nion * nd]; let mut emisti = vec![0.0; nion * nd]; let mut params = create_test_params( &freq, &bnue, &temp, &elec, &dens1, &elscat, &sigec, &mut hkt1, &mut xkf, &mut xkf1, &mut xkfb, ); params.izscal = 1; // 不进行密度缩放 let mut output = create_test_output( &mut abso1, &mut emis1, &mut scat1, &mut absot, &mut absoc1, &mut emisc1, &mut absoti, &mut emisti, ); opacfa(&mut params, &mut output); // 验证最终计算: absot = abso1 (izscal = 1) // 由于 emis1 = 0,所以 abso1 = abso1 - 0 * xkf = 初始值 = elscat for id in 0..nd { assert_relative_eq!(output.absot[id], output.abso1[id], epsilon = 1e-30); assert_relative_eq!(output.abso1[id], elscat[id], epsilon = 1e-30); } } }