299 lines
8.0 KiB
Rust
299 lines
8.0 KiB
Rust
//! 氢线系列的 ODF(Opacity Distribution Function)计算。
|
||
//!
|
||
//! 重构自 TLUSTY `odfhyd.f`
|
||
//!
|
||
//! 计算氢线系列的不透明度分布函数。
|
||
|
||
use crate::tlusty::math::divstr;
|
||
use crate::tlusty::math::indexx;
|
||
use crate::tlusty::math::odfhst;
|
||
use crate::tlusty::state::constants::{HALF, TWO, UN};
|
||
use crate::tlusty::state::model::StrAux;
|
||
|
||
// 物理常量
|
||
/// 玻尔兹曼常数 / 氢质量
|
||
const CDOP: f64 = 2.0 * 1.38054e-16 / 1.6726e-24;
|
||
/// 光速 (Angstrom/s)
|
||
const CA: f64 = 2.997925e18;
|
||
/// 转换因子
|
||
const CCM: f64 = CA / 1.0e8;
|
||
/// 氢的 Rydberg 频率
|
||
const FRH: f64 = 3.28805e15;
|
||
/// Rydberg 波长 (Angstrom)
|
||
const RYDEL: f64 = 911.764;
|
||
/// 2/3
|
||
const TTW: f64 = 2.0 / 3.0;
|
||
/// Stark 加宽常量
|
||
const C00: f64 = 1.25e-9;
|
||
/// 仪器加宽常量
|
||
const CID: f64 = 0.02654;
|
||
|
||
/// ODFHYD 输入参数
|
||
pub struct OdfhydParams {
|
||
/// 深度索引 (1-indexed)
|
||
pub id: usize,
|
||
/// 跃迁索引 (1-indexed)
|
||
pub itr: usize,
|
||
}
|
||
|
||
/// ODFHYD 配置参数
|
||
pub struct OdfhydConfig {
|
||
/// ODF 采样标志 (0: 标准 ODF, >0: 采样 ODF)
|
||
pub ispodf: i32,
|
||
/// 最大谱线数
|
||
pub nlmx: usize,
|
||
/// 光速 (Angstrom/s)
|
||
pub cas: f64,
|
||
/// 湍流速度 (cm/s)
|
||
pub vtb: f64,
|
||
}
|
||
|
||
/// ODFHYD 原子数据
|
||
pub struct OdfhydAtomicData<'a> {
|
||
/// 下能级索引 (ntrans)
|
||
pub ilow: &'a [i32],
|
||
/// 上能级索引 (ntrans)
|
||
pub iup: &'a [i32],
|
||
/// 主量子数 (nlevel)
|
||
pub nquant: &'a [i32],
|
||
/// ODF 的下量子数 (nlevel)
|
||
pub nqlodf: &'a [i32],
|
||
/// 跃迁起始频率索引 (ntrans)
|
||
pub ifr0: &'a [i32],
|
||
/// 跃迁结束频率索引 (ntrans)
|
||
pub ifr1: &'a [i32],
|
||
/// XKIJ 系数 (ntrans × nlmx)
|
||
pub xkij: &'a [f64],
|
||
/// FIJ 系数 (ntrans × nlmx)
|
||
pub fij: &'a [f64],
|
||
/// ODF 索引 (ntrans)
|
||
pub jndodf: &'a [i32],
|
||
}
|
||
|
||
/// ODFHYD 模型状态
|
||
pub struct OdfhydModelState<'a> {
|
||
/// 温度 (nd)
|
||
pub temp: &'a [f64],
|
||
/// 电子密度 (nd)
|
||
pub elec: &'a [f64],
|
||
/// WNHINT 数组 (nlmx × nd)
|
||
pub wnhint: &'a [f64],
|
||
/// Stark 展宽参数
|
||
pub straux: StrAux,
|
||
}
|
||
|
||
/// ODFHYD ODF 数据
|
||
pub struct OdfhydOdfData<'a> {
|
||
/// ODF 频率数 (ntrans)
|
||
pub nfrodf: &'a [i32],
|
||
/// ODF 频率 (mfro × ntrans)
|
||
pub fros: &'a [f64],
|
||
/// ODF 频率宽度 (mfro × ntrans)
|
||
pub wnus: &'a [f64],
|
||
/// 谱线轮廓 (nd × nfreq 或其他)
|
||
pub prflin: &'a mut [f64],
|
||
/// 频率数组 (nfreq)
|
||
pub freq: &'a [f64],
|
||
/// KFR0 索引 (ntrans)
|
||
pub kfr0: &'a [i32],
|
||
/// INDEXP 标志 (ntrans)
|
||
pub indexp: &'a [i32],
|
||
}
|
||
|
||
/// XI2 函数:计算 1/n^2
|
||
fn xi2(n: i32) -> f64 {
|
||
1.0 / (n as f64 * n as f64)
|
||
}
|
||
|
||
/// 计算氢线系列的 ODF。
|
||
///
|
||
/// # 参数
|
||
///
|
||
/// * `params` - 输入参数(id, itr)
|
||
/// * `config` - 配置参数
|
||
/// * `atomic` - 原子数据
|
||
/// * `model` - 模型状态
|
||
/// * `odf_data` - ODF 数据
|
||
///
|
||
/// # 返回值
|
||
///
|
||
/// 更新后的 PRFLIN 数组
|
||
pub fn odfhyd(
|
||
params: &OdfhydParams,
|
||
config: &OdfhydConfig,
|
||
atomic: &OdfhydAtomicData,
|
||
model: &mut OdfhydModelState,
|
||
odf_data: &mut OdfhydOdfData,
|
||
) {
|
||
let id = params.id;
|
||
let id_idx = id - 1;
|
||
let itr = params.itr;
|
||
let itr_idx = itr - 1;
|
||
|
||
let jo = atomic.jndodf[itr_idx] as usize - 1;
|
||
let ispodf = config.ispodf;
|
||
|
||
// 确定频率点数和初始化数组
|
||
let nf = if ispodf == 0 {
|
||
odf_data.nfrodf[jo] as usize
|
||
} else {
|
||
(atomic.ifr1[itr_idx] - atomic.ifr0[itr_idx] + 1) as usize
|
||
};
|
||
|
||
let mut sig = vec![0.0; nf];
|
||
let mut sgt = vec![0.0; nf];
|
||
let mut odf = vec![0.0; nf];
|
||
let mut iodf = vec![0; nf];
|
||
let mut ynus = vec![0.0; nf];
|
||
let mut alam = vec![0.0; nf];
|
||
|
||
// 初始化频率和波长
|
||
if ispodf == 0 {
|
||
for ij in 0..nf {
|
||
iodf[ij] = 0;
|
||
sig[ij] = 0.0;
|
||
odf[ij] = 0.0;
|
||
ynus[ij] = odf_data.fros[ij + jo * 1000]; // 假设 MFRO = 1000
|
||
alam[ij] = config.cas / ynus[ij];
|
||
}
|
||
} else {
|
||
let ifr0 = atomic.ifr0[itr_idx] as usize - 1;
|
||
for ij in 0..nf {
|
||
sig[ij] = 0.0;
|
||
ynus[ij] = odf_data.freq[ifr0 + ij];
|
||
alam[ij] = config.cas / ynus[ij];
|
||
}
|
||
}
|
||
|
||
// 获取能级索引
|
||
let ii = atomic.ilow[itr_idx] as usize - 1;
|
||
let jj = atomic.iup[itr_idx] as usize - 1;
|
||
|
||
// 计算电子密度因子
|
||
let anes = (TTW * model.elec[id_idx].ln()).exp();
|
||
let f00 = C00 * anes;
|
||
|
||
// 计算跃迁频率
|
||
let nquant_ii = atomic.nquant[ii];
|
||
let nquant_jj = atomic.nquant[jj];
|
||
let fra = FRH * (xi2(nquant_ii) - xi2(nquant_jj));
|
||
|
||
// 计算 Doppler 宽度
|
||
let dopo = fra / CCM * (CDOP * model.temp[id_idx] + config.vtb * config.vtb).sqrt();
|
||
|
||
// 遍历谱线系列
|
||
let nqlodf_ii = atomic.nqlodf[ii] as usize;
|
||
for j in nqlodf_ii..=config.nlmx {
|
||
let wl = RYDEL / (xi2(nquant_ii) - xi2(j as i32));
|
||
let fxk = f00 * atomic.xkij[jo * config.nlmx + j];
|
||
let dbeta = wl * wl / CA / fxk;
|
||
let betad = dbeta * dopo;
|
||
let fid = CID * atomic.fij[jo * config.nlmx + j] * dbeta;
|
||
|
||
// 调用 DIVSTR
|
||
let (adh, divh) = divstr(betad, 1);
|
||
|
||
// 获取 Stark 宽度
|
||
let wprob = model.wnhint[j * id + id_idx];
|
||
|
||
// 更新 straux 中的参数
|
||
model.straux.betad = betad;
|
||
model.straux.adh = adh;
|
||
model.straux.divh = divh;
|
||
|
||
// 调用 ODFHST
|
||
odfhst(nf, fxk, fid, wprob, wl, &alam, &model.straux, &mut sgt);
|
||
|
||
// 累加截面
|
||
for ij in 0..nf {
|
||
sig[ij] += sgt[ij];
|
||
}
|
||
}
|
||
|
||
// 后处理(仅对标准 ODF)
|
||
if ispodf == 0 {
|
||
// 排序
|
||
iodf = indexx(&sig);
|
||
|
||
// 重新排列 ODF
|
||
for ij in 0..nf {
|
||
odf[ij] = sig[iodf[ij]];
|
||
}
|
||
|
||
// 计算频率网格
|
||
let i0 = atomic.ifr0[itr_idx] as usize;
|
||
let i1 = atomic.ifr1[itr_idx] as usize;
|
||
|
||
if odf_data.indexp[itr_idx].abs() == 2 {
|
||
ynus[0] = odf_data.freq[i0 - 1];
|
||
}
|
||
|
||
let mut iw1 = iodf[0];
|
||
for ij in 1..nf {
|
||
let iw2 = iodf[ij];
|
||
if ij > 1 && ij < nf - 1 {
|
||
ynus[ij] = ynus[ij - 1]
|
||
- HALF * (odf_data.wnus[iw1 + jo * 1000] + odf_data.wnus[iw2 + jo * 1000]);
|
||
} else if ij == 1 {
|
||
ynus[ij] = ynus[ij - 1]
|
||
- HALF * (TWO * odf_data.wnus[iw1 + jo * 1000] + odf_data.wnus[iw2 + jo * 1000]);
|
||
} else if ij == nf - 1 {
|
||
ynus[ij] = ynus[ij - 1]
|
||
- HALF * (odf_data.wnus[iw1 + jo * 1000] + TWO * odf_data.wnus[iw2 + jo * 1000]);
|
||
}
|
||
iw1 = iw2;
|
||
}
|
||
|
||
// 插值到频率网格
|
||
odf_data.prflin[id_idx * 100000 + i1 - 1] = 1e-35;
|
||
|
||
for ij0 in i0..i1 {
|
||
let mut ji = 1;
|
||
for ij in 1..nf {
|
||
ji = ij;
|
||
if ynus[ij] <= odf_data.freq[ij0 - 1] {
|
||
break;
|
||
}
|
||
}
|
||
|
||
let prfln = if ji > 0 && ji < nf {
|
||
odf[ji - 1]
|
||
+ (odf[ji] - odf[ji - 1]) * (odf_data.freq[ij0 - 1] - ynus[ji - 1])
|
||
/ (ynus[ji] - ynus[ji - 1])
|
||
} else {
|
||
0.0
|
||
};
|
||
|
||
odf_data.prflin[id_idx * 100000 + ij0 - 1] = prfln;
|
||
}
|
||
} else {
|
||
// 采样 ODF 情况
|
||
let kfr0 = odf_data.kfr0[itr_idx] as usize;
|
||
for ij in 0..nf {
|
||
odf_data.prflin[id_idx * 100000 + kfr0 + ij - 1] = sig[ij];
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_xi2() {
|
||
assert!((xi2(1) - 1.0).abs() < 1e-15);
|
||
assert!((xi2(2) - 0.25).abs() < 1e-15);
|
||
assert!((xi2(3) - 1.0 / 9.0).abs() < 1e-15);
|
||
}
|
||
|
||
#[test]
|
||
fn test_odfhyd_constants() {
|
||
// CDOP = 2 * BOLK / HMASS = 2 * 1.38054e-16 / 1.6726e-24 ≈ 1.65e8
|
||
// 注意:Fortran 中的 BOLK 可能是 1.38054e-16,HMASS 是 1.6726e-24
|
||
// CDOP ≈ 1.65e8 cm/s/K^0.5
|
||
assert!(CDOP > 1e7 && CDOP < 1e9);
|
||
assert!((CA - 2.997925e18).abs() < 1e13);
|
||
assert!((FRH - 3.28805e15).abs() < 1e10);
|
||
}
|
||
}
|