SpectraRust/.claude/skills/fortran-to-rust/SKILL.md
2026-03-23 15:45:52 +08:00

11 KiB
Raw Blame History

name description
fortran-to-rust 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 源码

cat tlusty/extracted/TARGET.f

检查清单

[ ] INCLUDE 文件COMMON 块 → src/state/
[ ] 函数参数和返回值
[ ] 调用的其他函数(是否已实现?)
[ ] 是否有 I/O 操作READ/WRITE/OPEN
[ ] DATA 语句(已预提取到 src/data.rs

重要:不要删减任何逻辑,完整实现 Fortran 功能。

Step 3: 创建 Rust 模块


touch src/math/TARGET.rs

Step 4: 实现函数

命名映射:

Fortran Rust
FUNCTION pub fn
SUBROUTINE pub fn(返回值用元组或结构体)
COMMON 块 结构体参数

Step 5: 添加到 mod.rs

// src/math/mod.rs 或 src/io/mod.rs
mod target;
pub use target::target;

Step 6: 编写测试

注意: 需要编写完整的真实的函数测试,而不只是测试变量

#[cfg(test)]
mod tests {
    use super::*;
    use approx::assert_relative_eq;

    #[test]
    fn test_basic() {
        // 基本功能测试
    }
}

Step 7: 运行测试

# 推荐:静默警告
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

// ❌ 混合(难以测试)
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(&params);
    let mut writer = FortranWriter::to_file(output_path)?;
    write_result(&mut writer, &result)?;
    Ok(())
}

模式 2: 使用 I/O 兼容层

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() 自动转换

单元号映射

// 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

矩阵存储

// 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()?;
    }
}

类型推断

// ❌ 编译错误
let result = (z1 - z2).powi(2);

// ✅ 显式乘法
let result = (z1 - z2) * (z1 - z2);

I/O 生命周期

// ❌ 返回引用,但修改了源
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 模型文件往返
use approx::assert_relative_eq;

#[test]
fn test_calculation() {
    let result = calculate(35000.0);
    assert_relative_eq!(result, expected, epsilon = 1e-7);
}

I/O 测试模式

#[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);
    }
}

测试命令

# 单模块测试(推荐)
RUSTFLAGS="-A warnings" cargo test target 2>&1 | tail -10

# I/O 模块测试
cargo test io:: 2>&1 | grep -E "^test |^test result"

# 全量测试(谨慎!可能卡死)
# cargo test

代码规范

文件头注释

//! 模块简要说明。
//!
//! 重构自 TLUSTY `filename.f`

函数注释

/// 函数功能说明。
///
/// # 参数
/// * `x` - 参数说明
///
/// # 返回值
/// 返回值说明
pub fn func(x: f64) -> f64 { ... }

I/O 模块结构

// 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(&params);
    // 可选:写输出
    Ok(result)
}

快速命令参考

# === 分析 ===
# 查看重构进度
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 记录:

# 记录到 .learnings/LEARNINGS.md
# 格式:
## [LRN-YYYYMMDD-XXX] category
**Priority**: high | medium | low
**Status**: pending | resolved | promoted

### Summary
一句话总结

### Details
详细说明

### Metadata
- Source: 模块名
- Tags: 关键词