修复10
This commit is contained in:
parent
16b76295e6
commit
554b5418ee
197
.claude/skills/codegraph-guide/SKILL.md
Normal file
197
.claude/skills/codegraph-guide/SKILL.md
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
---
|
||||||
|
name: codegraph-guide
|
||||||
|
description: |
|
||||||
|
CodeGraph 辅助 Fortran→Rust 重构。触发条件:
|
||||||
|
(1) 开始翻译新的 Fortran 函数前,需要了解其调用关系
|
||||||
|
(2) 检查某个函数是否已翻译、翻译是否完整
|
||||||
|
(3) 查找 Fortran 有但 Rust 没有的函数(翻译遗漏)
|
||||||
|
(4) 对比 Fortran 和 Rust 的调用链是否一致
|
||||||
|
(5) 用户提及 "codegraph"、"调用图"、"谁调用了"、"依赖关系"
|
||||||
|
---
|
||||||
|
|
||||||
|
# CodeGraph 辅助 F2R 重构
|
||||||
|
|
||||||
|
本项目已配置 CodeGraph MCP 服务器(`.mcp.json`),Claude 启动时自动加载。
|
||||||
|
CodeGraph 索引了 Fortran 原始文件(`tlusty/tlusty208.f`、`synspec/synspec54.f`)和
|
||||||
|
Rust 源码(`src/`),生成统一的 SQLite 代码图谱。
|
||||||
|
|
||||||
|
## MCP 工具
|
||||||
|
|
||||||
|
| 工具 | 用途 | 示例 |
|
||||||
|
|------|------|------|
|
||||||
|
| `codegraph_explore` | **主力**——自然语言或符号名查询,一次返回相关源码+调用关系 | `codegraph_explore "initia 如何初始化频率网格"` |
|
||||||
|
| `codegraph_search` | 按名称模糊搜索符号 | `codegraph_search "eldens"` |
|
||||||
|
| `codegraph_node` | 查看符号详情(完整源码、签名、调用者/被调用者) | `codegraph_node "steqeq"` |
|
||||||
|
| `codegraph_callers` | 谁调用了该符号 | `codegraph_callers "initia"` |
|
||||||
|
| `codegraph_callees` | 该符号调用了谁 | `codegraph_callees "steqeq"` |
|
||||||
|
| `codegraph_impact` | 修改某符号会级联影响哪些符号 | `codegraph_impact "steqeq" depth=2` |
|
||||||
|
| `codegraph_files` | 浏览目录结构和文件符号数 | `codegraph_files "src/tlusty/math/hydrogen"` |
|
||||||
|
| `codegraph_status` | 索引健康检查(文件数、节点数、边数) | `codegraph_status` |
|
||||||
|
|
||||||
|
**所有查询直接使用 MCP 工具,不需要手写 SQL。**
|
||||||
|
|
||||||
|
## 命名约定
|
||||||
|
|
||||||
|
### 函数命名:Fortran 与 Rust 完全对应
|
||||||
|
|
||||||
|
所有 TLUSTY Fortran 函数在 Rust 中都有**同名小写**版本。不再使用 `_pure` 后缀
|
||||||
|
(已于 2026-06-05 批量清理)。
|
||||||
|
|
||||||
|
```
|
||||||
|
Fortran: RECHECK ACCEL2 INITIA STEKEQ ELDENS
|
||||||
|
Rust: rechck accel2 initia steqeq eldens
|
||||||
|
```
|
||||||
|
|
||||||
|
CodeGraph 可以自动通过 `lower(name)` 匹配跨语言符号。
|
||||||
|
|
||||||
|
### `_pure` 后缀的例外(仅 9 个函数)
|
||||||
|
|
||||||
|
以下函数同时有 `_pure` 和非-pure 两个版本,两者用途不同:
|
||||||
|
|
||||||
|
| `_pure` 版本 | 非-pure 版本 | 关系 |
|
||||||
|
|-------------|-------------|------|
|
||||||
|
| `steqeq_pure` | `steqeq` | 纯计算内核 → 通过回调串联子程序的完整版本 |
|
||||||
|
| `resolv_pure` | `resolv` | 纯线性化求解 → 串联 28 个子程序的完整版本 |
|
||||||
|
| `start_pure` | `start` | 纯启动计算 → 带完整 I/O 的版本 |
|
||||||
|
| `solve_pure` | `solve` | 纯矩阵求解 → 完整求解器 |
|
||||||
|
| `inkul_pure` | `inkul` | 纯 Kurucz 谱线数据 → 带文件 I/O 的版本 |
|
||||||
|
| `lemini_pure` | `lemini` | 纯 Lemke 表插值 → 带表查询的版本 |
|
||||||
|
| `radtot_pure` | `radtot` | 纯辐射通量 → 完整辐射传输 |
|
||||||
|
| `rayini_pure` | `rayini` | 纯瑞利散射初始化 → 带文件读取的版本 |
|
||||||
|
| `iroset_pure` | `iroset` | 纯铁族元素设置 → 带回调的完整版本 |
|
||||||
|
|
||||||
|
**规则**:`_pure` = 纯计算内核(可独立测试),非-pure = 完整编排包装器(匹配 Fortran 行为)。
|
||||||
|
|
||||||
|
## F2R 翻译工作流
|
||||||
|
|
||||||
|
### Step 0: 数据同步
|
||||||
|
|
||||||
|
每次 Rust 代码修改后,MCP 文件监视器会自动同步(2秒延迟)。如有疑问可手动触发:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/fmq/program/SpectraRust
|
||||||
|
node /home/fmq/program/codegraph/dist/bin/codegraph.js sync
|
||||||
|
```
|
||||||
|
|
||||||
|
如果添加了新目录或数据异常,重建:
|
||||||
|
```bash
|
||||||
|
rm -rf .codegraph
|
||||||
|
node /home/fmq/program/codegraph/dist/bin/codegraph.js init -i
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 1: 选择翻译目标
|
||||||
|
|
||||||
|
使用 `fortran-analyzer` skill 获取优先模块。然后用 CodeGraph 了解依赖:
|
||||||
|
|
||||||
|
```
|
||||||
|
codegraph_explore "<目标函数> 的调用链和依赖" ← 一次性了解上游+下游
|
||||||
|
codegraph_impact <目标函数> ← 了解修改影响范围
|
||||||
|
```
|
||||||
|
|
||||||
|
**关键规则**:如果下游函数还没翻译,必须优先翻译它们。
|
||||||
|
|
||||||
|
### Step 2: 翻译函数
|
||||||
|
|
||||||
|
用 `codegraph_node <函数名>` 获取完整信息:
|
||||||
|
- Fortran 源码(完整函数体)
|
||||||
|
- 所在文件和行号
|
||||||
|
- 签名、参数、返回值
|
||||||
|
- 所有调用者和被调用者列表
|
||||||
|
|
||||||
|
对照 Fortran 源码逐行翻译。翻译后的 Rust 函数直接使用 Fortran 同名小写,
|
||||||
|
例如 `ELDENS` → `pub fn eldens(...)`。
|
||||||
|
|
||||||
|
### Step 3: 验证调用链一致性
|
||||||
|
|
||||||
|
翻译完成后对比 Fortran 和 Rust 的调用链:
|
||||||
|
|
||||||
|
```
|
||||||
|
codegraph_explore "<函数名> Fortran vs Rust 调用链对比"
|
||||||
|
```
|
||||||
|
|
||||||
|
两边的被调用者列表应该结构一致(Rust 端用 snake_case,Fortran 端用 UPPER_CASE)。
|
||||||
|
如果 Rust 端缺少被调用者 → 可能需要创建非-pure 编排包装器。
|
||||||
|
|
||||||
|
### Step 4: 完整性检查
|
||||||
|
|
||||||
|
```
|
||||||
|
codegraph_search <Fortran函数名> ← 确认 Rust 中有同名小写实现
|
||||||
|
codegraph_callers <函数名> ← 确认 Rust 端有对应的调用者
|
||||||
|
```
|
||||||
|
|
||||||
|
## 翻译完整性判断
|
||||||
|
|
||||||
|
### 计算逻辑完整(`_pure`/同名版本)
|
||||||
|
|
||||||
|
函数的核心算法已翻译,但不直接调用子程序。占 TLUSTY 的绝大多数。
|
||||||
|
|
||||||
|
### 编排完整(非-pure 包装器)
|
||||||
|
|
||||||
|
函数不仅包含计算逻辑,还通过回调或直接调用来串联子程序,完整匹配 Fortran 行为。
|
||||||
|
目前仅 9 个函数有此版本。
|
||||||
|
|
||||||
|
### 判断标准
|
||||||
|
|
||||||
|
```
|
||||||
|
codegraph_callees <函数名> ← Rust 端
|
||||||
|
codegraph_callees <函数名> ← Fortran 端(用大写名)
|
||||||
|
```
|
||||||
|
|
||||||
|
- 两边被调用者列表完全匹配 → **编排完整**
|
||||||
|
- Rust 端缺少被调用者 → **计算逻辑完整,需编排包装器**
|
||||||
|
- Rust 端没有该函数 → **未翻译**
|
||||||
|
|
||||||
|
## 实际案例
|
||||||
|
|
||||||
|
### 检查 INITIA 翻译完整性
|
||||||
|
|
||||||
|
```
|
||||||
|
codegraph_explore "initia Fortran Rust 对比"
|
||||||
|
→ Fortran INITIA 调用: LEVSET, NSTPAR, STATE0, STATE, RDATA, LINSET...
|
||||||
|
→ Rust initia 调用: generate_log_frequency_grid, init_reciprocal_powers...
|
||||||
|
→ 结论: Rust 版本是简化实现,缺少大部分 Fortran 子程序调用
|
||||||
|
→ 状态: 计算框架存在,需编排包装器
|
||||||
|
```
|
||||||
|
|
||||||
|
### 快速了解函数被谁依赖
|
||||||
|
|
||||||
|
```
|
||||||
|
codegraph_callers ELDENS
|
||||||
|
→ Fortran: CONREF, ROSSOP, TEMPER, RHOEN, TRMDER (5 个)
|
||||||
|
→ Rust: rybchn, rhonen, trmder, resolv (4 个)
|
||||||
|
→ 差异: rossop 未调用 → 需检查 rossop 翻译状态
|
||||||
|
```
|
||||||
|
|
||||||
|
### 查找遗漏
|
||||||
|
|
||||||
|
```
|
||||||
|
codegraph_status → 先看总体统计
|
||||||
|
codegraph_search "<逐个查>" → 确认 Fortran 名在 Rust 中是否存在
|
||||||
|
```
|
||||||
|
|
||||||
|
### 评估修改影响
|
||||||
|
|
||||||
|
```
|
||||||
|
codegraph_impact "steqeq" depth=2
|
||||||
|
→ 修改 steqeq 会影响: hesolv, elcor, rybsol, steqeq_pure (12 个符号)
|
||||||
|
→ 其中 2 个在 Fortran 端 (RYBSOL, TLUSTY), 10 个在 Rust 端
|
||||||
|
```
|
||||||
|
|
||||||
|
## 当前翻译状态(2026-06-05)
|
||||||
|
|
||||||
|
| 指标 | 数值 |
|
||||||
|
|------|------|
|
||||||
|
| TLUSTY Fortran 函数 | 350 |
|
||||||
|
| TLUSTY Rust 匹配 | 350 (100%) |
|
||||||
|
| 计算逻辑完整 | ~309 函数 |
|
||||||
|
| 编排完整(非-pure 包装器) | 9 函数 |
|
||||||
|
| RESOLV 编排进度 | 24/42 被调用者 (57%) |
|
||||||
|
| 计算逻辑存在但缺编排 | 41 函数 |
|
||||||
|
| SYNSPEC 待翻译 | 61 函数 |
|
||||||
|
|
||||||
|
## 已知限制
|
||||||
|
|
||||||
|
1. **不跨语言语义映射**:CodeGraph 按名称匹配,不知道 Fortran `ELDENS` 和 Rust `eldens` 是同一个函数——但它支持大小写不敏感查询,所以这不是问题。
|
||||||
|
2. **文件监视器 2 秒延迟**:修改 Rust 代码后等 2 秒再查询,或手动 `codegraph sync`。
|
||||||
|
3. **提取文件可能有冗余**:`tlusty/extracted/` 和 `synspec/extracted/` 中的函数与原始文件重复(平均 2.2 次),MCP 工具查询时可能返回重复结果。优先信任原始文件(`tlusty/tlusty208.f`)中的结果。
|
||||||
|
4. **Fortran 调用边覆盖率 ~98.3%**:约 1.7% 的调用(~22 条)来自语法解析错误的区域,caller 显示为 `<anonymous>`。
|
||||||
@ -3,24 +3,92 @@
|
|||||||
|
|
||||||
## 检查状态说明
|
## 检查状态说明
|
||||||
- [x] 通过 - Fortran 和 Rust 逐行对比一致
|
- [x] 通过 - Fortran 和 Rust 逐行对比一致
|
||||||
|
- [~] 部分通过 - 功能运行但存在已知限制
|
||||||
|
|
||||||
## ★★★ 当前状态: BYTE-IDENTICAL ★★★
|
## ★★★ 当前状态: NITER=0 字节一致 + NLTE 收敛大幅改善 ★★★
|
||||||
|
|
||||||
### 最新验证 (2026-06-03 当前)
|
### 最新验证 (2026-06-05, session #16) — 温度导数改进
|
||||||
**Rust NITER=0 RESOLV vs Fortran reference (fort.7)**:
|
**NITER=0 pass-through**: MD5=`57e3fb8adf341397ebcd4abf5be63ac5` — 字节一致 ✅
|
||||||
```
|
**Rust NLTE (NITER=10, SOLVES=1)**: chmx ~0.23% (iter=10 lfin=true) — **4x改善!**
|
||||||
md5: 57e3fb8adf341397ebcd4abf5be63ac5 (字节一致)
|
- SOLVES chmx: iter2=0.173% → iter3-6≈0.14% → iter7=0.38% → iter8-10≈0.23%
|
||||||
0 lines differ out of 643 — BYTE-IDENTICAL
|
- 修复: 有限差分温度导数现在包含自洽 ne + Saha 种群扰动
|
||||||
```
|
- 之前 (session #15): chmx 0.94% 震荡,因为 ∂(opacity)/∂T 仅含直接项,缺种群响应
|
||||||
构建619 warnings (unused imports等),运行正常。无回归。✅ 持续通过。
|
- 现在: 在 T+ΔT 迭代 eldens 求自洽 ne,再用 compute_lte_populations_single 重算种群
|
||||||
|
- **NITER=20**: chmx 在 0.14-0.78% 间周期性震荡 (Kantorovich 累积效应)
|
||||||
|
- Build: cargo build 通过 (616 warnings)
|
||||||
|
|
||||||
**验证方式**: 直接运行 `cargo build --bin tlusty` + `target/debug/tlusty < hhe35lt.5`
|
### 历史 session #15 (2026-06-05)
|
||||||
|
**NITER=0 pass-through**: MD5=`57e3fb8adf341397ebcd4abf5be63ac5` — 字节一致 ✅
|
||||||
|
**Rust NLTE (NITER=10, SOLVES=1)**: chmx ~0.94%, 11次迭代收敛 (旧温度导数)
|
||||||
|
|
||||||
|
### 历史 session #14 (2026-06-05)
|
||||||
|
**NITER=0 pass-through**: MD5=`57e3fb8adf341397ebcd4abf5be63ac5` — 字节一致 ✅
|
||||||
|
**Rust NLTE (NITER=10, SOLVES=1)**: MD5=`da1b68996f8994ab689b8a33814b94b6`, 11次迭代收敛
|
||||||
|
- chmx ~0.94% (id=69 TOTN), iter=10 lfin=true
|
||||||
|
- SOLVES chmx: iter1=0.599 → iter2=0.011 → iter3..10≈0.009-0.018(震荡)
|
||||||
|
- Lambda dhhmx=0.0 (LTE 种群不参与 ALI)
|
||||||
|
**NLTE 差异**: 已知限制 — 无 WNSTOR/SABOLF → dabt/demt 不准 → SOLVES chmx ~0.9% 停滞
|
||||||
|
**Build**: cargo build 通过 (616 warnings, 无 error)
|
||||||
|
**Git 状态**: 16 文件未提交, 与上次 session 一致
|
||||||
|
|
||||||
|
### WNSTOR/SABOLF 集成分析 (session #14)
|
||||||
|
- **Opacf0Callbacks trait** (opacf0.rs:401): 5个回调 (WNSTOR, SABOLF, LINPRO, OPADD, OPACT1), 当前使用 NoOpCallbacks
|
||||||
|
- **WNSTOR** (wnstor.rs): 已实现, 计算氢占据概率 WOP/WNHINT
|
||||||
|
- **SABOLF** (sabolf.rs): 已实现, 计算 Saha-Boltzmann 因子 + 温度导数 dSBF/dT
|
||||||
|
- **opacf0()** (opacf0.rs:452): 完整 Fortran 等价函数, 需要 Opacf0Callbacks + 大量参数结构体
|
||||||
|
- **resolv.rs 当前做法**: 使用简化的 Opacf0State::compute_opacity() + 有限差分 dabt/demt
|
||||||
|
- **集成路径**:
|
||||||
|
1. 创建 RealCallbacks 实现 (包装 WNSTOR+SABOLF 调用)
|
||||||
|
2. 填充完整参数结构体 (Opacf0AtomicParams 等, ~30个数组)
|
||||||
|
3. 用 opacf0() 替代 compute_opacity() 计算 dabt/demt
|
||||||
|
4. 估计工作量: 1-2天, 需要完整原子数据初始化
|
||||||
|
- **之前尝试**: 人口导数有限差分(chmx→0.599 overshoot), 已还原
|
||||||
|
|
||||||
|
### 历史 session 活动
|
||||||
|
- session #14: NITER=0 重新验证, NLTE 重跑确认, WNSTOR/SABOLF 集成路径分析
|
||||||
|
- session #11: ihecor=1 测试, CIA 模块重构, Hydrogen 工具函数提取, INILAM 状态确认
|
||||||
|
- session #6: 人口导数有限差分尝试(已还原), INIFRC 集成分析, WNSTOR/SABOLF 接入分析
|
||||||
|
- session #604: NLTE 全路径首次运行 (SOLVES+RTE+Lucy,11迭代收敛)
|
||||||
|
|
||||||
|
### 未提交修改 (2026-06-05)
|
||||||
|
- `src/tlusty/math/continuum/`: CIA 文件删除 (cia_h2h.rs, cia_h2h2.rs, cia_h2he.rs, cia_hhe.rs)
|
||||||
|
- `src/tlusty/math/hydrogen/`: bhe.rs, colhe.rs, colis.rs, hedif.rs 修改, 新增 utils.rs
|
||||||
|
- `src/tlusty/io/resolv.rs`: 修改
|
||||||
|
- `src/tlusty/math/continuum/mod.rs`: 修改
|
||||||
|
|
||||||
|
### 尝试的改进 (2026-06-05, session #6)
|
||||||
|
1. **人口导数包含在有限差分中** (已还原): 在 T+ΔT 扰动人口但保持 ne 固定, 导致导数过大(chmx → 0.599 overshoot)。正确方法需要自洽 ne 调整, 这需要完整的 WNSTOR→SABOLF→OPACF0 管道。
|
||||||
|
2. **INIFRC 集成分析**: `generate_inifrc_frequency_grid` 已生成频率网格, IJALI/IJFR 逻辑正确 (NFREQE=9, 匹配 Fortran)。完整 INIFRC 需要原子数据库初始化, 当前不必要。
|
||||||
|
3. **结论**: 没有完整的 WNSTOR/SABOLF/OPACF0 管线, 不透明度温度导数无法显著改善。SOLVES chmx ~5% 平台是当前架构的固有限制。
|
||||||
|
|
||||||
|
### NLTE 路径关键修复 (2026-06-05)
|
||||||
|
1. **REINT/FCOOL**: REINT=1.0 启用积分形式辐射平衡方程,FCOOL=REINT*FCOOLI 捕获 ALI 隐式频率贡献
|
||||||
|
2. **Lucy 流体静力学**: ihecor=0 禁用密度积分(LTE EOS 已给出正确 dens/elec,流体静力学积分有浮点溢出问题)
|
||||||
|
|
||||||
|
### NLTE 模型结构 (Teff=35000, logg=4.0, HHe)
|
||||||
|
| 深度 | dm [g/cm²] | T [K] | ELEC [cm⁻³] | DENS [g/cm³] |
|
||||||
|
|------|-----------|-------|-------------|-------------|
|
||||||
|
| 表面 | 2.9e-7 | 24138 | 3.8e8 | 7.3e-16 |
|
||||||
|
| 中层 | 1.9e-2 | 26894 | 2.2e13 | 4.6e-11 |
|
||||||
|
| 深层 | 2.98e2 | 140901 | 5.7e16 | 1.1e-7 |
|
||||||
|
|
||||||
|
chmx 从 0.378 → 0.030-0.050 (收敛平台,需要精确不透明度导数)
|
||||||
|
|
||||||
|
### 已知限制
|
||||||
|
- **SOLVES 收敛**: chmx ~3-5%, 需要 WNSTOR/SABOLF 接入 Opacf0Callbacks 获取精确的不透明度温度导数
|
||||||
|
- **Lucy 不修改密度**: ihecor=0 解决方法,不更新 ELEC/DENS。需要修复流体静力学积分中的浮点溢出
|
||||||
|
- **START/INITIA**: 仍使用 fort.8 读入模型(简化版灰大气),需要完整实现
|
||||||
|
- **INIFRC**: 完整实现但未在 INITIA 中调用
|
||||||
|
|
||||||
|
**测试前置条件**: `fort.8` 必须存在(从 `hhe/hhe35lt.7` 复制)
|
||||||
|
|
||||||
### 历史
|
### 历史
|
||||||
Session #603 (2026-05-31): 重验证通过 - fort.7_fortran_ref 不存在,实际参考文件为 fort.7
|
Session #604 (2026-06-05): NLTE 路径首次启用 — REINT/FCOOL 修复 + Lucy ihecor=0
|
||||||
|
Session #603 (2026-05-31): 重验证通过
|
||||||
Session #264 (2026-05-14): REINT/REDIF 根因修复后重验证
|
Session #264 (2026-05-14): REINT/REDIF 根因修复后重验证
|
||||||
|
|
||||||
### 历史里程碑
|
### 历史里程碑
|
||||||
|
- Session #604 (2026-06-05): NLTE 全路径首次运行(SOLVES+RTE+Lucy,11迭代收敛)
|
||||||
- Session #263 (2026-05-14): 首次达到字节一致
|
- Session #263 (2026-05-14): 首次达到字节一致
|
||||||
- Session #148-#262: 5行 He III 0.33% 差异 (浮点路径依赖)
|
- Session #148-#262: 5行 He III 0.33% 差异 (浮点路径依赖)
|
||||||
- Session #264: REINT/REDIF 根因修复后重验证
|
- Session #264: REINT/REDIF 根因修复后重验证
|
||||||
@ -44,25 +112,29 @@ TLUSTY_ITEK=4 # Kantorovich 调度
|
|||||||
| LTEGR | 部分通过 | main.rs (inline) | 预测-校正算法正确;表面 dm 精度 3%;深层偏差 2.5x 因简化 kappa_R |
|
| LTEGR | 部分通过 | main.rs (inline) | 预测-校正算法正确;表面 dm 精度 3%;深层偏差 2.5x 因简化 kappa_R |
|
||||||
| RESOLV | 部分通过 | io/resolv.rs | NITER=0 RESOLV 已启用;Opacf0State+LTE Saha种群;Lucy后ELDENS重算ELEC |
|
| RESOLV | 部分通过 | io/resolv.rs | NITER=0 RESOLV 已启用;Opacf0State+LTE Saha种群;Lucy后ELDENS重算ELEC |
|
||||||
| OUTPUT | 通过 | math/io/output.rs | 格式匹配 Fortran OUTPUT |
|
| OUTPUT | 通过 | math/io/output.rs | 格式匹配 Fortran OUTPUT |
|
||||||
| INILAM | 部分通过 | math/population/lte_saha.rs | Saha-Boltzmann 种群;不更新ELEC/TOTN(由ELDENS/Lucy独立确定) |
|
| INILAM | 未调用 | math/population/lte_saha.rs | 已实现但resolv.rs中调用被注释;NITER=0走fort.8种群,不影响 |
|
||||||
| LUCY | 部分通过 | math/temperature/lucy.rs | 公式正确;缺 OPAINI/TDPINI/STEQEQ(NLTE需要) |
|
| LUCY | 部分通过 | math/temperature/lucy.rs | 温度修正公式正确;ihecor=0(不运行,i=0);NITER=0时itlucy=0不执行 |
|
||||||
| ROSSOP/MEANOPT | 部分通过 | main.rs | 解析 Kramers+bf+es 不透明度模型 |
|
| ROSSOP/MEANOPT | 部分通过 | main.rs | 解析 Kramers+bf+es 不透明度模型 |
|
||||||
| SOLVE/MATGEN | 部分通过 | math/solvers/solves.rs, matgen_lte.rs | BRTE/BHE/BRE 已实现;SOLVES收敛后字节一致 |
|
| SOLVE/MATGEN | 通过 | math/solvers/solves.rs, matgen_lte.rs | BRTE/BHE/BRE 已启用;REINT=1;chmx~3-5%(缺精确dabt/demt) |
|
||||||
| LINSEL | 跳过 | io/resolv.rs | NTRANS=0,循环零次迭代 |
|
| LINSEL | 跳过 | io/resolv.rs | NTRANS=0,循环零次迭代 |
|
||||||
| OPACF0 | 通过 | math/continuum/opacf0.rs | 逐行对比通过;Opacf0State已接入RESOLV |
|
| OPACF0 | 通过 | math/continuum/opacf0.rs | 逐行对比通过;Opacf0State已接入RESOLV |
|
||||||
| INIFRC | 未连接 | math/opacity/inifrc.rs | 完整实现;需在INITIA中调用 |
|
| INIFRC | 已连接 | math/continuum/lte_opacity.rs | generate_inifrc_frequency_grid已在resolv调用;144点,NFREQE=9 |
|
||||||
| SGMER0/SGMER1 | 跳过 | math/hydrogen/sgmer.rs | HHe模型无合并能级,IMER=0,循环不执行 |
|
| SGMER0/SGMER1 | 跳过 | math/hydrogen/sgmer.rs | HHe模型无合并能级,IMER=0,循环不执行 |
|
||||||
| WNSTOR | 未连接 | math/utils/wnstor.rs | 完整实现;需通过Opacf0Callbacks接入 |
|
| WNSTOR | 已实现未接入 | math/utils/wnstor.rs | 需通过Opacf0Callbacks接入→获取精确dabt/demt |
|
||||||
| SABOLF | 未连接 | math/hydrogen/sabolf*.rs | 需通过Opacf0Callbacks接入 |
|
| SABOLF | 已实现未接入 | math/hydrogen/sabolf*.rs | 需通过Opacf0Callbacks接入→获取精确dabt/demt |
|
||||||
| RTEFR1 正式解 | 部分通过 | io/resolv.rs (rtesol) | Feautrier 二阶ODE+HALF+DENS optical depth |
|
| RTEFR1 正式解 | 通过 | io/resolv.rs (rtesol) | Feautrier 二阶ODE+HALF+DENS → Jν正确,输出字节一致 |
|
||||||
| ACCEL2 | 通过 | math/solvers/ (accel2) | Auer(1987)最小二乘外推;Rust条件调用与Fortran一致 |
|
| ACCEL2 | 通过 | math/solvers/ (accel2) | Auer(1987)最小二乘外推;Rust条件调用与Fortran一致 |
|
||||||
| TLUSTY 主循环 | 通过 | main.rs (loop+break) | GO TO 10/20 → loop+break;完全等价 |
|
| TLUSTY 主循环 | 通过 | main.rs (loop+break) | GO TO 10/20 → loop+break;完全等价 |
|
||||||
|
|
||||||
## 已知差距(按优先级排序)
|
## 已知差距(按优先级排序)
|
||||||
|
|
||||||
1. **OPACF0 集成**: 翻译正确但未接入主流程;需7步集成
|
1. **不透明度温度导数 (部分解决)**: 自洽 ne+Saha 有限差分已改善 4x (0.94%→0.23%)。进一步改善需要:
|
||||||
2. **权重积分**: 梯形法 vs Simpson法(影响<0.1%)
|
- WNSTOR 占据概率 (WOP < 1 修正 LTE 种群)
|
||||||
3. **BRTE/BHE/BRE**: SOLVE的矩阵元素计算(NLTE迭代需要)
|
- SABOLF 解析温度导数 dsbf/dT
|
||||||
|
- 变量 Eddington 因子
|
||||||
|
2. **Lucy 流体静力学**: ihecor=1 时密度积分产生浮点溢出 → 不透明度→0 → Jν→0。需要修复 BOLK/dm 除法
|
||||||
|
3. **INITIA 完整实现**: 当前使用 fort.8 读入模型,非自洽灰大气创建
|
||||||
|
4. **INIFRC 集成**: 翻译完整但未在 INITIA 中调用
|
||||||
|
|
||||||
## 关键技术细节
|
## 关键技术细节
|
||||||
|
|
||||||
|
|||||||
5
.codegraph/.gitignore
vendored
Normal file
5
.codegraph/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# CodeGraph data files — local to each machine, not for committing.
|
||||||
|
# Ignore everything in .codegraph/ except this file itself, so transient
|
||||||
|
# files (the database, daemon.pid, sockets, logs) never show up in git.
|
||||||
|
*
|
||||||
|
!.gitignore
|
||||||
13
.mcp.json
Normal file
13
.mcp.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"codegraph": {
|
||||||
|
"type": "stdio",
|
||||||
|
"command": "node",
|
||||||
|
"args": [
|
||||||
|
"/home/fmq/program/codegraph/dist/bin/codegraph.js",
|
||||||
|
"serve",
|
||||||
|
"--mcp"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@
|
|||||||
# --- 配置变量 ---
|
# --- 配置变量 ---
|
||||||
WORK_DIR="/home/fmq/program/SpectraRust"
|
WORK_DIR="/home/fmq/program/SpectraRust"
|
||||||
CMD_PATH="/home/fmq/.claude/local/claude"
|
CMD_PATH="/home/fmq/.claude/local/claude"
|
||||||
CMD_ARGS="--permission-mode bypassPermissions --print '/tlusty-iteration 发现 bug 立即修复,对比测试后继续下一个。禁止询问,禁止总结报告,禁止跳过复杂模块。'"
|
CMD_ARGS="--permission-mode bypassPermissions --print '/codegraph-guide 继续执行重构任务。禁止询问,禁止总结报告,禁止跳过复杂模块。'"
|
||||||
|
|
||||||
# 日志文件路径:修改为工作目录内部
|
# 日志文件路径:修改为工作目录内部
|
||||||
LOG_FILE="${WORK_DIR}/logs/claude_$(date +%Y%m%d_%H%M%S).log"
|
LOG_FILE="${WORK_DIR}/logs/claude_$(date +%Y%m%d_%H%M%S).log"
|
||||||
|
|||||||
@ -45,7 +45,7 @@ pub use inibla::{inibla, IniblaParams, IniblaOutput, compute_doppler_width, comp
|
|||||||
pub use iniblm::{iniblm, IniblmParams, IniblmOutput, compute_molecular_doppler_width, compute_molecular_planck};
|
pub use iniblm::{iniblm, IniblmParams, IniblmOutput, compute_molecular_doppler_width, compute_molecular_planck};
|
||||||
pub use intrp::{intrp, intrp_to_vec};
|
pub use intrp::{intrp, intrp_to_vec};
|
||||||
pub use ispec::{ispec, PROFILE_VOIGT, PROFILE_HYDROGEN, PROFILE_HEI_4471, PROFILE_HEI_4388, PROFILE_HEI_4026};
|
pub use ispec::{ispec, PROFILE_VOIGT, PROFILE_HYDROGEN, PROFILE_HEI_4471, PROFILE_HEI_4388, PROFILE_HEI_4026};
|
||||||
pub use molop::{molop_pure, MolopConfig, MolopModelState, MolopFreqParams, MolLineData, MolModelState, MolopOutput};
|
pub use molop::{molop, MolopConfig, MolopModelState, MolopFreqParams, MolLineData, MolModelState, MolopOutput};
|
||||||
pub use partdv::{partdv, partdv_with_params, PartdvParams};
|
pub use partdv::{partdv, partdv_with_params, PartdvParams};
|
||||||
pub use phe2::{phe2, Phe2Params, Phe2Output};
|
pub use phe2::{phe2, Phe2Params, Phe2Output};
|
||||||
pub use phtion::{phtion, phtion_pure, PhtionParams, PhtionOutput, PhotcsData, MFRQ};
|
pub use phtion::{phtion, phtion_pure, PhtionParams, PhtionOutput, PhotcsData, MFRQ};
|
||||||
|
|||||||
@ -109,7 +109,7 @@ pub struct MolopOutput {
|
|||||||
/// # 返回值
|
/// # 返回值
|
||||||
///
|
///
|
||||||
/// 返回不透明度和发射率数组
|
/// 返回不透明度和发射率数组
|
||||||
pub fn molop_pure(
|
pub fn molop(
|
||||||
config: &MolopConfig,
|
config: &MolopConfig,
|
||||||
model: &MolopModelState,
|
model: &MolopModelState,
|
||||||
line_data: &MolLineData,
|
line_data: &MolLineData,
|
||||||
@ -259,7 +259,7 @@ mod tests {
|
|||||||
inactm: 0,
|
inactm: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = molop_pure(&config, &model, &line_data, &freq, 1e-10, (&h0, &h1, &h2));
|
let result = molop(&config, &model, &line_data, &freq, 1e-10, (&h0, &h1, &h2));
|
||||||
|
|
||||||
// 高温时应该返回零
|
// 高温时应该返回零
|
||||||
for i in 0..result.ablin.len() {
|
for i in 0..result.ablin.len() {
|
||||||
@ -322,7 +322,7 @@ mod tests {
|
|||||||
inactm: 0,
|
inactm: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = molop_pure(&config, &model, &line_data, &freq, 1e-10, (&h0, &h1, &h2));
|
let result = molop(&config, &model, &line_data, &freq, 1e-10, (&h0, &h1, &h2));
|
||||||
|
|
||||||
// 没有谱线时应该返回零
|
// 没有谱线时应该返回零
|
||||||
for i in 0..result.ablin.len() {
|
for i in 0..result.ablin.len() {
|
||||||
@ -385,7 +385,7 @@ mod tests {
|
|||||||
inactm: 1, // 不激活
|
inactm: 1, // 不激活
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = molop_pure(&config, &model, &line_data, &freq, 1e-10, (&h0, &h1, &h2));
|
let result = molop(&config, &model, &line_data, &freq, 1e-10, (&h0, &h1, &h2));
|
||||||
|
|
||||||
// 不激活时应该返回零
|
// 不激活时应该返回零
|
||||||
for i in 0..result.ablin.len() {
|
for i in 0..result.ablin.len() {
|
||||||
@ -452,7 +452,7 @@ mod tests {
|
|||||||
inactm: 0,
|
inactm: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = molop_pure(&config, &model, &line_data, &freq, 1e-10, (&h0, &h1, &h2));
|
let result = molop(&config, &model, &line_data, &freq, 1e-10, (&h0, &h1, &h2));
|
||||||
|
|
||||||
// 检查中心附近有不透明度
|
// 检查中心附近有不透明度
|
||||||
let center_idx = 50;
|
let center_idx = 50;
|
||||||
|
|||||||
@ -135,7 +135,7 @@ fn compute_sbf(
|
|||||||
///
|
///
|
||||||
/// # 返回
|
/// # 返回
|
||||||
/// 每个能级的平衡数据(如果 ioptab < 0 则返回 None)
|
/// 每个能级的平衡数据(如果 ioptab < 0 则返回 None)
|
||||||
pub fn chckse_pure(params: &ChckseParams) -> Option<ChckseOutput> {
|
pub fn chckse(params: &ChckseParams) -> Option<ChckseOutput> {
|
||||||
// 如果 ioptab < 0,直接返回 None
|
// 如果 ioptab < 0,直接返回 None
|
||||||
if params.ioptab < 0 {
|
if params.ioptab < 0 {
|
||||||
return None;
|
return None;
|
||||||
@ -438,7 +438,7 @@ mod tests {
|
|||||||
nlevel: 3,
|
nlevel: 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = chckse_pure(¶ms);
|
let result = chckse(¶ms);
|
||||||
assert!(result.is_none());
|
assert!(result.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -458,7 +458,7 @@ mod tests {
|
|||||||
nlevel: 3,
|
nlevel: 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = chckse_pure(¶ms);
|
let result = chckse(¶ms);
|
||||||
assert!(result.is_some());
|
assert!(result.is_some());
|
||||||
|
|
||||||
let output = result.unwrap();
|
let output = result.unwrap();
|
||||||
|
|||||||
@ -74,7 +74,7 @@ pub struct CloudyModelOutput {
|
|||||||
///
|
///
|
||||||
/// # 返回
|
/// # 返回
|
||||||
/// 计算结果
|
/// 计算结果
|
||||||
pub fn incldy_pure(
|
pub fn incldy(
|
||||||
input: &CloudyModelInput,
|
input: &CloudyModelInput,
|
||||||
modpar: &mut ModPar,
|
modpar: &mut ModPar,
|
||||||
inppar: &InpPar,
|
inppar: &InpPar,
|
||||||
@ -239,12 +239,12 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_incldy_pure() {
|
fn test_incldy() {
|
||||||
let input = create_test_input();
|
let input = create_test_input();
|
||||||
let mut modpar = create_test_modpar();
|
let mut modpar = create_test_modpar();
|
||||||
let inppar = create_test_inppar();
|
let inppar = create_test_inppar();
|
||||||
|
|
||||||
let result = incldy_pure(&input, &mut modpar, &inppar);
|
let result = incldy(&input, &mut modpar, &inppar);
|
||||||
|
|
||||||
// 验证深度点数
|
// 验证深度点数
|
||||||
assert_eq!(result.ndpth, 5);
|
assert_eq!(result.ndpth, 5);
|
||||||
@ -272,7 +272,7 @@ mod tests {
|
|||||||
let mut modpar = create_test_modpar();
|
let mut modpar = create_test_modpar();
|
||||||
let inppar = create_test_inppar();
|
let inppar = create_test_inppar();
|
||||||
|
|
||||||
let _result = incldy_pure(&input, &mut modpar, &inppar);
|
let _result = incldy(&input, &mut modpar, &inppar);
|
||||||
|
|
||||||
// 验证密度计算: DENS = WMM * pressure
|
// 验证密度计算: DENS = WMM * pressure
|
||||||
for i in 0..input.ndpth {
|
for i in 0..input.ndpth {
|
||||||
@ -291,7 +291,7 @@ mod tests {
|
|||||||
let mut modpar = create_test_modpar();
|
let mut modpar = create_test_modpar();
|
||||||
let inppar = create_test_inppar();
|
let inppar = create_test_inppar();
|
||||||
|
|
||||||
let _result = incldy_pure(&input, &mut modpar, &inppar);
|
let _result = incldy(&input, &mut modpar, &inppar);
|
||||||
|
|
||||||
// 验证电子密度被正确设置
|
// 验证电子密度被正确设置
|
||||||
for i in 0..input.ndpth {
|
for i in 0..input.ndpth {
|
||||||
|
|||||||
@ -423,7 +423,7 @@ pub struct InitiaOutput {
|
|||||||
/// # 返回
|
/// # 返回
|
||||||
///
|
///
|
||||||
/// 初始化输出结构体
|
/// 初始化输出结构体
|
||||||
pub fn initia_pure(
|
pub fn initia(
|
||||||
params: &InitiaParams,
|
params: &InitiaParams,
|
||||||
grid_params: &FrequencyGridParams,
|
grid_params: &FrequencyGridParams,
|
||||||
icompt: i32,
|
icompt: i32,
|
||||||
@ -606,7 +606,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_initia_pure() {
|
fn test_initia() {
|
||||||
let config = InitiaConfig::default();
|
let config = InitiaConfig::default();
|
||||||
let params = InitiaParams {
|
let params = InitiaParams {
|
||||||
config: config.clone(),
|
config: config.clone(),
|
||||||
@ -626,7 +626,7 @@ mod tests {
|
|||||||
ifrset: 0,
|
ifrset: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = initia_pure(¶ms, &grid_params, 0, 0);
|
let output = initia(¶ms, &grid_params, 0, 0);
|
||||||
|
|
||||||
assert_eq!(output.freq_grid.freq.len(), 100);
|
assert_eq!(output.freq_grid.freq.len(), 100);
|
||||||
assert_eq!(output.freq_grid.w.len(), 100);
|
assert_eq!(output.freq_grid.w.len(), 100);
|
||||||
|
|||||||
@ -504,7 +504,7 @@ pub fn inpmod<R: std::io::BufRead>(
|
|||||||
// Cloudy 格式: 调用 INCLDY 读取模型
|
// Cloudy 格式: 调用 INCLDY 读取模型
|
||||||
let incldy_input = super::incldy::read_cloudy_model(reader)?;
|
let incldy_input = super::incldy::read_cloudy_model(reader)?;
|
||||||
let mut modpar = crate::tlusty::state::model::ModPar::default();
|
let mut modpar = crate::tlusty::state::model::ModPar::default();
|
||||||
let incldy_output = super::incldy::incldy_pure(&incldy_input, &mut modpar, inppar);
|
let incldy_output = super::incldy::incldy(&incldy_input, &mut modpar, inppar);
|
||||||
|
|
||||||
// Convert INCLDY output to InpmodOutput
|
// Convert INCLDY output to InpmodOutput
|
||||||
Ok(InpmodOutput {
|
Ok(InpmodOutput {
|
||||||
|
|||||||
@ -22,7 +22,7 @@ use std::io::BufRead;
|
|||||||
// - Physics: caller must invoke for each depth point:
|
// - Physics: caller must invoke for each depth point:
|
||||||
// 1. RHONEN (IFIXDE path) or compute AN from pressure (standard path)
|
// 1. RHONEN (IFIXDE path) or compute AN from pressure (standard path)
|
||||||
// 2. WNSTOR(id, ...)
|
// 2. WNSTOR(id, ...)
|
||||||
// 3. SABOLF(id, ...) via sabolf_pure()
|
// 3. SABOLF(id, ...) via sabolf()
|
||||||
// 4. Set IIFOR0 = [1, 2, ..., NLEV0]
|
// 4. Set IIFOR0 = [1, 2, ..., NLEV0]
|
||||||
// 5. RATMAT(id, iifor0, -1, a, b)
|
// 5. RATMAT(id, iifor0, -1, a, b)
|
||||||
// 6. LEVSOL(a, b, poplte, iifor0, nlev0, 1)
|
// 6. LEVSOL(a, b, poplte, iifor0, nlev0, 1)
|
||||||
|
|||||||
@ -138,7 +138,7 @@ impl Default for LinsetState {
|
|||||||
/// - 2: Simpson 积分
|
/// - 2: Simpson 积分
|
||||||
/// - 3: 修正 Simpson 积分
|
/// - 3: 修正 Simpson 积分
|
||||||
/// - 4: 从文件读取频率点和权重
|
/// - 4: 从文件读取频率点和权重
|
||||||
pub fn linset_pure(
|
pub fn linset(
|
||||||
params: &LinsetParams,
|
params: &LinsetParams,
|
||||||
state: &mut LinsetState,
|
state: &mut LinsetState,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
|||||||
@ -26,7 +26,7 @@ use super::FortranWriter;
|
|||||||
use crate::tlusty::state::constants::{BOLK, MDEPTH, HALF, TWO, UN, SIG4P};
|
use crate::tlusty::state::constants::{BOLK, MDEPTH, HALF, TWO, UN, SIG4P};
|
||||||
use crate::tlusty::math::{
|
use crate::tlusty::math::{
|
||||||
compute_hopf, compute_temperature, rossop, RossopConfig, RossopParams, RossopModelState, RossopOutput,
|
compute_hopf, compute_temperature, rossop, RossopConfig, RossopParams, RossopModelState, RossopOutput,
|
||||||
contmp, conout_pure, temper_pure, hesolv_pure, eldens_pure, steqeq_pure, wnstor, interp, quit,
|
contmp, conout, temper, hesolv, eldens, steqeq_pure, wnstor, interp, quit,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@ -219,7 +219,7 @@ impl LtegrWork {
|
|||||||
/// 计算结果或错误信息
|
/// 计算结果或错误信息
|
||||||
pub fn ltegr<W: std::io::Write>(params: &LtegrParams, writer: Option<&mut FortranWriter<W>>) -> LtegrOutput {
|
pub fn ltegr<W: std::io::Write>(params: &LtegrParams, writer: Option<&mut FortranWriter<W>>) -> LtegrOutput {
|
||||||
// 标记已导入的函数
|
// 标记已导入的函数
|
||||||
let _ = (contmp, conout_pure, rossop, temper_pure, hesolv_pure, eldens_pure, steqeq_pure, wnstor, interp, quit);
|
let _ = (contmp, conout, rossop, temper, hesolv, eldens, steqeq_pure, wnstor, interp, quit);
|
||||||
|
|
||||||
let config = ¶ms.config;
|
let config = ¶ms.config;
|
||||||
let mut work = LtegrWork::new();
|
let mut work = LtegrWork::new();
|
||||||
@ -458,8 +458,8 @@ pub fn ltegr<W: std::io::Write>(params: &LtegrParams, writer: Option<&mut Fortra
|
|||||||
// 输出对流诊断
|
// 输出对流诊断
|
||||||
if config.hmix0 >= 0.0 {
|
if config.hmix0 >= 0.0 {
|
||||||
// 调用 CONOUT(2, IPRING)
|
// 调用 CONOUT(2, IPRING)
|
||||||
// conout_pure(&mut ConoutParams { mode: 2, ipring: config.ipring, ... });
|
// conout(&mut ConoutParams { mode: 2, ipring: config.ipring, ... });
|
||||||
let _ = conout_pure;
|
let _ = conout;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 恢复 LTE 标志
|
// 恢复 LTE 标志
|
||||||
|
|||||||
@ -239,7 +239,7 @@ impl LtegrdWork {
|
|||||||
///
|
///
|
||||||
/// # 返回值
|
/// # 返回值
|
||||||
/// 计算结果
|
/// 计算结果
|
||||||
pub fn ltegrd_pure(params: &mut LtegrdParams) -> LtegrdOutput {
|
pub fn ltegrd(params: &mut LtegrdParams) -> LtegrdOutput {
|
||||||
let config = ¶ms.config;
|
let config = ¶ms.config;
|
||||||
let mut work = LtegrdWork::new();
|
let mut work = LtegrdWork::new();
|
||||||
|
|
||||||
@ -815,7 +815,7 @@ mod tests {
|
|||||||
flopac: &mut flopac,
|
flopac: &mut flopac,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = ltegrd_pure(&mut params);
|
let result = ltegrd(&mut params);
|
||||||
|
|
||||||
assert!(result.nd > 0);
|
assert!(result.nd > 0);
|
||||||
assert!(!result.dm.is_empty());
|
assert!(!result.dm.is_empty());
|
||||||
|
|||||||
@ -51,29 +51,29 @@ pub mod settrm;
|
|||||||
pub mod tabini;
|
pub mod tabini;
|
||||||
pub mod xenini;
|
pub mod xenini;
|
||||||
|
|
||||||
pub use chckse::{chckse_pure, format_chckse_output, ChckseParams, ChckseOutput, LevelBalance};
|
pub use chckse::{chckse, format_chckse_output, ChckseParams, ChckseOutput, LevelBalance};
|
||||||
pub use format::{FormatSpec, FormatItem};
|
pub use format::{FormatSpec, FormatItem};
|
||||||
pub use initia::{
|
pub use initia::{
|
||||||
generate_log_frequency_grid, klein_nishina_cross_section, planck_function,
|
generate_log_frequency_grid, klein_nishina_cross_section, planck_function,
|
||||||
compute_external_irradiation, init_reciprocal_powers, get_statistical_weight,
|
compute_external_irradiation, init_reciprocal_powers, get_statistical_weight,
|
||||||
initia_pure, InitiaConfig, InitiaParams, InitiaOutput,
|
initia, InitiaConfig, InitiaParams, InitiaOutput,
|
||||||
FrequencyGridParams, FrequencyGridOutput,
|
FrequencyGridParams, FrequencyGridOutput,
|
||||||
};
|
};
|
||||||
pub use inpmod::{inpmod, read_tlusty_model, InputModelData, InpmodParams, InpmodOutput};
|
pub use inpmod::{inpmod, read_tlusty_model, InputModelData, InpmodParams, InpmodOutput};
|
||||||
pub use input::{InputParams, InputParser, read_input_file, FrequencyParams, AtomParams, IonParams, TransitionParams, Transition};
|
pub use input::{InputParams, InputParser, read_input_file, FrequencyParams, AtomParams, IonParams, TransitionParams, Transition};
|
||||||
pub use incldy::{incldy_pure, read_cloudy_model, CloudyModelInput, CloudyModelOutput};
|
pub use incldy::{incldy, read_cloudy_model, CloudyModelInput, CloudyModelOutput};
|
||||||
pub use iroset::{iroset, iroset_pure, ColKur as ColKurIroset, IrosetParams, IrosetOutput, Lined};
|
pub use iroset::{iroset, ColKur as ColKurIroset, IrosetParams, IrosetOutput, Lined};
|
||||||
pub use kurucz::{read_kurucz, read_kurucz_from_reader, KuruczModel, KuruczReadParams, KuruczHeader, KuruczDepthPoint, KuruczIfixdeDepthPoint};
|
pub use kurucz::{read_kurucz, read_kurucz_from_reader, KuruczModel, KuruczReadParams, KuruczHeader, KuruczDepthPoint, KuruczIfixdeDepthPoint};
|
||||||
pub use levcd::{levcd, ColKur, LevcdParams};
|
pub use levcd::{levcd, ColKur, LevcdParams};
|
||||||
pub use linset::{linset_pure, LinsetParams, LinsetState, LinsetOutput};
|
pub use linset::{linset, LinsetParams, LinsetState, LinsetOutput};
|
||||||
pub use ltegr::{ltegr, LtegrConfig, LtegrParams, LtegrOutput};
|
pub use ltegr::{ltegr, LtegrConfig, LtegrParams, LtegrOutput};
|
||||||
pub use ltegrd::{ltegrd_pure, LtegrdConfig, LtegrdParams, LtegrdOutput, LtegrdAtomicData};
|
pub use ltegrd::{ltegrd, LtegrdConfig, LtegrdParams, LtegrdOutput, LtegrdAtomicData};
|
||||||
pub use model::{ModelFile, ModelState, read_model, write_model};
|
pub use model::{ModelFile, ModelState, read_model, write_model};
|
||||||
pub use nstout::{nstout, NstoutParams, NstoutOutput};
|
pub use nstout::{nstout, NstoutParams, NstoutOutput};
|
||||||
pub use nstpar::{nstpar, parse_keyword_values, apply_nstpar_postprocessing, NstparParams, NstparOutput, MVAR, VARNAM, PVALUE_DEFAULT};
|
pub use nstpar::{nstpar, parse_keyword_values, apply_nstpar_postprocessing, NstparParams, NstparOutput, MVAR, VARNAM, PVALUE_DEFAULT};
|
||||||
pub use odfset::{odfset, odfset_process_transition, OdfsetParams, OdfsetOutput, StfCr};
|
pub use odfset::{odfset, odfset_process_transition, OdfsetParams, OdfsetOutput, StfCr};
|
||||||
pub use outpri::{
|
pub use outpri::{
|
||||||
outpri_pure, compute_radiation_output, compute_depth_output, compute_disk_depth_output,
|
outpri, compute_radiation_output, compute_depth_output, compute_disk_depth_output,
|
||||||
write_radiation_output, write_atmosphere_output, write_disk_output, write_bfac_output,
|
write_radiation_output, write_atmosphere_output, write_disk_output, write_bfac_output,
|
||||||
OutpriConfig, OutpriParams, OutpriOutput, OutpriFreqData, OutpriModelData,
|
OutpriConfig, OutpriParams, OutpriOutput, OutpriFreqData, OutpriModelData,
|
||||||
OutpriRadData, OutpriPopData, OutpriGrdData,
|
OutpriRadData, OutpriPopData, OutpriGrdData,
|
||||||
@ -82,11 +82,11 @@ pub use outpri::{
|
|||||||
pub use reader::{FortranReader, FromFortran};
|
pub use reader::{FortranReader, FromFortran};
|
||||||
pub use writer::{FortranWriter, format_exp_fortran};
|
pub use writer::{FortranWriter, format_exp_fortran};
|
||||||
pub use tabini::{tabini, tabini_read_text, tabini_read_binary, TabiniInputParams, TabiniOutput, AbnTabData, EletabData};
|
pub use tabini::{tabini, tabini_read_text, tabini_read_binary, TabiniInputParams, TabiniOutput, AbnTabData, EletabData};
|
||||||
pub use rayini::{rayini, rayini_pure, rayini_with_rayleigh, read_rayleigh_table, RayiniParams, RayiniOutput, RayleighTableData};
|
pub use rayini::{rayini, rayini_with_rayleigh, read_rayleigh_table, RayiniParams, RayiniOutput, RayleighTableData};
|
||||||
pub use srtfrq::{srtfrq_pure, SrtfrqParams, SrtfrqOutput, format_srtfrq_message};
|
pub use srtfrq::{srtfrq, SrtfrqParams, SrtfrqOutput, format_srtfrq_message};
|
||||||
pub use xenini::{xenini, xenini_clear};
|
pub use xenini::{xenini, xenini_clear};
|
||||||
pub use resolv::{resolv, resolv_pure, ResolvConfig, ResolvParams, ResolvOutput};
|
pub use resolv::{resolv, ResolvConfig, ResolvParams, ResolvOutput};
|
||||||
pub use start::{start, start_pure, start_with_callbacks, StartConfig, StartParams, StartOutput, StartCallbacks, NoOpStartCallbacks};
|
pub use start::{start, start_with_callbacks, StartConfig, StartParams, StartOutput, StartCallbacks, NoOpStartCallbacks};
|
||||||
|
|
||||||
/// 文件单元号常量(与 Fortran 保持一致)
|
/// 文件单元号常量(与 Fortran 保持一致)
|
||||||
pub mod units {
|
pub mod units {
|
||||||
|
|||||||
@ -19,7 +19,7 @@ use crate::tlusty::state::constants::{MDEPTH, MFREQ, MFREX, MLEVEL, UN, HALF};
|
|||||||
// - ELDENC: conditional diagnostic (ioptab != 0), handled externally
|
// - ELDENC: conditional diagnostic (ioptab != 0), handled externally
|
||||||
// - WNSTOR/SABOLF/RATMAL/LEVSOL: compute "absolute" b-factors for non-LTE output.
|
// - WNSTOR/SABOLF/RATMAL/LEVSOL: compute "absolute" b-factors for non-LTE output.
|
||||||
// Caller must: set LTE=true, then for each depth:
|
// Caller must: set LTE=true, then for each depth:
|
||||||
// wnstor(id,...), sabolf_pure(params), ratmal(id, aes, bes),
|
// wnstor(id,...), sabolf(params), ratmal(id, aes, bes),
|
||||||
// levsol(aes, bes, poplte, iifor, nlevel, 0),
|
// levsol(aes, bes, poplte, iifor, nlevel, 0),
|
||||||
// bfab(i,id) = popul(i,id) / poplte(i)
|
// bfab(i,id) = popul(i,id) / poplte(i)
|
||||||
// Then restore LTE=false
|
// Then restore LTE=false
|
||||||
@ -592,7 +592,7 @@ pub fn compute_disk_depth_output(
|
|||||||
///
|
///
|
||||||
/// # 返回
|
/// # 返回
|
||||||
/// 计算结果
|
/// 计算结果
|
||||||
pub fn outpri_pure(params: &OutpriParams, absoex: &[Vec<f64>]) -> OutpriOutput {
|
pub fn outpri(params: &OutpriParams, absoex: &[Vec<f64>]) -> OutpriOutput {
|
||||||
// WRITE(6,600) ITER-1
|
// WRITE(6,600) ITER-1
|
||||||
// FORMAT(/' ************************************'/' FINAL RESULTS:/' '/
|
// FORMAT(/' ************************************'/' FINAL RESULTS:/' '/
|
||||||
// ' MODEL QUANTITIES IN',I3,'. ITERATION'/' ************************************'/)
|
// ' MODEL QUANTITIES IN',I3,'. ITERATION'/' ************************************'/)
|
||||||
@ -1087,10 +1087,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_outpri_pure() {
|
fn test_outpri() {
|
||||||
let params = create_test_params();
|
let params = create_test_params();
|
||||||
let absoex = vec![vec![1e-8; 3]; 2];
|
let absoex = vec![vec![1e-8; 3]; 2];
|
||||||
let output = outpri_pure(¶ms, &absoex);
|
let output = outpri(¶ms, &absoex);
|
||||||
|
|
||||||
assert!(output.total_flux > 0.0);
|
assert!(output.total_flux > 0.0);
|
||||||
assert_eq!(output.radiation.len(), 5);
|
assert_eq!(output.radiation.len(), 5);
|
||||||
@ -1106,7 +1106,7 @@ mod tests {
|
|||||||
params.config.qgrav = 1e4;
|
params.config.qgrav = 1e4;
|
||||||
|
|
||||||
let absoex = vec![vec![1e-8; 3]; 2];
|
let absoex = vec![vec![1e-8; 3]; 2];
|
||||||
let output = outpri_pure(¶ms, &absoex);
|
let output = outpri(¶ms, &absoex);
|
||||||
|
|
||||||
// 磁盘模式下 depths 应该为空
|
// 磁盘模式下 depths 应该为空
|
||||||
assert!(output.depths.is_empty());
|
assert!(output.depths.is_empty());
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -98,7 +98,7 @@ pub struct SrtfrqParams<'a> {
|
|||||||
/// # 返回值
|
/// # 返回值
|
||||||
///
|
///
|
||||||
/// 输出信息
|
/// 输出信息
|
||||||
pub fn srtfrq_pure(params: &mut SrtfrqParams) -> SrtfrqOutput {
|
pub fn srtfrq(params: &mut SrtfrqParams) -> SrtfrqOutput {
|
||||||
// 标记已导入的函数
|
// 标记已导入的函数
|
||||||
let _ = (indexx, quit);
|
let _ = (indexx, quit);
|
||||||
|
|
||||||
|
|||||||
@ -62,9 +62,9 @@ use super::io::{
|
|||||||
input::InputParser,
|
input::InputParser,
|
||||||
};
|
};
|
||||||
use super::math::solvers::{
|
use super::math::solvers::{
|
||||||
accel2_pure, Accel2Config, Accel2Params,
|
accel2, Accel2Config, Accel2Params,
|
||||||
solve_pure, SolveConfig, DepthMatrices,
|
SolveConfig, DepthMatrices,
|
||||||
solves_pure,
|
solves,
|
||||||
solves_lte, KantorovichStorage, SolvesLteOutput,
|
solves_lte, KantorovichStorage, SolvesLteOutput,
|
||||||
};
|
};
|
||||||
use super::math::io::{timing, TimingParams, TimingMode, reset_timer, output, OutputParams};
|
use super::math::io::{timing, TimingParams, TimingMode, reset_timer, output, OutputParams};
|
||||||
@ -631,7 +631,7 @@ pub fn run_tlusty<R: io::BufRead, W: io::Write>(
|
|||||||
psy2: &mut accel_psy2,
|
psy2: &mut accel_psy2,
|
||||||
psy3: &mut accel_psy3,
|
psy3: &mut accel_psy3,
|
||||||
};
|
};
|
||||||
let accel_result = accel2_pure(&mut accel_params);
|
let accel_result = accel2(&mut accel_params);
|
||||||
|
|
||||||
// If ACCEL2 accelerated, update model state from PSY0
|
// If ACCEL2 accelerated, update model state from PSY0
|
||||||
// and recompute opacities via RESOLV (matching Fortran
|
// and recompute opacities via RESOLV (matching Fortran
|
||||||
|
|||||||
@ -236,7 +236,7 @@ pub struct Alisk1Output {
|
|||||||
///
|
///
|
||||||
/// 此函数是一个框架实现,实际调用 OPACF1、RTEFR1、ALIFRK、ROSSTD
|
/// 此函数是一个框架实现,实际调用 OPACF1、RTEFR1、ALIFRK、ROSSTD
|
||||||
/// 需要在完整系统中实现。当前版本主要用于结构验证。
|
/// 需要在完整系统中实现。当前版本主要用于结构验证。
|
||||||
pub fn alisk1_pure(
|
pub fn alisk1(
|
||||||
config: &Alisk1Config,
|
config: &Alisk1Config,
|
||||||
freq_params: &Alisk1FreqParams,
|
freq_params: &Alisk1FreqParams,
|
||||||
atomic_params: &Alisk1AtomicParams,
|
atomic_params: &Alisk1AtomicParams,
|
||||||
@ -732,7 +732,7 @@ mod tests {
|
|||||||
rad1: &mut rad1,
|
rad1: &mut rad1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = alisk1_pure(&config, &freq_params, &atomic_params, &model_state, &mut output_state);
|
let output = alisk1(&config, &freq_params, &atomic_params, &model_state, &mut output_state);
|
||||||
|
|
||||||
assert!(output.computed);
|
assert!(output.computed);
|
||||||
assert!(output.lross); // 因为 iter=1 且 ndre=0
|
assert!(output.lross); // 因为 iter=1 且 ndre=0
|
||||||
@ -869,7 +869,7 @@ mod tests {
|
|||||||
rad1: &mut rad1,
|
rad1: &mut rad1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = alisk1_pure(&_config, &freq_params, &atomic_params, &model_state, &mut output_state);
|
let output = alisk1(&_config, &freq_params, &atomic_params, &model_state, &mut output_state);
|
||||||
|
|
||||||
assert!(output.computed);
|
assert!(output.computed);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -235,7 +235,7 @@ pub struct Alisk2Output {
|
|||||||
/// # 返回值
|
/// # 返回值
|
||||||
///
|
///
|
||||||
/// 返回 `Alisk2Output`,包含计算结果信息。
|
/// 返回 `Alisk2Output`,包含计算结果信息。
|
||||||
pub fn alisk2_pure(
|
pub fn alisk2(
|
||||||
config: &Alisk2Config,
|
config: &Alisk2Config,
|
||||||
freq_params: &Alisk2FreqParams,
|
freq_params: &Alisk2FreqParams,
|
||||||
atomic_params: &Alisk2AtomicParams,
|
atomic_params: &Alisk2AtomicParams,
|
||||||
@ -822,7 +822,7 @@ mod tests {
|
|||||||
fcool: &mut fcool,
|
fcool: &mut fcool,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = alisk2_pure(&config, &freq_params, &atomic_params, &model_state, &mut output_state);
|
let output = alisk2(&config, &freq_params, &atomic_params, &model_state, &mut output_state);
|
||||||
|
|
||||||
assert!(output.computed);
|
assert!(output.computed);
|
||||||
assert!(output.lross); // 因为 iter=1 且 ndre=0
|
assert!(output.lross); // 因为 iter=1 且 ndre=0
|
||||||
@ -956,7 +956,7 @@ mod tests {
|
|||||||
fcool: &mut fcool,
|
fcool: &mut fcool,
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = alisk2_pure(&config, &freq_params, &atomic_params, &model_state, &mut output_state);
|
let _ = alisk2(&config, &freq_params, &atomic_params, &model_state, &mut output_state);
|
||||||
|
|
||||||
// FLEXP 应该被初始化为零
|
// FLEXP 应该被初始化为零
|
||||||
for id in 0..nd {
|
for id in 0..nd {
|
||||||
|
|||||||
@ -306,7 +306,7 @@ pub struct Alist1Output {
|
|||||||
///
|
///
|
||||||
/// # 返回
|
/// # 返回
|
||||||
/// - `Alist1Output`: 包含 prd0 和 prdx
|
/// - `Alist1Output`: 包含 prd0 和 prdx
|
||||||
pub fn alist1_pure(
|
pub fn alist1(
|
||||||
config: &Alist1Config,
|
config: &Alist1Config,
|
||||||
freq_params: &Alist1FreqParams,
|
freq_params: &Alist1FreqParams,
|
||||||
atomic: &Alist1AtomicParams,
|
atomic: &Alist1AtomicParams,
|
||||||
|
|||||||
@ -1,136 +0,0 @@
|
|||||||
//! CIA H2-H 不透明度。
|
|
||||||
//!
|
|
||||||
//! 重构自 TLUSTY `cia_h2h.f`
|
|
||||||
//!
|
|
||||||
//! # 功能
|
|
||||||
//!
|
|
||||||
//! 计算 H2-H 碰撞诱导吸收 (CIA) 不透明度。
|
|
||||||
//! 数据来源:TURBOSPEC
|
|
||||||
|
|
||||||
/// Amagat 单位转换常数 (cm^-3)
|
|
||||||
const AMAGAT: f64 = 2.6867774e19;
|
|
||||||
/// 不透明度转换因子
|
|
||||||
const FAC: f64 = 1.0 / (AMAGAT * AMAGAT);
|
|
||||||
/// 光速 (cm/s)
|
|
||||||
const CAS: f64 = 2.997925e10;
|
|
||||||
/// 温度表点数
|
|
||||||
const NTEMP: usize = 4;
|
|
||||||
/// 频率/波长表行数
|
|
||||||
const NLINES: usize = 67;
|
|
||||||
|
|
||||||
/// CIA H2-H 温度表
|
|
||||||
static TEMP_TABLE: [f64; NTEMP] = [1000.0, 1500.0, 2000.0, 2500.0];
|
|
||||||
|
|
||||||
/// CIA H2-H 不透明度表数据。
|
|
||||||
pub struct CiaH2HData {
|
|
||||||
/// 频率数组
|
|
||||||
freq: Vec<f64>,
|
|
||||||
/// 对数不透明度 alpha(freq, temp)
|
|
||||||
alpha: Vec<Vec<f64>>,
|
|
||||||
/// 是否已初始化
|
|
||||||
loaded: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CiaH2HData {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
freq: Vec::new(),
|
|
||||||
alpha: Vec::new(),
|
|
||||||
loaded: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CiaH2HData {
|
|
||||||
/// 加载 CIA 数据表。
|
|
||||||
pub fn load(&mut self) {
|
|
||||||
if self.loaded {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
println!("Reading in H2-H CIA opacity tables...");
|
|
||||||
|
|
||||||
// 在完整实现中,这里会从 "./data/CIA_H2H.dat" 读取数据
|
|
||||||
self.freq = vec![0.0; NLINES];
|
|
||||||
self.alpha = vec![vec![0.0; NTEMP]; NLINES];
|
|
||||||
self.loaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 计算 CIA H2-H 不透明度。
|
|
||||||
///
|
|
||||||
/// # 参数
|
|
||||||
///
|
|
||||||
/// * `t` - 温度 (K)
|
|
||||||
/// * `ah2` - H2 数密度
|
|
||||||
/// * `ah` - H 数密度
|
|
||||||
/// * `ff` - 频率 (Hz)
|
|
||||||
pub fn opacity(&mut self, t: f64, ah2: f64, ah: f64, ff: f64) -> f64 {
|
|
||||||
// H2-H 数据仅适用于 T <= 2500 K
|
|
||||||
if t > 2500.0 {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.load();
|
|
||||||
|
|
||||||
let f = ff / CAS;
|
|
||||||
|
|
||||||
let j = locate(&TEMP_TABLE, t);
|
|
||||||
|
|
||||||
if j == 0 {
|
|
||||||
println!();
|
|
||||||
println!(
|
|
||||||
"Warning: requested temperature is below{:6.0} K",
|
|
||||||
TEMP_TABLE[0]
|
|
||||||
);
|
|
||||||
println!("CIA H2-H CIA opacity set to zero");
|
|
||||||
println!();
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let i = locate(&self.freq, f);
|
|
||||||
|
|
||||||
let alp = if j == NTEMP {
|
|
||||||
let y1 = self.alpha[i][j - 1];
|
|
||||||
let y2 = self.alpha[i + 1][j - 1];
|
|
||||||
let tt = (f - self.freq[i]) / (self.freq[i + 1] - self.freq[i]);
|
|
||||||
(1.0 - tt) * y1 + tt * y2
|
|
||||||
} else if i == 0 || i == NLINES {
|
|
||||||
-50.0
|
|
||||||
} else {
|
|
||||||
let y1 = self.alpha[i][j - 1];
|
|
||||||
let y2 = self.alpha[i + 1][j - 1];
|
|
||||||
let y3 = self.alpha[i + 1][j];
|
|
||||||
let y4 = self.alpha[i][j];
|
|
||||||
|
|
||||||
let tt = (f - self.freq[i]) / (self.freq[i + 1] - self.freq[i]);
|
|
||||||
let uu = (t - TEMP_TABLE[j - 1]) / (TEMP_TABLE[j] - TEMP_TABLE[j - 1]);
|
|
||||||
|
|
||||||
(1.0 - tt) * (1.0 - uu) * y1
|
|
||||||
+ tt * (1.0 - uu) * y2
|
|
||||||
+ tt * uu * y3
|
|
||||||
+ (1.0 - tt) * uu * y4
|
|
||||||
};
|
|
||||||
|
|
||||||
let alp = alp.exp();
|
|
||||||
|
|
||||||
FAC * ah2 * ah * alp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 便捷函数:计算 CIA H2-H 不透明度(匹配 Fortran SUBROUTINE CIA_H2H 签名)。
|
|
||||||
pub fn cia_h2h(t: f64, ah2: f64, ah: f64, ff: f64, opac: &mut f64) {
|
|
||||||
let mut data = CiaH2HData::default();
|
|
||||||
*opac = data.opacity(t, ah2, ah, ff);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 在有序数组中定位 x 的位置。
|
|
||||||
fn locate(arr: &[f64], x: f64) -> usize {
|
|
||||||
if x < arr[0] {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
for i in 1..arr.len() {
|
|
||||||
if x < arr[i] {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
arr.len()
|
|
||||||
}
|
|
||||||
@ -1,147 +0,0 @@
|
|||||||
//! CIA H2-H2 不透明度。
|
|
||||||
//!
|
|
||||||
//! 重构自 TLUSTY `cia_h2h2.f`
|
|
||||||
//!
|
|
||||||
//! # 功能
|
|
||||||
//!
|
|
||||||
//! 计算 H2-H2 碰撞诱导吸收 (CIA) 不透明度。
|
|
||||||
//! 数据来源:Borysow A., Jorgensen U.G., Fu Y. 2001, JQSRT 68, 235
|
|
||||||
|
|
||||||
/// Amagat 单位转换常数 (cm^-3)
|
|
||||||
const AMAGAT: f64 = 2.6867774e19;
|
|
||||||
/// 不透明度转换因子
|
|
||||||
const FAC: f64 = 1.0 / (AMAGAT * AMAGAT);
|
|
||||||
/// 光速 (cm/s)
|
|
||||||
const CAS: f64 = 2.997925e10;
|
|
||||||
/// 温度表点数
|
|
||||||
const NTEMP: usize = 7;
|
|
||||||
/// 频率/波长表行数
|
|
||||||
const NLINES: usize = 1000;
|
|
||||||
|
|
||||||
/// CIA H2-H2 温度表
|
|
||||||
static TEMP_TABLE: [f64; NTEMP] = [1000.0, 2000.0, 3000.0, 4000.0, 5000.0, 6000.0, 7000.0];
|
|
||||||
|
|
||||||
/// CIA H2-H2 不透明度表数据。
|
|
||||||
///
|
|
||||||
/// 在首次调用时从文件加载,之后缓存。
|
|
||||||
pub struct CiaH2H2Data {
|
|
||||||
/// 频率数组
|
|
||||||
freq: Vec<f64>,
|
|
||||||
/// 对数不透明度 alpha(freq, temp)
|
|
||||||
alpha: Vec<Vec<f64>>,
|
|
||||||
/// 是否已初始化
|
|
||||||
loaded: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CiaH2H2Data {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
freq: Vec::new(),
|
|
||||||
alpha: Vec::new(),
|
|
||||||
loaded: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CiaH2H2Data {
|
|
||||||
/// 加载 CIA 数据表。
|
|
||||||
pub fn load(&mut self) {
|
|
||||||
if self.loaded {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
println!("Reading in H2-H2 CIA opacity tables...");
|
|
||||||
|
|
||||||
// 在完整实现中,这里会从 "./data/CIA_H2H2.dat" 读取数据
|
|
||||||
// 暂时初始化为空表
|
|
||||||
self.freq = vec![0.0; NLINES];
|
|
||||||
self.alpha = vec![vec![0.0; NTEMP]; NLINES];
|
|
||||||
self.loaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 计算 CIA H2-H2 不透明度。
|
|
||||||
///
|
|
||||||
/// # 参数
|
|
||||||
///
|
|
||||||
/// * `t` - 温度 (K)
|
|
||||||
/// * `ah2` - H2 数密度
|
|
||||||
/// * `ff` - 频率 (Hz)
|
|
||||||
///
|
|
||||||
/// # 返回值
|
|
||||||
///
|
|
||||||
/// CIA 不透明度
|
|
||||||
pub fn opacity(&mut self, t: f64, ah2: f64, ff: f64) -> f64 {
|
|
||||||
self.load();
|
|
||||||
|
|
||||||
// 输入频率为 Hz,但需要波数 (cm^-1)
|
|
||||||
let f = ff / CAS;
|
|
||||||
|
|
||||||
// 在温度数组中定位
|
|
||||||
let j = locate(&TEMP_TABLE, t);
|
|
||||||
|
|
||||||
if j == 0 {
|
|
||||||
println!();
|
|
||||||
println!(
|
|
||||||
"Warning: requested temperature is below{:6.0} K",
|
|
||||||
TEMP_TABLE[0]
|
|
||||||
);
|
|
||||||
println!("CIA H2-H2 opacity set to 0");
|
|
||||||
println!();
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在频率数组中定位
|
|
||||||
let i = locate(&self.freq, f);
|
|
||||||
|
|
||||||
let alp = if j == NTEMP {
|
|
||||||
// 高温端外推:保持恒定
|
|
||||||
let y1 = self.alpha[i][j - 1];
|
|
||||||
let y2 = self.alpha[i + 1][j - 1];
|
|
||||||
let tt = (f - self.freq[i]) / (self.freq[i + 1] - self.freq[i]);
|
|
||||||
(1.0 - tt) * y1 + tt * y2
|
|
||||||
} else if i == 0 || i == NLINES {
|
|
||||||
// 频率表外:设置非常小的值
|
|
||||||
-50.0
|
|
||||||
} else {
|
|
||||||
// 在表内双线性插值
|
|
||||||
let y1 = self.alpha[i][j - 1];
|
|
||||||
let y2 = self.alpha[i + 1][j - 1];
|
|
||||||
let y3 = self.alpha[i + 1][j];
|
|
||||||
let y4 = self.alpha[i][j];
|
|
||||||
|
|
||||||
let tt = (f - self.freq[i]) / (self.freq[i + 1] - self.freq[i]);
|
|
||||||
let uu = (t - TEMP_TABLE[j - 1]) / (TEMP_TABLE[j] - TEMP_TABLE[j - 1]);
|
|
||||||
|
|
||||||
(1.0 - tt) * (1.0 - uu) * y1
|
|
||||||
+ tt * (1.0 - uu) * y2
|
|
||||||
+ tt * uu * y3
|
|
||||||
+ (1.0 - tt) * uu * y4
|
|
||||||
};
|
|
||||||
|
|
||||||
let alp = alp.exp();
|
|
||||||
|
|
||||||
// 最终不透明度
|
|
||||||
FAC * ah2 * ah2 * alp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 便捷函数:计算 CIA H2-H2 不透明度(匹配 Fortran SUBROUTINE CIA_H2H2 签名)。
|
|
||||||
pub fn cia_h2h2(t: f64, ah2: f64, ff: f64, opac: &mut f64) {
|
|
||||||
let mut data = CiaH2H2Data::default();
|
|
||||||
*opac = data.opacity(t, ah2, ff);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 在有序数组中定位 x 的位置。
|
|
||||||
///
|
|
||||||
/// 返回索引 j,使得 arr[j-1] <= x < arr[j]。
|
|
||||||
/// 如果 x < arr[0],返回 0。
|
|
||||||
fn locate(arr: &[f64], x: f64) -> usize {
|
|
||||||
if x < arr[0] {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
for i in 1..arr.len() {
|
|
||||||
if x < arr[i] {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
arr.len()
|
|
||||||
}
|
|
||||||
@ -1,132 +0,0 @@
|
|||||||
//! CIA H2-He 不透明度。
|
|
||||||
//!
|
|
||||||
//! 重构自 TLUSTY `cia_h2he.f`
|
|
||||||
//!
|
|
||||||
//! # 功能
|
|
||||||
//!
|
|
||||||
//! 计算 H2-He 碰撞诱导吸收 (CIA) 不透明度。
|
|
||||||
//! 数据来源:Jorgensen U.G., Hammer D., Borysow A., Falkesgaard J., 2000,
|
|
||||||
//! Astronomy & Astrophysics 361, 283
|
|
||||||
|
|
||||||
/// Amagat 单位转换常数 (cm^-3)
|
|
||||||
const AMAGAT: f64 = 2.6867774e19;
|
|
||||||
/// 不透明度转换因子
|
|
||||||
const FAC: f64 = 1.0 / (AMAGAT * AMAGAT);
|
|
||||||
/// 光速 (cm/s)
|
|
||||||
const CAS: f64 = 2.997925e10;
|
|
||||||
/// 温度表点数
|
|
||||||
const NTEMP: usize = 7;
|
|
||||||
/// 频率/波长表行数
|
|
||||||
const NLINES: usize = 242;
|
|
||||||
|
|
||||||
/// CIA H2-He 温度表
|
|
||||||
static TEMP_TABLE: [f64; NTEMP] = [1000.0, 2000.0, 3000.0, 4000.0, 5000.0, 6000.0, 7000.0];
|
|
||||||
|
|
||||||
/// CIA H2-He 不透明度表数据。
|
|
||||||
pub struct CiaH2HeData {
|
|
||||||
/// 频率数组
|
|
||||||
freq: Vec<f64>,
|
|
||||||
/// 对数不透明度 alpha(freq, temp)
|
|
||||||
alpha: Vec<Vec<f64>>,
|
|
||||||
/// 是否已初始化
|
|
||||||
loaded: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CiaH2HeData {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
freq: Vec::new(),
|
|
||||||
alpha: Vec::new(),
|
|
||||||
loaded: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CiaH2HeData {
|
|
||||||
/// 加载 CIA 数据表。
|
|
||||||
pub fn load(&mut self) {
|
|
||||||
if self.loaded {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
println!("Reading in H2-He CIA opacity tables...");
|
|
||||||
|
|
||||||
// 在完整实现中,这里会从 "./data/CIA_H2He.dat" 读取数据
|
|
||||||
self.freq = vec![0.0; NLINES];
|
|
||||||
self.alpha = vec![vec![0.0; NTEMP]; NLINES];
|
|
||||||
self.loaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 计算 CIA H2-He 不透明度。
|
|
||||||
///
|
|
||||||
/// # 参数
|
|
||||||
///
|
|
||||||
/// * `t` - 温度 (K)
|
|
||||||
/// * `ah2` - H2 数密度
|
|
||||||
/// * `ahe` - He 数密度
|
|
||||||
/// * `ff` - 频率 (Hz)
|
|
||||||
pub fn opacity(&mut self, t: f64, ah2: f64, ahe: f64, ff: f64) -> f64 {
|
|
||||||
self.load();
|
|
||||||
|
|
||||||
let f = ff / CAS;
|
|
||||||
|
|
||||||
let j = locate(&TEMP_TABLE, t);
|
|
||||||
|
|
||||||
if j == 0 {
|
|
||||||
println!();
|
|
||||||
println!(
|
|
||||||
"Warning: requested temperature is below{:6.0} K",
|
|
||||||
TEMP_TABLE[0]
|
|
||||||
);
|
|
||||||
println!("CIA H2-He opacity set to 0");
|
|
||||||
println!();
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let i = locate(&self.freq, f);
|
|
||||||
|
|
||||||
let alp = if j == NTEMP {
|
|
||||||
let y1 = self.alpha[i][j - 1];
|
|
||||||
let y2 = self.alpha[i + 1][j - 1];
|
|
||||||
let tt = (f - self.freq[i]) / (self.freq[i + 1] - self.freq[i]);
|
|
||||||
(1.0 - tt) * y1 + tt * y2
|
|
||||||
} else if i == 0 || i == NLINES {
|
|
||||||
-50.0
|
|
||||||
} else {
|
|
||||||
let y1 = self.alpha[i][j - 1];
|
|
||||||
let y2 = self.alpha[i + 1][j - 1];
|
|
||||||
let y3 = self.alpha[i + 1][j];
|
|
||||||
let y4 = self.alpha[i][j];
|
|
||||||
|
|
||||||
let tt = (f - self.freq[i]) / (self.freq[i + 1] - self.freq[i]);
|
|
||||||
let uu = (t - TEMP_TABLE[j - 1]) / (TEMP_TABLE[j] - TEMP_TABLE[j - 1]);
|
|
||||||
|
|
||||||
(1.0 - tt) * (1.0 - uu) * y1
|
|
||||||
+ tt * (1.0 - uu) * y2
|
|
||||||
+ tt * uu * y3
|
|
||||||
+ (1.0 - tt) * uu * y4
|
|
||||||
};
|
|
||||||
|
|
||||||
let alp = alp.exp();
|
|
||||||
|
|
||||||
FAC * ah2 * ahe * alp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 便捷函数:计算 CIA H2-He 不透明度(匹配 Fortran SUBROUTINE CIA_H2HE 签名)。
|
|
||||||
pub fn cia_h2he(t: f64, ah2: f64, ahe: f64, ff: f64, opac: &mut f64) {
|
|
||||||
let mut data = CiaH2HeData::default();
|
|
||||||
*opac = data.opacity(t, ah2, ahe, ff);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 在有序数组中定位 x 的位置。
|
|
||||||
fn locate(arr: &[f64], x: f64) -> usize {
|
|
||||||
if x < arr[0] {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
for i in 1..arr.len() {
|
|
||||||
if x < arr[i] {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
arr.len()
|
|
||||||
}
|
|
||||||
@ -1,133 +0,0 @@
|
|||||||
//! CIA H-He 不透明度。
|
|
||||||
//!
|
|
||||||
//! 重构自 TLUSTY `cia_hhe.f`
|
|
||||||
//!
|
|
||||||
//! # 功能
|
|
||||||
//!
|
|
||||||
//! 计算 H-He 碰撞诱导吸收 (CIA) 不透明度。
|
|
||||||
//! 数据来源:Gustafsson M., Frommhold, L. 2001, ApJ 546, 1168
|
|
||||||
|
|
||||||
/// Amagat 单位转换常数 (cm^-3)
|
|
||||||
const AMAGAT: f64 = 2.6867774e19;
|
|
||||||
/// 不透明度转换因子
|
|
||||||
const FAC: f64 = 1.0 / (AMAGAT * AMAGAT);
|
|
||||||
/// 光速 (cm/s)
|
|
||||||
const CAS: f64 = 2.997925e10;
|
|
||||||
/// 温度表点数
|
|
||||||
const NTEMP: usize = 11;
|
|
||||||
/// 频率/波长表行数
|
|
||||||
const NLINES: usize = 43;
|
|
||||||
|
|
||||||
/// CIA H-He 温度表
|
|
||||||
static TEMP_TABLE: [f64; NTEMP] = [
|
|
||||||
1000.0, 1500.0, 2250.0, 3000.0, 4000.0, 5000.0, 6000.0, 7000.0, 8000.0, 9000.0, 10000.0,
|
|
||||||
];
|
|
||||||
|
|
||||||
/// CIA H-He 不透明度表数据。
|
|
||||||
pub struct CiaHHeData {
|
|
||||||
/// 频率数组
|
|
||||||
freq: Vec<f64>,
|
|
||||||
/// 对数不透明度 alpha(freq, temp)
|
|
||||||
alpha: Vec<Vec<f64>>,
|
|
||||||
/// 是否已初始化
|
|
||||||
loaded: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CiaHHeData {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
freq: Vec::new(),
|
|
||||||
alpha: Vec::new(),
|
|
||||||
loaded: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CiaHHeData {
|
|
||||||
/// 加载 CIA 数据表。
|
|
||||||
pub fn load(&mut self) {
|
|
||||||
if self.loaded {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
println!("Reading in H-He CIA opacity tables...");
|
|
||||||
|
|
||||||
// 在完整实现中,这里会从 "./data/CIA_HHe.dat" 读取数据
|
|
||||||
self.freq = vec![0.0; NLINES];
|
|
||||||
self.alpha = vec![vec![0.0; NTEMP]; NLINES];
|
|
||||||
self.loaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 计算 CIA H-He 不透明度。
|
|
||||||
///
|
|
||||||
/// # 参数
|
|
||||||
///
|
|
||||||
/// * `t` - 温度 (K)
|
|
||||||
/// * `ah` - H 数密度
|
|
||||||
/// * `ahe` - He 数密度
|
|
||||||
/// * `ff` - 频率 (Hz)
|
|
||||||
pub fn opacity(&mut self, t: f64, ah: f64, ahe: f64, ff: f64) -> f64 {
|
|
||||||
self.load();
|
|
||||||
|
|
||||||
let f = ff / CAS;
|
|
||||||
|
|
||||||
let j = locate(&TEMP_TABLE, t);
|
|
||||||
|
|
||||||
if j == 0 {
|
|
||||||
println!();
|
|
||||||
println!(
|
|
||||||
"Warning: requested temperature is below{:6.0} K",
|
|
||||||
TEMP_TABLE[0]
|
|
||||||
);
|
|
||||||
println!("CIA H-He opacity set to 0");
|
|
||||||
println!();
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let i = locate(&self.freq, f);
|
|
||||||
|
|
||||||
let alp = if j == NTEMP {
|
|
||||||
let y1 = self.alpha[i][j - 1];
|
|
||||||
let y2 = self.alpha[i + 1][j - 1];
|
|
||||||
let tt = (f - self.freq[i]) / (self.freq[i + 1] - self.freq[i]);
|
|
||||||
(1.0 - tt) * y1 + tt * y2
|
|
||||||
} else if i == 0 || i == NLINES {
|
|
||||||
-50.0
|
|
||||||
} else {
|
|
||||||
let y1 = self.alpha[i][j - 1];
|
|
||||||
let y2 = self.alpha[i + 1][j - 1];
|
|
||||||
let y3 = self.alpha[i + 1][j];
|
|
||||||
let y4 = self.alpha[i][j];
|
|
||||||
|
|
||||||
let tt = (f - self.freq[i]) / (self.freq[i + 1] - self.freq[i]);
|
|
||||||
let uu = (t - TEMP_TABLE[j - 1]) / (TEMP_TABLE[j] - TEMP_TABLE[j - 1]);
|
|
||||||
|
|
||||||
(1.0 - tt) * (1.0 - uu) * y1
|
|
||||||
+ tt * (1.0 - uu) * y2
|
|
||||||
+ tt * uu * y3
|
|
||||||
+ (1.0 - tt) * uu * y4
|
|
||||||
};
|
|
||||||
|
|
||||||
let alp = alp.exp();
|
|
||||||
|
|
||||||
FAC * ah * ahe * alp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 便捷函数:计算 CIA H-He 不透明度(匹配 Fortran SUBROUTINE CIA_HHE 签名)。
|
|
||||||
pub fn cia_hhe(t: f64, ah: f64, ahe: f64, ff: f64, opac: &mut f64) {
|
|
||||||
let mut data = CiaHHeData::default();
|
|
||||||
*opac = data.opacity(t, ah, ahe, ff);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 在有序数组中定位 x 的位置。
|
|
||||||
fn locate(arr: &[f64], x: f64) -> usize {
|
|
||||||
if x < arr[0] {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
for i in 1..arr.len() {
|
|
||||||
if x < arr[i] {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
arr.len()
|
|
||||||
}
|
|
||||||
@ -1,9 +1,5 @@
|
|||||||
//! continuum module
|
//! continuum module
|
||||||
|
|
||||||
mod cia_h2h;
|
|
||||||
mod cia_h2h2;
|
|
||||||
mod cia_h2he;
|
|
||||||
mod cia_hhe;
|
|
||||||
mod lte_opacity;
|
mod lte_opacity;
|
||||||
mod opacf0;
|
mod opacf0;
|
||||||
pub mod opacf0_state;
|
pub mod opacf0_state;
|
||||||
@ -23,12 +19,16 @@ mod opdata;
|
|||||||
mod opfrac;
|
mod opfrac;
|
||||||
mod opacity_table;
|
mod opacity_table;
|
||||||
|
|
||||||
pub use cia_h2h::CiaH2HData;
|
// CIA 不透明度:使用 opacity 模块中的权威实现,并提供向后兼容别名
|
||||||
pub use cia_h2h2::CiaH2H2Data;
|
pub use crate::tlusty::math::opacity::{
|
||||||
pub use cia_h2he::CiaH2HeData;
|
cia_h2h, cia_h2h2, cia_h2he, cia_hhe,
|
||||||
pub use cia_hhe::CiaHHeData;
|
CiaH2hData, CiaH2h2Data, CiaH2heData, CiaHheData,
|
||||||
// Re-export free functions from opacity module (the authoritative implementations)
|
};
|
||||||
pub use crate::tlusty::math::opacity::{cia_h2h, cia_h2h2, cia_h2he, cia_hhe};
|
// 大写后缀的旧版别名(向后兼容)
|
||||||
|
pub use crate::tlusty::math::opacity::CiaH2hData as CiaH2HData;
|
||||||
|
pub use crate::tlusty::math::opacity::CiaH2h2Data as CiaH2H2Data;
|
||||||
|
pub use crate::tlusty::math::opacity::CiaH2heData as CiaH2HeData;
|
||||||
|
pub use crate::tlusty::math::opacity::CiaHheData as CiaHHeData;
|
||||||
pub use lte_opacity::{
|
pub use lte_opacity::{
|
||||||
LteOpacityParams, LteOpacityOutput, LteFrequencyGrid,
|
LteOpacityParams, LteOpacityOutput, LteFrequencyGrid,
|
||||||
lte_meanopt, generate_lte_frequency_grid, generate_inifrc_frequency_grid,
|
lte_meanopt, generate_lte_frequency_grid, generate_inifrc_frequency_grid,
|
||||||
|
|||||||
@ -264,7 +264,7 @@ pub type PgsetFn = fn(usize);
|
|||||||
/// ...
|
/// ...
|
||||||
/// END
|
/// END
|
||||||
/// ```
|
/// ```
|
||||||
pub fn opactr_pure(
|
pub fn opactr(
|
||||||
ij: usize,
|
ij: usize,
|
||||||
config: &OpactrConfig,
|
config: &OpactrConfig,
|
||||||
model_state: &mut OpactrModelState,
|
model_state: &mut OpactrModelState,
|
||||||
|
|||||||
@ -142,7 +142,7 @@ pub struct OpfracOutput {
|
|||||||
///
|
///
|
||||||
/// # 返回
|
/// # 返回
|
||||||
/// 配分函数和电离分数
|
/// 配分函数和电离分数
|
||||||
pub fn opfrac_pure(params: &OpfracParams, pfoptb: &PfOptB) -> OpfracOutput {
|
pub fn opfrac(params: &OpfracParams, pfoptb: &PfOptB) -> OpfracOutput {
|
||||||
let iat = params.iat;
|
let iat = params.iat;
|
||||||
|
|
||||||
// 如果 IAT == 0,返回默认值
|
// 如果 IAT == 0,返回默认值
|
||||||
@ -345,7 +345,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_opfrac_pure_zero_iat() {
|
fn test_opfrac_zero_iat() {
|
||||||
let pfoptb = PfOptB::new();
|
let pfoptb = PfOptB::new();
|
||||||
|
|
||||||
let params = OpfracParams {
|
let params = OpfracParams {
|
||||||
@ -355,13 +355,13 @@ mod tests {
|
|||||||
ane: 1.0e12,
|
ane: 1.0e12,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = opfrac_pure(¶ms, &pfoptb);
|
let result = opfrac(¶ms, &pfoptb);
|
||||||
assert!((result.pf - 1.0).abs() < 1e-10);
|
assert!((result.pf - 1.0).abs() < 1e-10);
|
||||||
assert!((result.fra - 1.0).abs() < 1e-10);
|
assert!((result.fra - 1.0).abs() < 1e-10);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_opfrac_pure_with_data() {
|
fn test_opfrac_with_data() {
|
||||||
let mut pfoptb = PfOptB::new();
|
let mut pfoptb = PfOptB::new();
|
||||||
|
|
||||||
// 设置一些测试数据
|
// 设置一些测试数据
|
||||||
@ -386,7 +386,7 @@ mod tests {
|
|||||||
ane: 1.0e12,
|
ane: 1.0e12,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = opfrac_pure(¶ms, &pfoptb);
|
let result = opfrac(¶ms, &pfoptb);
|
||||||
|
|
||||||
assert!(result.pf > 0.0, "PF should be positive");
|
assert!(result.pf > 0.0, "PF should be positive");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -130,7 +130,7 @@ pub struct ConcorOutput {
|
|||||||
/// ...
|
/// ...
|
||||||
/// END
|
/// END
|
||||||
/// ```
|
/// ```
|
||||||
pub fn concor_pure(params: &mut ConcorParams) -> ConcorOutput {
|
pub fn concor(params: &mut ConcorParams) -> ConcorOutput {
|
||||||
// 检查是否需要执行
|
// 检查是否需要执行
|
||||||
if params.config.indl == 0 {
|
if params.config.indl == 0 {
|
||||||
return ConcorOutput {
|
return ConcorOutput {
|
||||||
@ -313,7 +313,7 @@ mod tests {
|
|||||||
prd0: 0.0,
|
prd0: 0.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = concor_pure(&mut params);
|
let output = concor(&mut params);
|
||||||
|
|
||||||
assert!(!output.computed);
|
assert!(!output.computed);
|
||||||
assert!(!output.temp_modified);
|
assert!(!output.temp_modified);
|
||||||
@ -348,7 +348,7 @@ mod tests {
|
|||||||
prd0: 0.0,
|
prd0: 0.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = concor_pure(&mut params);
|
let output = concor(&mut params);
|
||||||
|
|
||||||
assert!(output.computed);
|
assert!(output.computed);
|
||||||
assert!(output.temp_modified);
|
assert!(output.temp_modified);
|
||||||
@ -385,7 +385,7 @@ mod tests {
|
|||||||
prd0: 0.0,
|
prd0: 0.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = concor_pure(&mut params);
|
let output = concor(&mut params);
|
||||||
|
|
||||||
assert!(output.computed);
|
assert!(output.computed);
|
||||||
// 在盘模式下,ptotal 会被重新计算
|
// 在盘模式下,ptotal 会被重新计算
|
||||||
|
|||||||
@ -191,7 +191,7 @@ pub struct CubconData {
|
|||||||
/// ...
|
/// ...
|
||||||
/// END
|
/// END
|
||||||
/// ```
|
/// ```
|
||||||
pub fn conout_pure(params: &mut ConoutParams) -> ConoutOutput {
|
pub fn conout(params: &mut ConoutParams) -> ConoutOutput {
|
||||||
// f2r_depends: convec, meanop, meanopt, opacf0 (generic)
|
// f2r_depends: convec, meanop, meanopt, opacf0 (generic)
|
||||||
let _ = (convec, meanop, meanopt);
|
let _ = (convec, meanop, meanopt);
|
||||||
|
|
||||||
@ -639,7 +639,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_conout_basic() {
|
fn test_conout_basic() {
|
||||||
let mut params = TestParamsBuilder::new(50).build();
|
let mut params = TestParamsBuilder::new(50).build();
|
||||||
let output = conout_pure(&mut params);
|
let output = conout(&mut params);
|
||||||
|
|
||||||
// 验证基本输出
|
// 验证基本输出
|
||||||
assert_eq!(output.depth_results.len(), 50);
|
assert_eq!(output.depth_results.len(), 50);
|
||||||
@ -671,7 +671,7 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut params = TestParamsBuilder::new(50).config(config).build();
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
||||||
let output = conout_pure(&mut params);
|
let output = conout(&mut params);
|
||||||
|
|
||||||
// 禁用对流时不应该有对流区
|
// 禁用对流时不应该有对流区
|
||||||
assert_eq!(output.icbeg, 0);
|
assert_eq!(output.icbeg, 0);
|
||||||
@ -686,7 +686,7 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut params = TestParamsBuilder::new(50).config(config).build();
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
||||||
let output = conout_pure(&mut params);
|
let output = conout(&mut params);
|
||||||
|
|
||||||
// 验证基本功能
|
// 验证基本功能
|
||||||
assert_eq!(output.depth_results.len(), 50);
|
assert_eq!(output.depth_results.len(), 50);
|
||||||
@ -700,7 +700,7 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut params = TestParamsBuilder::new(50).config(config).build();
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
||||||
let output = conout_pure(&mut params);
|
let output = conout(&mut params);
|
||||||
|
|
||||||
// 验证基本功能
|
// 验证基本功能
|
||||||
assert_eq!(output.depth_results.len(), 50);
|
assert_eq!(output.depth_results.len(), 50);
|
||||||
@ -714,7 +714,7 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut params = TestParamsBuilder::new(50).config(config).build();
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
||||||
let output = conout_pure(&mut params);
|
let output = conout(&mut params);
|
||||||
|
|
||||||
// 盘模式应该正常工作
|
// 盘模式应该正常工作
|
||||||
assert_eq!(output.depth_results.len(), 50);
|
assert_eq!(output.depth_results.len(), 50);
|
||||||
|
|||||||
@ -340,7 +340,7 @@ fn call_convc1(
|
|||||||
/// ...
|
/// ...
|
||||||
/// END
|
/// END
|
||||||
/// ```
|
/// ```
|
||||||
pub fn conref_pure(params: &mut ConrefParams) -> ConrefOutput {
|
pub fn conref(params: &mut ConrefParams) -> ConrefOutput {
|
||||||
let nd = params.nd;
|
let nd = params.nd;
|
||||||
let config = ¶ms.config.clone();
|
let config = ¶ms.config.clone();
|
||||||
|
|
||||||
@ -978,7 +978,7 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut params = TestParamsBuilder::new(50).config(config).build();
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
||||||
let output = conref_pure(&mut params);
|
let output = conref(&mut params);
|
||||||
|
|
||||||
assert_eq!(output.icbeg, 0);
|
assert_eq!(output.icbeg, 0);
|
||||||
assert_eq!(output.icend, 0);
|
assert_eq!(output.icend, 0);
|
||||||
@ -993,7 +993,7 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut params = TestParamsBuilder::new(50).config(config).build();
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
||||||
let output = conref_pure(&mut params);
|
let output = conref(&mut params);
|
||||||
|
|
||||||
assert_eq!(output.icbeg, 0);
|
assert_eq!(output.icbeg, 0);
|
||||||
}
|
}
|
||||||
@ -1006,7 +1006,7 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut params = TestParamsBuilder::new(50).config(config).build();
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
||||||
let output = conref_pure(&mut params);
|
let output = conref(&mut params);
|
||||||
|
|
||||||
assert!(output.icbeg <= 50);
|
assert!(output.icbeg <= 50);
|
||||||
assert!(output.icend <= 50);
|
assert!(output.icend <= 50);
|
||||||
@ -1059,7 +1059,7 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut params = TestParamsBuilder::new(50).config(config).build();
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
||||||
let output = conref_pure(&mut params);
|
let output = conref(&mut params);
|
||||||
|
|
||||||
assert!(output.icbeg <= 50);
|
assert!(output.icbeg <= 50);
|
||||||
}
|
}
|
||||||
@ -1073,7 +1073,7 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut params = TestParamsBuilder::new(50).config(config).build();
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
||||||
let output = conref_pure(&mut params);
|
let output = conref(&mut params);
|
||||||
|
|
||||||
assert!(output.icbeg <= 50);
|
assert!(output.icbeg <= 50);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -171,7 +171,7 @@ pub struct ContmdOutput {
|
|||||||
/// 计算盘模型对流层的温度 (CONTMD) - 无回调版本。
|
/// 计算盘模型对流层的温度 (CONTMD) - 无回调版本。
|
||||||
///
|
///
|
||||||
/// 用于纯计算模式,不更新电子密度和不透明度。
|
/// 用于纯计算模式,不更新电子密度和不透明度。
|
||||||
pub fn contmd_pure(params: &mut ContmdParams) -> ContmdOutput {
|
pub fn contmd(params: &mut ContmdParams) -> ContmdOutput {
|
||||||
contmd_impl(params, &mut NoOpCallbacks)
|
contmd_impl(params, &mut NoOpCallbacks)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,7 +613,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_contmd_basic() {
|
fn test_contmd_basic() {
|
||||||
let mut params = create_test_params();
|
let mut params = create_test_params();
|
||||||
let output = contmd_pure(&mut params);
|
let output = contmd(&mut params);
|
||||||
|
|
||||||
// 验证迭代次数在合理范围内
|
// 验证迭代次数在合理范围内
|
||||||
assert!(output.iconit <= params.config.nconit);
|
assert!(output.iconit <= params.config.nconit);
|
||||||
@ -631,7 +631,7 @@ mod tests {
|
|||||||
// 禁用对流
|
// 禁用对流
|
||||||
params.config.hmix0 = -1.0;
|
params.config.hmix0 = -1.0;
|
||||||
|
|
||||||
let output = contmd_pure(&mut params);
|
let output = contmd(&mut params);
|
||||||
|
|
||||||
// 禁用对流时不应该有对流区
|
// 禁用对流时不应该有对流区
|
||||||
for &iconv in &output.iconv {
|
for &iconv in &output.iconv {
|
||||||
@ -646,7 +646,7 @@ mod tests {
|
|||||||
// 保存原始温度
|
// 保存原始温度
|
||||||
let orig_temp = params.temp.to_vec();
|
let orig_temp = params.temp.to_vec();
|
||||||
|
|
||||||
let _output = contmd_pure(&mut params);
|
let _output = contmd(&mut params);
|
||||||
|
|
||||||
// 温度可能被更新
|
// 温度可能被更新
|
||||||
// 检查温度仍然是有限值
|
// 检查温度仍然是有限值
|
||||||
@ -731,7 +731,7 @@ mod tests {
|
|||||||
hr1: Box::leak(Box::new(hr1)),
|
hr1: Box::leak(Box::new(hr1)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = contmd_pure(&mut params);
|
let output = contmd(&mut params);
|
||||||
|
|
||||||
assert_eq!(output.iconv.len(), nd);
|
assert_eq!(output.iconv.len(), nd);
|
||||||
assert!(output.iconit > 0);
|
assert!(output.iconit > 0);
|
||||||
|
|||||||
@ -7,9 +7,9 @@
|
|||||||
//! - 分析各元素对电子密度的贡献
|
//! - 分析各元素对电子密度的贡献
|
||||||
//! - 输出电子供体信息
|
//! - 输出电子供体信息
|
||||||
|
|
||||||
use crate::tlusty::math::{moleq_pure, MoleqParams};
|
use crate::tlusty::math::{moleq, MoleqParams};
|
||||||
use crate::tlusty::math::{rhonen_pure, RhonenParams};
|
use crate::tlusty::math::{rhonen, RhonenParams};
|
||||||
use crate::tlusty::math::{state_pure, StateParams};
|
use crate::tlusty::math::{state, StateParams};
|
||||||
use crate::tlusty::state::constants::{MDEPTH, MLEVEL};
|
use crate::tlusty::state::constants::{MDEPTH, MLEVEL};
|
||||||
// f2r_depends: RHONEN, STATE, MOLEQ
|
// f2r_depends: RHONEN, STATE, MOLEQ
|
||||||
|
|
||||||
@ -130,7 +130,7 @@ pub struct EldencOutput {
|
|||||||
///
|
///
|
||||||
/// # 返回值
|
/// # 返回值
|
||||||
/// 包含插值电子密度和贡献者信息的输出
|
/// 包含插值电子密度和贡献者信息的输出
|
||||||
pub fn eldenc_pure(params: &EldencParams) -> EldencOutput {
|
pub fn eldenc(params: &EldencParams) -> EldencOutput {
|
||||||
let nd = params.nd;
|
let nd = params.nd;
|
||||||
let config = ¶ms.config;
|
let config = ¶ms.config;
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ pub fn eldenc_pure(params: &EldencParams) -> EldencOutput {
|
|||||||
|
|
||||||
// 计算 LTE 电子密度
|
// 计算 LTE 电子密度
|
||||||
if let Some(rhonen_params) = ¶ms.rhonen_params {
|
if let Some(rhonen_params) = ¶ms.rhonen_params {
|
||||||
let rhonen_out = rhonen_pure(rhonen_params);
|
let rhonen_out = rhonen(rhonen_params);
|
||||||
ane_lte[id] = rhonen_out.ane;
|
ane_lte[id] = rhonen_out.ane;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,12 +190,12 @@ pub fn eldenc_pure(params: &EldencParams) -> EldencOutput {
|
|||||||
let rho = params.dens[id];
|
let rho = params.dens[id];
|
||||||
|
|
||||||
if let Some(rhonen_params) = ¶ms.rhonen_params {
|
if let Some(rhonen_params) = ¶ms.rhonen_params {
|
||||||
let rhonen_out = rhonen_pure(rhonen_params);
|
let rhonen_out = rhonen(rhonen_params);
|
||||||
let aein = rhonen_out.ane;
|
let aein = rhonen_out.ane;
|
||||||
|
|
||||||
// 调用 MOLEQ 计算分子平衡
|
// 调用 MOLEQ 计算分子平衡
|
||||||
if let Some(moleq_params) = ¶ms.moleq_params {
|
if let Some(moleq_params) = ¶ms.moleq_params {
|
||||||
let moleq_out = moleq_pure(moleq_params);
|
let moleq_out = moleq(moleq_params);
|
||||||
// Fortran: elcon(ia,id)=anion(ia,id)/elec(id)
|
// Fortran: elcon(ia,id)=anion(ia,id)/elec(id)
|
||||||
// moleq_out.anio0 包含离子数密度
|
// moleq_out.anio0 包含离子数密度
|
||||||
for ia in 0..30.min(moleq_out.anio0.len()) {
|
for ia in 0..30.min(moleq_out.anio0.len()) {
|
||||||
@ -214,7 +214,7 @@ pub fn eldenc_pure(params: &EldencParams) -> EldencOutput {
|
|||||||
} else {
|
} else {
|
||||||
// 使用 STATE
|
// 使用 STATE
|
||||||
if let Some(state_params) = ¶ms.state_params {
|
if let Some(state_params) = ¶ms.state_params {
|
||||||
let _state_out = state_pure(state_params);
|
let _state_out = state(state_params);
|
||||||
|
|
||||||
for ia in 0..30 {
|
for ia in 0..30 {
|
||||||
let iat = params.iatex[ia];
|
let iat = params.iatex[ia];
|
||||||
@ -402,7 +402,7 @@ mod tests {
|
|||||||
moleq_params: None,
|
moleq_params: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = eldenc_pure(¶ms);
|
let output = eldenc(¶ms);
|
||||||
|
|
||||||
// 检查输出维度
|
// 检查输出维度
|
||||||
assert_eq!(output.elecg.len(), 5);
|
assert_eq!(output.elecg.len(), 5);
|
||||||
|
|||||||
@ -9,9 +9,9 @@
|
|||||||
//! - 计算内能和熵
|
//! - 计算内能和熵
|
||||||
|
|
||||||
use crate::tlusty::math::lineqs;
|
use crate::tlusty::math::lineqs;
|
||||||
use crate::tlusty::math::{moleq_pure, MoleqParams, MoleculeEqData};
|
use crate::tlusty::math::{moleq, MoleqParams, MoleculeEqData};
|
||||||
use crate::tlusty::math::{mpartf, MpartfResult};
|
use crate::tlusty::math::{mpartf, MpartfResult};
|
||||||
use crate::tlusty::math::{state_pure, StateParams};
|
use crate::tlusty::math::{state, StateParams};
|
||||||
use crate::tlusty::math::{entene, EnteneParams, EnteneOutput};
|
use crate::tlusty::math::{entene, EnteneParams, EnteneOutput};
|
||||||
use crate::tlusty::state::constants::{BOLK, HMASS, UN, TWO, HALF};
|
use crate::tlusty::state::constants::{BOLK, HMASS, UN, TWO, HALF};
|
||||||
|
|
||||||
@ -138,7 +138,7 @@ pub struct EldensOutput {
|
|||||||
///
|
///
|
||||||
/// # 返回值
|
/// # 返回值
|
||||||
/// 包含电子密度、内能、熵等的输出结构体
|
/// 包含电子密度、内能、熵等的输出结构体
|
||||||
pub fn eldens_pure(params: &EldensParams, ipri: i32) -> EldensOutput {
|
pub fn eldens(params: &EldensParams, ipri: i32) -> EldensOutput {
|
||||||
// 检查 ioptab 标志
|
// 检查 ioptab 标志
|
||||||
if params.config.ioptab < -1 {
|
if params.config.ioptab < -1 {
|
||||||
return EldensOutput {
|
return EldensOutput {
|
||||||
@ -183,7 +183,7 @@ pub fn eldens_pure(params: &EldensParams, ipri: i32) -> EldensOutput {
|
|||||||
// 如果包含分子且温度低于分子温度上限,调用 MOLEQ
|
// 如果包含分子且温度低于分子温度上限,调用 MOLEQ
|
||||||
if params.config.ifmol > 0 && t < params.config.tmolim {
|
if params.config.ifmol > 0 && t < params.config.tmolim {
|
||||||
let aein = an * anerel;
|
let aein = an * anerel;
|
||||||
// 调用 moleq_pure 计算分子平衡
|
// 调用 moleq 计算分子平衡
|
||||||
let default_mol_data: MoleculeEqData = Default::default();
|
let default_mol_data: MoleculeEqData = Default::default();
|
||||||
let moleq_params = MoleqParams {
|
let moleq_params = MoleqParams {
|
||||||
id: params.id,
|
id: params.id,
|
||||||
@ -204,7 +204,7 @@ pub fn eldens_pure(params: &EldensParams, ipri: i32) -> EldensOutput {
|
|||||||
ifmol: params.config.ifmol,
|
ifmol: params.config.ifmol,
|
||||||
moltab: 0,
|
moltab: 0,
|
||||||
};
|
};
|
||||||
let moleq_output = moleq_pure(&moleq_params);
|
let moleq_output = moleq(&moleq_params);
|
||||||
let ane = moleq_output.ane;
|
let ane = moleq_output.ane;
|
||||||
anerel = ane / an;
|
anerel = ane / an;
|
||||||
|
|
||||||
@ -292,7 +292,7 @@ pub fn eldens_pure(params: &EldensParams, ipri: i32) -> EldensOutput {
|
|||||||
ifoppf: state_params.ifoppf,
|
ifoppf: state_params.ifoppf,
|
||||||
lrm: state_params.lrm,
|
lrm: state_params.lrm,
|
||||||
};
|
};
|
||||||
let state_output = state_pure(&updated_params);
|
let state_output = state(&updated_params);
|
||||||
q = state_output.q;
|
q = state_output.q;
|
||||||
dqn = state_output.dqn;
|
dqn = state_output.dqn;
|
||||||
}
|
}
|
||||||
@ -620,7 +620,7 @@ mod tests {
|
|||||||
anato_data: None,
|
anato_data: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = eldens_pure(¶ms, 0);
|
let output = eldens(¶ms, 0);
|
||||||
|
|
||||||
// 验证输出
|
// 验证输出
|
||||||
assert!(output.ane > 0.0);
|
assert!(output.ane > 0.0);
|
||||||
@ -648,7 +648,7 @@ mod tests {
|
|||||||
anato_data: None,
|
anato_data: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = eldens_pure(¶ms, 0);
|
let output = eldens(¶ms, 0);
|
||||||
|
|
||||||
// 低温时电子密度应该较低
|
// 低温时电子密度应该较低
|
||||||
assert!(output.anerel < 0.5);
|
assert!(output.anerel < 0.5);
|
||||||
|
|||||||
@ -193,7 +193,7 @@ pub fn parse_molecule_data(content: &str, exclude_large_carbon: bool) -> Molecul
|
|||||||
///
|
///
|
||||||
/// # 返回值
|
/// # 返回值
|
||||||
/// 包含电子密度、熵、内能、密度等的输出结构体
|
/// 包含电子密度、熵、内能、密度等的输出结构体
|
||||||
pub fn moleq_pure(params: &MoleqParams) -> MoleqOutput {
|
pub fn moleq(params: &MoleqParams) -> MoleqOutput {
|
||||||
// 如果不包含分子,直接返回空结果
|
// 如果不包含分子,直接返回空结果
|
||||||
if params.ifmol == 0 {
|
if params.ifmol == 0 {
|
||||||
return MoleqOutput {
|
return MoleqOutput {
|
||||||
@ -516,7 +516,7 @@ mod tests {
|
|||||||
moltab: 1,
|
moltab: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = moleq_pure(¶ms);
|
let output = moleq(¶ms);
|
||||||
|
|
||||||
// 当 ifmol=0 时,应返回初始估计
|
// 当 ifmol=0 时,应返回初始估计
|
||||||
assert!((output.ane - params.aein).abs() < 1e-10);
|
assert!((output.ane - params.aein).abs() < 1e-10);
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
//! - 从给定的温度和质量密度迭代求解总粒子密度和电子密度
|
//! - 从给定的温度和质量密度迭代求解总粒子密度和电子密度
|
||||||
//! - 使用 eldens 计算电子密度
|
//! - 使用 eldens 计算电子密度
|
||||||
|
|
||||||
use crate::tlusty::math::{eldens_pure, EldensConfig, EldensOutput, EldensParams};
|
use crate::tlusty::math::{eldens, EldensConfig, EldensOutput, EldensParams};
|
||||||
use crate::tlusty::state::constants::{HMASS, UN};
|
use crate::tlusty::state::constants::{HMASS, UN};
|
||||||
|
|
||||||
/// RHONEN 输入参数
|
/// RHONEN 输入参数
|
||||||
@ -58,7 +58,7 @@ pub struct RhonenOutput {
|
|||||||
///
|
///
|
||||||
/// # 返回值
|
/// # 返回值
|
||||||
/// 包含粒子密度、电子密度等的输出结构体
|
/// 包含粒子密度、电子密度等的输出结构体
|
||||||
pub fn rhonen_pure(params: &RhonenParams) -> RhonenOutput {
|
pub fn rhonen(params: &RhonenParams) -> RhonenOutput {
|
||||||
let id = params.id;
|
let id = params.id;
|
||||||
let t = params.t;
|
let t = params.t;
|
||||||
let rho = params.rho;
|
let rho = params.rho;
|
||||||
@ -125,7 +125,7 @@ pub fn rhonen_pure(params: &RhonenParams) -> RhonenOutput {
|
|||||||
anato_data: None,
|
anato_data: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let eldens_output = eldens_pure(&eldens_params, 0);
|
let eldens_output = eldens(&eldens_params, 0);
|
||||||
|
|
||||||
ane = eldens_output.ane;
|
ane = eldens_output.ane;
|
||||||
enrgi = eldens_output.energ;
|
enrgi = eldens_output.energ;
|
||||||
@ -202,7 +202,7 @@ mod tests {
|
|||||||
eldens_wmy: 1.0,
|
eldens_wmy: 1.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = rhonen_pure(¶ms);
|
let result = rhonen(¶ms);
|
||||||
|
|
||||||
// 验证基本物理约束
|
// 验证基本物理约束
|
||||||
assert!(result.an > 0.0, "粒子密度应为正");
|
assert!(result.an > 0.0, "粒子密度应为正");
|
||||||
@ -230,7 +230,7 @@ mod tests {
|
|||||||
eldens_wmy: 1.0,
|
eldens_wmy: 1.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = rhonen_pure(¶ms);
|
let result = rhonen(¶ms);
|
||||||
|
|
||||||
// 验证基本物理约束
|
// 验证基本物理约束
|
||||||
assert!(result.an > 0.0, "粒子密度应为正");
|
assert!(result.an > 0.0, "粒子密度应为正");
|
||||||
@ -258,7 +258,7 @@ mod tests {
|
|||||||
eldens_wmy: 1.0,
|
eldens_wmy: 1.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = rhonen_pure(¶ms);
|
let result = rhonen(¶ms);
|
||||||
|
|
||||||
assert!(result.an > 0.0);
|
assert!(result.an > 0.0);
|
||||||
assert!(result.ane > 0.0);
|
assert!(result.ane > 0.0);
|
||||||
@ -287,7 +287,7 @@ mod tests {
|
|||||||
eldens_wmy: 1.0,
|
eldens_wmy: 1.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = rhonen_pure(¶ms);
|
let result = rhonen(¶ms);
|
||||||
assert!(result.ane >= 0.0, "温度 {} K 时电子密度应为非负", t);
|
assert!(result.ane >= 0.0, "温度 {} K 时电子密度应为非负", t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
//! 这些函数填充矩阵 A, B, C 的流体静力学平衡行
|
//! 这些函数填充矩阵 A, B, C 的流体静力学平衡行
|
||||||
//! (NFREQE+INHE)-th row。
|
//! (NFREQE+INHE)-th row。
|
||||||
|
|
||||||
|
use crate::tlusty::math::special::erfcx;
|
||||||
use crate::tlusty::state::constants::{BOLK, HALF, MDEPTH, MFREQ, MLEVEL, MTOT, TWO, UN};
|
use crate::tlusty::state::constants::{BOLK, HALF, MDEPTH, MFREQ, MLEVEL, MTOT, TWO, UN};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@ -275,35 +276,6 @@ const PCK: f64 = 4.0 * std::f64::consts::PI / 3.0;
|
|||||||
// 辅助函数
|
// 辅助函数
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
/// 计算 erfcx(x) = exp(x²) * erfc(x)
|
|
||||||
/// 使用 Fortran 代码中的系数
|
|
||||||
fn erfcx(x: f64) -> f64 {
|
|
||||||
if x < 3.0 {
|
|
||||||
// 使用近似公式
|
|
||||||
8.86226925e-1 * (x * x).exp() * erfc_approx(x)
|
|
||||||
} else {
|
|
||||||
// 渐近展开
|
|
||||||
HALF * (UN - HALF / (x * x)) / x
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// erfc 近似
|
|
||||||
fn erfc_approx(x: f64) -> f64 {
|
|
||||||
// 简单近似,精度足够
|
|
||||||
if x < 0.0 {
|
|
||||||
2.0 - erfc_approx(-x)
|
|
||||||
} else if x < 6.0 {
|
|
||||||
let t = 1.0 / (1.0 + 0.5 * x);
|
|
||||||
let poly = t * (0.17087277 + t
|
|
||||||
* (-0.82215223 + t
|
|
||||||
* (1.48851587 + t
|
|
||||||
* (-1.13520398 + t
|
|
||||||
* (0.27886807 + t * (-0.18628806 + t * 0.4206475))))));
|
|
||||||
t * (-x * x).exp() * poly
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// BHE 主函数
|
// BHE 主函数
|
||||||
|
|||||||
@ -11,7 +11,7 @@ use crate::tlusty::math::collhe;
|
|||||||
use crate::tlusty::math::cspec;
|
use crate::tlusty::math::cspec;
|
||||||
use crate::tlusty::math::irc;
|
use crate::tlusty::math::irc;
|
||||||
use crate::tlusty::state::constants::{EH, HK, MLEVEL, UN};
|
use crate::tlusty::state::constants::{EH, HK, MLEVEL, UN};
|
||||||
// f2r_depends: COLLHE, CSPEC, IRC, CHEAV
|
use super::expi_approx;
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 常量和数据
|
// 常量和数据
|
||||||
@ -46,21 +46,7 @@ static A: [[f64; 10]; 6] = [
|
|||||||
-0.72724497, 1.0879648, 5.6239786, 9.5323009, 16.150818],
|
-0.72724497, 1.0879648, 5.6239786, 9.5323009, 16.150818],
|
||||||
];
|
];
|
||||||
|
|
||||||
// ============================================================================
|
// f2r_depends: COLLHE, CSPEC, IRC, CHEAV
|
||||||
// 辅助函数
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
/// 计算指数积分 E1(x) 的近似值(Abramowitz-Stegun 公式)。
|
|
||||||
fn expi_approx(u0: f64) -> f64 {
|
|
||||||
if u0 <= UN {
|
|
||||||
// 小参数展开
|
|
||||||
-u0.ln() + (-0.57721566 + u0 * (0.99999193 + u0 * (-0.24991055 + u0 * (0.05519968 + u0 * (-0.00976004 + u0 * 0.00107857)))))
|
|
||||||
} else {
|
|
||||||
// 大参数渐近展开
|
|
||||||
(-u0).exp() * ((0.2677734343 + u0 * (8.6347608925 + u0 * (18.059016973 + u0 * 8.5733287401)))
|
|
||||||
/ (3.9584969228 + u0 * (21.0996530827 + u0 * (25.6329561486 + u0 * 9.5733223454)))) / u0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 输入/输出结构体
|
// 输入/输出结构体
|
||||||
|
|||||||
@ -16,30 +16,14 @@ use crate::tlusty::math::cspec;
|
|||||||
use crate::tlusty::math::irc;
|
use crate::tlusty::math::irc;
|
||||||
use crate::tlusty::math::ylintp;
|
use crate::tlusty::math::ylintp;
|
||||||
use crate::tlusty::state::constants::{EH, HK, MLEVEL, TWO, UN};
|
use crate::tlusty::state::constants::{EH, HK, MLEVEL, TWO, UN};
|
||||||
|
use super::expi_approx;
|
||||||
|
|
||||||
// f2r_depends: COLH, COLHE, CSPEC, IRC, CION, YLININTP
|
// f2r_depends: COLH, COLHE, CSPEC, IRC, CION, YLININTP
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 常量
|
// 常量
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
/// 指数积分展开系数
|
|
||||||
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.2677734343;
|
|
||||||
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;
|
|
||||||
|
|
||||||
/// 最大碰撞类型数
|
/// 最大碰撞类型数
|
||||||
pub const MXTCOL: usize = 3;
|
pub const MXTCOL: usize = 3;
|
||||||
/// 最大碰撞拟合点数
|
/// 最大碰撞拟合点数
|
||||||
@ -79,16 +63,6 @@ fn cupsx(x: f64, u: f64, a: f64) -> f64 {
|
|||||||
8.631e-6 / x * (-u).exp() * a
|
8.631e-6 / x * (-u).exp() * a
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 指数积分 E1 近似
|
|
||||||
fn expi_approx(u0: f64) -> f64 {
|
|
||||||
if u0 <= UN {
|
|
||||||
-u0.ln() + EXPIA1 + u0 * (EXPIA2 + u0 * (EXPIA3 + u0 * (EXPIA4 + u0 * (EXPIA5 + u0 * EXPIA6))))
|
|
||||||
} else {
|
|
||||||
(-u0).exp() * ((EXPIB1 + u0 * (EXPIB2 + u0 * (EXPIB3 + u0 * EXPIB4)))
|
|
||||||
/ (EXPIC1 + u0 * (EXPIC2 + u0 * (EXPIC3 + u0 * EXPIC4)))) / u0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 输入/输出结构体
|
// 输入/输出结构体
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
//! 计算深度相关的氦丰度剖面。
|
//! 计算深度相关的氦丰度剖面。
|
||||||
|
|
||||||
use crate::tlusty::io::{FortranWriter, Result};
|
use crate::tlusty::io::{FortranWriter, Result};
|
||||||
|
use crate::tlusty::math::solvers::raph;
|
||||||
use crate::tlusty::state::atomic::AtomicData;
|
use crate::tlusty::state::atomic::AtomicData;
|
||||||
use crate::tlusty::state::config::TlustyConfig;
|
use crate::tlusty::state::config::TlustyConfig;
|
||||||
use crate::tlusty::state::constants::MDEPTH;
|
use crate::tlusty::state::constants::MDEPTH;
|
||||||
@ -19,15 +20,7 @@ const A2: f64 = 4.0; // 氦原子量
|
|||||||
const BIGG: f64 = 6.6732e-8; // 引力常数
|
const BIGG: f64 = 6.6732e-8; // 引力常数
|
||||||
const PI: f64 = std::f64::consts::PI;
|
const PI: f64 = std::f64::consts::PI;
|
||||||
|
|
||||||
/// 计算扩散因子 RAPH。
|
|
||||||
/// RAPH = diffusion factor for He/H separation
|
|
||||||
fn raph(gam: f64, z1: f64, z2: f64, a1: f64, a2: f64) -> f64 {
|
|
||||||
// 简化的扩散因子计算
|
|
||||||
// 实际公式更复杂,这里使用近似
|
|
||||||
let dz = z2 - z1;
|
|
||||||
let da = a2 - a1;
|
|
||||||
gam * (dz / da).abs().min(1.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// HEDIF 参数结构体
|
/// HEDIF 参数结构体
|
||||||
pub struct HedifParams<'a> {
|
pub struct HedifParams<'a> {
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
use crate::tlusty::math::erfcx;
|
use crate::tlusty::math::erfcx;
|
||||||
use crate::tlusty::math::matinv;
|
use crate::tlusty::math::matinv;
|
||||||
use crate::tlusty::math::{rhonen_pure, RhonenParams};
|
use crate::tlusty::math::{rhonen, RhonenParams};
|
||||||
use crate::tlusty::math::{steqeq_pure, SteqeqConfig, SteqeqParams};
|
use crate::tlusty::math::{steqeq_pure, SteqeqConfig, SteqeqParams};
|
||||||
use crate::tlusty::math::wnstor;
|
use crate::tlusty::math::wnstor;
|
||||||
use crate::tlusty::state::constants::{HALF, MDEPTH, TWO, UN};
|
use crate::tlusty::state::constants::{HALF, MDEPTH, TWO, UN};
|
||||||
@ -216,7 +216,7 @@ pub struct HesolvOutput {
|
|||||||
///
|
///
|
||||||
/// # 返回值
|
/// # 返回值
|
||||||
/// 包含更新后的压力、密度、深度等的结果结构体
|
/// 包含更新后的压力、密度、深度等的结果结构体
|
||||||
pub fn hesolv_pure(params: &HesolvParams) -> HesolvOutput {
|
pub fn hesolv(params: &HesolvParams) -> HesolvOutput {
|
||||||
let nd = params.model.nd;
|
let nd = params.model.nd;
|
||||||
let mut p = vec![0.0; nd];
|
let mut p = vec![0.0; nd];
|
||||||
let mut vsnd2 = params.aux.vsnd2.clone();
|
let mut vsnd2 = params.aux.vsnd2.clone();
|
||||||
@ -492,7 +492,7 @@ pub fn hesolv_pure(params: &HesolvParams) -> HesolvOutput {
|
|||||||
eldens_wmy: params.config.eldens_wmy,
|
eldens_wmy: params.config.eldens_wmy,
|
||||||
};
|
};
|
||||||
|
|
||||||
let rhonen_output = rhonen_pure(&rhonen_params);
|
let rhonen_output = rhonen(&rhonen_params);
|
||||||
elec[id] = rhonen_output.ane;
|
elec[id] = rhonen_output.ane;
|
||||||
anerel = rhonen_output.anerel;
|
anerel = rhonen_output.anerel;
|
||||||
|
|
||||||
@ -685,7 +685,7 @@ mod tests {
|
|||||||
config,
|
config,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = hesolv_pure(¶ms);
|
let output = hesolv(¶ms);
|
||||||
|
|
||||||
// 验证输出
|
// 验证输出
|
||||||
assert_eq!(output.ptotal.len(), nd);
|
assert_eq!(output.ptotal.len(), nd);
|
||||||
@ -753,7 +753,7 @@ mod tests {
|
|||||||
config,
|
config,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = hesolv_pure(¶ms);
|
let output = hesolv(¶ms);
|
||||||
|
|
||||||
// 验证压力随深度增加
|
// 验证压力随深度增加
|
||||||
for i in 1..nd {
|
for i in 1..nd {
|
||||||
@ -832,7 +832,7 @@ mod tests {
|
|||||||
config,
|
config,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = hesolv_pure(¶ms);
|
let output = hesolv(¶ms);
|
||||||
|
|
||||||
// 所有密度应为正
|
// 所有密度应为正
|
||||||
for (i, &d) in output.dens.iter().enumerate() {
|
for (i, &d) in output.dens.iter().enumerate() {
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
//! hydrogen module
|
//! hydrogen module
|
||||||
|
|
||||||
|
mod utils;
|
||||||
|
pub use utils::expi_approx;
|
||||||
|
|
||||||
mod bhe;
|
mod bhe;
|
||||||
mod bre;
|
mod bre;
|
||||||
mod brez;
|
mod brez;
|
||||||
|
|||||||
@ -212,7 +212,7 @@ fn interpolate_cross_section(
|
|||||||
/// SIGAVE 纯计算函数
|
/// SIGAVE 纯计算函数
|
||||||
///
|
///
|
||||||
/// 从读取的截面数据计算指定频率点的截面值
|
/// 从读取的截面数据计算指定频率点的截面值
|
||||||
pub fn sigave_pure(
|
pub fn sigave(
|
||||||
transitions: &[(usize, Vec<CrossSectionPoint>)],
|
transitions: &[(usize, Vec<CrossSectionPoint>)],
|
||||||
nfreqb: usize,
|
nfreqb: usize,
|
||||||
freq: &[f64],
|
freq: &[f64],
|
||||||
@ -355,7 +355,7 @@ fn sigave_impl<R: BufRead>(params: &SigaveParams, mut reader: FortranReader<R>)
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 计算截面
|
// 计算截面
|
||||||
sigave_pure(
|
sigave(
|
||||||
&transitions,
|
&transitions,
|
||||||
nfreqb,
|
nfreqb,
|
||||||
params.freq,
|
params.freq,
|
||||||
@ -513,7 +513,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sigave_pure() {
|
fn test_sigave() {
|
||||||
let transitions = vec![(
|
let transitions = vec![(
|
||||||
2,
|
2,
|
||||||
vec![
|
vec![
|
||||||
@ -532,7 +532,7 @@ mod tests {
|
|||||||
let ifreqb: Vec<i32> = (0..50).map(|i| i as i32).collect();
|
let ifreqb: Vec<i32> = (0..50).map(|i| i as i32).collect();
|
||||||
let mut bfcs = vec![vec![0.0_f32; 100]; 10];
|
let mut bfcs = vec![vec![0.0_f32; 100]; 10];
|
||||||
|
|
||||||
sigave_pure(&transitions, 100, &freq, &ifreqb, 0, &mut bfcs);
|
sigave(&transitions, 100, &freq, &ifreqb, 0, &mut bfcs);
|
||||||
|
|
||||||
// 检查截面是否被计算
|
// 检查截面是否被计算
|
||||||
assert!(bfcs[1].iter().any(|&x| x > 0.0));
|
assert!(bfcs[1].iter().any(|&x| x > 0.0));
|
||||||
|
|||||||
66
src/tlusty/math/hydrogen/utils.rs
Normal file
66
src/tlusty/math/hydrogen/utils.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
//! hydrogen 公共辅助函数
|
||||||
|
//!
|
||||||
|
//! 提取自 colhe.rs 和 colis.rs 中的重复实现。
|
||||||
|
|
||||||
|
use crate::tlusty::state::constants::UN;
|
||||||
|
|
||||||
|
// 指数积分展开系数(来自 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.2677734343;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// 指数积分 E1(x) 的近似值(Abramowitz-Stegun 公式 5.1.53 & 5.1.56)。
|
||||||
|
///
|
||||||
|
/// 精度约为 2×10⁻⁷。
|
||||||
|
#[inline]
|
||||||
|
pub fn expi_approx(u0: f64) -> f64 {
|
||||||
|
if u0 <= UN {
|
||||||
|
// 小参数:级数展开
|
||||||
|
-u0.ln()
|
||||||
|
+ EXPIA1
|
||||||
|
+ u0 * (EXPIA2 + u0 * (EXPIA3 + u0 * (EXPIA4 + u0 * (EXPIA5 + u0 * EXPIA6))))
|
||||||
|
} else {
|
||||||
|
// 大参数:有理近似(渐近展开)
|
||||||
|
(-u0).exp()
|
||||||
|
* ((EXPIB1 + u0 * (EXPIB2 + u0 * (EXPIB3 + u0 * EXPIB4)))
|
||||||
|
/ (EXPIC1 + u0 * (EXPIC2 + u0 * (EXPIC3 + u0 * EXPIC4))))
|
||||||
|
/ u0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use approx::assert_relative_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expi_small() {
|
||||||
|
let result = expi_approx(0.5);
|
||||||
|
assert_relative_eq!(result, 0.5598, epsilon = 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expi_large() {
|
||||||
|
let result = expi_approx(5.0);
|
||||||
|
assert_relative_eq!(result, 0.001148, epsilon = 1e-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expi_unity() {
|
||||||
|
let result = expi_approx(1.0);
|
||||||
|
assert_relative_eq!(result, 0.2194, epsilon = 0.01);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -21,7 +21,7 @@ use crate::tlusty::state::constants::{UN, HALF, HK, BOLK, BN, MDEPTH, MFREQ, MLE
|
|||||||
const NPTR: usize = 30;
|
const NPTR: usize = 30;
|
||||||
use crate::tlusty::state::atomic::AtomicData;
|
use crate::tlusty::state::atomic::AtomicData;
|
||||||
use crate::tlusty::state::model::ModelState;
|
use crate::tlusty::state::model::ModelState;
|
||||||
use crate::tlusty::math::{sabolf_pure, SabolfParams, SabolfOutput};
|
use crate::tlusty::math::{sabolf, SabolfParams, SabolfOutput};
|
||||||
use crate::tlusty::math::{linpro, LinproParams, LinproOutput};
|
use crate::tlusty::math::{linpro, LinproParams, LinproOutput};
|
||||||
use crate::tlusty::math::dwnfr;
|
use crate::tlusty::math::dwnfr;
|
||||||
use crate::tlusty::math::cross;
|
use crate::tlusty::math::cross;
|
||||||
@ -173,7 +173,7 @@ fn get_sbf(id: usize, model: &ModelState, atomic: &AtomicData) -> SabolfOutput {
|
|||||||
ioptab: 0,
|
ioptab: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
sabolf_pure(&mut params)
|
sabolf(&mut params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取谱线轮廓在指定频率点的值
|
/// 获取谱线轮廓在指定频率点的值
|
||||||
@ -235,7 +235,7 @@ fn get_bf_cross_section(
|
|||||||
///
|
///
|
||||||
/// # 返回
|
/// # 返回
|
||||||
/// 各跃迁的详细信息
|
/// 各跃迁的详细信息
|
||||||
pub fn princ_pure(params: &PrincParams) -> PrincOutput {
|
pub fn princ(params: &PrincParams) -> PrincOutput {
|
||||||
let nd = params.model.modpar.temp.len();
|
let nd = params.model.modpar.temp.len();
|
||||||
let nct = params.nct;
|
let nct = params.nct;
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ use crate::tlusty::state::config::InpPar;
|
|||||||
use crate::tlusty::state::constants::HK;
|
use crate::tlusty::state::constants::HK;
|
||||||
use crate::tlusty::state::model::{CraTes, LevPop, ModPar, MrgPar, RrRates, WmComp};
|
use crate::tlusty::state::model::{CraTes, LevPop, ModPar, MrgPar, RrRates, WmComp};
|
||||||
|
|
||||||
use crate::tlusty::math::{sabolf_pure, SabolfParams};
|
use crate::tlusty::math::{sabolf, SabolfParams};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 输出结构体
|
// 输出结构体
|
||||||
@ -77,7 +77,7 @@ pub struct PrntParams<'a> {
|
|||||||
///
|
///
|
||||||
/// # 返回
|
/// # 返回
|
||||||
/// 各能级的速率平衡结果
|
/// 各能级的速率平衡结果
|
||||||
pub fn prnt_pure(params: &PrntParams) -> PrntOutput {
|
pub fn prnt(params: &PrntParams) -> PrntOutput {
|
||||||
let mut balances = Vec::new();
|
let mut balances = Vec::new();
|
||||||
|
|
||||||
let nd = params.modpar.temp.len();
|
let nd = params.modpar.temp.len();
|
||||||
@ -118,7 +118,7 @@ pub fn prnt_pure(params: &PrntParams) -> PrntOutput {
|
|||||||
gmer: &mut gmer_work,
|
gmer: &mut gmer_work,
|
||||||
ioptab: 0,
|
ioptab: 0,
|
||||||
};
|
};
|
||||||
let sabolf_result = sabolf_pure(&mut sabolf_params);
|
let sabolf_result = sabolf(&mut sabolf_params);
|
||||||
let sbf = &sabolf_result.sbf;
|
let sbf = &sabolf_result.sbf;
|
||||||
let usum = &sabolf_result.usum;
|
let usum = &sabolf_result.usum;
|
||||||
|
|
||||||
@ -556,7 +556,7 @@ mod tests {
|
|||||||
ipop: &ipop,
|
ipop: &ipop,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = prnt_pure(¶ms);
|
let result = prnt(¶ms);
|
||||||
|
|
||||||
// 由于测试数据是空的,结果应该为空或只有有限的结果
|
// 由于测试数据是空的,结果应该为空或只有有限的结果
|
||||||
println!("Number of balances: {}", result.balances.len());
|
println!("Number of balances: {}", result.balances.len());
|
||||||
@ -593,7 +593,7 @@ mod tests {
|
|||||||
ipop: &ipop,
|
ipop: &ipop,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = prnt_pure(¶ms);
|
let result = prnt(¶ms);
|
||||||
|
|
||||||
// 验证结果
|
// 验证结果
|
||||||
for balance in &result.balances {
|
for balance in &result.balances {
|
||||||
|
|||||||
@ -164,7 +164,7 @@ pub struct PzevalOutput {
|
|||||||
/// ...
|
/// ...
|
||||||
/// END
|
/// END
|
||||||
/// ```
|
/// ```
|
||||||
pub fn pzeval_pure(params: &mut PzevalParams) -> PzevalOutput {
|
pub fn pzeval(params: &mut PzevalParams) -> PzevalOutput {
|
||||||
let nd = params.config.nd;
|
let nd = params.config.nd;
|
||||||
let mut depth_results = Vec::with_capacity(nd);
|
let mut depth_results = Vec::with_capacity(nd);
|
||||||
let mut conref_called = false;
|
let mut conref_called = false;
|
||||||
@ -359,7 +359,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_pzeval_basic() {
|
fn test_pzeval_basic() {
|
||||||
let mut params = TestParamsBuilder::new(50).build();
|
let mut params = TestParamsBuilder::new(50).build();
|
||||||
let output = pzeval_pure(&mut params);
|
let output = pzeval(&mut params);
|
||||||
|
|
||||||
assert_eq!(output.depth_results.len(), 50);
|
assert_eq!(output.depth_results.len(), 50);
|
||||||
assert!(!output.conref_called);
|
assert!(!output.conref_called);
|
||||||
@ -378,7 +378,7 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut params = TestParamsBuilder::new(50).config(config).build();
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
||||||
let output = pzeval_pure(&mut params);
|
let output = pzeval(&mut params);
|
||||||
|
|
||||||
assert_eq!(output.depth_results.len(), 50);
|
assert_eq!(output.depth_results.len(), 50);
|
||||||
}
|
}
|
||||||
@ -393,7 +393,7 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut params = TestParamsBuilder::new(50).config(config).build();
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
||||||
let output = pzeval_pure(&mut params);
|
let output = pzeval(&mut params);
|
||||||
|
|
||||||
// iter=5 在 iconrs=3 和 iconre=10 之间,应该触发 CONREF
|
// iter=5 在 iconrs=3 和 iconre=10 之间,应该触发 CONREF
|
||||||
assert!(output.conref_called);
|
assert!(output.conref_called);
|
||||||
@ -409,7 +409,7 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut params = TestParamsBuilder::new(50).config(config).build();
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
||||||
let output = pzeval_pure(&mut params);
|
let output = pzeval(&mut params);
|
||||||
|
|
||||||
// ipnzev=0 AND lfin=true → CONOUT(1,1)
|
// ipnzev=0 AND lfin=true → CONOUT(1,1)
|
||||||
assert!(output.conout_1);
|
assert!(output.conout_1);
|
||||||
|
|||||||
@ -771,7 +771,7 @@ pub fn compute_hydrogen_oscillator_strength(
|
|||||||
///
|
///
|
||||||
/// # 返回值
|
/// # 返回值
|
||||||
/// 处理后的能级数据数组
|
/// 处理后的能级数据数组
|
||||||
pub fn process_levels_pure(
|
pub fn process_levels(
|
||||||
params: &RdataParams,
|
params: &RdataParams,
|
||||||
level_inputs: &[LevelInputData],
|
level_inputs: &[LevelInputData],
|
||||||
) -> Vec<LevelData> {
|
) -> Vec<LevelData> {
|
||||||
@ -800,7 +800,7 @@ pub fn process_levels_pure(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 处理连续跃迁数据(纯计算部分)。
|
/// 处理连续跃迁数据(纯计算部分)。
|
||||||
pub fn process_continua_pure(
|
pub fn process_continua(
|
||||||
params: &RdataParams,
|
params: &RdataParams,
|
||||||
inputs: &[ContinuumInputData],
|
inputs: &[ContinuumInputData],
|
||||||
enion: &[f64],
|
enion: &[f64],
|
||||||
|
|||||||
@ -367,7 +367,7 @@ pub fn compute_cross_sections(
|
|||||||
///
|
///
|
||||||
/// 这是一个简化版本,只处理纯计算部分。
|
/// 这是一个简化版本,只处理纯计算部分。
|
||||||
/// 完整的 I/O 操作需要在外部处理。
|
/// 完整的 I/O 操作需要在外部处理。
|
||||||
pub fn rdatax_pure(
|
pub fn rdatax(
|
||||||
transitions: &[TransitionData],
|
transitions: &[TransitionData],
|
||||||
freq: &[f64],
|
freq: &[f64],
|
||||||
nfreq: usize,
|
nfreq: usize,
|
||||||
@ -441,23 +441,23 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rdatax_pure_basic() {
|
fn test_rdatax_basic() {
|
||||||
let transitions = vec![create_test_transition()];
|
let transitions = vec![create_test_transition()];
|
||||||
let freq = vec![1e15, 2e15, 3e15];
|
let freq = vec![1e15, 2e15, 3e15];
|
||||||
|
|
||||||
let output = rdatax_pure(&transitions, &freq, 3, 0, 0);
|
let output = rdatax(&transitions, &freq, 3, 0, 0);
|
||||||
|
|
||||||
assert_eq!(output.ntrx, 1);
|
assert_eq!(output.ntrx, 1);
|
||||||
assert_eq!(output.bfcs.len(), 1);
|
assert_eq!(output.bfcs.len(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rdatax_pure_with_ibfint() {
|
fn test_rdatax_with_ibfint() {
|
||||||
let transitions = vec![create_test_transition()];
|
let transitions = vec![create_test_transition()];
|
||||||
let freq = vec![1e15, 2e15, 3e15];
|
let freq = vec![1e15, 2e15, 3e15];
|
||||||
|
|
||||||
// ibfint > 0 时使用 nfreqc
|
// ibfint > 0 时使用 nfreqc
|
||||||
let output = rdatax_pure(&transitions, &freq, 3, 2, 1);
|
let output = rdatax(&transitions, &freq, 3, 2, 1);
|
||||||
|
|
||||||
assert_eq!(output.bfcs[0].len(), 2); // 使用 nfreqc=2
|
assert_eq!(output.bfcs[0].len(), 2); // 使用 nfreqc=2
|
||||||
}
|
}
|
||||||
@ -467,7 +467,7 @@ mod tests {
|
|||||||
let transitions: Vec<TransitionData> = vec![];
|
let transitions: Vec<TransitionData> = vec![];
|
||||||
let freq = vec![1e15, 2e15];
|
let freq = vec![1e15, 2e15];
|
||||||
|
|
||||||
let output = rdatax_pure(&transitions, &freq, 2, 0, 0);
|
let output = rdatax(&transitions, &freq, 2, 0, 0);
|
||||||
|
|
||||||
assert_eq!(output.ntrx, 0);
|
assert_eq!(output.ntrx, 0);
|
||||||
assert!(output.bfcs.is_empty());
|
assert!(output.bfcs.is_empty());
|
||||||
|
|||||||
@ -100,7 +100,7 @@ pub struct RechckOutput {
|
|||||||
/// ...
|
/// ...
|
||||||
/// END
|
/// END
|
||||||
/// ```
|
/// ```
|
||||||
pub fn rechck_pure(params: &RechckParams) -> RechckOutput {
|
pub fn rechck(params: &RechckParams) -> RechckOutput {
|
||||||
let nd = params.nd;
|
let nd = params.nd;
|
||||||
let nfreq = params.nfreq;
|
let nfreq = params.nfreq;
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ mod tests {
|
|||||||
emis1: &emis1,
|
emis1: &emis1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = rechck_pure(¶ms);
|
let output = rechck(¶ms);
|
||||||
|
|
||||||
assert_eq!(output.depth_results.len(), 3);
|
assert_eq!(output.depth_results.len(), 3);
|
||||||
|
|
||||||
@ -251,7 +251,7 @@ mod tests {
|
|||||||
emis1: &emis1,
|
emis1: &emis1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = rechck_pure(¶ms);
|
let output = rechck(¶ms);
|
||||||
|
|
||||||
assert_eq!(output.depth_results.len(), 1);
|
assert_eq!(output.depth_results.len(), 1);
|
||||||
let result = &output.depth_results[0];
|
let result = &output.depth_results[0];
|
||||||
@ -281,7 +281,7 @@ mod tests {
|
|||||||
emis1: &emis1,
|
emis1: &emis1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = rechck_pure(¶ms);
|
let output = rechck(¶ms);
|
||||||
|
|
||||||
// 当发射为零时,相对误差应为 0
|
// 当发射为零时,相对误差应为 0
|
||||||
assert_relative_eq!(output.depth_results[0].re, 0.0, epsilon = 1e-15);
|
assert_relative_eq!(output.depth_results[0].re, 0.0, epsilon = 1e-15);
|
||||||
|
|||||||
@ -346,7 +346,7 @@ fn set_2d<T: Copy>(arr: &mut [T], i: usize, j: usize, nrows: usize, val: T) {
|
|||||||
/// # 注意
|
/// # 注意
|
||||||
/// 此函数仅实现核心计算逻辑,不包含 I/O 操作。
|
/// 此函数仅实现核心计算逻辑,不包含 I/O 操作。
|
||||||
/// 外部函数调用(如 TDPINI、WNSTOR 等)需要通过回调实现。
|
/// 外部函数调用(如 TDPINI、WNSTOR 等)需要通过回调实现。
|
||||||
pub fn inilam_pure(
|
pub fn inilam(
|
||||||
config: &InilamConfig,
|
config: &InilamConfig,
|
||||||
model: &mut InilamModelState,
|
model: &mut InilamModelState,
|
||||||
atomic: &InilamAtomicParams,
|
atomic: &InilamAtomicParams,
|
||||||
@ -825,7 +825,7 @@ mod tests {
|
|||||||
hkt1: &hkt1,
|
hkt1: &hkt1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = inilam_pure(&config, &mut model, &atomic, &freq);
|
let output = inilam(&config, &mut model, &atomic, &freq);
|
||||||
|
|
||||||
// INIT=1 分支应该返回
|
// INIT=1 分支应该返回
|
||||||
assert_relative_eq!(output.anerel, 0.5); // TEFF >= 8000
|
assert_relative_eq!(output.anerel, 0.5); // TEFF >= 8000
|
||||||
|
|||||||
@ -114,7 +114,7 @@ impl Default for InpDisParams {
|
|||||||
///
|
///
|
||||||
/// # 返回值
|
/// # 返回值
|
||||||
/// 返回计算结果 InpDisResult
|
/// 返回计算结果 InpDisResult
|
||||||
pub fn inpdis_pure(params: &mut InpDisParams, cnu1: f64) -> InpDisResult {
|
pub fn inpdis(params: &mut InpDisParams, cnu1: f64) -> InpDisResult {
|
||||||
let un = 1.0;
|
let un = 1.0;
|
||||||
|
|
||||||
// 处理 fractv 和 dmvisc
|
// 处理 fractv 和 dmvisc
|
||||||
@ -340,7 +340,7 @@ pub fn inpdis_io<W: std::io::Write>(
|
|||||||
writer.write_newline()?;
|
writer.write_newline()?;
|
||||||
|
|
||||||
// 调用纯计算函数
|
// 调用纯计算函数
|
||||||
let result = inpdis_pure(params, cnu1);
|
let result = inpdis(params, cnu1);
|
||||||
|
|
||||||
// 写入输出结果
|
// 写入输出结果
|
||||||
writer.write_raw(&format!("TEFF ={:#10.0}", result.teff))?;
|
writer.write_raw(&format!("TEFF ={:#10.0}", result.teff))?;
|
||||||
@ -411,7 +411,7 @@ mod tests {
|
|||||||
reynum: 0.0,
|
reynum: 0.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = inpdis_pure(&mut params, 1e15);
|
let result = inpdis(&mut params, 1e15);
|
||||||
|
|
||||||
// 验证基本物理量
|
// 验证基本物理量
|
||||||
assert!(result.teff > 0.0, "TEFF should be positive");
|
assert!(result.teff > 0.0, "TEFF should be positive");
|
||||||
@ -445,7 +445,7 @@ mod tests {
|
|||||||
reynum: 0.0,
|
reynum: 0.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = inpdis_pure(&mut params, 1e15);
|
let result = inpdis(&mut params, 1e15);
|
||||||
|
|
||||||
// GR 修正应该生效
|
// GR 修正应该生效
|
||||||
assert!(result.teff > 0.0);
|
assert!(result.teff > 0.0);
|
||||||
@ -470,7 +470,7 @@ mod tests {
|
|||||||
reynum: 0.0,
|
reynum: 0.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = inpdis_pure(&mut params, 1e15);
|
let result = inpdis(&mut params, 1e15);
|
||||||
|
|
||||||
assert_relative_eq!(result.teff, 35000.0, epsilon = 1e-6);
|
assert_relative_eq!(result.teff, 35000.0, epsilon = 1e-6);
|
||||||
assert_relative_eq!(result.qgrav, 1e4, epsilon = 1e-6);
|
assert_relative_eq!(result.qgrav, 1e4, epsilon = 1e-6);
|
||||||
@ -493,7 +493,7 @@ mod tests {
|
|||||||
reynum: 0.0,
|
reynum: 0.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = inpdis_pure(&mut params, 0.0);
|
let result = inpdis(&mut params, 0.0);
|
||||||
|
|
||||||
// fractv 应该被重新计算
|
// fractv 应该被重新计算
|
||||||
assert!(params.fractv > 0.0);
|
assert!(params.fractv > 0.0);
|
||||||
@ -516,7 +516,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let cnu1 = 2.0e15;
|
let cnu1 = 2.0e15;
|
||||||
let result = inpdis_pure(&mut params, cnu1);
|
let result = inpdis(&mut params, cnu1);
|
||||||
|
|
||||||
// FRLMAX = 1e11 * cnu1 * teff
|
// FRLMAX = 1e11 * cnu1 * teff
|
||||||
let expected_frlmax = 1e11 * cnu1 * 30000.0;
|
let expected_frlmax = 1e11 * cnu1 * 30000.0;
|
||||||
|
|||||||
@ -209,7 +209,7 @@ pub struct LinselDebugLine {
|
|||||||
/// END
|
/// END
|
||||||
/// ```
|
/// ```
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn linsel_pure<F, G>(
|
pub fn linsel<F, G>(
|
||||||
config: &LinselConfig,
|
config: &LinselConfig,
|
||||||
atomic: &mut LinselAtomicParams,
|
atomic: &mut LinselAtomicParams,
|
||||||
freq: &mut LinselFreqParams,
|
freq: &mut LinselFreqParams,
|
||||||
@ -942,7 +942,7 @@ mod tests {
|
|||||||
vec![1.0; 10]
|
vec![1.0; 10]
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = linsel_pure(&config, &mut atomic, &mut freq_params, mock_opacf1, mock_rtefr1);
|
let output = linsel(&config, &mut atomic, &mut freq_params, mock_opacf1, mock_rtefr1);
|
||||||
|
|
||||||
// ODF 模式:nppx = nfreq
|
// ODF 模式:nppx = nfreq
|
||||||
assert_eq!(output.stats.nppx, nfreq as i32);
|
assert_eq!(output.stats.nppx, nfreq as i32);
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
//! - 基于 Klaus Werner 公式 A.3.4
|
//! - 基于 Klaus Werner 公式 A.3.4
|
||||||
//! - 归一化到单位
|
//! - 归一化到单位
|
||||||
|
|
||||||
use crate::tlusty::math::{sabolf_pure, SabolfParams, SabolfOutput};
|
use crate::tlusty::math::{sabolf, SabolfParams, SabolfOutput};
|
||||||
use crate::tlusty::math::ubeta;
|
use crate::tlusty::math::ubeta;
|
||||||
use crate::tlusty::math::voigt;
|
use crate::tlusty::math::voigt;
|
||||||
use crate::tlusty::state::atomic::AtomicData;
|
use crate::tlusty::state::atomic::AtomicData;
|
||||||
@ -287,7 +287,7 @@ fn compute_zmikro(params: &ProfspParams, id: usize) -> f64 {
|
|||||||
ioptab: 0,
|
ioptab: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let sabolf_result = sabolf_pure(&mut sabolf_params);
|
let sabolf_result = sabolf(&mut sabolf_params);
|
||||||
|
|
||||||
// 添加离子贡献
|
// 添加离子贡献
|
||||||
let nion = params.atomic.ionpar.iz.len();
|
let nion = params.atomic.ionpar.iz.len();
|
||||||
|
|||||||
@ -126,7 +126,7 @@ fn init_arrays() {
|
|||||||
///
|
///
|
||||||
/// # 返回
|
/// # 返回
|
||||||
/// 配分函数及其导数
|
/// 配分函数及其导数
|
||||||
pub fn partf_pure(params: &PartfParams) -> PartfOutput {
|
pub fn partf(params: &PartfParams) -> PartfOutput {
|
||||||
// 初始化辅助数组
|
// 初始化辅助数组
|
||||||
init_arrays();
|
init_arrays();
|
||||||
|
|
||||||
@ -433,7 +433,7 @@ fn partf_user_defined(params: &PartfParams) -> PartfOutput {
|
|||||||
/// Opacity Project 配分函数
|
/// Opacity Project 配分函数
|
||||||
fn partf_opacity_project(params: &PartfParams) -> PartfOutput {
|
fn partf_opacity_project(params: &PartfParams) -> PartfOutput {
|
||||||
// 调用 OPFRAC
|
// 调用 OPFRAC
|
||||||
use crate::tlusty::math::{opfrac_pure, OpfracParams, PfOptB};
|
use crate::tlusty::math::{opfrac, OpfracParams, PfOptB};
|
||||||
|
|
||||||
let opfrac_params = OpfracParams {
|
let opfrac_params = OpfracParams {
|
||||||
iat: params.iat,
|
iat: params.iat,
|
||||||
@ -443,7 +443,7 @@ fn partf_opacity_project(params: &PartfParams) -> PartfOutput {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let pfoptb = PfOptB::new();
|
let pfoptb = PfOptB::new();
|
||||||
let result = opfrac_pure(&opfrac_params, &pfoptb);
|
let result = opfrac(&opfrac_params, &pfoptb);
|
||||||
|
|
||||||
PartfOutput {
|
PartfOutput {
|
||||||
u: result.pf,
|
u: result.pf,
|
||||||
@ -473,7 +473,7 @@ fn partf_ni(params: &PartfParams) -> PartfOutput {
|
|||||||
/// 重元素配分函数 (Z > 30)
|
/// 重元素配分函数 (Z > 30)
|
||||||
fn partf_heavy(params: &PartfParams) -> PartfOutput {
|
fn partf_heavy(params: &PartfParams) -> PartfOutput {
|
||||||
// 调用 PFHEAV
|
// 调用 PFHEAV
|
||||||
use crate::tlusty::math::{pfheav_pure, PfheavParams};
|
use crate::tlusty::math::{pfheav, PfheavParams};
|
||||||
|
|
||||||
let pfheav_params = PfheavParams {
|
let pfheav_params = PfheavParams {
|
||||||
iiz: params.iat,
|
iiz: params.iat,
|
||||||
@ -483,7 +483,7 @@ fn partf_heavy(params: &PartfParams) -> PartfOutput {
|
|||||||
ane: params.ane,
|
ane: params.ane,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = pfheav_pure(&pfheav_params);
|
let result = pfheav(&pfheav_params);
|
||||||
PartfOutput {
|
PartfOutput {
|
||||||
u: result.u,
|
u: result.u,
|
||||||
dut: 0.0,
|
dut: 0.0,
|
||||||
@ -524,7 +524,7 @@ mod tests {
|
|||||||
iirwin: 0,
|
iirwin: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = partf_pure(¶ms);
|
let result = partf(¶ms);
|
||||||
assert!(result.u > 0.0, "Partition function should be positive");
|
assert!(result.u > 0.0, "Partition function should be positive");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,7 +540,7 @@ mod tests {
|
|||||||
iirwin: 0,
|
iirwin: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = partf_pure(¶ms);
|
let result = partf(¶ms);
|
||||||
assert!(result.u > 0.0, "Partition function should be positive");
|
assert!(result.u > 0.0, "Partition function should be positive");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -556,7 +556,7 @@ mod tests {
|
|||||||
iirwin: 0,
|
iirwin: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = partf_pure(¶ms);
|
let result = partf(¶ms);
|
||||||
assert!(result.u > 0.0, "Iron partition function should be positive");
|
assert!(result.u > 0.0, "Iron partition function should be positive");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -572,7 +572,7 @@ mod tests {
|
|||||||
iirwin: 0,
|
iirwin: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = partf_pure(¶ms);
|
let result = partf(¶ms);
|
||||||
assert!(result.u > 0.0, "Nickel partition function should be positive");
|
assert!(result.u > 0.0, "Nickel partition function should be positive");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -589,7 +589,7 @@ mod tests {
|
|||||||
iirwin: 0,
|
iirwin: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = partf_pure(¶ms);
|
let result = partf(¶ms);
|
||||||
assert!(result.u > 0.0, "High ionization partition function should be positive");
|
assert!(result.u > 0.0, "High ionization partition function should be positive");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -605,7 +605,7 @@ mod tests {
|
|||||||
iirwin: 0,
|
iirwin: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = partf_pure(¶ms);
|
let result = partf(¶ms);
|
||||||
assert!(result.u > 0.0, "Opacity Project partition function should be positive");
|
assert!(result.u > 0.0, "Opacity Project partition function should be positive");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -125,7 +125,7 @@ pub struct PfheavOutput {
|
|||||||
///
|
///
|
||||||
/// # 返回
|
/// # 返回
|
||||||
/// 配分函数值
|
/// 配分函数值
|
||||||
pub fn pfheav_pure(params: &PfheavParams) -> PfheavOutput {
|
pub fn pfheav(params: &PfheavParams) -> PfheavOutput {
|
||||||
let iiz = params.iiz;
|
let iiz = params.iiz;
|
||||||
let jnion = params.jnion;
|
let jnion = params.jnion;
|
||||||
let mode = params.mode;
|
let mode = params.mode;
|
||||||
@ -272,7 +272,7 @@ mod tests {
|
|||||||
ane: 1.0e12,
|
ane: 1.0e12,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = pfheav_pure(¶ms);
|
let result = pfheav(¶ms);
|
||||||
assert!((result.u - 1.0).abs() < 1e-10);
|
assert!((result.u - 1.0).abs() < 1e-10);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,7 +287,7 @@ mod tests {
|
|||||||
ane: 1.0e12,
|
ane: 1.0e12,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = pfheav_pure(¶ms);
|
let result = pfheav(¶ms);
|
||||||
assert!(result.u > 0.0, "Partition function should be positive");
|
assert!(result.u > 0.0, "Partition function should be positive");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,7 +302,7 @@ mod tests {
|
|||||||
ane: 1.0e12,
|
ane: 1.0e12,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = pfheav_pure(¶ms);
|
let result = pfheav(¶ms);
|
||||||
assert!(result.u > 0.0, "Partition function should be positive");
|
assert!(result.u > 0.0, "Partition function should be positive");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,7 +317,7 @@ mod tests {
|
|||||||
ane: 1.0e15,
|
ane: 1.0e15,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = pfheav_pure(¶ms);
|
let result = pfheav(¶ms);
|
||||||
assert!(result.u > 0.0, "Partition function should be positive");
|
assert!(result.u > 0.0, "Partition function should be positive");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,7 +332,7 @@ mod tests {
|
|||||||
ane: 1.0e10,
|
ane: 1.0e10,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = pfheav_pure(¶ms);
|
let result = pfheav(¶ms);
|
||||||
assert!(result.u > 0.0, "Partition function should be positive");
|
assert!(result.u > 0.0, "Partition function should be positive");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
//! - APM = (占据数向量) * (dAJ/dN)
|
//! - APM = (占据数向量) * (dAJ/dN)
|
||||||
|
|
||||||
use crate::tlusty::state::constants::*;
|
use crate::tlusty::state::constants::*;
|
||||||
use crate::tlusty::math::{state_pure, StateParams, StateOutput};
|
use crate::tlusty::math::{state, StateParams, StateOutput};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 输入参数结构体
|
// 输入参数结构体
|
||||||
@ -170,7 +170,7 @@ pub struct BpopcOutput {
|
|||||||
///
|
///
|
||||||
/// # 返回
|
/// # 返回
|
||||||
/// BPOPC 输出结构
|
/// BPOPC 输出结构
|
||||||
pub fn bpopc_pure(params: &BpopcParams) -> Option<BpopcOutput> {
|
pub fn bpopc(params: &BpopcParams) -> Option<BpopcOutput> {
|
||||||
let id = params.id;
|
let id = params.id;
|
||||||
|
|
||||||
// 计算索引
|
// 计算索引
|
||||||
@ -212,7 +212,7 @@ pub fn bpopc_pure(params: &BpopcParams) -> Option<BpopcOutput> {
|
|||||||
lrm: params.state_lrm,
|
lrm: params.state_lrm,
|
||||||
};
|
};
|
||||||
|
|
||||||
let state_out = state_pure(&state_params);
|
let state_out = state(&state_params);
|
||||||
|
|
||||||
let qq = if params.ioptab > 0 {
|
let qq = if params.ioptab > 0 {
|
||||||
state_out.q / params.ytot
|
state_out.q / params.ytot
|
||||||
@ -411,7 +411,7 @@ mod tests {
|
|||||||
state_natoms: 2,
|
state_natoms: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = bpopc_pure(¶ms);
|
let result = bpopc(¶ms);
|
||||||
assert!(result.is_none(), "Should return None when INPC=0");
|
assert!(result.is_none(), "Should return None when INPC=0");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -468,7 +468,7 @@ mod tests {
|
|||||||
state_natoms: 2,
|
state_natoms: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = bpopc_pure(¶ms);
|
let result = bpopc(¶ms);
|
||||||
assert!(result.is_some(), "Should return Some when INPC>0");
|
assert!(result.is_some(), "Should return Some when INPC>0");
|
||||||
|
|
||||||
let output = result.unwrap();
|
let output = result.unwrap();
|
||||||
@ -529,7 +529,7 @@ mod tests {
|
|||||||
state_natoms: 1,
|
state_natoms: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = bpopc_pure(¶ms);
|
let result = bpopc(¶ms);
|
||||||
assert!(result.is_some());
|
assert!(result.is_some());
|
||||||
|
|
||||||
let output = result.unwrap();
|
let output = result.unwrap();
|
||||||
|
|||||||
@ -135,7 +135,7 @@ impl Default for CoolrtOutput {
|
|||||||
/// 2. 累积各离子的加热率: HTRAT += W * ABSOTI * RAD1
|
/// 2. 累积各离子的加热率: HTRAT += W * ABSOTI * RAD1
|
||||||
/// 3. 累积净辐射冷却率: CLHT2 += W * (EM - ABSO1 * RAD1)
|
/// 3. 累积净辐射冷却率: CLHT2 += W * (EM - ABSO1 * RAD1)
|
||||||
/// 4. 累积纯发射冷却率: CLHT3 += W * EMIS1
|
/// 4. 累积纯发射冷却率: CLHT3 += W * EMIS1
|
||||||
pub fn coolrt_pure(params: &CoolrtParams) -> CoolrtOutput {
|
pub fn coolrt(params: &CoolrtParams) -> CoolrtOutput {
|
||||||
let nd = params.nd;
|
let nd = params.nd;
|
||||||
let nion = params.nion;
|
let nion = params.nion;
|
||||||
let nfreq = params.nfreq;
|
let nfreq = params.nfreq;
|
||||||
@ -408,9 +408,9 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_coolrt_pure_basic() {
|
fn test_coolrt_basic() {
|
||||||
let params = create_test_params();
|
let params = create_test_params();
|
||||||
let result = coolrt_pure(¶ms);
|
let result = coolrt(¶ms);
|
||||||
|
|
||||||
// 验证输出数组大小
|
// 验证输出数组大小
|
||||||
assert_eq!(result.clht1.len(), params.nd);
|
assert_eq!(result.clht1.len(), params.nd);
|
||||||
@ -429,9 +429,9 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_coolrt_pure_clht1_sum() {
|
fn test_coolrt_clht1_sum() {
|
||||||
let params = create_test_params();
|
let params = create_test_params();
|
||||||
let result = coolrt_pure(¶ms);
|
let result = coolrt(¶ms);
|
||||||
|
|
||||||
// CLHT1 应该是所有离子 (CLRAT - HTRAT) 的和
|
// CLHT1 应该是所有离子 (CLRAT - HTRAT) 的和
|
||||||
for id in 0..params.nd {
|
for id in 0..params.nd {
|
||||||
|
|||||||
@ -199,7 +199,7 @@ pub struct RadpreOutput {
|
|||||||
/// # 返回值
|
/// # 返回值
|
||||||
/// RADPRE 输出结果
|
/// RADPRE 输出结果
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn radpre_pure(
|
pub fn radpre(
|
||||||
config: &RadpreConfig,
|
config: &RadpreConfig,
|
||||||
model: &RadpreModelState,
|
model: &RadpreModelState,
|
||||||
freq: &mut RadpreFreqParamsMut,
|
freq: &mut RadpreFreqParamsMut,
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
//! 2. 调用 ELDENS 计算密度、能量和熵
|
//! 2. 调用 ELDENS 计算密度、能量和熵
|
||||||
//! 3. 使用有限差分计算导数
|
//! 3. 使用有限差分计算导数
|
||||||
|
|
||||||
use crate::tlusty::math::{eldens_pure, EldensConfig, EldensParams};
|
use crate::tlusty::math::{eldens, EldensConfig, EldensParams};
|
||||||
use crate::tlusty::math::StateParams;
|
use crate::tlusty::math::StateParams;
|
||||||
use crate::tlusty::state::constants::{BOLK, HMASS, UN};
|
use crate::tlusty::state::constants::{BOLK, HMASS, UN};
|
||||||
|
|
||||||
@ -202,7 +202,7 @@ pub fn trmder(params: &TrmderParams) -> TrmderOutput {
|
|||||||
anato_data: None,
|
anato_data: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = eldens_pure(&eldens_params, 0);
|
let result = eldens(&eldens_params, 0);
|
||||||
|
|
||||||
rhon[i] = result.rhoter;
|
rhon[i] = result.rhoter;
|
||||||
// 能量密度 = (1.5*P + 内能 + 3*Prad*(T'/T)^4) / rho
|
// 能量密度 = (1.5*P + 内能 + 3*Prad*(T'/T)^4) / rho
|
||||||
@ -377,7 +377,7 @@ mod tests {
|
|||||||
let result = trmder(¶ms);
|
let result = trmder(¶ms);
|
||||||
|
|
||||||
// 熵模式下应该也能正常工作
|
// 熵模式下应该也能正常工作
|
||||||
// 注意:由于 eldens_pure 的简化实现,熵值可能为零或很小
|
// 注意:由于 eldens 的简化实现,熵值可能为零或很小
|
||||||
// 这可能导致 heatcp 和 grdadb 不正确,所以我们只验证基本有限性
|
// 这可能导致 heatcp 和 grdadb 不正确,所以我们只验证基本有限性
|
||||||
assert!(result.rho > 0.0, "密度应该为正");
|
assert!(result.rho > 0.0, "密度应该为正");
|
||||||
assert!(result.heatcp.is_finite(), "定压比热应该是有限的");
|
assert!(result.heatcp.is_finite(), "定压比热应该是有限的");
|
||||||
|
|||||||
@ -241,7 +241,7 @@ pub struct Opacf1Result {
|
|||||||
///
|
///
|
||||||
/// # 返回值
|
/// # 返回值
|
||||||
/// 包含所有计算结果的结构体
|
/// 包含所有计算结果的结构体
|
||||||
pub fn rates1_pure(params: &mut Rates1Params) -> Rates1Output {
|
pub fn rates1(params: &mut Rates1Params) -> Rates1Output {
|
||||||
let nd = params.config.nd;
|
let nd = params.config.nd;
|
||||||
let nfreq = params.config.nfreq;
|
let nfreq = params.config.nfreq;
|
||||||
let ntrans = params.config.ntrans;
|
let ntrans = params.config.ntrans;
|
||||||
@ -665,7 +665,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rates1_pure_basic() {
|
fn test_rates1_basic() {
|
||||||
// 创建基本测试参数
|
// 创建基本测试参数
|
||||||
let config = Rates1Config {
|
let config = Rates1Config {
|
||||||
imor: 0,
|
imor: 0,
|
||||||
@ -780,7 +780,7 @@ mod tests {
|
|||||||
rosstd_contribute: None,
|
rosstd_contribute: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = rates1_pure(&mut params);
|
let output = rates1(&mut params);
|
||||||
|
|
||||||
// 验证输出维度
|
// 验证输出维度
|
||||||
assert_eq!(output.rru.len(), 1);
|
assert_eq!(output.rru.len(), 1);
|
||||||
|
|||||||
@ -22,7 +22,7 @@ use crate::tlusty::state::constants::{MDEPTH, MFREQ, MLEVEL};
|
|||||||
use crate::tlusty::state::model::ModelState;
|
use crate::tlusty::state::model::ModelState;
|
||||||
use crate::tlusty::state::config::TlustyConfig;
|
use crate::tlusty::state::config::TlustyConfig;
|
||||||
use crate::tlusty::state::atomic::AtomicData;
|
use crate::tlusty::state::atomic::AtomicData;
|
||||||
use crate::tlusty::io::resolv_pure;
|
use crate::tlusty::io::resolv;
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 配置参数
|
// 配置参数
|
||||||
@ -117,7 +117,7 @@ pub struct Accel2Output {
|
|||||||
///
|
///
|
||||||
/// # 注意
|
/// # 注意
|
||||||
/// 当 `need_resolv` 为 true 时,调用者需要执行 RESOLV 重新计算
|
/// 当 `need_resolv` 为 true 时,调用者需要执行 RESOLV 重新计算
|
||||||
pub fn accel2_pure(params: &mut Accel2Params) -> Accel2Output {
|
pub fn accel2(params: &mut Accel2Params) -> Accel2Output {
|
||||||
// 标记 RESOLV 依赖(Fortran 在加速后调用 RESOLV)
|
// 标记 RESOLV 依赖(Fortran 在加速后调用 RESOLV)
|
||||||
// 标记依赖(确保编译器知道 accel2 依赖 resolv_pure)
|
// 标记依赖(确保编译器知道 accel2 依赖 resolv_pure)
|
||||||
let _ = "accel2 depends on resolv_pure";
|
let _ = "accel2 depends on resolv_pure";
|
||||||
@ -287,7 +287,7 @@ pub fn accel2_io<W: std::io::Write>(
|
|||||||
params: &mut Accel2Params,
|
params: &mut Accel2Params,
|
||||||
writer: &mut W,
|
writer: &mut W,
|
||||||
) -> Accel2Output {
|
) -> Accel2Output {
|
||||||
let result = accel2_pure(params);
|
let result = accel2(params);
|
||||||
|
|
||||||
if result.accelerated {
|
if result.accelerated {
|
||||||
let _ = writeln!(writer, " **** ACCEL2, ITER={}", params.config.iter);
|
let _ = writeln!(writer, " **** ACCEL2, ITER={}", params.config.iter);
|
||||||
@ -343,7 +343,7 @@ mod tests {
|
|||||||
psy3: &mut psy3,
|
psy3: &mut psy3,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = accel2_pure(&mut params);
|
let result = accel2(&mut params);
|
||||||
|
|
||||||
assert!(!result.accelerated);
|
assert!(!result.accelerated);
|
||||||
assert!(!result.need_resolv);
|
assert!(!result.need_resolv);
|
||||||
@ -380,7 +380,7 @@ mod tests {
|
|||||||
psy3: &mut psy3,
|
psy3: &mut psy3,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = accel2_pure(&mut params);
|
let result = accel2(&mut params);
|
||||||
|
|
||||||
// 应该存储到 PSY3,但不执行加速
|
// 应该存储到 PSY3,但不执行加速
|
||||||
assert!(!result.accelerated);
|
assert!(!result.accelerated);
|
||||||
@ -426,7 +426,7 @@ mod tests {
|
|||||||
psy3: &mut psy3,
|
psy3: &mut psy3,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = accel2_pure(&mut params);
|
let result = accel2(&mut params);
|
||||||
|
|
||||||
// 应该执行加速
|
// 应该执行加速
|
||||||
assert!(result.accelerated);
|
assert!(result.accelerated);
|
||||||
@ -470,7 +470,7 @@ mod tests {
|
|||||||
psy3: &mut psy3,
|
psy3: &mut psy3,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = accel2_pure(&mut params);
|
let result = accel2(&mut params);
|
||||||
|
|
||||||
// ipng != 0,应该只做移位,不执行加速
|
// ipng != 0,应该只做移位,不执行加速
|
||||||
// iter - iacc = 5 - 3 = 2, iacd = 2, ipng = 2 % 2 = 0
|
// iter - iacc = 5 - 3 = 2, iacd = 2, ipng = 2 % 2 = 0
|
||||||
@ -514,7 +514,7 @@ mod tests {
|
|||||||
psy3: &mut psy3,
|
psy3: &mut psy3,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = accel2_pure(&mut params);
|
let result = accel2(&mut params);
|
||||||
|
|
||||||
assert!(result.accelerated);
|
assert!(result.accelerated);
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
//! - 使用 ELDENS 计算电子密度
|
//! - 使用 ELDENS 计算电子密度
|
||||||
//! - 使用 PGSET 迭代计算气体压力
|
//! - 使用 PGSET 迭代计算气体压力
|
||||||
|
|
||||||
use crate::tlusty::math::{eldens_pure, EldensConfig, EldensOutput, EldensParams};
|
use crate::tlusty::math::{eldens, EldensConfig, EldensOutput, EldensParams};
|
||||||
use crate::tlusty::math::{pgset, PgsetParams, PgsetOutput};
|
use crate::tlusty::math::{pgset, PgsetParams, PgsetOutput};
|
||||||
use crate::tlusty::state::constants::{BOLK, HALF, TWO, UN};
|
use crate::tlusty::state::constants::{BOLK, HALF, TWO, UN};
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ pub struct RybchnOutput {
|
|||||||
///
|
///
|
||||||
/// # 返回值
|
/// # 返回值
|
||||||
/// 包含更新后的温度、密度、压力等的输出结构体
|
/// 包含更新后的温度、密度、压力等的输出结构体
|
||||||
pub fn rybchn_pure(params: &RybchnParams) -> RybchnOutput {
|
pub fn rybchn(params: &RybchnParams) -> RybchnOutput {
|
||||||
let nd = params.nd;
|
let nd = params.nd;
|
||||||
let config = ¶ms.config;
|
let config = ¶ms.config;
|
||||||
|
|
||||||
@ -400,7 +400,7 @@ fn compute_eldens(params: &RybchnParams, id: usize, t: f64, an: f64) -> EldensOu
|
|||||||
anato_data: None,
|
anato_data: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
eldens_pure(&eldens_params, 1)
|
eldens(&eldens_params, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ERFCX 近似函数(缩放互补误差函数)
|
/// ERFCX 近似函数(缩放互补误差函数)
|
||||||
@ -463,7 +463,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_rybchn_basic() {
|
fn test_rybchn_basic() {
|
||||||
let params = create_test_params();
|
let params = create_test_params();
|
||||||
let output = rybchn_pure(¶ms);
|
let output = rybchn(¶ms);
|
||||||
|
|
||||||
// 检查输出维度
|
// 检查输出维度
|
||||||
assert_eq!(output.temp.len(), params.nd);
|
assert_eq!(output.temp.len(), params.nd);
|
||||||
@ -488,7 +488,7 @@ mod tests {
|
|||||||
// 设置较大的温度变化
|
// 设置较大的温度变化
|
||||||
params.changt = vec![0.5, 0.5, 0.5, 0.5, 0.5];
|
params.changt = vec![0.5, 0.5, 0.5, 0.5, 0.5];
|
||||||
|
|
||||||
let output = rybchn_pure(¶ms);
|
let output = rybchn(¶ms);
|
||||||
|
|
||||||
// 检查温度变化被限制
|
// 检查温度变化被限制
|
||||||
// 由于 dplp = dpsilt - 1 = 2 - 1 = 1, dplm = 1/dpsilt - 1 = 0.5 - 1 = -0.5
|
// 由于 dplp = dpsilt - 1 = 2 - 1 = 1, dplm = 1/dpsilt - 1 = 0.5 - 1 = -0.5
|
||||||
|
|||||||
@ -263,7 +263,7 @@ pub struct RybsolOutput {
|
|||||||
/// ...
|
/// ...
|
||||||
/// END
|
/// END
|
||||||
/// ```
|
/// ```
|
||||||
pub fn rybsol_pure<F_OPACTR, F_RTEFR1, F_ALIFR1, F_ROSSTD, F_RYBMAT, F_RYBENE, F_RYBCHN>(
|
pub fn rybsol<F_OPACTR, F_RTEFR1, F_ALIFR1, F_ROSSTD, F_RYBMAT, F_RYBENE, F_RYBCHN>(
|
||||||
params: &mut RybsolParams,
|
params: &mut RybsolParams,
|
||||||
rybmtx: &mut RybmtxWork,
|
rybmtx: &mut RybmtxWork,
|
||||||
// 回调函数
|
// 回调函数
|
||||||
@ -711,7 +711,7 @@ mod tests {
|
|||||||
let mut rybmtx = RybmtxWork::new();
|
let mut rybmtx = RybmtxWork::new();
|
||||||
|
|
||||||
// 使用空回调函数
|
// 使用空回调函数
|
||||||
let output = rybsol_pure(
|
let output = rybsol(
|
||||||
&mut params,
|
&mut params,
|
||||||
&mut rybmtx,
|
&mut rybmtx,
|
||||||
|_ij| {}, // opactr
|
|_ij| {}, // opactr
|
||||||
|
|||||||
@ -360,7 +360,7 @@ pub fn back_solution_step(
|
|||||||
/// 求解线性系统。
|
/// 求解线性系统。
|
||||||
///
|
///
|
||||||
/// 完整版本需要集成 matgen、matinv、wnstor 等。
|
/// 完整版本需要集成 matgen、matinv、wnstor 等。
|
||||||
pub fn solves_pure(
|
pub fn solves(
|
||||||
nn: usize,
|
nn: usize,
|
||||||
nd: usize,
|
nd: usize,
|
||||||
nfreqe: usize,
|
nfreqe: usize,
|
||||||
@ -515,10 +515,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_solves_pure_basic() {
|
fn test_solves_basic() {
|
||||||
let kant = vec![0; 200];
|
let kant = vec![0; 200];
|
||||||
|
|
||||||
let output = solves_pure(
|
let output = solves(
|
||||||
10, // nn
|
10, // nn
|
||||||
5, // nd
|
5, // nd
|
||||||
3, // nfreqe
|
3, // nfreqe
|
||||||
|
|||||||
@ -7,8 +7,8 @@
|
|||||||
//! - 使用迭代方法求解电荷守恒方程
|
//! - 使用迭代方法求解电荷守恒方程
|
||||||
//! - 与统计平衡方程耦合
|
//! - 与统计平衡方程耦合
|
||||||
|
|
||||||
use crate::tlusty::math::{moleq_pure, MoleqOutput, MoleqParams};
|
use crate::tlusty::math::{moleq, MoleqOutput, MoleqParams};
|
||||||
use crate::tlusty::math::{state_pure, StateOutput, StateParams};
|
use crate::tlusty::math::{state, StateOutput, StateParams};
|
||||||
use crate::tlusty::math::{steqeq_pure, SteqeqConfig, SteqeqParams};
|
use crate::tlusty::math::{steqeq_pure, SteqeqConfig, SteqeqParams};
|
||||||
use crate::tlusty::math::wnstor;
|
use crate::tlusty::math::wnstor;
|
||||||
use crate::tlusty::state::constants::{HALF, MDEPTH, MLEVEL, UN};
|
use crate::tlusty::state::constants::{HALF, MDEPTH, MLEVEL, UN};
|
||||||
@ -125,7 +125,7 @@ pub struct ElcorOutput {
|
|||||||
///
|
///
|
||||||
/// # 返回值
|
/// # 返回值
|
||||||
/// 包含更新后的电子密度和密度
|
/// 包含更新后的电子密度和密度
|
||||||
pub fn elcor_pure(params: &ElcorParams) -> ElcorOutput {
|
pub fn elcor(params: &ElcorParams) -> ElcorOutput {
|
||||||
let config = ¶ms.config;
|
let config = ¶ms.config;
|
||||||
|
|
||||||
// 检查 ioptab 标志
|
// 检查 ioptab 标志
|
||||||
@ -262,7 +262,7 @@ fn compute_qq(params: &ElcorParams, _id: usize, t: f64, an: f64, ane: f64, dens:
|
|||||||
if config.ifmol == 0 || t >= config.tmolim {
|
if config.ifmol == 0 || t >= config.tmolim {
|
||||||
// 使用 STATE 函数计算电荷
|
// 使用 STATE 函数计算电荷
|
||||||
if let Some(state_params) = ¶ms.state_params {
|
if let Some(state_params) = ¶ms.state_params {
|
||||||
let state_out = state_pure(state_params);
|
let state_out = state(state_params);
|
||||||
let q = state_out.q;
|
let q = state_out.q;
|
||||||
|
|
||||||
let qq = if config.ioptab > 0 {
|
let qq = if config.ioptab > 0 {
|
||||||
@ -277,7 +277,7 @@ fn compute_qq(params: &ElcorParams, _id: usize, t: f64, an: f64, ane: f64, dens:
|
|||||||
// 使用 MOLEQ 分子平衡计算
|
// 使用 MOLEQ 分子平衡计算
|
||||||
// f2r_depends: MOLEQ
|
// f2r_depends: MOLEQ
|
||||||
if let Some(moleq_params) = ¶ms.moleq_params {
|
if let Some(moleq_params) = ¶ms.moleq_params {
|
||||||
let _moleq_out = moleq_pure(moleq_params);
|
let _moleq_out = moleq(moleq_params);
|
||||||
}
|
}
|
||||||
params.qadd
|
params.qadd
|
||||||
}
|
}
|
||||||
@ -348,7 +348,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_elcor_basic() {
|
fn test_elcor_basic() {
|
||||||
let params = create_test_params();
|
let params = create_test_params();
|
||||||
let output = elcor_pure(¶ms);
|
let output = elcor(¶ms);
|
||||||
|
|
||||||
// 检查输出
|
// 检查输出
|
||||||
println!("ELEC: {}", output.elec);
|
println!("ELEC: {}", output.elec);
|
||||||
@ -366,7 +366,7 @@ mod tests {
|
|||||||
let mut params = create_test_params();
|
let mut params = create_test_params();
|
||||||
params.config.ioptab = 1; // 应该跳过计算
|
params.config.ioptab = 1; // 应该跳过计算
|
||||||
|
|
||||||
let output = elcor_pure(¶ms);
|
let output = elcor(¶ms);
|
||||||
|
|
||||||
// 检查跳过后的输出等于输入
|
// 检查跳过后的输出等于输入
|
||||||
assert!((output.elec - params.elec).abs() < 1e-10);
|
assert!((output.elec - params.elec).abs() < 1e-10);
|
||||||
|
|||||||
@ -250,7 +250,7 @@ where
|
|||||||
///
|
///
|
||||||
/// # 返回
|
/// # 返回
|
||||||
/// 计算结果
|
/// 计算结果
|
||||||
pub fn greyd_pure<F_Rhonen, F_Wnstor, F_Steqeq, F_Opacf0, F_Meanop>(
|
pub fn greyd<F_Rhonen, F_Wnstor, F_Steqeq, F_Opacf0, F_Meanop>(
|
||||||
config: &GreydConfig,
|
config: &GreydConfig,
|
||||||
state: &mut GreydState,
|
state: &mut GreydState,
|
||||||
rhonen_fn: &mut F_Rhonen,
|
rhonen_fn: &mut F_Rhonen,
|
||||||
@ -385,7 +385,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_greyd_pure() {
|
fn test_greyd() {
|
||||||
let config = GreydConfig {
|
let config = GreydConfig {
|
||||||
teff: 35000.0,
|
teff: 35000.0,
|
||||||
qgrav: 1e4,
|
qgrav: 1e4,
|
||||||
@ -420,7 +420,7 @@ mod tests {
|
|||||||
(1e-5, 1e-3) // 返回简化的不透明度
|
(1e-5, 1e-3) // 返回简化的不透明度
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = greyd_pure(
|
let output = greyd(
|
||||||
&config,
|
&config,
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut rhonen_fn,
|
&mut rhonen_fn,
|
||||||
|
|||||||
@ -262,7 +262,7 @@ impl LucyState {
|
|||||||
/// ! 积分流体静力学平衡
|
/// ! 积分流体静力学平衡
|
||||||
/// END
|
/// END
|
||||||
/// ```
|
/// ```
|
||||||
pub fn lucy_pure(
|
pub fn lucy(
|
||||||
config: &LucyConfig,
|
config: &LucyConfig,
|
||||||
model: &LucyModelParams,
|
model: &LucyModelParams,
|
||||||
opacfl_data: &[OpacflPointData],
|
opacfl_data: &[OpacflPointData],
|
||||||
@ -720,7 +720,7 @@ mod tests {
|
|||||||
..config.clone()
|
..config.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = lucy_pure(&config_zero, &model, &opacfl_data, &rad1_data);
|
let output = lucy(&config_zero, &model, &opacfl_data, &rad1_data);
|
||||||
assert_eq!(output.ilucy, 0);
|
assert_eq!(output.ilucy, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -164,7 +164,7 @@ pub struct CubconData {
|
|||||||
/// ...
|
/// ...
|
||||||
/// END
|
/// END
|
||||||
/// ```
|
/// ```
|
||||||
pub fn temcor_pure(params: &mut TemcorParams) -> TemcorOutput {
|
pub fn temcor(params: &mut TemcorParams) -> TemcorOutput {
|
||||||
let nd = params.nd;
|
let nd = params.nd;
|
||||||
let mut depth_results = Vec::new();
|
let mut depth_results = Vec::new();
|
||||||
let mut any_correction = false;
|
let mut any_correction = false;
|
||||||
@ -526,7 +526,7 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut params = TestParamsBuilder::new(50).config(config).build();
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
||||||
let output = temcor_pure(&mut params);
|
let output = temcor(&mut params);
|
||||||
|
|
||||||
// iconv <= 0 且 indl == 0 时应该跳过
|
// iconv <= 0 且 indl == 0 时应该跳过
|
||||||
assert_eq!(output.depth_results.len(), 0);
|
assert_eq!(output.depth_results.len(), 0);
|
||||||
@ -541,7 +541,7 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut params = TestParamsBuilder::new(50).config(config).build();
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
||||||
let output = temcor_pure(&mut params);
|
let output = temcor(&mut params);
|
||||||
|
|
||||||
// 应该处理所有深度点
|
// 应该处理所有深度点
|
||||||
assert_eq!(output.depth_results.len(), 49); // id from 2 to nd
|
assert_eq!(output.depth_results.len(), 49); // id from 2 to nd
|
||||||
@ -555,7 +555,7 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut params = TestParamsBuilder::new(50).config(config).build();
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
||||||
let output = temcor_pure(&mut params);
|
let output = temcor(&mut params);
|
||||||
|
|
||||||
// 盘模式应该正常工作
|
// 盘模式应该正常工作
|
||||||
assert_eq!(output.depth_results.len(), 49);
|
assert_eq!(output.depth_results.len(), 49);
|
||||||
|
|||||||
@ -234,7 +234,7 @@ impl FactrsData {
|
|||||||
/// ...
|
/// ...
|
||||||
/// END
|
/// END
|
||||||
/// ```
|
/// ```
|
||||||
pub fn temper_pure(
|
pub fn temper(
|
||||||
params: &mut TemperParams,
|
params: &mut TemperParams,
|
||||||
prsaux: &mut PrsauxData,
|
prsaux: &mut PrsauxData,
|
||||||
flxaux: &mut FlxauxData,
|
flxaux: &mut FlxauxData,
|
||||||
@ -588,7 +588,7 @@ mod tests {
|
|||||||
flxaux.t4 = 35000.0_f64.powi(4);
|
flxaux.t4 = 35000.0_f64.powi(4);
|
||||||
let factrs = FactrsData::new(50);
|
let factrs = FactrsData::new(50);
|
||||||
|
|
||||||
let output = temper_pure(&mut params, &mut prsaux, &mut flxaux, &factrs);
|
let output = temper(&mut params, &mut prsaux, &mut flxaux, &factrs);
|
||||||
|
|
||||||
// 验证基本输出
|
// 验证基本输出
|
||||||
assert!(output.t > 0.0);
|
assert!(output.t > 0.0);
|
||||||
@ -609,7 +609,7 @@ mod tests {
|
|||||||
let mut flxaux = FlxauxData::default();
|
let mut flxaux = FlxauxData::default();
|
||||||
let factrs = FactrsData::new(50);
|
let factrs = FactrsData::new(50);
|
||||||
|
|
||||||
let output = temper_pure(&mut params, &mut prsaux, &mut flxaux, &factrs);
|
let output = temper(&mut params, &mut prsaux, &mut flxaux, &factrs);
|
||||||
|
|
||||||
// itgr > 1 且 itgmax > 0 时应该只计算电子密度
|
// itgr > 1 且 itgmax > 0 时应该只计算电子密度
|
||||||
assert!(output.t > 0.0);
|
assert!(output.t > 0.0);
|
||||||
@ -626,7 +626,7 @@ mod tests {
|
|||||||
flxaux.t4 = 35000.0_f64.powi(4);
|
flxaux.t4 = 35000.0_f64.powi(4);
|
||||||
let factrs = FactrsData::new(50);
|
let factrs = FactrsData::new(50);
|
||||||
|
|
||||||
let output = temper_pure(&mut params, &mut prsaux, &mut flxaux, &factrs);
|
let output = temper(&mut params, &mut prsaux, &mut flxaux, &factrs);
|
||||||
|
|
||||||
// 第一个深度点应该设置 HG1, HR1, RR1
|
// 第一个深度点应该设置 HG1, HR1, RR1
|
||||||
assert!(prsaux.hg1 >= 0.0);
|
assert!(prsaux.hg1 >= 0.0);
|
||||||
|
|||||||
@ -133,7 +133,7 @@ pub struct ChangeOutput {
|
|||||||
///
|
///
|
||||||
/// # 返回值
|
/// # 返回值
|
||||||
/// 包含更新后的粒子数
|
/// 包含更新后的粒子数
|
||||||
pub fn change_pure(params: &ChangeParams) -> ChangeOutput {
|
pub fn change(params: &ChangeParams) -> ChangeOutput {
|
||||||
let nd = params.nd;
|
let nd = params.nd;
|
||||||
let nlevel = params.nlevel;
|
let nlevel = params.nlevel;
|
||||||
let config = ¶ms.config;
|
let config = ¶ms.config;
|
||||||
@ -399,7 +399,7 @@ mod tests {
|
|||||||
steqeq_fn: None,
|
steqeq_fn: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = change_pure(¶ms);
|
let output = change(¶ms);
|
||||||
|
|
||||||
// 检查输出维度
|
// 检查输出维度
|
||||||
assert_eq!(output.popul.len(), nlevel);
|
assert_eq!(output.popul.len(), nlevel);
|
||||||
|
|||||||
@ -204,7 +204,7 @@ impl Default for NewdmModelState {
|
|||||||
/// # 注意
|
/// # 注意
|
||||||
///
|
///
|
||||||
/// 完整实现需要调用 TEMPER 和 HESOLV,这里只实现网格计算部分。
|
/// 完整实现需要调用 TEMPER 和 HESOLV,这里只实现网格计算部分。
|
||||||
pub fn newdm_pure(
|
pub fn newdm(
|
||||||
config: &NewdmConfig,
|
config: &NewdmConfig,
|
||||||
state: &mut NewdmModelState,
|
state: &mut NewdmModelState,
|
||||||
_prs_aux: &mut PrsAux,
|
_prs_aux: &mut PrsAux,
|
||||||
@ -545,7 +545,7 @@ mod tests {
|
|||||||
let mut prs_aux = PrsAux::default();
|
let mut prs_aux = PrsAux::default();
|
||||||
let mut factrs = Factrs::default();
|
let mut factrs = Factrs::default();
|
||||||
|
|
||||||
newdm_pure(&config, &mut state, &mut prs_aux, &mut factrs);
|
newdm(&config, &mut state, &mut prs_aux, &mut factrs);
|
||||||
|
|
||||||
// 验证基本属性:dm[0] 应该是正数(因为它是从原始网格插值来的)
|
// 验证基本属性:dm[0] 应该是正数(因为它是从原始网格插值来的)
|
||||||
// 注意:由于我们使用简化的测试网格,tauros 可能很小
|
// 注意:由于我们使用简化的测试网格,tauros 可能很小
|
||||||
@ -584,7 +584,7 @@ mod tests {
|
|||||||
let mut prs_aux = PrsAux::default();
|
let mut prs_aux = PrsAux::default();
|
||||||
let mut factrs = Factrs::default();
|
let mut factrs = Factrs::default();
|
||||||
|
|
||||||
newdm_pure(&config, &mut state, &mut prs_aux, &mut factrs);
|
newdm(&config, &mut state, &mut prs_aux, &mut factrs);
|
||||||
|
|
||||||
// 验证 theta 在有效范围内
|
// 验证 theta 在有效范围内
|
||||||
// 注意:theta 的范围取决于 fractv 参数
|
// 注意:theta 的范围取决于 fractv 参数
|
||||||
|
|||||||
@ -177,7 +177,7 @@ impl Default for NewdmtModelState {
|
|||||||
/// # 注意
|
/// # 注意
|
||||||
///
|
///
|
||||||
/// 完整实现需要调用 TEMPER 和 HESOLV,这里只实现网格计算部分。
|
/// 完整实现需要调用 TEMPER 和 HESOLV,这里只实现网格计算部分。
|
||||||
pub fn newdmt_pure(
|
pub fn newdmt(
|
||||||
config: &NewdmtConfig,
|
config: &NewdmtConfig,
|
||||||
state: &mut NewdmtModelState,
|
state: &mut NewdmtModelState,
|
||||||
prs_aux: &mut NewdmtPrsAux,
|
prs_aux: &mut NewdmtPrsAux,
|
||||||
@ -527,7 +527,7 @@ mod tests {
|
|||||||
let mut prs_aux = NewdmtPrsAux::default();
|
let mut prs_aux = NewdmtPrsAux::default();
|
||||||
let mut factrs = NewdmtFactrs::default();
|
let mut factrs = NewdmtFactrs::default();
|
||||||
|
|
||||||
newdmt_pure(&config, &mut state, &mut prs_aux, &mut factrs);
|
newdmt(&config, &mut state, &mut prs_aux, &mut factrs);
|
||||||
|
|
||||||
// 验证基本属性
|
// 验证基本属性
|
||||||
assert!(state.dm[0] > 0.0, "dm[0] should be positive");
|
assert!(state.dm[0] > 0.0, "dm[0] should be positive");
|
||||||
@ -568,7 +568,7 @@ mod tests {
|
|||||||
let mut prs_aux = NewdmtPrsAux::default();
|
let mut prs_aux = NewdmtPrsAux::default();
|
||||||
let mut factrs = NewdmtFactrs::default();
|
let mut factrs = NewdmtFactrs::default();
|
||||||
|
|
||||||
newdmt_pure(&config, &mut state, &mut prs_aux, &mut factrs);
|
newdmt(&config, &mut state, &mut prs_aux, &mut factrs);
|
||||||
|
|
||||||
// 验证 theta 在有效范围内
|
// 验证 theta 在有效范围内
|
||||||
for i in 0..config.nd {
|
for i in 0..config.nd {
|
||||||
@ -614,7 +614,7 @@ mod tests {
|
|||||||
let mut prs_aux = NewdmtPrsAux::default();
|
let mut prs_aux = NewdmtPrsAux::default();
|
||||||
let mut factrs = NewdmtFactrs::default();
|
let mut factrs = NewdmtFactrs::default();
|
||||||
|
|
||||||
newdmt_pure(&config, &mut state, &mut prs_aux, &mut factrs);
|
newdmt(&config, &mut state, &mut prs_aux, &mut factrs);
|
||||||
|
|
||||||
// 验证 tauros 是单调递增的
|
// 验证 tauros 是单调递增的
|
||||||
for i in 1..config.nd {
|
for i in 1..config.nd {
|
||||||
|
|||||||
@ -97,7 +97,7 @@ pub struct SabolfOutput {
|
|||||||
///
|
///
|
||||||
/// # 返回
|
/// # 返回
|
||||||
/// Saha-Boltzmann 因子和上能级求和
|
/// Saha-Boltzmann 因子和上能级求和
|
||||||
pub fn sabolf_pure(params: &mut SabolfParams) -> SabolfOutput {
|
pub fn sabolf(params: &mut SabolfParams) -> SabolfOutput {
|
||||||
// 如果 ioptab < 0,返回空数组
|
// 如果 ioptab < 0,返回空数组
|
||||||
if params.ioptab < 0 {
|
if params.ioptab < 0 {
|
||||||
let nlevels = params.enion.len();
|
let nlevels = params.enion.len();
|
||||||
@ -269,7 +269,7 @@ pub fn sabolf_pure(params: &mut SabolfParams) -> SabolfOutput {
|
|||||||
};
|
};
|
||||||
let xmx = xmax * qz.sqrt();
|
let xmx = xmax * qz.sqrt();
|
||||||
|
|
||||||
use crate::tlusty::math::partition::{partf_pure, PartfParams, PartfMode};
|
use crate::tlusty::math::partition::{partf, PartfParams, PartfMode};
|
||||||
let partf_params = PartfParams {
|
let partf_params = PartfParams {
|
||||||
iat,
|
iat,
|
||||||
izi: params.iz[ion_idx],
|
izi: params.iz[ion_idx],
|
||||||
@ -279,7 +279,7 @@ pub fn sabolf_pure(params: &mut SabolfParams) -> SabolfOutput {
|
|||||||
mode: PartfMode::Standard,
|
mode: PartfMode::Standard,
|
||||||
iirwin: 0,
|
iirwin: 0,
|
||||||
};
|
};
|
||||||
let partf_result = partf_pure(&partf_params);
|
let partf_result = partf(&partf_params);
|
||||||
let u_pf = partf_result.u;
|
let u_pf = partf_result.u;
|
||||||
let dut_pf = partf_result.dut;
|
let dut_pf = partf_result.dut;
|
||||||
let dun_pf = partf_result.dun;
|
let dun_pf = partf_result.dun;
|
||||||
@ -445,7 +445,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_sabolf_basic() {
|
fn test_sabolf_basic() {
|
||||||
let mut params = create_test_params();
|
let mut params = create_test_params();
|
||||||
let result = sabolf_pure(&mut params);
|
let result = sabolf(&mut params);
|
||||||
|
|
||||||
assert_eq!(result.sbf.len(), 9);
|
assert_eq!(result.sbf.len(), 9);
|
||||||
assert_eq!(result.usum.len(), 3);
|
assert_eq!(result.usum.len(), 3);
|
||||||
@ -458,7 +458,7 @@ mod tests {
|
|||||||
fn test_sabolf_ioptab_negative() {
|
fn test_sabolf_ioptab_negative() {
|
||||||
let mut params = create_test_params();
|
let mut params = create_test_params();
|
||||||
params.ioptab = -1;
|
params.ioptab = -1;
|
||||||
let result = sabolf_pure(&mut params);
|
let result = sabolf(&mut params);
|
||||||
|
|
||||||
for &sb in &result.sbf {
|
for &sb in &result.sbf {
|
||||||
assert!((sb - 0.0).abs() < 1e-10);
|
assert!((sb - 0.0).abs() < 1e-10);
|
||||||
@ -470,7 +470,7 @@ mod tests {
|
|||||||
let mut params = create_test_params();
|
let mut params = create_test_params();
|
||||||
params.t = 50000.0;
|
params.t = 50000.0;
|
||||||
params.ane = 1.0e15;
|
params.ane = 1.0e15;
|
||||||
let result = sabolf_pure(&mut params);
|
let result = sabolf(&mut params);
|
||||||
|
|
||||||
for &sb in &result.sbf {
|
for &sb in &result.sbf {
|
||||||
assert!(sb >= 0.0, "SBF should be non-negative");
|
assert!(sb >= 0.0, "SBF should be non-negative");
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
//! - MODE=3: 类似 MODE=2,但计算导数
|
//! - MODE=3: 类似 MODE=2,但计算导数
|
||||||
|
|
||||||
use crate::tlusty::state::constants::*;
|
use crate::tlusty::state::constants::*;
|
||||||
use crate::tlusty::math::{partf_pure, PartfParams, PartfMode};
|
use crate::tlusty::math::{partf, PartfParams, PartfMode};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 常量
|
// 常量
|
||||||
@ -474,7 +474,7 @@ pub struct StateParams<'a> {
|
|||||||
///
|
///
|
||||||
/// # 返回
|
/// # 返回
|
||||||
/// STATE 输出结构
|
/// STATE 输出结构
|
||||||
pub fn state_pure(params: &StateParams) -> StateOutput {
|
pub fn state(params: &StateParams) -> StateOutput {
|
||||||
let mode = params.mode;
|
let mode = params.mode;
|
||||||
let id = params.id;
|
let id = params.id;
|
||||||
let t = params.t;
|
let t = params.t;
|
||||||
@ -497,7 +497,7 @@ pub fn state_pure(params: &StateParams) -> StateOutput {
|
|||||||
|
|
||||||
// 初始化 Opacity Project 离子分数(如果需要)
|
// 初始化 Opacity Project 离子分数(如果需要)
|
||||||
if params.ifoppf > 0 {
|
if params.ifoppf > 0 {
|
||||||
use crate::tlusty::math::{opfrac_pure, OpfracParams, PfOptB};
|
use crate::tlusty::math::{opfrac, OpfracParams, PfOptB};
|
||||||
let pfoptb = PfOptB::new();
|
let pfoptb = PfOptB::new();
|
||||||
let opfrac_params = OpfracParams {
|
let opfrac_params = OpfracParams {
|
||||||
iat: 0,
|
iat: 0,
|
||||||
@ -505,7 +505,7 @@ pub fn state_pure(params: &StateParams) -> StateOutput {
|
|||||||
t: params.t,
|
t: params.t,
|
||||||
ane: params.ane,
|
ane: params.ane,
|
||||||
};
|
};
|
||||||
let _ = opfrac_pure(&opfrac_params, &pfoptb);
|
let _ = opfrac(&opfrac_params, &pfoptb);
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@ -570,7 +570,7 @@ pub fn state_pure(params: &StateParams) -> StateOutput {
|
|||||||
iirwin: 0,
|
iirwin: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let pf_result = partf_pure(&partf_params);
|
let pf_result = partf(&partf_params);
|
||||||
let um = pf_result.u;
|
let um = pf_result.u;
|
||||||
let dutm = pf_result.dut;
|
let dutm = pf_result.dut;
|
||||||
let dunm = pf_result.dun;
|
let dunm = pf_result.dun;
|
||||||
@ -605,7 +605,7 @@ pub fn state_pure(params: &StateParams) -> StateOutput {
|
|||||||
iirwin: 0,
|
iirwin: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let pf_result_j = partf_pure(&partf_params_j);
|
let pf_result_j = partf(&partf_params_j);
|
||||||
let u = pf_result_j.u;
|
let u = pf_result_j.u;
|
||||||
let dut = pf_result_j.dut;
|
let dut = pf_result_j.dut;
|
||||||
let dun = pf_result_j.dun;
|
let dun = pf_result_j.dun;
|
||||||
@ -862,7 +862,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_state_pure_basic() {
|
fn test_state_basic() {
|
||||||
// 创建测试参数
|
// 创建测试参数
|
||||||
let abndd = vec![1.0; MATOM]; // 丰度
|
let abndd = vec![1.0; MATOM]; // 丰度
|
||||||
let ioniz = vec![2; MATOM]; // 电离级
|
let ioniz = vec![2; MATOM]; // 电离级
|
||||||
@ -887,7 +887,7 @@ mod tests {
|
|||||||
ifoppf: 0,
|
ifoppf: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = state_pure(¶ms);
|
let result = state(¶ms);
|
||||||
|
|
||||||
// 验证结果
|
// 验证结果
|
||||||
assert!(result.q >= 0.0, "Total charge should be non-negative");
|
assert!(result.q >= 0.0, "Total charge should be non-negative");
|
||||||
@ -924,7 +924,7 @@ mod tests {
|
|||||||
ifoppf: 0,
|
ifoppf: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = state_pure(¶ms);
|
let result = state(¶ms);
|
||||||
assert!((result.qm - qm_expected).abs() < 1e-20, "H- charge mismatch");
|
assert!((result.qm - qm_expected).abs() < 1e-20, "H- charge mismatch");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user