SpectraRust/src/math/starka.rs
2026-03-21 09:12:18 +08:00

181 lines
4.9 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.

//! 氢线 Stark 展宽近似表达式。
//!
//! 重构自 TLUSTY `starka.f`。
//!
//! 当温度或电子密度超出 Lemke 表格范围时,使用近似表达式计算氢线 Stark 轮廓。
// ============================================================================
// 常量参数
// ============================================================================
const F0: f64 = -0.5758228;
const F1: f64 = 0.4796232;
const F2: f64 = 0.07209481;
const AL: f64 = 1.26;
const SD: f64 = 0.5641895;
const SLO: f64 = -2.5;
const THRA: f64 = 1.5;
const BL1: f64 = 1.14;
const BL2: f64 = 11.4;
const SAC: f64 = 0.08;
const PISQ1: f64 = 1.0 / 1.77245385090551; // 1 / sqrt(pi)
// ============================================================================
// STARKA - Stark 展宽近似表达式
// ============================================================================
/// 计算氢线 Stark 展宽的近似表达式。
///
/// 当温度或电子密度超出 Lemke 表格范围时使用。
///
/// # 参数
///
/// - `beta` - 以 β 单位表示的波长位移 (Δλ/β)
/// - `fac` - 乘法因子 (H I 为 2.0He II 为 1.0)
/// - `adh` - 辅助参数 A = 1.5*ln(BETAD) - 1.761
/// - `betad` - 以 β 单位表示的 Doppler 宽度
/// - `divh` - Doppler 核心与 Stark 翼的分界点 (以 betad 为单位)
///
/// # 返回
///
/// Stark 轮廓值 S(β)
///
/// # Fortran 原始代码
///
/// ```fortran
/// FUNCTION STARKA(BETA,FAC)
/// ...
/// BETAD1=UN/BETAD
/// IF(ADH.GT.AL) THEN
/// XD=BETA*BETAD1
/// IF(XD.LE.DIVH) THEN
/// STARKA=EXP(-XD*XD)*BETAD1*PISQ1
/// ELSE
/// STARKA=THRA*FAC*EXP(SLO*LOG(BETA))
/// END IF
/// ELSE
/// IF(BETA.LE.BL1) THEN
/// STARKA=SAC
/// ELSE IF(BETA.LT.BL2) THEN
/// XL=LOG(BETA)
/// FL=(F0*XL+F1)*XL
/// STARKA=F2*EXP(FL)
/// ELSE
/// STARKA=THRA*FAC*EXP(SLO*LOG(BETA))
/// END IF
/// END IF
/// END
/// ```
pub fn starka(beta: f64, fac: f64, adh: f64, betad: f64, divh: f64) -> f64 {
let betad1 = 1.0 / betad;
if adh > AL {
// a > 1: Doppler 核心 + 渐近 Holtsmark 翼
let xd = beta * betad1;
if xd <= divh {
// Doppler 核心
(-xd * xd).exp() * betad1 * PISQ1
} else {
// 渐近 Holtsmark 翼
THRA * fac * beta.powf(SLO)
}
} else {
// a < 1: 经验公式
if beta <= BL1 {
SAC
} else if beta < BL2 {
let xl = beta.ln();
let fl = (F0 * xl + F1) * xl;
F2 * fl.exp()
} else {
THRA * fac * beta.powf(SLO)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_starka_doppler_core() {
// a > 1, xd <= divh: Doppler 核心
let adh = 2.0; // > AL = 1.26
let betad = 10.0;
let divh = 5.0;
let beta = 30.0; // xd = 30/10 = 3 <= 5
let result = starka(beta, 2.0, adh, betad, divh);
// 应该是 Doppler 核心值
let xd = beta / betad;
let expected = (-xd * xd).exp() / betad * PISQ1;
assert!((result - expected).abs() < 1e-10);
}
#[test]
fn test_starka_holtsmark_wing() {
// a > 1, xd > divh: Holtsmark 翼
let adh = 2.0;
let betad = 10.0;
let divh = 5.0;
let beta = 100.0; // xd = 100/10 = 10 > 5
let result = starka(beta, 2.0, adh, betad, divh);
// 应该是 Holtsmark 翼值
let expected = THRA * 2.0 * beta.powf(SLO);
assert!((result - expected).abs() < 1e-10);
}
#[test]
fn test_starka_empirical_low_beta() {
// a <= 1, beta <= BL1
let adh = 0.5; // < AL
let result = starka(0.5, 2.0, adh, 10.0, 5.0);
assert!((result - SAC).abs() < 1e-10);
}
#[test]
fn test_starka_empirical_mid_beta() {
// a <= 1, BL1 < beta < BL2
let adh = 0.5;
let beta = 5.0; // 1.14 < 5.0 < 11.4
let result = starka(beta, 2.0, adh, 10.0, 5.0);
let xl = beta.ln();
let fl = (F0 * xl + F1) * xl;
let expected = F2 * fl.exp();
assert!((result - expected).abs() < 1e-10);
}
#[test]
fn test_starka_empirical_high_beta() {
// a <= 1, beta >= BL2
let adh = 0.5;
let beta = 20.0; // > 11.4
let result = starka(beta, 2.0, adh, 10.0, 5.0);
let expected = THRA * 2.0 * beta.powf(SLO);
assert!((result - expected).abs() < 1e-10);
}
#[test]
fn test_starka_factor_difference() {
// 测试不同 fac 值的影响
let adh = 2.0;
let betad = 10.0;
let divh = 5.0;
let beta = 100.0;
let result_hi = starka(beta, 2.0, adh, betad, divh);
let result_he = starka(beta, 1.0, adh, betad, divh);
// fac 越大,结果越大
assert!(result_hi > result_he);
assert!((result_hi / result_he - 2.0).abs() < 1e-10);
}
}