5.7 KiB
5.7 KiB
TLUSTY/SYNSPEC Rust 重构计划
概述
目标: 将 TLUSTY/SYNSPEC Fortran 代码渐进式重构为 Rust
策略: 从 tlusty/extracted/ 中无 COMMON 依赖的纯函数开始,一个文件一个文件地重构
代码规模:
- TLUSTY: 304 个单元,195 个纯函数
- SYNSPEC: 168 个单元,93 个纯函数
1. 重构原则
- 渐进式: 每次只重构一个文件,保持系统可用
- 测试驱动: 每个重构的函数必须有测试验证
- 精度保证: 与 Fortran 输出对比,相对误差 < 1e-10
- 文档先行: 记录每个函数的算法和边界条件
2. 源文件位置
tlusty/extracted/ # TLUSTY 拆分后的文件
├── expo.f # 纯函数示例
├── yint.f
├── tridag.f
├── ...
├── _PURE_UNITS.txt # 无 COMMON 依赖的函数列表
├── _COMMON_ANALYSIS.txt # COMMON 依赖分析
└── _SUMMARY.txt # 提取摘要
synspec/extracted/ # SYNSPEC 拆分后的文件
└── ...
3. 推荐重构顺序
按文件大小从小到大排序(简单优先):
# 查看最小文件
cd /home/fmq/program/tlusty/tl208-s54/rust
while read name; do
if [ -f "tlusty/extracted/${name,,}.f" ]; then
lines=$(wc -l < "tlusty/extracted/${name,,}.f")
echo "$lines $name"
fi
done < tlusty/extracted/_PURE_UNITS.txt | sort -n | head -20
第一批 (最简单):
| 顺序 | 文件 | 行数 | 功能 |
|---|---|---|---|
| 1 | expo.f | 10 | 安全指数函数 |
| 2 | quit.f | 10 | 退出子程序 |
| 3 | ffcros.f | 13 | 截面计算 |
| 4 | gamsp.f | 14 | 展宽因子 |
| 5 | sgmer1.f | 14 | Stark展宽 |
| 6 | sgmerd.f | 15 | Stark展宽 |
| 7 | lagran.f | 16 | Lagrange插值 |
| 8 | gntk.f | 17 | Gaunt因子 |
| 9 | raph.f | 17 | 有理化函数 |
| 10 | cross.f | 18 | 截面计算 |
| 11 | eint.f | 18 | 指数积分 |
| 12 | sghe12.f | 18 | He 展宽 |
| 13 | yint.f | 18 | 二次插值 |
| 14 | erfcin.f | 20 | 误差函数补 |
| 15 | erfcx.f | 20 | 缩放误差函数 |
| 16 | gfree1.f | 21 | Gaunt自由 |
| 17 | sbfhmi_old.f | 22 | H- 截面 |
| 18 | tridag.f | 22 | 三对角矩阵求解 |
| 19 | timing.f | 24 | 计时 |
| 20 | expint.f | 30 | 指数积分 |
4. 单文件重构流程
Step 1: 读取并分析 Fortran 源码
# 读取源文件
cat tlusty/extracted/expo.f
记录以下信息:
- 函数名/子程序名
- 输入参数及其类型
- 返回值
- 算法逻辑
- 边界条件
Step 2: 创建 Rust 项目结构 (首次执行)
cd /home/fmq/program/tlusty/tl208-s54/rust
# 创建 Cargo.toml
cat > Cargo.toml << 'EOF'
[package]
name = "tlusty-rust"
version = "0.1.0"
edition = "2021"
[dependencies]
ndarray = "0.15"
num-traits = "0.2"
anyhow = "1.0"
[dev-dependencies]
approx = "0.5"
EOF
# 创建目录
mkdir -p src/math src/physics tests/fixtures
Step 3: 编写 Rust 实现
// src/math/expo.rs
/// 安全的指数函数,限制输入范围防止溢出
pub fn expo(x: f64) -> f64 {
const CRIT: f64 = 80.0;
x.clamp(-CRIT, CRIT).exp()
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_expo() {
assert_relative_eq!(expo(0.0), 1.0);
assert_relative_eq!(expo(1.0), std::f64::consts::E);
// 大数被限制
assert_relative_eq!(expo(100.0), 80.0_f64.exp());
}
}
Step 4: 更新 lib.rs
// src/lib.rs
pub mod math;
// 已完成的重构
pub mod expo;
Step 5: 运行测试
cargo test expo
Step 6: 记录进度
echo "expo - 10行 - ✓ 完成" >> REFACTORING_PROGRESS.txt
5. Fortran 语法转换参考
变量类型
| Fortran | Rust |
|---|---|
IMPLICIT REAL*8(A-H,O-Z) |
f64 |
INTEGER |
i32 |
LOGICAL |
bool |
CHARACTER*N |
[u8; N] 或 String |
数组
| Fortran (1-indexed) | Rust (0-indexed) |
|---|---|
DIMENSION A(3) |
a: [f64; 3] |
DIMENSION A(N) |
a: &[f64] 或 Vec<f64> |
A(1) |
a[0] |
控制结构
IF (X .LT. 0) THEN
Y = -X
ELSE
Y = X
END IF
let y = if x < 0.0 { -x } else { x };
6. 测试规范
单元测试
每个重构的函数必须有:
- 正常值测试
- 边界值测试
- 特殊情况测试
回归测试
对于复杂函数,用 Fortran 生成参考数据:
# 创建 Fortran 测试程序
cat > test_expint.f << 'EOF'
program test_expint
IMPLICIT REAL*8(A-H,O-Z)
do 10 x = 0.1, 10.0, 0.5
y = expint(x)
write(*,*) x, y
10 continue
end
FUNCTION EXPINT(X)
IMPLICIT REAL*8(A-H,O-Z)
... (复制原函数)
END
EOF
gfortran -o test_expint test_expint.f
./test_expint > tests/fixtures/expint_expected.txt
7. 进度跟踪
创建文件 REFACTORING_PROGRESS.txt:
# 重构进度
# 格式: 函数名 - 行数 - 状态 - 完成日期
## 已完成
expo - 10 - ✓ - 2026-03-XX
## 进行中
(无)
## 待处理
yint - 18 - ⬜
tridag - 22 - ⬜
...
8. 下一步行动
新会话启动后:
- 读取本文档:
cat REFACTORING_PLAN.md - 查看进度:
cat REFACTORING_PROGRESS.txt - 选择下一个文件(从未完成的最小文件开始)
- 按照流程执行重构
第一个文件: expo.f (10行,最简单)
RYBMAT 0 0 0 0 0 ○
SETDRT 0 0 0 0 0 ○
TABINT 0 0 0 0 0 ○
TAUFR1 可能有问题
SETDRT 调用的 RHOEOS 可能被分析脚本漏掉了(因为 RHOEOS 是 FUNCTION 不是 SUBROUTINE) 或者脚本认为 RHOEOS 不在调用依赖中