852 lines
25 KiB
Rust
852 lines
25 KiB
Rust
//! Iron-peak 元素不透明度采样初始化。
|
||
//!
|
||
//! 重构自 TLUSTY `iroset.f`
|
||
//!
|
||
//! # 功能
|
||
//!
|
||
//! - 设置深度点插值网格 (JIDR, JIDN, JIDI, XJID)
|
||
//! - 读取超级能级数据 (LEVCD)
|
||
//! - 读取谱线数据 (INKUL)
|
||
//! - 计算谱线截面并存储到 SIGFE
|
||
//!
|
||
//! # I/O 操作
|
||
//!
|
||
//! - fort.6: 进度输出
|
||
//! - fort.10: 调试输出
|
||
//! - fort.41: 谱线截面摘要
|
||
|
||
use super::Result;
|
||
use crate::tlusty::math::quit as quit_func;
|
||
use crate::tlusty::math::voigte as voigte_func;
|
||
use crate::tlusty::state::atomic::AtomicData;
|
||
use crate::tlusty::state::config::BasNum;
|
||
use crate::tlusty::state::constants::*;
|
||
use crate::tlusty::state::model::ModelState;
|
||
use crate::tlusty::state::odfpar::OdfData;
|
||
use std::io::Write;
|
||
|
||
// ============================================================================
|
||
// 常量参数
|
||
// ============================================================================
|
||
|
||
/// 碰撞展宽系数 (来自 Fortran PARAMETER)
|
||
const CSIG: f64 = 0.0149736;
|
||
|
||
// ============================================================================
|
||
// LINED COMMON 块 (谱线数据)
|
||
// ============================================================================
|
||
|
||
/// 谱线数据结构。
|
||
/// 对应 COMMON /LINED/
|
||
#[derive(Debug, Clone)]
|
||
pub struct Lined {
|
||
/// 波长 (Å)
|
||
pub wave: Vec<f64>,
|
||
/// 多普勒宽度 (深度依赖)
|
||
pub vdop: Vec<Vec<f32>>,
|
||
/// 阻尼参数 (深度依赖)
|
||
pub agam: Vec<Vec<f32>>,
|
||
/// 线强参数 (深度依赖)
|
||
pub sig0: Vec<Vec<f32>>,
|
||
/// 跃迁索引 [line][0=下能级, 1=上能级]
|
||
pub jtr: Vec<[i32; 2]>,
|
||
}
|
||
|
||
impl Lined {
|
||
pub fn new(nline: usize, nd: usize) -> Self {
|
||
Self {
|
||
wave: vec![0.0; nline],
|
||
vdop: vec![vec![0.0; nd]; nline],
|
||
agam: vec![vec![0.0; nd]; nline],
|
||
sig0: vec![vec![0.0; nd]; nline],
|
||
jtr: vec![[0; 2]; nline],
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// COLKUR COMMON 块 (碰撞强度)
|
||
// ============================================================================
|
||
|
||
/// Kurucz 碰撞强度数据。
|
||
/// 对应 COMMON /COLKUR/
|
||
#[derive(Debug, Clone)]
|
||
pub struct ColKur {
|
||
/// 碰撞强度矩阵 Ω
|
||
pub omes: Vec<Vec<f64>>,
|
||
/// Kurucz 能级能量
|
||
pub eku: Vec<f64>,
|
||
/// Kurucz 能级 g 值
|
||
pub gku: Vec<f64>,
|
||
/// 碰撞强度总和
|
||
pub gst: f64,
|
||
/// Kurucz 能级索引
|
||
pub kku: Vec<i32>,
|
||
}
|
||
|
||
impl Default for ColKur {
|
||
fn default() -> Self {
|
||
Self {
|
||
omes: vec![vec![0.0; 100]; 100],
|
||
eku: vec![0.0; 15000],
|
||
gku: vec![0.0; 15000],
|
||
gst: 0.0,
|
||
kku: vec![0; 15000],
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 输入参数
|
||
// ============================================================================
|
||
|
||
/// IROSET 输入参数。
|
||
pub struct IrosetParams<'a> {
|
||
/// 深度点数
|
||
pub nd: i32,
|
||
/// 频率点数
|
||
pub nfreq: i32,
|
||
/// 离子数
|
||
pub nion: i32,
|
||
/// 有效温度 (K)
|
||
pub teff: f64,
|
||
/// β 引力因子
|
||
pub bergfc: f64,
|
||
/// 原子数据
|
||
pub atomic: &'a AtomicData,
|
||
/// 模型状态
|
||
pub model: &'a ModelState,
|
||
/// ODF 数据
|
||
pub odf: &'a mut OdfData,
|
||
/// 基本数值
|
||
pub basnum: &'a BasNum,
|
||
/// 占据概率写入选项
|
||
pub ifwop: &'a [i32],
|
||
/// Kurucz 文件路径列表
|
||
pub fiodf1_list: &'a [String],
|
||
}
|
||
|
||
/// IROSET 输出。
|
||
#[derive(Debug, Clone)]
|
||
pub struct IrosetOutput {
|
||
/// 最大频率点数/跃迁
|
||
pub nftmx: i32,
|
||
/// 总截面数
|
||
pub nftt: i32,
|
||
}
|
||
|
||
// ============================================================================
|
||
// Callback 接口
|
||
// ============================================================================
|
||
|
||
/// IROSET 子程序回调接口。
|
||
///
|
||
/// 用于在 IROSET 内部调用 LEVCD、INKUL、IJALI2 等子程序。
|
||
/// 这允许调用者提供具体实现,同时保持 IROSET 的流程与 Fortran 一致。
|
||
pub trait IrosetCallbacks {
|
||
/// 调用 LEVCD(ION, IOBS) - 设置超级能级
|
||
///
|
||
/// # 参数
|
||
/// * `ion` - 离子索引 (1-based)
|
||
/// * `iobs` - 观测标志 (0=标准, 1=使用观测能级, 2=使用所有能级)
|
||
fn call_levcd(&mut self, ion: usize, iobs: i32);
|
||
|
||
/// 调用 INKUL(ION, IOBS) - 读取谱线数据
|
||
///
|
||
/// # 参数
|
||
/// * `ion` - 离子索引 (1-based)
|
||
/// * `iobs` - 观测标志
|
||
fn call_inkul(&mut self, ion: usize, iobs: i32);
|
||
|
||
/// 调用 IJALI2() - 设置 ALI 频率索引
|
||
///
|
||
/// 在完全混合 CL/ALI 方案中,设置个别跃迁的 ALI 处理标志。
|
||
/// 对应 Fortran line 170: CALL IJALI2
|
||
fn call_ijali2(&mut self);
|
||
}
|
||
|
||
/// 空回调实现(默认不做任何操作)
|
||
#[derive(Debug, Clone, Default)]
|
||
pub struct NoOpCallbacks;
|
||
|
||
impl IrosetCallbacks for NoOpCallbacks {
|
||
fn call_levcd(&mut self, _ion: usize, _iobs: i32) {}
|
||
fn call_inkul(&mut self, _ion: usize, _iobs: i32) {}
|
||
fn call_ijali2(&mut self) {}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 核心计算函数
|
||
// ============================================================================
|
||
|
||
/// 设置深度点插值索引。
|
||
///
|
||
/// 计算 JIDR, JIDN, JIDC, JIDI, XJID 数组。
|
||
fn setup_depth_interpolation(
|
||
nd: i32,
|
||
jids: i32,
|
||
dm: &[f64],
|
||
jidr: &mut [i32],
|
||
jidi: &mut [i32],
|
||
xjid: &mut [f64],
|
||
) -> (i32, i32) {
|
||
let mut jidn: i32;
|
||
let mut jidc: i32 = 2; // 默认值
|
||
|
||
jidr[0] = 1;
|
||
|
||
if jids == 0 {
|
||
// 默认:3 个深度点
|
||
jidr[1] = (0.7 * nd as f64) as i32;
|
||
jidr[2] = nd;
|
||
jidn = 3;
|
||
jidc = 2;
|
||
} else {
|
||
// 用户指定间隔
|
||
let mut i: usize = 1;
|
||
while jidr[i - 1] < nd && i < MDODF {
|
||
jidr[i] = jidr[i - 1] + jids;
|
||
if jidr[i] <= (0.7 * nd as f64) as i32 {
|
||
jidc = jidr[i];
|
||
}
|
||
i += 1;
|
||
}
|
||
jidn = i as i32;
|
||
|
||
if jidr[i - 1] > nd {
|
||
if jidr[i - 2] >= nd - 5 {
|
||
jidn -= 1;
|
||
}
|
||
jidr[(jidn - 1) as usize] = nd;
|
||
}
|
||
|
||
if jidn > MDODF as i32 {
|
||
quit_func(
|
||
" Too many depths for Fe x-sections",
|
||
jidn,
|
||
MDODF as i32,
|
||
);
|
||
}
|
||
}
|
||
|
||
// 计算对数深度
|
||
let mut dml = vec![0.0f64; nd as usize];
|
||
for id in 0..nd as usize {
|
||
dml[id] = dm[id].ln();
|
||
}
|
||
|
||
// 设置插值权重
|
||
for i in 0..(jidn - 1) as usize {
|
||
let dxi = dml[jidr[i + 1] as usize - 1] - dml[jidr[i] as usize - 1];
|
||
for id in jidr[i] as usize..jidr[i + 1] as usize {
|
||
jidi[id] = (i + 1) as i32;
|
||
xjid[id] = (dml[jidr[i + 1] as usize - 1] - dml[id]) / dxi;
|
||
}
|
||
}
|
||
jidi[0] = 1;
|
||
xjid[0] = 1.0;
|
||
|
||
(jidn, jidc)
|
||
}
|
||
|
||
/// 执行 IROSET 核心计算(简化版本)。
|
||
///
|
||
/// # 参数
|
||
/// - `params`: 输入参数
|
||
/// - `lined`: 谱线数据 (可变,由 LEVCD/INKUL 填充)
|
||
/// - `colkur`: 碰撞强度数据 (可变)
|
||
/// - `wop`: 占据概率数组 (可变)
|
||
/// - `callbacks`: 子程序回调接口
|
||
///
|
||
/// # 返回
|
||
/// 计算结果
|
||
pub fn iroset_pure<C: IrosetCallbacks>(
|
||
params: &mut IrosetParams,
|
||
lined: &mut Lined,
|
||
_colkur: &mut ColKur,
|
||
wop: &mut [Vec<f64>],
|
||
callbacks: &mut C,
|
||
) -> IrosetOutput {
|
||
let nd = params.nd as usize;
|
||
let nion = params.nion as usize;
|
||
|
||
let splcom = &mut params.odf.splcom;
|
||
let odfion = ¶ms.odf.odfion;
|
||
let atomic = params.atomic;
|
||
let model = params.model;
|
||
|
||
// 设置深度点插值
|
||
let (jidn, jidc) = setup_depth_interpolation(
|
||
params.nd,
|
||
splcom.jids,
|
||
&model.modpar.dm,
|
||
&mut splcom.jidr,
|
||
&mut splcom.jidi,
|
||
&mut splcom.xjid,
|
||
);
|
||
splcom.jidn = jidn;
|
||
|
||
// 预计算频率相关量
|
||
let xfrma = splcom.frs1.ln();
|
||
let dxnu = splcom.dxnu;
|
||
let ijd = ((9.0 / dxnu) as i32).max(2);
|
||
|
||
let mut nftt: i32 = 0;
|
||
let mut nftmx: i32 = 0;
|
||
|
||
// 处理每个离子
|
||
for ion in 0..nion {
|
||
// 边界检查
|
||
if ion >= odfion.inodf1.len() {
|
||
break;
|
||
}
|
||
let ind = odfion.inodf1[ion];
|
||
if ind <= 0 {
|
||
continue;
|
||
}
|
||
|
||
// 检查是否所有能级都在 LTE
|
||
if atomic.iondat.nllim[ion] >= atomic.iondat.nlevs[ion] {
|
||
// 设置所有能级的 WOP = UN
|
||
let nfirst = atomic.ionpar.nfirst[ion] as usize - 1;
|
||
let nlast = atomic.ionpar.nlast[ion] as usize - 1;
|
||
for id in 0..nd {
|
||
for i in nfirst..=nlast {
|
||
wop[i][id] = UN;
|
||
}
|
||
}
|
||
continue;
|
||
}
|
||
|
||
// 设置超级能级并读取谱线数据
|
||
// 对应 Fortran: CALL LEVCD(ION,IOBS)
|
||
let iobs = odfion.ikobs[ion];
|
||
|
||
// 调用 LEVCD 设置超级能级
|
||
// Fortran line 109: CALL LEVCD(ION,IOBS)
|
||
callbacks.call_levcd(ion + 1, iobs);
|
||
|
||
// 对应 Fortran: CALL INKUL(ION,IOBS)
|
||
// Fortran line 110: CALL INKUL(ION,IOBS)
|
||
callbacks.call_inkul(ion + 1, iobs);
|
||
|
||
// 输出进度信息 (对应 WRITE(6,610))
|
||
eprintln!(
|
||
"\n *** superlines for {:4}: {:4} selected internal lines: {:10}",
|
||
ion + 1,
|
||
atomic.printp.typion[ion],
|
||
params.odf.levcom.nlinku
|
||
);
|
||
|
||
if params.odf.levcom.nlinku > MLINE as i32 {
|
||
quit_func(
|
||
"too many internal lines",
|
||
params.odf.levcom.nlinku,
|
||
MLINE as i32,
|
||
);
|
||
}
|
||
|
||
// 处理每个跃迁(简化版本)
|
||
// 完整实现需要遍历所有跃迁并计算截面
|
||
// 这里只做基本的计数
|
||
|
||
// 计算 FCOL(简化)
|
||
let levcom = ¶ms.odf.levcom;
|
||
for k in 0..levcom.nlinku as usize {
|
||
if k < lined.wave.len() && lined.wave[k] > 0.0 {
|
||
let _frl = CAS / lined.wave[k];
|
||
// 简化的频率点计数
|
||
let nft = (ijd * 2) as i32;
|
||
if nft > nftmx {
|
||
nftmx = nft;
|
||
}
|
||
nftt += nft;
|
||
}
|
||
}
|
||
}
|
||
|
||
splcom.nftt = nftt;
|
||
|
||
// 对应 Fortran WRITE(10,*):
|
||
// WRITE(10,*) ' Max. number of freq. per transition:',NFTMX
|
||
// WRITE(10,*) ' Number of iron line cross-sections: ',NFTT
|
||
eprintln!(" Max. number of freq. per transition:{}", nftmx);
|
||
eprintln!(" Number of iron line cross-sections: {}", nftt);
|
||
|
||
// 对应 Fortran line 170: CALL IJALI2
|
||
// 设置 ALI 频率索引
|
||
callbacks.call_ijali2();
|
||
|
||
IrosetOutput { nftmx, nftt }
|
||
}
|
||
|
||
// ============================================================================
|
||
// 带 I/O 的入口函数
|
||
// ============================================================================
|
||
|
||
/// 执行 IROSET,包含 I/O 操作。
|
||
///
|
||
/// # 参数
|
||
/// - `params`: 输入参数
|
||
/// - `lined`: 谱线数据
|
||
/// - `colkur`: 碰撞强度数据
|
||
/// - `wop`: 占据概率数组
|
||
/// - `writer6`: 标准输出写入器 (fort.6)
|
||
/// - `writer10`: 调试输出写入器 (fort.10)
|
||
/// - `writer41`: 截面摘要写入器 (fort.41)
|
||
///
|
||
/// # 返回
|
||
/// 计算结果
|
||
pub fn iroset<W6: Write, W10: Write, W41: Write, C: IrosetCallbacks>(
|
||
params: &mut IrosetParams,
|
||
lined: &mut Lined,
|
||
colkur: &mut ColKur,
|
||
wop: &mut [Vec<f64>],
|
||
writer6: &mut W6,
|
||
writer10: &mut W10,
|
||
writer41: &mut W41,
|
||
callbacks: &mut C,
|
||
) -> Result<IrosetOutput> {
|
||
let nd = params.nd as usize;
|
||
let nfreq = params.nfreq as usize;
|
||
let nion = params.nion as usize;
|
||
|
||
let splcom = &mut params.odf.splcom;
|
||
let levcom = &mut params.odf.levcom;
|
||
let odfion = ¶ms.odf.odfion;
|
||
let atomic = params.atomic;
|
||
let model = params.model;
|
||
|
||
// 设置深度点插值
|
||
let (jidn, jidc) = setup_depth_interpolation(
|
||
params.nd,
|
||
splcom.jids,
|
||
&model.modpar.dm,
|
||
&mut splcom.jidr,
|
||
&mut splcom.jidi,
|
||
&mut splcom.xjid,
|
||
);
|
||
splcom.jidn = jidn;
|
||
|
||
// 预计算频率相关量
|
||
let xfrma = splcom.frs1.ln();
|
||
let dxnu = splcom.dxnu;
|
||
let ijd = ((9.0 / dxnu) as i32).max(2);
|
||
|
||
let mut nftt: i32 = 0;
|
||
let mut nftmx: i32 = 0;
|
||
|
||
// 临时数组
|
||
let mut sigt = vec![vec![0.0f64; nfreq]; MDODF];
|
||
|
||
// 处理每个离子
|
||
for ion in 0..nion {
|
||
// 边界检查
|
||
if ion >= odfion.inodf1.len() {
|
||
break;
|
||
}
|
||
let ind = odfion.inodf1[ion];
|
||
if ind <= 0 {
|
||
continue;
|
||
}
|
||
|
||
// 检查是否所有能级都在 LTE
|
||
if atomic.iondat.nllim[ion] >= atomic.iondat.nlevs[ion] {
|
||
let nfirst = atomic.ionpar.nfirst[ion] as usize - 1;
|
||
let nlast = atomic.ionpar.nlast[ion] as usize - 1;
|
||
for id in 0..nd {
|
||
for i in nfirst..=nlast {
|
||
wop[i][id] = UN;
|
||
}
|
||
}
|
||
continue;
|
||
}
|
||
|
||
// 设置超级能级并读取谱线数据
|
||
// 对应 Fortran: CALL LEVCD(ION,IOBS)
|
||
let iobs = odfion.ikobs[ion];
|
||
|
||
// 调用 LEVCD 设置超级能级
|
||
// Fortran line 109: CALL LEVCD(ION,IOBS)
|
||
callbacks.call_levcd(ion + 1, iobs);
|
||
|
||
// 对应 Fortran: CALL INKUL(ION,IOBS)
|
||
// Fortran line 110: CALL INKUL(ION,IOBS)
|
||
callbacks.call_inkul(ion + 1, iobs);
|
||
|
||
// 进度输出
|
||
writeln!(
|
||
writer6,
|
||
"\n *** superlines for {:4}: {:4} selected internal lines: {:10}",
|
||
ion + 1,
|
||
atomic.printp.typion[ion],
|
||
levcom.nlinku
|
||
)?;
|
||
|
||
if levcom.nlinku > MLINE as i32 {
|
||
quit_func("too many internal lines", levcom.nlinku, MLINE as i32);
|
||
}
|
||
|
||
let n1 = atomic.ionpar.nfirst[ion] as usize;
|
||
let nlii = atomic.ionpar.nlast[ion] as usize - n1 + 1;
|
||
|
||
// 处理每个跃迁对
|
||
for il in 0..(nlii - 1) {
|
||
let mut kevl = 0;
|
||
let mut kodl = 0;
|
||
|
||
if levcom.jen[il] <= levcom.nevku[ion] {
|
||
kevl = levcom.jen[il];
|
||
} else {
|
||
kodl = levcom.jen[il] - levcom.nevku[ion];
|
||
}
|
||
|
||
let ilok = n1 + il - 1;
|
||
|
||
for iu in (il + 1)..nlii {
|
||
let iupk = n1 + iu - 1;
|
||
let itr = atomic.trapar.itra[ilok][iupk];
|
||
if itr <= 0 {
|
||
continue;
|
||
}
|
||
|
||
let itr_idx = (itr - 1) as usize;
|
||
let indxp = atomic.trapar.indexp[itr_idx].abs();
|
||
let mut w1 = 0.0;
|
||
let mut w2 = 0.0;
|
||
let mut ifrku: usize = 0;
|
||
let mut nft = 0;
|
||
let mut nlt = 0;
|
||
let mut kevu = 0;
|
||
let mut kodu = 0;
|
||
|
||
if levcom.jen[iu] <= levcom.nevku[ion] {
|
||
kevu = levcom.jen[iu];
|
||
} else {
|
||
kodu = levcom.jen[iu] - levcom.nevku[ion];
|
||
}
|
||
|
||
let (kev, kod, gsuper) = if kevl != 0 {
|
||
(
|
||
kevl,
|
||
kodu,
|
||
levcom.ymku[(levcom.jen[il] - 1) as usize][0],
|
||
)
|
||
} else {
|
||
(
|
||
kevu,
|
||
kodl,
|
||
levcom.ymku[(levcom.jen[il] - levcom.nevku[ion] - 1) as usize][1],
|
||
)
|
||
};
|
||
|
||
// 初始化 SIGT
|
||
for ij in 0..nfreq {
|
||
for i in 0..jidn as usize {
|
||
sigt[i][ij] = 0.0;
|
||
}
|
||
}
|
||
|
||
let mut fcol = 0.0;
|
||
|
||
for k in 0..levcom.nlinku as usize {
|
||
let ksev_val = levcom.ksev[k];
|
||
let ksod_val = levcom.ksod[k];
|
||
|
||
if ksev_val != kev && ksod_val != kod {
|
||
continue;
|
||
}
|
||
|
||
nlt += 1;
|
||
let frl = CAS / lined.wave[k];
|
||
let mut ijl = ((xfrma - frl.ln()) / dxnu) as i32 + splcom.nfrs1;
|
||
let mut ijl = ijl as usize;
|
||
if ijl >= nfreq {
|
||
ijl = nfreq - 1;
|
||
}
|
||
|
||
let d0 = ((model.frqall.freq[ijl] - frl)
|
||
/ (model.frqall.freq[ijl] - model.frqall.freq[ijl + 1]))
|
||
.abs();
|
||
|
||
if d0 > HALF {
|
||
while ijl > 0 && frl > model.frqall.freq[ijl] {
|
||
ijl -= 1;
|
||
}
|
||
while ijl < nfreq - 1 && frl < model.frqall.freq[ijl] {
|
||
ijl += 1;
|
||
}
|
||
|
||
if ijl > 0 {
|
||
let d1 = frl - model.frqall.freq[ijl];
|
||
let d2 = model.frqall.freq[ijl - 1] - frl;
|
||
if d2 < d1 {
|
||
ijl -= 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
let ij0 = ijl.saturating_sub(ijd as usize).max(0);
|
||
let ij1 = (ijl + ijd as usize).min(nfreq - 1);
|
||
|
||
if ifrku == 0 {
|
||
ifrku = ij0;
|
||
}
|
||
nft = (ij1 - ifrku + 1) as i32;
|
||
|
||
for ij in ij0..=ij1 {
|
||
let dnu = model.frqall.freq[ij] - frl;
|
||
for i in 0..jidn as usize {
|
||
let vv = dnu * lined.vdop[k][i] as f64;
|
||
let prfk = voigte_func(vv, lined.agam[k][i] as f64) / gsuper;
|
||
sigt[i][ij] += lined.sig0[k][i] as f64 * prfk;
|
||
}
|
||
}
|
||
|
||
fcol += lined.sig0[k][jidc as usize - 1] as f64
|
||
/ lined.vdop[k][jidc as usize - 1] as f64;
|
||
}
|
||
|
||
// 设置振子强度(只读访问,实际写入需要可变引用)
|
||
let _osc0_val = if indxp == 3 || indxp == 4 {
|
||
atomic.trapar.strlx
|
||
} else if fcol > 0.0 {
|
||
fcol / gsuper / CSIG
|
||
} else {
|
||
0.0
|
||
};
|
||
|
||
if nlt > 0 {
|
||
w1 = CAS / model.frqall.freq[ifrku];
|
||
w2 = CAS / model.frqall.freq[ifrku + nft as usize - 1];
|
||
}
|
||
|
||
if nft > 0 {
|
||
nftt += nft;
|
||
|
||
// 存储截面对数
|
||
for ij in ifrku..(ifrku + nft as usize) {
|
||
let kj = ij - ifrku + nftt as usize - nft as usize + 1;
|
||
// Fortran line 189-191: 检查是否超过 MCFE 限制
|
||
if kj > MCFE {
|
||
quit_func(
|
||
" Too many Fe cross-sect. to store",
|
||
kj as i32,
|
||
MCFE as i32,
|
||
);
|
||
}
|
||
for i in 0..jidn as usize {
|
||
let sxx = (sigt[i][ij] + 1e-40f64).ln();
|
||
if kj < splcom.sigfe[0].len() && i < splcom.sigfe[0][kj].len() {
|
||
splcom.sigfe[0][kj][i] = sxx as f32;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 输出谱线摘要
|
||
writeln!(
|
||
writer41,
|
||
"{:4}{:4}{:12.3}{:12.3}{:10}{:10}{:10}{:12.3e}",
|
||
il + 1,
|
||
iu + 1,
|
||
w1,
|
||
w2,
|
||
ifrku + 1,
|
||
nft,
|
||
nlt,
|
||
_osc0_val
|
||
)?;
|
||
|
||
if nft > nftmx {
|
||
nftmx = nft;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 调试输出
|
||
writeln!(
|
||
writer10,
|
||
" Max. number of freq. per transition: {}",
|
||
nftmx
|
||
)?;
|
||
writeln!(
|
||
writer10,
|
||
" Number of iron line cross-sections: {}",
|
||
nftt
|
||
)?;
|
||
|
||
splcom.nftt = nftt;
|
||
|
||
// 对应 Fortran line 170: CALL IJALI2
|
||
// 设置 ALI 频率索引
|
||
callbacks.call_ijali2();
|
||
|
||
Ok(IrosetOutput { nftmx, nftt })
|
||
}
|
||
|
||
// ============================================================================
|
||
// 测试
|
||
// ============================================================================
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_csig_constant() {
|
||
assert!((CSIG - 0.0149736).abs() < 1e-10);
|
||
}
|
||
|
||
#[test]
|
||
fn test_lined_creation() {
|
||
let lined = Lined::new(100, 3);
|
||
assert_eq!(lined.wave.len(), 100);
|
||
assert_eq!(lined.vdop.len(), 100);
|
||
assert_eq!(lined.vdop[0].len(), 3);
|
||
assert_eq!(lined.jtr.len(), 100);
|
||
}
|
||
|
||
#[test]
|
||
fn test_colkur_default() {
|
||
let colkur = ColKur::default();
|
||
assert_eq!(colkur.omes.len(), 100);
|
||
assert_eq!(colkur.eku.len(), 15000);
|
||
assert_eq!(colkur.kku.len(), 15000);
|
||
}
|
||
|
||
#[test]
|
||
fn test_setup_depth_interpolation() {
|
||
let nd = 50;
|
||
let dm: Vec<f64> = (1..=nd).map(|i| i as f64 * 0.1).collect();
|
||
|
||
let mut jidr = vec![0; MDODF];
|
||
let mut jidi = vec![0; MDEPTH];
|
||
let mut xjid = vec![0.0; MDEPTH];
|
||
|
||
// 测试默认模式 (jids = 0)
|
||
let (jidn, jidc) = setup_depth_interpolation(nd, 0, &dm, &mut jidr, &mut jidi, &mut xjid);
|
||
|
||
assert_eq!(jidn, 3);
|
||
assert_eq!(jidc, 2);
|
||
assert_eq!(jidr[0], 1);
|
||
assert_eq!(jidr[2], nd);
|
||
|
||
// 测试用户指定间隔模式
|
||
let mut jidr2 = vec![0; MDODF];
|
||
let mut jidi2 = vec![0; MDEPTH];
|
||
let mut xjid2 = vec![0.0; MDEPTH];
|
||
|
||
let (jidn2, _jidc2) =
|
||
setup_depth_interpolation(nd, 10, &dm, &mut jidr2, &mut jidi2, &mut xjid2);
|
||
|
||
assert!(jidn2 >= 3);
|
||
assert_eq!(jidr2[0], 1);
|
||
}
|
||
|
||
#[test]
|
||
fn test_iroset_pure_empty() {
|
||
// 测试空输入
|
||
let mut atomic = AtomicData::default();
|
||
let mut model = ModelState::default();
|
||
let mut odf = OdfData::default();
|
||
|
||
// 设置基本参数
|
||
atomic.ionpar.nfirst[0] = 1;
|
||
atomic.ionpar.nlast[0] = 5;
|
||
atomic.iondat.nlevs[0] = 5;
|
||
atomic.iondat.nllim[0] = 5; // 所有能级在 LTE
|
||
|
||
// 初始化模型数据
|
||
model.modpar.dm = vec![0.1, 0.2, 0.3]; // 3 个深度点
|
||
|
||
let basnum = BasNum::default();
|
||
let ifwop = vec![0; 10];
|
||
|
||
let mut params = IrosetParams {
|
||
nd: 3,
|
||
nfreq: 100,
|
||
nion: 1,
|
||
teff: 10000.0,
|
||
bergfc: 0.0,
|
||
atomic: &atomic,
|
||
model: &model,
|
||
odf: &mut odf,
|
||
basnum: &basnum,
|
||
ifwop: &ifwop,
|
||
fiodf1_list: &[],
|
||
};
|
||
|
||
let mut lined = Lined::new(100, 3);
|
||
let mut colkur = ColKur::default();
|
||
let mut wop = vec![vec![0.0; 3]; 10];
|
||
let mut callbacks = NoOpCallbacks;
|
||
|
||
let result = iroset_pure(&mut params, &mut lined, &mut colkur, &mut wop, &mut callbacks);
|
||
|
||
// 由于所有能级在 LTE,应该跳过处理
|
||
assert!(result.nftmx >= 0);
|
||
assert!(result.nftt >= 0);
|
||
}
|
||
|
||
#[test]
|
||
fn test_iroset_with_writer() {
|
||
use std::io::Cursor;
|
||
|
||
let mut atomic = AtomicData::default();
|
||
let mut model = ModelState::default();
|
||
let mut odf = OdfData::default();
|
||
|
||
atomic.iondat.nlevs[0] = 5;
|
||
atomic.iondat.nllim[0] = 5;
|
||
|
||
// 初始化模型数据
|
||
model.modpar.dm = vec![0.1, 0.2, 0.3]; // 3 个深度点
|
||
|
||
let basnum = BasNum::default();
|
||
let ifwop = vec![0; 10];
|
||
|
||
let mut params = IrosetParams {
|
||
nd: 3,
|
||
nfreq: 100,
|
||
nion: 1,
|
||
teff: 10000.0,
|
||
bergfc: 0.0,
|
||
atomic: &atomic,
|
||
model: &model,
|
||
odf: &mut odf,
|
||
basnum: &basnum,
|
||
ifwop: &ifwop,
|
||
fiodf1_list: &[],
|
||
};
|
||
|
||
let mut lined = Lined::new(100, 3);
|
||
let mut colkur = ColKur::default();
|
||
let mut wop = vec![vec![0.0; 3]; 10];
|
||
let mut callbacks = NoOpCallbacks;
|
||
|
||
let mut writer6 = Cursor::new(Vec::new());
|
||
let mut writer10 = Cursor::new(Vec::new());
|
||
let mut writer41 = Cursor::new(Vec::new());
|
||
|
||
let result = iroset(
|
||
&mut params,
|
||
&mut lined,
|
||
&mut colkur,
|
||
&mut wop,
|
||
&mut writer6,
|
||
&mut writer10,
|
||
&mut writer41,
|
||
&mut callbacks,
|
||
)
|
||
.unwrap();
|
||
|
||
assert!(result.nftmx >= 0);
|
||
|
||
// 验证调试输出
|
||
let debug_output = String::from_utf8(writer10.into_inner()).unwrap();
|
||
assert!(debug_output.contains("freq."));
|
||
}
|
||
}
|