201 lines
5.2 KiB
Rust
201 lines
5.2 KiB
Rust
//! 辐射转移方程的角度积分点初始化。
|
||
//!
|
||
//! 重构自 TLUSTY `RTEANG.f`。
|
||
|
||
use crate::tlusty::math::gauleg;
|
||
|
||
/// RTEANG 的输入参数。
|
||
pub struct RteangParams {
|
||
/// 外部辐照的角度半宽(以弧度为单位)
|
||
/// 如果 WANGLE <= 0,则使用标准高斯积分
|
||
pub wangle: f64,
|
||
/// 外部辐照强度数组(长度 = nfreq)
|
||
pub extin: Vec<f64>,
|
||
}
|
||
|
||
/// RTEANG 的输出状态。
|
||
pub struct RteangOutput {
|
||
/// 角度点(cos(theta))
|
||
pub amu: Vec<f64>,
|
||
/// 角度权重
|
||
pub wtmu: Vec<f64>,
|
||
/// 辐照角度权重
|
||
pub fmu: Vec<f64>,
|
||
/// 角度点数
|
||
pub nmu: usize,
|
||
/// 表面 J 积分的贡献
|
||
pub extj: Vec<f64>,
|
||
/// 表面 H 积分的贡献
|
||
pub exth: Vec<f64>,
|
||
}
|
||
|
||
/// 初始化辐射转移方程的角度积分点。
|
||
///
|
||
/// # 参数
|
||
/// - `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);
|
||
}
|
||
}
|