316 lines
8.3 KiB
Rust
316 lines
8.3 KiB
Rust
//! 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");
|
||
}
|
||
}
|