671 lines
16 KiB
Markdown
671 lines
16 KiB
Markdown
# Fortran → Rust 重构问题与解决方案
|
||
|
||
记录重构过程中遇到的问题,避免重复踩坑。
|
||
|
||
---
|
||
|
||
## 1. Fortran 1-indexed 转 Rust 0-indexed
|
||
|
||
### 问题
|
||
Fortran 数组从 1 开始索引,Rust 从 0 开始。
|
||
|
||
### 解决方案
|
||
```rust
|
||
// 数组访问
|
||
arr(i) → arr[i-1]
|
||
|
||
// 循环范围
|
||
DO I=1,N → for i in 0..n
|
||
|
||
// 边界条件 (locate, ylintp 等)
|
||
// Fortran: jl=0 表示"在第一个有效索引之前"
|
||
// Rust: jl=0 就是第一个有效索引,无需调整
|
||
```
|
||
|
||
### 示例 (ylintp.f)
|
||
```fortran
|
||
! Fortran: jl=0 需要调整
|
||
IF (J.EQ.0) J = J+1 ! 调整到 J=1
|
||
```
|
||
```rust
|
||
// Rust: jl=0 就是第一个有效索引,直接使用
|
||
// 删除 IF (J.EQ.0) 的调整逻辑
|
||
```
|
||
|
||
---
|
||
|
||
## 2. Fortran 表达式解析错误
|
||
|
||
### 问题
|
||
`XL=-LOG(X)` 被误解为 `(-x).ln()` 而不是 `-x.ln()`
|
||
|
||
### 示例 (erfcin.f)
|
||
```fortran
|
||
XL=-LOG(X) ! 意思是: XL = -ln(X)
|
||
```
|
||
```rust
|
||
// 错误:
|
||
let xl = (-x).ln(); // ln(-x) = NaN for x>0
|
||
|
||
// 正确:
|
||
let xl = -x.ln(); // -ln(x)
|
||
```
|
||
|
||
### 教训
|
||
Fortran 中 `-LOG(X)` 是 `-(LOG(X))`,不是 `LOG(-X)`
|
||
|
||
---
|
||
|
||
## 3. powi 类型歧义
|
||
|
||
### 问题
|
||
`(z1 - z2).powi(2)` 编译错误,类型不明确
|
||
|
||
### 解决方案
|
||
```rust
|
||
// 错误:
|
||
(z1 - z2).powi(2) // 编译器无法推断类型
|
||
|
||
// 方案1: 显式乘法 (推荐)
|
||
(z1 - z2) * (z1 - z2)
|
||
|
||
// 方案2: 显式类型标注
|
||
((z1 - z2): f64).powi(2)
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 多项式近似精度
|
||
|
||
### 问题
|
||
eint 函数 Rust 与 Fortran 结果差异 ~1.3e-8
|
||
|
||
### 原因
|
||
Abramowitz-Stegun 多项式近似本身精度有限
|
||
|
||
### 解决方案
|
||
放宽 epsilon 到 1e-7
|
||
```rust
|
||
// 简单函数: epsilon = 1e-10
|
||
// 多项式近似: epsilon = 1e-7
|
||
assert_relative_eq!(e1, exp_e1, epsilon = 1e-7);
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 数组索引越界
|
||
|
||
### 问题
|
||
`locate` 函数无限循环
|
||
|
||
### 原因
|
||
Fortran 中 ju=N+1 (越界值),Rust 直接用导致越界访问
|
||
|
||
### 解决方案
|
||
使用 i64 允许负值,或调整边界逻辑
|
||
```rust
|
||
// 使用 i64 支持 jl=-1
|
||
let mut jl: i64 = -1;
|
||
let mut ju: i64 = n as i64;
|
||
```
|
||
|
||
---
|
||
|
||
## 6. voigte 索引偏移
|
||
|
||
### 问题
|
||
voigte 返回负值
|
||
|
||
### 原因
|
||
Fortran m 值是 1-indexed,Rust 中直接用导致索引偏移
|
||
|
||
### 解决方案
|
||
```rust
|
||
// Fortran: m=6 表示第6个元素
|
||
// Rust: 需要用 m-1
|
||
let (m, quo) = if v < 2.4 {
|
||
(5, 1.0) // Fortran m=6 → 0-indexed m=5
|
||
} else {
|
||
(10, 1.0) // Fortran m=11 → 0-indexed m=10
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 条件分支遗漏
|
||
|
||
### 问题
|
||
voigte 某些参数组合返回错误值
|
||
|
||
### 原因
|
||
漏掉了 Fortran 中的嵌套条件判断
|
||
|
||
### 教训
|
||
仔细对比 Fortran 的所有分支,特别是嵌套 IF
|
||
|
||
---
|
||
|
||
## 8. COMMON 依赖误判
|
||
|
||
### 问题
|
||
某些标记为"纯函数"的文件实际有 COMMON 依赖
|
||
|
||
### 已确认有依赖的文件
|
||
```
|
||
gamsp.f - 使用 VOIPAR COMMON
|
||
sgmer1.f - 使用 COMMON
|
||
sgmerd.f - 使用 COMMON
|
||
cross.f - 使用 COMMON
|
||
gfree1.f - 使用 COMMON
|
||
gfree0.f - 使用 BASICS, MODELQ COMMON
|
||
gfreed.f - 使用 BASICS, MODELQ COMMON
|
||
wn.f - 使用 BASICS COMMON
|
||
crossd.f - 使用 BASICS, ATOMIC, MODELQ COMMON
|
||
dopgam.f - 使用 BASICS, ATOMIC, MODELQ COMMON
|
||
verner.f - 使用 BASICS, ATOMIC COMMON
|
||
sbfhe1.f - 使用 BASICS, ATOMIC COMMON
|
||
rayini.f - 有文件 I/O + COMMON
|
||
```
|
||
|
||
### 解决方案
|
||
重构前检查所有 INCLUDE 语句,不仅是 IMPLIC.FOR
|
||
|
||
---
|
||
|
||
## 9. Clenshaw 求和整数溢出
|
||
|
||
### 问题
|
||
collhe 中递减循环溢出
|
||
|
||
### 原因
|
||
`ir` 和 `jj` 递减到负数时,无符号整数溢出
|
||
|
||
### 解决方案
|
||
```rust
|
||
// 错误: 用 usize
|
||
let mut ir: usize = ...;
|
||
ir -= 1; // 当 ir=0 时溢出!
|
||
|
||
// 正确: 用有符号整数
|
||
let mut ir: isize = ...;
|
||
ir -= 1; // 正常变为 -1
|
||
```
|
||
|
||
---
|
||
|
||
## 10. 索引计算中间结果溢出
|
||
|
||
### 问题
|
||
collhe 的索引计算 `((iu+1)^2 - 3*(iu+1) + 4)/2` 溢出
|
||
|
||
### 原因
|
||
中间结果超出 usize 范围
|
||
|
||
### 解决方案
|
||
```rust
|
||
// 使用 i32 避免中间结果溢出
|
||
let idx = (((iu + 1) * (iu + 1) - 3 * (iu + 1) + 4) / 2 - 1) as usize;
|
||
```
|
||
|
||
---
|
||
|
||
## 11. 数组变量命名冲突
|
||
|
||
### 问题
|
||
不同 Fortran 文件中同名变量冲突
|
||
|
||
### 原因
|
||
Fortran 文件局部作用域,Rust 需要唯一名称
|
||
|
||
### 解决方案
|
||
修改 `extract_fortran_data.py`:所有变量添加文件名前缀
|
||
```rust
|
||
// 旧: ADI, BDENS (冲突)
|
||
// 新: DIELRC_ADI, DIELRC_BDENS
|
||
pub const DIELRC_ADI: [f64; 18] = [...];
|
||
```
|
||
|
||
---
|
||
|
||
## 12. 2D 数组存储顺序
|
||
|
||
### 问题
|
||
Fortran 列优先,Rust 行优先
|
||
|
||
### 解决方案
|
||
脚本自动转置,访问方式改变
|
||
```rust
|
||
// Fortran: ARR(j, i) 列优先
|
||
// Rust: ARR[i][j] 行优先 (已转置)
|
||
```
|
||
|
||
---
|
||
|
||
## 13. DATA 语句数值解析
|
||
|
||
### 问题
|
||
负数和科学计数法中有空格
|
||
|
||
### 示例
|
||
```fortran
|
||
DATA A / - 14.2, 1.48 D-2 /
|
||
```
|
||
|
||
### 解决方案
|
||
```python
|
||
# 修复负数空格
|
||
val = re.sub(r'-\s+(\d)', r'-\1', val)
|
||
# 修复科学计数法空格
|
||
val = re.sub(r'(\d)\s+([eEdD])', r'\1\2', val)
|
||
```
|
||
|
||
---
|
||
|
||
## 14. BPOPF 测试失败 - 温度列与主循环列重叠
|
||
|
||
### 问题
|
||
BPOPF 函数测试中,B[20][21] 的值是 -0.12 而不是预期的 -0.02。
|
||
|
||
### 原因
|
||
测试参数设置导致温度列 (NRE) 与主循环的某一列 (NSE+II) 重叠:
|
||
- 主循环更新列 NSE+1 到 NSE+NLVEXP (Fortran 1-indexed: 21-23)
|
||
- 温度列是 NRE (Fortran 1-indexed: 22)
|
||
- 当 II=2 时,列 NSE+II = 22 = NRE,两者写入同一列
|
||
|
||
### 解决方案
|
||
测试参数设置时避免列重叠:
|
||
```rust
|
||
let params = BpopfParams {
|
||
nfreqe: 10,
|
||
inse: 11, // NSE = 10 + 11 - 1 = 20
|
||
inre: 0, // 不更新温度列,避免与主循环列重叠
|
||
inpc: 0, // 不更新电子密度列
|
||
// ...
|
||
};
|
||
```
|
||
|
||
### Fortran 索引转换注意事项
|
||
- `NSE = NFREQE + INSE - 1` (Fortran 起始列索引)
|
||
- `NRE = NFREQE + INRE` (温度列,1-indexed)
|
||
- Rust 主循环: `col = nse + ii` (nse 已经是 0-indexed 起始点)
|
||
- Rust 温度列: `col = (nre - 1) as usize` (需要减 1 转换为 0-indexed)
|
||
|
||
### 调试技巧
|
||
1. 添加 `#[cfg(test)]` 条件编译的调试输出
|
||
2. 打印 ESEMAT 矩阵验证是否为单位矩阵
|
||
3. 打印每次 B 矩阵更新的行、列、值
|
||
4. 检查温度列和电子密度列是否与主循环列重叠
|
||
|
||
---
|
||
|
||
## 15. RAYLEIGH 氦散射截面公式错误
|
||
|
||
### 问题
|
||
Rayleigh 氦散射截面计算结果不正确。
|
||
|
||
### 原因
|
||
公式理解错误:
|
||
- 错误: `5.484e-14 / x2 * (...)` 其中 `x2 = 1/x²`
|
||
- 正确: `5.484e-14 / (x * x) * (...)`
|
||
|
||
### 解决方案
|
||
```rust
|
||
// 错误写法
|
||
let x2 = 1.0 / (x * x);
|
||
raysct.rche[ik] = 5.484e-14 / x2 * (...); // 这等于 5.484e-14 * x²
|
||
|
||
// 正确写法
|
||
raysct.rche[ik] = 5.484e-14 / (x * x) * (...); // 这等于 5.484e-14 / x²
|
||
```
|
||
|
||
---
|
||
|
||
## 16. ALIFR3 - 大型 COMMON 块函数重构策略
|
||
|
||
### 问题
|
||
ALIFR3 函数依赖大量 COMMON 块变量(来自 FIXALP, MODELQ, ALIPAR 等)。
|
||
|
||
### 解决方案
|
||
创建综合的输入结构体,使用生命周期参数引用数据:
|
||
|
||
```rust
|
||
/// 输入状态结构体 (使用生命周期引用数据)
|
||
pub struct Alifr3ModelState<'a> {
|
||
// 深度相关 (MDEPTH)
|
||
pub elec: &'a [f64],
|
||
pub densi: &'a [f64],
|
||
// ... 其他字段
|
||
|
||
// 输出累积变量 (可变引用)
|
||
pub heit: &'a mut [f64],
|
||
pub hein: &'a mut [f64],
|
||
// ... 其他字段
|
||
}
|
||
```
|
||
|
||
### 多分支条件处理
|
||
Fortran 使用 GOTO 分支,Rust 使用 if-else:
|
||
|
||
```fortran
|
||
IF(ILMCOR.NE.3) GO TO 199
|
||
! ... ILMCOR==3 的代码
|
||
199 IF(ILASCT.NE.0) GO TO 299
|
||
! ... ILASCT==0 的代码
|
||
299 ! ... ILASCT!=0 的代码
|
||
```
|
||
|
||
```rust
|
||
if params.ilmcor == 3 {
|
||
// ... ILMCOR==3 的代码
|
||
return;
|
||
}
|
||
if params.ilasct == 0 {
|
||
// ... ILASCT==0 的代码
|
||
return;
|
||
}
|
||
// ... ILASCT!=0 的代码
|
||
```
|
||
|
||
### 注意事项
|
||
- ABST 的计算方式在不同分支中不同:
|
||
- ILMCOR==3: `ABST = UN/ABSO1(ID)`
|
||
- ILASCT==0: `ABST = UN/(ABSO1(ID)-ELSCAT(ID))`
|
||
- ILASCT!=0: `ABST = UN/ABSO1(ID)`
|
||
- DSFN1 的计算在不同分支也有差异(是否包含 SIGEC 项)
|
||
|
||
---
|
||
|
||
## 17. ALIFR6 - COMMON 块结构体命名冲突
|
||
|
||
### 问题
|
||
添加新的 COMMON 块结构体时,编译器报错 `ambiguous glob re-exports: the name 'Comptn'`。
|
||
|
||
### 原因
|
||
`Comptn` 结构体同时存在于:
|
||
- `src/state/config.rs` - Compton 散射角度参数(已有)
|
||
- `src/state/model.rs` - 新添加的重复定义
|
||
|
||
两个模块都通过 `pub use *` 重新导出,导致名称冲突。
|
||
|
||
### 解决方案
|
||
添加新结构体前,先搜索是否已存在:
|
||
```bash
|
||
grep -r "pub struct Comptn" src/state/
|
||
```
|
||
|
||
如果已存在,扩展现有结构体而不是创建新的。
|
||
|
||
---
|
||
|
||
## 18. ALIFR6 - 循环中可变变量重新赋值
|
||
|
||
### 问题
|
||
在循环中重新赋值 `s0p = s0p_new` 报错 `cannot assign twice to immutable variable`。
|
||
|
||
### 原因
|
||
变量在 if-else 块中首次定义后,无法在循环中重新赋值。
|
||
|
||
### 解决方案
|
||
```rust
|
||
// 错误:不可变变量
|
||
let (s0p, dsft1p, dsfn1p) = if ... { ... };
|
||
|
||
// 正确:声明为可变
|
||
let (mut s0p, mut dsft1p, mut dsfn1p) = if ... { ... };
|
||
```
|
||
|
||
---
|
||
|
||
## 19. ALIFR6/ALIFR3 - 大型函数三段式处理模式
|
||
|
||
### 模式
|
||
ALIFR6 和 ALIFR3 类函数有三个独立处理部分:
|
||
|
||
1. **第一个深度点 (ID=1)**
|
||
- 特殊边界条件
|
||
- DSFT1M/DSFN1M 初始化为 0
|
||
|
||
2. **深度循环 (ID=2 到 ND-1)**
|
||
- 保存前一步值 (DSFTMM, DSFNMM)
|
||
- 更新当前值
|
||
- 计算下一步值 (DSFT1P, DSFN1P)
|
||
|
||
3. **最深点 (ID=ND)**
|
||
- IBC 下边界条件处理
|
||
- 可能的额外导数 (DSFT1D, DSFN1D)
|
||
|
||
### IBC 下边界条件
|
||
| IBC | 说明 |
|
||
|-----|------|
|
||
| 0 | 无特殊处理 |
|
||
| 1 | 简单 Planck 函数修正 |
|
||
| 2 | 改进边界条件 |
|
||
| 3 | 完整边界条件 + DSFT1D/DSFN1D |
|
||
|
||
---
|
||
|
||
## 20. ALIFR6 - 三对角 Lambda* 算子
|
||
|
||
### 特点
|
||
ALIFR6 与 ALIFR3 的主要区别是额外计算三对角算子:
|
||
|
||
- **下对角线**: AREIT, AREIN, AREIP (使用 ALIM1)
|
||
- **上对角线**: CREIT, CREIN, CREIP (使用 ALIP1)
|
||
- **对角线**: REIT, REIN, REIP (使用 ALI1)
|
||
|
||
### IFALI >= 7 额外计算
|
||
- HEITP, HEINP, HEIPP (He 相关)
|
||
- REDTP, REDNP, REDPP (Red 相关)
|
||
- EHET, EHEN, EHEP (Ehe 相关)
|
||
- ERET, EREN, EREP (Ere 相关)
|
||
|
||
---
|
||
|
||
## 快速检查清单
|
||
|
||
重构新函数前检查:
|
||
|
||
- [ ] 是否有 INCLUDE 语句 (除 IMPLIC.FOR 外)
|
||
- [ ] 是否使用 COMMON 块
|
||
- [ ] COMMON 块结构体是否已存在于其他模块 (grep 检查)
|
||
- [ ] 是否有文件 I/O (OPEN, READ, WRITE)
|
||
- [ ] 数组索引是否需要 -1 调整
|
||
- [ ] 循环变量是否可能变负 (用 isize/i32)
|
||
- [ ] 多项式近似精度是否需要放宽
|
||
- [ ] 是否有嵌套 IF 分支遗漏
|
||
- [ ] 矩阵列索引是否可能与温度/密度列重叠
|
||
- [ ] 公式中 `1/x` 和 `x` 的顺序是否正确
|
||
- [ ] 大型 COMMON 块函数是否需要创建综合输入结构体
|
||
- [ ] 多分支 GOTO 是否正确转换为 if-else
|
||
- [ ] 循环中重新赋值的变量是否声明为 mut
|
||
- [ ] 变量字段在哪个 COMMON 块 → 对应哪个 Rust 结构体 (查 `.FOR` 原文)
|
||
- [ ] 测试初始化用 `ModelState::new()` 而非 `::default()`
|
||
- [ ] 二维数组内层维度是否是深度/角度/频率 (不要默认 MDEPTH)
|
||
- [ ] loop 内部积分变量是否在 loop 外声明以便 break 后使用
|
||
|
||
---
|
||
|
||
## 21. COMMON 块变量所属结构体查找
|
||
|
||
### 问题
|
||
Fortran COMMON 块变量(如 `IDISK`、`IBC`、`ICHCOO`、`IJORIG`)应放在哪个 Rust 结构体中?
|
||
|
||
### 解决方案
|
||
通过查阅 `.FOR` 中的 COMMON 定义来确认:
|
||
- `IDISK`、`IBC` → `BASICS.FOR` 的 `COMMON/BASNUM/` → `config.basnum`
|
||
- `ICHCOO`、`ICOMST`、`ICOMDE` → `BASICS.FOR` 的 `common/compti/` → `config.compti`
|
||
- `IJORIG` → `BASICS.FOR` 的 `common/comptn/` → `config.comptn.ijorig`
|
||
- `IWINBL` → `MODELQ.FOR` 的 `COMMON/WINDBL/` → `model.windbl.iwinbl`
|
||
- `IFZ0` → `BASICS.FOR` 的 `COMMON/CENTRL/` → `config.centrl.ifz0`
|
||
|
||
```bash
|
||
# 快速查找变量所在 COMMON 块
|
||
grep -i "变量名" tlusty/extracted/*.FOR
|
||
```
|
||
|
||
### 教训
|
||
编译报错 `no field 'xxx' on type 'Accel'` 时,不要盲目添加字段——先查原始 `.FOR` 确认 COMMON 归属。
|
||
|
||
---
|
||
|
||
## 22. 循环外变量对 break 后可见
|
||
|
||
### 问题
|
||
`ah`、`qq0`、`u0` 在 ALI 循环内定义,循环 `break` 后用于写回结果时编译报 `cannot find value`。
|
||
|
||
### 原因
|
||
Rust 变量作用域严格,`loop {}` 块内 `let` 声明的变量在块外不可见。
|
||
|
||
### 解决方案
|
||
```rust
|
||
// 正确:循环前提前声明,循环内用 _inner 临时变量
|
||
let mut ah = 0.0f64;
|
||
let mut qq0 = 0.0f64;
|
||
loop {
|
||
let mut ah_inner = 0.0f64;
|
||
let mut qq0_inner = 0.0f64;
|
||
// 积分计算...
|
||
if converged {
|
||
ah = ah_inner; // break 前赋值回外部变量
|
||
qq0 = qq0_inner;
|
||
break;
|
||
}
|
||
}
|
||
model.surfac.flux[ij] = ah; // 此处可用
|
||
```
|
||
|
||
### 常见错误(变量被遮蔽)
|
||
```rust
|
||
let mut ah = 0.0f64;
|
||
loop {
|
||
let mut ah = 0.0; // ← 遮蔽外层 ah!外层永远是 0
|
||
if converged { break; }
|
||
}
|
||
// ah 还是 0
|
||
```
|
||
|
||
---
|
||
|
||
## 23. 测试初始化:`ModelState::new()` vs `::default()`
|
||
|
||
### 问题
|
||
测试报错 `index out of bounds: the len is 0 but the index is 0`,即便访问 `model.totrad.rad[0][0]`。
|
||
|
||
### 原因
|
||
`ModelState` 有 `#[derive(Default)]`,但 `CurRad` 等子结构体**只有 `new()` 方法,没有手动 `Default` 实现**。
|
||
`derive(Default)` 对 `Vec` 产生空 Vec,访问任意元素都越界。
|
||
|
||
### 解决方案
|
||
```rust
|
||
// ❌ 错误:CurRad::default() 产生空 Vec
|
||
let mut model = ModelState::default();
|
||
|
||
// ✅ 正确:ModelState::new() 调用 CurRad::new(),正确分配数组
|
||
let mut model = ModelState::new();
|
||
```
|
||
|
||
---
|
||
|
||
## 24. `extint` 索引:内层是角度维度
|
||
|
||
### 问题
|
||
`model.totrad.extint[ij][i]` 在 `for i in 0..MDEPTH` 循环中越界(len=6, index=6)。
|
||
|
||
### 原因
|
||
`extint` 对应 Fortran `EXTINT(MFREQ, MMU)`:
|
||
- 外层:频率索引(MFREQ)
|
||
- **内层:角度索引(MMU = 6),不是深度!**
|
||
|
||
### 解决方案
|
||
```rust
|
||
// 正确:遍历角度 (MMU=6)
|
||
use crate::state::constants::MMU;
|
||
for i in 0..MMU {
|
||
model.totrad.extint[0][i] = 0.0;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 25. 常用二维数组维度汇总
|
||
|
||
| 变量 | 外层维度 | 内层维度 |
|
||
|------|---------|---------|
|
||
| `totrad.rad[ij][id]` | 频率 MFREQ | 深度 MDEPTH |
|
||
| `totrad.extint[ij][i]` | 频率 MFREQ | **角度 MMU** |
|
||
| `totrad.fak[ij][id]` | 频率 MFREQ | 深度 MDEPTH |
|
||
| `comptf.delj[iji][id]` | 频率 MFREQ | 深度 MDEPTH |
|
||
| `expraf.radex[ije][id]` | 显式频率 MFREX | 深度 MDEPTH |
|
||
| `angles.amu[i]` / `wtmu[i]` | 角度 MMU | — |
|
||
|
||
### 教训
|
||
看到二维数组前,确认两个维度各是什么,不要默认都是 MDEPTH。
|
||
|
||
---
|
||
|
||
## 26. 2026-03-21 新增模块完整性检查
|
||
|
||
### SETDRT (密度对温度的导数)
|
||
- **状态**: ✅ 完整
|
||
- 原版 Fortran 调用 RHOEOS 函数
|
||
- Rust 版使用泛型函数参数 `rhoeos_fn`,设计更灵活
|
||
- 有限差分计算完全一致
|
||
|
||
### TAUFR1 (光学深度计算)
|
||
- **状态**: ✅ 完整
|
||
- `ss0` 数组在原版中也计算但未使用,与 Fortran 一致
|
||
- `XCON` 常量在原版定义但未使用,已忽略
|
||
- 核心逻辑:光学深度计算、参考深度插值、Planck 函数计算
|
||
|
||
### TABINT (频率表插值)
|
||
- **状态**: ⚠️ 部分实现
|
||
- **问题**: `interpolate_opacity` 函数中未实际修改 `absopac` 数组
|
||
- 代码中有注释 "暂时跳过实际修改"
|
||
- 二分查找和插值系数计算完整
|
||
|
||
**待修复**:
|
||
```rust
|
||
// 当前代码 (错误):
|
||
let opac = rc * (params.freq[ij] / frtab[j - 1]).log10() + absort[j - 1];
|
||
let _ = opac; // 计算了但没有写回
|
||
|
||
// 应该:
|
||
// 需要设计一个可变引用来修改 absopac
|
||
```
|
||
|
||
### RYBMAT (Rybicki矩阵)
|
||
- **状态**: ⚠️ 简化实现
|
||
- 原版 ~390 行 → Rust 375 行
|
||
- **问题**: 测试失败 `result.rb[0].is_finite()` 返回 NaN
|
||
|
||
**可能原因**:
|
||
1. 边界条件 `id=0` 时 `dm[id+1] - dm[id]` 可能为零
|
||
2. `abso1` 数组可能包含零值导致除零
|
||
3. Hermitian 方法 (`isplin=2`) 被简化
|
||
|
||
**待修复**:
|
||
```rust
|
||
// 需要添加边界检查
|
||
let ddm = (params.dm[id + 1] - params.dm[id]) * HALF;
|
||
if ddm.abs() < 1e-30 {
|
||
continue; // 跳过无效深度点
|
||
}
|
||
let dtm = UN / ((params.abso1[id] + params.abso1[id + 1]) * ddm);
|
||
```
|
||
|
||
---
|
||
|
||
## 27. 优先级列表注意事项
|
||
|
||
优先级列表 (`python3 scripts/analyze_fortran.py --priority`) 显示"传递未实现=0"的函数可能有隐藏依赖:
|
||
|
||
- **SETDRT** 调用 RHOEOS(标记为 pending),但通过函数参数传入解决了
|
||
- **TABINT** 无外部调用,真正独立
|
||
- **TAUFR1** 无外部调用,真正独立
|
||
- **RYBMAT** 无外部调用,但依赖大量 COMMON 块变量
|
||
|
||
**教训**: 优先级列表只检查显式的 SUBROUTINE/FUNCTION 调用,不检查:
|
||
1. COMMON 块依赖
|
||
2. 通过参数传入的函数指针
|
||
3. 隐式的外部函数引用
|