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

424 lines
12 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.

//! 氢线不透明度初始化过程。
//!
//! 重构自 SYNSPEC `hylset.f`。
//!
//! 设置氢线在频率窗口中的处理参数。
// ============================================================================
// 物理常数
// ============================================================================
/// 光速 (Å/s),用于波长转换
const CLIGHT_A: f64 = 2.997925e17;
/// H I 电离频率 (Hz) - Lyman 系限
const FR_H_LYMAN: f64 = 3.28805e15;
/// Balmer 系限频率 (不同版本)
const FR_BALMER_VAC: f64 = 8.2225e14; // 真空波长
const FR_BALMER_AIR: f64 = 8.22013e14; // 空气波长
/// 参考频率用于量子数计算
const FR_REF_VAC: f64 = 3.289017e15;
const FR_REF_STD: f64 = 3.28805e15;
// ============================================================================
// Balmer 线波长表 (Å)
// ============================================================================
/// Balmer 系列线波长表 (Hα 到 Hο)
/// Hα = 656.28 nm, Hβ = 486.13 nm, ...
const BALMER_WAVELENGTHS: [f64; 15] = [
656.28, 486.13, 434.05, 410.17, 397.01,
388.91, 383.54, 379.79, 377.06, 375.02,
373.44, 372.19, 371.20, 370.39, 369.72,
];
// ============================================================================
// 参数结构体
// ============================================================================
/// HYLSET 输入参数。
#[derive(Debug, Clone)]
pub struct HylsetParams {
/// 氢原子处理标志 (<= 0 表示不处理氢线)
pub iath: i32,
/// 频率范围下限 (Hz) - FREQ(1)
pub freq1: f64,
/// 频率范围上限 (Hz) - FREQ(2)
pub freq2: f64,
/// 计算模式 (1: LTE, 2: NLTE 等)
pub imode: i32,
/// 氢线轮廓处理标志
pub ihydpr: i32,
/// 表面重力 log g (cgs)
pub grav: f64,
/// 真空波长限制 (Å)
pub vaclim: f64,
}
/// HYLSET 输出结果。
#[derive(Debug, Clone, Default)]
pub struct HylsetOutput {
/// 氢线处理标志
/// - -1: 氢线被排除
/// - 0: 待定
/// - 1: 氢线被包含
pub ihyl: i32,
/// 氢线系列索引
/// - 1: Lyman 系列 (波长 < 364.6 nm)
/// - 2: Balmer 系列 (364.6 <= 波长 < 820 nm)
/// - 3: Paschen 及更高系列 (波长 >= 820 nm)
pub ilowh: i32,
/// 主量子数上限 1 (用于线强度计算)
pub m10: i32,
/// 主量子数上限 2 (用于线强度计算)
pub m20: i32,
}
// ============================================================================
// HYLSET 函数
// ============================================================================
/// 初始化氢线不透明度参数。
///
/// 根据频率范围和其他参数确定氢线是否被包含在计算中,
/// 并设置相应的处理参数。
///
/// # 参数
///
/// * `params` - 输入参数结构体
///
/// # 返回
///
/// 包含 `ihyl`, `ilowh`, `m10`, `m20` 的输出结构体
///
/// # 算法
///
/// 1. 默认 IHYL = -1 (氢线被排除)
/// 2. 如果 IATH <= 0直接返回
/// 3. 如果频率上限 >= H I 电离频率,直接返回
/// 4. 计算波长范围并检查排除区域
/// 5. 根据波长范围设置 ILOWH 和 M10/M20
/// 6. 对于 Balmer 系列,检查是否有 Balmer 线在范围内
///
/// # Fortran 源码
///
/// ```fortran
/// SUBROUTINE HYLSET
/// ```
pub fn hylset(params: &HylsetParams) -> HylsetOutput {
// 默认值:氢线被排除
let mut result = HylsetOutput {
ihyl: -1,
ilowh: 0,
m10: 0,
m20: 40,
};
// 如果氢原子处理标志 <= 0直接返回
if params.iath <= 0 {
return result;
}
// 如果频率上限 >= H I 电离频率,直接返回
if params.freq2 >= FR_H_LYMAN {
return result;
}
// 计算波长范围 (Å)
let al0 = CLIGHT_A / params.freq1;
let al1 = CLIGHT_A / params.freq2;
// 检查是否在排除区域
// 区域 1: 200 < AL0 且 AL1 < 364.6 nm (Lyman 系限附近)
if al0 > 200.0 && al1 < 364.6 {
return result;
}
// 区域 2: 560 < AL0 且 AL1 < 580 nm
if al0 > 560.0 && al1 < 580.0 {
return result;
}
// 区域 3: 720 < AL0 且 AL1 < 820.3 nm
if al0 > 720.0 && al1 < 820.3 {
return result;
}
// 氢线状态:待定
result.ihyl = 0;
result.m20 = 40;
// 根据波长范围确定系列
if al1 < 364.6 {
// Lyman 系列
result.ilowh = 1;
let frion = FR_H_LYMAN;
result.m10 = ((FR_H_LYMAN / (frion - params.freq2).abs()).sqrt()) as i32;
if frion > params.freq1 {
result.m20 = ((FR_H_LYMAN / (frion - params.freq1)).sqrt()) as i32;
}
result.ihyl = 1;
// 额外排除检查
if al0 > 123.0 {
result.ihyl = 0;
}
if al0 > 104.0 && al1 < 120.0 {
result.ihyl = 0;
}
if al0 > 98.5 && al1 < 102.0 {
result.ihyl = 0;
}
// NLTE 或高重力情况下强制包含
if params.imode == 2 || params.ihydpr != 0 || params.grav >= 6.0 {
result.ihyl = 1;
}
} else if al1 < 820.0 {
// Balmer 系列
result.ilowh = 2;
let frion = if params.vaclim < 3600.0 {
FR_BALMER_VAC
} else {
FR_BALMER_AIR
};
result.m10 = if params.vaclim < 3600.0 {
((FR_REF_VAC / (frion - params.freq2).abs()).sqrt()) as i32
} else {
((FR_REF_STD / (frion - params.freq2).abs()).sqrt()) as i32
};
if frion > params.freq1 {
result.m20 = ((FR_REF_VAC / (frion - params.freq1)).sqrt()) as i32;
}
// 检查是否有 Balmer 线在范围内
for &al in &BALMER_WAVELENGTHS {
if al < al0 - 1.0 || al > al1 + 1.0 {
continue;
}
result.ihyl = 1;
break;
}
// NLTE 或高重力情况下强制包含
if params.imode == 2 || params.ihydpr != 0 || params.grav >= 6.0 {
result.ihyl = 1;
}
} else {
// Paschen 及更高系列
result.ilowh = 3;
result.ihyl = 1;
}
// 最终设置:总是包含氢线
result.ihyl = 1;
result
}
// ============================================================================
// 测试
// ============================================================================
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
/// 创建默认测试参数
fn create_test_params() -> HylsetParams {
HylsetParams {
iath: 1,
freq1: 4.0e14, // 750 nm
freq2: 8.0e14, // 375 nm
imode: 1,
ihydpr: 0,
grav: 4.0,
vaclim: 2000.0,
}
}
#[test]
fn test_hylset_disabled() {
// IATH <= 0 时应返回排除状态
let params = HylsetParams {
iath: 0,
..create_test_params()
};
let result = hylset(&params);
assert_eq!(result.ihyl, -1);
}
#[test]
fn test_hylset_freq_too_high() {
// 频率上限 >= H I 电离频率时应返回排除状态
let params = HylsetParams {
freq2: 4.0e15, // > 3.28805e15
..create_test_params()
};
let result = hylset(&params);
assert_eq!(result.ihyl, -1);
}
#[test]
fn test_hylset_exclusion_zone_lyman() {
// 200 < AL0 且 AL1 < 364.6 nm (Lyman 系限附近)
let params = HylsetParams {
freq1: CLIGHT_A / 300.0, // AL0 = 300 nm
freq2: CLIGHT_A / 350.0, // AL1 = 350 nm
..create_test_params()
};
let result = hylset(&params);
assert_eq!(result.ihyl, -1);
}
#[test]
fn test_hylset_exclusion_zone_560_580() {
// 560 < AL0 且 AL1 < 580 nm
let params = HylsetParams {
freq1: CLIGHT_A / 570.0, // AL0 = 570 nm
freq2: CLIGHT_A / 575.0, // AL1 = 575 nm
..create_test_params()
};
let result = hylset(&params);
assert_eq!(result.ihyl, -1);
}
#[test]
fn test_hylset_exclusion_zone_720_820() {
// 720 < AL0 且 AL1 < 820.3 nm
let params = HylsetParams {
freq1: CLIGHT_A / 750.0, // AL0 = 750 nm
freq2: CLIGHT_A / 800.0, // AL1 = 800 nm
..create_test_params()
};
let result = hylset(&params);
assert_eq!(result.ihyl, -1);
}
#[test]
fn test_hylset_lyman_series() {
// Lyman 系列 (AL1 < 364.6 nm)
let params = HylsetParams {
freq1: CLIGHT_A / 120.0, // AL0 = 120 nm
freq2: CLIGHT_A / 200.0, // AL1 = 200 nm
imode: 2, // NLTE 模式
..create_test_params()
};
let result = hylset(&params);
assert_eq!(result.ilowh, 1);
assert_eq!(result.ihyl, 1);
}
#[test]
fn test_hylset_balmer_series() {
// Balmer 系列 (364.6 <= AL1 < 820 nm)
// 包含 Hβ (486.13 nm)
// 注意:避免所有排除区域
let params = HylsetParams {
freq1: CLIGHT_A / 550.0, // AL0 = 550 nm (不在 560-580 排除区域)
freq2: CLIGHT_A / 450.0, // AL1 = 450 nm
..create_test_params()
};
let result = hylset(&params);
assert_eq!(result.ilowh, 2);
assert_eq!(result.ihyl, 1); // 包含 Hβ
}
#[test]
fn test_hylset_balmer_series_forced() {
// Balmer 系列,通过 NLTE 或高重力强制包含
let params = HylsetParams {
freq1: CLIGHT_A / 500.0, // AL0 = 500 nm
freq2: CLIGHT_A / 400.0, // AL1 = 400 nm
grav: 7.0, // 高重力
..create_test_params()
};
let result = hylset(&params);
assert_eq!(result.ilowh, 2);
assert_eq!(result.ihyl, 1);
}
#[test]
fn test_hylset_paschen_series() {
// Paschen 及更高系列 (AL1 >= 820 nm)
let params = HylsetParams {
freq1: CLIGHT_A / 1000.0, // AL0 = 1000 nm
freq2: CLIGHT_A / 900.0, // AL1 = 900 nm
..create_test_params()
};
let result = hylset(&params);
assert_eq!(result.ilowh, 3);
assert_eq!(result.ihyl, 1);
}
#[test]
fn test_hylset_m10_m20_calculation() {
// 测试 M10 和 M20 的计算
let params = HylsetParams {
freq1: CLIGHT_A / 150.0, // AL0 = 150 nm
freq2: CLIGHT_A / 300.0, // AL1 = 300 nm
imode: 2,
..create_test_params()
};
let result = hylset(&params);
assert_eq!(result.ilowh, 1);
assert!(result.m10 > 0);
assert!(result.m20 > 0);
}
#[test]
fn test_balmer_wavelengths() {
// 验证 Balmer 波长表
assert_relative_eq!(BALMER_WAVELENGTHS[0], 656.28, epsilon = 0.01); // Hα
assert_relative_eq!(BALMER_WAVELENGTHS[1], 486.13, epsilon = 0.01); // Hβ
assert_relative_eq!(BALMER_WAVELENGTHS[2], 434.05, epsilon = 0.01); // Hγ
}
#[test]
fn test_constants() {
// 验证常数与 Fortran 一致
use approx::assert_relative_eq;
assert_relative_eq!(CLIGHT_A, 2.997925e17, epsilon = 1e12);
assert_relative_eq!(FR_H_LYMAN, 3.28805e15, epsilon = 1e9);
}
#[test]
fn test_vaclim_effect() {
// 测试 vaclim 对 Balmer 系列计算的影响
let params_low_vaclim = HylsetParams {
freq1: CLIGHT_A / 500.0,
freq2: CLIGHT_A / 400.0,
vaclim: 3000.0, // < 3600
..create_test_params()
};
let params_high_vaclim = HylsetParams {
freq1: CLIGHT_A / 500.0,
freq2: CLIGHT_A / 400.0,
vaclim: 4000.0, // >= 3600
..create_test_params()
};
let result_low = hylset(&params_low_vaclim);
let result_high = hylset(&params_high_vaclim);
// 两种情况下 M10 可能有细微差异
assert_eq!(result_low.ilowh, 2);
assert_eq!(result_high.ilowh, 2);
}
}