重构5,无io和无io依赖的模块已经全部重构完毕,接下来是重构剩余的模块,主要是io和依赖io的模块。
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: fortran-analyzer
|
||||
description: "分析 Fortran 代码的依赖关系,生成重构优先级列表。触发条件:(1) 用户提到 Fortran 依赖分析/依赖树;(2) 查找重构优先级/从哪里开始重构;(3) 分析函数调用关系;(4) 查看哪些函数依赖其他函数;(5)继续重构任务;(6) 用户问'应该先重构哪个函数'。输出 优先级列表和依赖树。"
|
||||
---
|
||||
|
||||
# Fortran 依赖分析器
|
||||
|
||||
分析提取后的 Fortran 文件,生成依赖关系和重构优先级。
|
||||
|
||||
## 快速参考
|
||||
|
||||
```bash
|
||||
# 查看下一个优先重构的模块
|
||||
python3 .claude/skills/fortran-analyzer/scripts/analyze_fortran.py --priority | head -5
|
||||
|
||||
# 查看指定函数的依赖树
|
||||
python3 .claude/skills/fortran-analyzer/scripts/analyze_fortran.py --tree FUNCTION_NAME
|
||||
```
|
||||
|
||||
接下来调用 fortran-to-rust skills 对该模块进行重构。
|
||||
## 详细参考
|
||||
不需要看,当--priority | head -1给出的模块含有多个未完成的模块时,说明脚本出错了,应该先修复脚本再继续重构。
|
||||
- [output_formats.md](references/output_formats.md) - 输出格式说明
|
||||
- [advanced_usage.md](references/advanced_usage.md) - 高级用法和筛选命令
|
||||
@@ -0,0 +1,47 @@
|
||||
# 高级用法
|
||||
|
||||
## 重构策略
|
||||
|
||||
### 优先级规则
|
||||
|
||||
1. **传递未实现=0**: 可立即开始,无依赖
|
||||
2. **传递未实现=1~3**: 需先完成少数依赖
|
||||
3. **传递未实现>3**: 依赖链长,最后处理
|
||||
4. **有IO**: 最后处理(可能需要 I/O 抽象层)
|
||||
|
||||
### 筛选命令
|
||||
|
||||
```bash
|
||||
# 最佳候选:无IO + 无未实现依赖
|
||||
python3 .claude/skills/fortran-analyzer/scripts/analyze_fortran.py --priority | grep "○$" | head -10
|
||||
|
||||
# 查看特定函数依赖树
|
||||
python3 .claude/skills/fortran-analyzer/scripts/analyze_fortran.py --tree ALIFR1
|
||||
|
||||
# 统计进度
|
||||
echo "已完成: $(awk -F, '$11=="done"' fortran_analysis.csv | wc -l)"
|
||||
echo "待处理: $(awk -F, '$11=="pending"' fortran_analysis.csv | wc -l)"
|
||||
|
||||
# 生成完整 CSV(含传递依赖)
|
||||
python3 .claude/skills/fortran-analyzer/scripts/analyze_fortran.py --full > fortran_analysis.csv
|
||||
```
|
||||
|
||||
## SPECIAL_MAPPINGS
|
||||
|
||||
一个 Rust 文件实现多个 Fortran 函数时,需更新 `.claude/skills/fortran-analyzer/scripts/analyze_fortran.py`:
|
||||
|
||||
```python
|
||||
SPECIAL_MAPPINGS = {
|
||||
'gfree': ['gfree0', 'gfreed', 'gfree1'],
|
||||
'interpolate': ['yint', 'lagran'],
|
||||
'sgmer': ['sgmer0', 'sgmer1', 'sgmerd'],
|
||||
# 添加新映射...
|
||||
}
|
||||
```
|
||||
|
||||
## 脚本内部函数
|
||||
|
||||
- `extract_calls()`: 提取 CALL 和 FUNCTION 调用
|
||||
- `get_transitive_deps()`: 计算传递依赖
|
||||
- `get_pending_deps()`: 获取未实现依赖
|
||||
- `print_dependency_tree()`: 打印依赖树
|
||||
@@ -0,0 +1,49 @@
|
||||
# 输出格式说明
|
||||
|
||||
## 优先级列表 (`--priority`)
|
||||
|
||||
```
|
||||
重构优先级列表 (按未实现依赖排序,无IO优先):
|
||||
====================================================================================================
|
||||
单元名 未实现 传递未实现 深度 直接调用 传递调用 IO
|
||||
----------------------------------------------------------------------------------------------------
|
||||
ALIFR1 0 0 1 1 1 ○
|
||||
ALISK1 4 20 5 12 45 ○
|
||||
```
|
||||
|
||||
**列说明:**
|
||||
| 列 | 含义 |
|
||||
|----|------|
|
||||
| 未实现 | 直接依赖中未完成的函数数 |
|
||||
| 传递未实现 | **关键指标**:所有递归依赖中未完成的函数数 |
|
||||
| 深度 | 依赖链深度(叶子=0) |
|
||||
| IO | ○=无IO, ✓=有IO |
|
||||
|
||||
**排序规则**: 传递未实现少 → 深度低 → 依赖少
|
||||
|
||||
## 依赖树 (`--tree UNIT`)
|
||||
|
||||
```
|
||||
依赖树: ALISK1 ○
|
||||
============================================================
|
||||
直接依赖: 4, 传递依赖: 27, 未实现: 20
|
||||
未实现依赖: ALIFRK, OPACF1, OPADD, ROSSTD, ...
|
||||
------------------------------------------------------------
|
||||
○ ALISK1 (4未实现)
|
||||
├── ○ OPACF1 (6未实现)
|
||||
│ ├── ○ OPADD (5未实现)
|
||||
│ │ └── ○ cia_h2h2 (1未实现)
|
||||
│ └── ✓ locate
|
||||
└── ○ ALIFRK
|
||||
```
|
||||
|
||||
**符号说明:**
|
||||
- ✓ = 已完成
|
||||
- ○ = 待处理
|
||||
- `(N未实现)` = 该节点有 N 个直接依赖未完成
|
||||
|
||||
## CSV 格式
|
||||
|
||||
```
|
||||
fortran_file,unit_name,unit_type,is_pure,common_deps,call_deps,has_io,rust_module,status
|
||||
```
|
||||
@@ -0,0 +1,403 @@
|
||||
|
||||
---
|
||||
|
||||
## TLUSTY208.F 拆分工作 (2026-03-18)
|
||||
|
||||
### 任务:将单文件拆分为多个独立模块
|
||||
|
||||
**执行流程:**
|
||||
|
||||
```bash
|
||||
cd /home/fmq/program/tlusty/tl208-s54/rust
|
||||
python3 extract_fortran.py tlusty/tlusty208.f tlusty/extracted/
|
||||
cp tlusty/*.FOR tlusty/extracted/
|
||||
```
|
||||
|
||||
### 提取结果
|
||||
|
||||
| 项目 | 数量 |
|
||||
|------|------|
|
||||
| 程序单元 | **304** |
|
||||
| 子程序 (SUBROUTINE) | 269 |
|
||||
| 函数 (FUNCTION) | 32 |
|
||||
| 主程序 (PROGRAM) | 1 |
|
||||
| BLOCK DATA | **2** (含1个无名) |
|
||||
| 无 COMMON 依赖的纯函数 | 195 |
|
||||
|
||||
> 注意: 2026-03-19 修复了无名 BLOCK DATA 提取问题,单元数从 303 增加到 304
|
||||
|
||||
### Include 文件
|
||||
|
||||
```
|
||||
tlusty/extracted/*.FOR
|
||||
├── IMPLIC.FOR # 隐式类型声明
|
||||
├── BASICS.FOR # 基本参数和物理常数
|
||||
├── ITERAT.FOR # 迭代控制参数
|
||||
├── ALIPAR.FOR # ALI 参数
|
||||
├── ATOMIC.FOR # 原子数据
|
||||
├── MODELQ.FOR # 模型物理量
|
||||
├── ODFPAR.FOR # ODF 参数
|
||||
└── ARRAY1.FOR # 主工作数组
|
||||
```
|
||||
|
||||
### 编译配置
|
||||
|
||||
**关键编译选项**:
|
||||
```makefile
|
||||
FFLAGS = -O3 -fno-automatic -mcmodel=large
|
||||
```
|
||||
|
||||
- `-mcmodel=large`: 支持 >2GB 地址空间
|
||||
- `-fno-automatic`: 所有变量默认为静态存储(兼容旧Fortran代码)
|
||||
- **不要使用** `-ffixed-line-length-none`: 会将第73-80列的注释区当作代码解析
|
||||
|
||||
### 编译验证
|
||||
|
||||
```bash
|
||||
cd tlusty/extracted && make clean && make
|
||||
# 生成: tlusty_extracted (1,940,488 bytes)
|
||||
|
||||
# 直接编译
|
||||
gfortran -fno-automatic -mcmodel=large -O3 -o tlusty.exe tlusty208.f
|
||||
# 生成: tlusty.exe (1,997,416 bytes)
|
||||
```
|
||||
|
||||
**结论**: 功能等价,拆分编译文件更小 (-2.9%)
|
||||
|
||||
### 拆分编译 vs 直接编译对比
|
||||
|
||||
| 方面 | 直接编译 | 拆分编译 |
|
||||
|------|---------|---------|
|
||||
| 文件大小 | 1,997,416 B | 1,940,488 B |
|
||||
| 功能 | 相同 | 相同 |
|
||||
| 增量编译 | ❌ | ✅ |
|
||||
| 代码定位 | 困难 | 简单 |
|
||||
|
||||
### 无 COMMON 依赖的纯函数 (198个)
|
||||
|
||||
可独立测试和重构的单元:
|
||||
```
|
||||
ACCEL2, ALIFR1, ALIFR3, ALIFR6, ALIFRK, ALISK1, ALISK2, ALIST1, ALIST2,
|
||||
ANGSET, BETAH, BHE, BKHSGO, BPOP, BPOPE, BPOPF, BPOPT, BRE, BREZ,
|
||||
BRTE, BRTEZ, BUTLER, CARBON, CEH12, CHANGE, CHCKSE, CHEAV, CHEAVJ,
|
||||
CIA_H2H, CIA_H2H2, CIA_H2HE, CIA_HHE, CION, CKOEST, COLH, COLHE,
|
||||
COLLHE, CONCOR, CORRWM, CROSS, CROSSD, CSPEC, DIELRC, DIETOT, DIVSTR,
|
||||
DMEVAL, DOPGAM, DWNFR, DWNFR0, DWNFR1, EINT, EMAT, ENTENE, ERFCIN,
|
||||
ERFCX, EXPINT, EXPINX, EXPO, FFCROS, GAMI, GAMSP, GAULEG, GAUNT,
|
||||
GETWRD, GFREE0, GFREE1, GFREED, GNTK, GRCOR, GREYD, GRIDP, H2MINUS,
|
||||
HEPHOT, HIDALG, IJALI2, IJALIS, INCLDY, INDEXX, INIFRS, INILAM, INTERP,
|
||||
INTHYD, INTLEM, INTXEN, IRC, LAGRAN, LAGUER, LEMINI, LEVGRP, LEVSET,
|
||||
LEVSOL, LINEQS, LINSEL, LINSET, LINSPL, LOCATE, LTEGR, LUCY, LYMLIN,
|
||||
MATGEN, MATINV, MEANOP, MEANOPT, MINV3, NEWPOP, NSTOUT, ODF1, ODFFR,
|
||||
ODFHST, ODFHYD, ODFHYS, ODFMER, OPACFL, OPADD0, OPAHST, OPAINI, OPCTAB,
|
||||
OSCCOR, OUTPUT, PFCNO, PFFE, PFHEAV, PFNI, PFSPEC, PRCHAN, PRD, PRDINI,
|
||||
PRINC, PRNT, PROFSP, PSOLVE, PZERT, QUARTC, QUIT, RADPRE, RAPH, RATES1,
|
||||
RATMAL, RATMAT, RATSP1, RAYINI, RAYSET, RDATAX, READBF, RECHCK, REFLEV,
|
||||
REIMAN, RHOEOS, RHONEN, ROSSOP, ROSSTD, RTEDF2, RTEFE2, RTESOL, RTE_SC,
|
||||
SABOLF, SBFCH, SBFHE1, SBFHMI, SBFHMI_OLD, SBFOH, SFFHMI, SFFHMI_ADD,
|
||||
SGHE12, SGMER0, SGMER1, SGMERD, SIGAVE, SIGK, SIGMAR, SPSIGK, SRTFRQ,
|
||||
STARK0, STARKA, SWITCH, SZIRC, TDPINI, TIMING, TIOPF, TLUSTY, TRAINI,
|
||||
TRIDAG, UBETA, VERN16, VERN18, VERN20, VERN26, VERNER, VISINI, VOIGT,
|
||||
VOIGTE, WN, WNSTOR, XENINI, XK2DOP, YINT, YLINTP, ZMRHO
|
||||
```
|
||||
---
|
||||
|
||||
## SYNSPEC54.F 拆分工作 (2026-03-18)
|
||||
|
||||
### 任务:将单文件拆分为多个独立模块
|
||||
|
||||
**执行流程:**
|
||||
|
||||
#### 1. 创建提取脚本 `extract_fortran.py`
|
||||
|
||||
```python
|
||||
# 核心功能:
|
||||
# - 正则匹配 SUBROUTINE/FUNCTION/PROGRAM/BLOCK DATA
|
||||
# - 查找对应的 END 语句确定边界
|
||||
# - 提取到独立 .f 文件
|
||||
# - 分析 COMMON 块依赖
|
||||
# - 生成 Makefile
|
||||
```
|
||||
|
||||
#### 2. 运行提取
|
||||
|
||||
```bash
|
||||
cd /home/fmq/program/tlusty/tl208-s54/rust
|
||||
python3 extract_fortran.py synspec/synspec54.f synspec/extracted/
|
||||
cp synspec/*.FOR synspec/extracted/
|
||||
```
|
||||
|
||||
#### 3. 提取结果
|
||||
|
||||
| 项目 | 数量 |
|
||||
|------|------|
|
||||
| 程序单元 | 168 |
|
||||
| 子程序 (SUBROUTINE) | 134 |
|
||||
| 函数 (FUNCTION) | 33 |
|
||||
| 主程序 (PROGRAM) | 1 |
|
||||
| 总代码行数 | 23,050 |
|
||||
| 无 COMMON 依赖的纯函数 | 93 |
|
||||
|
||||
#### 4. 编译配置
|
||||
|
||||
**关键编译选项** (解决大型 COMMON 数组链接问题):
|
||||
```makefile
|
||||
FFLAGS = -O3 -fno-automatic -mcmodel=large
|
||||
```
|
||||
|
||||
- `-mcmodel=large`: 支持 >2GB 地址空间
|
||||
- `-fno-automatic`: 所有变量默认为静态存储(兼容旧Fortran代码)
|
||||
- **不要使用** `-ffixed-line-length-none`: 会将第73-80列的注释区当作代码解析
|
||||
|
||||
#### 5. 编译验证
|
||||
|
||||
```bash
|
||||
cd synspec/extracted && make clean && make
|
||||
# 生成: synspec_extracted (1,000,408 bytes)
|
||||
```
|
||||
|
||||
**对比原始编译:**
|
||||
```bash
|
||||
gfortran -O3 -fno-automatic -mcmodel=large -o synspec_direct.exe synspec54.f
|
||||
# 生成: synspec_direct.exe (1,044,928 bytes)
|
||||
```
|
||||
|
||||
**结论**: 功能完全等价,拆分编译更小 (-4.3%)
|
||||
|
||||
### 生成的文件结构
|
||||
|
||||
```
|
||||
synspec/extracted/
|
||||
├── synspec.f # 主程序 (174行)
|
||||
├── start.f # 子程序 (107行)
|
||||
├── sbfhmi.f # H⁻ 光电离截面函数 (42行)
|
||||
├── expint.f # 指数积分函数 (18行)
|
||||
├── ... # 共168个 .f 文件
|
||||
├── PARAMS.FOR # 参数定义 (include)
|
||||
├── MODELP.FOR # 模型参数 (include)
|
||||
├── LINDAT.FOR # 谱线数据 (include)
|
||||
├── SYNTHP.FOR # 合成谱参数 (include)
|
||||
├── WINCOM.FOR # 窗口通信 (include)
|
||||
├── Makefile # 自动构建
|
||||
├── _SUMMARY.txt # 提取摘要
|
||||
├── _COMMON_ANALYSIS.txt # COMMON 依赖分析
|
||||
└── _PURE_UNITS.txt # 纯函数列表
|
||||
```
|
||||
|
||||
### COMMON 块分析结果
|
||||
|
||||
**有 COMMON 依赖的单元**: 75 个
|
||||
**唯一 COMMON 块**: 68 个
|
||||
|
||||
主要 COMMON 块:
|
||||
- `BLAPAR`, `LIMPAR` - 谱线参数
|
||||
- `EMFLUX` - 辐射流
|
||||
- `RTEOPA` - 辐射转移不透明度
|
||||
- `NLTPOP` - 非LTE布居数
|
||||
- `lasers` - 激光数据处理
|
||||
|
||||
### 拆分编译 vs 直接编译对比
|
||||
|
||||
| 方面 | 直接编译 | 拆分编译 |
|
||||
|------|---------|---------|
|
||||
| 文件大小 | 1,044,928 B | 1,000,408 B |
|
||||
| 功能 | 相同 | 相同 |
|
||||
| 增量编译 | ❌ | ✅ |
|
||||
| 代码定位 | 困难 | 简单 |
|
||||
| 模块化重构 | 困难 | 容易 |
|
||||
|
||||
### 提取脚本位置
|
||||
|
||||
```
|
||||
extract_fortran.py
|
||||
```
|
||||
|
||||
### 推荐用法
|
||||
|
||||
```bash
|
||||
# 开发/调试/重构
|
||||
cd synspec/extracted && make
|
||||
|
||||
# 生产环境/快速编译
|
||||
cd synspec && gfortran -O3 -fno-automatic -mcmodel=large -o synspec.exe synspec54.f
|
||||
```
|
||||
|
||||
### 无 COMMON 依赖的纯函数 (93个)
|
||||
|
||||
可独立测试和重构的函数:
|
||||
```
|
||||
CARBON, CHANGE, CHCKAB, CIA_H2H, CIA_H2H2, CIA_H2HE, CIA_HHE,
|
||||
COUNT_WORDS, DENSIT, DIVHE2, DIVSTR, DWNFR0, DWNFR1, EPS,
|
||||
EXOPF, EXPINT, EXTPRF, FEAUTR, GAMHE, GAUNT, GETWRD, GFREE,
|
||||
GNTK, GRIEM, H2MINUS, H2OPF, HE2SET, HE2SEW, HEPHOT, HESET,
|
||||
HIDALG, HYDINI, HYDTAB, HYLSET, HYLSEW, INIBLM, INKUR, INPBF,
|
||||
INTERP, INTHYD, INTRP, INTXEN, IRWPF, ISPEC, LEVSOL, LINEQS,
|
||||
LOCATE, LYMLIN, MATINV, MOLOP, MPARTF, OPADD, PARTDV, PARTF,
|
||||
PFFE, PFHEAV, PFNI, PFSPEC, PHTX, QUIT, RATMAT, READBF,
|
||||
REIMAN, SABOLF, SBFCH, SBFHE1, SBFHMI, SBFHMI_OLD, SBFOH,
|
||||
SETRAY, SFFHMI, SFFHMI_OLD, SGHE12, SGMERG, SPSIGK, STARK0,
|
||||
STARKA, STARKIR, STATE0, SYNSPEC, TINT, TRIDAG, VELSET,
|
||||
VOIGTE, VOPF, WGTJH1, WN, WNSTOR, WTOT, XENINI, XK2DOP, YINT, YLINTP
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 功能验证测试 (2026-03-19)
|
||||
|
||||
### 测试环境变量
|
||||
|
||||
```bash
|
||||
export TL208=/home/fmq/program/tlusty
|
||||
export TLUSTY=$TL208/tl208-s54
|
||||
export LINELIST=$TL208/linelist
|
||||
export IRON=$TL208/irondata
|
||||
export OPTABLES=$TL208/optables
|
||||
```
|
||||
|
||||
### 测试目录
|
||||
|
||||
```
|
||||
tests/tlusty/hhe/ # H-He 模型测试用例
|
||||
├── hhe35lt.5 # LTE 模型输入
|
||||
├── hhe35nc.5 # NLTE continua 模型输入
|
||||
├── hhe35nl.5 # NLTE with lines 模型输入
|
||||
└── hhe35*.7,9,14 # 预期输出文件
|
||||
```
|
||||
|
||||
### 测试流程
|
||||
|
||||
```bash
|
||||
cd /home/fmq/program/tlusty/tl208-s54/rust/tests/tlusty
|
||||
|
||||
# 创建测试目录
|
||||
mkdir -p test_extracted
|
||||
cd test_extracted
|
||||
cp ../hhe/*.5 .
|
||||
ln -sf $TLUSTY/data data
|
||||
|
||||
# 设置环境变量
|
||||
export TL208=/home/fmq/program/tlusty
|
||||
export TLUSTY=$TL208/tl208-s54
|
||||
export LINELIST=$TL208/linelist
|
||||
export IRON=$TL208/irondata
|
||||
export OPTABLES=$TL208/optables
|
||||
|
||||
# 可执行文件路径
|
||||
EXE=../../tlusty/extracted/build/tlusty_extracted
|
||||
|
||||
# 测试1: LTE 模型 (从零开始)
|
||||
$EXE < hhe35lt.5 > hhe35lt.6
|
||||
cp fort.7 hhe35lt.7; cp fort.9 hhe35lt.9; cp fort.14 hhe35lt.14
|
||||
|
||||
# 测试2: NLTE continua (使用 LTE 作为初始模型)
|
||||
cp hhe35lt.7 fort.8
|
||||
$EXE < hhe35nc.5 > hhe35nc.6
|
||||
cp fort.7 hhe35nc.7; cp fort.9 hhe35nc.9; cp fort.14 hhe35nc.14
|
||||
|
||||
# 测试3: NLTE with lines (使用 NC 作为初始模型)
|
||||
cp hhe35nc.7 fort.8
|
||||
$EXE < hhe35nl.5 > hhe35nl.6
|
||||
cp fort.7 hhe35nl.7; cp fort.9 hhe35nl.9; cp fort.14 hhe35nl.14
|
||||
|
||||
# 验证: 与原始结果比较
|
||||
diff hhe35lt.7 ../hhe/hhe35lt.7
|
||||
diff hhe35nc.7 ../hhe/hhe35nc.7
|
||||
diff hhe35nl.7 ../hhe/hhe35nl.7
|
||||
```
|
||||
|
||||
### 测试结果
|
||||
|
||||
| 测试用例 | 拆分编译 | 直接编译 | 原始结果 |
|
||||
|----------|----------|----------|----------|
|
||||
| hhe35lt (LTE) | ✓ 通过 | ✓ 通过 | ✓ 相同 |
|
||||
| hhe35nc (NLTE continua) | ✓ 通过 | ✓ 通过 | ✓ 相同 |
|
||||
| hhe35nl (NLTE lines) | ✓ 通过 | ✓ 通过 | ✓ 相同 |
|
||||
|
||||
**MD5 校验和 (hhe35nl.7)**:
|
||||
```
|
||||
01f3169947ca24bf1c989619b83ae8f2
|
||||
```
|
||||
|
||||
**结论**: 拆分编译与直接编译输出完全相同,功能验证通过
|
||||
|
||||
---
|
||||
|
||||
## SYNSPEC54 功能验证测试 (2026-03-19)
|
||||
|
||||
### 测试目录
|
||||
|
||||
```
|
||||
tests/synspec/hhe/
|
||||
├── hhe35nl.5 # 输入文件
|
||||
├── hhe35nl.7 # 模型大气 (来自 TLUSTY 测试)
|
||||
├── fort.55.con # 附加输入文件
|
||||
├── data # 数据目录路径文件 (内容: $TLUSTY/data)
|
||||
└── results/ # 预期输出
|
||||
├── hhe35nl.spec # 合成光谱
|
||||
├── hhe35nl.cont # 连续谱
|
||||
└── hhe35nl.iden # 谱线标识
|
||||
```
|
||||
|
||||
### 测试流程
|
||||
|
||||
```bash
|
||||
cd /home/fmq/program/tlusty/tl208-s54/rust/tests/synspec/hhe
|
||||
|
||||
# 设置环境变量
|
||||
export TL208=/home/fmq/program/tlusty
|
||||
export TLUSTY=$TL208/tl208-s54
|
||||
export LINELIST=$TL208/linelist
|
||||
export IRON=$TL208/irondata
|
||||
export OPTABLES=$TL208/optables
|
||||
|
||||
# 准备输入文件
|
||||
cp hhe35nl.7 fort.8
|
||||
ln -sf fort.55.con fort.55
|
||||
ln -sf $TLUSTY/data/gfATO.dat fort.19
|
||||
|
||||
# 关键: data 必须是符号链接指向数据目录
|
||||
rm -f data
|
||||
ln -sf $TLUSTY/data data
|
||||
|
||||
# 可执行文件路径
|
||||
EXE_ORIG=$TLUSTY/synspec/synspec.exe
|
||||
EXE_DIRECT=../../synspec/synspec_direct.exe
|
||||
EXE_EXTRACTED=../../synspec/extracted/build/synspec_extracted
|
||||
|
||||
# 测试原始版本
|
||||
$EXE_ORIG < hhe35nl.5 > hhe35nl_orig.log
|
||||
cp fort.7 hhe35nl_orig.spec; cp fort.17 hhe35nl_orig.cont
|
||||
|
||||
# 测试直接编译版本
|
||||
rm -f fort.7 fort.17 fort.12
|
||||
$EXE_DIRECT < hhe35nl.5 > hhe35nl_direct.log
|
||||
cp fort.7 hhe35nl_direct.spec; cp fort.17 hhe35nl_direct.cont
|
||||
|
||||
# 测试拆分编译版本
|
||||
rm -f fort.7 fort.17 fort.12
|
||||
$EXE_EXTRACTED < hhe35nl.5 > hhe35nl_extracted.log
|
||||
cp fort.7 hhe35nl_extracted.spec; cp fort.17 hhe35nl_extracted.cont
|
||||
|
||||
# 验证
|
||||
diff hhe35nl_orig.spec hhe35nl_direct.spec
|
||||
diff hhe35nl_orig.spec hhe35nl_extracted.spec
|
||||
|
||||
# 恢复 data 文件
|
||||
rm -f data
|
||||
echo "/home/fmq/program/tlusty/tl208-s54/data" > data
|
||||
```
|
||||
|
||||
### 测试结果
|
||||
|
||||
| 测试用例 | 原始程序 | 直接编译 | 拆分编译 |
|
||||
|----------|----------|----------|----------|
|
||||
| hhe35nl (NLTE lines) | ✓ 通过 | ✓ 通过 | ✓ 相同 |
|
||||
|
||||
**MD5 校验和 (hhe35nl.spec)**:
|
||||
```
|
||||
7925533b21b16d6bcdfff40e626cab83
|
||||
```
|
||||
|
||||
**注意事项**:
|
||||
- `data` 文件/符号链接必须正确设置,否则报错 `Cannot open file './data/h1.dat': Not a directory`
|
||||
- 拆分编译程序与原始程序输出完全相同,功能验证通过
|
||||
@@ -0,0 +1,438 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
分析 TLUSTY Fortran 文件,提取函数依赖信息。
|
||||
|
||||
用法:
|
||||
python3 analyze_fortran.py # 输出 CSV(带完整依赖)
|
||||
python3 analyze_fortran.py --tree # 输出依赖树(文本格式)
|
||||
python3 analyze_fortran.py --priority # 输出重构优先级列表
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import glob
|
||||
import argparse
|
||||
from collections import defaultdict
|
||||
|
||||
def extract_includes(content):
|
||||
"""提取 INCLUDE 文件列表"""
|
||||
includes = re.findall(r"INCLUDE\s*'([^']+)\.FOR'", content, re.IGNORECASE)
|
||||
return [inc for inc in includes if inc.upper() != 'IMPLIC']
|
||||
|
||||
def extract_commons(content):
|
||||
"""提取 COMMON 块名称"""
|
||||
# 匹配 COMMON/NAME/ 或 common/name/
|
||||
commons = re.findall(r'(?i)^\s*COMMON\s*/(\w+)/', content, re.MULTILINE)
|
||||
return list(set(commons))
|
||||
|
||||
# Fortran 内置函数列表(不需要追踪)
|
||||
FORTRAN_INTRINSICS = {
|
||||
'SIN', 'COS', 'TAN', 'ASIN', 'ACOS', 'ATAN', 'ATAN2',
|
||||
'SINH', 'COSH', 'TANH',
|
||||
'EXP', 'LOG', 'LOG10', 'LOG2',
|
||||
'SQRT', 'ABS', 'MOD', 'SIGN',
|
||||
'MAX', 'MIN', 'MAX0', 'MIN0', 'MAX1', 'MIN1', 'AMAX0', 'AMIN0',
|
||||
'INT', 'IFIX', 'IDINT', 'FLOAT', 'SNGL', 'DBLE', 'CMPLX',
|
||||
'REAL', 'AIMAG', 'CONJG',
|
||||
'ICHAR', 'CHAR', 'INDEX', 'LEN', 'LGE', 'LGT', 'LLE', 'LLT',
|
||||
'DOT_PRODUCT', 'MATMUL', 'TRANSPOSE', 'RESHAPE',
|
||||
'SIZE', 'SHAPE', 'LBOUND', 'UBOUND',
|
||||
'ALLOCATED', 'ALLOCATE', 'DEALLOCATE',
|
||||
'KIND', 'SELECTED_REAL_KIND', 'SELECTED_INT_KIND',
|
||||
'DIGITS', 'EPSILON', 'HUGE', 'TINY', 'PRECISION', 'RANGE',
|
||||
'FLOOR', 'CEILING', 'NINT', 'ANINT',
|
||||
'ADJUSTL', 'ADJUSTR', 'TRIM', 'REPEAT', 'SCAN', 'VERIFY',
|
||||
'PRESENT', 'ASSOCIATED',
|
||||
# TLUSTY 常用数学函数
|
||||
'ERF', 'ERFC', 'GAMMA', 'LOG_GAMMA',
|
||||
}
|
||||
|
||||
def extract_calls(content, known_functions=None):
|
||||
"""提取 CALL 语句和 FUNCTION 调用
|
||||
|
||||
Args:
|
||||
content: Fortran 源码
|
||||
known_functions: 已知的函数名集合(用于区分函数调用和数组访问)
|
||||
"""
|
||||
calls = set()
|
||||
|
||||
# 1. 提取 CALL 语句(支持有括号和无括号两种形式)
|
||||
# CALL NAME(...) 或 CALL NAME
|
||||
call_stmts = re.findall(r'(?i)CALL\s+(\w+)(?:\s*\(|\s*$|\s*\n)', content)
|
||||
calls.update(c.upper() for c in call_stmts)
|
||||
|
||||
# 2. 提取可能的 FUNCTION 调用
|
||||
if known_functions:
|
||||
# 只匹配已知函数名
|
||||
func_assign = re.findall(r'(?i)=\s*([A-Z][A-Z0-9]*)\s*\(', content)
|
||||
calls.update(f.upper() for f in func_assign
|
||||
if f.upper() in known_functions and f.upper() not in FORTRAN_INTRINSICS)
|
||||
|
||||
func_expr = re.findall(r'(?i)[=(,]\s*([A-Z][A-Z0-9]*)\s*\(', content)
|
||||
calls.update(f.upper() for f in func_expr
|
||||
if f.upper() in known_functions and f.upper() not in FORTRAN_INTRINSICS)
|
||||
|
||||
return list(calls)
|
||||
|
||||
def has_file_io(content):
|
||||
"""检查是否有文件 I/O"""
|
||||
patterns = [
|
||||
r'OPEN\s*\(',
|
||||
r'READ\s*\(\s*\d+',
|
||||
r'WRITE\s*\(\s*\d+',
|
||||
r'write\s*\(',
|
||||
r'read\s*\(',
|
||||
]
|
||||
for p in patterns:
|
||||
if re.search(p, content, re.IGNORECASE):
|
||||
return True
|
||||
return False
|
||||
|
||||
def extract_unit_info(content, filename):
|
||||
"""提取单元信息"""
|
||||
units = []
|
||||
|
||||
# 匹配 SUBROUTINE
|
||||
sub_match = re.search(r'(?i)^\s*SUBROUTINE\s+(\w+)', content, re.MULTILINE)
|
||||
if sub_match:
|
||||
units.append(('SUBROUTINE', sub_match.group(1).upper()))
|
||||
|
||||
# 匹配 FUNCTION
|
||||
func_match = re.search(r'(?i)^\s*(?:REAL(?:\*\d+)?|INTEGER(?:\*\d+)?|DOUBLE\s*PRECISION)?\s*FUNCTION\s+(\w+)', content, re.MULTILINE)
|
||||
if func_match:
|
||||
units.append(('FUNCTION', func_match.group(1).upper()))
|
||||
|
||||
# 匹配 BLOCK DATA(注意:名字必须在同一行,不能跨行匹配注释 C)
|
||||
# Fortran 中 C 开头的行是注释,不应被匹配为名字
|
||||
block_match = re.search(r'(?i)^\s*BLOCK\s*DATA\s*(\w+)?\s*$', content, re.MULTILINE)
|
||||
if block_match:
|
||||
name = block_match.group(1).upper() if block_match.group(1) else '_UNNAMED_'
|
||||
units.append(('BLOCK DATA', name))
|
||||
|
||||
# 如果都没匹配到,使用文件名
|
||||
if not units:
|
||||
base = os.path.splitext(filename)[0]
|
||||
units.append(('UNKNOWN', base.upper()))
|
||||
|
||||
return units
|
||||
|
||||
# 特殊映射:一个 Rust 文件实现多个 Fortran 函数
|
||||
SPECIAL_MAPPINGS = {
|
||||
# Rust 文件名 -> [Fortran 函数名列表]
|
||||
'gfree': ['gfree0', 'gfreed', 'gfree1'],
|
||||
'interpolate': ['yint', 'lagran'],
|
||||
'sgmer': ['sgmer0', 'sgmer1', 'sgmerd'],
|
||||
'ctdata': ['hction', 'hctrecom'],
|
||||
'cross': ['cross', 'crossd'],
|
||||
'expint': ['eint', 'expinx'],
|
||||
'erfcx': ['erfcx', 'erfcin'],
|
||||
'lineqs': ['lineqs', 'lineqs_nr'],
|
||||
'gamsp': ['gamsp'], # alias
|
||||
'bhe': ['bhe', 'bhed', 'bhez'], # 流体静力学平衡方程
|
||||
'comset': ['comset'], # Compton 散射参数设置
|
||||
'ghydop': ['ghydop'], # 氢不透明度 (Gomez 表)
|
||||
'levgrp': ['levgrp'], # 能级分组
|
||||
'profil': ['profil'], # 标准吸收轮廓
|
||||
'linspl': ['linspl'], # 谱线轮廓设置
|
||||
}
|
||||
|
||||
def find_rust_module(fortran_name, rust_dir):
|
||||
"""查找对应的 Rust 模块"""
|
||||
# 先检查直接匹配
|
||||
rust_file = os.path.join(rust_dir, f"{fortran_name}.rs")
|
||||
if os.path.exists(rust_file):
|
||||
return f"src/math/{fortran_name}.rs"
|
||||
|
||||
# 检查特殊映射
|
||||
for rust_mod, fortran_funcs in SPECIAL_MAPPINGS.items():
|
||||
if fortran_name in fortran_funcs:
|
||||
return f"src/math/{rust_mod}.rs"
|
||||
|
||||
return ""
|
||||
|
||||
def get_transitive_deps(unit_name, units_dict, visited=None):
|
||||
"""递归获取所有传递调用依赖"""
|
||||
if visited is None:
|
||||
visited = set()
|
||||
|
||||
if unit_name in visited:
|
||||
return set()
|
||||
|
||||
visited.add(unit_name)
|
||||
|
||||
if unit_name not in units_dict:
|
||||
return set()
|
||||
|
||||
direct_calls = units_dict[unit_name].get('call_deps', [])
|
||||
all_deps = set(direct_calls)
|
||||
|
||||
for dep in direct_calls:
|
||||
all_deps.update(get_transitive_deps(dep, units_dict, visited.copy()))
|
||||
|
||||
return all_deps
|
||||
|
||||
def get_pending_deps(unit_name, units_dict, visited=None):
|
||||
"""获取尚未实现的直接依赖"""
|
||||
if unit_name not in units_dict:
|
||||
return []
|
||||
|
||||
calls = units_dict[unit_name].get('call_deps', [])
|
||||
pending = [d for d in calls if d not in units_dict or units_dict[d].get('status') != 'done']
|
||||
return pending
|
||||
|
||||
def get_transitive_pending_deps(unit_name, units_dict, visited=None):
|
||||
"""递归获取所有传递的未实现依赖"""
|
||||
if visited is None:
|
||||
visited = set()
|
||||
|
||||
if unit_name in visited:
|
||||
return set()
|
||||
|
||||
visited.add(unit_name)
|
||||
|
||||
if unit_name not in units_dict:
|
||||
return set()
|
||||
|
||||
direct_calls = units_dict[unit_name].get('call_deps', [])
|
||||
# 未实现的直接依赖
|
||||
pending_deps = set(d for d in direct_calls if d not in units_dict or units_dict[d].get('status') != 'done')
|
||||
|
||||
# 递归获取所有依赖的未实现依赖
|
||||
for dep in direct_calls:
|
||||
pending_deps.update(get_transitive_pending_deps(dep, units_dict, visited.copy()))
|
||||
|
||||
return pending_deps
|
||||
|
||||
def get_transitive_commons(unit_name, units_dict, visited=None):
|
||||
"""递归获取所有传递 COMMON 依赖"""
|
||||
if visited is None:
|
||||
visited = set()
|
||||
|
||||
if unit_name in visited:
|
||||
return set()
|
||||
|
||||
visited.add(unit_name)
|
||||
|
||||
if unit_name not in units_dict:
|
||||
return set()
|
||||
|
||||
direct_commons = set(units_dict[unit_name].get('common_deps', []))
|
||||
direct_calls = units_dict[unit_name].get('call_deps', [])
|
||||
|
||||
all_commons = direct_commons.copy()
|
||||
|
||||
for dep in direct_calls:
|
||||
all_commons.update(get_transitive_commons(dep, units_dict, visited.copy()))
|
||||
|
||||
return all_commons
|
||||
|
||||
def calculate_depth(unit_name, units_dict, memo=None):
|
||||
"""计算依赖深度(叶子节点深度为0)"""
|
||||
if memo is None:
|
||||
memo = {}
|
||||
|
||||
if unit_name in memo:
|
||||
return memo[unit_name]
|
||||
|
||||
if unit_name not in units_dict:
|
||||
return 0
|
||||
|
||||
calls = units_dict[unit_name].get('call_deps', [])
|
||||
if not calls:
|
||||
memo[unit_name] = 0
|
||||
return 0
|
||||
|
||||
max_dep_depth = 0
|
||||
for dep in calls:
|
||||
if dep != unit_name: # 避免自引用
|
||||
max_dep_depth = max(max_dep_depth, calculate_depth(dep, units_dict, memo))
|
||||
|
||||
depth = max_dep_depth + 1
|
||||
memo[unit_name] = depth
|
||||
return depth
|
||||
|
||||
def print_dependency_tree(unit_name, units_dict, indent=0, visited=None, prefix="", show_pending_count=True):
|
||||
"""打印依赖树(文本格式)"""
|
||||
if visited is None:
|
||||
visited = set()
|
||||
|
||||
if unit_name in visited:
|
||||
print(f"{prefix}[循环引用: {unit_name}]")
|
||||
return
|
||||
|
||||
visited.add(unit_name)
|
||||
|
||||
if unit_name not in units_dict:
|
||||
print(f"{prefix}{unit_name} [未找到/未实现]")
|
||||
return
|
||||
|
||||
unit = units_dict[unit_name]
|
||||
status = unit.get('status', 'pending')
|
||||
status_mark = "✓" if status == "done" else "○"
|
||||
|
||||
# 计算未实现依赖数
|
||||
pending_count = len(get_pending_deps(unit_name, units_dict))
|
||||
pending_str = f" ({pending_count}未实现)" if show_pending_count and pending_count > 0 else ""
|
||||
|
||||
print(f"{prefix}{status_mark} {unit_name}{pending_str}")
|
||||
|
||||
calls = unit.get('call_deps', [])
|
||||
# 按未实现依赖数排序(未实现多的在前,因为更紧迫)
|
||||
pending_sorted = sorted(calls, key=lambda d: -len(get_pending_deps(d, units_dict) if d in units_dict else []))
|
||||
|
||||
for i, dep in enumerate(pending_sorted):
|
||||
is_last = (i == len(pending_sorted) - 1)
|
||||
connector = "└── " if is_last else "├── "
|
||||
print_dependency_tree(dep, units_dict, indent + 1, visited.copy(), prefix + connector, show_pending_count)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='分析 TLUSTY Fortran 文件依赖')
|
||||
parser.add_argument('--tree', metavar='UNIT', help='输出指定单元的依赖树')
|
||||
parser.add_argument('--priority', action='store_true', help='输出重构优先级列表')
|
||||
parser.add_argument('--full', action='store_true', help='输出完整传递依赖')
|
||||
args = parser.parse_args()
|
||||
|
||||
extracted_dir = "/home/fmq/program/tlusty/tl208-s54/rust/tlusty/extracted"
|
||||
rust_dir = "/home/fmq/program/tlusty/tl208-s54/rust/src/math"
|
||||
|
||||
# 第一遍:收集所有已定义的 SUBROUTINE 和 FUNCTION 名称
|
||||
all_defined_units = set()
|
||||
fortran_files = sorted(glob.glob(os.path.join(extracted_dir, "*.f")))
|
||||
|
||||
for fpath in fortran_files:
|
||||
with open(fpath, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
content = f.read()
|
||||
units = extract_unit_info(content, os.path.basename(fpath))
|
||||
for unit_type, unit_name in units:
|
||||
all_defined_units.add(unit_name)
|
||||
|
||||
# 第二遍:收集所有单元信息(使用已知函数名来过滤调用)
|
||||
units_dict = {}
|
||||
|
||||
for fpath in fortran_files:
|
||||
fname = os.path.basename(fpath)
|
||||
base_name = os.path.splitext(fname)[0]
|
||||
|
||||
with open(fpath, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
content = f.read()
|
||||
|
||||
includes = extract_includes(content)
|
||||
commons = extract_commons(content)
|
||||
calls = extract_calls(content, known_functions=all_defined_units)
|
||||
io = has_file_io(content)
|
||||
units = extract_unit_info(content, fname)
|
||||
|
||||
is_pure = len(includes) <= 1 and len(commons) == 0 and not io
|
||||
rust_mod = find_rust_module(base_name, rust_dir)
|
||||
status = "done" if rust_mod else "pending"
|
||||
|
||||
for unit_type, unit_name in units:
|
||||
units_dict[unit_name] = {
|
||||
'fortran_file': fname,
|
||||
'unit_type': unit_type,
|
||||
'is_pure': is_pure,
|
||||
'common_deps': includes + commons,
|
||||
'call_deps': calls,
|
||||
'has_io': io,
|
||||
'rust_module': rust_mod,
|
||||
'status': status,
|
||||
}
|
||||
|
||||
# --tree 模式:输出依赖树
|
||||
if args.tree:
|
||||
unit_name = args.tree.upper()
|
||||
if unit_name in units_dict:
|
||||
unit = units_dict[unit_name]
|
||||
trans_pending = get_transitive_pending_deps(unit_name, units_dict)
|
||||
trans_calls = get_transitive_deps(unit_name, units_dict)
|
||||
status_mark = "✓" if unit['status'] == "done" else "○"
|
||||
|
||||
print(f"依赖树: {unit_name} {status_mark}")
|
||||
print("=" * 60)
|
||||
print(f"直接依赖: {len(unit['call_deps'])}, 传递依赖: {len(trans_calls)}, "
|
||||
f"未实现: {len(trans_pending)}")
|
||||
if trans_pending:
|
||||
print(f"未实现依赖: {', '.join(sorted(trans_pending)[:10])}")
|
||||
if len(trans_pending) > 10:
|
||||
print(f" ... 还有 {len(trans_pending) - 10} 个")
|
||||
print("-" * 60)
|
||||
print_dependency_tree(unit_name, units_dict)
|
||||
else:
|
||||
print(f"未找到单元: {unit_name}")
|
||||
# 尝试模糊匹配
|
||||
matches = [u for u in units_dict if args.tree.lower() in u.lower()]
|
||||
if matches:
|
||||
print(f"可能的匹配: {', '.join(matches[:10])}")
|
||||
return
|
||||
|
||||
# --priority 模式:输出重构优先级
|
||||
if args.priority:
|
||||
# 计算每个单元的依赖深度和传递依赖数
|
||||
priority_list = []
|
||||
memo = {}
|
||||
for unit_name, unit in units_dict.items():
|
||||
if unit['status'] == 'done':
|
||||
continue
|
||||
# 跳过无法识别程序单元的文件(如纯注释文件)
|
||||
if unit['unit_type'] == 'UNKNOWN':
|
||||
continue
|
||||
# 跳过 BLOCK DATA(数据初始化块,不是函数)
|
||||
if unit['unit_type'] == 'BLOCK DATA':
|
||||
continue
|
||||
|
||||
depth = calculate_depth(unit_name, units_dict, memo)
|
||||
trans_calls = len(get_transitive_deps(unit_name, units_dict))
|
||||
trans_commons = len(get_transitive_commons(unit_name, units_dict))
|
||||
pending_deps = len(get_pending_deps(unit_name, units_dict))
|
||||
trans_pending = len(get_transitive_pending_deps(unit_name, units_dict))
|
||||
|
||||
priority_list.append({
|
||||
'name': unit_name,
|
||||
'depth': depth,
|
||||
'direct_calls': len(unit['call_deps']),
|
||||
'trans_calls': trans_calls,
|
||||
'direct_commons': len(unit['common_deps']),
|
||||
'trans_commons': trans_commons,
|
||||
'pending_deps': pending_deps,
|
||||
'trans_pending': trans_pending,
|
||||
'has_io': unit['has_io'],
|
||||
'is_pure': unit['is_pure'],
|
||||
})
|
||||
|
||||
# 按优先级排序:未实现依赖少 > 无IO > 深度低
|
||||
priority_list.sort(key=lambda x: (x['trans_pending'], x['has_io'], x['depth'], x['trans_calls']))
|
||||
|
||||
print("重构优先级列表 (按未实现依赖排序,同数量优先无IO)")
|
||||
print("=" * 100)
|
||||
print(f"{'单元名':<20} {'未实现':>6} {'传递未实现':>10} {'深度':>4} {'直接调用':>8} {'传递调用':>8} {'IO':>4}")
|
||||
print("-" * 100)
|
||||
|
||||
for item in priority_list[:100]: # 显示前100个
|
||||
io_mark = "✓" if item['has_io'] else "○"
|
||||
print(f"{item['name']:<20} {item['pending_deps']:>6} {item['trans_pending']:>10} "
|
||||
f"{item['depth']:>4} {item['direct_calls']:>8} {item['trans_calls']:>8} {io_mark:>4}")
|
||||
return
|
||||
|
||||
# 默认模式:输出 CSV(带完整依赖)
|
||||
if args.full:
|
||||
print("fortran_file,unit_name,unit_type,is_pure,common_deps,call_deps,"
|
||||
"trans_commons,trans_calls,has_io,rust_module,status")
|
||||
else:
|
||||
print("fortran_file,unit_name,unit_type,is_pure,common_deps,call_deps,has_io,rust_module,status")
|
||||
|
||||
memo = {}
|
||||
for unit_name, unit in units_dict.items():
|
||||
if args.full:
|
||||
trans_commons = get_transitive_commons(unit_name, units_dict)
|
||||
trans_calls = get_transitive_deps(unit_name, units_dict)
|
||||
print(f"{unit['fortran_file']},{unit_name},{unit['unit_type']},{unit['is_pure']},"
|
||||
f"\"{'|'.join(unit['common_deps'])}\",\"{'|'.join(unit['call_deps'])}\","
|
||||
f"\"{'|'.join(trans_commons)}\",\"{'|'.join(trans_calls)}\","
|
||||
f"{unit['has_io']},{unit['rust_module']},{unit['status']}")
|
||||
else:
|
||||
print(f"{unit['fortran_file']},{unit_name},{unit['unit_type']},{unit['is_pure']},"
|
||||
f"\"{'|'.join(unit['common_deps'])}\",\"{'|'.join(unit['call_deps'])}\","
|
||||
f"{unit['has_io']},{unit['rust_module']},{unit['status']}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user