SpectraRust/src/tlusty/math/continuum/opacfa.rs
2026-04-01 16:35:36 +08:00

564 lines
17 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.

//! 所有深度点的吸收、发射和散射系数计算 (含离子贡献)。
//!
//! 重构自 TLUSTY `opacfa.f`
//!
//! 对于给定频率点,计算所有深度点的吸收、发射和散射系数,
//! 并保存每个离子的贡献(用于计算冷却和加热率)。
//!
//! # 算法流程
//!
//! 1. 初始化电子散射贡献
//! 2. 计算频率和深度相关的基础量 (XKF, XKFB 等)
//! 3. 计算束缚-自由 (bound-free) 贡献
//! 4. 计算自由-自由 (free-free) 贡献
//! 5. 计算附加不透明度 (OPADD)
//! 6. 计算谱线贡献 (如果 icoolp != 0)
//! 7. 最终不透明度计算
use crate::tlusty::state::constants::{HK, UN};
// f2r_depends: DWNFR1, OPADD, PRD, SGMER1
// 物理常数
const C14: f64 = 2.99793e14;
const CFF1: f64 = 1.3727e-25;
// ============================================================================
// 参数结构体
// ============================================================================
/// OPACFA 输入参数
#[derive(Debug)]
pub struct OpacfaParams<'a> {
/// 频率索引 (1-indexed)
pub ij: usize,
// 控制参数
/// Compton 散射标志 (>0: 计算)
pub icompt: i32,
/// 冷却率标志 (0: 跳过谱线贡献)
pub icoolp: i32,
/// ODF 采样标志 (0: 标准模式, >0: ODF 采样)
pub ispodf: i32,
/// 双电子复合标志 (0: 无, >0: 有)
pub ifdiel: i32,
/// 附加不透明度标志 (0: 无, !=0: 有)
pub iopadd: i32,
/// PRD 标志 (>0: 调用 PRD)
pub ifprd: i32,
/// 密度缩放标志 (0: 已缩放, >0: 需缩放)
pub izscal: i32,
// 频率数据
/// 频率数组 (nfreq)
pub freq: &'a [f64],
/// Planck 函数 (nfreq)
pub bnue: &'a [f64],
// 深度数据
/// 深度点数
pub nd: usize,
/// 温度 (nd)
pub temp: &'a [f64],
/// 电子密度 (nd)
pub elec: &'a [f64],
/// 密度倒数 (nd) - 用于 izscal > 0 时
pub dens1: &'a [f64],
/// 电子散射系数 (nd) - 输入/输出
pub elscat: &'a [f64],
/// 电子散射截面 (nfreq)
pub sigec: &'a [f64],
// 表格频率阈值
/// 表格最大频率
pub frtabm: f64,
// 工作数组 (输入/输出)
/// HKT1 (nd) - HK/T
pub hkt1: &'a mut [f64],
/// XKF (nd)
pub xkf: &'a mut [f64],
/// XKF1 (nd)
pub xkf1: &'a mut [f64],
/// XKFB (nd)
pub xkfb: &'a mut [f64],
}
/// OPACFA 输出状态
#[derive(Debug)]
pub struct OpacfaOutput<'a> {
/// 吸收系数 (nd)
pub abso1: &'a mut [f64],
/// 发射系数 (nd)
pub emis1: &'a mut [f64],
/// 散射系数 (nd)
pub scat1: &'a mut [f64],
/// 累积吸收系数 (nd)
pub absot: &'a mut [f64],
/// 连续谱吸收系数 (nd) - 不含谱线
pub absoc1: &'a mut [f64],
/// 连续谱发射系数 (nd) - 不含谱线
pub emisc1: &'a mut [f64],
/// 离子吸收贡献 (mion × nd)
pub absoti: &'a mut [f64],
/// 离子发射贡献 (mion × nd)
pub emisti: &'a mut [f64],
}
/// OPACFA 配置结构体 - 包含所有需要的状态数据
#[derive(Debug)]
pub struct OpacfaState<'a> {
/// 离子数
pub nion: usize,
/// 束缚-自由跃迁数
pub ntranc: usize,
// 跃迁相关
/// 束缚-自由跃迁索引 (ntranc), 1-indexed
pub itrbf: &'a [i32],
/// 低能级索引 (mtrans), 1-indexed
pub ilow: &'a [i32],
/// 高能级索引 (mtrans), 1-indexed
pub iup: &'a [i32],
/// 频率阈值索引 (mtrans), 1-indexed
pub ifr0: &'a [i32],
/// 频率终点索引 (mtrans), 1-indexed
pub ifr1: &'a [i32],
/// Macfarlane 下沉修正索引 (mtrans), <= 0 表示无
pub mcdw: &'a [i32],
/// 阈值频率 (mtrans)
pub fr0: &'a [f64],
/// Mermerges 处理标志 (mlevel), < 0 表示需要特殊处理
pub ifwop: &'a [i32],
/// Mermerges 索引 (mlevel)
pub imrg: &'a [i32],
// 离子相关
/// 离子对应的下一个能级索引 (mion), 1-indexed
pub nnext: &'a [i32],
/// 自由-自由阈值频率 (mion)
pub ff: &'a [f64],
/// 电荷² (mion)
pub charg2: &'a [i32],
/// 自由-自由系数 SFF2 (mion × nd)
pub sff2: &'a [f64],
/// 自由-自由系数 SFF3 (mion × nd)
pub sff3: &'a [f64],
/// 自由-自由类型 (mion): 1=氢型(Gaunt=1), 2=精确Gaunt, 3=H⁻, <0=特殊
pub itype_ff: &'a [i32],
// 能级相关
/// 能级对应的元素索引 (mlevel), 1-indexed
pub iel: &'a [i32],
/// 原子操作标志 (matom), 0=正常, >0=特殊
pub iadop: &'a [i32],
/// 能级对应的原子索引 (mlevel), 1-indexed
pub iatm: &'a [i32],
// 跃迁吸收/发射系数
/// 吸收系数 (mtrans × nd)
pub abtra: &'a [f64],
/// 发射系数 (mtrans × nd)
pub emtra: &'a [f64],
// 谱线相关
/// 主谱线索引 (nfreq), 0 表示无, 1-indexed
pub ijlin: &'a [i32],
/// 重叠谱线数 (nfreq)
pub nlines: &'a [i32],
/// 谱线展开标志 (mtrans), true=展开
pub linexp: &'a [bool],
/// 谱线轮廓 (nd × nfreql 或 nd × nfreq)
pub prflin: &'a [f64],
// 截面数据
/// 束缚-自由截面 (mcross × nfreq)
pub cross_bf: &'a [f64],
/// 双电子复合截面 (mcross × nfreq × nd)
pub cross_di: &'a [f64],
}
// ============================================================================
// 主函数
// ============================================================================
/// 计算所有深度点的吸收、发射和散射系数。
///
/// 这是 OPACFA 的简化版本,只实现核心逻辑框架。
/// 完整实现需要传入更多状态参数。
///
/// # 参数
///
/// * `params` - 基本输入参数
/// * `output` - 输出数组
pub fn opacfa(params: &mut OpacfaParams, output: &mut OpacfaOutput) {
let ij = params.ij;
let ij_idx = ij - 1; // 转换为 0-indexed
let nd = params.nd;
// ========================================================================
// 1. 初始化
// ========================================================================
// Compton 散射初始化
if params.icompt > 0 {
for id in 0..nd {
let sigec_val = if ij_idx < params.sigec.len() {
params.sigec[ij_idx]
} else {
0.0
};
// ELSCAT(ID) = ELEC(ID) * SIGEC(IJ)
// 注意: elscat 是输入,这里只是使用它
}
}
// 初始化输出数组
for id in 0..nd {
output.abso1[id] = params.elscat[id];
output.emis1[id] = 0.0;
output.scat1[id] = params.elscat[id];
output.absoc1[id] = output.abso1[id];
output.emisc1[id] = 0.0;
// 初始化离子贡献
for ion in 0..(output.absoti.len() / nd) {
let idx = ion * nd + id;
output.absoti[idx] = 0.0;
output.emisti[idx] = 0.0;
}
}
// ========================================================================
// 2. 计算频率和深度相关的基础量
// ========================================================================
let fr = if ij_idx < params.freq.len() {
params.freq[ij_idx]
} else {
return; // 频率索引越界
};
let frinv = UN / fr;
let fr3inv = frinv * frinv * frinv;
let lfre = fr > params.frtabm;
for id in 0..nd {
params.hkt1[id] = HK / params.temp[id];
params.xkf[id] = (-params.hkt1[id] * fr).exp();
params.xkf1[id] = UN - params.xkf[id];
params.xkfb[id] = params.xkf[id] * params.bnue[ij_idx];
}
// ========================================================================
// 3. 束缚-自由贡献 (简化版)
// ========================================================================
// 完整实现需要:
// - 遍历 NTRANC 个束缚-自由跃迁
// - 调用 CROSS 或 CROSSD 获取截面
// - 调用 DWNFR1 处理 Macfarlane 下沉修正
// - 调用 SGMER1 处理 Mermerges 能级
// 此处留作框架,实际计算在完整版本中实现
// ========================================================================
// 4. 自由-自由贡献 (简化版)
// ========================================================================
// 完整实现需要:
// - 遍历 NION 个离子
// - 根据 ITYPE_FF 选择计算方式
// - 调用 SFFHMI (H⁻ 自由-自由)
// - 调用 FFCROS (特殊截面)
// ========================================================================
// 5. 附加不透明度 (OPADD)
// ========================================================================
// 完整实现需要调用 OPADD
// ========================================================================
// 6. 保存连续谱系数
// ========================================================================
for id in 0..nd {
output.absoc1[id] = output.abso1[id];
output.emisc1[id] = output.emis1[id];
}
// ========================================================================
// 7. 谱线贡献 (如果 icoolp != 0)
// ========================================================================
// 完整实现需要:
// - 主谱线处理
// - 重叠谱线处理
// - ODF 采样模式处理
if params.icoolp == 0 {
// 跳过谱线贡献
finalize_opacities(params, output, nd);
return;
}
// ========================================================================
// 8. 最终不透明度计算
// ========================================================================
finalize_opacities(params, output, nd);
}
/// 最终不透明度计算
fn finalize_opacities(
params: &OpacfaParams,
output: &mut OpacfaOutput,
nd: usize,
) {
let nion = output.absoti.len() / nd;
for id in 0..nd {
// 总不透明度 = 吸收 - 发射 × 激发因子
output.abso1[id] = output.abso1[id] - output.emis1[id] * params.xkf[id];
output.absoc1[id] = output.absoc1[id] - output.emisc1[id] * params.xkf[id];
// 离子贡献
for ion in 0..nion {
let idx = ion * nd + id;
output.absoti[idx] = output.absoti[idx] - output.emisti[idx] * params.xkf[id];
}
// 发射系数 × Planck 因子
output.emis1[id] = output.emis1[id] * params.xkfb[id];
output.emisc1[id] = output.emisc1[id] * params.xkfb[id];
for ion in 0..nion {
let idx = ion * nd + id;
output.emisti[idx] = output.emisti[idx] * params.xkfb[id];
}
// 累积吸收系数
output.absot[id] = output.abso1[id];
// 密度缩放
if params.izscal == 0 {
output.absot[id] = output.abso1[id] * params.dens1[id];
}
}
}
// ============================================================================
// 测试
// ============================================================================
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
fn create_test_params<'a>(
freq: &'a [f64],
bnue: &'a [f64],
temp: &'a [f64],
elec: &'a [f64],
dens1: &'a [f64],
elscat: &'a [f64],
sigec: &'a [f64],
hkt1: &'a mut [f64],
xkf: &'a mut [f64],
xkf1: &'a mut [f64],
xkfb: &'a mut [f64],
) -> OpacfaParams<'a> {
OpacfaParams {
ij: 3,
icompt: 0,
icoolp: 0,
ispodf: 0,
ifdiel: 0,
iopadd: 0,
ifprd: 0,
izscal: 1,
freq,
bnue,
nd: temp.len(),
temp,
elec,
dens1,
elscat,
sigec,
frtabm: 1e16,
hkt1,
xkf,
xkf1,
xkfb,
}
}
fn create_test_output<'a>(
abso1: &'a mut [f64],
emis1: &'a mut [f64],
scat1: &'a mut [f64],
absot: &'a mut [f64],
absoc1: &'a mut [f64],
emisc1: &'a mut [f64],
absoti: &'a mut [f64],
emisti: &'a mut [f64],
) -> OpacfaOutput<'a> {
OpacfaOutput {
abso1,
emis1,
scat1,
absot,
absoc1,
emisc1,
absoti,
emisti,
}
}
#[test]
fn test_opacfa_initialization() {
let nd = 3;
let nfreq = 5;
let nion = 2;
let freq = vec![1e14, 2e14, 3e14, 4e14, 5e14];
let bnue = vec![1e-10, 2e-10, 3e-10, 4e-10, 5e-10];
let temp = vec![5000.0, 6000.0, 7000.0];
let elec = vec![1e10, 2e10, 3e10];
let dens1 = vec![1e-15, 1e-15, 1e-15];
let elscat = vec![1e-20, 2e-20, 3e-20];
let sigec = vec![1e-24; nfreq];
let mut hkt1 = vec![0.0; nd];
let mut xkf = vec![0.0; nd];
let mut xkf1 = vec![0.0; nd];
let mut xkfb = vec![0.0; nd];
let mut abso1 = vec![0.0; nd];
let mut emis1 = vec![0.0; nd];
let mut scat1 = vec![0.0; nd];
let mut absot = vec![0.0; nd];
let mut absoc1 = vec![0.0; nd];
let mut emisc1 = vec![0.0; nd];
let mut absoti = vec![0.0; nion * nd];
let mut emisti = vec![0.0; nion * nd];
let mut params = create_test_params(
&freq, &bnue, &temp, &elec, &dens1, &elscat, &sigec,
&mut hkt1, &mut xkf, &mut xkf1, &mut xkfb,
);
let mut output = create_test_output(
&mut abso1, &mut emis1, &mut scat1, &mut absot,
&mut absoc1, &mut emisc1, &mut absoti, &mut emisti,
);
opacfa(&mut params, &mut output);
// 验证初始化
for id in 0..nd {
assert_relative_eq!(output.abso1[id], elscat[id], epsilon = 1e-30);
assert_relative_eq!(output.scat1[id], elscat[id], epsilon = 1e-30);
}
}
#[test]
fn test_opacfa_frequency_quantities() {
let nd = 2;
let nfreq = 3;
let freq = vec![1e15, 2e15, 3e15];
let bnue = vec![1e-10, 2e-10, 3e-10];
let temp = vec![5770.0, 6000.0];
let elec = vec![1e13, 2e13];
let dens1 = vec![1e-7, 1e-7];
let elscat = vec![1e-8, 2e-8];
let sigec = vec![1e-24; nfreq];
let mut hkt1 = vec![0.0; nd];
let mut xkf = vec![0.0; nd];
let mut xkf1 = vec![0.0; nd];
let mut xkfb = vec![0.0; nd];
let nion = 1;
let mut abso1 = vec![0.0; nd];
let mut emis1 = vec![0.0; nd];
let mut scat1 = vec![0.0; nd];
let mut absot = vec![0.0; nd];
let mut absoc1 = vec![0.0; nd];
let mut emisc1 = vec![0.0; nd];
let mut absoti = vec![0.0; nion * nd];
let mut emisti = vec![0.0; nion * nd];
let mut params = create_test_params(
&freq, &bnue, &temp, &elec, &dens1, &elscat, &sigec,
&mut hkt1, &mut xkf, &mut xkf1, &mut xkfb,
);
params.ij = 2; // 使用第二个频率点
let mut output = create_test_output(
&mut abso1, &mut emis1, &mut scat1, &mut absot,
&mut absoc1, &mut emisc1, &mut absoti, &mut emisti,
);
opacfa(&mut params, &mut output);
// 验证 HKT1 = HK / T
let hk = 6.626176e-27 / 1.380662e-16; // HK constant
for id in 0..nd {
let expected_hkt1 = hk / temp[id];
assert_relative_eq!(params.hkt1[id], expected_hkt1, epsilon = 1e-10);
}
// 验证 XKF1 = 1 - XKF
for id in 0..nd {
assert_relative_eq!(params.xkf1[id], 1.0 - params.xkf[id], epsilon = 1e-15);
}
}
#[test]
fn test_finalize_opacities() {
let nd = 2;
let nfreq = 3; // 需要至少3个频率点因为 ij=3
let nion = 1;
let freq = vec![1e15, 2e15, 3e15];
let bnue = vec![1e-10, 2e-10, 3e-10];
let temp = vec![5770.0, 6000.0];
let elec = vec![1e13, 2e13];
let dens1 = vec![1e-7, 1e-7];
let elscat = vec![1e-8, 2e-8];
let sigec = vec![1e-24; nfreq];
let mut hkt1 = vec![0.0; nd];
let mut xkf = vec![0.0; nd];
let mut xkf1 = vec![0.0; nd];
let mut xkfb = vec![0.0; nd];
let mut abso1 = vec![0.0; nd];
let mut emis1 = vec![0.0; nd];
let mut scat1 = vec![0.0; nd];
let mut absot = vec![0.0; nd];
let mut absoc1 = vec![0.0; nd];
let mut emisc1 = vec![0.0; nd];
let mut absoti = vec![0.0; nion * nd];
let mut emisti = vec![0.0; nion * nd];
let mut params = create_test_params(
&freq, &bnue, &temp, &elec, &dens1, &elscat, &sigec,
&mut hkt1, &mut xkf, &mut xkf1, &mut xkfb,
);
params.izscal = 1; // 不进行密度缩放
let mut output = create_test_output(
&mut abso1, &mut emis1, &mut scat1, &mut absot,
&mut absoc1, &mut emisc1, &mut absoti, &mut emisti,
);
opacfa(&mut params, &mut output);
// 验证最终计算: absot = abso1 (izscal = 1)
// 由于 emis1 = 0所以 abso1 = abso1 - 0 * xkf = 初始值 = elscat
for id in 0..nd {
assert_relative_eq!(output.absot[id], output.abso1[id], epsilon = 1e-30);
assert_relative_eq!(output.abso1[id], elscat[id], epsilon = 1e-30);
}
}
}