599 lines
17 KiB
Rust
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(¶ms, &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(¶ms_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(¶ms_dec, &mut cache2);
|
|
|
|
// 验证输出大小
|
|
assert_eq!(result_inc.odf.len(), 3);
|
|
assert_eq!(result_dec.odf.len(), 3);
|
|
}
|
|
}
|