//! 辐射转移方程的角度积分点初始化。 //! //! 重构自 TLUSTY `RTEANG.f`。 use crate::tlusty::math::gauleg; /// RTEANG 的输入参数。 pub struct RteangParams { /// 外部辐照的角度半宽(以弧度为单位) /// 如果 WANGLE <= 0,则使用标准高斯积分 pub wangle: f64, /// 外部辐照强度数组(长度 = nfreq) pub extin: Vec, } /// RTEANG 的输出状态。 pub struct RteangOutput { /// 角度点(cos(theta)) pub amu: Vec, /// 角度权重 pub wtmu: Vec, /// 辐照角度权重 pub fmu: Vec, /// 角度点数 pub nmu: usize, /// 表面 J 积分的贡献 pub extj: Vec, /// 表面 H 积分的贡献 pub exth: Vec, } /// 初始化辐射转移方程的角度积分点。 /// /// # 参数 /// - `params`: 输入参数(wangle, extin) /// - `nmu_standard`: 标准角度点数(用于无辐照情况) /// /// # 返回 /// - `RteangOutput`: 包含角度点和权重 /// /// # 算法说明 /// - 如果 `wangle <= 0`:使用标准 NMU 点高斯积分 /// - 如果 `wangle > 0`:使用 5 点积分方案,考虑外部辐照 pub fn rteang(params: &RteangParams, nmu_standard: usize) -> RteangOutput { let nfreq = params.extin.len(); let half = 0.5_f64; let one = 1.0_f64; let pi = std::f64::consts::PI; // 5 点积分的常量 const NMU5: usize = 5; const NMU3: usize = 3; // 1/sqrt(3) const INV_SQRT3: f64 = 0.577350269189626; let x = params.wangle * half; // 初始化输出数组(最大可能的尺寸) let max_nmu = NMU5.max(nmu_standard); let mut amu = vec![0.0; max_nmu]; let mut wtmu = vec![0.0; max_nmu]; let mut fmu = vec![0.0; max_nmu]; let mut nmu: usize; let mut xj = 0.0; let mut xh = 0.0; if x <= 0.0 { // 无外部辐照:使用标准高斯积分 let (amu0, wtmu0) = gauleg(0.0, one, nmu_standard); nmu = nmu_standard; for i in 0..nmu { amu[i] = amu0[i]; wtmu[i] = wtmu0[i]; fmu[i] = 0.0; } } else { // 有外部辐照:使用特殊的 5 点积分方案 let x0 = half - x; let x1 = half + x; // 3 点高斯积分在 [-1, 1] 上 let (amu0, wtmu0) = gauleg(-one, one, NMU3); for i in 0..NMU3 { amu[i] = x0 * amu0[i] + x1; wtmu[i] = x0 * wtmu0[i]; fmu[i] = 0.0; } nmu = NMU5; // 第 4 和第 5 个角度点 let i4 = NMU3; // 0-indexed let i5 = NMU3 + 1; amu[i4] = x * (one + INV_SQRT3); amu[i5] = x * (one - INV_SQRT3); for i in NMU3..NMU5 { wtmu[i] = x; // 计算辐照角度权重 let amu_sq = amu[i] * amu[i]; let arg = (params.wangle * params.wangle - amu_sq) / (one - amu_sq); if arg > 0.0 { fmu[i] = (arg.sqrt().asin()) / pi; } else { fmu[i] = 0.0; } xj += wtmu[i] * fmu[i]; xh += wtmu[i] * amu[i] * fmu[i]; } } // 计算表面积分贡献 let mut extj = vec![0.0; nfreq]; let mut exth = vec![0.0; nfreq]; for ij in 0..nfreq { extj[ij] = xj * params.extin[ij] * half; exth[ij] = xh * params.extin[ij] * half; } // 截断数组到实际大小 amu.truncate(nmu); wtmu.truncate(nmu); fmu.truncate(nmu); RteangOutput { amu, wtmu, fmu, nmu, extj, exth, } } #[cfg(test)] mod tests { use super::*; #[test] fn test_rteang_no_irradiation() { // 无外部辐照的情况 let params = RteangParams { wangle: 0.0, extin: vec![1.0; 100], }; let result = rteang(¶ms, 3); // 应该使用 3 点高斯积分 assert_eq!(result.nmu, 3); // 检查权重和(应该接近 1) let wsum: f64 = result.wtmu.iter().sum(); assert!((wsum - 1.0).abs() < 1e-10); // 检查 fmu 全为零 for &f in &result.fmu { assert_eq!(f, 0.0); } // 检查 extj 和 exth 为零(因为 xj = xh = 0) for &e in &result.extj { assert_eq!(e, 0.0); } for &e in &result.exth { assert_eq!(e, 0.0); } } #[test] fn test_rteang_with_irradiation() { // 有外部辐照的情况 let params = RteangParams { wangle: 0.1, // 小角度 extin: vec![1.0; 100], }; let result = rteang(¶ms, 3); // 应该使用 5 点积分 assert_eq!(result.nmu, 5); // 检查 extj 和 exth 非零 assert!(result.extj[0] > 0.0); assert!(result.exth[0] > 0.0); } #[test] fn test_gauss_legendre_weights() { // 验证高斯积分权重的归一性 let (x, w) = gauleg(0.0, 1.0, 5); let sum: f64 = w.iter().sum(); assert!((sum - 1.0).abs() < 1e-14); // 验证积分 x^2 在 [0,1] 上等于 1/3 let integral: f64 = x.iter().zip(w.iter()).map(|(xi, wi)| xi * xi * wi).sum(); assert!((integral - 1.0 / 3.0).abs() < 1e-14); } }