--- name: fortran-to-rust description: "Fortran 到 Rust 的重构指南和工作流。触发条件:(1) 刚使用过fortran-analyzer skills 获得需要重构的模块(2)用户提到重构 Fortran 到 Rust;(3) 开始新的 Fortran 函数重构。(4) 继续重构下一个fortran模块。提供完整重构流程、常见陷阱、测试规范。" --- # Fortran → Rust 重构指南 将 TLUSTY/SYNSPEC 的 Fortran 函数重构为 Rust。 --- ## 重构流程 ### Step 1: 选择目标函数 注意:使用 fortran-analyzer skills 获取需要重构的模块。 **选择原则**: - **必须**选择fortran-analyzer skills推荐的第一个模块 - 不要因为行数多、复杂或者 COMMON 依赖就回避它们,不要回避复杂函数,它们往往是重构重点。 ### Step 2: 分析 Fortran 源码 ```bash cat tlusty/extracted/TARGET.f ``` **检查清单**: ``` [ ] INCLUDE 文件(COMMON 块 → src/state/) [ ] 函数参数和返回值 [ ] 调用的其他函数(是否已实现?) [ ] 是否有 I/O 操作(READ/WRITE/OPEN) [ ] DATA 语句(已预提取到 src/data.rs) ``` **重要**:不要删减任何逻辑,完整实现 Fortran 功能。 ### Step 3: 创建 Rust 模块 ```bash touch src/math/TARGET.rs ``` ### Step 4: 实现函数 **命名映射**: | Fortran | Rust | |---------|------| | `FUNCTION` | `pub fn` | | `SUBROUTINE` | `pub fn`(返回值用元组或结构体)| | COMMON 块 | 结构体参数 | ### Step 5: 添加到 mod.rs ```rust // src/math/mod.rs 或 src/io/mod.rs mod target; pub use target::target; ``` ### Step 6: 编写测试 **注意**: 需要编写完整的真实的函数测试,而不只是测试变量 ```rust #[cfg(test)] mod tests { use super::*; use approx::assert_relative_eq; #[test] fn test_basic() { // 基本功能测试 } } ``` ### Step 7: 运行测试 ```bash # 推荐:静默警告 RUSTFLAGS="-A warnings" cargo test target 2>&1 | tail -10 # 或简洁输出 cargo test target -- --quiet 2>&1 | grep -E "^test|^running|^test result" ``` --- ## I/O 模块重构 ### 何时使用 I/O 兼容层 当模块包含以下操作时: - `READ(unit, ...)` - 从文件/标准输入读取 - `WRITE(unit, ...)` - 写入文件/标准输出 - `OPEN(...)` - 打开文件 ### I/O 模块分类 | 类别 | 策略 | 示例 | |------|------|------| | **调试输出** | 删除或用 `log::debug!` | fort.10, fort.14, fort.18 | | **进度输出** | 保留,用 `FortranWriter` | fort.6, fort.9 | | **模型 I/O** | 必须兼容,用 `model.rs` | fort.7, fort.8 | | **参数文件** | 用 `FortranReader` | fort.5, fort.55 | ### I/O 重构模式 #### 模式 1: 分离计算与 I/O ```rust // ❌ 混合(难以测试) pub fn read_and_calculate() -> Result { let input = read_from_file()?; // I/O let result = calculate(input); // 计算 write_to_file(result)?; // I/O Ok(result) } // ✅ 分离(可独立测试) pub fn calculate(params: &CalcParams) -> CalcResult { // 纯计算,无 I/O } pub fn run(input_path: &str, output_path: &str) -> Result<()> { let reader = FortranReader::from_file(input_path)?; let params = parse_input(reader)?; let result = calculate(¶ms); let mut writer = FortranWriter::to_file(output_path)?; write_result(&mut writer, &result)?; Ok(()) } ``` #### 模式 2: 使用 I/O 兼容层 ```rust use crate::io::{FortranReader, FortranWriter, ModelFile}; // 读取输入 let mut reader = FortranReader::from_file("fort.5")?; let teff: f64 = reader.read_value()?; let grav: f64 = reader.read_value()?; // 读取模型 let model = ModelFile::read(FortranReader::from_file("fort.8")?)?; // 写入模型(与 Fortran 字节级兼容) ModelFile::write(&model, BufWriter::new(File::create("fort.7")?))?; ``` ### Fortran I/O 特性处理 | Fortran 特性 | Rust 处理 | |-------------|----------| | `READ(*,*)` 自由格式 | `FortranReader::read_value()` | | `READ(IBUFF,*)` 缓冲区 | `FortranReader` 从字符串创建 | | `WRITE(6,601)` FORMAT | `FortranWriter` + `format_exp_fortran` | | `!` 行内注释 | `FortranReader::read_line()` 自动跳过 | | `*` 固定格式注释 | `FortranReader::read_line()` 自动跳过 | | `'string'` 引号字符串 | `FortranReader::read_string()` | | `D` 指数符号 | `FromFortran::from_fortran_str()` 自动转换 | ### 单元号映射 ```rust // src/io/mod.rs pub mod units { pub const STDIN: u8 = 5; pub const STDOUT: u8 = 6; pub const MODEL_OUT: u8 = 7; pub const MODEL_IN: u8 = 8; pub const CONV_HIST: u8 = 9; pub const SCRATCH: [u8; 3] = [91, 92, 93]; } ``` ### 有 I/O 的模块列表 | 模块 | 主要 I/O | 重构策略 | |------|---------|---------| | **INITIA** | 打开所有输入文件 | 高:入口点 | | **OUTPUT** | fort.7, fort.12, fort.22 | 高:模型输出 | | **READBF** | 原子数据文件 | 中:用 FortranReader | | **KURUCZ** | Kurucz 格式模型 | 中:格式转换 | | **NSTPAR** | 非标准参数 | 低:可选 | | **TIMING** | fort.69 | 低:调试 | --- ## 常见陷阱 ### 索引和表达式 | 问题 | Fortran | Rust | |------|---------|------| | 数组索引 | `arr(i)` 1-indexed | `arr[i-1]` 0-indexed | | 负对数 | `-LOG(X)` | `-ln(X)` 不是 `ln(-X)` | | 幂运算 | `x**2` | `(x)*(x)` 避免类型歧义 | | 递减循环 | `DO I=N,1,-1` | 用 `isize` 不用 `usize` | ### 矩阵存储 ```rust // Fortran 列优先: A(j,i) = a[(i-1)*N + (j-1)] // 读取时: for i in 0..n { for j in 0..m { a[i * m + j] = reader.read_value()?; } } ``` ### 类型推断 ```rust // ❌ 编译错误 let result = (z1 - z2).powi(2); // ✅ 显式乘法 let result = (z1 - z2) * (z1 - z2); ``` ### I/O 生命周期 ```rust // ❌ 返回引用,但修改了源 fn extract_token(&mut self) -> Result<&str> { let token = &self.remaining[..]; self.remaining = "...".to_string(); // 借用冲突! Ok(token) } // ✅ 返回 String fn extract_token(&mut self) -> Result { let token = self.remaining[..end].to_string(); self.remaining = self.remaining[end..].to_string(); Ok(token) } ``` --- ## 测试规范 ### 精度要求 | 函数类型 | 容差 | 示例 | |---------|------|------| | 简单数学运算 | `1e-10` | `exp`, `log` | | 多项式近似 | `1e-7` | Abramowitz-Stegun | | f32 数组 | `1e-24` | Voigt 轮廓 | | I/O 读写 | `1e-6` | 模型文件往返 | ```rust use approx::assert_relative_eq; #[test] fn test_calculation() { let result = calculate(35000.0); assert_relative_eq!(result, expected, epsilon = 1e-7); } ``` ### I/O 测试模式 ```rust #[test] fn test_model_roundtrip() { // 创建测试模型 let mut model = ModelState::new(3, 3); model.temp = vec![10000.0, 8000.0, 5000.0]; // 写入内存 let mut buffer = Vec::new(); ModelFile::write(&model, BufWriter::new(&mut buffer)).unwrap(); // 读回 let cursor = Cursor::new(buffer); let reader = FortranReader::new(BufReader::new(cursor)); let model2 = ModelFile::read(reader).unwrap(); // 验证 for i in 0..model.nd { assert_relative_eq!(model2.temp[i], model.temp[i], epsilon = 1e-6); } } ``` ### 测试命令 ```bash # 单模块测试(推荐) RUSTFLAGS="-A warnings" cargo test target 2>&1 | tail -10 # I/O 模块测试 cargo test io:: 2>&1 | grep -E "^test |^test result" # 全量测试(谨慎!可能卡死) # cargo test ``` --- ## 代码规范 ### 文件头注释 ```rust //! 模块简要说明。 //! //! 重构自 TLUSTY `filename.f` ``` ### 函数注释 ```rust /// 函数功能说明。 /// /// # 参数 /// * `x` - 参数说明 /// /// # 返回值 /// 返回值说明 pub fn func(x: f64) -> f64 { ... } ``` ### I/O 模块结构 ```rust // src/io/TARGET.rs //! 模块说明 use super::{FortranReader, FortranWriter, Result}; /// 参数结构体 pub struct TargetParams<'a> { pub input: &'a [f64], } /// 输出结构体 pub struct TargetOutput { pub result: f64, } /// 纯计算函数(可测试) pub fn target_pure(params: &TargetParams) -> TargetOutput { // 实现 } /// 带 I/O 的入口(调用纯计算) pub fn target(input_path: &str, output_path: &str) -> Result { let reader = FortranReader::from_file(input_path)?; let params = parse_params(reader)?; let result = target_pure(¶ms); // 可选:写输出 Ok(result) } ``` --- ## 快速命令参考 ```bash # === 分析 === # 查看重构进度 python3 .claude/skills/fortran-analyzer/scripts/analyze_fortran.py --priority | head -20 # 查看函数依赖树 python3 .claude/skills/fortran-analyzer/scripts/analyze_fortran.py --tree FUNCTION_NAME # === 编译 === # 编译检查 cargo build 2>&1 | grep error # 编译检查(静默警告) RUSTFLAGS="-A warnings" cargo build 2>&1 | tail -5 # === 测试 === # 单模块测试(推荐) RUSTFLAGS="-A warnings" cargo test target 2>&1 | tail -10 # I/O 模块测试 cargo test io:: 2>&1 | grep -E "^test |^test result" ``` --- ## 项目结构 ``` rust/src/ ├── io/ # I/O 兼容层 │ ├── mod.rs # 模块入口,单元号常量 │ ├── reader.rs # FortranReader(自由格式) │ ├── writer.rs # FortranWriter(格式化输出) │ ├── model.rs # fort.7/8 模型文件 │ ├── input.rs # fort.5 主输入 │ └── format.rs # FORMAT 解析 ├── math/ # 纯计算函数 (120+ 个 .rs 文件) ├── state/ # COMMON 块 (8 个模块) │ ├── constants.rs # BASICS.FOR │ ├── atomic.rs # ATOMIC.FOR │ ├── model.rs # MODELQ.FOR │ ├── arrays.rs # ARRAY1.FOR │ ├── iterat.rs # ITERAT.FOR │ ├── alipar.rs # ALIPAR.FOR │ └── odfpar.rs # ODFPAR.FOR └── data.rs # 静态数据(DATA 语句) ``` --- ## 相关文档 | 文档 | 内容 | |------|------| | `.learnings/LEARNINGS.md` | 14 个实际案例(索引、表达式、COMMON 等) | | `docs/TLUSTY_IO_FILES.md` | 完整 I/O 文件映射 | | `docs/SYNSPEC_IO_FILES.md` | SYNSPEC I/O 文件 | | `docs/IO_COMPATIBILITY_LAYER.md` | I/O 兼容层设计 | --- ## 相关 Skills | Skill | 用途 | 状态 | |-------|------|------| | `fortran-extractor` | 提取 Fortran 文件 | ✅ 已完成 | | `fortran-analyzer` | 分析依赖关系 | 活跃使用 | | `fortran-to-rust` | 执行重构 | 本 skill | | `self-improving-agent` | 记录经验教训 | 建议配合使用 | --- ## 经验记录 重构过程中遇到问题或发现新模式时,使用 self-improving-agent 记录: ```bash # 记录到 .learnings/LEARNINGS.md # 格式: ## [LRN-YYYYMMDD-XXX] category **Priority**: high | medium | low **Status**: pending | resolved | promoted ### Summary 一句话总结 ### Details 详细说明 ### Metadata - Source: 模块名 - Tags: 关键词 ```