//! 吸收、发射和散射系数计算(频率和深度相关)。 //! //! 重构自 TLUSTY `OPACFL.f` //! //! 计算给定频率点在所有深度的不透明度和发射率: //! - 束缚-自由贡献(含/不含双电子复合) //! - 自由-自由贡献 //! - 额外连续谱不透明度 (OPADD) //! - 谱线贡献 // ============================================================================ // 常量 // ============================================================================ /// 光速 (cm/s) × 1e14 const C14: f64 = 2.99793e14; /// 单位常数 const UN: f64 = 1.0; // f2r_depends: DWNFR1, OPADD, SGMER1 /// Absorption, emission, and scattering coefficients wrapper (matches Fortran OPACFL subroutine signature). pub fn opacfl( ij: usize, nd: usize, freq: &[f64], bnue: &[f64], hkt1: &[f64], elscat: &[f64], ) -> OpacflOutput { opacfl_init(ij, nd, freq, bnue, hkt1, elscat) } // ============================================================================ // 输出结构体 // ============================================================================ /// OPACFL 输出结构体。 #[derive(Debug, Clone, Default)] pub struct OpacflOutput { /// 吸收系数 (MDEPTH) pub abso1: Vec, /// 发射系数 (MDEPTH) pub emis1: Vec, /// 散射系数 (MDEPTH) pub scat1: Vec, /// 谱线吸收系数 (MDEPTH) pub abso1l: Vec, /// 谱线发射系数 (MDEPTH) pub emis1l: Vec, /// 辐射权重 XKF (MDEPTH) pub xkf: Vec, /// 1 - XKF (MDEPTH) pub xkf1: Vec, /// 普朗克函数 × XKF (MDEPTH) pub xkfb: Vec, } // ============================================================================ // 主函数 - 简化版本 // ============================================================================ /// 计算给定频率点的不透明度、发射率和散射系数。 /// /// 这是简化版本,用于测试和验证核心逻辑。 /// 完整版本需要传入完整的模型状态。 /// /// # 参数 /// - `ij`: 频率索引 (0-indexed) /// - `nd`: 深度点数 /// - `freq`: 频率数组 /// - `bnue`: 普朗克函数数组 /// - `hkt1`: h/kT 数组 /// - `elscat`: 电子散射不透明度数组 /// /// # 返回值 /// 包含 ABSO1, EMIS1, SCAT1, ABSO1L, EMIS1L, XKF, XKF1, XKFB 的结构体 /// /// # Fortran 原始代码 /// /// ```fortran /// SUBROUTINE OPACFL(IJ) /// DO ID=1,ND /// ABSO1(ID)=ELSCAT(ID) /// EMIS1(ID)=0. /// SCAT1(ID)=ELSCAT(ID) /// ABSO1L(ID)=0. /// EMIS1L(ID)=0. /// END DO /// /// FR=FREQ(IJ) /// FRINV=UN/FR /// FR3INV=FRINV*FRINV*FRINV /// DO ID=1,ND /// XKF(ID)=EXP(-HKT1(ID)*FR) /// XKF1(ID)=UN-XKF(ID) /// XKFB(ID)=XKF(ID)*BNUE(IJ) /// END DO /// ``` pub fn opacfl_init( ij: usize, nd: usize, freq: &[f64], bnue: &[f64], hkt1: &[f64], elscat: &[f64], ) -> OpacflOutput { // 初始化输出 let mut output = OpacflOutput { abso1: vec![0.0; nd], emis1: vec![0.0; nd], scat1: vec![0.0; nd], abso1l: vec![0.0; nd], emis1l: vec![0.0; nd], xkf: vec![0.0; nd], xkf1: vec![0.0; nd], xkfb: vec![0.0; nd], }; // 初始化基本量 for id in 0..nd { output.abso1[id] = elscat[id]; output.emis1[id] = 0.0; output.scat1[id] = elscat[id]; output.abso1l[id] = 0.0; output.emis1l[id] = 0.0; } // 基本频率和深度相关量 let fr = freq[ij]; let _frinv = UN / fr; let _fr3inv = _frinv * _frinv * _frinv; for id in 0..nd { output.xkf[id] = (-hkt1[id] * fr).exp(); output.xkf1[id] = UN - output.xkf[id]; output.xkfb[id] = output.xkf[id] * bnue[ij]; } output } /// 完成不透明度计算的最后步骤。 /// /// # 参数 /// - `output`: 部分计算的输出(会被修改) /// /// # Fortran 原始代码 /// /// ```fortran /// DO ID=1,ND /// ABSO1(ID)=ABSO1(ID)-EMIS1(ID)*XKF(ID) /// EMIS1(ID)=EMIS1(ID)*XKFB(ID) /// ABSO1L(ID)=ABSO1L(ID)-EMIS1L(ID)*XKF(ID) /// EMIS1L(ID)=EMIS1L(ID)*XKFB(ID) /// ABSO1L(ID)=ABSO1(ID)-ABSO1L(ID) /// EMIS1L(ID)=EMIS1(ID)-EMIS1L(ID) /// END DO /// ``` pub fn opacfl_finalize(output: &mut OpacflOutput) { let nd = output.abso1.len(); for id in 0..nd { output.abso1[id] -= output.emis1[id] * output.xkf[id]; output.emis1[id] *= output.xkfb[id]; output.abso1l[id] -= output.emis1l[id] * output.xkf[id]; output.emis1l[id] *= output.xkfb[id]; output.abso1l[id] = output.abso1[id] - output.abso1l[id]; output.emis1l[id] = output.emis1[id] - output.emis1l[id]; } } // ============================================================================ // 辅助函数 // ============================================================================ /// 计算自由-自由不透明度(氢型,Gaunt 因子 = 1)。 /// /// # Fortran 原始代码 /// /// ```fortran /// IF(IT.EQ.1) THEN /// DO ID=1,ND /// SF1=SFF3(ION,ID)*FR3INV /// SF2=SFF2(ION,ID) /// IF(FR.LT.FF(ION)) SF2=UN/XKF(ID) /// ABSOFF=SF1*SF2 /// ABSO1(ID)=ABSO1(ID)+ABSOFF /// EMIS1(ID)=EMIS1(ID)+ABSOFF /// END DO /// ``` pub fn free_free_hydrogenic( nd: usize, sff2: &[f64], sff3: &[f64], fr: f64, fr3inv: f64, ff_ion: f64, xkf: &[f64], abso1: &mut [f64], emis1: &mut [f64], ) { for id in 0..nd { let sf1 = sff3[id] * fr3inv; let mut sf2 = sff2[id]; if fr < ff_ion { sf2 = UN / xkf[id]; } let absoff = sf1 * sf2; abso1[id] += absoff; emis1[id] += absoff; } } /// 计算自由-自由不透明度(氢型,精确 Gaunt 因子)。 /// /// # Fortran 原始代码 /// /// ```fortran /// ELSE IF(IT.EQ.2) THEN /// DO ID=1,ND /// SF1=SFF3(ION,ID)*FR3INV /// SF2=SFF2(ION,ID) /// IF(FR.LT.FF(ION)) SF2=UN/XKF(ID) /// X=C14*CHARG2(ION)/FR /// SF2=SF2-UN+GFREE1(ID,X) /// ABSOFF=SF1*SF2 /// ABSO1(ID)=ABSO1(ID)+ABSOFF /// EMIS1(ID)=EMIS1(ID)+ABSOFF /// END DO /// ``` pub fn free_free_hydrogenic_gaunt( nd: usize, sff2: &[f64], sff3: &[f64], fr: f64, fr3inv: f64, ff_ion: f64, charg2_ion: f64, xkf: &[f64], gfree1_values: &[f64], abso1: &mut [f64], emis1: &mut [f64], ) { let _x = C14 * charg2_ion / fr; for id in 0..nd { let sf1 = sff3[id] * fr3inv; let mut sf2 = sff2[id]; if fr < ff_ion { sf2 = UN / xkf[id]; } sf2 = sf2 - UN + gfree1_values[id]; let absoff = sf1 * sf2; abso1[id] += absoff; emis1[id] += absoff; } } /// 计算束缚-自由贡献(简化版)。 /// /// # 参数 /// - `sg`: 截面 /// - `abtra`: 吸收跃迁矩阵值 /// - `emtra`: 发射跃迁矩阵值 /// - `iluctr`: 跃迁控制标志 /// - `output`: 输出结构体 /// /// # Fortran 原始代码 /// /// ```fortran /// EMISBF=SGD*EMTRA(ITR,ID) /// ABSO1(ID)=ABSO1(ID)+SGD*ABTRA(ITR,ID) /// EMIS1(ID)=EMIS1(ID)+EMISBF /// if(iluctr(itr).gt.0) then /// ABSO1L(ID)=ABSO1L(ID)+SGD*ABTRA(ITR,ID) /// EMIS1L(ID)=EMIS1L(ID)+EMISBF /// end if /// ``` pub fn bound_free_contribution( nd: usize, sgd: f64, abtra: &[f64], emtra: &[f64], iluctr: i32, output: &mut OpacflOutput, ) { for id in 0..nd { let emisbf = sgd * emtra[id]; output.abso1[id] += sgd * abtra[id]; output.emis1[id] += emisbf; if iluctr > 0 { output.abso1l[id] += sgd * abtra[id]; output.emis1l[id] += emisbf; } } } /// 计算谱线贡献(主线索引)。 /// /// # Fortran 原始代码 /// /// ```fortran /// IF(IJLIN(IJ).GT.0) THEN /// ITR=IJLIN(IJ) /// ... /// DO ID=1,ND /// SG=PRFLIN(ID,IJ) /// ABSO1(ID)=ABSO1(ID)+SG*ABTRA(ITR,ID) /// EMIS1(ID)=EMIS1(ID)+SG*EMTRA(ITR,ID) /// END DO /// ``` pub fn line_contribution_primary( nd: usize, prflin_ij: &[f64], abtra_itr: &[f64], emtra_itr: &[f64], output: &mut OpacflOutput, ) { for id in 0..nd { let sg = prflin_ij[id]; output.abso1[id] += sg * abtra_itr[id]; output.emis1[id] += sg * emtra_itr[id]; } } #[cfg(test)] mod tests { use super::*; #[test] fn test_opacfl_output_init() { let output = OpacflOutput::default(); assert!(output.abso1.is_empty()); assert!(output.emis1.is_empty()); assert!(output.scat1.is_empty()); } #[test] fn test_opacfl_init_basic() { let nd = 3; let freq = vec![3.0e15; 100]; let bnue = vec![1e-10; 100]; let hkt1 = vec![4.8e-11; nd]; let elscat = vec![0.1, 0.2, 0.3]; let output = opacfl_init(50, nd, &freq, &bnue, &hkt1, &elscat); assert_eq!(output.abso1.len(), nd); assert_eq!(output.emis1.len(), nd); assert_eq!(output.scat1.len(), nd); // 检查初始化 assert!((output.abso1[0] - 0.1).abs() < 1e-10); assert!((output.scat1[0] - 0.1).abs() < 1e-10); assert!((output.emis1[0]).abs() < 1e-10); // 检查频率相关量 let fr = freq[50]; for id in 0..nd { let expected_xkf = (-hkt1[id] * fr).exp(); assert!((output.xkf[id] - expected_xkf).abs() < 1e-10); assert!((output.xkf1[id] - (1.0 - expected_xkf)).abs() < 1e-10); assert!((output.xkfb[id] - expected_xkf * bnue[50]).abs() < 1e-15); } } #[test] fn test_opacfl_finalize() { let mut output = OpacflOutput { abso1: vec![1.0, 2.0, 3.0], emis1: vec![0.5, 1.0, 1.5], scat1: vec![0.1, 0.2, 0.3], abso1l: vec![0.2, 0.4, 0.6], emis1l: vec![0.1, 0.2, 0.3], xkf: vec![0.5, 0.5, 0.5], xkf1: vec![0.5, 0.5, 0.5], xkfb: vec![1e-10, 1e-10, 1e-10], }; opacfl_finalize(&mut output); // 检查计算结果 // abso1 = abso1 - emis1 * xkf assert!((output.abso1[0] - (1.0 - 0.5 * 0.5)).abs() < 1e-10); // emis1 = emis1 * xkfb assert!((output.emis1[0] - 0.5 * 1e-10).abs() < 1e-15); } #[test] fn test_free_free_hydrogenic() { let nd = 3; let sff2 = vec![1.0, 1.0, 1.0]; let sff3 = vec![1e-25, 1e-25, 1e-25]; let fr = 3.0e15; let fr3inv = 1.0 / (fr * fr * fr); let ff_ion = 4.0e15; // fr < ff_ion let xkf = vec![0.5, 0.5, 0.5]; let mut abso1 = vec![0.0; nd]; let mut emis1 = vec![0.0; nd]; free_free_hydrogenic( nd, &sff2, &sff3, fr, fr3inv, ff_ion, &xkf, &mut abso1, &mut emis1, ); // 由于 fr < ff_ion, sf2 = 1/xkf = 2.0 let expected = sff3[0] * fr3inv * 2.0; assert!((abso1[0] - expected).abs() < 1e-35); assert!((emis1[0] - expected).abs() < 1e-35); } #[test] fn test_bound_free_contribution() { let nd = 3; let sgd = 1e-18; let abtra = vec![1e10, 2e10, 3e10]; let emtra = vec![0.5e10, 1e10, 1.5e10]; let mut output = OpacflOutput { abso1: vec![1.0; nd], emis1: vec![0.5; nd], scat1: vec![0.1; nd], abso1l: vec![0.0; nd], emis1l: vec![0.0; nd], xkf: vec![0.5; nd], xkf1: vec![0.5; nd], xkfb: vec![1e-10; nd], }; bound_free_contribution(nd, sgd, &abtra, &emtra, 1, &mut output); // 检查吸收和发射系数增加 assert!((output.abso1[0] - (1.0 + sgd * abtra[0])).abs() < 1e-10); assert!((output.emis1[0] - (0.5 + sgd * emtra[0])).abs() < 1e-10); // 由于 iluctr > 0, 应该更新 abso1l 和 emis1l assert!((output.abso1l[0] - sgd * abtra[0]).abs() < 1e-10); assert!((output.emis1l[0] - sgd * emtra[0]).abs() < 1e-10); } #[test] fn test_line_contribution_primary() { let nd = 3; let prflin_ij = vec![1e-15, 2e-15, 3e-15]; let abtra_itr = vec![1e10, 2e10, 3e10]; let emtra_itr = vec![0.5e10, 1e10, 1.5e10]; let mut output = OpacflOutput { abso1: vec![1.0; nd], emis1: vec![0.5; nd], scat1: vec![0.1; nd], abso1l: vec![0.0; nd], emis1l: vec![0.0; nd], xkf: vec![0.5; nd], xkf1: vec![0.5; nd], xkfb: vec![1e-10; nd], }; line_contribution_primary(nd, &prflin_ij, &abtra_itr, &emtra_itr, &mut output); // 检查谱线贡献 for id in 0..nd { let expected_abso = 1.0 + prflin_ij[id] * abtra_itr[id]; let expected_emis = 0.5 + prflin_ij[id] * emtra_itr[id]; assert!((output.abso1[id] - expected_abso).abs() < 1e-10); assert!((output.emis1[id] - expected_emis).abs() < 1e-10); } } }