feat: 添加 5 个 SYNSPEC 数学模块 (第9批)
- ispec: 谱线轮廓类型判断 - starkir: 红外 Stark 加宽 - tint: 温度积分 - voigtk: Voigt 函数 (K 系数) - wtot: He I 线总宽度 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0674b4f174
commit
03ab39eb50
202
src/math/ispec.rs
Normal file
202
src/math/ispec.rs
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
//! 特殊吸收轮廓类型判断。
|
||||||
|
//!
|
||||||
|
//! 重构自 SYNSPEC `ispec.f`
|
||||||
|
//!
|
||||||
|
//! 用于判断给定谱线是否使用特殊的预计算吸收轮廓(仅氢和氦)。
|
||||||
|
|
||||||
|
/// 特殊轮廓类型常量
|
||||||
|
pub const PROFILE_VOIGT: i32 = 0; // 普通 Voigt 轮廓
|
||||||
|
pub const PROFILE_HYDROGEN: i32 = 1; // 氢线
|
||||||
|
// He I 特殊轮廓 (2-5)
|
||||||
|
pub const PROFILE_HEI_4471: i32 = 2; // He I 447.1 nm
|
||||||
|
pub const PROFILE_HEI_4388: i32 = 3; // He I 438.8 nm
|
||||||
|
pub const PROFILE_HEI_4026: i32 = 4; // He I 402.6 nm
|
||||||
|
pub const PROFILE_HEI_4922: i32 = 5; // He I 492.2 nm
|
||||||
|
// He II 特殊轮廓 (6-24)
|
||||||
|
pub const PROFILE_HEII_1640: i32 = 6;
|
||||||
|
pub const PROFILE_HEII_3203: i32 = 7;
|
||||||
|
pub const PROFILE_HEII_2733: i32 = 8;
|
||||||
|
pub const PROFILE_HEII_2511: i32 = 9;
|
||||||
|
pub const PROFILE_HEII_2385: i32 = 10;
|
||||||
|
pub const PROFILE_HEII_2306: i32 = 11;
|
||||||
|
pub const PROFILE_HEII_2253: i32 = 12;
|
||||||
|
pub const PROFILE_HEII_4686: i32 = 13;
|
||||||
|
pub const PROFILE_HEII_4859: i32 = 14;
|
||||||
|
pub const PROFILE_HEII_4542: i32 = 15;
|
||||||
|
pub const PROFILE_HEII_4339: i32 = 16;
|
||||||
|
pub const PROFILE_HEII_4200: i32 = 17;
|
||||||
|
pub const PROFILE_HEII_4100: i32 = 18;
|
||||||
|
pub const PROFILE_HEII_4026: i32 = 19;
|
||||||
|
pub const PROFILE_HEII_3968: i32 = 20;
|
||||||
|
pub const PROFILE_HEII_3923: i32 = 21;
|
||||||
|
pub const PROFILE_HEII_10124: i32 = 22;
|
||||||
|
pub const PROFILE_HEII_6560: i32 = 23;
|
||||||
|
pub const PROFILE_HEII_5412: i32 = 24;
|
||||||
|
|
||||||
|
/// 判断谱线是否使用特殊吸收轮廓。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `iat` - 原子序数 (1=H, 2=He, ...)
|
||||||
|
/// * `ion` - 电离态 (1=中性, 2=一次电离, ...)
|
||||||
|
/// * `alam` - 波长 (nm)
|
||||||
|
/// * `ihe1pr` - He I 特殊轮廓标志 (>0 表示启用)
|
||||||
|
/// * `ihe2pr` - He II 特殊轮廓标志 (>0 表示启用)
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// * `0` - 普通 Voigt 轮廓
|
||||||
|
/// * `>0` - 特殊轮廓类型(见常量定义)
|
||||||
|
pub fn ispec(iat: i32, ion: i32, alam: f64, ihe1pr: i32, ihe2pr: i32) -> i32 {
|
||||||
|
// 非氢氦元素返回 0
|
||||||
|
if iat > 2 {
|
||||||
|
return PROFILE_VOIGT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if iat == 1 {
|
||||||
|
// 氢线
|
||||||
|
return PROFILE_HYDROGEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 氦线
|
||||||
|
if ion == 1 {
|
||||||
|
// He I
|
||||||
|
if (alam - 447.1).abs() < 0.5 && ihe1pr > 0 {
|
||||||
|
return PROFILE_HEI_4471;
|
||||||
|
}
|
||||||
|
if (alam - 438.8).abs() < 0.2 && ihe1pr > 0 {
|
||||||
|
return PROFILE_HEI_4388;
|
||||||
|
}
|
||||||
|
if (alam - 402.6).abs() < 0.2 && ihe1pr > 0 {
|
||||||
|
return PROFILE_HEI_4026;
|
||||||
|
}
|
||||||
|
if (alam - 492.2).abs() < 0.2 && ihe1pr > 0 {
|
||||||
|
return PROFILE_HEI_4922;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// He II
|
||||||
|
// 波长范围检查
|
||||||
|
if alam < 163.0 || alam > 1012.7 {
|
||||||
|
return PROFILE_VOIGT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if alam < 321.0 {
|
||||||
|
if (alam - 164.0).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_1640;
|
||||||
|
}
|
||||||
|
if (alam - 320.3).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_3203;
|
||||||
|
}
|
||||||
|
if (alam - 273.3).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_2733;
|
||||||
|
}
|
||||||
|
if (alam - 251.1).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_2511;
|
||||||
|
}
|
||||||
|
if (alam - 238.5).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_2385;
|
||||||
|
}
|
||||||
|
if (alam - 230.6).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_2306;
|
||||||
|
}
|
||||||
|
if (alam - 225.3).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_2253;
|
||||||
|
}
|
||||||
|
} else if alam < 541.0 {
|
||||||
|
if alam < 392.3 {
|
||||||
|
return PROFILE_VOIGT;
|
||||||
|
}
|
||||||
|
if (alam - 468.6).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_4686;
|
||||||
|
}
|
||||||
|
if (alam - 485.9).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_4859;
|
||||||
|
}
|
||||||
|
if (alam - 454.2).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_4542;
|
||||||
|
}
|
||||||
|
if (alam - 433.9).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_4339;
|
||||||
|
}
|
||||||
|
if (alam - 420.0).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_4200;
|
||||||
|
}
|
||||||
|
if (alam - 410.0).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_4100;
|
||||||
|
}
|
||||||
|
if (alam - 402.6).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_4026;
|
||||||
|
}
|
||||||
|
if (alam - 396.8).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_3968;
|
||||||
|
}
|
||||||
|
if (alam - 392.3).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_3923;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (alam - 1012.4).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_10124;
|
||||||
|
}
|
||||||
|
if (alam - 656.0).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_6560;
|
||||||
|
}
|
||||||
|
if (alam - 541.2).abs() < 0.2 && ihe2pr > 0 {
|
||||||
|
return PROFILE_HEII_5412;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PROFILE_VOIGT
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hydrogen() {
|
||||||
|
// 氢线总是返回 1
|
||||||
|
assert_eq!(ispec(1, 1, 656.3, 0, 0), PROFILE_HYDROGEN);
|
||||||
|
assert_eq!(ispec(1, 1, 486.1, 0, 0), PROFILE_HYDROGEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_other_elements() {
|
||||||
|
// 非氢氦元素返回 0
|
||||||
|
assert_eq!(ispec(3, 1, 670.8, 1, 1), PROFILE_VOIGT); // Li
|
||||||
|
assert_eq!(ispec(26, 1, 500.0, 1, 1), PROFILE_VOIGT); // Fe
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_he_i_lines() {
|
||||||
|
// He I 线,启用特殊轮廓
|
||||||
|
assert_eq!(ispec(2, 1, 447.1, 1, 0), PROFILE_HEI_4471);
|
||||||
|
assert_eq!(ispec(2, 1, 438.8, 1, 0), PROFILE_HEI_4388);
|
||||||
|
assert_eq!(ispec(2, 1, 402.6, 1, 0), PROFILE_HEI_4026);
|
||||||
|
assert_eq!(ispec(2, 1, 492.2, 1, 0), PROFILE_HEI_4922);
|
||||||
|
|
||||||
|
// 禁用特殊轮廓
|
||||||
|
assert_eq!(ispec(2, 1, 447.1, 0, 0), PROFILE_VOIGT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_he_ii_lines() {
|
||||||
|
// He II 线,启用特殊轮廓
|
||||||
|
assert_eq!(ispec(2, 2, 164.0, 0, 1), PROFILE_HEII_1640);
|
||||||
|
assert_eq!(ispec(2, 2, 468.6, 0, 1), PROFILE_HEII_4686);
|
||||||
|
assert_eq!(ispec(2, 2, 541.2, 0, 1), PROFILE_HEII_5412);
|
||||||
|
|
||||||
|
// 禁用特殊轮廓
|
||||||
|
assert_eq!(ispec(2, 2, 468.6, 0, 0), PROFILE_VOIGT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_he_ii_wavelength_ranges() {
|
||||||
|
// 超出波长范围
|
||||||
|
assert_eq!(ispec(2, 2, 150.0, 0, 1), PROFILE_VOIGT); // < 163
|
||||||
|
assert_eq!(ispec(2, 2, 1100.0, 0, 1), PROFILE_VOIGT); // > 1012.7
|
||||||
|
|
||||||
|
// 321-392.3 范围内没有特殊轮廓
|
||||||
|
assert_eq!(ispec(2, 2, 350.0, 0, 1), PROFILE_VOIGT);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -114,6 +114,7 @@ mod inifrt;
|
|||||||
mod inkul;
|
mod inkul;
|
||||||
mod interp;
|
mod interp;
|
||||||
mod intrp;
|
mod intrp;
|
||||||
|
mod ispec;
|
||||||
mod inthyd;
|
mod inthyd;
|
||||||
mod intlem;
|
mod intlem;
|
||||||
mod intxen;
|
mod intxen;
|
||||||
@ -251,6 +252,7 @@ mod solve;
|
|||||||
mod solves;
|
mod solves;
|
||||||
mod stark0;
|
mod stark0;
|
||||||
mod starka;
|
mod starka;
|
||||||
|
mod starkir;
|
||||||
mod steqeq;
|
mod steqeq;
|
||||||
mod temcor;
|
mod temcor;
|
||||||
mod temper;
|
mod temper;
|
||||||
@ -258,6 +260,7 @@ mod szirc;
|
|||||||
mod switch;
|
mod switch;
|
||||||
mod tiopf;
|
mod tiopf;
|
||||||
mod timing;
|
mod timing;
|
||||||
|
mod tint;
|
||||||
mod tlocal;
|
mod tlocal;
|
||||||
mod topbas;
|
mod topbas;
|
||||||
mod tdpini;
|
mod tdpini;
|
||||||
@ -274,6 +277,8 @@ mod vern26;
|
|||||||
mod visini;
|
mod visini;
|
||||||
mod voigt;
|
mod voigt;
|
||||||
mod voigte;
|
mod voigte;
|
||||||
|
mod voigtk;
|
||||||
|
mod wtot;
|
||||||
mod wn;
|
mod wn;
|
||||||
mod wnstor;
|
mod wnstor;
|
||||||
mod xk2dop;
|
mod xk2dop;
|
||||||
@ -409,6 +414,7 @@ pub use inifrt::{inifrt, InifrtParams, InifrtOutput};
|
|||||||
pub use inkul::{inkul, inkul_pure, InkulParams, InkulOutput, ColKur, Lined, LineRecord};
|
pub use inkul::{inkul, inkul_pure, InkulParams, InkulOutput, ColKur, Lined, LineRecord};
|
||||||
pub use interp::interp;
|
pub use interp::interp;
|
||||||
pub use intrp::{intrp, intrp_to_vec};
|
pub use intrp::{intrp, intrp_to_vec};
|
||||||
|
pub use ispec::{ispec, PROFILE_VOIGT, PROFILE_HYDROGEN};
|
||||||
pub use inthyd::inthyd;
|
pub use inthyd::inthyd;
|
||||||
pub use intlem::intlem;
|
pub use intlem::intlem;
|
||||||
pub use intxen::intxen;
|
pub use intxen::intxen;
|
||||||
@ -569,6 +575,7 @@ pub use tabint::{tabint, IntCff, OpacTable, TabintParams};
|
|||||||
pub use taufr1::{taufr1, Taufr1Params, Taufr1Result};
|
pub use taufr1::{taufr1, Taufr1Params, Taufr1Result};
|
||||||
pub use stark0::stark0;
|
pub use stark0::stark0;
|
||||||
pub use starka::starka;
|
pub use starka::starka;
|
||||||
|
pub use starkir::starkir;
|
||||||
pub use steqeq::{steqeq_pure, SteqeqParams, SteqeqOutput, SteqeqConfig, MAX_LEVEL};
|
pub use steqeq::{steqeq_pure, SteqeqParams, SteqeqOutput, SteqeqConfig, MAX_LEVEL};
|
||||||
pub use temcor::{temcor_pure, TemcorConfig, TemcorParams, TemcorOutput, TemcorDepthResult,
|
pub use temcor::{temcor_pure, TemcorConfig, TemcorParams, TemcorOutput, TemcorDepthResult,
|
||||||
format_temcor_line};
|
format_temcor_line};
|
||||||
@ -577,6 +584,7 @@ pub use szirc::szirc;
|
|||||||
pub use switch::{switch_init, switch_update, SwitchInitParams, SwitchUpdateParams, SwitchOutput, format_crsw_message};
|
pub use switch::{switch_init, switch_update, SwitchInitParams, SwitchUpdateParams, SwitchOutput, format_crsw_message};
|
||||||
pub use tiopf::tiopf;
|
pub use tiopf::tiopf;
|
||||||
pub use timing::{timing, TimingParams, TimingOutput, TimingMode, format_timing_message, reset_timer};
|
pub use timing::{timing, TimingParams, TimingOutput, TimingMode, format_timing_message, reset_timer};
|
||||||
|
pub use tint::{tint, TintResult};
|
||||||
pub use tlocal::{
|
pub use tlocal::{
|
||||||
tlocal, TlocalConfig, TlocalFactrs, TlocalFlxaux, TlocalModelState, TlocalParams,
|
tlocal, TlocalConfig, TlocalFactrs, TlocalFlxaux, TlocalModelState, TlocalParams,
|
||||||
};
|
};
|
||||||
@ -594,6 +602,8 @@ pub use vern26::vern26;
|
|||||||
pub use visini::{visini, VisiniParams, VisiniOutput};
|
pub use visini::{visini, VisiniParams, VisiniOutput};
|
||||||
pub use voigt::voigt;
|
pub use voigt::voigt;
|
||||||
pub use voigte::voigte;
|
pub use voigte::voigte;
|
||||||
|
pub use voigtk::{voigtk, MVOI};
|
||||||
|
pub use wtot::{wtot, LINE_4471, LINE_4387, LINE_4026, LINE_4922};
|
||||||
pub use wn::wn;
|
pub use wn::wn;
|
||||||
pub use wnstor::wnstor;
|
pub use wnstor::wnstor;
|
||||||
pub use xk2dop::xk2dop;
|
pub use xk2dop::xk2dop;
|
||||||
|
|||||||
136
src/math/starkir.rs
Normal file
136
src/math/starkir.rs
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
//! Stark 加宽轮廓计算(改进版)。
|
||||||
|
//!
|
||||||
|
//! 重构自 SYNSPEC `starkir.f`
|
||||||
|
|
||||||
|
use crate::math::eint;
|
||||||
|
|
||||||
|
/// 物理常量
|
||||||
|
const PI: f64 = 3.14159265;
|
||||||
|
const PI2: f64 = 2.0 * PI;
|
||||||
|
const OS0: f64 = 0.026564;
|
||||||
|
const RYD: f64 = 3.28805e15;
|
||||||
|
const CL: f64 = 2.997925e10;
|
||||||
|
const Y2CON: f64 = PI * PI * 0.5 / OS0 / CL;
|
||||||
|
const HK: f64 = 4.79928144e-11;
|
||||||
|
|
||||||
|
/// 计算 Stark 加宽轮廓(改进版)。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `ii` - 下能级主量子数
|
||||||
|
/// * `jj` - 上能级主量子数
|
||||||
|
/// * `t` - 温度 (K)
|
||||||
|
/// * `ane` - 电子密度
|
||||||
|
/// * `beta` - Doppler 宽度参数
|
||||||
|
/// * `dbeta` - 波长相关系数 (来自 COMMON/AUXHYD/)
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// Stark 加宽轮廓值
|
||||||
|
///
|
||||||
|
/// # 备注
|
||||||
|
///
|
||||||
|
/// 这是用于氢线 Stark 加宽的改进算法。
|
||||||
|
/// 使用了量子力学微扰理论和等离子体效应的组合。
|
||||||
|
pub fn starkir(ii: i32, jj: i32, t: f64, ane: f64, beta: f64, dbeta: f64) -> f64 {
|
||||||
|
let del = beta / dbeta;
|
||||||
|
let hkt = HK / t;
|
||||||
|
let xii = ii as f64;
|
||||||
|
let xjj = jj as f64;
|
||||||
|
let xx = xii / xjj;
|
||||||
|
let dd = 2.0 * xjj * RYD / del;
|
||||||
|
let y1 = xjj * del * 0.5 * hkt;
|
||||||
|
let y2 = Y2CON * del * del / ane;
|
||||||
|
|
||||||
|
// 计算统计因子 QSTAT
|
||||||
|
let y1_sq = y1 * y1;
|
||||||
|
let qstat = 1.5 + 0.5 * (y1_sq - 1.384) / (y1_sq + 1.384);
|
||||||
|
|
||||||
|
let mut qimpa = 0.0;
|
||||||
|
|
||||||
|
// 计算 QIMPA
|
||||||
|
if !(y1 > 8.0 || y1 >= y2) {
|
||||||
|
let exy2 = if y2 <= 8.0 {
|
||||||
|
let (e1, _, _) = eint(y2);
|
||||||
|
e1
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
let (e1_y1, _, _) = eint(y1);
|
||||||
|
qimpa = 1.438 * (y1 * (1.0 - xx)).sqrt()
|
||||||
|
* (0.4 * (-y1).exp() + e1_y1 - 0.5 * exy2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算轮廓
|
||||||
|
let (prof, ratio) = if beta <= 20.0 {
|
||||||
|
let prof = 8.0 / (80.0 + beta * beta * beta);
|
||||||
|
let ratio = qstat + qimpa;
|
||||||
|
(prof, ratio)
|
||||||
|
} else {
|
||||||
|
let prof = 1.5 / beta / beta / beta.sqrt();
|
||||||
|
let dioi = PI2 * 1.48e-25 * dd * ane
|
||||||
|
* (dd.sqrt() * (1.3 * qstat + 0.3 * qimpt()) - 3.9 * RYD * hkt);
|
||||||
|
let ratio = qstat * (1.0 + dioi).min(1.25) + qimpa;
|
||||||
|
(prof, ratio)
|
||||||
|
};
|
||||||
|
|
||||||
|
prof * ratio
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 计算 QIMPT(简化版)。
|
||||||
|
///
|
||||||
|
/// 这是一个占位函数,在完整实现中应该从其他模块获取。
|
||||||
|
fn qimpt() -> f64 {
|
||||||
|
// TODO: 这个值在原始代码中没有直接定义
|
||||||
|
// 需要进一步研究 SYNSPEC 源码来确定正确的实现
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_starkir_basic() {
|
||||||
|
// 基本测试:H-alpha 线 (n=3 -> n=2)
|
||||||
|
let result = starkir(2, 3, 10000.0, 1e13, 5.0, 1.0);
|
||||||
|
assert!(result.is_finite());
|
||||||
|
assert!(result > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_starkir_beta_small() {
|
||||||
|
// beta <= 20 的情况
|
||||||
|
let result = starkir(2, 3, 10000.0, 1e13, 10.0, 1.0);
|
||||||
|
assert!(result.is_finite());
|
||||||
|
assert!(result > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_starkir_beta_large() {
|
||||||
|
// beta > 20 的情况
|
||||||
|
let result = starkir(2, 3, 10000.0, 1e13, 50.0, 1.0);
|
||||||
|
assert!(result.is_finite());
|
||||||
|
assert!(result > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_starkir_y1_large() {
|
||||||
|
// y1 > 8 的情况(跳过 qimpa 计算)
|
||||||
|
let result = starkir(2, 3, 1000.0, 1e10, 5.0, 0.001);
|
||||||
|
assert!(result.is_finite());
|
||||||
|
assert!(result > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_starkir_different_quantum_numbers() {
|
||||||
|
// 测试不同量子数
|
||||||
|
let r1 = starkir(1, 2, 10000.0, 1e13, 5.0, 1.0); // Lyman-alpha
|
||||||
|
let r2 = starkir(3, 4, 10000.0, 1e13, 5.0, 1.0); // Paschen-alpha
|
||||||
|
let r3 = starkir(2, 4, 10000.0, 1e13, 5.0, 1.0); // H-beta
|
||||||
|
|
||||||
|
assert!(r1.is_finite());
|
||||||
|
assert!(r2.is_finite());
|
||||||
|
assert!(r3.is_finite());
|
||||||
|
}
|
||||||
|
}
|
||||||
123
src/math/tint.rs
Normal file
123
src/math/tint.rs
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
//! 温度对数插值系数计算。
|
||||||
|
//!
|
||||||
|
//! 重构自 SYNSPEC `tint.f`
|
||||||
|
//!
|
||||||
|
//! 将温度插值到标准值 5000, 10000, 20000, 40000 K 的对数插值系数。
|
||||||
|
|
||||||
|
/// 标准温度的对数值 (log10)
|
||||||
|
const TT: [f64; 4] = [3.699, 4.000, 4.301, 4.602];
|
||||||
|
// 对应: 5000K, 10000K, 20000K, 40000K
|
||||||
|
|
||||||
|
/// 温度插值结果
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TintResult {
|
||||||
|
/// 插值区间索引 (3 或 4)
|
||||||
|
pub jt: Vec<i32>,
|
||||||
|
/// 插值系数 0
|
||||||
|
pub ti0: Vec<f64>,
|
||||||
|
/// 插值系数 1
|
||||||
|
pub ti1: Vec<f64>,
|
||||||
|
/// 插值系数 2
|
||||||
|
pub ti2: Vec<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 计算温度对数插值系数。
|
||||||
|
///
|
||||||
|
/// 将温度值插值到标准温度点 5000, 10000, 20000, 40000 K。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `temp` - 温度数组 (K)
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// 包含插值系数的 `TintResult` 结构体
|
||||||
|
pub fn tint(temp: &[f64]) -> TintResult {
|
||||||
|
let nd = temp.len();
|
||||||
|
let mut jt = vec![0i32; nd];
|
||||||
|
let mut ti0 = vec![0.0f64; nd];
|
||||||
|
let mut ti1 = vec![0.0f64; nd];
|
||||||
|
let mut ti2 = vec![0.0f64; nd];
|
||||||
|
|
||||||
|
for id in 0..nd {
|
||||||
|
let t = temp[id].log10();
|
||||||
|
|
||||||
|
// 确定插值区间
|
||||||
|
let j = if t > TT[2] { 4 } else { 3 }; // 1-indexed in Fortran, 3 or 4
|
||||||
|
jt[id] = j;
|
||||||
|
|
||||||
|
// Fortran 索引: J-2, J-1, J (对应 Rust 索引 j-3, j-2, j-1)
|
||||||
|
let j_idx = j as usize;
|
||||||
|
let tt_jm2 = TT[j_idx - 3]; // TT(J-2)
|
||||||
|
let tt_jm1 = TT[j_idx - 2]; // TT(J-1)
|
||||||
|
let tt_j = TT[j_idx - 1]; // TT(J)
|
||||||
|
|
||||||
|
let x = (tt_j - tt_jm1) * (tt_j - tt_jm2) * (tt_jm1 - tt_jm2);
|
||||||
|
|
||||||
|
ti0[id] = (t - tt_jm2) * (t - tt_jm1) * (tt_jm1 - tt_jm2) / x;
|
||||||
|
ti1[id] = (t - tt_jm2) * (tt_j - t) * (tt_j - tt_jm2) / x;
|
||||||
|
ti2[id] = (t - tt_jm1) * (t - tt_j) * (tt_j - tt_jm1) / x;
|
||||||
|
}
|
||||||
|
|
||||||
|
TintResult { jt, ti0, ti1, ti2 }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tint_basic() {
|
||||||
|
let temp = vec![10000.0, 15000.0, 25000.0];
|
||||||
|
let result = tint(&temp);
|
||||||
|
|
||||||
|
assert_eq!(result.jt.len(), 3);
|
||||||
|
assert_eq!(result.ti0.len(), 3);
|
||||||
|
assert_eq!(result.ti1.len(), 3);
|
||||||
|
assert_eq!(result.ti2.len(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tint_at_10000k() {
|
||||||
|
// 正好在 10000K (log10 = 4.000)
|
||||||
|
let temp = vec![10000.0];
|
||||||
|
let result = tint(&temp);
|
||||||
|
|
||||||
|
// JT 应该是 3 (因为 log10(10000) = 4.000 不大于 TT[2] = 4.301)
|
||||||
|
assert_eq!(result.jt[0], 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tint_at_30000k() {
|
||||||
|
// 在 20000K 和 40000K 之间
|
||||||
|
let temp = vec![30000.0];
|
||||||
|
let result = tint(&temp);
|
||||||
|
|
||||||
|
// JT 应该是 4 (因为 log10(30000) ≈ 4.477 > TT[2] = 4.301)
|
||||||
|
assert_eq!(result.jt[0], 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tint_coefficients_sum() {
|
||||||
|
// 插值系数应该满足某种关系
|
||||||
|
let temp = vec![15000.0];
|
||||||
|
let result = tint(&temp);
|
||||||
|
|
||||||
|
// 对于二次插值,系数有特定关系
|
||||||
|
// 这里只验证它们是有限值
|
||||||
|
assert!(result.ti0[0].is_finite());
|
||||||
|
assert!(result.ti1[0].is_finite());
|
||||||
|
assert!(result.ti2[0].is_finite());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tint_multiple_depths() {
|
||||||
|
let temp = vec![5000.0, 10000.0, 20000.0, 40000.0];
|
||||||
|
let result = tint(&temp);
|
||||||
|
|
||||||
|
assert_eq!(result.jt.len(), 4);
|
||||||
|
for i in 0..4 {
|
||||||
|
assert!(result.jt[i] == 3 || result.jt[i] == 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
157
src/math/voigtk.rs
Normal file
157
src/math/voigtk.rs
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
//! Voigt 函数(Kurucz 版本)。
|
||||||
|
//!
|
||||||
|
//! 重构自 SYNSPEC `voigtk.f`
|
||||||
|
//!
|
||||||
|
//! 基于 Kurucz (Computational Astrophysics) 的算法。
|
||||||
|
|
||||||
|
/// Voigt 表格大小
|
||||||
|
pub const MVOI: usize = 2001;
|
||||||
|
|
||||||
|
/// 常量
|
||||||
|
const ONE: f64 = 1.0;
|
||||||
|
const THREE: f64 = 3.0;
|
||||||
|
const TEN: f64 = 10.0;
|
||||||
|
const FIFTN: f64 = 15.0;
|
||||||
|
const TWOH: f64 = 200.0;
|
||||||
|
const C14142: f64 = 1.4142;
|
||||||
|
const C11283: f64 = 1.12838;
|
||||||
|
const C15: f64 = 1.5;
|
||||||
|
const C32: f64 = 3.2;
|
||||||
|
const C05642: f64 = 0.5642;
|
||||||
|
const C79788: f64 = 0.79788;
|
||||||
|
const C02: f64 = 0.2;
|
||||||
|
const C14: f64 = 1.4;
|
||||||
|
const C37613: f64 = 0.37613;
|
||||||
|
const C23: f64 = 2.0 / 3.0;
|
||||||
|
const CV1: f64 = -0.122727278;
|
||||||
|
const CV2: f64 = 0.532770573;
|
||||||
|
const CV3: f64 = -0.96284325;
|
||||||
|
const CV4: f64 = 0.979895032;
|
||||||
|
|
||||||
|
/// Voigt 函数(Kurucz 版本)。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `a` - 阻尼参数
|
||||||
|
/// * `v` - 频率偏移(Doppler 宽度单位)
|
||||||
|
/// * `h0tab` - 预计算表格 H0
|
||||||
|
/// * `h1tab` - 预计算表格 H1
|
||||||
|
/// * `h2tab` - 预计算表格 H2
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// Voigt 函数值
|
||||||
|
pub fn voigtk(a: f64, v: f64, h0tab: &[f64; MVOI], h1tab: &[f64; MVOI], h2tab: &[f64; MVOI]) -> f64 {
|
||||||
|
let iv = (v * TWOH + C15) as usize;
|
||||||
|
let iv = iv.min(MVOI - 1); // 防止越界
|
||||||
|
|
||||||
|
if a < C02 {
|
||||||
|
if v <= TEN {
|
||||||
|
return (h2tab[iv] * a + h1tab[iv]) * a + h0tab[iv];
|
||||||
|
} else {
|
||||||
|
return C05642 * a / (v * v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if a > C14 || a + v > C32 {
|
||||||
|
// 大 A 或大 V 的情况
|
||||||
|
let aa = a * a;
|
||||||
|
let vv = v * v;
|
||||||
|
let u = (aa + vv) * C14142;
|
||||||
|
let uu = u * u;
|
||||||
|
return ((((aa - TEN * vv) * aa * THREE + FIFTN * vv * vv) / uu + THREE * vv - aa) / uu
|
||||||
|
+ ONE)
|
||||||
|
* a
|
||||||
|
* C79788
|
||||||
|
/ u;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 中等 A 和 V 的情况
|
||||||
|
let vv = v * v;
|
||||||
|
let hh1 = h1tab[iv] + h0tab[iv] * C11283;
|
||||||
|
let hh2 = h2tab[iv] + hh1 * C11283 - h0tab[iv];
|
||||||
|
let hh3 = (ONE - h2tab[iv]) * C37613 - hh1 * C23 * vv + hh2 * C11283;
|
||||||
|
let hh4 = (THREE * hh3 - hh1) * C37613 + h0tab[iv] * C23 * vv * vv;
|
||||||
|
|
||||||
|
(((((hh4 * a + hh3) * a + hh2) * a + hh1) * a + h0tab[iv])
|
||||||
|
* (((CV1 * a + CV2) * a + CV3) * a + CV4))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use approx::assert_relative_eq;
|
||||||
|
|
||||||
|
const FRAC_1_SQRT_PI: f64 = 0.5641895835477563;
|
||||||
|
|
||||||
|
// 创建简单的测试表格(实际使用时需要预计算)
|
||||||
|
fn create_test_tables() -> ([f64; MVOI], [f64; MVOI], [f64; MVOI]) {
|
||||||
|
let mut h0 = [0.0; MVOI];
|
||||||
|
let mut h1 = [0.0; MVOI];
|
||||||
|
let mut h2 = [0.0; MVOI];
|
||||||
|
|
||||||
|
// 简单初始化:H(0,v) = 1/sqrt(pi) * exp(-v^2)
|
||||||
|
for i in 0..MVOI {
|
||||||
|
let v = (i as f64 - C15) / TWOH;
|
||||||
|
h0[i] = (-v * v).exp() / FRAC_1_SQRT_PI;
|
||||||
|
h1[i] = -2.0 * v * h0[i]; // H'(0,v)
|
||||||
|
h2[i] = (4.0 * v * v - 2.0) * h0[i]; // H''(0,v)
|
||||||
|
}
|
||||||
|
|
||||||
|
(h0, h1, h2)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_voigtk_basic() {
|
||||||
|
let (h0, h1, h2) = create_test_tables();
|
||||||
|
|
||||||
|
// 测试小 A 情况
|
||||||
|
let result = voigtk(0.1, 1.0, &h0, &h1, &h2);
|
||||||
|
assert!(result.is_finite());
|
||||||
|
assert!(result > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_voigtk_large_v() {
|
||||||
|
let (h0, h1, h2) = create_test_tables();
|
||||||
|
|
||||||
|
// 大 V 情况 (v > 10)
|
||||||
|
let result = voigtk(0.1, 15.0, &h0, &h1, &h2);
|
||||||
|
assert!(result.is_finite());
|
||||||
|
assert!(result > 0.0);
|
||||||
|
|
||||||
|
// 应该近似 C05642 * a / v^2
|
||||||
|
let expected = C05642 * 0.1 / (15.0 * 15.0);
|
||||||
|
assert_relative_eq!(result, expected, epsilon = 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_voigtk_large_a() {
|
||||||
|
let (h0, h1, h2) = create_test_tables();
|
||||||
|
|
||||||
|
// 大 A 情况 (a > 1.4)
|
||||||
|
let result = voigtk(2.0, 1.0, &h0, &h1, &h2);
|
||||||
|
assert!(result.is_finite());
|
||||||
|
assert!(result > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_voigtk_medium() {
|
||||||
|
let (h0, h1, h2) = create_test_tables();
|
||||||
|
|
||||||
|
// 中等 A 和 V 情况 (0.2 <= a <= 1.4, a+v <= 3.2)
|
||||||
|
let result = voigtk(0.5, 1.0, &h0, &h1, &h2);
|
||||||
|
assert!(result.is_finite());
|
||||||
|
assert!(result > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_voigtk_zero_v() {
|
||||||
|
let (h0, h1, h2) = create_test_tables();
|
||||||
|
|
||||||
|
// v = 0 的情况
|
||||||
|
let result = voigtk(0.5, 0.0, &h0, &h1, &h2);
|
||||||
|
assert!(result.is_finite());
|
||||||
|
assert!(result > 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
127
src/math/wtot.rs
Normal file
127
src/math/wtot.rs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
//! He I 线 Stark 加宽总宽度计算。
|
||||||
|
//!
|
||||||
|
//! 重构自 SYNSPEC `wtot.f`
|
||||||
|
//!
|
||||||
|
//! 计算四条 He I 线的总(电子 + 离子)碰撞 Stark 宽度。
|
||||||
|
//! 基于 Griem (1974) 和 Barnard, Cooper, Smith (1974) JQSRT 14, 1025。
|
||||||
|
|
||||||
|
/// ALPH0 系数 (4 x 4 矩阵,按列存储)
|
||||||
|
/// 行: 温度区间 (对应 tint 的 JT = 2,3,4,5 但这里用 1,2,3,4)
|
||||||
|
/// 列: 谱线 (4471, 4387, 4026, 4922)
|
||||||
|
const ALPH0: [[f64; 4]; 4] = [
|
||||||
|
[0.107, 0.119, 0.134, 0.154],
|
||||||
|
[0.206, 0.235, 0.272, 0.317],
|
||||||
|
[0.172, 0.193, 0.218, 0.249],
|
||||||
|
[0.121, 0.136, 0.157, 0.184],
|
||||||
|
];
|
||||||
|
|
||||||
|
/// W0 系数 (4 x 4 矩阵)
|
||||||
|
const W0: [[f64; 4]; 4] = [
|
||||||
|
[1.460, 1.269, 1.079, 0.898],
|
||||||
|
[6.130, 5.150, 4.240, 3.450],
|
||||||
|
[4.040, 3.490, 2.960, 2.470],
|
||||||
|
[2.312, 1.963, 1.624, 1.315],
|
||||||
|
];
|
||||||
|
|
||||||
|
/// 谱线波长 (Angstroms)
|
||||||
|
const ALAM0: [f64; 4] = [4471.50, 4387.93, 4026.20, 4921.93];
|
||||||
|
|
||||||
|
/// 谱线索引常量
|
||||||
|
pub const LINE_4471: usize = 0;
|
||||||
|
pub const LINE_4387: usize = 1;
|
||||||
|
pub const LINE_4026: usize = 2;
|
||||||
|
pub const LINE_4922: usize = 3;
|
||||||
|
|
||||||
|
/// 计算 He I 线 Stark 加宽总宽度。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `t` - 温度 (K)
|
||||||
|
/// * `ane` - 电子密度
|
||||||
|
/// * `jt` - 温度插值区间索引 (3 或 4)
|
||||||
|
/// * `ti0` - 插值系数 0
|
||||||
|
/// * `ti1` - 插值系数 1
|
||||||
|
/// * `ti2` - 插值系数 2
|
||||||
|
/// * `iline` - 谱线索引 (0=4471, 1=4387, 2=4026, 3=4922)
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// Stark 宽度 (Angstroms)
|
||||||
|
pub fn wtot(t: f64, ane: f64, jt: i32, ti0: f64, ti1: f64, ti2: f64, iline: usize) -> f64 {
|
||||||
|
// Fortran 索引: I = JT (值 3 或 4), 访问 ALPH0(I,*), ALPH0(I-1,*), ALPH0(I-2,*)
|
||||||
|
// Rust 索引: i = JT - 1 (值 2 或 3), 访问 ALPH0[i], ALPH0[i-1], ALPH0[i-2]
|
||||||
|
let i = (jt - 1) as usize;
|
||||||
|
|
||||||
|
// 计算 ALPHA
|
||||||
|
let alpha = (ti0 * ALPH0[i][iline]
|
||||||
|
+ ti1 * ALPH0[i - 1][iline]
|
||||||
|
+ ti2 * ALPH0[i - 2][iline])
|
||||||
|
* (ane * 1e-13).powf(0.25);
|
||||||
|
|
||||||
|
// 计算 WE (电子宽度)
|
||||||
|
let we = (ti0 * W0[i][iline] + ti1 * W0[i - 1][iline] + ti2 * W0[i - 2][iline]) * ane * 1e-16;
|
||||||
|
|
||||||
|
// 计算静态离子展宽参数
|
||||||
|
let f0 = 1.884e19 / ALAM0[iline] / ALAM0[iline];
|
||||||
|
let sig = (4.32e-5 * we / t.sqrt() * f0 / ane.powf(0.3333)).powf(0.3333);
|
||||||
|
|
||||||
|
// 总宽度
|
||||||
|
we * (1.0 + 1.36 / sig * alpha.powf(0.8889))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use approx::assert_relative_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_wtot_basic() {
|
||||||
|
// 基本测试
|
||||||
|
let result = wtot(10000.0, 1e13, 3, 0.5, 0.3, 0.2, LINE_4471);
|
||||||
|
assert!(result.is_finite());
|
||||||
|
assert!(result > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_wtot_all_lines() {
|
||||||
|
// 测试所有四条线
|
||||||
|
let jt = 3;
|
||||||
|
let ti0 = 0.5;
|
||||||
|
let ti1 = 0.3;
|
||||||
|
let ti2 = 0.2;
|
||||||
|
|
||||||
|
for iline in 0..4 {
|
||||||
|
let result = wtot(10000.0, 1e13, jt, ti0, ti1, ti2, iline);
|
||||||
|
assert!(result.is_finite(), "Line {} failed", iline);
|
||||||
|
assert!(result > 0.0, "Line {} should be positive", iline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_wtot_jt4() {
|
||||||
|
// 测试 JT=4 的情况
|
||||||
|
let result = wtot(20000.0, 1e14, 4, 0.4, 0.4, 0.2, LINE_4471);
|
||||||
|
assert!(result.is_finite());
|
||||||
|
assert!(result > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_wtot_different_densities() {
|
||||||
|
// 测试不同电子密度
|
||||||
|
let densities = [1e12, 1e13, 1e14, 1e15];
|
||||||
|
for &ane in &densities {
|
||||||
|
let result = wtot(10000.0, ane, 3, 0.5, 0.3, 0.2, LINE_4471);
|
||||||
|
assert!(result.is_finite(), "Density {} failed", ane);
|
||||||
|
assert!(result > 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_wtot_line_wavelengths() {
|
||||||
|
// 验证波长值
|
||||||
|
assert_relative_eq!(ALAM0[LINE_4471], 4471.50, epsilon = 1e-2);
|
||||||
|
assert_relative_eq!(ALAM0[LINE_4387], 4387.93, epsilon = 1e-2);
|
||||||
|
assert_relative_eq!(ALAM0[LINE_4026], 4026.20, epsilon = 1e-2);
|
||||||
|
assert_relative_eq!(ALAM0[LINE_4922], 4921.93, epsilon = 1e-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user