424 lines
12 KiB
Rust
424 lines
12 KiB
Rust
//! 氢线不透明度初始化过程。
|
||
//!
|
||
//! 重构自 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(¶ms);
|
||
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(¶ms);
|
||
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(¶ms);
|
||
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(¶ms);
|
||
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(¶ms);
|
||
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(¶ms);
|
||
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(¶ms);
|
||
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(¶ms);
|
||
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(¶ms);
|
||
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(¶ms);
|
||
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(¶ms_low_vaclim);
|
||
let result_high = hylset(¶ms_high_vaclim);
|
||
|
||
// 两种情况下 M10 可能有细微差异
|
||
assert_eq!(result_low.ilowh, 2);
|
||
assert_eq!(result_high.ilowh, 2);
|
||
}
|
||
}
|