SpectraRust/src/io/input.rs
2026-03-23 15:45:52 +08:00

316 lines
8.3 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 主输入文件 (fort.5) 解析
//!
//! 解析 TLUSTY 的主输入文件,包含所有模型参数。
use std::path::Path;
use super::reader::FortranReader;
use super::Result;
/// TLUSTY 输入参数
#[derive(Debug, Clone)]
pub struct InputParams {
/// 有效温度 (K)
pub teff: f64,
/// 表面重力 (log g, cgs)
pub grav: f64,
/// LTE 开关
pub lte: bool,
/// 灰大气初始模型开关
pub ltgrey: bool,
/// 可选参数文件名
pub finstd: Option<String>,
/// 频率设置
pub frequencies: FrequencyParams,
/// 原子数据
pub atoms: Vec<AtomParams>,
/// 离子数据
pub ions: Vec<IonParams>,
/// 谱线数据
pub transitions: Option<TransitionParams>,
}
/// 频率参数
#[derive(Debug, Clone)]
pub struct FrequencyParams {
/// 频率点数
pub nfreq: usize,
/// 频率读取模式 (0=内部生成, >0=从文件读取)
pub nfread: i32,
}
/// 原子参数
#[derive(Debug, Clone)]
pub struct AtomParams {
/// 输入模式 (0=标准, 1=从文件读取)
pub mode: i32,
/// 丰度模式 (0=标准太阳丰度)
pub abn: i32,
/// 配分函数模式
pub modpf: i32,
}
/// 离子参数
#[derive(Debug, Clone)]
pub struct IonParams {
/// 原子序数 (1=H, 2=He, ...)
pub iat: usize,
/// 电离态 (0=中性, 1=一次电离, ...)
pub iz: usize,
/// 能级数
pub nlevs: usize,
/// 最后能级索引
pub ilast: i32,
/// 谱线能级数
pub ilvlin: i32,
/// 非标准数据标志
pub nonstd: i32,
/// 离子类型标识
pub typion: String,
/// 数据文件名
pub filei: String,
}
/// 谱线跃迁参数
#[derive(Debug, Clone)]
pub struct TransitionParams {
/// 跃迁数
pub ntrans: usize,
/// 跃迁列表
pub transitions: Vec<Transition>,
}
/// 单个跃迁
#[derive(Debug, Clone)]
pub struct Transition {
/// 下能级索引
pub ilow: usize,
/// 上能级索引
pub iup: usize,
/// 振子强度
pub fvalue: f64,
}
/// 读取输入文件
pub fn read_input_file<P: AsRef<Path>>(path: P) -> Result<InputParams> {
let reader = FortranReader::from_file(path)?;
InputParser::parse(reader)
}
/// 输入文件解析器
pub struct InputParser;
impl InputParser {
/// 解析输入文件
pub fn parse<R: std::io::BufRead>(mut reader: FortranReader<R>) -> Result<InputParams> {
// 第 1 行: TEFF, GRAV
reader.read_line()?;
let teff: f64 = reader.read_value()?;
let grav: f64 = reader.read_value()?;
// 第 2 行: LTE, LTGREY
reader.read_line()?;
let lte: bool = reader.read_value()?;
let ltgrey: bool = reader.read_value()?;
// 第 3 行: 可选参数文件名
reader.read_line()?;
let finstd_str: String = reader.read_string()?;
let finstd = if finstd_str.trim().is_empty() || finstd_str == "''" {
None
} else {
Some(finstd_str)
};
// 跳过分隔行
skip_separator(&mut reader)?;
// 频率设置skip_separator 已经读取了第一行)
let nfread: i32 = reader.read_value()?;
let frequencies = FrequencyParams {
nfreq: if nfread > 0 { nfread as usize } else { 50 },
nfread,
};
// 跳过分隔行
skip_separator(&mut reader)?;
// 原子数据skip_separator 已经读取了第一行)
let natoms: usize = reader.read_value()?;
let mut atoms = Vec::with_capacity(natoms);
for _ in 0..natoms {
reader.read_line()?;
let mode: i32 = reader.read_value()?;
let abn: i32 = reader.read_value()?;
let modpf: i32 = reader.read_value()?;
atoms.push(AtomParams { mode, abn, modpf });
}
// 跳过分隔行
skip_separator(&mut reader)?;
// 离子数据skip_separator 已经读取了第一行)
let mut ions = Vec::new();
loop {
let iat: i32 = reader.read_value()?;
if iat == 0 {
break; // 结束标志
}
let iz: i32 = reader.read_value()?;
let nlevs: i32 = reader.read_value()?;
let ilast: i32 = reader.read_value()?;
let ilvlin: i32 = reader.read_value()?;
let nonstd: i32 = reader.read_value()?;
let typion: String = reader.read_string()?;
let filei: String = reader.read_string()?;
ions.push(IonParams {
iat: iat as usize,
iz: iz as usize,
nlevs: nlevs as usize,
ilast,
ilvlin,
nonstd,
typion,
filei,
});
// 读取下一行
reader.read_line()?;
}
// 尝试读取跃迁数据(可选)
let transitions = Self::try_read_transitions(&mut reader)?;
Ok(InputParams {
teff,
grav,
lte,
ltgrey,
finstd,
frequencies,
atoms,
ions,
transitions,
})
}
/// 尝试读取跃迁数据
fn try_read_transitions<R: std::io::BufRead>(
reader: &mut FortranReader<R>,
) -> Result<Option<TransitionParams>> {
// 跳过可能的分隔行
if reader.remaining().trim().starts_with('*') {
reader.read_line()?;
}
// 尝试读取跃迁数
let ntrans: i32 = match reader.read_value() {
Ok(v) => v,
Err(_) => return Ok(None),
};
if ntrans <= 0 {
return Ok(None);
}
let mut transitions = Vec::with_capacity(ntrans as usize);
for _ in 0..ntrans {
let ilow: usize = reader.read_value()?;
let iup: usize = reader.read_value()?;
let fvalue: f64 = reader.read_value()?;
transitions.push(Transition {
ilow,
iup,
fvalue,
});
}
Ok(Some(TransitionParams {
ntrans: ntrans as usize,
transitions,
}))
}
}
/// 跳过分隔行(以 * 开头)
fn skip_separator<R: std::io::BufRead>(reader: &mut FortranReader<R>) -> Result<()> {
loop {
reader.read_line()?;
if reader.remaining().trim().starts_with('*') {
continue;
}
break;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_simple_input() {
let input = r#"
35000. 4.0 ! TEFF, GRAV
T T ! LTE, LTGRAY
'' ! no change
*-----------------------------------------------------------------
* frequencies
50
*-----------------------------------------------------------------
* atoms
2
0 0 0
0 0 0
*-----------------------------------------------------------------
* ions
1 0 9 0 100 0 ' H 1' './data/h1.dat'
0 0 0 -1 0 0 ' ' ' '
"#;
let reader = FortranReader::from_str(input);
let params = InputParser::parse(reader).unwrap();
assert!((params.teff - 35000.0).abs() < 1.0);
assert!((params.grav - 4.0).abs() < 0.01);
assert!(params.lte);
assert!(params.ltgrey);
assert!(params.finstd.is_none());
assert_eq!(params.atoms.len(), 2);
assert_eq!(params.ions.len(), 1);
assert_eq!(params.ions[0].iat, 1);
assert_eq!(params.ions[0].typion, " H 1"); // 4-character ion type with leading space
}
#[test]
fn test_parse_ion_params() {
let input = " 1 0 9 0 100 0 ' H 1' './data/h1.dat'";
let mut reader = FortranReader::from_str(input);
let iat: i32 = reader.read_value().unwrap();
let iz: i32 = reader.read_value().unwrap();
let nlevs: i32 = reader.read_value().unwrap();
let ilast: i32 = reader.read_value().unwrap();
let ilvlin: i32 = reader.read_value().unwrap();
let nonstd: i32 = reader.read_value().unwrap();
let typion: String = reader.read_string().unwrap();
let filei: String = reader.read_string().unwrap();
assert_eq!(iat, 1);
assert_eq!(iz, 0);
assert_eq!(nlevs, 9);
assert_eq!(typion, " H 1");
assert_eq!(filei, "./data/h1.dat");
}
}