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

599 lines
17 KiB
Rust

//! 重叠谱线系列极限的 ODF (不透明度分布函数) 计算。
//!
//! 重构自 TLUSTY `odf1.f`
//!
//! # 功能
//!
//! 计算重叠谱线在系列极限附近的 opacity distribution function (ODF)。
//! 谱线收敛到 (IL - IU) 连续跃迁的边缘。
//!
//! # 参数
//!
//! - `IL` - 下能级索引
//! - `IU` - 上能级索引 (通常是下一个离子的基态)
//! - `ID` - 深度索引
//! - `ODF` - 输出:插值到显式频率集的不透明度分布函数
use crate::tlusty::state::atomic::AtomicData;
use crate::tlusty::state::config::InpPar;
use crate::tlusty::state::constants::{CAS, H, HALF, UN};
use crate::tlusty::state::model::{ModPar, StrAux};
use crate::tlusty::state::odfpar::{OdfCtr, OdfMod, OdfStk, OdfFrq};
use crate::tlusty::state::{MFREQ, MFRO, NLMX, MDEPTH};
use crate::tlusty::math::divstr;
use crate::tlusty::math::dwnfr;
use crate::tlusty::math::odfhst;
use crate::tlusty::math::{sigk, SigkParams};
use crate::tlusty::math::OpData;
// ============================================================================
// 常量
// ============================================================================
/// Rydberg 常数 (Hz)
const FRH: f64 = 3.28805e15;
/// 多普勒宽度常数
const CQT: f64 = 1.284523e12;
/// C00 常数
const C00: f64 = 1.25e-9;
/// CID 常数
const CID: f64 = 0.02654;
// ============================================================================
// 输入参数结构体
// ============================================================================
/// ODF1 输入参数。
pub struct Odf1Params<'a> {
/// 模式: 0 = 首次调用, >0 = 后续调用
pub imode: i32,
/// 下能级索引 (0-indexed)
pub il: usize,
/// 上能级索引 (0-indexed)
pub iu: usize,
/// 深度索引 (0-indexed)
pub id: usize,
/// 频率数组
pub freq: &'a [f64],
/// 原子数据
pub atomic: &'a AtomicData,
/// 模型参数
pub modpar: &'a ModPar,
/// Stark 辅助参数
pub straux: &'a StrAux,
/// WNH 积分 [NLMX][MDEPTH]
pub wnhint: &'a [Vec<f64>],
/// ODF 控制参数
pub odfctr: &'a OdfCtr,
/// ODF 模型数据
pub odfmod: &'a OdfMod,
/// ODF Stark 数据
pub odfstk: &'a OdfStk,
/// ODF 频率数据
pub odffrq: &'a OdfFrq,
/// 输入参数
pub inppar: &'a InpPar,
/// Opacity Project 数据
pub opdata: &'a OpData,
}
/// ODF1 输出。
pub struct Odf1Output {
/// ODF 值 (插值到显式频率)
pub odf: Vec<f64>,
/// 首次 ODF 索引
pub i1odf: i32,
/// 末次 ODF 索引
pub i2odf: i32,
}
/// ODF1 内部缓存 (用于 IMODE 调用间保存状态)
#[derive(Debug, Clone)]
pub struct Odf1Cache {
/// 频率数组
pub fro: Vec<f64>,
/// 截面数组
pub sgfr: Vec<f64>,
/// ODF0 数组
pub odf0: Vec<f64>,
/// ODF 索引
pub iodf: Vec<i32>,
}
impl Default for Odf1Cache {
fn default() -> Self {
Self {
fro: vec![0.0; MFRO],
sgfr: vec![0.0; MFRO],
odf0: vec![0.0; MFRO],
iodf: vec![0; MFRO],
}
}
}
// ============================================================================
// 主函数
// ============================================================================
/// 计算 ODF1 (纯计算函数)。
///
/// # 参数
///
/// * `params` - 输入参数
/// * `cache` - 内部缓存 (用于 IMODE 调用间保存状态)
///
/// # 返回值
///
/// ODF1 输出结构
pub fn odf1(params: &Odf1Params, cache: &mut Odf1Cache) -> Odf1Output {
let il = params.il;
let iu = params.iu;
let id = params.id;
let imode = params.imode;
// 获取能级参数
let nquant_il = params.atomic.levpar.nquant[il];
let kl_idx = (params.odfctr.indodf[il] - 1) as usize;
let ielo = params.atomic.levpar.iel[il];
let ielo_idx = (ielo - 1) as usize;
let n1h = params.atomic.ionpar.nlast[ielo_idx];
let nq1 = params.odfmod.nqlodf[il];
let fre = params.atomic.levpar.enion[il] / H;
let t = params.modpar.temp[id];
let sqt = t.sqrt();
let ane = params.modpar.elec[id];
let anes = ane.powf(UN / 6.0);
let f00 = C00 * anes * anes * anes * anes;
let dop0 = CQT * sqt;
let qz = params.atomic.ionpar.iz[ielo_idx] as f64;
// 跃迁索引
let itr = (params.atomic.trapar.itra[il][iu] - 1) as usize;
let nfr0 = params.odfctr.nfrodf[kl_idx] as usize;
// 临时数组
let mut abs0 = vec![0.0; MFRO];
let mut alam = vec![0.0; MFRO];
let mut frod = vec![0.0; MFRO];
let mut sgt = vec![0.0; MFRO];
let mut dwf = vec![0.0; MFRO];
let mut iodr = vec![0i32; MFRO];
// 伪连续不透明度 (所有频率非零)
// 通过溶解分数表述
if imode == 0 {
for ij in 0..nfr0 {
cache.fro[ij] = params.odffrq.fros[ij][kl_idx];
let sigk_params = SigkParams {
fr: cache.fro[ij],
itr,
mode: 1,
atomic: params.atomic,
opdata: params.opdata,
};
cache.sgfr[ij] = sigk(&sigk_params);
alam[ij] = CAS / cache.fro[ij];
}
}
// 溶解分数 D(nu)
dwnfr(
1,
nfr0,
fre,
0.0, // acor 参数,需要从其他地方获取
ane,
qz,
&cache.fro[..nfr0],
params.inppar,
&mut dwf[..nfr0],
);
for ij in 0..nfr0 {
abs0[ij] = cache.sgfr[ij] * dwf[ij];
}
// 对各条谱线求和
for j in nq1 as usize..NLMX {
let xj = j as f64;
let fxk = f00 * params.odfstk.xkij[kl_idx][j];
let wl0 = params.odfstk.wl0[kl_idx][j];
let dop = dop0 / wl0;
let dbeta = wl0 * wl0 / CAS / fxk;
let betad = dop * dbeta;
let fid = CID * params.odfstk.fij[kl_idx][j] * dbeta;
// 计算 Stark 辅助参数
let (adh, divh) = divstr(betad, 1);
// 更新 straux (通过重新计算)
let straux_local = StrAux {
betad,
adh,
divh,
..Default::default()
};
let wprob = params.wnhint[j][id];
odfhst(
nfr0,
fxk,
fid,
wprob,
wl0,
&alam[..nfr0],
&straux_local,
&mut sgt[..nfr0],
);
for ij in 0..nfr0 {
abs0[ij] += sgt[ij];
}
}
// 内部频率集的不透明度分布函数
if imode == 0 {
cache.odf0[0] = abs0[0];
cache.iodf[0] = 1;
for ij in 1..nfr0 {
cache.odf0[ij] = abs0[ij];
cache.iodf[ij] = (ij + 1) as i32;
// 插入排序保持单调递增
if cache.odf0[ij] < cache.odf0[ij - 1] {
let ab = cache.odf0[ij];
let ijodf = cache.iodf[ij];
for ij0 in 1..=ij {
let ij1 = ij - ij0 + 1;
if cache.odf0[ij1] >= cache.odf0[ij1 - 1] {
break;
}
cache.odf0[ij1] = cache.odf0[ij1 - 1];
cache.odf0[ij1 - 1] = ab;
cache.iodf[ij1] = cache.iodf[ij1 - 1];
cache.iodf[ij1 - 1] = ijodf;
}
}
// WRITE(6,603) IJ,ID,ODF0(IJ) -- FORMAT(' ij,id,odf0',2I5,1PD10.3)
if cache.odf0[ij] > 0.001 {
eprintln!(" ij,id,odf0{:5}{:5}{:10.3}", ij + 1, id + 1, cache.odf0[ij]);
}
}
} else {
cache.odf0[0] = abs0[(cache.iodf[0] - 1) as usize];
iodr[0] = cache.iodf[0];
for ij in 1..nfr0 {
cache.odf0[ij] = abs0[(cache.iodf[ij] - 1) as usize];
iodr[ij] = cache.iodf[ij];
if cache.odf0[ij] < cache.odf0[ij - 1] {
let ab = cache.odf0[ij];
let ijodf = iodr[ij];
for ij0 in 1..=ij {
let ij1 = ij - ij0 + 1;
if cache.odf0[ij1] >= cache.odf0[ij1 - 1] {
break;
}
cache.odf0[ij1] = cache.odf0[ij1 - 1];
cache.odf0[ij1 - 1] = ab;
iodr[ij1] = iodr[ij1 - 1];
iodr[ij1 - 1] = ijodf;
}
}
// WRITE(6,603) IJ,ID,ODF0(IJ) -- FORMAT(' ij,id,odf0',2I5,1PD10.3)
if cache.odf0[ij] > 0.001 {
eprintln!(" ij,id,odf0{:5}{:5}{:10.3}", ij + 1, id + 1, cache.odf0[ij]);
}
}
for ij in 0..nfr0 {
cache.iodf[ij] = iodr[ij];
}
}
// 内部频率集的重新初始化
frod[0] = cache.fro[0];
let mut iw = cache.iodf[0] as usize;
let w1 = if iw > 1 && iw < nfr0 {
cache.fro[iw - 2] - cache.fro[iw]
} else if iw == 1 {
cache.fro[0] - cache.fro[1]
} else {
cache.fro[nfr0 - 2] - cache.fro[nfr0 - 1]
};
for ij in 1..nfr0 - 1 {
iw = cache.iodf[ij] as usize;
let w2 = if iw > 1 && iw < nfr0 {
HALF * (cache.fro[iw - 2] - cache.fro[iw])
} else if iw == 1 {
HALF * (cache.fro[0] - cache.fro[1])
} else {
HALF * (cache.fro[nfr0 - 2] - cache.fro[nfr0 - 1])
};
frod[ij] = frod[ij - 1] - HALF * (w1 + w2);
}
iw = cache.iodf[nfr0 - 1] as usize;
let w2 = if iw > 1 && iw < nfr0 {
cache.fro[iw - 2] - cache.fro[iw]
} else if iw == 1 {
cache.fro[0] - cache.fro[1]
} else {
cache.fro[nfr0 - 2] - cache.fro[nfr0 - 1]
};
frod[nfr0 - 1] = frod[nfr0 - 2] - HALF * (w1 + w2);
// 插值到显式频率
let nfreq = params.freq.len();
let mut odf = vec![0.0; nfreq];
let mut i1odf: i32 = 0;
let mut i2odf: i32 = 0;
for ij in 1..nfreq {
// 检查频率是否单调递减
if params.freq[ij] > params.freq[ij - 1] {
break;
}
odf[ij] = 0.0;
// 检查频率是否在范围内
if params.freq[ij] > frod[0] || params.freq[ij] < frod[nfr0 - 1] {
continue;
}
if id == 0 {
if params.freq[ij - 1] > frod[0] {
i1odf = ij as i32;
}
i2odf = ij as i32;
}
// 找到插值位置
let mut ij0 = 1;
for ij1 in 2..nfr0 {
ij0 = ij1;
if params.freq[ij] >= frod[ij1] {
break;
}
}
// 线性插值
let frod_ij0 = frod[ij0];
let frod_ij0_1 = frod[ij0 - 1];
let denom = frod_ij0 - frod_ij0_1;
if denom.abs() > 1e-30 {
odf[ij] = cache.odf0[ij0 - 1]
+ (cache.odf0[ij0] - cache.odf0[ij0 - 1]) / denom
* (params.freq[ij] - frod_ij0_1);
}
}
Odf1Output {
odf,
i1odf,
i2odf,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tlusty::state::atomic::AtomicData;
use crate::tlusty::state::config::InpPar;
use crate::tlusty::state::model::{ModPar, StrAux};
use crate::tlusty::state::odfpar::{OdfCtr, OdfMod, OdfStk, OdfFrq};
use crate::tlusty::math::OpData;
fn create_test_params<'a>(
atomic: &'a AtomicData,
modpar: &'a ModPar,
straux: &'a StrAux,
wnhint: &'a [Vec<f64>],
odfctr: &'a OdfCtr,
odfmod: &'a OdfMod,
odfstk: &'a OdfStk,
odffrq: &'a OdfFrq,
inppar: &'a InpPar,
opdata: &'a OpData,
freq: &'a [f64],
) -> Odf1Params<'a> {
Odf1Params {
imode: 0,
il: 0,
iu: 1,
id: 0,
freq,
atomic,
modpar,
straux,
wnhint,
odfctr,
odfmod,
odfstk,
odffrq,
inppar,
opdata,
}
}
fn create_test_wnhint() -> Vec<Vec<f64>> {
vec![vec![0.5; MDEPTH]; NLMX]
}
#[test]
fn test_odf1_basic() {
let mut atomic = AtomicData::new();
let mut modpar = ModPar::default();
let straux = StrAux::default();
let wnhint = create_test_wnhint();
let mut odfctr = OdfCtr::new();
let mut odfmod = OdfMod::new();
let mut odfstk = OdfStk::new(NLMX);
let mut odffrq = OdfFrq::new();
let inppar = InpPar::default();
let opdata = OpData::default();
// 设置基本测试数据
atomic.levpar.nquant[0] = 1;
atomic.levpar.iel[0] = 1;
atomic.levpar.enion[0] = 13.6 * 1.2398e-4; // 氢电离能 (erg)
atomic.ionpar.nlast[0] = 10;
atomic.ionpar.iz[0] = 1;
atomic.trapar.itra[0][1] = 1;
atomic.trapar.indexp[0] = 1;
atomic.trapar.itrcon[0] = 1; // 连续跃迁索引 (1-indexed)
atomic.trapar.ilow[0] = 1; // 下能级索引 (1-indexed)
atomic.trapar.fr0[0] = 3.29e15; // 阈值频率
// 设置光致电离截面模式
atomic.phoset.ibf[0] = 0; // 氢原子截面模式
modpar.temp[0] = 10000.0;
modpar.elec[0] = 1e12;
// ODF 参数 (1-indexed)
odfctr.indodf[0] = 1; // ODF 索引
odfctr.nfrodf[0] = 5; // 频率点数
odfmod.nqlodf[0] = 1; // 起始量子数
// 设置 ODF 频率数据
for ij in 0..5 {
odffrq.fros[ij][0] = 3.0e15 + (ij as f64) * 0.1e15;
}
// 设置 ODF Stark 数据
for j in 0..NLMX {
odfstk.xkij[0][j] = 1e-10;
odfstk.wl0[0][j] = 1215.0;
odfstk.fij[0][j] = 0.1;
}
let freq = vec![3.5e15, 3.4e15, 3.3e15, 3.2e15, 3.1e15];
let params = create_test_params(
&atomic,
&modpar,
&straux,
&wnhint,
&odfctr,
&odfmod,
&odfstk,
&odffrq,
&inppar,
&opdata,
&freq,
);
let mut cache = Odf1Cache::default();
let result = odf1(&params, &mut cache);
// 验证输出数组大小
assert_eq!(result.odf.len(), freq.len());
}
#[test]
fn test_odf1_cache_default() {
let cache = Odf1Cache::default();
assert_eq!(cache.fro.len(), MFRO);
assert_eq!(cache.sgfr.len(), MFRO);
assert_eq!(cache.odf0.len(), MFRO);
assert_eq!(cache.iodf.len(), MFRO);
}
#[test]
fn test_odf1_frequency_monotonic_check() {
// 测试频率单调性检查
let freq_increasing = vec![3.0e15, 3.5e15, 4.0e15]; // 递增,应该提前返回
let freq_decreasing = vec![4.0e15, 3.5e15, 3.0e15]; // 递减,应该正常处理
let mut atomic = AtomicData::new();
let mut modpar = ModPar::default();
let straux = StrAux::default();
let wnhint = create_test_wnhint();
let mut odfctr = OdfCtr::new();
let mut odfmod = OdfMod::new();
let mut odfstk = OdfStk::new(NLMX);
let mut odffrq = OdfFrq::new();
let inppar = InpPar::default();
let opdata = OpData::default();
// 设置基本测试数据
atomic.levpar.nquant[0] = 1;
atomic.levpar.iel[0] = 1;
atomic.levpar.enion[0] = 13.6 * 1.2398e-4;
atomic.ionpar.nlast[0] = 10;
atomic.ionpar.iz[0] = 1;
atomic.trapar.itra[0][1] = 1;
atomic.trapar.indexp[0] = 1;
atomic.trapar.itrcon[0] = 1; // 连续跃迁索引
atomic.trapar.ilow[0] = 1; // 下能级索引
atomic.trapar.fr0[0] = 3.29e15; // 阈值频率
// 设置光致电离截面模式
atomic.phoset.ibf[0] = 0; // 氢原子截面模式
modpar.temp[0] = 10000.0;
modpar.elec[0] = 1e12;
// ODF 参数 (1-indexed)
odfctr.indodf[0] = 1;
odfctr.nfrodf[0] = 5;
odfmod.nqlodf[0] = 1;
// 设置 ODF 频率数据
for ij in 0..5 {
odffrq.fros[ij][0] = 3.0e15 + (ij as f64) * 0.1e15;
}
// 设置 ODF Stark 数据
for j in 0..NLMX {
odfstk.xkij[0][j] = 1e-10;
odfstk.wl0[0][j] = 1215.0;
odfstk.fij[0][j] = 0.1;
}
// 递增频率应该在第 1 个元素后就停止
let params_inc = create_test_params(
&atomic,
&modpar,
&straux,
&wnhint,
&odfctr,
&odfmod,
&odfstk,
&odffrq,
&inppar,
&opdata,
&freq_increasing,
);
let mut cache = Odf1Cache::default();
let result_inc = odf1(&params_inc, &mut cache);
// 递减频率应该处理所有元素
let params_dec = create_test_params(
&atomic,
&modpar,
&straux,
&wnhint,
&odfctr,
&odfmod,
&odfstk,
&odffrq,
&inppar,
&opdata,
&freq_decreasing,
);
let mut cache2 = Odf1Cache::default();
let result_dec = odf1(&params_dec, &mut cache2);
// 验证输出大小
assert_eq!(result_inc.odf.len(), 3);
assert_eq!(result_dec.odf.len(), 3);
}
}