SpectraRust/src/tlusty/math/radiative/rteang.rs
2026-03-25 18:34:41 +08:00

201 lines
5.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 辐射转移方程的角度积分点初始化。
//!
//! 重构自 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(&params, 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(&params, 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);
}
}