466 lines
11 KiB
Markdown
466 lines
11 KiB
Markdown
---
|
||
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<f64> {
|
||
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<String> {
|
||
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<TargetOutput> {
|
||
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: 关键词
|
||
```
|