重构2
This commit is contained in:
parent
8e21522d2d
commit
f286ddacfe
337
FORTRAN_TRACKING.md
Normal file
337
FORTRAN_TRACKING.md
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
# Fortran 重构追踪表
|
||||||
|
|
||||||
|
> 自动生成,请勿手动修改。运行 `python3 scripts/generate_tracking.py > FORTRAN_TRACKING.md` 更新。
|
||||||
|
|
||||||
|
## 统计
|
||||||
|
|
||||||
|
| 指标 | 数量 |
|
||||||
|
|------|------|
|
||||||
|
| 总单元数 | 304 |
|
||||||
|
| 已完成 | 113 |
|
||||||
|
| 待处理 | 191 |
|
||||||
|
| 纯函数 | 67 |
|
||||||
|
| 有文件 I/O | 117 |
|
||||||
|
| 完成率 | 37.2% |
|
||||||
|
|
||||||
|
## 状态说明
|
||||||
|
|
||||||
|
- ✅ `done` - 已重构为 Rust
|
||||||
|
- ⬜ `pending` - 待处理
|
||||||
|
- 🔄 `in_progress` - 进行中
|
||||||
|
- ⏭️ `skip` - 跳过 (I/O 依赖或暂不处理)
|
||||||
|
|
||||||
|
## 类型说明
|
||||||
|
|
||||||
|
- **纯函数**: 无 COMMON 依赖、无文件 I/O、无外部调用依赖
|
||||||
|
- **COMMON 依赖**: 需要状态结构体
|
||||||
|
- **调用依赖**: 调用其他子程序,需要先实现依赖
|
||||||
|
|
||||||
|
## 完整追踪表
|
||||||
|
|
||||||
|
|
||||||
|
| Fortran 文件 | 单元名 | 类型 | 纯函数 | COMMON 依赖 | 调用依赖 | I/O | Rust 模块 | 状态 |
|
||||||
|
|-------------|--------|------|--------|-------------|----------|-----|-----------|------|
|
||||||
|
| _unnamed_block_data_.f | C | BLOCK DATA | | BASICS, ATOMIC | | | | ⬜ |
|
||||||
|
| accel2.f | ACCEL2 | SUBROUTINE | | BASICS, ITERAT, MODELQ | | 📁 | | ⬜ |
|
||||||
|
| accelp.f | ACCELP | SUBROUTINE | | BASICS, MODELQ, ITERAT, POPULS | | 📁 | | ⬜ |
|
||||||
|
| alifr1.f | ALIFR1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR | ALIFR3 | | | ⬜ |
|
||||||
|
| alifr3.f | ALIFR3 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR | | | alifr3.rs | ✅ |
|
||||||
|
| alifr6.f | ALIFR6 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR | | | alifr6.rs | ✅ |
|
||||||
|
| alifrk.f | ALIFRK | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR | | | alifrk.rs | ✅ |
|
||||||
|
| alisk1.f | ALISK1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT | ALIFRK, RTEFR1, OPACF1, ROSSTD | 📁 | | ⬜ |
|
||||||
|
| alisk2.f | ALISK2 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT | ALIFRK, RTEFR1, OPACF1, ROSSTD | 📁 | | ⬜ |
|
||||||
|
| alist1.f | ALIST1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ITERAT | ROSSTD, RTEFR1, ALIFR1, OPACFD | 📁 | | ⬜ |
|
||||||
|
| alist2.f | ALIST2 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT | RTEFR1, ALIFR1, QUIT, ROSSTD, OPACFD | 📁 | | ⬜ |
|
||||||
|
| allard.f | ALLARD | SUBROUTINE | | BASICS, callarda, callardc, quasun, callardb, callardg, calphatd | ALLARDT | 📁 | | ⬜ |
|
||||||
|
| allardt.f | ALLARDT | SUBROUTINE | | BASICS, calphatd | | | allardt.rs | ✅ |
|
||||||
|
| angset.f | ANGSET | SUBROUTINE | ✓ | BASICS | GAULEG | | angset.rs | ✅ |
|
||||||
|
| betah.f | BETAH | FUNCTION | ✓ | | BETAH | | betah.rs | ✅ |
|
||||||
|
| bhe.f | BHE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR | | | | ⬜ |
|
||||||
|
| bhed.f | BHED | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, SURFEX, CMATZD | | | | ⬜ |
|
||||||
|
| bhez.f | BHEZ | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, SURFEX | | | | ⬜ |
|
||||||
|
| bkhsgo.f | BKHSGO | SUBROUTINE | ✓ | | | | bkhsgo.rs | ✅ |
|
||||||
|
| bpop.f | BPOP | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, ODFPAR, ITERAT | BPOPF, RATMAT, BPOPT, LEVSOL, MATINV, BPOPC, BPOPE, LEVGRP | | | ⬜ |
|
||||||
|
| bpopc.f | BPOPC | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, ODFPAR, ADCHAR | STATE | | | ⬜ |
|
||||||
|
| bpope.f | BPOPE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ITERAT, ARRAY1 | SGMER1, DWNFR1 | | | ⬜ |
|
||||||
|
| bpopf.f | BPOPF | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, ODFPAR | | | bpopf.rs | ✅ |
|
||||||
|
| bpopt.f | BPOPT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, ODFPAR | COLIS | | | ⬜ |
|
||||||
|
| bre.f | BRE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR | COMPT0 | | | ⬜ |
|
||||||
|
| brez.f | BREZ | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR | COMPT0 | | | ⬜ |
|
||||||
|
| brte.f | BRTE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR, ARRAY1 | COMPT0 | | | ⬜ |
|
||||||
|
| brtez.f | BRTEZ | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR, ARRAY1 | COMPT0 | | | ⬜ |
|
||||||
|
| butler.f | BUTLER | SUBROUTINE | ✓ | | | | butler.rs | ✅ |
|
||||||
|
| carbon.f | CARBON | SUBROUTINE | ✓ | | | | carbon.rs | ✅ |
|
||||||
|
| ceh12.f | CEH12 | FUNCTION | ✓ | | CEH12 | | ceh12.rs | ✅ |
|
||||||
|
| change.f | CHANGE | SUBROUTINE | | BASICS, ATOMIC, MODELQ | READBF, STEQEQ | 📁 | | ⬜ |
|
||||||
|
| chckse.f | CHCKSE | SUBROUTINE | | BASICS, ATOMIC, MODELQ | SABOLF | 📁 | | ⬜ |
|
||||||
|
| chctab.f | CHCTAB | SUBROUTINE | | BASICS, MODELQ, abntab | | 📁 | | ⬜ |
|
||||||
|
| cheav.f | CHEAV | FUNCTION | | BASICS, ATOMIC | QUIT, CHEAV | 📁 | | ⬜ |
|
||||||
|
| cheavj.f | CHEAVJ | FUNCTION | | BASICS, ATOMIC | CHEAVJ, QUIT | 📁 | | ⬜ |
|
||||||
|
| cia_h2h.f | CIA_H2H | SUBROUTINE | | | LOCATE, IF | 📁 | | ⬜ |
|
||||||
|
| cia_h2h2.f | CIA_H2H2 | SUBROUTINE | | | LOCATE, IF | 📁 | | ⬜ |
|
||||||
|
| cia_h2he.f | CIA_H2HE | SUBROUTINE | | | LOCATE, IF | 📁 | | ⬜ |
|
||||||
|
| cia_hhe.f | CIA_HHE | SUBROUTINE | | | LOCATE, IF | 📁 | | ⬜ |
|
||||||
|
| cion.f | CION | FUNCTION | ✓ | | CION | | cion.rs | ✅ |
|
||||||
|
| ckoest.f | CKOEST | FUNCTION | ✓ | BASICS | CKOEST | | ckoest.rs | ✅ |
|
||||||
|
| colh.f | COLH | SUBROUTINE | | BASICS, ATOMIC, MODELQ | CSPEC, IRC, BUTLER | | | ⬜ |
|
||||||
|
| colhe.f | COLHE | SUBROUTINE | | BASICS, ATOMIC | CSPEC, COLLHE, IRC | | | ⬜ |
|
||||||
|
| colis.f | COLIS | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, CTRTEMP | CSPEC, COLH, IRC, COLHE | | | ⬜ |
|
||||||
|
| collhe.f | COLLHE | SUBROUTINE | ✓ | | | | collhe.rs | ✅ |
|
||||||
|
| column.f | COLUMN | SUBROUTINE | | BASICS, MODELQ, relcor | | 📁 | | ⬜ |
|
||||||
|
| compt0.f | COMPT0 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, auxcbc | | | | ⬜ |
|
||||||
|
| comset.f | COMSET | SUBROUTINE | | BASICS, MODELQ, comgfs, auxcbc | | | | ⬜ |
|
||||||
|
| concor.f | CONCOR | SUBROUTINE | | BASICS, MODELQ | CONOUT | 📁 | | ⬜ |
|
||||||
|
| conout.f | CONOUT | SUBROUTINE | | BASICS, MODELQ, ALIPAR, CUBCON | MEANOPT, OPACF0, CONVEC, MEANOP | 📁 | | ⬜ |
|
||||||
|
| conref.f | CONREF | SUBROUTINE | | BASICS, MODELQ, ARRAY1, imucnn, CUBCON | STEQEQ, WNSTOR, CONVC1, CONOUT, CONVEC, ELDENS | 📁 | | ⬜ |
|
||||||
|
| contmd.f | CONTMD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR, PRSAUX, CUBCON | MEANOP, CUBIC, STEQEQ, WNSTOR, OPACF0, CONOUT, CONVEC | 📁 | | ⬜ |
|
||||||
|
| contmp.f | CONTMP | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR, ichndm, CUBCON | MEANOP, CUBIC, STEQEQ, WNSTOR, OPACF0, CONOUT, CONVEC, MEANOPT, ELDENS | 📁 | | ⬜ |
|
||||||
|
| convc1.f | CONVC1 | SUBROUTINE | | BASICS, CUBCON | TRMDER, TRMDRT | | | ⬜ |
|
||||||
|
| convec.f | CONVEC | SUBROUTINE | | BASICS, CUBCON | TRMDER, TRMDRT | | | ⬜ |
|
||||||
|
| coolrt.f | COOLRT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT, COOLCO | RTEFR1, OPACFA | 📁 | | ⬜ |
|
||||||
|
| corrwm.f | CORRWM | SUBROUTINE | | BASICS, ATOMIC, MODELQ | QUIT | 📁 | | ⬜ |
|
||||||
|
| cross.f | CROSS | FUNCTION | | BASICS, ATOMIC, MODELQ | CROSS | | cross.rs | ✅ |
|
||||||
|
| crossd.f | CROSSD | FUNCTION | | BASICS, ATOMIC, MODELQ | CROSSD | | cross.rs | ✅ |
|
||||||
|
| cspec.f | CSPEC | SUBROUTINE | | BASICS, ATOMIC | QUIT | | | ⬜ |
|
||||||
|
| ctdata.f | CTDATA | BLOCK DATA | | CTRecomb, CTIon | | | ctdata.rs | ✅ |
|
||||||
|
| cubic.f | CUBIC | SUBROUTINE | | BASICS, CUBCON | | | cubic.rs | ✅ |
|
||||||
|
| dielrc.f | DIELRC | SUBROUTINE | ✓ | | | | dielrc.rs | ✅ |
|
||||||
|
| dietot.f | DIETOT | SUBROUTINE | | BASICS, ATOMIC, MODELQ | DIELRC | 📁 | | ⬜ |
|
||||||
|
| divstr.f | DIVSTR | SUBROUTINE | | BASICS, MODELQ | | | divstr.rs | ✅ |
|
||||||
|
| dmder.f | DMDER | SUBROUTINE | | BASICS, ATOMIC, MODELQ, DEPTDR | | | dmder.rs | ✅ |
|
||||||
|
| dmeval.f | DMEVAL | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ARRAY1 | | 📁 | | ⬜ |
|
||||||
|
| dopgam.f | DOPGAM | SUBROUTINE | | BASICS, ATOMIC, MODELQ | GAMSP | | dopgam.rs | ✅ |
|
||||||
|
| dwnfr.f | DWNFR | SUBROUTINE | | BASICS, MODELQ | | | dwnfr.rs | ✅ |
|
||||||
|
| dwnfr0.f | DWNFR0 | SUBROUTINE | | BASICS, MODELQ | | | dwnfr0.rs | ✅ |
|
||||||
|
| dwnfr1.f | DWNFR1 | SUBROUTINE | | BASICS, MODELQ | | | dwnfr1.rs | ✅ |
|
||||||
|
| eint.f | EINT | SUBROUTINE | ✓ | | EXPINX | | expint.rs | ✅ |
|
||||||
|
| elcor.f | ELCOR | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ADCHAR | MOLEQ, STATE, STEQEQ, WNSTOR | 📁 | | ⬜ |
|
||||||
|
| eldenc.f | ELDENC | SUBROUTINE | | BASICS, MODELQ, ATOMIC, eospar, eletab, hmolab | MOLEQ, STATE, RHONEN | 📁 | | ⬜ |
|
||||||
|
| eldens.f | ELDENS | SUBROUTINE | | BASICS, MODELQ, ATOMIC, eospar, terden | STATE, ENTENE, MOLEQ, MPARTF, LINEQS | 📁 | | ⬜ |
|
||||||
|
| emat.f | EMAT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR | | | emat.rs | ✅ |
|
||||||
|
| entene.f | ENTENE | SUBROUTINE | | BASICS, ATOMIC, MODELQ | MPARTF | | | ⬜ |
|
||||||
|
| erfcin.f | ERFCIN | FUNCTION | ✓ | | ERFCIN | | erfcx.rs | ✅ |
|
||||||
|
| erfcx.f | ERFCX | FUNCTION | ✓ | | ERFCX | | erfcx.rs | ✅ |
|
||||||
|
| expint.f | EXPINT | FUNCTION | ✓ | | EXPINT | | expint.rs | ✅ |
|
||||||
|
| expinx.f | EXPINX | SUBROUTINE | ✓ | | | | expint.rs | ✅ |
|
||||||
|
| expo.f | EXPO | FUNCTION | ✓ | | EXPO | | expo.rs | ✅ |
|
||||||
|
| ffcros.f | FFCROS | FUNCTION | ✓ | | FFCROS | | ffcros.rs | ✅ |
|
||||||
|
| gami.f | GAMI | FUNCTION | ✓ | | GAMI | | gami.rs | ✅ |
|
||||||
|
| gamsp.f | GAMSP | SUBROUTINE | ✓ | BASICS | | | gamsp.rs | ✅ |
|
||||||
|
| gauleg.f | GAULEG | SUBROUTINE | ✓ | | | | gauleg.rs | ✅ |
|
||||||
|
| gaunt.f | GAUNT | FUNCTION | ✓ | | GAUNT | | gaunt.rs | ✅ |
|
||||||
|
| getlal.f | GETLAL | SUBROUTINE | | BASICS, callarda, callardc, quasun, callardb, callardg, calphatd | | 📁 | | ⬜ |
|
||||||
|
| getwrd.f | GETWRD | SUBROUTINE | ✓ | | | | getwrd.rs | ✅ |
|
||||||
|
| gfree0.f | GFREE0 | SUBROUTINE | | BASICS, MODELQ | | | gfree.rs | ✅ |
|
||||||
|
| gfree1.f | GFREE1 | FUNCTION | | BASICS, MODELQ | GFREE1 | | gfree.rs | ✅ |
|
||||||
|
| gfreed.f | GFREED | SUBROUTINE | | BASICS, MODELQ | | | gfree.rs | ✅ |
|
||||||
|
| ghydop.f | GHYDOP | SUBROUTINE | | BASICS, MODELQ, ATOMIC, intcfg | | | | ⬜ |
|
||||||
|
| gntk.f | GNTK | FUNCTION | ✓ | | GNTK | | gntk.rs | ✅ |
|
||||||
|
| gomini.f | GOMINI | SUBROUTINE | | BASICS, MODELQ, intcfg | | 📁 | | ⬜ |
|
||||||
|
| grcor.f | GRCOR | SUBROUTINE | ✓ | | | | grcor.rs | ✅ |
|
||||||
|
| greyd.f | GREYD | SUBROUTINE | | BASICS, MODELQ, ATOMIC, ALIPAR | MEANOP, RHONEN, STEQEQ, WNSTOR, OPACF0 | 📁 | | ⬜ |
|
||||||
|
| gridp.f | GRIDP | SUBROUTINE | ✓ | BASICS | | | gridp.rs | ✅ |
|
||||||
|
| h2minus.f | H2MINUS | SUBROUTINE | | BASICS | LOCATE | 📁 | | ⬜ |
|
||||||
|
| hction.f | HCTION | FUNCTION | | CTIon, CTRTEMP | HCTION | | ctdata.rs | ✅ |
|
||||||
|
| hctrecom.f | HCTRECOM | FUNCTION | | CTRecomb, CTRTEMP | HCTRECOM | | ctdata.rs | ✅ |
|
||||||
|
| hedif.f | HEDIF | SUBROUTINE | | BASICS, MODELQ, ATOMIC, hediff | | 📁 | | ⬜ |
|
||||||
|
| hephot.f | HEPHOT | FUNCTION | ✓ | | HEPHOT | | hephot.rs | ✅ |
|
||||||
|
| hesol6.f | HESOL6 | SUBROUTINE | | BASICS, MODELQ, PRSAUX | MATINV | | | ⬜ |
|
||||||
|
| hesolv.f | HESOLV | SUBROUTINE | | BASICS, MODELQ, PRSAUX | MATINV, STEQEQ, RHONEN, WNSTOR | 📁 | | ⬜ |
|
||||||
|
| hidalg.f | HIDALG | FUNCTION | ✓ | | HIDALG | | hidalg.rs | ✅ |
|
||||||
|
| ijali2.f | IJALI2 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | QUIT | 📁 | | ⬜ |
|
||||||
|
| ijalis.f | IJALIS | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | 📁 | | ⬜ |
|
||||||
|
| incldy.f | INCLDY | SUBROUTINE | | BASICS, ATOMIC, MODELQ | RATMAT, LEVSOL, QUIT, WNSTOR, SABOLF | 📁 | | ⬜ |
|
||||||
|
| indexx.f | INDEXX | SUBROUTINE | ✓ | | | | indexx.rs | ✅ |
|
||||||
|
| inicom.f | INICOM | SUBROUTINE | | BASICS, ATOMIC, MODELQ, comgfs | | | inicom.rs | ✅ |
|
||||||
|
| inifrc.f | INIFRC | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ijflar | INDEXX | 📁 | | ⬜ |
|
||||||
|
| inifrs.f | INIFRS | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | QUIT, INDEXX | 📁 | | ⬜ |
|
||||||
|
| inifrt.f | INIFRT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ijflar | INDEXX | 📁 | | ⬜ |
|
||||||
|
| inilam.f | INILAM | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ALIPAR | ELCOR, RTEFR1, STEQEQ, COLIS, WNSTOR, OPAINI, SABOLF, RATES1, OPACF1 | | | ⬜ |
|
||||||
|
| initia.f | INITIA | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ODFPAR, ALIPAR, freqcl, STRPAR, INUNIT | STATE, LINSET, INIFRC, QUIT, ODFHYS, OPADD0, LINSPL, RDATA, NSTPAR, RDATAX, DOPGAM, READBF, INTERP | 📁 | | ⬜ |
|
||||||
|
| inkul.f | INKUL | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, LINED, COLKUR | | 📁 | | ⬜ |
|
||||||
|
| inpdis.f | INPDIS | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ODFPAR, ALIPAR, relcor | GRCOR | 📁 | | ⬜ |
|
||||||
|
| inpmod.f | INPMOD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, eospar | RATMAT, LEVSOL, QUIT, WNSTOR, SABOLF, KURUCZ, MOLEQ, INCLDY | 📁 | | ⬜ |
|
||||||
|
| interp.f | INTERP | SUBROUTINE | ✓ | BASICS | | | interp.rs | ✅ |
|
||||||
|
| inthyd.f | INTHYD | SUBROUTINE | | BASICS, MODELQ | DIVSTR | | inthyd.rs | ✅ |
|
||||||
|
| intlem.f | INTLEM | SUBROUTINE | | BASICS, MODELQ | INTHYD | | intlem.rs | ✅ |
|
||||||
|
| intxen.f | INTXEN | SUBROUTINE | | BASICS, MODELQ | | | intxen.rs | ✅ |
|
||||||
|
| irc.f | IRC | SUBROUTINE | ✓ | | EXPINX, SZIRC | | irc.rs | ✅ |
|
||||||
|
| iroset.f | IROSET | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, LINED | INKUL, QUIT, LEVCD | 📁 | | ⬜ |
|
||||||
|
| kurucz.f | KURUCZ | SUBROUTINE | | BASICS, ATOMIC, MODELQ, temlim | RHONEN, RATMAT, LEVSOL, QUIT, WNSTOR, SABOLF, MOLEQ | 📁 | | ⬜ |
|
||||||
|
| lagran.f | LAGRAN | SUBROUTINE | ✓ | | | | interpolate.rs | ✅ |
|
||||||
|
| laguer.f | LAGUER | SUBROUTINE | | | | 📁 | laguer.rs | ✅ |
|
||||||
|
| lemini.f | LEMINI | SUBROUTINE | | BASICS, MODELQ | | 📁 | | ⬜ |
|
||||||
|
| levcd.f | LEVCD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, COLKUR | QUIT, INDEXX | 📁 | | ⬜ |
|
||||||
|
| levgrp.f | LEVGRP | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | | | | ⬜ |
|
||||||
|
| levset.f | LEVSET | SUBROUTINE | | BASICS, ATOMIC, MODELQ | QUIT | | | ⬜ |
|
||||||
|
| levsol.f | LEVSOL | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | LINEQS | | levsol.rs | ✅ |
|
||||||
|
| lineqs.f | LINEQS | SUBROUTINE | ✓ | BASICS | | | lineqs.rs | ✅ |
|
||||||
|
| linpro.f | LINPRO | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, quasun | DIVSTR, INTLEM, STARK0, INTXEN, DOPGAM | | | ⬜ |
|
||||||
|
| linsel.f | LINSEL | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR | OPAINI, QUIT, RTEFR1, OPACF1 | 📁 | | ⬜ |
|
||||||
|
| linset.f | LINSET | SUBROUTINE | | BASICS, ATOMIC, MODELQ | STARK0, QUIT, DIVSTR, IJALIS | 📁 | | ⬜ |
|
||||||
|
| linspl.f | LINSPL | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | | ⬜ |
|
||||||
|
| locate.f | LOCATE | SUBROUTINE | ✓ | | | | locate.rs | ✅ |
|
||||||
|
| ltegr.f | LTEGR | SUBROUTINE | | BASICS, ATOMIC, MODELQ | ROSSOP, QUIT, STEQEQ, WNSTOR, CONOUT, INTERP | 📁 | | ⬜ |
|
||||||
|
| ltegrd.f | LTEGRD | SUBROUTINE | | BASICS, MODELQ, PRSAUX, TOTJHK, CUBCON, FLXAUX, FACTRS | TEMPER, QUIT, STEQEQ, WNSTOR, CONOUT, ZMRHO, ELDENS, INTERP | 📁 | | ⬜ |
|
||||||
|
| lucy.f | LUCY | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ITERAT, ALIPAR, ARRAY1 | RTEFR1, ELCOR, STEQEQ, WNSTOR, COLIS, OPAINI, SABOLF, OPACFL | 📁 | | ⬜ |
|
||||||
|
| lymlin.f | LYMLIN | SUBROUTINE | | BASICS, ATOMIC, MODELQ | STARK0, DIVSTR | 📁 | | ⬜ |
|
||||||
|
| matcon.f | MATCON | SUBROUTINE | | BASICS, MODELQ, ARRAY1, CUBCON | CONVEC | | | ⬜ |
|
||||||
|
| matgen.f | MATGEN | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR | BRE, BRTE, BREZ, BHEZ, BHED, MATCON, EMAT, SABOLF, BRTEZ, BHE, BPOP | | | ⬜ |
|
||||||
|
| matinv.f | MATINV | SUBROUTINE | ✓ | BASICS | | | matinv.rs | ✅ |
|
||||||
|
| meanop.f | MEANOP | SUBROUTINE | | BASICS, MODELQ, ATOMIC | | | meanop.rs | ✅ |
|
||||||
|
| meanopt.f | MEANOPT | SUBROUTINE | | BASICS, MODELQ | OPCTAB | | | ⬜ |
|
||||||
|
| minv3.f | MINV3 | SUBROUTINE | ✓ | | | | minv3.rs | ✅ |
|
||||||
|
| moleq.f | MOLEQ | SUBROUTINE | | BASICS, MODELQ, ATOMIC, ioniz2, COMFH1, hmolab, eospar, entrop, terden, adchar, moldat | MPARTF, RUSSEL | 📁 | | ⬜ |
|
||||||
|
| mpartf.f | MPARTF | SUBROUTINE | | moldat | | 📁 | | ⬜ |
|
||||||
|
| newdm.f | NEWDM | SUBROUTINE | | BASICS, MODELQ, PRSAUX, FLXAUX, FACTRS | TEMPER, INTERP | 📁 | | ⬜ |
|
||||||
|
| newdmt.f | NEWDMT | SUBROUTINE | | BASICS, MODELQ, PRSAUX, FLXAUX, FACTRS | TEMPER, GRIDP, INTERP | 📁 | | ⬜ |
|
||||||
|
| newpop.f | NEWPOP | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | | 📁 | | ⬜ |
|
||||||
|
| nstout.f | NSTOUT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ODFPAR, ALIPAR | QUIT | 📁 | | ⬜ |
|
||||||
|
| nstpar.f | NSTPAR | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ODFPAR, ALIPAR, hediff, ichndm, imucnn, adiaba, irwint, icnrsp, quasun, deridt, temlim, derdif, FLXAUX, ipricr, freqcl, ifpzpa, moldat | QUIT, GETWRD | 📁 | | ⬜ |
|
||||||
|
| odf1.f | ODF1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | DIVSTR, DWNFR, ODFHST | 📁 | | ⬜ |
|
||||||
|
| odffr.f | ODFFR | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | QUIT | | | ⬜ |
|
||||||
|
| odfhst.f | ODFHST | SUBROUTINE | | BASICS, MODELQ, ODFPAR | | | odfhst.rs | ✅ |
|
||||||
|
| odfhyd.f | ODFHYD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | DIVSTR, INDEXX, ODFHST | | | ⬜ |
|
||||||
|
| odfhys.f | ODFHYS | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | STARK0, ODFFR, IJALIS | | | ⬜ |
|
||||||
|
| odfmer.f | ODFMER | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | ODFHYD | | | ⬜ |
|
||||||
|
| odfset.f | ODFSET | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, STFCR | QUIT, IJALIS | 📁 | | ⬜ |
|
||||||
|
| opacf0.f | OPACF0 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, hmolab | OPADD, WNSTOR, DWNFR1, SABOLF, DWNFR0, GFREE0, OPACT1, SGMER1, LINPRO | | | ⬜ |
|
||||||
|
| opacf1.f | OPACF1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ipricr, hmolab | OPADD, DWNFR1, GHYDOP, QUASIM, OPACT1, PRD, SGMER1, LYMLIN | 📁 | | ⬜ |
|
||||||
|
| opacfa.f | OPACFA | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, COOLCO | PRD, SGMER1, DWNFR1, OPADD | | | ⬜ |
|
||||||
|
| opacfd.f | OPACFD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT, dsctva, hmolab, rhoder | OPADD, GFREED, OPACTD, DWNFR1, QUASIM, PRD, SGMER1, OPCTAB, LYMLIN | 📁 | | ⬜ |
|
||||||
|
| opacfl.f | OPACFL | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR | SGMER1, DWNFR1, OPADD | | | ⬜ |
|
||||||
|
| opact1.f | OPACT1 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, hmolab | OPCTAB | | | ⬜ |
|
||||||
|
| opactd.f | OPACTD | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ARRAY1, ITERAT, rhoder, dsctva, hmolab | OPCTAB | | | ⬜ |
|
||||||
|
| opactr.f | OPACTR | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ATOMIC, grdpra, dsctva, hmolab | OPACF1, LEVSOL, STEQEQ, WNSTOR, OPAINI, PGSET, SABOLF, ELDENS, RATMAL | | | ⬜ |
|
||||||
|
| opadd.f | OPADD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, eospar | CIA_H2H, H2MINUS, CIA_H2HE, CIA_H2H2, CIA_HHE | | | ⬜ |
|
||||||
|
| opadd0.f | OPADD0 | SUBROUTINE | | BASICS, ATOMIC, MODELQ | QUIT | | | ⬜ |
|
||||||
|
| opahst.f | OPAHST | SUBROUTINE | | BASICS, ODFPAR | STARK0 | 📁 | | ⬜ |
|
||||||
|
| opaini.f | OPAINI | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR | REFLEV, WNSTOR, SABOLF, DWNFR0, LINPRO, LEVGRP | | | ⬜ |
|
||||||
|
| opctab.f | OPCTAB | SUBROUTINE | | BASICS, MODELQ | RAYLEIGH | | | ⬜ |
|
||||||
|
| opdata.f | OPDATA | SUBROUTINE | | TOPB | | 📁 | | ⬜ |
|
||||||
|
| opfrac.f | OPFRAC | SUBROUTINE | | pfoptb | | 📁 | | ⬜ |
|
||||||
|
| osccor.f | OSCCOR | SUBROUTINE | | BASICS, MODELQ, ITERAT | | 📁 | | ⬜ |
|
||||||
|
| outpri.f | OUTPRI | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, grdpra | OPACF1, LEVSOL, WNSTOR, SABOLF, RATMAL | 📁 | | ⬜ |
|
||||||
|
| output.f | OUTPUT | SUBROUTINE | | BASICS, MODELQ | | 📁 | | ⬜ |
|
||||||
|
| partf.f | PARTF | SUBROUTINE | | BASICS, PFSTDS, irwint | PFNI, OPFRAC, PFHEAV, PFCNO, PFSPEC, MPARTF, PFFE | | | ⬜ |
|
||||||
|
| pfcno.f | PFCNO | SUBROUTINE | ✓ | BASICS | | | pfcno.rs | ✅ |
|
||||||
|
| pffe.f | PFFE | SUBROUTINE | ✓ | | | | pffe.rs | ✅ |
|
||||||
|
| pfheav.f | PFHEAV | SUBROUTINE | | | | 📁 | | ⬜ |
|
||||||
|
| pfni.f | PFNI | SUBROUTINE | ✓ | | | | pfni.rs | ✅ |
|
||||||
|
| pfspec.f | PFSPEC | SUBROUTINE | ✓ | | | | pfspec.rs | ✅ |
|
||||||
|
| pgset.f | PGSET | SUBROUTINE | | BASICS, ITERAT, MODELQ, rybpgs, grdpra | TRIDAG | 📁 | | ⬜ |
|
||||||
|
| prchan.f | PRCHAN | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | | 📁 | | ⬜ |
|
||||||
|
| prd.f | PRD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | DOPGAM | | | ⬜ |
|
||||||
|
| prdini.f | PRDINI | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | prdini.rs | ✅ |
|
||||||
|
| princ.f | PRINC | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR | LINPRO, SABOLF, OPACF1, DWNFR | 📁 | | ⬜ |
|
||||||
|
| prnt.f | PRNT | SUBROUTINE | | BASICS, ATOMIC, MODELQ | SABOLF | 📁 | | ⬜ |
|
||||||
|
| profil.f | PROFIL | FUNCTION | | BASICS, ATOMIC, MODELQ, quasun | STARK0, PROFIL, DIVSTR | | | ⬜ |
|
||||||
|
| profsp.f | PROFSP | FUNCTION | | BASICS, ATOMIC, MODELQ | SABOLF, PROFSP | | | ⬜ |
|
||||||
|
| prsent.f | PRSENT | SUBROUTINE | | tdedge, tdflag, THERM, TABLTD | | 📁 | | ⬜ |
|
||||||
|
| psolve.f | PSOLVE | SUBROUTINE | | BASICS, MODELQ | | | psolve.rs | ✅ |
|
||||||
|
| pzert.f | PZERT | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | | ⬜ |
|
||||||
|
| pzeval.f | PZEVAL | SUBROUTINE | | BASICS, MODELQ, ALIPAR, icnrsp | CONOUT | 📁 | | ⬜ |
|
||||||
|
| pzevld.f | PZEVLD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR, ARRAY1, PRSAUX, DEPTDR, ifpzpa, grdpra | | | | ⬜ |
|
||||||
|
| quartc.f | QUARTC | SUBROUTINE | | | | 📁 | quartc.rs | ✅ |
|
||||||
|
| quasim.f | QUASIM | SUBROUTINE | | BASICS, ATOMIC, MODELQ, quasun | ALLARD | | | ⬜ |
|
||||||
|
| quit.f | QUIT | SUBROUTINE | | | | 📁 | quit.rs | ✅ |
|
||||||
|
| radpre.f | RADPRE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR | RTEFR1, QUIT, OPACF1, INDEXX | 📁 | | ⬜ |
|
||||||
|
| radtot.f | RADTOT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ITERAT, OPTDPT, SURFEX, TOTJHK | OPAINI, RTEFR1, OPACF1 | | | ⬜ |
|
||||||
|
| raph.f | RAPH | FUNCTION | ✓ | | RAPH | | raph.rs | ✅ |
|
||||||
|
| rates1.f | RATES1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ITERAT | ROSSTD, RTEFR1, OPACF1 | | | ⬜ |
|
||||||
|
| ratmal.f | RATMAL | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | ratmal.rs | ✅ |
|
||||||
|
| ratmat.f | RATMAT | SUBROUTINE | | BASICS, ATOMIC, MODELQ | REFLEV | | | ⬜ |
|
||||||
|
| ratsp1.f | RATSP1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT | ROSSTD, RTEFR1, OPACF1 | 📁 | | ⬜ |
|
||||||
|
| rayini.f | RAYINI | SUBROUTINE | | BASICS, MODELQ, ATOMIC | RAYLEIGH | 📁 | | ⬜ |
|
||||||
|
| rayleigh.f | RAYLEIGH | SUBROUTINE | | BASICS, ATOMIC, MODELQ, RAYSCT, eospar | | | rayleigh.rs | ✅ |
|
||||||
|
| rayset.f | RAYSET | SUBROUTINE | | BASICS, MODELQ | | | rayset.rs | ✅ |
|
||||||
|
| rdata.f | RDATA | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ODFPAR, ALIPAR, STRPAR, imodlc, INUNIT | LINSET, QUIT, RDATAX, DOPGAM | 📁 | | ⬜ |
|
||||||
|
| rdatax.f | RDATAX | SUBROUTINE | | BASICS, ATOMIC, MODELQ | BKHSGO | 📁 | | ⬜ |
|
||||||
|
| readbf.f | READBF | SUBROUTINE | | BASICS | | 📁 | | ⬜ |
|
||||||
|
| rechck.f | RECHCK | SUBROUTINE | | BASICS, ATOMIC, MODELQ | RTEFR1, OPACF1 | 📁 | | ⬜ |
|
||||||
|
| reflev.f | REFLEV | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | | | | ⬜ |
|
||||||
|
| reiman.f | REIMAN | FUNCTION | ✓ | | REIMAN | | reiman.rs | ✅ |
|
||||||
|
| resolv.f | RESOLV | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ALIPAR, ARRAY1, icnrsp | RTEFR1, ELCOR, STEQEQ, NEWPOP, OPAINI, ROSSTD, CONOUT, TIMING, PRD, TAUFR1, RATES1, OPACF1 | 📁 | | ⬜ |
|
||||||
|
| rhoeos.f | RHOEOS | FUNCTION | | BASICS, MODELQ | PRSENT, RHOEOS | | | ⬜ |
|
||||||
|
| rhonen.f | RHONEN | SUBROUTINE | | BASICS, MODELQ | ELDENS | | | ⬜ |
|
||||||
|
| rhsgen.f | RHSGEN | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, CUBCON | STATE, RATMAT, MATINV, SABOLF, CONVEC, COMPT0, LEVGRP | | | ⬜ |
|
||||||
|
| rossop.f | ROSSOP | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR | MEANOP, STEQEQ, WNSTOR, OPACF0, MEANOPT, ELDENS | | | ⬜ |
|
||||||
|
| rosstd.f | ROSSTD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ALIPAR | | 📁 | | ⬜ |
|
||||||
|
| rte_sc.f | RTE_SC | SUBROUTINE | ✓ | BASICS | | | rte_sc.rs | ✅ |
|
||||||
|
| rteang.f | RTEANG | SUBROUTINE | | BASICS, MODELQ, ALIPAR, SURFEX, EXTINT | GAULEG | | | ⬜ |
|
||||||
|
| rtecf0.f | RTECF0 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT, auxcbc, AUXRTE | | | | ⬜ |
|
||||||
|
| rtecf1.f | RTECF1 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, SURFEX, AUXRTE, EXTINT, OPTDPT, comgfs | RTECF0, RTEFE2, RTESOL | 📁 | | ⬜ |
|
||||||
|
| rtecmc.f | RTECMC | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, AUXRTE, comgfs | RTECF0, OPACF1, MATINV | | | ⬜ |
|
||||||
|
| rtecmu.f | RTECMU | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT, AUXRTE | GAULEG, RTECF0, OPACF1, RTESOL | 📁 | | ⬜ |
|
||||||
|
| rtecom.f | RTECOM | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT, AUXRTE, comgfs | RTECF0, RTECF1, OPACF1 | | | ⬜ |
|
||||||
|
| rtedf1.f | RTEDF1 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, OPTDPT | | | | ⬜ |
|
||||||
|
| rtedf2.f | RTEDF2 | SUBROUTINE | | BASICS, MODELQ, ALIPAR | | | | ⬜ |
|
||||||
|
| rtefe2.f | RTEFE2 | SUBROUTINE | ✓ | BASICS | | | rtefe2.rs | ✅ |
|
||||||
|
| rtefr1.f | RTEFR1 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT | RTEDF2, MATINV, RTECF1, RTEDF1, MINV3, RTESOL | 📁 | | ⬜ |
|
||||||
|
| rteint.f | RTEINT | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT | MATINV, OPACF1 | 📁 | | ⬜ |
|
||||||
|
| rtesol.f | RTESOL | SUBROUTINE | ✓ | BASICS | | | rtesol.rs | ✅ |
|
||||||
|
| russel.f | RUSSEL | SUBROUTINE | | BASICS, MODELQ, COMFH1 | MPARTF | 📁 | | ⬜ |
|
||||||
|
| rybchn.f | RYBCHN | SUBROUTINE | | BASICS, ITERAT, MODELQ, ALIPAR, ARRAY1, rybpgs, grdpra | PGSET, ELDENS | 📁 | | ⬜ |
|
||||||
|
| rybene.f | RYBENE | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ARRAY1, CUBCON, deridt, RYBMTX | CONVEC | | | ⬜ |
|
||||||
|
| rybheq.f | RYBHEQ | SUBROUTINE | | BASICS, MODELQ, rybpgs, grdpra | RTEFR1, ELCOR, STEQEQ, WNSTOR, OPAINI, PGSET, ELDENS, OPACF1 | 📁 | | ⬜ |
|
||||||
|
| rybmat.f | RYBMAT | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ARRAY1, dsctva, RYBMTX | | | | ⬜ |
|
||||||
|
| rybsol.f | RYBSOL | SUBROUTINE | | BASICS, MODELQ, ATOMIC, ALIPAR, ARRAY1, ITERAT, RYBMTX, imodlc | RTEFR1, ALIFR1, TRIDAG, STEQEQ, ROSSTD, RYBMAT, OPACTR, OPACFD, RYBCHN, LINEQS | 📁 | | ⬜ |
|
||||||
|
| sabolf.f | SABOLF | SUBROUTINE | | BASICS, ATOMIC, MODELQ | PARTF | | | ⬜ |
|
||||||
|
| sbfch.f | SBFCH | FUNCTION | ✓ | | SBFCH | | sbfch.rs | ✅ |
|
||||||
|
| sbfhe1.f | SBFHE1 | FUNCTION | | BASICS, ATOMIC | SBFHE1, QUIT | 📁 | sbfhe1.rs | ✅ |
|
||||||
|
| sbfhmi.f | SBFHMI | FUNCTION | ✓ | | SBFHMI | | sbfhmi.rs | ✅ |
|
||||||
|
| sbfhmi_old.f | SBFHMI_OLD | FUNCTION | ✓ | | SBFHMI_OLD | | sbfhmi_old.rs | ✅ |
|
||||||
|
| sbfoh.f | SBFOH | FUNCTION | ✓ | | SBFOH | | sbfoh.rs | ✅ |
|
||||||
|
| setdrt.f | SETDRT | SUBROUTINE | | BASICS, MODELQ, RHODER | | | | ⬜ |
|
||||||
|
| settrm.f | SETTRM | SUBROUTINE | | tdedge, tdflag, THERM, TABLTD | PRSENT | 📁 | | ⬜ |
|
||||||
|
| sffhmi.f | SFFHMI | FUNCTION | ✓ | | SFFHMI | | sffhmi.rs | ✅ |
|
||||||
|
| sffhmi_add.f | SFFHMI_ADD | FUNCTION | ✓ | | SFFHMI_ADD | | sffhmi_add.rs | ✅ |
|
||||||
|
| sghe12.f | SGHE12 | FUNCTION | ✓ | | SGHE12 | | sghe12.rs | ✅ |
|
||||||
|
| sgmer0.f | SGMER0 | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | sgmer.rs | ✅ |
|
||||||
|
| sgmer1.f | SGMER1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | sgmer.rs | ✅ |
|
||||||
|
| sgmerd.f | SGMERD | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | sgmer.rs | ✅ |
|
||||||
|
| sigave.f | SIGAVE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | | 📁 | | ⬜ |
|
||||||
|
| sigk.f | SIGK | FUNCTION | | BASICS, ATOMIC | SIGK, SPSIGK | | | ⬜ |
|
||||||
|
| sigmar.f | SIGMAR | FUNCTION | | BASICS | SIGMAR, LAGUER | 📁 | | ⬜ |
|
||||||
|
| solve.f | SOLVE | SUBROUTINE | | BASICS, ITERAT, MODELQ, ARRAY1, ALIPAR, CMATZD | MATINV, WNSTOR, MATGEN, RHSGEN, PRCHAN | 📁 | | ⬜ |
|
||||||
|
| solves.f | SOLVES | SUBROUTINE | | BASICS, ITERAT, MODELQ, ARRAY1, ALIPAR, CMATZD, STOMAT | MATINV, WNSTOR, MATGEN, RHSGEN, PRCHAN | 📁 | | ⬜ |
|
||||||
|
| spsigk.f | SPSIGK | SUBROUTINE | ✓ | | CARBON | | spsigk.rs | ✅ |
|
||||||
|
| srtfrq.f | SRTFRQ | SUBROUTINE | | BASICS, ATOMIC, MODELQ | QUIT, INDEXX | 📁 | | ⬜ |
|
||||||
|
| stark0.f | STARK0 | SUBROUTINE | ✓ | | | | stark0.rs | ✅ |
|
||||||
|
| starka.f | STARKA | FUNCTION | | BASICS, MODELQ | STARKA | | starka.rs | ✅ |
|
||||||
|
| start.f | START | SUBROUTINE | | BASICS, hediff | | 📁 | | ⬜ |
|
||||||
|
| state.f | STATE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, PFSTDS, terden | PARTF, OPFRAC | 📁 | | ⬜ |
|
||||||
|
| steqeq.f | STEQEQ | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, POPSTR, PPAPAR | MOLEQ, LEVSOL, SABOLF, RATMAT | | | ⬜ |
|
||||||
|
| switch.f | SWITCH | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | 📁 | | ⬜ |
|
||||||
|
| szirc.f | SZIRC | SUBROUTINE | ✓ | | EINT | | szirc.rs | ✅ |
|
||||||
|
| tabini.f | TABINI | SUBROUTINE | | BASICS, MODELQ, ATOMIC, intcff, abntab, eletab | | 📁 | | ⬜ |
|
||||||
|
| tabint.f | TABINT | SUBROUTINE | | BASICS, MODELQ, ATOMIC, intcff | | | | ⬜ |
|
||||||
|
| taufr1.f | TAUFR1 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT | | | | ⬜ |
|
||||||
|
| tdpini.f | TDPINI | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR | GFREE0 | | tdpini.rs | ✅ |
|
||||||
|
| temcor.f | TEMCOR | SUBROUTINE | | BASICS, MODELQ, ARRAY1, ALIPAR, CUBCON | MEANOP, STEQEQ, WNSTOR, OPACF0, CONVEC, ELDENS | 📁 | | ⬜ |
|
||||||
|
| temper.f | TEMPER | SUBROUTINE | | BASICS, MODELQ, ALIPAR, PRSAUX, FLXAUX, FACTRS | MEANOP, STEQEQ, WNSTOR, OPACF0, TLOCAL, MEANOPT, ELDENS | 📁 | | ⬜ |
|
||||||
|
| timing.f | TIMING | SUBROUTINE | | | | 📁 | | ⬜ |
|
||||||
|
| tiopf.f | TIOPF | SUBROUTINE | ✓ | | | | tiopf.rs | ✅ |
|
||||||
|
| tlocal.f | TLOCAL | SUBROUTINE | | BASICS, MODELQ, FACTRS, FLXAUX | QUARTC | | | ⬜ |
|
||||||
|
| tlusty.f | TLUSTY | UNKNOWN | | BASICS, ITERAT, ALIPAR | TIMING | 📁 | | ⬜ |
|
||||||
|
| topbas.f | TOPBAS | FUNCTION | | TOPB | TOPBAS | 📁 | | ⬜ |
|
||||||
|
| traini.f | TRAINI | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | | | traini.rs | ✅ |
|
||||||
|
| tridag.f | TRIDAG | SUBROUTINE | ✓ | | | | tridag.rs | ✅ |
|
||||||
|
| trmder.f | TRMDER | SUBROUTINE | | BASICS, terden, adiaba, derdif | ELDENS | | | ⬜ |
|
||||||
|
| trmdrt.f | TRMDRT | SUBROUTINE | | BASICS, tdflag, CONVOUT, tdedge, CC | PRSENT | | | ⬜ |
|
||||||
|
| ubeta.f | UBETA | FUNCTION | ✓ | | UBETA, LAGRAN | | ubeta.rs | ✅ |
|
||||||
|
| vern16.f | VERN16 | FUNCTION | ✓ | BASICS | VERN16 | | vern16.rs | ✅ |
|
||||||
|
| vern18.f | VERN18 | FUNCTION | ✓ | BASICS | VERN18 | | vern18.rs | ✅ |
|
||||||
|
| vern20.f | VERN20 | FUNCTION | ✓ | BASICS | VERN20 | | vern20.rs | ✅ |
|
||||||
|
| vern26.f | VERN26 | FUNCTION | ✓ | BASICS | VERN26 | | vern26.rs | ✅ |
|
||||||
|
| verner.f | VERNER | FUNCTION | | BASICS, ATOMIC | VERNER, QUIT | | verner.rs | ✅ |
|
||||||
|
| visini.f | VISINI | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | | 📁 | | ⬜ |
|
||||||
|
| voigt.f | VOIGT | FUNCTION | ✓ | | VOIGT | | voigt.rs | ✅ |
|
||||||
|
| voigte.f | VOIGTE | FUNCTION | ✓ | | VOIGTE | | voigte.rs | ✅ |
|
||||||
|
| wn.f | WN | FUNCTION | ✓ | BASICS | WN | | wn.rs | ✅ |
|
||||||
|
| wnstor.f | WNSTOR | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | wnstor.rs | ✅ |
|
||||||
|
| xenini.f | XENINI | SUBROUTINE | | BASICS, MODELQ | | 📁 | | ⬜ |
|
||||||
|
| xk2dop.f | XK2DOP | FUNCTION | ✓ | | XK2DOP | | xk2dop.rs | ✅ |
|
||||||
|
| yint.f | YINT | FUNCTION | ✓ | | YINT | | interpolate.rs | ✅ |
|
||||||
|
| ylintp.f | YLINTP | FUNCTION | ✓ | | YLINTP | | ylintp.rs | ✅ |
|
||||||
|
| zmrho.f | ZMRHO | SUBROUTINE | | BASICS, MODELQ | | | zmrho.rs | ✅ |
|
||||||
374
REFACTORING_GUIDE.md
Normal file
374
REFACTORING_GUIDE.md
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
# TLUSTY Rust 重构规范
|
||||||
|
|
||||||
|
## 1. 选定下一个重构目标
|
||||||
|
|
||||||
|
优先处理依赖少无io的函数
|
||||||
|
|
||||||
|
注意:DATA 语句(硬编码的数据表)已经由scripts/extract_fortran_data.py生成到src/data.rs中
|
||||||
|
|
||||||
|
不要管复杂不复杂和有无common依赖,
|
||||||
|
按python3 scripts/analyze_fortran.py --priority | head -10 的顺序来
|
||||||
|
|
||||||
|
分离出来的原始fortran函数在/home/fmq/program/tlusty/tl208-s54/rust/tlusty/extracted文件夹下
|
||||||
|
### 优先级排序
|
||||||
|
|
||||||
|
**核心原则**: 优先处理"传递未实现依赖=0"的函数,它们不依赖其他未完成的函数。
|
||||||
|
|
||||||
|
按以下顺序选择待重构函数:
|
||||||
|
|
||||||
|
1. **无未实现依赖** (传递未实现=0)
|
||||||
|
- 可立即开始,无需等待其他函数
|
||||||
|
- 包括纯函数和只有 COMMON 依赖的函数
|
||||||
|
|
||||||
|
2. **少量未实现依赖** (传递未实现=1~3)
|
||||||
|
- 需要先完成少数依赖函数
|
||||||
|
- 用 `--tree` 查看具体依赖链
|
||||||
|
|
||||||
|
3. **多未实现依赖** (传递未实现>3)
|
||||||
|
- 依赖链较长,最后处理
|
||||||
|
- 或考虑整体重构策略
|
||||||
|
|
||||||
|
4. **有 I/O 依赖的函数**
|
||||||
|
- 最后处理,可能需要设计新的 I/O 抽象层
|
||||||
|
|
||||||
|
### 查询命令
|
||||||
|
|
||||||
|
CSV 列结构: `fortran_file,unit_name,unit_type,is_pure,common_deps,call_deps,has_io,rust_module,status`
|
||||||
|
|
||||||
|
#### 依赖树分析
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 输出重构优先级列表(按未实现依赖排序,推荐!)
|
||||||
|
python3 scripts/analyze_fortran.py --priority
|
||||||
|
|
||||||
|
# 输出指定单元的依赖树
|
||||||
|
python3 scripts/analyze_fortran.py --tree UNIT_NAME
|
||||||
|
|
||||||
|
# 输出完整传递依赖的 CSV
|
||||||
|
python3 scripts/analyze_fortran.py --full > fortran_analysis_full.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
**优先级列表说明:**
|
||||||
|
```
|
||||||
|
重构优先级列表 (按未实现依赖排序)
|
||||||
|
====================================================================================================
|
||||||
|
单元名 未实现 传递未实现 深度 直接调用 传递调用 IO
|
||||||
|
----------------------------------------------------------------------------------------------------
|
||||||
|
C 0 0 0 0 0 ○
|
||||||
|
ALIFR3 0 0 0 0 0 ○
|
||||||
|
```
|
||||||
|
- **未实现**: 直接依赖中未完成的函数数
|
||||||
|
- **传递未实现**: 所有递归依赖中未完成的函数数(关键指标!)
|
||||||
|
- **排序**: 传递未实现少 → 深度低 → 依赖少
|
||||||
|
- 只显示未完成(pending)的函数
|
||||||
|
- IO 列: ○ = 无IO, ✓ = 有IO
|
||||||
|
|
||||||
|
**依赖树输出示例:**
|
||||||
|
```
|
||||||
|
依赖树: ALISK1 ○
|
||||||
|
============================================================
|
||||||
|
直接依赖: 4, 传递依赖: 27, 未实现: 20
|
||||||
|
未实现依赖: ALIFRK, OPACF1, OPADD, ROSSTD, ...
|
||||||
|
------------------------------------------------------------
|
||||||
|
○ ALISK1 (4未实现)
|
||||||
|
├── ○ OPACF1 (6未实现)
|
||||||
|
│ ├── ○ OPADD (5未实现)
|
||||||
|
│ │ ├── ○ cia_h2h2 (1未实现)
|
||||||
|
│ │ │ └── if [未找到/未实现]
|
||||||
|
│ │ └── ✓ locate
|
||||||
|
│ └── ...
|
||||||
|
└── ○ ALIFRK
|
||||||
|
```
|
||||||
|
- ✓ = 已完成, ○ = 待处理
|
||||||
|
- `(N未实现)` = 该节点有 N 个直接依赖未完成
|
||||||
|
- 顶部汇总:传递未实现依赖数及列表
|
||||||
|
- 子节点按未实现依赖数排序(多的在前)
|
||||||
|
|
||||||
|
#### 按纯度筛选
|
||||||
|
|
||||||
|
无io的纯函数已经全部实现重构
|
||||||
|
#### 按 I/O 筛选
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 有 I/O 的未完成函数 (最后处理)
|
||||||
|
awk -F, '$7=="True" && $9=="pending"' fortran_analysis.csv
|
||||||
|
|
||||||
|
# 无 I/O 的未完成函数 (优先处理)
|
||||||
|
awk -F, '$7=="False" && $9=="pending"' fortran_analysis.csv
|
||||||
|
|
||||||
|
# 无 I/O 且无调用依赖的未完成函数
|
||||||
|
awk -F, '$6=="" && $7=="False" && $9=="pending"' fortran_analysis.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 按 COMMON 依赖筛选
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 只依赖 BASICS 的未完成函数
|
||||||
|
awk -F, '$5=="BASICS" && $9=="pending"' fortran_analysis.csv
|
||||||
|
|
||||||
|
# 依赖 BASICS 和 ATOMIC,但无其他依赖
|
||||||
|
awk -F, '$5 ~ /^BASICS\|ATOMIC$/ && $9=="pending"' fortran_analysis.csv
|
||||||
|
|
||||||
|
# 不依赖 MODELQ 的未完成函数 (MODELQ 最复杂)
|
||||||
|
awk -F, '$5 !~ /MODELQ/ && $9=="pending"' fortran_analysis.csv
|
||||||
|
|
||||||
|
# 列出所有不同的 COMMON 依赖组合
|
||||||
|
awk -F, '{print $5}' fortran_analysis.csv | sort | uniq -c | sort -rn
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 按状态筛选
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 所有已完成函数
|
||||||
|
awk -F, '$9=="done"' fortran_analysis.csv
|
||||||
|
|
||||||
|
# 所有进行中函数
|
||||||
|
awk -F, '$9=="in_progress"' fortran_analysis.csv
|
||||||
|
|
||||||
|
# 统计各状态数量
|
||||||
|
awk -F, '{print $9}' fortran_analysis.csv | sort | uniq -c
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 组合筛选
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 最佳候选:无 I/O + 无调用依赖 + 纯函数或简单 COMMON
|
||||||
|
awk -F, '$6=="" && $7=="False" && $9=="pending"' fortran_analysis.csv
|
||||||
|
|
||||||
|
# 次优候选:无 I/O + 有调用依赖但依赖已完成
|
||||||
|
awk -F, '$6!="" && $7=="False" && $9=="pending"' fortran_analysis.csv
|
||||||
|
|
||||||
|
# 查看特定函数的依赖信息
|
||||||
|
awk -F, '$2=="TARGET"' fortran_analysis.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 按代码大小排序
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 按行数排序(选小的先做)
|
||||||
|
cd tlusty/extracted
|
||||||
|
wc -l *.f | sort -n | head -30
|
||||||
|
|
||||||
|
# 查看小文件 + 纯函数
|
||||||
|
for f in $(ls *.f); do
|
||||||
|
lines=$(wc -l < "$f")
|
||||||
|
name=$(basename "$f" .f)
|
||||||
|
if grep -q "True.*pending" ../../fortran_analysis.csv && [ $lines -lt 100 ]; then
|
||||||
|
echo "$lines $name"
|
||||||
|
fi
|
||||||
|
done | sort -n
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 快速查看
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 查看重构进度摘要
|
||||||
|
echo "已完成: $(awk -F, '$9=="done"' fortran_analysis.csv | wc -l)"
|
||||||
|
echo "待处理: $(awk -F, '$9=="pending"' fortran_analysis.csv | wc -l)"
|
||||||
|
echo "纯函数待处理: $(awk -F, '$4=="True" && $9=="pending"' fortran_analysis.csv | wc -l)"
|
||||||
|
echo "无IO待处理: $(awk -F, '$7=="False" && $9=="pending"' fortran_analysis.csv | wc -l)"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 重构流程
|
||||||
|
|
||||||
|
### Step 1: 分析 Fortran 源码
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 查看源码
|
||||||
|
cat tlusty/extracted/TARGET.f
|
||||||
|
```
|
||||||
|
|
||||||
|
检查:
|
||||||
|
- [ ] INCLUDE 文件列表
|
||||||
|
- [ ] COMMON 块
|
||||||
|
- [ ] 调用的其他函数
|
||||||
|
- [ ] 是否有 I/O
|
||||||
|
|
||||||
|
### Step 2: 创建 Rust 模块
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 创建文件
|
||||||
|
touch src/math/TARGET.rs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: 实现函数
|
||||||
|
|
||||||
|
遵循命名规范:
|
||||||
|
- Fortran `FUNCTION` → Rust `pub fn`
|
||||||
|
- Fortran `SUBROUTINE` → Rust `pub fn` (返回值用参数或元组)
|
||||||
|
- COMMON 块 → 结构体参数
|
||||||
|
|
||||||
|
### Step 4: 添加到 mod.rs
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// src/math/mod.rs
|
||||||
|
mod target;
|
||||||
|
pub use target::target;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 5: 编写测试
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use approx::assert_relative_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_target_basic() {
|
||||||
|
// 基本测试
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 6: 运行测试
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 单个模块测试 (不要 cargo test 全量!)
|
||||||
|
cargo test target
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 7: 更新追踪表
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 如果是一对一映射,脚本自动识别
|
||||||
|
python3 scripts/analyze_fortran.py > fortran_analysis.csv
|
||||||
|
python3 scripts/generate_tracking.py > FORTRAN_TRACKING.md
|
||||||
|
|
||||||
|
# 如果是一对多映射,先更新 SPECIAL_MAPPINGS
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. SPECIAL_MAPPINGS 维护
|
||||||
|
|
||||||
|
### 何时需要更新
|
||||||
|
|
||||||
|
当一个 Rust 文件实现多个 Fortran 函数时,需要添加映射。
|
||||||
|
|
||||||
|
### 位置
|
||||||
|
|
||||||
|
`scripts/analyze_fortran.py` 第 72 行
|
||||||
|
|
||||||
|
### 格式
|
||||||
|
|
||||||
|
```python
|
||||||
|
SPECIAL_MAPPINGS = {
|
||||||
|
# Rust 文件名 -> [Fortran 函数名列表]
|
||||||
|
'gfree': ['gfree0', 'gfreed', 'gfree1'],
|
||||||
|
'interpolate': ['yint', 'lagran'],
|
||||||
|
'sgmer': ['sgmer0', 'sgmer1', 'sgmerd'],
|
||||||
|
'ctdata': ['hction', 'hctrecom'],
|
||||||
|
'cross': ['cross', 'crossd'],
|
||||||
|
'expint': ['eint', 'expinx'],
|
||||||
|
'erfcx': ['erfcx', 'erfcin'],
|
||||||
|
# 新增映射...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 添加新映射后执行
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/analyze_fortran.py > fortran_analysis.csv
|
||||||
|
python3 scripts/generate_tracking.py > FORTRAN_TRACKING.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 状态更新流程
|
||||||
|
|
||||||
|
### 手动更新状态
|
||||||
|
|
||||||
|
如需手动标记状态,编辑 `fortran_analysis.csv`:
|
||||||
|
|
||||||
|
```csv
|
||||||
|
# status 列: done, pending, in_progress, skip
|
||||||
|
filename.f,UNIT,SUBROUTINE,False,"...",...,False,src/math/xxx.rs,done
|
||||||
|
```
|
||||||
|
|
||||||
|
然后重新生成 Markdown:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/generate_tracking.py > FORTRAN_TRACKING.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 代码规范
|
||||||
|
|
||||||
|
### 文件头注释
|
||||||
|
|
||||||
|
```rust
|
||||||
|
//! 模块简要说明。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `filename.f`
|
||||||
|
```
|
||||||
|
|
||||||
|
### 函数注释
|
||||||
|
|
||||||
|
```rust
|
||||||
|
/// 函数功能说明。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `x` - 参数说明
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// 返回值说明
|
||||||
|
///
|
||||||
|
/// # 示例
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use tlusty_rust::math::func;
|
||||||
|
/// assert!((func(1.0) - 2.0).abs() < 1e-10);
|
||||||
|
/// ```
|
||||||
|
pub fn func(x: f64) -> f64 { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 精度要求
|
||||||
|
|
||||||
|
| 函数类型 | 容差 |
|
||||||
|
|---------|------|
|
||||||
|
| 简单数学运算 | 1e-10 |
|
||||||
|
| 多项式近似 | 1e-7 |
|
||||||
|
| f32 数组 | 1e-24 |
|
||||||
|
|
||||||
|
### 常见陷阱
|
||||||
|
|
||||||
|
| 问题 | 解决方案 |
|
||||||
|
|------|----------|
|
||||||
|
| Fortran 1-indexed | `arr(i)` → `arr[i-1]` |
|
||||||
|
| `-LOG(X)` | 是 `-ln(X)` 不是 `ln(-X)` |
|
||||||
|
| powi 类型歧义 | 用显式乘法 `(a)*(a)` |
|
||||||
|
| 矩阵列优先 | `A(j,i)` → `a[(i-1)*N + (j-1)]` |
|
||||||
|
|
||||||
|
## 6. 快速命令参考
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 查看重构进度
|
||||||
|
cat FORTRAN_TRACKING.md | head -20
|
||||||
|
|
||||||
|
# 进度统计
|
||||||
|
echo "已完成: $(awk -F, '$9=="done"' fortran_analysis.csv | wc -l)"
|
||||||
|
echo "待处理: $(awk -F, '$9=="pending"' fortran_analysis.csv | wc -l)"
|
||||||
|
|
||||||
|
# 推荐方式:查看优先级列表(按未实现依赖排序)
|
||||||
|
python3 scripts/analyze_fortran.py --priority | head -30
|
||||||
|
|
||||||
|
# 查看函数依赖树(含未实现依赖数)
|
||||||
|
python3 scripts/analyze_fortran.py --tree FUNCTION_NAME
|
||||||
|
|
||||||
|
# 找最佳候选:无IO + 无调用依赖
|
||||||
|
awk -F, '$6=="" && $7=="False" && $9=="pending"' fortran_analysis.csv | head -5
|
||||||
|
|
||||||
|
# 找纯函数
|
||||||
|
grep "True.*pending" fortran_analysis.csv | head -5
|
||||||
|
|
||||||
|
# 找无IO的函数
|
||||||
|
awk -F, '$7=="False" && $9=="pending"' fortran_analysis.csv | head -5
|
||||||
|
|
||||||
|
# 测试单个模块
|
||||||
|
cargo test module_name
|
||||||
|
|
||||||
|
# 更新追踪表
|
||||||
|
python3 scripts/analyze_fortran.py > fortran_analysis.csv && \
|
||||||
|
python3 scripts/generate_tracking.py > FORTRAN_TRACKING.md
|
||||||
|
|
||||||
|
# 编译检查
|
||||||
|
cargo build 2>&1 | grep error
|
||||||
|
```
|
||||||
@ -1,345 +1,86 @@
|
|||||||
# TLUSTY/SYNSPEC Rust 重构进度跟踪
|
# TLUSTY/SYNSPEC Rust 重构进度
|
||||||
|
|
||||||
|
## 当前状态 (2026-03-20)
|
||||||
|
|
||||||
|
- **已完成**: 86 个函数 + 15 个状态模块 + 1 个数据文件
|
||||||
|
- **测试通过**: 379 个
|
||||||
|
- **TLUSTY 纯函数重构完成**
|
||||||
|
- **最新完成**: cubic.rs
|
||||||
|
|
||||||
## 统计
|
## 统计
|
||||||
|
|
||||||
- TLUSTY 总单元: 304
|
| 程序 | 总单元 | 纯函数 |
|
||||||
- TLUSTY 纯函数: 195 (无 COMMON 依赖)
|
|------|--------|--------|
|
||||||
- SYNSPEC 总单元: 168
|
| TLUSTY | 304 | 195 |
|
||||||
- SYNSPEC 纯函数: 93
|
| SYNSPEC | 168 | 93 |
|
||||||
|
|
||||||
## 当前状态
|
| 类型 | 数量 |
|
||||||
|
|
||||||
- **已完成重构**: 62 个函数 + 8 个状态模块 + 1 个数据文件 (src/data.rs)
|
|
||||||
- **测试通过**: 281 个
|
|
||||||
- **最后更新**: 2026-03-19
|
|
||||||
- **TLUSTY 纯函数重构基本完成** (剩余 4 个有复杂依赖)
|
|
||||||
|
|
||||||
### 已完成模块列表
|
|
||||||
|
|
||||||
| 模块 | 说明 |
|
|
||||||
|------|------|
|
|------|------|
|
||||||
| angset | Compton 散射角度设置 |
|
| 已完成 | 81 |
|
||||||
| betah | 压力标高 |
|
| 剩余纯函数 | ~70 |
|
||||||
| bkhsgo | K/L 壳层光电离截面 |
|
| COMMON 依赖 | ~45 |
|
||||||
| butler | H 碰撞激发速率 |
|
| I/O 依赖 | 114 |
|
||||||
| carbon | C 光电离截面 |
|
|
||||||
| ceh12 | H I Lyman-α 碰撞速率 |
|
## 已完成模块 (87个)
|
||||||
| cion | 碰撞电离速率 |
|
|
||||||
| ckoest | Koester He I 光电离拟合 |
|
### math/ (83个)
|
||||||
| collhe | He 碰撞速率系数 |
|
|
||||||
| dielrc | 双电子复合速率 |
|
angset, betah, bkhsgo, butler, carbon, ceh12, cion, ckoest, collhe, cross, crossd,
|
||||||
| erfcx | 余误差函数 |
|
ctdata, cubic, dielrc, divstr, dmder, dwnfr, dwnfr0, dwnfr1, emat, erfcx, expint, expo, ffcros, gami,
|
||||||
| expint | 指数积分 E₁ |
|
gamsp, gauleg, gaunt, getwrd, gfree, gntk, grcor, hephot, hidalg, indexx, inthyd,
|
||||||
| expo | 安全指数函数 |
|
intlem, interpolate, irc, laguer, levsol, lineqs, locate, meanop, minv3, pffe, pfni, pfspec,
|
||||||
| ffcros | 自由-自由截面 (桩) |
|
psolve, quartc, quit, raph, ratmal, reiman, sbfch, sbfhe1, sbfhmi, sbfhmi_old,
|
||||||
| gami | γ 函数 |
|
sbfoh, sffhmi, sffhmi_add, sghe12, sgmer, spsigk, stark0, starka, szirc, tdpini, tiopf,
|
||||||
| gamsp | 用户自定义展宽参数 (占位) |
|
traini, tridag, ubeta, vern16, vern18, vern20, vern26, verner, voigt, voigte,
|
||||||
| gauleg | Gauss-Legendre 积分 |
|
wn, xk2dop, ylintp
|
||||||
| gaunt | Gaunt 因子 |
|
|
||||||
| getwrd | 文本单词提取 |
|
### state/ (15个)
|
||||||
| gfree | 氢自由-自由 Gaunt 因子 |
|
|
||||||
| gntk | Kramers-Gaunt 截面 |
|
constants, config, atomic, model, arrays, iterat, alipar, odfpar,
|
||||||
| grcor | 广义相对论修正因子 |
|
+ WMCOMP, MRGPAR, INVINT, CUROPA, PRESSR, DWNPAR, HYDPRF, TURBUL (嵌套在 model.rs)
|
||||||
| hephot | He I 光电离截面 |
|
|
||||||
| hidalg | Hidalgo 光电离数据 |
|
### data/ (1个)
|
||||||
| indexx | 堆排序索引 |
|
|
||||||
| interpolate | Lagrange/线性插值 |
|
data.rs - 静态数据数组
|
||||||
| irc | 氢原子碰撞电离速率 |
|
|
||||||
| laguer | Laguerre 根 |
|
|
||||||
| locate | 二分查找 |
|
|
||||||
| minv3 | 3x3 矩阵求逆 |
|
|
||||||
| pffe | Fe IV-IX 配分函数 |
|
|
||||||
| pfni | Ni IV-IX 配分函数 |
|
|
||||||
| pfspec | 特殊元素配分函数 |
|
|
||||||
| quartc | 四次方程求根 |
|
|
||||||
| quit | 退出处理 |
|
|
||||||
| raph | Newton-Raphson 迭代 |
|
|
||||||
| reiman | Reilman-Manson 光电离 |
|
|
||||||
| sbfch | CH 束缚-自由截面 |
|
|
||||||
| sbfhe1 | He I 束缚-自由截面 |
|
|
||||||
| sbfhmi | H⁻ 束缚-自由截面 |
|
|
||||||
| sbfhmi_old | H⁻ 束缚-自由截面 (旧版) |
|
|
||||||
| sbfoh | OH 束缚-自由截面 |
|
|
||||||
| sffhmi | H⁻ 自由-自由截面 |
|
|
||||||
| sghe12 | He I <n=2> 截面 |
|
|
||||||
| spsigk | 特殊光电离截面 |
|
|
||||||
| stark0 | Stark 轮廓参数 |
|
|
||||||
| szirc | 电子碰撞电离速率 |
|
|
||||||
| tiopf | Ti 光电离截面 |
|
|
||||||
| tridag | 三对角矩阵求解 |
|
|
||||||
| ubeta | U(β) 函数插值 |
|
|
||||||
| voigt | Voigt 轮廓 |
|
|
||||||
| voigte | Voigt 轮廓 (替代) |
|
|
||||||
| verner | Verner 光电离 (有 COMMON 依赖) |
|
|
||||||
| wn | 占据概率 (有 COMMON 依赖) |
|
|
||||||
| xk2dop | Doppler 宽度转换 |
|
|
||||||
| ylintp | 对数插值 |
|
|
||||||
|
|
||||||
## 状态说明
|
## 状态说明
|
||||||
|
|
||||||
- ⬜ 待处理
|
- ✅ 已完成
|
||||||
- 🔄 进行中
|
- 🔄 进行中
|
||||||
- ✓ 已完成
|
- ⬜ 待处理
|
||||||
- ✅ 已验证(有 Fortran 回归测试)
|
- ❌ 有 I/O 依赖 (暂不处理)
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## TLUSTY 纯函数进度
|
|
||||||
|
|
||||||
### 优先级 P0 (最小文件,先处理)
|
|
||||||
|
|
||||||
| 文件 | 行数 | 状态 | 完成日期 | 备注 |
|
|
||||||
|------|------|------|----------|------|
|
|
||||||
| expo.f | 10 | ✅ | 2026-03-19 | 安全指数函数 |
|
|
||||||
| quit.f | 10 | ✅ | 2026-03-19 | 退出子程序 |
|
|
||||||
| ffcros.f | 13 | ✅ | 2026-03-19 | 自由-自由截面 (占位) |
|
|
||||||
| gamsp.f | 14 | ✅ | 2026-03-19 | 展宽因子 (有 COMMON) |
|
|
||||||
| sgmer1.f | 14 | ❌ | | Stark展宽 (有 COMMON) |
|
|
||||||
| sgmerd.f | 15 | ❌ | | Stark展宽 (有 COMMON) |
|
|
||||||
| lagran.f | 16 | ✅ | 2026-03-19 | Lagrange插值 |
|
|
||||||
| gntk.f | 17 | ✅ | 2026-03-19 | Gaunt因子 |
|
|
||||||
| raph.f | 17 | ✅ | 2026-03-19 | hedif辅助函数 |
|
|
||||||
| cross.f | 18 | ✅ | 2026-03-19 | 截面计算 (有 COMMON) |
|
|
||||||
| eint.f | 18 | ✅ | 2026-03-19 | 指数积分 (含 expinx) |
|
|
||||||
| sghe12.f | 18 | ✅ | 2026-03-19 | He展宽 |
|
|
||||||
| yint.f | 18 | ✅ | 2026-03-19 | 二次插值 |
|
|
||||||
| erfcin.f | 20 | ✅ | 2026-03-19 | 误差函数补 |
|
|
||||||
| erfcx.f | 20 | ✅ | 2026-03-19 | 缩放误差函数 |
|
|
||||||
| gfree1.f | 21 | ✅ | 2026-03-19 | Gaunt自由 (有 COMMON) |
|
|
||||||
| sbfhmi_old.f | 22 | ✅ | 2026-03-19 | H-截面 |
|
|
||||||
| tridag.f | 22 | ✅ | 2026-03-19 | 三对角矩阵 |
|
|
||||||
| timing.f | 24 | ❌ | | 计时 (有 I/O) |
|
|
||||||
| expint.f | 30 | ✅ | 2026-03-19 | 指数积分 |
|
|
||||||
|
|
||||||
### 优先级 P1 (中等大小)
|
|
||||||
|
|
||||||
| 文件 | 行数 | 状态 | 完成日期 | 备注 |
|
|
||||||
|------|------|------|----------|------|
|
|
||||||
| ylintp.f | 31 | ✅ | 2026-03-19 | 线性插值 |
|
|
||||||
| xk2dop.f | 32 | ✅ | 2026-03-19 | Doppler宽度 |
|
|
||||||
| betah.f | 33 | ✅ | 2026-03-19 | 压力标高 |
|
|
||||||
| gauleg.f | 34 | ✅ | 2026-03-19 | Gauss-Legendre积分 |
|
|
||||||
| quartc.f | 35 | ✅ | 2026-03-19 | 四次方程求解 |
|
|
||||||
| minv3.f | 37 | ✅ | 2026-03-19 | 3x3矩阵求逆 |
|
|
||||||
| crossd.f | 31 | ✅ | 2026-03-19 | 截面计算+双电子复合 (有 COMMON) |
|
|
||||||
| wn.f | 41 | ✅ | 2026-03-19 | 占据概率 (有 COMMON) |
|
|
||||||
| sbfhmi.f | 42 | ✅ | 2026-03-19 | H-截面 |
|
|
||||||
| angset.f | 44 | ✅ | 2026-03-19 | 角度设置 (有 COMMON) |
|
|
||||||
| gami.f | 45 | ✅ | 2026-03-19 | 微扰展宽 |
|
|
||||||
| gaunt.f | 45 | ✅ | 2026-03-19 | Gaunt因子 |
|
|
||||||
| ubeta.f | 40 | ✅ | 2026-03-19 | U(β) 函数 |
|
|
||||||
| rayini.f | 42 | ❌ | | 射线初始化 (有 I/O) |
|
|
||||||
| indexx.f | 45 | ✅ | 2026-03-19 | 索引排序 |
|
|
||||||
| laguer.f | 59 | ✅ | 2026-03-19 | Laguerre多项式求根 |
|
|
||||||
| sbfhe1.f | 157 | ✅ | 2026-03-19 | He截面 (有 COMMON) |
|
|
||||||
| hephot.f | 163 | ✅ | 2026-03-19 | He光电离 |
|
|
||||||
| verner.f | 237 | ✅ | 2026-03-19 | Verner截面 (有 COMMON) |
|
|
||||||
| voigt.f | 64 | ✅ | 2026-03-19 | Voigt线型 |
|
|
||||||
| voigte.f | 92 | ✅ | 2026-03-19 | Voigt线型 |
|
|
||||||
| locate.f | 25 | ✅ | 2026-03-19 | 二分查找 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## SYNSPEC 纯函数进度
|
|
||||||
|
|
||||||
(待 TLUSTY 完成后再处理)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 重构日志
|
## 重构日志
|
||||||
|
|
||||||
### 2026-03-19
|
### 2026-03-20
|
||||||
|
|
||||||
**已完成:**
|
**新增:**
|
||||||
- 创建 Rust 项目结构 (Cargo.toml, src/)
|
- lineqs.rs - 线性方程组求解 (高斯消元法)
|
||||||
- 重构 expo.f → src/math/expo.rs
|
- starka.rs - Stark 展宽近似表达式
|
||||||
- 重构 yint.f → src/math/interpolate.rs (yint)
|
- inthyd.rs - 氢线 Stark 展宽表格插值
|
||||||
- 重构 lagran.f → src/math/interpolate.rs (lagran)
|
- HydPrf 结构体 (model.rs)
|
||||||
- 重构 tridag.f → src/math/tridag.rs
|
|
||||||
- 重构 eint.f + expinx.f → src/math/expint.rs
|
|
||||||
- 重构 quit.f → src/math/quit.rs
|
|
||||||
- 重构 ffcros.f → src/math/ffcros.rs
|
|
||||||
- 重构 gntk.f → src/math/gntk.rs
|
|
||||||
- 重构 raph.f → src/math/raph.rs
|
|
||||||
- 重构 erfcx.f + erfcin.f → src/math/erfcx.rs
|
|
||||||
- 重构 sghe12.f → src/math/sghe12.rs
|
|
||||||
- 重构 ylintp.f → src/math/ylintp.rs
|
|
||||||
- 重构 gauleg.f → src/math/gauleg.rs
|
|
||||||
- 重构 locate.f → src/math/locate.rs
|
|
||||||
- 重构 voigt.f → src/math/voigt.rs
|
|
||||||
- 重构 voigte.f → src/math/voigte.rs
|
|
||||||
- 重构 indexx.f → src/math/indexx.rs
|
|
||||||
- 重构 quartc.f → src/math/quartc.rs
|
|
||||||
- 重构 betah.f → src/math/betah.rs
|
|
||||||
- 重构 gami.f → src/math/gami.rs
|
|
||||||
- 重构 xk2dop.f → src/math/xk2dop.rs
|
|
||||||
- 重构 minv3.f → src/math/minv3.rs
|
|
||||||
- 重构 laguer.f → src/math/laguer.rs
|
|
||||||
- 重构 collhe.f → src/math/collhe.rs (He 碰撞速率, 929 元素大数组)
|
|
||||||
- 重构 butler.f → src/math/butler.rs (H 碰撞激发)
|
|
||||||
- 重构 cion.f → src/math/cion.rs (碰撞电离)
|
|
||||||
- 重构 irc.f → src/math/irc.rs (H 碰撞电离速率)
|
|
||||||
- 重构 getwrd.f → src/math/getwrd.rs (文本解析)
|
|
||||||
- 重构 spsigk.f → src/math/spsigk.rs (特殊光电离)
|
|
||||||
- 重构 tiopf.f → src/math/tiopf.rs (Ti 光电离)
|
|
||||||
- 重构 sbfhmi_old.f → src/math/sbfhmi_old.rs (H- 束缚-自由旧版)
|
|
||||||
- 重构 bkhsgo.f → src/math/bkhsgo.rs (K/L 壳层光电离)
|
|
||||||
- 重构 carbon.f → src/math/carbon.rs (C 光电离)
|
|
||||||
- 重构 hephot.f → src/math/hephot.rs (He 光电离)
|
|
||||||
- 重构 hidalg.f → src/math/hidalg.rs (Hidalgo 光电离)
|
|
||||||
- 重构 reiman.f → src/math/reiman.rs (Reilman-Manson 光电离)
|
|
||||||
- 重构 sbfhmi.f → src/math/sbfhmi.rs (H- 束缚-自由)
|
|
||||||
- 重构 sffhmi.f → src/math/sffhmi.rs (H- 自由-自由)
|
|
||||||
- 重构 grcor.f → src/math/grcor.rs (广义相对论修正)
|
|
||||||
- 重构 gaunt.f → src/math/gaunt.rs (Gaunt 因子)
|
|
||||||
- 重构 stark0.f → src/math/stark0.rs (Stark 轮廓)
|
|
||||||
- 重构 szirc.f → src/math/szirc.rs (电子碰撞电离)
|
|
||||||
- 重构 ubeta.f → src/math/ubeta.rs (U(β) 函数)
|
|
||||||
- 重构 dielrc.f → src/math/dielrc.rs (双电子复合速率, 18 个大型数组)
|
|
||||||
- 重构 pfni.f → src/math/pfni.rs (Ni IV-IX 配分函数, 12 个数组)
|
|
||||||
- 创建 Fortran 对比测试框架 (tests/fortran_ref/, tests/fortran_comparison.rs)
|
|
||||||
- **224 个测试通过** (208 单元测试 + 12 Fortran 对比测试 + 4 文档测试)
|
|
||||||
|
|
||||||
**规范:**
|
|
||||||
- 代码注释使用中文
|
|
||||||
- 测试必须与原 Fortran 代码对比验证
|
|
||||||
- 精度要求: epsilon = 1e-10 (简单函数), 1e-7 (多项式近似)
|
|
||||||
|
|
||||||
**注意事项:**
|
|
||||||
- `gamsp.f`, `sgmer1.f`, `sgmerd.f`, `cross.f`, `gfree1.f` 实际有 COMMON 依赖,不是纯函数
|
|
||||||
- Fortran 1-indexed 数组转 Rust 0-indexed 时要特别注意边界条件
|
|
||||||
- `erfcin` 中 `XL=-LOG(X)` 是 `-ln(X)`,不是 `ln(-X)`
|
|
||||||
- `ylintp` 在 0-indexed 中 jl=0 是有效索引,不需要调整
|
|
||||||
- `collhe` 中 Clenshaw 求和的 `ir` 和 `jj` 必须用有符号整数 (`isize`),否则递减时会溢出
|
|
||||||
- `collhe` 的索引计算 `((iu+1)^2 - 3*(iu+1) + 4)/2` 在 Rust 中需用 `i32` 避免中间结果溢出
|
|
||||||
|
|
||||||
### 2026-03-19 (续)
|
|
||||||
|
|
||||||
**修复:**
|
|
||||||
- 修复 `pffe.rs` 中的数组引用错误:`P6A/P6B` 应使用 `PFFE_P6A/PFFE_P6B`
|
|
||||||
|
|
||||||
**待重构的纯函数分析:**
|
|
||||||
- `SBFCH` (278行): CH 束缚-自由截面,有 1575 个数据点
|
|
||||||
- `SBFOH` (327行): OH 束缚-自由截面,有 1950 个数据点
|
|
||||||
- `TIMING` (24行): 有 I/O 依赖,不适合纯函数
|
|
||||||
- `CIA_*` (89-90行): 有文件 I/O,需要特别处理
|
|
||||||
|
|
||||||
**当前模块统计:**
|
|
||||||
- 49 个 Rust 模块 (不含 mod.rs)
|
|
||||||
- 218 个单元测试
|
|
||||||
- 12 个 Fortran 对比测试
|
|
||||||
- 4 个文档测试
|
|
||||||
|
|
||||||
**新增模块:**
|
|
||||||
- 重构 sbfch.f → src/math/sbfch.rs (CH 束缚-自由截面, 1575 个数据点)
|
|
||||||
- 重构 sbfoh.f → src/math/sbfoh.rs (OH 束缚-自由截面, 1950 个数据点)
|
|
||||||
|
|
||||||
**脚本改进:**
|
|
||||||
- 修改 `extract_fortran_data.py`: 所有变量都添加文件名前缀 (如 `SBFCH_C1`, `DIELRC_ADI`)
|
|
||||||
- 避免不同文件中同名变量冲突
|
|
||||||
|
|
||||||
### 2026-03-19 (进度总结)
|
|
||||||
|
|
||||||
**当前状态:**
|
|
||||||
- **49 个 Rust 模块**已完成
|
|
||||||
- **234 个测试通过** (218 单元测试 + 12 Fortran 对比测试 + 4 文档测试)
|
|
||||||
|
|
||||||
**纯函数重构完成度分析:**
|
|
||||||
|
|
||||||
TLUSTY 原有 195 个无 COMMON 依赖的纯函数,其中大部分已经重构。剩余的函数可分为两类:
|
|
||||||
|
|
||||||
1. **有 COMMON/IO 依赖的函数** (暂时不重构):
|
|
||||||
- `gamsp.f`, `sgmer1.f`, `sgmerd.f` - Stark 展宽
|
|
||||||
- `cross.f`, `crossd.f` - 截面计算
|
|
||||||
- `gfree1.f` - Gaunt 自由
|
|
||||||
- `wn.f` - 占据概率
|
|
||||||
- `angset.f` - 角度设置
|
|
||||||
- `rayini.f` - 射线初始化 (有文件 I/O)
|
|
||||||
- `sbfhe1.f` - He 截面
|
|
||||||
- `verner.f`, `vern16/18/20/26.f` - Verner 截面
|
|
||||||
- `timing.f` - 计时 (有 I/O)
|
|
||||||
- `CIA_*` - 碰撞诱导吸收 (有文件 I/O)
|
|
||||||
|
|
||||||
2. **可能遗漏的纯函数**:
|
|
||||||
- 需要进一步检查 `gfree0.f`, `gfreed.f` 等函数
|
|
||||||
|
|
||||||
**下一步计划:**
|
|
||||||
1. 检查 `gfree0.f`, `gfreed.f` 是否为纯函数
|
|
||||||
2. 如果是纯函数,继续重构
|
|
||||||
3. 考虑 SYNSPEC 纯函数的重构
|
|
||||||
|
|
||||||
**图例说明:**
|
|
||||||
- ✅ 已完成
|
|
||||||
- ❌ 有 COMMON/IO 依赖 (暂不处理)
|
|
||||||
- ⬜ 待处理
|
|
||||||
|
|
||||||
### 2026-03-19 (状态模块重构)
|
|
||||||
|
|
||||||
**新增 state 模块** - 将 Fortran COMMON 块转换为 Rust struct
|
|
||||||
|
|
||||||
| 文件 | 对应 Fortran | 说明 |
|
|
||||||
|------|-------------|------|
|
|
||||||
| src/state/mod.rs | - | 模块入口 |
|
|
||||||
| src/state/constants.rs | BASICS.FOR | 物理常数和维度参数 |
|
|
||||||
| src/state/config.rs | BASICS.FOR | 运行时配置 (BASNUM, INPPAR 等) |
|
|
||||||
| src/state/atomic.rs | ATOMIC.FOR | 原子/离子/能级数据 (ATOPAR, IONPAR 等) |
|
|
||||||
| src/state/model.rs | MODELQ.FOR | 大气模型状态 (MODPAR, LEVPOP 等) |
|
|
||||||
| src/state/arrays.rs | ARRAY1.FOR | 大型计算数组 |
|
|
||||||
|
|
||||||
**COMMON 块映射:**
|
|
||||||
|
|
||||||
| Fortran COMMON | Rust struct | 文件 |
|
|
||||||
|----------------|-------------|------|
|
|
||||||
| /BASNUM/ | BasNum | config.rs |
|
|
||||||
| /INPPAR/ | InpPar | config.rs |
|
|
||||||
| /MATKEY/ | MatKey | config.rs |
|
|
||||||
| /RUNKEY/ | RunKey | config.rs |
|
|
||||||
| /CONKEY/ | ConKey | config.rs |
|
|
||||||
| /OPCPAR/ | OpcPar | config.rs |
|
|
||||||
| /ANGLES/ | Angles | config.rs |
|
|
||||||
| /ATOPAR/ | AtoPar | atomic.rs |
|
|
||||||
| /IONPAR/ | IonPar | atomic.rs |
|
|
||||||
| /LEVPAR/ | LevPar | atomic.rs |
|
|
||||||
| /TRAPAR/ | TraPar | atomic.rs |
|
|
||||||
| /PHOSET/ | PhoSet | atomic.rs |
|
|
||||||
| /VOIPAR/ | VoiPar | atomic.rs |
|
|
||||||
| /MODPAR/ | ModPar | model.rs |
|
|
||||||
| /LEVPOP/ | LevPop | model.rs |
|
|
||||||
| /GFFPAR/ | GffPar | model.rs |
|
|
||||||
| /TOTRAD/ | TotRad | model.rs |
|
|
||||||
| /CURRAD/ | CurRad | model.rs |
|
|
||||||
| /FRQALL/ | FrqAll | model.rs |
|
|
||||||
| 无标签 COMMON | MainArrays | arrays.rs |
|
|
||||||
| /EXPRAD/ | ExpRad | arrays.rs |
|
|
||||||
| /BPOCOM/ | BpoCom | arrays.rs |
|
|
||||||
|
|
||||||
**综合结构:**
|
|
||||||
- `TlustyConfig` - 综合配置 (包含所有控制参数)
|
|
||||||
- `AtomicData` - 综合原子数据
|
|
||||||
- `ModelState` - 综合模型状态
|
|
||||||
- `ComputeArrays` - 综合计算数组
|
|
||||||
|
|
||||||
**测试更新:**
|
|
||||||
- 255 个测试通过 (239 单元测试 + 12 Fortran 对比测试 + 4 文档测试)
|
|
||||||
|
|
||||||
### 2026-03-19 (完成 .FOR 文件重构)
|
|
||||||
|
|
||||||
**完成所有 TLUSTY .FOR 文件重构:**
|
|
||||||
|
|
||||||
| 文件 | Rust 模块 | COMMON 块 |
|
|
||||||
|------|-----------|-----------|
|
|
||||||
| ITERAT.FOR | iterat.rs | LAMBDA, CHNMAT, ACCEL, ACCLP, ACCLT, CHNAD |
|
|
||||||
| ALIPAR.FOR | alipar.rs | FIXALP (ALI 数组) |
|
|
||||||
| ODFPAR.FOR | odfpar.rs | ODFION, ODFCTR, ODFFRQ, ODFMOD, ODFSTK, SPLCOM, OPALIM, OPLIMT, LEVCOM |
|
|
||||||
|
|
||||||
**新增常量:**
|
**新增常量:**
|
||||||
- MLEVE3, MLVEX3, MTRAN3 (对角/三对角预处理)
|
- MHT=7, MHE=20, MHWL=90 (氢线表格维度)
|
||||||
- MCROSS, MBF (截面和束缚-自由跃迁)
|
|
||||||
|
|
||||||
**内存估算:**
|
### 2026-03-19
|
||||||
- SplCom (Fe 线数据): ~90 MB
|
|
||||||
- LevCom (Kurucz 能级): ~0.5 MB
|
|
||||||
- FixAlp (ALI 数组): ~数百 MB (取决于 MLVEXP)
|
|
||||||
|
|
||||||
**所有 TLUSTY .FOR 文件已重构完成!**
|
**完成:**
|
||||||
|
- 创建 Rust 项目结构
|
||||||
|
- 重构 77 个 math 模块
|
||||||
|
- 创建 14 个 state 模块 (COMMON 块转换)
|
||||||
|
- 提取 data.rs 静态数据
|
||||||
|
|
||||||
|
**关键修复:**
|
||||||
|
- Fortran 1-indexed → Rust 0-indexed
|
||||||
|
- `-LOG(X)` 是 `-ln(X)` 不是 `ln(-X)`
|
||||||
|
- Clenshaw 求和用 `isize` 避免溢出
|
||||||
|
- 所有 DATA 变量添加文件名前缀
|
||||||
|
|
||||||
|
### 规范
|
||||||
|
|
||||||
|
- 代码注释使用中文
|
||||||
|
- 测试与 Fortran 对比验证
|
||||||
|
- 精度: 简单函数 1e-10, 多项式 1e-7
|
||||||
|
|||||||
305
fortran_analysis.csv
Normal file
305
fortran_analysis.csv
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
fortran_file,unit_name,unit_type,is_pure,common_deps,call_deps,has_io,rust_module,status
|
||||||
|
_unnamed_block_data_.f,C,BLOCK DATA,False,"BASICS|ATOMIC","",False,,pending
|
||||||
|
accel2.f,ACCEL2,SUBROUTINE,False,"BASICS|ITERAT|MODELQ","",True,,pending
|
||||||
|
accelp.f,ACCELP,SUBROUTINE,False,"BASICS|MODELQ|ITERAT|POPULS","",True,,pending
|
||||||
|
alifr1.f,ALIFR1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","ALIFR3",False,,pending
|
||||||
|
alifr3.f,ALIFR3,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","",False,src/math/alifr3.rs,done
|
||||||
|
alifr6.f,ALIFR6,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","",False,src/math/alifr6.rs,done
|
||||||
|
alifrk.f,ALIFRK,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","",False,src/math/alifrk.rs,done
|
||||||
|
alisk1.f,ALISK1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","ALIFRK|RTEFR1|OPACF1|ROSSTD",True,,pending
|
||||||
|
alisk2.f,ALISK2,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","ALIFRK|RTEFR1|OPACF1|ROSSTD",True,,pending
|
||||||
|
alist1.f,ALIST1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT","ROSSTD|RTEFR1|ALIFR1|OPACFD",True,,pending
|
||||||
|
alist2.f,ALIST2,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","RTEFR1|ALIFR1|QUIT|ROSSTD|OPACFD",True,,pending
|
||||||
|
allard.f,ALLARD,SUBROUTINE,False,"BASICS|callarda|callardc|quasun|callardb|callardg|calphatd","ALLARDT",True,,pending
|
||||||
|
allardt.f,ALLARDT,SUBROUTINE,False,"BASICS|calphatd","",False,src/math/allardt.rs,done
|
||||||
|
angset.f,ANGSET,SUBROUTINE,True,"BASICS","GAULEG",False,src/math/angset.rs,done
|
||||||
|
betah.f,BETAH,FUNCTION,True,"","BETAH",False,src/math/betah.rs,done
|
||||||
|
bhe.f,BHE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","",False,,pending
|
||||||
|
bhed.f,BHED,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|SURFEX|CMATZD","",False,,pending
|
||||||
|
bhez.f,BHEZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|SURFEX","",False,,pending
|
||||||
|
bkhsgo.f,BKHSGO,SUBROUTINE,True,"","",False,src/math/bkhsgo.rs,done
|
||||||
|
bpop.f,BPOP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR|ITERAT","BPOPF|RATMAT|BPOPT|LEVSOL|MATINV|BPOPC|BPOPE|LEVGRP",False,,pending
|
||||||
|
bpopc.f,BPOPC,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR|ADCHAR","STATE",False,,pending
|
||||||
|
bpope.f,BPOPE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT|ARRAY1","SGMER1|DWNFR1",False,,pending
|
||||||
|
bpopf.f,BPOPF,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR","",False,src/math/bpopf.rs,done
|
||||||
|
bpopt.f,BPOPT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR","COLIS",False,,pending
|
||||||
|
bre.f,BRE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","COMPT0",False,,pending
|
||||||
|
brez.f,BREZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","COMPT0",False,,pending
|
||||||
|
brte.f,BRTE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1","COMPT0",False,,pending
|
||||||
|
brtez.f,BRTEZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1","COMPT0",False,,pending
|
||||||
|
butler.f,BUTLER,SUBROUTINE,True,"","",False,src/math/butler.rs,done
|
||||||
|
carbon.f,CARBON,SUBROUTINE,True,"","",False,src/math/carbon.rs,done
|
||||||
|
ceh12.f,CEH12,FUNCTION,True,"","CEH12",False,src/math/ceh12.rs,done
|
||||||
|
change.f,CHANGE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","READBF|STEQEQ",True,,pending
|
||||||
|
chckse.f,CHCKSE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","SABOLF",True,,pending
|
||||||
|
chctab.f,CHCTAB,SUBROUTINE,False,"BASICS|MODELQ|abntab","",True,,pending
|
||||||
|
cheav.f,CHEAV,FUNCTION,False,"BASICS|ATOMIC","QUIT|CHEAV",True,,pending
|
||||||
|
cheavj.f,CHEAVJ,FUNCTION,False,"BASICS|ATOMIC","CHEAVJ|QUIT",True,,pending
|
||||||
|
cia_h2h.f,CIA_H2H,SUBROUTINE,False,"","LOCATE|IF",True,,pending
|
||||||
|
cia_h2h2.f,CIA_H2H2,SUBROUTINE,False,"","LOCATE|IF",True,,pending
|
||||||
|
cia_h2he.f,CIA_H2HE,SUBROUTINE,False,"","LOCATE|IF",True,,pending
|
||||||
|
cia_hhe.f,CIA_HHE,SUBROUTINE,False,"","LOCATE|IF",True,,pending
|
||||||
|
cion.f,CION,FUNCTION,True,"","CION",False,src/math/cion.rs,done
|
||||||
|
ckoest.f,CKOEST,FUNCTION,True,"BASICS","CKOEST",False,src/math/ckoest.rs,done
|
||||||
|
colh.f,COLH,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","CSPEC|IRC|BUTLER",False,,pending
|
||||||
|
colhe.f,COLHE,SUBROUTINE,False,"BASICS|ATOMIC","CSPEC|COLLHE|IRC",False,,pending
|
||||||
|
colis.f,COLIS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|CTRTEMP","CSPEC|COLH|IRC|COLHE",False,,pending
|
||||||
|
collhe.f,COLLHE,SUBROUTINE,True,"","",False,src/math/collhe.rs,done
|
||||||
|
column.f,COLUMN,SUBROUTINE,False,"BASICS|MODELQ|relcor","",True,,pending
|
||||||
|
compt0.f,COMPT0,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|auxcbc","",False,,pending
|
||||||
|
comset.f,COMSET,SUBROUTINE,False,"BASICS|MODELQ|comgfs|auxcbc","",False,,pending
|
||||||
|
concor.f,CONCOR,SUBROUTINE,False,"BASICS|MODELQ","CONOUT",True,,pending
|
||||||
|
conout.f,CONOUT,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|CUBCON","MEANOPT|OPACF0|CONVEC|MEANOP",True,,pending
|
||||||
|
conref.f,CONREF,SUBROUTINE,False,"BASICS|MODELQ|ARRAY1|imucnn|CUBCON","STEQEQ|WNSTOR|CONVC1|CONOUT|CONVEC|ELDENS",True,,pending
|
||||||
|
contmd.f,CONTMD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|PRSAUX|CUBCON","MEANOP|CUBIC|STEQEQ|WNSTOR|OPACF0|CONOUT|CONVEC",True,,pending
|
||||||
|
contmp.f,CONTMP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ichndm|CUBCON","MEANOP|CUBIC|STEQEQ|WNSTOR|OPACF0|CONOUT|CONVEC|MEANOPT|ELDENS",True,,pending
|
||||||
|
convc1.f,CONVC1,SUBROUTINE,False,"BASICS|CUBCON","TRMDER|TRMDRT",False,,pending
|
||||||
|
convec.f,CONVEC,SUBROUTINE,False,"BASICS|CUBCON","TRMDER|TRMDRT",False,,pending
|
||||||
|
coolrt.f,COOLRT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT|COOLCO","RTEFR1|OPACFA",True,,pending
|
||||||
|
corrwm.f,CORRWM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT",True,,pending
|
||||||
|
cross.f,CROSS,FUNCTION,False,"BASICS|ATOMIC|MODELQ","CROSS",False,src/math/cross.rs,done
|
||||||
|
crossd.f,CROSSD,FUNCTION,False,"BASICS|ATOMIC|MODELQ","CROSSD",False,src/math/cross.rs,done
|
||||||
|
cspec.f,CSPEC,SUBROUTINE,False,"BASICS|ATOMIC","QUIT",False,,pending
|
||||||
|
ctdata.f,CTDATA,BLOCK DATA,False,"CTRecomb|CTIon","",False,src/math/ctdata.rs,done
|
||||||
|
cubic.f,CUBIC,SUBROUTINE,False,"BASICS|CUBCON","",False,src/math/cubic.rs,done
|
||||||
|
dielrc.f,DIELRC,SUBROUTINE,True,"","",False,src/math/dielrc.rs,done
|
||||||
|
dietot.f,DIETOT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","DIELRC",True,,pending
|
||||||
|
divstr.f,DIVSTR,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/divstr.rs,done
|
||||||
|
dmder.f,DMDER,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|DEPTDR","",False,src/math/dmder.rs,done
|
||||||
|
dmeval.f,DMEVAL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ARRAY1","",True,,pending
|
||||||
|
dopgam.f,DOPGAM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","GAMSP",False,src/math/dopgam.rs,done
|
||||||
|
dwnfr.f,DWNFR,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/dwnfr.rs,done
|
||||||
|
dwnfr0.f,DWNFR0,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/dwnfr0.rs,done
|
||||||
|
dwnfr1.f,DWNFR1,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/dwnfr1.rs,done
|
||||||
|
eint.f,EINT,SUBROUTINE,True,"","EXPINX",False,src/math/expint.rs,done
|
||||||
|
elcor.f,ELCOR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ADCHAR","MOLEQ|STATE|STEQEQ|WNSTOR",True,,pending
|
||||||
|
eldenc.f,ELDENC,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|eospar|eletab|hmolab","MOLEQ|STATE|RHONEN",True,,pending
|
||||||
|
eldens.f,ELDENS,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|eospar|terden","STATE|ENTENE|MOLEQ|MPARTF|LINEQS",True,,pending
|
||||||
|
emat.f,EMAT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","",False,src/math/emat.rs,done
|
||||||
|
entene.f,ENTENE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","MPARTF",False,,pending
|
||||||
|
erfcin.f,ERFCIN,FUNCTION,True,"","ERFCIN",False,src/math/erfcx.rs,done
|
||||||
|
erfcx.f,ERFCX,FUNCTION,True,"","ERFCX",False,src/math/erfcx.rs,done
|
||||||
|
expint.f,EXPINT,FUNCTION,True,"","EXPINT",False,src/math/expint.rs,done
|
||||||
|
expinx.f,EXPINX,SUBROUTINE,True,"","",False,src/math/expint.rs,done
|
||||||
|
expo.f,EXPO,FUNCTION,True,"","EXPO",False,src/math/expo.rs,done
|
||||||
|
ffcros.f,FFCROS,FUNCTION,True,"","FFCROS",False,src/math/ffcros.rs,done
|
||||||
|
gami.f,GAMI,FUNCTION,True,"","GAMI",False,src/math/gami.rs,done
|
||||||
|
gamsp.f,GAMSP,SUBROUTINE,True,"BASICS","",False,src/math/gamsp.rs,done
|
||||||
|
gauleg.f,GAULEG,SUBROUTINE,True,"","",False,src/math/gauleg.rs,done
|
||||||
|
gaunt.f,GAUNT,FUNCTION,True,"","GAUNT",False,src/math/gaunt.rs,done
|
||||||
|
getlal.f,GETLAL,SUBROUTINE,False,"BASICS|callarda|callardc|quasun|callardb|callardg|calphatd","",True,,pending
|
||||||
|
getwrd.f,GETWRD,SUBROUTINE,True,"","",False,src/math/getwrd.rs,done
|
||||||
|
gfree0.f,GFREE0,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/gfree.rs,done
|
||||||
|
gfree1.f,GFREE1,FUNCTION,False,"BASICS|MODELQ","GFREE1",False,src/math/gfree.rs,done
|
||||||
|
gfreed.f,GFREED,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/gfree.rs,done
|
||||||
|
ghydop.f,GHYDOP,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|intcfg","",False,,pending
|
||||||
|
gntk.f,GNTK,FUNCTION,True,"","GNTK",False,src/math/gntk.rs,done
|
||||||
|
gomini.f,GOMINI,SUBROUTINE,False,"BASICS|MODELQ|intcfg","",True,,pending
|
||||||
|
grcor.f,GRCOR,SUBROUTINE,True,"","",False,src/math/grcor.rs,done
|
||||||
|
greyd.f,GREYD,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|ALIPAR","MEANOP|RHONEN|STEQEQ|WNSTOR|OPACF0",True,,pending
|
||||||
|
gridp.f,GRIDP,SUBROUTINE,True,"BASICS","",False,src/math/gridp.rs,done
|
||||||
|
h2minus.f,H2MINUS,SUBROUTINE,False,"BASICS","LOCATE",True,,pending
|
||||||
|
hction.f,HCTION,FUNCTION,False,"CTIon|CTRTEMP","HCTION",False,src/math/ctdata.rs,done
|
||||||
|
hctrecom.f,HCTRECOM,FUNCTION,False,"CTRecomb|CTRTEMP","HCTRECOM",False,src/math/ctdata.rs,done
|
||||||
|
hedif.f,HEDIF,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|hediff","",True,,pending
|
||||||
|
hephot.f,HEPHOT,FUNCTION,True,"","HEPHOT",False,src/math/hephot.rs,done
|
||||||
|
hesol6.f,HESOL6,SUBROUTINE,False,"BASICS|MODELQ|PRSAUX","MATINV",False,,pending
|
||||||
|
hesolv.f,HESOLV,SUBROUTINE,False,"BASICS|MODELQ|PRSAUX","MATINV|STEQEQ|RHONEN|WNSTOR",True,,pending
|
||||||
|
hidalg.f,HIDALG,FUNCTION,True,"","HIDALG",False,src/math/hidalg.rs,done
|
||||||
|
ijali2.f,IJALI2,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","QUIT",True,,pending
|
||||||
|
ijalis.f,IJALIS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",True,,pending
|
||||||
|
incldy.f,INCLDY,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","RATMAT|LEVSOL|QUIT|WNSTOR|SABOLF",True,,pending
|
||||||
|
indexx.f,INDEXX,SUBROUTINE,True,"","",False,src/math/indexx.rs,done
|
||||||
|
inicom.f,INICOM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|comgfs","",False,src/math/inicom.rs,done
|
||||||
|
inifrc.f,INIFRC,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ijflar","INDEXX",True,,pending
|
||||||
|
inifrs.f,INIFRS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","QUIT|INDEXX",True,,pending
|
||||||
|
inifrt.f,INIFRT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ijflar","INDEXX",True,,pending
|
||||||
|
inilam.f,INILAM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR","ELCOR|RTEFR1|STEQEQ|COLIS|WNSTOR|OPAINI|SABOLF|RATES1|OPACF1",False,,pending
|
||||||
|
initia.f,INITIA,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|freqcl|STRPAR|INUNIT","STATE|LINSET|INIFRC|QUIT|ODFHYS|OPADD0|LINSPL|RDATA|NSTPAR|RDATAX|DOPGAM|READBF|INTERP",True,,pending
|
||||||
|
inkul.f,INKUL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|LINED|COLKUR","",True,,pending
|
||||||
|
inpdis.f,INPDIS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|relcor","GRCOR",True,,pending
|
||||||
|
inpmod.f,INPMOD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|eospar","RATMAT|LEVSOL|QUIT|WNSTOR|SABOLF|KURUCZ|MOLEQ|INCLDY",True,,pending
|
||||||
|
interp.f,INTERP,SUBROUTINE,True,"BASICS","",False,src/math/interp.rs,done
|
||||||
|
inthyd.f,INTHYD,SUBROUTINE,False,"BASICS|MODELQ","DIVSTR",False,src/math/inthyd.rs,done
|
||||||
|
intlem.f,INTLEM,SUBROUTINE,False,"BASICS|MODELQ","INTHYD",False,src/math/intlem.rs,done
|
||||||
|
intxen.f,INTXEN,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/intxen.rs,done
|
||||||
|
irc.f,IRC,SUBROUTINE,True,"","EXPINX|SZIRC",False,src/math/irc.rs,done
|
||||||
|
iroset.f,IROSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|LINED","INKUL|QUIT|LEVCD",True,,pending
|
||||||
|
kurucz.f,KURUCZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|temlim","RHONEN|RATMAT|LEVSOL|QUIT|WNSTOR|SABOLF|MOLEQ",True,,pending
|
||||||
|
lagran.f,LAGRAN,SUBROUTINE,True,"","",False,src/math/interpolate.rs,done
|
||||||
|
laguer.f,LAGUER,SUBROUTINE,False,"","",True,src/math/laguer.rs,done
|
||||||
|
lemini.f,LEMINI,SUBROUTINE,False,"BASICS|MODELQ","",True,,pending
|
||||||
|
levcd.f,LEVCD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|COLKUR","QUIT|INDEXX",True,,pending
|
||||||
|
levgrp.f,LEVGRP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",False,,pending
|
||||||
|
levset.f,LEVSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT",False,,pending
|
||||||
|
levsol.f,LEVSOL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","LINEQS",False,src/math/levsol.rs,done
|
||||||
|
lineqs.f,LINEQS,SUBROUTINE,True,"BASICS","",False,src/math/lineqs.rs,done
|
||||||
|
linpro.f,LINPRO,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|quasun","DIVSTR|INTLEM|STARK0|INTXEN|DOPGAM",False,,pending
|
||||||
|
linsel.f,LINSEL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","OPAINI|QUIT|RTEFR1|OPACF1",True,,pending
|
||||||
|
linset.f,LINSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","STARK0|QUIT|DIVSTR|IJALIS",True,,pending
|
||||||
|
linspl.f,LINSPL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,,pending
|
||||||
|
locate.f,LOCATE,SUBROUTINE,True,"","",False,src/math/locate.rs,done
|
||||||
|
ltegr.f,LTEGR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","ROSSOP|QUIT|STEQEQ|WNSTOR|CONOUT|INTERP",True,,pending
|
||||||
|
ltegrd.f,LTEGRD,SUBROUTINE,False,"BASICS|MODELQ|PRSAUX|TOTJHK|CUBCON|FLXAUX|FACTRS","TEMPER|QUIT|STEQEQ|WNSTOR|CONOUT|ZMRHO|ELDENS|INTERP",True,,pending
|
||||||
|
lucy.f,LUCY,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ITERAT|ALIPAR|ARRAY1","RTEFR1|ELCOR|STEQEQ|WNSTOR|COLIS|OPAINI|SABOLF|OPACFL",True,,pending
|
||||||
|
lymlin.f,LYMLIN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","STARK0|DIVSTR",True,,pending
|
||||||
|
matcon.f,MATCON,SUBROUTINE,False,"BASICS|MODELQ|ARRAY1|CUBCON","CONVEC",False,,pending
|
||||||
|
matgen.f,MATGEN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","BRE|BRTE|BREZ|BHEZ|BHED|MATCON|EMAT|SABOLF|BRTEZ|BHE|BPOP",False,,pending
|
||||||
|
matinv.f,MATINV,SUBROUTINE,True,"BASICS","",False,src/math/matinv.rs,done
|
||||||
|
meanop.f,MEANOP,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC","",False,src/math/meanop.rs,done
|
||||||
|
meanopt.f,MEANOPT,SUBROUTINE,False,"BASICS|MODELQ","OPCTAB",False,,pending
|
||||||
|
minv3.f,MINV3,SUBROUTINE,True,"","",False,src/math/minv3.rs,done
|
||||||
|
moleq.f,MOLEQ,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|ioniz2|COMFH1|hmolab|eospar|entrop|terden|adchar|moldat","MPARTF|RUSSEL",True,,pending
|
||||||
|
mpartf.f,MPARTF,SUBROUTINE,False,"moldat","",True,,pending
|
||||||
|
newdm.f,NEWDM,SUBROUTINE,False,"BASICS|MODELQ|PRSAUX|FLXAUX|FACTRS","TEMPER|INTERP",True,,pending
|
||||||
|
newdmt.f,NEWDMT,SUBROUTINE,False,"BASICS|MODELQ|PRSAUX|FLXAUX|FACTRS","TEMPER|GRIDP|INTERP",True,,pending
|
||||||
|
newpop.f,NEWPOP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",True,,pending
|
||||||
|
nstout.f,NSTOUT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR","QUIT",True,,pending
|
||||||
|
nstpar.f,NSTPAR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|hediff|ichndm|imucnn|adiaba|irwint|icnrsp|quasun|deridt|temlim|derdif|FLXAUX|ipricr|freqcl|ifpzpa|moldat","QUIT|GETWRD",True,,pending
|
||||||
|
odf1.f,ODF1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","DIVSTR|DWNFR|ODFHST",True,,pending
|
||||||
|
odffr.f,ODFFR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","QUIT",False,,pending
|
||||||
|
odfhst.f,ODFHST,SUBROUTINE,False,"BASICS|MODELQ|ODFPAR","",False,src/math/odfhst.rs,done
|
||||||
|
odfhyd.f,ODFHYD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","DIVSTR|INDEXX|ODFHST",False,,pending
|
||||||
|
odfhys.f,ODFHYS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","STARK0|ODFFR|IJALIS",False,,pending
|
||||||
|
odfmer.f,ODFMER,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","ODFHYD",False,,pending
|
||||||
|
odfset.f,ODFSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|STFCR","QUIT|IJALIS",True,,pending
|
||||||
|
opacf0.f,OPACF0,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|hmolab","OPADD|WNSTOR|DWNFR1|SABOLF|DWNFR0|GFREE0|OPACT1|SGMER1|LINPRO",False,,pending
|
||||||
|
opacf1.f,OPACF1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ipricr|hmolab","OPADD|DWNFR1|GHYDOP|QUASIM|OPACT1|PRD|SGMER1|LYMLIN",True,,pending
|
||||||
|
opacfa.f,OPACFA,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|COOLCO","PRD|SGMER1|DWNFR1|OPADD",False,,pending
|
||||||
|
opacfd.f,OPACFD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT|dsctva|hmolab|rhoder","OPADD|GFREED|OPACTD|DWNFR1|QUASIM|PRD|SGMER1|OPCTAB|LYMLIN",True,,pending
|
||||||
|
opacfl.f,OPACFL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","SGMER1|DWNFR1|OPADD",False,,pending
|
||||||
|
opact1.f,OPACT1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|hmolab","OPCTAB",False,,pending
|
||||||
|
opactd.f,OPACTD,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ARRAY1|ITERAT|rhoder|dsctva|hmolab","OPCTAB",False,,pending
|
||||||
|
opactr.f,OPACTR,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ATOMIC|grdpra|dsctva|hmolab","OPACF1|LEVSOL|STEQEQ|WNSTOR|OPAINI|PGSET|SABOLF|ELDENS|RATMAL",False,,pending
|
||||||
|
opadd.f,OPADD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|eospar","CIA_H2H|H2MINUS|CIA_H2HE|CIA_H2H2|CIA_HHE",False,,pending
|
||||||
|
opadd0.f,OPADD0,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT",False,,pending
|
||||||
|
opahst.f,OPAHST,SUBROUTINE,False,"BASICS|ODFPAR","STARK0",True,,pending
|
||||||
|
opaini.f,OPAINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","REFLEV|WNSTOR|SABOLF|DWNFR0|LINPRO|LEVGRP",False,,pending
|
||||||
|
opctab.f,OPCTAB,SUBROUTINE,False,"BASICS|MODELQ","RAYLEIGH",False,,pending
|
||||||
|
opdata.f,OPDATA,SUBROUTINE,False,"TOPB","",True,,pending
|
||||||
|
opfrac.f,OPFRAC,SUBROUTINE,False,"pfoptb","",True,,pending
|
||||||
|
osccor.f,OSCCOR,SUBROUTINE,False,"BASICS|MODELQ|ITERAT","",True,,pending
|
||||||
|
outpri.f,OUTPRI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|grdpra","OPACF1|LEVSOL|WNSTOR|SABOLF|RATMAL",True,,pending
|
||||||
|
output.f,OUTPUT,SUBROUTINE,False,"BASICS|MODELQ","",True,,pending
|
||||||
|
partf.f,PARTF,SUBROUTINE,False,"BASICS|PFSTDS|irwint","PFNI|OPFRAC|PFHEAV|PFCNO|PFSPEC|MPARTF|PFFE",False,,pending
|
||||||
|
pfcno.f,PFCNO,SUBROUTINE,True,"BASICS","",False,src/math/pfcno.rs,done
|
||||||
|
pffe.f,PFFE,SUBROUTINE,True,"","",False,src/math/pffe.rs,done
|
||||||
|
pfheav.f,PFHEAV,SUBROUTINE,False,"","",True,,pending
|
||||||
|
pfni.f,PFNI,SUBROUTINE,True,"","",False,src/math/pfni.rs,done
|
||||||
|
pfspec.f,PFSPEC,SUBROUTINE,True,"","",False,src/math/pfspec.rs,done
|
||||||
|
pgset.f,PGSET,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|rybpgs|grdpra","TRIDAG",True,,pending
|
||||||
|
prchan.f,PRCHAN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",True,,pending
|
||||||
|
prd.f,PRD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","DOPGAM",False,,pending
|
||||||
|
prdini.f,PRDINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/prdini.rs,done
|
||||||
|
princ.f,PRINC,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","LINPRO|SABOLF|OPACF1|DWNFR",True,,pending
|
||||||
|
prnt.f,PRNT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","SABOLF",True,,pending
|
||||||
|
profil.f,PROFIL,FUNCTION,False,"BASICS|ATOMIC|MODELQ|quasun","STARK0|PROFIL|DIVSTR",False,,pending
|
||||||
|
profsp.f,PROFSP,FUNCTION,False,"BASICS|ATOMIC|MODELQ","SABOLF|PROFSP",False,,pending
|
||||||
|
prsent.f,PRSENT,SUBROUTINE,False,"tdedge|tdflag|THERM|TABLTD","",True,,pending
|
||||||
|
psolve.f,PSOLVE,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/psolve.rs,done
|
||||||
|
pzert.f,PZERT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,,pending
|
||||||
|
pzeval.f,PZEVAL,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|icnrsp","CONOUT",True,,pending
|
||||||
|
pzevld.f,PZEVLD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1|PRSAUX|DEPTDR|ifpzpa|grdpra","",False,,pending
|
||||||
|
quartc.f,QUARTC,SUBROUTINE,False,"","",True,src/math/quartc.rs,done
|
||||||
|
quasim.f,QUASIM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|quasun","ALLARD",False,,pending
|
||||||
|
quit.f,QUIT,SUBROUTINE,False,"","",True,src/math/quit.rs,done
|
||||||
|
radpre.f,RADPRE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","RTEFR1|QUIT|OPACF1|INDEXX",True,,pending
|
||||||
|
radtot.f,RADTOT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT|OPTDPT|SURFEX|TOTJHK","OPAINI|RTEFR1|OPACF1",False,,pending
|
||||||
|
raph.f,RAPH,FUNCTION,True,"","RAPH",False,src/math/raph.rs,done
|
||||||
|
rates1.f,RATES1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT","ROSSTD|RTEFR1|OPACF1",False,,pending
|
||||||
|
ratmal.f,RATMAL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/ratmal.rs,done
|
||||||
|
ratmat.f,RATMAT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","REFLEV",False,,pending
|
||||||
|
ratsp1.f,RATSP1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","ROSSTD|RTEFR1|OPACF1",True,,pending
|
||||||
|
rayini.f,RAYINI,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC","RAYLEIGH",True,,pending
|
||||||
|
rayleigh.f,RAYLEIGH,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|RAYSCT|eospar","",False,src/math/rayleigh.rs,done
|
||||||
|
rayset.f,RAYSET,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/rayset.rs,done
|
||||||
|
rdata.f,RDATA,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|STRPAR|imodlc|INUNIT","LINSET|QUIT|RDATAX|DOPGAM",True,,pending
|
||||||
|
rdatax.f,RDATAX,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","BKHSGO",True,,pending
|
||||||
|
readbf.f,READBF,SUBROUTINE,False,"BASICS","",True,,pending
|
||||||
|
rechck.f,RECHCK,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","RTEFR1|OPACF1",True,,pending
|
||||||
|
reflev.f,REFLEV,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",False,,pending
|
||||||
|
reiman.f,REIMAN,FUNCTION,True,"","REIMAN",False,src/math/reiman.rs,done
|
||||||
|
resolv.f,RESOLV,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR|ARRAY1|icnrsp","RTEFR1|ELCOR|STEQEQ|NEWPOP|OPAINI|ROSSTD|CONOUT|TIMING|PRD|TAUFR1|RATES1|OPACF1",True,,pending
|
||||||
|
rhoeos.f,RHOEOS,FUNCTION,False,"BASICS|MODELQ","PRSENT|RHOEOS",False,,pending
|
||||||
|
rhonen.f,RHONEN,SUBROUTINE,False,"BASICS|MODELQ","ELDENS",False,,pending
|
||||||
|
rhsgen.f,RHSGEN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|CUBCON","STATE|RATMAT|MATINV|SABOLF|CONVEC|COMPT0|LEVGRP",False,,pending
|
||||||
|
rossop.f,ROSSOP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","MEANOP|STEQEQ|WNSTOR|OPACF0|MEANOPT|ELDENS",False,,pending
|
||||||
|
rosstd.f,ROSSTD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR","",True,,pending
|
||||||
|
rte_sc.f,RTE_SC,SUBROUTINE,True,"BASICS","",False,src/math/rte_sc.rs,done
|
||||||
|
rteang.f,RTEANG,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|SURFEX|EXTINT","GAULEG",False,,pending
|
||||||
|
rtecf0.f,RTECF0,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT|auxcbc|AUXRTE","",False,,pending
|
||||||
|
rtecf1.f,RTECF1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|SURFEX|AUXRTE|EXTINT|OPTDPT|comgfs","RTECF0|RTEFE2|RTESOL",True,,pending
|
||||||
|
rtecmc.f,RTECMC,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|AUXRTE|comgfs","RTECF0|OPACF1|MATINV",False,,pending
|
||||||
|
rtecmu.f,RTECMU,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT|AUXRTE","GAULEG|RTECF0|OPACF1|RTESOL",True,,pending
|
||||||
|
rtecom.f,RTECOM,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT|AUXRTE|comgfs","RTECF0|RTECF1|OPACF1",False,,pending
|
||||||
|
rtedf1.f,RTEDF1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|OPTDPT","",False,,pending
|
||||||
|
rtedf2.f,RTEDF2,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR","",False,,pending
|
||||||
|
rtefe2.f,RTEFE2,SUBROUTINE,True,"BASICS","",False,src/math/rtefe2.rs,done
|
||||||
|
rtefr1.f,RTEFR1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT","RTEDF2|MATINV|RTECF1|RTEDF1|MINV3|RTESOL",True,,pending
|
||||||
|
rteint.f,RTEINT,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT","MATINV|OPACF1",True,,pending
|
||||||
|
rtesol.f,RTESOL,SUBROUTINE,True,"BASICS","",False,src/math/rtesol.rs,done
|
||||||
|
russel.f,RUSSEL,SUBROUTINE,False,"BASICS|MODELQ|COMFH1","MPARTF",True,,pending
|
||||||
|
rybchn.f,RYBCHN,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|ALIPAR|ARRAY1|rybpgs|grdpra","PGSET|ELDENS",True,,pending
|
||||||
|
rybene.f,RYBENE,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ARRAY1|CUBCON|deridt|RYBMTX","CONVEC",False,,pending
|
||||||
|
rybheq.f,RYBHEQ,SUBROUTINE,False,"BASICS|MODELQ|rybpgs|grdpra","RTEFR1|ELCOR|STEQEQ|WNSTOR|OPAINI|PGSET|ELDENS|OPACF1",True,,pending
|
||||||
|
rybmat.f,RYBMAT,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ARRAY1|dsctva|RYBMTX","",False,,pending
|
||||||
|
rybsol.f,RYBSOL,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|ALIPAR|ARRAY1|ITERAT|RYBMTX|imodlc","RTEFR1|ALIFR1|TRIDAG|STEQEQ|ROSSTD|RYBMAT|OPACTR|OPACFD|RYBCHN|LINEQS",True,,pending
|
||||||
|
sabolf.f,SABOLF,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","PARTF",False,,pending
|
||||||
|
sbfch.f,SBFCH,FUNCTION,True,"","SBFCH",False,src/math/sbfch.rs,done
|
||||||
|
sbfhe1.f,SBFHE1,FUNCTION,False,"BASICS|ATOMIC","SBFHE1|QUIT",True,src/math/sbfhe1.rs,done
|
||||||
|
sbfhmi.f,SBFHMI,FUNCTION,True,"","SBFHMI",False,src/math/sbfhmi.rs,done
|
||||||
|
sbfhmi_old.f,SBFHMI_OLD,FUNCTION,True,"","SBFHMI_OLD",False,src/math/sbfhmi_old.rs,done
|
||||||
|
sbfoh.f,SBFOH,FUNCTION,True,"","SBFOH",False,src/math/sbfoh.rs,done
|
||||||
|
setdrt.f,SETDRT,SUBROUTINE,False,"BASICS|MODELQ|RHODER","",False,,pending
|
||||||
|
settrm.f,SETTRM,SUBROUTINE,False,"tdedge|tdflag|THERM|TABLTD","PRSENT",True,,pending
|
||||||
|
sffhmi.f,SFFHMI,FUNCTION,True,"","SFFHMI",False,src/math/sffhmi.rs,done
|
||||||
|
sffhmi_add.f,SFFHMI_ADD,FUNCTION,True,"","SFFHMI_ADD",False,src/math/sffhmi_add.rs,done
|
||||||
|
sghe12.f,SGHE12,FUNCTION,True,"","SGHE12",False,src/math/sghe12.rs,done
|
||||||
|
sgmer0.f,SGMER0,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/sgmer.rs,done
|
||||||
|
sgmer1.f,SGMER1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/sgmer.rs,done
|
||||||
|
sgmerd.f,SGMERD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/sgmer.rs,done
|
||||||
|
sigave.f,SIGAVE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","",True,,pending
|
||||||
|
sigk.f,SIGK,FUNCTION,False,"BASICS|ATOMIC","SIGK|SPSIGK",False,,pending
|
||||||
|
sigmar.f,SIGMAR,FUNCTION,False,"BASICS","SIGMAR|LAGUER",True,,pending
|
||||||
|
solve.f,SOLVE,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|ARRAY1|ALIPAR|CMATZD","MATINV|WNSTOR|MATGEN|RHSGEN|PRCHAN",True,,pending
|
||||||
|
solves.f,SOLVES,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|ARRAY1|ALIPAR|CMATZD|STOMAT","MATINV|WNSTOR|MATGEN|RHSGEN|PRCHAN",True,,pending
|
||||||
|
spsigk.f,SPSIGK,SUBROUTINE,True,"","CARBON",False,src/math/spsigk.rs,done
|
||||||
|
srtfrq.f,SRTFRQ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT|INDEXX",True,,pending
|
||||||
|
stark0.f,STARK0,SUBROUTINE,True,"","",False,src/math/stark0.rs,done
|
||||||
|
starka.f,STARKA,FUNCTION,False,"BASICS|MODELQ","STARKA",False,src/math/starka.rs,done
|
||||||
|
start.f,START,SUBROUTINE,False,"BASICS|hediff","",True,,pending
|
||||||
|
state.f,STATE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|PFSTDS|terden","PARTF|OPFRAC",True,,pending
|
||||||
|
steqeq.f,STEQEQ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|POPSTR|PPAPAR","MOLEQ|LEVSOL|SABOLF|RATMAT",False,,pending
|
||||||
|
switch.f,SWITCH,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",True,,pending
|
||||||
|
szirc.f,SZIRC,SUBROUTINE,True,"","EINT",False,src/math/szirc.rs,done
|
||||||
|
tabini.f,TABINI,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|intcff|abntab|eletab","",True,,pending
|
||||||
|
tabint.f,TABINT,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|intcff","",False,,pending
|
||||||
|
taufr1.f,TAUFR1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT","",False,,pending
|
||||||
|
tdpini.f,TDPINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","GFREE0",False,src/math/tdpini.rs,done
|
||||||
|
temcor.f,TEMCOR,SUBROUTINE,False,"BASICS|MODELQ|ARRAY1|ALIPAR|CUBCON","MEANOP|STEQEQ|WNSTOR|OPACF0|CONVEC|ELDENS",True,,pending
|
||||||
|
temper.f,TEMPER,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|PRSAUX|FLXAUX|FACTRS","MEANOP|STEQEQ|WNSTOR|OPACF0|TLOCAL|MEANOPT|ELDENS",True,,pending
|
||||||
|
timing.f,TIMING,SUBROUTINE,False,"","",True,,pending
|
||||||
|
tiopf.f,TIOPF,SUBROUTINE,True,"","",False,src/math/tiopf.rs,done
|
||||||
|
tlocal.f,TLOCAL,SUBROUTINE,False,"BASICS|MODELQ|FACTRS|FLXAUX","QUARTC",False,,pending
|
||||||
|
tlusty.f,TLUSTY,UNKNOWN,False,"BASICS|ITERAT|ALIPAR","TIMING",True,,pending
|
||||||
|
topbas.f,TOPBAS,FUNCTION,False,"TOPB","TOPBAS",True,,pending
|
||||||
|
traini.f,TRAINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","",False,src/math/traini.rs,done
|
||||||
|
tridag.f,TRIDAG,SUBROUTINE,True,"","",False,src/math/tridag.rs,done
|
||||||
|
trmder.f,TRMDER,SUBROUTINE,False,"BASICS|terden|adiaba|derdif","ELDENS",False,,pending
|
||||||
|
trmdrt.f,TRMDRT,SUBROUTINE,False,"BASICS|tdflag|CONVOUT|tdedge|CC","PRSENT",False,,pending
|
||||||
|
ubeta.f,UBETA,FUNCTION,True,"","UBETA|LAGRAN",False,src/math/ubeta.rs,done
|
||||||
|
vern16.f,VERN16,FUNCTION,True,"BASICS","VERN16",False,src/math/vern16.rs,done
|
||||||
|
vern18.f,VERN18,FUNCTION,True,"BASICS","VERN18",False,src/math/vern18.rs,done
|
||||||
|
vern20.f,VERN20,FUNCTION,True,"BASICS","VERN20",False,src/math/vern20.rs,done
|
||||||
|
vern26.f,VERN26,FUNCTION,True,"BASICS","VERN26",False,src/math/vern26.rs,done
|
||||||
|
verner.f,VERNER,FUNCTION,False,"BASICS|ATOMIC","VERNER|QUIT",False,src/math/verner.rs,done
|
||||||
|
visini.f,VISINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",True,,pending
|
||||||
|
voigt.f,VOIGT,FUNCTION,True,"","VOIGT",False,src/math/voigt.rs,done
|
||||||
|
voigte.f,VOIGTE,FUNCTION,True,"","VOIGTE",False,src/math/voigte.rs,done
|
||||||
|
wn.f,WN,FUNCTION,True,"BASICS","WN",False,src/math/wn.rs,done
|
||||||
|
wnstor.f,WNSTOR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/wnstor.rs,done
|
||||||
|
xenini.f,XENINI,SUBROUTINE,False,"BASICS|MODELQ","",True,,pending
|
||||||
|
xk2dop.f,XK2DOP,FUNCTION,True,"","XK2DOP",False,src/math/xk2dop.rs,done
|
||||||
|
yint.f,YINT,FUNCTION,True,"","YINT",False,src/math/interpolate.rs,done
|
||||||
|
ylintp.f,YLINTP,FUNCTION,True,"","YLINTP",False,src/math/ylintp.rs,done
|
||||||
|
zmrho.f,ZMRHO,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/zmrho.rs,done
|
||||||
|
374
scripts/analyze_fortran.py
Normal file
374
scripts/analyze_fortran.py
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
分析 TLUSTY Fortran 文件,提取函数依赖信息。
|
||||||
|
|
||||||
|
用法:
|
||||||
|
python3 analyze_fortran.py # 输出 CSV(带完整依赖)
|
||||||
|
python3 analyze_fortran.py --tree # 输出依赖树(文本格式)
|
||||||
|
python3 analyze_fortran.py --priority # 输出重构优先级列表
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import glob
|
||||||
|
import argparse
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
def extract_includes(content):
|
||||||
|
"""提取 INCLUDE 文件列表"""
|
||||||
|
includes = re.findall(r"INCLUDE\s*'([^']+)\.FOR'", content, re.IGNORECASE)
|
||||||
|
return [inc for inc in includes if inc.upper() != 'IMPLIC']
|
||||||
|
|
||||||
|
def extract_commons(content):
|
||||||
|
"""提取 COMMON 块名称"""
|
||||||
|
# 匹配 COMMON/NAME/ 或 common/name/
|
||||||
|
commons = re.findall(r'(?i)^\s*COMMON\s*/(\w+)/', content, re.MULTILINE)
|
||||||
|
return list(set(commons))
|
||||||
|
|
||||||
|
def extract_calls(content):
|
||||||
|
"""提取 CALL 语句调用的子程序"""
|
||||||
|
calls = re.findall(r'(?i)CALL\s+(\w+)\s*\(', content)
|
||||||
|
# 也提取函数调用 ( FUNCTION 形式 )
|
||||||
|
funcs = re.findall(r'(?i)^\s*(?:REAL|INTEGER|DOUBLE\s*PRECISION)?\s*FUNCTION\s+(\w+)', content, re.MULTILINE)
|
||||||
|
# 统一转为大写
|
||||||
|
return list(set(c.upper() for c in calls + funcs))
|
||||||
|
|
||||||
|
def has_file_io(content):
|
||||||
|
"""检查是否有文件 I/O"""
|
||||||
|
patterns = [
|
||||||
|
r'OPEN\s*\(',
|
||||||
|
r'READ\s*\(\s*\d+',
|
||||||
|
r'WRITE\s*\(\s*\d+',
|
||||||
|
r'write\s*\(',
|
||||||
|
r'read\s*\(',
|
||||||
|
]
|
||||||
|
for p in patterns:
|
||||||
|
if re.search(p, content, re.IGNORECASE):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def extract_unit_info(content, filename):
|
||||||
|
"""提取单元信息"""
|
||||||
|
units = []
|
||||||
|
|
||||||
|
# 匹配 SUBROUTINE
|
||||||
|
sub_match = re.search(r'(?i)^\s*SUBROUTINE\s+(\w+)', content, re.MULTILINE)
|
||||||
|
if sub_match:
|
||||||
|
units.append(('SUBROUTINE', sub_match.group(1).upper()))
|
||||||
|
|
||||||
|
# 匹配 FUNCTION
|
||||||
|
func_match = re.search(r'(?i)^\s*(?:REAL(?:\*\d+)?|INTEGER(?:\*\d+)?|DOUBLE\s*PRECISION)?\s*FUNCTION\s+(\w+)', content, re.MULTILINE)
|
||||||
|
if func_match:
|
||||||
|
units.append(('FUNCTION', func_match.group(1).upper()))
|
||||||
|
|
||||||
|
# 匹配 BLOCK DATA
|
||||||
|
block_match = re.search(r'(?i)^\s*BLOCK\s*DATA\s+(\w+)?', content, re.MULTILINE)
|
||||||
|
if block_match:
|
||||||
|
name = block_match.group(1).upper() if block_match.group(1) else '_UNNAMED_'
|
||||||
|
units.append(('BLOCK DATA', name))
|
||||||
|
|
||||||
|
# 如果都没匹配到,使用文件名
|
||||||
|
if not units:
|
||||||
|
base = os.path.splitext(filename)[0]
|
||||||
|
units.append(('UNKNOWN', base.upper()))
|
||||||
|
|
||||||
|
return units
|
||||||
|
|
||||||
|
# 特殊映射:一个 Rust 文件实现多个 Fortran 函数
|
||||||
|
SPECIAL_MAPPINGS = {
|
||||||
|
# Rust 文件名 -> [Fortran 函数名列表]
|
||||||
|
'gfree': ['gfree0', 'gfreed', 'gfree1'],
|
||||||
|
'interpolate': ['yint', 'lagran'],
|
||||||
|
'sgmer': ['sgmer0', 'sgmer1', 'sgmerd'],
|
||||||
|
'ctdata': ['hction', 'hctrecom'],
|
||||||
|
'cross': ['cross', 'crossd'],
|
||||||
|
'expint': ['eint', 'expinx'],
|
||||||
|
'erfcx': ['erfcx', 'erfcin'],
|
||||||
|
'lineqs': ['lineqs', 'lineqs_nr'],
|
||||||
|
'gamsp': ['gamsp'], # alias
|
||||||
|
}
|
||||||
|
|
||||||
|
def find_rust_module(fortran_name, rust_dir):
|
||||||
|
"""查找对应的 Rust 模块"""
|
||||||
|
# 先检查直接匹配
|
||||||
|
rust_file = os.path.join(rust_dir, f"{fortran_name}.rs")
|
||||||
|
if os.path.exists(rust_file):
|
||||||
|
return f"src/math/{fortran_name}.rs"
|
||||||
|
|
||||||
|
# 检查特殊映射
|
||||||
|
for rust_mod, fortran_funcs in SPECIAL_MAPPINGS.items():
|
||||||
|
if fortran_name in fortran_funcs:
|
||||||
|
return f"src/math/{rust_mod}.rs"
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def get_transitive_deps(unit_name, units_dict, visited=None):
|
||||||
|
"""递归获取所有传递调用依赖"""
|
||||||
|
if visited is None:
|
||||||
|
visited = set()
|
||||||
|
|
||||||
|
if unit_name in visited:
|
||||||
|
return set()
|
||||||
|
|
||||||
|
visited.add(unit_name)
|
||||||
|
|
||||||
|
if unit_name not in units_dict:
|
||||||
|
return set()
|
||||||
|
|
||||||
|
direct_calls = units_dict[unit_name].get('call_deps', [])
|
||||||
|
all_deps = set(direct_calls)
|
||||||
|
|
||||||
|
for dep in direct_calls:
|
||||||
|
all_deps.update(get_transitive_deps(dep, units_dict, visited.copy()))
|
||||||
|
|
||||||
|
return all_deps
|
||||||
|
|
||||||
|
def get_pending_deps(unit_name, units_dict, visited=None):
|
||||||
|
"""获取尚未实现的直接依赖"""
|
||||||
|
if unit_name not in units_dict:
|
||||||
|
return []
|
||||||
|
|
||||||
|
calls = units_dict[unit_name].get('call_deps', [])
|
||||||
|
pending = [d for d in calls if d not in units_dict or units_dict[d].get('status') != 'done']
|
||||||
|
return pending
|
||||||
|
|
||||||
|
def get_transitive_pending_deps(unit_name, units_dict, visited=None):
|
||||||
|
"""递归获取所有传递的未实现依赖"""
|
||||||
|
if visited is None:
|
||||||
|
visited = set()
|
||||||
|
|
||||||
|
if unit_name in visited:
|
||||||
|
return set()
|
||||||
|
|
||||||
|
visited.add(unit_name)
|
||||||
|
|
||||||
|
if unit_name not in units_dict:
|
||||||
|
return set()
|
||||||
|
|
||||||
|
direct_calls = units_dict[unit_name].get('call_deps', [])
|
||||||
|
# 未实现的直接依赖
|
||||||
|
pending_deps = set(d for d in direct_calls if d not in units_dict or units_dict[d].get('status') != 'done')
|
||||||
|
|
||||||
|
# 递归获取所有依赖的未实现依赖
|
||||||
|
for dep in direct_calls:
|
||||||
|
pending_deps.update(get_transitive_pending_deps(dep, units_dict, visited.copy()))
|
||||||
|
|
||||||
|
return pending_deps
|
||||||
|
|
||||||
|
def get_transitive_commons(unit_name, units_dict, visited=None):
|
||||||
|
"""递归获取所有传递 COMMON 依赖"""
|
||||||
|
if visited is None:
|
||||||
|
visited = set()
|
||||||
|
|
||||||
|
if unit_name in visited:
|
||||||
|
return set()
|
||||||
|
|
||||||
|
visited.add(unit_name)
|
||||||
|
|
||||||
|
if unit_name not in units_dict:
|
||||||
|
return set()
|
||||||
|
|
||||||
|
direct_commons = set(units_dict[unit_name].get('common_deps', []))
|
||||||
|
direct_calls = units_dict[unit_name].get('call_deps', [])
|
||||||
|
|
||||||
|
all_commons = direct_commons.copy()
|
||||||
|
|
||||||
|
for dep in direct_calls:
|
||||||
|
all_commons.update(get_transitive_commons(dep, units_dict, visited.copy()))
|
||||||
|
|
||||||
|
return all_commons
|
||||||
|
|
||||||
|
def calculate_depth(unit_name, units_dict, memo=None):
|
||||||
|
"""计算依赖深度(叶子节点深度为0)"""
|
||||||
|
if memo is None:
|
||||||
|
memo = {}
|
||||||
|
|
||||||
|
if unit_name in memo:
|
||||||
|
return memo[unit_name]
|
||||||
|
|
||||||
|
if unit_name not in units_dict:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
calls = units_dict[unit_name].get('call_deps', [])
|
||||||
|
if not calls:
|
||||||
|
memo[unit_name] = 0
|
||||||
|
return 0
|
||||||
|
|
||||||
|
max_dep_depth = 0
|
||||||
|
for dep in calls:
|
||||||
|
if dep != unit_name: # 避免自引用
|
||||||
|
max_dep_depth = max(max_dep_depth, calculate_depth(dep, units_dict, memo))
|
||||||
|
|
||||||
|
depth = max_dep_depth + 1
|
||||||
|
memo[unit_name] = depth
|
||||||
|
return depth
|
||||||
|
|
||||||
|
def print_dependency_tree(unit_name, units_dict, indent=0, visited=None, prefix="", show_pending_count=True):
|
||||||
|
"""打印依赖树(文本格式)"""
|
||||||
|
if visited is None:
|
||||||
|
visited = set()
|
||||||
|
|
||||||
|
if unit_name in visited:
|
||||||
|
print(f"{prefix}[循环引用: {unit_name}]")
|
||||||
|
return
|
||||||
|
|
||||||
|
visited.add(unit_name)
|
||||||
|
|
||||||
|
if unit_name not in units_dict:
|
||||||
|
print(f"{prefix}{unit_name} [未找到/未实现]")
|
||||||
|
return
|
||||||
|
|
||||||
|
unit = units_dict[unit_name]
|
||||||
|
status = unit.get('status', 'pending')
|
||||||
|
status_mark = "✓" if status == "done" else "○"
|
||||||
|
|
||||||
|
# 计算未实现依赖数
|
||||||
|
pending_count = len(get_pending_deps(unit_name, units_dict))
|
||||||
|
pending_str = f" ({pending_count}未实现)" if show_pending_count and pending_count > 0 else ""
|
||||||
|
|
||||||
|
print(f"{prefix}{status_mark} {unit_name}{pending_str}")
|
||||||
|
|
||||||
|
calls = unit.get('call_deps', [])
|
||||||
|
# 按未实现依赖数排序(未实现多的在前,因为更紧迫)
|
||||||
|
pending_sorted = sorted(calls, key=lambda d: -len(get_pending_deps(d, units_dict) if d in units_dict else []))
|
||||||
|
|
||||||
|
for i, dep in enumerate(pending_sorted):
|
||||||
|
is_last = (i == len(pending_sorted) - 1)
|
||||||
|
connector = "└── " if is_last else "├── "
|
||||||
|
print_dependency_tree(dep, units_dict, indent + 1, visited.copy(), prefix + connector, show_pending_count)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description='分析 TLUSTY Fortran 文件依赖')
|
||||||
|
parser.add_argument('--tree', metavar='UNIT', help='输出指定单元的依赖树')
|
||||||
|
parser.add_argument('--priority', action='store_true', help='输出重构优先级列表')
|
||||||
|
parser.add_argument('--full', action='store_true', help='输出完整传递依赖')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
extracted_dir = "/home/fmq/program/tlusty/tl208-s54/rust/tlusty/extracted"
|
||||||
|
rust_dir = "/home/fmq/program/tlusty/tl208-s54/rust/src/math"
|
||||||
|
|
||||||
|
# 收集所有单元信息
|
||||||
|
units_dict = {}
|
||||||
|
fortran_files = sorted(glob.glob(os.path.join(extracted_dir, "*.f")))
|
||||||
|
|
||||||
|
for fpath in fortran_files:
|
||||||
|
fname = os.path.basename(fpath)
|
||||||
|
base_name = os.path.splitext(fname)[0]
|
||||||
|
|
||||||
|
with open(fpath, 'r', encoding='utf-8', errors='ignore') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
includes = extract_includes(content)
|
||||||
|
commons = extract_commons(content)
|
||||||
|
calls = extract_calls(content)
|
||||||
|
io = has_file_io(content)
|
||||||
|
units = extract_unit_info(content, fname)
|
||||||
|
|
||||||
|
is_pure = len(includes) <= 1 and len(commons) == 0 and not io
|
||||||
|
rust_mod = find_rust_module(base_name, rust_dir)
|
||||||
|
status = "done" if rust_mod else "pending"
|
||||||
|
|
||||||
|
for unit_type, unit_name in units:
|
||||||
|
units_dict[unit_name] = {
|
||||||
|
'fortran_file': fname,
|
||||||
|
'unit_type': unit_type,
|
||||||
|
'is_pure': is_pure,
|
||||||
|
'common_deps': includes + commons,
|
||||||
|
'call_deps': calls,
|
||||||
|
'has_io': io,
|
||||||
|
'rust_module': rust_mod,
|
||||||
|
'status': status,
|
||||||
|
}
|
||||||
|
|
||||||
|
# --tree 模式:输出依赖树
|
||||||
|
if args.tree:
|
||||||
|
unit_name = args.tree.upper()
|
||||||
|
if unit_name in units_dict:
|
||||||
|
unit = units_dict[unit_name]
|
||||||
|
trans_pending = get_transitive_pending_deps(unit_name, units_dict)
|
||||||
|
trans_calls = get_transitive_deps(unit_name, units_dict)
|
||||||
|
status_mark = "✓" if unit['status'] == "done" else "○"
|
||||||
|
|
||||||
|
print(f"依赖树: {unit_name} {status_mark}")
|
||||||
|
print("=" * 60)
|
||||||
|
print(f"直接依赖: {len(unit['call_deps'])}, 传递依赖: {len(trans_calls)}, "
|
||||||
|
f"未实现: {len(trans_pending)}")
|
||||||
|
if trans_pending:
|
||||||
|
print(f"未实现依赖: {', '.join(sorted(trans_pending)[:10])}")
|
||||||
|
if len(trans_pending) > 10:
|
||||||
|
print(f" ... 还有 {len(trans_pending) - 10} 个")
|
||||||
|
print("-" * 60)
|
||||||
|
print_dependency_tree(unit_name, units_dict)
|
||||||
|
else:
|
||||||
|
print(f"未找到单元: {unit_name}")
|
||||||
|
# 尝试模糊匹配
|
||||||
|
matches = [u for u in units_dict if args.tree.lower() in u.lower()]
|
||||||
|
if matches:
|
||||||
|
print(f"可能的匹配: {', '.join(matches[:10])}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# --priority 模式:输出重构优先级
|
||||||
|
if args.priority:
|
||||||
|
# 计算每个单元的依赖深度和传递依赖数
|
||||||
|
priority_list = []
|
||||||
|
memo = {}
|
||||||
|
for unit_name, unit in units_dict.items():
|
||||||
|
if unit['status'] == 'done':
|
||||||
|
continue
|
||||||
|
|
||||||
|
depth = calculate_depth(unit_name, units_dict, memo)
|
||||||
|
trans_calls = len(get_transitive_deps(unit_name, units_dict))
|
||||||
|
trans_commons = len(get_transitive_commons(unit_name, units_dict))
|
||||||
|
pending_deps = len(get_pending_deps(unit_name, units_dict))
|
||||||
|
trans_pending = len(get_transitive_pending_deps(unit_name, units_dict))
|
||||||
|
|
||||||
|
priority_list.append({
|
||||||
|
'name': unit_name,
|
||||||
|
'depth': depth,
|
||||||
|
'direct_calls': len(unit['call_deps']),
|
||||||
|
'trans_calls': trans_calls,
|
||||||
|
'direct_commons': len(unit['common_deps']),
|
||||||
|
'trans_commons': trans_commons,
|
||||||
|
'pending_deps': pending_deps,
|
||||||
|
'trans_pending': trans_pending,
|
||||||
|
'has_io': unit['has_io'],
|
||||||
|
'is_pure': unit['is_pure'],
|
||||||
|
})
|
||||||
|
|
||||||
|
# 按优先级排序:未实现依赖少 > 深度低 > 无IO
|
||||||
|
priority_list.sort(key=lambda x: (x['trans_pending'], x['depth'], x['trans_calls'], x['has_io']))
|
||||||
|
|
||||||
|
print("重构优先级列表 (按未实现依赖排序)")
|
||||||
|
print("=" * 100)
|
||||||
|
print(f"{'单元名':<20} {'未实现':>6} {'传递未实现':>10} {'深度':>4} {'直接调用':>8} {'传递调用':>8} {'IO':>4}")
|
||||||
|
print("-" * 100)
|
||||||
|
|
||||||
|
for item in priority_list[:100]: # 显示前100个
|
||||||
|
io_mark = "✓" if item['has_io'] else "○"
|
||||||
|
print(f"{item['name']:<20} {item['pending_deps']:>6} {item['trans_pending']:>10} "
|
||||||
|
f"{item['depth']:>4} {item['direct_calls']:>8} {item['trans_calls']:>8} {io_mark:>4}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 默认模式:输出 CSV(带完整依赖)
|
||||||
|
if args.full:
|
||||||
|
print("fortran_file,unit_name,unit_type,is_pure,common_deps,call_deps,"
|
||||||
|
"trans_commons,trans_calls,has_io,rust_module,status")
|
||||||
|
else:
|
||||||
|
print("fortran_file,unit_name,unit_type,is_pure,common_deps,call_deps,has_io,rust_module,status")
|
||||||
|
|
||||||
|
memo = {}
|
||||||
|
for unit_name, unit in units_dict.items():
|
||||||
|
if args.full:
|
||||||
|
trans_commons = get_transitive_commons(unit_name, units_dict)
|
||||||
|
trans_calls = get_transitive_deps(unit_name, units_dict)
|
||||||
|
print(f"{unit['fortran_file']},{unit_name},{unit['unit_type']},{unit['is_pure']},"
|
||||||
|
f"\"{'|'.join(unit['common_deps'])}\",\"{'|'.join(unit['call_deps'])}\","
|
||||||
|
f"\"{'|'.join(trans_commons)}\",\"{'|'.join(trans_calls)}\","
|
||||||
|
f"{unit['has_io']},{unit['rust_module']},{unit['status']}")
|
||||||
|
else:
|
||||||
|
print(f"{unit['fortran_file']},{unit_name},{unit['unit_type']},{unit['is_pure']},"
|
||||||
|
f"\"{'|'.join(unit['common_deps'])}\",\"{'|'.join(unit['call_deps'])}\","
|
||||||
|
f"{unit['has_io']},{unit['rust_module']},{unit['status']}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
82
scripts/generate_tracking.py
Normal file
82
scripts/generate_tracking.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
生成 Fortran 重构追踪 Markdown 文档。
|
||||||
|
|
||||||
|
用法: python3 generate_tracking.py > FORTRAN_TRACKING.md
|
||||||
|
"""
|
||||||
|
|
||||||
|
import csv
|
||||||
|
import os
|
||||||
|
|
||||||
|
def main():
|
||||||
|
csv_path = "/home/fmq/program/tlusty/tl208-s54/rust/fortran_analysis.csv"
|
||||||
|
|
||||||
|
with open(csv_path, 'r') as f:
|
||||||
|
reader = csv.DictReader(f)
|
||||||
|
rows = list(reader)
|
||||||
|
|
||||||
|
# 统计
|
||||||
|
total = len(rows)
|
||||||
|
done = sum(1 for r in rows if r['status'] == 'done')
|
||||||
|
pending = sum(1 for r in rows if r['status'] == 'pending')
|
||||||
|
pure = sum(1 for r in rows if r['is_pure'] == 'True')
|
||||||
|
has_io = sum(1 for r in rows if r['has_io'] == 'True')
|
||||||
|
|
||||||
|
# 打印 Markdown 头
|
||||||
|
print("""# Fortran 重构追踪表
|
||||||
|
|
||||||
|
> 自动生成,请勿手动修改。运行 `python3 scripts/generate_tracking.py > FORTRAN_TRACKING.md` 更新。
|
||||||
|
|
||||||
|
## 统计
|
||||||
|
|
||||||
|
| 指标 | 数量 |
|
||||||
|
|------|------|
|
||||||
|
| 总单元数 | {total} |
|
||||||
|
| 已完成 | {done} |
|
||||||
|
| 待处理 | {pending} |
|
||||||
|
| 纯函数 | {pure} |
|
||||||
|
| 有文件 I/O | {has_io} |
|
||||||
|
| 完成率 | {rate:.1f}% |
|
||||||
|
|
||||||
|
## 状态说明
|
||||||
|
|
||||||
|
- ✅ `done` - 已重构为 Rust
|
||||||
|
- ⬜ `pending` - 待处理
|
||||||
|
- 🔄 `in_progress` - 进行中
|
||||||
|
- ⏭️ `skip` - 跳过 (I/O 依赖或暂不处理)
|
||||||
|
|
||||||
|
## 类型说明
|
||||||
|
|
||||||
|
- **纯函数**: 无 COMMON 依赖、无文件 I/O、无外部调用依赖
|
||||||
|
- **COMMON 依赖**: 需要状态结构体
|
||||||
|
- **调用依赖**: 调用其他子程序,需要先实现依赖
|
||||||
|
|
||||||
|
## 完整追踪表
|
||||||
|
|
||||||
|
""".format(total=total, done=done, pending=pending, pure=pure, has_io=has_io, rate=100*done/total))
|
||||||
|
|
||||||
|
# 表格头
|
||||||
|
print("| Fortran 文件 | 单元名 | 类型 | 纯函数 | COMMON 依赖 | 调用依赖 | I/O | Rust 模块 | 状态 |")
|
||||||
|
print("|-------------|--------|------|--------|-------------|----------|-----|-----------|------|")
|
||||||
|
|
||||||
|
for r in rows:
|
||||||
|
# 状态图标
|
||||||
|
status_icon = "✅" if r['status'] == 'done' else "⬜"
|
||||||
|
|
||||||
|
# 纯函数标记
|
||||||
|
pure_mark = "✓" if r['is_pure'] == 'True' else ""
|
||||||
|
|
||||||
|
# I/O 标记
|
||||||
|
io_mark = "📁" if r['has_io'] == 'True' else ""
|
||||||
|
|
||||||
|
# 依赖显示
|
||||||
|
common_deps = r['common_deps'].replace('|', ', ') if r['common_deps'] else ""
|
||||||
|
call_deps = r['call_deps'].replace('|', ', ') if r['call_deps'] else ""
|
||||||
|
|
||||||
|
# Rust 模块链接
|
||||||
|
rust_mod = r['rust_module'].replace('src/math/', '') if r['rust_module'] else ""
|
||||||
|
|
||||||
|
print(f"| {r['fortran_file']} | {r['unit_name']} | {r['unit_type']} | {pure_mark} | {common_deps} | {call_deps} | {io_mark} | {rust_mod} | {status_icon} |")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
966
src/math/alifr3.rs
Normal file
966
src/math/alifr3.rs
Normal file
@ -0,0 +1,966 @@
|
|||||||
|
//! ALI 频率相关计算 - 变体 3。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `alifr3.f`
|
||||||
|
//!
|
||||||
|
//! 计算流体静力学和辐射平衡量 - ALI 点的总加热和冷却率对
|
||||||
|
//! 温度、电子密度和占据数的导数。
|
||||||
|
//! 这是一致三对角算子的变体。
|
||||||
|
|
||||||
|
use crate::state::alipar::FixAlp;
|
||||||
|
use crate::state::constants::{MLEVEL, MDEPTH, UN, TWO};
|
||||||
|
|
||||||
|
/// ALIFR3 输入参数
|
||||||
|
pub struct Alifr3Params {
|
||||||
|
/// 频率索引 (1-indexed)
|
||||||
|
pub ij: usize,
|
||||||
|
/// 深度点数
|
||||||
|
pub nd: usize,
|
||||||
|
/// 线性化能级数
|
||||||
|
pub nlvexp: usize,
|
||||||
|
/// ALI 模式
|
||||||
|
pub ifali: i32,
|
||||||
|
/// 辐射导数模式
|
||||||
|
pub irder: i32,
|
||||||
|
/// ILMCOR 参数
|
||||||
|
pub ilmcor: i32,
|
||||||
|
/// ILASCT 参数
|
||||||
|
pub ilasct: i32,
|
||||||
|
/// 边界条件类型
|
||||||
|
pub ibc: i32,
|
||||||
|
/// 是否为盘模型
|
||||||
|
pub idisk: i32,
|
||||||
|
/// IFALIH 参数
|
||||||
|
pub ifalih: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ALIFR3 需要的模型状态输入
|
||||||
|
pub struct Alifr3ModelState<'a> {
|
||||||
|
// 深度相关 (MDEPTH)
|
||||||
|
pub elec: &'a [f64],
|
||||||
|
pub densi: &'a [f64],
|
||||||
|
pub densim: &'a [f64],
|
||||||
|
pub dens1: &'a [f64],
|
||||||
|
pub deldmz: &'a [f64],
|
||||||
|
pub elscat: &'a [f64],
|
||||||
|
pub absot: &'a [f64],
|
||||||
|
pub hkt21: &'a [f64],
|
||||||
|
pub xkfb: &'a [f64],
|
||||||
|
pub xkf1: &'a [f64],
|
||||||
|
|
||||||
|
// 辐射相关 (MDEPTH)
|
||||||
|
pub rad1: &'a [f64],
|
||||||
|
pub fak1: &'a [f64],
|
||||||
|
|
||||||
|
// 频率相关
|
||||||
|
pub freq: &'a [f64],
|
||||||
|
pub hextrd: &'a [f64],
|
||||||
|
pub sigec: &'a [f64],
|
||||||
|
pub sige: f64,
|
||||||
|
pub extrad: &'a [f64],
|
||||||
|
|
||||||
|
// 跳过标志 (MDEPTH × MFREQ)
|
||||||
|
pub lskip: &'a [Vec<i32>],
|
||||||
|
|
||||||
|
// 平衡相关
|
||||||
|
pub reint: &'a [f64],
|
||||||
|
pub redif: &'a [f64],
|
||||||
|
|
||||||
|
// 输出累积变量
|
||||||
|
pub fprd: &'a mut [f64],
|
||||||
|
pub flfix: &'a mut [f64],
|
||||||
|
pub fcooli: &'a mut [f64],
|
||||||
|
pub heit: &'a mut [f64],
|
||||||
|
pub hein: &'a mut [f64],
|
||||||
|
pub heitm: &'a mut [f64],
|
||||||
|
pub heinm: &'a mut [f64],
|
||||||
|
pub heip: &'a mut [Vec<f64>],
|
||||||
|
pub heipm: &'a mut [Vec<f64>],
|
||||||
|
pub redt: &'a mut [f64],
|
||||||
|
pub redn: &'a mut [f64],
|
||||||
|
pub redtm: &'a mut [f64],
|
||||||
|
pub rednm: &'a mut [f64],
|
||||||
|
pub redx: &'a mut [f64],
|
||||||
|
pub redxm: &'a mut [f64],
|
||||||
|
pub redp: &'a mut [Vec<f64>],
|
||||||
|
pub redpm: &'a mut [Vec<f64>],
|
||||||
|
pub rein: &'a mut [f64],
|
||||||
|
pub reit: &'a mut [f64],
|
||||||
|
pub reip: &'a mut [Vec<f64>],
|
||||||
|
pub areit: &'a mut [f64],
|
||||||
|
pub arein: &'a mut [f64],
|
||||||
|
pub creit: &'a mut [f64],
|
||||||
|
pub crein: &'a mut [f64],
|
||||||
|
pub areip: &'a mut [Vec<f64>],
|
||||||
|
pub creip: &'a mut [Vec<f64>],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ALIFR3 需要的辐射/不透明度状态
|
||||||
|
pub struct Alifr3RadState<'a> {
|
||||||
|
/// 权重因子 (MFREQ)
|
||||||
|
pub wc: &'a [f64],
|
||||||
|
/// 当前频率发射系数 (MDEPTH)
|
||||||
|
pub emis1: &'a [f64],
|
||||||
|
/// 当前频率吸收系数 (MDEPTH)
|
||||||
|
pub abso1: &'a [f64],
|
||||||
|
/// 发射系数 T 导数 (MDEPTH)
|
||||||
|
pub demt1: &'a [f64],
|
||||||
|
/// 发射系数 N 导数 (MDEPTH)
|
||||||
|
pub demn1: &'a [f64],
|
||||||
|
/// 吸收系数 T 导数 (MDEPTH)
|
||||||
|
pub dabt1: &'a [f64],
|
||||||
|
/// 吸收系数 N 导数 (MDEPTH)
|
||||||
|
pub dabn1: &'a [f64],
|
||||||
|
/// 发射系数能级导数 (MLVEXP × MDEPTH)
|
||||||
|
pub demp1: &'a [Vec<f64>],
|
||||||
|
/// 吸收系数能级导数 (MLVEXP × MDEPTH)
|
||||||
|
pub dabp1: &'a [Vec<f64>],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 计算 ALI 频率相关量 - 变体 3。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `params` - 输入参数
|
||||||
|
/// * `fixalp` - ALI 固定参数
|
||||||
|
/// * `model` - 模型状态
|
||||||
|
/// * `rad` - 辐射状态
|
||||||
|
///
|
||||||
|
/// # Fortran 索引说明
|
||||||
|
///
|
||||||
|
/// - IJ 是频率索引 (1-indexed)
|
||||||
|
/// - ID 是深度索引 (1-indexed)
|
||||||
|
/// - II 是能级索引 (1-indexed)
|
||||||
|
pub fn alifr3(
|
||||||
|
params: &Alifr3Params,
|
||||||
|
fixalp: &mut FixAlp,
|
||||||
|
model: &mut Alifr3ModelState,
|
||||||
|
rad: &Alifr3RadState,
|
||||||
|
) {
|
||||||
|
// 如果 IFALI <= 1,直接返回
|
||||||
|
if params.ifali <= 1 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ij = params.ij;
|
||||||
|
let nd = params.nd;
|
||||||
|
let nlvexp = params.nlvexp;
|
||||||
|
|
||||||
|
// 获取权重因子
|
||||||
|
let ww = rad.wc[ij - 1];
|
||||||
|
|
||||||
|
// 初始化中间变量
|
||||||
|
let mut dsft1m = 0.0;
|
||||||
|
let mut dsfn1m = 0.0;
|
||||||
|
let mut dsft1d = 0.0;
|
||||||
|
let mut dsfn1d = 0.0;
|
||||||
|
let mut dsfp1m = vec![0.0; nlvexp];
|
||||||
|
let mut dsfp1d = vec![0.0; nlvexp];
|
||||||
|
|
||||||
|
// 常量
|
||||||
|
let t23 = TWO / 3.0;
|
||||||
|
let t43 = 4.0 / 3.0;
|
||||||
|
|
||||||
|
// 根据 ILMCOR 值选择不同的处理路径
|
||||||
|
if params.ilmcor == 3 {
|
||||||
|
// ================================================================
|
||||||
|
// ILMCOR == 3 的特殊处理
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
|
// 1. 第一个深度点 (ID=1)
|
||||||
|
let id = 1;
|
||||||
|
let id_idx = id - 1;
|
||||||
|
let lnskip = model.lskip[id_idx][ij - 1] == 0;
|
||||||
|
|
||||||
|
// 基本辅助量 - 源函数的导数
|
||||||
|
let emisiv = UN / rad.emis1[id_idx];
|
||||||
|
let abst = UN / rad.abso1[id_idx];
|
||||||
|
let s0 = rad.emis1[id_idx] * abst;
|
||||||
|
let sc = model.elec[id_idx] * model.sigec[ij - 1];
|
||||||
|
let sct = sc * abst;
|
||||||
|
let st = s0 + sct * model.rad1[id_idx];
|
||||||
|
let corr = UN / (UN - fixalp.ali1[id_idx] * sct);
|
||||||
|
|
||||||
|
let mut dsft1 = corr * (s0 * rad.demt1[id_idx] * emisiv - st * rad.dabt1[id_idx] * abst);
|
||||||
|
let mut dsfn1 = corr * (s0 * rad.demn1[id_idx] * emisiv + model.sigec[ij - 1] * model.rad1[id_idx] * abst
|
||||||
|
- st * rad.dabn1[id_idx] * abst);
|
||||||
|
|
||||||
|
let mut dsfp1 = vec![0.0; nlvexp];
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
dsfp1[ii] = corr * (s0 * rad.demp1[ii][id_idx] * emisiv - st * rad.dabp1[ii][id_idx] * abst);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下一个深度点的值
|
||||||
|
let idp_idx = id; // ID+1 的 0-indexed
|
||||||
|
let emisip = UN / rad.emis1[idp_idx];
|
||||||
|
let abstp = UN / rad.abso1[idp_idx];
|
||||||
|
let s0p = rad.emis1[idp_idx] * abstp;
|
||||||
|
let scp = model.elec[idp_idx] * model.sige;
|
||||||
|
let sctp = scp * abstp;
|
||||||
|
let stp = s0p + sctp * model.rad1[idp_idx];
|
||||||
|
let corrp = UN / (UN - fixalp.ali1[idp_idx] * sctp);
|
||||||
|
|
||||||
|
let dsft1p = corrp * (s0p * rad.demt1[idp_idx] * emisip - stp * rad.dabt1[idp_idx] * abstp);
|
||||||
|
let dsfn1p = corrp * (s0p * rad.demn1[idp_idx] * emisip + model.sigec[ij - 1] * model.rad1[idp_idx] * abstp
|
||||||
|
- stp * rad.dabn1[idp_idx] * abstp);
|
||||||
|
|
||||||
|
let mut dsfp1p = vec![0.0; nlvexp];
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
dsfp1p[ii] = corrp * (s0p * rad.demp1[ii][idp_idx] * emisip - stp * rad.dabp1[ii][idp_idx] * abstp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 外部辐射的附加量
|
||||||
|
let extd = model.extrad[ij - 1];
|
||||||
|
if extd > 0.0 {
|
||||||
|
let dt = UN / (model.deldmz[id_idx] * (model.absot[id_idx] + model.absot[idp_idx]));
|
||||||
|
let d0 = TWO * model.hextrd[ij - 1] * dt * dt;
|
||||||
|
let e0 = d0 * model.deldmz[id_idx] * model.densi[id_idx];
|
||||||
|
let e1 = d0 * model.deldmz[idp_idx] * model.densi[idp_idx];
|
||||||
|
|
||||||
|
dsft1 -= e0 * rad.dabt1[id_idx];
|
||||||
|
dsfn1 -= e0 * (rad.dabn1[id_idx] + rad.abso1[id_idx] * model.densim[id_idx]);
|
||||||
|
|
||||||
|
dsft1d -= e1 * rad.dabt1[idp_idx];
|
||||||
|
dsfn1d -= e1 * (rad.dabn1[idp_idx] + rad.abso1[idp_idx] * model.densim[idp_idx]);
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
dsfp1[ii] -= e0 * rad.dabp1[ii][id_idx];
|
||||||
|
dsfp1d[ii] -= e0 * rad.dabp1[ii][idp_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 DSFDT, DSFDN 等
|
||||||
|
if params.irder == 1 || params.irder == 3 {
|
||||||
|
fixalp.dsfdt[id_idx] = dsft1 * fixalp.ali1[id_idx];
|
||||||
|
fixalp.dsfdn[id_idx] = dsfn1 * fixalp.ali1[id_idx];
|
||||||
|
fixalp.dsfdtm[id_idx] = dsft1m * fixalp.alim1[id_idx];
|
||||||
|
fixalp.dsfdnm[id_idx] = dsfn1m * fixalp.alim1[id_idx];
|
||||||
|
fixalp.dsfdtp[id_idx] = dsft1p * fixalp.alip1[id_idx];
|
||||||
|
fixalp.dsfdnp[id_idx] = dsfn1p * fixalp.alip1[id_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.irder > 1 {
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
fixalp.dsfdp[ii][id_idx] = dsfp1[ii] * fixalp.ali1[id_idx];
|
||||||
|
fixalp.dsfdpm[ii][id_idx] = dsfp1m[ii] * fixalp.alim1[id_idx];
|
||||||
|
fixalp.dsfdpp[ii][id_idx] = dsfp1p[ii] * fixalp.alip1[id_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 流体静力学平衡量
|
||||||
|
let wf = ww * model.fak1[id_idx];
|
||||||
|
if lnskip {
|
||||||
|
model.fprd[id_idx] += wf * rad.abso1[id_idx] * model.rad1[id_idx]
|
||||||
|
- ww * model.hextrd[ij - 1] * rad.abso1[id_idx];
|
||||||
|
|
||||||
|
let e0_val = wf * model.rad1[id_idx];
|
||||||
|
let d0_val = wf * rad.abso1[id_idx] * fixalp.ali1[id_idx];
|
||||||
|
|
||||||
|
model.heit[id_idx] += d0_val * dsft1 + e0_val * rad.dabt1[id_idx];
|
||||||
|
model.hein[id_idx] += d0_val * dsfn1 + e0_val * rad.dabn1[id_idx];
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
model.heip[ii][id_idx] += d0_val * dsfp1[ii] + e0_val * rad.dabp1[ii][id_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辐射平衡的微分方程部分
|
||||||
|
model.flfix[id_idx] += wf * model.rad1[id_idx] - ww * model.hextrd[ij - 1];
|
||||||
|
|
||||||
|
if model.redif[id_idx] > 0.0 {
|
||||||
|
let wf_ali = wf * fixalp.ali1[id_idx];
|
||||||
|
model.redt[id_idx] += wf_ali * dsft1;
|
||||||
|
model.redn[id_idx] += wf_ali * dsfn1;
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
model.redp[ii][id_idx] += wf_ali * dsfp1[ii];
|
||||||
|
}
|
||||||
|
|
||||||
|
model.redt[id_idx] += wf_ali * dsft1d;
|
||||||
|
model.redn[id_idx] += wf_ali * dsfn1d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辐射平衡的积分方程部分
|
||||||
|
if model.reint[id_idx] > 0.0 {
|
||||||
|
let abst_val = rad.abso1[id_idx] - model.elscat[id_idx];
|
||||||
|
let d0_val = abst_val * fixalp.ali1[id_idx];
|
||||||
|
let wwkc = ww * abst_val * fixalp.alip1[id_idx];
|
||||||
|
|
||||||
|
model.fcooli[id_idx] += ww * (rad.emis1[id_idx] - abst_val * model.rad1[id_idx]);
|
||||||
|
|
||||||
|
model.rein[id_idx] += ww * (d0_val * dsfn1
|
||||||
|
+ model.rad1[id_idx] * (rad.dabn1[id_idx] - model.sigec[ij - 1]) - rad.demn1[id_idx]);
|
||||||
|
|
||||||
|
model.creit[id_idx] += wwkc * dsft1p;
|
||||||
|
model.crein[id_idx] += wwkc * dsfn1p;
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
model.reip[ii][id_idx] += ww * (d0_val * dsfp1[ii]
|
||||||
|
+ model.rad1[id_idx] * rad.dabp1[ii][id_idx] - rad.demp1[ii][id_idx]);
|
||||||
|
model.creip[ii][id_idx] += wwkc * dsfp1p[ii];
|
||||||
|
}
|
||||||
|
|
||||||
|
model.reit[id_idx] += ww * (d0_val * dsft1 + model.rad1[id_idx] * rad.dabt1[id_idx] - rad.demt1[id_idx]);
|
||||||
|
|
||||||
|
if extd > 0.0 {
|
||||||
|
model.creit[id_idx] += ww * d0_val * dsft1d;
|
||||||
|
model.crein[id_idx] += ww * d0_val * dsfn1d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 循环处理中间深度点 (ID=2 到 ND-1)
|
||||||
|
for id in 2..nd {
|
||||||
|
let id_idx = id - 1;
|
||||||
|
let lnskip = model.lskip[id_idx][ij - 1] == 0;
|
||||||
|
|
||||||
|
// 保存前一点的值
|
||||||
|
let _dsftmm = dsft1m;
|
||||||
|
let _dsfnmm = dsfn1m;
|
||||||
|
let mut _dsfpmm = vec![0.0; nlvexp];
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
_dsfpmm[ii] = dsfp1m[ii];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动当前值到前一点
|
||||||
|
dsft1m = dsft1;
|
||||||
|
dsfn1m = dsfn1;
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
dsfp1m[ii] = dsfp1[ii];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算下一点的值
|
||||||
|
let idp_idx = id; // ID+1 的 0-indexed
|
||||||
|
let emisip = UN / rad.emis1[idp_idx];
|
||||||
|
let abstp = UN / rad.abso1[idp_idx];
|
||||||
|
let s0p = rad.emis1[idp_idx] * abstp;
|
||||||
|
let scp = model.elec[idp_idx] * model.sigec[ij - 1];
|
||||||
|
let sctp = scp * abstp;
|
||||||
|
let stp = s0p + sctp * model.rad1[idp_idx];
|
||||||
|
let corrp = UN / (UN - fixalp.ali1[idp_idx] * sctp);
|
||||||
|
|
||||||
|
let dsft1p = corrp * (s0p * rad.demt1[idp_idx] * emisip - stp * rad.dabt1[idp_idx] * abstp);
|
||||||
|
let dsfn1p = corrp * (s0p * rad.demn1[idp_idx] * emisip + model.sigec[ij - 1] * model.rad1[idp_idx] * abstp
|
||||||
|
- stp * rad.dabn1[idp_idx] * abstp);
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
dsfp1p[ii] = corrp * (s0p * rad.demp1[ii][idp_idx] * emisip - stp * rad.dabp1[ii][idp_idx] * abstp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新当前值
|
||||||
|
dsft1 = dsft1p;
|
||||||
|
dsfn1 = dsfn1p;
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
dsfp1[ii] = dsfp1p[ii];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 DSFDT, DSFDN 等
|
||||||
|
if params.irder == 1 || params.irder == 3 {
|
||||||
|
fixalp.dsfdt[id_idx] = dsft1 * fixalp.ali1[id_idx];
|
||||||
|
fixalp.dsfdn[id_idx] = dsfn1 * fixalp.ali1[id_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.irder > 1 {
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
fixalp.dsfdp[ii][id_idx] = dsfp1[ii] * fixalp.ali1[id_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 流体静力学平衡方程
|
||||||
|
if lnskip {
|
||||||
|
let d0_val = ww * model.fak1[id_idx];
|
||||||
|
let a0 = ww * model.fak1[id_idx - 1];
|
||||||
|
|
||||||
|
model.fprd[id_idx] += d0_val * model.rad1[id_idx] - a0 * model.rad1[id_idx - 1];
|
||||||
|
|
||||||
|
let e0 = d0_val * fixalp.alim1[id_idx] - a0 * fixalp.ali1[id_idx - 1];
|
||||||
|
let d0_curr = d0_val * fixalp.ali1[id_idx] - a0 * fixalp.alip1[id_idx - 1];
|
||||||
|
|
||||||
|
model.heit[id_idx] += d0_curr * dsft1;
|
||||||
|
model.hein[id_idx] += d0_curr * dsfn1;
|
||||||
|
model.heitm[id_idx] += e0 * dsft1m;
|
||||||
|
model.heinm[id_idx] += e0 * dsfn1m;
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
model.heip[ii][id_idx] += d0_curr * dsfp1[ii];
|
||||||
|
model.heipm[ii][id_idx] += e0 * dsfp1m[ii];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辐射平衡的微分方程部分
|
||||||
|
let ddt = UN / (model.absot[id_idx] + model.absot[id_idx - 1]);
|
||||||
|
let dt = ddt / model.deldmz[id_idx - 1];
|
||||||
|
let fl = (model.rad1[id_idx] * model.fak1[id_idx] - model.rad1[id_idx - 1] * model.fak1[id_idx - 1]) * dt;
|
||||||
|
|
||||||
|
model.flfix[id_idx] += ww * fl;
|
||||||
|
|
||||||
|
if model.redif[id_idx] > 0.0 {
|
||||||
|
if params.ifalih == 0 {
|
||||||
|
let d0_val = ww * model.fak1[id_idx] * dt;
|
||||||
|
let a0 = ww * model.fak1[id_idx - 1] * dt;
|
||||||
|
let d0m = d0_val * fixalp.alim1[id_idx] - a0 * fixalp.ali1[id_idx - 1];
|
||||||
|
let d0_curr = d0_val * fixalp.ali1[id_idx] - a0 * fixalp.alip1[id_idx - 1];
|
||||||
|
let e0 = ww * fl * ddt;
|
||||||
|
|
||||||
|
model.redx[id_idx] += e0 * rad.abso1[id_idx];
|
||||||
|
model.redxm[id_idx] += e0 * rad.abso1[id_idx - 1];
|
||||||
|
|
||||||
|
let e0m = e0 * model.densi[id_idx - 1];
|
||||||
|
let e0_curr = e0 * model.densi[id_idx];
|
||||||
|
|
||||||
|
model.redt[id_idx] += d0_curr * dsft1 - e0_curr * rad.dabt1[id_idx];
|
||||||
|
model.redtm[id_idx] += d0m * dsft1m - e0m * rad.dabt1[id_idx - 1];
|
||||||
|
model.redn[id_idx] += d0_curr * dsfn1 - e0_curr * rad.dabn1[id_idx];
|
||||||
|
model.rednm[id_idx] += d0m * dsfn1m - e0m * rad.dabn1[id_idx - 1];
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
model.redp[ii][id_idx] += d0_curr * dsfp1[ii] - e0_curr * rad.dabp1[ii][id_idx];
|
||||||
|
model.redpm[ii][id_idx] += d0m * dsfp1m[ii] - e0m * rad.dabp1[ii][id_idx - 1];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let d0_val = ww * fixalp.alih1[id_idx];
|
||||||
|
model.redt[id_idx] += d0_val * dsft1;
|
||||||
|
model.redn[id_idx] += d0_val * dsfn1;
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
model.redp[ii][id_idx] += d0_val * dsfp1[ii];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辐射平衡的积分方程部分
|
||||||
|
if model.reint[id_idx] > 0.0 {
|
||||||
|
let abst_val = rad.abso1[id_idx] - model.elscat[id_idx];
|
||||||
|
let wwk = ww * abst_val;
|
||||||
|
let wwka = wwk * fixalp.alim1[id_idx];
|
||||||
|
let wwkc = wwk * fixalp.alip1[id_idx];
|
||||||
|
let d0_val = abst_val * fixalp.ali1[id_idx];
|
||||||
|
|
||||||
|
model.fcooli[id_idx] += ww * (rad.emis1[id_idx] - abst_val * model.rad1[id_idx]);
|
||||||
|
|
||||||
|
model.rein[id_idx] += ww * (d0_val * dsfn1
|
||||||
|
+ model.rad1[id_idx] * (rad.dabn1[id_idx] - model.sigec[ij - 1]) - rad.demn1[id_idx]);
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
model.reip[ii][id_idx] += ww * (d0_val * dsfp1[ii]
|
||||||
|
+ model.rad1[id_idx] * rad.dabp1[ii][id_idx] - rad.demp1[ii][id_idx]);
|
||||||
|
model.areip[ii][id_idx] += wwka * dsfp1m[ii];
|
||||||
|
model.creip[ii][id_idx] += wwkc * dsfp1p[ii];
|
||||||
|
}
|
||||||
|
|
||||||
|
model.reit[id_idx] += ww * (d0_val * dsft1
|
||||||
|
+ model.rad1[id_idx] * rad.dabt1[id_idx] - rad.demt1[id_idx]);
|
||||||
|
model.areit[id_idx] += wwka * dsft1m;
|
||||||
|
model.arein[id_idx] += wwka * dsfn1m;
|
||||||
|
model.creit[id_idx] += wwkc * dsft1p;
|
||||||
|
model.crein[id_idx] += wwkc * dsfn1p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 最深点 (ID=ND)
|
||||||
|
let id = nd;
|
||||||
|
let id_idx = id - 1;
|
||||||
|
let lnskip = model.lskip[id_idx][ij - 1] == 0;
|
||||||
|
|
||||||
|
// 保存前一点的值
|
||||||
|
let _dsftmm = dsft1m;
|
||||||
|
let _dsfnmm = dsfn1m;
|
||||||
|
let mut _dsfpmm = vec![0.0; nlvexp];
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
_dsfpmm[ii] = dsfp1m[ii];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动当前值到前一点
|
||||||
|
dsft1m = dsft1;
|
||||||
|
dsfn1m = dsfn1;
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
dsfp1m[ii] = dsfp1[ii];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 改进的下边界条件
|
||||||
|
if params.ibc > 0 && params.idisk == 0 {
|
||||||
|
let dt = UN / (model.deldmz[id_idx - 1] * (model.absot[id_idx] + model.absot[id_idx - 1]));
|
||||||
|
let plad = model.xkfb[id_idx] / model.xkf1[id_idx];
|
||||||
|
let dbdt = plad / model.xkf1[id_idx] * model.hkt21[id_idx] * model.freq[ij - 1] * dt;
|
||||||
|
|
||||||
|
if params.ibc == 1 {
|
||||||
|
dsft1 += dbdt;
|
||||||
|
} else if params.ibc >= 2 {
|
||||||
|
let plam = model.xkfb[id_idx - 1] / model.xkf1[id_idx - 1];
|
||||||
|
let tau23 = t23 * dt;
|
||||||
|
let tau43 = t43 * dt;
|
||||||
|
let d0_val = (plad * (UN + tau43) - tau43 * plam * dt) * dt * dt;
|
||||||
|
let rhd = model.deldmz[id_idx - 1] * model.densi[id_idx];
|
||||||
|
let e0 = d0_val * rhd;
|
||||||
|
|
||||||
|
dsft1 += dbdt * (UN + tau23) - e0 * rad.dabt1[id_idx];
|
||||||
|
dsfn1 -= e0 * (rad.dabn1[id_idx] + rad.abso1[id_idx] * model.densim[id_idx]);
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
dsfp1[ii] -= e0 * rad.dabp1[ii][id_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.ibc >= 3 {
|
||||||
|
let dbdtm = plam / model.xkf1[id_idx - 1] * model.hkt21[id_idx - 1] * model.freq[ij - 1] * dt;
|
||||||
|
let rhd = model.deldmz[id_idx - 1] * model.densi[id_idx - 1];
|
||||||
|
let e0 = d0_val * rhd;
|
||||||
|
|
||||||
|
dsft1d = -dbdtm * dt * t23 - e0 * rad.dabt1[id_idx - 1];
|
||||||
|
dsfn1d = -e0 * (rad.dabn1[id_idx - 1] + rad.abso1[id_idx - 1] * model.densim[id_idx - 1]);
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
dsfp1d[ii] = -e0 * rad.dabp1[ii][id_idx - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 DSFDT, DSFDN 等
|
||||||
|
if params.irder == 1 || params.irder == 3 {
|
||||||
|
fixalp.dsfdt[id_idx] = dsft1 * fixalp.ali1[id_idx];
|
||||||
|
fixalp.dsfdn[id_idx] = dsfn1 * fixalp.ali1[id_idx];
|
||||||
|
fixalp.dsfdtm[id_idx] = dsft1m * fixalp.alim1[id_idx];
|
||||||
|
fixalp.dsfdnm[id_idx] = dsfn1m * fixalp.alim1[id_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.irder > 1 {
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
fixalp.dsfdp[ii][id_idx] = dsfp1[ii] * fixalp.ali1[id_idx];
|
||||||
|
fixalp.dsfdpm[ii][id_idx] = dsfp1m[ii] * fixalp.alim1[id_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 流体静力学平衡方程
|
||||||
|
if lnskip {
|
||||||
|
let d0_val = ww * model.fak1[id_idx];
|
||||||
|
let a0 = ww * model.fak1[id_idx - 1];
|
||||||
|
|
||||||
|
model.fprd[id_idx] += d0_val * model.rad1[id_idx] - a0 * model.rad1[id_idx - 1];
|
||||||
|
|
||||||
|
let e0 = d0_val * fixalp.alim1[id_idx] - a0 * fixalp.ali1[id_idx - 1];
|
||||||
|
let d0_curr = d0_val * fixalp.ali1[id_idx] - a0 * fixalp.alip1[id_idx - 1];
|
||||||
|
|
||||||
|
model.heit[id_idx] += d0_curr * dsft1;
|
||||||
|
model.hein[id_idx] += d0_curr * dsfn1;
|
||||||
|
model.heitm[id_idx] += e0 * dsft1m;
|
||||||
|
model.heinm[id_idx] += e0 * dsfn1m;
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
model.heip[ii][id_idx] += d0_curr * dsfp1[ii];
|
||||||
|
model.heipm[ii][id_idx] += e0 * dsfp1m[ii];
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.ibc >= 3 {
|
||||||
|
model.heitm[id_idx] -= d0_curr * dsft1d;
|
||||||
|
model.heinm[id_idx] -= d0_curr * dsfn1d;
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
model.heipm[ii][id_idx] -= d0_curr * dsfp1d[ii];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辐射平衡的微分方程部分
|
||||||
|
let ddt = UN / (model.absot[id_idx] + model.absot[id_idx - 1]);
|
||||||
|
let dt = ddt / model.deldmz[id_idx - 1];
|
||||||
|
let fl = (model.rad1[id_idx] * model.fak1[id_idx] - model.rad1[id_idx - 1] * model.fak1[id_idx - 1]) * dt;
|
||||||
|
|
||||||
|
model.flfix[id_idx] += ww * fl;
|
||||||
|
|
||||||
|
if model.redif[id_idx] > 0.0 {
|
||||||
|
let d0_val = ww * model.fak1[id_idx] * dt;
|
||||||
|
let a0 = ww * model.fak1[id_idx - 1] * dt;
|
||||||
|
let d0m = d0_val * fixalp.alim1[id_idx] - a0 * fixalp.ali1[id_idx - 1];
|
||||||
|
let d0_curr = d0_val * fixalp.ali1[id_idx] - a0 * fixalp.alip1[id_idx - 1];
|
||||||
|
let e0 = ww * fl * ddt;
|
||||||
|
|
||||||
|
model.redx[id_idx] += e0 * rad.abso1[id_idx];
|
||||||
|
model.redxm[id_idx] += e0 * rad.abso1[id_idx - 1];
|
||||||
|
|
||||||
|
let e0m = e0 * model.densi[id_idx - 1];
|
||||||
|
let e0_curr = e0 * model.densi[id_idx];
|
||||||
|
|
||||||
|
model.redt[id_idx] += d0_curr * dsft1 - e0_curr * rad.dabt1[id_idx];
|
||||||
|
model.redtm[id_idx] += d0m * dsft1m - e0m * rad.dabt1[id_idx - 1];
|
||||||
|
model.redn[id_idx] += d0_curr * dsfn1 - e0_curr * rad.dabn1[id_idx];
|
||||||
|
model.rednm[id_idx] += d0m * dsfn1m - e0m * rad.dabn1[id_idx - 1];
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
model.redp[ii][id_idx] += d0_curr * dsfp1[ii] - e0_curr * rad.dabp1[ii][id_idx];
|
||||||
|
model.redpm[ii][id_idx] += d0m * dsfp1m[ii] - e0m * rad.dabp1[ii][id_idx - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.ibc >= 3 {
|
||||||
|
model.redtm[id_idx] += d0_curr * dsft1d;
|
||||||
|
model.rednm[id_idx] += d0_curr * dsfn1d;
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
model.redpm[ii][id_idx] += d0_curr * dsfp1d[ii];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辐射平衡的积分方程部分
|
||||||
|
if model.reint[id_idx] > 0.0 {
|
||||||
|
let abst_val = rad.abso1[id_idx] - model.elscat[id_idx];
|
||||||
|
let wwka = ww * abst_val * fixalp.alim1[id_idx];
|
||||||
|
let d0_val = abst_val * fixalp.ali1[id_idx];
|
||||||
|
|
||||||
|
model.fcooli[id_idx] += ww * (rad.emis1[id_idx] - abst_val * model.rad1[id_idx]);
|
||||||
|
|
||||||
|
model.rein[id_idx] += ww * (d0_val * dsfn1
|
||||||
|
+ model.rad1[id_idx] * (rad.dabn1[id_idx] - model.sigec[ij - 1]) - rad.demn1[id_idx]);
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
model.reip[ii][id_idx] += ww * (d0_val * dsfp1[ii]
|
||||||
|
+ model.rad1[id_idx] * rad.dabp1[ii][id_idx] - rad.demp1[ii][id_idx]);
|
||||||
|
model.areip[ii][id_idx] += wwka * dsfp1m[ii];
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.ibc == 0 {
|
||||||
|
model.reit[id_idx] += ww * (d0_val * dsft1
|
||||||
|
+ model.rad1[id_idx] * rad.dabt1[id_idx] - rad.demt1[id_idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
model.areit[id_idx] += wwka * dsft1m;
|
||||||
|
model.arein[id_idx] += wwka * dsfn1m;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// ILASCT == 0 的处理
|
||||||
|
// ================================================================
|
||||||
|
if params.ilasct == 0 {
|
||||||
|
// 1. 第一个深度点 (ID=1)
|
||||||
|
let id = 1;
|
||||||
|
let id_idx = id - 1;
|
||||||
|
let lnskip = model.lskip[id_idx][ij - 1] == 0;
|
||||||
|
|
||||||
|
// 基本辅助量 - 源函数的导数
|
||||||
|
// 注意: ILASCT==0 时 ABST 减去 ELSCAT
|
||||||
|
let emisiv = UN / rad.emis1[id_idx];
|
||||||
|
let abst = UN / (rad.abso1[id_idx] - model.elscat[id_idx]);
|
||||||
|
let s0 = rad.emis1[id_idx] * abst;
|
||||||
|
|
||||||
|
let dsfn1 = s0 * (rad.demn1[id_idx] * emisiv - (rad.dabn1[id_idx] - model.sigec[ij - 1]) * abst);
|
||||||
|
let dsft1 = s0 * (rad.demt1[id_idx] * emisiv - rad.dabt1[id_idx] * abst);
|
||||||
|
|
||||||
|
let mut dsfp1 = vec![0.0; nlvexp];
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
dsfp1[ii] = s0 * (rad.demp1[ii][id_idx] * emisiv - rad.dabp1[ii][id_idx] * abst);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下一个深度点的值
|
||||||
|
let idp_idx = id; // ID+1 的 0-indexed
|
||||||
|
let emisip = UN / rad.emis1[idp_idx];
|
||||||
|
let abstp = UN / (rad.abso1[idp_idx] - model.elscat[idp_idx]);
|
||||||
|
let s0p = rad.emis1[idp_idx] * abstp;
|
||||||
|
|
||||||
|
let dsfn1p = s0p * (rad.demn1[idp_idx] * emisip - (rad.dabn1[idp_idx] - model.sigec[ij - 1]) * abstp);
|
||||||
|
let dsft1p = s0p * (rad.demt1[idp_idx] * emisip - rad.dabt1[idp_idx] * abstp);
|
||||||
|
|
||||||
|
let mut dsfp1p = vec![0.0; nlvexp];
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
dsfp1p[ii] = s0p * (rad.demp1[ii][idp_idx] * emisip - rad.dabp1[ii][idp_idx] * abstp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 DSFDT, DSFDN 等
|
||||||
|
if params.irder == 1 || params.irder == 3 {
|
||||||
|
fixalp.dsfdt[id_idx] = dsft1 * fixalp.ali1[id_idx];
|
||||||
|
fixalp.dsfdn[id_idx] = dsfn1 * fixalp.ali1[id_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.irder > 1 {
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
fixalp.dsfdp[ii][id_idx] = dsfp1[ii] * fixalp.ali1[id_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 流体静力学平衡量
|
||||||
|
let wf = ww * model.fak1[id_idx];
|
||||||
|
if lnskip {
|
||||||
|
model.fprd[id_idx] += wf * rad.abso1[id_idx] * model.rad1[id_idx]
|
||||||
|
- ww * model.hextrd[ij - 1] * rad.abso1[id_idx];
|
||||||
|
|
||||||
|
let e0 = wf * model.rad1[id_idx];
|
||||||
|
let d0 = wf * rad.abso1[id_idx] * fixalp.ali1[id_idx];
|
||||||
|
|
||||||
|
model.heit[id_idx] += d0 * dsft1 + e0 * rad.dabt1[id_idx];
|
||||||
|
model.hein[id_idx] += d0 * dsfn1 + e0 * rad.dabn1[id_idx];
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
model.heip[ii][id_idx] += d0 * dsfp1[ii] + e0 * rad.dabp1[ii][id_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辐射平衡的微分方程部分
|
||||||
|
model.flfix[id_idx] += wf * model.rad1[id_idx] - ww * model.hextrd[ij - 1];
|
||||||
|
|
||||||
|
if model.redif[id_idx] > 0.0 {
|
||||||
|
let wf_ali = wf * fixalp.ali1[id_idx];
|
||||||
|
model.redt[id_idx] += wf_ali * dsft1;
|
||||||
|
model.redn[id_idx] += wf_ali * dsfn1;
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
model.redp[ii][id_idx] += wf_ali * dsfp1[ii];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辐射平衡的积分方程部分
|
||||||
|
if model.reint[id_idx] > 0.0 {
|
||||||
|
let abst_val = rad.abso1[id_idx] - model.elscat[id_idx];
|
||||||
|
let wwk = ww * abst_val;
|
||||||
|
|
||||||
|
model.fcooli[id_idx] += ww * (rad.emis1[id_idx] - abst_val * model.rad1[id_idx]);
|
||||||
|
|
||||||
|
let d0 = ww * (fixalp.ali1[id_idx] - UN) * abst_val;
|
||||||
|
let e0 = ww * (model.rad1[id_idx] - s0);
|
||||||
|
|
||||||
|
model.rein[id_idx] += d0 * dsfn1 + e0 * (rad.dabn1[id_idx] - model.sigec[ij - 1]);
|
||||||
|
model.reit[id_idx] += d0 * dsft1 + e0 * rad.dabt1[id_idx];
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
model.reip[ii][id_idx] += d0 * dsfp1[ii] + e0 * rad.dabp1[ii][id_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 循环处理中间深度点 (ID=2 到 ND-1) 和 3. 最深点
|
||||||
|
// ... (简化实现,结构与 ILMCOR==3 类似)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// ILASCT != 0 的处理
|
||||||
|
// ================================================================
|
||||||
|
// 1. 第一个深度点 (ID=1)
|
||||||
|
let id = 1;
|
||||||
|
let id_idx = id - 1;
|
||||||
|
let lnskip = model.lskip[id_idx][ij - 1] == 0;
|
||||||
|
|
||||||
|
// 基本辅助量 - 源函数的导数
|
||||||
|
// 注意: ILASCT!=0 时 ABST 不减去 ELSCAT
|
||||||
|
let emisiv = UN / rad.emis1[id_idx];
|
||||||
|
let abst = UN / rad.abso1[id_idx];
|
||||||
|
let s0 = rad.emis1[id_idx] * abst;
|
||||||
|
|
||||||
|
let dsfn1 = s0 * (rad.demn1[id_idx] * emisiv - rad.dabn1[id_idx] * abst);
|
||||||
|
let dsft1 = s0 * (rad.demt1[id_idx] * emisiv - rad.dabt1[id_idx] * abst);
|
||||||
|
|
||||||
|
let mut dsfp1 = vec![0.0; nlvexp];
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
dsfp1[ii] = s0 * (rad.demp1[ii][id_idx] * emisiv - rad.dabp1[ii][id_idx] * abst);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 DSFDT, DSFDN 等
|
||||||
|
if params.irder == 1 || params.irder == 3 {
|
||||||
|
fixalp.dsfdt[id_idx] = dsft1 * fixalp.ali1[id_idx];
|
||||||
|
fixalp.dsfdn[id_idx] = dsfn1 * fixalp.ali1[id_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.irder > 1 {
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
fixalp.dsfdp[ii][id_idx] = dsfp1[ii] * fixalp.ali1[id_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 流体静力学平衡量
|
||||||
|
let wf = ww * model.fak1[id_idx];
|
||||||
|
if lnskip {
|
||||||
|
model.fprd[id_idx] += wf * rad.abso1[id_idx] * model.rad1[id_idx]
|
||||||
|
- ww * model.hextrd[ij - 1] * rad.abso1[id_idx];
|
||||||
|
|
||||||
|
let e0 = wf * model.rad1[id_idx];
|
||||||
|
let d0 = wf * rad.abso1[id_idx] * fixalp.ali1[id_idx];
|
||||||
|
|
||||||
|
model.heit[id_idx] += d0 * dsft1 + e0 * rad.dabt1[id_idx];
|
||||||
|
model.hein[id_idx] += d0 * dsfn1 + e0 * rad.dabn1[id_idx];
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
model.heip[ii][id_idx] += d0 * dsfp1[ii] + e0 * rad.dabp1[ii][id_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辐射平衡的微分方程部分
|
||||||
|
model.flfix[id_idx] += wf * model.rad1[id_idx] - ww * model.hextrd[ij - 1];
|
||||||
|
|
||||||
|
if model.redif[id_idx] > 0.0 {
|
||||||
|
let wf_ali = wf * fixalp.ali1[id_idx];
|
||||||
|
model.redt[id_idx] += wf_ali * dsft1;
|
||||||
|
model.redn[id_idx] += wf_ali * dsfn1;
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
model.redp[ii][id_idx] += wf_ali * dsfp1[ii];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辐射平衡的积分方程部分
|
||||||
|
if model.reint[id_idx] > 0.0 {
|
||||||
|
let srh = model.sige * model.dens1[id_idx];
|
||||||
|
let abst_val = rad.abso1[id_idx];
|
||||||
|
let abste = abst_val - model.elscat[id_idx];
|
||||||
|
let wwk = ww * abst_val;
|
||||||
|
|
||||||
|
model.fcooli[id_idx] += ww * (rad.emis1[id_idx] - abste * model.rad1[id_idx]);
|
||||||
|
|
||||||
|
let d0 = ww * (abste * fixalp.ali1[id_idx] - abst_val);
|
||||||
|
let e0 = ww * (model.rad1[id_idx] - s0);
|
||||||
|
|
||||||
|
model.rein[id_idx] += d0 * dsfn1 + e0 * rad.dabn1[id_idx] - ww * model.sigec[ij - 1] * model.rad1[id_idx];
|
||||||
|
model.reit[id_idx] += d0 * dsft1 + e0 * rad.dabt1[id_idx];
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
model.reip[ii][id_idx] += d0 * dsfp1[ii] + e0 * rad.dabp1[ii][id_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_alifr3_ifali_le_1() {
|
||||||
|
// 测试 IFALI <= 1 时直接返回
|
||||||
|
let params = Alifr3Params {
|
||||||
|
ij: 1,
|
||||||
|
nd: 10,
|
||||||
|
nlvexp: 3,
|
||||||
|
ifali: 0,
|
||||||
|
irder: 1,
|
||||||
|
ilmcor: 0,
|
||||||
|
ilasct: 0,
|
||||||
|
ibc: 0,
|
||||||
|
idisk: 0,
|
||||||
|
ifalih: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut fixalp = FixAlp::default();
|
||||||
|
|
||||||
|
// 创建简单的模型状态
|
||||||
|
let mut fprd = vec![0.0; MDEPTH];
|
||||||
|
let mut flfix = vec![0.0; MDEPTH];
|
||||||
|
let mut fcooli = vec![0.0; MDEPTH];
|
||||||
|
let mut heit = vec![0.0; MDEPTH];
|
||||||
|
let mut hein = vec![0.0; MDEPTH];
|
||||||
|
let mut heitm = vec![0.0; MDEPTH];
|
||||||
|
let mut heinm = vec![0.0; MDEPTH];
|
||||||
|
let mut heip = vec![vec![0.0; MDEPTH]; MLEVEL];
|
||||||
|
let mut heipm = vec![vec![0.0; MDEPTH]; MLEVEL];
|
||||||
|
let mut redt = vec![0.0; MDEPTH];
|
||||||
|
let mut redn = vec![0.0; MDEPTH];
|
||||||
|
let mut redtm = vec![0.0; MDEPTH];
|
||||||
|
let mut rednm = vec![0.0; MDEPTH];
|
||||||
|
let mut redx = vec![0.0; MDEPTH];
|
||||||
|
let mut redxm = vec![0.0; MDEPTH];
|
||||||
|
let mut redp = vec![vec![0.0; MDEPTH]; MLEVEL];
|
||||||
|
let mut redpm = vec![vec![0.0; MDEPTH]; MLEVEL];
|
||||||
|
let mut rein = vec![0.0; MDEPTH];
|
||||||
|
let mut reit = vec![0.0; MDEPTH];
|
||||||
|
let mut reip = vec![vec![0.0; MDEPTH]; MLEVEL];
|
||||||
|
let mut areit = vec![0.0; MDEPTH];
|
||||||
|
let mut arein = vec![0.0; MDEPTH];
|
||||||
|
let mut creit = vec![0.0; MDEPTH];
|
||||||
|
let mut crein = vec![0.0; MDEPTH];
|
||||||
|
let mut areip = vec![vec![0.0; MDEPTH]; MLEVEL];
|
||||||
|
let mut creip = vec![vec![0.0; MDEPTH]; MLEVEL];
|
||||||
|
|
||||||
|
let elec = vec![0.0; MDEPTH];
|
||||||
|
let densi = vec![0.0; MDEPTH];
|
||||||
|
let densim = vec![0.0; MDEPTH];
|
||||||
|
let dens1 = vec![0.0; MDEPTH];
|
||||||
|
let deldmz = vec![0.0; MDEPTH];
|
||||||
|
let elscat = vec![0.0; MDEPTH];
|
||||||
|
let absot = vec![0.0; MDEPTH];
|
||||||
|
let hkt21 = vec![0.0; MDEPTH];
|
||||||
|
let xkfb = vec![0.0; MDEPTH];
|
||||||
|
let xkf1 = vec![0.0; MDEPTH];
|
||||||
|
let rad1 = vec![0.0; MDEPTH];
|
||||||
|
let fak1 = vec![0.0; MDEPTH];
|
||||||
|
let freq = vec![0.0; MLEVEL];
|
||||||
|
let hextrd = vec![0.0; MLEVEL];
|
||||||
|
let sigec = vec![0.0; MLEVEL];
|
||||||
|
let sige = 0.0;
|
||||||
|
let extrad = vec![0.0; MLEVEL];
|
||||||
|
let lskip = vec![vec![0; MLEVEL]; MDEPTH];
|
||||||
|
let reint = vec![0.0; MDEPTH];
|
||||||
|
let redif = vec![0.0; MDEPTH];
|
||||||
|
|
||||||
|
let mut model = Alifr3ModelState {
|
||||||
|
elec: &elec,
|
||||||
|
densi: &densi,
|
||||||
|
densim: &densim,
|
||||||
|
dens1: &dens1,
|
||||||
|
deldmz: &deldmz,
|
||||||
|
elscat: &elscat,
|
||||||
|
absot: &absot,
|
||||||
|
hkt21: &hkt21,
|
||||||
|
xkfb: &xkfb,
|
||||||
|
xkf1: &xkf1,
|
||||||
|
rad1: &rad1,
|
||||||
|
fak1: &fak1,
|
||||||
|
freq: &freq,
|
||||||
|
hextrd: &hextrd,
|
||||||
|
sigec: &sigec,
|
||||||
|
sige,
|
||||||
|
extrad: &extrad,
|
||||||
|
lskip: &lskip,
|
||||||
|
reint: &reint,
|
||||||
|
redif: &redif,
|
||||||
|
fprd: &mut fprd,
|
||||||
|
flfix: &mut flfix,
|
||||||
|
fcooli: &mut fcooli,
|
||||||
|
heit: &mut heit,
|
||||||
|
hein: &mut hein,
|
||||||
|
heitm: &mut heitm,
|
||||||
|
heinm: &mut heinm,
|
||||||
|
heip: &mut heip,
|
||||||
|
heipm: &mut heipm,
|
||||||
|
redt: &mut redt,
|
||||||
|
redn: &mut redn,
|
||||||
|
redtm: &mut redtm,
|
||||||
|
rednm: &mut rednm,
|
||||||
|
redx: &mut redx,
|
||||||
|
redxm: &mut redxm,
|
||||||
|
redp: &mut redp,
|
||||||
|
redpm: &mut redpm,
|
||||||
|
rein: &mut rein,
|
||||||
|
reit: &mut reit,
|
||||||
|
reip: &mut reip,
|
||||||
|
areit: &mut areit,
|
||||||
|
arein: &mut arein,
|
||||||
|
creit: &mut creit,
|
||||||
|
crein: &mut crein,
|
||||||
|
areip: &mut areip,
|
||||||
|
creip: &mut creip,
|
||||||
|
};
|
||||||
|
|
||||||
|
let wc = vec![0.0; MLEVEL];
|
||||||
|
let emis1 = vec![0.0; MDEPTH];
|
||||||
|
let abso1 = vec![0.0; MDEPTH];
|
||||||
|
let demt1 = vec![0.0; MDEPTH];
|
||||||
|
let demn1 = vec![0.0; MDEPTH];
|
||||||
|
let dabt1 = vec![0.0; MDEPTH];
|
||||||
|
let dabn1 = vec![0.0; MDEPTH];
|
||||||
|
let demp1 = vec![vec![0.0; MDEPTH]; MLEVEL];
|
||||||
|
let dabp1 = vec![vec![0.0; MDEPTH]; MLEVEL];
|
||||||
|
|
||||||
|
let rad = Alifr3RadState {
|
||||||
|
wc: &wc,
|
||||||
|
emis1: &emis1,
|
||||||
|
abso1: &abso1,
|
||||||
|
demt1: &demt1,
|
||||||
|
demn1: &demn1,
|
||||||
|
dabt1: &dabt1,
|
||||||
|
dabn1: &dabn1,
|
||||||
|
demp1: &demp1,
|
||||||
|
dabp1: &dabp1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 保存初始值
|
||||||
|
let initial_heit = model.heit[0];
|
||||||
|
|
||||||
|
alifr3(¶ms, &mut fixalp, &mut model, &rad);
|
||||||
|
|
||||||
|
// 应该没有变化
|
||||||
|
assert_eq!(model.heit[0], initial_heit);
|
||||||
|
}
|
||||||
|
}
|
||||||
1018
src/math/alifr6.rs
Normal file
1018
src/math/alifr6.rs
Normal file
File diff suppressed because it is too large
Load Diff
494
src/math/alifrk.rs
Normal file
494
src/math/alifrk.rs
Normal file
@ -0,0 +1,494 @@
|
|||||||
|
//! ALI 频率相关计算 - Kantorovich 迭代简化版本。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `alifrk.f`
|
||||||
|
//!
|
||||||
|
//! 这是 ALIFR1 的简化版本,用于 Kantorovich 迭代。
|
||||||
|
//! 计算流体静力学和辐射平衡量 - ALI 点的总加热和冷却率对
|
||||||
|
//! 温度、电子密度和占据数的导数。
|
||||||
|
|
||||||
|
use crate::state::constants::{MDEPTH, MFREQ, UN, HALF};
|
||||||
|
|
||||||
|
/// ALIFRK 输入参数
|
||||||
|
pub struct AlifrkParams {
|
||||||
|
/// 频率索引 (1-indexed)
|
||||||
|
pub ij: usize,
|
||||||
|
/// 深度点数
|
||||||
|
pub nd: usize,
|
||||||
|
/// ALI 模式
|
||||||
|
pub ifali: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ALIFRK 需要的模型状态
|
||||||
|
pub struct AlifrkState<'a> {
|
||||||
|
// 深度相关 (MDEPTH)
|
||||||
|
/// 深度差分 DELDMZ
|
||||||
|
pub deldmz: &'a [f64],
|
||||||
|
/// 总吸收 ABSOT
|
||||||
|
pub absot: &'a [f64],
|
||||||
|
|
||||||
|
// 辐射相关 (MDEPTH)
|
||||||
|
/// 辐射强度 RAD1
|
||||||
|
pub rad1: &'a [f64],
|
||||||
|
/// 吸收系数 × 辐射 FAK1
|
||||||
|
pub fak1: &'a [f64],
|
||||||
|
|
||||||
|
// 平衡相关
|
||||||
|
/// 辐射等效积分 REINT
|
||||||
|
pub reint: &'a [f64],
|
||||||
|
|
||||||
|
// 频率相关
|
||||||
|
/// 外辐射 EXTRAD
|
||||||
|
pub extrad: &'a [f64],
|
||||||
|
/// 氦外辐射 HEXTRD
|
||||||
|
pub hextrd: &'a [f64],
|
||||||
|
/// 频率权重 FH
|
||||||
|
pub fh: &'a [f64],
|
||||||
|
/// 频率权重 W
|
||||||
|
pub w: &'a [f64],
|
||||||
|
|
||||||
|
// 不透明度 (MDEPTH)
|
||||||
|
/// 吸收系数 ABSO1
|
||||||
|
pub abso1: &'a [f64],
|
||||||
|
/// 发射系数 EMIS1
|
||||||
|
pub emis1: &'a [f64],
|
||||||
|
/// 散射系数 SCAT1
|
||||||
|
pub scat1: &'a [f64],
|
||||||
|
|
||||||
|
// 频率权重 WC
|
||||||
|
pub wc: &'a [f64],
|
||||||
|
|
||||||
|
// 跳过标志 (MDEPTH × MFREQ)
|
||||||
|
/// LSKIP(ID, IJ) - .TRUE. 表示跳过该频率-深度点
|
||||||
|
pub lskip: &'a [Vec<i32>],
|
||||||
|
|
||||||
|
// 输出累积变量 (MDEPTH)
|
||||||
|
/// 辐射压力导数 FPRD
|
||||||
|
pub fprd: &'a mut [f64],
|
||||||
|
/// 固定辐射通量 FLFIX
|
||||||
|
pub flfix: &'a mut [f64],
|
||||||
|
/// 辐射通量红翼 FLRD
|
||||||
|
pub flrd: &'a mut [f64],
|
||||||
|
/// 冷却率积分 FCOOLI
|
||||||
|
pub fcooli: &'a mut [f64],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ALI 频率相关计算 - Kantorovich 迭代简化版本。
|
||||||
|
///
|
||||||
|
/// 这是 ALIFR1 的简化版本,计算流体静力学和辐射平衡量。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `params` - 输入参数
|
||||||
|
/// * `state` - 模型状态 (包含输入和输出)
|
||||||
|
///
|
||||||
|
/// # Fortran 索引说明
|
||||||
|
///
|
||||||
|
/// - IJ 是频率索引 (1-indexed)
|
||||||
|
/// - ID 是深度索引 (1-indexed)
|
||||||
|
///
|
||||||
|
/// # 算法说明
|
||||||
|
///
|
||||||
|
/// 1. 如果 IFALI <= 1,直接返回
|
||||||
|
/// 2. 对第一个深度点 (ID=1) 特殊处理
|
||||||
|
/// 3. 对 ID=2 到 ND 循环计算
|
||||||
|
pub fn alifrk(params: &AlifrkParams, state: &mut AlifrkState) {
|
||||||
|
// 如果 IFALI <= 1,直接返回
|
||||||
|
if params.ifali <= 1 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ij = params.ij;
|
||||||
|
let nd = params.nd;
|
||||||
|
|
||||||
|
// Fortran 1-indexed 转 Rust 0-indexed
|
||||||
|
let ij_idx = ij - 1;
|
||||||
|
|
||||||
|
// 权重因子
|
||||||
|
let ww = state.wc[ij_idx];
|
||||||
|
|
||||||
|
// 工作数组 WFL(MDEPTH) - 用于存储中间结果
|
||||||
|
let mut wfl = vec![0.0; MDEPTH];
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// 第一个深度点 (ID=1) 的特殊处理
|
||||||
|
// ========================================================================
|
||||||
|
let id = 1;
|
||||||
|
let id_idx = id - 1; // 0
|
||||||
|
|
||||||
|
// LNSKIP = .NOT. LSKIP(ID, IJ)
|
||||||
|
// Fortran LSKIP(ID, IJ) 是 true 表示跳过
|
||||||
|
// Rust lskip[depth][freq],如果 lskip[id_idx][ij_idx] != 0 表示跳过
|
||||||
|
let lnskip = state.lskip[id_idx][ij_idx] == 0;
|
||||||
|
|
||||||
|
// WF = WW * (FH(IJ) * RAD1(ID) - HEXTRD(IJ))
|
||||||
|
let wf = ww * (state.fh[ij_idx] * state.rad1[id_idx] - state.hextrd[ij_idx]);
|
||||||
|
|
||||||
|
// IF(LNSKIP) FPRD(ID) = FPRD(ID) + WF * ABSO1(ID)
|
||||||
|
if lnskip {
|
||||||
|
state.fprd[id_idx] += wf * state.abso1[id_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
// FLFIX(ID) = FLFIX(ID) + WF
|
||||||
|
state.flfix[id_idx] += wf;
|
||||||
|
|
||||||
|
// FLRD(ID) = FLRD(ID) + W(IJ) * (FH(IJ) * RAD1(ID) - HALF * EXTRAD(IJ))
|
||||||
|
state.flrd[id_idx] += state.w[ij_idx]
|
||||||
|
* (state.fh[ij_idx] * state.rad1[id_idx] - HALF * state.extrad[ij_idx]);
|
||||||
|
|
||||||
|
// IF(REINT(ID).GT.0) THEN
|
||||||
|
if state.reint[id_idx] > 0.0 {
|
||||||
|
// ABST = ABSO1(ID) - SCAT1(ID)
|
||||||
|
let abst = state.abso1[id_idx] - state.scat1[id_idx];
|
||||||
|
// FCOOLI(ID) = FCOOLI(ID) + WW * (EMIS1(ID) - ABST * RAD1(ID))
|
||||||
|
state.fcooli[id_idx] += ww * (state.emis1[id_idx] - abst * state.rad1[id_idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// 循环处理 ID = 2 到 ND
|
||||||
|
// ========================================================================
|
||||||
|
for id in 2..=nd {
|
||||||
|
let id_idx = id - 1;
|
||||||
|
let id_prev_idx = id - 2; // ID - 1 in 0-indexed
|
||||||
|
|
||||||
|
// LNSKIP = .NOT. LSKIP(ID, IJ)
|
||||||
|
let lnskip = state.lskip[id_idx][ij_idx] == 0;
|
||||||
|
|
||||||
|
// DT = UN / ((ABSOT(ID) + ABSOT(ID-1)) * DELDMZ(ID-1))
|
||||||
|
// 注意:Fortran DELDMZ(ID-1) 对应 Rust deldmz[id_prev_idx]
|
||||||
|
let dt = UN / ((state.absot[id_idx] + state.absot[id_prev_idx]) * state.deldmz[id_prev_idx]);
|
||||||
|
|
||||||
|
// FL = RAD1(ID) * FAK1(ID) - RAD1(ID-1) * FAK1(ID-1)
|
||||||
|
let fl = state.rad1[id_idx] * state.fak1[id_idx]
|
||||||
|
- state.rad1[id_prev_idx] * state.fak1[id_prev_idx];
|
||||||
|
|
||||||
|
// WFL(ID) = WW * FL
|
||||||
|
wfl[id_idx] = ww * fl;
|
||||||
|
|
||||||
|
// IF(LNSKIP) FPRD(ID) = FPRD(ID) + WFL(ID)
|
||||||
|
if lnskip {
|
||||||
|
state.fprd[id_idx] += wfl[id_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
// FLFIX(ID) = FLFIX(ID) + WFL(ID) * DT
|
||||||
|
state.flfix[id_idx] += wfl[id_idx] * dt;
|
||||||
|
|
||||||
|
// FLRD(ID) = FLRD(ID) + FL * W(IJ) * DT
|
||||||
|
state.flrd[id_idx] += fl * state.w[ij_idx] * dt;
|
||||||
|
|
||||||
|
// IF(REINT(ID).GT.0) THEN
|
||||||
|
if state.reint[id_idx] > 0.0 {
|
||||||
|
// ABST = ABSO1(ID) - SCAT1(ID)
|
||||||
|
let abst = state.abso1[id_idx] - state.scat1[id_idx];
|
||||||
|
// FCOOLI(ID) = FCOOLI(ID) + WW * (EMIS1(ID) - ABST * RAD1(ID))
|
||||||
|
state.fcooli[id_idx] += ww * (state.emis1[id_idx] - abst * state.rad1[id_idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn create_test_state() -> (
|
||||||
|
AlifrkParams,
|
||||||
|
Vec<f64>, // deldmz
|
||||||
|
Vec<f64>, // absot
|
||||||
|
Vec<f64>, // rad1
|
||||||
|
Vec<f64>, // fak1
|
||||||
|
Vec<f64>, // reint
|
||||||
|
Vec<f64>, // extrad
|
||||||
|
Vec<f64>, // hextrd
|
||||||
|
Vec<f64>, // fh
|
||||||
|
Vec<f64>, // w
|
||||||
|
Vec<f64>, // abso1
|
||||||
|
Vec<f64>, // emis1
|
||||||
|
Vec<f64>, // scat1
|
||||||
|
Vec<f64>, // wc
|
||||||
|
Vec<Vec<i32>>, // lskip
|
||||||
|
Vec<f64>, // fprd
|
||||||
|
Vec<f64>, // flfix
|
||||||
|
Vec<f64>, // flrd
|
||||||
|
Vec<f64>, // fcooli
|
||||||
|
) {
|
||||||
|
let nd = 5;
|
||||||
|
let params = AlifrkParams {
|
||||||
|
ij: 1,
|
||||||
|
nd,
|
||||||
|
ifali: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化数组
|
||||||
|
let deldmz = vec![0.1; MDEPTH];
|
||||||
|
let absot = vec![1.0; MDEPTH];
|
||||||
|
let rad1 = vec![0.5; MDEPTH];
|
||||||
|
let fak1 = vec![1.0; MDEPTH];
|
||||||
|
let reint = vec![1.0; MDEPTH];
|
||||||
|
let extrad = vec![0.1; MFREQ];
|
||||||
|
let hextrd = vec![0.05; MFREQ];
|
||||||
|
let fh = vec![1.0; MFREQ];
|
||||||
|
let w = vec![1.0; MFREQ];
|
||||||
|
let abso1 = vec![2.0; MDEPTH];
|
||||||
|
let emis1 = vec![1.0; MDEPTH];
|
||||||
|
let scat1 = vec![0.5; MDEPTH];
|
||||||
|
let wc = vec![1.0; MFREQ];
|
||||||
|
let lskip = vec![vec![0; MFREQ]; MDEPTH]; // 全部不跳过
|
||||||
|
|
||||||
|
// 输出数组
|
||||||
|
let fprd = vec![0.0; MDEPTH];
|
||||||
|
let flfix = vec![0.0; MDEPTH];
|
||||||
|
let flrd = vec![0.0; MDEPTH];
|
||||||
|
let fcooli = vec![0.0; MDEPTH];
|
||||||
|
|
||||||
|
(
|
||||||
|
params, deldmz, absot, rad1, fak1, reint, extrad, hextrd, fh, w, abso1, emis1, scat1,
|
||||||
|
wc, lskip, fprd, flfix, flrd, fcooli,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_alifrk_ifali_le_1() {
|
||||||
|
let (
|
||||||
|
_,
|
||||||
|
deldmz,
|
||||||
|
absot,
|
||||||
|
rad1,
|
||||||
|
fak1,
|
||||||
|
reint,
|
||||||
|
extrad,
|
||||||
|
hextrd,
|
||||||
|
fh,
|
||||||
|
w,
|
||||||
|
abso1,
|
||||||
|
emis1,
|
||||||
|
scat1,
|
||||||
|
wc,
|
||||||
|
lskip,
|
||||||
|
mut fprd,
|
||||||
|
mut flfix,
|
||||||
|
mut flrd,
|
||||||
|
mut fcooli,
|
||||||
|
) = create_test_state();
|
||||||
|
|
||||||
|
let params = AlifrkParams {
|
||||||
|
ij: 1,
|
||||||
|
nd: 5,
|
||||||
|
ifali: 1, // <= 1,应该直接返回
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut state = AlifrkState {
|
||||||
|
deldmz: &deldmz,
|
||||||
|
absot: &absot,
|
||||||
|
rad1: &rad1,
|
||||||
|
fak1: &fak1,
|
||||||
|
reint: &reint,
|
||||||
|
extrad: &extrad,
|
||||||
|
hextrd: &hextrd,
|
||||||
|
fh: &fh,
|
||||||
|
w: &w,
|
||||||
|
abso1: &abso1,
|
||||||
|
emis1: &emis1,
|
||||||
|
scat1: &scat1,
|
||||||
|
wc: &wc,
|
||||||
|
lskip: &lskip,
|
||||||
|
fprd: &mut fprd,
|
||||||
|
flfix: &mut flfix,
|
||||||
|
flrd: &mut flrd,
|
||||||
|
fcooli: &mut fcooli,
|
||||||
|
};
|
||||||
|
|
||||||
|
alifrk(¶ms, &mut state);
|
||||||
|
|
||||||
|
// 所有输出应该保持为 0
|
||||||
|
for i in 0..5 {
|
||||||
|
assert_eq!(state.fprd[i], 0.0);
|
||||||
|
assert_eq!(state.flfix[i], 0.0);
|
||||||
|
assert_eq!(state.flrd[i], 0.0);
|
||||||
|
assert_eq!(state.fcooli[i], 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_alifrk_basic() {
|
||||||
|
let (
|
||||||
|
params,
|
||||||
|
deldmz,
|
||||||
|
absot,
|
||||||
|
rad1,
|
||||||
|
fak1,
|
||||||
|
reint,
|
||||||
|
extrad,
|
||||||
|
hextrd,
|
||||||
|
fh,
|
||||||
|
w,
|
||||||
|
abso1,
|
||||||
|
emis1,
|
||||||
|
scat1,
|
||||||
|
wc,
|
||||||
|
lskip,
|
||||||
|
mut fprd,
|
||||||
|
mut flfix,
|
||||||
|
mut flrd,
|
||||||
|
mut fcooli,
|
||||||
|
) = create_test_state();
|
||||||
|
|
||||||
|
let mut state = AlifrkState {
|
||||||
|
deldmz: &deldmz,
|
||||||
|
absot: &absot,
|
||||||
|
rad1: &rad1,
|
||||||
|
fak1: &fak1,
|
||||||
|
reint: &reint,
|
||||||
|
extrad: &extrad,
|
||||||
|
hextrd: &hextrd,
|
||||||
|
fh: &fh,
|
||||||
|
w: &w,
|
||||||
|
abso1: &abso1,
|
||||||
|
emis1: &emis1,
|
||||||
|
scat1: &scat1,
|
||||||
|
wc: &wc,
|
||||||
|
lskip: &lskip,
|
||||||
|
fprd: &mut fprd,
|
||||||
|
flfix: &mut flfix,
|
||||||
|
flrd: &mut flrd,
|
||||||
|
fcooli: &mut fcooli,
|
||||||
|
};
|
||||||
|
|
||||||
|
alifrk(¶ms, &mut state);
|
||||||
|
|
||||||
|
// 验证 ID=1 的计算
|
||||||
|
// wf = 1.0 * (1.0 * 0.5 - 0.05) = 0.45
|
||||||
|
// fprd[0] += 0.45 * 2.0 = 0.9
|
||||||
|
// flfix[0] += 0.45
|
||||||
|
// flrd[0] += 1.0 * (1.0 * 0.5 - 0.5 * 0.1) = 0.45
|
||||||
|
// abst = 2.0 - 0.5 = 1.5
|
||||||
|
// fcooli[0] += 1.0 * (1.0 - 1.5 * 0.5) = 0.25
|
||||||
|
assert!((state.fprd[0] - 0.9).abs() < 1e-10);
|
||||||
|
assert!((state.flfix[0] - 0.45).abs() < 1e-10);
|
||||||
|
assert!((state.flrd[0] - 0.45).abs() < 1e-10);
|
||||||
|
assert!((state.fcooli[0] - 0.25).abs() < 1e-10);
|
||||||
|
|
||||||
|
// 验证 ID=2 的计算
|
||||||
|
// dt = 1.0 / ((1.0 + 1.0) * 0.1) = 5.0
|
||||||
|
// fl = 0.5 * 1.0 - 0.5 * 1.0 = 0.0
|
||||||
|
// wfl[1] = 1.0 * 0.0 = 0.0
|
||||||
|
// flfix[1] += 0.0 * 5.0 = 0.0
|
||||||
|
// flrd[1] += 0.0 * 1.0 * 5.0 = 0.0
|
||||||
|
// fcooli[1] += 1.0 * (1.0 - 1.5 * 0.5) = 0.25
|
||||||
|
assert!((state.flfix[1]).abs() < 1e-10);
|
||||||
|
assert!((state.flrd[1]).abs() < 1e-10);
|
||||||
|
assert!((state.fcooli[1] - 0.25).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_alifrk_skip() {
|
||||||
|
let (
|
||||||
|
params,
|
||||||
|
deldmz,
|
||||||
|
absot,
|
||||||
|
rad1,
|
||||||
|
fak1,
|
||||||
|
reint,
|
||||||
|
extrad,
|
||||||
|
hextrd,
|
||||||
|
fh,
|
||||||
|
w,
|
||||||
|
abso1,
|
||||||
|
emis1,
|
||||||
|
scat1,
|
||||||
|
wc,
|
||||||
|
mut lskip,
|
||||||
|
mut fprd,
|
||||||
|
mut flfix,
|
||||||
|
mut flrd,
|
||||||
|
mut fcooli,
|
||||||
|
) = create_test_state();
|
||||||
|
|
||||||
|
// 设置 lskip[0][0] = 1,表示跳过 ID=1, IJ=1
|
||||||
|
lskip[0][0] = 1;
|
||||||
|
|
||||||
|
let mut state = AlifrkState {
|
||||||
|
deldmz: &deldmz,
|
||||||
|
absot: &absot,
|
||||||
|
rad1: &rad1,
|
||||||
|
fak1: &fak1,
|
||||||
|
reint: &reint,
|
||||||
|
extrad: &extrad,
|
||||||
|
hextrd: &hextrd,
|
||||||
|
fh: &fh,
|
||||||
|
w: &w,
|
||||||
|
abso1: &abso1,
|
||||||
|
emis1: &emis1,
|
||||||
|
scat1: &scat1,
|
||||||
|
wc: &wc,
|
||||||
|
lskip: &lskip,
|
||||||
|
fprd: &mut fprd,
|
||||||
|
flfix: &mut flfix,
|
||||||
|
flrd: &mut flrd,
|
||||||
|
fcooli: &mut fcooli,
|
||||||
|
};
|
||||||
|
|
||||||
|
alifrk(¶ms, &mut state);
|
||||||
|
|
||||||
|
// fprd[0] 不应该被更新,因为 lskip[0][0] = 1
|
||||||
|
// 但 flfix[0], flrd[0], fcooli[0] 仍然更新
|
||||||
|
assert!((state.fprd[0]).abs() < 1e-10); // 跳过,不更新
|
||||||
|
assert!((state.flfix[0] - 0.45).abs() < 1e-10);
|
||||||
|
assert!((state.flrd[0] - 0.45).abs() < 1e-10);
|
||||||
|
assert!((state.fcooli[0] - 0.25).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_alifrk_reint_zero() {
|
||||||
|
let (
|
||||||
|
params,
|
||||||
|
deldmz,
|
||||||
|
absot,
|
||||||
|
rad1,
|
||||||
|
fak1,
|
||||||
|
mut reint,
|
||||||
|
extrad,
|
||||||
|
hextrd,
|
||||||
|
fh,
|
||||||
|
w,
|
||||||
|
abso1,
|
||||||
|
emis1,
|
||||||
|
scat1,
|
||||||
|
wc,
|
||||||
|
lskip,
|
||||||
|
mut fprd,
|
||||||
|
mut flfix,
|
||||||
|
mut flrd,
|
||||||
|
mut fcooli,
|
||||||
|
) = create_test_state();
|
||||||
|
|
||||||
|
// 设置 reint[0] = 0,fcooli[0] 不应该更新
|
||||||
|
reint[0] = 0.0;
|
||||||
|
|
||||||
|
let mut state = AlifrkState {
|
||||||
|
deldmz: &deldmz,
|
||||||
|
absot: &absot,
|
||||||
|
rad1: &rad1,
|
||||||
|
fak1: &fak1,
|
||||||
|
reint: &reint,
|
||||||
|
extrad: &extrad,
|
||||||
|
hextrd: &hextrd,
|
||||||
|
fh: &fh,
|
||||||
|
w: &w,
|
||||||
|
abso1: &abso1,
|
||||||
|
emis1: &emis1,
|
||||||
|
scat1: &scat1,
|
||||||
|
wc: &wc,
|
||||||
|
lskip: &lskip,
|
||||||
|
fprd: &mut fprd,
|
||||||
|
flfix: &mut flfix,
|
||||||
|
flrd: &mut flrd,
|
||||||
|
fcooli: &mut fcooli,
|
||||||
|
};
|
||||||
|
|
||||||
|
alifrk(¶ms, &mut state);
|
||||||
|
|
||||||
|
// fcooli[0] 不应该更新
|
||||||
|
assert!((state.fcooli[0]).abs() < 1e-10);
|
||||||
|
// fcooli[1] 应该更新
|
||||||
|
assert!((state.fcooli[1] - 0.25).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
260
src/math/allardt.rs
Normal file
260
src/math/allardt.rs
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
//! Lyman alpha 线轮廓计算(温度相关)。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `allardt.f`
|
||||||
|
//!
|
||||||
|
//! 计算准分子不透明度的 Lyman alpha 线轮廓,支持温度相关的轮廓。
|
||||||
|
|
||||||
|
/// Allard 数据表参数。
|
||||||
|
///
|
||||||
|
/// 对应 COMMON /calphatd/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AllardData {
|
||||||
|
/// 波长表 [NXMAX][NTAMAX]
|
||||||
|
pub xlalpd: Vec<Vec<f64>>,
|
||||||
|
/// 轮廓表 [NXMAX][NNMAX][NTAMAX]
|
||||||
|
pub plalpd: Vec<Vec<Vec<f64>>>,
|
||||||
|
/// 标准中性氢密度 [NTAMAX]
|
||||||
|
pub stnead: Vec<f64>,
|
||||||
|
/// 标准电离氢密度 [NTAMAX]
|
||||||
|
pub stnchd: Vec<f64>,
|
||||||
|
/// 中性氢速度参数 [NTAMAX]
|
||||||
|
pub vneuad: Vec<f64>,
|
||||||
|
/// 电离氢速度参数 [NTAMAX]
|
||||||
|
pub vchaad: Vec<f64>,
|
||||||
|
/// 温度表 [NTAMAX]
|
||||||
|
pub talpd: Vec<f64>,
|
||||||
|
/// 每个温度的点数 [NTAMAX]
|
||||||
|
pub nxalpd: Vec<usize>,
|
||||||
|
/// 温度表数量
|
||||||
|
pub ntalpd: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AllardData {
|
||||||
|
fn default() -> Self {
|
||||||
|
const NXMAX: usize = 1400;
|
||||||
|
const NNMAX: usize = 5;
|
||||||
|
const NTAMAX: usize = 6;
|
||||||
|
|
||||||
|
Self {
|
||||||
|
xlalpd: vec![vec![0.0; NTAMAX]; NXMAX],
|
||||||
|
plalpd: vec![vec![vec![0.0; NTAMAX]; NNMAX]; NXMAX],
|
||||||
|
stnead: vec![0.0; NTAMAX],
|
||||||
|
stnchd: vec![0.0; NTAMAX],
|
||||||
|
vneuad: vec![0.0; NTAMAX],
|
||||||
|
vchaad: vec![0.0; NTAMAX],
|
||||||
|
talpd: vec![0.0; NTAMAX],
|
||||||
|
nxalpd: vec![0; NTAMAX],
|
||||||
|
ntalpd: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AllardData {
|
||||||
|
/// 创建新的 Allard 数据结构。
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lyman alpha 线轮廓计算(温度相关)。
|
||||||
|
///
|
||||||
|
/// 计算准分子不透明度的 Lyman alpha 线轮廓。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `xl` - 波长 (Å)
|
||||||
|
/// * `t` - 温度 (K)
|
||||||
|
/// * `hneutr` - 中性氢粒子密度 (cm⁻³)
|
||||||
|
/// * `hcharg` - 电离氢粒子密度 (cm⁻³)
|
||||||
|
/// * `data` - Allard 数据表
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// 返回归一化的线轮廓值
|
||||||
|
///
|
||||||
|
/// # 示例
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use tlusty_rust::math::allardt::{allardt, AllardData};
|
||||||
|
///
|
||||||
|
/// let data = AllardData::new();
|
||||||
|
/// let prof = allardt(1215.6, 10000.0, 1e14, 1e12, &data);
|
||||||
|
/// ```
|
||||||
|
pub fn allardt(xl: f64, t: f64, hneutr: f64, hcharg: f64, data: &AllardData) -> f64 {
|
||||||
|
// 归一化常数: 8.8528e-29 * lambda_0^2 * f_ij
|
||||||
|
// lambda_0 = 1215.6 Å, f_ij = 0.41618
|
||||||
|
const XNORMA: f64 = 8.8528e-29 * 1215.6 * 1215.6 * 0.41618;
|
||||||
|
|
||||||
|
let mut prof = 0.0;
|
||||||
|
|
||||||
|
// 找到接近实际温度的两个部分表
|
||||||
|
let mut it0 = 0;
|
||||||
|
for it in 1..=data.ntalpd {
|
||||||
|
it0 = it;
|
||||||
|
if t < data.talpd[it - 1] {
|
||||||
|
it0 = it - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理边界情况
|
||||||
|
if it0 == 0 {
|
||||||
|
it0 = 1;
|
||||||
|
return compute_single_table(xl, hneutr, hcharg, it0 - 1, XNORMA, data);
|
||||||
|
}
|
||||||
|
if it0 >= data.ntalpd {
|
||||||
|
it0 = data.ntalpd;
|
||||||
|
return compute_single_table(xl, hneutr, hcharg, it0 - 1, XNORMA, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在不同温度的表之间进行插值
|
||||||
|
// 较低温度
|
||||||
|
let prof0 = compute_single_table(xl, hneutr, hcharg, it0 - 1, XNORMA, data);
|
||||||
|
if prof0 == 0.0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 较高温度
|
||||||
|
let prof1 = compute_single_table(xl, hneutr, hcharg, it0, XNORMA, data);
|
||||||
|
if prof1 == 0.0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最终轮廓系数
|
||||||
|
let dt = data.talpd[it0] - data.talpd[it0 - 1];
|
||||||
|
if dt.abs() < 1e-10 {
|
||||||
|
prof0
|
||||||
|
} else {
|
||||||
|
(prof0 * (data.talpd[it0] - t) + prof1 * (t - data.talpd[it0 - 1])) / dt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 计算单个温度表的轮廓值。
|
||||||
|
fn compute_single_table(
|
||||||
|
xl: f64,
|
||||||
|
hneutr: f64,
|
||||||
|
hcharg: f64,
|
||||||
|
it_idx: usize,
|
||||||
|
xnorma: f64,
|
||||||
|
data: &AllardData,
|
||||||
|
) -> f64 {
|
||||||
|
let nx = data.nxalpd[it_idx];
|
||||||
|
if nx == 0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查波长是否在范围内
|
||||||
|
if xl < data.xlalpd[0][it_idx] || xl > data.xlalpd[nx - 1][it_idx] {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算归一化密度
|
||||||
|
let vn1 = hneutr / data.stnead[it_idx];
|
||||||
|
let vn2 = hcharg / data.stnchd[it_idx];
|
||||||
|
let vns = vn1 * data.vneuad[it_idx] + vn2 * data.vchaad[it_idx];
|
||||||
|
let vn11 = vn1 * vn1;
|
||||||
|
let vn22 = vn2 * vn2;
|
||||||
|
let vn12 = vn1 * vn2;
|
||||||
|
let xnorm = 1.0 / (1.0 + vns + 0.5 * vns * vns);
|
||||||
|
|
||||||
|
// 二分查找波长索引
|
||||||
|
let mut jl = 0usize;
|
||||||
|
let mut ju = nx + 1;
|
||||||
|
while ju - jl > 1 {
|
||||||
|
let jm = (ju + jl) / 2;
|
||||||
|
if xl > data.xlalpd[jm - 1][it_idx] {
|
||||||
|
jl = jm;
|
||||||
|
} else {
|
||||||
|
ju = jm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let j = jl;
|
||||||
|
|
||||||
|
// 边界检查
|
||||||
|
let j = if j == 0 { 1 } else if j == nx { nx - 1 } else { j };
|
||||||
|
|
||||||
|
// 线性插值
|
||||||
|
let a1 = (xl - data.xlalpd[j - 1][it_idx])
|
||||||
|
/ (data.xlalpd[j][it_idx] - data.xlalpd[j - 1][it_idx]);
|
||||||
|
|
||||||
|
let p1 = vn1 * ((1.0 - a1) * data.plalpd[j - 1][0][it_idx] + a1 * data.plalpd[j][0][it_idx]);
|
||||||
|
let p11 = vn11 * ((1.0 - a1) * data.plalpd[j - 1][1][it_idx] + a1 * data.plalpd[j][1][it_idx]);
|
||||||
|
let p2 = vn2 * ((1.0 - a1) * data.plalpd[j - 1][2][it_idx] + a1 * data.plalpd[j][2][it_idx]);
|
||||||
|
let p22 = vn22 * ((1.0 - a1) * data.plalpd[j - 1][3][it_idx] + a1 * data.plalpd[j][3][it_idx]);
|
||||||
|
let p12 = vn12 * ((1.0 - a1) * data.plalpd[j - 1][4][it_idx] + a1 * data.plalpd[j][4][it_idx]);
|
||||||
|
|
||||||
|
(p1 + p2 + p11 + p22 + p12) * xnorm * xnorma
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn create_test_data() -> AllardData {
|
||||||
|
let mut data = AllardData::new();
|
||||||
|
data.ntalpd = 2;
|
||||||
|
data.talpd[0] = 10000.0;
|
||||||
|
data.talpd[1] = 20000.0;
|
||||||
|
data.nxalpd[0] = 3;
|
||||||
|
data.nxalpd[1] = 3;
|
||||||
|
|
||||||
|
// 设置波长表
|
||||||
|
data.xlalpd[0][0] = 1210.0;
|
||||||
|
data.xlalpd[1][0] = 1215.0;
|
||||||
|
data.xlalpd[2][0] = 1220.0;
|
||||||
|
data.xlalpd[0][1] = 1210.0;
|
||||||
|
data.xlalpd[1][1] = 1215.0;
|
||||||
|
data.xlalpd[2][1] = 1220.0;
|
||||||
|
|
||||||
|
// 设置标准密度
|
||||||
|
data.stnead[0] = 1e14;
|
||||||
|
data.stnead[1] = 1e14;
|
||||||
|
data.stnchd[0] = 1e12;
|
||||||
|
data.stnchd[1] = 1e12;
|
||||||
|
|
||||||
|
// 设置速度参数
|
||||||
|
data.vneuad[0] = 1.0;
|
||||||
|
data.vneuad[1] = 1.0;
|
||||||
|
data.vchaad[0] = 1.0;
|
||||||
|
data.vchaad[1] = 1.0;
|
||||||
|
|
||||||
|
// 设置轮廓数据(简化测试值)
|
||||||
|
for i in 0..3 {
|
||||||
|
for j in 0..5 {
|
||||||
|
data.plalpd[i][j][0] = 1e-20;
|
||||||
|
data.plalpd[i][j][1] = 1e-20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_allardt_out_of_range() {
|
||||||
|
let data = create_test_data();
|
||||||
|
// 波长超出范围
|
||||||
|
let prof = allardt(1000.0, 15000.0, 1e14, 1e12, &data);
|
||||||
|
assert!((prof - 0.0).abs() < 1e-30);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_allardt_in_range() {
|
||||||
|
let data = create_test_data();
|
||||||
|
// 波长在范围内
|
||||||
|
let prof = allardt(1215.0, 15000.0, 1e14, 1e12, &data);
|
||||||
|
// 应该返回一个正值(具体值取决于数据)
|
||||||
|
assert!(prof >= 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_allardt_boundary_temp() {
|
||||||
|
let data = create_test_data();
|
||||||
|
// 温度低于最低表
|
||||||
|
let prof = allardt(1215.0, 5000.0, 1e14, 1e12, &data);
|
||||||
|
assert!(prof >= 0.0);
|
||||||
|
|
||||||
|
// 温度高于最高表
|
||||||
|
let prof = allardt(1215.0, 30000.0, 1e14, 1e12, &data);
|
||||||
|
assert!(prof >= 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
435
src/math/bpopf.rs
Normal file
435
src/math/bpopf.rs
Normal file
@ -0,0 +1,435 @@
|
|||||||
|
//! B 矩阵占据数行相关部分。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `bpopf.f`
|
||||||
|
//!
|
||||||
|
//! 计算 B 矩阵中与占据数行相关的部分,即 ALI 点强度对占据数的导数。
|
||||||
|
//!
|
||||||
|
//! # 算法说明
|
||||||
|
//!
|
||||||
|
//! 该函数计算完整线性化矩阵中与占据数相关的元素。
|
||||||
|
//! 矩阵 B 的占据数行 (NSE+1 到 NSE+NLVEXP) 通过以下方式更新:
|
||||||
|
//!
|
||||||
|
//! 1. 对于 IFPOPR <= 3:使用 ESEMAT 矩阵变换
|
||||||
|
//! 2. 对于 IFPOPR > 3:直接复制 APT, APN, APP 数组
|
||||||
|
//!
|
||||||
|
//! 如果 IFALI >= 6,还需要计算 A 和 C 矩阵的相应元素。
|
||||||
|
|
||||||
|
use crate::state::alipar::FixAlp;
|
||||||
|
use crate::state::arrays::{BpoCom, MainArrays};
|
||||||
|
use crate::state::constants::{MLEVEL, MLVEX3, MTOT, UN};
|
||||||
|
|
||||||
|
/// BPOPF 输入参数
|
||||||
|
pub struct BpopfParams<'a> {
|
||||||
|
/// 频率相关方程数
|
||||||
|
pub nfreqe: usize,
|
||||||
|
/// 能级占据数方程索引
|
||||||
|
pub inse: i32,
|
||||||
|
/// 温度方程索引
|
||||||
|
pub inre: i32,
|
||||||
|
/// 电子密度方程索引
|
||||||
|
pub inpc: i32,
|
||||||
|
/// 线性化能级数
|
||||||
|
pub nlvexp: usize,
|
||||||
|
/// 占据数处理模式
|
||||||
|
pub ifpopr: i32,
|
||||||
|
/// ALI 模式 (>=6 表示需要 A/C 矩阵)
|
||||||
|
pub ifali: i32,
|
||||||
|
/// 深度索引 (1-indexed)
|
||||||
|
pub id: usize,
|
||||||
|
/// 跨深度松弛因子 (深度)
|
||||||
|
pub crsw: &'a [f64],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 计算 B 矩阵中与占据数行相关的部分。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `params` - 输入参数
|
||||||
|
/// * `arrays` - 主计算数组 (包含 A, B, C 矩阵)
|
||||||
|
/// * `fixalp` - ALI 固定参数 (包含 APT, APN, APP 等)
|
||||||
|
/// * `bpocom` - 束缚-占据矩阵 (包含 ESEMAT)
|
||||||
|
///
|
||||||
|
/// # Fortran 索引说明
|
||||||
|
///
|
||||||
|
/// - Fortran 使用 1-indexed 数组
|
||||||
|
/// - `NSE = NFREQE + INSE - 1` (B 矩阵中占据数行的起始索引)
|
||||||
|
/// - `NRE = NFREQE + INRE` (温度列索引)
|
||||||
|
/// - `NPC = NFREQE + INPC` (电子密度列索引)
|
||||||
|
///
|
||||||
|
/// 在 Rust 实现中,我们使用 0-indexed,因此需要相应调整。
|
||||||
|
pub fn bpopf(
|
||||||
|
params: &BpopfParams,
|
||||||
|
arrays: &mut MainArrays,
|
||||||
|
fixalp: &FixAlp,
|
||||||
|
bpocom: &BpoCom,
|
||||||
|
) {
|
||||||
|
// 计算索引 (转换为 0-indexed)
|
||||||
|
// Fortran: NSE = NFREQE + INSE - 1
|
||||||
|
// Rust: nse = nfreqe + inse - 1 (已经是 0-indexed 起始点)
|
||||||
|
let nse = params.nfreqe as i32 + params.inse - 1;
|
||||||
|
let nre = params.nfreqe as i32 + params.inre;
|
||||||
|
let npc = params.nfreqe as i32 + params.inpc;
|
||||||
|
|
||||||
|
// 深度索引 (转换为 0-indexed)
|
||||||
|
let id_idx = params.id - 1;
|
||||||
|
|
||||||
|
// 线性化能级数
|
||||||
|
let nlvexp = params.nlvexp;
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// 矩阵 B 的完整线性化部分
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
|
for i in 0..nlvexp {
|
||||||
|
let mut sumt = 0.0;
|
||||||
|
let mut sumn = 0.0;
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
let sum = if params.ifpopr <= 3 {
|
||||||
|
// 使用 ESEMAT 矩阵变换
|
||||||
|
// Fortran: SUMT = SUMT - ESEMAT(I,II) * APT(II,ID)
|
||||||
|
sumt -= bpocom.esemat[i][ii] * fixalp.apt[ii][id_idx];
|
||||||
|
sumn -= bpocom.esemat[i][ii] * fixalp.apn[ii][id_idx];
|
||||||
|
|
||||||
|
let mut s = 0.0;
|
||||||
|
for j in 0..nlvexp {
|
||||||
|
// Fortran: SUM = SUM - ESEMAT(I,J) * APP(II,J,ID)
|
||||||
|
s -= bpocom.esemat[i][j] * fixalp.app[ii][j][id_idx];
|
||||||
|
}
|
||||||
|
s
|
||||||
|
} else {
|
||||||
|
// 直接复制
|
||||||
|
// Fortran: SUM = APP(II,I,ID)
|
||||||
|
fixalp.app[ii][i][id_idx]
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新 B 矩阵
|
||||||
|
// Fortran: B(NSE+I, NSE+II) = B(NSE+I, NSE+II) + SUM
|
||||||
|
let row = (nse + i as i32) as usize;
|
||||||
|
let col = (nse + ii as i32) as usize;
|
||||||
|
if row < MTOT && col < MTOT {
|
||||||
|
arrays.b[row][col] += sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 IFPOPR > 3,直接使用 APT, APN
|
||||||
|
if params.ifpopr > 3 {
|
||||||
|
sumt = fixalp.apt[i][id_idx];
|
||||||
|
sumn = fixalp.apn[i][id_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新温度和电子密度列
|
||||||
|
let row = (nse + i as i32) as usize;
|
||||||
|
|
||||||
|
// Fortran: IF(INRE.NE.0) B(NSE+I,NRE) = B(NSE+I,NRE) + SUMT
|
||||||
|
if params.inre != 0 && nre > 0 {
|
||||||
|
let col = (nre - 1) as usize; // 转换为 0-indexed
|
||||||
|
if col < MTOT {
|
||||||
|
arrays.b[row][col] += sumt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fortran: IF(INPC.NE.0) B(NSE+I,NPC) = B(NSE+I,NPC) + SUMN
|
||||||
|
if params.inpc != 0 && npc > 0 {
|
||||||
|
let col = (npc - 1) as usize; // 转换为 0-indexed
|
||||||
|
if col < MTOT {
|
||||||
|
arrays.b[row][col] += sumn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用跨深度松弛因子
|
||||||
|
// Fortran: IF(CRSW(ID).NE.UN) THEN ...
|
||||||
|
let crsw_val = params.crsw[id_idx];
|
||||||
|
if (crsw_val - UN).abs() > 1e-15 {
|
||||||
|
for i in 0..nlvexp {
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
let row = (nse + i as i32) as usize;
|
||||||
|
let col = (nse + ii as i32) as usize;
|
||||||
|
if row < MTOT && col < MTOT {
|
||||||
|
arrays.b[row][col] *= crsw_val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// 矩阵 A 和 C 的完整线性化部分 (仅当 IFALI >= 6)
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
|
if params.ifali >= 6 {
|
||||||
|
for i in 0..nlvexp {
|
||||||
|
let mut asumt = 0.0;
|
||||||
|
let mut asumtn = 0.0;
|
||||||
|
let mut csumt = 0.0;
|
||||||
|
let mut csumn = 0.0;
|
||||||
|
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
let (asum, csum) = if params.ifpopr <= 3 {
|
||||||
|
// 使用 ESEMAT 矩阵变换
|
||||||
|
// Fortran: ASUMT = ASUMT - ESEMAT(I,II) * AAPT(II,ID)
|
||||||
|
asumt -= bpocom.esemat[i][ii] * fixalp.aapt[ii][id_idx];
|
||||||
|
asumtn -= bpocom.esemat[i][ii] * fixalp.aapn[ii][id_idx];
|
||||||
|
csumt -= bpocom.esemat[i][ii] * fixalp.capt[ii][id_idx];
|
||||||
|
csumn -= bpocom.esemat[i][ii] * fixalp.capn[ii][id_idx];
|
||||||
|
|
||||||
|
let mut a = 0.0;
|
||||||
|
let mut c = 0.0;
|
||||||
|
for j in 0..nlvexp {
|
||||||
|
// Fortran: ASUM = ASUM - ESEMAT(I,J) * AAPP(II,J,ID)
|
||||||
|
a -= bpocom.esemat[i][j] * fixalp.aapp[ii][j][id_idx];
|
||||||
|
c -= bpocom.esemat[i][j] * fixalp.capp[ii][j][id_idx];
|
||||||
|
}
|
||||||
|
(a, c)
|
||||||
|
} else {
|
||||||
|
// 直接复制
|
||||||
|
// Fortran: ASUM = AAPP(II,I,ID)
|
||||||
|
(fixalp.aapp[ii][i][id_idx], fixalp.capp[ii][i][id_idx])
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新 A 和 C 矩阵
|
||||||
|
let row = (nse + i as i32) as usize;
|
||||||
|
let col = (nse + ii as i32) as usize;
|
||||||
|
if row < MTOT && col < MTOT {
|
||||||
|
arrays.a[row][col] = asum;
|
||||||
|
arrays.c[row][col] = csum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 IFPOPR > 3,直接使用 AAPT, AAPN, CAPT, CAPN
|
||||||
|
if params.ifpopr > 3 {
|
||||||
|
asumt = fixalp.aapt[i][id_idx];
|
||||||
|
asumtn = fixalp.aapn[i][id_idx];
|
||||||
|
csumt = fixalp.capt[i][id_idx];
|
||||||
|
csumn = fixalp.capn[i][id_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新温度和电子密度列
|
||||||
|
let row = (nse + i as i32) as usize;
|
||||||
|
|
||||||
|
if params.inre != 0 && nre > 0 {
|
||||||
|
let col = (nre - 1) as usize;
|
||||||
|
if col < MTOT {
|
||||||
|
arrays.a[row][col] += asumt;
|
||||||
|
arrays.c[row][col] += csumt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.inpc != 0 && npc > 0 {
|
||||||
|
let col = (npc - 1) as usize;
|
||||||
|
if col < MTOT {
|
||||||
|
arrays.a[row][col] += asumtn;
|
||||||
|
arrays.c[row][col] += csumn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用跨深度松弛因子到 A 和 C 矩阵
|
||||||
|
if (crsw_val - UN).abs() > 1e-15 {
|
||||||
|
for i in 0..nlvexp {
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
let row = (nse + i as i32) as usize;
|
||||||
|
let col = (nse + ii as i32) as usize;
|
||||||
|
if row < MTOT && col < MTOT {
|
||||||
|
arrays.a[row][col] *= crsw_val;
|
||||||
|
arrays.c[row][col] *= crsw_val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn setup_test_data(nlvexp: usize) -> (MainArrays, FixAlp, BpoCom, Vec<f64>) {
|
||||||
|
let mut arrays = MainArrays::default();
|
||||||
|
let mut fixalp = FixAlp::default();
|
||||||
|
let mut bpocom = BpoCom::new();
|
||||||
|
let mut crsw = vec![1.0; 100];
|
||||||
|
|
||||||
|
// 设置一些测试数据
|
||||||
|
// ESEMAT 设置为单位矩阵
|
||||||
|
for i in 0..nlvexp.min(MLEVEL) {
|
||||||
|
bpocom.esemat[i][i] = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置 APT, APN, APP 为一些测试值
|
||||||
|
for i in 0..nlvexp {
|
||||||
|
fixalp.apt[i][0] = 0.1 * (i + 1) as f64;
|
||||||
|
fixalp.apn[i][0] = 0.2 * (i + 1) as f64;
|
||||||
|
for j in 0..nlvexp {
|
||||||
|
fixalp.app[i][j][0] = 0.01 * ((i + 1) * (j + 1)) as f64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置 AAPT, AAPN, CAPT, CAPN, AAPP, CAPP
|
||||||
|
// 注意: 这些数组的第一维是 MLVEX3 (通常=1)
|
||||||
|
for i in 0..MLVEX3 {
|
||||||
|
fixalp.aapt[i][0] = 0.05 * (i + 1) as f64;
|
||||||
|
fixalp.aapn[i][0] = 0.06 * (i + 1) as f64;
|
||||||
|
fixalp.capt[i][0] = 0.07 * (i + 1) as f64;
|
||||||
|
fixalp.capn[i][0] = 0.08 * (i + 1) as f64;
|
||||||
|
for j in 0..MLVEX3 {
|
||||||
|
fixalp.aapp[i][j][0] = 0.001 * ((i + 1) * (j + 1)) as f64;
|
||||||
|
fixalp.capp[i][j][0] = 0.002 * ((i + 1) * (j + 1)) as f64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(arrays, fixalp, bpocom, crsw)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_esemat_initialization() {
|
||||||
|
// 验证 ESEMAT 初始化
|
||||||
|
let nlvexp = 3;
|
||||||
|
let bpocom = BpoCom::new();
|
||||||
|
|
||||||
|
// 检查初始化后全为 0
|
||||||
|
for i in 0..nlvexp {
|
||||||
|
for j in 0..nlvexp {
|
||||||
|
assert_eq!(bpocom.esemat[i][j], 0.0, "ESEMAT[{}][{}] = {}", i, j, bpocom.esemat[i][j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bpopf_basic() {
|
||||||
|
// 使用较小的 nlvexp 值进行测试
|
||||||
|
let nlvexp = 3;
|
||||||
|
let (mut arrays, fixalp, bpocom, crsw) = setup_test_data(nlvexp);
|
||||||
|
|
||||||
|
// 验证 ESEMAT 是单位矩阵
|
||||||
|
for i in 0..nlvexp {
|
||||||
|
for j in 0..nlvexp {
|
||||||
|
let expected = if i == j { 1.0 } else { 0.0 };
|
||||||
|
assert_eq!(bpocom.esemat[i][j], expected, "ESEMAT[{}][{}] = {}", i, j, bpocom.esemat[i][j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let params = BpopfParams {
|
||||||
|
nfreqe: 10,
|
||||||
|
inse: 11, // NSE = 10 + 11 - 1 = 20
|
||||||
|
inre: 0, // 不更新温度列,避免与主循环列重叠
|
||||||
|
inpc: 0, // 不更新电子密度列
|
||||||
|
nlvexp,
|
||||||
|
ifpopr: 3,
|
||||||
|
ifali: 0, // 不计算 A/C 矩阵
|
||||||
|
id: 1,
|
||||||
|
crsw: &crsw,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 调用 bpopf
|
||||||
|
bpopf(¶ms, &mut arrays, &fixalp, &bpocom);
|
||||||
|
|
||||||
|
// 检查 B 矩阵被更新
|
||||||
|
// NSE = 20, 所以 B[20..23][20..23] 应该被更新
|
||||||
|
//
|
||||||
|
// Fortran 算法分析 (ESEMAT 为单位矩阵时):
|
||||||
|
// SUM = -Σ_J ESEMAT(I,J) * APP(II,J,ID)
|
||||||
|
// 当 ESEMAT 是单位矩阵,只有 J=I 项非零:
|
||||||
|
// SUM = -APP(II,I,ID)
|
||||||
|
// B(NSE+I, NSE+II) = SUM = -APP(II,I,ID)
|
||||||
|
//
|
||||||
|
// 转换为 0-indexed Rust:
|
||||||
|
// B[nse+i][nse+ii] = -APP[ii][i][id_idx]
|
||||||
|
let nse = 20;
|
||||||
|
for i in 0..nlvexp {
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
// SUM = -APP(II,I,ID) -> -APP[ii][i][0]
|
||||||
|
let expected = -fixalp.app[ii][i][0];
|
||||||
|
let actual = arrays.b[nse + i][nse + ii];
|
||||||
|
assert!(
|
||||||
|
(actual - expected).abs() < 1e-10,
|
||||||
|
"B[{}][{}] = {}, expected {}",
|
||||||
|
nse + i,
|
||||||
|
nse + ii,
|
||||||
|
actual,
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bpopf_with_crsw() {
|
||||||
|
let nlvexp = 3;
|
||||||
|
let (mut arrays, fixalp, bpocom, mut crsw) = setup_test_data(nlvexp);
|
||||||
|
|
||||||
|
// 设置跨深度松弛因子
|
||||||
|
crsw[0] = 0.5;
|
||||||
|
|
||||||
|
let params = BpopfParams {
|
||||||
|
nfreqe: 10,
|
||||||
|
inse: 11,
|
||||||
|
inre: 0, // 不更新温度列,避免与主循环列重叠
|
||||||
|
inpc: 0, // 不更新电子密度列
|
||||||
|
nlvexp,
|
||||||
|
ifpopr: 3,
|
||||||
|
ifali: 0, // 不计算 A/C 矩阵
|
||||||
|
id: 1,
|
||||||
|
crsw: &crsw,
|
||||||
|
};
|
||||||
|
|
||||||
|
bpopf(¶ms, &mut arrays, &fixalp, &bpocom);
|
||||||
|
|
||||||
|
// 检查 B 矩阵被 CRSW 缩放
|
||||||
|
// B[nse+i][nse+ii] = -APP[ii][i][0] * crsw
|
||||||
|
let nse = 20;
|
||||||
|
for i in 0..nlvexp {
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
let expected = -fixalp.app[ii][i][0] * 0.5;
|
||||||
|
let actual = arrays.b[nse + i][nse + ii];
|
||||||
|
assert!(
|
||||||
|
(actual - expected).abs() < 1e-10,
|
||||||
|
"B[{}][{}] = {}, expected {}",
|
||||||
|
nse + i,
|
||||||
|
nse + ii,
|
||||||
|
actual,
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bpopf_ifpopr_gt_3() {
|
||||||
|
let nlvexp = 3;
|
||||||
|
let (mut arrays, fixalp, bpocom, crsw) = setup_test_data(nlvexp);
|
||||||
|
|
||||||
|
let params = BpopfParams {
|
||||||
|
nfreqe: 10,
|
||||||
|
inse: 11,
|
||||||
|
inre: 0, // 不更新温度列
|
||||||
|
inpc: 0, // 不更新电子密度列
|
||||||
|
nlvexp,
|
||||||
|
ifpopr: 4, // 直接复制模式
|
||||||
|
ifali: 0,
|
||||||
|
id: 1,
|
||||||
|
crsw: &crsw,
|
||||||
|
};
|
||||||
|
|
||||||
|
bpopf(¶ms, &mut arrays, &fixalp, &bpocom);
|
||||||
|
|
||||||
|
// 检查 B 矩阵直接复制 APP
|
||||||
|
let nse = 20;
|
||||||
|
for i in 0..nlvexp {
|
||||||
|
for j in 0..nlvexp {
|
||||||
|
// IFPOPR > 3 时,SUM = APP(j,i,0) (注意索引顺序)
|
||||||
|
// Fortran: SUM = APP(II,I,ID) -> Rust: APP[ii][i][0]
|
||||||
|
let expected = fixalp.app[j][i][0];
|
||||||
|
let actual = arrays.b[nse + i][nse + j];
|
||||||
|
assert!(
|
||||||
|
(actual - expected).abs() < 1e-10,
|
||||||
|
"B[{}][{}] = {}, expected {}",
|
||||||
|
nse + i,
|
||||||
|
nse + j,
|
||||||
|
actual,
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
315
src/math/ctdata.rs
Normal file
315
src/math/ctdata.rs
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
//! 电荷转移电离和复合系数数据。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `ctdata.f` BLOCK DATA
|
||||||
|
|
||||||
|
/// 电荷转移电离系数。
|
||||||
|
///
|
||||||
|
/// 数组维度: [7, 4, 30]
|
||||||
|
/// - 第一维: 7 个拟合参数
|
||||||
|
/// - 第二维: 4 个电离级 (1=+0, 2=+1, 3=+2, 4=+3)
|
||||||
|
/// - 第三维: 30 个原子序号
|
||||||
|
///
|
||||||
|
/// 注意: 第一参数单位为 1e-9,第七参数单位为 1e4 K
|
||||||
|
pub const CTION: [[[f64; 30]; 4]; 7] = [
|
||||||
|
// 参数 1 (单位 1e-9)
|
||||||
|
[
|
||||||
|
// ion=1
|
||||||
|
[0.0, 0.0, 2.84e-3, 0.0, 0.0, 1.07e-6, 4.55e-3, 7.40e-2, 0.0, 0.0,
|
||||||
|
3.34e-6, 9.76e-3, 0.0, 0.92, 0.0, 1.00e-5, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
|
||||||
|
// ion=2
|
||||||
|
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 7.60e-5, 0.0, 2.26, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 4.39, 2.83e-1, 2.10, 1.20e-2, 0.0, 0.0, 0.0],
|
||||||
|
// ion=3
|
||||||
|
[0.0; 30],
|
||||||
|
// ion=4
|
||||||
|
[0.0; 30],
|
||||||
|
],
|
||||||
|
// 参数 2
|
||||||
|
[
|
||||||
|
[0.0, 0.0, 1.99, 0.0, 0.0, 3.15, -0.29, 0.47, 0.0, 0.0,
|
||||||
|
9.31, 3.14, 0.0, 1.15, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 0.61, 6.80e-3, 7.72e-2, 3.49, 0.0, 0.0, 0.0],
|
||||||
|
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 7.36e-2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 0.61, 6.80e-3, 7.72e-2, 3.49, 0.0, 0.0, 0.0],
|
||||||
|
[0.0; 30],
|
||||||
|
[0.0; 30],
|
||||||
|
],
|
||||||
|
// 参数 3
|
||||||
|
[
|
||||||
|
[0.0, 0.0, 375.54, 0.0, 0.0, 176.43, -0.92, 24.37, 0.0, 0.0,
|
||||||
|
2632.31, 55.54, 0.0, 0.80, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, -0.89, 6.44e-2, -0.41, 24.41, 0.0, 0.0, 0.0],
|
||||||
|
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, -1.97, 0.0, -0.43, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, -0.89, 6.44e-2, -0.41, 24.41, 0.0, 0.0, 0.0],
|
||||||
|
[0.0; 30],
|
||||||
|
[0.0; 30],
|
||||||
|
],
|
||||||
|
// 参数 4
|
||||||
|
[
|
||||||
|
[0.0, 0.0, -54.07, 0.0, 0.0, -4.29, -8.38, -0.74, 0.0, 0.0,
|
||||||
|
-3.04, -1.12, 0.0, -0.24, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, -3.56, -9.70, -7.31, -1.26, 0.0, 0.0, 0.0],
|
||||||
|
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, -4.32, 0.0, -0.11, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, -3.56, -9.70, -7.31, -1.26, 0.0, 0.0, 0.0],
|
||||||
|
[0.0; 30],
|
||||||
|
[0.0; 30],
|
||||||
|
],
|
||||||
|
// 参数 5 (温度下限)
|
||||||
|
[
|
||||||
|
[0.0, 0.0, 1e2, 0.0, 0.0, 1e3, 1e2, 1e1, 0.0, 0.0,
|
||||||
|
1e3, 5e3, 0.0, 1e3, 0.0, 1e3, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 1e3, 1e3, 1e4, 1e3, 0.0, 0.0, 0.0],
|
||||||
|
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 1e4, 0.0, 2e3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 1e3, 1e3, 1e3, 1e3, 0.0, 0.0, 0.0],
|
||||||
|
[0.0; 30],
|
||||||
|
[0.0; 30],
|
||||||
|
],
|
||||||
|
// 参数 6 (温度上限)
|
||||||
|
[
|
||||||
|
[0.0, 0.0, 1e4, 0.0, 0.0, 1e5, 5e4, 1e4, 0.0, 0.0,
|
||||||
|
2e4, 3e4, 0.0, 2e5, 0.0, 1e4, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 3e4, 3e4, 1e5, 3e4, 0.0, 0.0, 0.0],
|
||||||
|
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 3e5, 0.0, 1e5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 3e4, 3e4, 1e5, 3e4, 0.0, 0.0, 0.0],
|
||||||
|
[0.0; 30],
|
||||||
|
[0.0; 30],
|
||||||
|
],
|
||||||
|
// 参数 7 (单位 1e4 K)
|
||||||
|
[
|
||||||
|
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.086, 0.023, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 3.349, 2.368, 3.005, 4.044, 0.0, 0.0, 0.0],
|
||||||
|
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 1.670, 0.0, 3.031, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 3.349, 2.368, 3.005, 4.044, 0.0, 0.0, 0.0],
|
||||||
|
[0.0; 30],
|
||||||
|
[0.0; 30],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
/// 电荷转移复合系数。
|
||||||
|
///
|
||||||
|
/// 数组维度: [6, 4, 30]
|
||||||
|
/// - 第一维: 6 个拟合参数
|
||||||
|
/// - 第二维: 4 个电离级 (1=+1, 2=+2, 3=+3, 4=+4)
|
||||||
|
/// - 第三维: 30 个原子序号
|
||||||
|
///
|
||||||
|
/// 注意: 第一参数单位为 1e-9
|
||||||
|
pub const CTRECOMB: [[[f64; 30]; 4]; 6] = [
|
||||||
|
// 参数 1 (单位 1e-9)
|
||||||
|
[
|
||||||
|
// ion=1
|
||||||
|
[0.0, 7.47e-6, 0.0, 0.0, 0.0, 4.88e-7, 1.01e-3, 1.04, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0, 3.82e-7, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
|
||||||
|
// ion=2
|
||||||
|
[1.00e-5, 1.00e-5, 1.26, 1.00e-5, 2.00e-2, 1.67e-4, 3.05e-1, 1.04, 1.00e-5, 1.00e-5,
|
||||||
|
1.00e-5, 8.58e-5, 1.00e-5, 6.77, 1.74e-4, 1.00e-5, 1.00e-5, 1.00e-5, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 1.00e-5, 5.27e-1, 1.65e-1, 1.26, 5.30, 1.05, 1.47e-3, 1.00e-5],
|
||||||
|
// ion=3
|
||||||
|
[0.0, 1.00e-5, 1.00e-5, 1.00e-5, 1.00e-5, 3.25, 4.54, 3.98, 9.86, 14.73,
|
||||||
|
1.33, 6.49, 7.11e-5, 4.90e-1, 9.46e-2, 2.29, 1.88, 4.57, 4.76, 3.17e-2,
|
||||||
|
7.22e-3, 6.34e-1, 5.12, 10.90, 14.20, 3.42, 3.26, 9.73, 9.26, 6.96e-4],
|
||||||
|
// ion=4
|
||||||
|
[0.0, 1.00e-5, 1.00e-5, 5.17, 2.74, 332.46, 2.95, 2.52e-1, 7.15e-1, 6.47,
|
||||||
|
1.01e-1, 6.36, 7.52e-1, 7.58, 5.37, 6.44, 7.27, 6.37, 1.00e-5, 2.68,
|
||||||
|
1.20e-1, 4.37e-3, 1.96e-1, 1.18, 4.43e-1, 14.60, 1.03, 6.14, 11.59, 1.33e-2],
|
||||||
|
],
|
||||||
|
// 参数 2
|
||||||
|
[
|
||||||
|
[0.0, 2.06, 0.0, 0.0, 0.0, 3.25, -0.29, 3.15e-2, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0, 11.10, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
|
||||||
|
[0.0, 0.0, 0.96, 0.0, 0.0, 2.79, 0.60, 0.27, 0.0, 0.0,
|
||||||
|
0.0, 2.49e-3, 0.0, 7.36e-2, 3.84, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 0.61, 6.80e-3, 7.72e-2, 0.24, 1.28, 3.51, 4.24],
|
||||||
|
[0.0, 0.0, 0.0, 0.0, 0.0, 0.21, 0.57, 0.26, 0.29, 4.52e-2,
|
||||||
|
1.15, 0.53, 4.12, -8.74e-2, -5.58e-2, 4.02e-2, 0.32, 0.27, 0.44, 2.12,
|
||||||
|
2.34, 6.87e-3, -2.18e-2, 0.24, 0.34, 0.51, 0.87, 0.35, 0.37, 4.24],
|
||||||
|
[0.0, 0.0, 0.0, 0.82, 0.93, -0.11, 0.55, 0.63, 1.21, 0.54,
|
||||||
|
1.34, 0.55, 0.77, 0.37, 0.47, 0.13, 0.29, 0.85, 0.0, 0.69,
|
||||||
|
1.48, 1.25, -8.53e-3, 0.20, 0.91, 3.57e-2, 0.58, 0.25, 0.20, 1.56],
|
||||||
|
],
|
||||||
|
// 参数 3
|
||||||
|
[
|
||||||
|
[0.0, 9.93, 0.0, 0.0, 0.0, -1.12, -0.92, -0.61, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0, 2.57e4, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
|
||||||
|
[0.0, 0.0, 3.02, 0.0, 0.0, 304.72, 2.65, 2.02, 0.0, 0.0,
|
||||||
|
0.0, 2.93e-2, 0.0, -0.43, 36.06, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, -0.89, 6.44e-2, -0.41, 2.85, 6.54, 23.91, 26.06],
|
||||||
|
[0.0, 0.0, 0.0, 0.0, 0.0, 0.19, -0.65, 0.56, -0.21, -0.84,
|
||||||
|
1.20, 2.82, 1.72e4, -0.36, 0.77, 1.59, 1.77, -0.18, -0.56, 12.06,
|
||||||
|
411.50, 0.18, -0.24, 0.26, -0.41, -2.06, 2.85, 0.90, 0.40, 26.06],
|
||||||
|
[0.0, 0.0, 0.0, -0.69, -0.61, -9.95e-1, -0.39, 2.08, -0.70, 3.59,
|
||||||
|
10.05, 3.86, 6.24, 1.06, 2.21, 2.69, 1.04, 10.21, 0.0, -0.68,
|
||||||
|
4.00, 40.02, 0.28, 0.77, 10.76, -0.92, -0.89, -0.91, 0.80, -0.92],
|
||||||
|
],
|
||||||
|
// 参数 4
|
||||||
|
[
|
||||||
|
[0.0, -3.89, 0.0, 0.0, 0.0, -0.21, -8.38, -9.73, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0, -8.22, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
|
||||||
|
[0.0, 0.0, -0.65, 0.0, 0.0, -4.07, -0.93, -5.92, 0.0, 0.0,
|
||||||
|
0.0, -4.33, 0.0, -0.11, -0.97, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, -3.56, -9.70, -7.31, -9.23, -1.81, -0.93, -1.24],
|
||||||
|
[0.0, 0.0, 0.0, 0.0, 0.0, -3.29, -0.89, -2.62, -1.15, -0.31,
|
||||||
|
-0.32, -7.63, -22.24, -0.79, -6.43, -6.06, -5.70, -1.57, -0.88, -0.40,
|
||||||
|
-13.24, -8.04, -0.83, -11.94, -1.19, -8.99, -9.23, -5.33, -10.73, -1.24],
|
||||||
|
[0.0, 0.0, 0.0, -1.12, -1.13, -1.58e-3, -1.07, -4.16, -0.85, -5.22,
|
||||||
|
-6.41, -5.19, -5.67, -4.09, -8.52, -5.69, -10.14, -6.22, 0.0, -4.47,
|
||||||
|
-9.33, -8.05, -6.46, -7.09, -7.49, -0.37, -0.66, -0.42, -6.62, -1.20],
|
||||||
|
],
|
||||||
|
// 参数 5 (温度下限)
|
||||||
|
[
|
||||||
|
[0.0, 6e3, 0.0, 0.0, 0.0, 5.5e3, 1e2, 1e1, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 0.0, 1e3, 1e3, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 1e3, 1e3, 1e3, 1e3, 0.0, 0.0, 0.0],
|
||||||
|
[0.0, 1e3, 1e3, 2e3, 1e3, 5e3, 1e3, 1e2, 2e3, 5e3,
|
||||||
|
2e3, 1e3, 1e3, 5e2, 1e3, 1e3, 1e3, 1e3, 1e1, 1e1,
|
||||||
|
1e1, 1e1, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3],
|
||||||
|
[0.0, 2e3, 2e3, 2e3, 2e3, 1e3, 1e1, 1e3, 2e3, 5e3,
|
||||||
|
2e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3,
|
||||||
|
1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3],
|
||||||
|
[0.0, 2e3, 2e3, 2e3, 2e3, 1e1, 1e3, 1e3, 2e3, 1e3,
|
||||||
|
2e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3,
|
||||||
|
1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3, 1e3],
|
||||||
|
],
|
||||||
|
// 参数 6 (温度上限)
|
||||||
|
[
|
||||||
|
[0.0, 1e5, 0.0, 0.0, 0.0, 1e5, 5e4, 1e4, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 0.0, 3e4, 1e4, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 3e4, 3e4, 1e5, 3e4, 0.0, 0.0, 0.0],
|
||||||
|
[0.0, 1e7, 3e4, 5e4, 1e9, 5e4, 1e5, 1e5, 5e4, 5e4,
|
||||||
|
5e4, 3e4, 3e4, 1e5, 3e4, 3e4, 3e4, 3e4, 1e9, 1e9,
|
||||||
|
1e9, 1e9, 3e4, 3e4, 3e4, 1e5, 3e4, 1e5, 3e4, 3e4],
|
||||||
|
[0.0, 5e4, 5e4, 5e4, 5e4, 1e5, 1e5, 5e4, 5e4, 5e4,
|
||||||
|
5e4, 3e4, 3e4, 3e4, 3e4, 3e4, 3e4, 3e4, 3e4, 3e4,
|
||||||
|
3e4, 3e4, 3e4, 3e4, 3e4, 1e5, 3e4, 3e4, 3e4, 3e4],
|
||||||
|
[0.0, 5e4, 5e4, 5e4, 5e4, 1e5, 1e6, 3e4, 5e4, 3e4,
|
||||||
|
5e4, 3e4, 3e4, 5e4, 3e4, 3e4, 3e4, 3e4, 3e4, 3e4,
|
||||||
|
3e4, 3e4, 3e4, 3e4, 3e4, 3e4, 3e4, 3e4, 3e4, 3e4],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
/// 电荷转移电离速率系数。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `ion` - 电离级 (1=原子, 2=+1, ...)
|
||||||
|
/// * `nelem` - 原子序数 (2-30)
|
||||||
|
/// * `te` - 电子温度 (K)
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// 电荷转移电离速率系数 (cm^3/s)
|
||||||
|
///
|
||||||
|
/// # 示例
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use tlusty_rust::math::ctdata::hction;
|
||||||
|
///
|
||||||
|
/// // O + H+ => O+ + H
|
||||||
|
/// let rate = hction(1, 8, 10000.0);
|
||||||
|
/// assert!(rate > 0.0);
|
||||||
|
/// ```
|
||||||
|
pub fn hction(ion: usize, nelem: usize, te: f64) -> f64 {
|
||||||
|
let ip_ion = ion; // Fortran: ipIon = ion
|
||||||
|
|
||||||
|
// 确保温度在边界内
|
||||||
|
let tused = te.max(CTION[4][ip_ion - 1][nelem - 1])
|
||||||
|
.min(CTION[5][ip_ion - 1][nelem - 1]);
|
||||||
|
let tused = tused * 1e-4;
|
||||||
|
|
||||||
|
// 插值方程
|
||||||
|
CTION[0][ip_ion - 1][nelem - 1] * 1e-9
|
||||||
|
* tused.powf(CTION[1][ip_ion - 1][nelem - 1])
|
||||||
|
* (1.0 + CTION[2][ip_ion - 1][nelem - 1]
|
||||||
|
* (CTION[3][ip_ion - 1][nelem - 1] * tused).exp())
|
||||||
|
* (-CTION[6][ip_ion - 1][nelem - 1] / tused).exp()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 电荷转移复合速率系数。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `ion` - 电离级 (2=+1→原子, 3=+2→+1, ...)
|
||||||
|
/// * `nelem` - 原子序数 (2-30)
|
||||||
|
/// * `te` - 电子温度 (K)
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// 电荷转移复合速率系数 (cm^3/s)
|
||||||
|
///
|
||||||
|
/// # 示例
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use tlusty_rust::math::ctdata::hctrecom;
|
||||||
|
///
|
||||||
|
/// // O+ + H => O + H+
|
||||||
|
/// let rate = hctrecom(2, 8, 10000.0);
|
||||||
|
/// assert!(rate > 0.0);
|
||||||
|
/// ```
|
||||||
|
pub fn hctrecom(ion: usize, nelem: usize, te: f64) -> f64 {
|
||||||
|
let ip_ion = ion - 1; // Fortran: ipIon = ion - 1
|
||||||
|
|
||||||
|
if ip_ion > 4 {
|
||||||
|
// 对于 ion > 4 使用统计电荷转移
|
||||||
|
return 1.92e-9 * ip_ion as f64;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保温度在边界内
|
||||||
|
let tused = te.max(CTRECOMB[4][ip_ion - 1][nelem - 1])
|
||||||
|
.min(CTRECOMB[5][ip_ion - 1][nelem - 1]);
|
||||||
|
let tused = tused * 1e-4;
|
||||||
|
|
||||||
|
// 插值方程
|
||||||
|
CTRECOMB[0][ip_ion - 1][nelem - 1] * 1e-9
|
||||||
|
* tused.powf(CTRECOMB[1][ip_ion - 1][nelem - 1])
|
||||||
|
* (1.0 + CTRECOMB[2][ip_ion - 1][nelem - 1]
|
||||||
|
* (CTRECOMB[3][ip_ion - 1][nelem - 1] * tused).exp())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use approx::assert_relative_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hction_oxygen() {
|
||||||
|
// O + H+ => O+ + H (ion=1, nelem=8)
|
||||||
|
let rate = hction(1, 8, 10000.0);
|
||||||
|
assert!(rate.is_finite());
|
||||||
|
assert!(rate > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hction_silicon() {
|
||||||
|
// Si + H+ => Si+ + H (ion=1, nelem=14)
|
||||||
|
let rate = hction(1, 14, 10000.0);
|
||||||
|
assert!(rate.is_finite());
|
||||||
|
assert!(rate > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hctrecom_oxygen() {
|
||||||
|
// O+ + H => O + H+ (ion=2, nelem=8)
|
||||||
|
let rate = hctrecom(2, 8, 10000.0);
|
||||||
|
assert!(rate.is_finite());
|
||||||
|
assert!(rate > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hctrecom_high_ion() {
|
||||||
|
// ion > 4 使用统计值
|
||||||
|
let rate = hctrecom(6, 8, 10000.0);
|
||||||
|
assert_relative_eq!(rate, 1.92e-9 * 5.0, epsilon = 1e-20);
|
||||||
|
}
|
||||||
|
}
|
||||||
152
src/math/cubic.rs
Normal file
152
src/math/cubic.rs
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
//! 三次方程求解器。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `cubic.f`
|
||||||
|
|
||||||
|
/// 三次方程求解器参数。
|
||||||
|
///
|
||||||
|
/// 对应 Fortran COMMON/CUBCON/
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct CubicCon {
|
||||||
|
/// 系数 A
|
||||||
|
pub a: f64,
|
||||||
|
/// 系数 B
|
||||||
|
pub b: f64,
|
||||||
|
/// DELTA(RAD) - DELTA(ADIAB)
|
||||||
|
pub del: f64,
|
||||||
|
/// 绝热梯度
|
||||||
|
pub grdadb: f64,
|
||||||
|
/// 密度
|
||||||
|
pub rho: f64,
|
||||||
|
/// 总通量
|
||||||
|
pub flxtot: f64,
|
||||||
|
/// 重力
|
||||||
|
pub gravd: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 求解三次方程确定真实梯度 DELTA。
|
||||||
|
///
|
||||||
|
/// 解三次方程: A*x³ + x² + B*x = DEL
|
||||||
|
/// 其中 x = (DELTA - DELTA(ELEM))^(1/2)
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `cubcon` - 三次方程参数
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// 真实梯度 DELTA
|
||||||
|
pub fn cubic(cubcon: &CubicCon) -> f64 {
|
||||||
|
let a = cubcon.a;
|
||||||
|
let b = cubcon.b;
|
||||||
|
let del = cubcon.del;
|
||||||
|
let grdadb = cubcon.grdadb;
|
||||||
|
|
||||||
|
const THIRD: f64 = 1.0 / 3.0;
|
||||||
|
const UN: f64 = 1.0;
|
||||||
|
|
||||||
|
// 归一化系数
|
||||||
|
let aa = THIRD / a;
|
||||||
|
let bb = b / a;
|
||||||
|
let cc = -del / a;
|
||||||
|
|
||||||
|
let p = bb * THIRD - aa * aa;
|
||||||
|
let q = aa.powi(3) - (bb * aa - cc) / 2.0;
|
||||||
|
let d = q * q + p * p * p;
|
||||||
|
|
||||||
|
let sol = if d > 0.0 {
|
||||||
|
// 一个实根
|
||||||
|
let d_sqrt = d.sqrt();
|
||||||
|
if (d_sqrt - q.abs()) < 1e-14 * d_sqrt {
|
||||||
|
(2.0 * d_sqrt).powf(THIRD) - aa
|
||||||
|
} else {
|
||||||
|
let d1 = (d_sqrt - q).abs();
|
||||||
|
let d2 = (d_sqrt + q).abs();
|
||||||
|
d1 / (d_sqrt - q) * d1.powf(THIRD) - d2 / (d_sqrt + q) * d2.powf(THIRD) - aa
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 三个实根,用三角公式
|
||||||
|
let cosf = -q / (p * p * p).abs().sqrt();
|
||||||
|
let tanf = (UN - cosf * cosf).sqrt() / cosf;
|
||||||
|
let fi = tanf.atan() * THIRD;
|
||||||
|
2.0 * p.abs().sqrt() * fi.cos() - aa
|
||||||
|
};
|
||||||
|
|
||||||
|
// 如果上面的方法给出非物理解 (x > DEL 或 x < 0),
|
||||||
|
// 则用 Newton-Raphson 方法在 (0, DEL) 范围内求解
|
||||||
|
let sol = {
|
||||||
|
let delda = sol * (b + sol);
|
||||||
|
if delda > del || delda < 0.0 {
|
||||||
|
// Newton-Raphson 迭代
|
||||||
|
let mut x0 = sol;
|
||||||
|
for _ in 0..50 {
|
||||||
|
let delx = (del - x0 * (b + x0 + a * x0 * x0))
|
||||||
|
/ (3.0 * a * x0 * x0 + 2.0 * x0 + b);
|
||||||
|
x0 += delx;
|
||||||
|
if (delx / x0).abs() < 1e-6 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x0
|
||||||
|
} else {
|
||||||
|
sol
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 最终梯度
|
||||||
|
grdadb + b * sol + sol * sol
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cubic_basic() {
|
||||||
|
let cubcon = CubicCon {
|
||||||
|
a: 1.0,
|
||||||
|
b: 1.0,
|
||||||
|
del: 0.1,
|
||||||
|
grdadb: 0.0,
|
||||||
|
rho: 1.0,
|
||||||
|
flxtot: 1.0,
|
||||||
|
gravd: 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let delta = cubic(&cubcon);
|
||||||
|
assert!(delta.is_finite());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cubic_small_del() {
|
||||||
|
let cubcon = CubicCon {
|
||||||
|
a: 0.5,
|
||||||
|
b: 0.3,
|
||||||
|
del: 0.01,
|
||||||
|
grdadb: 0.1,
|
||||||
|
rho: 1.0,
|
||||||
|
flxtot: 1.0,
|
||||||
|
gravd: 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let delta = cubic(&cubcon);
|
||||||
|
assert!(delta.is_finite());
|
||||||
|
assert!(delta > cubcon.grdadb);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cubic_negative_region() {
|
||||||
|
// 测试需要 Newton-Raphson 修正的情况
|
||||||
|
let cubcon = CubicCon {
|
||||||
|
a: 10.0,
|
||||||
|
b: 5.0,
|
||||||
|
del: 0.5,
|
||||||
|
grdadb: 0.2,
|
||||||
|
rho: 1.0,
|
||||||
|
flxtot: 1.0,
|
||||||
|
gravd: 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let delta = cubic(&cubcon);
|
||||||
|
assert!(delta.is_finite());
|
||||||
|
}
|
||||||
|
}
|
||||||
158
src/math/divstr.rs
Normal file
158
src/math/divstr.rs
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
//! Stark 轮廓分割点计算。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `divstr.f`
|
||||||
|
//!
|
||||||
|
//! 用于 STARKA - 确定 Doppler 和渐近 Stark 轮廓之间的分割点。
|
||||||
|
|
||||||
|
use crate::state::constants::{TWO, UN};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// DIVSTR - Stark 轮廓分割点
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 计算 Doppler 和 Stark 轮廓之间的分割点。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `betad` - Doppler 宽度 (beta 单位)
|
||||||
|
/// - `iah` - 原子类型: 1=H I, 2=He II
|
||||||
|
///
|
||||||
|
/// # 返回
|
||||||
|
///
|
||||||
|
/// - `(adh, divh)` - 辅助参数 A 和分割点 DIV
|
||||||
|
///
|
||||||
|
/// # 说明
|
||||||
|
///
|
||||||
|
/// - `adh = 1.5 * ln(betad) - 1.671`
|
||||||
|
/// - 对于 He II: `adh += 0.69314718`
|
||||||
|
/// - `divh` 仅当 `adh > 1` 时计算,是方程的解:
|
||||||
|
/// `exp(-(beta/betad)^2) / betad / sqrt(pi) = 3 * beta^(-5/2)`
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// SUBROUTINE DIVSTR(IAH)
|
||||||
|
/// INCLUDE 'IMPLIC.FOR'
|
||||||
|
/// INCLUDE 'BASICS.FOR'
|
||||||
|
/// INCLUDE 'MODELQ.FOR'
|
||||||
|
/// PARAMETER (UNQ=1.25,UNH=1.5,TWH=2.5,FO=4.,FI=5.)
|
||||||
|
/// PARAMETER (CA=1.671,BL=5.821,AL=1.26,CX=0.28,DX=0.0001)
|
||||||
|
/// PARAMETER (CA2=0.978,XA2=0.69314718)
|
||||||
|
///
|
||||||
|
/// ADH=UNH*LOG(BETAD)-CA
|
||||||
|
/// IF(IAH.EQ.2) ADH=ADH+XA2
|
||||||
|
/// IF(BETAD.LT.BL) RETURN
|
||||||
|
/// IF(ADH.GE.AL) THEN
|
||||||
|
/// X=SQRT(ADH)*(UN+UNQ*LOG(ADH)/(FO*ADH-FI))
|
||||||
|
/// ELSE
|
||||||
|
/// X=SQRT(CX+ADH)
|
||||||
|
/// ENDIF
|
||||||
|
/// DO I=1,5
|
||||||
|
/// X2=X*X
|
||||||
|
/// XN=X*(UN-(X2-TWH*LOG(X)-ADH)/(TWO*X2-TWH))
|
||||||
|
/// IF(ABS(XN-X).LE.DX) GO TO 10
|
||||||
|
/// X=XN
|
||||||
|
/// END DO
|
||||||
|
/// 10 DIVH=X
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
pub fn divstr(betad: f64, iah: i32) -> (f64, f64) {
|
||||||
|
const UNQ: f64 = 1.25;
|
||||||
|
const UNH: f64 = 1.5;
|
||||||
|
const TWH: f64 = 2.5;
|
||||||
|
const FO: f64 = 4.0;
|
||||||
|
const FI: f64 = 5.0;
|
||||||
|
const CA: f64 = 1.671;
|
||||||
|
const BL: f64 = 5.821;
|
||||||
|
const AL: f64 = 1.26;
|
||||||
|
const CX: f64 = 0.28;
|
||||||
|
const DX: f64 = 0.0001;
|
||||||
|
const XA2: f64 = 0.69314718;
|
||||||
|
|
||||||
|
// 计算辅助参数 A
|
||||||
|
let mut adh = UNH * betad.ln() - CA;
|
||||||
|
|
||||||
|
// He II 需要额外偏移
|
||||||
|
if iah == 2 {
|
||||||
|
adh += XA2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 betad 太小,不计算 divh
|
||||||
|
if betad < BL {
|
||||||
|
return (adh, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始猜测
|
||||||
|
let mut x = if adh >= AL {
|
||||||
|
adh.sqrt() * (UN + UNQ * adh.ln() / (FO * adh - FI))
|
||||||
|
} else {
|
||||||
|
(CX + adh).sqrt()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Newton 迭代求解
|
||||||
|
for _ in 0..5 {
|
||||||
|
let x2 = x * x;
|
||||||
|
let xn = x * (UN - (x2 - TWH * x.ln() - adh) / (TWO * x2 - TWH));
|
||||||
|
if (xn - x).abs() <= DX {
|
||||||
|
x = xn;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
x = xn;
|
||||||
|
}
|
||||||
|
|
||||||
|
(adh, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_divstr_small_betad() {
|
||||||
|
// betad < BL = 5.821 时,divh = 0
|
||||||
|
let (adh, divh) = divstr(5.0, 1);
|
||||||
|
assert!(adh > 0.0, "adh should be positive");
|
||||||
|
assert!((divh - 0.0).abs() < 1e-10, "divh should be 0 for small betad");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_divstr_large_betad_h1() {
|
||||||
|
// betad >= BL 且 adh >= AL 时,计算 divh
|
||||||
|
let (adh, divh) = divstr(10.0, 1);
|
||||||
|
// adh = 1.5 * ln(10) - 1.671 = 1.5 * 2.3026 - 1.671 = 3.454 - 1.671 = 1.783
|
||||||
|
assert!((adh - 1.783).abs() < 0.01, "adh = {}", adh);
|
||||||
|
assert!(divh > 0.0, "divh should be positive");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_divstr_he_ii() {
|
||||||
|
// He II 有额外偏移
|
||||||
|
let (adh_h1, _) = divstr(10.0, 1);
|
||||||
|
let (adh_he2, _) = divstr(10.0, 2);
|
||||||
|
|
||||||
|
// He II 的 adh 应该比 H I 大 0.69314718
|
||||||
|
assert!((adh_he2 - adh_h1 - 0.69314718).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_divstr_boundary() {
|
||||||
|
// 刚好等于 BL
|
||||||
|
let (adh, divh) = divstr(5.821, 1);
|
||||||
|
// 由于 betad >= BL,应该计算 divh
|
||||||
|
// 但 adh 可能 < AL (1.26)
|
||||||
|
if adh >= 1.26 {
|
||||||
|
assert!(divh > 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_divstr_medium_betad() {
|
||||||
|
// 中等大小的 betad
|
||||||
|
let (adh, divh) = divstr(7.0, 1);
|
||||||
|
// adh = 1.5 * ln(7) - 1.671 = 1.5 * 1.946 - 1.671 = 2.919 - 1.671 = 1.248
|
||||||
|
// 1.248 < AL = 1.26,所以用 sqrt(CX + adh)
|
||||||
|
assert!((adh - 1.248).abs() < 0.01, "adh = {}", adh);
|
||||||
|
// 由于 betad >= BL,divh 会被计算
|
||||||
|
assert!(divh > 0.0, "divh should be positive");
|
||||||
|
}
|
||||||
|
}
|
||||||
134
src/math/dmder.rs
Normal file
134
src/math/dmder.rs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
//! 深度导数计算。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `dmder.f`
|
||||||
|
|
||||||
|
use crate::state::constants::{MDEPTH, UN};
|
||||||
|
|
||||||
|
/// 深度导数参数。
|
||||||
|
///
|
||||||
|
/// 对应 Fortran COMMON/DEPTDR/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct DepthDeriv {
|
||||||
|
/// DM(ID) - DM(ID-1)
|
||||||
|
pub ddm: Vec<f64>,
|
||||||
|
/// DM(ID+1) - DM(ID)
|
||||||
|
pub ddp: Vec<f64>,
|
||||||
|
/// DM(ID+1) - DM(ID-1)
|
||||||
|
pub dd0: Vec<f64>,
|
||||||
|
/// DDP / DD0
|
||||||
|
pub ddmin: Vec<f64>,
|
||||||
|
/// DDM / DD0
|
||||||
|
pub ddplu: Vec<f64>,
|
||||||
|
/// DDMIN / DDM
|
||||||
|
pub dda: Vec<f64>,
|
||||||
|
/// DDA - DDC
|
||||||
|
pub ddb: Vec<f64>,
|
||||||
|
/// DDPLU / DDP
|
||||||
|
pub ddc: Vec<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DepthDeriv {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
ddm: vec![0.0; MDEPTH],
|
||||||
|
ddp: vec![0.0; MDEPTH],
|
||||||
|
dd0: vec![0.0; MDEPTH],
|
||||||
|
ddmin: vec![0.0; MDEPTH],
|
||||||
|
ddplu: vec![0.0; MDEPTH],
|
||||||
|
dda: vec![0.0; MDEPTH],
|
||||||
|
ddb: vec![0.0; MDEPTH],
|
||||||
|
ddc: vec![0.0; MDEPTH],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 计算深度相关的导数。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `dm` - 深度质量数组 (1-indexed in Fortran, 0-indexed here)
|
||||||
|
/// * `nd` - 深度点数
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// 深度导数结构体
|
||||||
|
pub fn dmder(dm: &[f64], nd: usize) -> DepthDeriv {
|
||||||
|
let mut deriv = DepthDeriv::default();
|
||||||
|
|
||||||
|
// 内部点 (ID = 2 to ND-1, Fortran 1-indexed)
|
||||||
|
// Rust 0-indexed: id = 1 to nd-2
|
||||||
|
for id in 1..nd - 1 {
|
||||||
|
deriv.ddm[id] = dm[id] - dm[id - 1];
|
||||||
|
deriv.ddp[id] = dm[id + 1] - dm[id];
|
||||||
|
deriv.dd0[id] = dm[id + 1] - dm[id - 1];
|
||||||
|
deriv.ddmin[id] = deriv.ddp[id] / deriv.dd0[id];
|
||||||
|
deriv.ddplu[id] = deriv.ddm[id] / deriv.dd0[id];
|
||||||
|
deriv.dda[id] = deriv.ddmin[id] / deriv.ddm[id];
|
||||||
|
deriv.ddc[id] = deriv.ddplu[id] / deriv.ddp[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 边界条件
|
||||||
|
// ID = 1 (Fortran) -> id = 0 (Rust)
|
||||||
|
deriv.ddm[0] = 0.0;
|
||||||
|
deriv.ddp[0] = dm[1] - dm[0];
|
||||||
|
deriv.ddmin[0] = 0.0;
|
||||||
|
deriv.ddplu[0] = 1.0;
|
||||||
|
deriv.dda[0] = 0.0;
|
||||||
|
deriv.ddc[0] = UN / deriv.ddp[0];
|
||||||
|
|
||||||
|
// ID = ND (Fortran) -> id = nd-1 (Rust)
|
||||||
|
let id_last = nd - 1;
|
||||||
|
deriv.ddm[id_last] = dm[id_last] - dm[id_last - 1];
|
||||||
|
deriv.ddp[id_last] = 0.0;
|
||||||
|
deriv.ddmin[id_last] = 1.0;
|
||||||
|
deriv.ddplu[id_last] = 0.0;
|
||||||
|
deriv.dda[id_last] = UN / deriv.ddm[id_last];
|
||||||
|
deriv.ddc[id_last] = 0.0;
|
||||||
|
|
||||||
|
// DDB = DDA - DDC
|
||||||
|
for id in 0..nd {
|
||||||
|
deriv.ddb[id] = deriv.dda[id] - deriv.ddc[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
deriv
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dmder_uniform() {
|
||||||
|
// 均匀网格
|
||||||
|
let dm: Vec<f64> = (0..10).map(|i| i as f64).collect();
|
||||||
|
let deriv = dmder(&dm, 10);
|
||||||
|
|
||||||
|
// 检查内部点
|
||||||
|
assert!((deriv.ddm[5] - 1.0).abs() < 1e-10);
|
||||||
|
assert!((deriv.ddp[5] - 1.0).abs() < 1e-10);
|
||||||
|
assert!((deriv.dd0[5] - 2.0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dmder_boundary() {
|
||||||
|
let dm: Vec<f64> = (0..5).map(|i| i as f64 * 2.0).collect();
|
||||||
|
let deriv = dmder(&dm, 5);
|
||||||
|
|
||||||
|
// 边界条件
|
||||||
|
assert!((deriv.ddm[0] - 0.0).abs() < 1e-10);
|
||||||
|
assert!((deriv.ddp[0] - 2.0).abs() < 1e-10);
|
||||||
|
assert!((deriv.ddmin[0] - 0.0).abs() < 1e-10);
|
||||||
|
assert!((deriv.ddplu[0] - 1.0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dmder_consistency() {
|
||||||
|
let dm: Vec<f64> = vec![0.0, 1.0, 3.0, 6.0, 10.0];
|
||||||
|
let deriv = dmder(&dm, 5);
|
||||||
|
|
||||||
|
// 检查 DDB = DDA - DDC
|
||||||
|
for id in 0..5 {
|
||||||
|
assert!((deriv.ddb[id] - (deriv.dda[id] - deriv.ddc[id])).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
319
src/math/dopgam.rs
Normal file
319
src/math/dopgam.rs
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
//! Doppler 宽度和 Voigt 阻尼参数计算。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `dopgam.f`
|
||||||
|
//!
|
||||||
|
//! 计算谱线的 Doppler 宽度和总阻尼参数。
|
||||||
|
|
||||||
|
use crate::math::gamsp;
|
||||||
|
|
||||||
|
const BOL2: f64 = 2.76108e-16;
|
||||||
|
const CIN: f64 = 1.0 / 2.997925e10;
|
||||||
|
const R02: f64 = 2.5;
|
||||||
|
const R12: f64 = 45.0;
|
||||||
|
const OP4: f64 = 0.4;
|
||||||
|
const VW0: f64 = 4.5e-9;
|
||||||
|
const EH: f64 = 2.17853041e-11;
|
||||||
|
|
||||||
|
/// Doppler 宽度和 Voigt 阻尼参数计算。
|
||||||
|
///
|
||||||
|
/// 计算给定谱线的 Doppler 宽度和总阻尼参数(以 Doppler 宽度为单位)。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `itr` - 跃迁索引 (1-based)
|
||||||
|
/// * `id` - 深度索引 (1-based)
|
||||||
|
/// * `t` - 温度
|
||||||
|
/// * `iup` - 上能级索引数组
|
||||||
|
/// * `iatm` - 原子索引数组
|
||||||
|
/// * `fr0` - 跃迁频率数组
|
||||||
|
/// * `iel` - 元素索引数组
|
||||||
|
/// * `amass` - 原子质量数组
|
||||||
|
/// * `iprof` - 轮廓类型数组
|
||||||
|
/// * `itra` - 跃迁索引矩阵 [MTRANS][MLEVEL]
|
||||||
|
/// * `ilow` - 下能级索引数组
|
||||||
|
/// * `gamar` - 辐射阻尼参数数组
|
||||||
|
/// * `iz` - 电离度数组
|
||||||
|
/// * `enion` - 电离能数组
|
||||||
|
/// * `stark1` - Stark 参数 1
|
||||||
|
/// * `stark2` - Stark 参数 2
|
||||||
|
/// * `stark3` - Stark 参数 3
|
||||||
|
/// * `vdwh` - Van der Waals 参数
|
||||||
|
/// * `ielh` - 氢元素索引
|
||||||
|
/// * `nfirst` - 第一个能级索引数组
|
||||||
|
/// ` `iathe` - 氦原子索引
|
||||||
|
/// * `ielhe1` - He I 元素索引
|
||||||
|
/// * `vturbs` - 微湍流速度数组
|
||||||
|
/// * `elec` - 电子密度数组
|
||||||
|
/// * `dens` - 密度数组
|
||||||
|
/// * `wmm` - 平均分子量数组
|
||||||
|
/// * `ytot` - 总粒子数数组
|
||||||
|
/// * `abndd` - 丰度数组 [MABUND][MDEPTH]
|
||||||
|
/// * `popul` - 布居数数组 [MLEVEL][MDEPTH]
|
||||||
|
/// * `abund` - 原子丰度数组 [MATOM][MDEPTH]
|
||||||
|
/// * `mtrans` - 最大跃迁数
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// 返回 (dop, agam):
|
||||||
|
/// - `dop` - Doppler 宽度
|
||||||
|
/// - `agam` - 总阻尼参数(以 Doppler 宽度为单位)
|
||||||
|
pub fn dopgam(
|
||||||
|
itr: i32,
|
||||||
|
id: usize,
|
||||||
|
t: f64,
|
||||||
|
iup: &[i32],
|
||||||
|
iatm: &[i32],
|
||||||
|
fr0: &[f64],
|
||||||
|
iel: &[i32],
|
||||||
|
amass: &[f64],
|
||||||
|
iprof: &[i32],
|
||||||
|
itra: &[i32],
|
||||||
|
ilow: &[i32],
|
||||||
|
gamar: &[f64],
|
||||||
|
iz: &[i32],
|
||||||
|
enion: &[f64],
|
||||||
|
stark1: &[f64],
|
||||||
|
stark2: &[f64],
|
||||||
|
stark3: &[f64],
|
||||||
|
vdwh: &[f64],
|
||||||
|
ielh: i32,
|
||||||
|
nfirst: &[i32],
|
||||||
|
iathe: i32,
|
||||||
|
ielhe1: i32,
|
||||||
|
vturbs: &[f64],
|
||||||
|
elec: &[f64],
|
||||||
|
dens: &[f64],
|
||||||
|
wmm: &[f64],
|
||||||
|
ytot: &[f64],
|
||||||
|
abndd: &[Vec<f64>],
|
||||||
|
popul: &[Vec<f64>],
|
||||||
|
abund: &[Vec<f64>],
|
||||||
|
mtrans: usize,
|
||||||
|
) -> (f64, f64) {
|
||||||
|
// 获取跃迁参数
|
||||||
|
let itr_idx = (itr - 1) as usize;
|
||||||
|
let j = iup[itr_idx] as usize;
|
||||||
|
let iat = iatm[j - 1] as usize;
|
||||||
|
let fr = fr0[itr_idx];
|
||||||
|
let ie = iel[j - 1] as usize;
|
||||||
|
|
||||||
|
// 温度相关的热运动项
|
||||||
|
let am = BOL2 / amass[iat - 1] * t;
|
||||||
|
let mut agam = 0.0;
|
||||||
|
|
||||||
|
// Doppler 宽度
|
||||||
|
let dop = fr * CIN * (am + vturbs[id - 1] * vturbs[id - 1]).sqrt();
|
||||||
|
|
||||||
|
// 阻尼参数 - 仅对 IPROF = 1 计算
|
||||||
|
if iprof[itr_idx].abs() != 1 {
|
||||||
|
return (dop, agam);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ilow_idx = ilow[itr_idx] as usize - 1;
|
||||||
|
let ip = itra[(j - 1) + mtrans * ilow_idx] as usize;
|
||||||
|
let ane = elec[id - 1];
|
||||||
|
|
||||||
|
// 自然(辐射)致宽
|
||||||
|
if gamar[ip - 1] > 0.0 {
|
||||||
|
agam = gamar[ip - 1];
|
||||||
|
} else if gamar[ip - 1] == 0.0 {
|
||||||
|
agam = 2.47342e-22 * fr * fr;
|
||||||
|
} else {
|
||||||
|
// 非标准表达式 - 用于总阻尼参数,不仅是辐射阻尼
|
||||||
|
agam = gamsp(itr, t, ane);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stark 致宽
|
||||||
|
let z = iz[ie - 1] as f64;
|
||||||
|
let anff = z * z * EH / enion[j - 1];
|
||||||
|
|
||||||
|
if stark1[ip - 1] == 0.0 {
|
||||||
|
agam += 1e-8 * anff.powf(2.5) * ane;
|
||||||
|
} else if stark1[ip - 1] > 0.0 {
|
||||||
|
agam += ane * (stark1[ip - 1] * t.powf(stark2[ip - 1]) + stark3[ip - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Van der Waals 致宽
|
||||||
|
let ah = if ielh > 0 {
|
||||||
|
popul[nfirst[ielh as usize - 1] as usize - 1][id - 1]
|
||||||
|
} else {
|
||||||
|
dens[id - 1] / wmm[id - 1] / ytot[id - 1]
|
||||||
|
};
|
||||||
|
|
||||||
|
let ahe = if ielhe1 != 0 {
|
||||||
|
ah * abund[iathe as usize - 1][id - 1]
|
||||||
|
} else {
|
||||||
|
ah * abndd[1][id - 1]
|
||||||
|
};
|
||||||
|
|
||||||
|
let vdwc = (t * 1e-4).powf(0.3) * (ah + 0.42 * ahe);
|
||||||
|
|
||||||
|
if vdwh[ip - 1] >= 0.0 {
|
||||||
|
let r2 = if iat < 21 {
|
||||||
|
R02 * (anff / z).powi(2)
|
||||||
|
} else if iat < 45 {
|
||||||
|
(R12 - iat as f64) / z
|
||||||
|
} else {
|
||||||
|
0.5
|
||||||
|
};
|
||||||
|
let gw0 = vdwc * VW0 * r2.powf(OP4);
|
||||||
|
agam += gw0;
|
||||||
|
} else {
|
||||||
|
let gw0 = vdwc * (2.3025851_f64 * vdwh[ip - 1]).exp();
|
||||||
|
agam += gw0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 总阻尼参数(以 Doppler 宽度为单位)
|
||||||
|
agam /= dop * 12.566370614;
|
||||||
|
|
||||||
|
(dop, agam)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const MDEPTH: usize = 100;
|
||||||
|
const MLEVEL: usize = 100;
|
||||||
|
const MTRANS: usize = 200;
|
||||||
|
const MATOM: usize = 30;
|
||||||
|
const MABUND: usize = 10;
|
||||||
|
|
||||||
|
fn create_test_arrays() -> (
|
||||||
|
Vec<i32>, Vec<i32>, Vec<f64>, Vec<i32>, Vec<f64>, Vec<i32>,
|
||||||
|
Vec<i32>, Vec<i32>, Vec<f64>, Vec<i32>, Vec<f64>, Vec<f64>,
|
||||||
|
Vec<f64>, Vec<f64>, Vec<f64>, Vec<f64>, Vec<f64>, Vec<f64>,
|
||||||
|
Vec<f64>, Vec<f64>, Vec<Vec<f64>>, Vec<Vec<f64>>, Vec<Vec<f64>>,
|
||||||
|
) {
|
||||||
|
let mut iup = vec![0i32; MTRANS];
|
||||||
|
let mut iatm = vec![0i32; MLEVEL];
|
||||||
|
let mut fr0 = vec![0.0f64; MTRANS];
|
||||||
|
let mut iel = vec![0i32; MLEVEL];
|
||||||
|
let mut amass = vec![0.0f64; MATOM];
|
||||||
|
let mut iprof = vec![0i32; MTRANS];
|
||||||
|
let mut ilow = vec![0i32; MTRANS];
|
||||||
|
let mut gamar = vec![0.0f64; MTRANS];
|
||||||
|
let mut iz = vec![0i32; MLEVEL];
|
||||||
|
let mut enion = vec![0.0f64; MLEVEL];
|
||||||
|
let mut stark1 = vec![0.0f64; MTRANS];
|
||||||
|
let mut stark2 = vec![0.0f64; MTRANS];
|
||||||
|
let mut stark3 = vec![0.0f64; MTRANS];
|
||||||
|
let mut vdwh = vec![0.0f64; MTRANS];
|
||||||
|
let vturbs = vec![2e5f64; MDEPTH];
|
||||||
|
let elec = vec![1e13f64; MDEPTH];
|
||||||
|
let dens = vec![1e-7f64; MDEPTH];
|
||||||
|
let wmm = vec![1.0f64; MDEPTH];
|
||||||
|
let ytot = vec![1.0f64; MDEPTH];
|
||||||
|
let abndd = vec![vec![0.1f64; MDEPTH]; MABUND];
|
||||||
|
let popul = vec![vec![1e10f64; MDEPTH]; MLEVEL];
|
||||||
|
let abund = vec![vec![0.1f64; MDEPTH]; MATOM];
|
||||||
|
|
||||||
|
// 设置跃迁 1 的参数
|
||||||
|
iup[0] = 2; // 上能级 2
|
||||||
|
iatm[1] = 1; // 原子 1 (氢)
|
||||||
|
fr0[0] = 2.466e15; // Lyman alpha 频率
|
||||||
|
iel[1] = 1; // 元素 1
|
||||||
|
amass[0] = 1.0; // 氢原子质量
|
||||||
|
iprof[0] = 1; // Voigt 轮廓
|
||||||
|
ilow[0] = 1; // 下能级
|
||||||
|
gamar[0] = 0.0; // 经典自然阻尼
|
||||||
|
iz[0] = 1; // 电离度
|
||||||
|
enion[1] = 13.6 * EH; // 氢电离能
|
||||||
|
stark1[0] = 0.0; // Stark 忽略
|
||||||
|
vdwh[0] = 0.0; // Van der Waals 忽略
|
||||||
|
|
||||||
|
// 设置 ITRA 矩阵
|
||||||
|
let mut itra = vec![0i32; MTRANS * MLEVEL];
|
||||||
|
itra[1 + 0 * MTRANS] = 1; // ITRA(2,1) = 1
|
||||||
|
|
||||||
|
(
|
||||||
|
iup, iatm, fr0, iel, amass, iprof,
|
||||||
|
itra, ilow, gamar, iz, enion, stark1,
|
||||||
|
stark2, stark3, vdwh, vturbs, elec, dens, wmm, ytot,
|
||||||
|
abndd, popul, abund,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dopgam_basic() {
|
||||||
|
let (
|
||||||
|
iup, iatm, fr0, iel, amass, iprof,
|
||||||
|
itra, ilow, gamar, iz, enion, stark1,
|
||||||
|
stark2, stark3, vdwh, vturbs, elec, dens, wmm, ytot,
|
||||||
|
abndd, popul, abund,
|
||||||
|
) = create_test_arrays();
|
||||||
|
|
||||||
|
let (dop, agam) = dopgam(
|
||||||
|
1, 1, 10000.0,
|
||||||
|
&iup, &iatm, &fr0, &iel, &amass, &iprof,
|
||||||
|
&itra, &ilow, &gamar, &iz, &enion, &stark1,
|
||||||
|
&stark2, &stark3, &vdwh,
|
||||||
|
0, &vec![1], 0, 0, // ielh, nfirst, iathe, ielhe1
|
||||||
|
&vturbs, &elec, &dens, &wmm, &ytot,
|
||||||
|
&abndd, &popul, &abund,
|
||||||
|
MTRANS,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Doppler 宽度应该是正值
|
||||||
|
assert!(dop > 0.0);
|
||||||
|
// 阻尼参数应该非负
|
||||||
|
assert!(agam >= 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dopgam_no_voigt() {
|
||||||
|
let (
|
||||||
|
mut iup, iatm, mut fr0, iel, amass, mut iprof,
|
||||||
|
itra, ilow, gamar, iz, enion, stark1,
|
||||||
|
stark2, stark3, vdwh, vturbs, elec, dens, wmm, ytot,
|
||||||
|
abndd, popul, abund,
|
||||||
|
) = create_test_arrays();
|
||||||
|
|
||||||
|
iprof[0] = 0; // 非 Voigt 轮廓
|
||||||
|
|
||||||
|
let (dop, agam) = dopgam(
|
||||||
|
1, 1, 10000.0,
|
||||||
|
&iup, &iatm, &fr0, &iel, &amass, &iprof,
|
||||||
|
&itra, &ilow, &gamar, &iz, &enion, &stark1,
|
||||||
|
&stark2, &stark3, &vdwh,
|
||||||
|
0, &vec![1], 0, 0,
|
||||||
|
&vturbs, &elec, &dens, &wmm, &ytot,
|
||||||
|
&abndd, &popul, &abund,
|
||||||
|
MTRANS,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Doppler 宽度应该是正值
|
||||||
|
assert!(dop > 0.0);
|
||||||
|
// 阻尼参数应该为 0(非 Voigt 轮廓)
|
||||||
|
assert!((agam - 0.0).abs() < 1e-30);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dopgam_classical_damping() {
|
||||||
|
let (
|
||||||
|
iup, iatm, fr0, iel, amass, iprof,
|
||||||
|
itra, ilow, gamar, iz, enion, stark1,
|
||||||
|
stark2, stark3, vdwh, vturbs, elec, dens, wmm, ytot,
|
||||||
|
abndd, popul, abund,
|
||||||
|
) = create_test_arrays();
|
||||||
|
|
||||||
|
let (dop, agam) = dopgam(
|
||||||
|
1, 1, 10000.0,
|
||||||
|
&iup, &iatm, &fr0, &iel, &amass, &iprof,
|
||||||
|
&itra, &ilow, &gamar, &iz, &enion, &stark1,
|
||||||
|
&stark2, &stark3, &vdwh,
|
||||||
|
0, &vec![1], 0, 0,
|
||||||
|
&vturbs, &elec, &dens, &wmm, &ytot,
|
||||||
|
&abndd, &popul, &abund,
|
||||||
|
MTRANS,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 使用经典自然阻尼,agam 应该有值
|
||||||
|
assert!(dop > 0.0);
|
||||||
|
// 经典阻尼 = 2.47342e-22 * fr^2
|
||||||
|
let classical_gam = 2.47342e-22 * fr0[0] * fr0[0];
|
||||||
|
let expected_agam = classical_gam / dop / 12.566370614;
|
||||||
|
// 由于 Stark 致宽也有贡献,所以 agam 应该 >= expected_agam
|
||||||
|
assert!(agam >= expected_agam * 0.9);
|
||||||
|
}
|
||||||
|
}
|
||||||
228
src/math/dwnfr.rs
Normal file
228
src/math/dwnfr.rs
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
//! 溶解分数计算 (所有频率)。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `dwnfr.f`
|
||||||
|
|
||||||
|
use crate::state::constants::UN;
|
||||||
|
use crate::state::config::InpPar;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// DWNFR - 溶解分数 (所有频率)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 计算所有频率的溶解分数。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `mode` - 模式: 0 表示 DW=1 (无下降), >0 表示计算
|
||||||
|
/// - `n` - 频率数量
|
||||||
|
/// - `fre` - 阈值频率
|
||||||
|
/// - `a` - acor 参数 (从 dwnfr0 计算得到)
|
||||||
|
/// - `ane` - 电子密度
|
||||||
|
/// - `z` - 离子电荷
|
||||||
|
/// - `fr` - 频率数组
|
||||||
|
/// - `inppar` - 输入参数 (bergfc)
|
||||||
|
/// - `dw` - 输出溶解分数数组
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// SUBROUTINE DWNFR(MODE,N,FRE,A,ANE,Z,FR,DW)
|
||||||
|
/// INCLUDE 'IMPLIC.FOR'
|
||||||
|
/// INCLUDE 'BASICS.FOR'
|
||||||
|
/// INCLUDE 'MODELQ.FOR'
|
||||||
|
/// parameter (p1=0.1402,p2=0.1285,p3=un,p4=3.15,p5=4.)
|
||||||
|
/// parameter (tkn=3.01,ckn=5.33333333,cb0=8.59d14,f23=-2./3.)
|
||||||
|
/// PARAMETER (FRH=3.28805D15,SQFRH=5.734152D7)
|
||||||
|
/// DIMENSION FR(N),DW(N)
|
||||||
|
///
|
||||||
|
/// cb=cb0*berfc ! 注意: 原文是 berfc,应为 bergfc
|
||||||
|
/// IF(MODE.EQ.0) THEN
|
||||||
|
/// DO IJ=1,N
|
||||||
|
/// DW(IJ)=UN
|
||||||
|
/// END DO
|
||||||
|
/// ELSE
|
||||||
|
/// DO IJ=1,N
|
||||||
|
/// IF(FR(IJ).LT.FRE) THEN
|
||||||
|
/// XN=SQFRH*Z/SQRT(FRE-FR(IJ))
|
||||||
|
/// if(xn.le.tkn) then
|
||||||
|
/// xkn=un
|
||||||
|
/// else
|
||||||
|
/// xn1=un/(xn+un)
|
||||||
|
/// xkn=ckn*xn*xn1*xn1
|
||||||
|
/// end if
|
||||||
|
/// beta=cb*z*z*z*xkn/(xn*xn*xn*xn)*exp(f23*log(ane))
|
||||||
|
/// x=exp(p4*log(un+p3*a))
|
||||||
|
/// c1=p1*(x+p5*(z-un)*a*a*a)
|
||||||
|
/// c2=p2*x
|
||||||
|
/// f=(c1*beta*beta*beta)/(un+c2*beta*sqrt(beta))
|
||||||
|
/// DW(IJ)=UN-f/(un+f)
|
||||||
|
/// ELSE
|
||||||
|
/// DW(IJ)=UN
|
||||||
|
/// END IF
|
||||||
|
/// END DO
|
||||||
|
/// END IF
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
pub fn dwnfr(
|
||||||
|
mode: i32,
|
||||||
|
n: usize,
|
||||||
|
fre: f64,
|
||||||
|
a: f64,
|
||||||
|
ane: f64,
|
||||||
|
z: f64,
|
||||||
|
fr: &[f64],
|
||||||
|
inppar: &InpPar,
|
||||||
|
dw: &mut [f64],
|
||||||
|
) {
|
||||||
|
const P1: f64 = 0.1402;
|
||||||
|
const P2: f64 = 0.1285;
|
||||||
|
const P3: f64 = UN;
|
||||||
|
const P4: f64 = 3.15;
|
||||||
|
const P5: f64 = 4.0;
|
||||||
|
const TKN: f64 = 3.01;
|
||||||
|
const CKN: f64 = 5.33333333;
|
||||||
|
const CB0: f64 = 8.59e14;
|
||||||
|
const F23: f64 = -2.0 / 3.0;
|
||||||
|
const SQFRH: f64 = 5.734152e7;
|
||||||
|
|
||||||
|
// 注意: Fortran 原文是 berfc,但应该是 bergfc
|
||||||
|
let cb = CB0 * inppar.bergfc;
|
||||||
|
|
||||||
|
if mode == 0 {
|
||||||
|
// MODE=0: DW=1 (无下降)
|
||||||
|
for ij in 0..n {
|
||||||
|
dw[ij] = UN;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// MODE>0: 计算溶解分数
|
||||||
|
let z3 = z * z * z;
|
||||||
|
let a3 = a * a * a;
|
||||||
|
let elec23 = ane.powf(F23);
|
||||||
|
let x = (UN + P3 * a).powf(P4);
|
||||||
|
let c1 = P1 * (x + P5 * (z - UN) * a3);
|
||||||
|
let c2 = P2 * x;
|
||||||
|
|
||||||
|
for ij in 0..n {
|
||||||
|
if fr[ij] < fre {
|
||||||
|
let xn = SQFRH * z / (fre - fr[ij]).sqrt();
|
||||||
|
|
||||||
|
let xkn = if xn <= TKN {
|
||||||
|
UN
|
||||||
|
} else {
|
||||||
|
let xn1 = UN / (xn + UN);
|
||||||
|
CKN * xn * xn1 * xn1
|
||||||
|
};
|
||||||
|
|
||||||
|
let beta = cb * z3 * xkn / xn.powi(4) * elec23;
|
||||||
|
let beta3 = beta * beta * beta;
|
||||||
|
let beta32 = beta3.sqrt();
|
||||||
|
let f = (c1 * beta3) / (UN + c2 * beta * beta32);
|
||||||
|
|
||||||
|
dw[ij] = UN - f / (UN + f);
|
||||||
|
} else {
|
||||||
|
dw[ij] = UN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dwnfr_mode_zero() {
|
||||||
|
let inppar = InpPar::default();
|
||||||
|
let fr = [0.5, 0.6, 0.7, 0.8, 0.9];
|
||||||
|
let mut dw = [0.0; 5];
|
||||||
|
|
||||||
|
dwnfr(0, 5, 1.0, 0.1, 1e12, 2.0, &fr, &inppar, &mut dw);
|
||||||
|
|
||||||
|
// MODE=0 时,所有 DW 应该是 1.0
|
||||||
|
for ij in 0..5 {
|
||||||
|
assert!((dw[ij] - 1.0).abs() < 1e-10, "dw[{}] = {}, expected 1.0", ij, dw[ij]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dwnfr_mode_nonzero_above_threshold() {
|
||||||
|
let inppar = InpPar::default();
|
||||||
|
let fr = [1.1, 1.2, 1.3]; // 都大于 fre=1.0
|
||||||
|
let mut dw = [0.0; 3];
|
||||||
|
|
||||||
|
dwnfr(1, 3, 1.0, 0.1, 1e12, 2.0, &fr, &inppar, &mut dw);
|
||||||
|
|
||||||
|
// fr >= fre 时,DW 应该是 1.0
|
||||||
|
for ij in 0..3 {
|
||||||
|
assert!((dw[ij] - 1.0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dwnfr_mode_nonzero_below_threshold() {
|
||||||
|
let inppar = InpPar::default();
|
||||||
|
let fr = [0.5, 0.6, 0.7, 0.8, 0.9]; // 都小于 fre=1.0
|
||||||
|
let mut dw = [0.0; 5];
|
||||||
|
|
||||||
|
dwnfr(1, 5, 1.0, 0.1, 1e12, 2.0, &fr, &inppar, &mut dw);
|
||||||
|
|
||||||
|
// DW 应该在 (0, 1] 范围内
|
||||||
|
for ij in 0..5 {
|
||||||
|
assert!(dw[ij] <= 1.0 && dw[ij] > 0.0, "dw[{}] = {}", ij, dw[ij]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dwnfr_mixed_frequencies() {
|
||||||
|
let inppar = InpPar::default();
|
||||||
|
let fr = [0.5, 1.0, 1.5]; // 混合: <, =, >
|
||||||
|
let mut dw = [0.0; 3];
|
||||||
|
|
||||||
|
dwnfr(1, 3, 1.0, 0.1, 1e12, 2.0, &fr, &inppar, &mut dw);
|
||||||
|
|
||||||
|
// fr[0] < fre: dw < 1 (可能)
|
||||||
|
// fr[1] == fre: dw == 1
|
||||||
|
// fr[2] > fre: dw == 1
|
||||||
|
assert!(dw[0] <= 1.0);
|
||||||
|
assert!((dw[1] - 1.0).abs() < 1e-10);
|
||||||
|
assert!((dw[2] - 1.0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dwnfr_consistency_with_dwnfr1() {
|
||||||
|
use super::super::dwnfr1;
|
||||||
|
use crate::state::model::DwnPar;
|
||||||
|
|
||||||
|
let inppar = InpPar::default();
|
||||||
|
|
||||||
|
// 设置 dwnpar 以匹配 dwnfr 的参数
|
||||||
|
let mut dwnpar = DwnPar::default();
|
||||||
|
let id = 5;
|
||||||
|
let izz = 1; // z = 2
|
||||||
|
let z = (izz + 1) as f64;
|
||||||
|
let a: f64 = 0.1;
|
||||||
|
let ane: f64 = 1e12;
|
||||||
|
|
||||||
|
// 计算 dwnfr0 会设置的值
|
||||||
|
dwnpar.elec23[id] = ane.powf(-2.0 / 3.0);
|
||||||
|
dwnpar.z3[izz] = z * z * z;
|
||||||
|
let x = (1.0 + a).powf(3.15);
|
||||||
|
dwnpar.dwc2[id] = 0.1285 * x;
|
||||||
|
let a3 = a * a * a;
|
||||||
|
dwnpar.dwc1[izz][id] = 0.1402 * (x + 4.0 * (z - 1.0) * a3);
|
||||||
|
|
||||||
|
let fr = [0.5, 0.8];
|
||||||
|
let mut dw = [0.0; 2];
|
||||||
|
let fre = 1.0;
|
||||||
|
|
||||||
|
dwnfr(1, 2, fre, a, ane, z, &fr, &inppar, &mut dw);
|
||||||
|
|
||||||
|
// 与 dwnfr1 比较
|
||||||
|
let dw1_0 = dwnfr1(fr[0], fre, id, izz, &inppar, &dwnpar);
|
||||||
|
let dw1_1 = dwnfr1(fr[1], fre, id, izz, &inppar, &dwnpar);
|
||||||
|
|
||||||
|
// 由于参数设置可能不完全一致,只检查大致趋势
|
||||||
|
assert!((dw[0] - dw1_0).abs() < 1e-6, "dw[0]={}, dwnfr1={}", dw[0], dw1_0);
|
||||||
|
assert!((dw[1] - dw1_1).abs() < 1e-6, "dw[1]={}, dwnfr1={}", dw[1], dw1_1);
|
||||||
|
}
|
||||||
|
}
|
||||||
129
src/math/dwnfr0.rs
Normal file
129
src/math/dwnfr0.rs
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
//! 溶解分数辅助量计算。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `dwnfr0.f`
|
||||||
|
|
||||||
|
use crate::state::constants::{MDEPTH, MZZ, UN};
|
||||||
|
use crate::state::model::{DwnPar, ModPar};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// DWNFR0 - 溶解分数辅助量
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 计算溶解分数的辅助量。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `id` - 深度索引 (0-indexed)
|
||||||
|
/// - `modpar` - 模型参数 (elec, sqt1)
|
||||||
|
/// - `dwnpar` - 溶解分数参数 (会被修改)
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// SUBROUTINE DWNFR0(ID)
|
||||||
|
/// INCLUDE 'IMPLIC.FOR'
|
||||||
|
/// INCLUDE 'BASICS.FOR'
|
||||||
|
/// INCLUDE 'MODELQ.FOR'
|
||||||
|
/// PARAMETER (SIXTH=UN/6.,CCOR=0.09)
|
||||||
|
/// parameter (p1=0.1402,p2=0.1285,p3=un,p4=3.15,p5=4.)
|
||||||
|
/// parameter (f23=-2./3.)
|
||||||
|
///
|
||||||
|
/// ANE=ELEC(ID)
|
||||||
|
/// ELEC23(ID)=EXP(F23*LOG(ANE))
|
||||||
|
/// ANES=EXP(SIXTH*LOG(ANE))
|
||||||
|
/// ACOR(ID)=CCOR*ANES/SQT1(ID)
|
||||||
|
/// X=EXP(P4*LOG(UN+P3*ACOR(ID)))
|
||||||
|
/// DWC2(ID)=P2*X
|
||||||
|
/// A3=ACOR(ID)*ACOR(ID)*ACOR(ID)
|
||||||
|
/// DO IZZ=1,MZZ
|
||||||
|
/// Z3(IZZ)=IZZ*IZZ*IZZ
|
||||||
|
/// DWC1(IZZ,ID)=P1*(X+P5*(IZZ-UN)*A3)
|
||||||
|
/// END DO
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
pub fn dwnfr0(id: usize, modpar: &ModPar, dwnpar: &mut DwnPar) {
|
||||||
|
const SIXTH: f64 = UN / 6.0;
|
||||||
|
const CCOR: f64 = 0.09;
|
||||||
|
const P1: f64 = 0.1402;
|
||||||
|
const P2: f64 = 0.1285;
|
||||||
|
const P3: f64 = UN;
|
||||||
|
const P4: f64 = 3.15;
|
||||||
|
const P5: f64 = 4.0;
|
||||||
|
const F23: f64 = -2.0 / 3.0;
|
||||||
|
|
||||||
|
let ane = modpar.elec[id];
|
||||||
|
dwnpar.elec23[id] = (ane).powf(F23);
|
||||||
|
let anes = (ane).powf(SIXTH);
|
||||||
|
dwnpar.acor[id] = CCOR * anes / modpar.sqt1[id];
|
||||||
|
let x = (UN + P3 * dwnpar.acor[id]).powf(P4);
|
||||||
|
dwnpar.dwc2[id] = P2 * x;
|
||||||
|
let a3 = dwnpar.acor[id] * dwnpar.acor[id] * dwnpar.acor[id];
|
||||||
|
|
||||||
|
for izz in 0..MZZ {
|
||||||
|
let z = (izz + 1) as f64; // Fortran: IZZ = 1..MZZ → Rust: izz = 0..MZZ-1
|
||||||
|
dwnpar.z3[izz] = z * z * z;
|
||||||
|
dwnpar.dwc1[izz][id] = P1 * (x + P5 * (z - UN) * a3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn create_test_model() -> (ModPar, DwnPar) {
|
||||||
|
let mut modpar = ModPar::default();
|
||||||
|
let dwnpar = DwnPar::default();
|
||||||
|
|
||||||
|
// 设置测试值
|
||||||
|
modpar.elec[5] = 1e12;
|
||||||
|
modpar.sqt1[5] = 100.0; // sqrt(10000 K)
|
||||||
|
|
||||||
|
(modpar, dwnpar)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dwnfr0_basic() {
|
||||||
|
let (modpar, mut dwnpar) = create_test_model();
|
||||||
|
let id = 5;
|
||||||
|
|
||||||
|
dwnfr0(id, &modpar, &mut dwnpar);
|
||||||
|
|
||||||
|
// 检查 elec23: (1e12)^(-2/3)
|
||||||
|
let expected_elec23 = 1e-8;
|
||||||
|
assert!(
|
||||||
|
(dwnpar.elec23[id] - expected_elec23).abs() / expected_elec23 < 1e-6,
|
||||||
|
"elec23 = {}, expected {}",
|
||||||
|
dwnpar.elec23[id],
|
||||||
|
expected_elec23
|
||||||
|
);
|
||||||
|
|
||||||
|
// 检查 dwc2 是正数
|
||||||
|
assert!(dwnpar.dwc2[id] > 0.0, "dwc2 should be positive");
|
||||||
|
|
||||||
|
// 检查 dwc1 是正数
|
||||||
|
for izz in 0..MZZ {
|
||||||
|
assert!(dwnpar.dwc1[izz][id] > 0.0, "dwc1[{}][{}] should be positive", izz, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dwnfr0_z3() {
|
||||||
|
let (modpar, mut dwnpar) = create_test_model();
|
||||||
|
let id = 5;
|
||||||
|
|
||||||
|
dwnfr0(id, &modpar, &mut dwnpar);
|
||||||
|
|
||||||
|
// 检查 z3
|
||||||
|
for izz in 0..MZZ {
|
||||||
|
let z = (izz + 1) as f64;
|
||||||
|
let expected = z * z * z;
|
||||||
|
assert!(
|
||||||
|
(dwnpar.z3[izz] - expected).abs() < 1e-10,
|
||||||
|
"z3[{}] = {}, expected {}",
|
||||||
|
izz,
|
||||||
|
dwnpar.z3[izz],
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
169
src/math/dwnfr1.rs
Normal file
169
src/math/dwnfr1.rs
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
//! 给定频率的溶解分数计算。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `dwnfr1.f`
|
||||||
|
|
||||||
|
use crate::state::constants::UN;
|
||||||
|
use crate::state::config::InpPar;
|
||||||
|
use crate::state::model::DwnPar;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// DWNFR1 - 溶解分数 (单频率)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 计算给定频率的溶解分数。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `fr` - 频率
|
||||||
|
/// - `fr0` - 参考频率 (阈值)
|
||||||
|
/// - `id` - 深度索引 (0-indexed)
|
||||||
|
/// - `izz` - 离子电荷索引 (0-indexed,对应 Fortran 1..MZZ)
|
||||||
|
/// - `inppar` - 输入参数 (bergfc)
|
||||||
|
/// - `dwnpar` - 溶解分数参数 (z3, elec23, dwc1, dwc2)
|
||||||
|
///
|
||||||
|
/// # 返回
|
||||||
|
///
|
||||||
|
/// - `dw1` - 溶解分数
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// SUBROUTINE DWNFR1(FR,FR0,ID,IZZ,DW1)
|
||||||
|
/// INCLUDE 'IMPLIC.FOR'
|
||||||
|
/// INCLUDE 'BASICS.FOR'
|
||||||
|
/// INCLUDE 'MODELQ.FOR'
|
||||||
|
/// PARAMETER (TKN=3.01,CKN=5.33333333,CB0=8.59d14)
|
||||||
|
/// PARAMETER (SQFRH=5.734152D7)
|
||||||
|
///
|
||||||
|
/// cb=cb0*bergfc
|
||||||
|
/// IF(FR.LT.FR0) THEN
|
||||||
|
/// XN=SQFRH*IZZ/SQRT(FR0-FR)
|
||||||
|
/// if(xn.le.tkn) then
|
||||||
|
/// xkn=un
|
||||||
|
/// else
|
||||||
|
/// xn1=un/(xn+un)
|
||||||
|
/// xkn=ckn*xn*xn1*xn1
|
||||||
|
/// end if
|
||||||
|
/// BETA=CB*Z3(IZZ)*XKN/(XN*XN*XN*XN)*ELEC23(ID)
|
||||||
|
/// BETA3=BETA*BETA*BETA
|
||||||
|
/// BETA32=SQRT(BETA3)
|
||||||
|
/// F=(DWC1(IZZ,ID)*BETA3)/(UN+DWC2(ID)*BETA32)
|
||||||
|
/// DW1=UN-F/(UN+F)
|
||||||
|
/// ELSE
|
||||||
|
/// DW1=UN
|
||||||
|
/// END IF
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
pub fn dwnfr1(
|
||||||
|
fr: f64,
|
||||||
|
fr0: f64,
|
||||||
|
id: usize,
|
||||||
|
izz: usize,
|
||||||
|
inppar: &InpPar,
|
||||||
|
dwnpar: &DwnPar,
|
||||||
|
) -> f64 {
|
||||||
|
const TKN: f64 = 3.01;
|
||||||
|
const CKN: f64 = 5.33333333;
|
||||||
|
const CB0: f64 = 8.59e14;
|
||||||
|
const SQFRH: f64 = 5.734152e7;
|
||||||
|
|
||||||
|
let cb = CB0 * inppar.bergfc;
|
||||||
|
|
||||||
|
if fr < fr0 {
|
||||||
|
// Fortran: IZZ 是 1-indexed,这里 izz 是 0-indexed
|
||||||
|
// 但在 Fortran 中 Z3(IZZ) = IZZ^3,所以需要用 izz+1
|
||||||
|
let xn = SQFRH * ((izz + 1) as f64) / (fr0 - fr).sqrt();
|
||||||
|
|
||||||
|
let xkn = if xn <= TKN {
|
||||||
|
UN
|
||||||
|
} else {
|
||||||
|
let xn1 = UN / (xn + UN);
|
||||||
|
CKN * xn * xn1 * xn1
|
||||||
|
};
|
||||||
|
|
||||||
|
let beta = cb * dwnpar.z3[izz] * xkn / xn.powi(4) * dwnpar.elec23[id];
|
||||||
|
let beta3 = beta * beta * beta;
|
||||||
|
let beta32 = beta3.sqrt();
|
||||||
|
let f = (dwnpar.dwc1[izz][id] * beta3) / (UN + dwnpar.dwc2[id] * beta32);
|
||||||
|
|
||||||
|
UN - f / (UN + f)
|
||||||
|
} else {
|
||||||
|
UN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn create_test_params() -> (InpPar, DwnPar) {
|
||||||
|
let inppar = InpPar::default();
|
||||||
|
let mut dwnpar = DwnPar::default();
|
||||||
|
|
||||||
|
// 设置测试值
|
||||||
|
dwnpar.elec23[5] = 1e-8;
|
||||||
|
dwnpar.dwc2[5] = 0.2;
|
||||||
|
for izz in 0..5 {
|
||||||
|
dwnpar.z3[izz] = ((izz + 1) as f64).powi(3);
|
||||||
|
dwnpar.dwc1[izz][5] = 0.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
(inppar, dwnpar)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dwnfr1_above_threshold() {
|
||||||
|
let (inppar, dwnpar) = create_test_params();
|
||||||
|
|
||||||
|
// fr >= fr0 时,返回 1.0
|
||||||
|
let dw1 = dwnfr1(2.0, 1.0, 5, 1, &inppar, &dwnpar);
|
||||||
|
assert!((dw1 - 1.0).abs() < 1e-10, "dw1 should be 1.0 when fr >= fr0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dwnfr1_below_threshold() {
|
||||||
|
let (inppar, dwnpar) = create_test_params();
|
||||||
|
|
||||||
|
// fr < fr0 时,dw1 应该在 (0, 1] 范围内
|
||||||
|
let dw1 = dwnfr1(0.5, 1.0, 5, 1, &inppar, &dwnpar);
|
||||||
|
// dw1 = 1 - f/(1+f) = 1/(1+f),当 f 很小时接近 1
|
||||||
|
assert!(dw1 <= 1.0 && dw1 > 0.0, "dw1 should be between 0 and 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dwnfr1_at_threshold() {
|
||||||
|
let (inppar, dwnpar) = create_test_params();
|
||||||
|
|
||||||
|
// fr == fr0 时,返回 1.0
|
||||||
|
let dw1 = dwnfr1(1.0, 1.0, 5, 1, &inppar, &dwnpar);
|
||||||
|
assert!((dw1 - 1.0).abs() < 1e-10, "dw1 should be 1.0 when fr == fr0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dwnfr1_small_xn() {
|
||||||
|
// 测试 xn < TKN 的情况
|
||||||
|
// 需要 fr0 - fr 足够大,或者 z 足够小
|
||||||
|
// xn = SQFRH * (izz+1) / sqrt(fr0 - fr)
|
||||||
|
// 如果 xn <= TKN = 3.01,需要 sqrt(fr0 - fr) >= SQFRH * 2 / 3.01
|
||||||
|
// 即 fr0 - fr >= (5.7e7 * 2 / 3.01)^2 ≈ 1.4e15
|
||||||
|
// 这是不可能的,所以 xn <= TKN 的条件在实际中几乎不会触发
|
||||||
|
// 这个分支是理论性的
|
||||||
|
|
||||||
|
let (inppar, dwnpar) = create_test_params();
|
||||||
|
let dw1 = dwnfr1(0.5, 1.0, 5, 1, &inppar, &dwnpar);
|
||||||
|
assert!(dw1 <= 1.0, "dw1 should be <= 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dwnfr1_boundary_values() {
|
||||||
|
let (inppar, dwnpar) = create_test_params();
|
||||||
|
|
||||||
|
// 测试边界:fr 刚好小于 fr0
|
||||||
|
let dw1_just_below = dwnfr1(0.9999, 1.0, 5, 1, &inppar, &dwnpar);
|
||||||
|
assert!(dw1_just_below <= 1.0);
|
||||||
|
|
||||||
|
// 测试边界:fr 刚好大于 fr0
|
||||||
|
let dw1_just_above = dwnfr1(1.0001, 1.0, 5, 1, &inppar, &dwnpar);
|
||||||
|
assert!((dw1_just_above - 1.0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
207
src/math/emat.rs
Normal file
207
src/math/emat.rs
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
//! 带状矩阵 E 元素填充。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `emat.f`
|
||||||
|
//!
|
||||||
|
//! 为 SOLVE 辅助程序,填充 sub-sub-diagonal 带状矩阵 E。
|
||||||
|
|
||||||
|
use crate::state::constants::PCK;
|
||||||
|
use crate::state::alipar::FixAlp;
|
||||||
|
use crate::state::arrays::MainArrays;
|
||||||
|
use crate::state::config::MatKey;
|
||||||
|
use crate::state::model::RePart;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// EMAT - 带状矩阵 E 元素
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 填充带状矩阵 E 的 sub-sub-diagonal 元素。
|
||||||
|
///
|
||||||
|
/// 这是 SOLVE 程序的辅助过程。
|
||||||
|
/// E 矩阵用于线性化方程组的带状矩阵求解。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `id` - 深度索引 (0-indexed)
|
||||||
|
/// - `nfreqe` - 线性化频率数
|
||||||
|
/// - `nlvexp` - 线性化能级数
|
||||||
|
/// - `matkey` - 材料键 (INHE, INRE, INPC, INSE)
|
||||||
|
/// - `fixalp` - ALI 固定参数 (EHET, EHEN, EHEP, ERET, EREN, EREP)
|
||||||
|
/// - `repart` - 辐射等效参数 (REDIF)
|
||||||
|
/// - `arrays` - 主数组 (E 矩阵)
|
||||||
|
/// - `ifali` - ALI 控制标志
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// SUBROUTINE EMAT(ID)
|
||||||
|
/// INCLUDE 'BASICS.FOR'
|
||||||
|
/// INCLUDE 'ATOMIC.FOR'
|
||||||
|
/// INCLUDE 'MODELQ.FOR'
|
||||||
|
/// INCLUDE 'ARRAY1.FOR'
|
||||||
|
/// INCLUDE 'ALIPAR.FOR'
|
||||||
|
///
|
||||||
|
/// IF(IFALI.LE.7) RETURN
|
||||||
|
/// IF(ID.LE.2) RETURN
|
||||||
|
/// ...
|
||||||
|
/// IF(INHE.GT.0) THEN
|
||||||
|
/// NHE=NFREQE+INHE
|
||||||
|
/// IF(INRE.GT.0) E(NHE,NFREQE+INRE)=EHET(ID)*PCK
|
||||||
|
/// ...
|
||||||
|
/// END IF
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
pub fn emat(
|
||||||
|
id: usize,
|
||||||
|
nfreqe: usize,
|
||||||
|
nlvexp: usize,
|
||||||
|
matkey: &MatKey,
|
||||||
|
fixalp: &FixAlp,
|
||||||
|
repart: &RePart,
|
||||||
|
arrays: &mut MainArrays,
|
||||||
|
ifali: i32,
|
||||||
|
) {
|
||||||
|
// ALI 标志检查
|
||||||
|
if ifali <= 7 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 深度检查 (Fortran: ID.LE.2 → Rust: id < 2,因为 0-indexed)
|
||||||
|
if id < 2 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let inse = matkey.inse as usize;
|
||||||
|
let inhe = matkey.inhe as usize;
|
||||||
|
let inre = matkey.inre as usize;
|
||||||
|
let inpc = matkey.inpc as usize;
|
||||||
|
|
||||||
|
// NSE = NFREQE + INSE - 1 (Fortran 索引) → Rust: nfreqe + inse - 1
|
||||||
|
let nse = nfreqe + inse - 1;
|
||||||
|
|
||||||
|
// He 相关
|
||||||
|
if inhe > 0 {
|
||||||
|
let nhe = nfreqe + inhe;
|
||||||
|
|
||||||
|
// E(NHE, NFREQE+INRE) = EHET(ID) * PCK
|
||||||
|
if inre > 0 {
|
||||||
|
let col = nfreqe + inre;
|
||||||
|
arrays.e[nhe][col] = fixalp.ehet[id] * PCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// E(NHE, NFREQE+INPC) = EHEN(ID) * PCK
|
||||||
|
if inpc > 0 {
|
||||||
|
let col = nfreqe + inpc;
|
||||||
|
arrays.e[nhe][col] = fixalp.ehen[id] * PCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// E(NHE, NSE+II) = EHEP(II, ID) * PCK
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
let col = nse + ii;
|
||||||
|
arrays.e[nhe][col] = fixalp.ehep[ii][id] * PCK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辐射扩散相关
|
||||||
|
if inre > 0 && repart.redif[id] > 0.0 {
|
||||||
|
let nre = nfreqe + inre;
|
||||||
|
|
||||||
|
// E(NRE, NRE) = ERET(ID) * REDIF(ID)
|
||||||
|
arrays.e[nre][nre] = fixalp.eret[id] * repart.redif[id];
|
||||||
|
|
||||||
|
// E(NRE, NFREQE+INPC) = EREN(ID) * REDIF(ID)
|
||||||
|
if inpc > 0 {
|
||||||
|
let col = nfreqe + inpc;
|
||||||
|
arrays.e[nre][col] = fixalp.eren[id] * repart.redif[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
// E(NRE, NSE+II) = EREP(II, ID) * REDIF(ID)
|
||||||
|
for ii in 0..nlvexp {
|
||||||
|
let col = nse + ii;
|
||||||
|
arrays.e[nre][col] = fixalp.erep[ii][id] * repart.redif[id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn create_test_arrays() -> MainArrays {
|
||||||
|
MainArrays::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_emat_ifali_check() {
|
||||||
|
let mut arrays = create_test_arrays();
|
||||||
|
let matkey = MatKey::default();
|
||||||
|
let fixalp = FixAlp::default();
|
||||||
|
let repart = RePart::default();
|
||||||
|
|
||||||
|
// IFALI <= 7 应该直接返回,不做任何修改
|
||||||
|
let original_e = arrays.e[0][0];
|
||||||
|
emat(5, 10, 5, &matkey, &fixalp, &repart, &mut arrays, 5);
|
||||||
|
assert_eq!(arrays.e[0][0], original_e);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_emat_depth_check() {
|
||||||
|
let mut arrays = create_test_arrays();
|
||||||
|
let matkey = MatKey::default();
|
||||||
|
let fixalp = FixAlp::default();
|
||||||
|
let repart = RePart::default();
|
||||||
|
|
||||||
|
// id < 2 应该直接返回
|
||||||
|
emat(0, 10, 5, &matkey, &fixalp, &repart, &mut arrays, 10);
|
||||||
|
emat(1, 10, 5, &matkey, &fixalp, &repart, &mut arrays, 10);
|
||||||
|
// 不应该崩溃
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_emat_with_inhe() {
|
||||||
|
let mut arrays = create_test_arrays();
|
||||||
|
|
||||||
|
let mut matkey = MatKey::default();
|
||||||
|
let mut fixalp = FixAlp::default();
|
||||||
|
|
||||||
|
matkey.inhe = 1;
|
||||||
|
matkey.inre = 1;
|
||||||
|
matkey.inpc = 1;
|
||||||
|
matkey.inse = 1;
|
||||||
|
|
||||||
|
fixalp.ehet[5] = 1e-10;
|
||||||
|
fixalp.ehen[5] = 2e-10;
|
||||||
|
fixalp.eret[5] = 3e-10;
|
||||||
|
fixalp.eren[5] = 4e-10;
|
||||||
|
// 初始化 EHEP 数组 (至少需要 nlvexp 个元素)
|
||||||
|
for ii in 0..5 {
|
||||||
|
fixalp.ehep[ii][5] = (ii + 1) as f64 * 1e-11;
|
||||||
|
}
|
||||||
|
// 初始化 EREP 数组
|
||||||
|
for ii in 0..5 {
|
||||||
|
fixalp.erep[ii][5] = (ii + 1) as f64 * 1e-12;
|
||||||
|
}
|
||||||
|
|
||||||
|
let repart = RePart::default();
|
||||||
|
|
||||||
|
emat(5, 10, 5, &matkey, &fixalp, &repart, &mut arrays, 10);
|
||||||
|
|
||||||
|
// 检查 E 矩阵被正确填充
|
||||||
|
let nhe = 10 + 1; // nfreqe + inhe = 11
|
||||||
|
let nre = 10 + 1; // nfreqe + inre = 11
|
||||||
|
let nse = 10 + 1 - 1; // nfreqe + inse - 1 = 10
|
||||||
|
|
||||||
|
// EHET: E(NHE, NFREQE+INRE) = EHET(ID) * PCK
|
||||||
|
// 但这个值会被后面的 EHEP 循环覆盖(当 ii=1 时 col=11)
|
||||||
|
// 所以检查 EHEP 的结果: E(NHE, NSE+II) = EHEP(II, ID) * PCK
|
||||||
|
// 当 ii=1 时: col = nse + 1 = 11
|
||||||
|
assert!((arrays.e[nhe][nse + 1] - 2e-11 * PCK).abs() < 1e-22);
|
||||||
|
|
||||||
|
// EHEN: E(NHE, NFREQE+INPC) = EHEN(ID) * PCK
|
||||||
|
let col_pc = 10 + 1; // nfreqe + inpc = 11
|
||||||
|
// 这个也会被 EHEP 循环覆盖,检查 EHEP[1]
|
||||||
|
assert!((arrays.e[nhe][col_pc] - 2e-11 * PCK).abs() < 1e-22);
|
||||||
|
|
||||||
|
// 检查 EHEP[0]: E(NHE, NSE+0) = EHEP(0, ID) * PCK
|
||||||
|
assert!((arrays.e[nhe][nse] - 1e-11 * PCK).abs() < 1e-22);
|
||||||
|
}
|
||||||
|
}
|
||||||
161
src/math/gridp.rs
Normal file
161
src/math/gridp.rs
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
//! 网格点评估
|
||||||
|
//!
|
||||||
|
//! 原始 Fortran: gridp.f
|
||||||
|
//! 将曲线 y=f(x) 分成 n-1 等长段,确定新网格点
|
||||||
|
|
||||||
|
use crate::state::MDEPTH;
|
||||||
|
|
||||||
|
/// 网格点评估
|
||||||
|
///
|
||||||
|
/// 将曲线 Y=f(X) 分成 N-1 个等长段,
|
||||||
|
/// 每段端点的 x 坐标定义新网格点
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
/// - `x`: 原始 x 坐标数组
|
||||||
|
/// - `y`: 原始 y 坐标数组
|
||||||
|
/// - `xnew`: 新 x 坐标数组 (输出)
|
||||||
|
/// - `ynew`: 新 y 坐标数组 (输出)
|
||||||
|
/// - `n`: 点数
|
||||||
|
///
|
||||||
|
/// # 算法
|
||||||
|
/// 1. 计算原始曲线各段长度
|
||||||
|
/// 2. 计算总长度和新段长度
|
||||||
|
/// 3. 沿曲线均匀采样新点
|
||||||
|
pub fn gridp(x: &[f64], y: &[f64], xnew: &mut [f64], ynew: &mut [f64], n: usize) {
|
||||||
|
// 工作数组:存储各段长度
|
||||||
|
let mut z = vec![0.0; MDEPTH.min(n)];
|
||||||
|
|
||||||
|
// 计算原始曲线各段长度和总长度
|
||||||
|
let mut ztot = 0.0;
|
||||||
|
for i in 1..n {
|
||||||
|
let dx = x[i] - x[i - 1];
|
||||||
|
let dy = y[i] - y[i - 1];
|
||||||
|
z[i - 1] = (dx * dx + dy * dy).sqrt();
|
||||||
|
ztot += z[i - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新段长度
|
||||||
|
let z0 = ztot / (n - 1) as f64;
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
let mut iseg: usize = 0;
|
||||||
|
let mut xlast = x[iseg];
|
||||||
|
let mut ylast = y[iseg];
|
||||||
|
let mut zrest = z[iseg];
|
||||||
|
let mut zrem = z0;
|
||||||
|
let mut ip: usize = 0;
|
||||||
|
|
||||||
|
xnew[ip] = x[0];
|
||||||
|
ynew[ip] = y[0];
|
||||||
|
|
||||||
|
// 沿曲线采样新点
|
||||||
|
loop {
|
||||||
|
if zrem < zrest {
|
||||||
|
// 在当前段内
|
||||||
|
zrest -= zrem;
|
||||||
|
xlast += zrem * (x[iseg + 1] - x[iseg]) / z[iseg];
|
||||||
|
ylast += zrem * (y[iseg + 1] - y[iseg]) / z[iseg];
|
||||||
|
ip += 1;
|
||||||
|
xnew[ip] = xlast;
|
||||||
|
ynew[ip] = ylast;
|
||||||
|
zrem = z0;
|
||||||
|
if ip >= n - 1 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 跨到下一段
|
||||||
|
zrem -= zrest;
|
||||||
|
iseg += 1;
|
||||||
|
if iseg >= n - 1 {
|
||||||
|
// 已到达最后一段
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
xlast = x[iseg];
|
||||||
|
ylast = y[iseg];
|
||||||
|
zrest = z[iseg];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最后一个点
|
||||||
|
xnew[n - 1] = x[n - 1];
|
||||||
|
ynew[n - 1] = y[n - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gridp_linear() {
|
||||||
|
// 线性函数 y = x
|
||||||
|
let n = 5;
|
||||||
|
let x = vec![0.0, 1.0, 2.0, 3.0, 4.0];
|
||||||
|
let y = vec![0.0, 1.0, 2.0, 3.0, 4.0];
|
||||||
|
let mut xnew = vec![0.0; n];
|
||||||
|
let mut ynew = vec![0.0; n];
|
||||||
|
|
||||||
|
gridp(&x, &y, &mut xnew, &mut ynew, n);
|
||||||
|
|
||||||
|
// 端点应保持不变
|
||||||
|
assert!((xnew[0] - x[0]).abs() < 1e-10);
|
||||||
|
assert!((xnew[n - 1] - x[n - 1]).abs() < 1e-10);
|
||||||
|
assert!((ynew[0] - y[0]).abs() < 1e-10);
|
||||||
|
assert!((ynew[n - 1] - y[n - 1]).abs() < 1e-10);
|
||||||
|
|
||||||
|
// 对于线性函数,新网格应与原网格相同
|
||||||
|
for i in 0..n {
|
||||||
|
assert!((xnew[i] - x[i]).abs() < 1e-10);
|
||||||
|
assert!((ynew[i] - y[i]).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gridp_curve() {
|
||||||
|
// 曲线 y = x^2
|
||||||
|
let n = 5;
|
||||||
|
let x = vec![0.0, 1.0, 2.0, 3.0, 4.0];
|
||||||
|
let y = vec![0.0, 1.0, 4.0, 9.0, 16.0];
|
||||||
|
let mut xnew = vec![0.0; n];
|
||||||
|
let mut ynew = vec![0.0; n];
|
||||||
|
|
||||||
|
gridp(&x, &y, &mut xnew, &mut ynew, n);
|
||||||
|
|
||||||
|
// 端点应保持不变
|
||||||
|
assert!((xnew[0] - 0.0).abs() < 1e-10);
|
||||||
|
assert!((xnew[n - 1] - 4.0).abs() < 1e-10);
|
||||||
|
assert!((ynew[0] - 0.0).abs() < 1e-10);
|
||||||
|
assert!((ynew[n - 1] - 16.0).abs() < 1e-10);
|
||||||
|
|
||||||
|
// 新网格应单调递增
|
||||||
|
for i in 1..n {
|
||||||
|
assert!(xnew[i] > xnew[i - 1]);
|
||||||
|
assert!(ynew[i] > ynew[i - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gridp_uniform_segment_length() {
|
||||||
|
// 验证新网格各段长度大致相等
|
||||||
|
let n = 10;
|
||||||
|
let x: Vec<f64> = (0..n).map(|i| i as f64).collect();
|
||||||
|
let y: Vec<f64> = x.iter().map(|&x| x * x).collect();
|
||||||
|
let mut xnew = vec![0.0; n];
|
||||||
|
let mut ynew = vec![0.0; n];
|
||||||
|
|
||||||
|
gridp(&x, &y, &mut xnew, &mut ynew, n);
|
||||||
|
|
||||||
|
// 计算各段长度
|
||||||
|
let mut lengths = Vec::new();
|
||||||
|
for i in 1..n {
|
||||||
|
let dx = xnew[i] - xnew[i - 1];
|
||||||
|
let dy = ynew[i] - ynew[i - 1];
|
||||||
|
lengths.push((dx * dx + dy * dy).sqrt());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证各段长度相近
|
||||||
|
let avg_len: f64 = lengths.iter().sum::<f64>() / lengths.len() as f64;
|
||||||
|
for &len in &lengths {
|
||||||
|
assert!((len - avg_len).abs() / avg_len < 0.1); // 误差 < 10%
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
108
src/math/inicom.rs
Normal file
108
src/math/inicom.rs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
//! Compton 散射 g 因子初始化。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `inicom.f`
|
||||||
|
//!
|
||||||
|
//! INILAM 的辅助过程,初始化 Compton 散射的 g 因子。
|
||||||
|
|
||||||
|
use crate::state::constants::{MDEPTH, UN};
|
||||||
|
|
||||||
|
/// Compton 散射 g 因子初始化。
|
||||||
|
///
|
||||||
|
/// 计算每个频率和深度点的 Planck 函数。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `nd` - 深度点数
|
||||||
|
/// * `nfreq` - 频率点数
|
||||||
|
/// * `bnue` - Planck 函数常数
|
||||||
|
/// * `hkt1` - h*k/T 数组
|
||||||
|
/// * `freq` - 频率数组
|
||||||
|
/// * `ijorig` - 频率索引映射
|
||||||
|
/// * `gfm` - 输出:g- 因子减
|
||||||
|
/// * `gfp` - 输出:g+ 因子加
|
||||||
|
pub fn inicom(
|
||||||
|
nd: usize,
|
||||||
|
nfreq: usize,
|
||||||
|
bnue: &[f64],
|
||||||
|
hkt1: &[f64],
|
||||||
|
freq: &[f64],
|
||||||
|
ijorig: &[i32],
|
||||||
|
_gfm: &mut [Vec<f64>],
|
||||||
|
_gfp: &mut [Vec<f64>],
|
||||||
|
) {
|
||||||
|
// 辅助数组
|
||||||
|
let mut plm = vec![0.0; MDEPTH];
|
||||||
|
let mut pl = vec![0.0; MDEPTH];
|
||||||
|
|
||||||
|
// 第一个频率点
|
||||||
|
let ij: usize = 1;
|
||||||
|
let ijo = ijorig[ij - 1] as usize;
|
||||||
|
|
||||||
|
for id in 0..nd {
|
||||||
|
let arg = hkt1[id] * freq[ijo - 1];
|
||||||
|
if arg < 200.0 {
|
||||||
|
plm[id] = bnue[ijo - 1] / (arg.exp() - UN);
|
||||||
|
} else {
|
||||||
|
plm[id] = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其余频率点
|
||||||
|
for ij in 2..=nfreq {
|
||||||
|
let ijo = ijorig[ij - 1] as usize;
|
||||||
|
for id in 0..nd {
|
||||||
|
let arg = hkt1[id] * freq[ijo - 1];
|
||||||
|
if arg < 200.0 {
|
||||||
|
pl[id] = bnue[ijo - 1] / (arg.exp() - UN);
|
||||||
|
} else {
|
||||||
|
pl[id] = plm[id];
|
||||||
|
}
|
||||||
|
plm[id] = pl[id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_inicom_basic() {
|
||||||
|
let nd = 10;
|
||||||
|
let nfreq = 5;
|
||||||
|
let bnue = vec![1.0; 10];
|
||||||
|
let hkt1 = vec![0.01; nd];
|
||||||
|
let freq = vec![1e14, 2e14, 3e14, 4e14, 5e14];
|
||||||
|
let ijorig = vec![1, 2, 3, 4, 5];
|
||||||
|
|
||||||
|
let mut gfm = vec![vec![0.0; nd]; nfreq];
|
||||||
|
let mut gfp = vec![vec![0.0; nd]; nfreq];
|
||||||
|
|
||||||
|
inicom(
|
||||||
|
nd, nfreq, &bnue, &hkt1, &freq, &ijorig,
|
||||||
|
&mut gfm, &mut gfp,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 函数应该正常完成,不 panic
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_inicom_large_arg() {
|
||||||
|
let nd = 5;
|
||||||
|
let nfreq = 3;
|
||||||
|
let bnue = vec![1.0; 10];
|
||||||
|
let hkt1 = vec![100.0; nd]; // 大参数会导致 exp 溢出
|
||||||
|
let freq = vec![1e14, 2e14, 3e14];
|
||||||
|
let ijorig = vec![1, 2, 3];
|
||||||
|
|
||||||
|
let mut gfm = vec![vec![0.0; nd]; nfreq];
|
||||||
|
let mut gfp = vec![vec![0.0; nd]; nfreq];
|
||||||
|
|
||||||
|
inicom(
|
||||||
|
nd, nfreq, &bnue, &hkt1, &freq, &ijorig,
|
||||||
|
&mut gfm, &mut gfp,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 应该不 panic,使用截断处理
|
||||||
|
}
|
||||||
|
}
|
||||||
181
src/math/interp.rs
Normal file
181
src/math/interp.rs
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
//! 通用插值过程
|
||||||
|
//!
|
||||||
|
//! 原始 Fortran: interp.f
|
||||||
|
//! 支持 (NPOL-1) 阶插值,可选对数插值
|
||||||
|
|
||||||
|
use crate::state::MDEPTH;
|
||||||
|
|
||||||
|
/// 通用插值过程
|
||||||
|
///
|
||||||
|
/// 对给定点进行 (NPOL-1) 阶拉格朗日插值
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
/// - `x`: 原始 x 坐标数组 (可能被修改)
|
||||||
|
/// - `y`: 原始 y 值数组 (可能被修改)
|
||||||
|
/// - `xx`: 新 x 坐标数组 (可能被修改)
|
||||||
|
/// - `yy`: 输出的插值结果
|
||||||
|
/// - `nx`: 原始数组大小
|
||||||
|
/// - `nxx`: 新数组大小
|
||||||
|
/// - `npol`: 插值阶数+1
|
||||||
|
/// - `ilogx`: 1 表示对 x 取对数插值
|
||||||
|
/// - `ilogy`: 1 表示对 y 取对数插值
|
||||||
|
///
|
||||||
|
/// # 说明
|
||||||
|
/// - 当 NPOL <= 0 或 NX <= 0 时,直接复制数据
|
||||||
|
/// - 对数插值会修改输入数组
|
||||||
|
pub fn interp(
|
||||||
|
x: &mut [f64],
|
||||||
|
y: &mut [f64],
|
||||||
|
xx: &mut [f64],
|
||||||
|
yy: &mut [f64],
|
||||||
|
nx: usize,
|
||||||
|
nxx: usize,
|
||||||
|
npol: i32,
|
||||||
|
ilogx: i32,
|
||||||
|
ilogy: i32,
|
||||||
|
) {
|
||||||
|
// 常量
|
||||||
|
const LN10: f64 = 2.30258509299405;
|
||||||
|
|
||||||
|
// 无效情况: 直接复制
|
||||||
|
if npol <= 0 || nx == 0 {
|
||||||
|
let n = if nxx >= nx { nxx } else { nx };
|
||||||
|
for i in 0..n.min(MDEPTH) {
|
||||||
|
if i < xx.len() && i < x.len() {
|
||||||
|
xx[i] = x[i];
|
||||||
|
}
|
||||||
|
if i < yy.len() && i < y.len() {
|
||||||
|
yy[i] = y[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对数插值预处理
|
||||||
|
if ilogx > 0 {
|
||||||
|
for i in 0..nx.min(x.len()) {
|
||||||
|
x[i] = x[i].ln() / LN10; // log10
|
||||||
|
}
|
||||||
|
for i in 0..nxx.min(xx.len()) {
|
||||||
|
xx[i] = xx[i].ln() / LN10; // log10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ilogy > 0 {
|
||||||
|
for i in 0..nx.min(y.len()) {
|
||||||
|
y[i] = y[i].ln() / LN10; // log10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 插值参数
|
||||||
|
let nm = (npol + 1) as usize / 2;
|
||||||
|
let nm1 = nm + 1;
|
||||||
|
let nup = nx + nm1 - npol as usize;
|
||||||
|
|
||||||
|
// 对每个目标点进行插值
|
||||||
|
for id in 0..nxx {
|
||||||
|
let xxx = xx[id];
|
||||||
|
|
||||||
|
// 找到插值区间
|
||||||
|
let mut i = nm1;
|
||||||
|
while i <= nup && xxx > x[i - 1] {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
if i > nup {
|
||||||
|
i = nup;
|
||||||
|
}
|
||||||
|
|
||||||
|
let j = i - nm;
|
||||||
|
let jj = j + npol as usize - 1;
|
||||||
|
|
||||||
|
// 拉格朗日插值
|
||||||
|
let mut yyy = 0.0;
|
||||||
|
for k in j..=jj {
|
||||||
|
let mut t = 1.0;
|
||||||
|
for m in j..=jj {
|
||||||
|
if k != m {
|
||||||
|
t *= (xxx - x[m - 1]) / (x[k - 1] - x[m - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yyy += y[k - 1] * t;
|
||||||
|
}
|
||||||
|
yy[id] = yyy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对数插值后处理: 恢复原值
|
||||||
|
if ilogx > 0 {
|
||||||
|
for i in 0..nx.min(x.len()) {
|
||||||
|
x[i] = (x[i] * LN10).exp();
|
||||||
|
}
|
||||||
|
for i in 0..nxx.min(xx.len()) {
|
||||||
|
xx[i] = (xx[i] * LN10).exp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ilogy > 0 {
|
||||||
|
for i in 0..nx.min(y.len()) {
|
||||||
|
y[i] = (y[i] * LN10).exp();
|
||||||
|
}
|
||||||
|
for i in 0..nxx.min(yy.len()) {
|
||||||
|
yy[i] = (yy[i] * LN10).exp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_interp_linear() {
|
||||||
|
// 线性函数 y = 2x
|
||||||
|
let mut x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
|
||||||
|
let mut y = vec![2.0, 4.0, 6.0, 8.0, 10.0];
|
||||||
|
let mut xx = vec![1.5, 2.5, 3.5, 4.5];
|
||||||
|
let mut yy = vec![0.0; 4];
|
||||||
|
|
||||||
|
interp(
|
||||||
|
&mut x, &mut y, &mut xx, &mut yy,
|
||||||
|
5, 4, 2, 0, 0 // npol=2 (线性), 无对数
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!((yy[0] - 3.0).abs() < 1e-10);
|
||||||
|
assert!((yy[1] - 5.0).abs() < 1e-10);
|
||||||
|
assert!((yy[2] - 7.0).abs() < 1e-10);
|
||||||
|
assert!((yy[3] - 9.0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_interp_quadratic() {
|
||||||
|
// 二次函数 y = x^2
|
||||||
|
let mut x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
|
||||||
|
let mut y = vec![1.0, 4.0, 9.0, 16.0, 25.0];
|
||||||
|
let mut xx = vec![2.5];
|
||||||
|
let mut yy = vec![0.0];
|
||||||
|
|
||||||
|
interp(
|
||||||
|
&mut x, &mut y, &mut xx, &mut yy,
|
||||||
|
5, 1, 3, 0, 0 // npol=3 (二次)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2.5^2 = 6.25
|
||||||
|
assert!((yy[0] - 6.25).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_interp_log() {
|
||||||
|
// 对数插值测试
|
||||||
|
let mut x = vec![1.0, 10.0, 100.0, 1000.0];
|
||||||
|
let mut y = vec![1.0, 2.0, 3.0, 4.0];
|
||||||
|
let mut xx = vec![10.0_f64.sqrt()]; // sqrt(10)
|
||||||
|
let mut yy = vec![0.0];
|
||||||
|
|
||||||
|
interp(
|
||||||
|
&mut x, &mut y, &mut xx, &mut yy,
|
||||||
|
4, 1, 2, 1, 0 // 对数 x
|
||||||
|
);
|
||||||
|
|
||||||
|
// 在 log10 空间中,sqrt(10) ≈ 0.5,应该得到 1.5
|
||||||
|
assert!((yy[0] - 1.5).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
220
src/math/inthyd.rs
Normal file
220
src/math/inthyd.rs
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
//! 氢线 Stark 展宽表格插值。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `inthyd.f`。
|
||||||
|
//!
|
||||||
|
//! 从 Lemke 表格中插值计算氢线的 Stark 展宽轮廓。
|
||||||
|
|
||||||
|
use crate::math::{divstr, starka, yint};
|
||||||
|
use crate::state::HydPrf;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// INTHYD - 氢线表格插值
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 从 Lemke 表格插值计算氢线 Stark 展宽轮廓。
|
||||||
|
///
|
||||||
|
/// 在温度和电子密度方向上进行二维插值。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `x0` - log10(温度)
|
||||||
|
/// - `z0` - log10(电子密度)
|
||||||
|
/// - `iwl` - 波长索引 (0-indexed)
|
||||||
|
/// - `iline` - 谱线索引 (0-indexed)
|
||||||
|
/// - `hydprf` - 氢线表格数据
|
||||||
|
/// - `dbeta` - Doppler 宽度 (β 单位)
|
||||||
|
/// - `xk` - 参考波数
|
||||||
|
///
|
||||||
|
/// # 返回
|
||||||
|
///
|
||||||
|
/// log10(轮廓值)
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// SUBROUTINE INTHYD(W0,X0,Z0,IWL,ILINE)
|
||||||
|
/// ...
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
pub fn inthyd(
|
||||||
|
x0: f64,
|
||||||
|
z0: f64,
|
||||||
|
iwl: usize,
|
||||||
|
iline: usize,
|
||||||
|
hydprf: &HydPrf,
|
||||||
|
dbeta: f64,
|
||||||
|
xk: f64,
|
||||||
|
) -> f64 {
|
||||||
|
let nt = hydprf.nth[iline] as usize;
|
||||||
|
let ne = hydprf.neh[iline] as usize;
|
||||||
|
let beta = hydprf.get_wlh(iwl, iline) / xk;
|
||||||
|
let izh = 1; // H I
|
||||||
|
|
||||||
|
// 检查是否低于最低电子密度网格值
|
||||||
|
// 对于低于最低网格值的情况,使用近似表达式 (starka)
|
||||||
|
let xnelem_min = hydprf.get_xnelem(0, iline);
|
||||||
|
if z0 < xnelem_min * 0.99 {
|
||||||
|
let (adh, divh) = divstr(dbeta, izh);
|
||||||
|
let stark_val = starka(beta, 2.0, adh, dbeta, divh);
|
||||||
|
return (stark_val * dbeta).log10();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找电子密度插值区间
|
||||||
|
let mut ipz = 0;
|
||||||
|
for izz in 0..(ne - 1) {
|
||||||
|
ipz = izz;
|
||||||
|
if z0 <= hydprf.get_xnelem(izz + 1, iline) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确定插值窗口
|
||||||
|
let nz = 2;
|
||||||
|
let mut n0z = ipz as i32 - (nz as i32 / 2) + 1;
|
||||||
|
if n0z < 1 {
|
||||||
|
n0z = 1;
|
||||||
|
}
|
||||||
|
if n0z > (ne - nz + 1) as i32 {
|
||||||
|
n0z = (ne - nz + 1) as i32;
|
||||||
|
}
|
||||||
|
let n1z = n0z + nz as i32 - 1;
|
||||||
|
|
||||||
|
// 准备插值数组
|
||||||
|
let mut zz = [0.0; 3];
|
||||||
|
let mut wz = [0.0; 3];
|
||||||
|
|
||||||
|
for izz in n0z..=n1z {
|
||||||
|
let i0z = (izz - n0z) as usize;
|
||||||
|
zz[i0z] = hydprf.get_xnelem((izz - 1) as usize, iline);
|
||||||
|
|
||||||
|
// 检查是否超过最高温度网格值
|
||||||
|
let xtlem_max = hydprf.get_xtlem(nt - 1, iline);
|
||||||
|
if x0 > 1.01 * xtlem_max {
|
||||||
|
let (adh, divh) = divstr(dbeta, izh);
|
||||||
|
let stark_val = starka(beta, 2.0, adh, dbeta, divh);
|
||||||
|
return (stark_val * dbeta).log10();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 温度方向插值
|
||||||
|
let nx = 2;
|
||||||
|
let mut ipx = 0;
|
||||||
|
for ix in 0..(nt - 1) {
|
||||||
|
ipx = ix;
|
||||||
|
if x0 <= hydprf.get_xtlem(ix + 1, iline) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut n0x = ipx as i32 - (nx as i32 / 2) + 1;
|
||||||
|
if n0x < 1 {
|
||||||
|
n0x = 1;
|
||||||
|
}
|
||||||
|
if n0x > (nt - nx + 1) as i32 {
|
||||||
|
n0x = (nt - nx + 1) as i32;
|
||||||
|
}
|
||||||
|
let n1x = n0x + nx as i32 - 1;
|
||||||
|
|
||||||
|
let mut xx = [0.0; 3];
|
||||||
|
let mut wx = [0.0; 3];
|
||||||
|
|
||||||
|
for ix in n0x..=n1x {
|
||||||
|
let i0 = (ix - n0x) as usize;
|
||||||
|
xx[i0] = hydprf.get_xtlem((ix - 1) as usize, iline);
|
||||||
|
wx[i0] = hydprf.get_prfhyd(iline, iwl, (ix - 1) as usize, (izz - 1) as usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有无效值
|
||||||
|
if wx[0] < -99.0 || wx[1] < -99.0 || wx[2] < -99.0 {
|
||||||
|
let (adh, divh) = divstr(dbeta, izh);
|
||||||
|
let stark_val = starka(beta, 2.0, adh, dbeta, divh);
|
||||||
|
return (stark_val * dbeta).log10();
|
||||||
|
} else {
|
||||||
|
wz[i0z] = yint(&xx, &wx, x0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 电子密度方向插值
|
||||||
|
yint(&zz, &wz, z0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn create_test_hydprf() -> HydPrf {
|
||||||
|
let mut hydprf = HydPrf::default();
|
||||||
|
|
||||||
|
// 设置谱线 0 的参数
|
||||||
|
hydprf.nth[0] = 7;
|
||||||
|
hydprf.neh[0] = 20;
|
||||||
|
|
||||||
|
// 设置温度网格 (log10)
|
||||||
|
for it in 0..7 {
|
||||||
|
hydprf.xtlem[it] = 4.0 + it as f64 * 0.1; // 10^4.0 到 10^4.6
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置电子密度网格 (log10)
|
||||||
|
for ie in 0..20 {
|
||||||
|
hydprf.xnelem[ie] = 12.0 + ie as f64 * 0.2; // 10^12 到 10^15.8
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置波长网格
|
||||||
|
for iwl in 0..90 {
|
||||||
|
hydprf.wlh[iwl] = 4000.0 + iwl as f64 * 10.0; // 4000 Å 到 4900 Å
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置轮廓数据 (简单的测试值)
|
||||||
|
for it in 0..7 {
|
||||||
|
for ie in 0..20 {
|
||||||
|
hydprf.set_prfhyd(0, 0, it, ie, -2.0 + it as f64 * 0.1 + ie as f64 * 0.01);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hydprf
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_inthyd_basic() {
|
||||||
|
let hydprf = create_test_hydprf();
|
||||||
|
let dbeta = 10.0;
|
||||||
|
let xk = 1.0;
|
||||||
|
|
||||||
|
// 在表格范围内的插值
|
||||||
|
let x0 = 4.3; // log10(T)
|
||||||
|
let z0 = 13.5; // log10(Ne)
|
||||||
|
let result = inthyd(x0, z0, 0, 0, &hydprf, dbeta, xk);
|
||||||
|
|
||||||
|
// 结果应该是有限值
|
||||||
|
assert!(result.is_finite());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_inthyd_low_ne() {
|
||||||
|
let hydprf = create_test_hydprf();
|
||||||
|
let dbeta = 10.0;
|
||||||
|
let xk = 1.0;
|
||||||
|
|
||||||
|
// 低于最低电子密度,应该使用近似表达式
|
||||||
|
let x0 = 4.3;
|
||||||
|
let z0 = 10.0; // 低于 10^12
|
||||||
|
let result = inthyd(x0, z0, 0, 0, &hydprf, dbeta, xk);
|
||||||
|
|
||||||
|
// 结果应该是有限值
|
||||||
|
assert!(result.is_finite());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_inthyd_high_temp() {
|
||||||
|
let hydprf = create_test_hydprf();
|
||||||
|
let dbeta = 10.0;
|
||||||
|
let xk = 1.0;
|
||||||
|
|
||||||
|
// 高于最高温度,应该使用近似表达式
|
||||||
|
let x0 = 5.0; // 高于 10^4.6
|
||||||
|
let z0 = 13.5;
|
||||||
|
let result = inthyd(x0, z0, 0, 0, &hydprf, dbeta, xk);
|
||||||
|
|
||||||
|
// 结果应该是有限值
|
||||||
|
assert!(result.is_finite());
|
||||||
|
}
|
||||||
|
}
|
||||||
176
src/math/intlem.rs
Normal file
176
src/math/intlem.rs
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
//! Lemke 表格氢线轮廓积分。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `intlem.f`。
|
||||||
|
//!
|
||||||
|
//! 从 Lemke 表格中计算氢线 Stark 展宽轮廓。
|
||||||
|
|
||||||
|
use crate::math::inthyd;
|
||||||
|
use crate::state::model::{HydPrf, ModPar, StrAux, Turbul};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// INTLEM - Lemke 表格积分
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 从 Lemke 表格计算氢线 Stark 展宽轮廓。
|
||||||
|
///
|
||||||
|
/// 对每个波长点进行温度和电子密度方向的二维插值。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `prfh` - 输出轮廓数组 (MHWL 个元素)
|
||||||
|
/// - `wl0` - 参考波长 (Å)
|
||||||
|
/// - `iline` - 谱线索引 (0-indexed)
|
||||||
|
/// - `id` - 深度索引 (0-indexed)
|
||||||
|
/// - `modpar` - 模型参数 (包含温度、电子密度)
|
||||||
|
/// - `turbul` - 湍流参数
|
||||||
|
/// - `straux` - Stark 辅助参数
|
||||||
|
/// - `hydprf` - 氢线表格数据
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// SUBROUTINE INTLEM(PRFH,WL0,ILINE,ID)
|
||||||
|
/// ...
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
pub fn intlem(
|
||||||
|
prfh: &mut [f64],
|
||||||
|
wl0: f64,
|
||||||
|
iline: usize,
|
||||||
|
id: usize,
|
||||||
|
modpar: &ModPar,
|
||||||
|
turbul: &Turbul,
|
||||||
|
straux: &StrAux,
|
||||||
|
hydprf: &HydPrf,
|
||||||
|
) {
|
||||||
|
const FOC1: f64 = 1.25e-9;
|
||||||
|
const TTW: f64 = 2.0 / 3.0;
|
||||||
|
const VTBC: f64 = 6.06e-9;
|
||||||
|
|
||||||
|
// 考虑湍流速度对 Doppler 宽度的影响,修正温度
|
||||||
|
let t = modpar.temp[id] + VTBC * turbul.vturbs[id] * turbul.vturbs[id];
|
||||||
|
let ane = modpar.elec[id];
|
||||||
|
let tl = t.log10();
|
||||||
|
let anel = ane.log10();
|
||||||
|
|
||||||
|
let f00 = FOC1 * (anel * TTW).exp();
|
||||||
|
let xk = straux.xk0[iline];
|
||||||
|
let fxk = f00 * xk;
|
||||||
|
let dop = 1.0e8 / wl0 * (1.65e8 * t).sqrt();
|
||||||
|
let dbeta = wl0 * wl0 / 2.997925e18 / fxk;
|
||||||
|
let betad = dbeta * dop;
|
||||||
|
|
||||||
|
// 对每个波长点进行插值
|
||||||
|
let nwl = hydprf.nwlhyd[iline] as usize;
|
||||||
|
for iwl in 0..nwl {
|
||||||
|
let prfh0 = inthyd(tl, anel, iwl, iline, hydprf, dbeta, xk);
|
||||||
|
prfh[iwl] = prfh0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::state::constants::MHWL;
|
||||||
|
|
||||||
|
fn create_test_modpar() -> ModPar {
|
||||||
|
let mut modpar = ModPar::default();
|
||||||
|
modpar.temp[0] = 10000.0; // 10000 K
|
||||||
|
modpar.elec[0] = 1e13; // 10^13 cm^-3
|
||||||
|
modpar
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_test_turbul() -> Turbul {
|
||||||
|
let mut turbul = Turbul::default();
|
||||||
|
turbul.vturbs[0] = 5.0; // 5 km/s
|
||||||
|
turbul
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_test_straux() -> StrAux {
|
||||||
|
let mut straux = StrAux::default();
|
||||||
|
straux.xk0[0] = 1e4; // 参考波数
|
||||||
|
straux
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_test_hydprf() -> HydPrf {
|
||||||
|
let mut hydprf = HydPrf::default();
|
||||||
|
|
||||||
|
// 设置谱线 0 的参数
|
||||||
|
hydprf.nwlhyd[0] = 10; // 10 个波长点
|
||||||
|
hydprf.nth[0] = 7;
|
||||||
|
hydprf.neh[0] = 20;
|
||||||
|
|
||||||
|
// 设置温度网格 (log10)
|
||||||
|
for it in 0..7 {
|
||||||
|
hydprf.set_xtlem(it, 0, 4.0 + it as f64 * 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置电子密度网格 (log10)
|
||||||
|
for ie in 0..20 {
|
||||||
|
hydprf.set_xnelem(ie, 0, 12.0 + ie as f64 * 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置轮廓数据
|
||||||
|
for it in 0..7 {
|
||||||
|
for ie in 0..20 {
|
||||||
|
for iwl in 0..10 {
|
||||||
|
hydprf.set_prfhyd(0, iwl, it, ie, -2.0 + it as f64 * 0.1 + ie as f64 * 0.01);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hydprf
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_intlem_basic() {
|
||||||
|
let modpar = create_test_modpar();
|
||||||
|
let turbul = create_test_turbul();
|
||||||
|
let straux = create_test_straux();
|
||||||
|
let hydprf = create_test_hydprf();
|
||||||
|
|
||||||
|
let mut prfh = vec![0.0; MHWL];
|
||||||
|
let wl0 = 4861.0; // H-beta 波长
|
||||||
|
|
||||||
|
intlem(
|
||||||
|
&mut prfh,
|
||||||
|
wl0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&modpar,
|
||||||
|
&turbul,
|
||||||
|
&straux,
|
||||||
|
&hydprf,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 检查前几个波长点是否有有效值
|
||||||
|
for i in 0..10 {
|
||||||
|
assert!(prfh[i].is_finite(), "prfh[{}] should be finite", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_intlem_with_zero_turbulence() {
|
||||||
|
let modpar = create_test_modpar();
|
||||||
|
let turbul = Turbul::default(); // 零湍流
|
||||||
|
let straux = create_test_straux();
|
||||||
|
let hydprf = create_test_hydprf();
|
||||||
|
|
||||||
|
let mut prfh = vec![0.0; MHWL];
|
||||||
|
let wl0 = 4861.0;
|
||||||
|
|
||||||
|
intlem(
|
||||||
|
&mut prfh,
|
||||||
|
wl0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&modpar,
|
||||||
|
&turbul,
|
||||||
|
&straux,
|
||||||
|
&hydprf,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 结果应该是有限值
|
||||||
|
assert!(prfh[0].is_finite());
|
||||||
|
}
|
||||||
|
}
|
||||||
215
src/math/intxen.rs
Normal file
215
src/math/intxen.rs
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
//! Xenomorph 表温度和电子密度插值。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `intxen.f`
|
||||||
|
//!
|
||||||
|
//! 对氢线的 Xenomorph 表进行温度和电子密度的二维插值。
|
||||||
|
|
||||||
|
use crate::math::interpolate::yint;
|
||||||
|
|
||||||
|
/// Xenomorph 表插值。
|
||||||
|
///
|
||||||
|
/// 对温度和电子密度进行二维插值,得到氢线轮廓值。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `x0` - 温度(对数)
|
||||||
|
/// * `z0` - 电子密度(对数)
|
||||||
|
/// * `iwl` - 波长索引 (1-based)
|
||||||
|
/// * `iline` - 谱线索引 (1-based)
|
||||||
|
/// * `nthxen` - 每条线的温度点数
|
||||||
|
/// * `nehxen` - 每条线的电子密度点数
|
||||||
|
/// * `xtxen` - 温度网格 [MHT][MLINH]
|
||||||
|
/// * `xnexen` - 电子密度网格 [MHE][MLINH]
|
||||||
|
/// * `prfxb` - 蓝翼轮廓 [MLINH][MHWL][MHT][MHE]
|
||||||
|
/// * `prfxr` - 红翼轮廓 [MLINH][MHWL][MHT][MHE]
|
||||||
|
/// * `mht` - 温度网格最大点数
|
||||||
|
/// * `mhe` - 电子密度网格最大点数
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// 返回 (w0b, w0r) - 蓝翼和红翼的插值结果
|
||||||
|
pub fn intxen(
|
||||||
|
x0: f64,
|
||||||
|
z0: f64,
|
||||||
|
iwl: usize,
|
||||||
|
iline: usize,
|
||||||
|
nthxen: &[i32],
|
||||||
|
nehxen: &[i32],
|
||||||
|
xtxen: &[f64],
|
||||||
|
xnexen: &[f64],
|
||||||
|
prfxb: &[f64],
|
||||||
|
prfxr: &[f64],
|
||||||
|
mht: usize,
|
||||||
|
mhe: usize,
|
||||||
|
mlinh: usize,
|
||||||
|
mhwl: usize,
|
||||||
|
) -> (f64, f64) {
|
||||||
|
const NX: usize = 3; // 3点二次插值
|
||||||
|
const NZ: usize = 3; // 3点二次插值
|
||||||
|
|
||||||
|
let iline_idx = iline - 1; // 转为 0-indexed
|
||||||
|
let iwl_idx = iwl - 1;
|
||||||
|
|
||||||
|
let nt = nthxen[iline_idx] as usize;
|
||||||
|
let ne = nehxen[iline_idx] as usize;
|
||||||
|
|
||||||
|
if nt == 0 || ne == 0 {
|
||||||
|
return (0.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找电子密度索引
|
||||||
|
let mut ipz = 1usize;
|
||||||
|
for izz in 1..ne {
|
||||||
|
ipz = izz;
|
||||||
|
let xnexen_val = xnexen[izz + mhe * iline_idx]; // XNEXEN(IZZ+1, ILINE)
|
||||||
|
if z0 <= xnexen_val {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算电子密度网格范围
|
||||||
|
let n0z = if ipz < NZ / 2 + 1 {
|
||||||
|
1
|
||||||
|
} else if ipz > ne - NZ + 1 {
|
||||||
|
ne - NZ + 1
|
||||||
|
} else {
|
||||||
|
ipz - NZ / 2 + 1
|
||||||
|
};
|
||||||
|
let n1z = n0z + NZ - 1;
|
||||||
|
|
||||||
|
let mut zz = [0.0; 3];
|
||||||
|
let mut wzb = [0.0; 3];
|
||||||
|
let mut wzr = [0.0; 3];
|
||||||
|
|
||||||
|
// 对每个电子密度点
|
||||||
|
for izz in n0z..=n1z {
|
||||||
|
let i0z = izz - n0z;
|
||||||
|
zz[i0z] = xnexen[(izz - 1) + mhe * iline_idx]; // XNEXEN(IZZ, ILINE)
|
||||||
|
|
||||||
|
// 查找温度索引
|
||||||
|
let mut ipx = 1usize;
|
||||||
|
for ix in 1..nt {
|
||||||
|
ipx = ix;
|
||||||
|
let xtxen_val = xtxen[ix + mht * iline_idx]; // XTXEN(IX+1, ILINE)
|
||||||
|
if x0 <= xtxen_val {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算温度网格范围
|
||||||
|
let n0x = if ipx < NX / 2 + 1 {
|
||||||
|
1
|
||||||
|
} else if ipx > nt - NX + 1 {
|
||||||
|
nt - NX + 1
|
||||||
|
} else {
|
||||||
|
ipx - NX / 2 + 1
|
||||||
|
};
|
||||||
|
let n1x = n0x + NX - 1;
|
||||||
|
|
||||||
|
let mut xx = [0.0; 3];
|
||||||
|
let mut wxb = [0.0; 3];
|
||||||
|
let mut wxr = [0.0; 3];
|
||||||
|
|
||||||
|
// 对每个温度点
|
||||||
|
for ix in n0x..=n1x {
|
||||||
|
let i0 = ix - n0x;
|
||||||
|
xx[i0] = xtxen[(ix - 1) + mht * iline_idx]; // XTXEN(IX, ILINE)
|
||||||
|
|
||||||
|
// 获取轮廓值
|
||||||
|
let idx = iline_idx + mlinh * iwl_idx + mlinh * mhwl * (ix - 1) + mlinh * mhwl * mht * (izz - 1);
|
||||||
|
wxb[i0] = prfxb[idx];
|
||||||
|
wxr[i0] = prfxr[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 温度方向插值
|
||||||
|
wzb[i0z] = yint(&xx[..NX], &wxb[..NX], x0);
|
||||||
|
wzr[i0z] = yint(&xx[..NX], &wxr[..NX], x0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 电子密度方向插值
|
||||||
|
let w0b = yint(&zz[..NZ], &wzb[..NZ], z0);
|
||||||
|
let w0r = yint(&zz[..NZ], &wzr[..NZ], z0);
|
||||||
|
|
||||||
|
(w0b, w0r)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn create_test_data() -> (Vec<i32>, Vec<i32>, Vec<f64>, Vec<f64>, Vec<f64>, Vec<f64>) {
|
||||||
|
let mlinh = 78;
|
||||||
|
let mhwl = 90;
|
||||||
|
let mht = 7;
|
||||||
|
let mhe = 20;
|
||||||
|
|
||||||
|
let mut nthxen = vec![0i32; mlinh];
|
||||||
|
let mut nehxen = vec![0i32; mlinh];
|
||||||
|
let mut xtxen = vec![0.0; mht * mlinh];
|
||||||
|
let mut xnexen = vec![0.0; mhe * mlinh];
|
||||||
|
let prfxb = vec![1e-20; mlinh * mhwl * mht * mhe];
|
||||||
|
let prfxr = vec![1e-20; mlinh * mhwl * mht * mhe];
|
||||||
|
|
||||||
|
// 设置第一条线的数据
|
||||||
|
nthxen[0] = 5;
|
||||||
|
nehxen[0] = 5;
|
||||||
|
|
||||||
|
// 温度网格: 3.5, 3.7, 3.9, 4.1, 4.3 (log T)
|
||||||
|
for i in 0..5 {
|
||||||
|
xtxen[i] = 3.5 + 0.2 * i as f64;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 电子密度网格: 10, 11, 12, 13, 14 (log ne)
|
||||||
|
for i in 0..5 {
|
||||||
|
xnexen[i] = 10.0 + i as f64;
|
||||||
|
}
|
||||||
|
|
||||||
|
(nthxen, nehxen, xtxen, xnexen, prfxb, prfxr)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_intxen_basic() {
|
||||||
|
let (nthxen, nehxen, xtxen, xnexen, prfxb, prfxr) = create_test_data();
|
||||||
|
|
||||||
|
let (w0b, w0r) = intxen(
|
||||||
|
3.8, // 温度 log T
|
||||||
|
11.5, // 电子密度 log ne
|
||||||
|
1, // 波长索引
|
||||||
|
1, // 谱线索引
|
||||||
|
&nthxen,
|
||||||
|
&nehxen,
|
||||||
|
&xtxen,
|
||||||
|
&xnexen,
|
||||||
|
&prfxb,
|
||||||
|
&prfxr,
|
||||||
|
7, // mht
|
||||||
|
20, // mhe
|
||||||
|
78, // mlinh
|
||||||
|
90, // mhwl
|
||||||
|
);
|
||||||
|
|
||||||
|
// 应该返回正值
|
||||||
|
assert!(w0b > 0.0);
|
||||||
|
assert!(w0r > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_intxen_zero_data() {
|
||||||
|
let nthxen = vec![0; 78];
|
||||||
|
let nehxen = vec![0; 78];
|
||||||
|
let xtxen = vec![0.0; 7 * 78];
|
||||||
|
let xnexen = vec![0.0; 20 * 78];
|
||||||
|
let prfxb = vec![0.0; 78 * 90 * 7 * 20];
|
||||||
|
let prfxr = vec![0.0; 78 * 90 * 7 * 20];
|
||||||
|
|
||||||
|
let (w0b, w0r) = intxen(
|
||||||
|
3.8, 11.5, 1, 1,
|
||||||
|
&nthxen, &nehxen, &xtxen, &xnexen, &prfxb, &prfxr,
|
||||||
|
7, 20, 78, 90,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 零数据应该返回 0
|
||||||
|
assert!((w0b - 0.0).abs() < 1e-30);
|
||||||
|
assert!((w0r - 0.0).abs() < 1e-30);
|
||||||
|
}
|
||||||
|
}
|
||||||
229
src/math/levsol.rs
Normal file
229
src/math/levsol.rs
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
//! 能级占据数求解器。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `levsol.f`。
|
||||||
|
//!
|
||||||
|
//! 通过求解速率方程得到新的能级占据数。
|
||||||
|
|
||||||
|
use crate::math::lineqs::lineqs_nr;
|
||||||
|
use crate::state::atomic::AtoPar;
|
||||||
|
use crate::state::config::{BasNum, InpPar};
|
||||||
|
use crate::state::constants::MLEVEL;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// LEVSOL - 能级求解器
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 求解速率方程得到新的能级占据数。
|
||||||
|
///
|
||||||
|
/// 通过两种方式之一:
|
||||||
|
/// a) 反演全局速率矩阵 (如果 IRSPLT=0)
|
||||||
|
/// b) 反演各个化学元素的部分速率矩阵
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `a` - 速率矩阵 A(MLEVEL,MLEVEL)
|
||||||
|
/// - `b` - 右端向量 B(MLEVEL)
|
||||||
|
/// - `popp` - 输出占据数 POPP(MLEVEL)
|
||||||
|
/// - `iical` - 能级索引映射 IICAL(MLEVEL)
|
||||||
|
/// - `nlvcal` - 实际计算的能级数
|
||||||
|
/// - `iall` - 标志 (0 表示跳过固定元素)
|
||||||
|
/// - `basnum` - 基本数值参数 (包含 natom, ioptab)
|
||||||
|
/// - `inppar` - 输入参数 (包含 irsplt)
|
||||||
|
/// - `atopar` - 原子参数 (包含 n0a, nka, iifix)
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// SUBROUTINE LEVSOL(A,B,POPP,IICAL,NLVCAL,IALL)
|
||||||
|
/// ...
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
pub fn levsol(
|
||||||
|
a: &mut [f64],
|
||||||
|
b: &mut [f64],
|
||||||
|
popp: &mut [f64],
|
||||||
|
iical: &[i32],
|
||||||
|
nlvcal: usize,
|
||||||
|
iall: i32,
|
||||||
|
basnum: &BasNum,
|
||||||
|
inppar: &InpPar,
|
||||||
|
atopar: &AtoPar,
|
||||||
|
) {
|
||||||
|
// 检查是否跳过
|
||||||
|
if basnum.ioptab < 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方式 a: 反演全局速率矩阵
|
||||||
|
if inppar.irsplt == 0 {
|
||||||
|
lineqs_nr(a, b, popp, nlvcal, MLEVEL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方式 b: 反演各个化学元素的部分速率矩阵
|
||||||
|
let natom = basnum.natom as usize;
|
||||||
|
|
||||||
|
// 工作数组
|
||||||
|
let mut ap = vec![0.0; MLEVEL * MLEVEL];
|
||||||
|
let mut bp = vec![0.0; MLEVEL];
|
||||||
|
let mut popp1 = vec![0.0; MLEVEL];
|
||||||
|
|
||||||
|
for iat in 0..natom {
|
||||||
|
// 跳过固定元素
|
||||||
|
if atopar.iifix[iat] == 1 && iall == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut n1 = atopar.n0a[iat] as usize;
|
||||||
|
let nk = atopar.nka[iat] as usize;
|
||||||
|
|
||||||
|
n1 = iical[n1] as usize;
|
||||||
|
let mut nk_idx = iical[nk] as usize;
|
||||||
|
|
||||||
|
// 查找有效的起始索引
|
||||||
|
if n1 == 0 {
|
||||||
|
for i in atopar.n0a[iat] as usize..=atopar.nka[iat] as usize {
|
||||||
|
let idx = iical[i] as usize;
|
||||||
|
if idx > 0 {
|
||||||
|
n1 = idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if n1 == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修正 nk_idx (Fortran 中可能是 0,这里需要调整)
|
||||||
|
if nk_idx == 0 {
|
||||||
|
nk_idx = n1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let nlp = nk_idx - n1 + 1;
|
||||||
|
if nlp == 0 || n1 + nlp > MLEVEL {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取部分矩阵
|
||||||
|
for i in 0..nlp {
|
||||||
|
for j in 0..nlp {
|
||||||
|
ap[j * MLEVEL + i] = a[(n1 + i) * MLEVEL + (n1 + j)];
|
||||||
|
}
|
||||||
|
bp[i] = b[n1 + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 求解部分矩阵
|
||||||
|
lineqs_nr(&mut ap, &mut bp, &mut popp1, nlp, MLEVEL);
|
||||||
|
|
||||||
|
// 存储结果
|
||||||
|
for i in 0..nlp {
|
||||||
|
popp[n1 + i] = popp1[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::state::atomic::AtoPar;
|
||||||
|
use crate::state::config::{BasNum, InpPar};
|
||||||
|
use crate::state::constants::MLEVEL;
|
||||||
|
|
||||||
|
fn create_test_atopar() -> AtoPar {
|
||||||
|
let mut atopar = AtoPar::default();
|
||||||
|
atopar.n0a[0] = 0;
|
||||||
|
atopar.n0a[1] = 3;
|
||||||
|
atopar.nka[0] = 2;
|
||||||
|
atopar.nka[1] = 5;
|
||||||
|
atopar.iifix[0] = 0;
|
||||||
|
atopar.iifix[1] = 0;
|
||||||
|
atopar
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_test_basnum() -> BasNum {
|
||||||
|
BasNum {
|
||||||
|
natom: 2,
|
||||||
|
ioptab: 0,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_test_inppar() -> InpPar {
|
||||||
|
InpPar {
|
||||||
|
irsplt: 0,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_levsol_global_matrix() {
|
||||||
|
let mut a = vec![0.0; MLEVEL * MLEVEL];
|
||||||
|
let mut b = vec![0.0; MLEVEL];
|
||||||
|
let mut popp = vec![0.0; MLEVEL];
|
||||||
|
let iical = vec![0i32; MLEVEL];
|
||||||
|
|
||||||
|
// 设置简单的 2x2 系统
|
||||||
|
// [2 1] [x] [3]
|
||||||
|
// [1 2] [y] = [3]
|
||||||
|
// 解是 x=1, y=1
|
||||||
|
// Fortran 列优先: A(j,i) = a[j + i*MLEVEL]
|
||||||
|
a[0 + 0 * MLEVEL] = 2.0; // A(1,1)
|
||||||
|
a[1 + 0 * MLEVEL] = 1.0; // A(2,1)
|
||||||
|
a[0 + 1 * MLEVEL] = 1.0; // A(1,2)
|
||||||
|
a[1 + 1 * MLEVEL] = 2.0; // A(2,2)
|
||||||
|
b[0] = 3.0;
|
||||||
|
b[1] = 3.0;
|
||||||
|
|
||||||
|
let basnum = create_test_basnum();
|
||||||
|
let inppar = create_test_inppar();
|
||||||
|
let atopar = create_test_atopar();
|
||||||
|
|
||||||
|
levsol(
|
||||||
|
&mut a,
|
||||||
|
&mut b,
|
||||||
|
&mut popp,
|
||||||
|
&iical,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
&basnum,
|
||||||
|
&inppar,
|
||||||
|
&atopar,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 解应该是 x=1, y=1
|
||||||
|
assert!((popp[0] - 1.0).abs() < 1e-10);
|
||||||
|
assert!((popp[1] - 1.0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_levsol_skip_negative_ioptab() {
|
||||||
|
let basnum = BasNum {
|
||||||
|
ioptab: -2,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let inppar = create_test_inppar();
|
||||||
|
let atopar = create_test_atopar();
|
||||||
|
|
||||||
|
let mut a = vec![0.0; MLEVEL * MLEVEL];
|
||||||
|
let mut b = vec![0.0; MLEVEL];
|
||||||
|
let mut popp = vec![1.0; MLEVEL]; // 初始值
|
||||||
|
let iical = vec![0i32; MLEVEL];
|
||||||
|
|
||||||
|
let popp_before = popp[0];
|
||||||
|
levsol(
|
||||||
|
&mut a,
|
||||||
|
&mut b,
|
||||||
|
&mut popp,
|
||||||
|
&iical,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
&basnum,
|
||||||
|
&inppar,
|
||||||
|
&atopar,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 应该跳过,popp 保持不变
|
||||||
|
assert!((popp[0] - popp_before).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
223
src/math/lineqs.rs
Normal file
223
src/math/lineqs.rs
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
//! 线性方程组求解。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `lineqs.f`。
|
||||||
|
//!
|
||||||
|
//! 使用高斯消元法(带部分选主元)求解线性方程组 A*X = B。
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// LINEQS - 高斯消元法求解线性方程组
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 使用高斯消元法(带部分选主元)求解线性方程组 A*X = B。
|
||||||
|
///
|
||||||
|
/// 这是简化版本,假设物理维度等于逻辑维度。
|
||||||
|
/// 对于物理维度大于逻辑维度的情况,使用 `lineqs_nr`。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `a` - 系数矩阵,大小为 n×n(列优先存储,会被修改)
|
||||||
|
/// - `b` - 右端向量,大小为 n(会被修改)
|
||||||
|
/// - `x` - 解向量,大小为 n(输出)
|
||||||
|
/// - `n` - 实际方程数
|
||||||
|
pub fn lineqs(a: &mut [f64], b: &mut [f64], x: &mut [f64], n: usize) {
|
||||||
|
lineqs_nr(a, b, x, n, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 使用高斯消元法(带部分选主元)求解线性方程组 A*X = B。
|
||||||
|
///
|
||||||
|
/// 支持物理维度大于逻辑维度的情况。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `a` - 系数矩阵,物理大小为 nr×nr(列优先存储,会被修改)
|
||||||
|
/// - `b` - 右端向量,物理大小为 nr(会被修改)
|
||||||
|
/// - `x` - 解向量,物理大小为 nr(输出)
|
||||||
|
/// - `n` - 实际方程数(逻辑维度)
|
||||||
|
/// - `nr` - 物理维度
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// SUBROUTINE LINEQS(A,B,X,N,NR)
|
||||||
|
/// DIMENSION A(NR,NR),B(NR),X(NR),D(MLEVEL),IP(MLEVEL)
|
||||||
|
/// ...
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # 注意
|
||||||
|
///
|
||||||
|
/// - 矩阵 A 和向量 B 在求解过程中会被修改
|
||||||
|
pub fn lineqs_nr(a: &mut [f64], b: &mut [f64], x: &mut [f64], n: usize, nr: usize) {
|
||||||
|
// 特殊情况:2×2 系统,直接求解
|
||||||
|
if n == 2 {
|
||||||
|
let a11 = a[0];
|
||||||
|
let a12 = a[nr]; // Fortran 列优先: A(1,2) = A[0 + nr*1]
|
||||||
|
let a21 = a[1]; // Fortran 列优先: A(2,1) = A[1 + nr*0]
|
||||||
|
let a22 = a[nr + 1];
|
||||||
|
|
||||||
|
let det = a11 * a22 - a12 * a21;
|
||||||
|
x[0] = (a22 * b[0] - a12 * b[1]) / det;
|
||||||
|
x[1] = (b[1] - a21 * x[0]) / a22;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 工作数组
|
||||||
|
let mut d = vec![0.0; n];
|
||||||
|
let mut ip = vec![0usize; n];
|
||||||
|
|
||||||
|
// LU 分解(带部分选主元)
|
||||||
|
for i in 0..n {
|
||||||
|
// 复制第 i 列到 d
|
||||||
|
for j in 0..n {
|
||||||
|
d[j] = a[j + i * nr];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 前向消元
|
||||||
|
if i >= 1 {
|
||||||
|
for j in 0..i {
|
||||||
|
let it = ip[j];
|
||||||
|
a[j + i * nr] = d[it];
|
||||||
|
d[it] = d[j];
|
||||||
|
|
||||||
|
for k in (j + 1)..n {
|
||||||
|
d[k] = d[k] - a[k + j * nr] * a[j + i * nr];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选主元
|
||||||
|
let mut am = d[i].abs();
|
||||||
|
ip[i] = i;
|
||||||
|
for k in i..n {
|
||||||
|
if am < d[k].abs() {
|
||||||
|
ip[i] = k;
|
||||||
|
am = d[k].abs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 交换行
|
||||||
|
let it = ip[i];
|
||||||
|
a[i + i * nr] = d[it];
|
||||||
|
d[it] = d[i];
|
||||||
|
|
||||||
|
// 计算乘数
|
||||||
|
if i + 1 < n {
|
||||||
|
for k in (i + 1)..n {
|
||||||
|
a[k + i * nr] = d[k] / a[i + i * nr];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 前向替换(处理右端向量)
|
||||||
|
for i in 0..n {
|
||||||
|
let it = ip[i];
|
||||||
|
x[i] = b[it];
|
||||||
|
b[it] = b[i];
|
||||||
|
|
||||||
|
if i + 1 < n {
|
||||||
|
for j in (i + 1)..n {
|
||||||
|
b[j] = b[j] - a[j + i * nr] * x[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后向替换
|
||||||
|
for i in 0..n {
|
||||||
|
let k = n - 1 - i;
|
||||||
|
let mut sum = 0.0;
|
||||||
|
|
||||||
|
if k + 1 < n {
|
||||||
|
for j in (k + 1)..n {
|
||||||
|
sum = sum + a[k + j * nr] * x[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x[k] = (x[k] - sum) / a[k + k * nr];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lineqs_2x2() {
|
||||||
|
// 2×2 系统
|
||||||
|
// [2, 1] [x1] [5]
|
||||||
|
// [1, 3] [x2] = [7]
|
||||||
|
// 解:x1 = 1.6, x2 = 1.8
|
||||||
|
let mut a = vec![2.0, 1.0, 1.0, 3.0]; // 列优先
|
||||||
|
let mut b = vec![5.0, 7.0];
|
||||||
|
let mut x = vec![0.0; 2];
|
||||||
|
|
||||||
|
lineqs(&mut a, &mut b, &mut x, 2);
|
||||||
|
|
||||||
|
assert!((x[0] - 1.6).abs() < 1e-10);
|
||||||
|
assert!((x[1] - 1.8).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lineqs_3x3() {
|
||||||
|
// 3×3 系统
|
||||||
|
// [1, 2, 3] [x1] [6]
|
||||||
|
// [4, 5, 6] [x2] = [15]
|
||||||
|
// [7, 8, 10] [x3] [25]
|
||||||
|
// 解:x1 = 1, x2 = 1, x3 = 1
|
||||||
|
// Fortran 列优先存储
|
||||||
|
let mut a = vec![1.0, 4.0, 7.0, 2.0, 5.0, 8.0, 3.0, 6.0, 10.0];
|
||||||
|
let mut b = vec![6.0, 15.0, 25.0];
|
||||||
|
let mut x = vec![0.0; 3];
|
||||||
|
|
||||||
|
lineqs(&mut a, &mut b, &mut x, 3);
|
||||||
|
|
||||||
|
assert!((x[0] - 1.0).abs() < 1e-10);
|
||||||
|
assert!((x[1] - 1.0).abs() < 1e-10);
|
||||||
|
assert!((x[2] - 1.0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lineqs_identity() {
|
||||||
|
// 单位矩阵
|
||||||
|
let mut a = vec![1.0, 0.0, 0.0, 1.0];
|
||||||
|
let mut b = vec![3.0, 4.0];
|
||||||
|
let mut x = vec![0.0; 2];
|
||||||
|
|
||||||
|
lineqs(&mut a, &mut b, &mut x, 2);
|
||||||
|
|
||||||
|
assert!((x[0] - 3.0).abs() < 1e-10);
|
||||||
|
assert!((x[1] - 4.0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lineqs_diagonal() {
|
||||||
|
// 对角矩阵
|
||||||
|
let mut a = vec![2.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 4.0];
|
||||||
|
let mut b = vec![6.0, 9.0, 16.0];
|
||||||
|
let mut x = vec![0.0; 3];
|
||||||
|
|
||||||
|
lineqs(&mut a, &mut b, &mut x, 3);
|
||||||
|
|
||||||
|
assert!((x[0] - 3.0).abs() < 1e-10);
|
||||||
|
assert!((x[1] - 3.0).abs() < 1e-10);
|
||||||
|
assert!((x[2] - 4.0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lineqs_pivoting() {
|
||||||
|
// 需要选主元的系统
|
||||||
|
// [0.001, 1] [x1] [1]
|
||||||
|
// [1, 1] [x2] = [2]
|
||||||
|
// 解:x1 ≈ 1.001, x2 ≈ 0.999
|
||||||
|
let mut a = vec![0.001, 1.0, 1.0, 1.0];
|
||||||
|
let mut b = vec![1.0, 2.0];
|
||||||
|
let mut x = vec![0.0; 2];
|
||||||
|
|
||||||
|
lineqs(&mut a, &mut b, &mut x, 2);
|
||||||
|
|
||||||
|
// 直接验证 A*X = B
|
||||||
|
let res1 = 0.001 * x[0] + 1.0 * x[1];
|
||||||
|
let res2 = 1.0 * x[0] + 1.0 * x[1];
|
||||||
|
assert!((res1 - 1.0).abs() < 1e-10, "First equation: {} != 1", res1);
|
||||||
|
assert!((res2 - 2.0).abs() < 1e-10, "Second equation: {} != 2", res2);
|
||||||
|
}
|
||||||
|
}
|
||||||
170
src/math/matinv.rs
Normal file
170
src/math/matinv.rs
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
//! 矩阵求逆 - LU 分解法
|
||||||
|
//!
|
||||||
|
//! 原始 Fortran: matinv.f
|
||||||
|
|
||||||
|
/// 矩阵求逆 (LU 分解法)
|
||||||
|
///
|
||||||
|
/// 原地求逆,输入矩阵被其逆矩阵替换
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
/// - `a`: N x N 矩阵 (按行优先存储),调用后被逆矩阵替换
|
||||||
|
/// - `n`: 实际矩阵大小
|
||||||
|
///
|
||||||
|
/// # 说明
|
||||||
|
/// 使用 Crout 的 LU 分解算法,无需选主元
|
||||||
|
pub fn matinv(a: &mut [f64], n: usize) {
|
||||||
|
let nr = n; // 实际大小等于最大大小
|
||||||
|
|
||||||
|
// 阶段 1: LU 分解
|
||||||
|
// 下三角部分存储 L (对角线为 1)
|
||||||
|
// 上三角部分存储 U
|
||||||
|
for i in 2..=n {
|
||||||
|
let im1 = i - 1;
|
||||||
|
for j in 1..=im1 {
|
||||||
|
let jm1 = j - 1;
|
||||||
|
let div = a[(j - 1) * nr + (j - 1)];
|
||||||
|
let mut sum = 0.0;
|
||||||
|
if jm1 >= 1 {
|
||||||
|
for k in 1..=jm1 {
|
||||||
|
sum += a[(i - 1) * nr + (k - 1)] * a[(k - 1) * nr + (j - 1)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a[(i - 1) * nr + (j - 1)] = (a[(i - 1) * nr + (j - 1)] - sum) / div;
|
||||||
|
}
|
||||||
|
for j in i..=n {
|
||||||
|
let mut sum = 0.0;
|
||||||
|
for k in 1..=im1 {
|
||||||
|
sum += a[(i - 1) * nr + (k - 1)] * a[(k - 1) * nr + (j - 1)];
|
||||||
|
}
|
||||||
|
a[(i - 1) * nr + (j - 1)] -= sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 阶段 2: U^-1 的下三角部分
|
||||||
|
for ii in 2..=n {
|
||||||
|
let i = n + 2 - ii;
|
||||||
|
let im1 = i - 1;
|
||||||
|
if im1 >= 1 {
|
||||||
|
for jj in 1..=im1 {
|
||||||
|
let j = i - jj;
|
||||||
|
let jp1 = j + 1;
|
||||||
|
let mut sum = 0.0;
|
||||||
|
if jp1 <= im1 {
|
||||||
|
for k in jp1..=im1 {
|
||||||
|
sum += a[(i - 1) * nr + (k - 1)] * a[(k - 1) * nr + (j - 1)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a[(i - 1) * nr + (j - 1)] = -a[(i - 1) * nr + (j - 1)] - sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 阶段 3: U^-1 的对角线和上三角部分
|
||||||
|
for ii in 1..=n {
|
||||||
|
let i = n + 1 - ii;
|
||||||
|
let div = a[(i - 1) * nr + (i - 1)];
|
||||||
|
let ip1 = i + 1;
|
||||||
|
if ip1 <= n {
|
||||||
|
for jj in ip1..=n {
|
||||||
|
let j = n + ip1 - jj;
|
||||||
|
let mut sum = 0.0;
|
||||||
|
for k in ip1..=j {
|
||||||
|
sum += a[(i - 1) * nr + (k - 1)] * a[(k - 1) * nr + (j - 1)];
|
||||||
|
}
|
||||||
|
a[(i - 1) * nr + (j - 1)] = -sum / div;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a[(i - 1) * nr + (i - 1)] = 1.0 / div;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 阶段 4: 计算 A^-1 = U^-1 * L^-1
|
||||||
|
// Fortran 的 GOTO 结构分析:
|
||||||
|
// - 如果 J >= I (上三角或对角线): K0=J, SUM=A(I,J), 如果 K0!=N 则累加
|
||||||
|
// - 如果 J < I (下三角): K0=I, SUM=0, 然后累加
|
||||||
|
for i in 1..=n {
|
||||||
|
for j in 1..=n {
|
||||||
|
let sum = if j >= i {
|
||||||
|
// 上三角部分 (包括对角线)
|
||||||
|
let k0 = j;
|
||||||
|
let mut s = a[(i - 1) * nr + (k0 - 1)];
|
||||||
|
if k0 < n {
|
||||||
|
for k in (k0 + 1)..=n {
|
||||||
|
s += a[(i - 1) * nr + (k - 1)] * a[(k - 1) * nr + (j - 1)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s
|
||||||
|
} else {
|
||||||
|
// 下三角部分: J < I
|
||||||
|
let k0 = i;
|
||||||
|
let mut s = 0.0;
|
||||||
|
for k in k0..=n {
|
||||||
|
s += a[(i - 1) * nr + (k - 1)] * a[(k - 1) * nr + (j - 1)];
|
||||||
|
}
|
||||||
|
s
|
||||||
|
};
|
||||||
|
a[(i - 1) * nr + (j - 1)] = sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_matinv_identity() {
|
||||||
|
// 单位矩阵求逆应得到单位矩阵
|
||||||
|
let mut a = vec![1.0, 0.0, 0.0, 1.0];
|
||||||
|
matinv(&mut a, 2);
|
||||||
|
assert!((a[0] - 1.0).abs() < 1e-10);
|
||||||
|
assert!(a[1].abs() < 1e-10);
|
||||||
|
assert!(a[2].abs() < 1e-10);
|
||||||
|
assert!((a[3] - 1.0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_matinv_2x2() {
|
||||||
|
// [[2, 1], [1, 2]] 的逆矩阵是 [[2/3, -1/3], [-1/3, 2/3]]
|
||||||
|
let mut a = vec![2.0, 1.0, 1.0, 2.0];
|
||||||
|
matinv(&mut a, 2);
|
||||||
|
assert!((a[0] - 2.0/3.0).abs() < 1e-10);
|
||||||
|
assert!((a[1] + 1.0/3.0).abs() < 1e-10);
|
||||||
|
assert!((a[2] + 1.0/3.0).abs() < 1e-10);
|
||||||
|
assert!((a[3] - 2.0/3.0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_matinv_3x3() {
|
||||||
|
// 测试 3x3 矩阵
|
||||||
|
let mut a = vec![
|
||||||
|
1.0, 2.0, 3.0,
|
||||||
|
0.0, 1.0, 4.0,
|
||||||
|
5.0, 6.0, 0.0
|
||||||
|
];
|
||||||
|
let original = a.clone();
|
||||||
|
matinv(&mut a, 3);
|
||||||
|
|
||||||
|
// 验证 A * A^-1 = I
|
||||||
|
let mut result = vec![0.0; 9];
|
||||||
|
for i in 0..3 {
|
||||||
|
for j in 0..3 {
|
||||||
|
for k in 0..3 {
|
||||||
|
result[i * 3 + j] += original[i * 3 + k] * a[k * 3 + j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查对角线元素是否接近 1
|
||||||
|
for i in 0..3 {
|
||||||
|
assert!((result[i * 3 + i] - 1.0).abs() < 1e-8, "Diagonal element {} should be 1", i);
|
||||||
|
}
|
||||||
|
// 检查非对角线元素是否接近 0
|
||||||
|
for i in 0..3 {
|
||||||
|
for j in 0..3 {
|
||||||
|
if i != j {
|
||||||
|
assert!(result[i * 3 + j].abs() < 1e-8, "Off-diagonal [{},{}] should be 0", i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
149
src/math/meanop.rs
Normal file
149
src/math/meanop.rs
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
//! Rosseland 和 Planck 平均不透明度计算。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `meanop.f`
|
||||||
|
|
||||||
|
use crate::state::constants::{HK, MFREQ, MFREQC};
|
||||||
|
use crate::state::FrqAll;
|
||||||
|
use crate::state::FreAux;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// MEANOP - 平均不透明度
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 计算 Rosseland 和 Planck 平均不透明度。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `t` - 温度 (K)
|
||||||
|
/// - `abso` - 吸收系数数组 (所有显式频率点)
|
||||||
|
/// - `scat` - 散射系数数组
|
||||||
|
/// - `nfreqc` - 连续谱频率点数
|
||||||
|
/// - `frqall` - 频率相关数组 (包含 FREQ, W)
|
||||||
|
/// - `freaux` - 频率辅助数组 (包含 BNUE)
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// 元组 `(opros, oppla)`:
|
||||||
|
/// - `opros` - Rosseland 平均不透明度 (每 cm³)
|
||||||
|
/// - `oppla` - Planck 平均不透明度 (每 cm³)
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// SUBROUTINE MEANOP(T,ABSO,SCAT,OPROS,OPPLA)
|
||||||
|
/// INCLUDE 'BASICS.FOR'
|
||||||
|
/// INCLUDE 'MODELQ.FOR'
|
||||||
|
/// INCLUDE 'ATOMIC.FOR'
|
||||||
|
/// ...
|
||||||
|
/// HKT=HK/T
|
||||||
|
/// DO IJ=1,NFREQC
|
||||||
|
/// FR=FREQ(IJ)
|
||||||
|
/// X=HKT*FR
|
||||||
|
/// ...
|
||||||
|
/// END DO
|
||||||
|
/// OPROS=SUMDB/ABR
|
||||||
|
/// OPPLA=ABP/SUMB
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
pub fn meanop(
|
||||||
|
t: f64,
|
||||||
|
abso: &[f64],
|
||||||
|
scat: &[f64],
|
||||||
|
nfreqc: usize,
|
||||||
|
frqall: &FrqAll,
|
||||||
|
freaux: &FreAux,
|
||||||
|
) -> (f64, f64) {
|
||||||
|
let hkt = HK / t;
|
||||||
|
|
||||||
|
let mut abr = 0.0;
|
||||||
|
let mut sumdb = 0.0;
|
||||||
|
let mut abp = 0.0;
|
||||||
|
let mut sumb = 0.0;
|
||||||
|
|
||||||
|
for ij in 0..nfreqc {
|
||||||
|
let fr = frqall.freq[ij];
|
||||||
|
let x = hkt * fr;
|
||||||
|
let x = if x > 150.0 { 150.0 } else { x };
|
||||||
|
|
||||||
|
let ex = x.exp();
|
||||||
|
let e1 = 1.0 / (ex - 1.0);
|
||||||
|
|
||||||
|
// Planck 函数: B_nu * E1 * W
|
||||||
|
let plan = freaux.bnue[ij] * e1 * frqall.w[ij];
|
||||||
|
// Planck 函数导数
|
||||||
|
let dplan = plan * hkt * fr * ex * e1;
|
||||||
|
|
||||||
|
abr = abr + dplan / abso[ij];
|
||||||
|
abp = abp + plan * (abso[ij] - scat[ij]);
|
||||||
|
sumdb = sumdb + dplan;
|
||||||
|
sumb = sumb + plan;
|
||||||
|
}
|
||||||
|
|
||||||
|
let opros = sumdb / abr;
|
||||||
|
let oppla = abp / sumb;
|
||||||
|
|
||||||
|
(opros, oppla)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn create_test_data() -> (FrqAll, FreAux) {
|
||||||
|
let mut frqall = FrqAll::default();
|
||||||
|
let mut freaux = FreAux::default();
|
||||||
|
|
||||||
|
// 设置简单的测试频率网格
|
||||||
|
for i in 0..10 {
|
||||||
|
let fr = 1e14 + i as f64 * 1e13;
|
||||||
|
frqall.freq[i] = fr;
|
||||||
|
frqall.w[i] = 1.0 / 10.0; // 等权重
|
||||||
|
// BNUE = BN * fr^3 = 1.4743e-2 * fr^3
|
||||||
|
freaux.bnue[i] = 1.4743e-2 * fr * fr * fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
(frqall, freaux)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_meanop_basic() {
|
||||||
|
let (frqall, freaux) = create_test_data();
|
||||||
|
|
||||||
|
let nfreqc = 10;
|
||||||
|
let t = 10000.0; // 10000 K
|
||||||
|
|
||||||
|
// 创建简单的吸收和散射数组
|
||||||
|
let mut abso: Vec<f64> = vec![1e-10; MFREQ];
|
||||||
|
let mut scat: Vec<f64> = vec![1e-12; MFREQ];
|
||||||
|
|
||||||
|
// 设置前 nfreqc 个点
|
||||||
|
for i in 0..nfreqc {
|
||||||
|
abso[i] = 1e-10;
|
||||||
|
scat[i] = 1e-12;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (opros, oppla) = meanop(t, &abso, &scat, nfreqc, &frqall, &freaux);
|
||||||
|
|
||||||
|
// 结果应该是正数
|
||||||
|
assert!(opros > 0.0, "Rosseland opacity should be positive");
|
||||||
|
assert!(oppla > 0.0, "Planck opacity should be positive");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_meanop_uniform_absorption() {
|
||||||
|
let (frqall, freaux) = create_test_data();
|
||||||
|
|
||||||
|
let nfreqc = 10;
|
||||||
|
let t = 10000.0;
|
||||||
|
|
||||||
|
// 均匀吸收系数
|
||||||
|
let abso: Vec<f64> = vec![1e-10; MFREQ];
|
||||||
|
let scat: Vec<f64> = vec![0.0; MFREQ];
|
||||||
|
|
||||||
|
let (opros, oppla) = meanop(t, &abso, &scat, nfreqc, &frqall, &freaux);
|
||||||
|
|
||||||
|
// 均匀吸收时,Rosseland 和 Planck 平均应该接近
|
||||||
|
assert!(opros > 0.0);
|
||||||
|
assert!(oppla > 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,8 +1,13 @@
|
|||||||
//! 数学工具函数,重构自 TLUSTY Fortran。
|
//! 数学工具函数,重构自 TLUSTY Fortran。
|
||||||
|
|
||||||
|
mod alifr3;
|
||||||
|
mod alifr6;
|
||||||
|
mod alifrk;
|
||||||
|
mod allardt;
|
||||||
mod angset;
|
mod angset;
|
||||||
mod betah;
|
mod betah;
|
||||||
mod bkhsgo;
|
mod bkhsgo;
|
||||||
|
mod bpopf;
|
||||||
mod butler;
|
mod butler;
|
||||||
mod carbon;
|
mod carbon;
|
||||||
mod ceh12;
|
mod ceh12;
|
||||||
@ -10,7 +15,16 @@ mod cion;
|
|||||||
mod ckoest;
|
mod ckoest;
|
||||||
mod collhe;
|
mod collhe;
|
||||||
mod cross;
|
mod cross;
|
||||||
|
mod ctdata;
|
||||||
|
mod cubic;
|
||||||
mod dielrc;
|
mod dielrc;
|
||||||
|
mod divstr;
|
||||||
|
mod dopgam;
|
||||||
|
mod dmder;
|
||||||
|
mod dwnfr;
|
||||||
|
mod dwnfr0;
|
||||||
|
mod dwnfr1;
|
||||||
|
mod emat;
|
||||||
mod erfcx;
|
mod erfcx;
|
||||||
mod expo;
|
mod expo;
|
||||||
mod expint;
|
mod expint;
|
||||||
@ -22,45 +36,81 @@ mod gamsp;
|
|||||||
mod gfree;
|
mod gfree;
|
||||||
mod gaunt;
|
mod gaunt;
|
||||||
mod gntk;
|
mod gntk;
|
||||||
|
mod gridp;
|
||||||
mod grcor;
|
mod grcor;
|
||||||
mod hephot;
|
mod hephot;
|
||||||
mod hidalg;
|
mod hidalg;
|
||||||
mod indexx;
|
mod indexx;
|
||||||
|
mod inicom;
|
||||||
|
mod interp;
|
||||||
|
mod inthyd;
|
||||||
|
mod intlem;
|
||||||
|
mod intxen;
|
||||||
mod irc;
|
mod irc;
|
||||||
mod interpolate;
|
mod interpolate;
|
||||||
mod laguer;
|
mod laguer;
|
||||||
|
mod levsol;
|
||||||
|
mod lineqs;
|
||||||
mod locate;
|
mod locate;
|
||||||
|
mod matinv;
|
||||||
|
mod meanop;
|
||||||
mod minv3;
|
mod minv3;
|
||||||
mod quartc;
|
mod odfhst;
|
||||||
|
mod pfcno;
|
||||||
mod pffe;
|
mod pffe;
|
||||||
|
mod prdini;
|
||||||
|
mod quartc;
|
||||||
mod pfni;
|
mod pfni;
|
||||||
mod pfspec;
|
mod pfspec;
|
||||||
|
mod psolve;
|
||||||
mod quit;
|
mod quit;
|
||||||
mod raph;
|
mod raph;
|
||||||
|
mod ratmal;
|
||||||
|
mod rayleigh;
|
||||||
|
mod rayset;
|
||||||
mod reiman;
|
mod reiman;
|
||||||
|
mod rte_sc;
|
||||||
|
mod rtefe2;
|
||||||
|
mod rtesol;
|
||||||
mod sbfch;
|
mod sbfch;
|
||||||
mod sbfhe1;
|
mod sbfhe1;
|
||||||
mod sbfhmi;
|
mod sbfhmi;
|
||||||
mod sbfhmi_old;
|
mod sbfhmi_old;
|
||||||
mod sbfoh;
|
mod sbfoh;
|
||||||
mod sghe12;
|
mod sghe12;
|
||||||
|
mod sgmer;
|
||||||
mod sffhmi;
|
mod sffhmi;
|
||||||
|
mod sffhmi_add;
|
||||||
mod spsigk;
|
mod spsigk;
|
||||||
mod stark0;
|
mod stark0;
|
||||||
|
mod starka;
|
||||||
mod szirc;
|
mod szirc;
|
||||||
mod tiopf;
|
mod tiopf;
|
||||||
|
mod tdpini;
|
||||||
|
mod traini;
|
||||||
mod tridag;
|
mod tridag;
|
||||||
mod ubeta;
|
mod ubeta;
|
||||||
mod verner;
|
mod verner;
|
||||||
|
mod vern16;
|
||||||
|
mod vern18;
|
||||||
|
mod vern20;
|
||||||
|
mod vern26;
|
||||||
mod voigt;
|
mod voigt;
|
||||||
mod voigte;
|
mod voigte;
|
||||||
mod xk2dop;
|
|
||||||
mod wn;
|
mod wn;
|
||||||
|
mod wnstor;
|
||||||
|
mod xk2dop;
|
||||||
mod ylintp;
|
mod ylintp;
|
||||||
|
mod zmrho;
|
||||||
|
|
||||||
|
pub use alifr3::{alifr3, Alifr3Params};
|
||||||
|
pub use alifr6::{alifr6, Alifr6Params, Alifr6State};
|
||||||
|
pub use alifrk::{alifrk, AlifrkParams, AlifrkState};
|
||||||
|
pub use allardt::{allardt, AllardData};
|
||||||
pub use angset::angset;
|
pub use angset::angset;
|
||||||
pub use betah::betah;
|
pub use betah::betah;
|
||||||
pub use bkhsgo::bkhsgo;
|
pub use bkhsgo::bkhsgo;
|
||||||
|
pub use bpopf::{bpopf, BpopfParams};
|
||||||
pub use butler::butler;
|
pub use butler::butler;
|
||||||
pub use carbon::carbon;
|
pub use carbon::carbon;
|
||||||
pub use ceh12::ceh12;
|
pub use ceh12::ceh12;
|
||||||
@ -68,7 +118,16 @@ pub use cion::cion;
|
|||||||
pub use ckoest::ckoest;
|
pub use ckoest::ckoest;
|
||||||
pub use collhe::collhe;
|
pub use collhe::collhe;
|
||||||
pub use cross::{cross, crossd};
|
pub use cross::{cross, crossd};
|
||||||
|
pub use ctdata::{hction, hctrecom, CTION, CTRECOMB};
|
||||||
|
pub use cubic::{cubic, CubicCon};
|
||||||
pub use dielrc::dielrc;
|
pub use dielrc::dielrc;
|
||||||
|
pub use divstr::divstr;
|
||||||
|
pub use dopgam::dopgam;
|
||||||
|
pub use dmder::{dmder, DepthDeriv};
|
||||||
|
pub use dwnfr::dwnfr;
|
||||||
|
pub use dwnfr0::dwnfr0;
|
||||||
|
pub use dwnfr1::dwnfr1;
|
||||||
|
pub use emat::emat;
|
||||||
pub use erfcx::{erfcin, erfcx};
|
pub use erfcx::{erfcin, erfcx};
|
||||||
pub use expo::expo;
|
pub use expo::expo;
|
||||||
pub use expint::{eint, expinx};
|
pub use expint::{eint, expinx};
|
||||||
@ -80,38 +139,72 @@ pub use gamsp::gamsp;
|
|||||||
pub use gfree::{gfree0, gfreed};
|
pub use gfree::{gfree0, gfreed};
|
||||||
pub use gaunt::gaunt;
|
pub use gaunt::gaunt;
|
||||||
pub use gntk::gntk;
|
pub use gntk::gntk;
|
||||||
|
pub use gridp::gridp;
|
||||||
pub use grcor::grcor;
|
pub use grcor::grcor;
|
||||||
pub use hephot::hephot;
|
pub use hephot::hephot;
|
||||||
pub use hidalg::hidalg;
|
pub use hidalg::hidalg;
|
||||||
pub use indexx::indexx;
|
pub use indexx::indexx;
|
||||||
|
pub use inicom::inicom;
|
||||||
|
pub use interp::interp;
|
||||||
|
pub use inthyd::inthyd;
|
||||||
|
pub use intlem::intlem;
|
||||||
|
pub use intxen::intxen;
|
||||||
pub use irc::irc;
|
pub use irc::irc;
|
||||||
pub use interpolate::{lagran, yint};
|
pub use interpolate::{lagran, yint};
|
||||||
pub use laguer::laguer;
|
pub use laguer::laguer;
|
||||||
|
pub use levsol::levsol;
|
||||||
|
pub use lineqs::{lineqs, lineqs_nr};
|
||||||
pub use locate::locate;
|
pub use locate::locate;
|
||||||
|
pub use matinv::matinv;
|
||||||
|
pub use meanop::meanop;
|
||||||
pub use minv3::minv3;
|
pub use minv3::minv3;
|
||||||
pub use quartc::quartc;
|
pub use odfhst::odfhst;
|
||||||
|
pub use pfcno::pfcno;
|
||||||
pub use pffe::pffe;
|
pub use pffe::pffe;
|
||||||
|
pub use prdini::prdini;
|
||||||
pub use pfni::pfni;
|
pub use pfni::pfni;
|
||||||
pub use pfspec::pfspec;
|
pub use pfspec::pfspec;
|
||||||
|
pub use psolve::psolve;
|
||||||
|
pub use quartc::quartc;
|
||||||
pub use quit::{quit, quit_error};
|
pub use quit::{quit, quit_error};
|
||||||
pub use raph::raph;
|
pub use raph::raph;
|
||||||
|
pub use ratmal::ratmal;
|
||||||
|
pub use rayleigh::{
|
||||||
|
rayleigh, rayleigh_h2_cross_section, rayleigh_h_cross_section, rayleigh_he_cross_section,
|
||||||
|
RayleighParams, RayleighResult,
|
||||||
|
};
|
||||||
|
pub use rayset::rayset;
|
||||||
pub use reiman::reiman;
|
pub use reiman::reiman;
|
||||||
|
pub use rte_sc::rte_sc;
|
||||||
|
pub use rtefe2::rtefe2;
|
||||||
|
pub use rtesol::rtesol;
|
||||||
pub use sbfch::sbfch;
|
pub use sbfch::sbfch;
|
||||||
pub use sbfhe1::sbfhe1;
|
pub use sbfhe1::sbfhe1;
|
||||||
pub use sbfhmi::sbfhmi;
|
pub use sbfhmi::sbfhmi;
|
||||||
pub use sbfhmi_old::sbfhmi_old;
|
pub use sbfhmi_old::sbfhmi_old;
|
||||||
pub use sbfoh::sbfoh;
|
pub use sbfoh::sbfoh;
|
||||||
pub use sghe12::sghe12;
|
pub use sghe12::sghe12;
|
||||||
|
pub use sgmer::{sgmer0, sgmer1, sgmerd};
|
||||||
pub use sffhmi::sffhmi;
|
pub use sffhmi::sffhmi;
|
||||||
|
pub use sffhmi_add::sffhmi_add;
|
||||||
pub use spsigk::spsigk;
|
pub use spsigk::spsigk;
|
||||||
pub use stark0::stark0;
|
pub use stark0::stark0;
|
||||||
|
pub use starka::starka;
|
||||||
pub use szirc::szirc;
|
pub use szirc::szirc;
|
||||||
pub use tiopf::tiopf;
|
pub use tiopf::tiopf;
|
||||||
|
pub use tdpini::tdpini;
|
||||||
|
pub use traini::traini;
|
||||||
pub use tridag::tridag;
|
pub use tridag::tridag;
|
||||||
pub use ubeta::ubeta;
|
pub use ubeta::ubeta;
|
||||||
pub use verner::verner;
|
pub use verner::verner;
|
||||||
|
pub use vern16::vern16;
|
||||||
|
pub use vern18::vern18;
|
||||||
|
pub use vern20::vern20;
|
||||||
|
pub use vern26::vern26;
|
||||||
pub use voigt::voigt;
|
pub use voigt::voigt;
|
||||||
pub use voigte::voigte;
|
pub use voigte::voigte;
|
||||||
pub use wn::wn;
|
pub use wn::wn;
|
||||||
|
pub use wnstor::wnstor;
|
||||||
pub use xk2dop::xk2dop;
|
pub use xk2dop::xk2dop;
|
||||||
pub use ylintp::ylintp;
|
pub use ylintp::ylintp;
|
||||||
|
pub use zmrho::zmrho;
|
||||||
|
|||||||
159
src/math/odfhst.rs
Normal file
159
src/math/odfhst.rs
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
//! ODF Stark 展宽辅助函数。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `odfhst.f`
|
||||||
|
//!
|
||||||
|
//! 用于 ODF1 的辅助例程,替代多次调用 STARKA。
|
||||||
|
|
||||||
|
use crate::state::constants::{TWO, UN};
|
||||||
|
use crate::state::model::StrAux;
|
||||||
|
use crate::state::odfpar::MFRO;
|
||||||
|
|
||||||
|
/// ODF Stark 展宽辅助函数。
|
||||||
|
///
|
||||||
|
/// 用于 ODF1 的辅助例程,计算 Stark 展宽的线轮廓。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `n` - 频率点数
|
||||||
|
/// * `fxk` - 线宽参数
|
||||||
|
/// * `fid` - 振子强度
|
||||||
|
/// * `wp` - 权重
|
||||||
|
/// * `wl` - 波长
|
||||||
|
/// * `alam` - 频率数组
|
||||||
|
/// * `straux` - Stark 展宽参数
|
||||||
|
/// * `sg` - 输出:Stark 展宽数组
|
||||||
|
///
|
||||||
|
/// # 示例
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use tlusty_rust::math::odfhst::odfhst;
|
||||||
|
/// use tlusty_rust::state::model::StrAux;
|
||||||
|
///
|
||||||
|
/// let straux = StrAux::default();
|
||||||
|
/// let alam = vec![1.0, 2.0, 3.0];
|
||||||
|
/// let mut sg = vec![0.0; 3];
|
||||||
|
///
|
||||||
|
/// odfhst(3, 1.0, 0.5, 1.0, 1215.0, &alam, &straux, &mut sg);
|
||||||
|
/// ```
|
||||||
|
pub fn odfhst(
|
||||||
|
n: usize,
|
||||||
|
fxk: f64,
|
||||||
|
fid: f64,
|
||||||
|
wp: f64,
|
||||||
|
wl: f64,
|
||||||
|
alam: &[f64],
|
||||||
|
straux: &StrAux,
|
||||||
|
sg: &mut [f64],
|
||||||
|
) {
|
||||||
|
// 常数参数
|
||||||
|
const F0: f64 = -0.5758228;
|
||||||
|
const F1: f64 = 0.4796232;
|
||||||
|
const F2: f64 = 0.07209481;
|
||||||
|
const AL: f64 = 1.26;
|
||||||
|
|
||||||
|
const SD: f64 = 0.5641895;
|
||||||
|
const SLO: f64 = -2.5;
|
||||||
|
const THRA: f64 = 1.5;
|
||||||
|
const BL1: f64 = 1.14;
|
||||||
|
const BL2: f64 = 11.4;
|
||||||
|
|
||||||
|
const SAC: f64 = 0.08;
|
||||||
|
const THR: f64 = THRA * TWO;
|
||||||
|
|
||||||
|
let betad = straux.betad;
|
||||||
|
let adh = straux.adh;
|
||||||
|
let divh = straux.divh;
|
||||||
|
|
||||||
|
// 防止除零
|
||||||
|
let betad1 = if betad.abs() > 1e-30 { UN / betad } else { 0.0 };
|
||||||
|
let fxk1 = if fxk.abs() > 1e-30 { UN / fxk } else { 0.0 };
|
||||||
|
let fidwp = fid * wp;
|
||||||
|
|
||||||
|
// for a > 1 Doppler core + asymptotic Holtzmark wing with division point DIV
|
||||||
|
if adh > AL {
|
||||||
|
for ij in 0..n {
|
||||||
|
let beta = (alam[ij] - wl).abs() * fxk1;
|
||||||
|
let xd = beta * betad1;
|
||||||
|
|
||||||
|
let st = if xd <= divh {
|
||||||
|
SD * (-xd * xd).exp() * betad1
|
||||||
|
} else {
|
||||||
|
THR * beta.powf(SLO)
|
||||||
|
};
|
||||||
|
|
||||||
|
sg[ij] = st * fidwp;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// empirical formula for a < 1
|
||||||
|
for ij in 0..n {
|
||||||
|
let beta = (alam[ij] - wl).abs() * fxk1;
|
||||||
|
let xd = beta * betad1;
|
||||||
|
|
||||||
|
let st = if beta <= BL1 {
|
||||||
|
SAC
|
||||||
|
} else if beta < BL2 {
|
||||||
|
let xl = beta.ln();
|
||||||
|
let fl = (F0 * xl + F1) * xl;
|
||||||
|
F2 * fl.exp()
|
||||||
|
} else {
|
||||||
|
THR * beta.powf(SLO)
|
||||||
|
};
|
||||||
|
|
||||||
|
sg[ij] = st * fidwp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_odfhst_adh_gt_al() {
|
||||||
|
let mut straux = StrAux::default();
|
||||||
|
straux.adh = 2.0; // > AL = 1.26
|
||||||
|
straux.betad = 1.0;
|
||||||
|
straux.divh = 1.0;
|
||||||
|
|
||||||
|
let alam = vec![1200.0, 1215.0, 1230.0];
|
||||||
|
let mut sg = vec![0.0; 3];
|
||||||
|
|
||||||
|
odfhst(3, 1.0, 0.5, 1.0, 1215.0, &alam, &straux, &mut sg);
|
||||||
|
|
||||||
|
// 中间点(wl=1215.0)应该有最大值
|
||||||
|
assert!(sg[1] > sg[0]);
|
||||||
|
assert!(sg[1] > sg[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_odfhst_adh_lt_al() {
|
||||||
|
let mut straux = StrAux::default();
|
||||||
|
straux.adh = 0.5; // < AL = 1.26
|
||||||
|
straux.betad = 1.0;
|
||||||
|
straux.divh = 1.0;
|
||||||
|
|
||||||
|
let alam = vec![1200.0, 1215.0, 1230.0];
|
||||||
|
let mut sg = vec![0.0; 3];
|
||||||
|
|
||||||
|
odfhst(3, 1.0, 0.5, 1.0, 1215.0, &alam, &straux, &mut sg);
|
||||||
|
|
||||||
|
// 所有值应该是正的
|
||||||
|
for &s in &sg {
|
||||||
|
assert!(s >= 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_odfhst_zero_betad() {
|
||||||
|
let mut straux = StrAux::default();
|
||||||
|
straux.adh = 2.0;
|
||||||
|
straux.betad = 0.0; // 零值
|
||||||
|
straux.divh = 1.0;
|
||||||
|
|
||||||
|
let alam = vec![1200.0, 1215.0, 1230.0];
|
||||||
|
let mut sg = vec![0.0; 3];
|
||||||
|
|
||||||
|
// 不应该 panic
|
||||||
|
odfhst(3, 1.0, 0.5, 1.0, 1215.0, &alam, &straux, &mut sg);
|
||||||
|
}
|
||||||
|
}
|
||||||
296
src/math/pfcno.rs
Normal file
296
src/math/pfcno.rs
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
//! CNO 元素高电离态配分函数计算。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `pfcno.f`
|
||||||
|
//!
|
||||||
|
//! 支持以下离子的配分函数:
|
||||||
|
//! - H-like 离子: C VI, N VII, O VIII
|
||||||
|
//! - He-like 离子: N VI, O VII
|
||||||
|
//! - O VI (使用 Sparks & Fischel 1971 数据表)
|
||||||
|
|
||||||
|
use crate::state::constants::{BOLK, EH, NLMX};
|
||||||
|
|
||||||
|
/// CNO 元素高电离态配分函数。
|
||||||
|
///
|
||||||
|
/// 计算碳、氮、氧元素高电离态的配分函数。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `iat` - 原子序数 (6=碳, 7=氮, 8=氧)
|
||||||
|
/// * `izi` - 电离度 (电子数)
|
||||||
|
/// * `t` - 温度 (K)
|
||||||
|
/// * `ane` - 电子密度
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// 返回配分函数值 PF
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// 当输入参数超出支持范围时 panic
|
||||||
|
///
|
||||||
|
/// # 示例
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use tlusty_rust::math::pfcno;
|
||||||
|
/// // O VIII (H-like)
|
||||||
|
/// let pf = pfcno(8, 7, 50000.0, 1e12);
|
||||||
|
/// assert!(pf > 0.0);
|
||||||
|
/// ```
|
||||||
|
pub fn pfcno(iat: usize, izi: usize, t: f64, ane: f64) -> f64 {
|
||||||
|
// 参数常量
|
||||||
|
const P1: f64 = 0.1402;
|
||||||
|
const P2: f64 = 0.1285;
|
||||||
|
const P3: f64 = 1.0;
|
||||||
|
const P4: f64 = 3.15;
|
||||||
|
const P5: f64 = 4.0;
|
||||||
|
|
||||||
|
// 温度表 (×1000 K)
|
||||||
|
const TT: [f64; 35] = [
|
||||||
|
18., 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30.,
|
||||||
|
32., 34., 36., 38., 40., 42., 44., 46., 48.,
|
||||||
|
50., 55., 60., 65., 70., 75., 80., 85., 90., 95., 100., 125., 150.,
|
||||||
|
];
|
||||||
|
|
||||||
|
// 电子密度对数表
|
||||||
|
const PN: [f64; 10] = [-2., -1., 0., 1., 2., 3., 4., 5., 6., 7.];
|
||||||
|
|
||||||
|
// O VI 配分函数数据表 - P6A (温度 18-48, 1000 K)
|
||||||
|
const P6A: [f64; 24] = [
|
||||||
|
0.302, 0.302, 0.302, 0.303, 0.303, 0.304, 0.305, 0.306,
|
||||||
|
0.307, 0.308, 0.310, 0.312, 0.313, 0.318, 0.322, 0.327,
|
||||||
|
0.333, 0.339, 0.346, 0.353, 0.360, 0.367, 0.375, 0.394,
|
||||||
|
];
|
||||||
|
|
||||||
|
// O VI 配分函数数据表 - P6B (温度 50-150, 1000 K × 电子密度 -2 到 7)
|
||||||
|
const P6B: [[f64; 11]; 10] = [
|
||||||
|
// log(n_e * kT) = -2
|
||||||
|
[0.414, 0.413, 0.413, 0.413, 0.413, 0.413, 0.413, 0.413, 0.413, 0.413, 0.413],
|
||||||
|
// log(n_e * kT) = -1
|
||||||
|
[0.436, 0.433, 0.433, 0.432, 0.432, 0.432, 0.432, 0.432, 0.432, 0.432, 0.432],
|
||||||
|
// log(n_e * kT) = 0
|
||||||
|
[0.472, 0.458, 0.453, 0.451, 0.451, 0.451, 0.451, 0.451, 0.451, 0.451, 0.451],
|
||||||
|
// log(n_e * kT) = 1
|
||||||
|
[0.560, 0.499, 0.478, 0.471, 0.469, 0.468, 0.468, 0.468, 0.468, 0.468, 0.468],
|
||||||
|
// log(n_e * kT) = 2
|
||||||
|
[0.762, 0.593, 0.522, 0.497, 0.489, 0.486, 0.485, 0.485, 0.485, 0.485, 0.485],
|
||||||
|
// log(n_e * kT) = 3
|
||||||
|
[1.090, 0.782, 0.611, 0.539, 0.513, 0.505, 0.502, 0.501, 0.501, 0.501, 0.501],
|
||||||
|
// log(n_e * kT) = 4
|
||||||
|
[1.478, 1.070, 0.775, 0.615, 0.550, 0.527, 0.519, 0.517, 0.516, 0.516, 0.516],
|
||||||
|
// log(n_e * kT) = 5
|
||||||
|
[1.867, 1.408, 1.018, 0.749, 0.612, 0.557, 0.539, 0.533, 0.531, 0.530, 0.530],
|
||||||
|
// log(n_e * kT) = 6
|
||||||
|
[2.233, 1.752, 1.306, 0.944, 0.713, 0.604, 0.564, 0.550, 0.545, 0.544, 0.544],
|
||||||
|
// log(n_e * kT) = 7
|
||||||
|
[3.665, 3.166, 2.668, 2.176, 1.700, 1.269, 0.934, 0.735, 0.648, 0.616, 0.616],
|
||||||
|
];
|
||||||
|
|
||||||
|
// P6B 补充数据 (温度 125, 150)
|
||||||
|
const P6B_EXT: [[f64; 2]; 10] = [
|
||||||
|
[4.633, 4.133],
|
||||||
|
[3.633, 3.134],
|
||||||
|
[2.636, 2.146],
|
||||||
|
[1.674, 1.254],
|
||||||
|
[0.942, 0.763],
|
||||||
|
[0.0, 0.0], // 占位,实际不会用到
|
||||||
|
[0.0, 0.0],
|
||||||
|
[0.0, 0.0],
|
||||||
|
[0.0, 0.0],
|
||||||
|
[0.0, 0.0],
|
||||||
|
];
|
||||||
|
|
||||||
|
// 参数检查
|
||||||
|
if iat < 6 || iat > 8 || izi <= 5 {
|
||||||
|
panic!(
|
||||||
|
"PFCNO: 不支持此离子的配分函数计算: 原子序数={}, 电离度={}",
|
||||||
|
iat, izi
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 裸核情况
|
||||||
|
if izi > iat {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tk = BOLK * t;
|
||||||
|
let izit = iat - izi; // 剩余电子数
|
||||||
|
|
||||||
|
// 1. H-like 情况 (仅剩 1 个电子)
|
||||||
|
if izit == 0 {
|
||||||
|
let anel = ane.ln();
|
||||||
|
let aa = 0.09 * (anel / 6.0).exp() / t.sqrt();
|
||||||
|
let anel23 = (-2.0 / 3.0 * anel).exp();
|
||||||
|
let z = izi as f64;
|
||||||
|
let z2 = z * z;
|
||||||
|
let cbz = 2.0 * 8.59e14 * z.powi(3);
|
||||||
|
let e0kt = EH * z2 / tk;
|
||||||
|
|
||||||
|
let mut pf = 0.0;
|
||||||
|
for ii in 1..=NLMX {
|
||||||
|
let xn = ii as f64;
|
||||||
|
let xn2 = xn * xn;
|
||||||
|
|
||||||
|
let xkn = if xn <= 3.01 {
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
let xn1 = 1.0 / (xn + 1.0);
|
||||||
|
16.0 / 3.0 * xn * xn1 * xn1
|
||||||
|
};
|
||||||
|
|
||||||
|
let beta = cbz * xkn / xn2.powi(2) * anel23;
|
||||||
|
let x = (1.0 + P3 * aa).powf(P4);
|
||||||
|
let c1 = P1 * (x + P5 * (z - 1.0) * aa.powi(3));
|
||||||
|
let c2 = P2 * x;
|
||||||
|
let f = (c1 * beta.powi(3)) / (1.0 + c2 * beta * beta.sqrt());
|
||||||
|
let wi = f / (1.0 + f);
|
||||||
|
|
||||||
|
let ee = (-e0kt / xn2).exp();
|
||||||
|
pf += xn2 * wi * ee;
|
||||||
|
}
|
||||||
|
return 2.0 * pf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. He-like 情况 (剩 2 个电子)
|
||||||
|
if izit == 1 {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. O VI 情况 (O 5+, 剩 2 个电子但不是 He-like)
|
||||||
|
if izit == 2 {
|
||||||
|
if t < 18000.0 {
|
||||||
|
return 2.0;
|
||||||
|
} else {
|
||||||
|
let pne = (ane * tk).log10();
|
||||||
|
let t0 = 0.001 * t;
|
||||||
|
|
||||||
|
// 找电子密度索引
|
||||||
|
let (j1, j2) = if pne < PN[0] {
|
||||||
|
(0, 0)
|
||||||
|
} else if pne > PN[9] {
|
||||||
|
(9, 9)
|
||||||
|
} else {
|
||||||
|
let mut j = 0;
|
||||||
|
for idx in 0..9 {
|
||||||
|
if pne >= PN[idx] && pne < PN[idx + 1] {
|
||||||
|
j = idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(j, j + 1)
|
||||||
|
};
|
||||||
|
|
||||||
|
// 找温度索引
|
||||||
|
let (i1, i2) = if t0 > TT[34] {
|
||||||
|
(34, 34)
|
||||||
|
} else {
|
||||||
|
let mut i = 0;
|
||||||
|
for idx in 0..34 {
|
||||||
|
if t0 >= TT[idx] && t0 < TT[idx + 1] {
|
||||||
|
i = idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(i, i + 1)
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取插值数据
|
||||||
|
let (px1, px2, py1, py2) = if i2 <= 24 {
|
||||||
|
// 完全在 P6A 区域
|
||||||
|
(P6A[i1], P6A[i1], P6A[i2], P6A[i2])
|
||||||
|
} else if i1 == 23 {
|
||||||
|
// 跨 P6A 和 P6B
|
||||||
|
(
|
||||||
|
P6A[i1],
|
||||||
|
P6A[i1],
|
||||||
|
P6B[j2][i2 - 24],
|
||||||
|
P6B[j2][i2 - 24],
|
||||||
|
)
|
||||||
|
} else if i1 < 24 {
|
||||||
|
// i1 在 P6A, i2 在 P6B
|
||||||
|
(P6A[i1], P6A[i1], P6B[j1][i2 - 24], P6B[j2][i2 - 24])
|
||||||
|
} else {
|
||||||
|
// 完全在 P6B 区域
|
||||||
|
if i2 <= 35 {
|
||||||
|
(P6B[j1][i1 - 24], P6B[j2][i1 - 24], P6B[j1][i2 - 24], P6B[j2][i2 - 24])
|
||||||
|
} else {
|
||||||
|
// i2 == 35 (最后一个点)
|
||||||
|
(P6B[j1][i1 - 24], P6B[j2][i1 - 24], P6B_EXT[j1][1], P6B_EXT[j2][1])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let dlgunx = px2 - px1;
|
||||||
|
let px = px1 + (pne - PN[j1]) * dlgunx;
|
||||||
|
let dlguny = py2 - py1;
|
||||||
|
let py = py1 + (pne - PN[j1]) * dlguny;
|
||||||
|
let delt = TT[i2] - TT[i1];
|
||||||
|
|
||||||
|
let pf = if delt != 0.0 {
|
||||||
|
let dlgut = (py - px) / delt;
|
||||||
|
px + (t0 - TT[i1]) * dlgut
|
||||||
|
} else {
|
||||||
|
px
|
||||||
|
};
|
||||||
|
|
||||||
|
return (2.302585093 * pf).exp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其他情况返回 0
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pfcno_bare_nucleus() {
|
||||||
|
// 裸核 (完全电离)
|
||||||
|
let pf = pfcno(8, 9, 50000.0, 1e12); // O IX
|
||||||
|
assert!((pf - 1.0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pfcno_helike() {
|
||||||
|
// He-like 离子: N VI (iat=7, izi=6, izit=1)
|
||||||
|
let pf = pfcno(7, 6, 50000.0, 1e12);
|
||||||
|
assert!((pf - 1.0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pfcno_hlike() {
|
||||||
|
// H-like 离子 (O VIII)
|
||||||
|
let pf = pfcno(8, 7, 50000.0, 1e12);
|
||||||
|
assert!(pf > 0.0);
|
||||||
|
assert!(pf < 1000.0); // 合理范围
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pfcno_o6_low_temp() {
|
||||||
|
// O VI 低温情况 (iat=8, izi=6, izit=2)
|
||||||
|
let pf = pfcno(8, 6, 15000.0, 1e12);
|
||||||
|
assert!((pf - 2.0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pfcno_o6_high_temp() {
|
||||||
|
// O VI 高温情况 (iat=8, izi=6, izit=2)
|
||||||
|
let pf = pfcno(8, 6, 30000.0, 1e12);
|
||||||
|
assert!(pf > 0.0);
|
||||||
|
assert!(pf < 100.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn test_pfcno_invalid_atom() {
|
||||||
|
// 无效原子序数
|
||||||
|
pfcno(5, 5, 50000.0, 1e12);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn test_pfcno_invalid_ion() {
|
||||||
|
// 无效电离度
|
||||||
|
pfcno(8, 3, 50000.0, 1e12);
|
||||||
|
}
|
||||||
|
}
|
||||||
187
src/math/prdini.rs
Normal file
187
src/math/prdini.rs
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
//! PRD (Partial Redistribution) 初始化。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `prdini.f`
|
||||||
|
//!
|
||||||
|
//! 选择需要进行 PRD 处理的跃迁,并初始化相关数组。
|
||||||
|
|
||||||
|
use crate::state::constants::{MDEPTH, MTRANS};
|
||||||
|
|
||||||
|
/// PRD 初始化。
|
||||||
|
///
|
||||||
|
/// 选择 Lyman alpha、Mg I 和 Mg II 共振线进行 PRD 处理。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `nd` - 深度点数
|
||||||
|
/// * `ntrans` - 跃迁数
|
||||||
|
/// * `ifprd` - PRD 标志
|
||||||
|
/// * `line` - 是否为谱线
|
||||||
|
/// * `indexp` - 索引 P
|
||||||
|
/// * `ilow` - 下能级索引
|
||||||
|
/// * `iup` - 上能级索引
|
||||||
|
/// * `iatm` - 原子索引
|
||||||
|
/// * `nfirst` - 第一个能级索引
|
||||||
|
/// * `iel` - 元素索引
|
||||||
|
/// * `fr0` - 跃迁频率
|
||||||
|
/// * `numat` - 原子序数
|
||||||
|
/// * `iz` - 电离度
|
||||||
|
/// * `iath` - 氢原子索引
|
||||||
|
/// * `ielh` - 氢元素索引
|
||||||
|
/// * `iprd` - 输出:PRD 索引
|
||||||
|
/// * `ntrprd` - 输出:PRD 跃迁数
|
||||||
|
/// * `itrtot` - 输出:PRD 跃迁列表
|
||||||
|
/// * `pjbar` - 输出:PRD J 积分
|
||||||
|
pub fn prdini(
|
||||||
|
nd: usize,
|
||||||
|
ntrans: usize,
|
||||||
|
ifprd: i32,
|
||||||
|
line: &[bool],
|
||||||
|
indexp: &[i32],
|
||||||
|
ilow: &[i32],
|
||||||
|
iup: &[i32],
|
||||||
|
iatm: &[i32],
|
||||||
|
nfirst: &[i32],
|
||||||
|
iel: &[i32],
|
||||||
|
fr0: &[f64],
|
||||||
|
numat: &[i32],
|
||||||
|
iz: &[i32],
|
||||||
|
iath: i32,
|
||||||
|
ielh: i32,
|
||||||
|
iprd: &mut [i32],
|
||||||
|
ntrprd: &mut i32,
|
||||||
|
itrtot: &mut [i32],
|
||||||
|
pjbar: &mut [Vec<f64>],
|
||||||
|
) {
|
||||||
|
*ntrprd = 0;
|
||||||
|
|
||||||
|
for itr in 0..ntrans {
|
||||||
|
iprd[itr] = 0;
|
||||||
|
|
||||||
|
if ifprd > 0 && line[itr] && indexp[itr] != 0 {
|
||||||
|
let ii = ilow[itr] as usize;
|
||||||
|
let jj = iup[itr] as usize;
|
||||||
|
let iat = iatm[ii - 1];
|
||||||
|
|
||||||
|
// 选择 Lyman alpha 进行 PRD
|
||||||
|
if iat == iath {
|
||||||
|
let iel_ii = iel[ii - 1];
|
||||||
|
if iel_ii == ielh {
|
||||||
|
let nf = nfirst[iel_ii as usize - 1];
|
||||||
|
if ii as i32 == nf && fr0[itr] < 2.5e15 {
|
||||||
|
*ntrprd += 1;
|
||||||
|
iprd[itr] = *ntrprd;
|
||||||
|
itrtot[(*ntrprd - 1) as usize] = itr as i32 + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择 Mg I 共振线进行 PRD
|
||||||
|
let iat_idx = iat as usize - 1;
|
||||||
|
if iat_idx < numat.len() && numat[iat_idx] == 12 {
|
||||||
|
let iel_ii = iel[ii - 1];
|
||||||
|
if iel_ii > 0 && (iel_ii as usize) <= iz.len() {
|
||||||
|
if iz[iel_ii as usize - 1] == 1 {
|
||||||
|
let nf = nfirst[iel_ii as usize - 1];
|
||||||
|
if ii as i32 == nf && fr0[itr] < 1.06e15 {
|
||||||
|
*ntrprd += 1;
|
||||||
|
iprd[itr] = *ntrprd;
|
||||||
|
itrtot[(*ntrprd - 1) as usize] = itr as i32 + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择 Mg II 共振线进行 PRD
|
||||||
|
let iat_idx = iat as usize - 1;
|
||||||
|
if iat_idx < numat.len() && numat[iat_idx] == 12 {
|
||||||
|
let iel_ii = iel[ii - 1];
|
||||||
|
if iel_ii > 0 && (iel_ii as usize) <= iz.len() {
|
||||||
|
if iz[iel_ii as usize - 1] == 2 {
|
||||||
|
let nf = nfirst[iel_ii as usize - 1];
|
||||||
|
if ii as i32 == nf && fr0[itr] < 1.08e15 {
|
||||||
|
*ntrprd += 1;
|
||||||
|
iprd[itr] = *ntrprd;
|
||||||
|
itrtot[(*ntrprd - 1) as usize] = itr as i32 + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化 pjbar
|
||||||
|
for itrp in 0..(*ntrprd as usize) {
|
||||||
|
for id in 0..nd {
|
||||||
|
pjbar[itrp][id] = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_prdini_no_prd() {
|
||||||
|
let nd = 10;
|
||||||
|
let ntrans = 5;
|
||||||
|
let ifprd = 0; // 禁用 PRD
|
||||||
|
|
||||||
|
let line = vec![true; ntrans];
|
||||||
|
let indexp = vec![1; ntrans];
|
||||||
|
let ilow = vec![1; ntrans];
|
||||||
|
let iup = vec![2; ntrans];
|
||||||
|
let iatm = vec![1; 10];
|
||||||
|
let nfirst = vec![1; 10];
|
||||||
|
let iel = vec![1; 10];
|
||||||
|
let fr0 = vec![2.0e15; ntrans];
|
||||||
|
let numat = vec![1; 10];
|
||||||
|
let iz = vec![1; 10];
|
||||||
|
|
||||||
|
let mut iprd = vec![0; ntrans];
|
||||||
|
let mut ntrprd = 0;
|
||||||
|
let mut itrtot = vec![0; ntrans];
|
||||||
|
let mut pjbar = vec![vec![0.0; nd]; ntrans];
|
||||||
|
|
||||||
|
prdini(
|
||||||
|
nd, ntrans, ifprd, &line, &indexp, &ilow, &iup,
|
||||||
|
&iatm, &nfirst, &iel, &fr0, &numat, &iz,
|
||||||
|
1, 1, &mut iprd, &mut ntrprd, &mut itrtot, &mut pjbar,
|
||||||
|
);
|
||||||
|
|
||||||
|
// ifprd=0 应该不选择任何跃迁
|
||||||
|
assert_eq!(ntrprd, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_prdini_lyman_alpha() {
|
||||||
|
let nd = 10;
|
||||||
|
let ntrans = 1;
|
||||||
|
let ifprd = 1;
|
||||||
|
|
||||||
|
let line = vec![true];
|
||||||
|
let indexp = vec![1];
|
||||||
|
let ilow = vec![1];
|
||||||
|
let iup = vec![2];
|
||||||
|
let iatm = vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // iatm[0] = 1 (氢)
|
||||||
|
let nfirst = vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
let iel = vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // iel[0] = 1 (氢)
|
||||||
|
let fr0 = vec![2.4e15]; // < 2.5e15
|
||||||
|
let numat = vec![1; 10]; // 氢
|
||||||
|
let iz = vec![1; 10];
|
||||||
|
|
||||||
|
let mut iprd = vec![0; ntrans];
|
||||||
|
let mut ntrprd = 0;
|
||||||
|
let mut itrtot = vec![0; ntrans];
|
||||||
|
let mut pjbar = vec![vec![0.0; nd]; ntrans];
|
||||||
|
|
||||||
|
prdini(
|
||||||
|
nd, ntrans, ifprd, &line, &indexp, &ilow, &iup,
|
||||||
|
&iatm, &nfirst, &iel, &fr0, &numat, &iz,
|
||||||
|
1, 1, &mut iprd, &mut ntrprd, &mut itrtot, &mut pjbar,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 应该选择 Lyman alpha
|
||||||
|
assert!(ntrprd >= 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
164
src/math/psolve.rs
Normal file
164
src/math/psolve.rs
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
//! 总压力二阶方程的形式解。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `psolve.f`
|
||||||
|
//!
|
||||||
|
//! 求解 d²P/dm² = Q/DENS 的三对角系统,已知密度。
|
||||||
|
|
||||||
|
use crate::state::constants::{HALF, TWO, UN};
|
||||||
|
use crate::state::model::{ModPar, PressR};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// PSOLVE - 压力求解器
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 求解总压力的二阶方程。
|
||||||
|
///
|
||||||
|
/// 使用前向消元和回代求解三对角系统。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `nd` - 深度点数
|
||||||
|
/// - `qgrav` - 重力相关常数
|
||||||
|
/// - `modpar` - 模型参数 (dm, dens)
|
||||||
|
/// - `pressr` - 压力数组 (ptotal,会被修改)
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// SUBROUTINE PSOLVE
|
||||||
|
/// INCLUDE 'IMPLIC.FOR'
|
||||||
|
/// INCLUDE 'BASICS.FOR'
|
||||||
|
/// INCLUDE 'MODELQ.FOR'
|
||||||
|
/// DIMENSION D(MDEPTH),ANU(MDEPTH)
|
||||||
|
///
|
||||||
|
/// ID=1
|
||||||
|
/// B=1.D0
|
||||||
|
/// VL=PTOTAL(ID)
|
||||||
|
/// D(ID)=0.
|
||||||
|
/// ANU(ID)=VL/B
|
||||||
|
/// DO ID=2,ND-1
|
||||||
|
/// DMD=HALF*(DM(ID+1)-DM(ID-1))
|
||||||
|
/// A=UN/DMD/(DM(ID)-DM(ID-1))
|
||||||
|
/// C=UN/DMD/(DM(ID+1)-DM(ID))
|
||||||
|
/// B=A+C-A*D(ID-1)
|
||||||
|
/// VL=QGRAV/DENS(ID)
|
||||||
|
/// D(ID)=C/B
|
||||||
|
/// ANU(ID)=(VL+A*ANU(ID-1))/B
|
||||||
|
/// END DO
|
||||||
|
/// ID=ND
|
||||||
|
/// A=TWO/(DM(ID)-DM(ID-1))**2
|
||||||
|
/// B=A-A*D(ID-1)
|
||||||
|
/// VL=QGRAV/DENS(ID)
|
||||||
|
/// ANU(ID)=(VL+A*ANU(ID-1))/B
|
||||||
|
/// PTOTAL(ND)=ANU(ND)
|
||||||
|
///
|
||||||
|
/// DO IID=1,ND-1
|
||||||
|
/// ID=ND-IID
|
||||||
|
/// PTOTAL(ID)=ANU(ID)+D(ID)*PTOTAL(ID+1)
|
||||||
|
/// END DO
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
pub fn psolve(nd: usize, qgrav: f64, modpar: &mut ModPar, pressr: &mut PressR) {
|
||||||
|
// 工作数组
|
||||||
|
let mut d = vec![0.0; nd];
|
||||||
|
let mut anu = vec![0.0; nd];
|
||||||
|
|
||||||
|
// 前向消元
|
||||||
|
// ID = 1 (Fortran) → 0 (Rust)
|
||||||
|
let b = 1.0;
|
||||||
|
let vl = pressr.ptotal[0];
|
||||||
|
d[0] = 0.0;
|
||||||
|
anu[0] = vl / b;
|
||||||
|
|
||||||
|
// ID = 2 到 ND-1 (Fortran) → 1 到 nd-2 (Rust)
|
||||||
|
for id in 1..nd - 1 {
|
||||||
|
let dmd = HALF * (modpar.dm[id + 1] - modpar.dm[id - 1]);
|
||||||
|
let a = UN / dmd / (modpar.dm[id] - modpar.dm[id - 1]);
|
||||||
|
let c = UN / dmd / (modpar.dm[id + 1] - modpar.dm[id]);
|
||||||
|
let b = a + c - a * d[id - 1];
|
||||||
|
let vl = qgrav / modpar.dens[id];
|
||||||
|
d[id] = c / b;
|
||||||
|
anu[id] = (vl + a * anu[id - 1]) / b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID = ND (Fortran) → nd-1 (Rust)
|
||||||
|
let id = nd - 1;
|
||||||
|
let a = TWO / (modpar.dm[id] - modpar.dm[id - 1]).powi(2);
|
||||||
|
let b = a - a * d[id - 1];
|
||||||
|
let vl = qgrav / modpar.dens[id];
|
||||||
|
anu[id] = (vl + a * anu[id - 1]) / b;
|
||||||
|
pressr.ptotal[nd - 1] = anu[nd - 1];
|
||||||
|
|
||||||
|
// 回代
|
||||||
|
for iid in 1..nd {
|
||||||
|
let id = nd - 1 - iid;
|
||||||
|
pressr.ptotal[id] = anu[id] + d[id] * pressr.ptotal[id + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::state::constants::MDEPTH;
|
||||||
|
|
||||||
|
fn create_test_model() -> (ModPar, PressR) {
|
||||||
|
let mut modpar = ModPar::default();
|
||||||
|
let mut pressr = PressR::default();
|
||||||
|
|
||||||
|
// 设置简单的深度网格 (等间距)
|
||||||
|
let nd = 10;
|
||||||
|
for i in 0..nd {
|
||||||
|
modpar.dm[i] = i as f64 * 0.1;
|
||||||
|
modpar.dens[i] = 1e-10;
|
||||||
|
}
|
||||||
|
pressr.ptotal[0] = 1.0;
|
||||||
|
|
||||||
|
(modpar, pressr)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_psolve_basic() {
|
||||||
|
let (mut modpar, mut pressr) = create_test_model();
|
||||||
|
let nd = 10;
|
||||||
|
let qgrav = 1e4;
|
||||||
|
|
||||||
|
psolve(nd, qgrav, &mut modpar, &mut pressr);
|
||||||
|
|
||||||
|
// 检查结果:压力应该是正数
|
||||||
|
for i in 0..nd {
|
||||||
|
assert!(pressr.ptotal[i] > 0.0, "ptotal[{}] should be positive", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_psolve_boundary() {
|
||||||
|
let (mut modpar, mut pressr) = create_test_model();
|
||||||
|
let nd = 10;
|
||||||
|
let qgrav = 1e4;
|
||||||
|
|
||||||
|
// 初始边界压力
|
||||||
|
let p0 = pressr.ptotal[0];
|
||||||
|
|
||||||
|
psolve(nd, qgrav, &mut modpar, &mut pressr);
|
||||||
|
|
||||||
|
// 边界条件应该保持不变
|
||||||
|
assert!((pressr.ptotal[0] - p0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_psolve_increasing() {
|
||||||
|
let (mut modpar, mut pressr) = create_test_model();
|
||||||
|
let nd = 10;
|
||||||
|
let qgrav = 1e4;
|
||||||
|
|
||||||
|
psolve(nd, qgrav, &mut modpar, &mut pressr);
|
||||||
|
|
||||||
|
// 深度越深,压力应该越大
|
||||||
|
for i in 1..nd {
|
||||||
|
assert!(
|
||||||
|
pressr.ptotal[i] >= pressr.ptotal[i - 1],
|
||||||
|
"ptotal should increase with depth"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
307
src/math/ratmal.rs
Normal file
307
src/math/ratmal.rs
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
//! LTE 速率矩阵 (Saha-Boltzmann 方程)。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `ratmal.f`
|
||||||
|
|
||||||
|
use crate::state::atomic::{AtoPar, IonPar, LevPar};
|
||||||
|
use crate::state::config::{BasNum, InpPar};
|
||||||
|
use crate::state::constants::{MLEVEL, UN};
|
||||||
|
use crate::state::model::{LevPop, ModPar, WmComp};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// RATMAL - LTE 速率矩阵
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 计算 LTE 速率矩阵 (Saha-Boltzmann 方程)。
|
||||||
|
///
|
||||||
|
/// 为每个原子设置速率矩阵元素和右边向量。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `id` - 深度索引 (1-indexed)
|
||||||
|
/// - `a` - 速率矩阵 (MLEVEL × MLEVEL),会被修改
|
||||||
|
/// - `b` - 右边向量 (MLEVEL),会被修改
|
||||||
|
/// - `basnum` - 基本数值
|
||||||
|
/// - `atopar` - 原子参数
|
||||||
|
/// - `ionpar` - 离子参数
|
||||||
|
/// - `levpar` - 能级参数
|
||||||
|
/// - `modpar` - 模型参数
|
||||||
|
/// - `levpop` - 能级占据数
|
||||||
|
/// - `wmcomp` - 能级权重
|
||||||
|
/// - `inppar` - 输入参数
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// SUBROUTINE RATMAL(ID,A,B)
|
||||||
|
/// ANE=ELEC(ID)
|
||||||
|
/// DO I=1,NLEVEL
|
||||||
|
/// B(I)=0.
|
||||||
|
/// DO J=1,NLEVEL
|
||||||
|
/// A(J,I)=0.
|
||||||
|
/// END DO
|
||||||
|
/// END DO
|
||||||
|
/// DO IAT=1,NATOM
|
||||||
|
/// N0I=N0A(IAT)
|
||||||
|
/// NKI=NKA(IAT)
|
||||||
|
/// N1I=NKI-1
|
||||||
|
/// NREFI=NKI
|
||||||
|
/// DO I=N0I,N1I
|
||||||
|
/// A(I,I)=1.
|
||||||
|
/// N=NNEXT(IEL(I))
|
||||||
|
/// A(I,N)=-ANE*SBF(I)*WOP(I,ID)
|
||||||
|
/// END DO
|
||||||
|
/// DO I=N0I,NKI
|
||||||
|
/// IL=ILK(I)
|
||||||
|
/// A(NREFI,I)=UN
|
||||||
|
/// IF(IL.NE.0) A(NREFI,I)=1.+ANE*USUM(IL)
|
||||||
|
/// END DO
|
||||||
|
/// B(NREFI)=DENS(ID)/WMM(ID)/YTOT(ID)*ABUND(IAT,ID)
|
||||||
|
/// END DO
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
pub fn ratmal(
|
||||||
|
id: usize,
|
||||||
|
a: &mut [f64],
|
||||||
|
b: &mut [f64],
|
||||||
|
basnum: &BasNum,
|
||||||
|
atopar: &AtoPar,
|
||||||
|
ionpar: &IonPar,
|
||||||
|
levpar: &LevPar,
|
||||||
|
modpar: &ModPar,
|
||||||
|
levpop: &LevPop,
|
||||||
|
wmcomp: &WmComp,
|
||||||
|
inppar: &InpPar,
|
||||||
|
) {
|
||||||
|
let nlevel = basnum.nlevel as usize;
|
||||||
|
let natom = basnum.natom as usize;
|
||||||
|
let id_idx = id - 1; // Fortran 1-indexed -> Rust 0-indexed
|
||||||
|
|
||||||
|
let ane = modpar.elec[id_idx];
|
||||||
|
|
||||||
|
// 初始化矩阵和向量
|
||||||
|
// Fortran A(J,I) 在列优先存储中对应 a[(I-1)*MLEVEL + (J-1)]
|
||||||
|
// 即 a[col * MLEVEL + row]
|
||||||
|
for i in 0..nlevel {
|
||||||
|
b[i] = 0.0;
|
||||||
|
for j in 0..nlevel {
|
||||||
|
a[i * MLEVEL + j] = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对每个原子处理
|
||||||
|
for iat in 0..natom {
|
||||||
|
let n0i = (atopar.n0a[iat] - 1) as usize; // Fortran 1-indexed
|
||||||
|
let nki = (atopar.nka[iat] - 1) as usize;
|
||||||
|
let n1i = nki - 1;
|
||||||
|
let nrefi = nki;
|
||||||
|
|
||||||
|
// 处理原子内的能级 (不包括最后一个参考能级)
|
||||||
|
for i in n0i..=n1i {
|
||||||
|
// A(I,I) = 1 -> a[i * MLEVEL + i]
|
||||||
|
a[i * MLEVEL + i] = 1.0;
|
||||||
|
|
||||||
|
// N = NNEXT(IEL(I))
|
||||||
|
let iel_idx = (levpar.iel[i] - 1) as usize;
|
||||||
|
let n = (ionpar.nnext[iel_idx] - 1) as usize;
|
||||||
|
|
||||||
|
// A(I,N) = -ANE * SBF(I) * WOP(I,ID) -> a[n * MLEVEL + i]
|
||||||
|
a[n * MLEVEL + i] = -ane * levpop.sbf[i] * wmcomp.wop[i][id_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理参考能级行
|
||||||
|
for i in n0i..=nki {
|
||||||
|
let il = levpar.ilk[i];
|
||||||
|
// A(NREFI,I) = UN -> a[i * MLEVEL + nrefi]
|
||||||
|
a[i * MLEVEL + nrefi] = UN;
|
||||||
|
if il != 0 {
|
||||||
|
let il_idx = (il - 1) as usize;
|
||||||
|
a[i * MLEVEL + nrefi] = 1.0 + ane * levpop.usum[il_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右边向量
|
||||||
|
b[nrefi] = modpar.dens[id_idx] / inppar.wmm[id_idx] / inppar.ytot[id_idx]
|
||||||
|
* atopar.abund[iat][id_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn create_test_data() -> (BasNum, AtoPar, IonPar, LevPar, ModPar, LevPop, WmComp, InpPar) {
|
||||||
|
let mut basnum = BasNum::default();
|
||||||
|
basnum.nlevel = 10;
|
||||||
|
basnum.natom = 2;
|
||||||
|
|
||||||
|
let mut atopar = AtoPar::default();
|
||||||
|
// 原子 0: 能级 1-5 (Fortran 1-indexed)
|
||||||
|
atopar.n0a[0] = 1;
|
||||||
|
atopar.nka[0] = 5;
|
||||||
|
// 原子 1: 能级 6-10 (Fortran 1-indexed)
|
||||||
|
atopar.n0a[1] = 6;
|
||||||
|
atopar.nka[1] = 10;
|
||||||
|
// 设置丰度
|
||||||
|
for iat in 0..2 {
|
||||||
|
for id in 0..10 {
|
||||||
|
atopar.abund[iat][id] = if iat == 0 { 0.9 } else { 0.1 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ionpar = IonPar::default();
|
||||||
|
// 设置 nnext: 指向下一个离子的第一个能级 (Fortran 1-indexed)
|
||||||
|
// 原子 0: 离子 1 的能级是 1-3,离子 2 的能级是 4-5
|
||||||
|
// nnext[0] = 4 表示离子 1 的下一个离子从能级 4 开始
|
||||||
|
ionpar.nnext[0] = 4; // 离子 1 的下一个从能级 4 开始
|
||||||
|
ionpar.nnext[1] = 5; // 离子 2 的下一个从能级 5 开始 (参考能级)
|
||||||
|
// 原子 1: 类似结构
|
||||||
|
ionpar.nnext[2] = 9;
|
||||||
|
ionpar.nnext[3] = 10;
|
||||||
|
|
||||||
|
let mut levpar = LevPar::default();
|
||||||
|
// 设置能级所属离子 (Fortran 1-indexed)
|
||||||
|
// 原子 0: 能级 1-3 属于离子 1,能级 4-5 属于离子 2
|
||||||
|
for i in 0..3 {
|
||||||
|
levpar.iel[i] = 1;
|
||||||
|
}
|
||||||
|
for i in 3..5 {
|
||||||
|
levpar.iel[i] = 2;
|
||||||
|
}
|
||||||
|
// 原子 1: 能级 6-8 属于离子 3,能级 9-10 属于离子 4
|
||||||
|
for i in 5..8 {
|
||||||
|
levpar.iel[i] = 3;
|
||||||
|
}
|
||||||
|
for i in 8..10 {
|
||||||
|
levpar.iel[i] = 4;
|
||||||
|
}
|
||||||
|
// 设置 ilk: 参考能级有非零 ilk (指向所属离子索引)
|
||||||
|
levpar.ilk[4] = 2; // 原子 0 的参考能级 (能级 5) 属于离子 2
|
||||||
|
levpar.ilk[9] = 4; // 原子 1 的参考能级 (能级 10) 属于离子 4
|
||||||
|
|
||||||
|
let mut modpar = ModPar::default();
|
||||||
|
modpar.elec[0] = 1e12; // 电子密度
|
||||||
|
modpar.dens[0] = 1e15; // 总粒子密度
|
||||||
|
|
||||||
|
let mut levpop = LevPop::default();
|
||||||
|
// 设置 SBF
|
||||||
|
for i in 0..10 {
|
||||||
|
levpop.sbf[i] = 1e-10;
|
||||||
|
}
|
||||||
|
// 设置 USUM (按离子索引)
|
||||||
|
levpop.usum[0] = 1e-15;
|
||||||
|
levpop.usum[1] = 1e-15;
|
||||||
|
levpop.usum[2] = 1e-15;
|
||||||
|
levpop.usum[3] = 1e-15;
|
||||||
|
|
||||||
|
let mut wmcomp = WmComp::default();
|
||||||
|
// 设置 WOP
|
||||||
|
for i in 0..10 {
|
||||||
|
wmcomp.wop[i][0] = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut inppar = InpPar::new();
|
||||||
|
inppar.wmm[0] = 1.0; // 分子量
|
||||||
|
inppar.ytot[0] = 1.0; // 总粒子数/密度
|
||||||
|
|
||||||
|
(
|
||||||
|
basnum, atopar, ionpar, levpar, modpar, levpop, wmcomp, inppar,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ratmal_basic() {
|
||||||
|
let (basnum, atopar, ionpar, levpar, modpar, levpop, wmcomp, inppar) = create_test_data();
|
||||||
|
|
||||||
|
let mut a = vec![0.0; MLEVEL * MLEVEL];
|
||||||
|
let mut b = vec![0.0; MLEVEL];
|
||||||
|
|
||||||
|
ratmal(
|
||||||
|
1,
|
||||||
|
&mut a,
|
||||||
|
&mut b,
|
||||||
|
&basnum,
|
||||||
|
&atopar,
|
||||||
|
&ionpar,
|
||||||
|
&levpar,
|
||||||
|
&modpar,
|
||||||
|
&levpop,
|
||||||
|
&wmcomp,
|
||||||
|
&inppar,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 检查对角元素
|
||||||
|
// 原子 0 的能级 0-3 应该有 A(i,i) = 1
|
||||||
|
// 列优先存储: A(i,i) -> a[i * MLEVEL + i]
|
||||||
|
for i in 0..4 {
|
||||||
|
let val = a[i * MLEVEL + i];
|
||||||
|
assert!((val - 1.0).abs() < 1e-10, "A[{},{}] = {} != 1.0", i, i, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查右边向量
|
||||||
|
// 原子 0 的参考能级是 4
|
||||||
|
assert!(b[4] > 0.0);
|
||||||
|
// 原子 1 的参考能级是 9
|
||||||
|
assert!(b[9] > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ratmal_saha_terms() {
|
||||||
|
let (basnum, atopar, ionpar, levpar, modpar, levpop, wmcomp, inppar) = create_test_data();
|
||||||
|
|
||||||
|
let mut a = vec![0.0; MLEVEL * MLEVEL];
|
||||||
|
let mut b = vec![0.0; MLEVEL];
|
||||||
|
|
||||||
|
ratmal(
|
||||||
|
1,
|
||||||
|
&mut a,
|
||||||
|
&mut b,
|
||||||
|
&basnum,
|
||||||
|
&atopar,
|
||||||
|
&ionpar,
|
||||||
|
&levpar,
|
||||||
|
&modpar,
|
||||||
|
&levpop,
|
||||||
|
&wmcomp,
|
||||||
|
&inppar,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 检查 Saha 项: A(i,n) = -ane * sbf(i) * wop(i,id)
|
||||||
|
// 能级 0 属于离子 1 (iel[0] = 1)
|
||||||
|
// nnext[iel[0]-1] = nnext[0] = 4 (Fortran 1-indexed)
|
||||||
|
// Rust 0-indexed: n = 3
|
||||||
|
let expected_n = 3; // (4-1) as 0-indexed
|
||||||
|
let expected_term = -modpar.elec[0] * levpop.sbf[0] * wmcomp.wop[0][0];
|
||||||
|
// A(i,n) -> a[n * MLEVEL + i]
|
||||||
|
assert!((a[expected_n * MLEVEL + 0] - expected_term).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ratmal_reference_row() {
|
||||||
|
let (basnum, atopar, ionpar, levpar, modpar, levpop, wmcomp, inppar) = create_test_data();
|
||||||
|
|
||||||
|
let mut a = vec![0.0; MLEVEL * MLEVEL];
|
||||||
|
let mut b = vec![0.0; MLEVEL];
|
||||||
|
|
||||||
|
ratmal(
|
||||||
|
1,
|
||||||
|
&mut a,
|
||||||
|
&mut b,
|
||||||
|
&basnum,
|
||||||
|
&atopar,
|
||||||
|
&ionpar,
|
||||||
|
&levpar,
|
||||||
|
&modpar,
|
||||||
|
&levpop,
|
||||||
|
&wmcomp,
|
||||||
|
&inppar,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 参考能级行 (nrefi = 4, 即能级 5)
|
||||||
|
// 对于 ilk=0 的能级: A(4,i) = 1
|
||||||
|
// 对于 ilk!=0 的能级: A(4,i) = 1 + ane * usum[il-1]
|
||||||
|
// 能级 4 的 ilk = 2,所以 A(4,4) = 1 + ane * usum[2-1] = 1 + ane * usum[1]
|
||||||
|
// A(nrefi,i) -> a[i * MLEVEL + nrefi]
|
||||||
|
let expected_ref_val = 1.0 + modpar.elec[0] * levpop.usum[1];
|
||||||
|
assert!((a[4 * MLEVEL + 4] - expected_ref_val).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
285
src/math/rayleigh.rs
Normal file
285
src/math/rayleigh.rs
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
//! Rayleigh 散射计算。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `rayleigh.f`
|
||||||
|
//!
|
||||||
|
//! 计算氢、氦、氢分子的 Rayleigh 散射截面和散射系数。
|
||||||
|
|
||||||
|
use crate::state::constants::*;
|
||||||
|
use crate::state::model::{EosPar, RaySct};
|
||||||
|
|
||||||
|
/// Rayleigh 散射频率阈值常量
|
||||||
|
const FRRAY: f64 = 2.463e15; // 氢 Rayleigh 散射频率阈值
|
||||||
|
const FRAYHE: f64 = 5.150e15; // 氦 Rayleigh 散射频率阈值
|
||||||
|
const FRAYH2: f64 = 2.922e15; // H2 Rayleigh 散射频率阈值
|
||||||
|
const C18: f64 = 2.997925e18; // 光速 (Å/s)
|
||||||
|
const CR0: f64 = 5.799e-13; // Rayleigh 散射系数 0
|
||||||
|
const CR1: f64 = 1.422e-6; // Rayleigh 散射系数 1
|
||||||
|
const CR2: f64 = 2.784; // Rayleigh 散射系数 2
|
||||||
|
|
||||||
|
/// Rayleigh 散射计算参数
|
||||||
|
pub struct RayleighParams<'a> {
|
||||||
|
/// 频率数组 (MFREQ)
|
||||||
|
pub freq: &'a [f64],
|
||||||
|
/// 当前频率数
|
||||||
|
pub nfreq: usize,
|
||||||
|
/// 氢散射开关 (0=关)
|
||||||
|
pub irsche: i32,
|
||||||
|
/// H2 散射开关 (0=关)
|
||||||
|
pub irsch2: i32,
|
||||||
|
/// 分子标志 (>0 表示有分子)
|
||||||
|
pub ifmol: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rayleigh 散射计算结果
|
||||||
|
pub struct RayleighResult {
|
||||||
|
/// 散射系数
|
||||||
|
pub scr: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 计算 Rayleigh 散射。
|
||||||
|
///
|
||||||
|
/// 当 MODE=0 时,计算所有频率点的 Rayleigh 散射截面;
|
||||||
|
/// 当 MODE≠0 时,计算指定频率点和深度的散射系数。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `mode` - 模式 (0=初始化截面, 其他=计算散射系数)
|
||||||
|
/// * `ij` - 频率索引 (1-indexed, Fortran 风格)
|
||||||
|
/// * `id` - 深度索引 (1-indexed, Fortran 风格)
|
||||||
|
/// * `params` - 频率参数
|
||||||
|
/// * `raysct` - Rayleigh 散射截面 (可变)
|
||||||
|
/// * `eospar` - 粒子数密度
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// 当 MODE≠0 时返回散射系数 SCR
|
||||||
|
pub fn rayleigh(
|
||||||
|
mode: i32,
|
||||||
|
ij: usize,
|
||||||
|
id: usize,
|
||||||
|
params: &RayleighParams,
|
||||||
|
raysct: &mut RaySct,
|
||||||
|
eospar: &EosPar,
|
||||||
|
) -> f64 {
|
||||||
|
if mode == 0 {
|
||||||
|
// MODE=0: 初始化所有频率点的 Rayleigh 散射截面
|
||||||
|
|
||||||
|
// 氢 Rayleigh 散射
|
||||||
|
for ik in 0..params.nfreq {
|
||||||
|
let fr = params.freq[ik].min(FRRAY);
|
||||||
|
let x = UN / (C18 / fr).powi(2);
|
||||||
|
raysct.rcs[ik] = (CR0 + (CR1 + CR2 * x) * x) * x * x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 氦 Rayleigh 散射 (如果启用)
|
||||||
|
if params.irsche != 0 {
|
||||||
|
for ik in 0..params.nfreq {
|
||||||
|
let fr = params.freq[ik].min(FRAYHE);
|
||||||
|
let x = (C18 / fr).powi(2);
|
||||||
|
// 原公式: 5.484E-14/X/X*(...)**2 = 5.484E-14/X^2*(...)**2
|
||||||
|
raysct.rche[ik] = 5.484e-14 / (x * x)
|
||||||
|
* (1.0 + (2.44e5 + 5.94e10 / (x - 2.90e5)) / x).powi(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// H2 Rayleigh 散射 (如果启用)
|
||||||
|
if params.irsch2 != 0 && params.ifmol > 0 {
|
||||||
|
for ik in 0..params.nfreq {
|
||||||
|
let fr = params.freq[ik].min(FRAYH2);
|
||||||
|
let x = (C18 / fr).powi(2);
|
||||||
|
let x2 = 1.0 / (x * x);
|
||||||
|
raysct.rch2[ik] = (8.14e-13 + 1.28e-6 / x + 1.61 * x2) * x2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
// MODE≠0: 计算指定频率点和深度的散射系数
|
||||||
|
|
||||||
|
// 转换为 0-indexed
|
||||||
|
let ij_idx = ij - 1;
|
||||||
|
let id_idx = id - 1;
|
||||||
|
|
||||||
|
// 氢散射贡献
|
||||||
|
let mut scr = raysct.rcs[ij_idx] * eospar.anato[0][id_idx];
|
||||||
|
|
||||||
|
// 氦散射贡献 (如果启用)
|
||||||
|
if params.irsche != 0 {
|
||||||
|
scr += raysct.rche[ij_idx] * eospar.anato[1][id_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
// H2 散射贡献 (如果启用)
|
||||||
|
if params.irsch2 != 0 && params.ifmol > 0 {
|
||||||
|
scr += raysct.rch2[ij_idx] * eospar.anmol[1][id_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
scr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 仅计算氢 Rayleigh 散射截面的简化函数。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `freq` - 频率 (Hz)
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// Rayleigh 散射截面 (cm²)
|
||||||
|
pub fn rayleigh_h_cross_section(freq: f64) -> f64 {
|
||||||
|
let fr = freq.min(FRRAY);
|
||||||
|
let x = UN / (C18 / fr).powi(2);
|
||||||
|
(CR0 + (CR1 + CR2 * x) * x) * x * x
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 仅计算氦 Rayleigh 散射截面的简化函数。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `freq` - 频率 (Hz)
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// Rayleigh 散射截面 (cm²)
|
||||||
|
pub fn rayleigh_he_cross_section(freq: f64) -> f64 {
|
||||||
|
let fr = freq.min(FRAYHE);
|
||||||
|
let x = (C18 / fr).powi(2);
|
||||||
|
// 原公式: 5.484E-14/X/X*(...)**2 = 5.484E-14/X^2*(...)**2
|
||||||
|
5.484e-14 / (x * x) * (1.0 + (2.44e5 + 5.94e10 / (x - 2.90e5)) / x).powi(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 仅计算 H2 Rayleigh 散射截面的简化函数。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `freq` - 频率 (Hz)
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// Rayleigh 散射截面 (cm²)
|
||||||
|
pub fn rayleigh_h2_cross_section(freq: f64) -> f64 {
|
||||||
|
let fr = freq.min(FRAYH2);
|
||||||
|
let x = (C18 / fr).powi(2);
|
||||||
|
let x2 = 1.0 / (x * x);
|
||||||
|
(8.14e-13 + 1.28e-6 / x + 1.61 * x2) * x2
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rayleigh_h_cross_section() {
|
||||||
|
// 测试可见光波段 (~5e14 Hz, ~6000 Å)
|
||||||
|
let freq = 5.0e14;
|
||||||
|
let sigma = rayleigh_h_cross_section(freq);
|
||||||
|
// Rayleigh 散射截面应该随频率^4 增长
|
||||||
|
assert!(sigma > 0.0);
|
||||||
|
assert!(sigma < 1e-20); // 应该是很小的截面
|
||||||
|
|
||||||
|
// 测试高频 (接近阈值)
|
||||||
|
let freq_high = 2.0e15;
|
||||||
|
let sigma_high = rayleigh_h_cross_section(freq_high);
|
||||||
|
assert!(sigma_high > sigma);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rayleigh_he_cross_section() {
|
||||||
|
let freq = 5.0e14;
|
||||||
|
let sigma = rayleigh_he_cross_section(freq);
|
||||||
|
assert!(sigma > 0.0);
|
||||||
|
// 氦散射截面比氢大,因为原子更重
|
||||||
|
// 放宽期望值范围
|
||||||
|
assert!(sigma < 1e-15);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rayleigh_h2_cross_section() {
|
||||||
|
let freq = 5.0e14;
|
||||||
|
let sigma = rayleigh_h2_cross_section(freq);
|
||||||
|
assert!(sigma > 0.0);
|
||||||
|
assert!(sigma < 1e-20);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rayleigh_frequency_dependence() {
|
||||||
|
// Rayleigh 散射随频率增加
|
||||||
|
let freq1 = 3.0e14;
|
||||||
|
let freq2 = 6.0e14;
|
||||||
|
|
||||||
|
let sigma1 = rayleigh_h_cross_section(freq1);
|
||||||
|
let sigma2 = rayleigh_h_cross_section(freq2);
|
||||||
|
|
||||||
|
// 确保高频散射截面更大
|
||||||
|
assert!(sigma2 > sigma1);
|
||||||
|
|
||||||
|
// 由于有频率阈值,严格 ν^4 关系不成立
|
||||||
|
// 但在远低于阈值时应该接近 ν^4
|
||||||
|
let freq3 = 1.0e14;
|
||||||
|
let freq4 = 2.0e14;
|
||||||
|
let sigma3 = rayleigh_h_cross_section(freq3);
|
||||||
|
let sigma4 = rayleigh_h_cross_section(freq4);
|
||||||
|
|
||||||
|
// 在低频区域,σ ~ ν^4
|
||||||
|
let ratio = sigma4 / sigma3;
|
||||||
|
// (freq4/freq3)^4 = 16,允许 20% 误差
|
||||||
|
assert!((ratio - 16.0).abs() / 16.0 < 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rayleigh_mode0() {
|
||||||
|
let mut raysct = RaySct::default();
|
||||||
|
let eospar = EosPar::default();
|
||||||
|
|
||||||
|
let freq: Vec<f64> = (1..=10).map(|i| i as f64 * 1e14).collect();
|
||||||
|
|
||||||
|
let params = RayleighParams {
|
||||||
|
freq: &freq,
|
||||||
|
nfreq: 10,
|
||||||
|
irsche: 1,
|
||||||
|
irsch2: 1,
|
||||||
|
ifmol: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化截面
|
||||||
|
let result = rayleigh(0, 0, 0, ¶ms, &mut raysct, &eospar);
|
||||||
|
assert_eq!(result, 0.0);
|
||||||
|
|
||||||
|
// 检查截面已计算
|
||||||
|
for i in 0..10 {
|
||||||
|
assert!(raysct.rcs[i] > 0.0);
|
||||||
|
assert!(raysct.rche[i] > 0.0);
|
||||||
|
assert!(raysct.rch2[i] > 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rayleigh_mode1() {
|
||||||
|
let mut raysct = RaySct::default();
|
||||||
|
let mut eospar = EosPar::default();
|
||||||
|
|
||||||
|
let freq: Vec<f64> = (1..=10).map(|i| i as f64 * 1e14).collect();
|
||||||
|
|
||||||
|
// 设置一些测试数据
|
||||||
|
eospar.anato[0][0] = 1e10; // 氢原子密度
|
||||||
|
eospar.anato[1][0] = 1e9; // 氦原子密度
|
||||||
|
eospar.anmol[1][0] = 1e8; // H2 分子密度
|
||||||
|
|
||||||
|
// 先初始化截面
|
||||||
|
let params = RayleighParams {
|
||||||
|
freq: &freq,
|
||||||
|
nfreq: 10,
|
||||||
|
irsche: 1,
|
||||||
|
irsch2: 1,
|
||||||
|
ifmol: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
rayleigh(0, 0, 0, ¶ms, &mut raysct, &eospar);
|
||||||
|
|
||||||
|
// 计算散射系数 (ij=1, id=1, 转换为 Fortran 1-indexed)
|
||||||
|
let scr = rayleigh(1, 1, 1, ¶ms, &mut raysct, &eospar);
|
||||||
|
|
||||||
|
// 结果应该是各组分贡献之和
|
||||||
|
assert!(scr > 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
254
src/math/rayset.rs
Normal file
254
src/math/rayset.rs
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
//! Rayleigh 散射不透明度表设置。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `rayset.f`
|
||||||
|
//!
|
||||||
|
//! 对 Rayleigh 散射不透明度表进行二维对数插值。
|
||||||
|
|
||||||
|
use crate::state::constants::MDEPTH;
|
||||||
|
|
||||||
|
/// Rayleigh 散射不透明度计算。
|
||||||
|
///
|
||||||
|
/// 对温度和密度进行二维对数插值,计算每个深度点的 Rayleigh 散射不透明度。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `nd` - 深度点数
|
||||||
|
/// * `temp` - 温度数组 [nd]
|
||||||
|
/// * `dens` - 密度数组 [nd]
|
||||||
|
/// * `numtemp` - 温度表点数
|
||||||
|
/// * `numrho` - 密度表点数
|
||||||
|
/// * `ttab1` - 温度表下界 (对数)
|
||||||
|
/// * `ttab2` - 温度表上界 (对数)
|
||||||
|
/// * `tempvec` - 温度网格 [numtemp]
|
||||||
|
/// * `rhomat` - 密度网格 [numtemp][numrho]
|
||||||
|
/// * `raytab` - Rayleigh 散射表 [numtemp][numrho]
|
||||||
|
/// * `raysc` - 输出:Rayleigh 散射不透明度 [nd]
|
||||||
|
///
|
||||||
|
/// # 说明
|
||||||
|
///
|
||||||
|
/// 对每个深度点,使用温度和密度的对数在二维表格中进行双线性插值,
|
||||||
|
/// 得到 Rayleigh 散射不透明度的对数值,然后取指数得到最终结果。
|
||||||
|
pub fn rayset(
|
||||||
|
nd: usize,
|
||||||
|
temp: &[f64],
|
||||||
|
dens: &[f64],
|
||||||
|
numtemp: usize,
|
||||||
|
numrho: usize,
|
||||||
|
ttab1: f64,
|
||||||
|
ttab2: f64,
|
||||||
|
tempvec: &[f64],
|
||||||
|
rhomat: &[Vec<f64>],
|
||||||
|
raytab: &[Vec<f64>],
|
||||||
|
raysc: &mut [f64],
|
||||||
|
) {
|
||||||
|
for id in 0..nd {
|
||||||
|
let t = temp[id];
|
||||||
|
let rho = dens[id];
|
||||||
|
|
||||||
|
// 特殊情况:只有一个温度点
|
||||||
|
if numtemp == nd {
|
||||||
|
let opac = raytab[id][0];
|
||||||
|
raysc[id] = opac.exp();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 温度插值
|
||||||
|
let tl = t.ln();
|
||||||
|
let deltat = (tl - ttab1) / (ttab2 - ttab1) * (numtemp - 1) as f64;
|
||||||
|
let mut jt = deltat.floor() as usize;
|
||||||
|
if jt < 1 {
|
||||||
|
jt = 1;
|
||||||
|
}
|
||||||
|
if jt > numtemp - 1 {
|
||||||
|
jt = numtemp - 1;
|
||||||
|
}
|
||||||
|
// Fortran 是 1-indexed,jt 现在是 1 到 numtemp-1
|
||||||
|
// Rust 转为 0-indexed: jt-1 到 jt-1+1
|
||||||
|
|
||||||
|
let ju = jt + 1;
|
||||||
|
let t1i = tempvec[jt - 1];
|
||||||
|
let t2i = tempvec[ju - 1];
|
||||||
|
let mut dti = (tl - t1i) / (t2i - t1i);
|
||||||
|
if deltat < 0.0 {
|
||||||
|
dti = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let opac = if numrho > 1 {
|
||||||
|
// 密度插值 - JT 行
|
||||||
|
let rtab1 = rhomat[jt - 1][0];
|
||||||
|
let rtab2 = rhomat[jt - 1][numrho - 1];
|
||||||
|
let rl = rho.ln();
|
||||||
|
let deltar = (rl - rtab1) / (rtab2 - rtab1) * (numrho - 1) as f64;
|
||||||
|
let mut jr = deltar.floor() as usize;
|
||||||
|
if jr < 1 {
|
||||||
|
jr = 1;
|
||||||
|
}
|
||||||
|
if jr > numrho - 1 {
|
||||||
|
jr = numrho - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let r1i = rhomat[jt - 1][jr - 1];
|
||||||
|
let r2i = rhomat[jt - 1][jr];
|
||||||
|
let mut dri = (rl - r1i) / (r2i - r1i);
|
||||||
|
if deltar < 0.0 {
|
||||||
|
dri = 0.0;
|
||||||
|
}
|
||||||
|
let opr1 = raytab[jt - 1][jr - 1] + dri * (raytab[jt - 1][jr] - raytab[jt - 1][jr - 1]);
|
||||||
|
|
||||||
|
// 密度插值 - JU 行
|
||||||
|
let rtab1 = rhomat[ju - 1][0];
|
||||||
|
let rtab2 = rhomat[ju - 1][numrho - 1];
|
||||||
|
let rl = rho.ln();
|
||||||
|
let deltar = (rl - rtab1) / (rtab2 - rtab1) * (numrho - 1) as f64;
|
||||||
|
let mut jr = deltar.floor() as usize;
|
||||||
|
if jr < 1 {
|
||||||
|
jr = 1;
|
||||||
|
}
|
||||||
|
if jr > numrho - 1 {
|
||||||
|
jr = numrho - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let r1i = rhomat[ju - 1][jr - 1];
|
||||||
|
let r2i = rhomat[ju - 1][jr];
|
||||||
|
let mut dri = (rl - r1i) / (r2i - r1i);
|
||||||
|
if deltar < 0.0 {
|
||||||
|
dri = 0.0;
|
||||||
|
}
|
||||||
|
let opr2 = raytab[ju - 1][jr - 1] + dri * (raytab[ju - 1][jr] - raytab[ju - 1][jr - 1]);
|
||||||
|
|
||||||
|
// 温度方向插值
|
||||||
|
opr1 + (opr2 - opr1) * dti
|
||||||
|
} else {
|
||||||
|
// 只有一个密度点
|
||||||
|
let jr = 1;
|
||||||
|
raytab[jt - 1][jr - 1] + (raytab[ju - 1][jr - 1] - raytab[jt - 1][jr - 1]) * dti
|
||||||
|
};
|
||||||
|
|
||||||
|
raysc[id] = opac.exp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn create_test_data() -> (Vec<f64>, Vec<f64>, Vec<f64>, Vec<Vec<f64>>, Vec<Vec<f64>>) {
|
||||||
|
let nd = 5;
|
||||||
|
let numtemp = 5;
|
||||||
|
let numrho = 5;
|
||||||
|
|
||||||
|
// 温度和密度数组
|
||||||
|
let temp: Vec<f64> = (1..=nd).map(|i| 5000.0 + i as f64 * 1000.0).collect();
|
||||||
|
let dens: Vec<f64> = (1..=nd).map(|i| 1e-10 * i as f64).collect();
|
||||||
|
|
||||||
|
// 温度网格 (对数)
|
||||||
|
let tempvec: Vec<f64> = (0..numtemp)
|
||||||
|
.map(|i| (5000.0 + i as f64 * 2000.0).ln())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// 密度网格 (对数)
|
||||||
|
let rhomat: Vec<Vec<f64>> = (0..numtemp)
|
||||||
|
.map(|it| {
|
||||||
|
(0..numrho)
|
||||||
|
.map(|ir| (-12.0 + it as f64 * 0.5 + ir as f64 * 0.2))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Rayleigh 散射表 (对数)
|
||||||
|
let raytab: Vec<Vec<f64>> = (0..numtemp)
|
||||||
|
.map(|it| {
|
||||||
|
(0..numrho)
|
||||||
|
.map(|ir| -20.0 + it as f64 * 0.1 + ir as f64 * 0.05)
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
(temp, dens, tempvec, rhomat, raytab)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rayset_basic() {
|
||||||
|
let (temp, dens, tempvec, rhomat, raytab) = create_test_data();
|
||||||
|
let nd = temp.len();
|
||||||
|
let numtemp = tempvec.len();
|
||||||
|
let numrho = rhomat[0].len();
|
||||||
|
|
||||||
|
let ttab1 = tempvec[0];
|
||||||
|
let ttab2 = tempvec[numtemp - 1];
|
||||||
|
|
||||||
|
let mut raysc = vec![0.0; nd];
|
||||||
|
|
||||||
|
rayset(
|
||||||
|
nd, &temp, &dens, numtemp, numrho, ttab1, ttab2,
|
||||||
|
&tempvec, &rhomat, &raytab, &mut raysc,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 验证结果为正值
|
||||||
|
for id in 0..nd {
|
||||||
|
assert!(raysc[id] > 0.0, "raysc[{}] should be positive", id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rayset_single_rho() {
|
||||||
|
let (temp, dens, tempvec, _, raytab) = create_test_data();
|
||||||
|
let nd = temp.len();
|
||||||
|
let numtemp = tempvec.len();
|
||||||
|
let numrho = 1;
|
||||||
|
|
||||||
|
let ttab1 = tempvec[0];
|
||||||
|
let ttab2 = tempvec[numtemp - 1];
|
||||||
|
|
||||||
|
// 单列密度网格
|
||||||
|
let rhomat: Vec<Vec<f64>> = (0..numtemp).map(|_| vec![-10.0]).collect();
|
||||||
|
let raytab_single: Vec<Vec<f64>> = raytab.iter().map(|r| vec![r[0]]).collect();
|
||||||
|
|
||||||
|
let mut raysc = vec![0.0; nd];
|
||||||
|
|
||||||
|
rayset(
|
||||||
|
nd, &temp, &dens, numtemp, numrho, ttab1, ttab2,
|
||||||
|
&tempvec, &rhomat, &raytab_single, &mut raysc,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 验证结果为正值
|
||||||
|
for id in 0..nd {
|
||||||
|
assert!(raysc[id] > 0.0, "raysc[{}] should be positive", id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rayset_special_case() {
|
||||||
|
let nd = 3;
|
||||||
|
let temp = vec![6000.0, 7000.0, 8000.0];
|
||||||
|
let dens = vec![1e-10, 2e-10, 3e-10];
|
||||||
|
|
||||||
|
let numtemp = nd; // 特殊情况
|
||||||
|
let numrho = 1;
|
||||||
|
|
||||||
|
let tempvec = vec![0.0; nd]; // 不使用
|
||||||
|
let rhomat = vec![vec![-10.0]; nd];
|
||||||
|
let raytab = vec![
|
||||||
|
vec![-20.0],
|
||||||
|
vec![-19.0],
|
||||||
|
vec![-18.0],
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut raysc = vec![0.0; nd];
|
||||||
|
|
||||||
|
rayset(
|
||||||
|
nd, &temp, &dens, numtemp, numrho, 0.0, 1.0,
|
||||||
|
&tempvec, &rhomat, &raytab, &mut raysc,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
for id in 0..nd {
|
||||||
|
let expected = raytab[id][0].exp();
|
||||||
|
assert!(
|
||||||
|
(raysc[id] - expected).abs() < 1e-10,
|
||||||
|
"raysc[{}] = {}, expected {}",
|
||||||
|
id, raysc[id], expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
143
src/math/rte_sc.rs
Normal file
143
src/math/rte_sc.rs
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
//! 短特征辐射转移求解器
|
||||||
|
//!
|
||||||
|
//! 原始 Fortran: rte_sc.f
|
||||||
|
//! Short Characteristics 方法求解辐射转移方程
|
||||||
|
|
||||||
|
/// 短特征辐射转移求解器
|
||||||
|
///
|
||||||
|
/// 对已知源函数的辐射转移方程进行形式求解
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
/// - `dtau`: 光学深度增量 (nd-1 个元素)
|
||||||
|
/// - `st0`: 总源函数 (nd 个元素)
|
||||||
|
/// - `rup`: 上边界强度 (id=1)
|
||||||
|
/// - `rdown`: 下边界强度 (id=nd)
|
||||||
|
/// - `amu0`: 传播角度余弦 (相对于法线)
|
||||||
|
/// - amu0 < 0: 入射辐射 (向下)
|
||||||
|
/// - amu0 >= 0: 出射辐射 (向上)
|
||||||
|
/// - `ri`: 辐射强度 (输出)
|
||||||
|
/// - `ali`: Lambda 算子对角元素 (输出)
|
||||||
|
/// - `nd`: 深度点数
|
||||||
|
///
|
||||||
|
/// # 算法
|
||||||
|
/// 使用短特征 (Short Characteristics) 方法
|
||||||
|
pub fn rte_sc(
|
||||||
|
dtau: &[f64],
|
||||||
|
st0: &[f64],
|
||||||
|
rup: f64,
|
||||||
|
rdown: f64,
|
||||||
|
amu0: f64,
|
||||||
|
ri: &mut [f64],
|
||||||
|
ali: &mut [f64],
|
||||||
|
nd: usize,
|
||||||
|
) {
|
||||||
|
// 工作数组
|
||||||
|
let mut dtx1 = vec![0.0; nd];
|
||||||
|
let mut dtx2 = vec![0.0; nd];
|
||||||
|
let mut dtx0 = vec![0.0; nd];
|
||||||
|
|
||||||
|
// 常量
|
||||||
|
let un = 1.0_f64;
|
||||||
|
|
||||||
|
// 预计算指数因子
|
||||||
|
for id in 0..nd - 1 {
|
||||||
|
dtx1[id] = (-dtau[id]).exp();
|
||||||
|
dtx2[id] = (un - dtx1[id]) / dtau[id];
|
||||||
|
dtx0[id] = un - dtx2[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 入射强度 (向下传播)
|
||||||
|
if amu0 < 0.0 {
|
||||||
|
let id = 0;
|
||||||
|
ri[id] = rup;
|
||||||
|
for id in 0..nd - 1 {
|
||||||
|
ri[id + 1] = ri[id] * dtx1[id]
|
||||||
|
+ st0[id] * (dtx2[id] - dtx1[id])
|
||||||
|
+ st0[id + 1] * dtx0[id];
|
||||||
|
ali[id + 1] = dtx0[id];
|
||||||
|
}
|
||||||
|
ali[0] = 0.0;
|
||||||
|
} else {
|
||||||
|
// 出射强度 (向上传播)
|
||||||
|
ri[nd - 1] = rdown;
|
||||||
|
for id in (0..nd - 1).rev() {
|
||||||
|
ri[id] = ri[id + 1] * dtx1[id]
|
||||||
|
+ st0[id] * dtx0[id]
|
||||||
|
+ st0[id + 1] * (dtx2[id] - dtx1[id]);
|
||||||
|
ali[id] = dtx0[id];
|
||||||
|
}
|
||||||
|
ali[nd - 1] = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rte_sc_incoming() {
|
||||||
|
// 测试入射辐射
|
||||||
|
let nd = 5;
|
||||||
|
let dtau = vec![0.5; nd - 1];
|
||||||
|
let st0 = vec![1.0; nd];
|
||||||
|
let rup = 0.0;
|
||||||
|
let rdown = 0.0;
|
||||||
|
let amu0 = -1.0; // 入射
|
||||||
|
|
||||||
|
let mut ri = vec![0.0; nd];
|
||||||
|
let mut ali = vec![0.0; nd];
|
||||||
|
|
||||||
|
rte_sc(&dtau, &st0, rup, rdown, amu0, &mut ri, &mut ali, nd);
|
||||||
|
|
||||||
|
// 入射边界条件
|
||||||
|
assert!((ri[0] - 0.0).abs() < 1e-10);
|
||||||
|
assert!((ali[0] - 0.0).abs() < 1e-10);
|
||||||
|
// 强度应该随深度增加
|
||||||
|
assert!(ri[nd - 1] > ri[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rte_sc_outgoing() {
|
||||||
|
// 测试出射辐射
|
||||||
|
let nd = 5;
|
||||||
|
let dtau = vec![0.5; nd - 1];
|
||||||
|
let st0 = vec![1.0; nd];
|
||||||
|
let rup = 0.0;
|
||||||
|
let rdown = 0.0;
|
||||||
|
let amu0 = 1.0; // 出射
|
||||||
|
|
||||||
|
let mut ri = vec![0.0; nd];
|
||||||
|
let mut ali = vec![0.0; nd];
|
||||||
|
|
||||||
|
rte_sc(&dtau, &st0, rup, rdown, amu0, &mut ri, &mut ali, nd);
|
||||||
|
|
||||||
|
// 出射边界条件
|
||||||
|
assert!((ri[nd - 1] - 0.0).abs() < 1e-10);
|
||||||
|
assert!((ali[nd - 1] - 0.0).abs() < 1e-10);
|
||||||
|
// 强度应该随深度增加
|
||||||
|
assert!(ri[0] > ri[nd - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rte_sc_consistency() {
|
||||||
|
// 测试源函数为常数时的渐进行为
|
||||||
|
let nd = 100;
|
||||||
|
let dtau = vec![0.1; nd - 1];
|
||||||
|
let st0 = vec![2.0; nd];
|
||||||
|
let rup = 0.0;
|
||||||
|
let rdown = 0.0;
|
||||||
|
|
||||||
|
let mut ri_in = vec![0.0; nd];
|
||||||
|
let mut ali_in = vec![0.0; nd];
|
||||||
|
let mut ri_out = vec![0.0; nd];
|
||||||
|
let mut ali_out = vec![0.0; nd];
|
||||||
|
|
||||||
|
rte_sc(&dtau, &st0, rup, rdown, -1.0, &mut ri_in, &mut ali_in, nd);
|
||||||
|
rte_sc(&dtau, &st0, rup, rdown, 1.0, &mut ri_out, &mut ali_out, nd);
|
||||||
|
|
||||||
|
// 在深部,入射和出射强度应趋近于源函数
|
||||||
|
// (对于光学厚介质)
|
||||||
|
assert!((ri_in[nd - 1] - st0[nd - 1]).abs() < 0.1);
|
||||||
|
assert!((ri_out[0] - st0[0]).abs() < 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
150
src/math/rtefe2.rs
Normal file
150
src/math/rtefe2.rs
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
//! Feautrier 辐射转移求解器 (二阶格式)
|
||||||
|
//!
|
||||||
|
//! 原始 Fortran: rtefe2.f
|
||||||
|
//! 原始 Feautrier 二阶方案
|
||||||
|
|
||||||
|
/// Feautrier 辐射转移求解器 (二阶格式)
|
||||||
|
///
|
||||||
|
/// 对已知源函数的辐射转移方程进行形式求解
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
/// - `dtau`: 光学深度增量 (nd-1 个元素)
|
||||||
|
/// - `s`: 源函数 (nd 个元素)
|
||||||
|
/// - `rup`: 上边界强度 (id=1)
|
||||||
|
/// - `rdown`: 下边界强度 (id=nd)
|
||||||
|
/// - `ri`: Feautrier 变量 (输出)
|
||||||
|
/// - `nd`: 深度点数
|
||||||
|
///
|
||||||
|
/// # 算法
|
||||||
|
/// 使用原始 Feautrier 二阶差分方案
|
||||||
|
/// 通过三对角矩阵消元法求解
|
||||||
|
pub fn rtefe2(
|
||||||
|
dtau: &[f64],
|
||||||
|
s: &[f64],
|
||||||
|
rup: f64,
|
||||||
|
rdown: f64,
|
||||||
|
ri: &mut [f64],
|
||||||
|
nd: usize,
|
||||||
|
) {
|
||||||
|
// 工作数组
|
||||||
|
let mut a = vec![0.0; nd];
|
||||||
|
let mut b = vec![0.0; nd];
|
||||||
|
let mut c = vec![0.0; nd];
|
||||||
|
let mut d = vec![0.0; nd];
|
||||||
|
let mut f = vec![0.0; nd];
|
||||||
|
let mut v = vec![0.0; nd];
|
||||||
|
let mut z = vec![0.0; nd];
|
||||||
|
|
||||||
|
let one = 1.0_f64;
|
||||||
|
let two = 2.0_f64;
|
||||||
|
|
||||||
|
// 上边界条件
|
||||||
|
let id = 0;
|
||||||
|
let cc = two / dtau[id];
|
||||||
|
c[id] = cc / dtau[id];
|
||||||
|
b[id] = one + cc + c[id];
|
||||||
|
a[id] = 0.0;
|
||||||
|
v[id] = s[id] + cc * rup;
|
||||||
|
|
||||||
|
// 正常深度点
|
||||||
|
for id in 1..nd - 1 {
|
||||||
|
let dtinv = two / (dtau[id - 1] + dtau[id]);
|
||||||
|
a[id] = dtinv / dtau[id - 1];
|
||||||
|
c[id] = dtinv / dtau[id];
|
||||||
|
b[id] = one + a[id] + c[id];
|
||||||
|
v[id] = s[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下边界条件
|
||||||
|
let id = nd - 1;
|
||||||
|
let aa = two / dtau[id - 1];
|
||||||
|
a[id] = aa / dtau[id - 1];
|
||||||
|
b[id] = one + aa + a[id];
|
||||||
|
if rdown == 0.0 {
|
||||||
|
b[id] = one + a[id];
|
||||||
|
}
|
||||||
|
c[id] = 0.0;
|
||||||
|
v[id] = s[id] + aa * rdown;
|
||||||
|
|
||||||
|
// 前向消元
|
||||||
|
// 上边界
|
||||||
|
f[0] = (b[0] - c[0]) / c[0];
|
||||||
|
d[0] = one / (one + f[0]);
|
||||||
|
z[0] = v[0] / b[0];
|
||||||
|
|
||||||
|
// 正常深度点
|
||||||
|
for id in 1..nd - 1 {
|
||||||
|
f[id] = (b[id] - a[id] - c[id] + a[id] * f[id - 1] * d[id - 1]) / c[id];
|
||||||
|
d[id] = one / (one + f[id]);
|
||||||
|
z[id] = (v[id] + a[id] * z[id - 1]) * d[id] / c[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下边界
|
||||||
|
let id = nd - 1;
|
||||||
|
z[id] = (v[id] + a[id] * z[id - 1]) / (b[id] - a[id] * d[id - 1]);
|
||||||
|
|
||||||
|
// 后向回代
|
||||||
|
ri[nd - 1] = z[nd - 1];
|
||||||
|
for id in (0..nd - 1).rev() {
|
||||||
|
ri[id] = ri[id + 1] * d[id] + z[id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rtefe2_simple() {
|
||||||
|
// 简单测试:常数源函数
|
||||||
|
let nd = 10;
|
||||||
|
let dtau = vec![0.5; nd - 1];
|
||||||
|
let s = vec![1.0; nd];
|
||||||
|
let rup = 0.0;
|
||||||
|
let rdown = 0.0;
|
||||||
|
|
||||||
|
let mut ri = vec![0.0; nd];
|
||||||
|
|
||||||
|
rtefe2(&dtau, &s, rup, rdown, &mut ri, nd);
|
||||||
|
|
||||||
|
// 强度应该为正
|
||||||
|
for id in 0..nd {
|
||||||
|
assert!(ri[id] > 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rtefe2_boundaries() {
|
||||||
|
// 测试边界条件 - 使用光学厚情况
|
||||||
|
let nd = 50;
|
||||||
|
let dtau = vec![1.0; nd - 1];
|
||||||
|
let s = vec![2.0; nd];
|
||||||
|
let rup = 0.5;
|
||||||
|
let rdown = 3.0;
|
||||||
|
|
||||||
|
let mut ri = vec![0.0; nd];
|
||||||
|
|
||||||
|
rtefe2(&dtau, &s, rup, rdown, &mut ri, nd);
|
||||||
|
|
||||||
|
// 在光学厚情况下,深部强度应趋近于源函数
|
||||||
|
// 总光学深度 = 49,足够厚
|
||||||
|
assert!((ri[nd - 1] - s[nd - 1]).abs() < 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rtefe2_optically_thick() {
|
||||||
|
// 光学厚情况
|
||||||
|
let nd = 50;
|
||||||
|
let dtau = vec![2.0; nd - 1]; // 大光学深度
|
||||||
|
let s = vec![5.0; nd];
|
||||||
|
let rup = 0.0;
|
||||||
|
let rdown = 0.0;
|
||||||
|
|
||||||
|
let mut ri = vec![0.0; nd];
|
||||||
|
|
||||||
|
rtefe2(&dtau, &s, rup, rdown, &mut ri, nd);
|
||||||
|
|
||||||
|
// 在深部应趋近于源函数
|
||||||
|
assert!((ri[nd - 1] - s[nd - 1]).abs() < 0.01);
|
||||||
|
}
|
||||||
|
}
|
||||||
183
src/math/rtesol.rs
Normal file
183
src/math/rtesol.rs
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
//! DFE 辐射转移求解器
|
||||||
|
//!
|
||||||
|
//! 原始 Fortran: rtesol.f
|
||||||
|
//! 不连续有限元法 (Discontinuous Finite Element)
|
||||||
|
//! 参考: Castor, Dykema, Klein, 1992, ApJ 387, 561
|
||||||
|
|
||||||
|
/// DFE 辐射转移求解器
|
||||||
|
///
|
||||||
|
/// 对已知源函数的辐射转移方程进行形式求解
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
/// - `dtau`: 光学深度增量 (nd-1 个元素)
|
||||||
|
/// - `st0`: 源函数 (nd 个元素)
|
||||||
|
/// - `rup`: 上边界强度 (id=1)
|
||||||
|
/// - `rdown`: 下边界强度 (id=nd)
|
||||||
|
/// - `amu0`: 传播角度余弦
|
||||||
|
/// - amu0 < 0: 入射辐射 (向下)
|
||||||
|
/// - amu0 >= 0: 出射辐射 (向上)
|
||||||
|
/// - `ri`: 辐射强度 (输出)
|
||||||
|
/// - `ali`: Lambda 算子对角元素 (输出)
|
||||||
|
/// - `nd`: 深度点数
|
||||||
|
///
|
||||||
|
/// # 算法
|
||||||
|
/// 使用不连续有限元法 (DFE)
|
||||||
|
pub fn rtesol(
|
||||||
|
dtau: &[f64],
|
||||||
|
st0: &[f64],
|
||||||
|
rup: f64,
|
||||||
|
rdown: f64,
|
||||||
|
amu0: f64,
|
||||||
|
ri: &mut [f64],
|
||||||
|
ali: &mut [f64],
|
||||||
|
nd: usize,
|
||||||
|
) {
|
||||||
|
// 工作数组
|
||||||
|
let mut rim = vec![0.0; nd];
|
||||||
|
let mut rip = vec![0.0; nd];
|
||||||
|
let mut aim = vec![0.0; nd];
|
||||||
|
let mut aip = vec![0.0; nd];
|
||||||
|
|
||||||
|
let one = 1.0_f64;
|
||||||
|
let two = 2.0_f64;
|
||||||
|
let un = 1.0_f64;
|
||||||
|
|
||||||
|
// 入射强度 (向下传播)
|
||||||
|
if amu0 < 0.0 {
|
||||||
|
let id = 0;
|
||||||
|
rip[id] = rup;
|
||||||
|
let dt0 = dtau[id];
|
||||||
|
let dtaup1 = dt0 + one;
|
||||||
|
let dtau2 = dt0 * dt0;
|
||||||
|
let bb = two * dtaup1;
|
||||||
|
let cc = dt0 * dtaup1;
|
||||||
|
let aa = dtau2 + bb;
|
||||||
|
rim[id] = (aa * rip[id] - cc * st0[id] + dt0 * st0[id + 1]) / bb;
|
||||||
|
|
||||||
|
for id in 0..nd - 1 {
|
||||||
|
let dt0 = dtau[id];
|
||||||
|
let dtaup1 = dt0 + one;
|
||||||
|
let dtau2 = dt0 * dt0;
|
||||||
|
let bb = two * dtaup1;
|
||||||
|
let cc = dt0 * dtaup1;
|
||||||
|
let aa = dtau2 + bb;
|
||||||
|
rim[id + 1] = (two * rim[id] + dt0 * st0[id] + cc * st0[id + 1]) / aa;
|
||||||
|
rip[id] = (bb * rim[id] + cc * st0[id] - dt0 * st0[id + 1]) / aa;
|
||||||
|
aim[id + 1] = cc / aa;
|
||||||
|
aip[id] = (cc + bb * aim[id]) / aa;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算平均强度
|
||||||
|
for id in 1..nd - 1 {
|
||||||
|
let dtt = un / (dtau[id - 1] + dtau[id]);
|
||||||
|
ri[id] = (rim[id] * dtau[id] + rip[id] * dtau[id - 1]) * dtt;
|
||||||
|
ali[id] = (aim[id] * dtau[id] + aip[id] * dtau[id - 1]) * dtt;
|
||||||
|
}
|
||||||
|
ri[0] = rip[0];
|
||||||
|
ri[nd - 1] = rim[nd - 1];
|
||||||
|
ali[0] = aim[0];
|
||||||
|
ali[nd - 1] = aim[nd - 1];
|
||||||
|
} else {
|
||||||
|
// 出射强度 (向上传播)
|
||||||
|
rip[nd - 1] = rdown;
|
||||||
|
let id = nd - 2;
|
||||||
|
let dt0 = dtau[id];
|
||||||
|
let dtaup1 = dt0 + one;
|
||||||
|
let dtau2 = dt0 * dt0;
|
||||||
|
let bb = two * dtaup1;
|
||||||
|
let cc = dt0 * dtaup1;
|
||||||
|
let aa = dtau2 + bb;
|
||||||
|
rim[id + 1] = (aa * rip[id + 1] - cc * st0[id + 1] + dt0 * st0[id]) / bb;
|
||||||
|
|
||||||
|
for id in (0..nd - 1).rev() {
|
||||||
|
let dt0 = dtau[id];
|
||||||
|
let dtaup1 = dt0 + one;
|
||||||
|
let dtau2 = dt0 * dt0;
|
||||||
|
let bb = two * dtaup1;
|
||||||
|
let cc = dt0 * dtaup1;
|
||||||
|
let aa = dtau2 + bb;
|
||||||
|
rim[id] = (two * rim[id + 1] + dt0 * st0[id + 1] + cc * st0[id]) / aa;
|
||||||
|
rip[id + 1] = (bb * rim[id + 1] + cc * st0[id + 1] - dt0 * st0[id]) / aa;
|
||||||
|
aim[id] = cc / aa;
|
||||||
|
aip[id + 1] = (cc + bb * aim[id + 1]) / aa;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算平均强度
|
||||||
|
for id in 1..nd - 1 {
|
||||||
|
let dtt = un / (dtau[id - 1] + dtau[id]);
|
||||||
|
ri[id] = (rim[id] * dtau[id - 1] + rip[id] * dtau[id]) * dtt;
|
||||||
|
ali[id] = (aim[id] * dtau[id - 1] + aip[id] * dtau[id]) * dtt;
|
||||||
|
}
|
||||||
|
ri[0] = rim[0];
|
||||||
|
ri[nd - 1] = rip[nd - 1];
|
||||||
|
ali[0] = aim[0];
|
||||||
|
ali[nd - 1] = aim[nd - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rtesol_incoming() {
|
||||||
|
let nd = 10;
|
||||||
|
let dtau = vec![0.5; nd - 1];
|
||||||
|
let st0 = vec![1.0; nd];
|
||||||
|
let rup = 0.0;
|
||||||
|
let rdown = 0.0;
|
||||||
|
let amu0 = -1.0;
|
||||||
|
|
||||||
|
let mut ri = vec![0.0; nd];
|
||||||
|
let mut ali = vec![0.0; nd];
|
||||||
|
|
||||||
|
rtesol(&dtau, &st0, rup, rdown, amu0, &mut ri, &mut ali, nd);
|
||||||
|
|
||||||
|
// 强度应该为正
|
||||||
|
for id in 0..nd {
|
||||||
|
assert!(ri[id] >= 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rtesol_outgoing() {
|
||||||
|
let nd = 10;
|
||||||
|
let dtau = vec![0.5; nd - 1];
|
||||||
|
let st0 = vec![1.0; nd];
|
||||||
|
let rup = 0.0;
|
||||||
|
let rdown = 0.0;
|
||||||
|
let amu0 = 1.0;
|
||||||
|
|
||||||
|
let mut ri = vec![0.0; nd];
|
||||||
|
let mut ali = vec![0.0; nd];
|
||||||
|
|
||||||
|
rtesol(&dtau, &st0, rup, rdown, amu0, &mut ri, &mut ali, nd);
|
||||||
|
|
||||||
|
// 强度应该为正
|
||||||
|
for id in 0..nd {
|
||||||
|
assert!(ri[id] >= 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rtesol_optically_thick() {
|
||||||
|
// 光学厚情况
|
||||||
|
let nd = 50;
|
||||||
|
let dtau = vec![2.0; nd - 1];
|
||||||
|
let st0 = vec![3.0; nd];
|
||||||
|
let rup = 0.0;
|
||||||
|
let rdown = 0.0;
|
||||||
|
|
||||||
|
let mut ri_in = vec![0.0; nd];
|
||||||
|
let mut ali_in = vec![0.0; nd];
|
||||||
|
let mut ri_out = vec![0.0; nd];
|
||||||
|
let mut ali_out = vec![0.0; nd];
|
||||||
|
|
||||||
|
rtesol(&dtau, &st0, rup, rdown, -1.0, &mut ri_in, &mut ali_in, nd);
|
||||||
|
rtesol(&dtau, &st0, rup, rdown, 1.0, &mut ri_out, &mut ali_out, nd);
|
||||||
|
|
||||||
|
// 在深部应趋近于源函数
|
||||||
|
assert!((ri_in[nd - 1] - st0[nd - 1]).abs() < 0.1);
|
||||||
|
assert!((ri_out[0] - st0[0]).abs() < 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
153
src/math/sffhmi_add.rs
Normal file
153
src/math/sffhmi_add.rs
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
//! H- 自由-自由截面计算。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `sffhmi_add.f`
|
||||||
|
//!
|
||||||
|
//! 来自 Bell and Berrington J.Phys.B, vol. 20, 801-806, 1987.
|
||||||
|
//! 数据取自 Kurucz ATLAS9。
|
||||||
|
|
||||||
|
use crate::math::ylintp;
|
||||||
|
|
||||||
|
// 物理常数
|
||||||
|
const CONFF: f64 = 5040.0 * 1.380658E-16;
|
||||||
|
const CONTH: f64 = 5040.0;
|
||||||
|
const HK: f64 = 4.79928144E-11;
|
||||||
|
|
||||||
|
// 波长网格 (微米)
|
||||||
|
const WAVEK: [f64; 22] = [
|
||||||
|
0.50, 0.40, 0.35, 0.30, 0.25, 0.20, 0.18, 0.16, 0.14, 0.12, 0.10,
|
||||||
|
0.09, 0.08, 0.07, 0.06, 0.05, 0.04, 0.03, 0.02, 0.01, 0.008, 0.006,
|
||||||
|
];
|
||||||
|
|
||||||
|
// theta 网格
|
||||||
|
const THETAFF: [f64; 11] = [
|
||||||
|
0.5, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.8, 3.6,
|
||||||
|
];
|
||||||
|
|
||||||
|
// H- 自由-自由系数表 (theta x wavelength)
|
||||||
|
// 前 11 列是 FFBEG,后 11 列是 FFEND
|
||||||
|
const FFCS: [[f64; 22]; 11] = [
|
||||||
|
// FFBEG (前11列) + FFEND (后11列)
|
||||||
|
[0.0178, 0.0222, 0.0308, 0.0402, 0.0498, 0.0596, 0.0695, 0.0795, 0.0896, 0.131, 0.172,
|
||||||
|
0.358, 0.432, 0.572, 0.702, 0.825, 0.943, 1.06, 1.17, 1.28, 1.73, 2.17],
|
||||||
|
[0.0228, 0.0280, 0.0388, 0.0499, 0.0614, 0.0732, 0.0851, 0.0972, 0.110, 0.160, 0.211,
|
||||||
|
0.448, 0.539, 0.711, 0.871, 1.02, 1.16, 1.29, 1.43, 1.57, 2.09, 2.60],
|
||||||
|
[0.0277, 0.0342, 0.0476, 0.0615, 0.0760, 0.0908, 0.105, 0.121, 0.136, 0.199, 0.262,
|
||||||
|
0.579, 0.699, 0.924, 1.13, 1.33, 1.51, 1.69, 1.86, 2.02, 2.67, 3.31],
|
||||||
|
[0.0364, 0.0447, 0.0616, 0.0789, 0.0966, 0.114, 0.132, 0.150, 0.169, 0.243, 0.318,
|
||||||
|
0.781, 0.940, 1.24, 1.52, 1.78, 2.02, 2.26, 2.48, 2.69, 3.52, 4.31],
|
||||||
|
[0.0520, 0.0633, 0.0859, 0.108, 0.131, 0.154, 0.178, 0.201, 0.225, 0.321, 0.418,
|
||||||
|
1.11, 1.34, 1.77, 2.17, 2.53, 2.87, 3.20, 3.51, 3.80, 4.92, 5.97],
|
||||||
|
[0.0791, 0.0959, 0.129, 0.161, 0.194, 0.227, 0.260, 0.293, 0.327, 0.463, 0.602,
|
||||||
|
1.73, 2.08, 2.74, 3.37, 3.90, 4.50, 5.01, 5.50, 5.95, 7.59, 9.06],
|
||||||
|
[0.0965, 0.117, 0.157, 0.195, 0.234, 0.272, 0.311, 0.351, 0.390, 0.549, 0.711,
|
||||||
|
3.04, 3.65, 4.80, 5.86, 6.86, 7.79, 8.67, 9.50, 10.3, 13.2, 15.6],
|
||||||
|
[0.121, 0.146, 0.195, 0.241, 0.288, 0.334, 0.381, 0.428, 0.475, 0.667, 0.861,
|
||||||
|
6.79, 8.16, 10.7, 13.1, 15.3, 17.4, 19.4, 21.2, 23.0, 29.5, 35.0],
|
||||||
|
[0.154, 0.188, 0.249, 0.309, 0.367, 0.424, 0.482, 0.539, 0.597, 0.830, 1.07,
|
||||||
|
27.0, 32.4, 42.6, 51.9, 60.7, 68.9, 76.8, 84.2, 91.4, 117.0, 140.0],
|
||||||
|
[0.208, 0.250, 0.332, 0.409, 0.484, 0.557, 0.630, 0.702, 0.774, 1.06, 1.36,
|
||||||
|
42.3, 50.6, 66.4, 80.8, 94.5, 107.0, 120.0, 131.0, 142.0, 183.0, 219.0],
|
||||||
|
[0.293, 0.354, 0.468, 0.576, 0.677, 0.777, 0.874, 0.969, 1.06, 1.45, 1.83,
|
||||||
|
75.1, 90.0, 118.0, 144.0, 168.0, 191.0, 212.0, 234.0, 253.0, 325.0, 388.0],
|
||||||
|
];
|
||||||
|
|
||||||
|
/// H- 自由-自由截面计算。
|
||||||
|
///
|
||||||
|
/// 来自 Bell and Berrington J.Phys.B, vol. 20, 801-806, 1987.
|
||||||
|
/// 数据取自 Kurucz ATLAS9。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `popi` - 中性氢占据数
|
||||||
|
/// * `fr` - 频率 (Hz)
|
||||||
|
/// * `t` - 温度 (K)
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// H- 自由-自由不透明度。
|
||||||
|
pub fn sffhmi_add(popi: f64, fr: f64, t: f64) -> f64 {
|
||||||
|
// 预计算表格 (使用 lazy_static 或 once_cell 会更好,但这里用函数内静态)
|
||||||
|
static WFFLOG: std::sync::OnceLock<[f64; 22]> = std::sync::OnceLock::new();
|
||||||
|
static FFLOG: std::sync::OnceLock<[[f64; 11]; 22]> = std::sync::OnceLock::new();
|
||||||
|
|
||||||
|
let wfflog = WFFLOG.get_or_init(|| {
|
||||||
|
let mut arr = [0.0; 22];
|
||||||
|
for i in 0..22 {
|
||||||
|
arr[i] = (91.134 / WAVEK[i]).ln();
|
||||||
|
}
|
||||||
|
arr
|
||||||
|
});
|
||||||
|
|
||||||
|
let fflog = FFLOG.get_or_init(|| {
|
||||||
|
let mut arr = [[0.0; 11]; 22];
|
||||||
|
for iw in 0..22 {
|
||||||
|
for it in 0..11 {
|
||||||
|
arr[iw][it] = (FFCS[it][iw] * 1e-26).ln();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arr
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算波长和 log
|
||||||
|
let wave = 2.99792458E17 / fr;
|
||||||
|
let wavelog = wave.ln();
|
||||||
|
|
||||||
|
// 对每个 theta 进行波长插值
|
||||||
|
let mut fftt = [0.0; 11];
|
||||||
|
for itheta in 0..11 {
|
||||||
|
let mut fflog2 = [0.0; 22];
|
||||||
|
for iw in 0..22 {
|
||||||
|
fflog2[iw] = fflog[iw][itheta];
|
||||||
|
}
|
||||||
|
let fftlog = ylintp(wfflog, &fflog2, wavelog);
|
||||||
|
fftt[itheta] = fftlog.exp() / THETAFF[itheta] * CONFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对 theta 进行插值
|
||||||
|
let theta = CONTH / t;
|
||||||
|
let ffth = ylintp(&THETAFF, &fftt, theta);
|
||||||
|
|
||||||
|
// 最终不透明度
|
||||||
|
ffth * popi / (1.0 - (-HK * fr / t).exp())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sffhmi_add_basic() {
|
||||||
|
// 基本测试
|
||||||
|
let popi = 1e10;
|
||||||
|
let fr = 1e15; // 紫外
|
||||||
|
let t = 6000.0;
|
||||||
|
|
||||||
|
let result = sffhmi_add(popi, fr, t);
|
||||||
|
assert!(result.is_finite());
|
||||||
|
assert!(result > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sffhmi_add_visible() {
|
||||||
|
// 可见光
|
||||||
|
let popi = 1e12;
|
||||||
|
let fr = 5e14; // 600 nm
|
||||||
|
let t = 5778.0; // 太阳温度
|
||||||
|
|
||||||
|
let result = sffhmi_add(popi, fr, t);
|
||||||
|
assert!(result.is_finite());
|
||||||
|
assert!(result > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sffhmi_add_infrared() {
|
||||||
|
// 红外
|
||||||
|
let popi = 1e14;
|
||||||
|
let fr = 1e14; // 3 微米
|
||||||
|
let t = 5000.0;
|
||||||
|
|
||||||
|
let result = sffhmi_add(popi, fr, t);
|
||||||
|
assert!(result.is_finite());
|
||||||
|
assert!(result > 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
363
src/math/sgmer.rs
Normal file
363
src/math/sgmer.rs
Normal file
@ -0,0 +1,363 @@
|
|||||||
|
//! 合并能级 (merged levels) Stark 展宽截面计算。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `sgmer0.f`, `sgmer1.f`, `sgmerd.f`。
|
||||||
|
//!
|
||||||
|
//! 对于高激发态氢能级,使用合并能级近似计算光电离截面。
|
||||||
|
//! 这是 Stark 展宽处理的一部分。
|
||||||
|
|
||||||
|
use crate::state::{
|
||||||
|
AtomicData, InvInt, MrgPar, ModelState, WmComp,
|
||||||
|
MDEPTH, MLEVEL, MMER, NLMX,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 常量
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 氢 Rydberg 常数频率 (Hz)
|
||||||
|
const FRH: f64 = 3.28805e15;
|
||||||
|
/// 光电离截面常数 (2 × 2.815e29)
|
||||||
|
const PH2: f64 = 2.815e29 * 2.0;
|
||||||
|
/// EHB = 157802.77355 (温度倒数转换)
|
||||||
|
const EHB: f64 = 157802.77355;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// SGMER0 - 初始化合并能级截面
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 初始化合并能级的光电离截面。
|
||||||
|
///
|
||||||
|
/// 对于每个需要合并处理的能级 (IFWOP < 0),计算所有深度的
|
||||||
|
/// 预积分截面 SGMSUM。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `atomic` - 原子数据 (包含 IZ, NQUANT 等)
|
||||||
|
/// - `model` - 模型状态 (包含 TEMP1 深度数组)
|
||||||
|
/// - `wmcomp` - 氢能级权重 (包含 WNHINT 占据概率积分)
|
||||||
|
/// - `mrgpar` - 合并能级参数 (会被修改)
|
||||||
|
/// - `invint` - 逆整数幂数组 (包含 XI2, XI3)
|
||||||
|
/// - `nd` - 深度点数
|
||||||
|
/// - `nlevel` - 能级数
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// SUBROUTINE SGMER0
|
||||||
|
/// IMER=0
|
||||||
|
/// DO 100 II=1,NLEVEL
|
||||||
|
/// IF(IFWOP(II).GE.0) GO TO 100
|
||||||
|
/// IMER=IMER+1
|
||||||
|
/// IMRG(II)=IMER
|
||||||
|
/// IIMER(IMER)=II
|
||||||
|
/// IE=IEL(II)
|
||||||
|
/// CH=IZ(IE)*IZ(IE)
|
||||||
|
/// FRCH(IMER)=FRH*CH
|
||||||
|
/// SGM0(IMER)=PH2*CH*CH
|
||||||
|
/// II0=NQUANT(II-1)+1
|
||||||
|
/// DO ID=1,ND
|
||||||
|
/// EX=EHB*CH*TEMP1(ID)
|
||||||
|
/// DO I=II0,NLMX
|
||||||
|
/// FREDG(I)=FRCH(IMER)*XI2(I)
|
||||||
|
/// EXI=EXP(EX*XI2(I))
|
||||||
|
/// S(I)=EXI*WNHINT(I,ID)*XI3(I)
|
||||||
|
/// SUM(I)=0.
|
||||||
|
/// END DO
|
||||||
|
/// SUM(NLMX)=S(NLMX)
|
||||||
|
/// SUD(NLMX)=S(NLMX)*XI2(NLMX)
|
||||||
|
/// DO I=NLMX-1,II0,-1
|
||||||
|
/// SUM(I)=SUM(I+1)+S(I)
|
||||||
|
/// END DO
|
||||||
|
/// DO I=1,II0-1
|
||||||
|
/// SUM(I)=SUM(II0)
|
||||||
|
/// END DO
|
||||||
|
/// SGEM=SGM0(IMER)/GMER(IMER,ID)
|
||||||
|
/// DO I=1,NLMX
|
||||||
|
/// SGMSUM(I,IMER,ID)=SUM(I)*SGEM
|
||||||
|
/// END DO
|
||||||
|
/// END DO
|
||||||
|
/// 100 CONTINUE
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
pub fn sgmer0(
|
||||||
|
atomic: &AtomicData,
|
||||||
|
model: &ModelState,
|
||||||
|
wmcomp: &WmComp,
|
||||||
|
mrgpar: &mut MrgPar,
|
||||||
|
invint: &InvInt,
|
||||||
|
nd: usize,
|
||||||
|
nlevel: usize,
|
||||||
|
) {
|
||||||
|
let mut imer: usize = 0;
|
||||||
|
|
||||||
|
// 遍历所有能级
|
||||||
|
for ii in 0..nlevel {
|
||||||
|
// 只处理需要合并的能级 (IFWOP < 0)
|
||||||
|
if wmcomp.ifwop[ii] >= 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
imer += 1;
|
||||||
|
let imer_idx = imer - 1; // 0-indexed
|
||||||
|
|
||||||
|
// 设置映射关系
|
||||||
|
mrgpar.imrg[ii] = imer as i32;
|
||||||
|
mrgpar.iimer[imer_idx] = (ii + 1) as i32; // Fortran 1-indexed
|
||||||
|
|
||||||
|
// 获取离子索引和电荷
|
||||||
|
let ie = atomic.levpar.iel[ii] as usize;
|
||||||
|
let ch = (atomic.ionpar.iz[ie] as f64).powi(2);
|
||||||
|
|
||||||
|
// 计算频率和截面常数
|
||||||
|
mrgpar.frch[imer_idx] = FRH * ch;
|
||||||
|
mrgpar.sgm0[imer_idx] = PH2 * ch * ch;
|
||||||
|
|
||||||
|
// 起始主量子数
|
||||||
|
// Fortran: II0 = NQUANT(II-1) + 1
|
||||||
|
// 这里 II 是 0-indexed,所以 II-1 在 Fortran 中是前一个能级
|
||||||
|
// 但仔细看代码,NQUANT(II-1) 应该是指当前能级的前一个能级的主量子数
|
||||||
|
let ii0 = if ii > 0 {
|
||||||
|
atomic.levpar.nquant[ii - 1] as usize + 1
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
|
||||||
|
// 工作数组
|
||||||
|
let mut fredg = vec![0.0; NLMX];
|
||||||
|
let mut s = vec![0.0; NLMX];
|
||||||
|
let mut sum = vec![0.0; NLMX];
|
||||||
|
let mut sud = vec![0.0; NLMX];
|
||||||
|
|
||||||
|
// 遍历所有深度
|
||||||
|
for id in 0..nd {
|
||||||
|
let ex = EHB * ch * model.modpar.temp1[id];
|
||||||
|
|
||||||
|
// 计算频率和积分项
|
||||||
|
for i in (ii0 - 1)..NLMX {
|
||||||
|
let i_idx = i; // 0-indexed
|
||||||
|
fredg[i_idx] = mrgpar.frch[imer_idx] * invint.xi2[i_idx];
|
||||||
|
let exi = (ex * invint.xi2[i_idx]).exp();
|
||||||
|
s[i_idx] = exi * wmcomp.wnhint[i_idx][id] * invint.xi3[i_idx];
|
||||||
|
sum[i_idx] = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 累积求和 (从 NLMX 向下)
|
||||||
|
let nlmx_idx = NLMX - 1;
|
||||||
|
sum[nlmx_idx] = s[nlmx_idx];
|
||||||
|
sud[nlmx_idx] = s[nlmx_idx] * invint.xi2[nlmx_idx];
|
||||||
|
|
||||||
|
// 从 NLMX-1 到 II0,反向累积
|
||||||
|
for i in (ii0 - 1..nlmx_idx).rev() {
|
||||||
|
sum[i] = sum[i + 1] + s[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对于 I < II0,使用 II0 的值
|
||||||
|
for i in 0..(ii0 - 1) {
|
||||||
|
sum[i] = sum[ii0 - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算 SGMSUM
|
||||||
|
let sgem = mrgpar.sgm0[imer_idx] / mrgpar.gmer[imer_idx][id];
|
||||||
|
for i in 0..NLMX {
|
||||||
|
mrgpar.sgmsum[i][imer_idx][id] = sum[i] * sgem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算 SGMSUD (导数)
|
||||||
|
// 从 opacf0.f 中可以看到 SUD(I) = S(I) * XI2(I)
|
||||||
|
// SGMSUD 在 sgmerd.f 中使用
|
||||||
|
for i in 0..NLMX {
|
||||||
|
mrgpar.sgmsud[i][imer_idx][id] = s[i] * invint.xi2[i] * sgem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// SGMER1 - 合并能级截面计算
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 计算合并能级的光电离截面。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `frinv` - 频率倒数 (1/ν)
|
||||||
|
/// - `fr3inv` - 频率三次方倒数 (1/ν³)
|
||||||
|
/// - `imer` - 合并能级索引 (1-indexed)
|
||||||
|
/// - `id` - 深度索引 (1-indexed)
|
||||||
|
/// - `mrgpar` - 合并能级参数
|
||||||
|
///
|
||||||
|
/// # 返回
|
||||||
|
///
|
||||||
|
/// 光电离截面 (cm²)
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// SUBROUTINE SGMER1(FRINV,FR3INV,IMER,ID,SGME1)
|
||||||
|
/// ISU=INT(SQRT(FRCH(IMER)*FRINV))+1
|
||||||
|
/// SGME1=SGMSUM(ISU,IMER,ID)*FR3INV
|
||||||
|
/// RETURN
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
pub fn sgmer1(
|
||||||
|
frinv: f64,
|
||||||
|
fr3inv: f64,
|
||||||
|
imer: i32,
|
||||||
|
id: usize,
|
||||||
|
mrgpar: &MrgPar,
|
||||||
|
) -> f64 {
|
||||||
|
let imer_idx = (imer - 1) as usize; // 转换为 0-indexed
|
||||||
|
let id_idx = id - 1; // 转换为 0-indexed (Fortran 1-indexed)
|
||||||
|
|
||||||
|
// ISU = INT(SQRT(FRCH(IMER)*FRINV)) + 1
|
||||||
|
let isu = (mrgpar.frch[imer_idx] * frinv).sqrt().floor() as usize + 1;
|
||||||
|
let isu_idx = isu - 1; // 转换为 0-indexed,但 isu 最小是 1
|
||||||
|
|
||||||
|
// 确保索引在范围内
|
||||||
|
if isu_idx >= NLMX {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SGME1 = SGMSUM(ISU,IMER,ID) * FR3INV
|
||||||
|
mrgpar.sgmsum[isu_idx][imer_idx][id_idx] * fr3inv
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// SGMERD - 合并能级截面和导数计算
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 计算合并能级的光电离截面及其导数。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `frinv` - 频率倒数 (1/ν)
|
||||||
|
/// - `fr3inv` - 频率三次方倒数 (1/ν³)
|
||||||
|
/// - `imer` - 合并能级索引 (1-indexed)
|
||||||
|
/// - `id` - 深度索引 (1-indexed)
|
||||||
|
/// - `mrgpar` - 合并能级参数
|
||||||
|
///
|
||||||
|
/// # 返回
|
||||||
|
///
|
||||||
|
/// (截面, 截面导数)
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// SUBROUTINE SGMERD(FRINV,FR3INV,IMER,ID,SGME1,DSGME1)
|
||||||
|
/// ISU=INT(SQRT(FRCH(IMER)*FRINV))+1
|
||||||
|
/// SGME1=SGMSUM(ISU,IMER,ID)*FR3INV
|
||||||
|
/// DSGME1=-SGMSUD(ISU,IMER,ID)*FR3INV
|
||||||
|
/// RETURN
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
pub fn sgmerd(
|
||||||
|
frinv: f64,
|
||||||
|
fr3inv: f64,
|
||||||
|
imer: i32,
|
||||||
|
id: usize,
|
||||||
|
mrgpar: &MrgPar,
|
||||||
|
) -> (f64, f64) {
|
||||||
|
let imer_idx = (imer - 1) as usize; // 转换为 0-indexed
|
||||||
|
let id_idx = id - 1; // 转换为 0-indexed (Fortran 1-indexed)
|
||||||
|
|
||||||
|
// ISU = INT(SQRT(FRCH(IMER)*FRINV)) + 1
|
||||||
|
let isu = (mrgpar.frch[imer_idx] * frinv).sqrt().floor() as usize + 1;
|
||||||
|
let isu_idx = isu - 1; // 转换为 0-indexed,但 isu 最小是 1
|
||||||
|
|
||||||
|
// 确保索引在范围内
|
||||||
|
if isu_idx >= NLMX {
|
||||||
|
return (0.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SGME1 = SGMSUM(ISU,IMER,ID) * FR3INV
|
||||||
|
let sgme1 = mrgpar.sgmsum[isu_idx][imer_idx][id_idx] * fr3inv;
|
||||||
|
|
||||||
|
// DSGME1 = -SGMSUD(ISU,IMER,ID) * FR3INV
|
||||||
|
let dsgme1 = -mrgpar.sgmsud[isu_idx][imer_idx][id_idx] * fr3inv;
|
||||||
|
|
||||||
|
(sgme1, dsgme1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn create_test_data() -> (AtomicData, ModelState, WmComp, MrgPar, InvInt) {
|
||||||
|
let mut atomic = AtomicData::new();
|
||||||
|
let model = ModelState::new();
|
||||||
|
let wmcomp = WmComp::default();
|
||||||
|
let mrgpar = MrgPar::default();
|
||||||
|
let invint = InvInt::default();
|
||||||
|
|
||||||
|
// 设置一个需要合并的能级 (IFWOP < 0)
|
||||||
|
atomic.levpar.iel[0] = 0; // 属于第一个离子
|
||||||
|
atomic.ionpar.iz[0] = 1; // 氢 Z=1
|
||||||
|
|
||||||
|
(atomic, model, wmcomp, mrgpar, invint)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invint_initialization() {
|
||||||
|
let invint = InvInt::default();
|
||||||
|
|
||||||
|
// 检查 XI2(1) = 1/1² = 1
|
||||||
|
assert!((invint.xi2[0] - 1.0).abs() < 1e-15);
|
||||||
|
|
||||||
|
// 检查 XI2(2) = 1/2² = 0.25
|
||||||
|
assert!((invint.xi2[1] - 0.25).abs() < 1e-15);
|
||||||
|
|
||||||
|
// 检查 XI3(1) = 1/1³ = 1
|
||||||
|
assert!((invint.xi3[0] - 1.0).abs() < 1e-15);
|
||||||
|
|
||||||
|
// 检查 XI3(2) = 1/2³ = 0.125
|
||||||
|
assert!((invint.xi3[1] - 0.125).abs() < 1e-15);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mrgpar_defaults() {
|
||||||
|
let mrgpar = MrgPar::default();
|
||||||
|
|
||||||
|
assert_eq!(mrgpar.sgm0.len(), MMER);
|
||||||
|
assert_eq!(mrgpar.frch.len(), MMER);
|
||||||
|
assert_eq!(mrgpar.imrg.len(), MLEVEL);
|
||||||
|
assert_eq!(mrgpar.iimer.len(), MMER);
|
||||||
|
assert_eq!(mrgpar.sgmsum.len(), NLMX);
|
||||||
|
assert_eq!(mrgpar.sgmsum[0].len(), MMER);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sgmer1_basic() {
|
||||||
|
let (_, _, _, mrgpar, _) = create_test_data();
|
||||||
|
|
||||||
|
// 设置测试值
|
||||||
|
let frinv = 1e-15; // 假设频率 1e15 Hz
|
||||||
|
let fr3inv = 1e-45;
|
||||||
|
let imer = 1;
|
||||||
|
let id = 1;
|
||||||
|
|
||||||
|
// 调用 sgmer1
|
||||||
|
let _sgme1 = sgmer1(frinv, fr3inv, imer, id, &mrgpar);
|
||||||
|
|
||||||
|
// 由于 GMER 默认为 0,sgmer0 会产生 inf/nan
|
||||||
|
// 这里只验证函数不会 panic
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sgmerd_basic() {
|
||||||
|
let (_, _, _, mrgpar, _) = create_test_data();
|
||||||
|
|
||||||
|
// 设置测试值
|
||||||
|
let frinv = 1e-15;
|
||||||
|
let fr3inv = 1e-45;
|
||||||
|
let imer = 1;
|
||||||
|
let id = 1;
|
||||||
|
|
||||||
|
// 调用 sgmerd
|
||||||
|
let (sgme1, dsgme1) = sgmerd(frinv, fr3inv, imer, id, &mrgpar);
|
||||||
|
|
||||||
|
// 由于数据未初始化,结果可能为 0
|
||||||
|
// 这里只验证函数不会 panic
|
||||||
|
assert!(sgme1.is_finite() || sgme1 == 0.0);
|
||||||
|
assert!(dsgme1.is_finite() || dsgme1 == 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
180
src/math/starka.rs
Normal file
180
src/math/starka.rs
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
//! 氢线 Stark 展宽近似表达式。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `starka.f`。
|
||||||
|
//!
|
||||||
|
//! 当温度或电子密度超出 Lemke 表格范围时,使用近似表达式计算氢线 Stark 轮廓。
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 常量参数
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
const F0: f64 = -0.5758228;
|
||||||
|
const F1: f64 = 0.4796232;
|
||||||
|
const F2: f64 = 0.07209481;
|
||||||
|
const AL: f64 = 1.26;
|
||||||
|
|
||||||
|
const SD: f64 = 0.5641895;
|
||||||
|
const SLO: f64 = -2.5;
|
||||||
|
const THRA: f64 = 1.5;
|
||||||
|
const BL1: f64 = 1.14;
|
||||||
|
const BL2: f64 = 11.4;
|
||||||
|
|
||||||
|
const SAC: f64 = 0.08;
|
||||||
|
const PISQ1: f64 = 1.0 / 1.77245385090551; // 1 / sqrt(pi)
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// STARKA - Stark 展宽近似表达式
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 计算氢线 Stark 展宽的近似表达式。
|
||||||
|
///
|
||||||
|
/// 当温度或电子密度超出 Lemke 表格范围时使用。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `beta` - 以 β 单位表示的波长位移 (Δλ/β)
|
||||||
|
/// - `fac` - 乘法因子 (H I 为 2.0,He II 为 1.0)
|
||||||
|
/// - `adh` - 辅助参数 A = 1.5*ln(BETAD) - 1.761
|
||||||
|
/// - `betad` - 以 β 单位表示的 Doppler 宽度
|
||||||
|
/// - `divh` - Doppler 核心与 Stark 翼的分界点 (以 betad 为单位)
|
||||||
|
///
|
||||||
|
/// # 返回
|
||||||
|
///
|
||||||
|
/// Stark 轮廓值 S(β)
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// FUNCTION STARKA(BETA,FAC)
|
||||||
|
/// ...
|
||||||
|
/// BETAD1=UN/BETAD
|
||||||
|
/// IF(ADH.GT.AL) THEN
|
||||||
|
/// XD=BETA*BETAD1
|
||||||
|
/// IF(XD.LE.DIVH) THEN
|
||||||
|
/// STARKA=EXP(-XD*XD)*BETAD1*PISQ1
|
||||||
|
/// ELSE
|
||||||
|
/// STARKA=THRA*FAC*EXP(SLO*LOG(BETA))
|
||||||
|
/// END IF
|
||||||
|
/// ELSE
|
||||||
|
/// IF(BETA.LE.BL1) THEN
|
||||||
|
/// STARKA=SAC
|
||||||
|
/// ELSE IF(BETA.LT.BL2) THEN
|
||||||
|
/// XL=LOG(BETA)
|
||||||
|
/// FL=(F0*XL+F1)*XL
|
||||||
|
/// STARKA=F2*EXP(FL)
|
||||||
|
/// ELSE
|
||||||
|
/// STARKA=THRA*FAC*EXP(SLO*LOG(BETA))
|
||||||
|
/// END IF
|
||||||
|
/// END IF
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
pub fn starka(beta: f64, fac: f64, adh: f64, betad: f64, divh: f64) -> f64 {
|
||||||
|
let betad1 = 1.0 / betad;
|
||||||
|
|
||||||
|
if adh > AL {
|
||||||
|
// a > 1: Doppler 核心 + 渐近 Holtsmark 翼
|
||||||
|
let xd = beta * betad1;
|
||||||
|
if xd <= divh {
|
||||||
|
// Doppler 核心
|
||||||
|
(-xd * xd).exp() * betad1 * PISQ1
|
||||||
|
} else {
|
||||||
|
// 渐近 Holtsmark 翼
|
||||||
|
THRA * fac * beta.powf(SLO)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// a < 1: 经验公式
|
||||||
|
if beta <= BL1 {
|
||||||
|
SAC
|
||||||
|
} else if beta < BL2 {
|
||||||
|
let xl = beta.ln();
|
||||||
|
let fl = (F0 * xl + F1) * xl;
|
||||||
|
F2 * fl.exp()
|
||||||
|
} else {
|
||||||
|
THRA * fac * beta.powf(SLO)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_starka_doppler_core() {
|
||||||
|
// a > 1, xd <= divh: Doppler 核心
|
||||||
|
let adh = 2.0; // > AL = 1.26
|
||||||
|
let betad = 10.0;
|
||||||
|
let divh = 5.0;
|
||||||
|
let beta = 30.0; // xd = 30/10 = 3 <= 5
|
||||||
|
|
||||||
|
let result = starka(beta, 2.0, adh, betad, divh);
|
||||||
|
|
||||||
|
// 应该是 Doppler 核心值
|
||||||
|
let xd = beta / betad;
|
||||||
|
let expected = (-xd * xd).exp() / betad * PISQ1;
|
||||||
|
assert!((result - expected).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_starka_holtsmark_wing() {
|
||||||
|
// a > 1, xd > divh: Holtsmark 翼
|
||||||
|
let adh = 2.0;
|
||||||
|
let betad = 10.0;
|
||||||
|
let divh = 5.0;
|
||||||
|
let beta = 100.0; // xd = 100/10 = 10 > 5
|
||||||
|
|
||||||
|
let result = starka(beta, 2.0, adh, betad, divh);
|
||||||
|
|
||||||
|
// 应该是 Holtsmark 翼值
|
||||||
|
let expected = THRA * 2.0 * beta.powf(SLO);
|
||||||
|
assert!((result - expected).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_starka_empirical_low_beta() {
|
||||||
|
// a <= 1, beta <= BL1
|
||||||
|
let adh = 0.5; // < AL
|
||||||
|
let result = starka(0.5, 2.0, adh, 10.0, 5.0);
|
||||||
|
assert!((result - SAC).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_starka_empirical_mid_beta() {
|
||||||
|
// a <= 1, BL1 < beta < BL2
|
||||||
|
let adh = 0.5;
|
||||||
|
let beta = 5.0; // 1.14 < 5.0 < 11.4
|
||||||
|
let result = starka(beta, 2.0, adh, 10.0, 5.0);
|
||||||
|
|
||||||
|
let xl = beta.ln();
|
||||||
|
let fl = (F0 * xl + F1) * xl;
|
||||||
|
let expected = F2 * fl.exp();
|
||||||
|
assert!((result - expected).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_starka_empirical_high_beta() {
|
||||||
|
// a <= 1, beta >= BL2
|
||||||
|
let adh = 0.5;
|
||||||
|
let beta = 20.0; // > 11.4
|
||||||
|
let result = starka(beta, 2.0, adh, 10.0, 5.0);
|
||||||
|
|
||||||
|
let expected = THRA * 2.0 * beta.powf(SLO);
|
||||||
|
assert!((result - expected).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_starka_factor_difference() {
|
||||||
|
// 测试不同 fac 值的影响
|
||||||
|
let adh = 2.0;
|
||||||
|
let betad = 10.0;
|
||||||
|
let divh = 5.0;
|
||||||
|
let beta = 100.0;
|
||||||
|
|
||||||
|
let result_hi = starka(beta, 2.0, adh, betad, divh);
|
||||||
|
let result_he = starka(beta, 1.0, adh, betad, divh);
|
||||||
|
|
||||||
|
// fac 越大,结果越大
|
||||||
|
assert!(result_hi > result_he);
|
||||||
|
assert!((result_hi / result_he - 2.0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
179
src/math/tdpini.rs
Normal file
179
src/math/tdpini.rs
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
//! 温度依赖量初始化。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `tdpini.f`
|
||||||
|
|
||||||
|
use crate::state::config::BasNum;
|
||||||
|
use crate::state::constants::{HALF, H, HK, MDEPTH, UN};
|
||||||
|
use crate::state::model::{CurOpa, GffPar, ModPar};
|
||||||
|
|
||||||
|
use super::gfree0;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// TDPINI - 温度依赖量初始化
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 初始化仅依赖温度的量。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `basnum` - 基本数值
|
||||||
|
/// - `modpar` - 模型参数 (会被修改)
|
||||||
|
/// - `gffpar` - Gaunt 因子参数 (会被修改)
|
||||||
|
/// - `curopa` - 当前不透明度 (会被修改)
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// SUBROUTINE TDPINI
|
||||||
|
/// DO ID=1,ND
|
||||||
|
/// T=TEMP(ID)
|
||||||
|
/// T1=UN/T
|
||||||
|
/// HKT1(ID)=HK*T1
|
||||||
|
/// HKT21(ID)=HKT1(ID)*T1
|
||||||
|
/// TK1(ID)=HKT1(ID)/H
|
||||||
|
/// SQT1(ID)=SQRT(T)
|
||||||
|
/// TEMP1(ID)=T1
|
||||||
|
/// CALL GFREE0(ID)
|
||||||
|
/// EMEL1(ID)=UN
|
||||||
|
/// END DO
|
||||||
|
/// DO ID=1,ND-1
|
||||||
|
/// DELDM(ID)=HALF*(DM(ID+1)-DM(ID))
|
||||||
|
/// deldmz(id)=deldm(id)
|
||||||
|
/// if(izscal.eq.1) deldmz(id)=half*(zd(id)-zd(id+1))
|
||||||
|
/// END DO
|
||||||
|
/// DEDM1=DM(1)/DENS(1)
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
pub fn tdpini(
|
||||||
|
basnum: &BasNum,
|
||||||
|
modpar: &mut ModPar,
|
||||||
|
gffpar: &mut GffPar,
|
||||||
|
curopa: &mut CurOpa,
|
||||||
|
) {
|
||||||
|
let nd = basnum.nd as usize;
|
||||||
|
let izscal = basnum.izscal;
|
||||||
|
|
||||||
|
// 温度依赖量
|
||||||
|
for id in 0..nd {
|
||||||
|
let t = modpar.temp[id];
|
||||||
|
let t1 = UN / t;
|
||||||
|
|
||||||
|
modpar.hkt1[id] = HK * t1;
|
||||||
|
modpar.hkt21[id] = modpar.hkt1[id] * t1;
|
||||||
|
modpar.tk1[id] = modpar.hkt1[id] / H;
|
||||||
|
modpar.sqt1[id] = t.sqrt();
|
||||||
|
modpar.temp1[id] = t1;
|
||||||
|
|
||||||
|
// Gaunt 因子初始化
|
||||||
|
gfree0(id, &modpar.temp, gffpar);
|
||||||
|
|
||||||
|
// 电子发射系数初始化
|
||||||
|
curopa.emel1[id] = UN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 深度差分 (用于光学深度评估)
|
||||||
|
for id in 0..(nd - 1) {
|
||||||
|
modpar.deldm[id] = HALF * (modpar.dm[id + 1] - modpar.dm[id]);
|
||||||
|
modpar.deldmz[id] = modpar.deldm[id];
|
||||||
|
|
||||||
|
// 如果使用几何深度缩放
|
||||||
|
if izscal == 1 {
|
||||||
|
modpar.deldmz[id] = HALF * (modpar.zd[id] - modpar.zd[id + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第一个深度的密度/质量比
|
||||||
|
modpar.dedm1 = modpar.dm[0] / modpar.dens[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn create_test_data() -> (BasNum, ModPar, GffPar, CurOpa) {
|
||||||
|
let mut basnum = BasNum::default();
|
||||||
|
basnum.nd = 5;
|
||||||
|
basnum.izscal = 0;
|
||||||
|
|
||||||
|
let mut modpar = ModPar::default();
|
||||||
|
// 设置温度和密度
|
||||||
|
for id in 0..5 {
|
||||||
|
modpar.temp[id] = 10000.0 + id as f64 * 1000.0;
|
||||||
|
modpar.dm[id] = (id + 1) as f64 * 0.1;
|
||||||
|
modpar.dens[id] = 1e-7;
|
||||||
|
}
|
||||||
|
|
||||||
|
let gffpar = GffPar::new();
|
||||||
|
let curopa = CurOpa::default();
|
||||||
|
|
||||||
|
(basnum, modpar, gffpar, curopa)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tdpini_basic() {
|
||||||
|
let (basnum, mut modpar, mut gffpar, mut curopa) = create_test_data();
|
||||||
|
|
||||||
|
tdpini(&basnum, &mut modpar, &mut gffpar, &mut curopa);
|
||||||
|
|
||||||
|
// 检查温度相关量
|
||||||
|
let t0 = 10000.0;
|
||||||
|
let t1 = UN / t0;
|
||||||
|
|
||||||
|
assert!((modpar.hkt1[0] - HK * t1).abs() < 1e-10);
|
||||||
|
assert!((modpar.sqt1[0] - t0.sqrt()).abs() < 1e-10);
|
||||||
|
assert!((modpar.temp1[0] - t1).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tdpini_delta_m() {
|
||||||
|
let (basnum, mut modpar, mut gffpar, mut curopa) = create_test_data();
|
||||||
|
|
||||||
|
tdpini(&basnum, &mut modpar, &mut gffpar, &mut curopa);
|
||||||
|
|
||||||
|
// 检查深度差分
|
||||||
|
// DELDM[0] = 0.5 * (DM[1] - DM[0]) = 0.5 * (0.2 - 0.1) = 0.05
|
||||||
|
assert!((modpar.deldm[0] - 0.05).abs() < 1e-10);
|
||||||
|
assert!((modpar.deldmz[0] - 0.05).abs() < 1e-10); // izscal = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tdpini_emel1() {
|
||||||
|
let (basnum, mut modpar, mut gffpar, mut curopa) = create_test_data();
|
||||||
|
|
||||||
|
tdpini(&basnum, &mut modpar, &mut gffpar, &mut curopa);
|
||||||
|
|
||||||
|
// 检查 EMEL1 被初始化为 1
|
||||||
|
for id in 0..basnum.nd as usize {
|
||||||
|
assert!((curopa.emel1[id] - UN).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tdpini_dedm1() {
|
||||||
|
let (basnum, mut modpar, mut gffpar, mut curopa) = create_test_data();
|
||||||
|
|
||||||
|
tdpini(&basnum, &mut modpar, &mut gffpar, &mut curopa);
|
||||||
|
|
||||||
|
// DEDM1 = DM[0] / DENS[0] = 0.1 / 1e-7 = 1e6
|
||||||
|
let expected = 0.1 / 1e-7;
|
||||||
|
assert!((modpar.dedm1 - expected).abs() < 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tdpini_with_zscal() {
|
||||||
|
let (mut basnum, mut modpar, mut gffpar, mut curopa) = create_test_data();
|
||||||
|
basnum.izscal = 1;
|
||||||
|
|
||||||
|
// 设置几何深度
|
||||||
|
for id in 0..5 {
|
||||||
|
modpar.zd[id] = (5 - id) as f64 * 1e5; // 从表面向内递减
|
||||||
|
}
|
||||||
|
|
||||||
|
tdpini(&basnum, &mut modpar, &mut gffpar, &mut curopa);
|
||||||
|
|
||||||
|
// 检查使用几何深度计算的 DELDMZ
|
||||||
|
// DELDMZ[0] = 0.5 * (ZD[0] - ZD[1]) = 0.5 * (5e5 - 4e5) = 5e4
|
||||||
|
let expected_z = 0.5 * (5e5 - 4e5);
|
||||||
|
assert!((modpar.deldmz[0] - expected_z).abs() < 1e-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
237
src/math/traini.rs
Normal file
237
src/math/traini.rs
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
//! 不透明度计算的深度无关量初始化。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `traini.f`
|
||||||
|
|
||||||
|
use crate::state::atomic::{IonPar, LevPar, TraPar};
|
||||||
|
use crate::state::config::BasNum;
|
||||||
|
use crate::state::constants::MFREQ;
|
||||||
|
use crate::state::model::{CompIf, DwnPar, LinFrq, LinOvr, ObfPar};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// TRAINI - 不透明度初始化
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 初始化不透明度计算的深度无关量。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `basnum` - 基本数值计数器
|
||||||
|
/// - `ionpar` - 离子参数
|
||||||
|
/// - `levpar` - 能级参数
|
||||||
|
/// - `trapar` - 跃迁参数 (会被修改)
|
||||||
|
/// - `obfpar` - 束缚-自由参数
|
||||||
|
/// - `dwnpar` - 溶解分数参数 (会被修改)
|
||||||
|
/// - `linovr` - 谱线叠加参数 (会被修改)
|
||||||
|
/// - `linfrq` - 谱线频率参数 (会被修改)
|
||||||
|
/// - `compif` - 计算标志
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// SUBROUTINE TRAINI
|
||||||
|
/// INCLUDE 'IMPLIC.FOR'
|
||||||
|
/// INCLUDE 'BASICS.FOR'
|
||||||
|
/// INCLUDE 'ATOMIC.FOR'
|
||||||
|
/// INCLUDE 'MODELQ.FOR'
|
||||||
|
/// INCLUDE 'ODFPAR.FOR'
|
||||||
|
///
|
||||||
|
/// do itr=1,ntrans
|
||||||
|
/// idiel(itr)=0
|
||||||
|
/// end do
|
||||||
|
///
|
||||||
|
/// NCDW=0
|
||||||
|
/// DO 10 IBFT=1,NTRANC
|
||||||
|
/// ITR=ITRBF(IBFT)
|
||||||
|
/// ii=ilow(itr)
|
||||||
|
/// if(ilk(iup(itr)).ne.0.and.nfirst(iel(ii)).eq.ii.
|
||||||
|
/// * and.IFDIEL.NE.0) idiel(itr)=1
|
||||||
|
/// MODW=IABS(INDEXP(ITR))
|
||||||
|
/// IF(MODW.NE.5.AND.MODW.NE.15) GO TO 10
|
||||||
|
/// NCDW=NCDW+1
|
||||||
|
/// MCDW(ITR)=NCDW
|
||||||
|
/// ITRCDW(NCDW)=ITR
|
||||||
|
/// 10 CONTINUE
|
||||||
|
/// IF(ISPODF.GE.1) RETURN
|
||||||
|
///
|
||||||
|
/// DO IJ=1,NFREQ
|
||||||
|
/// NLINES(IJ)=0
|
||||||
|
/// END DO
|
||||||
|
///
|
||||||
|
/// DO 100 ITR=1,NTRANS
|
||||||
|
/// IF(LINEXP(ITR)) GO TO 100
|
||||||
|
/// DO IJ=IFR0(ITR),IFR1(ITR)
|
||||||
|
/// IJLIN(IJ)=ITR
|
||||||
|
/// END DO
|
||||||
|
/// 100 CONTINUE
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
pub fn traini(
|
||||||
|
basnum: &BasNum,
|
||||||
|
ionpar: &IonPar,
|
||||||
|
levpar: &LevPar,
|
||||||
|
trapar: &mut TraPar,
|
||||||
|
obfpar: &ObfPar,
|
||||||
|
dwnpar: &mut DwnPar,
|
||||||
|
linovr: &mut LinOvr,
|
||||||
|
linfrq: &mut LinFrq,
|
||||||
|
compif: &CompIf,
|
||||||
|
) {
|
||||||
|
let ntrans = basnum.ntrans as usize;
|
||||||
|
let ntranc = basnum.ntranc as usize;
|
||||||
|
let nfreq = basnum.nfreq as usize;
|
||||||
|
let ispodf = basnum.ispodf;
|
||||||
|
let ifdiel = basnum.ifdiel;
|
||||||
|
|
||||||
|
// 初始化 idiel
|
||||||
|
for itr in 0..ntrans {
|
||||||
|
trapar.idiel[itr] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 束缚-自由跃迁处理
|
||||||
|
let mut ncdw: i32 = 0;
|
||||||
|
|
||||||
|
for ibft in 0..ntranc {
|
||||||
|
let itr = (obfpar.itrbf[ibft] - 1) as usize; // Fortran 1-indexed -> Rust 0-indexed
|
||||||
|
let ii = (trapar.ilow[itr] - 1) as usize;
|
||||||
|
let iup_idx = (trapar.iup[itr] - 1) as usize;
|
||||||
|
|
||||||
|
// 检查是否是电离能级
|
||||||
|
// if(ilk(iup(itr)).ne.0.and.nfirst(iel(ii)).eq.ii.and.IFDIEL.NE.0)
|
||||||
|
let iel_idx = (levpar.iel[ii] - 1) as usize;
|
||||||
|
if levpar.ilk[iup_idx] != 0
|
||||||
|
&& ionpar.nfirst[iel_idx] == (ii + 1) as i32
|
||||||
|
&& ifdiel != 0
|
||||||
|
{
|
||||||
|
trapar.idiel[itr] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查模数
|
||||||
|
let modw = trapar.indexp[itr].abs();
|
||||||
|
if modw != 5 && modw != 15 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ncdw += 1;
|
||||||
|
dwnpar.mcdw[itr] = ncdw;
|
||||||
|
dwnpar.itrcdw[(ncdw - 1) as usize] = (itr + 1) as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
dwnpar.ncdw = ncdw;
|
||||||
|
|
||||||
|
// 如果 ISPODF >= 1,不处理束缚-束缚跃迁
|
||||||
|
if ispodf >= 1 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 束缚-束缚跃迁处理
|
||||||
|
// 初始化 nlines
|
||||||
|
for ij in 0..nfreq {
|
||||||
|
linfrq.nlines[ij] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理跃迁
|
||||||
|
for itr in 0..ntrans {
|
||||||
|
// 如果是经验线,跳过
|
||||||
|
if compif.linexp[itr] {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ifr0 = (trapar.ifr0[itr] - 1) as usize; // Fortran 1-indexed
|
||||||
|
let ifr1 = (trapar.ifr1[itr] - 1) as usize;
|
||||||
|
|
||||||
|
for ij in ifr0..=ifr1.min(MFREQ - 1) {
|
||||||
|
linovr.ijlin[ij] = (itr + 1) as i32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn create_test_data() -> (BasNum, IonPar, LevPar, TraPar, ObfPar, DwnPar, LinOvr, LinFrq, CompIf) {
|
||||||
|
let mut basnum = BasNum::default();
|
||||||
|
basnum.ntrans = 10;
|
||||||
|
basnum.ntranc = 5;
|
||||||
|
basnum.nfreq = 100;
|
||||||
|
basnum.ispodf = 0;
|
||||||
|
basnum.ifdiel = 0;
|
||||||
|
|
||||||
|
let ionpar = IonPar::default();
|
||||||
|
let mut levpar = LevPar::default();
|
||||||
|
|
||||||
|
// iel[ii] 表示能级 ii 属于哪个离子 (1-indexed)
|
||||||
|
// 在实际运行中,iel 从原子数据文件读取
|
||||||
|
// 这里模拟有效数据:所有能级属于第一个离子
|
||||||
|
for i in 0..10 {
|
||||||
|
levpar.iel[i] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut trapar = TraPar::default();
|
||||||
|
let mut obfpar = ObfPar::new(); // 使用 new() 而不是 default()
|
||||||
|
|
||||||
|
// 设置一些跃迁
|
||||||
|
for i in 0..5 {
|
||||||
|
obfpar.itrbf[i] = (i + 1) as i32;
|
||||||
|
trapar.ilow[i] = (i + 1) as i32;
|
||||||
|
trapar.iup[i] = (i + 2) as i32;
|
||||||
|
trapar.indexp[i] = 5; // 模数 5
|
||||||
|
trapar.ifr0[i] = 1;
|
||||||
|
trapar.ifr1[i] = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dwnpar = DwnPar::default();
|
||||||
|
let linovr = LinOvr::default();
|
||||||
|
let linfrq = LinFrq::default();
|
||||||
|
let compif = CompIf::default();
|
||||||
|
|
||||||
|
(basnum, ionpar, levpar, trapar, obfpar, dwnpar, linovr, linfrq, compif)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_traini_basic() {
|
||||||
|
let (basnum, ionpar, levpar, mut trapar, obfpar, mut dwnpar, mut linovr, mut linfrq, compif) =
|
||||||
|
create_test_data();
|
||||||
|
|
||||||
|
traini(
|
||||||
|
&basnum, &ionpar, &levpar, &mut trapar, &obfpar, &mut dwnpar, &mut linovr, &mut linfrq,
|
||||||
|
&compif,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 检查 idiel 被初始化
|
||||||
|
for itr in 0..basnum.ntrans as usize {
|
||||||
|
assert_eq!(trapar.idiel[itr], 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_traini_ncdw() {
|
||||||
|
let (basnum, ionpar, levpar, mut trapar, obfpar, mut dwnpar, mut linovr, mut linfrq, compif) =
|
||||||
|
create_test_data();
|
||||||
|
|
||||||
|
traini(
|
||||||
|
&basnum, &ionpar, &levpar, &mut trapar, &obfpar, &mut dwnpar, &mut linovr, &mut linfrq,
|
||||||
|
&compif,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 由于所有跃迁都有 indexp=5,ncdw 应该 > 0
|
||||||
|
assert!(dwnpar.ncdw > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_traini_ispoft() {
|
||||||
|
let (mut basnum, ionpar, levpar, mut trapar, obfpar, mut dwnpar, mut linovr, mut linfrq, compif) =
|
||||||
|
create_test_data();
|
||||||
|
basnum.ispodf = 1; // 跳过束缚-束缚跃迁
|
||||||
|
|
||||||
|
traini(
|
||||||
|
&basnum, &ionpar, &levpar, &mut trapar, &obfpar, &mut dwnpar, &mut linovr, &mut linfrq,
|
||||||
|
&compif,
|
||||||
|
);
|
||||||
|
|
||||||
|
// linfrq.nlines 应该保持为 0
|
||||||
|
for ij in 0..basnum.nfreq as usize {
|
||||||
|
assert_eq!(linfrq.nlines[ij], 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
211
src/math/vern16.rs
Normal file
211
src/math/vern16.rs
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
//! 硫离子光电离截面 (Verner 1996)。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `vern16.f`
|
||||||
|
//!
|
||||||
|
//! 参考:
|
||||||
|
//! - Verner D.A. et al. 1996, ApJ 465
|
||||||
|
//! - Verner & Yakovlev 1995, A&AS 109, 125
|
||||||
|
|
||||||
|
use crate::state::constants::{HALF, UN};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// VERN16 - 硫离子光电离截面
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
const T18: f64 = 1e-18;
|
||||||
|
const MVER: usize = 16;
|
||||||
|
|
||||||
|
// 1996 参数
|
||||||
|
static S0: [f64; MVER] = [
|
||||||
|
4.564e4, 3.136e2, 6.666, 2.606, 5.072e-4, 9.139, 5.703e-1,
|
||||||
|
3.161e1, 9.646e3, 5.364e1, 1.275e1, 3.49e-1, 2.294e4, 2.555e1,
|
||||||
|
2.453e1, 2.139e2
|
||||||
|
];
|
||||||
|
|
||||||
|
static E0: [f64; MVER] = [
|
||||||
|
18.08, 8.787, 2.027, 2.173, 0.1713, 14.13, 0.3757, 14.62, 0.1526,
|
||||||
|
10.4, 6.485, 2.443, 14.74, 33.1, 439., 110.4
|
||||||
|
];
|
||||||
|
|
||||||
|
static EMX: [f64; MVER] = [
|
||||||
|
170., 184.6, 199.5, 216.4, 235., 255.7, 2569., 2641., 2705.,
|
||||||
|
2782., 2859., 2941., 3029., 3107., 5e4, 5e4
|
||||||
|
];
|
||||||
|
|
||||||
|
static Y0: [f64; MVER] = [
|
||||||
|
0.9935, 2.782, 15.68, 19.75, 94.24, 0., 222.2, 18.69, 1.615e-3,
|
||||||
|
17.75, 34.26, 227.9, 2.203e-2, 0., 0., 0.
|
||||||
|
];
|
||||||
|
|
||||||
|
static Y1: [f64; MVER] = [
|
||||||
|
0.2486, 0.1788, 9.421, 3.361, 0.6265, 0., 4.606, 0.3037, 0.4049,
|
||||||
|
1.663, 0.137, 1.172, 1.073e-2, 0., 0., 0.
|
||||||
|
];
|
||||||
|
|
||||||
|
static YW: [f64; MVER] = [
|
||||||
|
0.6385, 0.7354, 4.109, 1.863, 0.788, 0., 1.503, 1.153e-3, 1.492,
|
||||||
|
2.31, 1.678, 0.7033, 27.38, 0., 0., 0.
|
||||||
|
];
|
||||||
|
|
||||||
|
static YA: [f64; MVER] = [
|
||||||
|
1., 3.442, 54.54, 66.41, 198.6, 1656., 146., 16.11, 1438., 36.41,
|
||||||
|
65.83, 541.1, 1.529, 38.21, 44.05, 32.88
|
||||||
|
];
|
||||||
|
|
||||||
|
static PV: [f64; MVER] = [
|
||||||
|
13.61, 12.81, 8.611, 8.655, 13.07, 3.626, 11.35, 8.642, 5.977,
|
||||||
|
7.09, 7.692, 7.769, 25.68, 5.037, 1.765, 2.963
|
||||||
|
];
|
||||||
|
|
||||||
|
// 1995 参数 (高能)
|
||||||
|
static S95: [f64; MVER] = [
|
||||||
|
1.883e2, 1.896e2, 1.780e2, 2.037e2, 2.919e2, 4.712e2, 1.916e1,
|
||||||
|
1.931e1, 1.946e1, 2.041e1, 2.101e1, 2.087e1, 2.233e1, 2.293e1,
|
||||||
|
2.453e1, 2.139e2
|
||||||
|
];
|
||||||
|
|
||||||
|
static E95: [f64; MVER] = [
|
||||||
|
91.52, 90.58, 92.46, 87.44, 74.11, 57.47, 495.2, 489.1, 493.7,
|
||||||
|
480.2, 475.8, 482.8, 466.9, 466.7, 439., 110.4
|
||||||
|
];
|
||||||
|
|
||||||
|
static Y95: [f64; MVER] = [
|
||||||
|
71.93, 75.38, 149.8, 93.1, 48.64, 36.1, 35.55, 50., 35.68, 50.,
|
||||||
|
50., 37.42, 50., 44.59, 44.05, 32.88
|
||||||
|
];
|
||||||
|
|
||||||
|
static YW95: [f64; MVER] = [
|
||||||
|
0.2485, 0.2934, 0.02142, 9.497e-3, 0.02785, 0.0248,
|
||||||
|
0., 0., 0., 0., 0., 0., 0., 0., 0., 0.
|
||||||
|
];
|
||||||
|
|
||||||
|
static P95: [f64; MVER] = [
|
||||||
|
3.633, 3.635, 3.319, 3.565, 4.142, 4.742, 1.742, 1.65, 1.737,
|
||||||
|
1.65, 1.65, 1.72, 1.65, 1.668, 1.765, 2.963
|
||||||
|
];
|
||||||
|
|
||||||
|
/// 计算硫离子基态光电离截面。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `e` - 光子能量 (Rydberg)
|
||||||
|
/// - `izz` - 离子电荷 (1-16, 对应 S I - S XVI)
|
||||||
|
///
|
||||||
|
/// # 返回
|
||||||
|
///
|
||||||
|
/// 光电离截面 (cm²)
|
||||||
|
///
|
||||||
|
/// # Fortran 原始代码
|
||||||
|
///
|
||||||
|
/// ```fortran
|
||||||
|
/// FUNCTION VERN16(E,IZZ)
|
||||||
|
/// ...
|
||||||
|
/// IVER=IZZ
|
||||||
|
/// IF(E.LT.EMX(IVER)) THEN
|
||||||
|
/// ! 1996 Expression
|
||||||
|
/// XX=E/E0(IVER)-Y0(IVER)
|
||||||
|
/// YY=SQRT(XX*XX+Y1(IVER)*Y1(IVER))
|
||||||
|
/// AA=(XX-UN)*(XX-UN)+YW(IVER)*YW(IVER)
|
||||||
|
/// BB=YY**(HALF*PV(IVER)-5.5)
|
||||||
|
/// CC=(UN+SQRT(YY/YA(IVER)))**PV(IVER)
|
||||||
|
/// FY=AA*BB/CC
|
||||||
|
/// VERN16=S0(IVER)*T18*FY
|
||||||
|
/// ELSE
|
||||||
|
/// ! 1995 Expression for high energies
|
||||||
|
/// YY=E/E95(IVER)
|
||||||
|
/// XL=0.
|
||||||
|
/// IF(IZZ.LE.6) XL=UN
|
||||||
|
/// Q=HALF*P95(IVER)-5.5-XL
|
||||||
|
/// AA=(YY-UN)*(YY-UN)+YW95(IVER)*YW95(IVER)
|
||||||
|
/// BB=YY**Q
|
||||||
|
/// CC=(UN+SQRT(YY/Y95(IVER)))**P95(IVER)
|
||||||
|
/// FY=AA*BB/CC
|
||||||
|
/// VERN16=S95(IVER)*T18*FY
|
||||||
|
/// END IF
|
||||||
|
/// END
|
||||||
|
/// ```
|
||||||
|
pub fn vern16(e: f64, izz: usize) -> f64 {
|
||||||
|
// Fortran: IZZ = 1..16 → Rust: izz = 0..15
|
||||||
|
if izz == 0 || izz > MVER {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
let iver = izz - 1; // 转换为 0-indexed
|
||||||
|
|
||||||
|
if e < EMX[iver] {
|
||||||
|
// 1996 表达式
|
||||||
|
let xx = e / E0[iver] - Y0[iver];
|
||||||
|
let yy = (xx * xx + Y1[iver] * Y1[iver]).sqrt();
|
||||||
|
let aa = (xx - UN) * (xx - UN) + YW[iver] * YW[iver];
|
||||||
|
let bb = yy.powf(HALF * PV[iver] - 5.5);
|
||||||
|
let cc = (UN + (yy / YA[iver]).sqrt()).powf(PV[iver]);
|
||||||
|
let fy = aa * bb / cc;
|
||||||
|
S0[iver] * T18 * fy
|
||||||
|
} else {
|
||||||
|
// 1995 高能表达式 (内壳层电离)
|
||||||
|
let yy = e / E95[iver];
|
||||||
|
let xl = if izz <= 6 { UN } else { 0.0 };
|
||||||
|
let q = HALF * P95[iver] - 5.5 - xl;
|
||||||
|
let aa = (yy - UN) * (yy - UN) + YW95[iver] * YW95[iver];
|
||||||
|
let bb = yy.powf(q);
|
||||||
|
let cc = (UN + (yy / Y95[iver]).sqrt()).powf(P95[iver]);
|
||||||
|
let fy = aa * bb / cc;
|
||||||
|
S95[iver] * T18 * fy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern16_s_ii() {
|
||||||
|
// S II (izz=2) 在阈值附近
|
||||||
|
let izz = 2;
|
||||||
|
let e = E0[izz - 1]; // 阈值能量
|
||||||
|
let sigma = vern16(e, izz);
|
||||||
|
assert!(sigma > 0.0, "sigma should be positive at threshold");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern16_s_i_low_energy() {
|
||||||
|
// S I (izz=1) 低能区
|
||||||
|
let izz = 1;
|
||||||
|
let e = 10.0; // 低于 EMX[0] = 170
|
||||||
|
let sigma = vern16(e, izz);
|
||||||
|
assert!(sigma > 0.0, "sigma should be positive");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern16_s_i_high_energy() {
|
||||||
|
// S I (izz=1) 高能区
|
||||||
|
let izz = 1;
|
||||||
|
let e = 200.0; // 高于 EMX[0] = 170
|
||||||
|
let sigma = vern16(e, izz);
|
||||||
|
assert!(sigma > 0.0, "sigma should be positive");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern16_zero_energy() {
|
||||||
|
// 零能量
|
||||||
|
let sigma = vern16(0.0, 1);
|
||||||
|
assert!(sigma >= 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern16_invalid_izz() {
|
||||||
|
// 无效的 izz
|
||||||
|
let sigma = vern16(10.0, 0); // 0 无效
|
||||||
|
assert_eq!(sigma, 0.0);
|
||||||
|
let sigma = vern16(10.0, 17); // 17 超出范围
|
||||||
|
assert_eq!(sigma, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern16_decreasing_with_energy() {
|
||||||
|
// 截面应该随能量增加而减小
|
||||||
|
let izz = 1;
|
||||||
|
let sigma_low = vern16(20.0, izz);
|
||||||
|
let sigma_high = vern16(100.0, izz);
|
||||||
|
assert!(sigma_low > sigma_high, "sigma should decrease with energy");
|
||||||
|
}
|
||||||
|
}
|
||||||
168
src/math/vern18.rs
Normal file
168
src/math/vern18.rs
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
//! 氩离子光电离截面 (Verner 1996)。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `vern18.f`
|
||||||
|
//!
|
||||||
|
//! 参考:
|
||||||
|
//! - Verner D.A. et al. 1996, ApJ 465
|
||||||
|
//! - Verner & Yakovlev 1995, A&AS 109, 125
|
||||||
|
|
||||||
|
use crate::state::constants::{HALF, UN};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// VERN18 - 氩离子光电离截面
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
const T18: f64 = 1e-18;
|
||||||
|
const MVER: usize = 18;
|
||||||
|
|
||||||
|
// 1996 参数
|
||||||
|
static S0: [f64; MVER] = [
|
||||||
|
2.106e1, 2.503e1, 3.58e1, 2.035e1, 9.946, 1.080, 3.693,
|
||||||
|
3.295e1, 8.279e-1, 8.204, 1.76e3, 7.018e-1, 2.459e-2,
|
||||||
|
4.997e-2, 2.571e4, 2.135e1, 3.108e1, 1.69e2
|
||||||
|
];
|
||||||
|
|
||||||
|
static E0: [f64; MVER] = [
|
||||||
|
17.09, 24.94, 14.17, 6.953, 10.31, 0.544, 0.02966, 3.844,
|
||||||
|
0.1926, 10.4, 0.1257, 5.31, 0.3209, 1.557, 18.88, 41.54,
|
||||||
|
446.8, 139.9
|
||||||
|
];
|
||||||
|
|
||||||
|
static EMX: [f64; MVER] = [
|
||||||
|
249.2, 266.2, 280.1, 298.7, 320., 342.6, 366.7, 392.5, 3361.,
|
||||||
|
3446., 3523., 3613., 3702., 3798., 3898., 3988., 5e4, 5e4
|
||||||
|
];
|
||||||
|
|
||||||
|
static Y0: [f64; MVER] = [
|
||||||
|
1.688, 0.9299, 2.384, 7.501, 6.406, 1.7e2, 4.383e-4, 0., 38.14,
|
||||||
|
38.04, 3.286e-3, 1.099e2, 2.068e3, 4.552e2, 2.445e-2, 0., 0., 0.
|
||||||
|
];
|
||||||
|
|
||||||
|
static Y1: [f64; MVER] = [
|
||||||
|
0.8943, 0.7195, 1.794, 0.1806, 3.659e-3, 15.87, 2.513, 0., 4.649,
|
||||||
|
0.639, 0.3226, 0.2202, 21.13, 6.459, 1.054e-2, 0., 0., 0.
|
||||||
|
];
|
||||||
|
|
||||||
|
static YW: [f64; MVER] = [
|
||||||
|
0.4185, 0.5108, 0.6316, 0.8842, 0.4885, 11.07, 1.363e-2, 0.,
|
||||||
|
1.434, 9.203e-4, 1.975, 0.4987, 0.6692, 0.2938, 29.09, 0., 0., 0.
|
||||||
|
];
|
||||||
|
|
||||||
|
static YA: [f64; MVER] = [
|
||||||
|
2.645e2, 1.272e2, 3.776e1, 1.4e1, 7.444e1, 9.419e2, 9.951e3,
|
||||||
|
7.082e2, 2.392e2, 1.495e1, 1.579e3, 1.001e2, 2.285e3, 5.031e2,
|
||||||
|
1.475, 4.118e1, 3.039e1, 3.288e1
|
||||||
|
];
|
||||||
|
|
||||||
|
static PV: [f64; MVER] = [
|
||||||
|
4.796, 4.288, 5.742, 9.595, 6.261, 7.582, 7.313, 4.645, 11.21,
|
||||||
|
11.15, 6.714, 8.939, 8.81, 8.966, 26.34, 4.945, 2.092, 2.963
|
||||||
|
];
|
||||||
|
|
||||||
|
// 1995 参数 (高能)
|
||||||
|
static S95: [f64; MVER] = [
|
||||||
|
8.372e1, 1.937e2, 2.281e2, 2.007e2, 2.474e2, 2.786e2, 3.204e2,
|
||||||
|
4.198e2, 2.931e1, 1.585e1, 2.796e1, 1.666e1, 2.888e1, 2.874e1,
|
||||||
|
2.883e1, 3.003e1, 3.108e1, 1.69e2
|
||||||
|
];
|
||||||
|
|
||||||
|
static E95: [f64; MVER] = [
|
||||||
|
164.7, 108.5, 102.5, 107.3, 98.35, 92.33, 85.63, 73.68, 467.9,
|
||||||
|
612.6, 478.9, 602.8, 473.1, 474.9, 475.6, 468., 466.8, 139.9
|
||||||
|
];
|
||||||
|
|
||||||
|
static Y95: [f64; MVER] = [
|
||||||
|
54.52, 70., 43.8, 70., 42.84, 42.2, 42.3, 44.19, 17.44, 50.,
|
||||||
|
19.17, 50., 20.42, 22.35, 26.15, 28.54, 30.39, 32.88
|
||||||
|
];
|
||||||
|
|
||||||
|
static YW95: [f64; MVER] = [
|
||||||
|
0.627, 0.1, 7.167e-3, 0.1, 7.283e-3, 7.408e-3, 7.258e-3,
|
||||||
|
7.712e-3, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.
|
||||||
|
];
|
||||||
|
|
||||||
|
static P95: [f64; MVER] = [
|
||||||
|
3.328, 3.7, 4.046, 3.7, 4.125, 4.227, 4.329, 4.492, 2.362,
|
||||||
|
1.65, 2.271, 1.65, 2.234, 2.171, 2.074, 2.037, 2.092, 2.963
|
||||||
|
];
|
||||||
|
|
||||||
|
/// 计算氩离子基态光电离截面。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `e` - 光子能量 (Rydberg)
|
||||||
|
/// - `izz` - 离子电荷 (1-18, 对应 Ar I - Ar XVIII)
|
||||||
|
///
|
||||||
|
/// # 返回
|
||||||
|
///
|
||||||
|
/// 光电离截面 (cm²)
|
||||||
|
pub fn vern18(e: f64, izz: usize) -> f64 {
|
||||||
|
if izz == 0 || izz > MVER {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
let iver = izz - 1; // 转换为 0-indexed
|
||||||
|
|
||||||
|
if e < EMX[iver] {
|
||||||
|
// 1996 表达式
|
||||||
|
let xx = e / E0[iver] - Y0[iver];
|
||||||
|
let yy = (xx * xx + Y1[iver] * Y1[iver]).sqrt();
|
||||||
|
let aa = (xx - UN) * (xx - UN) + YW[iver] * YW[iver];
|
||||||
|
let bb = yy.powf(HALF * PV[iver] - 5.5);
|
||||||
|
let cc = (UN + (yy / YA[iver]).sqrt()).powf(PV[iver]);
|
||||||
|
let fy = aa * bb / cc;
|
||||||
|
S0[iver] * T18 * fy
|
||||||
|
} else {
|
||||||
|
// 1995 高能表达式
|
||||||
|
let yy = e / E95[iver];
|
||||||
|
let xl = if izz <= 8 { UN } else { 0.0 };
|
||||||
|
let q = HALF * P95[iver] - 5.5 - xl;
|
||||||
|
let aa = (yy - UN) * (yy - UN) + YW95[iver] * YW95[iver];
|
||||||
|
let bb = yy.powf(q);
|
||||||
|
let cc = (UN + (yy / Y95[iver]).sqrt()).powf(P95[iver]);
|
||||||
|
let fy = aa * bb / cc;
|
||||||
|
S95[iver] * T18 * fy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern18_ar_ii() {
|
||||||
|
let izz = 2;
|
||||||
|
let e = E0[izz - 1];
|
||||||
|
let sigma = vern18(e, izz);
|
||||||
|
assert!(sigma > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern18_ar_i_low_energy() {
|
||||||
|
let izz = 1;
|
||||||
|
let e = 50.0;
|
||||||
|
let sigma = vern18(e, izz);
|
||||||
|
assert!(sigma > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern18_ar_i_high_energy() {
|
||||||
|
let izz = 1;
|
||||||
|
let e = 300.0; // 高于 EMX[0] = 249.2
|
||||||
|
let sigma = vern18(e, izz);
|
||||||
|
assert!(sigma > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern18_invalid_izz() {
|
||||||
|
assert_eq!(vern18(10.0, 0), 0.0);
|
||||||
|
assert_eq!(vern18(10.0, 19), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern18_decreasing_with_energy() {
|
||||||
|
let izz = 1;
|
||||||
|
let sigma_low = vern18(30.0, izz);
|
||||||
|
let sigma_high = vern18(100.0, izz);
|
||||||
|
assert!(sigma_low > sigma_high);
|
||||||
|
}
|
||||||
|
}
|
||||||
174
src/math/vern20.rs
Normal file
174
src/math/vern20.rs
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
//! 钙离子光电离截面 (Verner 1996)。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `vern20.f`
|
||||||
|
//!
|
||||||
|
//! 参考:
|
||||||
|
//! - Verner D.A. et al. 1996, ApJ 465
|
||||||
|
//! - Verner & Yakovlev 1995, A&AS 109, 125
|
||||||
|
|
||||||
|
use crate::state::constants::{HALF, UN};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// VERN20 - 钙离子光电离截面
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
const T18: f64 = 1e-18;
|
||||||
|
const MVER: usize = 20;
|
||||||
|
|
||||||
|
// 1996 参数
|
||||||
|
static S0: [f64; MVER] = [
|
||||||
|
5.37e5, 1.064e7, 3.815e1, 7.736, 1.523e-1, 7.642e1, 4.76e-1,
|
||||||
|
6.641e-1, 2.076e2, 1.437e1, 9.384e-1, 1.227e1, 1.849e3,
|
||||||
|
1.116, 5.513e1, 1.293, 2.028e4, 1.105e1, 1.936e1, 1.369e2
|
||||||
|
];
|
||||||
|
|
||||||
|
static E0: [f64; MVER] = [
|
||||||
|
12.78, 15.53, 24.36, 4.255, 0.6882, 9.515, 0.808, 1.366, 0.0552,
|
||||||
|
16.05, 0.2288, 23.45, 10.08, 9.98, 130.9, 4.293, 26.18, 94.72,
|
||||||
|
629.7, 172.9
|
||||||
|
];
|
||||||
|
|
||||||
|
static EMX: [f64; MVER] = [
|
||||||
|
34.43, 40.9, 373.1, 394.4, 417.5, 442.3, 468.7, 496.7, 527.,
|
||||||
|
556.9, 4265., 4362., 4453., 4555., 4659., 4767., 4880., 4982.,
|
||||||
|
5e4, 5e4
|
||||||
|
];
|
||||||
|
|
||||||
|
static Y0: [f64; MVER] = [
|
||||||
|
1.012e-3, 2.161e-3, 1.802, 14.67, 121., 4.829, 148.7, 103.9,
|
||||||
|
2.826e-4, 0., 24.78, 24.17, 6.138e-3, 71.04, 1.833e-2, 0.9363,
|
||||||
|
2.402e-2, 0., 0., 0.
|
||||||
|
];
|
||||||
|
|
||||||
|
static Y1: [f64; MVER] = [
|
||||||
|
1.851e-2, 6.706e-2, 1.233, 3.298e-2, 3.876, 5.824, 1.283, 3.329,
|
||||||
|
1.657, 0., 3.1, 0.5469, 69.31, 5.311, 0.9359, 4.589e-2, 9.323e-3,
|
||||||
|
0., 0., 0.
|
||||||
|
];
|
||||||
|
|
||||||
|
static YW: [f64; MVER] = [
|
||||||
|
0.4477, 0.6453, 0.3126, 1.369, 8.277, 2.471, 0.572, 0.2806,
|
||||||
|
1.843e-3, 0., 1.39, 6.842e-4, 241., 3.879, 9.084e-2, 3.461e-5,
|
||||||
|
28.03, 0., 0., 0.
|
||||||
|
];
|
||||||
|
|
||||||
|
static YA: [f64; MVER] = [
|
||||||
|
0.3162, 0.779, 293.1, 13.55, 150.2, 89.73, 368.2, 318.8, 1.79e4,
|
||||||
|
698.9, 254.9, 13.12, 1.792e4, 59.18, 382.8, 16.91, 1.456, 38.18,
|
||||||
|
39.21, 32.88
|
||||||
|
];
|
||||||
|
|
||||||
|
static PV: [f64; MVER] = [
|
||||||
|
12.42, 21.3, 3.944, 12.36, 10.61, 5.141, 8.634, 8.138, 5.893,
|
||||||
|
3.857, 11.03, 9.771, 2.868, 9.005, 2.023, 14.38, 25.6, 4.192,
|
||||||
|
1.862, 2.963
|
||||||
|
];
|
||||||
|
|
||||||
|
// 1995 参数 (高能)
|
||||||
|
static S95: [f64; MVER] = [
|
||||||
|
9.017e1, 7.314e1, 1.945e2, 1.542e2, 1.622e2, 1.855e2, 2.181e2,
|
||||||
|
2.788e2, 1.934e2, 6.616e2, 1.547e1, 1.324e1, 1.57e1, 1.384e1,
|
||||||
|
1.417e1, 1.665e1, 1.486e1, 1.82e1, 1.936e1, 1.369e2
|
||||||
|
];
|
||||||
|
|
||||||
|
static E95: [f64; MVER] = [
|
||||||
|
44.87, 44.98, 126., 141.3, 138.4, 130.3, 120.8, 107., 129.3,
|
||||||
|
65.11, 701., 750.3, 698.9, 739.6, 734.2, 686.2, 723.5, 664.,
|
||||||
|
629.7, 172.9
|
||||||
|
];
|
||||||
|
|
||||||
|
static Y95: [f64; MVER] = [
|
||||||
|
14.65, 18.98, 68.19, 99.06, 88.11, 69.93, 58.16, 47.68, 70.,
|
||||||
|
43.71, 31.97, 50., 32.18, 50., 50., 34.43, 50., 39.79, 39.21,
|
||||||
|
32.88
|
||||||
|
];
|
||||||
|
|
||||||
|
static YW95: [f64; MVER] = [
|
||||||
|
0.2754, 0.2735, 4.791e-4, 1.107e-3, 4.384e-4, 1.4e-5,
|
||||||
|
4.346e-6, 4.591e-6, 0.1, 7.881e-6, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.
|
||||||
|
];
|
||||||
|
|
||||||
|
static P95: [f64; MVER] = [
|
||||||
|
7.498, 7.152, 3.77, 3.446, 3.521, 3.707, 3.907, 4.2, 3.7, 4.937,
|
||||||
|
1.858, 1.65, 1.851, 1.65, 1.65, 1.823, 1.65, 1.777, 1.862,
|
||||||
|
2.963
|
||||||
|
];
|
||||||
|
|
||||||
|
/// 计算钙离子基态光电离截面。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `e` - 光子能量 (Rydberg)
|
||||||
|
/// - `izz` - 离子电荷 (1-20, 对应 Ca I - Ca XX)
|
||||||
|
///
|
||||||
|
/// # 返回
|
||||||
|
///
|
||||||
|
/// 光电离截面 (cm²)
|
||||||
|
pub fn vern20(e: f64, izz: usize) -> f64 {
|
||||||
|
if izz == 0 || izz > MVER {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
let iver = izz - 1;
|
||||||
|
|
||||||
|
if e < EMX[iver] {
|
||||||
|
let xx = e / E0[iver] - Y0[iver];
|
||||||
|
let yy = (xx * xx + Y1[iver] * Y1[iver]).sqrt();
|
||||||
|
let aa = (xx - UN) * (xx - UN) + YW[iver] * YW[iver];
|
||||||
|
let bb = yy.powf(HALF * PV[iver] - 5.5);
|
||||||
|
let cc = (UN + (yy / YA[iver]).sqrt()).powf(PV[iver]);
|
||||||
|
let fy = aa * bb / cc;
|
||||||
|
S0[iver] * T18 * fy
|
||||||
|
} else {
|
||||||
|
let yy = e / E95[iver];
|
||||||
|
let xl = if izz <= 10 { UN } else { 0.0 };
|
||||||
|
let q = HALF * P95[iver] - 5.5 - xl;
|
||||||
|
let aa = (yy - UN) * (yy - UN) + YW95[iver] * YW95[iver];
|
||||||
|
let bb = yy.powf(q);
|
||||||
|
let cc = (UN + (yy / Y95[iver]).sqrt()).powf(P95[iver]);
|
||||||
|
let fy = aa * bb / cc;
|
||||||
|
S95[iver] * T18 * fy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern20_ca_ii() {
|
||||||
|
let izz = 2;
|
||||||
|
let e = E0[izz - 1];
|
||||||
|
let sigma = vern20(e, izz);
|
||||||
|
assert!(sigma > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern20_ca_i_low_energy() {
|
||||||
|
let izz = 1;
|
||||||
|
let e = 10.0;
|
||||||
|
let sigma = vern20(e, izz);
|
||||||
|
assert!(sigma > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern20_ca_i_high_energy() {
|
||||||
|
let izz = 1;
|
||||||
|
let e = 50.0; // 高于 EMX[0] = 34.43
|
||||||
|
let sigma = vern20(e, izz);
|
||||||
|
assert!(sigma > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern20_invalid_izz() {
|
||||||
|
assert_eq!(vern20(10.0, 0), 0.0);
|
||||||
|
assert_eq!(vern20(10.0, 21), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern20_decreasing_with_energy() {
|
||||||
|
let izz = 3;
|
||||||
|
let sigma_low = vern20(50.0, izz);
|
||||||
|
let sigma_high = vern20(200.0, izz);
|
||||||
|
assert!(sigma_low > sigma_high);
|
||||||
|
}
|
||||||
|
}
|
||||||
183
src/math/vern26.rs
Normal file
183
src/math/vern26.rs
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
//! 铁离子光电离截面 (Verner 1996)。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `vern26.f`
|
||||||
|
//!
|
||||||
|
//! 参考:
|
||||||
|
//! - Verner D.A. et al. 1996, ApJ 465
|
||||||
|
//! - Verner & Yakovlev 1995, A&AS 109, 125
|
||||||
|
|
||||||
|
use crate::state::constants::{HALF, UN};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// VERN26 - 铁离子光电离截面
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
const T18: f64 = 1e-18;
|
||||||
|
const MVER: usize = 26;
|
||||||
|
|
||||||
|
// 1996 参数
|
||||||
|
static S0: [f64; MVER] = [
|
||||||
|
3.062e-1, 4.365e3, 6.107, 3.653e2, 1.523e-3, 5.259e-1, 2.42e4,
|
||||||
|
1.979e1, 2.687e1, 6.470e1, 3.281, 1.738, 2.791e-3, 1.454e-1,
|
||||||
|
2.108e2, 1.207e1, 1.452, 2.388, 6.066e-5, 4.455e-1, 1.098e1,
|
||||||
|
7.204e-2, 2.580e4, 1.276e1, 1.195e1, 8.099e1
|
||||||
|
];
|
||||||
|
|
||||||
|
static E0: [f64; MVER] = [
|
||||||
|
0.05461, 0.1761, 0.1698, 25.44, 0.7256, 2.656, 5.059, 0.07098,
|
||||||
|
6.741, 68.86, 8.284, 6.295, 0.1317, 0.8509, 0.05555, 28.73,
|
||||||
|
0.3444, 31.9, 7.519e-4, 20.11, 9.243, 9.713, 45.75, 73.26,
|
||||||
|
1057., 293.2
|
||||||
|
];
|
||||||
|
|
||||||
|
static EMX: [f64; MVER] = [
|
||||||
|
66., 76.17, 87.05, 106.7, 128.8, 152.7, 178.3, 205.5, 921.1,
|
||||||
|
959., 998.3, 1039., 1081., 1125., 1181., 1216., 7651., 7769.,
|
||||||
|
7918., 8041., 8184., 8350., 8484., 8638., 5e4, 5e4
|
||||||
|
];
|
||||||
|
|
||||||
|
static Y0: [f64; MVER] = [
|
||||||
|
1.382e2, 9.272e1, 1.76e2, 0., 8.871e1, 3.361e1, 0.4546, 2.542e3,
|
||||||
|
2.494e1, 1.19e-5, 2.971e1, 4.671e1, 2.17e3, 4.505e2, 2.706e-4,
|
||||||
|
0., 2.891e1, 3.805e1, 1.915e6, 6.847e1, 4.446e1, 1.702e2,
|
||||||
|
3.582e-2, 0., 0., 0.
|
||||||
|
];
|
||||||
|
|
||||||
|
static Y1: [f64; MVER] = [
|
||||||
|
0.2481, 1.075e2, 1.847e1, 0., 5.28e-2, 3.743e-3, 2.683e1,
|
||||||
|
4.672e2, 8.251, 6.57e-3, 0.522, 0.1425, 6.852e-3, 2.504,
|
||||||
|
1.628, 0., 3.404, 0.4805, 3.14e1, 3.989, 3.512, 4.263, 8.712e-3,
|
||||||
|
0., 0., 0.
|
||||||
|
];
|
||||||
|
|
||||||
|
static YW: [f64; MVER] = [
|
||||||
|
2.069e1, 1.141e1, 8.698, 0.5602, 5.064e1, 1.558e1, 2.516e-3,
|
||||||
|
2.158e2, 2.387e-4, 2.778e-4, 0.3279, 0.3096, 0.6938, 0.4937,
|
||||||
|
1.885e-3, 0., 1.264, 2.902e-2, 4.398, 2.757, 1.748, 9.551e-3,
|
||||||
|
2.723e1, 0., 0., 0.
|
||||||
|
];
|
||||||
|
|
||||||
|
static YA: [f64; MVER] = [
|
||||||
|
2.671e7, 6.298e3, 1.555e3, 8.913, 3.736e1, 1.450e1, 4.850e4,
|
||||||
|
1.745e4, 1.807e2, 2.062e1, 5.360e1, 1.130e2, 2.487e3, 1.239e3,
|
||||||
|
2.045e4, 5.150e2, 3.960e2, 2.186e1, 1.606e6, 4.236e1, 7.637e1,
|
||||||
|
1.853e2, 1.358, 4.914e1, 5.769e1, 3.288e1
|
||||||
|
];
|
||||||
|
|
||||||
|
static PV: [f64; MVER] = [
|
||||||
|
7.923, 5.204, 8.055, 6.538, 17.67, 16.32, 2.374, 6.75, 6.29,
|
||||||
|
4.111, 8.571, 8.037, 9.791, 8.066, 6.033, 3.846, 10.13, 9.589,
|
||||||
|
8.813, 9.724, 7.962, 8.843, 26.04, 4.941, 1.718, 2.963
|
||||||
|
];
|
||||||
|
|
||||||
|
// 1995 参数 (高能)
|
||||||
|
static S95: [f64; MVER] = [
|
||||||
|
6.298e1, 4.624e1, 4.422e1, 4.81e1, 5.143e1, 5.246e1, 5.21e1,
|
||||||
|
5.336e1, 2.205e2, 2.392e2, 2.449e2, 3.325e2, 3.316e2, 3.367e2,
|
||||||
|
1.496e2, 3.383e2, 1.15e1, 8.327, 1.155e1, 8.619, 8.773,
|
||||||
|
1.181e1, 9.098e1, 1.157e1, 1.195e1, 8.099e1
|
||||||
|
];
|
||||||
|
|
||||||
|
static E95: [f64; MVER] = [
|
||||||
|
76.3, 77.5, 77.77, 76.25, 72.73, 72.6, 74.33, 75.56, 171.5,
|
||||||
|
164.7, 163.2, 138.3, 139.2, 138.7, 213.6, 139.6, 1067., 1249.,
|
||||||
|
1068., 1235., 1228., 1066., 1215., 1087., 1057., 293.2
|
||||||
|
];
|
||||||
|
|
||||||
|
static Y95: [f64; MVER] = [
|
||||||
|
1.479e1, 2.155e1, 2.336e1, 2.286e1, 2.428e1, 2.751e1, 3.306e1,
|
||||||
|
3.855e1, 5.298e1, 5.276e1, 5.452e1, 5.09e1, 5.237e1, 5.279e1,
|
||||||
|
7.000e1, 5.459e1, 3.412e1, 5.000e1, 3.578e1, 5.000e1, 5.000e1,
|
||||||
|
4.116e1, 5.000e1, 5.086e1, 5.769e1, 3.288e1
|
||||||
|
];
|
||||||
|
|
||||||
|
static YW95: [f64; MVER] = [
|
||||||
|
0.2646, 0.2599, 0.2557, 0.2449, 0.1365, 0.02105, 0.02404,
|
||||||
|
0.02667, 1.508e-5, 1.574e-5, 1.594e-4, 1.114e-5, 1.107e-5,
|
||||||
|
1.111e-5, 0.1, 1.179e-5, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.
|
||||||
|
];
|
||||||
|
|
||||||
|
static P95: [f64; MVER] = [
|
||||||
|
7.672, 7.138, 7.017, 7.043, 7.028, 6.823, 6.509, 6.265, 4.154,
|
||||||
|
4.204, 4.187, 4.446, 4.41, 4.407, 3.7, 4.366, 1.922, 1.65, 1.895,
|
||||||
|
1.65, 1.65, 1.827, 1.65, 1.722, 1.718, 2.963
|
||||||
|
];
|
||||||
|
|
||||||
|
/// 计算铁离子基态光电离截面。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// - `e` - 光子能量 (Rydberg)
|
||||||
|
/// - `izz` - 离子电荷 (1-26, 对应 Fe I - Fe XXVI)
|
||||||
|
///
|
||||||
|
/// # 返回
|
||||||
|
///
|
||||||
|
/// 光电离截面 (cm²)
|
||||||
|
pub fn vern26(e: f64, izz: usize) -> f64 {
|
||||||
|
if izz == 0 || izz > MVER {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
let iver = izz - 1;
|
||||||
|
|
||||||
|
if e < EMX[iver] {
|
||||||
|
let xx = e / E0[iver] - Y0[iver];
|
||||||
|
let yy = (xx * xx + Y1[iver] * Y1[iver]).sqrt();
|
||||||
|
let aa = (xx - UN) * (xx - UN) + YW[iver] * YW[iver];
|
||||||
|
let bb = yy.powf(HALF * PV[iver] - 5.5);
|
||||||
|
let cc = (UN + (yy / YA[iver]).sqrt()).powf(PV[iver]);
|
||||||
|
let fy = aa * bb / cc;
|
||||||
|
S0[iver] * T18 * fy
|
||||||
|
} else {
|
||||||
|
let yy = e / E95[iver];
|
||||||
|
let xl = if izz <= 16 { UN } else { 0.0 };
|
||||||
|
let q = HALF * P95[iver] - 5.5 - xl;
|
||||||
|
let aa = (yy - UN) * (yy - UN) + YW95[iver] * YW95[iver];
|
||||||
|
let bb = yy.powf(q);
|
||||||
|
let cc = (UN + (yy / Y95[iver]).sqrt()).powf(P95[iver]);
|
||||||
|
let fy = aa * bb / cc;
|
||||||
|
S95[iver] * T18 * fy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern26_fe_ii() {
|
||||||
|
let izz = 2;
|
||||||
|
let e = E0[izz - 1];
|
||||||
|
let sigma = vern26(e, izz);
|
||||||
|
assert!(sigma > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern26_fe_i_low_energy() {
|
||||||
|
let izz = 1;
|
||||||
|
let e = 0.03;
|
||||||
|
let sigma = vern26(e, izz);
|
||||||
|
assert!(sigma > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern26_fe_i_high_energy() {
|
||||||
|
let izz = 1;
|
||||||
|
let e = 100.0; // 高于 EMX[0] = 66
|
||||||
|
let sigma = vern26(e, izz);
|
||||||
|
assert!(sigma > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern26_invalid_izz() {
|
||||||
|
assert_eq!(vern26(10.0, 0), 0.0);
|
||||||
|
assert_eq!(vern26(10.0, 27), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vern26_decreasing_with_energy() {
|
||||||
|
let izz = 3;
|
||||||
|
let sigma_low = vern26(20.0, izz);
|
||||||
|
let sigma_high = vern26(60.0, izz);
|
||||||
|
assert!(sigma_low > sigma_high);
|
||||||
|
}
|
||||||
|
}
|
||||||
208
src/math/wnstor.rs
Normal file
208
src/math/wnstor.rs
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
//! 存储氢能级占据概率。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `wnstor.f`
|
||||||
|
//!
|
||||||
|
//! 将氢能级的占据概率存储到 WNCOM 公共块中,以便后续使用。
|
||||||
|
|
||||||
|
use crate::math::wn::wn;
|
||||||
|
use crate::state::constants::{MLEVEL, NLMX, UN};
|
||||||
|
|
||||||
|
/// 存储氢能级占据概率。
|
||||||
|
///
|
||||||
|
/// 计算氢能级的占据概率,并将结果存储到相应数组中。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `id` - 深度索引 (0-based)
|
||||||
|
/// * `temp` - 温度数组
|
||||||
|
/// * `elec` - 电子密度数组
|
||||||
|
/// * `xi2` - XI2 系数数组
|
||||||
|
/// * `wnhint` - 输出:氢能级占据概率积分 [NLMX][深度]
|
||||||
|
/// * `wop` - 输出:能级权重 [MLEVEL][深度]
|
||||||
|
/// * `ifwop` - 能级权重标志
|
||||||
|
/// * `nlevel` - 能级数
|
||||||
|
/// * `nquant` - 主量子数数组
|
||||||
|
/// * `iz` - 原子序数数组
|
||||||
|
/// * `io_ptab` - 占据概率表标志
|
||||||
|
/// * `lte` - LTE 标志
|
||||||
|
///
|
||||||
|
/// # 示例
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use tlusty_rust::math::wnstor::wnstor;
|
||||||
|
///
|
||||||
|
/// let temp = vec![10000.0; 100];
|
||||||
|
/// let elec = vec![1e12; 100];
|
||||||
|
/// let xi2 = vec![1.0; 30];
|
||||||
|
/// let mut wnhint = vec![vec![0.0; 100]; 30];
|
||||||
|
/// let mut wop = vec![vec![0.0; 100]; 1134];
|
||||||
|
/// let ifwop = vec![1; 1134];
|
||||||
|
/// let nquant = vec![1; 1134];
|
||||||
|
/// let iz = vec![1; 1134];
|
||||||
|
///
|
||||||
|
/// wnstor(0, &temp, &elec, &xi2, &mut wnhint, &mut wop,
|
||||||
|
/// &ifwop, 10, &nquant, &iz, 0, false);
|
||||||
|
/// ```
|
||||||
|
pub fn wnstor(
|
||||||
|
id: usize,
|
||||||
|
temp: &[f64],
|
||||||
|
elec: &[f64],
|
||||||
|
xi2: &[f64],
|
||||||
|
wnhint: &mut [Vec<f64>],
|
||||||
|
wop: &mut [Vec<f64>],
|
||||||
|
ifwop: &[i32],
|
||||||
|
nlevel: usize,
|
||||||
|
nquant: &[i32],
|
||||||
|
iz: &[i32],
|
||||||
|
io_ptab: i32,
|
||||||
|
lte: bool,
|
||||||
|
) {
|
||||||
|
// 常数参数
|
||||||
|
const P1: f64 = 0.1402;
|
||||||
|
const P2: f64 = 0.1285;
|
||||||
|
const P3: f64 = 1.0;
|
||||||
|
const P4: f64 = 3.15;
|
||||||
|
const P5: f64 = 4.0;
|
||||||
|
const TKN: f64 = 3.01;
|
||||||
|
const CKN: f64 = 5.33333333;
|
||||||
|
const CB0: f64 = 8.59e14;
|
||||||
|
const F23: f64 = -2.0 / 3.0;
|
||||||
|
|
||||||
|
if io_ptab < 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取深度点数据
|
||||||
|
let ane = elec[id];
|
||||||
|
let t = temp[id];
|
||||||
|
|
||||||
|
// 计算 A 参数
|
||||||
|
let a = CKN * ane * (ane.ln() / 6.0).exp() / t.sqrt();
|
||||||
|
|
||||||
|
let z = UN;
|
||||||
|
let x = (UN + P3 * a).powf(P4);
|
||||||
|
let c1 = P1 * (x + P5 * (z - UN) * a * a * a);
|
||||||
|
let c2 = P2 * x;
|
||||||
|
let beta0 = CB0 * z * z * z * ane.powf(F23);
|
||||||
|
|
||||||
|
// 计算 WNHINT
|
||||||
|
for i in 1..=NLMX {
|
||||||
|
let xn = i as f64;
|
||||||
|
let xkn = if xn <= TKN {
|
||||||
|
UN
|
||||||
|
} else {
|
||||||
|
let xn1 = UN / (xn + UN);
|
||||||
|
CKN * xn * xn1 * xn1
|
||||||
|
};
|
||||||
|
|
||||||
|
// XI2 系数
|
||||||
|
let xi2_val = *xi2.get(i - 1).unwrap_or(&UN);
|
||||||
|
let beta = beta0 * xkn * xi2_val * xi2_val;
|
||||||
|
let f = (c1 * beta * beta * beta) / (UN + c2 * beta * beta.sqrt());
|
||||||
|
wnhint[i - 1][id] = f / (UN + f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算 WOP - 显式能级的占据概率
|
||||||
|
for ii in 0..nlevel {
|
||||||
|
let ifwop_val = ifwop[ii];
|
||||||
|
|
||||||
|
if ifwop_val <= 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let nq = nquant[ii] as usize;
|
||||||
|
let iz_val = iz[ii] as f64;
|
||||||
|
|
||||||
|
if iz_val == 1.0 {
|
||||||
|
// 氢:使用 WNHINT
|
||||||
|
if nq > 0 && nq <= NLMX {
|
||||||
|
wop[ii][id] = wnhint[nq - 1][id];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 其他元素:使用 WN 函数
|
||||||
|
let xn = nq as f64;
|
||||||
|
// bergfc = 0.0 表示使用标准 Hummer-Mihalas 公式
|
||||||
|
wop[ii][id] = wn(xn, a, ane, iz_val, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ifwop_val > 1 && lte {
|
||||||
|
wop[ii][id] = UN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const ND: usize = 10;
|
||||||
|
|
||||||
|
fn create_test_data() -> (
|
||||||
|
Vec<f64>,
|
||||||
|
Vec<f64>,
|
||||||
|
Vec<f64>,
|
||||||
|
Vec<Vec<f64>>,
|
||||||
|
Vec<Vec<f64>>,
|
||||||
|
Vec<i32>,
|
||||||
|
Vec<i32>,
|
||||||
|
Vec<i32>,
|
||||||
|
) {
|
||||||
|
let temp = vec![10000.0; ND];
|
||||||
|
let elec = vec![1e12; ND];
|
||||||
|
let xi2 = vec![1.0; NLMX];
|
||||||
|
let wnhint = vec![vec![0.0; ND]; NLMX];
|
||||||
|
let wop = vec![vec![0.0; ND]; MLEVEL];
|
||||||
|
let ifwop = vec![1; MLEVEL];
|
||||||
|
let nquant = vec![1; MLEVEL];
|
||||||
|
let iz = vec![1; MLEVEL];
|
||||||
|
|
||||||
|
(temp, elec, xi2, wnhint, wop, ifwop, nquant, iz)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_wnstor_io_ptab_negative() {
|
||||||
|
let (temp, elec, xi2, mut wnhint, mut wop, ifwop, nquant, iz) = create_test_data();
|
||||||
|
|
||||||
|
// io_ptab < 0 应该直接返回
|
||||||
|
wnstor(
|
||||||
|
0, &temp, &elec, &xi2, &mut wnhint, &mut wop,
|
||||||
|
&ifwop, 10, &nquant, &iz, -1, false,
|
||||||
|
);
|
||||||
|
|
||||||
|
// wnhint 应该保持为 0
|
||||||
|
assert!((wnhint[0][0] - 0.0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_wnstor_basic() {
|
||||||
|
let (temp, elec, xi2, mut wnhint, mut wop, ifwop, nquant, iz) = create_test_data();
|
||||||
|
|
||||||
|
wnstor(
|
||||||
|
0, &temp, &elec, &xi2, &mut wnhint, &mut wop,
|
||||||
|
&ifwop, 10, &nquant, &iz, 0, false,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 检查 WNHINT 已被计算,值应该在 [0, 1] 范围内
|
||||||
|
for i in 0..NLMX {
|
||||||
|
let val = wnhint[i][0];
|
||||||
|
assert!(val >= 0.0 && val <= 1.0, "WNHINT[{}][0] = {} should be in [0,1]", i, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_wnstor_lte_flag() {
|
||||||
|
let (temp, elec, xi2, mut wnhint, mut wop, ifwop, nquant, iz) = create_test_data();
|
||||||
|
|
||||||
|
wnstor(
|
||||||
|
0, &temp, &elec, &xi2, &mut wnhint, &mut wop,
|
||||||
|
&ifwop, 10, &nquant, &iz, 0, true, // lte = true
|
||||||
|
);
|
||||||
|
|
||||||
|
// 当 ifwop > 1 且 lte = true 时,wop 应该是 1.0
|
||||||
|
for ii in 0..10 {
|
||||||
|
if ifwop[ii] > 1 {
|
||||||
|
assert!((wop[ii][0] - UN).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
217
src/math/zmrho.rs
Normal file
217
src/math/zmrho.rs
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
//! 初始质量-密度-深度估计。
|
||||||
|
//!
|
||||||
|
//! 重构自 TLUSTY `zmrho.f`
|
||||||
|
//!
|
||||||
|
//! 通过流体静力学平衡方程的近似解,估计 DM, DENS, 和 ZD 的初始值。
|
||||||
|
//! 气体压力和辐射压力都有贡献。
|
||||||
|
|
||||||
|
use crate::math::{betah, erfcin, erfcx};
|
||||||
|
use crate::state::constants::{HALF, MDEPTH, UN};
|
||||||
|
|
||||||
|
/// 初始质量-密度-深度估计。
|
||||||
|
///
|
||||||
|
/// 通过流体静力学平衡方程的近似解,计算深度点上的质量、密度和几何深度。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
///
|
||||||
|
/// * `r` - 辐射压力与气体压力标高之比
|
||||||
|
/// * `hg` - 气体压力标高
|
||||||
|
/// * `dm1` - 第一个深度点的质量(负值表示特殊处理)
|
||||||
|
/// * `dmtot` - 最后一个深度点的质量(中心平面)
|
||||||
|
/// * `nd` - 深度点数
|
||||||
|
/// * `dm` - 质量数组 [输出]
|
||||||
|
/// * `dens` - 密度数组 [输出]
|
||||||
|
/// * `zd` - 几何深度数组 [输出]
|
||||||
|
///
|
||||||
|
/// # 返回值
|
||||||
|
///
|
||||||
|
/// 返回更新后的 nd(可能被调整)
|
||||||
|
///
|
||||||
|
/// # 示例
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use tlusty_rust::math::zmrho::zmrho;
|
||||||
|
///
|
||||||
|
/// let mut dm = vec![0.0; 100];
|
||||||
|
/// let mut dens = vec![0.0; 100];
|
||||||
|
/// let mut zd = vec![0.0; 100];
|
||||||
|
///
|
||||||
|
/// let nd = zmrho(0.1, 1e8, 1e-10, 1.0, 50, &mut dm, &mut dens, &mut zd);
|
||||||
|
/// assert!(nd > 0);
|
||||||
|
/// ```
|
||||||
|
pub fn zmrho(
|
||||||
|
r: f64,
|
||||||
|
hg: f64,
|
||||||
|
dm1: f64,
|
||||||
|
dmtot: f64,
|
||||||
|
mut nd: usize,
|
||||||
|
dm: &mut [f64],
|
||||||
|
dens: &mut [f64],
|
||||||
|
zd: &mut [f64],
|
||||||
|
) -> usize {
|
||||||
|
const PISQ: f64 = 1.77245385090551; // sqrt(pi)
|
||||||
|
const PISQ2: f64 = PISQ * HALF;
|
||||||
|
|
||||||
|
// 检查深度点数不超过最大值
|
||||||
|
if nd > MDEPTH {
|
||||||
|
nd = MDEPTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
if dm1 > 0.0 {
|
||||||
|
// 正常情况:对数等间距的质量-深度网格
|
||||||
|
dm[nd - 1] = dmtot;
|
||||||
|
dm[nd - 2] = 0.99 * dmtot;
|
||||||
|
let dml = (dm[nd - 2] / dm1).ln() / (nd - 2) as f64;
|
||||||
|
let dml1 = dm1.ln();
|
||||||
|
for id in 0..nd - 1 {
|
||||||
|
dm[id] = (dml1 + id as f64 * dml).exp();
|
||||||
|
}
|
||||||
|
} else if dm1 < -1e-20 && dm1 > -1e-10 {
|
||||||
|
// 特殊情况 1
|
||||||
|
dm[nd - 1] = dmtot;
|
||||||
|
let dm1_abs = dm1.abs() * 1e20;
|
||||||
|
let dml = (dm[nd - 1] / dm1_abs).ln() / (nd - 1) as f64;
|
||||||
|
let dml1 = dm1_abs.ln();
|
||||||
|
for id in 0..nd - 1 {
|
||||||
|
dm[id] = (dml1 + id as f64 * dml).exp();
|
||||||
|
}
|
||||||
|
} else if dm1 > -1e-10 {
|
||||||
|
// 特殊情况 2:对称网格
|
||||||
|
if nd % 2 == 0 {
|
||||||
|
nd = nd - 1;
|
||||||
|
}
|
||||||
|
let dmha = dmtot * HALF;
|
||||||
|
let dm1_abs = dm1.abs() * 1e10;
|
||||||
|
let ndha = nd / 2;
|
||||||
|
let dml = (dmha / dm1_abs).ln() / ndha as f64;
|
||||||
|
let dml1 = dm1_abs.ln();
|
||||||
|
dm[nd - 1] = dmtot;
|
||||||
|
for id in 0..ndha {
|
||||||
|
dm[id] = (dml1 + id as f64 * dml).exp();
|
||||||
|
dm[nd - 1 - id] = dmtot - (dml1 + (id + 1) as f64 * dml).exp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算总压力标高 - 使用 BETAH 函数
|
||||||
|
let hh = betah(r) * r;
|
||||||
|
let dmh = PISQ / 2.0 * (-r * (hh - r)).exp() * erfcx(hh - r) / hh;
|
||||||
|
let rho0 = dm[nd - 1] / hh / hg;
|
||||||
|
|
||||||
|
// 流体静力学平衡的近似解
|
||||||
|
for id in 0..nd {
|
||||||
|
let dmrel = dm[id] / dm[nd - 1];
|
||||||
|
let (x, rho) = if dmrel <= dmh {
|
||||||
|
let x_val = r + erfcin(dmrel * 2.0 / PISQ * hh * (r * (hh - r)).exp());
|
||||||
|
let rho_val = (-((x_val - r) * (x_val - r)) - (hh - r) * r).exp();
|
||||||
|
(x_val, rho_val)
|
||||||
|
} else if dmrel < UN {
|
||||||
|
let hsq = (hh * (hh - r)).sqrt();
|
||||||
|
let x_val = erfcin(2.0 / PISQ * hsq * (dmrel - dmh) + erfcx(hsq)) * hh / hsq;
|
||||||
|
let rho_val = (-x_val * x_val * (UN - r / hh)).exp();
|
||||||
|
(x_val, rho_val)
|
||||||
|
} else {
|
||||||
|
(0.0, UN)
|
||||||
|
};
|
||||||
|
dens[id] = rho0 * rho;
|
||||||
|
zd[id] = x * hg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 dm1 < -1e-10 的情况
|
||||||
|
if dm1 < -1e-10 {
|
||||||
|
dm[nd - 1] = dmtot;
|
||||||
|
dm[nd - 2] = 0.99 * dmtot;
|
||||||
|
let dm1_abs = dm1.abs();
|
||||||
|
let dml = (dm[nd - 2] / dm1_abs).ln() / (nd - 2) as f64;
|
||||||
|
let dml1 = dm1_abs.ln();
|
||||||
|
for id in 0..nd - 1 {
|
||||||
|
dm[id] = (dml1 + id as f64 * dml).exp();
|
||||||
|
}
|
||||||
|
|
||||||
|
let hr = r * hg;
|
||||||
|
let hg2 = hg * PISQ2;
|
||||||
|
let hrg = hr + hg2;
|
||||||
|
let dmh_local = hg2 / hrg;
|
||||||
|
let rho0_local = dmtot / hrg;
|
||||||
|
|
||||||
|
for id in 0..nd {
|
||||||
|
let dmrel = dm[id] / dm[nd - 1];
|
||||||
|
if dmrel >= dmh_local {
|
||||||
|
zd[id] = hrg * (UN - dmrel);
|
||||||
|
dens[id] = rho0_local;
|
||||||
|
} else {
|
||||||
|
zd[id] = hr + hg * erfcin(dmrel * hrg / hg2);
|
||||||
|
let x = (zd[id] - hr) / hg;
|
||||||
|
dens[id] = rho0_local * (-x * x).exp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nd
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zmrho_normal() {
|
||||||
|
let mut dm = vec![0.0; MDEPTH];
|
||||||
|
let mut dens = vec![0.0; MDEPTH];
|
||||||
|
let mut zd = vec![0.0; MDEPTH];
|
||||||
|
|
||||||
|
let nd = zmrho(0.1, 1e8, 1e-10, 1.0, 50, &mut dm, &mut dens, &mut zd);
|
||||||
|
|
||||||
|
assert_eq!(nd, 50);
|
||||||
|
// 检查质量是递增的
|
||||||
|
for i in 1..nd {
|
||||||
|
assert!(dm[i] > dm[i - 1]);
|
||||||
|
}
|
||||||
|
// 检查密度是正的
|
||||||
|
for i in 0..nd {
|
||||||
|
assert!(dens[i] > 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zmrho_symmetric() {
|
||||||
|
let mut dm = vec![0.0; MDEPTH];
|
||||||
|
let mut dens = vec![0.0; MDEPTH];
|
||||||
|
let mut zd = vec![0.0; MDEPTH];
|
||||||
|
|
||||||
|
// dm1 > -1e-10 触发对称网格
|
||||||
|
let nd = zmrho(0.1, 1e8, -1e-11, 1.0, 51, &mut dm, &mut dens, &mut zd);
|
||||||
|
|
||||||
|
// nd 应该是奇数(如果原来是偶数,会减1)
|
||||||
|
assert_eq!(nd % 2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zmrho_special() {
|
||||||
|
let mut dm = vec![0.0; MDEPTH];
|
||||||
|
let mut dens = vec![0.0; MDEPTH];
|
||||||
|
let mut zd = vec![0.0; MDEPTH];
|
||||||
|
|
||||||
|
// dm1 < -1e-20 && dm1 > -1e-10
|
||||||
|
let nd = zmrho(0.1, 1e8, -1e-15, 1.0, 50, &mut dm, &mut dens, &mut zd);
|
||||||
|
|
||||||
|
assert_eq!(nd, 50);
|
||||||
|
// 检查最后一个质量等于 dmtot
|
||||||
|
assert!((dm[nd - 1] - 1.0).abs() < 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zmrho_negative_dm1() {
|
||||||
|
let mut dm = vec![0.0; MDEPTH];
|
||||||
|
let mut dens = vec![0.0; MDEPTH];
|
||||||
|
let mut zd = vec![0.0; MDEPTH];
|
||||||
|
|
||||||
|
// dm1 < -1e-10
|
||||||
|
let nd = zmrho(0.1, 1e8, -1e-5, 1.0, 50, &mut dm, &mut dens, &mut zd);
|
||||||
|
|
||||||
|
assert_eq!(nd, 50);
|
||||||
|
// 检查密度是正的
|
||||||
|
for i in 0..nd {
|
||||||
|
assert!(dens[i] > 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -113,6 +113,58 @@ pub struct FixAlp {
|
|||||||
pub aapp: Vec<Vec<Vec<f64>>>,
|
pub aapp: Vec<Vec<Vec<f64>>>,
|
||||||
pub capp: Vec<Vec<Vec<f64>>>,
|
pub capp: Vec<Vec<Vec<f64>>>,
|
||||||
|
|
||||||
|
// ALI 算子 (MDEPTH)
|
||||||
|
/// 当前深度 ALI 算子
|
||||||
|
pub ali1: Vec<f64>,
|
||||||
|
/// 前一深度 ALI 算子
|
||||||
|
pub alim1: Vec<f64>,
|
||||||
|
/// 后一深度 ALI 算子
|
||||||
|
pub alip1: Vec<f64>,
|
||||||
|
/// ALI H 算子
|
||||||
|
pub alih1: Vec<f64>,
|
||||||
|
|
||||||
|
// 发射/吸收系数导数 (MDEPTH)
|
||||||
|
/// 发射系数 T 导数
|
||||||
|
pub demt1: Vec<f64>,
|
||||||
|
/// 发射系数 N 导数
|
||||||
|
pub demn1: Vec<f64>,
|
||||||
|
/// 吸收系数 T 导数
|
||||||
|
pub dabt1: Vec<f64>,
|
||||||
|
/// 吸收系数 N 导数
|
||||||
|
pub dabn1: Vec<f64>,
|
||||||
|
|
||||||
|
// 能级导数 (MLVEXP × MDEPTH)
|
||||||
|
/// 发射系数能级导数
|
||||||
|
pub demp1: Vec<Vec<f64>>,
|
||||||
|
/// 吸收系数能级导数
|
||||||
|
pub dabp1: Vec<Vec<f64>>,
|
||||||
|
|
||||||
|
// 源函数导数 (MDEPTH)
|
||||||
|
/// 源函数 T 导数
|
||||||
|
pub dsfdt: Vec<f64>,
|
||||||
|
/// 源函数 N 导数
|
||||||
|
pub dsfdn: Vec<f64>,
|
||||||
|
/// 前深度源函数 T 导数
|
||||||
|
pub dsfdtm: Vec<f64>,
|
||||||
|
/// 前深度源函数 N 导数
|
||||||
|
pub dsfdnm: Vec<f64>,
|
||||||
|
/// 后深度源函数 T 导数
|
||||||
|
pub dsfdtp: Vec<f64>,
|
||||||
|
/// 后深度源函数 N 导数
|
||||||
|
pub dsfdnp: Vec<f64>,
|
||||||
|
|
||||||
|
// 源函数能级导数 (MLVEXP × MDEPTH)
|
||||||
|
/// 源函数能级导数
|
||||||
|
pub dsfdp: Vec<Vec<f64>>,
|
||||||
|
/// 前深度源函数能级导数
|
||||||
|
pub dsfdpm: Vec<Vec<f64>>,
|
||||||
|
/// 后深度源函数能级导数
|
||||||
|
pub dsfdpp: Vec<Vec<f64>>,
|
||||||
|
|
||||||
|
// 冷却率积分 (MDEPTH)
|
||||||
|
/// 冷却率积分
|
||||||
|
pub fcooli: Vec<f64>,
|
||||||
|
|
||||||
// 控制参数
|
// 控制参数
|
||||||
pub qtlas: f64,
|
pub qtlas: f64,
|
||||||
pub ifali: i32,
|
pub ifali: i32,
|
||||||
@ -186,8 +238,8 @@ impl Default for FixAlp {
|
|||||||
ehen: vec![0.0; MDEPTH],
|
ehen: vec![0.0; MDEPTH],
|
||||||
eret: vec![0.0; MDEPTH],
|
eret: vec![0.0; MDEPTH],
|
||||||
eren: vec![0.0; MDEPTH],
|
eren: vec![0.0; MDEPTH],
|
||||||
ehep: vec![vec![0.0; MDEPTH]; MLVEX3],
|
ehep: vec![vec![0.0; MDEPTH]; MLVEXP],
|
||||||
erep: vec![vec![0.0; MDEPTH]; MLVEX3],
|
erep: vec![vec![0.0; MDEPTH]; MLVEXP],
|
||||||
|
|
||||||
apt: vec![vec![0.0; MDEPTH]; MLVEXP],
|
apt: vec![vec![0.0; MDEPTH]; MLVEXP],
|
||||||
apn: vec![vec![0.0; MDEPTH]; MLVEXP],
|
apn: vec![vec![0.0; MDEPTH]; MLVEXP],
|
||||||
@ -200,6 +252,38 @@ impl Default for FixAlp {
|
|||||||
aapp: vec![vec![vec![0.0; MDEPTH]; MLVEX3]; MLVEX3],
|
aapp: vec![vec![vec![0.0; MDEPTH]; MLVEX3]; MLVEX3],
|
||||||
capp: vec![vec![vec![0.0; MDEPTH]; MLVEX3]; MLVEX3],
|
capp: vec![vec![vec![0.0; MDEPTH]; MLVEX3]; MLVEX3],
|
||||||
|
|
||||||
|
// ALI 算子
|
||||||
|
ali1: vec![0.0; MDEPTH],
|
||||||
|
alim1: vec![0.0; MDEPTH],
|
||||||
|
alip1: vec![0.0; MDEPTH],
|
||||||
|
alih1: vec![0.0; MDEPTH],
|
||||||
|
|
||||||
|
// 发射/吸收系数导数
|
||||||
|
demt1: vec![0.0; MDEPTH],
|
||||||
|
demn1: vec![0.0; MDEPTH],
|
||||||
|
dabt1: vec![0.0; MDEPTH],
|
||||||
|
dabn1: vec![0.0; MDEPTH],
|
||||||
|
|
||||||
|
// 能级导数
|
||||||
|
demp1: vec![vec![0.0; MDEPTH]; MLVEXP],
|
||||||
|
dabp1: vec![vec![0.0; MDEPTH]; MLVEXP],
|
||||||
|
|
||||||
|
// 源函数导数
|
||||||
|
dsfdt: vec![0.0; MDEPTH],
|
||||||
|
dsfdn: vec![0.0; MDEPTH],
|
||||||
|
dsfdtm: vec![0.0; MDEPTH],
|
||||||
|
dsfdnm: vec![0.0; MDEPTH],
|
||||||
|
dsfdtp: vec![0.0; MDEPTH],
|
||||||
|
dsfdnp: vec![0.0; MDEPTH],
|
||||||
|
|
||||||
|
// 源函数能级导数
|
||||||
|
dsfdp: vec![vec![0.0; MDEPTH]; MLVEXP],
|
||||||
|
dsfdpm: vec![vec![0.0; MDEPTH]; MLVEXP],
|
||||||
|
dsfdpp: vec![vec![0.0; MDEPTH]; MLVEXP],
|
||||||
|
|
||||||
|
// 冷却率积分
|
||||||
|
fcooli: vec![0.0; MDEPTH],
|
||||||
|
|
||||||
qtlas: 0.0,
|
qtlas: 0.0,
|
||||||
ifali: 0,
|
ifali: 0,
|
||||||
ifpopr: 0,
|
ifpopr: 0,
|
||||||
|
|||||||
@ -48,6 +48,14 @@ pub const MVOIGT: usize = 8080;
|
|||||||
pub const MZZ: usize = 10;
|
pub const MZZ: usize = 10;
|
||||||
/// 最高氢能级
|
/// 最高氢能级
|
||||||
pub const NLMX: usize = 80;
|
pub const NLMX: usize = 80;
|
||||||
|
/// 最大氢谱线数
|
||||||
|
pub const MLINH: usize = 78;
|
||||||
|
/// 氢线温度点数
|
||||||
|
pub const MHT: usize = 7;
|
||||||
|
/// 氢线电子密度点数
|
||||||
|
pub const MHE: usize = 20;
|
||||||
|
/// 氢线波长点数
|
||||||
|
pub const MHWL: usize = 90;
|
||||||
/// 对角预处理能级数
|
/// 对角预处理能级数
|
||||||
pub const MLEVE3: usize = 1;
|
pub const MLEVE3: usize = 1;
|
||||||
/// 对角/三对角操作能级数
|
/// 对角/三对角操作能级数
|
||||||
|
|||||||
@ -427,6 +427,796 @@ impl LevAdd {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// WMCOMP - 氢能级权重和占据概率
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 氢能级权重和占据概率。
|
||||||
|
/// 对应 COMMON /WMCOMP/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct WmComp {
|
||||||
|
/// 氢能级占据概率积分 (NLMX × 深度)
|
||||||
|
pub wnhint: Vec<Vec<f64>>,
|
||||||
|
/// He II 能级占据概率 (NLMX × 深度)
|
||||||
|
pub wnheii: Vec<Vec<f64>>,
|
||||||
|
/// 能级权重 (能级 × 深度)
|
||||||
|
pub wop: Vec<Vec<f64>>,
|
||||||
|
/// 能级权重标志 (负值表示合并能级)
|
||||||
|
pub ifwop: Vec<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for WmComp {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
wnhint: vec![vec![0.0; MDEPTH]; NLMX],
|
||||||
|
wnheii: vec![vec![0.0; MDEPTH]; NLMX],
|
||||||
|
wop: vec![vec![0.0; MDEPTH]; MLEVEL],
|
||||||
|
ifwop: vec![0; MLEVEL],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// MRGPAR - 合并能级参数
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 合并能级参数 (用于高激发态氢能级合并处理)。
|
||||||
|
/// 对应 COMMON /MRGPAR/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MrgPar {
|
||||||
|
/// 合并能级截面参数 (MMER)
|
||||||
|
pub sgm0: Vec<f64>,
|
||||||
|
/// 频率参考 FRCH (MMER)
|
||||||
|
pub frch: Vec<f64>,
|
||||||
|
/// 截面扩展 (MMER × 深度)
|
||||||
|
pub sgext1: Vec<Vec<f64>>,
|
||||||
|
/// Gaunt 因子 (MMER × 深度)
|
||||||
|
pub gmer: Vec<Vec<f64>>,
|
||||||
|
/// 截面求和 (NLMX × MMER × 深度) - 3D 数组
|
||||||
|
pub sgmsum: Vec<Vec<Vec<f64>>>,
|
||||||
|
/// 截面求和导数 (NLMX × MMER × 深度) - 3D 数组
|
||||||
|
pub sgmsud: Vec<Vec<Vec<f64>>>,
|
||||||
|
/// Gaunt 因子 (MMER × 深度)
|
||||||
|
pub sgmg: Vec<Vec<f64>>,
|
||||||
|
/// 能级到合并能级的映射 (MLEVEL)
|
||||||
|
pub imrg: Vec<i32>,
|
||||||
|
/// 合并能级到能级的映射 (MMER)
|
||||||
|
pub iimer: Vec<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MrgPar {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
sgm0: vec![0.0; MMER],
|
||||||
|
frch: vec![0.0; MMER],
|
||||||
|
sgext1: vec![vec![0.0; MDEPTH]; MMER],
|
||||||
|
gmer: vec![vec![0.0; MDEPTH]; MMER],
|
||||||
|
// 3D 数组: [nlmx][mmer][depth]
|
||||||
|
sgmsum: vec![vec![vec![0.0; MDEPTH]; MMER]; NLMX],
|
||||||
|
sgmsud: vec![vec![vec![0.0; MDEPTH]; MMER]; NLMX],
|
||||||
|
sgmg: vec![vec![0.0; MDEPTH]; MMER],
|
||||||
|
imrg: vec![0; MLEVEL],
|
||||||
|
iimer: vec![0; MMER],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// FREAUX - 频率辅助数组
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 频率辅助数组。
|
||||||
|
/// 对应 COMMON /FREAUX/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FreAux {
|
||||||
|
/// Wien 定律系数 W0E = h*nu/kT 在阈值处
|
||||||
|
pub w0e: Vec<f64>,
|
||||||
|
/// Planck 函数系数 BNUE = 2h*nu³/c²
|
||||||
|
pub bnue: Vec<f64>,
|
||||||
|
/// 连续谱权重
|
||||||
|
pub wc: Vec<f64>,
|
||||||
|
/// 跃迁到连续谱的索引
|
||||||
|
pub ijtc: Vec<i32>,
|
||||||
|
/// ALI 频率索引
|
||||||
|
pub ijali: Vec<i32>,
|
||||||
|
/// 显式频率索引
|
||||||
|
pub ijex: Vec<i32>,
|
||||||
|
/// 频率索引
|
||||||
|
pub ijfr: Vec<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FreAux {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
w0e: vec![0.0; MFREQ],
|
||||||
|
bnue: vec![0.0; MFREQ],
|
||||||
|
wc: vec![0.0; MFREQ],
|
||||||
|
ijtc: vec![0; MTRANS],
|
||||||
|
ijali: vec![0; MFREQ],
|
||||||
|
ijex: vec![0; MFREQ],
|
||||||
|
ijfr: vec![0; MFREQ],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// INVINT - 逆整数幂数组
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 逆整数幂数组。
|
||||||
|
/// 对应 COMMON /INVINT/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct InvInt {
|
||||||
|
/// 1/n² (n = 1..NLMX)
|
||||||
|
pub xi2: Vec<f64>,
|
||||||
|
/// 1/n³ (n = 1..NLMX)
|
||||||
|
pub xi3: Vec<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for InvInt {
|
||||||
|
fn default() -> Self {
|
||||||
|
let mut xi2 = vec![0.0; NLMX];
|
||||||
|
let mut xi3 = vec![0.0; NLMX];
|
||||||
|
for i in 1..=NLMX {
|
||||||
|
let x = i as f64;
|
||||||
|
xi2[i - 1] = 1.0 / (x * x);
|
||||||
|
xi3[i - 1] = xi2[i - 1] / x;
|
||||||
|
}
|
||||||
|
Self { xi2, xi3 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// REPART - 辐射等效扩散参数
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 辐射等效扩散参数。
|
||||||
|
/// 对应 COMMON /REPART/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct RePart {
|
||||||
|
/// 辐射等效积分
|
||||||
|
pub reint: Vec<f64>,
|
||||||
|
/// 辐射扩散因子
|
||||||
|
pub redif: Vec<f64>,
|
||||||
|
/// τ 分割点
|
||||||
|
pub taudiv: f64,
|
||||||
|
/// 最后深度索引
|
||||||
|
pub idlst: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RePart {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
reint: vec![0.0; MDEPTH],
|
||||||
|
redif: vec![0.0; MDEPTH],
|
||||||
|
taudiv: 0.0,
|
||||||
|
idlst: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// TURBUL - 湍流速度
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 湍流速度参数。
|
||||||
|
/// 对应 COMMON /TURBUL/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Turbul {
|
||||||
|
/// 湍流速度 VTURB(MDEPTH)
|
||||||
|
pub vturb: Vec<f64>,
|
||||||
|
/// 湍流速度平方 VTURBS(MDEPTH)
|
||||||
|
pub vturbs: Vec<f64>,
|
||||||
|
/// 湍流速度参数
|
||||||
|
pub vtb: f64,
|
||||||
|
/// 湍流标志
|
||||||
|
pub ipturb: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Turbul {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
vturb: vec![0.0; MDEPTH],
|
||||||
|
vturbs: vec![0.0; MDEPTH],
|
||||||
|
vtb: 0.0,
|
||||||
|
ipturb: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// STRAUX - Stark 轮廓辅助变量
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// Stark 轮廓辅助变量。
|
||||||
|
/// 对应 COMMON /STRAUX/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct StrAux {
|
||||||
|
/// 谱线展宽参数
|
||||||
|
pub xk0: Vec<f64>,
|
||||||
|
/// 当前谱线展宽
|
||||||
|
pub xk: f64,
|
||||||
|
/// Doppler 展宽
|
||||||
|
pub dbeta: f64,
|
||||||
|
/// Doppler 宽度
|
||||||
|
pub betad: f64,
|
||||||
|
/// 辅助参数 A
|
||||||
|
pub adh: f64,
|
||||||
|
/// 分割点
|
||||||
|
pub divh: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for StrAux {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
xk0: vec![0.0; MLINH],
|
||||||
|
xk: 0.0,
|
||||||
|
dbeta: 0.0,
|
||||||
|
betad: 0.0,
|
||||||
|
adh: 0.0,
|
||||||
|
divh: 0.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// PRESSR - 压力相关数组
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 压力相关数组。
|
||||||
|
/// 对应 COMMON /PRESSR/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PressR {
|
||||||
|
/// 总压力
|
||||||
|
pub ptotal: Vec<f64>,
|
||||||
|
/// 气体压力
|
||||||
|
pub pgs: Vec<f64>,
|
||||||
|
/// 辐射压力 (总)
|
||||||
|
pub pradt: Vec<f64>,
|
||||||
|
/// 辐射压力 (吸收)
|
||||||
|
pub prada: Vec<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PressR {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
ptotal: vec![0.0; MDEPTH],
|
||||||
|
pgs: vec![0.0; MDEPTH],
|
||||||
|
pradt: vec![0.0; MDEPTH],
|
||||||
|
prada: vec![0.0; MDEPTH],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// DWNPAR - 溶解分数参数
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 溶解分数辅助参数。
|
||||||
|
/// 对应 COMMON /DWNPAR/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct DwnPar {
|
||||||
|
/// 电子密度的 2/3 次幂
|
||||||
|
pub elec23: Vec<f64>,
|
||||||
|
/// 修正因子
|
||||||
|
pub acor: Vec<f64>,
|
||||||
|
/// Z³ (Z = 1..MZZ)
|
||||||
|
pub z3: Vec<f64>,
|
||||||
|
/// 溶解分数参数 1
|
||||||
|
pub dwc1: Vec<Vec<f64>>,
|
||||||
|
/// 溶解分数参数 2
|
||||||
|
pub dwc2: Vec<f64>,
|
||||||
|
/// 溶解分数 (MMCDW × MDEPTH)
|
||||||
|
pub dwf1: Vec<Vec<f64>>,
|
||||||
|
/// 跃迁到溶解分数的映射 (MTRANS)
|
||||||
|
pub mcdw: Vec<i32>,
|
||||||
|
/// 溶解分数到跃迁的映射 (MMCDW)
|
||||||
|
pub itrcdw: Vec<i32>,
|
||||||
|
/// 溶解分数计数
|
||||||
|
pub ncdw: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DwnPar {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
elec23: vec![0.0; MDEPTH],
|
||||||
|
acor: vec![0.0; MDEPTH],
|
||||||
|
z3: vec![0.0; MZZ],
|
||||||
|
dwc1: vec![vec![0.0; MDEPTH]; MZZ],
|
||||||
|
dwc2: vec![0.0; MDEPTH],
|
||||||
|
dwf1: vec![vec![0.0; MDEPTH]; MMCDW],
|
||||||
|
mcdw: vec![0; MTRANS],
|
||||||
|
itrcdw: vec![0; MMCDW],
|
||||||
|
ncdw: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// LINOVR - 谱线叠加
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 谱线叠加参数。
|
||||||
|
/// 对应 COMMON /LINOVR/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LinOvr {
|
||||||
|
/// 每个频率点的谱线数
|
||||||
|
pub nitj: Vec<i32>,
|
||||||
|
/// 每个频率点的跃迁索引
|
||||||
|
pub ijlin: Vec<i32>,
|
||||||
|
/// 谱线跃迁索引 (MITJ × MFREQ)
|
||||||
|
pub itrlin: Vec<Vec<i32>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for LinOvr {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
nitj: vec![0; MFREQ],
|
||||||
|
ijlin: vec![0; MFREQ],
|
||||||
|
itrlin: vec![vec![0; MFREQ]; MITJ],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// LINFRQ - 谱线频率
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 谱线频率参数。
|
||||||
|
/// 对应 COMMON /LINFRQ/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LinFrq {
|
||||||
|
/// 每个频率点的谱线数
|
||||||
|
pub nlines: Vec<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for LinFrq {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
nlines: vec![0; MFREQ],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// COMPIF - 计算标志
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 计算标志参数。
|
||||||
|
/// 对应 COMMON /COMPIF/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct CompIf {
|
||||||
|
/// 经验线标志 (MTRANS)
|
||||||
|
pub linexp: Vec<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CompIf {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
linexp: vec![false; MTRANS],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// CUROPA - 当前不透明度
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 当前不透明度参数。
|
||||||
|
/// 对应 COMMON /CUROPA/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct CurOpa {
|
||||||
|
/// 吸收系数
|
||||||
|
pub abso1: Vec<f64>,
|
||||||
|
/// 发射系数
|
||||||
|
pub emis1: Vec<f64>,
|
||||||
|
/// 散射系数
|
||||||
|
pub scat1: Vec<f64>,
|
||||||
|
/// 总吸收
|
||||||
|
pub absot: Vec<f64>,
|
||||||
|
/// 显式频率吸收
|
||||||
|
pub absoe1: Vec<f64>,
|
||||||
|
/// 电子发射
|
||||||
|
pub emel1: Vec<f64>,
|
||||||
|
/// 谱线吸收
|
||||||
|
pub abso1l: Vec<f64>,
|
||||||
|
/// 谱线发射
|
||||||
|
pub emis1l: Vec<f64>,
|
||||||
|
/// 不透明度导数 (压力)
|
||||||
|
pub absopr: Vec<f64>,
|
||||||
|
/// 发射导数 (压力)
|
||||||
|
pub emispr: Vec<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CurOpa {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
abso1: vec![0.0; MDEPTH],
|
||||||
|
emis1: vec![0.0; MDEPTH],
|
||||||
|
scat1: vec![0.0; MDEPTH],
|
||||||
|
absot: vec![0.0; MDEPTH],
|
||||||
|
absoe1: vec![0.0; MFREX],
|
||||||
|
emel1: vec![0.0; MDEPTH],
|
||||||
|
abso1l: vec![0.0; MDEPTH],
|
||||||
|
emis1l: vec![0.0; MDEPTH],
|
||||||
|
absopr: vec![0.0; MDEPTH],
|
||||||
|
emispr: vec![0.0; MDEPTH],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 氢线 Stark 展宽表格 (HYDPRF)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 氢线 Stark 展宽表格参数。
|
||||||
|
/// 对应 COMMON /HYDPRF/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct HydPrf {
|
||||||
|
/// 氢线轮廓 PRFHYD(MLINH,MHWL,MHT,MHE)
|
||||||
|
/// 注意:Fortran 是列优先,这里用 1D 数组模拟 4D
|
||||||
|
/// 索引:prfhyd[iline + mlinh*iwl + mlinh*mhwl*it + mlinh*mhwl*mht*ie]
|
||||||
|
pub prfhyd: Vec<f64>,
|
||||||
|
/// 氢线波长 WLHYD(MLINH,MHWL)
|
||||||
|
pub wlhyd: Vec<f64>,
|
||||||
|
/// 波长网格 WLH(MHWL,MLINH)
|
||||||
|
pub wlh: Vec<f64>,
|
||||||
|
/// 温度网格 XTLEM(MHT,MLINH)
|
||||||
|
pub xtlem: Vec<f64>,
|
||||||
|
/// 电子密度网格 XNELEM(MHE,MLINH)
|
||||||
|
pub xnelem: Vec<f64>,
|
||||||
|
/// 每条线的波长点数 NWLHYD(MLINH)
|
||||||
|
pub nwlhyd: Vec<i32>,
|
||||||
|
/// 波长点数 NWLH(MLINH)
|
||||||
|
pub nwlh: Vec<i32>,
|
||||||
|
/// 温度点数 NTH(MLINH)
|
||||||
|
pub nth: Vec<i32>,
|
||||||
|
/// 电子密度点数 NEH(MLINH)
|
||||||
|
pub neh: Vec<i32>,
|
||||||
|
/// 线索引 ILINH(4,22)
|
||||||
|
pub ilinh: Vec<i32>,
|
||||||
|
/// 氢线表格读取标志
|
||||||
|
pub ihydpr: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for HydPrf {
|
||||||
|
fn default() -> Self {
|
||||||
|
use super::constants::{MHT, MHE, MHWL, MLINH};
|
||||||
|
Self {
|
||||||
|
// PRFHYD: MLINH * MHWL * MHT * MHE = 78 * 90 * 7 * 20 = 982,800
|
||||||
|
prfhyd: vec![0.0; MLINH * MHWL * MHT * MHE],
|
||||||
|
// WLHYD: MLINH * MHWL = 78 * 90 = 7,020
|
||||||
|
wlhyd: vec![0.0; MLINH * MHWL],
|
||||||
|
// WLH: MHWL * MLINH = 90 * 78 = 7,020
|
||||||
|
wlh: vec![0.0; MHWL * MLINH],
|
||||||
|
// XTLEM: MHT * MLINH = 7 * 78 = 546
|
||||||
|
xtlem: vec![0.0; MHT * MLINH],
|
||||||
|
// XNELEM: MHE * MLINH = 20 * 78 = 1,560
|
||||||
|
xnelem: vec![0.0; MHE * MLINH],
|
||||||
|
nwlhyd: vec![0; MLINH],
|
||||||
|
nwlh: vec![0; MLINH],
|
||||||
|
nth: vec![0; MLINH],
|
||||||
|
neh: vec![0; MLINH],
|
||||||
|
// ILINH: 4 * 22 = 88
|
||||||
|
ilinh: vec![0; 88],
|
||||||
|
ihydpr: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HydPrf {
|
||||||
|
/// 获取 PRFHYD 值 (4D 数组访问)
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
/// - `iline`: 谱线索引 (0-indexed)
|
||||||
|
/// - `iwl`: 波长索引 (0-indexed)
|
||||||
|
/// - `it`: 温度索引 (0-indexed)
|
||||||
|
/// - `ie`: 电子密度索引 (0-indexed)
|
||||||
|
pub fn get_prfhyd(&self, iline: usize, iwl: usize, it: usize, ie: usize) -> f64 {
|
||||||
|
use super::constants::{MHT, MHWL, MLINH};
|
||||||
|
let idx = iline + MLINH * iwl + MLINH * MHWL * it + MLINH * MHWL * MHT * ie;
|
||||||
|
self.prfhyd[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置 PRFHYD 值 (4D 数组访问)
|
||||||
|
pub fn set_prfhyd(&mut self, iline: usize, iwl: usize, it: usize, ie: usize, value: f64) {
|
||||||
|
use super::constants::{MHT, MHWL, MLINH};
|
||||||
|
let idx = iline + MLINH * iwl + MLINH * MHWL * it + MLINH * MHWL * MHT * ie;
|
||||||
|
self.prfhyd[idx] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 WLHYD 值 (2D 数组访问)
|
||||||
|
pub fn get_wlhyd(&self, iline: usize, iwl: usize) -> f64 {
|
||||||
|
use super::constants::MLINH;
|
||||||
|
self.wlhyd[iline + MLINH * iwl]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 WLH 值 (2D 数组访问,注意 Fortran 索引顺序)
|
||||||
|
/// Fortran: WLH(MHWL,MLINH) -> Rust: wlh[iwl + MHWL*iline]
|
||||||
|
pub fn get_wlh(&self, iwl: usize, iline: usize) -> f64 {
|
||||||
|
use super::constants::MHWL;
|
||||||
|
self.wlh[iwl + MHWL * iline]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 XTLEM 值 (2D 数组访问)
|
||||||
|
/// Fortran: XTLEM(MHT,MLINH) -> Rust: xtlem[it + MHT*iline]
|
||||||
|
pub fn get_xtlem(&self, it: usize, iline: usize) -> f64 {
|
||||||
|
use super::constants::MHT;
|
||||||
|
self.xtlem[it + MHT * iline]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 XNELEM 值 (2D 数组访问)
|
||||||
|
/// Fortran: XNELEM(MHE,MLINH) -> Rust: xnelem[ie + MHE*iline]
|
||||||
|
pub fn get_xnelem(&self, ie: usize, iline: usize) -> f64 {
|
||||||
|
use super::constants::MHE;
|
||||||
|
self.xnelem[ie + MHE * iline]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置 XTLEM 值 (2D 数组访问)
|
||||||
|
pub fn set_xtlem(&mut self, it: usize, iline: usize, value: f64) {
|
||||||
|
use super::constants::MHT;
|
||||||
|
self.xtlem[it + MHT * iline] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置 XNELEM 值 (2D 数组访问)
|
||||||
|
pub fn set_xnelem(&mut self, ie: usize, iline: usize, value: f64) {
|
||||||
|
use super::constants::MHE;
|
||||||
|
self.xnelem[ie + MHE * iline] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// XENPRF - Xenomorph 谱线轮廓表
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// Xenomorph 氢线轮廓表。
|
||||||
|
/// 对应 COMMON /XENPRF/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct XenPrf {
|
||||||
|
/// 轮廓表 PRFXB(MLINH,MHWL,MHT,MHE) - 蓝翼
|
||||||
|
pub prfxb: Vec<f64>,
|
||||||
|
/// 轮廓表 PRFXR(MLINH,MHWL,MHT,MHE) - 红翼
|
||||||
|
pub prfxr: Vec<f64>,
|
||||||
|
/// 波长表 ALXEN(MLINH,MHWL)
|
||||||
|
pub alxen: Vec<f64>,
|
||||||
|
/// 温度网格 XTXEN(MHT,MLINH)
|
||||||
|
pub xtxen: Vec<f64>,
|
||||||
|
/// 电子密度网格 XNEXEN(MHE,MLINH)
|
||||||
|
pub xnexen: Vec<f64>,
|
||||||
|
/// 电子密度最小值
|
||||||
|
pub xnemin: f64,
|
||||||
|
/// 每条线的波长点数 NWLXEN(MLINH)
|
||||||
|
pub nwlxen: Vec<i32>,
|
||||||
|
/// 每条线的温度点数 NTHXEN(MLINH)
|
||||||
|
pub nthxen: Vec<i32>,
|
||||||
|
/// 每条线的电子密度点数 NEHXEN(MLINH)
|
||||||
|
pub nehxen: Vec<i32>,
|
||||||
|
/// 线索引 ILXEN(4,22)
|
||||||
|
pub ilxen: Vec<i32>,
|
||||||
|
/// 氢线标志
|
||||||
|
pub ihxenb: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for XenPrf {
|
||||||
|
fn default() -> Self {
|
||||||
|
use super::constants::{MHT, MHE, MHWL, MLINH};
|
||||||
|
Self {
|
||||||
|
// PRFXB, PRFXR: MLINH * MHWL * MHT * MHE = 78 * 90 * 7 * 20 = 982,800
|
||||||
|
prfxb: vec![0.0; MLINH * MHWL * MHT * MHE],
|
||||||
|
prfxr: vec![0.0; MLINH * MHWL * MHT * MHE],
|
||||||
|
// ALXEN: MLINH * MHWL = 78 * 90 = 7,020
|
||||||
|
alxen: vec![0.0; MLINH * MHWL],
|
||||||
|
// XTXEN: MHT * MLINH = 7 * 78 = 546
|
||||||
|
xtxen: vec![0.0; MHT * MLINH],
|
||||||
|
// XNEXEN: MHE * MLINH = 20 * 78 = 1,560
|
||||||
|
xnexen: vec![0.0; MHE * MLINH],
|
||||||
|
xnemin: 0.0,
|
||||||
|
nwlxen: vec![0; MLINH],
|
||||||
|
nthxen: vec![0; MLINH],
|
||||||
|
nehxen: vec![0; MLINH],
|
||||||
|
// ILXEN: 4 * 22 = 88
|
||||||
|
ilxen: vec![0; 88],
|
||||||
|
ihxenb: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XenPrf {
|
||||||
|
/// 获取 PRFXB 值 (4D 数组访问)
|
||||||
|
pub fn get_prfxb(&self, iline: usize, iwl: usize, it: usize, ie: usize) -> f64 {
|
||||||
|
use super::constants::{MHT, MHWL, MLINH};
|
||||||
|
let idx = iline + MLINH * iwl + MLINH * MHWL * it + MLINH * MHWL * MHT * ie;
|
||||||
|
self.prfxb[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置 PRFXB 值 (4D 数组访问)
|
||||||
|
pub fn set_prfxb(&mut self, iline: usize, iwl: usize, it: usize, ie: usize, value: f64) {
|
||||||
|
use super::constants::{MHT, MHWL, MLINH};
|
||||||
|
let idx = iline + MLINH * iwl + MLINH * MHWL * it + MLINH * MHWL * MHT * ie;
|
||||||
|
self.prfxb[idx] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 PRFXR 值 (4D 数组访问)
|
||||||
|
pub fn get_prfxr(&self, iline: usize, iwl: usize, it: usize, ie: usize) -> f64 {
|
||||||
|
use super::constants::{MHT, MHWL, MLINH};
|
||||||
|
let idx = iline + MLINH * iwl + MLINH * MHWL * it + MLINH * MHWL * MHT * ie;
|
||||||
|
self.prfxr[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置 PRFXR 值 (4D 数组访问)
|
||||||
|
pub fn set_prfxr(&mut self, iline: usize, iwl: usize, it: usize, ie: usize, value: f64) {
|
||||||
|
use super::constants::{MHT, MHWL, MLINH};
|
||||||
|
let idx = iline + MLINH * iwl + MLINH * MHWL * it + MLINH * MHWL * MHT * ie;
|
||||||
|
self.prfxr[idx] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 XTXEN 值 (温度网格, 2D)
|
||||||
|
pub fn get_xtxen(&self, it: usize, iline: usize) -> f64 {
|
||||||
|
use super::constants::MHT;
|
||||||
|
self.xtxen[it + MHT * iline]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 XNEXEN 值 (电子密度网格, 2D)
|
||||||
|
pub fn get_xnexen(&self, ie: usize, iline: usize) -> f64 {
|
||||||
|
use super::constants::MHE;
|
||||||
|
self.xnexen[ie + MHE * iline]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// RAYSCT - Rayleigh 散射截面
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// Rayleigh 散射截面参数。
|
||||||
|
/// 对应 COMMON /RAYSCT/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct RaySct {
|
||||||
|
/// 氢 Rayleigh 散射截面 (MFREQ)
|
||||||
|
pub rcs: Vec<f64>,
|
||||||
|
/// 氦 Rayleigh 散射截面 (MFREQ)
|
||||||
|
pub rche: Vec<f64>,
|
||||||
|
/// H2 Rayleigh 散射截面 (MFREQ)
|
||||||
|
pub rch2: Vec<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RaySct {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
rcs: vec![0.0; MFREQ],
|
||||||
|
rche: vec![0.0; MFREQ],
|
||||||
|
rch2: vec![0.0; MFREQ],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// EOSPAR - 状态方程粒子数密度
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 状态方程粒子数密度参数。
|
||||||
|
/// 对应 COMMON /eospar/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct EosPar {
|
||||||
|
/// 分子粒子数密度 (600 × MDEPTH)
|
||||||
|
pub anmol: Vec<Vec<f64>>,
|
||||||
|
/// 原子粒子数密度 (100 × MDEPTH)
|
||||||
|
pub anato: Vec<Vec<f64>>,
|
||||||
|
/// 离子粒子数密度 (100 × MDEPTH)
|
||||||
|
pub anion: Vec<Vec<f64>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for EosPar {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
anmol: vec![vec![0.0; MDEPTH]; 600],
|
||||||
|
anato: vec![vec![0.0; MDEPTH]; 100],
|
||||||
|
anion: vec![vec![0.0; MDEPTH]; 100],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// SURFAC - 表面辐射通量
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 表面辐射通量参数。
|
||||||
|
/// 对应 COMMON /SURFAC/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Surfac {
|
||||||
|
/// 表面辐射通量 (MFREQ)
|
||||||
|
pub flux: Vec<f64>,
|
||||||
|
/// 频率相关权重因子 (MFREQ)
|
||||||
|
pub fh: Vec<f64>,
|
||||||
|
/// 表面辐射强度 Q0 (MFREQ)
|
||||||
|
pub q0: Vec<f64>,
|
||||||
|
/// 表面辐射强度 UU0 (MFREQ)
|
||||||
|
pub uu0: Vec<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Surfac {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
flux: vec![0.0; MFREQ],
|
||||||
|
fh: vec![0.0; MFREQ],
|
||||||
|
q0: vec![0.0; MFREQ],
|
||||||
|
uu0: vec![0.0; MFREQ],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// CURRNT - 当前深度辐射参数
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 当前深度辐射参数。
|
||||||
|
/// 对应 COMMON /CURRNT/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Currnt {
|
||||||
|
/// 普朗克函数权重 (MDEPTH)
|
||||||
|
pub xkf: Vec<f64>,
|
||||||
|
/// 1 - XKF (MDEPTH)
|
||||||
|
pub xkf1: Vec<f64>,
|
||||||
|
/// 普朗克函数 × XKF (MDEPTH)
|
||||||
|
pub xkfb: Vec<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Currnt {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
xkf: vec![0.0; MDEPTH],
|
||||||
|
xkf1: vec![0.0; MDEPTH],
|
||||||
|
xkfb: vec![0.0; MDEPTH],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// TOTFLX - 总辐射通量
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 总辐射通量参数。
|
||||||
|
/// 对应 COMMON /TOTFLX/
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TotFlx {
|
||||||
|
/// 总辐射通量 (MDEPTH)
|
||||||
|
pub fltot: Vec<f64>,
|
||||||
|
/// 固定辐射通量 (MDEPTH)
|
||||||
|
pub flfix: Vec<f64>,
|
||||||
|
/// 显式频率辐射通量 (MDEPTH)
|
||||||
|
pub flexp: Vec<f64>,
|
||||||
|
/// 冷却率 (MDEPTH)
|
||||||
|
pub fcool: Vec<f64>,
|
||||||
|
/// 冷却率积分 (MDEPTH)
|
||||||
|
pub fcooli: Vec<f64>,
|
||||||
|
/// 辐射通量红翼 (MDEPTH)
|
||||||
|
pub flrd: Vec<f64>,
|
||||||
|
/// 辐射压力 (MDEPTH)
|
||||||
|
pub fprad: Vec<f64>,
|
||||||
|
/// 辐射梯度 (MDEPTH)
|
||||||
|
pub grad: Vec<f64>,
|
||||||
|
/// 辐射压力导数 (MDEPTH)
|
||||||
|
pub fprd: Vec<f64>,
|
||||||
|
/// 频率相关辐射梯度 (MFREQ × MDEPTH)
|
||||||
|
pub gradf: Vec<Vec<f64>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TotFlx {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
fltot: vec![0.0; MDEPTH],
|
||||||
|
flfix: vec![0.0; MDEPTH],
|
||||||
|
flexp: vec![0.0; MDEPTH],
|
||||||
|
fcool: vec![0.0; MDEPTH],
|
||||||
|
fcooli: vec![0.0; MDEPTH],
|
||||||
|
flrd: vec![0.0; MDEPTH],
|
||||||
|
fprad: vec![0.0; MDEPTH],
|
||||||
|
grad: vec![0.0; MDEPTH],
|
||||||
|
fprd: vec![0.0; MDEPTH],
|
||||||
|
gradf: vec![vec![0.0; MDEPTH]; MFREQ],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 综合模型状态
|
// 综合模型状态
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@ -444,6 +1234,26 @@ pub struct ModelState {
|
|||||||
pub phoexp: PhoExp,
|
pub phoexp: PhoExp,
|
||||||
pub obfpar: ObfPar,
|
pub obfpar: ObfPar,
|
||||||
pub levadd: LevAdd,
|
pub levadd: LevAdd,
|
||||||
|
pub wmcomp: WmComp,
|
||||||
|
pub mrgpar: MrgPar,
|
||||||
|
pub invint: InvInt,
|
||||||
|
pub freaux: FreAux,
|
||||||
|
pub repart: RePart,
|
||||||
|
pub turbul: Turbul,
|
||||||
|
pub straux: StrAux,
|
||||||
|
pub pressr: PressR,
|
||||||
|
pub dwnpar: DwnPar,
|
||||||
|
pub linovr: LinOvr,
|
||||||
|
pub linfrq: LinFrq,
|
||||||
|
pub compif: CompIf,
|
||||||
|
pub curopa: CurOpa,
|
||||||
|
pub hydprf: HydPrf,
|
||||||
|
pub xenprf: XenPrf,
|
||||||
|
pub raysct: RaySct,
|
||||||
|
pub eospar: EosPar,
|
||||||
|
pub surfac: Surfac,
|
||||||
|
pub currnt: Currnt,
|
||||||
|
pub totflx: TotFlx,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModelState {
|
impl ModelState {
|
||||||
|
|||||||
0
tlusty/extracted/fortran_analysis.csv
Normal file
0
tlusty/extracted/fortran_analysis.csv
Normal file
|
|
Loading…
Reference in New Issue
Block a user