From 59404207c3a322a340d48efe2238f865da32653a Mon Sep 17 00:00:00 2001 From: Asfmq <2696428814@qq.com> Date: Sun, 22 Mar 2026 00:30:20 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=844?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .learnings/ERRORS.md | 9 + .learnings/FEATURE_REQUESTS.md | 9 + .learnings/LEARNINGS.md | 446 +++++++++ CLAUDE.md | 101 +- MEMORY/FORTRAN_TRACKING.md | 337 ------- MEMORY/MEMORY.md | 110 --- MEMORY/REFACTORING_GUIDE.md | 374 -------- MEMORY/fortran_analysis.csv | 305 ------ MEMORY/plan.md | 77 -- MEMORY/refactoring_notes.md | 670 -------------- docs/REFACTORING_PLAN.md | 283 ------ docs/sp_fortran.md | 403 -------- extract_fortran.py | 302 ------ fortran_analysis.csv | 305 ------ scripts/analyze_fortran.py | 434 --------- scripts/extract_fortran_data.py | 438 --------- scripts/fortran_to_rust_array.py | 205 ----- scripts/generate_tracking.py | 82 -- src/math/alifr1.rs | 1483 ++++++++++++++++++++++++++++++ src/math/cspec.rs | 199 ++++ src/math/levset.rs | 427 +++++++++ src/math/mod.rs | 14 + src/math/odffr.rs | 307 +++++++ src/math/opadd0.rs | 271 ++++++ src/math/opctab.rs | 418 +++++++++ src/math/ratmat.rs | 369 ++++++++ src/state/alipar.rs | 9 + 27 files changed, 4035 insertions(+), 4352 deletions(-) create mode 100644 .learnings/ERRORS.md create mode 100644 .learnings/FEATURE_REQUESTS.md create mode 100644 .learnings/LEARNINGS.md delete mode 100644 MEMORY/FORTRAN_TRACKING.md delete mode 100644 MEMORY/MEMORY.md delete mode 100644 MEMORY/REFACTORING_GUIDE.md delete mode 100644 MEMORY/fortran_analysis.csv delete mode 100644 MEMORY/plan.md delete mode 100644 MEMORY/refactoring_notes.md delete mode 100644 docs/REFACTORING_PLAN.md delete mode 100644 docs/sp_fortran.md delete mode 100644 extract_fortran.py delete mode 100644 fortran_analysis.csv delete mode 100644 scripts/analyze_fortran.py delete mode 100644 scripts/extract_fortran_data.py delete mode 100644 scripts/fortran_to_rust_array.py delete mode 100644 scripts/generate_tracking.py create mode 100644 src/math/alifr1.rs create mode 100644 src/math/cspec.rs create mode 100644 src/math/levset.rs create mode 100644 src/math/odffr.rs create mode 100644 src/math/opadd0.rs create mode 100644 src/math/opctab.rs create mode 100644 src/math/ratmat.rs diff --git a/.learnings/ERRORS.md b/.learnings/ERRORS.md new file mode 100644 index 0000000..23ed797 --- /dev/null +++ b/.learnings/ERRORS.md @@ -0,0 +1,9 @@ +# Errors + +Command failures, exceptions, and unexpected behavior captured during development. + +**Areas**: frontend | backend | infra | tests | docs | config +**Statuses**: pending | in_progress | resolved | wont_fix + +--- + diff --git a/.learnings/FEATURE_REQUESTS.md b/.learnings/FEATURE_REQUESTS.md new file mode 100644 index 0000000..a431ce0 --- /dev/null +++ b/.learnings/FEATURE_REQUESTS.md @@ -0,0 +1,9 @@ +# Feature Requests + +User-requested capabilities and enhancements. + +**Areas**: frontend | backend | infra | tests | docs | config +**Statuses**: pending | in_progress | resolved | wont_fix + +--- + diff --git a/.learnings/LEARNINGS.md b/.learnings/LEARNINGS.md new file mode 100644 index 0000000..3119742 --- /dev/null +++ b/.learnings/LEARNINGS.md @@ -0,0 +1,446 @@ +# Learnings + +Corrections, insights, and knowledge gaps captured during development. + +**Categories**: correction | insight | knowledge_gap | best_practice +**Areas**: frontend | backend | infra | tests | docs | config +**Statuses**: pending | in_progress | resolved | wont_fix | promoted | promoted_to_skill + +## Status Definitions + +| Status | Meaning | +|--------|---------| +| `pending` | Not yet addressed | +| `in_progress` | Actively being worked on | +| `resolved` | Issue fixed or knowledge integrated | +| `wont_fix` | Decided not to address (reason in Resolution) | +| `promoted` | Elevated to CLAUDE.md, AGENTS.md, or copilot-instructions.md | +| `promoted_to_skill` | Extracted as a reusable skill | + +--- + +## [LRN-20260321-F01] correction + +**Logged**: 2026-03-21T19:55:00Z +**Priority**: high +**Status**: resolved +**Area**: backend + +### Summary +Fortran 1-indexed 转 Rust 0-indexed 的关键规则 + +### Details +- 数组访问: `arr(i)` → `arr[i-1]` +- 循环范围: `DO I=1,N` → `for i in 0..n` +- 边界条件: Fortran jl=0 表示"在第一个有效索引之前",Rust jl=0 就是第一个有效索引 + +### Suggested Action +重构时删除类似 `IF (J.EQ.0) J = J+1` 的调整逻辑 + +### Metadata +- Source: fortran-to-rust refactoring +- Related Files: ylintp.f, locate.f +- Tags: indexing, fortran, rust + +--- + +## [LRN-20260321-F02] correction + +**Logged**: 2026-03-21T19:55:00Z +**Priority**: high +**Status**: resolved +**Area**: backend + +### Summary +Fortran 表达式 `-LOG(X)` 是 `-(LOG(X))`,不是 `LOG(-X)` + +### Details +`XL=-LOG(X)` 应翻译为 `let xl = -x.ln()`,不是 `let xl = (-x).ln()` +后者对 x>0 会返回 NaN + +### Suggested Action +翻译数学表达式时注意运算符优先级 + +### Metadata +- Source: fortran-to-rust refactoring +- Related Files: erfcin.f +- Tags: expression, fortran, rust + +--- + +## [LRN-20260321-F03] insight + +**Logged**: 2026-03-21T19:55:00Z +**Priority**: medium +**Status**: resolved +**Area**: backend + +### Summary +`(z1 - z2).powi(2)` 编译错误 - 类型推断失败 + +### Details +编译器无法推断中间表达式的类型,使用显式乘法 `(z1 - z2) * (z1 - z2)` 代替 + +### Metadata +- Source: fortran-to-rust refactoring +- Tags: rust, type_inference, powi + +--- + +## [LRN-20260321-F04] best_practice + +**Logged**: 2026-03-21T19:55:00Z +**Priority**: medium +**Status**: resolved +**Area**: tests + +### Summary +多项式近似精度容差:简单函数 1e-10,多项式 1e-7 + +### Details +Abramowitz-Stegun 多项式近似本身精度有限 + +### Metadata +- Source: fortran-to-rust refactoring +- Related Files: eint.f +- Tags: precision, polynomial, testing + +--- + +## [LRN-20260321-F05] correction + +**Logged**: 2026-03-21T19:55:00Z +**Priority**: high +**Status**: resolved +**Area**: backend + +### Summary +递减循环变量用 isize 不用 usize,避免溢出 + +### Details +当循环变量递减到负数时,无符号整数会溢出 + +### Metadata +- Source: fortran-to-rust refactoring +- Related Files: collhe.f +- Tags: overflow, rust, loop + +--- + +## [LRN-20260321-F06] best_practice + +**Logged**: 2026-03-21T19:55:00Z +**Priority**: high +**Status**: resolved +**Area**: backend + +### Summary +重构前必须检查所有 INCLUDE 语句,不仅是 IMPLIC.FOR + +### Details +已确认有 COMMON 依赖的"纯函数"文件: +gamsp.f, sgmer1.f, sgmerd.f, cross.f, gfree0.f, gfreed.f, gfree1.f, wn.f, crossd.f, dopgam.f, verner.f, sbfhe1.f + +### Suggested Action +添加新结构体前先 grep 检查是否已存在 + +### Metadata +- Source: fortran-to-rust refactoring +- Tags: common, fortran, dependency + +--- + +## [LRN-20260321-F07] correction + +**Logged**: 2026-03-21T19:55:00Z +**Priority**: high +**Status**: resolved +**Area**: tests + +### Summary +测试数组值不能全部相同,会导致除零 + +### Details +```rust +// 错误:全部相同 → ddm = 0 → dtm = 1/0 = inf +let dm = vec![0.01; MDEPTH]; + +// 正确:递增模拟物理分布 +let dm: Vec = (0..MDEPTH).map(|i| 0.01 + i as f64 * 0.01).collect(); +``` + +### Metadata +- Source: fortran-to-rust refactoring +- Related Files: rybmat.rs +- Tags: testing, data_design + +--- + +## [LRN-20260321-F08] insight + +**Logged**: 2026-03-21T19:55:00Z +**Priority**: medium +**Status**: resolved +**Area**: backend + +### Summary +Fortran 修改 COMMON 块数组 → Rust 需要 &mut 引用 + +### Metadata +- Source: fortran-to-rust refactoring +- Related Files: tabint.f +- Tags: rust, mutability, reference + +--- + +## [LRN-20260321-F09] correction + +**Logged**: 2026-03-21T19:55:00Z +**Priority**: high +**Status**: resolved +**Area**: config + +### Summary +analyze_fortran.py 只匹配 CALL 语句,漏掉 FUNCTION 调用 + +### Details +需要两遍扫描:1) 收集所有已定义函数名 2) 只匹配已知函数名 + +### Metadata +- Source: fortran-to-rust refactoring +- Related Files: scripts/analyze_fortran.py +- Tags: dependency, analysis, python + +--- + +## [LRN-20260321-F10] correction + +**Logged**: 2026-03-21T19:55:00Z +**Priority**: high +**Status**: resolved +**Area**: tests + +### Summary +测试参数设置时避免矩阵列重叠 + +### Details +主循环更新列和温度/密度列可能重叠,设置 inre=0, inpc=0 避免重叠 + +### Metadata +- Source: fortran-to-rust refactoring +- Related Files: bpopf.rs +- Tags: testing, matrix, indexing + +--- + +## [LRN-20260321-F11] correction + +**Logged**: 2026-03-21T19:55:00Z +**Priority**: high +**Status**: resolved +**Area**: backend + +### Summary +公式中 `1/x` 和 `x` 的顺序要仔细核对 + +### Details +`5.484e-14 / x2 * (...)` 其中 x2=1/x²,等于乘以 x²,不是除以 + +### Metadata +- Source: fortran-to-rust refactoring +- Related Files: rayleig.f +- Tags: formula, rust, physics + +--- + +## [LRN-20260321-F12] best_practice + +**Logged**: 2026-03-21T19:55:00Z +**Priority**: medium +**Status**: resolved +**Area**: backend + +### Summary +大型 COMMON 块函数使用生命周期结构体 + +### Details +```rust +pub struct Alifr3ModelState<'a> { + pub elec: &'a [f64], // 输入 + pub heit: &'a mut [f64], // 输出 +} +``` + +### Metadata +- Source: fortran-to-rust refactoring +- Related Files: alifr3.rs, alifr6.rs +- Tags: rust, struct, lifetime + +--- + +## [LRN-20260321-F13] insight + +**Logged**: 2026-03-21T19:55:00Z +**Priority**: medium +**Status**: resolved +**Area**: backend + +### Summary +Fortran GOTO 分支 → Rust if-else + early return + +### Details +每个 GOTO 标签对应一个 if 块 + +### Metadata +- Source: fortran-to-rust refactoring +- Tags: fortran, goto, control_flow + +--- + +## [LRN-20260321-F14] correction + +**Logged**: 2026-03-21T19:55:00Z +**Priority**: medium +**Status**: resolved +**Area**: backend + +### Summary +循环中重新赋值的变量必须声明为 mut + +### Metadata +- Source: fortran-to-rust refactoring +- Related Files: alifr6.rs +- Tags: rust, mutability + +--- + +## [LRN-20260321-F15] correction + +**Logged**: 2026-03-21T12:00:00Z +**Priority**: high +**Status**: resolved +**Area**: backend + +### Summary +Fortran 3D 数组转 Rust 扁平数组时,必须知道所有维度才能正确计算索引 + +### Details +Fortran `absopac(jt,jr,jf)` 是 3D 数组 (numtemp × numrh × nfreq),转 Rust 扁平数组时: +- **错误**: 用 `jt_idx * numtemp * nfreq` (用了 numtemp 作为第二维度) +- **正确**: 用 `jt_idx * max_numrh * nfreq` (必须用实际维度 max_numrh) + +类似地,2D 数组 `rhomat(jt,jr)` 转扁平时: +- **错误**: `rhomat[jt_idx * numtemp]` +- **正确**: `rhomat[jt_idx * max_numrh]` + +### Suggested Action +重构多维数组时,结构体必须包含所有维度信息,特别是可能变化的维度(如每个温度有不同的密度数) + +### Metadata +- Source: fortran-to-rust refactoring +- Related Files: opctab.rs, opctab.f +- Tags: array, indexing, fortran, rust, multi-dimensional + +--- + +## [LRN-20260321-F16] correction + +**Logged**: 2026-03-21T12:00:00Z +**Priority**: high +**Status**: resolved +**Area**: backend + +### Summary +Fortran 条件 `if(a.eq.b)` 转 Rust 时不要误用其他变量 + +### Details +Fortran `if(numtemp.eq.nd)` 检查表温度数是否等于总深度数: +- **错误**: `if table.numtemp == params.id` (用了当前深度索引) +- **正确**: `if table.numtemp == table.nd` (用总深度数) + +### Metadata +- Source: fortran-to-rust refactoring +- Related Files: opctab.rs, opctab.f +- Tags: condition, variable, fortran, rust + +--- + +## [LRN-20260321-F17] correction + +**Logged**: 2026-03-21T12:00:00Z +**Priority**: high +**Status**: resolved +**Area**: backend + +### Summary +分支条件相同代码是 bug,必须对照 Fortran 完整实现 + +### Details +`if params.ibc == 0 { ... } else { ... }` 两个分支代码完全相同时是错误的。 + +Fortran 原代码 ELSE 分支有额外项: +```fortran +REIT(ID)=REIT(ID)+WW*(D0*(DSFT1-DBDT)+E0*DABT1(ID)+ + * RAD1(ID)*DABT1(ID)-DEMT1(ID)+ + * ALI1(ID)/ABST*DBDT) +``` + +变量 `DBDT` 和 `E0` 在边界条件块中计算,需要在更大作用域中声明才能在积分方程块中使用。 + +### Suggested Action +重构时如果看到 if-else 分支代码相同,立即检查 Fortran 原代码 + +### Metadata +- Source: fortran-to-rust refactoring +- Related Files: alifr1.rs, alifr1.f +- Tags: branch, bug, fortran, rust + +--- + +## [LRN-20260321-F18] best_practice + +**Logged**: 2026-03-21T12:00:00Z +**Priority**: medium +**Status**: resolved +**Area**: docs + +### Summary +模块文档应完整描述所有功能,不仅是主要功能 + +### Details +opctab.rs 原文档只提到"插值计算",漏掉了: +- Rayleigh 散射计算(简单公式或调用 rayleigh 函数) +- 电子散射计算 + +### Suggested Action +重构完成后对照 Fortran 原代码检查文档是否完整 + +### Metadata +- Source: fortran-to-rust refactoring +- Related Files: opctab.rs, opctab.f +- Tags: documentation, completeness + +--- + +## Quick Reference - Refactoring Checklist + +重构新函数前检查: + +- [ ] 是否有 INCLUDE 语句 (除 IMPLIC.FOR 外) +- [ ] 是否使用 COMMON 块 +- [ ] COMMON 块结构体是否已存在于其他模块 +- [ ] 是否有文件 I/O (OPEN, READ, WRITE) +- [ ] 数组索引是否需要 -1 调整 +- [ ] 循环变量是否可能变负 (用 isize/i32) +- [ ] 多项式近似精度是否需要放宽 +- [ ] 是否有嵌套 IF 分支遗漏 +- [ ] 矩阵列索引是否可能与温度/密度列重叠 +- [ ] 公式中 `1/x` 和 `x` 的顺序是否正确 +- [ ] 大型 COMMON 块函数是否需要创建综合输入结构体 +- [ ] 多分支 GOTO 是否正确转换为 if-else +- [ ] 循环中重新赋值的变量是否声明为 mut +- [ ] 多维数组扁平化时是否使用了正确的维度(不是第一个维度) +- [ ] 条件语句中的变量是否与 Fortran 原代码一致 +- [ ] 模块文档是否描述了所有功能(包括次要功能) +- [ ] if-else 分支代码是否完全相同(相同则是 bug) +- [ ] 边界条件块中计算的变量是否在积分方程块中可用(作用域问题) diff --git a/CLAUDE.md b/CLAUDE.md index e89193e..4fa3eb7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,8 +6,9 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co Fortran stellar atmosphere modeling suite being refactored to Rust. Strategy: **split Fortran into modules first, then incrementally rewrite in Rust**. -- **TLUSTY 208**: Non-LTE stellar atmosphere calculator (50,009 lines → 304 modules) -- **SYNSPEC 54**: Synthetic spectrum evaluator (23,917 lines → 168 modules) +- **TLUSTY 208**: Non-LTE stellar atmosphere calculator (~50,000 lines → 304 modules) +- **SYNSPEC 54**: Synthetic spectrum evaluator (~24,000 lines → 168 modules) +- **Progress**: 120/~472 Fortran units translated to Rust ## Environment Variables @@ -21,60 +22,106 @@ export OPTABLES=$TL208/optables ## Build Commands +### Rust +```bash +cargo build # Debug build +cargo build --release # Release build +cargo test # Run all tests (includes Fortran comparison tests) +cargo test test_expo # Run specific test +``` + +### Fortran ```bash # Production (single file) gfortran -O3 -fno-automatic -mcmodel=large -o tlusty/tlusty.exe tlusty/tlusty208.f gfortran -O3 -fno-automatic -mcmodel=large -o synspec/synspec.exe synspec/synspec54.f # Development (modular) -python3 extract_fortran.py tlusty/tlusty208.f tlusty/extracted/ -cp tlusty/*.FOR tlusty/extracted/ -cd tlusty/extracted && make # Output: build/tlusty_extracted +cd rust/tlusty/extracted && make # Output: build/tlusty_extracted ``` -**Compile flags:** +**Fortran compile flags:** - `-mcmodel=large`: Required for large COMMON blocks (>2GB address space) - `-fno-automatic`: Static storage (old Fortran compatibility) - **Never use** `-ffixed-line-length-none`: Breaks columns 73-80 handling +## Rust Architecture + +``` +src/ +├── lib.rs # Module exports +├── data.rs # Static data arrays (translated from BLOCK DATA) +├── math/ # Pure math functions (no COMMON dependency) - 120 modules +│ ├── expint.rs # Exponential integrals +│ ├── voigt.rs # Voigt profile +│ ├── tridag.rs # Tridiagonal solver +│ └── ... +├── state/ # COMMON block translations as structs +│ ├── constants.rs # Physical/math constants, array dimensions +│ ├── config.rs # Runtime config +│ ├── atomic.rs # Atomic/ion/level data +│ ├── model.rs # Atmosphere model state (largest struct) +│ ├── arrays.rs # Main linear equation arrays +│ ├── iterat.rs # Iteration control +│ ├── alipar.rs # ALI (Accelerated Lambda Iteration) arrays +│ └── odfpar.rs # ODF (Opacity Distribution Function) data +└── physics/ # Physics calculations (placeholder) +``` + ## Running Tests +### Rust Tests +```bash +cargo test # All unit tests +cargo test --test fortran_comparison # Fortran comparison tests only +``` + +### Fortran Integration Tests ```bash # TLUSTY: H-He model test -cd tests/tlusty/hhe +cd $TLUSTY/tests/tlusty/hhe $TLUSTY/tlusty/tlusty.exe < hhe35lt.5 > hhe35lt.6 cp fort.7 hhe35lt.7 -diff hhe35lt.7 hhe35lt.7.bak # Verify against expected +diff hhe35lt.7 hhe35lt.7.bak # SYNSPEC: spectrum test -cd tests/synspec/hhe -ln -sf $TLUSTY/data data # MUST be symlink, not file +cd $TLUSTY/tests/synspec/hhe +ln -sf $TLUSTY/data data cp hhe35nl.7 fort.8 ln -sf fort.55.con fort.55 $TLUSTY/synspec/synspec.exe < hhe35nl.5 -# Output: fort.7 (spectrum), fort.17 (continuum) ``` -## Module Extraction +## Refactoring Workflow -`extract_fortran.py` splits monolithic Fortran into individual `.f` files: -- Generates Makefile with correct flags -- Analyzes COMMON block dependencies -- Identifies pure functions (no COMMON) for independent testing -- Handles unnamed BLOCK DATA units +1. **Find pure functions**: Check `rust/tlusty/extracted/_PURE_UNITS.txt` for units without COMMON dependencies +2. **Translate**: Create `src/math/.rs`, add to `src/math/mod.rs` +3. **Verify**: Add test case in `tests/fortran_comparison.rs` with Fortran reference values ## Key Architecture -**TLUSTY include files** define COMMON blocks shared across subroutines: -- `BASICS.FOR`: Array dimensions (`MDEPTH`=100, `MFREQ`=135000, `MLEVEL`=1134) -- `ATOMIC.FOR`: Atomic masses, abundances, energy levels -- `MODELQ.FOR`: Temperature, density, populations -- `ARRAY1.FOR`: Main linear equation arrays +**TLUSTY COMMON blocks** (mapped to `src/state/` structs): +- `BASICS.FOR` → `constants.rs`: Array dimensions (`MDEPTH`=100, `MFREQ`=135000, `MLEVEL`=1134) +- `ATOMIC.FOR` → `atomic.rs`: Atomic masses, abundances, energy levels +- `MODELQ.FOR` → `model.rs`: Temperature, density, populations +- `ARRAY1.FOR` → `arrays.rs`: Main linear equation arrays -**SYNSPEC** reads model atmosphere from `fort.8`, outputs spectrum to `fort.7`. +**SYNSPEC** reads model atmosphere from `fort.8`, outputs spectrum to `fort.7` -## Refactoring Notes +## Fortran → Rust Translation Notes -- Pure functions (no COMMON dependency) can be rewritten independently -- TLUSTY has 195 pure units, SYNSPEC has 93 -- See `memory/MEMORY.md` for detailed extraction results and test procedures +Critical patterns to avoid mistakes: + +- **Index conversion**: Fortran 1-indexed → Rust 0-indexed. `arr(i)` → `arr[i-1]`, `DO I=1,N` → `for i in 0..n` +- **Expression precedence**: `-LOG(X)` is `-(LOG(X))`, not `LOG(-X)` +- **Decrementing loops**: Use `isize`/`i32` when loop variable may become negative +- **COMMON dependency**: Check INCLUDE statements before assuming a function is pure +- **Large structs**: Functions with many COMMON dependencies use lifetime structs: + ```rust + pub struct Params<'a> { + pub input: &'a [f64], + pub output: &'a mut [f64], + } + ``` +- **Type inference**: `(z1 - z2).powi(2)` may fail; use `(z1 - z2) * (z1 - z2)` +- **Test tolerance**: Polynomial approximations (Abramowitz-Stegun) need relaxed tolerance (~1e-7) diff --git a/MEMORY/FORTRAN_TRACKING.md b/MEMORY/FORTRAN_TRACKING.md deleted file mode 100644 index a5c3e5a..0000000 --- a/MEMORY/FORTRAN_TRACKING.md +++ /dev/null @@ -1,337 +0,0 @@ -# Fortran 重构追踪表 - -> 自动生成,请勿手动修改。运行 `python3 scripts/generate_tracking.py > FORTRAN_TRACKING.md` 更新。 - -## 统计 - -| 指标 | 数量 | -|------|------| -| 总单元数 | 304 | -| 已完成 | 125 | -| 待处理 | 179 | -| 纯函数 | 67 | -| 有文件 I/O | 117 | -| 完成率 | 41.1% | - -## 状态说明 - -- ✅ `done` - 已重构为 Rust -- ⬜ `pending` - 待处理 -- 🔄 `in_progress` - 进行中 -- ⏭️ `skip` - 跳过 (I/O 依赖或暂不处理) - -## 类型说明 - -- **纯函数**: 无 COMMON 依赖、无文件 I/O、无外部调用依赖 -- **COMMON 依赖**: 需要状态结构体 -- **调用依赖**: 调用其他子程序,需要先实现依赖 - -## 完整追踪表 - - -| Fortran 文件 | 单元名 | 类型 | 纯函数 | COMMON 依赖 | 调用依赖 | I/O | Rust 模块 | 状态 | -|-------------|--------|------|--------|-------------|----------|-----|-----------|------| -| _unnamed_block_data_.f | C | BLOCK DATA | | BASICS, ATOMIC | | | | ⬜ | -| accel2.f | ACCEL2 | SUBROUTINE | | BASICS, ITERAT, MODELQ | | 📁 | | ⬜ | -| accelp.f | ACCELP | SUBROUTINE | | BASICS, MODELQ, ITERAT, POPULS | | 📁 | | ⬜ | -| alifr1.f | ALIFR1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR | ALIFR3 | | | ⬜ | -| alifr3.f | ALIFR3 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR | | | alifr3.rs | ✅ | -| alifr6.f | ALIFR6 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR | | | alifr6.rs | ✅ | -| alifrk.f | ALIFRK | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR | | | alifrk.rs | ✅ | -| alisk1.f | ALISK1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT | ALIFRK, ROSSTD, OPACF1, RTEFR1 | 📁 | | ⬜ | -| alisk2.f | ALISK2 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT | ALIFRK, ROSSTD, OPACF1, RTEFR1 | 📁 | | ⬜ | -| alist1.f | ALIST1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ITERAT | OPACFD, ALIFR1, ROSSTD, RTEFR1 | 📁 | | ⬜ | -| alist2.f | ALIST2 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT | RTEFR1, ROSSTD, ALIFR1, QUIT, OPACFD | 📁 | | ⬜ | -| allard.f | ALLARD | SUBROUTINE | | BASICS, calphatd, callarda, callardc, callardg, callardb, quasun | ALLARDT | 📁 | | ⬜ | -| allardt.f | ALLARDT | SUBROUTINE | | BASICS, calphatd | | | allardt.rs | ✅ | -| angset.f | ANGSET | SUBROUTINE | ✓ | BASICS | GAULEG | | angset.rs | ✅ | -| betah.f | BETAH | FUNCTION | ✓ | | BETAH | | betah.rs | ✅ | -| bhe.f | BHE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR | | | bhe.rs | ✅ | -| bhed.f | BHED | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, CMATZD, SURFEX | | | bhe.rs | ✅ | -| bhez.f | BHEZ | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, SURFEX | | | bhe.rs | ✅ | -| bkhsgo.f | BKHSGO | SUBROUTINE | ✓ | | | | bkhsgo.rs | ✅ | -| bpop.f | BPOP | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, ODFPAR, ITERAT | MATINV, BPOPT, BPOPF, LEVSOL, RATMAT, BPOPC, BPOPE, LEVGRP | | | ⬜ | -| bpopc.f | BPOPC | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, ODFPAR, ADCHAR | STATE | | | ⬜ | -| bpope.f | BPOPE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ITERAT, ARRAY1 | SGMER1, DWNFR1 | | | ⬜ | -| bpopf.f | BPOPF | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, ODFPAR | | | bpopf.rs | ✅ | -| bpopt.f | BPOPT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, ODFPAR | COLIS | | | ⬜ | -| bre.f | BRE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR | COMPT0 | | | ⬜ | -| brez.f | BREZ | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR | COMPT0 | | | ⬜ | -| brte.f | BRTE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR, ARRAY1 | COMPT0 | | | ⬜ | -| brtez.f | BRTEZ | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR, ARRAY1 | COMPT0 | | | ⬜ | -| butler.f | BUTLER | SUBROUTINE | ✓ | | | | butler.rs | ✅ | -| carbon.f | CARBON | SUBROUTINE | ✓ | | | | carbon.rs | ✅ | -| ceh12.f | CEH12 | FUNCTION | ✓ | | CEH12 | | ceh12.rs | ✅ | -| change.f | CHANGE | SUBROUTINE | | BASICS, ATOMIC, MODELQ | STEQEQ, READBF | 📁 | | ⬜ | -| chckse.f | CHCKSE | SUBROUTINE | | BASICS, ATOMIC, MODELQ | SABOLF | 📁 | | ⬜ | -| chctab.f | CHCTAB | SUBROUTINE | | BASICS, MODELQ, abntab | | 📁 | | ⬜ | -| cheav.f | CHEAV | FUNCTION | | BASICS, ATOMIC | CHEAV, QUIT | 📁 | | ⬜ | -| cheavj.f | CHEAVJ | FUNCTION | | BASICS, ATOMIC | QUIT, CHEAVJ | 📁 | | ⬜ | -| cia_h2h.f | CIA_H2H | SUBROUTINE | | | LOCATE, IF | 📁 | | ⬜ | -| cia_h2h2.f | CIA_H2H2 | SUBROUTINE | | | LOCATE, IF | 📁 | | ⬜ | -| cia_h2he.f | CIA_H2HE | SUBROUTINE | | | LOCATE, IF | 📁 | | ⬜ | -| cia_hhe.f | CIA_HHE | SUBROUTINE | | | LOCATE, IF | 📁 | | ⬜ | -| cion.f | CION | FUNCTION | ✓ | | CION | | cion.rs | ✅ | -| ckoest.f | CKOEST | FUNCTION | ✓ | BASICS | CKOEST | | ckoest.rs | ✅ | -| colh.f | COLH | SUBROUTINE | | BASICS, ATOMIC, MODELQ | CSPEC, BUTLER, IRC | | | ⬜ | -| colhe.f | COLHE | SUBROUTINE | | BASICS, ATOMIC | COLLHE, CSPEC, IRC | | | ⬜ | -| colis.f | COLIS | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, CTRTEMP | CSPEC, COLH, COLHE, IRC | | | ⬜ | -| collhe.f | COLLHE | SUBROUTINE | ✓ | | | | collhe.rs | ✅ | -| column.f | COLUMN | SUBROUTINE | | BASICS, MODELQ, relcor | | 📁 | | ⬜ | -| compt0.f | COMPT0 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, auxcbc | | | compt0.rs | ✅ | -| comset.f | COMSET | SUBROUTINE | | BASICS, MODELQ, comgfs, auxcbc | | | comset.rs | ✅ | -| concor.f | CONCOR | SUBROUTINE | | BASICS, MODELQ | CONOUT | 📁 | | ⬜ | -| conout.f | CONOUT | SUBROUTINE | | BASICS, MODELQ, ALIPAR, CUBCON | MEANOP, MEANOPT, CONVEC, OPACF0 | 📁 | | ⬜ | -| conref.f | CONREF | SUBROUTINE | | BASICS, MODELQ, ARRAY1, imucnn, CUBCON | CONVC1, WNSTOR, STEQEQ, ELDENS, CONVEC, CONOUT | 📁 | | ⬜ | -| contmd.f | CONTMD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR, PRSAUX, CUBCON | MEANOP, WNSTOR, STEQEQ, OPACF0, CONVEC, CONOUT, CUBIC | 📁 | | ⬜ | -| contmp.f | CONTMP | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR, ichndm, CUBCON | MEANOP, WNSTOR, STEQEQ, ELDENS, OPACF0, CONVEC, MEANOPT, CONOUT, CUBIC | 📁 | | ⬜ | -| convc1.f | CONVC1 | SUBROUTINE | | BASICS, CUBCON | TRMDER, TRMDRT | | | ⬜ | -| convec.f | CONVEC | SUBROUTINE | | BASICS, CUBCON | TRMDER, TRMDRT | | | ⬜ | -| coolrt.f | COOLRT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT, COOLCO | OPACFA, RTEFR1 | 📁 | | ⬜ | -| corrwm.f | CORRWM | SUBROUTINE | | BASICS, ATOMIC, MODELQ | QUIT | 📁 | | ⬜ | -| cross.f | CROSS | FUNCTION | | BASICS, ATOMIC, MODELQ | CROSS | | cross.rs | ✅ | -| crossd.f | CROSSD | FUNCTION | | BASICS, ATOMIC, MODELQ | CROSSD | | cross.rs | ✅ | -| cspec.f | CSPEC | SUBROUTINE | | BASICS, ATOMIC | QUIT | | | ⬜ | -| ctdata.f | CTDATA | BLOCK DATA | | CTIon, CTRecomb | | | ctdata.rs | ✅ | -| cubic.f | CUBIC | SUBROUTINE | | BASICS, CUBCON | | | cubic.rs | ✅ | -| dielrc.f | DIELRC | SUBROUTINE | ✓ | | | | dielrc.rs | ✅ | -| dietot.f | DIETOT | SUBROUTINE | | BASICS, ATOMIC, MODELQ | DIELRC | 📁 | | ⬜ | -| divstr.f | DIVSTR | SUBROUTINE | | BASICS, MODELQ | | | divstr.rs | ✅ | -| dmder.f | DMDER | SUBROUTINE | | BASICS, ATOMIC, MODELQ, DEPTDR | | | dmder.rs | ✅ | -| dmeval.f | DMEVAL | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ARRAY1 | | 📁 | | ⬜ | -| dopgam.f | DOPGAM | SUBROUTINE | | BASICS, ATOMIC, MODELQ | GAMSP | | dopgam.rs | ✅ | -| dwnfr.f | DWNFR | SUBROUTINE | | BASICS, MODELQ | | | dwnfr.rs | ✅ | -| dwnfr0.f | DWNFR0 | SUBROUTINE | | BASICS, MODELQ | | | dwnfr0.rs | ✅ | -| dwnfr1.f | DWNFR1 | SUBROUTINE | | BASICS, MODELQ | | | dwnfr1.rs | ✅ | -| eint.f | EINT | SUBROUTINE | ✓ | | EXPINX | | expint.rs | ✅ | -| elcor.f | ELCOR | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ADCHAR | WNSTOR, STEQEQ, STATE, MOLEQ | 📁 | | ⬜ | -| eldenc.f | ELDENC | SUBROUTINE | | BASICS, MODELQ, ATOMIC, eletab, hmolab, eospar | RHONEN, STATE, MOLEQ | 📁 | | ⬜ | -| eldens.f | ELDENS | SUBROUTINE | | BASICS, MODELQ, ATOMIC, terden, eospar | ENTENE, STATE, MPARTF, LINEQS, MOLEQ | 📁 | | ⬜ | -| emat.f | EMAT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR | | | emat.rs | ✅ | -| entene.f | ENTENE | SUBROUTINE | | BASICS, ATOMIC, MODELQ | MPARTF | | | ⬜ | -| erfcin.f | ERFCIN | FUNCTION | ✓ | | ERFCIN | | erfcx.rs | ✅ | -| erfcx.f | ERFCX | FUNCTION | ✓ | | ERFCX | | erfcx.rs | ✅ | -| expint.f | EXPINT | FUNCTION | ✓ | | EXPINT | | expint.rs | ✅ | -| expinx.f | EXPINX | SUBROUTINE | ✓ | | | | expint.rs | ✅ | -| expo.f | EXPO | FUNCTION | ✓ | | EXPO | | expo.rs | ✅ | -| ffcros.f | FFCROS | FUNCTION | ✓ | | FFCROS | | ffcros.rs | ✅ | -| gami.f | GAMI | FUNCTION | ✓ | | GAMI | | gami.rs | ✅ | -| gamsp.f | GAMSP | SUBROUTINE | ✓ | BASICS | | | gamsp.rs | ✅ | -| gauleg.f | GAULEG | SUBROUTINE | ✓ | | | | gauleg.rs | ✅ | -| gaunt.f | GAUNT | FUNCTION | ✓ | | GAUNT | | gaunt.rs | ✅ | -| getlal.f | GETLAL | SUBROUTINE | | BASICS, callardc, callarda, calphatd, callardg, callardb, quasun | | 📁 | | ⬜ | -| getwrd.f | GETWRD | SUBROUTINE | ✓ | | | | getwrd.rs | ✅ | -| gfree0.f | GFREE0 | SUBROUTINE | | BASICS, MODELQ | | | gfree.rs | ✅ | -| gfree1.f | GFREE1 | FUNCTION | | BASICS, MODELQ | GFREE1 | | gfree.rs | ✅ | -| gfreed.f | GFREED | SUBROUTINE | | BASICS, MODELQ | | | gfree.rs | ✅ | -| ghydop.f | GHYDOP | SUBROUTINE | | BASICS, MODELQ, ATOMIC, intcfg | | | ghydop.rs | ✅ | -| gntk.f | GNTK | FUNCTION | ✓ | | GNTK | | gntk.rs | ✅ | -| gomini.f | GOMINI | SUBROUTINE | | BASICS, MODELQ, intcfg | | 📁 | | ⬜ | -| grcor.f | GRCOR | SUBROUTINE | ✓ | | | | grcor.rs | ✅ | -| greyd.f | GREYD | SUBROUTINE | | BASICS, MODELQ, ATOMIC, ALIPAR | WNSTOR, RHONEN, STEQEQ, OPACF0, MEANOP | 📁 | | ⬜ | -| gridp.f | GRIDP | SUBROUTINE | ✓ | BASICS | | | gridp.rs | ✅ | -| h2minus.f | H2MINUS | SUBROUTINE | | BASICS | LOCATE | 📁 | | ⬜ | -| hction.f | HCTION | FUNCTION | | CTRTEMP, CTIon | HCTION | | ctdata.rs | ✅ | -| hctrecom.f | HCTRECOM | FUNCTION | | CTRTEMP, CTRecomb | HCTRECOM | | ctdata.rs | ✅ | -| hedif.f | HEDIF | SUBROUTINE | | BASICS, MODELQ, ATOMIC, hediff | | 📁 | | ⬜ | -| hephot.f | HEPHOT | FUNCTION | ✓ | | HEPHOT | | hephot.rs | ✅ | -| hesol6.f | HESOL6 | SUBROUTINE | | BASICS, MODELQ, PRSAUX | MATINV | | | ⬜ | -| hesolv.f | HESOLV | SUBROUTINE | | BASICS, MODELQ, PRSAUX | MATINV, RHONEN, STEQEQ, WNSTOR | 📁 | | ⬜ | -| hidalg.f | HIDALG | FUNCTION | ✓ | | HIDALG | | hidalg.rs | ✅ | -| ijali2.f | IJALI2 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | QUIT | 📁 | | ⬜ | -| ijalis.f | IJALIS | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | 📁 | | ⬜ | -| incldy.f | INCLDY | SUBROUTINE | | BASICS, ATOMIC, MODELQ | LEVSOL, RATMAT, WNSTOR, QUIT, SABOLF | 📁 | | ⬜ | -| indexx.f | INDEXX | SUBROUTINE | ✓ | | | | indexx.rs | ✅ | -| inicom.f | INICOM | SUBROUTINE | | BASICS, ATOMIC, MODELQ, comgfs | | | inicom.rs | ✅ | -| inifrc.f | INIFRC | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ijflar | INDEXX | 📁 | | ⬜ | -| inifrs.f | INIFRS | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | INDEXX, QUIT | 📁 | | ⬜ | -| inifrt.f | INIFRT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ijflar | INDEXX | 📁 | | ⬜ | -| inilam.f | INILAM | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ALIPAR | OPACF1, RTEFR1, COLIS, OPAINI, WNSTOR, STEQEQ, SABOLF, ELCOR, RATES1 | | | ⬜ | -| initia.f | INITIA | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ODFPAR, ALIPAR, STRPAR, INUNIT, freqcl | RDATA, RDATAX, OPADD0, STATE, DOPGAM, ODFHYS, INTERP, LINSET, NSTPAR, QUIT, LINSPL, INIFRC, READBF | 📁 | | ⬜ | -| inkul.f | INKUL | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, COLKUR, LINED | | 📁 | | ⬜ | -| inpdis.f | INPDIS | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ODFPAR, ALIPAR, relcor | GRCOR | 📁 | | ⬜ | -| inpmod.f | INPMOD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, eospar | LEVSOL, RATMAT, WNSTOR, QUIT, SABOLF, MOLEQ, KURUCZ, INCLDY | 📁 | | ⬜ | -| interp.f | INTERP | SUBROUTINE | ✓ | BASICS | | | interp.rs | ✅ | -| inthyd.f | INTHYD | SUBROUTINE | | BASICS, MODELQ | DIVSTR | | inthyd.rs | ✅ | -| intlem.f | INTLEM | SUBROUTINE | | BASICS, MODELQ | INTHYD | | intlem.rs | ✅ | -| intxen.f | INTXEN | SUBROUTINE | | BASICS, MODELQ | | | intxen.rs | ✅ | -| irc.f | IRC | SUBROUTINE | ✓ | | EXPINX, SZIRC | | irc.rs | ✅ | -| iroset.f | IROSET | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, LINED | LEVCD, QUIT, INKUL | 📁 | | ⬜ | -| kurucz.f | KURUCZ | SUBROUTINE | | BASICS, ATOMIC, MODELQ, temlim | LEVSOL, RATMAT, WNSTOR, RHONEN, QUIT, SABOLF, MOLEQ | 📁 | | ⬜ | -| lagran.f | LAGRAN | SUBROUTINE | ✓ | | | | interpolate.rs | ✅ | -| laguer.f | LAGUER | SUBROUTINE | | | | 📁 | laguer.rs | ✅ | -| lemini.f | LEMINI | SUBROUTINE | | BASICS, MODELQ | | 📁 | | ⬜ | -| levcd.f | LEVCD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, COLKUR | INDEXX, QUIT | 📁 | | ⬜ | -| levgrp.f | LEVGRP | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | | | levgrp.rs | ✅ | -| levset.f | LEVSET | SUBROUTINE | | BASICS, ATOMIC, MODELQ | QUIT | | | ⬜ | -| levsol.f | LEVSOL | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | LINEQS | | levsol.rs | ✅ | -| lineqs.f | LINEQS | SUBROUTINE | ✓ | BASICS | | | lineqs.rs | ✅ | -| linpro.f | LINPRO | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, quasun | STARK0, DOPGAM, INTLEM, DIVSTR, INTXEN | | | ⬜ | -| linsel.f | LINSEL | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR | OPAINI, QUIT, OPACF1, RTEFR1 | 📁 | | ⬜ | -| linset.f | LINSET | SUBROUTINE | | BASICS, ATOMIC, MODELQ | IJALIS, STARK0, QUIT, DIVSTR | 📁 | | ⬜ | -| linspl.f | LINSPL | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | linspl.rs | ✅ | -| locate.f | LOCATE | SUBROUTINE | ✓ | | | | locate.rs | ✅ | -| ltegr.f | LTEGR | SUBROUTINE | | BASICS, ATOMIC, MODELQ | INTERP, WNSTOR, STEQEQ, QUIT, ROSSOP, CONOUT | 📁 | | ⬜ | -| ltegrd.f | LTEGRD | SUBROUTINE | | BASICS, MODELQ, FACTRS, CUBCON, TOTJHK, FLXAUX, PRSAUX | TEMPER, INTERP, ZMRHO, WNSTOR, STEQEQ, ELDENS, QUIT, CONOUT | 📁 | | ⬜ | -| lucy.f | LUCY | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ITERAT, ALIPAR, ARRAY1 | OPACFL, RTEFR1, COLIS, WNSTOR, STEQEQ, SABOLF, ELCOR, OPAINI | 📁 | | ⬜ | -| lymlin.f | LYMLIN | SUBROUTINE | | BASICS, ATOMIC, MODELQ | STARK0, DIVSTR | 📁 | | ⬜ | -| matcon.f | MATCON | SUBROUTINE | | BASICS, MODELQ, ARRAY1, CUBCON | CONVEC | | | ⬜ | -| matgen.f | MATGEN | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR | BHED, BRTE, BHEZ, BREZ, MATCON, BRTEZ, EMAT, BHE, SABOLF, BPOP, BRE | | | ⬜ | -| matinv.f | MATINV | SUBROUTINE | ✓ | BASICS | | | matinv.rs | ✅ | -| meanop.f | MEANOP | SUBROUTINE | | BASICS, MODELQ, ATOMIC | | | meanop.rs | ✅ | -| meanopt.f | MEANOPT | SUBROUTINE | | BASICS, MODELQ | OPCTAB | | | ⬜ | -| minv3.f | MINV3 | SUBROUTINE | ✓ | | | | minv3.rs | ✅ | -| moleq.f | MOLEQ | SUBROUTINE | | BASICS, MODELQ, ATOMIC, COMFH1, entrop, moldat, eospar, adchar, ioniz2, terden, hmolab | RUSSEL, MPARTF | 📁 | | ⬜ | -| mpartf.f | MPARTF | SUBROUTINE | | moldat | | 📁 | | ⬜ | -| newdm.f | NEWDM | SUBROUTINE | | BASICS, MODELQ, FACTRS, PRSAUX, FLXAUX | INTERP, TEMPER | 📁 | | ⬜ | -| newdmt.f | NEWDMT | SUBROUTINE | | BASICS, MODELQ, FACTRS, PRSAUX, FLXAUX | GRIDP, INTERP, TEMPER | 📁 | | ⬜ | -| newpop.f | NEWPOP | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | | 📁 | | ⬜ | -| nstout.f | NSTOUT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ODFPAR, ALIPAR | QUIT | 📁 | | ⬜ | -| nstpar.f | NSTPAR | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ODFPAR, ALIPAR, imucnn, temlim, adiaba, ifpzpa, derdif, ichndm, ipricr, deridt, hediff, FLXAUX, irwint, moldat, quasun, freqcl, icnrsp | QUIT, GETWRD | 📁 | | ⬜ | -| odf1.f | ODF1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | DWNFR, ODFHST, DIVSTR | 📁 | | ⬜ | -| odffr.f | ODFFR | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | QUIT | | | ⬜ | -| odfhst.f | ODFHST | SUBROUTINE | | BASICS, MODELQ, ODFPAR | | | odfhst.rs | ✅ | -| odfhyd.f | ODFHYD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | INDEXX, ODFHST, DIVSTR | | | ⬜ | -| odfhys.f | ODFHYS | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | IJALIS, STARK0, ODFFR | | | ⬜ | -| odfmer.f | ODFMER | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | ODFHYD | | | ⬜ | -| odfset.f | ODFSET | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, STFCR | IJALIS, QUIT | 📁 | | ⬜ | -| opacf0.f | OPACF0 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, hmolab | GFREE0, DWNFR1, DWNFR0, WNSTOR, LINPRO, SABOLF, SGMER1, OPADD, OPACT1 | | | ⬜ | -| opacf1.f | OPACF1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ipricr, hmolab | QUASIM, GHYDOP, DWNFR1, LYMLIN, SGMER1, OPADD, PRD, OPACT1 | 📁 | | ⬜ | -| opacfa.f | OPACFA | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, COOLCO | SGMER1, OPADD, PRD, DWNFR1 | | | ⬜ | -| opacfd.f | OPACFD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT, dsctva, hmolab, rhoder | OPACTD, QUASIM, DWNFR1, LYMLIN, OPCTAB, SGMER1, OPADD, PRD, GFREED | 📁 | | ⬜ | -| opacfl.f | OPACFL | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR | SGMER1, OPADD, DWNFR1 | | | ⬜ | -| opact1.f | OPACT1 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, hmolab | OPCTAB | | | ⬜ | -| opactd.f | OPACTD | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ARRAY1, ITERAT, dsctva, hmolab, rhoder | OPCTAB | | | ⬜ | -| opactr.f | OPACTR | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ATOMIC, dsctva, hmolab, grdpra | RATMAL, OPACF1, LEVSOL, WNSTOR, STEQEQ, ELDENS, SABOLF, OPAINI, PGSET | | | ⬜ | -| opadd.f | OPADD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, eospar | CIA_H2H, CIA_H2HE, CIA_HHE, H2MINUS, CIA_H2H2 | | | ⬜ | -| opadd0.f | OPADD0 | SUBROUTINE | | BASICS, ATOMIC, MODELQ | QUIT | | | ⬜ | -| opahst.f | OPAHST | SUBROUTINE | | BASICS, ODFPAR | STARK0 | 📁 | | ⬜ | -| opaini.f | OPAINI | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR | DWNFR0, REFLEV, WNSTOR, LINPRO, SABOLF, LEVGRP | | | ⬜ | -| opctab.f | OPCTAB | SUBROUTINE | | BASICS, MODELQ | RAYLEIGH | | | ⬜ | -| opdata.f | OPDATA | SUBROUTINE | | TOPB | | 📁 | | ⬜ | -| opfrac.f | OPFRAC | SUBROUTINE | | pfoptb | | 📁 | | ⬜ | -| osccor.f | OSCCOR | SUBROUTINE | | BASICS, MODELQ, ITERAT | | 📁 | | ⬜ | -| outpri.f | OUTPRI | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, grdpra | RATMAL, OPACF1, LEVSOL, WNSTOR, SABOLF | 📁 | | ⬜ | -| output.f | OUTPUT | SUBROUTINE | | BASICS, MODELQ | | 📁 | | ⬜ | -| partf.f | PARTF | SUBROUTINE | | BASICS, irwint, PFSTDS | PFFE, PFNI, OPFRAC, MPARTF, PFHEAV, PFSPEC, PFCNO | | | ⬜ | -| pfcno.f | PFCNO | SUBROUTINE | ✓ | BASICS | | | pfcno.rs | ✅ | -| pffe.f | PFFE | SUBROUTINE | ✓ | | | | pffe.rs | ✅ | -| pfheav.f | PFHEAV | SUBROUTINE | | | | 📁 | | ⬜ | -| pfni.f | PFNI | SUBROUTINE | ✓ | | | | pfni.rs | ✅ | -| pfspec.f | PFSPEC | SUBROUTINE | ✓ | | | | pfspec.rs | ✅ | -| pgset.f | PGSET | SUBROUTINE | | BASICS, ITERAT, MODELQ, rybpgs, grdpra | TRIDAG | 📁 | | ⬜ | -| prchan.f | PRCHAN | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | | 📁 | | ⬜ | -| prd.f | PRD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | DOPGAM | | | ⬜ | -| prdini.f | PRDINI | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | prdini.rs | ✅ | -| princ.f | PRINC | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR | LINPRO, DWNFR, OPACF1, SABOLF | 📁 | | ⬜ | -| prnt.f | PRNT | SUBROUTINE | | BASICS, ATOMIC, MODELQ | SABOLF | 📁 | | ⬜ | -| profil.f | PROFIL | FUNCTION | | BASICS, ATOMIC, MODELQ, quasun | PROFIL, STARK0, DIVSTR | | profil.rs | ✅ | -| profsp.f | PROFSP | FUNCTION | | BASICS, ATOMIC, MODELQ | PROFSP, SABOLF | | | ⬜ | -| prsent.f | PRSENT | SUBROUTINE | | tdflag, tdedge, THERM, TABLTD | | 📁 | | ⬜ | -| psolve.f | PSOLVE | SUBROUTINE | | BASICS, MODELQ | | | psolve.rs | ✅ | -| pzert.f | PZERT | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | pzert.rs | ✅ | -| pzeval.f | PZEVAL | SUBROUTINE | | BASICS, MODELQ, ALIPAR, icnrsp | CONOUT | 📁 | | ⬜ | -| pzevld.f | PZEVLD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR, ARRAY1, ifpzpa, PRSAUX, grdpra, DEPTDR | | | pzevld.rs | ✅ | -| quartc.f | QUARTC | SUBROUTINE | | | | 📁 | quartc.rs | ✅ | -| quasim.f | QUASIM | SUBROUTINE | | BASICS, ATOMIC, MODELQ, quasun | ALLARD | | | ⬜ | -| quit.f | QUIT | SUBROUTINE | | | | 📁 | quit.rs | ✅ | -| radpre.f | RADPRE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR | QUIT, INDEXX, OPACF1, RTEFR1 | 📁 | | ⬜ | -| radtot.f | RADTOT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ITERAT, OPTDPT, TOTJHK, SURFEX | OPAINI, OPACF1, RTEFR1 | | | ⬜ | -| raph.f | RAPH | FUNCTION | ✓ | | RAPH | | raph.rs | ✅ | -| rates1.f | RATES1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ITERAT | ROSSTD, OPACF1, RTEFR1 | | | ⬜ | -| ratmal.f | RATMAL | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | ratmal.rs | ✅ | -| ratmat.f | RATMAT | SUBROUTINE | | BASICS, ATOMIC, MODELQ | REFLEV | | | ⬜ | -| ratsp1.f | RATSP1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT | ROSSTD, OPACF1, RTEFR1 | 📁 | | ⬜ | -| rayini.f | RAYINI | SUBROUTINE | | BASICS, MODELQ, ATOMIC | RAYLEIGH | 📁 | | ⬜ | -| rayleigh.f | RAYLEIGH | SUBROUTINE | | BASICS, ATOMIC, MODELQ, RAYSCT, eospar | | | rayleigh.rs | ✅ | -| rayset.f | RAYSET | SUBROUTINE | | BASICS, MODELQ | | | rayset.rs | ✅ | -| rdata.f | RDATA | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ODFPAR, ALIPAR, STRPAR, INUNIT, imodlc | RDATAX, LINSET, QUIT, DOPGAM | 📁 | | ⬜ | -| rdatax.f | RDATAX | SUBROUTINE | | BASICS, ATOMIC, MODELQ | BKHSGO | 📁 | | ⬜ | -| readbf.f | READBF | SUBROUTINE | | BASICS | | 📁 | | ⬜ | -| rechck.f | RECHCK | SUBROUTINE | | BASICS, ATOMIC, MODELQ | OPACF1, RTEFR1 | 📁 | | ⬜ | -| reflev.f | REFLEV | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | | | | ⬜ | -| reiman.f | REIMAN | FUNCTION | ✓ | | REIMAN | | reiman.rs | ✅ | -| resolv.f | RESOLV | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ALIPAR, ARRAY1, icnrsp | NEWPOP, TAUFR1, TIMING, OPACF1, RTEFR1, ROSSTD, STEQEQ, CONOUT, ELCOR, OPAINI, RATES1, PRD | 📁 | | ⬜ | -| rhoeos.f | RHOEOS | FUNCTION | | BASICS, MODELQ | PRSENT, RHOEOS | | | ⬜ | -| rhonen.f | RHONEN | SUBROUTINE | | BASICS, MODELQ | ELDENS | | | ⬜ | -| rhsgen.f | RHSGEN | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, CUBCON | MATINV, STATE, RATMAT, COMPT0, SABOLF, CONVEC, LEVGRP | | | ⬜ | -| rossop.f | ROSSOP | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR | WNSTOR, STEQEQ, ELDENS, OPACF0, MEANOP, MEANOPT | | | ⬜ | -| rosstd.f | ROSSTD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ALIPAR | | 📁 | | ⬜ | -| rte_sc.f | RTE_SC | SUBROUTINE | ✓ | BASICS | | | rte_sc.rs | ✅ | -| rteang.f | RTEANG | SUBROUTINE | | BASICS, MODELQ, ALIPAR, EXTINT, SURFEX | GAULEG | | | ⬜ | -| rtecf0.f | RTECF0 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT, auxcbc, AUXRTE | | | | ⬜ | -| rtecf1.f | RTECF1 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT, comgfs, AUXRTE, EXTINT, SURFEX | RTEFE2, RTESOL, RTECF0 | 📁 | | ⬜ | -| rtecmc.f | RTECMC | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, comgfs, AUXRTE | MATINV, OPACF1, RTECF0 | | | ⬜ | -| rtecmu.f | RTECMU | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT, AUXRTE | RTESOL, OPACF1, RTECF0, GAULEG | 📁 | | ⬜ | -| rtecom.f | RTECOM | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT, comgfs, AUXRTE | OPACF1, RTECF0, RTECF1 | | | ⬜ | -| rtedf1.f | RTEDF1 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, OPTDPT | | | rtedf1.rs | ✅ | -| rtedf2.f | RTEDF2 | SUBROUTINE | | BASICS, MODELQ, ALIPAR | | | | ⬜ | -| rtefe2.f | RTEFE2 | SUBROUTINE | ✓ | BASICS | | | rtefe2.rs | ✅ | -| rtefr1.f | RTEFR1 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT | MATINV, RTESOL, RTEDF1, RTECF1, RTEDF2, MINV3 | 📁 | | ⬜ | -| rteint.f | RTEINT | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT | MATINV, OPACF1 | 📁 | | ⬜ | -| rtesol.f | RTESOL | SUBROUTINE | ✓ | BASICS | | | rtesol.rs | ✅ | -| russel.f | RUSSEL | SUBROUTINE | | BASICS, MODELQ, COMFH1 | MPARTF | 📁 | | ⬜ | -| rybchn.f | RYBCHN | SUBROUTINE | | BASICS, ITERAT, MODELQ, ALIPAR, ARRAY1, rybpgs, grdpra | PGSET, ELDENS | 📁 | | ⬜ | -| rybene.f | RYBENE | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ARRAY1, RYBMTX, deridt, CUBCON | CONVEC | | | ⬜ | -| rybheq.f | RYBHEQ | SUBROUTINE | | BASICS, MODELQ, rybpgs, grdpra | OPACF1, RTEFR1, WNSTOR, STEQEQ, ELDENS, ELCOR, OPAINI, PGSET | 📁 | | ⬜ | -| rybmat.f | RYBMAT | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ARRAY1, dsctva, RYBMTX | | | | ⬜ | -| rybsol.f | RYBSOL | SUBROUTINE | | BASICS, MODELQ, ATOMIC, ALIPAR, ARRAY1, ITERAT, imodlc, RYBMTX | RYBMAT, RTEFR1, ROSSTD, OPACTR, LINEQS, STEQEQ, ALIFR1, TRIDAG, RYBCHN, OPACFD | 📁 | | ⬜ | -| sabolf.f | SABOLF | SUBROUTINE | | BASICS, ATOMIC, MODELQ | PARTF | | | ⬜ | -| sbfch.f | SBFCH | FUNCTION | ✓ | | SBFCH | | sbfch.rs | ✅ | -| sbfhe1.f | SBFHE1 | FUNCTION | | BASICS, ATOMIC | SBFHE1, QUIT | 📁 | sbfhe1.rs | ✅ | -| sbfhmi.f | SBFHMI | FUNCTION | ✓ | | SBFHMI | | sbfhmi.rs | ✅ | -| sbfhmi_old.f | SBFHMI_OLD | FUNCTION | ✓ | | SBFHMI_OLD | | sbfhmi_old.rs | ✅ | -| sbfoh.f | SBFOH | FUNCTION | ✓ | | SBFOH | | sbfoh.rs | ✅ | -| setdrt.f | SETDRT | SUBROUTINE | | BASICS, MODELQ, RHODER | | | | ⬜ | -| settrm.f | SETTRM | SUBROUTINE | | tdflag, tdedge, THERM, TABLTD | PRSENT | 📁 | | ⬜ | -| sffhmi.f | SFFHMI | FUNCTION | ✓ | | SFFHMI | | sffhmi.rs | ✅ | -| sffhmi_add.f | SFFHMI_ADD | FUNCTION | ✓ | | SFFHMI_ADD | | sffhmi_add.rs | ✅ | -| sghe12.f | SGHE12 | FUNCTION | ✓ | | SGHE12 | | sghe12.rs | ✅ | -| sgmer0.f | SGMER0 | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | sgmer.rs | ✅ | -| sgmer1.f | SGMER1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | sgmer.rs | ✅ | -| sgmerd.f | SGMERD | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | sgmer.rs | ✅ | -| sigave.f | SIGAVE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | | 📁 | | ⬜ | -| sigk.f | SIGK | FUNCTION | | BASICS, ATOMIC | SPSIGK, SIGK | | | ⬜ | -| sigmar.f | SIGMAR | FUNCTION | | BASICS | SIGMAR, LAGUER | 📁 | | ⬜ | -| solve.f | SOLVE | SUBROUTINE | | BASICS, ITERAT, MODELQ, ARRAY1, ALIPAR, CMATZD | MATINV, PRCHAN, RHSGEN, WNSTOR, MATGEN | 📁 | | ⬜ | -| solves.f | SOLVES | SUBROUTINE | | BASICS, ITERAT, MODELQ, ARRAY1, ALIPAR, STOMAT, CMATZD | MATINV, PRCHAN, RHSGEN, WNSTOR, MATGEN | 📁 | | ⬜ | -| spsigk.f | SPSIGK | SUBROUTINE | ✓ | | CARBON | | spsigk.rs | ✅ | -| srtfrq.f | SRTFRQ | SUBROUTINE | | BASICS, ATOMIC, MODELQ | INDEXX, QUIT | 📁 | | ⬜ | -| stark0.f | STARK0 | SUBROUTINE | ✓ | | | | stark0.rs | ✅ | -| starka.f | STARKA | FUNCTION | | BASICS, MODELQ | STARKA | | starka.rs | ✅ | -| start.f | START | SUBROUTINE | | BASICS, hediff | | 📁 | | ⬜ | -| state.f | STATE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, terden, PFSTDS | OPFRAC, PARTF | 📁 | | ⬜ | -| steqeq.f | STEQEQ | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, POPSTR, PPAPAR | LEVSOL, RATMAT, SABOLF, MOLEQ | | | ⬜ | -| switch.f | SWITCH | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | 📁 | | ⬜ | -| szirc.f | SZIRC | SUBROUTINE | ✓ | | EINT | | szirc.rs | ✅ | -| tabini.f | TABINI | SUBROUTINE | | BASICS, MODELQ, ATOMIC, eletab, abntab, intcff | | 📁 | | ⬜ | -| tabint.f | TABINT | SUBROUTINE | | BASICS, MODELQ, ATOMIC, intcff | | | | ⬜ | -| taufr1.f | TAUFR1 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT | | | | ⬜ | -| tdpini.f | TDPINI | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR | GFREE0 | | tdpini.rs | ✅ | -| temcor.f | TEMCOR | SUBROUTINE | | BASICS, MODELQ, ARRAY1, ALIPAR, CUBCON | MEANOP, WNSTOR, STEQEQ, ELDENS, OPACF0, CONVEC | 📁 | | ⬜ | -| temper.f | TEMPER | SUBROUTINE | | BASICS, MODELQ, ALIPAR, FACTRS, PRSAUX, FLXAUX | WNSTOR, TLOCAL, STEQEQ, ELDENS, OPACF0, MEANOP, MEANOPT | 📁 | | ⬜ | -| timing.f | TIMING | SUBROUTINE | | | | 📁 | | ⬜ | -| tiopf.f | TIOPF | SUBROUTINE | ✓ | | | | tiopf.rs | ✅ | -| tlocal.f | TLOCAL | SUBROUTINE | | BASICS, MODELQ, FACTRS, FLXAUX | QUARTC | | | ⬜ | -| tlusty.f | TLUSTY | UNKNOWN | | BASICS, ITERAT, ALIPAR | TIMING | 📁 | | ⬜ | -| topbas.f | TOPBAS | FUNCTION | | TOPB | TOPBAS | 📁 | | ⬜ | -| traini.f | TRAINI | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | | | traini.rs | ✅ | -| tridag.f | TRIDAG | SUBROUTINE | ✓ | | | | tridag.rs | ✅ | -| trmder.f | TRMDER | SUBROUTINE | | BASICS, derdif, terden, adiaba | ELDENS | | | ⬜ | -| trmdrt.f | TRMDRT | SUBROUTINE | | BASICS, tdedge, CC, tdflag, CONVOUT | PRSENT | | | ⬜ | -| ubeta.f | UBETA | FUNCTION | ✓ | | UBETA, LAGRAN | | ubeta.rs | ✅ | -| vern16.f | VERN16 | FUNCTION | ✓ | BASICS | VERN16 | | vern16.rs | ✅ | -| vern18.f | VERN18 | FUNCTION | ✓ | BASICS | VERN18 | | vern18.rs | ✅ | -| vern20.f | VERN20 | FUNCTION | ✓ | BASICS | VERN20 | | vern20.rs | ✅ | -| vern26.f | VERN26 | FUNCTION | ✓ | BASICS | VERN26 | | vern26.rs | ✅ | -| verner.f | VERNER | FUNCTION | | BASICS, ATOMIC | QUIT, VERNER | | verner.rs | ✅ | -| visini.f | VISINI | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | | 📁 | | ⬜ | -| voigt.f | VOIGT | FUNCTION | ✓ | | VOIGT | | voigt.rs | ✅ | -| voigte.f | VOIGTE | FUNCTION | ✓ | | VOIGTE | | voigte.rs | ✅ | -| wn.f | WN | FUNCTION | ✓ | BASICS | WN | | wn.rs | ✅ | -| wnstor.f | WNSTOR | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | wnstor.rs | ✅ | -| xenini.f | XENINI | SUBROUTINE | | BASICS, MODELQ | | 📁 | | ⬜ | -| xk2dop.f | XK2DOP | FUNCTION | ✓ | | XK2DOP | | xk2dop.rs | ✅ | -| yint.f | YINT | FUNCTION | ✓ | | YINT | | interpolate.rs | ✅ | -| ylintp.f | YLINTP | FUNCTION | ✓ | | YLINTP | | ylintp.rs | ✅ | -| zmrho.f | ZMRHO | SUBROUTINE | | BASICS, MODELQ | | | zmrho.rs | ✅ | diff --git a/MEMORY/MEMORY.md b/MEMORY/MEMORY.md deleted file mode 100644 index b2e48fb..0000000 --- a/MEMORY/MEMORY.md +++ /dev/null @@ -1,110 +0,0 @@ -# TLUSTY/SYNSPEC Rust 重构项目记忆 - -注意用中文回复 - -重要!!!:不要进行全量测试 `cargo test`,系统会卡死,需要时一个个来测 - -## 分离子模块(已经完成) -```bash -cd /home/fmq/program/tlusty/tl208-s54/rust -python3 extract_fortran.py tlusty/tlusty208.f tlusty/extracted/ -cp tlusty/*.FOR tlusty/extracted/ -``` - -分离后的子文件在tlusty/extracted/下 -## common模块 - -common模块(×.FOR)已经重构到src/state目录下 - -## 重构追踪系统 - -```bash -# 更新追踪表 (必须按顺序执行) -python3 scripts/analyze_fortran.py > MEMORY/fortran_analysis.csv -python3 scripts/generate_tracking.py > MEMORY/FORTRAN_TRACKING.md - -# 依赖树分析命令 -python3 scripts/analyze_fortran.py --priority # 按未实现依赖排序的重构优先级(只显示pending) -python3 scripts/analyze_fortran.py --tree UNIT # 输出依赖树(含未实现依赖数) -python3 scripts/analyze_fortran.py --full # 输出完整传递依赖 -``` - -| 指标 | 数量 | -|------|------| -| 总单元 | 305 | -| 已完成 | 107 | -| 待处理 | 198 | -| 完成率 | 35.1% | - -## 重构流程 - -DATA 语句(硬编码的数据表)已经由scripts/extract_fortran_data.py生成到src/data.rs中 - -详细规范见 [MEMORY/REFACTORING_GUIDE.md](MEMORY/REFACTORING_GUIDE.md) - -**步骤:** -1. `python3 scripts/analyze_fortran.py --priority` - 找未实现依赖少的函数 -2. 分析tlusty/extracted/文件夹下对应的 Fortran 源码 (INCLUDE/COMMON/调用/I/O) -3. 创建 `src/math/TARGET.rs` -4. 实现函数 + 测试 -5. 添加到 `mod.rs` -6. `cargo test target` (单个测试!) -7. 更新追踪表 - -**SPECIAL_MAPPINGS** (`scripts/analyze_fortran.py` 第 72 行): -```python -SPECIAL_MAPPINGS = { - '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'], - # 新增映射... -} -``` - -## 当前状态 (2026-03-20) - -- **已完成**: 107 个函数 + 15 个状态模块 -- **测试通过**: 421+ 个 -- **最新完成**: alifrk.rs (Kantorovich 迭代简化版) - -**查看实时进度**: `cat FORTRAN_TRACKING.md | head -20` - -## 项目结构 - -``` -rust/src/ -├── math/ # 函数 (85+ 个 .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 # 静态数据 -``` - -## 关键陷阱 - -| 问题 | 解决方案 | -|------|----------| -| Fortran 1-indexed | `arr(i)` → `arr[i-1]` | -| `-LOG(X)` | 是 `-ln(X)` 不是 `ln(-X)` | -| powi 类型歧义 | 用显式乘法 `(a)*(a)` | -| 循环变量变负 | 用 `isize` 不用 `usize` | -| 多项式近似精度 | 放宽到 1e-7 | -| 矩阵列优先 | `A(j,i)` → `a[(i-1)*N + (j-1)]` | -| COMMON 块冲突 | 添加前 grep 检查是否已存在 | -| 循环变量重赋值 | 声明为 `mut` | - -## 详细文档 - -- [MEMORY/REFACTORING_GUIDE.md](MEMORY/REFACTORING_GUIDE.md) - 重构规范 ⭐ -- [MEMORY/FORTRAN_TRACKING.md](MEMORY/FORTRAN_TRACKING.md) - 函数追踪表 -- [MEMORY/refactoring_notes.md](MEMORY/refactoring_notes.md) - 重构过程中遇到的问题及解决方法 diff --git a/MEMORY/REFACTORING_GUIDE.md b/MEMORY/REFACTORING_GUIDE.md deleted file mode 100644 index 14292c0..0000000 --- a/MEMORY/REFACTORING_GUIDE.md +++ /dev/null @@ -1,374 +0,0 @@ -# TLUSTY Rust 重构规范 - -## 1. 选定下一个重构目标 - -优先处理依赖少无io的函数 - -注意:DATA 语句(硬编码的数据表)已经由scripts/extract_fortran_data.py生成到src/data.rs中 - -不要管复杂不复杂和有无common依赖, -按python3 scripts/analyze_fortran.py --priority | head -10 的顺序来 - -分离出来的原始fortran函数在/home/fmq/program/tlusty/tl208-s54/rust/tlusty/extracted文件夹下 -### 优先级排序 - -**核心原则**: 优先处理"传递未实现依赖=0"的函数,它们不依赖其他未完成的函数。 - -按以下顺序选择待重构函数: - -1. **无未实现依赖** (传递未实现=0) - - 可立即开始,无需等待其他函数 - - 包括纯函数和只有 COMMON 依赖的函数 - -2. **少量未实现依赖** (传递未实现=1~3) - - 需要先完成少数依赖函数 - - 用 `--tree` 查看具体依赖链 - -3. **多未实现依赖** (传递未实现>3) - - 依赖链较长,最后处理 - - 或考虑整体重构策略 - -4. **有 I/O 依赖的函数** - - 最后处理,可能需要设计新的 I/O 抽象层 - -### 查询命令 - -CSV 列结构: `fortran_file,unit_name,unit_type,is_pure,common_deps,call_deps,has_io,rust_module,status` - -#### 依赖树分析 - -```bash -# 输出重构优先级列表(按未实现依赖排序,推荐!) -python3 scripts/analyze_fortran.py --priority - -# 输出指定单元的依赖树 -python3 scripts/analyze_fortran.py --tree UNIT_NAME - -# 输出完整传递依赖的 CSV -python3 scripts/analyze_fortran.py --full > fortran_analysis_full.csv -``` - -**优先级列表说明:** -``` -重构优先级列表 (按未实现依赖排序) -==================================================================================================== -单元名 未实现 传递未实现 深度 直接调用 传递调用 IO ----------------------------------------------------------------------------------------------------- -C 0 0 0 0 0 ○ -ALIFR3 0 0 0 0 0 ○ -``` -- **未实现**: 直接依赖中未完成的函数数 -- **传递未实现**: 所有递归依赖中未完成的函数数(关键指标!) -- **排序**: 传递未实现少 → 深度低 → 依赖少 -- 只显示未完成(pending)的函数 -- IO 列: ○ = 无IO, ✓ = 有IO - -**依赖树输出示例:** -``` -依赖树: ALISK1 ○ -============================================================ -直接依赖: 4, 传递依赖: 27, 未实现: 20 -未实现依赖: ALIFRK, OPACF1, OPADD, ROSSTD, ... ------------------------------------------------------------- -○ ALISK1 (4未实现) -├── ○ OPACF1 (6未实现) -│ ├── ○ OPADD (5未实现) -│ │ ├── ○ cia_h2h2 (1未实现) -│ │ │ └── if [未找到/未实现] -│ │ └── ✓ locate -│ └── ... -└── ○ ALIFRK -``` -- ✓ = 已完成, ○ = 待处理 -- `(N未实现)` = 该节点有 N 个直接依赖未完成 -- 顶部汇总:传递未实现依赖数及列表 -- 子节点按未实现依赖数排序(多的在前) - -#### 按纯度筛选 - -无io的纯函数已经全部实现重构 -#### 按 I/O 筛选 - -```bash -# 有 I/O 的未完成函数 (最后处理) -awk -F, '$7=="True" && $9=="pending"' fortran_analysis.csv - -# 无 I/O 的未完成函数 (优先处理) -awk -F, '$7=="False" && $9=="pending"' fortran_analysis.csv - -# 无 I/O 且无调用依赖的未完成函数 -awk -F, '$6=="" && $7=="False" && $9=="pending"' fortran_analysis.csv -``` - -#### 按 COMMON 依赖筛选 - -```bash -# 只依赖 BASICS 的未完成函数 -awk -F, '$5=="BASICS" && $9=="pending"' fortran_analysis.csv - -# 依赖 BASICS 和 ATOMIC,但无其他依赖 -awk -F, '$5 ~ /^BASICS\|ATOMIC$/ && $9=="pending"' fortran_analysis.csv - -# 不依赖 MODELQ 的未完成函数 (MODELQ 最复杂) -awk -F, '$5 !~ /MODELQ/ && $9=="pending"' fortran_analysis.csv - -# 列出所有不同的 COMMON 依赖组合 -awk -F, '{print $5}' fortran_analysis.csv | sort | uniq -c | sort -rn -``` - -#### 按状态筛选 - -```bash -# 所有已完成函数 -awk -F, '$9=="done"' fortran_analysis.csv - -# 所有进行中函数 -awk -F, '$9=="in_progress"' fortran_analysis.csv - -# 统计各状态数量 -awk -F, '{print $9}' fortran_analysis.csv | sort | uniq -c -``` - -#### 组合筛选 - -```bash -# 最佳候选:无 I/O + 无调用依赖 + 纯函数或简单 COMMON -awk -F, '$6=="" && $7=="False" && $9=="pending"' fortran_analysis.csv - -# 次优候选:无 I/O + 有调用依赖但依赖已完成 -awk -F, '$6!="" && $7=="False" && $9=="pending"' fortran_analysis.csv - -# 查看特定函数的依赖信息 -awk -F, '$2=="TARGET"' fortran_analysis.csv -``` - -#### 按代码大小排序 - -```bash -# 按行数排序(选小的先做) -cd tlusty/extracted -wc -l *.f | sort -n | head -30 - -# 查看小文件 + 纯函数 -for f in $(ls *.f); do - lines=$(wc -l < "$f") - name=$(basename "$f" .f) - if grep -q "True.*pending" ../../fortran_analysis.csv && [ $lines -lt 100 ]; then - echo "$lines $name" - fi -done | sort -n -``` - -#### 快速查看 - -```bash -# 查看重构进度摘要 -echo "已完成: $(awk -F, '$9=="done"' fortran_analysis.csv | wc -l)" -echo "待处理: $(awk -F, '$9=="pending"' fortran_analysis.csv | wc -l)" -echo "纯函数待处理: $(awk -F, '$4=="True" && $9=="pending"' fortran_analysis.csv | wc -l)" -echo "无IO待处理: $(awk -F, '$7=="False" && $9=="pending"' fortran_analysis.csv | wc -l)" -``` - -## 2. 重构流程 - -### Step 1: 分析 Fortran 源码 - -```bash -# 查看源码 -cat tlusty/extracted/TARGET.f -``` - -检查: -- [ ] INCLUDE 文件列表 -- [ ] COMMON 块 -- [ ] 调用的其他函数 -- [ ] 是否有 I/O - -### Step 2: 创建 Rust 模块 - -```bash -# 创建文件 -touch src/math/TARGET.rs -``` - -### Step 3: 实现函数 - -遵循命名规范: -- Fortran `FUNCTION` → Rust `pub fn` -- Fortran `SUBROUTINE` → Rust `pub fn` (返回值用参数或元组) -- COMMON 块 → 结构体参数 - -### Step 4: 添加到 mod.rs - -```rust -// src/math/mod.rs -mod target; -pub use target::target; -``` - -### Step 5: 编写测试 - -```rust -#[cfg(test)] -mod tests { - use super::*; - use approx::assert_relative_eq; - - #[test] - fn test_target_basic() { - // 基本测试 - } -} -``` - -### Step 6: 运行测试 - -```bash -# 单个模块测试 (不要 cargo test 全量!) -cargo test target -``` - -### Step 7: 更新追踪表 - -```bash -# 如果是一对一映射,脚本自动识别 -python3 scripts/analyze_fortran.py > fortran_analysis.csv -python3 scripts/generate_tracking.py > FORTRAN_TRACKING.md - -# 如果是一对多映射,先更新 SPECIAL_MAPPINGS -``` - -## 3. SPECIAL_MAPPINGS 维护 - -### 何时需要更新 - -当一个 Rust 文件实现多个 Fortran 函数时,需要添加映射。 - -### 位置 - -`scripts/analyze_fortran.py` 第 72 行 - -### 格式 - -```python -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'], - # 新增映射... -} -``` - -### 添加新映射后执行 - -```bash -python3 scripts/analyze_fortran.py > fortran_analysis.csv -python3 scripts/generate_tracking.py > FORTRAN_TRACKING.md -``` - -## 4. 状态更新流程 - -### 手动更新状态 - -如需手动标记状态,编辑 `fortran_analysis.csv`: - -```csv -# status 列: done, pending, in_progress, skip -filename.f,UNIT,SUBROUTINE,False,"...",...,False,src/math/xxx.rs,done -``` - -然后重新生成 Markdown: - -```bash -python3 scripts/generate_tracking.py > FORTRAN_TRACKING.md -``` - -## 5. 代码规范 - -### 文件头注释 - -```rust -//! 模块简要说明。 -//! -//! 重构自 TLUSTY `filename.f` -``` - -### 函数注释 - -```rust -/// 函数功能说明。 -/// -/// # 参数 -/// -/// * `x` - 参数说明 -/// -/// # 返回值 -/// -/// 返回值说明 -/// -/// # 示例 -/// -/// ``` -/// use tlusty_rust::math::func; -/// assert!((func(1.0) - 2.0).abs() < 1e-10); -/// ``` -pub fn func(x: f64) -> f64 { ... } -``` - -### 精度要求 - -| 函数类型 | 容差 | -|---------|------| -| 简单数学运算 | 1e-10 | -| 多项式近似 | 1e-7 | -| f32 数组 | 1e-24 | - -### 常见陷阱 - -| 问题 | 解决方案 | -|------|----------| -| Fortran 1-indexed | `arr(i)` → `arr[i-1]` | -| `-LOG(X)` | 是 `-ln(X)` 不是 `ln(-X)` | -| powi 类型歧义 | 用显式乘法 `(a)*(a)` | -| 矩阵列优先 | `A(j,i)` → `a[(i-1)*N + (j-1)]` | - -## 6. 快速命令参考 - -```bash -# 查看重构进度 -cat FORTRAN_TRACKING.md | head -20 - -# 进度统计 -echo "已完成: $(awk -F, '$9=="done"' fortran_analysis.csv | wc -l)" -echo "待处理: $(awk -F, '$9=="pending"' fortran_analysis.csv | wc -l)" - -# 推荐方式:查看优先级列表(按未实现依赖排序) -python3 scripts/analyze_fortran.py --priority | head -30 - -# 查看函数依赖树(含未实现依赖数) -python3 scripts/analyze_fortran.py --tree FUNCTION_NAME - -# 找最佳候选:无IO + 无调用依赖 -awk -F, '$6=="" && $7=="False" && $9=="pending"' fortran_analysis.csv | head -5 - -# 找纯函数 -grep "True.*pending" fortran_analysis.csv | head -5 - -# 找无IO的函数 -awk -F, '$7=="False" && $9=="pending"' fortran_analysis.csv | head -5 - -# 测试单个模块 -cargo test module_name - -# 更新追踪表 -python3 scripts/analyze_fortran.py > fortran_analysis.csv && \ -python3 scripts/generate_tracking.py > FORTRAN_TRACKING.md - -# 编译检查 -cargo build 2>&1 | grep error -``` diff --git a/MEMORY/fortran_analysis.csv b/MEMORY/fortran_analysis.csv deleted file mode 100644 index 867b9fe..0000000 --- a/MEMORY/fortran_analysis.csv +++ /dev/null @@ -1,305 +0,0 @@ -fortran_file,unit_name,unit_type,is_pure,common_deps,call_deps,has_io,rust_module,status -_unnamed_block_data_.f,C,BLOCK DATA,False,"BASICS|ATOMIC","",False,,pending -accel2.f,ACCEL2,SUBROUTINE,False,"BASICS|ITERAT|MODELQ","",True,,pending -accelp.f,ACCELP,SUBROUTINE,False,"BASICS|MODELQ|ITERAT|POPULS","",True,,pending -alifr1.f,ALIFR1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","ALIFR3",False,,pending -alifr3.f,ALIFR3,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","",False,src/math/alifr3.rs,done -alifr6.f,ALIFR6,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","",False,src/math/alifr6.rs,done -alifrk.f,ALIFRK,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","",False,src/math/alifrk.rs,done -alisk1.f,ALISK1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","RTEFR1|ALIFRK|ROSSTD|OPACF1",True,,pending -alisk2.f,ALISK2,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","RTEFR1|ALIFRK|ROSSTD|OPACF1",True,,pending -alist1.f,ALIST1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT","RTEFR1|ALIFR1|OPACFD|ROSSTD",True,,pending -alist2.f,ALIST2,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","QUIT|OPACFD|RTEFR1|ALIFR1|ROSSTD",True,,pending -allard.f,ALLARD,SUBROUTINE,False,"BASICS|callardb|callardc|callarda|quasun|callardg|calphatd","ALLARDT",True,,pending -allardt.f,ALLARDT,SUBROUTINE,False,"BASICS|calphatd","",False,src/math/allardt.rs,done -angset.f,ANGSET,SUBROUTINE,True,"BASICS","GAULEG",False,src/math/angset.rs,done -betah.f,BETAH,FUNCTION,True,"","",False,src/math/betah.rs,done -bhe.f,BHE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","",False,src/math/bhe.rs,done -bhed.f,BHED,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|CMATZD|SURFEX","",False,src/math/bhe.rs,done -bhez.f,BHEZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|SURFEX","",False,src/math/bhe.rs,done -bkhsgo.f,BKHSGO,SUBROUTINE,True,"","",False,src/math/bkhsgo.rs,done -bpop.f,BPOP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR|ITERAT","LEVGRP|BPOPE|RATMAT|BPOPT|MATINV|BPOPC|LEVSOL|BPOPF",False,,pending -bpopc.f,BPOPC,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR|ADCHAR","STATE",False,,pending -bpope.f,BPOPE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT|ARRAY1","DWNFR1|SGMER1",False,,pending -bpopf.f,BPOPF,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR","",False,src/math/bpopf.rs,done -bpopt.f,BPOPT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR","COLIS",False,,pending -bre.f,BRE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","COMPT0",False,,pending -brez.f,BREZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","COMPT0",False,,pending -brte.f,BRTE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1","COMPT0",False,,pending -brtez.f,BRTEZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1","COMPT0",False,,pending -butler.f,BUTLER,SUBROUTINE,True,"","",False,src/math/butler.rs,done -carbon.f,CARBON,SUBROUTINE,True,"","",False,src/math/carbon.rs,done -ceh12.f,CEH12,FUNCTION,True,"","",False,src/math/ceh12.rs,done -change.f,CHANGE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","READBF|STEQEQ",True,,pending -chckse.f,CHCKSE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","SABOLF",True,,pending -chctab.f,CHCTAB,SUBROUTINE,False,"BASICS|MODELQ|abntab","",True,,pending -cheav.f,CHEAV,FUNCTION,False,"BASICS|ATOMIC","QUIT",True,,pending -cheavj.f,CHEAVJ,FUNCTION,False,"BASICS|ATOMIC","QUIT",True,,pending -cia_h2h.f,CIA_H2H,SUBROUTINE,False,"","LOCATE|IF",True,,pending -cia_h2h2.f,CIA_H2H2,SUBROUTINE,False,"","LOCATE|IF",True,,pending -cia_h2he.f,CIA_H2HE,SUBROUTINE,False,"","LOCATE|IF",True,,pending -cia_hhe.f,CIA_HHE,SUBROUTINE,False,"","LOCATE|IF",True,,pending -cion.f,CION,FUNCTION,True,"","",False,src/math/cion.rs,done -ckoest.f,CKOEST,FUNCTION,True,"BASICS","",False,src/math/ckoest.rs,done -colh.f,COLH,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","IRC|CSPEC|BUTLER",False,,pending -colhe.f,COLHE,SUBROUTINE,False,"BASICS|ATOMIC","IRC|COLLHE|CSPEC",False,,pending -colis.f,COLIS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|CTRTEMP","IRC|COLHE|CSPEC|COLH",False,,pending -collhe.f,COLLHE,SUBROUTINE,True,"","",False,src/math/collhe.rs,done -column.f,COLUMN,SUBROUTINE,False,"BASICS|MODELQ|relcor","",True,,pending -compt0.f,COMPT0,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|auxcbc","",False,src/math/compt0.rs,done -comset.f,COMSET,SUBROUTINE,False,"BASICS|MODELQ|auxcbc|comgfs","",False,src/math/comset.rs,done -concor.f,CONCOR,SUBROUTINE,False,"BASICS|MODELQ","CONOUT",True,,pending -conout.f,CONOUT,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|CUBCON","MEANOPT|MEANOP|CONVEC|OPACF0",True,,pending -conref.f,CONREF,SUBROUTINE,False,"BASICS|MODELQ|ARRAY1|imucnn|CUBCON","WNSTOR|ELDENS|CONVC1|STEQEQ|CONOUT|CONVEC",True,,pending -contmd.f,CONTMD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|PRSAUX|CUBCON","MEANOP|CUBIC|OPACF0|STEQEQ|WNSTOR|CONOUT|CONVEC",True,,pending -contmp.f,CONTMP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ichndm|CUBCON","MEANOPT|MEANOP|CUBIC|OPACF0|STEQEQ|ELDENS|WNSTOR|CONOUT|CONVEC",True,,pending -convc1.f,CONVC1,SUBROUTINE,False,"BASICS|CUBCON","TRMDER|TRMDRT",False,,pending -convec.f,CONVEC,SUBROUTINE,False,"BASICS|CUBCON","TRMDER|TRMDRT",False,,pending -coolrt.f,COOLRT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT|COOLCO","RTEFR1|OPACFA",True,,pending -corrwm.f,CORRWM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT",True,,pending -cross.f,CROSS,FUNCTION,False,"BASICS|ATOMIC|MODELQ","",False,src/math/cross.rs,done -crossd.f,CROSSD,FUNCTION,False,"BASICS|ATOMIC|MODELQ","",False,src/math/cross.rs,done -cspec.f,CSPEC,SUBROUTINE,False,"BASICS|ATOMIC","QUIT",False,,pending -ctdata.f,CTDATA,BLOCK DATA,False,"CTIon|CTRecomb","",False,src/math/ctdata.rs,done -cubic.f,CUBIC,SUBROUTINE,False,"BASICS|CUBCON","",False,src/math/cubic.rs,done -dielrc.f,DIELRC,SUBROUTINE,True,"","",False,src/math/dielrc.rs,done -dietot.f,DIETOT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","DIELRC",True,,pending -divstr.f,DIVSTR,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/divstr.rs,done -dmder.f,DMDER,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|DEPTDR","",False,src/math/dmder.rs,done -dmeval.f,DMEVAL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ARRAY1","",True,,pending -dopgam.f,DOPGAM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","GAMSP",False,src/math/dopgam.rs,done -dwnfr.f,DWNFR,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/dwnfr.rs,done -dwnfr0.f,DWNFR0,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/dwnfr0.rs,done -dwnfr1.f,DWNFR1,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/dwnfr1.rs,done -eint.f,EINT,SUBROUTINE,True,"","EXPINX",False,src/math/expint.rs,done -elcor.f,ELCOR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ADCHAR","WNSTOR|STEQEQ|STATE|MOLEQ",True,,pending -eldenc.f,ELDENC,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|eospar|hmolab|eletab","RHONEN|STATE|MOLEQ",True,,pending -eldens.f,ELDENS,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|eospar|terden","ENTENE|LINEQS|MPARTF|STATE|MOLEQ",True,,pending -emat.f,EMAT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","",False,src/math/emat.rs,done -entene.f,ENTENE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","MPARTF",False,,pending -erfcin.f,ERFCIN,FUNCTION,True,"","",False,src/math/erfcx.rs,done -erfcx.f,ERFCX,FUNCTION,True,"","",False,src/math/erfcx.rs,done -expint.f,EXPINT,FUNCTION,True,"","",False,src/math/expint.rs,done -expinx.f,EXPINX,SUBROUTINE,True,"","",False,src/math/expint.rs,done -expo.f,EXPO,FUNCTION,True,"","",False,src/math/expo.rs,done -ffcros.f,FFCROS,FUNCTION,True,"","",False,src/math/ffcros.rs,done -gami.f,GAMI,FUNCTION,True,"","",False,src/math/gami.rs,done -gamsp.f,GAMSP,SUBROUTINE,True,"BASICS","",False,src/math/gamsp.rs,done -gauleg.f,GAULEG,SUBROUTINE,True,"","",False,src/math/gauleg.rs,done -gaunt.f,GAUNT,FUNCTION,True,"","",False,src/math/gaunt.rs,done -getlal.f,GETLAL,SUBROUTINE,False,"BASICS|callardb|callardc|callarda|quasun|callardg|calphatd","",True,,pending -getwrd.f,GETWRD,SUBROUTINE,True,"","",False,src/math/getwrd.rs,done -gfree0.f,GFREE0,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/gfree.rs,done -gfree1.f,GFREE1,FUNCTION,False,"BASICS|MODELQ","",False,src/math/gfree.rs,done -gfreed.f,GFREED,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/gfree.rs,done -ghydop.f,GHYDOP,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|intcfg","",False,src/math/ghydop.rs,done -gntk.f,GNTK,FUNCTION,True,"","",False,src/math/gntk.rs,done -gomini.f,GOMINI,SUBROUTINE,False,"BASICS|MODELQ|intcfg","",True,,pending -grcor.f,GRCOR,SUBROUTINE,True,"","",False,src/math/grcor.rs,done -greyd.f,GREYD,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|ALIPAR","WNSTOR|MEANOP|OPACF0|STEQEQ|RHONEN",True,,pending -gridp.f,GRIDP,SUBROUTINE,True,"BASICS","",False,src/math/gridp.rs,done -h2minus.f,H2MINUS,SUBROUTINE,False,"BASICS","LOCATE",True,,pending -hction.f,HCTION,FUNCTION,False,"CTIon|CTRTEMP","",False,src/math/ctdata.rs,done -hctrecom.f,HCTRECOM,FUNCTION,False,"CTRTEMP|CTRecomb","",False,src/math/ctdata.rs,done -hedif.f,HEDIF,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|hediff","",True,,pending -hephot.f,HEPHOT,FUNCTION,True,"","",False,src/math/hephot.rs,done -hesol6.f,HESOL6,SUBROUTINE,False,"BASICS|MODELQ|PRSAUX","MATINV",False,,pending -hesolv.f,HESOLV,SUBROUTINE,False,"BASICS|MODELQ|PRSAUX","WNSTOR|STEQEQ|RHONEN|MATINV",True,,pending -hidalg.f,HIDALG,FUNCTION,True,"","",False,src/math/hidalg.rs,done -ijali2.f,IJALI2,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","QUIT",True,,pending -ijalis.f,IJALIS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",True,,pending -incldy.f,INCLDY,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT|RATMAT|SABOLF|WNSTOR|LEVSOL",True,,pending -indexx.f,INDEXX,SUBROUTINE,True,"","",False,src/math/indexx.rs,done -inicom.f,INICOM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|comgfs","",False,src/math/inicom.rs,done -inifrc.f,INIFRC,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ijflar","INDEXX",True,,pending -inifrs.f,INIFRS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","QUIT|INDEXX",True,,pending -inifrt.f,INIFRT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ijflar","INDEXX",True,,pending -inilam.f,INILAM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR","WNSTOR|RTEFR1|RATES1|ELCOR|COLIS|SABOLF|STEQEQ|OPAINI|OPACF1",False,,pending -initia.f,INITIA,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|freqcl|INUNIT|STRPAR","OPADD0|QUIT|ODFHYS|LINSPL|NSTPAR|INIFRC|READBF|RDATA|STATE|DOPGAM|LINSET|RDATAX|INTERP",True,,pending -inkul.f,INKUL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|COLKUR|LINED","",True,,pending -inpdis.f,INPDIS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|relcor","GRCOR",True,,pending -inpmod.f,INPMOD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|eospar","QUIT|RATMAT|KURUCZ|INCLDY|MOLEQ|SABOLF|WNSTOR|LEVSOL",True,,pending -interp.f,INTERP,SUBROUTINE,True,"BASICS","",False,src/math/interp.rs,done -inthyd.f,INTHYD,SUBROUTINE,False,"BASICS|MODELQ","DIVSTR",False,src/math/inthyd.rs,done -intlem.f,INTLEM,SUBROUTINE,False,"BASICS|MODELQ","INTHYD",False,src/math/intlem.rs,done -intxen.f,INTXEN,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/intxen.rs,done -irc.f,IRC,SUBROUTINE,True,"","EXPINX|SZIRC",False,src/math/irc.rs,done -iroset.f,IROSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|LINED","QUIT|LEVCD|INKUL",True,,pending -kurucz.f,KURUCZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|temlim","QUIT|RATMAT|MOLEQ|SABOLF|WNSTOR|LEVSOL|RHONEN",True,,pending -lagran.f,LAGRAN,SUBROUTINE,True,"","",False,src/math/interpolate.rs,done -laguer.f,LAGUER,SUBROUTINE,False,"","",True,src/math/laguer.rs,done -lemini.f,LEMINI,SUBROUTINE,False,"BASICS|MODELQ","",True,,pending -levcd.f,LEVCD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|COLKUR","QUIT|INDEXX",True,,pending -levgrp.f,LEVGRP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",False,src/math/levgrp.rs,done -levset.f,LEVSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT",False,,pending -levsol.f,LEVSOL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","LINEQS",False,src/math/levsol.rs,done -lineqs.f,LINEQS,SUBROUTINE,True,"BASICS","",False,src/math/lineqs.rs,done -linpro.f,LINPRO,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|quasun","DIVSTR|STARK0|DOPGAM|INTLEM|INTXEN",False,,pending -linsel.f,LINSEL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","RTEFR1|QUIT|OPAINI|OPACF1",True,,pending -linset.f,LINSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","DIVSTR|QUIT|IJALIS|STARK0",True,,pending -linspl.f,LINSPL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/linspl.rs,done -locate.f,LOCATE,SUBROUTINE,True,"","",False,src/math/locate.rs,done -ltegr.f,LTEGR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT|CONOUT|ROSSOP|WNSTOR|STEQEQ|INTERP",True,,pending -ltegrd.f,LTEGRD,SUBROUTINE,False,"BASICS|MODELQ|PRSAUX|CUBCON|TOTJHK|FACTRS|FLXAUX","QUIT|TEMPER|CONOUT|ELDENS|WNSTOR|STEQEQ|INTERP|ZMRHO",True,,pending -lucy.f,LUCY,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ITERAT|ALIPAR|ARRAY1","OPACFL|RTEFR1|ELCOR|COLIS|SABOLF|WNSTOR|STEQEQ|OPAINI",True,,pending -lymlin.f,LYMLIN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","DIVSTR|STARK0",True,,pending -matcon.f,MATCON,SUBROUTINE,False,"BASICS|MODELQ|ARRAY1|CUBCON","CONVEC",False,,pending -matgen.f,MATGEN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","BHED|MATCON|BREZ|BRE|BRTEZ|BHE|BRTE|SABOLF|BHEZ|EMAT|BPOP",False,,pending -matinv.f,MATINV,SUBROUTINE,True,"BASICS","",False,src/math/matinv.rs,done -meanop.f,MEANOP,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC","",False,src/math/meanop.rs,done -meanopt.f,MEANOPT,SUBROUTINE,False,"BASICS|MODELQ","OPCTAB",False,,pending -minv3.f,MINV3,SUBROUTINE,True,"","",False,src/math/minv3.rs,done -moleq.f,MOLEQ,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|adchar|eospar|hmolab|terden|ioniz2|COMFH1|moldat|entrop","MPARTF|RUSSEL",True,,pending -mpartf.f,MPARTF,SUBROUTINE,False,"moldat","",True,,pending -newdm.f,NEWDM,SUBROUTINE,False,"BASICS|MODELQ|FACTRS|PRSAUX|FLXAUX","INTERP|TEMPER",True,,pending -newdmt.f,NEWDMT,SUBROUTINE,False,"BASICS|MODELQ|FACTRS|PRSAUX|FLXAUX","INTERP|GRIDP|TEMPER",True,,pending -newpop.f,NEWPOP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",True,,pending -nstout.f,NSTOUT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR","QUIT",True,,pending -nstpar.f,NSTPAR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|deridt|irwint|icnrsp|imucnn|ichndm|derdif|hediff|freqcl|temlim|moldat|ifpzpa|quasun|ipricr|adiaba|FLXAUX","QUIT|GETWRD",True,,pending -odf1.f,ODF1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","DIVSTR|ODFHST|DWNFR",True,,pending -odffr.f,ODFFR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","QUIT",False,,pending -odfhst.f,ODFHST,SUBROUTINE,False,"BASICS|MODELQ|ODFPAR","",False,src/math/odfhst.rs,done -odfhyd.f,ODFHYD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","DIVSTR|ODFHST|INDEXX",False,,pending -odfhys.f,ODFHYS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","ODFFR|STARK0|IJALIS",False,,pending -odfmer.f,ODFMER,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","ODFHYD",False,,pending -odfset.f,ODFSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|STFCR","QUIT|IJALIS",True,,pending -opacf0.f,OPACF0,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|hmolab","OPACT1|OPADD|SGMER1|GFREE0|DWNFR0|DWNFR1|SABOLF|WNSTOR|LINPRO",False,,pending -opacf1.f,OPACF1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|hmolab|ipricr","OPACT1|OPADD|QUASIM|LYMLIN|GHYDOP|DWNFR1|PRD|SGMER1",True,,pending -opacfa.f,OPACFA,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|COOLCO","DWNFR1|OPADD|PRD|SGMER1",False,,pending -opacfd.f,OPACFD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT|dsctva|rhoder|hmolab","OPADD|OPACTD|QUASIM|LYMLIN|OPCTAB|DWNFR1|PRD|SGMER1|GFREED",True,,pending -opacfl.f,OPACFL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","DWNFR1|OPADD|SGMER1",False,,pending -opact1.f,OPACT1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|hmolab","OPCTAB",False,,pending -opactd.f,OPACTD,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ARRAY1|ITERAT|dsctva|rhoder|hmolab","OPCTAB",False,,pending -opactr.f,OPACTR,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ATOMIC|grdpra|dsctva|hmolab","STEQEQ|PGSET|ELDENS|SABOLF|RATMAL|WNSTOR|LEVSOL|OPAINI|OPACF1",False,,pending -opadd.f,OPADD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|eospar","H2MINUS|CIA_H2H|CIA_H2HE|CIA_H2H2|CIA_HHE",False,,pending -opadd0.f,OPADD0,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT",False,,pending -opahst.f,OPAHST,SUBROUTINE,False,"BASICS|ODFPAR","STARK0",True,,pending -opaini.f,OPAINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","LEVGRP|DWNFR0|REFLEV|SABOLF|WNSTOR|LINPRO",False,,pending -opctab.f,OPCTAB,SUBROUTINE,False,"BASICS|MODELQ","RAYLEIGH",False,,pending -opdata.f,OPDATA,SUBROUTINE,False,"TOPB","",True,,pending -opfrac.f,OPFRAC,SUBROUTINE,False,"pfoptb","",True,,pending -osccor.f,OSCCOR,SUBROUTINE,False,"BASICS|MODELQ|ITERAT","",True,,pending -outpri.f,OUTPRI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|grdpra","SABOLF|RATMAL|WNSTOR|LEVSOL|OPACF1",True,,pending -output.f,OUTPUT,SUBROUTINE,False,"BASICS|MODELQ","",True,,pending -partf.f,PARTF,SUBROUTINE,False,"BASICS|PFSTDS|irwint","OPFRAC|MPARTF|PFNI|PFFE|PFCNO|PFHEAV|PFSPEC",False,,pending -pfcno.f,PFCNO,SUBROUTINE,True,"BASICS","",False,src/math/pfcno.rs,done -pffe.f,PFFE,SUBROUTINE,True,"","",False,src/math/pffe.rs,done -pfheav.f,PFHEAV,SUBROUTINE,False,"","",True,,pending -pfni.f,PFNI,SUBROUTINE,True,"","",False,src/math/pfni.rs,done -pfspec.f,PFSPEC,SUBROUTINE,True,"","",False,src/math/pfspec.rs,done -pgset.f,PGSET,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|grdpra|rybpgs","TRIDAG",True,,pending -prchan.f,PRCHAN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",True,,pending -prd.f,PRD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","DOPGAM",False,,pending -prdini.f,PRDINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/prdini.rs,done -princ.f,PRINC,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","DWNFR|LINPRO|SABOLF|OPACF1",True,,pending -prnt.f,PRNT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","SABOLF",True,,pending -profil.f,PROFIL,FUNCTION,False,"BASICS|ATOMIC|MODELQ|quasun","DIVSTR|STARK0",False,src/math/profil.rs,done -profsp.f,PROFSP,FUNCTION,False,"BASICS|ATOMIC|MODELQ","SABOLF",False,,pending -prsent.f,PRSENT,SUBROUTINE,False,"tdedge|TABLTD|tdflag|THERM","",True,,pending -psolve.f,PSOLVE,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/psolve.rs,done -pzert.f,PZERT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/pzert.rs,done -pzeval.f,PZEVAL,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|icnrsp","CONOUT",True,,pending -pzevld.f,PZEVLD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1|grdpra|DEPTDR|ifpzpa|PRSAUX","",False,src/math/pzevld.rs,done -quartc.f,QUARTC,SUBROUTINE,False,"","",True,src/math/quartc.rs,done -quasim.f,QUASIM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|quasun","ALLARD",False,,pending -quit.f,QUIT,SUBROUTINE,False,"","",True,src/math/quit.rs,done -radpre.f,RADPRE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","RTEFR1|INDEXX|QUIT|OPACF1",True,,pending -radtot.f,RADTOT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT|TOTJHK|SURFEX|OPTDPT","RTEFR1|OPAINI|OPACF1",False,,pending -raph.f,RAPH,FUNCTION,True,"","",False,src/math/raph.rs,done -rates1.f,RATES1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT","RTEFR1|ROSSTD|OPACF1",False,,pending -ratmal.f,RATMAL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/ratmal.rs,done -ratmat.f,RATMAT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","REFLEV",False,,pending -ratsp1.f,RATSP1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","RTEFR1|ROSSTD|OPACF1",True,,pending -rayini.f,RAYINI,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC","RAYLEIGH",True,,pending -rayleigh.f,RAYLEIGH,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|eospar|RAYSCT","",False,src/math/rayleigh.rs,done -rayset.f,RAYSET,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/rayset.rs,done -rdata.f,RDATA,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|INUNIT|STRPAR|imodlc","QUIT|DOPGAM|LINSET|RDATAX",True,,pending -rdatax.f,RDATAX,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","BKHSGO",True,,pending -readbf.f,READBF,SUBROUTINE,False,"BASICS","",True,,pending -rechck.f,RECHCK,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","RTEFR1|OPACF1",True,,pending -reflev.f,REFLEV,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",False,src/math/reflev.rs,done -reiman.f,REIMAN,FUNCTION,True,"","",False,src/math/reiman.rs,done -resolv.f,RESOLV,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR|ARRAY1|icnrsp","NEWPOP|RTEFR1|RATES1|TAUFR1|ELCOR|ROSSTD|PRD|STEQEQ|CONOUT|OPAINI|TIMING|OPACF1",True,,pending -rhoeos.f,RHOEOS,FUNCTION,False,"BASICS|MODELQ","PRSENT",False,,pending -rhonen.f,RHONEN,SUBROUTINE,False,"BASICS|MODELQ","ELDENS",False,,pending -rhsgen.f,RHSGEN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|CUBCON","LEVGRP|RATMAT|MATINV|STATE|SABOLF|CONVEC|COMPT0",False,,pending -rossop.f,ROSSOP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","WNSTOR|MEANOPT|MEANOP|OPACF0|ELDENS|STEQEQ",False,,pending -rosstd.f,ROSSTD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR","",True,,pending -rte_sc.f,RTE_SC,SUBROUTINE,True,"BASICS","",False,src/math/rte_sc.rs,done -rteang.f,RTEANG,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|SURFEX|EXTINT","GAULEG",False,,pending -rtecf0.f,RTECF0,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|auxcbc|AUXRTE|OPTDPT","",False,src/math/rtecf0.rs,done -rtecf1.f,RTECF1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|SURFEX|comgfs|AUXRTE|EXTINT|OPTDPT","RTECF0|RTEFE2|RTESOL",True,,pending -rtecmc.f,RTECMC,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|AUXRTE|comgfs","RTECF0|MATINV|OPACF1",False,,pending -rtecmu.f,RTECMU,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|AUXRTE|OPTDPT","GAULEG|RTESOL|RTECF0|OPACF1",True,,pending -rtecom.f,RTECOM,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|AUXRTE|comgfs|OPTDPT","RTECF1|RTECF0|OPACF1",False,,pending -rtedf1.f,RTEDF1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|OPTDPT","",False,src/math/rtedf1.rs,done -rtedf2.f,RTEDF2,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR","",False,src/math/rtedf2.rs,done -rtefe2.f,RTEFE2,SUBROUTINE,True,"BASICS","",False,src/math/rtefe2.rs,done -rtefr1.f,RTEFR1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT","RTEDF2|RTESOL|MATINV|RTECF1|MINV3|RTEDF1",True,,pending -rteint.f,RTEINT,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT","MATINV|OPACF1",True,,pending -rtesol.f,RTESOL,SUBROUTINE,True,"BASICS","",False,src/math/rtesol.rs,done -russel.f,RUSSEL,SUBROUTINE,False,"BASICS|MODELQ|COMFH1","MPARTF",True,,pending -rybchn.f,RYBCHN,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|ALIPAR|ARRAY1|grdpra|rybpgs","ELDENS|PGSET",True,,pending -rybene.f,RYBENE,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ARRAY1|deridt|RYBMTX|CUBCON","CONVEC",False,,pending -rybheq.f,RYBHEQ,SUBROUTINE,False,"BASICS|MODELQ|grdpra|rybpgs","RTEFR1|ELCOR|PGSET|ELDENS|WNSTOR|STEQEQ|OPAINI|OPACF1",True,,pending -rybmat.f,RYBMAT,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ARRAY1|dsctva|RYBMTX","",False,src/math/rybmat.rs,done -rybsol.f,RYBSOL,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|ALIPAR|ARRAY1|ITERAT|imodlc|RYBMTX","OPACTR|OPACFD|RYBMAT|RYBCHN|LINEQS|RTEFR1|ALIFR1|ROSSTD|TRIDAG|STEQEQ",True,,pending -sabolf.f,SABOLF,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","PARTF",False,,pending -sbfch.f,SBFCH,FUNCTION,True,"","",False,src/math/sbfch.rs,done -sbfhe1.f,SBFHE1,FUNCTION,False,"BASICS|ATOMIC","QUIT",True,src/math/sbfhe1.rs,done -sbfhmi.f,SBFHMI,FUNCTION,True,"","",False,src/math/sbfhmi.rs,done -sbfhmi_old.f,SBFHMI_OLD,FUNCTION,True,"","",False,src/math/sbfhmi_old.rs,done -sbfoh.f,SBFOH,FUNCTION,True,"","",False,src/math/sbfoh.rs,done -setdrt.f,SETDRT,SUBROUTINE,False,"BASICS|MODELQ|RHODER","",False,src/math/setdrt.rs,done -settrm.f,SETTRM,SUBROUTINE,False,"tdedge|TABLTD|tdflag|THERM","PRSENT",True,,pending -sffhmi.f,SFFHMI,FUNCTION,True,"","",False,src/math/sffhmi.rs,done -sffhmi_add.f,SFFHMI_ADD,FUNCTION,True,"","",False,src/math/sffhmi_add.rs,done -sghe12.f,SGHE12,FUNCTION,True,"","",False,src/math/sghe12.rs,done -sgmer0.f,SGMER0,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/sgmer.rs,done -sgmer1.f,SGMER1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/sgmer.rs,done -sgmerd.f,SGMERD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/sgmer.rs,done -sigave.f,SIGAVE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","",True,,pending -sigk.f,SIGK,FUNCTION,False,"BASICS|ATOMIC","SPSIGK",False,,pending -sigmar.f,SIGMAR,FUNCTION,False,"BASICS","LAGUER",True,,pending -solve.f,SOLVE,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|ARRAY1|ALIPAR|CMATZD","MATGEN|PRCHAN|MATINV|WNSTOR|RHSGEN",True,,pending -solves.f,SOLVES,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|ARRAY1|ALIPAR|CMATZD|STOMAT","MATGEN|PRCHAN|MATINV|WNSTOR|RHSGEN",True,,pending -spsigk.f,SPSIGK,SUBROUTINE,True,"","CARBON",False,src/math/spsigk.rs,done -srtfrq.f,SRTFRQ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT|INDEXX",True,,pending -stark0.f,STARK0,SUBROUTINE,True,"","",False,src/math/stark0.rs,done -starka.f,STARKA,FUNCTION,False,"BASICS|MODELQ","",False,src/math/starka.rs,done -start.f,START,SUBROUTINE,False,"BASICS|hediff","",True,,pending -state.f,STATE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|PFSTDS|terden","OPFRAC|PARTF",True,,pending -steqeq.f,STEQEQ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|POPSTR|PPAPAR","LEVSOL|RATMAT|SABOLF|MOLEQ",False,,pending -switch.f,SWITCH,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",True,,pending -szirc.f,SZIRC,SUBROUTINE,True,"","EINT",False,src/math/szirc.rs,done -tabini.f,TABINI,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|intcff|abntab|eletab","",True,,pending -tabint.f,TABINT,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|intcff","",False,src/math/tabint.rs,done -taufr1.f,TAUFR1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT","",False,src/math/taufr1.rs,done -tdpini.f,TDPINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","GFREE0",False,src/math/tdpini.rs,done -temcor.f,TEMCOR,SUBROUTINE,False,"BASICS|MODELQ|ARRAY1|ALIPAR|CUBCON","WNSTOR|MEANOP|OPACF0|ELDENS|STEQEQ|CONVEC",True,,pending -temper.f,TEMPER,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|FACTRS|PRSAUX|FLXAUX","WNSTOR|MEANOPT|TLOCAL|MEANOP|OPACF0|ELDENS|STEQEQ",True,,pending -timing.f,TIMING,SUBROUTINE,False,"","",True,,pending -tiopf.f,TIOPF,SUBROUTINE,True,"","",False,src/math/tiopf.rs,done -tlocal.f,TLOCAL,SUBROUTINE,False,"BASICS|MODELQ|FACTRS|FLXAUX","QUARTC",False,,pending -tlusty.f,TLUSTY,UNKNOWN,False,"BASICS|ITERAT|ALIPAR","TIMING",True,,pending -topbas.f,TOPBAS,FUNCTION,False,"TOPB","",True,,pending -traini.f,TRAINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","",False,src/math/traini.rs,done -tridag.f,TRIDAG,SUBROUTINE,True,"","",False,src/math/tridag.rs,done -trmder.f,TRMDER,SUBROUTINE,False,"BASICS|terden|adiaba|derdif","ELDENS",False,,pending -trmdrt.f,TRMDRT,SUBROUTINE,False,"BASICS|CC|CONVOUT|tdedge|tdflag","PRSENT",False,,pending -ubeta.f,UBETA,FUNCTION,True,"","LAGRAN",False,src/math/ubeta.rs,done -vern16.f,VERN16,FUNCTION,True,"BASICS","",False,src/math/vern16.rs,done -vern18.f,VERN18,FUNCTION,True,"BASICS","",False,src/math/vern18.rs,done -vern20.f,VERN20,FUNCTION,True,"BASICS","",False,src/math/vern20.rs,done -vern26.f,VERN26,FUNCTION,True,"BASICS","",False,src/math/vern26.rs,done -verner.f,VERNER,FUNCTION,False,"BASICS|ATOMIC","QUIT",False,src/math/verner.rs,done -visini.f,VISINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",True,,pending -voigt.f,VOIGT,FUNCTION,True,"","",False,src/math/voigt.rs,done -voigte.f,VOIGTE,FUNCTION,True,"","",False,src/math/voigte.rs,done -wn.f,WN,FUNCTION,True,"BASICS","",False,src/math/wn.rs,done -wnstor.f,WNSTOR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/wnstor.rs,done -xenini.f,XENINI,SUBROUTINE,False,"BASICS|MODELQ","",True,,pending -xk2dop.f,XK2DOP,FUNCTION,True,"","",False,src/math/xk2dop.rs,done -yint.f,YINT,FUNCTION,True,"","",False,src/math/interpolate.rs,done -ylintp.f,YLINTP,FUNCTION,True,"","",False,src/math/ylintp.rs,done -zmrho.f,ZMRHO,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/zmrho.rs,done diff --git a/MEMORY/plan.md b/MEMORY/plan.md deleted file mode 100644 index 250ff36..0000000 --- a/MEMORY/plan.md +++ /dev/null @@ -1,77 +0,0 @@ -# TLUSTY/SYNSPEC 重构计划 - -> **实时追踪见 [FORTRAN_TRACKING.md](../../FORTRAN_TRACKING.md)** -> 本文档提供重构优先级和阶段划分参考。 - -## 优先级原则 - -1. **纯函数优先** - 无 COMMON/I/O 依赖,独立测试 -2. **依赖少的优先** - 只依赖 BASICS 的函数 -3. **小型函数优先** - 行数少,风险低 -4. **核心功能优先** - 辐射转移、不透明度 - -## 阶段划分 - -### 阶段 1: 纯函数 (is_pure=True) - -已完成大部分,剩余查看: -```bash -grep "True.*pending" fortran_analysis.csv -``` - -### 阶段 2: 简单 COMMON 依赖 - -只依赖 BASICS 或已有状态结构体的函数。 - -### 阶段 3: 复杂 COMMON 依赖 - -依赖多个 COMMON 块的函数。 - -### 阶段 4: I/O 依赖 (117个) - -最后处理,可能需要设计 I/O 抽象层。 - -## 核心函数分类 - -### 辐射转移 (RTE) -- `bre.f`, `brez.f` - 基本辐射转移 -- `brte.f`, `brtez.f` - 扩展辐射转移 -- `rte_sc.f`, `rtedf*.f` - 深度/角度积分 - -### 不透明度 (Opacity) -- `opacfl.f` - 核心不透明度计算 -- `opctab.f` - 不透明度表插值 -- `opadd*.f` - 不透明度叠加 - -### 统计平衡 (Statistical Equilibrium) -- `bpop*.f` - 束缚态占据数 -- `levsol.f` - 能级求解 ✅ -- `ratmat.f` - 速率矩阵 - -### ALI 求解器 -- `alifr1.f`, `alifr3.f`, `alifr6.f` - ALI 迭代 - -## 推荐执行顺序 - -``` -1. 完成剩余纯函数 -2. 简单 COMMON 函数 (setdrt, cubic, dmder ✅) -3. 辐射转移核心 -4. 不透明度核心 -5. 统计平衡核心 -6. I/O 封装 -7. 主程序集成 -``` - -## 查询命令 - -```bash -# 未完成的纯函数 -grep "True.*pending" fortran_analysis.csv - -# 只依赖 BASICS 的待处理函数 -grep "BASICS.*pending" fortran_analysis.csv | grep -v "ATOMIC\|MODELQ" - -# 按行数排序 -cd tlusty/extracted && wc -l *.f | sort -n | head -30 -``` diff --git a/MEMORY/refactoring_notes.md b/MEMORY/refactoring_notes.md deleted file mode 100644 index deff244..0000000 --- a/MEMORY/refactoring_notes.md +++ /dev/null @@ -1,670 +0,0 @@ -# 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. 隐式的外部函数引用 diff --git a/docs/REFACTORING_PLAN.md b/docs/REFACTORING_PLAN.md deleted file mode 100644 index 437cb5e..0000000 --- a/docs/REFACTORING_PLAN.md +++ /dev/null @@ -1,283 +0,0 @@ -# TLUSTY/SYNSPEC Rust 重构计划 - -## 概述 - -**目标**: 将 TLUSTY/SYNSPEC Fortran 代码渐进式重构为 Rust - -**策略**: 从 `tlusty/extracted/` 中无 COMMON 依赖的纯函数开始,一个文件一个文件地重构 - -**代码规模**: -- TLUSTY: 304 个单元,195 个纯函数 -- SYNSPEC: 168 个单元,93 个纯函数 - ---- - -## 1. 重构原则 - -1. **渐进式**: 每次只重构一个文件,保持系统可用 -2. **测试驱动**: 每个重构的函数必须有测试验证 -3. **精度保证**: 与 Fortran 输出对比,相对误差 < 1e-10 -4. **文档先行**: 记录每个函数的算法和边界条件 - ---- - -## 2. 源文件位置 - -``` -tlusty/extracted/ # TLUSTY 拆分后的文件 -├── expo.f # 纯函数示例 -├── yint.f -├── tridag.f -├── ... -├── _PURE_UNITS.txt # 无 COMMON 依赖的函数列表 -├── _COMMON_ANALYSIS.txt # COMMON 依赖分析 -└── _SUMMARY.txt # 提取摘要 - -synspec/extracted/ # SYNSPEC 拆分后的文件 -└── ... -``` - ---- - -## 3. 推荐重构顺序 - -按文件大小从小到大排序(简单优先): - -```bash -# 查看最小文件 -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 源码 - -```bash -# 读取源文件 -cat tlusty/extracted/expo.f -``` - -记录以下信息: -- 函数名/子程序名 -- 输入参数及其类型 -- 返回值 -- 算法逻辑 -- 边界条件 - -### Step 2: 创建 Rust 项目结构 (首次执行) - -```bash -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 实现 - -```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 - -```rust -// src/lib.rs -pub mod math; - -// 已完成的重构 -pub mod expo; -``` - -### Step 5: 运行测试 - -```bash -cargo test expo -``` - -### Step 6: 记录进度 - -```bash -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` | -| `A(1)` | `a[0]` | - -### 控制结构 - -```fortran -IF (X .LT. 0) THEN - Y = -X -ELSE - Y = X -END IF -``` - -```rust -let y = if x < 0.0 { -x } else { x }; -``` - ---- - -## 6. 测试规范 - -### 单元测试 - -每个重构的函数必须有: -1. 正常值测试 -2. 边界值测试 -3. 特殊情况测试 - -### 回归测试 - -对于复杂函数,用 Fortran 生成参考数据: - -```bash -# 创建 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. 下一步行动 - -**新会话启动后**: - -1. 读取本文档: `cat REFACTORING_PLAN.md` -2. 查看进度: `cat REFACTORING_PROGRESS.txt` -3. 选择下一个文件(从未完成的最小文件开始) -4. 按照流程执行重构 - -**第一个文件**: `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 不在调用依赖中 \ No newline at end of file diff --git a/docs/sp_fortran.md b/docs/sp_fortran.md deleted file mode 100644 index bab0380..0000000 --- a/docs/sp_fortran.md +++ /dev/null @@ -1,403 +0,0 @@ - ---- - -## 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` -- 拆分编译程序与原始程序输出完全相同,功能验证通过 diff --git a/extract_fortran.py b/extract_fortran.py deleted file mode 100644 index ce79a25..0000000 --- a/extract_fortran.py +++ /dev/null @@ -1,302 +0,0 @@ -#!/usr/bin/env python3 -""" -提取 synspec54.f 中的各个子程序/函数到独立文件 -""" -import re -import os -import sys -from pathlib import Path - -def extract_units(source_file, output_dir): - """提取 Fortran 程序单元到独立文件""" - - with open(source_file, 'r') as f: - content = f.read() - lines = content.split('\n') - - # 创建输出目录 - os.makedirs(output_dir, exist_ok=True) - - # 匹配程序单元开始的正则表达式 - # 注意: BLOCK DATA 和 PROGRAM 可以是无名的 - # 使用 \s* 允许名称前没有空格(无名情况) - unit_pattern = re.compile( - r'^\s*(' - r'SUBROUTINE\s+(\w+)|' - r'FUNCTION\s+(\w+)|' - r'PROGRAM\s*(\w*)|' - r'BLOCK\s+DATA\s*(\w*)' - r')', - re.IGNORECASE - ) - - # 找到所有单元的起始位置 - units = [] - for i, line in enumerate(lines): - match = unit_pattern.match(line) - if match: - groups = match.groups() - # groups: (整体匹配, SUBROUTINE名, FUNCTION名, PROGRAM名, BLOCK DATA名) - - if groups[1]: # SUBROUTINE - name, unit_type = groups[1], 'SUBROUTINE' - elif groups[2]: # FUNCTION - name, unit_type = groups[2], 'FUNCTION' - elif groups[3]: # PROGRAM (非空) - name, unit_type = groups[3], 'PROGRAM' - elif groups[3] is not None: # PROGRAM (空字符串,无名) - name, unit_type = None, 'PROGRAM' - elif groups[4]: # BLOCK DATA (非空) - name, unit_type = groups[4], 'BLOCK DATA' - elif groups[4] is not None: # BLOCK DATA (空字符串,无名) - name, unit_type = None, 'BLOCK DATA' - else: - name, unit_type = None, 'UNKNOWN' - - # 处理无名单元 - if not name: - name = f"_UNNAMED_{unit_type.replace(' ', '_')}_" - - units.append((i, name.upper(), unit_type)) - - print(f"找到 {len(units)} 个程序单元") - - # 提取每个单元 - extracted = [] - for idx, (start_line, name, unit_type) in enumerate(units): - # 确定结束位置 - if idx + 1 < len(units): - end_line = units[idx + 1][0] - else: - end_line = len(lines) - - # 提取单元内容 - unit_lines = lines[start_line:end_line] - - # 查找实际的 END 语句 - actual_end = end_line - for i in range(len(unit_lines) - 1, -1, -1): - if re.match(r'^\s*END\s*$', unit_lines[i], re.IGNORECASE): - actual_end = start_line + i + 1 - break - - unit_content = '\n'.join(lines[start_line:actual_end]) - - # 写入文件 - filename = f"{name.lower()}.f" - filepath = os.path.join(output_dir, filename) - - with open(filepath, 'w') as f: - f.write(unit_content) - if not unit_content.endswith('\n'): - f.write('\n') - - extracted.append({ - 'name': name, - 'type': unit_type, - 'file': filename, - 'start': start_line + 1, - 'end': actual_end, - 'lines': actual_end - start_line - }) - print(f" 提取: {name} ({unit_type}) -> {filename} ({actual_end - start_line} 行)") - - # 生成摘要文件 - summary_path = os.path.join(output_dir, '_SUMMARY.txt') - with open(summary_path, 'w') as f: - f.write(f"SYNSPEC54.F 提取摘要\n") - f.write(f"{'='*60}\n\n") - f.write(f"源文件: {source_file}\n") - f.write(f"总单元数: {len(extracted)}\n") - f.write(f"总行数: {len(lines)}\n\n") - - f.write(f"{'名称':<20} {'类型':<12} {'文件':<20} {'行数':>8}\n") - f.write(f"{'-'*60}\n") - for unit in extracted: - f.write(f"{unit['name']:<20} {unit['type']:<12} {unit['file']:<20} {unit['lines']:>8}\n") - - # 按类型统计 - types = {} - for unit in extracted: - types[unit['type']] = types.get(unit['type'], 0) + 1 - f.write(f"\n按类型统计:\n") - for t, c in types.items(): - f.write(f" {t}: {c}\n") - - print(f"\n摘要已保存到: {summary_path}") - return extracted - -def analyze_commons(output_dir): - """分析 COMMON 块依赖""" - # 命名COMMON块: COMMON /NAME/ ... - named_common_pattern = re.compile(r'COMMON\s*/\s*(\w+)\s*/', re.IGNORECASE) - # 空白COMMON块: COMMON varname (不带斜杠) - blank_common_pattern = re.compile(r'^\s*COMMON\s+[A-Z]', re.IGNORECASE | re.MULTILINE) - include_pattern = re.compile(r'INCLUDE\s*[\'"]([^\'"]+)[\'"]', re.IGNORECASE) - - commons = {} - includes = {} - - for filepath in Path(output_dir).glob('*.f'): - if filepath.name.startswith('_'): - continue - - with open(filepath, 'r') as f: - content = f.read() - - unit_name = filepath.stem.upper() - found_commons = named_common_pattern.findall(content) - found_includes = include_pattern.findall(content) - - # 检查空白COMMON块 - if blank_common_pattern.search(content): - found_commons.append('BLANK') # 添加空白COMMON块标识 - - if found_commons: - commons[unit_name] = list(set(found_commons)) - if found_includes: - includes[unit_name] = list(set(found_includes)) - - # 写入 COMMON 分析 - common_path = os.path.join(output_dir, '_COMMON_ANALYSIS.txt') - with open(common_path, 'w') as f: - f.write("COMMON 块依赖分析\n") - f.write(f"{'='*60}\n\n") - - f.write("有 COMMON 依赖的单元:\n") - f.write(f"{'-'*60}\n") - for unit, common_list in sorted(commons.items()): - f.write(f"{unit}: {', '.join(common_list)}\n") - - f.write(f"\n共 {len(commons)} 个单元有 COMMON 依赖\n") - f.write(f"共 {len([u for u in commons.values()])} 个 COMMON 块被引用\n") - - # 找出所有唯一的 COMMON 块 - all_commons = set() - for c in commons.values(): - all_commons.update(c) - f.write(f"\n唯一的 COMMON 块: {sorted(all_commons)}\n") - - f.write(f"\n\nINCLUDE 文件依赖:\n") - f.write(f"{'-'*60}\n") - for unit, inc_list in sorted(includes.items()): - f.write(f"{unit}: {', '.join(inc_list)}\n") - - print(f"COMMON 分析已保存到: {common_path}") - - # 返回无 COMMON 依赖的纯函数 - pure_units = [] - for filepath in Path(output_dir).glob('*.f'): - if filepath.name.startswith('_'): - continue - unit_name = filepath.stem.upper() - if unit_name not in commons: - pure_units.append(unit_name) - - return pure_units, commons, includes - -def generate_makefile(output_dir, extracted, source_file): - """生成 Makefile 用于编译所有提取的文件""" - - # 根据源文件名确定程序名称 - source_name = os.path.basename(source_file).lower() - if 'tlusty' in source_name: - prog_name = 'tlusty' - elif 'synspec' in source_name: - prog_name = 'synspec' - else: - prog_name = os.path.splitext(os.path.basename(source_file))[0].lower() - - makefile_path = os.path.join(output_dir, 'Makefile') - with open(makefile_path, 'w') as f: - f.write(f"# Makefile for {prog_name.upper()} extracted modules\n") - f.write("# 使用大内存模型支持大型 COMMON 数组\n\n") - - f.write("FC = gfortran\n") - f.write("FFLAGS = -O3 -fno-automatic -mcmodel=large\n\n") - - f.write("# 编译输出目录\n") - f.write("BUILD_DIR = build\n\n") - - f.write("# 目标可执行文件\n") - f.write(f"MAIN = $(BUILD_DIR)/{prog_name}_extracted\n\n") - - f.write("# 所有 .f 源文件\n") - f.write("SRCS = $(wildcard *.f)\n\n") - - f.write("# 目标文件(放在build目录)\n") - f.write("OBJS = $(patsubst %.f,$(BUILD_DIR)/%.o,$(notdir $(SRCS)))\n\n") - - f.write("# 默认目标\n") - f.write("all: $(BUILD_DIR) $(MAIN)\n") - f.write("\t@echo \"==========================================\"\n") - f.write("\t@echo \"编译成功: $(MAIN)\"\n") - f.write("\t@echo \"==========================================\"\n\n") - - f.write("# 创建build目录\n") - f.write("$(BUILD_DIR):\n") - f.write("\tmkdir -p $(BUILD_DIR)\n\n") - - f.write("# 链接所有目标文件\n") - f.write("$(MAIN): $(OBJS)\n") - f.write("\t$(FC) $(FFLAGS) -o $@ $(OBJS)\n\n") - - f.write("# 编译规则\n") - f.write("$(BUILD_DIR)/%.o: %.f | $(BUILD_DIR)\n") - f.write("\t$(FC) $(FFLAGS) -c $< -o $@\n\n") - - f.write("# 清理\n") - f.write("clean:\n") - f.write("\trm -rf $(BUILD_DIR)\n\n") - - f.write("# 只编译不链接(检查语法)\n") - f.write("compile-only: $(OBJS)\n") - f.write("\t@echo \"所有文件编译完成(未链接)\"\n\n") - - f.write("# 统计信息\n") - f.write("stats:\n") - f.write("\t@echo \"=== 编译统计 ===\"\n") - f.write("\t@echo \"源文件数: $(words $(SRCS))\"\n") - f.write("\t@echo \"目标文件数: $(words $(OBJS))\"\n") - f.write("\t@wc -l *.f | tail -1\n\n") - - f.write(".PHONY: all clean compile-only stats\n") - - print(f"Makefile 已生成: {makefile_path}") - -def main(): - if len(sys.argv) < 2: - source_file = "/home/fmq/program/tlusty/tl208-s54/rust/synspec/synspec54.f" - output_dir = "/home/fmq/program/tlusty/tl208-s54/rust/synspec/extracted" - else: - source_file = sys.argv[1] - output_dir = sys.argv[2] if len(sys.argv) > 2 else "extracted" - - print(f"源文件: {source_file}") - print(f"输出目录: {output_dir}\n") - - # 提取单元 - extracted = extract_units(source_file, output_dir) - - # 分析 COMMON 依赖 - print("\n分析 COMMON 依赖...") - pure_units, commons, includes = analyze_commons(output_dir) - - print(f"\n无 COMMON 依赖的纯函数/子程序: {len(pure_units)} 个") - for u in sorted(pure_units): - print(f" {u}") - - # 生成 Makefile - generate_makefile(output_dir, extracted, source_file) - - # 保存纯函数列表 - pure_path = os.path.join(output_dir, '_PURE_UNITS.txt') - with open(pure_path, 'w') as f: - f.write("无 COMMON 依赖的纯函数/子程序\n") - f.write(f"{'='*40}\n\n") - for u in sorted(pure_units): - f.write(f"{u}\n") - print(f"\n纯函数列表已保存到: {pure_path}") - -if __name__ == '__main__': - main() diff --git a/fortran_analysis.csv b/fortran_analysis.csv deleted file mode 100644 index c2141c0..0000000 --- a/fortran_analysis.csv +++ /dev/null @@ -1,305 +0,0 @@ -fortran_file,unit_name,unit_type,is_pure,common_deps,call_deps,has_io,rust_module,status -_unnamed_block_data_.f,C,BLOCK DATA,False,"BASICS|ATOMIC","",False,,pending -accel2.f,ACCEL2,SUBROUTINE,False,"BASICS|ITERAT|MODELQ","",True,,pending -accelp.f,ACCELP,SUBROUTINE,False,"BASICS|MODELQ|ITERAT|POPULS","",True,,pending -alifr1.f,ALIFR1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","ALIFR3",False,,pending -alifr3.f,ALIFR3,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","",False,src/math/alifr3.rs,done -alifr6.f,ALIFR6,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","",False,src/math/alifr6.rs,done -alifrk.f,ALIFRK,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","",False,src/math/alifrk.rs,done -alisk1.f,ALISK1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","ALIFRK|ROSSTD|OPACF1|RTEFR1",True,,pending -alisk2.f,ALISK2,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","ALIFRK|ROSSTD|OPACF1|RTEFR1",True,,pending -alist1.f,ALIST1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT","OPACFD|ALIFR1|ROSSTD|RTEFR1",True,,pending -alist2.f,ALIST2,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","RTEFR1|ROSSTD|ALIFR1|QUIT|OPACFD",True,,pending -allard.f,ALLARD,SUBROUTINE,False,"BASICS|calphatd|callarda|callardc|callardg|callardb|quasun","ALLARDT",True,,pending -allardt.f,ALLARDT,SUBROUTINE,False,"BASICS|calphatd","",False,src/math/allardt.rs,done -angset.f,ANGSET,SUBROUTINE,True,"BASICS","GAULEG",False,src/math/angset.rs,done -betah.f,BETAH,FUNCTION,True,"","BETAH",False,src/math/betah.rs,done -bhe.f,BHE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","",False,src/math/bhe.rs,done -bhed.f,BHED,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|CMATZD|SURFEX","",False,src/math/bhe.rs,done -bhez.f,BHEZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|SURFEX","",False,src/math/bhe.rs,done -bkhsgo.f,BKHSGO,SUBROUTINE,True,"","",False,src/math/bkhsgo.rs,done -bpop.f,BPOP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR|ITERAT","MATINV|BPOPT|BPOPF|LEVSOL|RATMAT|BPOPC|BPOPE|LEVGRP",False,,pending -bpopc.f,BPOPC,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR|ADCHAR","STATE",False,,pending -bpope.f,BPOPE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT|ARRAY1","SGMER1|DWNFR1",False,,pending -bpopf.f,BPOPF,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR","",False,src/math/bpopf.rs,done -bpopt.f,BPOPT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR","COLIS",False,,pending -bre.f,BRE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","COMPT0",False,,pending -brez.f,BREZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","COMPT0",False,,pending -brte.f,BRTE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1","COMPT0",False,,pending -brtez.f,BRTEZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1","COMPT0",False,,pending -butler.f,BUTLER,SUBROUTINE,True,"","",False,src/math/butler.rs,done -carbon.f,CARBON,SUBROUTINE,True,"","",False,src/math/carbon.rs,done -ceh12.f,CEH12,FUNCTION,True,"","CEH12",False,src/math/ceh12.rs,done -change.f,CHANGE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","STEQEQ|READBF",True,,pending -chckse.f,CHCKSE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","SABOLF",True,,pending -chctab.f,CHCTAB,SUBROUTINE,False,"BASICS|MODELQ|abntab","",True,,pending -cheav.f,CHEAV,FUNCTION,False,"BASICS|ATOMIC","CHEAV|QUIT",True,,pending -cheavj.f,CHEAVJ,FUNCTION,False,"BASICS|ATOMIC","QUIT|CHEAVJ",True,,pending -cia_h2h.f,CIA_H2H,SUBROUTINE,False,"","LOCATE|IF",True,,pending -cia_h2h2.f,CIA_H2H2,SUBROUTINE,False,"","LOCATE|IF",True,,pending -cia_h2he.f,CIA_H2HE,SUBROUTINE,False,"","LOCATE|IF",True,,pending -cia_hhe.f,CIA_HHE,SUBROUTINE,False,"","LOCATE|IF",True,,pending -cion.f,CION,FUNCTION,True,"","CION",False,src/math/cion.rs,done -ckoest.f,CKOEST,FUNCTION,True,"BASICS","CKOEST",False,src/math/ckoest.rs,done -colh.f,COLH,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","CSPEC|BUTLER|IRC",False,,pending -colhe.f,COLHE,SUBROUTINE,False,"BASICS|ATOMIC","COLLHE|CSPEC|IRC",False,,pending -colis.f,COLIS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|CTRTEMP","CSPEC|COLH|COLHE|IRC",False,,pending -collhe.f,COLLHE,SUBROUTINE,True,"","",False,src/math/collhe.rs,done -column.f,COLUMN,SUBROUTINE,False,"BASICS|MODELQ|relcor","",True,,pending -compt0.f,COMPT0,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|auxcbc","",False,src/math/compt0.rs,done -comset.f,COMSET,SUBROUTINE,False,"BASICS|MODELQ|comgfs|auxcbc","",False,src/math/comset.rs,done -concor.f,CONCOR,SUBROUTINE,False,"BASICS|MODELQ","CONOUT",True,,pending -conout.f,CONOUT,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|CUBCON","MEANOP|MEANOPT|CONVEC|OPACF0",True,,pending -conref.f,CONREF,SUBROUTINE,False,"BASICS|MODELQ|ARRAY1|imucnn|CUBCON","CONVC1|WNSTOR|STEQEQ|ELDENS|CONVEC|CONOUT",True,,pending -contmd.f,CONTMD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|PRSAUX|CUBCON","MEANOP|WNSTOR|STEQEQ|OPACF0|CONVEC|CONOUT|CUBIC",True,,pending -contmp.f,CONTMP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ichndm|CUBCON","MEANOP|WNSTOR|STEQEQ|ELDENS|OPACF0|CONVEC|MEANOPT|CONOUT|CUBIC",True,,pending -convc1.f,CONVC1,SUBROUTINE,False,"BASICS|CUBCON","TRMDER|TRMDRT",False,,pending -convec.f,CONVEC,SUBROUTINE,False,"BASICS|CUBCON","TRMDER|TRMDRT",False,,pending -coolrt.f,COOLRT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT|COOLCO","OPACFA|RTEFR1",True,,pending -corrwm.f,CORRWM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT",True,,pending -cross.f,CROSS,FUNCTION,False,"BASICS|ATOMIC|MODELQ","CROSS",False,src/math/cross.rs,done -crossd.f,CROSSD,FUNCTION,False,"BASICS|ATOMIC|MODELQ","CROSSD",False,src/math/cross.rs,done -cspec.f,CSPEC,SUBROUTINE,False,"BASICS|ATOMIC","QUIT",False,,pending -ctdata.f,CTDATA,BLOCK DATA,False,"CTIon|CTRecomb","",False,src/math/ctdata.rs,done -cubic.f,CUBIC,SUBROUTINE,False,"BASICS|CUBCON","",False,src/math/cubic.rs,done -dielrc.f,DIELRC,SUBROUTINE,True,"","",False,src/math/dielrc.rs,done -dietot.f,DIETOT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","DIELRC",True,,pending -divstr.f,DIVSTR,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/divstr.rs,done -dmder.f,DMDER,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|DEPTDR","",False,src/math/dmder.rs,done -dmeval.f,DMEVAL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ARRAY1","",True,,pending -dopgam.f,DOPGAM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","GAMSP",False,src/math/dopgam.rs,done -dwnfr.f,DWNFR,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/dwnfr.rs,done -dwnfr0.f,DWNFR0,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/dwnfr0.rs,done -dwnfr1.f,DWNFR1,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/dwnfr1.rs,done -eint.f,EINT,SUBROUTINE,True,"","EXPINX",False,src/math/expint.rs,done -elcor.f,ELCOR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ADCHAR","WNSTOR|STEQEQ|STATE|MOLEQ",True,,pending -eldenc.f,ELDENC,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|eletab|hmolab|eospar","RHONEN|STATE|MOLEQ",True,,pending -eldens.f,ELDENS,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|terden|eospar","ENTENE|STATE|MPARTF|LINEQS|MOLEQ",True,,pending -emat.f,EMAT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","",False,src/math/emat.rs,done -entene.f,ENTENE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","MPARTF",False,,pending -erfcin.f,ERFCIN,FUNCTION,True,"","ERFCIN",False,src/math/erfcx.rs,done -erfcx.f,ERFCX,FUNCTION,True,"","ERFCX",False,src/math/erfcx.rs,done -expint.f,EXPINT,FUNCTION,True,"","EXPINT",False,src/math/expint.rs,done -expinx.f,EXPINX,SUBROUTINE,True,"","",False,src/math/expint.rs,done -expo.f,EXPO,FUNCTION,True,"","EXPO",False,src/math/expo.rs,done -ffcros.f,FFCROS,FUNCTION,True,"","FFCROS",False,src/math/ffcros.rs,done -gami.f,GAMI,FUNCTION,True,"","GAMI",False,src/math/gami.rs,done -gamsp.f,GAMSP,SUBROUTINE,True,"BASICS","",False,src/math/gamsp.rs,done -gauleg.f,GAULEG,SUBROUTINE,True,"","",False,src/math/gauleg.rs,done -gaunt.f,GAUNT,FUNCTION,True,"","GAUNT",False,src/math/gaunt.rs,done -getlal.f,GETLAL,SUBROUTINE,False,"BASICS|callardc|callarda|calphatd|callardg|callardb|quasun","",True,,pending -getwrd.f,GETWRD,SUBROUTINE,True,"","",False,src/math/getwrd.rs,done -gfree0.f,GFREE0,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/gfree.rs,done -gfree1.f,GFREE1,FUNCTION,False,"BASICS|MODELQ","GFREE1",False,src/math/gfree.rs,done -gfreed.f,GFREED,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/gfree.rs,done -ghydop.f,GHYDOP,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|intcfg","",False,src/math/ghydop.rs,done -gntk.f,GNTK,FUNCTION,True,"","GNTK",False,src/math/gntk.rs,done -gomini.f,GOMINI,SUBROUTINE,False,"BASICS|MODELQ|intcfg","",True,,pending -grcor.f,GRCOR,SUBROUTINE,True,"","",False,src/math/grcor.rs,done -greyd.f,GREYD,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|ALIPAR","WNSTOR|RHONEN|STEQEQ|OPACF0|MEANOP",True,,pending -gridp.f,GRIDP,SUBROUTINE,True,"BASICS","",False,src/math/gridp.rs,done -h2minus.f,H2MINUS,SUBROUTINE,False,"BASICS","LOCATE",True,,pending -hction.f,HCTION,FUNCTION,False,"CTRTEMP|CTIon","HCTION",False,src/math/ctdata.rs,done -hctrecom.f,HCTRECOM,FUNCTION,False,"CTRTEMP|CTRecomb","HCTRECOM",False,src/math/ctdata.rs,done -hedif.f,HEDIF,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|hediff","",True,,pending -hephot.f,HEPHOT,FUNCTION,True,"","HEPHOT",False,src/math/hephot.rs,done -hesol6.f,HESOL6,SUBROUTINE,False,"BASICS|MODELQ|PRSAUX","MATINV",False,,pending -hesolv.f,HESOLV,SUBROUTINE,False,"BASICS|MODELQ|PRSAUX","MATINV|RHONEN|STEQEQ|WNSTOR",True,,pending -hidalg.f,HIDALG,FUNCTION,True,"","HIDALG",False,src/math/hidalg.rs,done -ijali2.f,IJALI2,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","QUIT",True,,pending -ijalis.f,IJALIS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",True,,pending -incldy.f,INCLDY,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","LEVSOL|RATMAT|WNSTOR|QUIT|SABOLF",True,,pending -indexx.f,INDEXX,SUBROUTINE,True,"","",False,src/math/indexx.rs,done -inicom.f,INICOM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|comgfs","",False,src/math/inicom.rs,done -inifrc.f,INIFRC,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ijflar","INDEXX",True,,pending -inifrs.f,INIFRS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","INDEXX|QUIT",True,,pending -inifrt.f,INIFRT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ijflar","INDEXX",True,,pending -inilam.f,INILAM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR","OPACF1|RTEFR1|COLIS|OPAINI|WNSTOR|STEQEQ|SABOLF|ELCOR|RATES1",False,,pending -initia.f,INITIA,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|STRPAR|INUNIT|freqcl","RDATA|RDATAX|OPADD0|STATE|DOPGAM|ODFHYS|INTERP|LINSET|NSTPAR|QUIT|LINSPL|INIFRC|READBF",True,,pending -inkul.f,INKUL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|COLKUR|LINED","",True,,pending -inpdis.f,INPDIS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|relcor","GRCOR",True,,pending -inpmod.f,INPMOD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|eospar","LEVSOL|RATMAT|WNSTOR|QUIT|SABOLF|MOLEQ|KURUCZ|INCLDY",True,,pending -interp.f,INTERP,SUBROUTINE,True,"BASICS","",False,src/math/interp.rs,done -inthyd.f,INTHYD,SUBROUTINE,False,"BASICS|MODELQ","DIVSTR",False,src/math/inthyd.rs,done -intlem.f,INTLEM,SUBROUTINE,False,"BASICS|MODELQ","INTHYD",False,src/math/intlem.rs,done -intxen.f,INTXEN,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/intxen.rs,done -irc.f,IRC,SUBROUTINE,True,"","EXPINX|SZIRC",False,src/math/irc.rs,done -iroset.f,IROSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|LINED","LEVCD|QUIT|INKUL",True,,pending -kurucz.f,KURUCZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|temlim","LEVSOL|RATMAT|WNSTOR|RHONEN|QUIT|SABOLF|MOLEQ",True,,pending -lagran.f,LAGRAN,SUBROUTINE,True,"","",False,src/math/interpolate.rs,done -laguer.f,LAGUER,SUBROUTINE,False,"","",True,src/math/laguer.rs,done -lemini.f,LEMINI,SUBROUTINE,False,"BASICS|MODELQ","",True,,pending -levcd.f,LEVCD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|COLKUR","INDEXX|QUIT",True,,pending -levgrp.f,LEVGRP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",False,src/math/levgrp.rs,done -levset.f,LEVSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT",False,,pending -levsol.f,LEVSOL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","LINEQS",False,src/math/levsol.rs,done -lineqs.f,LINEQS,SUBROUTINE,True,"BASICS","",False,src/math/lineqs.rs,done -linpro.f,LINPRO,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|quasun","STARK0|DOPGAM|INTLEM|DIVSTR|INTXEN",False,,pending -linsel.f,LINSEL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","OPAINI|QUIT|OPACF1|RTEFR1",True,,pending -linset.f,LINSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","IJALIS|STARK0|QUIT|DIVSTR",True,,pending -linspl.f,LINSPL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/linspl.rs,done -locate.f,LOCATE,SUBROUTINE,True,"","",False,src/math/locate.rs,done -ltegr.f,LTEGR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","INTERP|WNSTOR|STEQEQ|QUIT|ROSSOP|CONOUT",True,,pending -ltegrd.f,LTEGRD,SUBROUTINE,False,"BASICS|MODELQ|FACTRS|CUBCON|TOTJHK|FLXAUX|PRSAUX","TEMPER|INTERP|ZMRHO|WNSTOR|STEQEQ|ELDENS|QUIT|CONOUT",True,,pending -lucy.f,LUCY,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ITERAT|ALIPAR|ARRAY1","OPACFL|RTEFR1|COLIS|WNSTOR|STEQEQ|SABOLF|ELCOR|OPAINI",True,,pending -lymlin.f,LYMLIN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","STARK0|DIVSTR",True,,pending -matcon.f,MATCON,SUBROUTINE,False,"BASICS|MODELQ|ARRAY1|CUBCON","CONVEC",False,,pending -matgen.f,MATGEN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","BHED|BRTE|BHEZ|BREZ|MATCON|BRTEZ|EMAT|BHE|SABOLF|BPOP|BRE",False,,pending -matinv.f,MATINV,SUBROUTINE,True,"BASICS","",False,src/math/matinv.rs,done -meanop.f,MEANOP,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC","",False,src/math/meanop.rs,done -meanopt.f,MEANOPT,SUBROUTINE,False,"BASICS|MODELQ","OPCTAB",False,,pending -minv3.f,MINV3,SUBROUTINE,True,"","",False,src/math/minv3.rs,done -moleq.f,MOLEQ,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|COMFH1|entrop|moldat|eospar|adchar|ioniz2|terden|hmolab","RUSSEL|MPARTF",True,,pending -mpartf.f,MPARTF,SUBROUTINE,False,"moldat","",True,,pending -newdm.f,NEWDM,SUBROUTINE,False,"BASICS|MODELQ|FACTRS|PRSAUX|FLXAUX","INTERP|TEMPER",True,,pending -newdmt.f,NEWDMT,SUBROUTINE,False,"BASICS|MODELQ|FACTRS|PRSAUX|FLXAUX","GRIDP|INTERP|TEMPER",True,,pending -newpop.f,NEWPOP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",True,,pending -nstout.f,NSTOUT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR","QUIT",True,,pending -nstpar.f,NSTPAR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|imucnn|temlim|adiaba|ifpzpa|derdif|ichndm|ipricr|deridt|hediff|FLXAUX|irwint|moldat|quasun|freqcl|icnrsp","QUIT|GETWRD",True,,pending -odf1.f,ODF1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","DWNFR|ODFHST|DIVSTR",True,,pending -odffr.f,ODFFR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","QUIT",False,,pending -odfhst.f,ODFHST,SUBROUTINE,False,"BASICS|MODELQ|ODFPAR","",False,src/math/odfhst.rs,done -odfhyd.f,ODFHYD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","INDEXX|ODFHST|DIVSTR",False,,pending -odfhys.f,ODFHYS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","IJALIS|STARK0|ODFFR",False,,pending -odfmer.f,ODFMER,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","ODFHYD",False,,pending -odfset.f,ODFSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|STFCR","IJALIS|QUIT",True,,pending -opacf0.f,OPACF0,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|hmolab","GFREE0|DWNFR1|DWNFR0|WNSTOR|LINPRO|SABOLF|SGMER1|OPADD|OPACT1",False,,pending -opacf1.f,OPACF1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ipricr|hmolab","QUASIM|GHYDOP|DWNFR1|LYMLIN|SGMER1|OPADD|PRD|OPACT1",True,,pending -opacfa.f,OPACFA,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|COOLCO","SGMER1|OPADD|PRD|DWNFR1",False,,pending -opacfd.f,OPACFD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT|dsctva|hmolab|rhoder","OPACTD|QUASIM|DWNFR1|LYMLIN|OPCTAB|SGMER1|OPADD|PRD|GFREED",True,,pending -opacfl.f,OPACFL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","SGMER1|OPADD|DWNFR1",False,,pending -opact1.f,OPACT1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|hmolab","OPCTAB",False,,pending -opactd.f,OPACTD,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ARRAY1|ITERAT|dsctva|hmolab|rhoder","OPCTAB",False,,pending -opactr.f,OPACTR,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ATOMIC|dsctva|hmolab|grdpra","RATMAL|OPACF1|LEVSOL|WNSTOR|STEQEQ|ELDENS|SABOLF|OPAINI|PGSET",False,,pending -opadd.f,OPADD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|eospar","CIA_H2H|CIA_H2HE|CIA_HHE|H2MINUS|CIA_H2H2",False,,pending -opadd0.f,OPADD0,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT",False,,pending -opahst.f,OPAHST,SUBROUTINE,False,"BASICS|ODFPAR","STARK0",True,,pending -opaini.f,OPAINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","DWNFR0|REFLEV|WNSTOR|LINPRO|SABOLF|LEVGRP",False,,pending -opctab.f,OPCTAB,SUBROUTINE,False,"BASICS|MODELQ","RAYLEIGH",False,,pending -opdata.f,OPDATA,SUBROUTINE,False,"TOPB","",True,,pending -opfrac.f,OPFRAC,SUBROUTINE,False,"pfoptb","",True,,pending -osccor.f,OSCCOR,SUBROUTINE,False,"BASICS|MODELQ|ITERAT","",True,,pending -outpri.f,OUTPRI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|grdpra","RATMAL|OPACF1|LEVSOL|WNSTOR|SABOLF",True,,pending -output.f,OUTPUT,SUBROUTINE,False,"BASICS|MODELQ","",True,,pending -partf.f,PARTF,SUBROUTINE,False,"BASICS|irwint|PFSTDS","PFFE|PFNI|OPFRAC|MPARTF|PFHEAV|PFSPEC|PFCNO",False,,pending -pfcno.f,PFCNO,SUBROUTINE,True,"BASICS","",False,src/math/pfcno.rs,done -pffe.f,PFFE,SUBROUTINE,True,"","",False,src/math/pffe.rs,done -pfheav.f,PFHEAV,SUBROUTINE,False,"","",True,,pending -pfni.f,PFNI,SUBROUTINE,True,"","",False,src/math/pfni.rs,done -pfspec.f,PFSPEC,SUBROUTINE,True,"","",False,src/math/pfspec.rs,done -pgset.f,PGSET,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|rybpgs|grdpra","TRIDAG",True,,pending -prchan.f,PRCHAN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",True,,pending -prd.f,PRD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","DOPGAM",False,,pending -prdini.f,PRDINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/prdini.rs,done -princ.f,PRINC,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","LINPRO|DWNFR|OPACF1|SABOLF",True,,pending -prnt.f,PRNT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","SABOLF",True,,pending -profil.f,PROFIL,FUNCTION,False,"BASICS|ATOMIC|MODELQ|quasun","PROFIL|STARK0|DIVSTR",False,src/math/profil.rs,done -profsp.f,PROFSP,FUNCTION,False,"BASICS|ATOMIC|MODELQ","PROFSP|SABOLF",False,,pending -prsent.f,PRSENT,SUBROUTINE,False,"tdflag|tdedge|THERM|TABLTD","",True,,pending -psolve.f,PSOLVE,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/psolve.rs,done -pzert.f,PZERT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/pzert.rs,done -pzeval.f,PZEVAL,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|icnrsp","CONOUT",True,,pending -pzevld.f,PZEVLD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1|ifpzpa|PRSAUX|grdpra|DEPTDR","",False,src/math/pzevld.rs,done -quartc.f,QUARTC,SUBROUTINE,False,"","",True,src/math/quartc.rs,done -quasim.f,QUASIM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|quasun","ALLARD",False,,pending -quit.f,QUIT,SUBROUTINE,False,"","",True,src/math/quit.rs,done -radpre.f,RADPRE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","QUIT|INDEXX|OPACF1|RTEFR1",True,,pending -radtot.f,RADTOT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT|OPTDPT|TOTJHK|SURFEX","OPAINI|OPACF1|RTEFR1",False,,pending -raph.f,RAPH,FUNCTION,True,"","RAPH",False,src/math/raph.rs,done -rates1.f,RATES1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT","ROSSTD|OPACF1|RTEFR1",False,,pending -ratmal.f,RATMAL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/ratmal.rs,done -ratmat.f,RATMAT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","REFLEV",False,,pending -ratsp1.f,RATSP1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","ROSSTD|OPACF1|RTEFR1",True,,pending -rayini.f,RAYINI,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC","RAYLEIGH",True,,pending -rayleigh.f,RAYLEIGH,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|RAYSCT|eospar","",False,src/math/rayleigh.rs,done -rayset.f,RAYSET,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/rayset.rs,done -rdata.f,RDATA,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|STRPAR|INUNIT|imodlc","RDATAX|LINSET|QUIT|DOPGAM",True,,pending -rdatax.f,RDATAX,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","BKHSGO",True,,pending -readbf.f,READBF,SUBROUTINE,False,"BASICS","",True,,pending -rechck.f,RECHCK,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","OPACF1|RTEFR1",True,,pending -reflev.f,REFLEV,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",False,,pending -reiman.f,REIMAN,FUNCTION,True,"","REIMAN",False,src/math/reiman.rs,done -resolv.f,RESOLV,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR|ARRAY1|icnrsp","NEWPOP|TAUFR1|TIMING|OPACF1|RTEFR1|ROSSTD|STEQEQ|CONOUT|ELCOR|OPAINI|RATES1|PRD",True,,pending -rhoeos.f,RHOEOS,FUNCTION,False,"BASICS|MODELQ","PRSENT|RHOEOS",False,,pending -rhonen.f,RHONEN,SUBROUTINE,False,"BASICS|MODELQ","ELDENS",False,,pending -rhsgen.f,RHSGEN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|CUBCON","MATINV|STATE|RATMAT|COMPT0|SABOLF|CONVEC|LEVGRP",False,,pending -rossop.f,ROSSOP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","WNSTOR|STEQEQ|ELDENS|OPACF0|MEANOP|MEANOPT",False,,pending -rosstd.f,ROSSTD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR","",True,,pending -rte_sc.f,RTE_SC,SUBROUTINE,True,"BASICS","",False,src/math/rte_sc.rs,done -rteang.f,RTEANG,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|EXTINT|SURFEX","GAULEG",False,,pending -rtecf0.f,RTECF0,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT|auxcbc|AUXRTE","",False,,pending -rtecf1.f,RTECF1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT|comgfs|AUXRTE|EXTINT|SURFEX","RTEFE2|RTESOL|RTECF0",True,,pending -rtecmc.f,RTECMC,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|comgfs|AUXRTE","MATINV|OPACF1|RTECF0",False,,pending -rtecmu.f,RTECMU,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT|AUXRTE","RTESOL|OPACF1|RTECF0|GAULEG",True,,pending -rtecom.f,RTECOM,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT|comgfs|AUXRTE","OPACF1|RTECF0|RTECF1",False,,pending -rtedf1.f,RTEDF1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|OPTDPT","",False,src/math/rtedf1.rs,done -rtedf2.f,RTEDF2,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR","",False,,pending -rtefe2.f,RTEFE2,SUBROUTINE,True,"BASICS","",False,src/math/rtefe2.rs,done -rtefr1.f,RTEFR1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT","MATINV|RTESOL|RTEDF1|RTECF1|RTEDF2|MINV3",True,,pending -rteint.f,RTEINT,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT","MATINV|OPACF1",True,,pending -rtesol.f,RTESOL,SUBROUTINE,True,"BASICS","",False,src/math/rtesol.rs,done -russel.f,RUSSEL,SUBROUTINE,False,"BASICS|MODELQ|COMFH1","MPARTF",True,,pending -rybchn.f,RYBCHN,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|ALIPAR|ARRAY1|rybpgs|grdpra","PGSET|ELDENS",True,,pending -rybene.f,RYBENE,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ARRAY1|RYBMTX|deridt|CUBCON","CONVEC",False,,pending -rybheq.f,RYBHEQ,SUBROUTINE,False,"BASICS|MODELQ|rybpgs|grdpra","OPACF1|RTEFR1|WNSTOR|STEQEQ|ELDENS|ELCOR|OPAINI|PGSET",True,,pending -rybmat.f,RYBMAT,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ARRAY1|dsctva|RYBMTX","",False,,pending -rybsol.f,RYBSOL,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|ALIPAR|ARRAY1|ITERAT|imodlc|RYBMTX","RYBMAT|RTEFR1|ROSSTD|OPACTR|LINEQS|STEQEQ|ALIFR1|TRIDAG|RYBCHN|OPACFD",True,,pending -sabolf.f,SABOLF,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","PARTF",False,,pending -sbfch.f,SBFCH,FUNCTION,True,"","SBFCH",False,src/math/sbfch.rs,done -sbfhe1.f,SBFHE1,FUNCTION,False,"BASICS|ATOMIC","SBFHE1|QUIT",True,src/math/sbfhe1.rs,done -sbfhmi.f,SBFHMI,FUNCTION,True,"","SBFHMI",False,src/math/sbfhmi.rs,done -sbfhmi_old.f,SBFHMI_OLD,FUNCTION,True,"","SBFHMI_OLD",False,src/math/sbfhmi_old.rs,done -sbfoh.f,SBFOH,FUNCTION,True,"","SBFOH",False,src/math/sbfoh.rs,done -setdrt.f,SETDRT,SUBROUTINE,False,"BASICS|MODELQ|RHODER","",False,,pending -settrm.f,SETTRM,SUBROUTINE,False,"tdflag|tdedge|THERM|TABLTD","PRSENT",True,,pending -sffhmi.f,SFFHMI,FUNCTION,True,"","SFFHMI",False,src/math/sffhmi.rs,done -sffhmi_add.f,SFFHMI_ADD,FUNCTION,True,"","SFFHMI_ADD",False,src/math/sffhmi_add.rs,done -sghe12.f,SGHE12,FUNCTION,True,"","SGHE12",False,src/math/sghe12.rs,done -sgmer0.f,SGMER0,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/sgmer.rs,done -sgmer1.f,SGMER1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/sgmer.rs,done -sgmerd.f,SGMERD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/sgmer.rs,done -sigave.f,SIGAVE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","",True,,pending -sigk.f,SIGK,FUNCTION,False,"BASICS|ATOMIC","SPSIGK|SIGK",False,,pending -sigmar.f,SIGMAR,FUNCTION,False,"BASICS","SIGMAR|LAGUER",True,,pending -solve.f,SOLVE,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|ARRAY1|ALIPAR|CMATZD","MATINV|PRCHAN|RHSGEN|WNSTOR|MATGEN",True,,pending -solves.f,SOLVES,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|ARRAY1|ALIPAR|STOMAT|CMATZD","MATINV|PRCHAN|RHSGEN|WNSTOR|MATGEN",True,,pending -spsigk.f,SPSIGK,SUBROUTINE,True,"","CARBON",False,src/math/spsigk.rs,done -srtfrq.f,SRTFRQ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","INDEXX|QUIT",True,,pending -stark0.f,STARK0,SUBROUTINE,True,"","",False,src/math/stark0.rs,done -starka.f,STARKA,FUNCTION,False,"BASICS|MODELQ","STARKA",False,src/math/starka.rs,done -start.f,START,SUBROUTINE,False,"BASICS|hediff","",True,,pending -state.f,STATE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|terden|PFSTDS","OPFRAC|PARTF",True,,pending -steqeq.f,STEQEQ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|POPSTR|PPAPAR","LEVSOL|RATMAT|SABOLF|MOLEQ",False,,pending -switch.f,SWITCH,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",True,,pending -szirc.f,SZIRC,SUBROUTINE,True,"","EINT",False,src/math/szirc.rs,done -tabini.f,TABINI,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|eletab|abntab|intcff","",True,,pending -tabint.f,TABINT,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|intcff","",False,,pending -taufr1.f,TAUFR1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT","",False,,pending -tdpini.f,TDPINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","GFREE0",False,src/math/tdpini.rs,done -temcor.f,TEMCOR,SUBROUTINE,False,"BASICS|MODELQ|ARRAY1|ALIPAR|CUBCON","MEANOP|WNSTOR|STEQEQ|ELDENS|OPACF0|CONVEC",True,,pending -temper.f,TEMPER,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|FACTRS|PRSAUX|FLXAUX","WNSTOR|TLOCAL|STEQEQ|ELDENS|OPACF0|MEANOP|MEANOPT",True,,pending -timing.f,TIMING,SUBROUTINE,False,"","",True,,pending -tiopf.f,TIOPF,SUBROUTINE,True,"","",False,src/math/tiopf.rs,done -tlocal.f,TLOCAL,SUBROUTINE,False,"BASICS|MODELQ|FACTRS|FLXAUX","QUARTC",False,,pending -tlusty.f,TLUSTY,UNKNOWN,False,"BASICS|ITERAT|ALIPAR","TIMING",True,,pending -topbas.f,TOPBAS,FUNCTION,False,"TOPB","TOPBAS",True,,pending -traini.f,TRAINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","",False,src/math/traini.rs,done -tridag.f,TRIDAG,SUBROUTINE,True,"","",False,src/math/tridag.rs,done -trmder.f,TRMDER,SUBROUTINE,False,"BASICS|derdif|terden|adiaba","ELDENS",False,,pending -trmdrt.f,TRMDRT,SUBROUTINE,False,"BASICS|tdedge|CC|tdflag|CONVOUT","PRSENT",False,,pending -ubeta.f,UBETA,FUNCTION,True,"","UBETA|LAGRAN",False,src/math/ubeta.rs,done -vern16.f,VERN16,FUNCTION,True,"BASICS","VERN16",False,src/math/vern16.rs,done -vern18.f,VERN18,FUNCTION,True,"BASICS","VERN18",False,src/math/vern18.rs,done -vern20.f,VERN20,FUNCTION,True,"BASICS","VERN20",False,src/math/vern20.rs,done -vern26.f,VERN26,FUNCTION,True,"BASICS","VERN26",False,src/math/vern26.rs,done -verner.f,VERNER,FUNCTION,False,"BASICS|ATOMIC","QUIT|VERNER",False,src/math/verner.rs,done -visini.f,VISINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",True,,pending -voigt.f,VOIGT,FUNCTION,True,"","VOIGT",False,src/math/voigt.rs,done -voigte.f,VOIGTE,FUNCTION,True,"","VOIGTE",False,src/math/voigte.rs,done -wn.f,WN,FUNCTION,True,"BASICS","WN",False,src/math/wn.rs,done -wnstor.f,WNSTOR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/wnstor.rs,done -xenini.f,XENINI,SUBROUTINE,False,"BASICS|MODELQ","",True,,pending -xk2dop.f,XK2DOP,FUNCTION,True,"","XK2DOP",False,src/math/xk2dop.rs,done -yint.f,YINT,FUNCTION,True,"","YINT",False,src/math/interpolate.rs,done -ylintp.f,YLINTP,FUNCTION,True,"","YLINTP",False,src/math/ylintp.rs,done -zmrho.f,ZMRHO,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/zmrho.rs,done diff --git a/scripts/analyze_fortran.py b/scripts/analyze_fortran.py deleted file mode 100644 index 1cd93f8..0000000 --- a/scripts/analyze_fortran.py +++ /dev/null @@ -1,434 +0,0 @@ -#!/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 - block_match = re.search(r'(?i)^\s*BLOCK\s*DATA\s+(\w+)?', 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 - - 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['has_io'], x['trans_pending'], 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() diff --git a/scripts/extract_fortran_data.py b/scripts/extract_fortran_data.py deleted file mode 100644 index 5bc017d..0000000 --- a/scripts/extract_fortran_data.py +++ /dev/null @@ -1,438 +0,0 @@ -#!/usr/bin/env python3 -""" -从 Fortran 源文件提取数组数据,生成 Rust data.rs - -用法: - python3 scripts/extract_fortran_data.py - -输出: src/data.rs -""" - -import re -from pathlib import Path - - -def parse_fortran_file(filepath: Path, global_params: dict = None) -> list[dict]: - """解析单个 Fortran 文件中的数组 - - Args: - filepath: Fortran 文件路径 - global_params: 从 include 文件中提取的全局参数表 - """ - if global_params is None: - global_params = {} - - with open(filepath, 'r') as f: - content = f.read() - - arrays = {} - - # 0. 预处理 - # 先清理每行的 Fortran 注释 (! 后面的内容) - # 同时移除 Fortran 77 风格的注释行 (以 C 或 * 开头) - lines = content.split('\n') - cleaned_lines = [] - for line in lines: - # Fortran 77 注释行 (第1列是 C, c, *, 或完全空行) - if len(line) > 0 and line[0] in 'Cc*': - continue # 跳过整行注释 - # Fortran 90 行内注释 - if '!' in line: - line = line.split('!')[0] - cleaned_lines.append(line) - - # 再合并 Fortran 续行 (第6列是 *, +, &, 数字, 或字母) - # Fortran 允许使用字母作为续行标记 (A, B, C, ... 用于超过9个续行) - merged_lines = [] - for line in cleaned_lines: - # 检查是否是续行 (第6列是 *, +, &, 数字, 或非空格字符) - if len(line) >= 6 and line[5] != ' ' and line[5] not in '\n\r\t': - # 续行: 追加到上一行 (去掉前6列) - if merged_lines: - merged_lines[-1] += ' ' + line[6:].strip() - else: - merged_lines.append(line) - content = '\n'.join(merged_lines) - - # 1. 首先解析所有 parameter 语句,建立局部常量表 - # 合并全局参数和局部参数 - param_table = dict(global_params) # 复制全局参数 - param_pattern = r'parameter\s*\(([^)]+)\)' - for match in re.finditer(param_pattern, content, re.IGNORECASE): - params_str = match.group(1) - for param in params_str.split(','): - param = param.strip() - if '=' in param: - name, val = param.split('=', 1) - name = name.strip().lower() - val = val.strip().lower() - # 尝试解析为整数或浮点数 - try: - # 先尝试整数 - if '.' not in val and 'e' not in val and 'd' not in val: - param_table[name] = int(val) - else: - # 浮点数,转换为整数(用于数组维度) - val = val.replace('d', 'e') - param_table[name] = int(float(val)) - except ValueError: - pass - - # 2. 解析 dimension 语句 - # dimension p4a(22), p4b(10,28), adi(nni), ... - # 注意: 使用 [ \t] 代替 \s 避免跨行匹配 - dim_pattern = r'dimension[ \t]+([a-z0-9_,() \t]+)' - for match in re.finditer(dim_pattern, content, re.IGNORECASE): - dim_str = match.group(1) - # 清理 dim_str - 移除可能包含的下一个关键字 - for keyword in ['\nreal', '\ninteger', '\ncomplex', '\nlogical', '\ncharacter', - '\ndimension', '\ndata', '\nparameter', '\nequivalence']: - if keyword in dim_str.lower(): - dim_str = dim_str[:dim_str.lower().find(keyword)] - break - arr_pattern = r'(\w+)\s*\(([^)]+)\)' - for arr_match in re.finditer(arr_pattern, dim_str): - name = arr_match.group(1).lower() - dims_str = arr_match.group(2) - # 解析维度,支持常量和 parameter 变量 - dims = [] - valid = True - for d in dims_str.split(','): - d = d.strip().lower() - if d in param_table: - dims.append(param_table[d]) - else: - try: - dims.append(int(d)) - except ValueError: - valid = False - break - if valid and dims: - arrays[name] = {"name": name, "dims": dims, "data": None, "source": filepath.name} - - # 2.5 解析类型声明中的数组 - # REAL frac(MR), INTEGER arr(10), REAL*4 arr(10), CHARACTER*10 str(5), etc. - # 注意: 使用 [ \t] 代替 \s 避免跨行匹配 - type_decl_pattern = r'(real(?:\*[\d]+)?|integer(?:\*[\d]+)?|complex(?:\*[\d]+)?|logical(?:\*[\d]+)?|character(?:\*[\d]+)?)[ \t]+([a-z0-9_,() \t]+)' - for match in re.finditer(type_decl_pattern, content, re.IGNORECASE): - decl_str = match.group(2) - # 清理 decl_str - 移除可能包含的下一个类型声明 - for keyword in ['\nreal', '\ninteger', '\ncomplex', '\nlogical', '\ncharacter', - '\ndimension', '\ndata', '\nparameter', '\nequivalence']: - if keyword in decl_str.lower(): - decl_str = decl_str[:decl_str.lower().find(keyword)] - break - - # 匹配变量名(维度) - arr_pattern = r'(\w+)\s*\(([^)]+)\)' - for arr_match in re.finditer(arr_pattern, decl_str): - name = arr_match.group(1).lower() - if name in arrays: - continue # 已有定义 - dims_str = arr_match.group(2) - # 解析维度 - dims = [] - valid = True - for d in dims_str.split(','): - d = d.strip().lower() - if d in param_table: - dims.append(param_table[d]) - else: - try: - dims.append(int(d)) - except ValueError: - valid = False - break - if valid and dims: - arrays[name] = {"name": name, "dims": dims, "data": None, "source": filepath.name} - - # 3. 解析 data 语句 (支持多行) - # data name / val1, val2, ... / - data_pattern = r'data\s+(\w+)\s*/\s*([^/]+)\s*/' - for match in re.finditer(data_pattern, content, re.IGNORECASE | re.DOTALL): - name = match.group(1).lower() - data_str = match.group(2) - - if name not in arrays: - arrays[name] = {"name": name, "dims": [], "data": None, "source": filepath.name} - - values = parse_data_values(data_str) - arrays[name]["data"] = values - - # 4. 处理 parameter 语句中的标量常量(用于导出) - for match in re.finditer(param_pattern, content, re.IGNORECASE): - params_str = match.group(1) - for param in params_str.split(','): - param = param.strip() - if '=' in param: - name, val = param.split('=', 1) - name = name.strip().lower() - val = val.strip().lower() - if name not in arrays: - try: - val = val.replace('d', 'e') - arrays[name] = {"name": name, "dims": [], "data": [float(val)], "source": filepath.name, "is_param": True} - except ValueError: - pass - - return list(arrays.values()) - - -def parse_data_values(data_str: str) -> list[float]: - """解析 DATA 语句中的数值,处理重复语法如 7*1.387""" - values = [] - - # 清理 - lines = data_str.split('\n') - cleaned_lines = [] - for line in lines: - line = line.strip() - if line.startswith('*'): - line = line[1:].strip() - cleaned_lines.append(line) - data_str = ' '.join(cleaned_lines) - - for part in data_str.split(','): - part = part.strip() - if not part: - continue - - # 移除末尾的 / (DATA 语句结束符) - part = part.rstrip('/') - - # 处理重复语法: "7*1.387" - if '*' in part and not part.startswith('-'): - match = re.match(r'(\d+)\s*\*\s*(-?[\d.]+)', part) - if match: - count = int(match.group(1)) - val = float(match.group(2)) - values.extend([val] * count) - continue - - try: - # 处理 Fortran 科学计数法 - # 处理 "- 14.2" 这种中间有空格的负数 - val = part.replace('d', 'e').replace('D', 'e') - # 移除负号和数字之间的空格 - val = re.sub(r'-\s+(\d)', r'-\1', val) - # 移除科学计数法中的多余空格 (如 "1.48 e-2" -> "1.48e-2") - val = re.sub(r'(\d)\s+([eEdD])', r'\1\2', val) - values.append(float(val)) - except ValueError: - pass - - return values - - -def generate_data_rs(all_arrays: dict[str, list[dict]]) -> str: - """生成 src/data.rs 内容""" - lines = [] - lines.append("//! Fortran 数据数组自动导出") - lines.append("//!") - lines.append("//! 由 extract_fortran_data.py 自动生成,请勿手动修改") - lines.append("") - - # 收集已使用的名称,避免重复 - used_names = set() - - # 按源文件分组 - for source, arrays in sorted(all_arrays.items()): - # 过滤有数据的数组 - valid_arrays = [a for a in arrays if a.get("data") and len(a["data"]) > 0] - if not valid_arrays: - continue - - lines.append(f"// ========== {source} ==========") - lines.append("") - - for arr in valid_arrays: - base_name = arr["name"].upper() - # 清理名称中的特殊字符 - base_name = re.sub(r'[^A-Z0-9_]', '', base_name) - - # 所有变量都添加文件名前缀,避免命名冲突 - prefix = Path(source).stem.upper()[:8] # 取文件名前8个字符 - name = f"{prefix}_{base_name}" - - # 如果加上前缀后仍有冲突,添加序号 - if name in used_names: - counter = 1 - while f"{name}_{counter}" in used_names: - counter += 1 - name = f"{name}_{counter}" - - used_names.add(name) - - dims = arr["dims"] - data = arr["data"] - - if not data: - continue - - total_size = len(data) - - # 跳过单值 parameter - if arr.get("is_param") and total_size == 1: - lines.append(f"/// {arr['name']} (from {source})") - lines.append(f"pub const {name}: f64 = {data[0]};") - lines.append("") - continue - - if len(dims) == 0: - # 未知维度,用 Vec 格式输出以便检查 - lines.append(f"/// {arr['name']} (from {source}, 未知维度,共 {len(data)} 个值)") - lines.append(f"pub const {name}: [f64; {len(data)}] = [") - for i, val in enumerate(data): - if i % 10 == 0: - lines.append(" ") - lines[-1] += f"{val}," - lines.append("];") - lines.append("") - - elif len(dims) == 1: - # 1D 数组 - 检查数据量是否匹配 - expected_size = dims[0] - if len(data) != expected_size: - print(f"警告: {name} 期望 {expected_size} 个值,实际 {len(data)} 个,跳过") - continue - - lines.append(f"/// {arr['name']}({dims[0]}) from {source}") - lines.append(f"pub const {name}: [f64; {dims[0]}] = [") - for i, val in enumerate(data): - if i % 10 == 0: - lines.append(" ") - lines[-1] += f"{val}," - lines.append("];") - lines.append("") - - elif len(dims) == 2: - # 2D 数组 - 直接转换为 Rust 行优先格式 - nj, ni = dims[0], dims[1] - expected_size = nj * ni - - if len(data) < expected_size: - print(f"警告: {name} 期望 {expected_size} 个值,实际 {len(data)} 个,跳过") - continue - - lines.append(f"/// {arr['name']}({nj}, {ni}) from {source}") - lines.append(f"/// 已转换为 Rust 行优先格式") - lines.append(f"pub const {name}: [[f64; {ni}]; {nj}] = [") - - # 列优先 → 行优先 转换 - for j in range(nj): - row = [] - for i in range(ni): - idx = j + i * nj # Fortran 列优先索引 - row.append(str(data[idx])) - lines.append(f" [{','.join(row)}],") - - lines.append("];") - lines.append("") - - # 不再需要转换函数和 getter,2D 数组直接生成为 const - - return '\n'.join(lines) - - -def parse_include_files(extracted_dir: Path) -> dict: - """解析 .FOR include 文件中的全局参数""" - global_params = {} - - # 扫描 .FOR 文件 - for for_file in extracted_dir.glob("*.FOR"): - try: - content = for_file.read_text() - except: - # 也尝试 tlusty/ 根目录 - for_file = Path("tlusty") / for_file.name - if for_file.exists(): - content = for_file.read_text() - else: - continue - - # 清理注释 - lines = [] - for line in content.split('\n'): - if '!' in line: - line = line.split('!')[0] - lines.append(line) - content = '\n'.join(lines) - - # 解析 parameter 语句 - param_pattern = r'parameter\s*\(([^)]+)\)' - for match in re.finditer(param_pattern, content, re.IGNORECASE): - params_str = match.group(1) - for param in params_str.split(','): - param = param.strip() - if '=' in param: - name, val = param.split('=', 1) - name = name.strip().lower() - val = val.strip().lower() - try: - if '.' not in val and 'e' not in val and 'd' not in val: - global_params[name] = int(val) - else: - val = val.replace('d', 'e') - global_params[name] = int(float(val)) - except ValueError: - pass - - return global_params - - -def main(): - # 扫描 tlusty/extracted 目录 - extracted_dir = Path("tlusty/extracted") - - if not extracted_dir.exists(): - print(f"错误: 目录不存在: {extracted_dir}") - return - - # 首先解析 include 文件中的全局参数 - global_params = parse_include_files(extracted_dir) - print(f"从 .FOR include 文件中提取了 {len(global_params)} 个全局参数") - - all_arrays = {} - - # 扫描所有 .f 文件 - for fortran_file in sorted(extracted_dir.glob("*.f")): - arrays = parse_fortran_file(fortran_file, global_params) - if arrays: - all_arrays[fortran_file.name] = arrays - print(f"解析: {fortran_file.name} -> {len(arrays)} 个数组") - - # 统计 - total_arrays = sum(len(arrs) for arrs in all_arrays.values()) - arrays_with_data = sum( - 1 for arrs in all_arrays.values() - for a in arrs if a.get("data") and len(a["data"]) > 0 - ) - arrays_2d = sum( - 1 for arrs in all_arrays.values() - for a in arrs if len(a.get("dims", [])) == 2 and a.get("data") - ) - - print() - print("=" * 60) - print(f"总计: {total_arrays} 个数组, {arrays_with_data} 个有数据, {arrays_2d} 个 2D 数组") - print("=" * 60) - - # 生成 data.rs - output_path = Path("src/data.rs") - rust_code = generate_data_rs(all_arrays) - - with open(output_path, 'w') as f: - f.write(rust_code) - - print(f"已生成: {output_path}") - print() - print("在 lib.rs 或 main.rs 中添加:") - print(" pub mod data;") - print() - print("使用方法:") - print(" use crate::data::{TT, PN, get_p4b};") - print(" let p4b = get_p4b(); // 自动初始化并返回 2D 数组") - - -if __name__ == "__main__": - main() diff --git a/scripts/fortran_to_rust_array.py b/scripts/fortran_to_rust_array.py deleted file mode 100644 index 7681b4a..0000000 --- a/scripts/fortran_to_rust_array.py +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env python3 -""" -将 Fortran DATA 语句转换为 Rust 2D 数组 - -用法: - python3 scripts/fortran_to_rust_array.py tlusty/extracted/pffe.f - -输出: Rust 代码片段,可直接复制到 .rs 文件中 -""" - -import re -import sys -from pathlib import Path - - -def parse_fortran_arrays(filepath: str) -> list[dict]: - """ - 解析 Fortran 文件中的数组定义和 DATA 语句 - - 返回: [{"name": "p4a", "dims": [22], "data": [...]}, ...] - """ - with open(filepath, 'r') as f: - content = f.read() - - arrays = {} - - # 1. 解析 dimension 语句 - # dimension p4a(22), p4b(10,28), ... - dim_pattern = r'dimension\s+([a-z0-9_,()\s]+)' - for match in re.finditer(dim_pattern, content, re.IGNORECASE): - dim_str = match.group(1) - # 解析每个数组 - arr_pattern = r'(\w+)\s*\(([^)]+)\)' - for arr_match in re.finditer(arr_pattern, dim_str): - name = arr_match.group(1).lower() - dims = [int(d.strip()) for d in arr_match.group(2).split(',')] - arrays[name] = {"name": name, "dims": dims, "data": None} - - # 2. 解析 data 语句 - # data p4a / val1, val2, ... / - # 或多行: - # data p4b / - # * val1, val2, ..., - # * val3, ... / - - # 先找到所有 data 块 - data_pattern = r'data\s+(\w+)\s*/\s*([^/]+)\s*/' - for match in re.finditer(data_pattern, content, re.IGNORECASE | re.DOTALL): - name = match.group(1).lower() - data_str = match.group(2) - - if name not in arrays: - arrays[name] = {"name": name, "dims": [], "data": None} - - # 解析数值 - values = parse_data_values(data_str) - arrays[name]["data"] = values - - return list(arrays.values()) - - -def parse_data_values(data_str: str) -> list[float]: - """解析 DATA 语句中的数值,处理重复语法如 7*1.387""" - values = [] - - # 清理: 移除注释、换行,但保留 * 用于重复语法 - data_str = re.sub(r'[cC]\s*$', '', data_str) # 行尾注释 - # 移除 Fortran 续行符 * (行首的 *),但保留数据中的 * - lines = data_str.split('\n') - cleaned_lines = [] - for line in lines: - line = line.strip() - if line.startswith('*'): - line = line[1:].strip() - cleaned_lines.append(line) - data_str = ' '.join(cleaned_lines) - - for part in data_str.split(','): - part = part.strip() - if not part: - continue - - # 处理重复语法: "7*1.387" 或 "7*1.387" - if '*' in part and not part.startswith('-'): # 避免把负数当重复 - # 检查是否真的是重复语法 - match = re.match(r'(\d+)\s*\*\s*(-?[\d.]+)', part) - if match: - count = int(match.group(1)) - val = float(match.group(2)) - values.extend([val] * count) - continue - - try: - values.append(float(part)) - except ValueError: - # 跳过无法解析的部分 - pass - - return values - - -def generate_rust_code(arrays: list[dict]) -> str: - """生成 Rust 代码""" - lines = [] - lines.append("// 自动生成的数组数据") - lines.append("") - - for arr in arrays: - name = arr["name"].upper() - dims = arr["dims"] - data = arr["data"] - - if not data: - continue - - total_size = len(data) - - if len(dims) == 1: - # 1D 数组 - lines.append(f"const {name}_RAW: [f64; {total_size}] = [") - for i, val in enumerate(data): - if i % 10 == 0: - lines.append(" ",) - lines[-1] += f"{val}," - lines.append("];") - lines.append(f"static {name}: OnceLock<[f64; {dims[0]}]> = OnceLock::new();") - lines.append("") - - elif len(dims) == 2: - # 2D 数组 - nj, ni = dims[0], dims[1] - lines.append(f"// {name}: Fortran {name.lower()}({nj}, {ni})") - lines.append(f"const {name}_RAW: [f64; {total_size}] = [") - for i, val in enumerate(data): - if i % 10 == 0: - lines.append(" ") - lines[-1] += f"{val}," - lines.append("];") - lines.append(f"static {name}: OnceLock<[[f64; {ni}]; {nj}]> = OnceLock::new();") - lines.append("") - - # 添加转换函数 - lines.append("") - lines.append("/// Fortran 列优先 → Rust 行优先") - lines.append("const fn fortran_to_rust_2d(") - lines.append(" data: &[f64; NJ * NI],") - lines.append(") -> [[f64; NI]; NJ] {") - lines.append(" let mut result = [[0.0; NI]; NJ];") - lines.append(" let mut i = 0;") - lines.append(" while i < NI {") - lines.append(" let mut j = 0;") - lines.append(" while j < NJ {") - lines.append(" result[j][i] = data[j + i * NJ];") - lines.append(" j += 1;") - lines.append(" }") - lines.append(" i += 1;") - lines.append(" }") - lines.append(" result") - lines.append("}") - lines.append("") - - # 添加初始化代码 - lines.append("// 初始化函数中调用:") - for arr in arrays: - name = arr["name"].upper() - dims = arr["dims"] - if len(dims) == 2 and arr["data"]: - nj, ni = dims[0], dims[1] - lines.append(f"let {name.lower()} = {name}.get_or_init(|| fortran_to_rust_2d::<{nj}, {ni}>(&{name}_RAW));") - - return '\n'.join(lines) - - -def main(): - if len(sys.argv) < 2: - print("用法: python3 scripts/fortran_to_rust_array.py ") - print("示例: python3 scripts/fortran_to_rust_array.py tlusty/extracted/pffe.f") - sys.exit(1) - - filepath = sys.argv[1] - - if not Path(filepath).exists(): - print(f"错误: 文件不存在: {filepath}") - sys.exit(1) - - print(f"解析: {filepath}") - print("=" * 60) - - arrays = parse_fortran_arrays(filepath) - - print(f"找到 {len(arrays)} 个数组:") - for arr in arrays: - dims_str = ', '.join(str(d) for d in arr['dims']) - data_count = len(arr['data']) if arr['data'] else 0 - print(f" - {arr['name']}({dims_str}): {data_count} 个值") - - print() - print("=" * 60) - print("生成的 Rust 代码:") - print("=" * 60) - print(generate_rust_code(arrays)) - - -if __name__ == "__main__": - main() diff --git a/scripts/generate_tracking.py b/scripts/generate_tracking.py deleted file mode 100644 index c9a87d5..0000000 --- a/scripts/generate_tracking.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python3 -""" -生成 Fortran 重构追踪 Markdown 文档。 - -用法: python3 generate_tracking.py > FORTRAN_TRACKING.md -""" - -import csv -import os - -def main(): - csv_path = "/home/fmq/program/tlusty/tl208-s54/rust/fortran_analysis.csv" - - with open(csv_path, 'r') as f: - reader = csv.DictReader(f) - rows = list(reader) - - # 统计 - total = len(rows) - done = sum(1 for r in rows if r['status'] == 'done') - pending = sum(1 for r in rows if r['status'] == 'pending') - pure = sum(1 for r in rows if r['is_pure'] == 'True') - has_io = sum(1 for r in rows if r['has_io'] == 'True') - - # 打印 Markdown 头 - print("""# Fortran 重构追踪表 - -> 自动生成,请勿手动修改。运行 `python3 scripts/generate_tracking.py > FORTRAN_TRACKING.md` 更新。 - -## 统计 - -| 指标 | 数量 | -|------|------| -| 总单元数 | {total} | -| 已完成 | {done} | -| 待处理 | {pending} | -| 纯函数 | {pure} | -| 有文件 I/O | {has_io} | -| 完成率 | {rate:.1f}% | - -## 状态说明 - -- ✅ `done` - 已重构为 Rust -- ⬜ `pending` - 待处理 -- 🔄 `in_progress` - 进行中 -- ⏭️ `skip` - 跳过 (I/O 依赖或暂不处理) - -## 类型说明 - -- **纯函数**: 无 COMMON 依赖、无文件 I/O、无外部调用依赖 -- **COMMON 依赖**: 需要状态结构体 -- **调用依赖**: 调用其他子程序,需要先实现依赖 - -## 完整追踪表 - -""".format(total=total, done=done, pending=pending, pure=pure, has_io=has_io, rate=100*done/total)) - - # 表格头 - print("| Fortran 文件 | 单元名 | 类型 | 纯函数 | COMMON 依赖 | 调用依赖 | I/O | Rust 模块 | 状态 |") - print("|-------------|--------|------|--------|-------------|----------|-----|-----------|------|") - - for r in rows: - # 状态图标 - status_icon = "✅" if r['status'] == 'done' else "⬜" - - # 纯函数标记 - pure_mark = "✓" if r['is_pure'] == 'True' else "" - - # I/O 标记 - io_mark = "📁" if r['has_io'] == 'True' else "" - - # 依赖显示 - common_deps = r['common_deps'].replace('|', ', ') if r['common_deps'] else "" - call_deps = r['call_deps'].replace('|', ', ') if r['call_deps'] else "" - - # Rust 模块链接 - rust_mod = r['rust_module'].replace('src/math/', '') if r['rust_module'] else "" - - print(f"| {r['fortran_file']} | {r['unit_name']} | {r['unit_type']} | {pure_mark} | {common_deps} | {call_deps} | {io_mark} | {rust_mod} | {status_icon} |") - -if __name__ == "__main__": - main() diff --git a/src/math/alifr1.rs b/src/math/alifr1.rs new file mode 100644 index 0000000..9fe67e9 --- /dev/null +++ b/src/math/alifr1.rs @@ -0,0 +1,1483 @@ +//! ALI 频率相关计算 - 变体 1。 +//! +//! 重构自 TLUSTY `alifr1.f` +//! +//! 计算流体静力学和辐射平衡量 - ALI 点的总加热和冷却率对 +//! 温度、电子密度和占据数的导数。 +//! 这是一致三对角算子的变体。 +//! +//! ## 实现状态 +//! 完整实现。包含以下代码路径: +//! - ILMCOR == 3, ILASCT == 0(标准路径) +//! - ILMCOR == 3, ILASCT != 0 +//! - ILMCOR != 3, ILASCT == 0 +//! - ILMCOR != 3, ILASCT != 0 +//! - 完整的边界条件处理 (IBC = 0, 1, 2, 3+) +//! +//! 当 IFALI > 5 时,返回 `true`,调用者应调用 `alifr3`。 + +use crate::state::alipar::FixAlp; +use crate::state::constants::{UN, TWO, HALF}; + +/// ALIFR1 输入参数 +pub struct Alifr1Params { + /// 频率索引 (1-indexed) + pub ij: usize, + /// 深度点数 + pub nd: usize, + /// 线性化能级数 + pub nlvexp: usize, + /// ALI 模式 + pub ifali: i32, + /// 辐射导数模式 + pub irder: i32, + /// ILMCOR 参数 + pub ilmcor: i32, + /// ILASCT 参数 + pub ilasct: i32, + /// 边界条件类型 + pub ibc: i32, + /// 是否为盘模型 + pub idisk: i32, + /// IFALIH 参数 + pub ifalih: i32, +} + +/// ALIFR1 需要的模型状态输入 +pub struct Alifr1ModelState<'a> { + // 深度相关 (MDEPTH) + pub elec: &'a [f64], + pub dens: &'a [f64], + pub densi: &'a [f64], + pub densim: &'a [f64], + pub dens1: &'a [f64], + pub dm: &'a [f64], + pub deldmz: &'a [f64], + pub elscat: &'a [f64], + pub absot: &'a [f64], + pub hkt21: &'a [f64], + pub xkfb: &'a [f64], + pub xkf1: &'a [f64], + + // 辐射相关 (MDEPTH) + pub rad1: &'a [f64], + pub fak1: &'a [f64], + + // 频率相关 + pub freq: &'a [f64], + pub hextrd: &'a [f64], + pub sigec: &'a [f64], + pub sige: f64, + pub extrad: &'a [f64], + pub fh: &'a [f64], + pub w: &'a [f64], + /// 表面辐射通量 Q0 (MFREQ) + pub q0: &'a [f64], + + // 跳过标志 (MDEPTH × MFREQ) + pub lskip: &'a [Vec], + + // 平衡相关 + pub reint: &'a [f64], + pub redif: &'a [f64], + + // 输出累积变量 + pub fprd: &'a mut [f64], + pub flfix: &'a mut [f64], + pub flrd: &'a mut [f64], + pub fcooli: &'a mut [f64], + pub heit: &'a mut [f64], + pub hein: &'a mut [f64], + pub heim: &'a mut [f64], + pub heitm: &'a mut [f64], + pub heinm: &'a mut [f64], + pub heimm: &'a mut [f64], + pub heip: &'a mut [Vec], + pub heipm: &'a mut [Vec], + pub redt: &'a mut [f64], + pub redn: &'a mut [f64], + pub redm: &'a mut [f64], + pub redx: &'a mut [f64], + pub redtm: &'a mut [f64], + pub rednm: &'a mut [f64], + pub redmm: &'a mut [f64], + pub redxm: &'a mut [f64], + pub redp: &'a mut [Vec], + pub redpm: &'a mut [Vec], + pub rein: &'a mut [f64], + pub reit: &'a mut [f64], + pub reim: &'a mut [f64], + pub reip: &'a mut [Vec], +} + +/// ALIFR1 需要的辐射/不透明度状态 +pub struct Alifr1RadState<'a> { + /// 权重因子 (MFREQ) + pub wc: &'a [f64], + /// 当前频率发射系数 (MDEPTH) + pub emis1: &'a [f64], + /// 当前频率吸收系数 (MDEPTH) + pub abso1: &'a [f64], + /// 当前频率散射系数 (MDEPTH) + pub scat1: &'a [f64], + /// 发射系数 T 导数 (MDEPTH) + pub demt1: &'a [f64], + /// 发射系数 N 导数 (MDEPTH) + pub demn1: &'a [f64], + /// 发射系数 M 导数 (MDEPTH) + pub demm1: &'a [f64], + /// 吸收系数 T 导数 (MDEPTH) + pub dabt1: &'a [f64], + /// 吸收系数 N 导数 (MDEPTH) + pub dabn1: &'a [f64], + /// 吸收系数 M 导数 (MDEPTH) + pub dabm1: &'a [f64], + /// 发射系数能级导数 (MLVEXP × MDEPTH) + pub demp1: &'a [Vec], + /// 吸收系数能级导数 (MLVEXP × MDEPTH) + pub dabp1: &'a [Vec], +} + +/// 计算 ALI 频率相关量 - 变体 1。 +/// +/// # 参数 +/// +/// * `params` - 输入参数 +/// * `fixalp` - ALI 固定参数 +/// * `model` - 模型状态 +/// * `rad` - 辐射状态 +/// +/// # 返回值 +/// +/// 返回 `true` 表示调用者应该调用 `alifr3`(当 IFALI > 5 时)。 +/// 返回 `false` 表示处理已完成或无需进一步操作。 +/// +/// # Fortran 索引说明 +/// +/// - IJ 是频率索引 (1-indexed) +/// - ID 是深度索引 (1-indexed) +/// - II 是能级索引 (1-indexed) +/// +/// # 实现状态 +/// +/// 完整实现: +/// - IFALI <= 1 时直接返回 +/// - IFALI > 5 时返回 true(调用者应调用 alifr3) +/// - ILMCOR == 3 且 ILASCT == 0 的标准路径 +/// - ILMCOR == 3 且 ILASCT != 0 的路径 +/// - ILMCOR != 3 的路径 +/// - 完整的深度循环和边界条件处理(IBC = 0, 1, 2, 3+) +#[allow(clippy::too_many_arguments)] +pub fn alifr1( + params: &Alifr1Params, + fixalp: &mut FixAlp, + model: &mut Alifr1ModelState, + rad: &Alifr1RadState, +) -> bool { + // 如果 IFALI <= 1,直接返回 + if params.ifali <= 1 { + return false; + } + + // 如果 IFALI > 5,返回 true 指示调用者应该调用 ALIFR3 + // 注意:由于 Rust 借用限制,ALIFR3 需要由调用者直接调用 + if params.ifali > 5 { + return true; + } + + let ij = params.ij; + let nd = params.nd; + let nlvexp = params.nlvexp; + + // 获取权重因子 + let ww = rad.wc[ij - 1]; + + // 常量 + let t23 = TWO / 3.0; + + // 根据 ILMCOR 值选择不同的处理路径 + if params.ilmcor == 3 { + // ================================================================ + // ILMCOR == 3 的处理(标准情况) + // ================================================================ + process_standard_path(params, fixalp, model, rad, ij, nd, nlvexp, ww, t23); + return false; + } + + // ================================================================ + // ILMCOR != 3 的处理(非标准情况) + // ILASCT == 0 的分支 + // ================================================================ + if params.ilasct == 0 { + process_ilasct_zero(params, fixalp, model, rad, ij, nd, nlvexp, ww); + return false; + } + + // ================================================================ + // ILASCT != 0 的处理 + // ================================================================ + process_ilasct_nonzero(params, fixalp, model, rad, ij, nd, nlvexp, ww); + + false +} + +/// 处理标准路径 (ILMCOR == 3, ILASCT == 0) +#[allow(clippy::too_many_arguments)] +fn process_standard_path( + params: &Alifr1Params, + fixalp: &mut FixAlp, + model: &mut Alifr1ModelState, + rad: &Alifr1RadState, + ij: usize, + nd: usize, + nlvexp: usize, + ww: f64, + t23: f64, +) { + // 初始化中间变量 + let mut dsft1m: f64 = 0.0; + let mut dsfn1m: f64 = 0.0; + let mut dsfm1m: f64 = 0.0; + let mut dsfp1m = vec![0.0; nlvexp]; + + // 1. 第一个深度点 (ID=1) + let id = 1; + let id_idx = id - 1; + let lnskip = model.lskip[id_idx][ij - 1] == 0; + + // 基本辅助量 - 源函数的导数 + let emisiv = if rad.emis1[id_idx] < 1e-35 { 1e35 } else { UN / rad.emis1[id_idx] }; + let abst = UN / rad.abso1[id_idx]; + let mut s0 = rad.emis1[id_idx] * abst; + + // 改进边界条件的贡献 + let dt = (rad.abso1[id_idx] / model.dens[id_idx] + rad.abso1[id] / model.dens[id]) + * (model.dm[id] - model.dm[id_idx]) * HALF; + let sa = s0 * (UN + 4.0 / dt * model.q0[ij - 1]); + s0 = s0 * (UN + TWO / dt * model.q0[ij - 1]); + let sc = rad.scat1[id_idx]; + let sct = sc * abst; + let corr = UN / (UN - fixalp.ali1[id_idx] * sct); + + let dsft1 = corr * (s0 * rad.demt1[id_idx] * emisiv - s0 * rad.dabt1[id_idx] * abst); + let dsfn1 = corr * (s0 * rad.demn1[id_idx] * emisiv - s0 * rad.dabn1[id_idx] * abst); + let dsfm1 = corr * (s0 * rad.demm1[id_idx] * emisiv - s0 * rad.dabm1[id_idx] * abst); + let mut dsfp1 = vec![0.0; nlvexp]; + for ii in 0..nlvexp { + dsfp1[ii] = corr * (s0 * rad.demp1[ii][id_idx] * emisiv - sa * rad.dabp1[ii][id_idx] * abst); + } + + // ID+1 的值 + let idp_idx = id; + let emisip = if rad.emis1[idp_idx] < 1e-35 { 1e35 } else { UN / rad.emis1[idp_idx] }; + let abstp = UN / rad.abso1[idp_idx]; + let s0p = rad.emis1[idp_idx] * abstp; + let scp = rad.scat1[idp_idx]; + let sctp = scp * abstp; + let corrp = UN / (UN - fixalp.ali1[idp_idx] * sctp); + + let mut dsft1p = corrp * (s0p * rad.demt1[idp_idx] * emisip - s0p * rad.dabt1[idp_idx] * abstp); + let mut dsfn1p = corrp * (s0p * rad.demn1[idp_idx] * emisip - s0p * rad.dabn1[idp_idx] * abstp); + let mut dsfm1p = corrp * (s0p * rad.demm1[idp_idx] * emisip - s0p * rad.dabm1[idp_idx] * abstp); + let mut dsfp1p = vec![0.0; nlvexp]; + for ii in 0..nlvexp { + dsfp1p[ii] = corrp * (s0p * rad.demp1[ii][idp_idx] * emisip - s0p * rad.dabp1[ii][idp_idx] * abstp); + } + + // 更新 DSFDT, DSFDN, DSFDM 等 + if params.irder == 1 || params.irder == 3 { + fixalp.dsfdt[id_idx] = dsft1 * fixalp.ali1[id_idx]; + fixalp.dsfdn[id_idx] = dsfn1 * fixalp.ali1[id_idx]; + fixalp.dsfdm[id_idx] = dsfm1 * fixalp.ali1[id_idx]; + } + if params.irder > 1 { + for ii in 0..nlvexp { + fixalp.dsfdp[ii][id_idx] = dsfp1[ii] * fixalp.ali1[id_idx]; + } + } + + // 流体静力学平衡量 + let wf = ww * model.fh[ij - 1]; + if lnskip { + model.fprd[id_idx] += wf * rad.abso1[id_idx] * model.rad1[id_idx] + - ww * model.hextrd[ij - 1] * rad.abso1[id_idx]; + let e0 = wf * model.rad1[id_idx]; + let d0 = wf * rad.abso1[id_idx] * fixalp.ali1[id_idx]; + model.heit[id_idx] += d0 * dsft1 + e0 * rad.dabt1[id_idx]; + model.hein[id_idx] += d0 * dsfn1 + e0 * rad.dabn1[id_idx]; + model.heim[id_idx] += d0 * dsfm1 + e0 * rad.dabm1[id_idx]; + for ii in 0..nlvexp { + model.heip[ii][id_idx] += d0 * dsfp1[ii] + e0 * rad.dabp1[ii][id_idx]; + } + } + + // 辐射平衡微分方程部分 + model.flfix[id_idx] += wf * model.rad1[id_idx] - ww * model.hextrd[ij - 1]; + model.flrd[id_idx] += model.w[ij - 1] * model.fh[ij - 1] * model.rad1[id_idx] + - model.w[ij - 1] * HALF * model.extrad[ij - 1]; + if model.redif[id_idx] > 0.0 { + let wf = wf * fixalp.ali1[id_idx]; + model.redt[id_idx] += wf * dsft1; + model.redn[id_idx] += wf * dsfn1; + model.redm[id_idx] += wf * dsfm1; + for ii in 0..nlvexp { + model.redp[ii][id_idx] += wf * dsfp1[ii]; + } + } + + // 辐射平衡积分方程部分 + if model.reint[id_idx] > 0.0 { + let abst_true = rad.abso1[id_idx] - model.elscat[id_idx]; + let d0 = abst_true * fixalp.ali1[id_idx]; + model.fcooli[id_idx] += ww * (rad.emis1[id_idx] - abst_true * model.rad1[id_idx]); + model.reit[id_idx] += ww * (d0 * dsft1 + model.rad1[id_idx] * rad.dabt1[id_idx] - rad.demt1[id_idx]); + model.rein[id_idx] += ww * (d0 * dsfn1 + + model.rad1[id_idx] * (rad.dabn1[id_idx] - model.sigec[ij - 1]) + - rad.demn1[id_idx]); + model.reim[id_idx] += ww * (d0 * dsfm1 + model.rad1[id_idx] * rad.dabm1[id_idx] - rad.demm1[id_idx]); + for ii in 0..nlvexp { + model.reip[ii][id_idx] += ww * (d0 * dsfp1[ii] + + model.rad1[id_idx] * rad.dabp1[ii][id_idx] + - rad.demp1[ii][id_idx]); + } + } + + // 2. 深度循环 (ID = 2 to ND-1) + for id in 2..nd { + let id_idx = id - 1; + let lnskip = model.lskip[id_idx][ij - 1] == 0; + + // 保存前一个点的值 + dsft1m = dsft1; + dsfn1m = dsfn1; + dsfm1m = dsfm1; + for ii in 0..nlvexp { + dsfp1m[ii] = dsfp1[ii]; + } + + // 更新当前点为上一个点的 ID+1 值 + let dsft1_curr = dsft1p; + let dsfn1_curr = dsfn1p; + let dsfm1_curr = dsfm1p; + let dsfp1_curr = dsfp1p.clone(); + + // 计算 ID+1 的值 + let idp_idx = id; + let emisip_new = if rad.emis1[idp_idx] < 1e-35 { 1e35 } else { UN / rad.emis1[idp_idx] }; + let abstp_new = UN / rad.abso1[idp_idx]; + let s0p_new = rad.emis1[idp_idx] * abstp_new; + let scp_new = rad.scat1[idp_idx]; + let sctp_new = scp_new * abstp_new; + let corrp_new = UN / (UN - fixalp.ali1[idp_idx] * sctp_new); + + dsft1p = corrp_new * (s0p_new * rad.demt1[idp_idx] * emisip_new - s0p_new * rad.dabt1[idp_idx] * abstp_new); + dsfn1p = corrp_new * (s0p_new * rad.demn1[idp_idx] * emisip_new - s0p_new * rad.dabn1[idp_idx] * abstp_new); + dsfm1p = corrp_new * (s0p_new * rad.demm1[idp_idx] * emisip_new - s0p_new * rad.dabm1[idp_idx] * abstp_new); + dsfp1p = vec![0.0; nlvexp]; + for ii in 0..nlvexp { + dsfp1p[ii] = corrp_new * (s0p_new * rad.demp1[ii][idp_idx] * emisip_new - s0p_new * rad.dabp1[ii][idp_idx] * abstp_new); + } + + // 更新 DSFDT, DSFDN, DSFDM 等 + if params.irder == 1 || params.irder == 3 { + fixalp.dsfdt[id_idx] = dsft1_curr * fixalp.ali1[id_idx]; + fixalp.dsfdn[id_idx] = dsfn1_curr * fixalp.ali1[id_idx]; + fixalp.dsfdm[id_idx] = dsfm1_curr * fixalp.ali1[id_idx]; + } + if params.irder > 1 { + for ii in 0..nlvexp { + fixalp.dsfdp[ii][id_idx] = dsfp1_curr[ii] * fixalp.ali1[id_idx]; + } + } + + // 流体静力学平衡方程 + if lnskip { + let d0 = ww * model.fak1[id_idx]; + let a0 = ww * model.fak1[id_idx - 1]; + model.fprd[id_idx] += d0 * model.rad1[id_idx] - a0 * model.rad1[id_idx - 1]; + let e0 = d0 * fixalp.alim1[id_idx] - a0 * fixalp.ali1[id_idx - 1]; + let d0_new = d0 * fixalp.ali1[id_idx] - a0 * fixalp.alip1[id_idx - 1]; + model.heit[id_idx] += d0_new * dsft1_curr; + model.hein[id_idx] += d0_new * dsfn1_curr; + model.heim[id_idx] += d0_new * dsfm1_curr; + model.heitm[id_idx] += e0 * dsft1m; + model.heinm[id_idx] += e0 * dsfn1m; + model.heimm[id_idx] += e0 * dsfm1m; + for ii in 0..nlvexp { + model.heip[ii][id_idx] += d0_new * dsfp1_curr[ii]; + model.heipm[ii][id_idx] += e0 * dsfp1m[ii]; + } + } + + // 辐射平衡微分方程部分 + let ddt = UN / (model.absot[id_idx] + model.absot[id_idx - 1]); + let dt = ddt / model.deldmz[id_idx - 1]; + let fl = (model.rad1[id_idx] * model.fak1[id_idx] - model.rad1[id_idx - 1] * model.fak1[id_idx - 1]) * dt; + model.flfix[id_idx] += ww * fl; + model.flrd[id_idx] += model.w[ij - 1] * fl; + + if model.redif[id_idx] > 0.0 { + if params.ifalih == 0 { + let d0 = ww * model.fak1[id_idx] * dt; + let a0 = ww * model.fak1[id_idx - 1] * dt; + let d0m = d0 * fixalp.alim1[id_idx] - a0 * fixalp.ali1[id_idx - 1]; + let d0_new = d0 * fixalp.ali1[id_idx] - a0 * fixalp.alip1[id_idx - 1]; + let e0 = ww * fl * ddt; + model.redx[id_idx] += e0 * rad.abso1[id_idx]; + model.redxm[id_idx] += e0 * rad.abso1[id_idx - 1]; + let e0m = e0 * model.densi[id_idx - 1]; + let e0_new = e0 * model.densi[id_idx]; + model.redt[id_idx] += d0_new * dsft1_curr - e0_new * rad.dabt1[id_idx]; + model.redtm[id_idx] += d0m * dsft1m - e0m * rad.dabt1[id_idx - 1]; + model.redn[id_idx] += d0_new * dsfn1_curr - e0_new * rad.dabn1[id_idx]; + model.rednm[id_idx] += d0m * dsfn1m - e0m * rad.dabn1[id_idx - 1]; + model.redm[id_idx] += d0_new * dsfm1_curr - e0_new * rad.dabm1[id_idx]; + model.redmm[id_idx] += d0m * dsfm1m - e0m * rad.dabm1[id_idx - 1]; + for ii in 0..nlvexp { + model.redp[ii][id_idx] += d0_new * dsfp1_curr[ii] - e0_new * rad.dabp1[ii][id_idx]; + model.redpm[ii][id_idx] += d0m * dsfp1m[ii] - e0m * rad.dabp1[ii][id_idx - 1]; + } + } else { + let d0 = ww * fixalp.alih1[id_idx]; + model.redt[id_idx] += d0 * dsft1_curr; + model.redn[id_idx] += d0 * dsfn1_curr; + model.redm[id_idx] += d0 * dsfm1_curr; + for ii in 0..nlvexp { + model.redp[ii][id_idx] += d0 * dsfp1_curr[ii]; + } + } + } + + // 辐射平衡积分方程部分 + if model.reint[id_idx] > 0.0 { + let abst_true = rad.abso1[id_idx] - model.elscat[id_idx]; + let d0 = abst_true * fixalp.ali1[id_idx]; + model.fcooli[id_idx] += ww * (rad.emis1[id_idx] - abst_true * model.rad1[id_idx]); + model.rein[id_idx] += ww * (d0 * dsfn1_curr + + model.rad1[id_idx] * (rad.dabn1[id_idx] - model.sigec[ij - 1]) + - rad.demn1[id_idx]); + model.reit[id_idx] += ww * (d0 * dsft1_curr + model.rad1[id_idx] * rad.dabt1[id_idx] - rad.demt1[id_idx]); + model.reim[id_idx] += ww * (d0 * dsfm1_curr + model.rad1[id_idx] * rad.dabm1[id_idx] - rad.demm1[id_idx]); + for ii in 0..nlvexp { + model.reip[ii][id_idx] += ww * (d0 * dsfp1_curr[ii] + + model.rad1[id_idx] * rad.dabp1[ii][id_idx] + - rad.demp1[ii][id_idx]); + } + } + } + + // 3. 最深点 ID=ND + let id = nd; + let id_idx = id - 1; + let lnskip = model.lskip[id_idx][ij - 1] == 0; + + // 保存前一个点的值 + let dsftmm = dsft1m; + let dsfnmm = dsfn1m; + let dsfmmm = dsfm1m; + let mut dsfpmm = vec![0.0; nlvexp]; + for ii in 0..nlvexp { + dsfpmm[ii] = dsfp1m[ii]; + } + + // 更新当前点为上一个点的 ID+1 值 + dsft1m = dsft1; + dsfn1m = dsfn1; + dsfm1m = dsfm1; + for ii in 0..nlvexp { + dsfp1m[ii] = dsfp1[ii]; + } + + let dsft1_curr = dsft1p; + let dsfn1_curr = dsfn1p; + let dsfm1_curr = dsfm1p; + let dsfp1_curr = dsfp1p.clone(); + + // 初始化 ID-1 的导数(用于边界条件) + let mut dsft1d: f64 = 0.0; + let mut dsfn1d: f64 = 0.0; + let mut dsfm1d: f64 = 0.0; + let mut dsfp1d = vec![0.0; nlvexp]; + + // 初始化 DBDT 和 E0(用于改进边界条件的积分方程) + // 这些变量在边界条件块中计算,在积分方程块中使用 + let mut dbdt: f64 = 0.0; + let mut e0: f64 = 0.0; + + // 改进的下边界条件 + if params.ibc > 0 && params.idisk == 0 { + let dt = UN / (model.deldmz[id_idx - 1] * (model.absot[id_idx] + model.absot[id_idx - 1])); + let plad = model.xkfb[id_idx] / model.xkf1[id_idx]; + dbdt = plad / model.xkf1[id_idx] * model.hkt21[id_idx] * model.freq[ij - 1] * dt; + + if params.ibc == 1 { + // 简单边界条件 + fixalp.dsfdt[id_idx] = (dsft1_curr + dbdt) * fixalp.ali1[id_idx]; + } else if params.ibc >= 2 { + // 改进的边界条件 + let plam = model.xkfb[id_idx - 1] / model.xkf1[id_idx - 1]; + let tau23 = t23 * dt; + let tau43 = 4.0 / 3.0 * dt; + let d0 = (plad * (UN + tau43) - tau43 * plam * dt) * dt * dt; + let rhd = model.deldmz[id_idx - 1] * model.densi[id_idx]; + e0 = d0 * rhd; + + let dsft1_new = dsft1_curr + dbdt * (UN + tau23) - e0 * rad.dabt1[id_idx]; + let dsfn1_new = dsfn1_curr - e0 * (rad.dabn1[id_idx] + rad.abso1[id_idx] * model.densim[id_idx]); + let dsfm1_new = dsfm1_curr - e0 * rad.dabm1[id_idx]; + let mut dsfp1_new = dsfp1_curr.clone(); + for ii in 0..nlvexp { + dsfp1_new[ii] = dsfp1_curr[ii] - e0 * rad.dabp1[ii][id_idx]; + } + + if params.ibc >= 3 { + let dbdtm = plam / model.xkf1[id_idx - 1] * model.hkt21[id_idx - 1] * model.freq[ij - 1] * dt; + let rhd = model.deldmz[id_idx - 1] * model.densi[id_idx - 1]; + let e0 = d0 * rhd; + dsft1d = -dbdtm * dt * t23 - e0 * rad.dabt1[id_idx - 1]; + dsfn1d = -e0 * (rad.dabn1[id_idx - 1] + rad.abso1[id_idx - 1] * model.densim[id_idx - 1]); + dsfm1d = -e0 * rad.dabm1[id_idx - 1]; + for ii in 0..nlvexp { + dsfp1d[ii] = -e0 * rad.dabp1[ii][id_idx - 1]; + } + } + + // 更新 DSFDT, DSFDN, DSFDM + if params.irder == 1 || params.irder == 3 { + fixalp.dsfdt[id_idx] = dsft1_new * fixalp.ali1[id_idx]; + fixalp.dsfdn[id_idx] = dsfn1_new * fixalp.ali1[id_idx]; + fixalp.dsfdm[id_idx] = dsfm1_new * fixalp.ali1[id_idx]; + } + if params.irder > 1 { + for ii in 0..nlvexp { + fixalp.dsfdp[ii][id_idx] = dsfp1_new[ii] * fixalp.ali1[id_idx]; + } + } + } else { + // IBC == 1 的情况 + if params.irder == 1 || params.irder == 3 { + fixalp.dsfdt[id_idx] = (dsft1_curr + dbdt) * fixalp.ali1[id_idx]; + fixalp.dsfdn[id_idx] = dsfn1_curr * fixalp.ali1[id_idx]; + fixalp.dsfdm[id_idx] = dsfm1_curr * fixalp.ali1[id_idx]; + } + if params.irder > 1 { + for ii in 0..nlvexp { + fixalp.dsfdp[ii][id_idx] = dsfp1_curr[ii] * fixalp.ali1[id_idx]; + } + } + } + } else { + // 标准边界条件 + if params.irder == 1 || params.irder == 3 { + fixalp.dsfdt[id_idx] = dsft1_curr * fixalp.ali1[id_idx]; + fixalp.dsfdn[id_idx] = dsfn1_curr * fixalp.ali1[id_idx]; + fixalp.dsfdm[id_idx] = dsfm1_curr * fixalp.ali1[id_idx]; + } + if params.irder > 1 { + for ii in 0..nlvexp { + fixalp.dsfdp[ii][id_idx] = dsfp1_curr[ii] * fixalp.ali1[id_idx]; + } + } + } + + // 流体静力学平衡方程 + if lnskip { + let d0 = ww * model.fak1[id_idx]; + let a0 = ww * model.fak1[id_idx - 1]; + model.fprd[id_idx] += d0 * model.rad1[id_idx] - a0 * model.rad1[id_idx - 1]; + let e0 = d0 * fixalp.alim1[id_idx] - a0 * fixalp.ali1[id_idx - 1]; + let d0_new = d0 * fixalp.ali1[id_idx] - a0 * fixalp.alip1[id_idx - 1]; + model.heit[id_idx] += d0_new * dsft1_curr; + model.hein[id_idx] += d0_new * dsfn1_curr; + model.heim[id_idx] += d0_new * dsfm1_curr; + model.heitm[id_idx] += e0 * dsft1m; + model.heinm[id_idx] += e0 * dsfn1m; + model.heimm[id_idx] += e0 * dsfm1m; + for ii in 0..nlvexp { + model.heip[ii][id_idx] += d0_new * dsfp1_curr[ii]; + model.heipm[ii][id_idx] += e0 * dsfp1m[ii]; + } + if params.ibc >= 3 { + model.heitm[id_idx] -= d0_new * dsft1d; + model.heinm[id_idx] -= d0_new * dsfn1d; + model.heimm[id_idx] -= d0_new * dsfm1d; + for ii in 0..nlvexp { + model.heipm[ii][id_idx] -= d0_new * dsfp1d[ii]; + } + } + } + + // 辐射平衡微分方程部分 + let ddt = UN / (model.absot[id_idx] + model.absot[id_idx - 1]); + let dt = ddt / model.deldmz[id_idx - 1]; + let fl = (model.rad1[id_idx] * model.fak1[id_idx] - model.rad1[id_idx - 1] * model.fak1[id_idx - 1]) * dt; + model.flfix[id_idx] += ww * fl; + model.flrd[id_idx] += model.w[ij - 1] * fl; + + if model.redif[id_idx] > 0.0 { + let d0 = ww * model.fak1[id_idx] * dt; + let a0 = ww * model.fak1[id_idx - 1] * dt; + let d0m = d0 * fixalp.alim1[id_idx] - a0 * fixalp.ali1[id_idx - 1]; + let d0_new = d0 * fixalp.ali1[id_idx] - a0 * fixalp.alip1[id_idx - 1]; + let e0 = ww * fl * ddt; + model.redx[id_idx] += e0 * rad.abso1[id_idx]; + model.redxm[id_idx] += e0 * rad.abso1[id_idx - 1]; + let e0m = e0 * model.densi[id_idx - 1]; + let e0_new = e0 * model.densi[id_idx]; + model.redt[id_idx] += d0_new * dsft1_curr - e0_new * rad.dabt1[id_idx]; + model.redtm[id_idx] += d0m * dsft1m - e0m * rad.dabt1[id_idx - 1]; + model.redn[id_idx] += d0_new * dsfn1_curr - e0_new * rad.dabn1[id_idx]; + model.rednm[id_idx] += d0m * dsfn1m - e0m * rad.dabn1[id_idx - 1]; + model.redm[id_idx] += d0_new * dsfm1_curr - e0_new * rad.dabm1[id_idx]; + model.redmm[id_idx] += d0m * dsfm1m - e0m * rad.dabm1[id_idx - 1]; + for ii in 0..nlvexp { + model.redp[ii][id_idx] += d0_new * dsfp1_curr[ii] - e0_new * rad.dabp1[ii][id_idx]; + model.redpm[ii][id_idx] += d0m * dsfp1m[ii] - e0m * rad.dabp1[ii][id_idx - 1]; + } + if params.ibc >= 3 { + model.redtm[id_idx] += d0_new * dsft1d; + model.rednm[id_idx] += d0_new * dsfn1d; + model.redmm[id_idx] += d0_new * dsfm1d; + for ii in 0..nlvexp { + model.redpm[ii][id_idx] += d0_new * dsfp1d[ii]; + } + } + } + + // 辐射平衡积分方程部分 + if model.reint[id_idx] > 0.0 { + let abst_true = rad.abso1[id_idx] - model.elscat[id_idx]; + let d0 = abst_true * fixalp.ali1[id_idx]; + model.fcooli[id_idx] += ww * (rad.emis1[id_idx] - abst_true * model.rad1[id_idx]); + model.rein[id_idx] += ww * (d0 * dsfn1_curr + + model.rad1[id_idx] * (rad.dabn1[id_idx] - model.sigec[ij - 1]) + - rad.demn1[id_idx]); + model.reim[id_idx] += ww * (d0 * dsfm1_curr + model.rad1[id_idx] * rad.dabm1[id_idx] - rad.demm1[id_idx]); + for ii in 0..nlvexp { + model.reip[ii][id_idx] += ww * (d0 * dsfp1_curr[ii] + + model.rad1[id_idx] * rad.dabp1[ii][id_idx] + - rad.demp1[ii][id_idx]); + } + if params.ibc == 0 { + model.reit[id_idx] += ww * (d0 * dsft1_curr + model.rad1[id_idx] * rad.dabt1[id_idx] - rad.demt1[id_idx]); + } else { + // 改进的边界条件下的积分方程 + // Fortran: REIT(ID)=REIT(ID)+WW*(D0*(DSFT1-DBDT)+E0*DABT1(ID)+ + // * RAD1(ID)*DABT1(ID)-DEMT1(ID)+ + // * ALI1(ID)/ABST*DBDT) + model.reit[id_idx] += ww * (d0 * (dsft1_curr - dbdt) + e0 * rad.dabt1[id_idx] + + model.rad1[id_idx] * rad.dabt1[id_idx] - rad.demt1[id_idx] + + fixalp.ali1[id_idx] / abst_true * dbdt); + } + } +} + +/// 处理 ILASCT == 0 但 ILMCOR != 3 的情况 +/// 这个路径与标准路径的主要区别是源函数导数的计算方式 +#[allow(clippy::too_many_arguments)] +fn process_ilasct_zero( + params: &Alifr1Params, + fixalp: &mut FixAlp, + model: &mut Alifr1ModelState, + rad: &Alifr1RadState, + ij: usize, + nd: usize, + nlvexp: usize, + ww: f64, +) { + // 常量 + let t23 = TWO / 3.0; + + // 初始化中间变量 + let mut dsft1m: f64 = 0.0; + let mut dsfn1m: f64 = 0.0; + let mut dsfp1m = vec![0.0; nlvexp]; + + // 1. 第一个深度点 (ID=1) + let id = 1; + let id_idx = id - 1; + let lnskip = model.lskip[id_idx][ij - 1] == 0; + + // 基本辅助量 - 源函数的导数(非标准方式) + let emisiv = UN / rad.emis1[id_idx]; + let abst = UN / (rad.abso1[id_idx] - model.elscat[id_idx]); + let s0 = rad.emis1[id_idx] * abst; + let dsfn1 = s0 * (rad.demn1[id_idx] * emisiv - (rad.dabn1[id_idx] - model.sigec[ij - 1]) * abst); + let dsft1 = s0 * (rad.demt1[id_idx] * emisiv - rad.dabt1[id_idx] * abst); + let mut dsfp1 = vec![0.0; nlvexp]; + for ii in 0..nlvexp { + dsfp1[ii] = s0 * (rad.demp1[ii][id_idx] * emisiv - rad.dabp1[ii][id_idx] * abst); + } + + // ID+1 的值 + let idp_idx = id; + let emisip = UN / rad.emis1[idp_idx]; + let abstp = UN / (rad.abso1[idp_idx] - model.elscat[idp_idx]); + let s0p = rad.emis1[idp_idx] * abstp; + let mut dsfn1p = s0p * (rad.demn1[idp_idx] * emisip - (rad.dabn1[idp_idx] - model.sigec[ij - 1]) * abstp); + let mut dsft1p = s0p * (rad.demt1[idp_idx] * emisip - rad.dabt1[idp_idx] * abstp); + let mut dsfp1p = vec![0.0; nlvexp]; + for ii in 0..nlvexp { + dsfp1p[ii] = s0p * (rad.demp1[ii][idp_idx] * emisip - rad.dabp1[ii][idp_idx] * abstp); + } + + // 更新 DSFDT, DSFDN 等 + if params.irder == 1 || params.irder == 3 { + fixalp.dsfdt[id_idx] = dsft1 * fixalp.ali1[id_idx]; + fixalp.dsfdn[id_idx] = dsfn1 * fixalp.ali1[id_idx]; + } + if params.irder > 1 { + for ii in 0..nlvexp { + fixalp.dsfdp[ii][id_idx] = dsfp1[ii] * fixalp.ali1[id_idx]; + } + } + + // 流体静力学平衡量 + let wf = ww * model.fh[ij - 1]; + if lnskip { + model.fprd[id_idx] += wf * rad.abso1[id_idx] * model.rad1[id_idx] + - ww * model.hextrd[ij - 1] * rad.abso1[id_idx]; + let e0 = wf * model.rad1[id_idx]; + let d0 = wf * rad.abso1[id_idx] * fixalp.ali1[id_idx]; + model.heit[id_idx] += d0 * dsft1 + e0 * rad.dabt1[id_idx]; + model.hein[id_idx] += d0 * dsfn1 + e0 * rad.dabn1[id_idx]; + for ii in 0..nlvexp { + model.heip[ii][id_idx] += d0 * dsfp1[ii] + e0 * rad.dabp1[ii][id_idx]; + } + } + + // 辐射平衡微分方程部分 + model.flfix[id_idx] += wf * model.rad1[id_idx] - ww * model.hextrd[ij - 1]; + model.flrd[id_idx] += model.w[ij - 1] * model.fh[ij - 1] * model.rad1[id_idx] + - model.w[ij - 1] * HALF * model.extrad[ij - 1]; + if model.redif[id_idx] > 0.0 { + let wf = wf * fixalp.ali1[id_idx]; + model.redt[id_idx] += wf * dsft1; + model.redn[id_idx] += wf * dsfn1; + for ii in 0..nlvexp { + model.redp[ii][id_idx] += wf * dsfp1[ii]; + } + } + + // 辐射平衡积分方程部分 + if model.reint[id_idx] > 0.0 { + let abst_true = rad.abso1[id_idx] - model.elscat[id_idx]; + let wwk = ww * abst_true; + model.fcooli[id_idx] += ww * (rad.emis1[id_idx] - abst_true * model.rad1[id_idx]); + let d0 = ww * (fixalp.ali1[id_idx] - UN) * abst_true; + let e0 = ww * (model.rad1[id_idx] - s0); + model.rein[id_idx] += d0 * dsfn1 + e0 * (rad.dabn1[id_idx] - model.sigec[ij - 1]); + model.reit[id_idx] += d0 * dsft1 + e0 * rad.dabt1[id_idx]; + for ii in 0..nlvexp { + model.reip[ii][id_idx] += d0 * dsfp1[ii] + e0 * rad.dabp1[ii][id_idx]; + } + } + + // 2. 深度循环 (ID = 2 to ND-1) + for id in 2..nd { + let id_idx = id - 1; + let lnskip = model.lskip[id_idx][ij - 1] == 0; + + // 保存前一个点的值 + dsft1m = dsft1; + dsfn1m = dsfn1; + for ii in 0..nlvexp { + dsfp1m[ii] = dsfp1[ii]; + } + + // 更新当前点为上一个点的 ID+1 值 + let dsft1_curr = dsft1p; + let dsfn1_curr = dsfn1p; + let dsfp1_curr = dsfp1p.clone(); + + // 计算 ID+1 的值 + let idp_idx = id; + let emisip_new = UN / rad.emis1[idp_idx]; + let abstp_new = UN / (rad.abso1[idp_idx] - model.elscat[idp_idx]); + let s0p_new = rad.emis1[idp_idx] * abstp_new; + let dsfn1p_new = s0p_new * (rad.demn1[idp_idx] * emisip_new - (rad.dabn1[idp_idx] - model.sigec[ij - 1]) * abstp_new); + let dsft1p_new = s0p_new * (rad.demt1[idp_idx] * emisip_new - rad.dabt1[idp_idx] * abstp_new); + let mut dsfp1p_new = vec![0.0; nlvexp]; + for ii in 0..nlvexp { + dsfp1p_new[ii] = s0p_new * (rad.demp1[ii][idp_idx] * emisip_new - rad.dabp1[ii][idp_idx] * abstp_new); + } + dsft1p = dsft1p_new; + dsfn1p = dsfn1p_new; + dsfp1p = dsfp1p_new; + + // 更新 DSFDT, DSFDN 等 + if params.irder == 1 || params.irder == 3 { + fixalp.dsfdt[id_idx] = dsft1_curr * fixalp.ali1[id_idx]; + fixalp.dsfdn[id_idx] = dsfn1_curr * fixalp.ali1[id_idx]; + } + if params.irder > 1 { + for ii in 0..nlvexp { + fixalp.dsfdp[ii][id_idx] = dsfp1_curr[ii] * fixalp.ali1[id_idx]; + } + } + + // 流体静力学平衡方程 + if lnskip { + let d0 = ww * model.fak1[id_idx]; + let a0 = ww * model.fak1[id_idx - 1]; + model.fprd[id_idx] += d0 * model.rad1[id_idx] - a0 * model.rad1[id_idx - 1]; + let e0 = d0 * fixalp.alim1[id_idx] - a0 * fixalp.ali1[id_idx - 1]; + let d0_new = d0 * fixalp.ali1[id_idx] - a0 * fixalp.alip1[id_idx - 1]; + model.heit[id_idx] += d0_new * dsft1_curr; + model.hein[id_idx] += d0_new * dsfn1_curr; + model.heitm[id_idx] += e0 * dsft1m; + model.heinm[id_idx] += e0 * dsfn1m; + for ii in 0..nlvexp { + model.heip[ii][id_idx] += d0_new * dsfp1_curr[ii]; + model.heipm[ii][id_idx] += e0 * dsfp1m[ii]; + } + } + + // 辐射平衡微分方程部分 + let ddt = UN / (model.absot[id_idx] + model.absot[id_idx - 1]); + let dt = ddt / model.deldmz[id_idx - 1]; + let fl = (model.rad1[id_idx] * model.fak1[id_idx] - model.rad1[id_idx - 1] * model.fak1[id_idx - 1]) * dt; + model.flfix[id_idx] += ww * fl; + + if model.redif[id_idx] > 0.0 { + let d0 = ww * model.fak1[id_idx] * dt; + let a0 = ww * model.fak1[id_idx - 1] * dt; + let d0m = d0 * fixalp.alim1[id_idx] - a0 * fixalp.ali1[id_idx - 1]; + let d0_new = d0 * fixalp.ali1[id_idx] - a0 * fixalp.alip1[id_idx - 1]; + let e0 = ww * fl * ddt; + model.redx[id_idx] += e0 * rad.abso1[id_idx]; + model.redxm[id_idx] += e0 * rad.abso1[id_idx - 1]; + let e0m = e0 * model.densi[id_idx - 1]; + let e0_new = e0 * model.densi[id_idx]; + model.redt[id_idx] += d0_new * dsft1_curr - e0_new * rad.dabt1[id_idx]; + model.redtm[id_idx] += d0m * dsft1m - e0m * rad.dabt1[id_idx - 1]; + model.redn[id_idx] += d0_new * dsfn1_curr - e0_new * rad.dabn1[id_idx]; + model.rednm[id_idx] += d0m * dsfn1m - e0m * rad.dabn1[id_idx - 1]; + for ii in 0..nlvexp { + model.redp[ii][id_idx] += d0_new * dsfp1_curr[ii] - e0_new * rad.dabp1[ii][id_idx]; + model.redpm[ii][id_idx] += d0m * dsfp1m[ii] - e0m * rad.dabp1[ii][id_idx - 1]; + } + } + + // 辐射平衡积分方程部分 + if model.reint[id_idx] > 0.0 { + let abst_true = rad.abso1[id_idx] - model.elscat[id_idx]; + let wwk = ww * abst_true; + model.fcooli[id_idx] += ww * (rad.emis1[id_idx] - abst_true * model.rad1[id_idx]); + let d0 = ww * (fixalp.ali1[id_idx] - UN) * abst_true; + let e0 = ww * (model.rad1[id_idx] - s0); + model.rein[id_idx] += d0 * dsfn1_curr + e0 * (rad.dabn1[id_idx] - model.sigec[ij - 1]); + model.reit[id_idx] += d0 * dsft1_curr + e0 * rad.dabt1[id_idx]; + for ii in 0..nlvexp { + model.reip[ii][id_idx] += d0 * dsfp1_curr[ii] + e0 * rad.dabp1[ii][id_idx]; + } + } + } + + // 3. 最深点 ID=ND + let id = nd; + let id_idx = id - 1; + let lnskip = model.lskip[id_idx][ij - 1] == 0; + + // 保存前一个点的值 + let _dsftmm = dsft1m; + let _dsfnmm = dsfn1m; + let mut _dsfpmm = vec![0.0; nlvexp]; + for ii in 0..nlvexp { + _dsfpmm[ii] = dsfp1m[ii]; + } + + // 更新当前点为上一个点的 ID+1 值 + dsft1m = dsft1; + dsfn1m = dsfn1; + for ii in 0..nlvexp { + dsfp1m[ii] = dsfp1[ii]; + } + + let s0_curr = s0p; + let dsft1_curr = dsft1p; + let dsfn1_curr = dsfn1p; + let dsfp1_curr = dsfp1p.clone(); + + // 初始化 ID-1 的导数(用于边界条件) + let mut dsft1d: f64 = 0.0; + let mut dsfn1d: f64 = 0.0; + let mut dsfp1d = vec![0.0; nlvexp]; + + // 改进的下边界条件 + let mut dbdt: f64 = 0.0; + if params.ibc > 0 && params.idisk == 0 { + let dt = UN / (model.deldmz[id_idx - 1] * (model.absot[id_idx] + model.absot[id_idx - 1])); + let plad = model.xkfb[id_idx] / model.xkf1[id_idx]; + dbdt = plad / model.xkf1[id_idx] * model.hkt21[id_idx] * model.freq[ij - 1] * dt; + + if params.ibc == 1 { + // 简单边界条件 - DSFT1 = DSFT1 + DBDT + let dsft1_new = dsft1_curr + dbdt; + if params.irder == 1 || params.irder == 3 { + fixalp.dsfdt[id_idx] = dsft1_new * fixalp.ali1[id_idx]; + fixalp.dsfdn[id_idx] = dsfn1_curr * fixalp.ali1[id_idx]; + } + if params.irder > 1 { + for ii in 0..nlvexp { + fixalp.dsfdp[ii][id_idx] = dsfp1_curr[ii] * fixalp.ali1[id_idx]; + } + } + } else if params.ibc >= 2 { + // 改进的边界条件 + let plam = model.xkfb[id_idx - 1] / model.xkf1[id_idx - 1]; + let tau23 = t23 * dt; + let tau43 = 4.0 / 3.0 * dt; + let d0_val = (plad * (UN + tau43) - tau43 * plam * dt) * dt * dt; + let rhd = model.deldmz[id_idx - 1] * model.densi[id_idx]; + let e0 = d0_val * rhd; + + let dsft1_new = dsft1_curr + dbdt * (UN + tau23) - e0 * rad.dabt1[id_idx]; + let dsfn1_new = dsfn1_curr - e0 * (rad.dabn1[id_idx] + rad.abso1[id_idx] * model.densim[id_idx]); + let mut dsfp1_new = dsfp1_curr.clone(); + for ii in 0..nlvexp { + dsfp1_new[ii] = dsfp1_curr[ii] - e0 * rad.dabp1[ii][id_idx]; + } + + if params.ibc >= 3 { + let dbdtm = plam / model.xkf1[id_idx - 1] * model.hkt21[id_idx - 1] * model.freq[ij - 1] * dt; + let rhd = model.deldmz[id_idx - 1] * model.densi[id_idx - 1]; + let e0 = d0_val * rhd; + dsft1d = -dbdtm * dt * t23 - e0 * rad.dabt1[id_idx - 1]; + dsfn1d = -e0 * (rad.dabn1[id_idx - 1] + rad.abso1[id_idx - 1] * model.densim[id_idx - 1]); + for ii in 0..nlvexp { + dsfp1d[ii] = -e0 * rad.dabp1[ii][id_idx - 1]; + } + } + + // 更新 DSFDT, DSFDN + if params.irder == 1 || params.irder == 3 { + fixalp.dsfdt[id_idx] = dsft1_new * fixalp.ali1[id_idx]; + fixalp.dsfdn[id_idx] = dsfn1_new * fixalp.ali1[id_idx]; + } + if params.irder > 1 { + for ii in 0..nlvexp { + fixalp.dsfdp[ii][id_idx] = dsfp1_new[ii] * fixalp.ali1[id_idx]; + } + } + } else { + // IBC == 1 的情况 + if params.irder == 1 || params.irder == 3 { + fixalp.dsfdt[id_idx] = (dsft1_curr + dbdt) * fixalp.ali1[id_idx]; + fixalp.dsfdn[id_idx] = dsfn1_curr * fixalp.ali1[id_idx]; + } + if params.irder > 1 { + for ii in 0..nlvexp { + fixalp.dsfdp[ii][id_idx] = dsfp1_curr[ii] * fixalp.ali1[id_idx]; + } + } + } + } else { + // 标准边界条件 + if params.irder == 1 || params.irder == 3 { + fixalp.dsfdt[id_idx] = dsft1_curr * fixalp.ali1[id_idx]; + fixalp.dsfdn[id_idx] = dsfn1_curr * fixalp.ali1[id_idx]; + } + if params.irder > 1 { + for ii in 0..nlvexp { + fixalp.dsfdp[ii][id_idx] = dsfp1_curr[ii] * fixalp.ali1[id_idx]; + } + } + } + + // 流体静力学平衡方程 + if lnskip { + let d0 = ww * model.fak1[id_idx]; + let a0 = ww * model.fak1[id_idx - 1]; + model.fprd[id_idx] += d0 * model.rad1[id_idx] - a0 * model.rad1[id_idx - 1]; + let e0 = d0 * fixalp.alim1[id_idx] - a0 * fixalp.ali1[id_idx - 1]; + let d0_new = d0 * fixalp.ali1[id_idx] - a0 * fixalp.alip1[id_idx - 1]; + model.heit[id_idx] += d0_new * dsft1_curr; + model.hein[id_idx] += d0_new * dsfn1_curr; + model.heitm[id_idx] += e0 * dsft1m; + model.heinm[id_idx] += e0 * dsfn1m; + for ii in 0..nlvexp { + model.heip[ii][id_idx] += d0_new * dsfp1_curr[ii]; + model.heipm[ii][id_idx] += e0 * dsfp1m[ii]; + } + if params.ibc >= 3 { + model.heitm[id_idx] -= d0_new * dsft1d; + model.heinm[id_idx] -= d0_new * dsfn1d; + for ii in 0..nlvexp { + model.heipm[ii][id_idx] -= d0_new * dsfp1d[ii]; + } + } + } + + // 辐射平衡微分方程部分 + let ddt = UN / (model.absot[id_idx] + model.absot[id_idx - 1]); + let dt = ddt / model.deldmz[id_idx - 1]; + let fl = (model.rad1[id_idx] * model.fak1[id_idx] - model.rad1[id_idx - 1] * model.fak1[id_idx - 1]) * dt; + model.flfix[id_idx] += ww * fl; + + if model.redif[id_idx] > 0.0 { + let d0 = ww * model.fak1[id_idx] * dt; + let a0 = ww * model.fak1[id_idx - 1] * dt; + let d0m = d0 * fixalp.alim1[id_idx] - a0 * fixalp.ali1[id_idx - 1]; + let d0_new = d0 * fixalp.ali1[id_idx] - a0 * fixalp.alip1[id_idx - 1]; + let e0 = ww * fl * ddt; + model.redx[id_idx] += e0 * rad.abso1[id_idx]; + model.redxm[id_idx] += e0 * rad.abso1[id_idx - 1]; + let e0m = e0 * model.densi[id_idx - 1]; + let e0_new = e0 * model.densi[id_idx]; + model.redt[id_idx] += d0_new * dsft1_curr - e0_new * rad.dabt1[id_idx]; + model.redtm[id_idx] += d0m * dsft1m - e0m * rad.dabt1[id_idx - 1]; + model.redn[id_idx] += d0_new * dsfn1_curr - e0_new * rad.dabn1[id_idx]; + model.rednm[id_idx] += d0m * dsfn1m - e0m * rad.dabn1[id_idx - 1]; + for ii in 0..nlvexp { + model.redp[ii][id_idx] += d0_new * dsfp1_curr[ii] - e0_new * rad.dabp1[ii][id_idx]; + model.redpm[ii][id_idx] += d0m * dsfp1m[ii] - e0m * rad.dabp1[ii][id_idx - 1]; + } + if params.ibc >= 3 { + model.redtm[id_idx] += d0_new * dsft1d; + model.rednm[id_idx] += d0_new * dsfn1d; + for ii in 0..nlvexp { + model.redpm[ii][id_idx] += d0_new * dsfp1d[ii]; + } + } + } + + // 辐射平衡积分方程部分 + if model.reint[id_idx] > 0.0 { + let abst_true = rad.abso1[id_idx] - model.elscat[id_idx]; + let _wwk = ww * abst_true; + model.fcooli[id_idx] += ww * (rad.emis1[id_idx] - abst_true * model.rad1[id_idx]); + let d0 = ww * (fixalp.ali1[id_idx] - UN) * abst_true; + let e0 = ww * (model.rad1[id_idx] - s0_curr); + model.rein[id_idx] += d0 * dsfn1_curr + e0 * (rad.dabn1[id_idx] - model.sigec[ij - 1]); + if params.ibc == 0 { + model.reit[id_idx] += d0 * dsft1_curr + e0 * rad.dabt1[id_idx]; + } else { + // 改进的边界条件下的积分方程 + model.reit[id_idx] += d0 * (dsft1_curr - dbdt) + e0 * rad.dabt1[id_idx] + + fixalp.ali1[id_idx] / abst_true * dbdt; + } + for ii in 0..nlvexp { + model.reip[ii][id_idx] += d0 * dsfp1_curr[ii] + e0 * rad.dabp1[ii][id_idx]; + } + } +} + +/// 处理 ILASCT != 0 的情况 +/// 对应 Fortran GOTO 299 块 +#[allow(clippy::too_many_arguments)] +fn process_ilasct_nonzero( + params: &Alifr1Params, + fixalp: &mut FixAlp, + model: &mut Alifr1ModelState, + rad: &Alifr1RadState, + ij: usize, + nd: usize, + nlvexp: usize, + ww: f64, +) { + // 常量 + let t23 = TWO / 3.0; + let t43 = 4.0 / 3.0; + let t23_inv = 2.0 / 3.0; // T23 in Fortran + + // 初始化中间变量 + let mut dsft1m: f64 = 0.0; + let mut dsfn1m: f64 = 0.0; + let mut dsfp1m = vec![0.0; nlvexp]; + + // 1. 第一个深度点 (ID=1) + let id = 1; + let id_idx = id - 1; + let lnskip = model.lskip[id_idx][ij - 1] == 0; + + // 基本辅助量 - 源函数的导数 + // 注意:这里 ABST = UN/ABSO1(ID),不同于 ILASCT == 0 的情况 + let emisiv = UN / rad.emis1[id_idx]; + let abst = UN / rad.abso1[id_idx]; + let s0 = rad.emis1[id_idx] * abst; + let dsfn1 = s0 * (rad.demn1[id_idx] * emisiv - rad.dabn1[id_idx] * abst); + let dsft1 = s0 * (rad.demt1[id_idx] * emisiv - rad.dabt1[id_idx] * abst); + let mut dsfp1 = vec![0.0; nlvexp]; + for ii in 0..nlvexp { + dsfp1[ii] = s0 * (rad.demp1[ii][id_idx] * emisiv - rad.dabp1[ii][id_idx] * abst); + } + + // ID+1 的值 + let idp_idx = id; + let emisip = UN / rad.emis1[idp_idx]; + let abstp = UN / rad.abso1[idp_idx]; + let s0p = rad.emis1[idp_idx] * abstp; + let mut dsfn1p = s0p * (rad.demn1[idp_idx] * emisip - rad.dabn1[idp_idx] * abstp); + let mut dsft1p = s0p * (rad.demt1[idp_idx] * emisip - rad.dabt1[idp_idx] * abstp); + let mut dsfp1p = vec![0.0; nlvexp]; + for ii in 0..nlvexp { + dsfp1p[ii] = s0p * (rad.demp1[ii][idp_idx] * emisip - rad.dabp1[ii][idp_idx] * abstp); + } + + // 更新 DSFDT, DSFDN 等 + if params.irder == 1 || params.irder == 3 { + fixalp.dsfdt[id_idx] = dsft1 * fixalp.ali1[id_idx]; + fixalp.dsfdn[id_idx] = dsfn1 * fixalp.ali1[id_idx]; + } + if params.irder > 1 { + for ii in 0..nlvexp { + fixalp.dsfdp[ii][id_idx] = dsfp1[ii] * fixalp.ali1[id_idx]; + } + } + + // 流体静力学平衡量 + let wf = ww * model.fh[ij - 1]; + if lnskip { + model.fprd[id_idx] += wf * rad.abso1[id_idx] * model.rad1[id_idx] + - ww * model.hextrd[ij - 1] * rad.abso1[id_idx]; + let e0 = wf * model.rad1[id_idx]; + let d0 = wf * rad.abso1[id_idx] * fixalp.ali1[id_idx]; + model.heit[id_idx] += d0 * dsft1 + e0 * rad.dabt1[id_idx]; + model.hein[id_idx] += d0 * dsfn1 + e0 * rad.dabn1[id_idx]; + for ii in 0..nlvexp { + model.heip[ii][id_idx] += d0 * dsfp1[ii] + e0 * rad.dabp1[ii][id_idx]; + } + } + + // 辐射平衡微分方程部分 + model.flfix[id_idx] += wf * model.rad1[id_idx] - ww * model.hextrd[ij - 1]; + model.flrd[id_idx] += model.w[ij - 1] * model.fh[ij - 1] * model.rad1[id_idx] + - model.w[ij - 1] * HALF * model.extrad[ij - 1]; + if model.redif[id_idx] > 0.0 { + let wf = wf * fixalp.ali1[id_idx]; + model.redt[id_idx] += wf * dsft1; + model.redn[id_idx] += wf * dsfn1; + for ii in 0..nlvexp { + model.redp[ii][id_idx] += wf * dsfp1[ii]; + } + } + + // 辐射平衡积分方程部分 + if model.reint[id_idx] > 0.0 { + let abst_true = rad.abso1[id_idx] - model.elscat[id_idx]; + model.fcooli[id_idx] += ww * (rad.emis1[id_idx] - abst_true * model.rad1[id_idx]); + // 注意:D0 的计算不同于 ILASCT == 0 + let d0 = ww * (abst_true * fixalp.ali1[id_idx] - rad.abso1[id_idx]); + let e0 = ww * (model.rad1[id_idx] - s0); + model.rein[id_idx] += d0 * dsfn1 + e0 * rad.dabn1[id_idx] - ww * model.sigec[ij - 1] * model.rad1[id_idx]; + model.reit[id_idx] += d0 * dsft1 + e0 * rad.dabt1[id_idx]; + for ii in 0..nlvexp { + model.reip[ii][id_idx] += d0 * dsfp1[ii] + e0 * rad.dabp1[ii][id_idx]; + } + } + + // 2. 深度循环 (ID = 2 to ND-1) + for id in 2..nd { + let id_idx = id - 1; + let lnskip = model.lskip[id_idx][ij - 1] == 0; + + // 保存前一个点的值 + dsft1m = dsft1; + dsfn1m = dsfn1; + for ii in 0..nlvexp { + dsfp1m[ii] = dsfp1[ii]; + } + + // 更新当前点为上一个点的 ID+1 值 + let dsft1_curr = dsft1p; + let dsfn1_curr = dsfn1p; + let dsfp1_curr = dsfp1p.clone(); + + // 更新 DSFT1, DSFN1 等 + let dsft1_new = dsft1_curr; + let dsfn1_new = dsfn1_curr; + let dsfp1_new = dsfp1_curr.clone(); + + // 计算 ID+1 的值 + let idp_idx = id; + let emisip_new = UN / rad.emis1[idp_idx]; + let abstp_new = UN / rad.abso1[idp_idx]; + let s0p_new = rad.emis1[idp_idx] * abstp_new; + let dsfn1p_new = s0p_new * (rad.demn1[idp_idx] * emisip_new - rad.dabn1[idp_idx] * abstp_new); + let dsft1p_new = s0p_new * (rad.demt1[idp_idx] * emisip_new - rad.dabt1[idp_idx] * abstp_new); + let mut dsfp1p_new = vec![0.0; nlvexp]; + for ii in 0..nlvexp { + dsfp1p_new[ii] = s0p_new * (rad.demp1[ii][idp_idx] * emisip_new - rad.dabp1[ii][idp_idx] * abstp_new); + } + dsft1p = dsft1p_new; + dsfn1p = dsfn1p_new; + dsfp1p = dsfp1p_new; + + // 更新 DSFDT, DSFDN 等 + if params.irder == 1 || params.irder == 3 { + fixalp.dsfdt[id_idx] = dsft1_new * fixalp.ali1[id_idx]; + fixalp.dsfdn[id_idx] = dsfn1_new * fixalp.ali1[id_idx]; + } + if params.irder > 1 { + for ii in 0..nlvexp { + fixalp.dsfdp[ii][id_idx] = dsfp1_new[ii] * fixalp.ali1[id_idx]; + } + } + + // 流体静力学平衡方程 + if lnskip { + let d0 = ww * model.fak1[id_idx]; + let a0 = ww * model.fak1[id_idx - 1]; + model.fprd[id_idx] += d0 * model.rad1[id_idx] - a0 * model.rad1[id_idx - 1]; + let e0 = d0 * fixalp.alim1[id_idx] - a0 * fixalp.ali1[id_idx - 1]; + let d0_new = d0 * fixalp.ali1[id_idx] - a0 * fixalp.alip1[id_idx - 1]; + model.heit[id_idx] += d0_new * dsft1_new; + model.hein[id_idx] += d0_new * dsfn1_new; + model.heitm[id_idx] += e0 * dsft1m; + model.heinm[id_idx] += e0 * dsfn1m; + for ii in 0..nlvexp { + model.heip[ii][id_idx] += d0_new * dsfp1_new[ii]; + model.heipm[ii][id_idx] += e0 * dsfp1m[ii]; + } + } + + // 辐射平衡微分方程部分 + let ddt = UN / (model.absot[id_idx] + model.absot[id_idx - 1]); + let dt = ddt / model.deldmz[id_idx - 1]; + let fl = (model.rad1[id_idx] * model.fak1[id_idx] - model.rad1[id_idx - 1] * model.fak1[id_idx - 1]) * dt; + model.flfix[id_idx] += ww * fl; + + if model.redif[id_idx] > 0.0 { + let d0 = ww * model.fak1[id_idx] * dt; + let a0 = ww * model.fak1[id_idx - 1] * dt; + let d0m = d0 * fixalp.alim1[id_idx] - a0 * fixalp.ali1[id_idx - 1]; + let d0_new = d0 * fixalp.ali1[id_idx] - a0 * fixalp.alip1[id_idx - 1]; + let e0 = ww * fl * ddt; + model.redx[id_idx] += e0 * rad.abso1[id_idx]; + model.redxm[id_idx] += e0 * rad.abso1[id_idx - 1]; + let e0m = e0 * model.densi[id_idx - 1]; + let e0_new = e0 * model.densi[id_idx]; + model.redt[id_idx] += d0_new * dsft1_new - e0_new * rad.dabt1[id_idx]; + model.redtm[id_idx] += d0m * dsft1m - e0m * rad.dabt1[id_idx - 1]; + model.redn[id_idx] += d0_new * dsfn1_new - e0_new * rad.dabn1[id_idx]; + model.rednm[id_idx] += d0m * dsfn1m - e0m * rad.dabn1[id_idx - 1]; + for ii in 0..nlvexp { + model.redp[ii][id_idx] += d0_new * dsfp1_new[ii] - e0_new * rad.dabp1[ii][id_idx]; + model.redpm[ii][id_idx] += d0m * dsfp1m[ii] - e0m * rad.dabp1[ii][id_idx - 1]; + } + } + + // 辐射平衡积分方程部分 + if model.reint[id_idx] > 0.0 { + let abst_true = rad.abso1[id_idx] - model.elscat[id_idx]; + model.fcooli[id_idx] += ww * (rad.emis1[id_idx] - abst_true * model.rad1[id_idx]); + let d0 = ww * (abst_true * fixalp.ali1[id_idx] - rad.abso1[id_idx]); + let e0 = ww * (model.rad1[id_idx] - s0); + model.rein[id_idx] += d0 * dsfn1_new + e0 * rad.dabn1[id_idx] - ww * model.sigec[ij - 1] * model.rad1[id_idx]; + model.reit[id_idx] += d0 * dsft1_new + e0 * rad.dabt1[id_idx]; + for ii in 0..nlvexp { + model.reip[ii][id_idx] += d0 * dsfp1_new[ii] + e0 * rad.dabp1[ii][id_idx]; + } + } + } + + // 3. 最深点 ID=ND + let id = nd; + let id_idx = id - 1; + let lnskip = model.lskip[id_idx][ij - 1] == 0; + + // 保存前两个点的值 + let dsftmm = dsft1m; + let dsfnmm = dsfn1m; + let mut dsfpmm = vec![0.0; nlvexp]; + for ii in 0..nlvexp { + dsfpmm[ii] = dsfp1m[ii]; + } + + // 更新前一个点的值 + dsft1m = dsft1; + dsfn1m = dsfn1; + for ii in 0..nlvexp { + dsfp1m[ii] = dsfp1[ii]; + } + + // 更新当前点为上一个点的 ID+1 值 + let s0_curr = s0p; + let dsft1_curr = dsft1p; + let dsfn1_curr = dsfn1p; + let dsfp1_curr = dsfp1p.clone(); + + // 初始化 ID-1 的导数(用于边界条件) + let mut dsft1d: f64 = 0.0; + let mut dsfn1d: f64 = 0.0; + let mut dsfp1d = vec![0.0; nlvexp]; + + // 改进的下边界条件 + let mut dbdt: f64 = 0.0; + if params.ibc > 0 && params.idisk == 0 { + let dt = UN / (model.deldmz[id_idx - 1] * (model.absot[id_idx] + model.absot[id_idx - 1])); + let plad = model.xkfb[id_idx] / model.xkf1[id_idx]; + dbdt = plad / model.xkf1[id_idx] * model.hkt21[id_idx] * model.freq[ij - 1] * dt; + + if params.ibc == 1 { + // 简单边界条件 + let dsft1_new = dsft1_curr + dbdt; + if params.irder == 1 || params.irder == 3 { + fixalp.dsfdt[id_idx] = dsft1_new * fixalp.ali1[id_idx]; + fixalp.dsfdn[id_idx] = dsfn1_curr * fixalp.ali1[id_idx]; + } + if params.irder > 1 { + for ii in 0..nlvexp { + fixalp.dsfdp[ii][id_idx] = dsfp1_curr[ii] * fixalp.ali1[id_idx]; + } + } + } else if params.ibc >= 2 { + // 改进的边界条件 + let plam = model.xkfb[id_idx - 1] / model.xkf1[id_idx - 1]; + let tau23 = t23 * dt; + let tau43 = t43 * dt; + let d0 = (plad * (UN + tau43) - t43 * plam * dt) * dt * dt; + let rhd = model.deldmz[id_idx - 1] * model.densi[id_idx]; + let e0 = d0 * rhd; + + let dsft1_new = dsft1_curr + dbdt * (UN + tau23) - e0 * rad.dabt1[id_idx]; + let dsfn1_new = dsfn1_curr - e0 * (rad.dabn1[id_idx] + rad.abso1[id_idx] * model.densim[id_idx]); + let mut dsfp1_new = dsfp1_curr.clone(); + for ii in 0..nlvexp { + dsfp1_new[ii] = dsfp1_curr[ii] - e0 * rad.dabp1[ii][id_idx]; + } + + if params.ibc >= 3 { + let dbdtm = plam / model.xkf1[id_idx - 1] * model.hkt21[id_idx - 1] * model.freq[ij - 1] * dt; + let rhd = model.deldmz[id_idx - 1] * model.densi[id_idx - 1]; + let e0 = d0 * rhd; + dsft1d = -dbdtm * dt * t23_inv - e0 * rad.dabt1[id_idx - 1]; + dsfn1d = -e0 * (rad.dabn1[id_idx - 1] + rad.abso1[id_idx - 1] * model.densim[id_idx - 1]); + for ii in 0..nlvexp { + dsfp1d[ii] = -e0 * rad.dabp1[ii][id_idx - 1]; + } + } + + // 更新 DSFDT, DSFDN + if params.irder == 1 || params.irder == 3 { + fixalp.dsfdt[id_idx] = dsft1_new * fixalp.ali1[id_idx]; + fixalp.dsfdn[id_idx] = dsfn1_new * fixalp.ali1[id_idx]; + } + if params.irder > 1 { + for ii in 0..nlvexp { + fixalp.dsfdp[ii][id_idx] = dsfp1_new[ii] * fixalp.ali1[id_idx]; + } + } + } + } else { + // 标准边界条件 + if params.irder == 1 || params.irder == 3 { + fixalp.dsfdt[id_idx] = dsft1_curr * fixalp.ali1[id_idx]; + fixalp.dsfdn[id_idx] = dsfn1_curr * fixalp.ali1[id_idx]; + } + if params.irder > 1 { + for ii in 0..nlvexp { + fixalp.dsfdp[ii][id_idx] = dsfp1_curr[ii] * fixalp.ali1[id_idx]; + } + } + } + + // 流体静力学平衡方程 + if lnskip { + let d0 = ww * model.fak1[id_idx]; + let a0 = ww * model.fak1[id_idx - 1]; + model.fprd[id_idx] += d0 * model.rad1[id_idx] - a0 * model.rad1[id_idx - 1]; + let e0 = d0 * fixalp.alim1[id_idx] - a0 * fixalp.ali1[id_idx - 1]; + let d0_new = d0 * fixalp.ali1[id_idx] - a0 * fixalp.alip1[id_idx - 1]; + model.heit[id_idx] += d0_new * dsft1_curr; + model.hein[id_idx] += d0_new * dsfn1_curr; + model.heitm[id_idx] += e0 * dsft1m; + model.heinm[id_idx] += e0 * dsfn1m; + for ii in 0..nlvexp { + model.heip[ii][id_idx] += d0_new * dsfp1_curr[ii]; + model.heipm[ii][id_idx] += e0 * dsfp1m[ii]; + } + if params.ibc >= 3 { + model.heitm[id_idx] -= d0_new * dsft1d; + model.heinm[id_idx] -= d0_new * dsfn1d; + for ii in 0..nlvexp { + model.heipm[ii][id_idx] -= d0_new * dsfp1d[ii]; + } + } + } + + // 辐射平衡微分方程部分 + let ddt = UN / (model.absot[id_idx] + model.absot[id_idx - 1]); + let dt = ddt / model.deldmz[id_idx - 1]; + let fl = (model.rad1[id_idx] * model.fak1[id_idx] - model.rad1[id_idx - 1] * model.fak1[id_idx - 1]) * dt; + model.flfix[id_idx] += ww * fl; + + if model.redif[id_idx] > 0.0 { + let d0 = ww * model.fak1[id_idx] * dt; + let a0 = ww * model.fak1[id_idx - 1] * dt; + let d0m = d0 * fixalp.alim1[id_idx] - a0 * fixalp.ali1[id_idx - 1]; + let d0_new = d0 * fixalp.ali1[id_idx] - a0 * fixalp.alip1[id_idx - 1]; + let e0 = ww * fl * ddt; + model.redx[id_idx] += e0 * rad.abso1[id_idx]; + model.redxm[id_idx] += e0 * rad.abso1[id_idx - 1]; + let e0m = e0 * model.densi[id_idx - 1]; + let e0_new = e0 * model.densi[id_idx]; + model.redt[id_idx] += d0_new * dsft1_curr - e0_new * rad.dabt1[id_idx]; + model.redtm[id_idx] += d0m * dsft1m - e0m * rad.dabt1[id_idx - 1]; + model.redn[id_idx] += d0_new * dsfn1_curr - e0_new * rad.dabn1[id_idx]; + model.rednm[id_idx] += d0m * dsfn1m - e0m * rad.dabn1[id_idx - 1]; + for ii in 0..nlvexp { + model.redp[ii][id_idx] += d0_new * dsfp1_curr[ii] - e0_new * rad.dabp1[ii][id_idx]; + model.redpm[ii][id_idx] += d0m * dsfp1m[ii] - e0m * rad.dabp1[ii][id_idx - 1]; + } + if params.ibc >= 3 { + model.redtm[id_idx] += d0_new * dsft1d; + model.rednm[id_idx] += d0_new * dsfn1d; + for ii in 0..nlvexp { + model.redpm[ii][id_idx] += d0_new * dsfp1d[ii]; + } + } + } + + // 辐射平衡积分方程部分 + if model.reint[id_idx] > 0.0 { + let abst_true = rad.abso1[id_idx] - model.elscat[id_idx]; + model.fcooli[id_idx] += ww * (rad.emis1[id_idx] - abst_true * model.rad1[id_idx]); + let d0 = ww * (abst_true * fixalp.ali1[id_idx] - rad.abso1[id_idx]); + let e0 = ww * (model.rad1[id_idx] - s0_curr); + model.rein[id_idx] += d0 * dsfn1_curr + e0 * rad.dabn1[id_idx] - ww * model.sigec[ij - 1] * model.rad1[id_idx]; + if params.ibc == 0 { + model.reit[id_idx] += d0 * dsft1_curr + e0 * rad.dabt1[id_idx]; + } else { + // 改进的边界条件下的积分方程 + model.reit[id_idx] += d0 * (dsft1_curr - dbdt) + e0 * rad.dabt1[id_idx] + + fixalp.ali1[id_idx] / rad.abso1[id_idx] * dbdt; + } + for ii in 0..nlvexp { + model.reip[ii][id_idx] += d0 * dsfp1_curr[ii] + e0 * rad.dabp1[ii][id_idx]; + } + } + + // 避免未使用变量警告 + let _ = (dsftmm, dsfnmm, dsfpmm); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_alifr1_ifali_le_1() { + // 测试 IFALI <= 1 时直接返回 + let params = Alifr1Params { + ij: 1, + nd: 10, + nlvexp: 5, + ifali: 1, + irder: 1, + ilmcor: 3, + ilasct: 0, + ibc: 0, + idisk: 0, + ifalih: 0, + }; + + // 创建空的 FixAlp(实际使用需要完整初始化) + let mut fixalp = FixAlp::default(); + + // 简单测试:确保函数不会 panic + // 实际测试需要完整的模型状态 + let _ = (¶ms, &mut fixalp); + } +} diff --git a/src/math/cspec.rs b/src/math/cspec.rs new file mode 100644 index 0000000..d7085e3 --- /dev/null +++ b/src/math/cspec.rs @@ -0,0 +1,199 @@ +//! 碰撞强度计算。 +//! +//! 重构自 TLUSTY `cspec.f` +//! +//! 使用 Van Regemorter 公式计算非标准的碰撞率, +//! 遵循 Mihalas (1978, Stellar Atmospheres, 2nd edition) 的建议。 +//! +//! ## 碰撞类型 (IC) +//! +//! - IC = -1: 中性粒子(Neutrals) +//! - IC = -2: 离子(Ions) +//! - IC = -11: 特殊处理 +//! - IC = -12: He I 禁戒跃迁 + +use crate::state::constants::UN; + +/// He I 禁戒跃迁的系数 (CHE1FB) +/// 原始 Fortran DATA 语句: +/// CHE1FB(1,1-4) = 9.63675, -2.22941, -17.30103 +/// CHE1FB(2,1-4) = 10.85578, -2.40931, -27.00903 +/// CHE1FB(3,1-4) = 8.38043, -2.04791, -7.36621 +const CHE1FB: [[f64; 4]; 3] = [ + [9.63675, -2.22941, -17.30103, 0.0], // IFORB = 1 + [10.85578, -2.40931, -27.00903, 0.0], // IFORB = 2 + [8.38043, -2.04791, -7.36621, 0.0], // IFORB = 3 +]; + +/// 指数积分近似系数 (Abramowitz & Stegun) +const EXPIA1: f64 = -0.57721566; +const EXPIA2: f64 = 0.99999193; +const EXPIA3: f64 = -0.24991055; +const EXPIA4: f64 = 0.05519968; +const EXPIA5: f64 = -0.00976004; +const EXPIA6: f64 = 0.00107857; + +const EXPIB1: f64 = 0.2677737343; +const EXPIB2: f64 = 8.6347608925; +const EXPIB3: f64 = 18.059016973; +const EXPIB4: f64 = 8.5733287401; + +const EXPIC1: f64 = 3.9584969228; +const EXPIC2: f64 = 21.0996530827; +const EXPIC3: f64 = 25.6329561486; +const EXPIC4: f64 = 9.5733223454; + +/// 计算碰撞强度。 +/// +/// # 参数 +/// +/// * `i` - 下能级索引 (1-indexed) +/// * `j` - 上能级索引 (1-indexed) +/// * `ic` - 碰撞类型: +/// - -1: 中性粒子 +/// - -2: 离子 +/// - -11: 特殊处理 +/// - -12: He I 禁戒跃迁 +/// * `os` - 振子强度 +/// * `cp` - 碰撞参数(用于离子) +/// * `u0` - 约化能量 (E/kT) +/// * `t` - 温度 (K) +/// +/// # 返回值 +/// +/// 碰撞强度 CS +#[allow(clippy::too_many_arguments)] +pub fn cspec(i: i32, j: i32, ic: i32, os: f64, cp: f64, u0: f64, t: f64) -> f64 { + let mut cs = 0.0; + + if ic > -10 { + // 计算指数积分 E1(U0) + let expiu0 = if u0 <= UN { + // U0 <= 1: 使用级数展开 + (-u0.ln()) + EXPIA1 + + u0 * (EXPIA2 + u0 * (EXPIA3 + u0 * (EXPIA4 + u0 * (EXPIA5 + u0 * EXPIA6)))) + } else { + // U0 > 1: 使用连分式近似 + let num = EXPIB1 + u0 * (EXPIB2 + u0 * (EXPIB3 + u0 * EXPIB4)); + let den = EXPIC1 + u0 * (EXPIC2 + u0 * (EXPIC3 + u0 * EXPIC4)); + (-u0).exp() * num / den / u0 + }; + + let gg = if ic == -1 { + // 中性粒子 (Auer & Mihalas 1973) + if u0 <= 14.0 { + 0.276 * u0.exp() * expiu0 + } else { + 0.066 * (1.0 + 1.5 / u0) / u0.sqrt() + } + } else if ic == -2 { + // 离子 (Mihalas 1972) + let gg0 = 0.276 * u0.exp() * expiu0; + if gg0 > cp { gg0 } else { cp } + } else { + 0.0 + }; + + let t32 = t.powf(-1.5); + cs += 19.7363 * t32 * (-u0).exp() / u0 * gg * os; + + return cs; + } + + if ic == -11 { + // 特殊处理 + let xr = -1.68_f64; + cs += 2.16 * u0.powf(xr) / t / t.sqrt() * (-u0).exp() * os; + return cs; + } + + if ic == -12 { + // He I 禁戒跃迁 (from Klaus Werner) + // 确定跃迁类型 + let iforb = match (i, j) { + (2, 3) => 1, + (2, 5) => 2, + (3, 4) => 3, + (4, 5) => 4, + _ => { + panic!("Inconsistent ICOL - CSPEC: i={}, j={}, iforb=0", i, j); + } + }; + + let xt = t.log10(); + let gam = if iforb <= 3 { + CHE1FB[0][iforb as usize - 1] + + CHE1FB[1][iforb as usize - 1] * xt + + CHE1FB[2][iforb as usize - 1] / xt / xt + } else { + 0.0 + }; + let gam = (2.30258509299405_f64 * gam).exp(); + + cs += 5.465e-11 * t.sqrt() * (-u0).exp() * gam; + } + + cs +} + +#[cfg(test)] +mod tests { + use super::*; + use approx::assert_relative_eq; + + #[test] + fn test_cspec_neutral_low_u0() { + // 中性粒子,U0 <= 1 + let cs = cspec(1, 2, -1, 0.5, 0.0, 0.5, 10000.0); + assert!(cs > 0.0); + } + + #[test] + fn test_cspec_neutral_high_u0() { + // 中性粒子,U0 > 14 + let cs = cspec(1, 2, -1, 0.5, 0.0, 15.0, 10000.0); + assert!(cs > 0.0); + } + + #[test] + fn test_cspec_ion() { + // 离子 + let cs = cspec(1, 2, -2, 0.5, 0.1, 2.0, 10000.0); + assert!(cs > 0.0); + } + + #[test] + fn test_cspec_special() { + // IC = -11 + let cs = cspec(1, 2, -11, 0.5, 0.0, 2.0, 10000.0); + assert!(cs > 0.0); + } + + #[test] + fn test_cspec_hei_forbidden() { + // He I 禁戒跃迁 (2->3) + let cs = cspec(2, 3, -12, 0.0, 0.0, 2.0, 10000.0); + assert!(cs > 0.0); + } + + #[test] + fn test_expiu0_low() { + // 测试指数积分 U0 <= 1 + let u0: f64 = 0.5; + let expiu0 = (-u0.ln()) + EXPIA1 + + u0 * (EXPIA2 + u0 * (EXPIA3 + u0 * (EXPIA4 + u0 * (EXPIA5 + u0 * EXPIA6)))); + // E1(0.5) ≈ 0.5598 + assert_relative_eq!(expiu0, 0.5598, epsilon = 1e-3); + } + + #[test] + fn test_expiu0_high() { + // 测试指数积分 U0 > 1 + let u0: f64 = 2.0; + let num = EXPIB1 + u0 * (EXPIB2 + u0 * (EXPIB3 + u0 * EXPIB4)); + let den = EXPIC1 + u0 * (EXPIC2 + u0 * (EXPIC3 + u0 * EXPIC4)); + let expiu0 = (-u0).exp() * num / den / u0; + // E1(2.0) ≈ 0.0477 (Abramowitz & Stegun 近似) + assert_relative_eq!(expiu0, 0.0477, epsilon = 1e-3); + } +} diff --git a/src/math/levset.rs b/src/math/levset.rs new file mode 100644 index 0000000..26c0a52 --- /dev/null +++ b/src/math/levset.rs @@ -0,0 +1,427 @@ +//! 能级参数设置。 +//! +//! 重构自 TLUSTY `levset.f` +//! +//! 设置能级参数 IIEXP 和 IIFOR,控制能级的处理方式。 +//! +//! ## 能级模式 (IMODL) +//! +//! - 0: 显式处理,完全 NLTE +//! - 1, 3: 跳过 +//! - 4, 5: LTE +//! - -1, -3: 跳过,禁戒跃迁 +//! - 6: 跳过 +//! - -5, -6: 跳过,禁戒跃迁 +//! - < -100: 分组处理 +//! - < -200: 分组处理(不同方式) + +use crate::state::constants::{MLEVEL, MDEPTH, MLVEXP}; + +/// LEVSET 的输入参数 +pub struct LevsetParams { + /// 是否启用 LTE 模式 + pub lte: bool, + /// 能级处理模式 (0: 自动, 其他: 由 IMODL 决定) + pub iflev: i32, + /// 选项表标志 + pub ioptab: i32, +} + +/// LEVSET 的模型状态 +pub struct LevsetModelState<'a> { + /// 深度点数 + pub nd: usize, + /// 原子数 + pub natom: usize, + /// 能级数 + pub nlevel: usize, + /// 能级模式 (NLEVEL) + pub imodl: &'a mut [i32], + /// 能级所属原子 (NLEVEL) + pub iatm: &'a [i32], + /// 能级所属元素/离子 (NLEVEL) + pub iel: &'a [i32], + /// 原子固定标志 (NATOM) + pub iifix: &'a [i32], + /// 原子起始能级 (NATOM) + pub n0a: &'a [i32], + /// 原子结束能级 (NATOM) + pub nka: &'a [i32], + /// 离子第一个能级 (NLEVEL) + pub nfirst: &'a [i32], + /// 离子最后一个能级 (NLEVEL) + pub nnext: &'a [i32], + /// LTE 能级标志 (NLEVEL) + pub iltlev: &'a [i32], +} + +/// LEVSET 的输出状态 +pub struct LevsetOutputState<'a> { + /// 显式能级索引 (NLEVEL) - 正值表示显式处理 + pub iiexp: &'a mut [i32], + /// 禁戒能级索引 (NLEVEL) + pub iifor: &'a mut [i32], + /// 显式能级对应的实际能级 (MLVEXP) + pub indlev: &'a mut [i32], + /// LTE 参考能级 (NLEVEL × MDEPTH) + pub iltref: &'a mut [i32], + /// B 因子 (NLEVEL × MDEPTH) + pub bfac: &'a mut [f64], + /// 积分零标志 (MLVEXP × MDEPTH) + pub igzero: &'a mut [i32], + /// 种群零标志 (NLEVEL × MDEPTH) + pub ipzero: &'a mut [i32], + /// 显式能级数 + pub nlvexp: &'a mut i32, + /// 禁戒能级数 + pub nlvfor: &'a mut i32, +} + +/// 设置能级参数。 +/// +/// # 参数 +/// +/// * `params` - 输入参数 +/// * `model` - 模型状态 +/// * `output` - 输出状态 +pub fn levset( + params: &LevsetParams, + model: &mut LevsetModelState, + output: &mut LevsetOutputState, +) { + // 如果 IOPTAB < 0,直接返回 + if params.ioptab < 0 { + return; + } + + let nd = model.nd; + let natom = model.natom; + let nlevel = model.nlevel; + + // 初始化 B 因子 + for i in 0..nlevel { + for id in 0..nd { + output.bfac[i * nd + id] = 1.0; + } + } + + if params.iflev == 0 { + // 情况 1: 由 IMODL 决定处理方式 + process_by_imodl(params, model, output, natom, nlevel, nd); + } else { + // 情况 2: 自动处理 - 所有 ILK=0 的能级在更新的 LTE 模式下 + process_automatic(params, model, output, nlevel, nd); + } + + // 初始化 IGZERO 和 IPZERO + let nlvexp = *output.nlvexp as usize; + for ii in 0..nlvexp.min(MLVEXP) { + output.indlev[ii] = 0; + for id in 0..nd { + output.igzero[ii * nd + id] = 0; + } + } + + for i in 0..nlevel { + for id in 0..nd { + output.ipzero[i * nd + id] = 0; + } + if model.imodl[i].abs() <= 6 { + if output.iiexp[i] > 0 { + let ii_idx = (output.iiexp[i] - 1) as usize; + if ii_idx < MLVEXP { + output.indlev[ii_idx] = (i + 1) as i32; + } + } + } + } +} + +/// 按能级模式处理 +#[allow(clippy::too_many_arguments)] +fn process_by_imodl( + params: &LevsetParams, + model: &mut LevsetModelState, + output: &mut LevsetOutputState, + natom: usize, + nlevel: usize, + nd: usize, +) { + // 初始化 + for i in 0..nlevel { + output.iiexp[i] = 0; + output.iifor[i] = 0; + } + + let mut iie: i32 = 0; + let mut iif: i32 = 0; + let mut igrp: i32 = 0; + + for iat in 0..natom { + igrp = 0; + if model.iifix[iat] == 1 { + continue; + } + + let n0a = model.n0a[iat] as usize; + let nka = model.nka[iat] as usize; + + for i in n0a..=nka { + let imodl = model.imodl[i]; + let mut inew = 1; + + if imodl == 0 { + // 显式处理 + iie += 1; + iif += 1; + output.iiexp[i] = iie; + output.iifor[i] = iif; + output.indlev[(iie - 1) as usize] = (i + 1) as i32; + } else if imodl > 0 { + // 正模式 + iif += 1; + output.iifor[i] = iif; + if model.iltlev[i] >= 1 { + iie += 1; + output.iiexp[i] = iie; + } + let nfirst = model.nfirst[model.iel[i] as usize] as usize; + let nnext = model.nnext[model.iel[i] as usize] as usize; + if i + 1 == nfirst || i + 1 == nnext { + iie += 1; + output.iiexp[i] = iie; + } + } else if imodl < -100 { + // 分组处理 (IMODL < -100) + if i > n0a { + if model.imodl[i] == model.imodl[i - 1] { + inew = 0; + } + } + output.iiexp[i] = -iie; + if inew == 1 { + iie += 1; + output.iiexp[i] = -iie; + let nfirst = model.nfirst[model.iel[i] as usize] as usize; + let mut im = nfirst; + let mut lml = true; + while im < i && lml { + if model.imodl[i] == model.imodl[im] { + output.iiexp[i] = output.iiexp[im]; + iie -= 1; + lml = false; + } + im += 1; + } + } + igrp = 1; + iif += 1; + output.iifor[i] = iif; + } else if imodl < -200 { + // 分组处理 (IMODL < -200) + if i > n0a { + if model.imodl[i] == model.imodl[i - 1] { + inew = 0; + } + } + if inew == 1 { + iie += 1; + } + if inew == 1 { + iif += 1; + } + output.iiexp[i] = -iie; + output.iifor[i] = -iif; + igrp = 1; + } + } + + // 处理分组 + if igrp == 1 { + for i in n0a..=nka { + if output.iiexp[i] > 0 { + output.iiexp[i] = -output.iiexp[i]; + } + if model.imodl[i] == 0 { + model.imodl[i] = 7; + } + } + } + } + + *output.nlvexp = iie.abs(); + if *output.nlvexp > MLVEXP as i32 { + panic!("nlvexp.gt.mlvexp: {} > {}", *output.nlvexp, MLVEXP); + } + + *output.nlvfor = iif.abs(); + + // 清理特定模式 + for i in 0..nlevel { + let imodl = model.imodl[i]; + if imodl == 1 || imodl == 3 { + output.iiexp[i] = 0; + } else if imodl == 4 || imodl == 5 { + output.iiexp[i] = 0; + } else if imodl == -1 || imodl == -3 { + output.iiexp[i] = 0; + output.iifor[i] = 0; + } else if imodl == 6 { + output.iiexp[i] = 0; + } else if imodl == -5 || imodl == -6 { + output.iiexp[i] = 0; + output.iifor[i] = 0; + } else if imodl < -100 { + model.imodl[i] = 7; + } else if imodl < -200 { + model.imodl[i] = -7; + } + + // 设置 ILTREF + let nnext = model.nnext[model.iel[i] as usize]; + for id in 0..nd { + output.iltref[i * nd + id] = nnext; + } + } + + // 如果有分组,将所有能级设为模式 7 + if igrp == 1 { + for iat in 0..natom { + if model.iifix[iat] == 1 { + continue; + } + let n0a = model.n0a[iat] as usize; + let nka = model.nka[iat] as usize; + for i in n0a..=nka { + model.imodl[i] = 7; + } + } + } +} + +/// 自动处理模式 +fn process_automatic( + params: &LevsetParams, + model: &mut LevsetModelState, + output: &mut LevsetOutputState, + nlevel: usize, + nd: usize, +) { + let mut iif: i32 = 0; + + for i in 0..nlevel { + if model.iifix[model.iatm[i] as usize] == 1 { + continue; + } + model.imodl[i] = 5; + let nfirst = model.nfirst[model.iel[i] as usize] as usize; + let nnext = model.nnext[model.iel[i] as usize] as usize; + if i + 1 == nfirst || i + 1 == nnext { + iif += 1; + output.iifor[i] = iif; + } + } + + *output.nlvexp = iif; + if *output.nlvexp > MLVEXP as i32 { + panic!("nlvexp.gt.mlvexp: {} > {}", *output.nlvexp, MLVEXP); + } + + *output.nlvfor = iif; + + // 清理非第一/最后能级的 IIFOR + for i in 0..nlevel { + let nfirst = model.nfirst[model.iel[i] as usize] as usize; + let nnext = model.nnext[model.iel[i] as usize] as usize; + if i + 1 != nfirst && i + 1 != nnext { + output.iifor[i] = 0; + } + } + + // 设置 IIEXP 和 INDLEV + for i in 0..nlevel { + output.iiexp[i] = output.iifor[i]; + if output.iiexp[i] > 0 { + output.indlev[(output.iiexp[i] - 1) as usize] = (i + 1) as i32; + } + let nnext = model.nnext[model.iel[i] as usize]; + for id in 0..nd { + output.iltref[i * nd + id] = nnext; + } + } + + // 非 LTE 模式下,所有能级都是显式的 + if !params.lte { + for i in 0..nlevel { + output.iifor[i] = (i + 1) as i32; + } + *output.nlvfor = nlevel as i32; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_levset_ioptab_negative() { + // IOPTAB < 0 时应直接返回 + let params = LevsetParams { + lte: false, + iflev: 0, + ioptab: -1, + }; + // 创建简单的模型状态 + let mut imodl = vec![0i32; 10]; + let iatm = vec![1i32; 10]; + let iel = vec![1i32; 10]; + let iifix = vec![0i32; 2]; + let n0a = vec![0i32; 2]; + let nka = vec![9i32; 2]; + let nfirst = vec![0i32; 10]; + let nnext = vec![9i32; 10]; + let iltlev = vec![0i32; 10]; + + let mut model = LevsetModelState { + nd: 5, + natom: 2, + nlevel: 10, + imodl: &mut imodl, + iatm: &iatm, + iel: &iel, + iifix: &iifix, + n0a: &n0a, + nka: &nka, + nfirst: &nfirst, + nnext: &nnext, + iltlev: &iltlev, + }; + + let mut iiexp = vec![0i32; 10]; + let mut iifor = vec![0i32; 10]; + let mut indlev = vec![0i32; MLVEXP]; + let mut iltref = vec![0i32; 10 * 5]; + let mut bfac = vec![1.0; 10 * 5]; + let mut igzero = vec![0i32; MLVEXP * 5]; + let mut ipzero = vec![0i32; 10 * 5]; + let mut nlvexp = 0i32; + let mut nlvfor = 0i32; + + let mut output = LevsetOutputState { + iiexp: &mut iiexp, + iifor: &mut iifor, + indlev: &mut indlev, + iltref: &mut iltref, + bfac: &mut bfac, + igzero: &mut igzero, + ipzero: &mut ipzero, + nlvexp: &mut nlvexp, + nlvfor: &mut nlvfor, + }; + + levset(¶ms, &mut model, &mut output); + + // IOPTAB < 0 时,nlvexp 应该保持为 0 + assert_eq!(*output.nlvexp, 0); + } +} diff --git a/src/math/mod.rs b/src/math/mod.rs index 6fcf96c..00cb099 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -1,5 +1,6 @@ //! 数学工具函数,重构自 TLUSTY Fortran。 +mod alifr1; mod alifr3; mod alifr6; mod alifrk; @@ -18,6 +19,7 @@ mod collhe; mod compt0; mod comset; mod cross; +mod cspec; mod ctdata; mod cubic; mod dielrc; @@ -54,6 +56,7 @@ mod irc; mod interpolate; mod laguer; mod levsol; +mod levset; mod levgrp; mod lineqs; mod linspl; @@ -62,6 +65,9 @@ mod matinv; mod meanop; mod minv3; mod odfhst; +mod odffr; +mod opadd0; +mod opctab; mod pfcno; mod pffe; mod prdini; @@ -76,6 +82,7 @@ mod quit; mod reflev; mod raph; mod ratmal; +mod ratmat; mod rayleigh; mod rybmat; mod rayset; @@ -120,6 +127,7 @@ mod xk2dop; mod ylintp; mod zmrho; +pub use alifr1::{alifr1, Alifr1Params, Alifr1ModelState, Alifr1RadState}; pub use alifr3::{alifr3, Alifr3Params}; pub use alifr6::{alifr6, Alifr6Params, Alifr6State}; pub use alifrk::{alifrk, AlifrkParams, AlifrkState}; @@ -137,6 +145,7 @@ pub use ckoest::ckoest; pub use collhe::collhe; pub use comset::{comset, ComsetParams, ComsetResult}; pub use cross::{cross, crossd}; +pub use cspec::cspec; pub use ctdata::{hction, hctrecom, CTION, CTRECOMB}; pub use cubic::{cubic, CubicCon}; pub use dielrc::dielrc; @@ -173,6 +182,7 @@ pub use irc::irc; pub use interpolate::{lagran, yint}; pub use laguer::laguer; pub use levsol::levsol; +pub use levset::{levset, LevsetParams, LevsetModelState, LevsetOutputState}; pub use levgrp::{levgrp, LevgrpParams, LevgrpResult}; pub use lineqs::{lineqs, lineqs_nr}; pub use linspl::{linspl, LinsplParams}; @@ -180,7 +190,10 @@ pub use locate::locate; pub use matinv::matinv; pub use meanop::meanop; pub use minv3::minv3; +pub use opadd0::{opadd0, Opadd0Params, Opadd0FreqData, Opadd0OutputState}; +pub use opctab::{opctab, OpctabParams, OpctabTableData, OpctabModelState, OpctabOutput}; pub use odfhst::odfhst; +pub use odffr::{odffr, OdffrParams, OdffrAtomicData, OdffrModelData, OdffrOutputState}; pub use pfcno::pfcno; pub use pffe::pffe; pub use prdini::prdini; @@ -195,6 +208,7 @@ pub use reflev::reflev; pub use quit::{quit, quit_error}; pub use raph::raph; pub use ratmal::ratmal; +pub use ratmat::{ratmat, RatmatParams, RatmatOutput}; pub use rayleigh::{ rayleigh, rayleigh_h2_cross_section, rayleigh_h_cross_section, rayleigh_he_cross_section, RayleighParams, RayleighResult, diff --git a/src/math/odffr.rs b/src/math/odffr.rs new file mode 100644 index 0000000..3bd03f0 --- /dev/null +++ b/src/math/odffr.rs @@ -0,0 +1,307 @@ +//! ODF 频率设置。 +//! +//! 重构自 TLUSTY `odffr.f` +//! +//! 为 ODF(Opacity Distribution Function)设置内部频率, +//! 用于处理系限附近的重叠谱线。 +//! +//! ## 算法说明 +//! +//! 谱线向连续跃迁 (IL - IU) 的边缘收敛。 +//! - IL: 下能级索引 +//! - IU: 上能级索引(通常是下一离子的基态或 ODF 形式中的平均能级) + +use crate::state::constants::{HALF, MFRO}; + +/// 常量 (从 Fortran PARAMETER 语句) +/// FRH = 3.28805D15 +const FRH: f64 = 3.28805e15; +/// CDOP = 2.84511D-7 +const CDOP: f64 = 2.84511e-7; +/// CDOM = 14. +const CDOM: f64 = 14.0; +/// SIX = 6. +const SIX: f64 = 6.0; +/// SEPT = 7. +const SEPT: f64 = 7.0; + +/// ODFFR 输入参数 +pub struct OdffrParams { + /// 下能级索引 (1-indexed) + pub il: usize, + /// 上能级索引 (1-indexed) + pub iu: usize, + /// 有效温度 (K) + pub teff: f64, + /// 最大主量子数 + pub nlmx: usize, +} + +/// ODFFR 原子数据 +pub struct OdffrAtomicData<'a> { + /// 元素索引 (NLEVEL) + pub iel: &'a [i32], + /// 电荷数 (NATOM) + pub iz: &'a [f64], + /// 电离能 (NLEVEL) + pub enion: &'a [f64], + /// 主量子数 (NLEVEL) + pub nquant: &'a [i32], +} + +/// ODFFR 模型数据 +pub struct OdffrModelData<'a> { + /// 跃迁索引 (NLEVEL × NLEVEL) + pub itra: &'a [i32], + /// ODF 索引 + pub jndodf: &'a [i32], +} + +/// ODFFR 输出状态 +pub struct OdffrOutputState<'a> { + /// ODF 频率点数 (MODF) + pub nfrodf: &'a mut [i32], + /// ODF 频率 (MFRO × MODF) + pub fros: &'a mut [f64], + /// ODF 权重 (MFRO × MODF) + pub wnus: &'a mut [f64], +} + +/// 普朗克常数 (从 TLUSTY 常量) +const H: f64 = 6.626176e-27; +/// UN = 1.0 (里德伯常数的倍数) +const UN: f64 = 1.0; + +/// 为 ODF 设置内部频率。 +/// +/// # 参数 +/// +/// * `params` - 输入参数 +/// * `atomic` - 原子数据 +/// * `model` - 模型数据 +/// * `output` - 输出状态 +/// +/// # Panics +/// +/// 当频率点数超过 MFRO 时 panic。 +#[allow(clippy::too_many_arguments)] +pub fn odffr( + params: &OdffrParams, + atomic: &OdffrAtomicData, + model: &OdffrModelData, + output: &mut OdffrOutputState, +) { + let il = params.il; + let iu = params.iu; + let il_idx = il - 1; + let iu_idx = iu - 1; + + // 计算电荷平方 + let iel_idx = atomic.iel[il_idx] as usize; + let ch = atomic.iz[iel_idx - 1] * atomic.iz[iel_idx - 1]; + let frion = ch * FRH; + + // 电离频率 + let fre = atomic.enion[il_idx] / H; + + // 临时频率数组 + let mut ffro = vec![0.0_f64; MFRO]; + + let mut nf: usize = 1; + + let nq1 = atomic.nquant[iu_idx] as usize; + let xl2 = UN / (atomic.nquant[il_idx] as f64 * atomic.nquant[il_idx] as f64); + let xu1 = UN / ((atomic.nquant[iu_idx] - 1) as f64 * (atomic.nquant[iu_idx] - 1) as f64); + let xu2 = UN / (atomic.nquant[iu_idx] as f64 * atomic.nquant[iu_idx] as f64); + + let frc = frion * (xl2 - xu2); + ffro[nf - 1] = HALF * (frc + frion * (xl2 - xu1)); + + let kt = model.itra[il_idx * atomic.iel.len() + iu_idx] as usize; + let kl = model.jndodf[kt - 1] as usize; + + let dopo = CDOP * params.teff.sqrt() * frc; + let dopm = CDOM * dopo; + + let mut fr1 = ffro[0]; + + // 遍历主量子数 + for i in nq1..=params.nlmx { + let ii = (i * i) as f64; + let fr2 = fre - frion / ii; + let df = fr2 - fr1; + + if df > dopm { + // 大间隔:添加密集点 + for j in 1..=7 { + nf += 1; + if nf > MFRO { + nf -= 1; + goto_end(&ffro, nf, kl, output); + return; + } + ffro[nf - 1] = fr1 + j as f64 * dopo; + } + + let df_inner = fr2 - SEPT * dopo - ffro[nf - 1]; + let ni = (df_inner / (SIX * dopo)) as usize; + let ddf = df_inner / ((ni + 1) as f64); + + for j in 1..=ni { + nf += 1; + if nf > MFRO - 3 { + nf -= 1; + goto_end(&ffro, nf, kl, output); + return; + } + ffro[nf - 1] = fr1 + SEPT * dopo + j as f64 * ddf; + } + + for j in (0..=7).rev() { + nf += 1; + if nf > MFRO { + nf -= 1; + goto_end(&ffro, nf, kl, output); + return; + } + ffro[nf - 1] = fr2 - j as f64 * dopo; + } + + fr1 = fr2; + } else { + // 小间隔:均匀分布 + let ni = (df / dopo) as usize; + let ddf = df / ((ni + 1) as f64); + + for j in 1..=ni { + nf += 1; + if nf > MFRO - 3 { + nf -= 1; + goto_end(&ffro, nf, kl, output); + return; + } + ffro[nf - 1] = fr1 + j as f64 * ddf; + } + + nf += 1; + if nf > MFRO { + nf -= 1; + goto_end(&ffro, nf, kl, output); + return; + } + ffro[nf - 1] = fr2; + fr1 = fr2; + } + } + + goto_end(&ffro, nf, kl, output); +} + +/// 完成频率设置并计算权重 +fn goto_end(ffro: &[f64], nf: usize, kl: usize, output: &mut OdffrOutputState) { + let mut nf = nf; + nf += 1; + if nf > MFRO { + panic!( + "too many points for hydrogen ODF - nf.gt.mfro: {} > {}", + nf, MFRO + ); + } + + // 添加最后一个点(略低于电离限) + let kl_idx = kl - 1; + let mut fros_local = vec![0.0; nf]; + + // 逆序复制 + let fre = ffro[0] / 0.999999999_f64; // 从第一个点估算 fre + fros_local[nf - 1] = fre * 0.999999999; + for i in 0..nf - 1 { + fros_local[i] = ffro[nf - 2 - i]; + } + + output.nfrodf[kl_idx] = nf as i32; + + if nf > MFRO { + panic!( + "too many points for hydrogen ODF - nf.gt.mfro: {} > {}", + nf, MFRO + ); + } + + // 复制到输出 + for i in 0..nf { + output.fros[i * output.nfrodf.len() + kl_idx] = fros_local[i]; + } + + // 计算权重 + // 第一个点 + output.wnus[kl_idx] = HALF * (fros_local[0] - fros_local[1]); + // 最后一个点 + output.wnus[(nf - 1) * output.nfrodf.len() + kl_idx] = + HALF * (fros_local[nf - 2] - fros_local[nf - 1]); + + // 中间点 + for i in 2..nf { + output.wnus[(i - 1) * output.nfrodf.len() + kl_idx] = + HALF * (fros_local[i - 2] - fros_local[i]); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use approx::assert_relative_eq; + + #[test] + fn test_odffr_constants() { + // 验证常量 + assert_relative_eq!(FRH, 3.28805e15, epsilon = 1e10); + assert_relative_eq!(CDOP, 2.84511e-7, epsilon = 1e-12); + assert_relative_eq!(CDOM, 14.0, epsilon = 1e-10); + } + + #[test] + fn test_odffr_basic() { + // 基本测试 + let params = OdffrParams { + il: 2, + iu: 10, + teff: 10000.0, + nlmx: 10, + }; + + let iel = vec![1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; + let iz = vec![1.0]; + let enion = vec![13.6, 13.5, 13.4, 13.3, 13.2, 13.1, 13.0, 12.9, 12.8, 12.7]; + let nquant = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + let atomic = OdffrAtomicData { + iel: &iel, + iz: &iz, + enion: &enion, + nquant: &nquant, + }; + + let itra = vec![1i32; 100]; // 所有跃迁索引设为 1 + let jndodf = vec![1i32]; // ODF 索引 + let model = OdffrModelData { + itra: &itra, + jndodf: &jndodf, + }; + + let mut nfrodf = vec![0i32; 10]; + let mut fros = vec![0.0; MFRO * 10]; + let mut wnus = vec![0.0; MFRO * 10]; + + let mut output = OdffrOutputState { + nfrodf: &mut nfrodf, + fros: &mut fros, + wnus: &mut wnus, + }; + + odffr(¶ms, &atomic, &model, &mut output); + + // 验证输出 + assert!(output.nfrodf[0] > 0); + } +} diff --git a/src/math/opadd0.rs b/src/math/opadd0.rs new file mode 100644 index 0000000..94f05df --- /dev/null +++ b/src/math/opadd0.rs @@ -0,0 +1,271 @@ +//! 附加不透明度源截面设置。 +//! +//! 重构自 TLUSTY `opadd0.f` +//! +//! 设置各种附加不透明度源的截面,包括: +//! - H I Rayleigh 散射 +//! - He I Rayleigh 散射 +//! - H2 Rayleigh 散射 +//! - H- 束缚-自由和自由-自由 +//! - H2+ 束缚-自由和自由-自由 +//! - He- 自由-自由 + +use crate::math::sbfhmi::sbfhmi; + +/// 常量 (从 Fortran PARAMETER 语句) +/// H I Rayleigh 散射阈值频率 +const FRRAY: f64 = 2.463e15; +/// He I Rayleigh 散射阈值频率 +const FRAYHE: f64 = 5.150e15; +/// H2 Rayleigh 散射阈值频率 +const FRAYH2: f64 = 2.922e15; +/// 光速 (Angstrom/s) +const CLS: f64 = 2.997925e18; +/// H I Rayleigh 散射系数 +const CR0: f64 = 5.799e-13; +const CR1: f64 = 1.422e-6; +const CR2: f64 = 2.784; + +/// OPADD0 输入参数 +pub struct Opadd0Params { + /// 频率索引 (1-indexed) + pub ij: usize, + /// 连续谱数 + pub ncon: usize, + /// 最大截面数 + pub mcross: usize, + /// H I Rayleigh 散射标志 + pub irsct: i32, + /// He I Rayleigh 散射标志 + pub irsche: i32, + /// H2 Rayleigh 散射标志 + pub irsch2: i32, + /// 分子标志 + pub ifmol: i32, + /// H- 不透明度标志 + pub iophmi: i32, + /// H2+ 不透明度标志 + pub ioph2p: i32, + /// He- 不透明度标志 + pub iophem: i32, + /// ODF 标志 + pub ispodf: i32, +} + +/// OPADD0 频率数据 +pub struct Opadd0FreqData<'a> { + /// 频率数组 + pub freq: &'a [f64], + /// ODF 频率映射 (可选) + pub ifreqb: Option<&'a [i32]>, +} + +/// OPADD0 输出状态 +pub struct Opadd0OutputState<'a> { + /// 截面数组 (MCROSS × MFREQ) + pub bfcs: &'a mut [f32], + /// 频率维度 + pub mfreq: usize, +} + +/// 设置附加不透明度源的截面。 +/// +/// # 参数 +/// +/// * `params` - 输入参数 +/// * `freq_data` - 频率数据 +/// * `output` - 输出状态 +/// +/// # Panics +/// +/// 当 IT > MCROSS 时 panic。 +pub fn opadd0( + params: &Opadd0Params, + freq_data: &Opadd0FreqData, + output: &mut Opadd0OutputState, +) { + let ij = params.ij; + let ij_idx = ij - 1; + + // 获取频率 + let fr = if params.ispodf >= 1 { + let ifreqb = freq_data.ifreqb.expect("ifreqb required for ODF mode"); + freq_data.freq[ifreqb[ij_idx] as usize - 1] + } else { + freq_data.freq[ij_idx] + }; + + let mut it = params.ncon; + + // H I Rayleigh 散射 + if params.irsct != 0 { + it += 1; + if it > params.mcross { + panic!("it.gt.mcross in opadd: {} > {}", it, params.mcross); + } + let frm = fr.min(FRRAY); + let x = (CLS / frm).powi(2); + let cs = (CR0 + (CR1 + CR2 / x) / x) / x / x / x; + output.bfcs[(it - 1) * output.mfreq + ij_idx] = cs as f32; + } + + // He I Rayleigh 散射 + if params.irsche != 0 { + it += 1; + if it > params.mcross { + panic!("it.gt.mcross in opadd: {} > {}", it, params.mcross); + } + let x = (CLS / fr.min(FRAYHE)).powi(2); + let cs = 5.484e-14 / x / x * (1.0 + (2.44e5 + 5.94e10 / (x - 2.90e5)) / x).powi(2); + output.bfcs[(it - 1) * output.mfreq + ij_idx] = cs as f32; + } + + // H2 Rayleigh 散射 + if params.irsch2 != 0 && params.ifmol > 0 { + it += 1; + if it > params.mcross { + panic!("it.gt.mcross in opadd: {} > {}", it, params.mcross); + } + let x = (CLS / fr.min(FRAYH2)).powi(2); + let x2 = 1.0 / x / x; + let cs = (8.14e-13 + 1.28e-6 / x + 1.61 * x2) * x2; + output.bfcs[(it - 1) * output.mfreq + ij_idx] = cs as f32; + } + + // H- 束缚-自由和自由-自由 + if params.iophmi > 0 { + it += 1; + if it > params.mcross { + panic!("it.gt.mcross in opadd: {} > {}", it, params.mcross); + } + let cs = sbfhmi(fr); + output.bfcs[(it - 1) * output.mfreq + ij_idx] = cs as f32; + } + + // H2+ 束缚-自由和自由-自由 + if params.ioph2p > 0 { + it += 1; + if it + 1 > params.mcross { + panic!("it.gt.mcross in opadd: {} > {}", it, params.mcross); + } + let x = fr * 1e-15; + // H2+ bound-free + let cs_bf = (-7.342e-3 + (-2.409 + (1.028 + (-4.23e-1 + (1.224e-1 - 1.351e-2 * x) * x) * x) * x) * x) + * 1.602e-12 + / 1.3806e-16; // BOLK + output.bfcs[(it - 1) * output.mfreq + ij_idx] = cs_bf as f32; + + it += 1; + let x = fr.ln(); + // H2+ free-free + let cs_ff = -3.0233e3 + (3.7797e2 + (-1.82496e1 + (3.9207e-1 - 3.1672e-3 * x) * x) * x) * x; + output.bfcs[(it - 1) * output.mfreq + ij_idx] = cs_ff as f32; + } + + // He- 自由-自由 + if params.iophem > 0 { + it += 1; + if it + 2 > params.mcross { + panic!("it.gt.mcross in opadd: {} > {}", it, params.mcross); + } + let a = 3.397e-46 + (-5.216e-31 + 7.039e-15 / fr) / fr; + let b = -4.116e-42 + (1.067e-26 + 8.135e-11 / fr) / fr; + let c = 5.081e-37 + (-8.724e-23 - 5.659e-8 / fr) / fr; + + output.bfcs[(it - 1) * output.mfreq + ij_idx] = a as f32; + output.bfcs[it * output.mfreq + ij_idx] = b as f32; + output.bfcs[(it + 1) * output.mfreq + ij_idx] = c as f32; + } +} + +#[cfg(test)] +mod tests { + use super::*; + use approx::assert_relative_eq; + + #[test] + fn test_opadd0_constants() { + assert_relative_eq!(FRRAY, 2.463e15, epsilon = 1e10); + assert_relative_eq!(FRAYHE, 5.150e15, epsilon = 1e10); + assert_relative_eq!(FRAYH2, 2.922e15, epsilon = 1e10); + assert_relative_eq!(CLS, 2.997925e18, epsilon = 1e13); + } + + #[test] + fn test_opadd0_hi_rayleigh() { + let params = Opadd0Params { + ij: 1, + ncon: 0, + mcross: 10, + irsct: 1, + irsche: 0, + irsch2: 0, + ifmol: 0, + iophmi: 0, + ioph2p: 0, + iophem: 0, + ispodf: 0, + }; + + let freq = vec![1e15]; + let freq_data = Opadd0FreqData { + freq: &freq, + ifreqb: None, + }; + + let mut bfcs = vec![0.0f32; 10 * 1]; + let mut output = Opadd0OutputState { + bfcs: &mut bfcs, + mfreq: 1, + }; + + opadd0(¶ms, &freq_data, &mut output); + + // 验证 H I Rayleigh 截面被设置 + assert!(output.bfcs[0] > 0.0); + } + + #[test] + fn test_opadd0_all_sources() { + let params = Opadd0Params { + ij: 1, + ncon: 0, + mcross: 20, + irsct: 1, + irsche: 1, + irsch2: 1, + ifmol: 1, + iophmi: 1, + ioph2p: 1, + iophem: 1, + ispodf: 0, + }; + + let freq = vec![1e15]; + let freq_data = Opadd0FreqData { + freq: &freq, + ifreqb: None, + }; + + let mut bfcs = vec![0.0f32; 20 * 1]; + let mut output = Opadd0OutputState { + bfcs: &mut bfcs, + mfreq: 1, + }; + + opadd0(¶ms, &freq_data, &mut output); + + // 验证所有截面都被设置 + // H I Rayleigh (1) + assert!(output.bfcs[0] > 0.0); + // He I Rayleigh (2) + assert!(output.bfcs[1] > 0.0); + // H2 Rayleigh (3) + assert!(output.bfcs[2] > 0.0); + // H- (4) + assert!(output.bfcs[3] > 0.0); + // H2+ bf (5) + // H2+ ff (6) + // He- (7,8,9) + } +} diff --git a/src/math/opctab.rs b/src/math/opctab.rs new file mode 100644 index 0000000..4749c61 --- /dev/null +++ b/src/math/opctab.rs @@ -0,0 +1,418 @@ + //! 不透明度表插值与散射计算。 + //! + //! 重构自 TLUSTY `opctab.f` + //! + //! 通过对预计算的不透明度表进行二维线性插值(温度-密度)来计算给定温度和密度下的吸收不透明度。 + //! 同时计算散射不透明度,包括: + //! - Rayleigh 散射(简单公式或完整 rayleigh 函数) + //! - 电子散射 + //! + //! 这是一个简化版本,所有插值都是线性的,fortran中也是线性插值。 + +use crate::math::rayleigh::{rayleigh, RayleighParams}; +use crate::state::model::{EosPar, RaySct}; + +/// 参考频率 (FRRAY0) +const FRRAY0: f64 = 5.0872638e14; + +/// OPCTAB 输入参数 +pub struct OpctabParams<'a> { + /// 频率 (Hz) + pub fr: f64, + /// 频率索引 (1-indexed) + pub ij: usize, + /// 深度索引 (1-indexed) + pub id: usize, + /// 温度 (K) + pub t: f64, + /// 密度 (g cm^-3) + pub rho: f64, + /// 克拉马标志 (0: 返回每克, 1: 返回每体积) + pub igram: i32, + /// 迭代次数 + pub iter: i32, + /// Rayleigh 散射标志 (<0: 使用简单公式, >0: 调用 rayleigh, =0: 关闭) + pub ifrayl: i32, + /// 选项表标志 (<0: 添加电子散射) + pub ioptab: i32, + /// Rayleigh 参数 (当 ifrayl > 0 时需要) + pub rayleigh_params: Option<&'a RayleighParams<'a>>, +} + +/// OPCTAB 表数据 +pub struct OpctabTableData<'a> { + /// 温度数 + pub numtemp: usize, + /// 深度点数 + pub nd: usize, + /// 频率数 + pub nfreq: usize, + /// 最大密度点数 (用于数组维度) + pub max_numrh: usize, + /// 温度向量 (numtemp) + pub tempvec: &'a [f64], + /// 温度下限 (ln T) + pub ttab1: f64, + /// 温度上限 (ln T) + pub ttab2: f64, + /// 每个温度的密度数 (numtemp) + pub numrh: &'a [i32], + /// 密度矩阵 (numtemp × max_numrh) - 存储 ln(rho) + pub rhomat: &'a [f64], + /// 吸收不透明度表 (numtemp × max_numrh × nfreq) - 存储 ln(opacity) + pub absopac: &'a [f64], + /// Rayleigh 散射系数 (nd) + pub raysc: &'a [f64], + /// 频率数组 (nfreq) + pub freq: &'a [f64], + /// 电子散射系数 + pub sige: f64, +} + +/// OPCTAB 模型状态 +pub struct OpctabModelState<'a> { + /// 电子密度 (nd) + pub elec: &'a [f64], + /// 密度 (nd) + pub dens: &'a [f64], + /// Rayleigh 散射截面 (当 ifrayl > 0 时需要) + pub raysct: Option<&'a mut RaySct>, + /// EOS 粒子数密度 (当 ifrayl > 0 时需要) + pub eospar: Option<&'a EosPar>, +} + +/// OPCTAB 输出 +pub struct OpctabOutput { + /// 吸收不透明度 + pub ab: f64, + /// 散射不透明度 + pub sc: f64, + /// 总散射 + pub sct: f64, +} + +/// 通过插值计算不透明度。 +/// +/// # 参数 +/// +/// * `params` - 输入参数 +/// * `table` - 表数据 +/// * `model` - 模型状态 +/// +/// # 返回值 +/// +/// 不透明度输出结构 +/// +/// # Panics +/// +/// 当 `ifrayl > 0` 但 `rayleigh_params`、`raysct` 或 `eospar` 为 `None` 时 panic。 +pub fn opctab( + params: &OpctabParams, + table: &OpctabTableData, + model: &mut OpctabModelState, +) -> OpctabOutput { + let jf = params.ij; + let id = params.id; + let id_idx = id - 1; + + // 检查是否直接使用预计算的值 (numtemp == nd) + // Fortran: if(numtemp.eq.nd) then + let opac = if table.numtemp == table.nd { + // 直接使用当前深度点的值 + // Fortran: opac=absopac(id,1,jf) + table.absopac[id_idx * table.max_numrh * table.nfreq + jf - 1] + } else { + compute_interpolated_opacity(params, table, jf) + }; + + let ab = opac.exp(); + + // 散射计算 + let mut sct = 0.0; + + // 1. Rayleigh 散射 + if params.ifrayl < 0 { + // 简单公式: sct = raysc(id) * (freq(jf) / frray0)^4 + sct = table.raysc[id_idx] * (table.freq[jf - 1] / FRRAY0).powi(4); + } else if params.ifrayl > 0 { + // 调用完整的 rayleigh 函数 + let rayleigh_params = params + .rayleigh_params + .expect("rayleigh_params required when ifrayl > 0"); + let raysct = model + .raysct + .as_mut() + .expect("raysct required when ifrayl > 0"); + let eospar = model + .eospar + .as_ref() + .expect("eospar required when ifrayl > 0"); + + let scr = rayleigh(1, params.ij, params.id, rayleigh_params, raysct, eospar); + sct = scr / model.dens[id_idx]; + } + + // 电子散射 + if params.ioptab < 0 { + sct = sct + table.sige * model.elec[id_idx] / params.rho; + } + + // 如果迭代次数 <= 0,直接返回 + if params.iter <= 0 { + return OpctabOutput { ab, sc: sct, sct }; + } + + // 根据 IGRAM 标志调整 + let (ab_out, sc_out, sct_out) = if params.igram == 0 { + (ab * params.rho, sct * params.rho, sct * params.rho) + } else { + (ab, sct, sct) + }; + + OpctabOutput { + ab: ab_out, + sc: sc_out, + sct: sct_out, + } +} + +/// 计算插值不透明度(内部函数) +fn compute_interpolated_opacity(params: &OpctabParams, table: &OpctabTableData, jf: usize) -> f64 { + let tl = params.t.ln(); + let deltat = (tl - table.ttab1) / (table.ttab2 - table.ttab1) * (table.numtemp - 1) as f64; + + let mut jt = (1.0 + deltat.floor()) as isize; + if jt < 1 { + jt = 1; + } + if jt > table.numtemp as isize - 1 { + jt = table.numtemp as isize - 1; + } + let jt = jt as usize; + let jt_idx = jt - 1; + + let t1i = table.tempvec[jt_idx]; + let t2i = table.tempvec[jt_idx + 1]; + let mut dti = (tl - t1i) / (t2i - t1i); + if deltat < 0.0 { + dti = 0.0; + } + + // 检查是否需要密度插值 + // Fortran: if(numrh(1).ne.1) then + if table.numrh[0] != 1 { + compute_2d_interpolation(params, table, jf, jt_idx, dti) + } else { + // 单密度情况: jr = 1 (Fortran), 所以 jr_idx = 0 + // Fortran: opac=absopac(jt,jr,jf)+(absopac(ju,jr,jf)-absopac(jt,jr,jf))*dti + let jr_idx = 0; // jr = 1 in Fortran + let idx_jt = jt_idx * table.max_numrh * table.nfreq + jr_idx * table.nfreq + (jf - 1); + let idx_ju = (jt_idx + 1) * table.max_numrh * table.nfreq + jr_idx * table.nfreq + (jf - 1); + table.absopac[idx_jt] + (table.absopac[idx_ju] - table.absopac[idx_jt]) * dti + } +} + +/// 二维插值(温度和密度) +fn compute_2d_interpolation( + params: &OpctabParams, + table: &OpctabTableData, + jf: usize, + jt_idx: usize, + dti: f64, +) -> f64 { + let rl = params.rho.ln(); + + // 低温下的密度插值 + // Fortran: numrho=numrh(jt), rtab1=rhomat(jt,1), rtab2=rhomat(jt,numrho) + let numrho = table.numrh[jt_idx] as usize; + let rtab1 = table.rhomat[jt_idx * table.max_numrh]; + let rtab2 = table.rhomat[jt_idx * table.max_numrh + numrho - 1]; + + let deltar = (rl - rtab1) / (rtab2 - rtab1) * (numrho - 1) as f64; + + let mut jr = (1.0 + deltar.floor()) as isize; + if jr < 1 { + jr = 1; + } + if jr > numrho as isize - 1 { + jr = numrho as isize - 1; + } + let jr = jr as usize; + let jr_idx = jr - 1; + + // Fortran: r1i=rhomat(jt,jr), r2i=rhomat(jt,jr+1) + let r1i = table.rhomat[jt_idx * table.max_numrh + jr_idx]; + let r2i = table.rhomat[jt_idx * table.max_numrh + jr_idx + 1]; + let mut dri = (rl - r1i) / (r2i - r1i); + if deltar < 0.0 { + dri = 0.0; + } + + // Fortran: opr1=absopac(jt,jr,jf)+dri*(absopac(jt,jr+1,jf)-absopac(jt,jr,jf)) + let idx_jt_jr = jt_idx * table.max_numrh * table.nfreq + jr_idx * table.nfreq + (jf - 1); + let idx_jt_jrp1 = jt_idx * table.max_numrh * table.nfreq + (jr_idx + 1) * table.nfreq + (jf - 1); + let opr1 = + table.absopac[idx_jt_jr] + dri * (table.absopac[idx_jt_jrp1] - table.absopac[idx_jt_jr]); + + // 高温下的密度插值 + // Fortran: ju=jt+1, numrho=numrh(ju) + let ju_idx = jt_idx + 1; + let numrho_h = table.numrh[ju_idx] as usize; + let rtab1_h = table.rhomat[ju_idx * table.max_numrh]; + let rtab2_h = table.rhomat[ju_idx * table.max_numrh + numrho_h - 1]; + + let deltar_h = (rl - rtab1_h) / (rtab2_h - rtab1_h) * (numrho_h - 1) as f64; + + let mut jr_h = (1.0 + deltar_h.floor()) as isize; + if jr_h < 1 { + jr_h = 1; + } + if jr_h > numrho_h as isize - 1 { + jr_h = numrho_h as isize - 1; + } + let jr_h = jr_h as usize; + let jr_h_idx = jr_h - 1; + + let r1i_h = table.rhomat[ju_idx * table.max_numrh + jr_h_idx]; + let r2i_h = table.rhomat[ju_idx * table.max_numrh + jr_h_idx + 1]; + let mut dri_h = (rl - r1i_h) / (r2i_h - r1i_h); + if deltar_h < 0.0 { + dri_h = 0.0; + } + + // Fortran: opr2=absopac(ju,jr,jf)+dri*(absopac(ju,jr+1,jf)-absopac(ju,jr,jf)) + let idx_ju_jr = ju_idx * table.max_numrh * table.nfreq + jr_h_idx * table.nfreq + (jf - 1); + let idx_ju_jrp1 = ju_idx * table.max_numrh * table.nfreq + (jr_h_idx + 1) * table.nfreq + (jf - 1); + let opr2 = table.absopac[idx_ju_jr] + + dri_h * (table.absopac[idx_ju_jrp1] - table.absopac[idx_ju_jr]); + + // Fortran: opac=opr1+(opr2-opr1)*dti + opr1 + (opr2 - opr1) * dti +} + +#[cfg(test)] +mod tests { + use super::*; + use approx::assert_relative_eq; + + #[test] + fn test_opctab_constants() { + assert_relative_eq!(FRRAY0, 5.0872638e14, epsilon = 1e9); + } + + #[test] + fn test_opctab_direct_value() { + let params = OpctabParams { + fr: 1e15, + ij: 1, + id: 1, + t: 10000.0, + rho: 1e-7, + igram: 0, + iter: 1, + ifrayl: -1, + ioptab: 0, + rayleigh_params: None, + }; + + let numtemp = 1; + let nd = 1; + let nfreq = 1; + let max_numrh = 1; + let tempvec = vec![9.2103]; + let numrh = vec![1]; + let rhomat = vec![-16.1181]; + let absopac = vec![0.0]; + let raysc = vec![1e-20]; + let freq = vec![5.0872638e14]; + + let table = OpctabTableData { + numtemp, + nd, + nfreq, + max_numrh, + tempvec: &tempvec, + ttab1: 8.0, + ttab2: 11.0, + numrh: &numrh, + rhomat: &rhomat, + absopac: &absopac, + raysc: &raysc, + freq: &freq, + sige: 6.65e-25, + }; + + let elec = vec![1e-10]; + let dens = vec![1e-7]; + let mut model = OpctabModelState { + elec: &elec, + dens: &dens, + raysct: None, + eospar: None, + }; + + let result = opctab(¶ms, &table, &mut model); + + // 当 numtemp == nd 时,直接使用 absopac(id, 1, jf) = 0.0 + // ab = exp(0.0) * rho = 1.0 * 1e-7 = 1e-7 + assert_relative_eq!(result.ab, 1e-7, epsilon = 1e-10); + } + + #[test] + fn test_opctab_rayleigh_scattering() { + let params = OpctabParams { + fr: 1e15, + ij: 1, + id: 1, + t: 10000.0, + rho: 1e-7, + igram: 0, + iter: 1, + ifrayl: -1, + ioptab: 0, + rayleigh_params: None, + }; + + let numtemp = 1; + let nd = 1; + let nfreq = 1; + let max_numrh = 1; + let tempvec = vec![9.2103]; + let numrh = vec![1]; + let rhomat = vec![-16.1181]; + let absopac = vec![0.0]; + let raysc = vec![1e-20]; + let freq = vec![5.0872638e14]; + + let table = OpctabTableData { + numtemp, + nd, + nfreq, + max_numrh, + tempvec: &tempvec, + ttab1: 8.0, + ttab2: 11.0, + numrh: &numrh, + rhomat: &rhomat, + absopac: &absopac, + raysc: &raysc, + freq: &freq, + sige: 6.65e-25, + }; + + let elec = vec![1e-10]; + let dens = vec![1e-7]; + let mut model = OpctabModelState { + elec: &elec, + dens: &dens, + raysct: None, + eospar: None, + }; + + let result = opctab(¶ms, &table, &mut model); + + // Rayleigh 散射 = raysc * (freq/frray0)^4 * rho + // = 1e-20 * 1.0^4 * 1e-7 = 1e-27 + assert_relative_eq!(result.sct, 1e-27, epsilon = 1e-30); + } +} diff --git a/src/math/ratmat.rs b/src/math/ratmat.rs new file mode 100644 index 0000000..7cf55e7 --- /dev/null +++ b/src/math/ratmat.rs @@ -0,0 +1,369 @@ +//! 速率矩阵计算 - RATMAT。 +//! +//! 重构自 TLUSTY `ratmat.f` +//! +//! 计算统计平衡方程的速率矩阵 A 和右端向量 B。 +//! +//! 方程形式为:A * n = B,其中 n 是能级占据数向量。 +//! +//! 参考:Mihalas, 1978, pp.138-139 + +use crate::math::reflev::reflev; +use crate::state::atomic::AtomicData; +use crate::state::config::TlustyConfig; +use crate::state::constants::{HK, MLEVEL, UN}; +use crate::state::iterat::IterControl; +use crate::state::model::ModelState; + +/// RATMAT 输入参数 +pub struct RatmatParams<'a> { + /// 深度索引 (1-indexed) + pub id: usize, + /// 能级索引数组 (可能为负) + pub iical: &'a mut [i32], + /// 模式标志 (<0: 特殊模式, 0: 正常, >0: 调用 REFLEV) + pub imode: i32, +} + +/// RATMAT 输出 +pub struct RatmatOutput { + /// 速率矩阵 A (MLEVEL × MLEVEL) + pub a: Vec>, + /// 右端向量 B (MLEVEL) + pub b: Vec, +} + +/// 计算速率矩阵。 +/// +/// # 参数 +/// +/// * `params` - 输入参数 +/// * `config` - 配置 +/// * `atomic` - 原子数据 +/// * `model` - 模型状态 +/// * `iterat` - 迭代控制 +/// +/// # 返回值 +/// +/// 速率矩阵 A 和右端向量 B +pub fn ratmat( + params: &mut RatmatParams, + config: &mut TlustyConfig, + atomic: &mut AtomicData, + model: &mut ModelState, + iterat: &IterControl, +) -> RatmatOutput { + let id = params.id; + let id_idx = id - 1; + + // 检查选项表标志 + if config.basnum.ioptab < 0 { + return RatmatOutput { + a: vec![vec![0.0; MLEVEL]; MLEVEL], + b: vec![0.0; MLEVEL], + }; + } + + let nlevel = config.basnum.nlevel as usize; + let ntrans = config.basnum.ntrans as usize; + let natom = config.basnum.natom as usize; + let lte = config.inppar.lte; + let ipslte = config.inppar.ipslte; + + // 如果使用 Slater 迭代,清零辐射速率 + if ipslte != 0 { + for itr in 0..ntrans { + model.rrrates.rru[itr][id_idx] = 0.0; + model.rrrates.rrd[itr][id_idx] = 0.0; + model.rrrates.drdt[itr][id_idx] = 0.0; + } + } + + // 初始化输出 + let mut a = vec![vec![0.0; MLEVEL]; MLEVEL]; + let mut b = vec![0.0; MLEVEL]; + + // 工作数组 + let mut aij = vec![0.0; ntrans.max(1)]; + let mut aji = vec![0.0; ntrans.max(1)]; + let mut sbw = vec![0.0; MLEVEL]; + let mut llte = vec![false; MLEVEL]; + + // 获取物理量 + let t = model.modpar.temp[id_idx]; + let ane = model.modpar.elec[id_idx]; + let hkt = HK / t; + let tk = hkt / HK; + + // 初始化 + for i in 0..nlevel { + b[i] = 0.0; + sbw[i] = ane * model.levpop.sbf[i] * model.wmcomp.wop[i][id_idx]; + llte[i] = false; + + // 设置 LTE 标志 + let iel_i = atomic.levpar.iel[i] as usize; + let ilt = atomic.ionpar.iltion[iel_i]; + let llt = ilt == 1 && params.imode == 0; + llte[i] = llt || lte || atomic.ionpar.iltlev[i] >= 1 || ilt >= 2; + llte[i] = llte[i] || id >= config.inppar.idlte as usize; + + for j in 0..nlevel { + a[j][i] = 0.0; + } + } + + // 确定参考能级 + if params.imode != 0 { + reflev(id, params.imode.abs(), config, atomic, model, iterat); + } + + // 第一部分:LTE 情况下的简化表达式 + for iat in 0..natom { + if atomic.atopar.iifix[iat] != 1 || params.imode < 0 { + let nrefi = atomic.atopar.nrefs[iat][id_idx] as usize; + let n0 = atomic.atopar.n0a[iat] as usize; + let nk = atomic.atopar.nka[iat] as usize; + + for i in n0..=nk { + let ii = params.iical[i].abs() as usize; + if i != nrefi && ii != 0 && llte[i] { + a[ii][ii] = UN; + let n = params.iical[model.levref.ilterf[i][id_idx] as usize].abs() as usize; + a[ii][n] = a[ii][n] - model.levref.sblpsi[i][id_idx]; + } + } + } + } + + // 第二部分:NLTE 速率方程 + if !lte { + // 计算跃迁速率 + for itr in 0..ntrans { + let i = atomic.trapar.ilow[itr] as usize; + if atomic.atopar.iifix[atomic.trapar.iatm[i] as usize] == 1 { + continue; + } + let j = atomic.trapar.iup[itr] as usize; + let nke = atomic.ionpar.nnext[atomic.levpar.iel[i] as usize] as usize; + + // 向上总速率 + aij[itr] = (model.rrrates.colrat[itr][id_idx] + model.rrrates.rru[itr][id_idx]) + * model.wmcomp.wop[j][id_idx]; + + // 向下总速率 + if atomic.trapar.line[itr] { + // 束缚-束缚跃迁 + aji[itr] = (model.rrrates.coltar[itr][id_idx] + + model.rrrates.rrd[itr][id_idx] + * atomic.levpar.g[i] / atomic.levpar.g[j] + * (hkt * atomic.trapar.fr0[itr]).exp()) + * model.wmcomp.wop[i][id_idx]; + } else { + // 束缚-自由跃迁 + let corr = if nke != j { + atomic.levpar.g[nke] / atomic.levpar.g[j] + * ((atomic.levpar.enion[nke] - atomic.levpar.enion[j]) * tk).exp() + } else { + UN + }; + aji[itr] = model.rrrates.coltar[itr][id_idx] * model.wmcomp.wop[i][id_idx] + + model.rrrates.rrd[itr][id_idx] * sbw[i] * corr; + } + + // 处理负索引 + if params.iical[i] < 0 && params.iical[j] < 0 { + aij[itr] *= model.levref.sbpsi[i][id_idx]; + aji[itr] *= model.levref.sbpsi[j][id_idx]; + } + } + + // 填充速率矩阵 + for itr in 0..ntrans { + let i = atomic.trapar.ilow[itr] as usize; + if atomic.atopar.iifix[atomic.trapar.iatm[i] as usize] == 1 { + continue; + } + let nrefi = atomic.atopar.nrefs[atomic.trapar.iatm[i] as usize][id_idx] as usize; + let j = atomic.trapar.iup[itr] as usize; + let ii = params.iical[i].abs() as usize; + let jj = params.iical[j].abs() as usize; + + if model.popzr0.ipzero[i][id_idx] != 0 || model.popzr0.ipzero[j][id_idx] != 0 { + continue; + } + + // 下能级贡献 + if i != nrefi && ii > 0 && !llte[i] { + a[ii][ii] += aij[itr]; + if jj > 0 { + a[ii][jj] -= aji[itr]; + } else { + let jjj = params.iical[model.levref.iltref[j][id_idx] as usize] as usize; + a[ii][jjj] -= aji[itr] * model.levref.sbpsi[j][id_idx]; + } + } + + // 上能级贡献 + if j != nrefi && jj > 0 && !llte[j] { + a[jj][jj] += aji[itr]; + if ii > 0 { + a[jj][ii] -= aij[itr]; + } else { + let iii = params.iical[model.levref.iltref[i][id_idx] as usize] as usize; + a[jj][iii] -= aij[itr] * model.levref.sbpsi[i][id_idx]; + } + } + } + } + + // 重置"小"占据数的速率矩阵元素 + for i in 0..nlevel { + let ii = params.iical[i]; + if ii > 0 { + let ii_idx = ii as usize; + if model.popzr0.ipzero[i][id_idx] > 0 { + for j in 0..nlevel { + a[ii_idx][j] = 0.0; + } + a[ii_idx][ii_idx] = 1.0; + } + } else if ii < 0 { + let ii_idx = (-ii) as usize; + if model.popzr0.igzero[ii_idx - 1][id_idx] > 0 { + for j in 0..nlevel { + a[ii_idx - 1][j] = 0.0; + } + a[ii_idx - 1][ii_idx - 1] = 1.0; + } + } + } + + // 第三部分:丰度定义方程 + for iat in 0..natom { + if atomic.atopar.iifix[iat] == 1 && params.imode >= 0 { + continue; + } + let nrefii = params.iical[atomic.atopar.nrefs[iat][id_idx] as usize].abs() as usize; + let n0 = atomic.atopar.n0a[iat] as usize; + let nk = atomic.atopar.nka[iat] as usize; + + for i in n0..=nk { + let il = atomic.levpar.ilk[i]; + let ii = params.iical[i]; + if ii > 0 { + let ii_idx = ii as usize; + a[nrefii][ii_idx] += UN; + if il != 0 { + a[nrefii][ii_idx] += ane * model.levpop.usum[il as usize]; + } + } else if ii < 0 { + let ii_idx = (-ii) as usize; + a[nrefii][ii_idx - 1] += model.levref.sbpsi[i][id_idx]; + if il != 0 { + a[nrefii][ii_idx - 1] += + ane * model.levpop.usum[il as usize] * model.levref.sbpsi[i][id_idx]; + } + } else { + let iii = params.iical[model.levref.iltref[i][id_idx] as usize] as usize; + if iii > 0 && iii <= nlevel { + a[nrefii][iii - 1] += model.levref.sbpsi[i][id_idx]; + } + } + } + + b[nrefii] += model.modpar.dens[id_idx] + / model.modpar.wmm[id_idx] + / model.modpar.ytot[id_idx] + * atomic.atopar.abund[iat][id_idx]; + } + + RatmatOutput { a, b } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ratmat_ioptab_negative() { + let mut config = TlustyConfig::default(); + let mut atomic = AtomicData::default(); + let mut model = ModelState::default(); + let iterat = IterControl::default(); + + config.basnum.ioptab = -1; + config.basnum.nlevel = 10; + config.basnum.ntrans = 5; + config.basnum.natom = 1; + + let mut iical = vec![0; MLEVEL]; + let mut params = RatmatParams { + id: 1, + iical: &mut iical, + imode: 0, + }; + + let result = ratmat(&mut params, &mut config, &mut atomic, &mut model, &iterat); + + // 当 ioptab < 0 时,应返回零矩阵 + assert_eq!(result.a.len(), MLEVEL); + assert_eq!(result.b.len(), MLEVEL); + for i in 0..MLEVEL { + assert_relative_eq!(result.b[i], 0.0); + } + } + + #[test] + fn test_ratmat_basic_lte() { + let mut config = TlustyConfig::default(); + let mut atomic = AtomicData::default(); + let mut model = ModelState::default(); + let iterat = IterControl::default(); + + config.basnum.ioptab = 0; + config.basnum.nlevel = 5; + config.basnum.ntrans = 3; + config.basnum.natom = 1; + config.inppar.lte = true; + + atomic.atopar.n0a[0] = 0; + atomic.atopar.nka[0] = 4; + atomic.atopar.nrefs[0][0] = 2; + atomic.atopar.iifix[0] = 0; + + atomic.levpar.iel[0] = 0; + atomic.levpar.iel[1] = 0; + atomic.levpar.iel[2] = 1; + atomic.levpar.iel[3] = 1; + atomic.levpar.iel[4] = 1; + atomic.levpar.ilk[0] = 0; + atomic.levpar.ilk[1] = 0; + atomic.levpar.ilk[2] = 1; + atomic.levpar.ilk[3] = 0; + atomic.levpar.ilk[4] = 0; + + atomic.ionpar.iltion[0] = 1; + atomic.ionpar.iltion[1] = 1; + + model.modpar.temp[0] = 10000.0; + model.modpar.elec[0] = 1e12; + model.modpar.dens[0] = 1e14; + model.modpar.wmm[0] = 1.0; + model.modpar.ytot[0] = 1.0; + atomic.atopar.abund[0][0] = 1.0; + + let mut iical = vec![1, 2, 3, 4, 5]; + let mut params = RatmatParams { + id: 1, + iical: &mut iical, + imode: 0, + }; + + let result = ratmat(&mut params, &mut config, &mut atomic, &mut model, &iterat); + + // 验证矩阵维度 + assert_eq!(result.a.len(), MLEVEL); + assert_eq!(result.b.len(), MLEVEL); + } +} diff --git a/src/state/alipar.rs b/src/state/alipar.rs index 2b477c2..e90a46c 100644 --- a/src/state/alipar.rs +++ b/src/state/alipar.rs @@ -144,14 +144,20 @@ pub struct FixAlp { pub dsfdt: Vec, /// 源函数 N 导数 pub dsfdn: Vec, + /// 源函数 M 导数 + pub dsfdm: Vec, /// 前深度源函数 T 导数 pub dsfdtm: Vec, /// 前深度源函数 N 导数 pub dsfdnm: Vec, + /// 前深度源函数 M 导数 + pub dsfdmm: Vec, /// 后深度源函数 T 导数 pub dsfdtp: Vec, /// 后深度源函数 N 导数 pub dsfdnp: Vec, + /// 后深度源函数 M 导数 + pub dsfdmp: Vec, // 源函数能级导数 (MLVEXP × MDEPTH) /// 源函数能级导数 @@ -271,10 +277,13 @@ impl Default for FixAlp { // 源函数导数 dsfdt: vec![0.0; MDEPTH], dsfdn: vec![0.0; MDEPTH], + dsfdm: vec![0.0; MDEPTH], dsfdtm: vec![0.0; MDEPTH], dsfdnm: vec![0.0; MDEPTH], + dsfdmm: vec![0.0; MDEPTH], dsfdtp: vec![0.0; MDEPTH], dsfdnp: vec![0.0; MDEPTH], + dsfdmp: vec![0.0; MDEPTH], // 源函数能级导数 dsfdp: vec![vec![0.0; MDEPTH]; MLVEXP],