SpectraRust/src/tlusty/math/io/rdata.rs
2026-03-27 11:59:23 +08:00

1163 lines
32 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 `rdata.f`。
//!
//! 功能:
//! - 读取离子的能级数据(能量、统计权重、量子数等)
//! - 读取连续跃迁(束缚-自由)数据
//! - 读取谱线跃迁(束缚-束缚)数据
//! - 处理各种特殊情况ODF、ALI、碰撞数据等
use crate::tlusty::data::_UNNAMED_OSH;
use crate::tlusty::state::atomic::{AtoPar, IonDat, IonFil, IonPar, LevPar, PhoSet, TabCol, TopCs, TraPar, VoiPar};
use crate::tlusty::state::config::{BasNum, InpPar};
use crate::tlusty::state::constants::{EH, H, MCORAT, MCROSS, MFIT, MLEVEL, MTRANS, MVOIGT, MXTCOL};
use crate::tlusty::state::iterat::IterControl;
use crate::tlusty::state::model::ModelState;
use crate::tlusty::state::odfpar::OdfData;
/// 光速 (cm/s)
pub const C_LIGHT: f64 = 2.997925e18;
/// 1.6018e-12 erg/eV
pub const EV_TO_ERG: f64 = 1.6018e-12;
/// 1.9857e-16 erg/cm⁻¹
pub const CM1_TO_ERG: f64 = 1.9857e-16;
// ============================================================================
// 能量转换
// ============================================================================
/// 将能量值转换为 erg。
///
/// Fortran 逻辑:
/// - E = 0: 使用氢原子能级公式
/// - 0 < E < 100: eV
/// - 100 < E < 1e7: cm⁻¹
/// - E > 1e7: Hz
pub fn convert_energy(e: f64, zz: f64, iq: i32) -> f64 {
let x = (iq * iq) as f64;
if e == 0.0 {
// 氢原子能级公式: E = EH * Z² / n²
EH * zz * zz / x
} else if e > 1e-7 && e < 100.0 {
// eV 转 erg
EV_TO_ERG * e
} else if e > 100.0 && e < 1e7 {
// cm⁻¹ 转 erg
CM1_TO_ERG * e
} else {
// Hz 转 erg (E = h * nu)
H * e
}
}
// ============================================================================
// 氢振子强度
// ============================================================================
/// 获取氢振子强度 OSH(n1, n2)。
///
/// 数组存储为 20x20 矩阵,在 _UNNAMED_OSH 中按列优先存储。
pub fn get_osh(n1: i32, n2: i32) -> f64 {
if n1 < 1 || n1 > 20 || n2 < 1 || n2 > 20 {
return 0.0;
}
// Fortran 列优先存储: OSH(n1, n2) = _UNNAMED_OSH[(n2-1)*20 + (n1-1)]
let idx = ((n2 - 1) * 20 + (n1 - 1)) as usize;
_UNNAMED_OSH[idx]
}
// ============================================================================
// 能级数据
// ============================================================================
/// 单个能级的输入数据。
#[derive(Debug, Clone, Default)]
pub struct LevelInputData {
/// 电离能(需要转换)
pub enion: f64,
/// 统计权重
pub g: f64,
/// 主量子数
pub nquant: i32,
/// 能级类型
pub typlev: String,
/// FWOP 标志
pub ifwop: i32,
/// ODF 频率
pub frodf: f64,
/// 模型能级
pub imodl: i32,
}
/// 能级处理结果。
#[derive(Debug, Clone, Default)]
pub struct LevelData {
/// 电离能 (erg)
pub enion: f64,
/// 统计权重
pub g: f64,
/// 主量子数
pub nquant: i32,
/// LTE 能级标志
pub iltlev: i32,
/// 模型能级
pub imodl: i32,
/// FWOP 标志
pub ifwop: i32,
/// 引导能级索引
pub iguide: i32,
}
/// 处理单个能级数据。
pub fn process_level(
input: &LevelInputData,
level_idx: usize,
zz: f64,
iq: i32,
ispodf: i32,
) -> LevelData {
let mut result = LevelData::default();
// 能量转换
let e = input.enion.abs();
let e0 = convert_energy(e, zz, iq);
result.enion = if input.enion >= 0.0 { e0 } else { -e0 };
// 统计权重
result.g = if input.g == 0.0 {
2.0 * (iq * iq) as f64
} else {
input.g
};
// 主量子数
result.nquant = if input.nquant == 0 { iq } else { input.nquant };
// LTE 标志(负量子数表示 LTE
if input.nquant < 0 {
result.iltlev = 1;
result.nquant = input.nquant.abs();
}
// FWOP 处理
result.ifwop = input.ifwop;
if ispodf == 0 && input.ifwop >= 2 {
result.ifwop = 0;
}
// 模型能级
result.imodl = input.imodl;
if input.imodl > 100 {
// imodl > 100 表示引导能级
result.iguide = input.imodl - 100;
result.imodl = 6;
}
result
}
// ============================================================================
// 跃迁数据
// ============================================================================
/// 连续跃迁(束缚-自由)输入数据。
#[derive(Debug, Clone, Default)]
pub struct ContinuumInputData {
/// 下能级索引(文件中的)
pub ii: i32,
/// 上能级索引(文件中的)
pub jj: i32,
/// 模式
pub mode: i32,
/// IFANCY 参数
pub ifancy: i32,
/// 碰撞标志
pub icolis: i32,
/// 起始频率索引
pub ifrq0: i32,
/// 结束频率索引
pub ifrq1: i32,
/// 振子强度
pub osc: f64,
/// C 参数
pub cparam: f64,
/// 碰撞数据点数
pub ncol: i32,
/// 额外频率输入
pub fr0inp: Option<f64>,
/// FR0PCI 参数
pub fr0pci: Option<f64>,
/// 截面参数 (S0, ALF, BET, GAM)
pub cross_section: Option<[f64; 4]>,
/// TOPBASE 拟合点数
pub nfit: Option<i32>,
/// TOPBASE X 数组
pub xtop: Option<Vec<f64>>,
/// TOPBASE C 数组
pub ctop: Option<Vec<f64>>,
/// 碰撞数据
pub collision_data: Option<Vec<CollisionData>>,
}
/// 碰撞数据。
#[derive(Debug, Clone, Default)]
pub struct CollisionData {
/// 类型
pub itype: i32,
/// 温度点数
pub nctemp: i32,
/// 温度数组
pub ctemp: Vec<f64>,
/// 速率数组
pub colrate: Vec<f64>,
}
/// 谱线跃迁(束缚-束缚)输入数据。
#[derive(Debug, Clone, Default)]
pub struct LineInputData {
/// 下能级索引
pub ii: i32,
/// 上能级索引
pub jj: i32,
/// 模式
pub mode: i32,
/// IFANCY 参数
pub ifancy: i32,
/// 碰撞标志
pub icolis: i32,
/// 起始频率索引
pub ifrq0: i32,
/// 结束频率索引
pub ifrq1: i32,
/// 振子强度
pub osc: f64,
/// C 参数
pub cparam: f64,
/// 碰撞数据点数
pub ncol: i32,
/// 额外频率输入
pub fr0inp: Option<f64>,
/// 轮廓参数
pub profile: Option<LineProfileData>,
/// ODF 参数
pub odf: Option<OdfLineData>,
/// 碰撞数据
pub collision_data: Option<Vec<CollisionData>>,
}
/// 谱线轮廓数据。
#[derive(Debug, Clone, Default)]
pub struct LineProfileData {
/// 深度相关轮廓标志
pub lcomp: bool,
/// 积分模式
pub intmod: i32,
/// 频率点数
pub nf: i32,
/// 最大频率偏移
pub xmax: f64,
/// 标准温度
pub tstd: f64,
/// Voigt 参数(如果 iprof = 1
pub voigt: Option<VoigtParams>,
}
/// Voigt 参数。
#[derive(Debug, Clone, Default)]
pub struct VoigtParams {
/// 辐射阻尼
pub gamar: f64,
/// Stark 参数 1
pub stark1: f64,
/// Stark 参数 2
pub stark2: f64,
/// Stark 参数 3
pub stark3: f64,
/// Van der Waals 宽度
pub vdwh: f64,
}
/// ODF 谱线数据。
#[derive(Debug, Clone, Default)]
pub struct OdfLineData {
/// KDO 数组 [4]
pub kdo: [i32; 4],
/// XDO 数组 [3]
pub xdo: [f64; 3],
}
// ============================================================================
// RDATA 参数
// ============================================================================
/// RDATA 输入参数。
#[derive(Debug, Clone)]
pub struct RdataParams<'a> {
/// 离子索引 (1-based)
pub ion: i32,
/// 有效温度
pub teff: f64,
/// 氢元素索引
pub ielh: i32,
/// 氦原子索引
pub iathe: i32,
/// ODF 模式
pub ispodf: i32,
/// Lyman 截断频率
pub cutlym: f64,
/// Balmer 截断频率
pub cutbal: f64,
/// 氢轮廓模式
pub ihydpr: i32,
/// 频率范围
pub frlmin: f64,
pub frlmax: f64,
/// IOPTAB 参数
pub ioptab: i32,
// 原子数据引用
pub atopar: &'a AtoPar,
pub ionpar: &'a IonPar,
pub iondat: &'a IonDat,
pub ionfil: &'a IonFil,
}
/// RDATA 输出结构体。
#[derive(Debug, Clone, Default)]
pub struct RdataOutput {
/// 能级数据
pub levels: Vec<LevelData>,
/// 连续跃迁数据
pub continua: Vec<ContinuumTransition>,
/// 谱线跃迁数据
pub lines: Vec<LineTransition>,
/// 跃迁总数
pub ntrans: i32,
/// 连续跃迁数
pub ntranc: i32,
/// 最后频率索引
pub nlaste: i32,
/// MER 计数器
pub imer: i32,
/// MER 能级索引
pub imrg: Vec<i32>,
/// IMER 索引
pub iimer: Vec<i32>,
/// LBPFX 标志
pub lbpfx: bool,
/// HOD 计数器
pub nhod: i32,
/// LASV 标志
pub lasv: bool,
/// 氢轮廓初始化标志
pub ihydp0: i32,
}
/// 连续跃迁处理结果。
#[derive(Debug, Clone, Default)]
pub struct ContinuumTransition {
/// 跃迁索引
pub itr: i32,
/// 下能级索引
pub ii: i32,
/// 上能级索引
pub jj: i32,
/// 模式
pub mode: i32,
/// 频率 (Hz)
pub fr0: f64,
/// 振子强度
pub osc0: f64,
/// 碰撞标志
pub icol: i32,
/// C 参数
pub cpar: f64,
/// 频率索引范围
pub ifc0: i32,
pub ifc1: i32,
/// 连续跃迁索引
pub ic: i32,
/// IFANCY 参数
pub ifancy: i32,
/// FR0PCI
pub fr0pc: f64,
}
/// 谱线跃迁处理结果。
#[derive(Debug, Clone, Default)]
pub struct LineTransition {
/// 跃迁索引
pub itr: i32,
/// 下能级索引
pub ii: i32,
/// 上能级索引
pub jj: i32,
/// 模式
pub mode: i32,
/// 频率 (Hz)
pub fr0: f64,
/// 振子强度
pub osc0: f64,
/// 碰撞标志
pub icol: i32,
/// C 参数
pub cpar: f64,
/// 频率索引范围
pub ifr0: i32,
pub ifr1: i32,
/// 轮廓类型
pub iprof: i32,
/// 积分模式
pub intmod: i32,
/// 深度相关轮廓
pub lcomp: bool,
/// ODF 索引
pub jndodf: i32,
/// 是否为谱线
pub is_line: bool,
}
// ============================================================================
// 原子数据文件读取
// ============================================================================
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
/// 读取原子数据文件(如 h1.dat, he1.dat 等)。
///
/// # 文件格式
///
/// ```text
/// ****** Levels
/// ENION G NQUANT TYPLEV IFWOP FRODF IMODL
/// ...
/// ****** Continuum transitions
/// II JJ MODE IFANCY ICOLIS IFRQ0 IFRQ1 OSC CPARAM
/// ...
/// ****** Line transitions
/// II JJ MODE IFANCY ICOLIS IFRQ0 IFRQ1 OSC CPARAM
/// LCOMP INTMOD NF XMAX TSTD
/// GAMAR STARK1 STARK2 STARK3 VDWH
/// ...
/// ```
///
/// # 参数
/// * `path` - 文件路径
/// * `nlevs` - 期望读取的能级数
///
/// # 返回值
/// (能级数据, 连续跃迁数据, 谱线跃迁数据)
pub fn read_ion_data_file<P: AsRef<Path>>(
path: P,
nlevs: usize,
) -> Result<(Vec<LevelInputData>, Vec<ContinuumInputData>, Vec<LineInputData>), String> {
let file = File::open(&path).map_err(|e| format!("无法打开文件 {:?}: {}", path.as_ref(), e))?;
let reader = BufReader::new(file);
let mut lines = reader.lines().peekable();
// 跳过第一个 ****** 行
let first_line = lines.next().ok_or("文件为空")?.map_err(|e| e.to_string())?;
if !first_line.contains('*') {
return Err("文件格式错误:第一行应该是 ****** Levels".to_string());
}
// 读取能级数据
let mut levels = Vec::with_capacity(nlevs);
for _ in 0..nlevs {
let line = lines.next().ok_or("能级数据不完整")?.map_err(|e| e.to_string())?;
let level = parse_level_line(&line)?;
levels.push(level);
}
// 跳到连续跃迁部分
skip_to_section_peekable(&mut lines, "Continuum")?;
let mut continua = Vec::new();
loop {
let line = match lines.next() {
Some(Ok(l)) => l,
_ => break,
};
// 检查是否到达谱线部分
if line.contains("******") {
if line.contains("Line") {
break;
}
continue;
}
if line.trim().is_empty() {
continue;
}
if let Ok(cont) = parse_continuum_line(&line) {
// 检查是否结束ii >= nlevs 表示结束)
if cont.ii >= nlevs as i32 {
break;
}
continua.push(cont);
}
}
// 跳到谱线部分(如果还没到)
if !lines.peek().map_or(false, |r| r.as_ref().map_or(false, |l| l.contains("Line"))) {
skip_to_section_peekable(&mut lines, "Line")?;
}
let mut line_transitions = Vec::new();
loop {
let line = match lines.next() {
Some(Ok(l)) => l,
_ => break,
};
if line.contains("******") {
continue;
}
if line.trim().is_empty() {
continue;
}
// 跳过注释行(以 ! 开头)
let trimmed = line.trim();
if trimmed.starts_with('!') {
continue;
}
// 跳过续行(以 T 或 F 开头的是轮廓参数行)
if trimmed.starts_with('T') || trimmed.starts_with('F') {
continue;
}
// 跳过纯数字行(可能是额外参数)
if trimmed.chars().all(|c| c.is_numeric() || c.is_whitespace() || c == '.') {
// 可能是额外的数值行,跳过
if trimmed.split_whitespace().all(|s| s.parse::<f64>().is_ok()) {
continue;
}
}
if let Ok(line_data) = parse_line_transition(&line) {
// 检查是否结束
if line_data.ii >= nlevs as i32 {
break;
}
line_transitions.push(line_data);
}
}
Ok((levels, continua, line_transitions))
}
/// 跳到指定部分peekable 版本)
fn skip_to_section_peekable<T: BufRead>(
lines: &mut std::iter::Peekable<std::io::Lines<T>>,
section_name: &str,
) -> Result<(), String> {
loop {
let line = lines.next().ok_or(format!("未找到 {} 部分", section_name))?.map_err(|e| e.to_string())?;
if line.contains("******") && line.contains(section_name) {
return Ok(());
}
}
}
/// 跳到指定部分(查找 ****** Section 标记)
fn skip_to_section<T: BufRead>(lines: &mut std::io::Lines<T>, section_name: &str) -> Result<(), String> {
loop {
let line = lines.next().ok_or(format!("未找到 {} 部分", section_name))?.map_err(|e| e.to_string())?;
if line.contains("******") && line.contains(section_name) {
return Ok(());
}
}
}
/// 解析能级行
fn parse_level_line(line: &str) -> Result<LevelInputData, String> {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 7 {
return Err(format!("能级行字段不足: {}", line));
}
let mut level = LevelInputData::default();
// ENION (能量) - 处理 Fortran D 指数格式
let enion_str = parts[0].to_uppercase().replace('D', "E");
level.enion = enion_str.parse().map_err(|_| format!("无法解析能量: {}", parts[0]))?;
// G (统计权重) - 同样处理 D 指数
let g_str = parts[1].to_uppercase().replace('D', "E");
level.g = g_str.parse().map_err(|_| format!("无法解析统计权重: {}", parts[1]))?;
// NQUANT (主量子数)
level.nquant = parts[2].parse().map_err(|_| format!("无法解析量子数: {}", parts[2]))?;
// TYPLEV (能级类型,字符串,可能带引号)
if parts.len() > 3 {
// 处理带引号的字符串
let typlev = parts[3].trim_matches('\'').to_string();
level.typlev = typlev;
}
// IFWOP
if parts.len() > 4 {
level.ifwop = parts[4].parse().unwrap_or(0);
}
// FRODF
if parts.len() > 5 {
let frodf_str = parts[5].to_uppercase().replace('D', "E");
level.frodf = frodf_str.parse().unwrap_or(0.0);
}
// IMODL
if parts.len() > 6 {
level.imodl = parts[6].parse().unwrap_or(0);
}
Ok(level)
}
/// 解析连续跃迁行
fn parse_continuum_line(line: &str) -> Result<ContinuumInputData, String> {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 9 {
return Err(format!("连续跃迁行字段不足: {}", line));
}
let mut cont = ContinuumInputData::default();
cont.ii = parts[0].parse().map_err(|_| format!("无法解析 ii: {}", parts[0]))?;
cont.jj = parts[1].parse().map_err(|_| format!("无法解析 jj: {}", parts[1]))?;
cont.mode = parts[2].parse().map_err(|_| format!("无法解析 mode: {}", parts[2]))?;
cont.ifancy = parts[3].parse().map_err(|_| format!("无法解析 ifancy: {}", parts[3]))?;
cont.icolis = parts[4].parse().map_err(|_| format!("无法解析 icolis: {}", parts[4]))?;
cont.ifrq0 = parts[5].parse().map_err(|_| format!("无法解析 ifrq0: {}", parts[5]))?;
cont.ifrq1 = parts[6].parse().map_err(|_| format!("无法解析 ifrq1: {}", parts[6]))?;
cont.osc = parts[7].parse().map_err(|_| format!("无法解析 osc: {}", parts[7]))?;
cont.cparam = parts[8].parse().map_err(|_| format!("无法解析 cparam: {}", parts[8]))?;
if parts.len() > 9 {
cont.ncol = parts[9].parse().unwrap_or(0);
}
Ok(cont)
}
/// 解析谱线跃迁行(简化版,只读取基本信息)
fn parse_line_transition(line: &str) -> Result<LineInputData, String> {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 9 {
return Err(format!("谱线跃迁行字段不足: {}", line));
}
let mut line_data = LineInputData::default();
line_data.ii = parts[0].parse().map_err(|_| format!("无法解析 ii: {}", parts[0]))?;
line_data.jj = parts[1].parse().map_err(|_| format!("无法解析 jj: {}", parts[1]))?;
line_data.mode = parts[2].parse().map_err(|_| format!("无法解析 mode: {}", parts[2]))?;
line_data.ifancy = parts[3].parse().map_err(|_| format!("无法解析 ifancy: {}", parts[3]))?;
line_data.icolis = parts[4].parse().map_err(|_| format!("无法解析 icolis: {}", parts[4]))?;
line_data.ifrq0 = parts[5].parse().map_err(|_| format!("无法解析 ifrq0: {}", parts[5]))?;
line_data.ifrq1 = parts[6].parse().map_err(|_| format!("无法解析 ifrq1: {}", parts[6]))?;
line_data.osc = parts[7].parse().map_err(|_| format!("无法解析 osc: {}", parts[7]))?;
line_data.cparam = parts[8].parse().map_err(|_| format!("无法解析 cparam: {}", parts[8]))?;
if parts.len() > 9 {
line_data.ncol = parts[9].parse().unwrap_or(0);
}
Ok(line_data)
}
// ============================================================================
// 纯计算函数
// ============================================================================
/// 计算默认振子强度(用于未指定的连续跃迁)。
///
/// 使用氢原子近似公式。
pub fn compute_default_oscillator_strength(
zz: f64,
xq: f64,
fr0: f64,
) -> f64 {
if fr0 <= 0.0 || xq <= 0.0 {
return 0.0;
}
let mut sig0 = 2.815e-20 * zz * zz / (fr0 * 1e-16).powi(3) / xq.powi(5);
if zz > 1.9 {
sig0 *= 2.0;
}
if zz > 2.9 {
sig0 *= 1.5;
}
sig0
}
/// 计算氢振子强度(用于谱线)。
pub fn compute_hydrogen_oscillator_strength(
n1: i32,
n2: i32,
g: f64,
ifwop_jj: i32,
nquant_jj_1: i32,
nlmx: i32,
) -> f64 {
if n1 > 20 || n2 > 20 {
// 超出 OSH 表范围,需要外推
return 0.0;
}
let gh = 2.0 * (n1 * n1) as f64;
let mut osc = get_osh(n1, n2) * g / gh;
if ifwop_jj < 0 {
// 合并能级
osc = 0.0;
let jj0 = nquant_jj_1;
let j20 = nlmx.min(20);
if j20 >= jj0 {
for jtr in jj0..=j20 {
osc += get_osh(n1, jtr);
}
}
if nlmx > 20 {
// 外推到 n > 20
let xii = (n1 * n1) as f64;
let mut suf = 0.0;
for jtr in 21..=nlmx {
let xj = jtr as f64;
let xjj = xj * xj;
let xjtr = xj / (xjj - xii);
suf += xjtr.powi(3);
}
let xitr = (400.0 - xii) / 20.0;
osc += get_osh(n1, 20) * suf * xitr.powi(3);
}
}
osc
}
// ============================================================================
// RDATA 主处理函数
// ============================================================================
/// 处理能级数据(纯计算部分)。
///
/// # 参数
/// * `params` - 输入参数
/// * `level_inputs` - 从文件读取的能级数据
///
/// # 返回值
/// 处理后的能级数据数组
pub fn process_levels_pure(
params: &RdataParams,
level_inputs: &[LevelInputData],
) -> Vec<LevelData> {
let nlevs = params.iondat.nlevs[params.ion as usize - 1] as usize;
let nfirst = params.ionpar.nfirst[params.ion as usize - 1];
let zz = params.ionpar.iz[params.ion as usize - 1] as f64;
let mut levels = Vec::with_capacity(nlevs);
let mut lbpfx = true;
for (il, input) in level_inputs.iter().enumerate().take(nlevs) {
let i = (nfirst as usize) + il;
let iq = (i + 1 - nfirst as usize) as i32; // 相对于离子的量子数
let mut level = process_level(input, i, zz, iq, params.ispodf);
// 检查 LBPFX 条件
let imodl_ok = level.imodl == 0;
// 简化:假设 iifix 总是 0
lbpfx = lbpfx && imodl_ok;
levels.push(level);
}
levels
}
/// 处理连续跃迁数据(纯计算部分)。
pub fn process_continua_pure(
params: &RdataParams,
inputs: &[ContinuumInputData],
enion: &[f64],
nfirst_ion: i32,
nnext_ion: i32,
nlevs: i32,
) -> (Vec<ContinuumTransition>, i32, i32, i32, bool) {
let ii0 = nfirst_ion - 1;
let illim = 0; // 简化
let mut continua = Vec::new();
let mut itr = 0;
let mut ic = 0;
let mut nhod = 0;
let mut lasv = false;
for input in inputs {
let mut ct = ContinuumTransition::default();
// 索引转换
let (ii, jj) = if input.jj < 1000 {
let jcorr = if input.ii == 1 { nlevs + 1 - input.jj } else { 0 };
(input.ii + ii0, input.jj + ii0 + jcorr)
} else {
// jj >= 1000 表示电离态
(input.ii + ii0, input.jj)
};
ct.ii = ii;
ct.jj = jj;
ct.mode = input.mode;
ct.ifancy = input.ifancy;
ct.osc0 = input.osc;
ct.icol = input.icolis;
ct.cpar = input.cparam;
ct.ifc0 = input.ifrq0;
ct.ifc1 = input.ifrq1;
itr += 1;
ct.itr = itr;
// 计算频率
let enion_ii = enion.get(ii as usize - 1).copied().unwrap_or(0.0);
let enion_jj = enion.get(jj as usize - 1).copied().unwrap_or(0.0);
let enion_nk = enion.get(nnext_ion as usize - 1).copied().unwrap_or(0.0);
ct.fr0 = if let Some(fr0inp) = input.fr0inp {
if fr0inp < 1e10 {
C_LIGHT / fr0inp
} else {
fr0inp
}
} else {
(enion_ii - enion_jj + enion_nk) / H
};
// FR0PCI 处理
ct.fr0pc = if let Some(fr0pci) = input.fr0pci {
if fr0pci < 1e10 {
C_LIGHT / fr0pci
} else {
fr0pci
}
} else {
0.0
};
// 特殊处理氢
if params.ion == params.ielh {
if input.ii == 1 && params.cutlym != 0.0 {
ct.fr0pc = params.cutlym;
}
if input.ii == 2 && params.cutbal != 0.0 {
ct.fr0pc = params.cutbal;
}
}
ic += 1;
ct.ic = ic;
// 检查 LASV 标志
if input.ifancy > 49 && input.ifancy < 100 {
lasv = true;
}
// 检查是否跳过
let should_skip = ii < illim || ct.fr0 <= params.frlmin || ct.fr0 >= params.frlmax;
if should_skip {
ct.mode = 0;
}
continua.push(ct);
}
(continua, itr, ic, nhod, lasv)
}
// ============================================================================
// 填充 AtomicData 结构体
// ============================================================================
/// 填充原子数据到 AtomicData 结构体。
///
/// 将从文件读取的能级、连续跃迁、谱线跃迁数据填充到全局原子数据结构中。
///
/// # 参数
/// * `atomic` - 原子数据结构体(可变引用)
/// * `ion_idx` - 离子索引0-based
/// * `nfirst` - 离子起始能级索引1-based
/// * `iat` - 原子序数
/// * `iz` - 电离态
/// * `ff_ion` - 电离势 (Ry)
/// * `charg2` - 电荷²
/// * `levels` - 能级输入数据
/// * `continua` - 连续跃迁输入数据
/// * `lines` - 谱线跃迁输入数据
/// * `teff` - 有效温度
pub fn populate_atomic_data(
atomic: &mut crate::tlusty::state::atomic::AtomicData,
ion_idx: usize,
nfirst: i32,
iat: i32,
iz: i32,
ff_ion: f64,
charg2: f64,
levels: &[LevelInputData],
continua: &[ContinuumInputData],
lines: &[LineInputData],
teff: f64,
) -> (i32, i32) {
let nlevs = levels.len() as i32;
// 填充离子参数
atomic.ionpar.ff[ion_idx] = ff_ion;
atomic.ionpar.charg2[ion_idx] = charg2;
atomic.ionpar.nfirst[ion_idx] = nfirst;
atomic.ionpar.nlast[ion_idx] = nfirst + nlevs - 1;
atomic.ionpar.nnext[ion_idx] = nfirst + nlevs;
atomic.ionpar.iz[ion_idx] = iat; // 原子序数 Z
// 填充离子数据索引
atomic.iondat.iati[ion_idx] = iat;
atomic.iondat.izi[ion_idx] = iz;
atomic.iondat.nlevs[ion_idx] = nlevs;
atomic.iondat.nllim[ion_idx] = nfirst + nlevs - 1;
// ZZ: 有效核电荷 = Z - iz + 1对于类氢离子
let zz = (iat - iz + 1) as f64;
// 填充能级参数
let mut ntrans = 0i32;
let mut ntranc = 0i32;
for (il, input) in levels.iter().enumerate() {
let level_idx = (nfirst as usize) + il - 1; // 转换为 0-based
// 能量转换
let e = input.enion.abs();
let e0 = convert_energy(e, zz, (il + 1) as i32);
let enion_value = if input.enion >= 0.0 { e0 } else { -e0 };
atomic.levpar.enion[level_idx] = enion_value;
atomic.levpar.g[level_idx] = if input.g == 0.0 {
2.0 * ((il + 1) as f64).powi(2)
} else {
input.g
};
atomic.levpar.nquant[level_idx] = if input.nquant == 0 {
(il + 1) as i32
} else {
input.nquant.abs()
};
atomic.levpar.iatm[level_idx] = iat;
atomic.levpar.iel[level_idx] = (ion_idx + 1) as i32; // 1-based ion index
atomic.levpar.indlev[level_idx] = (level_idx + 1) as i32;
// LTE 标志(负量子数表示 LTE
if input.nquant < 0 {
atomic.levpar.iltlev[level_idx] = 1;
}
// 模型能级
atomic.levpar.imodl[level_idx] = input.imodl;
}
// 填充连续跃迁参数
for input in continua {
let itr = ntrans as usize;
if itr >= MTRANS {
break;
}
// 索引转换
let (ii, jj) = if input.jj < 1000 {
(input.ii + nfirst - 1, input.jj + nfirst - 1)
} else {
(input.ii + nfirst - 1, input.jj)
};
// 计算频率
let enion_ii = atomic.levpar.enion.get(ii as usize - 1).copied().unwrap_or(0.0);
let enion_jj = if input.jj < 1000 {
atomic.levpar.enion.get(jj as usize - 1).copied().unwrap_or(0.0)
} else {
0.0
};
let enion_nk = 0.0; // 简化:下一个离子的基态能级
let fr0 = (enion_ii - enion_jj + enion_nk) / H;
atomic.trapar.fr0[itr] = fr0;
atomic.trapar.osc0[itr] = input.osc;
atomic.trapar.cpar[itr] = input.cparam;
atomic.trapar.ilow[itr] = ii;
atomic.trapar.iup[itr] = jj;
atomic.trapar.icol[itr] = input.icolis;
atomic.trapar.ifc0[itr] = input.ifrq0;
atomic.trapar.ifc1[itr] = input.ifrq1;
// 标记为连续跃迁
atomic.trapar.itrcon[itr] = 1;
ntrans += 1;
ntranc += 1;
}
// 填充谱线跃迁参数
for input in lines {
let itr = ntrans as usize;
if itr >= MTRANS {
break;
}
let ii = input.ii + nfirst - 1;
let jj = input.jj + nfirst - 1;
// 计算频率
let enion_ii = atomic.levpar.enion.get(ii as usize - 1).copied().unwrap_or(0.0);
let enion_jj = atomic.levpar.enion.get(jj as usize - 1).copied().unwrap_or(0.0);
let fr0 = (enion_jj - enion_ii) / H;
atomic.trapar.fr0[itr] = fr0;
atomic.trapar.osc0[itr] = input.osc;
atomic.trapar.cpar[itr] = input.cparam;
atomic.trapar.ilow[itr] = ii;
atomic.trapar.iup[itr] = jj;
atomic.trapar.icol[itr] = input.icolis;
atomic.trapar.ifr0[itr] = input.ifrq0;
atomic.trapar.ifr1[itr] = input.ifrq1;
// 标记为谱线跃迁
atomic.trapar.itrcon[itr] = 0;
ntrans += 1;
}
(ntrans, ntranc)
}
// ============================================================================
// 测试
// ============================================================================
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_convert_energy_zero() {
// E = 0 时使用氢原子公式
let e = convert_energy(0.0, 1.0, 2);
// EH * Z² / n² = EH * 1 / 4
assert_relative_eq!(e, EH / 4.0, epsilon = 1e-20);
}
#[test]
fn test_convert_energy_ev() {
// eV 转 erg
let e = convert_energy(10.0, 1.0, 2);
assert_relative_eq!(e, 10.0 * EV_TO_ERG, epsilon = 1e-25);
}
#[test]
fn test_convert_energy_cm1() {
// cm⁻¹ 转 erg
let e = convert_energy(1000.0, 1.0, 2);
assert_relative_eq!(e, 1000.0 * CM1_TO_ERG, epsilon = 1e-28);
}
#[test]
fn test_convert_energy_hz() {
// Hz 转 erg
let e = convert_energy(1e10, 1.0, 2);
assert_relative_eq!(e, H * 1e10, epsilon = 1e-40);
}
#[test]
fn test_get_osh() {
// OSH(1,2) = 0.4162 (氢 Lyman-alpha)
let osh = get_osh(1, 2);
assert_relative_eq!(osh, 0.4162, epsilon = 1e-4);
}
#[test]
fn test_get_osh_out_of_range() {
assert_eq!(get_osh(0, 1), 0.0);
assert_eq!(get_osh(21, 1), 0.0);
assert_eq!(get_osh(1, 21), 0.0);
}
#[test]
fn test_compute_default_oscillator_strength() {
let osc = compute_default_oscillator_strength(1.0, 2.0, 1e15);
assert!(osc > 0.0);
}
#[test]
fn test_process_level_basic() {
let input = LevelInputData {
enion: 10.0, // eV
g: 4.0,
nquant: 2,
typlev: "test".to_string(),
ifwop: 0,
frodf: 0.0,
imodl: 0,
};
let level = process_level(&input, 0, 1.0, 2, 0);
assert_relative_eq!(level.enion, 10.0 * EV_TO_ERG, epsilon = 1e-25);
assert_eq!(level.g, 4.0);
assert_eq!(level.nquant, 2);
assert_eq!(level.iltlev, 0);
}
#[test]
fn test_process_level_negative_quantum() {
let input = LevelInputData {
enion: 10.0,
g: 4.0,
nquant: -3, // 负值表示 LTE
typlev: "test".to_string(),
ifwop: 0,
frodf: 0.0,
imodl: 0,
};
let level = process_level(&input, 0, 1.0, 3, 0);
assert_eq!(level.nquant, 3);
assert_eq!(level.iltlev, 1);
}
#[test]
fn test_process_level_zero_g() {
let input = LevelInputData {
enion: 10.0,
g: 0.0, // 应使用 2*n²
nquant: 3,
typlev: "test".to_string(),
ifwop: 0,
frodf: 0.0,
imodl: 0,
};
let level = process_level(&input, 0, 1.0, 3, 0);
assert_eq!(level.g, 2.0 * 9.0); // 2 * n²
}
}