SpectraRust/src/synspec/math/griem.rs
2026-03-25 13:31:23 +08:00

178 lines
4.4 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.

//! Griem Stark 阻尼参数计算。
//!
//! 重构自 SYNSPEC `griem.f`
//!
//! 从输入的 Stark 宽度值计算 Stark 阻尼参数 (GAM)。
//! 输入值对应 T=5000, 10000, 20000, 40000 K
//! 以及 NE=1e16 (中性原子) 或 NE=1e17 (离子)。
/// Griem 计算所需的模型层参数。
#[derive(Debug, Clone)]
pub struct GriemParams {
/// 温度插值索引 (JT)
pub jt: usize,
/// 温度插值系数 0
pub ti0: f64,
/// 温度插值系数 1
pub ti1: f64,
/// 温度插值系数 2
pub ti2: f64,
}
/// 计算 Griem Stark 阻尼参数 GAM。
///
/// # 参数
///
/// * `t` - 温度 (K),如果 <= 0 则返回 0
/// * `ane` - 电子数密度
/// * `ion` - 电离级数 (1=中性, 2=一次电离, ...)
/// * `fr` - 频率相关因子
/// * `wgr` - Stark 宽度数组 [W@5000K, W@10000K, W@20000K, W@40000K]
/// * `params` - 模型层插值参数
///
/// # 返回值
///
/// Stark 阻尼参数 GAM
///
/// # 算法
///
/// ```text
/// GAM = (插值后的WGR) * ANE * 1e-10 * FR * 1e-10 * FR * 4.2e-14
/// 如果 ION > 1: GAM = GAM * 0.1
/// 如果 GAM < 0: GAM = 0
/// ```
pub fn griem(t: f64, ane: f64, ion: i32, fr: f64, wgr: &[f64; 4], params: &GriemParams) -> f64 {
// 温度检查
if t <= 0.0 {
return 0.0;
}
// 温度插值
let jt_idx = params.jt; // 已是 0-indexed
// 插值计算 Stark 宽度
let w_interp = params.ti0 * wgr[jt_idx]
+ params.ti1 * wgr[jt_idx - 1]
+ params.ti2 * wgr[jt_idx - 2];
// 计算 GAM
let mut gam = w_interp * ane * 1e-10 * fr * 1e-10 * fr * 4.2e-14;
// 离子的修正
if ion > 1 {
gam *= 0.1;
}
// 确保非负
gam.max(0.0)
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
fn make_params(jt: usize) -> GriemParams {
GriemParams {
jt,
ti0: 1.0,
ti1: 0.0,
ti2: 0.0,
}
}
#[test]
fn test_basic() {
let params = make_params(2);
let wgr = [1.0, 1.0, 1.0, 1.0];
let gam = griem(10000.0, 1e13, 1, 1.0, &wgr, &params);
assert!(gam >= 0.0);
assert!(gam.is_finite());
}
#[test]
fn test_zero_temperature() {
let params = make_params(2);
let wgr = [1.0, 1.0, 1.0, 1.0];
let gam = griem(0.0, 1e13, 1, 1.0, &wgr, &params);
assert_relative_eq!(gam, 0.0);
}
#[test]
fn test_negative_temperature() {
let params = make_params(2);
let wgr = [1.0, 1.0, 1.0, 1.0];
let gam = griem(-100.0, 1e13, 1, 1.0, &wgr, &params);
assert_relative_eq!(gam, 0.0);
}
#[test]
fn test_ion_correction() {
let params = make_params(2);
let wgr = [1.0, 1.0, 1.0, 1.0];
let gam_neutral = griem(10000.0, 1e13, 1, 1.0, &wgr, &params);
let gam_ion = griem(10000.0, 1e13, 2, 1.0, &wgr, &params);
// 离子的 GAM 应该是中性的 0.1 倍
assert_relative_eq!(gam_ion, 0.1 * gam_neutral, epsilon = 1e-10);
}
#[test]
fn test_density_scaling() {
let params = make_params(2);
let wgr = [1.0, 1.0, 1.0, 1.0];
let gam1 = griem(10000.0, 1e13, 1, 1.0, &wgr, &params);
let gam2 = griem(10000.0, 2e13, 1, 1.0, &wgr, &params);
// 密度翻倍GAM 应该翻倍
assert_relative_eq!(gam2, 2.0 * gam1, epsilon = 1e-10);
}
#[test]
fn test_fr_scaling() {
let params = make_params(2);
let wgr = [1.0, 1.0, 1.0, 1.0];
let gam1 = griem(10000.0, 1e13, 1, 1.0, &wgr, &params);
let gam2 = griem(10000.0, 1e13, 1, 2.0, &wgr, &params);
// FR 翻倍GAM 应该是 4 倍 (因为 FR^2)
assert_relative_eq!(gam2, 4.0 * gam1, epsilon = 1e-10);
}
#[test]
fn test_temperature_interpolation() {
let params = make_params(2);
let wgr = [1.0, 2.0, 4.0, 8.0]; // 不同的温度宽度
let gam = griem(10000.0, 1e13, 1, 1.0, &wgr, &params);
// jt=2 使用 wgr[2]=4.0
assert!(gam > 0.0);
}
#[test]
fn test_negative_result_clamped() {
let params = GriemParams {
jt: 2,
ti0: -1.0, // 负系数
ti1: 0.0,
ti2: 0.0,
};
let wgr = [1.0, 1.0, 1.0, 1.0];
let gam = griem(10000.0, 1e13, 1, 1.0, &wgr, &params);
// 负值应该被钳制为 0
assert_relative_eq!(gam, 0.0);
}
}