//! 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, /// 频率设置 pub frequencies: FrequencyParams, /// 原子数据 pub atoms: Vec, /// 离子数据 pub ions: Vec, /// 谱线数据 pub transitions: Option, } /// 频率参数 #[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, } /// 单个跃迁 #[derive(Debug, Clone)] pub struct Transition { /// 下能级索引 pub ilow: usize, /// 上能级索引 pub iup: usize, /// 振子强度 pub fvalue: f64, } /// 读取输入文件 pub fn read_input_file>(path: P) -> Result { let reader = FortranReader::from_file(path)?; InputParser::parse(reader) } /// 输入文件解析器 pub struct InputParser; impl InputParser { /// 解析输入文件 pub fn parse(mut reader: FortranReader) -> Result { // 第 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( reader: &mut FortranReader, ) -> Result> { // 跳过可能的分隔行 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(reader: &mut FortranReader) -> 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"); } }