This commit is contained in:
Asfmq 2026-03-21 16:23:35 +08:00
parent f286ddacfe
commit 497e62e13c
35 changed files with 10781 additions and 364 deletions

View File

@ -7,11 +7,11 @@
| 指标 | 数量 |
|------|------|
| 总单元数 | 304 |
| 已完成 | 113 |
| 待处理 | 191 |
| 已完成 | 125 |
| 待处理 | 179 |
| 纯函数 | 67 |
| 有文件 I/O | 117 |
| 完成率 | 37.2% |
| 完成率 | 41.1% |
## 状态说明
@ -38,19 +38,19 @@
| 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 | 📁 | | ⬜ |
| alisk1.f | ALISK1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT | ALIFRK, ROSSTD, OPACF1, RTEFR1 | 📁 | | ⬜ |
| alisk2.f | ALISK2 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT | ALIFRK, ROSSTD, OPACF1, RTEFR1 | 📁 | | ⬜ |
| alist1.f | ALIST1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ITERAT | OPACFD, ALIFR1, ROSSTD, RTEFR1 | 📁 | | ⬜ |
| alist2.f | ALIST2 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT | RTEFR1, ROSSTD, ALIFR1, QUIT, OPACFD | 📁 | | ⬜ |
| allard.f | ALLARD | SUBROUTINE | | BASICS, calphatd, callarda, callardc, callardg, callardb, quasun | ALLARDT | 📁 | | ⬜ |
| allardt.f | ALLARDT | SUBROUTINE | | BASICS, calphatd | | | allardt.rs | ✅ |
| angset.f | ANGSET | SUBROUTINE | ✓ | BASICS | GAULEG | | angset.rs | ✅ |
| betah.f | BETAH | FUNCTION | ✓ | | BETAH | | betah.rs | ✅ |
| bhe.f | BHE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR | | | | ⬜ |
| bhed.f | BHED | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, SURFEX, CMATZD | | | | ⬜ |
| bhez.f | BHEZ | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, SURFEX | | | | ⬜ |
| bhe.f | BHE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR | | | bhe.rs | ✅ |
| bhed.f | BHED | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, CMATZD, SURFEX | | | bhe.rs | ✅ |
| bhez.f | BHEZ | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, SURFEX | | | bhe.rs | ✅ |
| bkhsgo.f | BKHSGO | SUBROUTINE | ✓ | | | | bkhsgo.rs | ✅ |
| bpop.f | BPOP | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, ODFPAR, ITERAT | BPOPF, RATMAT, BPOPT, LEVSOL, MATINV, BPOPC, BPOPE, LEVGRP | | | ⬜ |
| bpop.f | BPOP | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, ODFPAR, ITERAT | MATINV, BPOPT, BPOPF, LEVSOL, RATMAT, BPOPC, BPOPE, LEVGRP | | | ⬜ |
| bpopc.f | BPOPC | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, ODFPAR, ADCHAR | STATE | | | ⬜ |
| bpope.f | BPOPE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ITERAT, ARRAY1 | SGMER1, DWNFR1 | | | ⬜ |
| bpopf.f | BPOPF | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, ODFPAR | | | bpopf.rs | ✅ |
@ -62,37 +62,37 @@
| 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 | 📁 | | ⬜ |
| change.f | CHANGE | SUBROUTINE | | BASICS, ATOMIC, MODELQ | STEQEQ, READBF | 📁 | | ⬜ |
| chckse.f | CHCKSE | SUBROUTINE | | BASICS, ATOMIC, MODELQ | SABOLF | 📁 | | ⬜ |
| chctab.f | CHCTAB | SUBROUTINE | | BASICS, MODELQ, abntab | | 📁 | | ⬜ |
| cheav.f | CHEAV | FUNCTION | | BASICS, ATOMIC | QUIT, CHEAV | 📁 | | ⬜ |
| cheavj.f | CHEAVJ | FUNCTION | | BASICS, ATOMIC | CHEAVJ, QUIT | 📁 | | ⬜ |
| cheav.f | CHEAV | FUNCTION | | BASICS, ATOMIC | CHEAV, QUIT | 📁 | | ⬜ |
| cheavj.f | CHEAVJ | FUNCTION | | BASICS, ATOMIC | QUIT, CHEAVJ | 📁 | | ⬜ |
| cia_h2h.f | CIA_H2H | SUBROUTINE | | | LOCATE, IF | 📁 | | ⬜ |
| cia_h2h2.f | CIA_H2H2 | SUBROUTINE | | | LOCATE, IF | 📁 | | ⬜ |
| cia_h2he.f | CIA_H2HE | SUBROUTINE | | | LOCATE, IF | 📁 | | ⬜ |
| cia_hhe.f | CIA_HHE | SUBROUTINE | | | LOCATE, IF | 📁 | | ⬜ |
| cion.f | CION | FUNCTION | ✓ | | CION | | cion.rs | ✅ |
| ckoest.f | CKOEST | FUNCTION | ✓ | BASICS | CKOEST | | ckoest.rs | ✅ |
| colh.f | COLH | SUBROUTINE | | BASICS, ATOMIC, MODELQ | CSPEC, IRC, BUTLER | | | ⬜ |
| colhe.f | COLHE | SUBROUTINE | | BASICS, ATOMIC | CSPEC, COLLHE, IRC | | | ⬜ |
| colis.f | COLIS | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, CTRTEMP | CSPEC, COLH, IRC, COLHE | | | ⬜ |
| colh.f | COLH | SUBROUTINE | | BASICS, ATOMIC, MODELQ | CSPEC, BUTLER, IRC | | | ⬜ |
| colhe.f | COLHE | SUBROUTINE | | BASICS, ATOMIC | COLLHE, CSPEC, IRC | | | ⬜ |
| colis.f | COLIS | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, CTRTEMP | CSPEC, COLH, COLHE, IRC | | | ⬜ |
| collhe.f | COLLHE | SUBROUTINE | ✓ | | | | collhe.rs | ✅ |
| column.f | COLUMN | SUBROUTINE | | BASICS, MODELQ, relcor | | 📁 | | ⬜ |
| compt0.f | COMPT0 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, auxcbc | | | | ⬜ |
| comset.f | COMSET | SUBROUTINE | | BASICS, MODELQ, comgfs, auxcbc | | | | ⬜ |
| compt0.f | COMPT0 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, auxcbc | | | compt0.rs | ✅ |
| comset.f | COMSET | SUBROUTINE | | BASICS, MODELQ, comgfs, auxcbc | | | comset.rs | ✅ |
| concor.f | CONCOR | SUBROUTINE | | BASICS, MODELQ | CONOUT | 📁 | | ⬜ |
| conout.f | CONOUT | SUBROUTINE | | BASICS, MODELQ, ALIPAR, CUBCON | 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 | 📁 | | ⬜ |
| conout.f | CONOUT | SUBROUTINE | | BASICS, MODELQ, ALIPAR, CUBCON | MEANOP, MEANOPT, CONVEC, OPACF0 | 📁 | | ⬜ |
| conref.f | CONREF | SUBROUTINE | | BASICS, MODELQ, ARRAY1, imucnn, CUBCON | CONVC1, WNSTOR, STEQEQ, ELDENS, CONVEC, CONOUT | 📁 | | ⬜ |
| contmd.f | CONTMD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR, PRSAUX, CUBCON | MEANOP, WNSTOR, STEQEQ, OPACF0, CONVEC, CONOUT, CUBIC | 📁 | | ⬜ |
| contmp.f | CONTMP | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR, ichndm, CUBCON | MEANOP, WNSTOR, STEQEQ, ELDENS, OPACF0, CONVEC, MEANOPT, CONOUT, CUBIC | 📁 | | ⬜ |
| convc1.f | CONVC1 | SUBROUTINE | | BASICS, CUBCON | TRMDER, TRMDRT | | | ⬜ |
| convec.f | CONVEC | SUBROUTINE | | BASICS, CUBCON | TRMDER, TRMDRT | | | ⬜ |
| coolrt.f | COOLRT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT, COOLCO | RTEFR1, OPACFA | 📁 | | ⬜ |
| coolrt.f | COOLRT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT, COOLCO | OPACFA, RTEFR1 | 📁 | | ⬜ |
| corrwm.f | CORRWM | SUBROUTINE | | BASICS, ATOMIC, MODELQ | QUIT | 📁 | | ⬜ |
| cross.f | CROSS | FUNCTION | | BASICS, ATOMIC, MODELQ | CROSS | | cross.rs | ✅ |
| crossd.f | CROSSD | FUNCTION | | BASICS, ATOMIC, MODELQ | CROSSD | | cross.rs | ✅ |
| cspec.f | CSPEC | SUBROUTINE | | BASICS, ATOMIC | QUIT | | | ⬜ |
| ctdata.f | CTDATA | BLOCK DATA | | CTRecomb, CTIon | | | ctdata.rs | ✅ |
| ctdata.f | CTDATA | BLOCK DATA | | CTIon, CTRecomb | | | ctdata.rs | ✅ |
| cubic.f | CUBIC | SUBROUTINE | | BASICS, CUBCON | | | cubic.rs | ✅ |
| dielrc.f | DIELRC | SUBROUTINE | ✓ | | | | dielrc.rs | ✅ |
| dietot.f | DIETOT | SUBROUTINE | | BASICS, ATOMIC, MODELQ | DIELRC | 📁 | | ⬜ |
@ -104,9 +104,9 @@
| 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 | 📁 | | ⬜ |
| elcor.f | ELCOR | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ADCHAR | WNSTOR, STEQEQ, STATE, MOLEQ | 📁 | | ⬜ |
| eldenc.f | ELDENC | SUBROUTINE | | BASICS, MODELQ, ATOMIC, eletab, hmolab, eospar | RHONEN, STATE, MOLEQ | 📁 | | ⬜ |
| eldens.f | ELDENS | SUBROUTINE | | BASICS, MODELQ, ATOMIC, terden, eospar | ENTENE, STATE, MPARTF, LINEQS, MOLEQ | 📁 | | ⬜ |
| emat.f | EMAT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR | | | emat.rs | ✅ |
| entene.f | ENTENE | SUBROUTINE | | BASICS, ATOMIC, MODELQ | MPARTF | | | ⬜ |
| erfcin.f | ERFCIN | FUNCTION | ✓ | | ERFCIN | | erfcx.rs | ✅ |
@ -119,101 +119,101 @@
| 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 | | 📁 | | ⬜ |
| getlal.f | GETLAL | SUBROUTINE | | BASICS, callardc, callarda, calphatd, callardg, callardb, quasun | | 📁 | | ⬜ |
| getwrd.f | GETWRD | SUBROUTINE | ✓ | | | | getwrd.rs | ✅ |
| gfree0.f | GFREE0 | SUBROUTINE | | BASICS, MODELQ | | | gfree.rs | ✅ |
| gfree1.f | GFREE1 | FUNCTION | | BASICS, MODELQ | GFREE1 | | gfree.rs | ✅ |
| gfreed.f | GFREED | SUBROUTINE | | BASICS, MODELQ | | | gfree.rs | ✅ |
| ghydop.f | GHYDOP | SUBROUTINE | | BASICS, MODELQ, ATOMIC, intcfg | | | | ⬜ |
| ghydop.f | GHYDOP | SUBROUTINE | | BASICS, MODELQ, ATOMIC, intcfg | | | ghydop.rs | ✅ |
| gntk.f | GNTK | FUNCTION | ✓ | | GNTK | | gntk.rs | ✅ |
| gomini.f | GOMINI | SUBROUTINE | | BASICS, MODELQ, intcfg | | 📁 | | ⬜ |
| grcor.f | GRCOR | SUBROUTINE | ✓ | | | | grcor.rs | ✅ |
| greyd.f | GREYD | SUBROUTINE | | BASICS, MODELQ, ATOMIC, ALIPAR | MEANOP, RHONEN, STEQEQ, WNSTOR, OPACF0 | 📁 | | ⬜ |
| greyd.f | GREYD | SUBROUTINE | | BASICS, MODELQ, ATOMIC, ALIPAR | WNSTOR, RHONEN, STEQEQ, OPACF0, MEANOP | 📁 | | ⬜ |
| gridp.f | GRIDP | SUBROUTINE | ✓ | BASICS | | | gridp.rs | ✅ |
| h2minus.f | H2MINUS | SUBROUTINE | | BASICS | LOCATE | 📁 | | ⬜ |
| hction.f | HCTION | FUNCTION | | CTIon, CTRTEMP | HCTION | | ctdata.rs | ✅ |
| hctrecom.f | HCTRECOM | FUNCTION | | CTRecomb, CTRTEMP | HCTRECOM | | ctdata.rs | ✅ |
| hction.f | HCTION | FUNCTION | | CTRTEMP, CTIon | HCTION | | ctdata.rs | ✅ |
| hctrecom.f | HCTRECOM | FUNCTION | | CTRTEMP, CTRecomb | HCTRECOM | | ctdata.rs | ✅ |
| hedif.f | HEDIF | SUBROUTINE | | BASICS, MODELQ, ATOMIC, hediff | | 📁 | | ⬜ |
| hephot.f | HEPHOT | FUNCTION | ✓ | | HEPHOT | | hephot.rs | ✅ |
| hesol6.f | HESOL6 | SUBROUTINE | | BASICS, MODELQ, PRSAUX | MATINV | | | ⬜ |
| hesolv.f | HESOLV | SUBROUTINE | | BASICS, MODELQ, PRSAUX | MATINV, STEQEQ, RHONEN, WNSTOR | 📁 | | ⬜ |
| hesolv.f | HESOLV | SUBROUTINE | | BASICS, MODELQ, PRSAUX | MATINV, RHONEN, STEQEQ, WNSTOR | 📁 | | ⬜ |
| hidalg.f | HIDALG | FUNCTION | ✓ | | HIDALG | | hidalg.rs | ✅ |
| ijali2.f | IJALI2 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | QUIT | 📁 | | ⬜ |
| ijalis.f | IJALIS | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | 📁 | | ⬜ |
| incldy.f | INCLDY | SUBROUTINE | | BASICS, ATOMIC, MODELQ | RATMAT, LEVSOL, QUIT, WNSTOR, SABOLF | 📁 | | ⬜ |
| incldy.f | INCLDY | SUBROUTINE | | BASICS, ATOMIC, MODELQ | LEVSOL, RATMAT, WNSTOR, QUIT, SABOLF | 📁 | | ⬜ |
| indexx.f | INDEXX | SUBROUTINE | ✓ | | | | indexx.rs | ✅ |
| inicom.f | INICOM | SUBROUTINE | | BASICS, ATOMIC, MODELQ, comgfs | | | inicom.rs | ✅ |
| inifrc.f | INIFRC | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ijflar | INDEXX | 📁 | | ⬜ |
| inifrs.f | INIFRS | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | QUIT, INDEXX | 📁 | | ⬜ |
| inifrs.f | INIFRS | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | INDEXX, QUIT | 📁 | | ⬜ |
| inifrt.f | INIFRT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ijflar | INDEXX | 📁 | | ⬜ |
| inilam.f | INILAM | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ALIPAR | 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 | | 📁 | | ⬜ |
| inilam.f | INILAM | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ALIPAR | OPACF1, RTEFR1, COLIS, OPAINI, WNSTOR, STEQEQ, SABOLF, ELCOR, RATES1 | | | ⬜ |
| initia.f | INITIA | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ODFPAR, ALIPAR, STRPAR, INUNIT, freqcl | RDATA, RDATAX, OPADD0, STATE, DOPGAM, ODFHYS, INTERP, LINSET, NSTPAR, QUIT, LINSPL, INIFRC, READBF | 📁 | | ⬜ |
| inkul.f | INKUL | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, COLKUR, LINED | | 📁 | | ⬜ |
| inpdis.f | INPDIS | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ODFPAR, ALIPAR, relcor | GRCOR | 📁 | | ⬜ |
| inpmod.f | INPMOD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, eospar | RATMAT, LEVSOL, QUIT, WNSTOR, SABOLF, KURUCZ, MOLEQ, INCLDY | 📁 | | ⬜ |
| inpmod.f | INPMOD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, eospar | LEVSOL, RATMAT, WNSTOR, QUIT, SABOLF, MOLEQ, KURUCZ, INCLDY | 📁 | | ⬜ |
| interp.f | INTERP | SUBROUTINE | ✓ | BASICS | | | interp.rs | ✅ |
| inthyd.f | INTHYD | SUBROUTINE | | BASICS, MODELQ | DIVSTR | | inthyd.rs | ✅ |
| intlem.f | INTLEM | SUBROUTINE | | BASICS, MODELQ | INTHYD | | intlem.rs | ✅ |
| intxen.f | INTXEN | SUBROUTINE | | BASICS, MODELQ | | | intxen.rs | ✅ |
| irc.f | IRC | SUBROUTINE | ✓ | | EXPINX, SZIRC | | irc.rs | ✅ |
| iroset.f | IROSET | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, LINED | INKUL, QUIT, LEVCD | 📁 | | ⬜ |
| kurucz.f | KURUCZ | SUBROUTINE | | BASICS, ATOMIC, MODELQ, temlim | RHONEN, RATMAT, LEVSOL, QUIT, WNSTOR, SABOLF, MOLEQ | 📁 | | ⬜ |
| iroset.f | IROSET | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, LINED | LEVCD, QUIT, INKUL | 📁 | | ⬜ |
| kurucz.f | KURUCZ | SUBROUTINE | | BASICS, ATOMIC, MODELQ, temlim | LEVSOL, RATMAT, WNSTOR, RHONEN, QUIT, SABOLF, MOLEQ | 📁 | | ⬜ |
| lagran.f | LAGRAN | SUBROUTINE | ✓ | | | | interpolate.rs | ✅ |
| laguer.f | LAGUER | SUBROUTINE | | | | 📁 | laguer.rs | ✅ |
| lemini.f | LEMINI | SUBROUTINE | | BASICS, MODELQ | | 📁 | | ⬜ |
| levcd.f | LEVCD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, COLKUR | QUIT, INDEXX | 📁 | | ⬜ |
| levgrp.f | LEVGRP | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | | | | ⬜ |
| levcd.f | LEVCD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, COLKUR | INDEXX, QUIT | 📁 | | ⬜ |
| levgrp.f | LEVGRP | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | | | levgrp.rs | ✅ |
| levset.f | LEVSET | SUBROUTINE | | BASICS, ATOMIC, MODELQ | QUIT | | | ⬜ |
| levsol.f | LEVSOL | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | LINEQS | | levsol.rs | ✅ |
| lineqs.f | LINEQS | SUBROUTINE | ✓ | BASICS | | | lineqs.rs | ✅ |
| linpro.f | LINPRO | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, quasun | 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 | | | | ⬜ |
| linpro.f | LINPRO | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, quasun | STARK0, DOPGAM, INTLEM, DIVSTR, INTXEN | | | ⬜ |
| linsel.f | LINSEL | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR | OPAINI, QUIT, OPACF1, RTEFR1 | 📁 | | ⬜ |
| linset.f | LINSET | SUBROUTINE | | BASICS, ATOMIC, MODELQ | IJALIS, STARK0, QUIT, DIVSTR | 📁 | | ⬜ |
| linspl.f | LINSPL | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | linspl.rs | ✅ |
| locate.f | LOCATE | SUBROUTINE | ✓ | | | | locate.rs | ✅ |
| ltegr.f | LTEGR | SUBROUTINE | | BASICS, ATOMIC, MODELQ | 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 | 📁 | | ⬜ |
| ltegr.f | LTEGR | SUBROUTINE | | BASICS, ATOMIC, MODELQ | INTERP, WNSTOR, STEQEQ, QUIT, ROSSOP, CONOUT | 📁 | | ⬜ |
| ltegrd.f | LTEGRD | SUBROUTINE | | BASICS, MODELQ, FACTRS, CUBCON, TOTJHK, FLXAUX, PRSAUX | TEMPER, INTERP, ZMRHO, WNSTOR, STEQEQ, ELDENS, QUIT, CONOUT | 📁 | | ⬜ |
| lucy.f | LUCY | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ITERAT, ALIPAR, ARRAY1 | OPACFL, RTEFR1, COLIS, WNSTOR, STEQEQ, SABOLF, ELCOR, OPAINI | 📁 | | ⬜ |
| lymlin.f | LYMLIN | SUBROUTINE | | BASICS, ATOMIC, MODELQ | STARK0, DIVSTR | 📁 | | ⬜ |
| matcon.f | MATCON | SUBROUTINE | | BASICS, MODELQ, ARRAY1, CUBCON | CONVEC | | | ⬜ |
| matgen.f | MATGEN | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR | BRE, BRTE, BREZ, BHEZ, BHED, MATCON, EMAT, SABOLF, BRTEZ, BHE, BPOP | | | ⬜ |
| matgen.f | MATGEN | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR | BHED, BRTE, BHEZ, BREZ, MATCON, BRTEZ, EMAT, BHE, SABOLF, BPOP, BRE | | | ⬜ |
| matinv.f | MATINV | SUBROUTINE | ✓ | BASICS | | | matinv.rs | ✅ |
| meanop.f | MEANOP | SUBROUTINE | | BASICS, MODELQ, ATOMIC | | | meanop.rs | ✅ |
| meanopt.f | MEANOPT | SUBROUTINE | | BASICS, MODELQ | OPCTAB | | | ⬜ |
| minv3.f | MINV3 | SUBROUTINE | ✓ | | | | minv3.rs | ✅ |
| moleq.f | MOLEQ | SUBROUTINE | | BASICS, MODELQ, ATOMIC, ioniz2, COMFH1, hmolab, eospar, entrop, terden, adchar, moldat | MPARTF, RUSSEL | 📁 | | ⬜ |
| moleq.f | MOLEQ | SUBROUTINE | | BASICS, MODELQ, ATOMIC, COMFH1, entrop, moldat, eospar, adchar, ioniz2, terden, hmolab | RUSSEL, MPARTF | 📁 | | ⬜ |
| mpartf.f | MPARTF | SUBROUTINE | | moldat | | 📁 | | ⬜ |
| newdm.f | NEWDM | SUBROUTINE | | BASICS, MODELQ, PRSAUX, FLXAUX, FACTRS | TEMPER, INTERP | 📁 | | ⬜ |
| newdmt.f | NEWDMT | SUBROUTINE | | BASICS, MODELQ, PRSAUX, FLXAUX, FACTRS | TEMPER, GRIDP, INTERP | 📁 | | ⬜ |
| newdm.f | NEWDM | SUBROUTINE | | BASICS, MODELQ, FACTRS, PRSAUX, FLXAUX | INTERP, TEMPER | 📁 | | ⬜ |
| newdmt.f | NEWDMT | SUBROUTINE | | BASICS, MODELQ, FACTRS, PRSAUX, FLXAUX | GRIDP, INTERP, TEMPER | 📁 | | ⬜ |
| newpop.f | NEWPOP | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | | 📁 | | ⬜ |
| nstout.f | NSTOUT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ODFPAR, ALIPAR | QUIT | 📁 | | ⬜ |
| nstpar.f | NSTPAR | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ODFPAR, ALIPAR, 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 | 📁 | | ⬜ |
| nstpar.f | NSTPAR | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ODFPAR, ALIPAR, imucnn, temlim, adiaba, ifpzpa, derdif, ichndm, ipricr, deridt, hediff, FLXAUX, irwint, moldat, quasun, freqcl, icnrsp | QUIT, GETWRD | 📁 | | ⬜ |
| odf1.f | ODF1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | DWNFR, ODFHST, DIVSTR | 📁 | | ⬜ |
| odffr.f | ODFFR | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | QUIT | | | ⬜ |
| odfhst.f | ODFHST | SUBROUTINE | | BASICS, MODELQ, ODFPAR | | | odfhst.rs | ✅ |
| odfhyd.f | ODFHYD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | DIVSTR, INDEXX, ODFHST | | | ⬜ |
| odfhys.f | ODFHYS | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | STARK0, ODFFR, IJALIS | | | ⬜ |
| odfhyd.f | ODFHYD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | INDEXX, ODFHST, DIVSTR | | | ⬜ |
| odfhys.f | ODFHYS | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | IJALIS, STARK0, ODFFR | | | ⬜ |
| odfmer.f | ODFMER | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR | ODFHYD | | | ⬜ |
| odfset.f | ODFSET | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, STFCR | 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 | | | ⬜ |
| odfset.f | ODFSET | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, STFCR | IJALIS, QUIT | 📁 | | ⬜ |
| opacf0.f | OPACF0 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, hmolab | GFREE0, DWNFR1, DWNFR0, WNSTOR, LINPRO, SABOLF, SGMER1, OPADD, OPACT1 | | | ⬜ |
| opacf1.f | OPACF1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ipricr, hmolab | QUASIM, GHYDOP, DWNFR1, LYMLIN, SGMER1, OPADD, PRD, OPACT1 | 📁 | | ⬜ |
| opacfa.f | OPACFA | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, COOLCO | SGMER1, OPADD, PRD, DWNFR1 | | | ⬜ |
| opacfd.f | OPACFD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT, dsctva, hmolab, rhoder | OPACTD, QUASIM, DWNFR1, LYMLIN, OPCTAB, SGMER1, OPADD, PRD, GFREED | 📁 | | ⬜ |
| opacfl.f | OPACFL | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR | SGMER1, OPADD, DWNFR1 | | | ⬜ |
| opact1.f | OPACT1 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, hmolab | OPCTAB | | | ⬜ |
| opactd.f | OPACTD | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ARRAY1, ITERAT, 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 | | | ⬜ |
| opactd.f | OPACTD | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ARRAY1, ITERAT, dsctva, hmolab, rhoder | OPCTAB | | | ⬜ |
| opactr.f | OPACTR | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ATOMIC, dsctva, hmolab, grdpra | RATMAL, OPACF1, LEVSOL, WNSTOR, STEQEQ, ELDENS, SABOLF, OPAINI, PGSET | | | ⬜ |
| opadd.f | OPADD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, eospar | CIA_H2H, CIA_H2HE, CIA_HHE, H2MINUS, CIA_H2H2 | | | ⬜ |
| opadd0.f | OPADD0 | SUBROUTINE | | BASICS, ATOMIC, MODELQ | QUIT | | | ⬜ |
| opahst.f | OPAHST | SUBROUTINE | | BASICS, ODFPAR | STARK0 | 📁 | | ⬜ |
| opaini.f | OPAINI | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR | REFLEV, WNSTOR, SABOLF, DWNFR0, LINPRO, LEVGRP | | | ⬜ |
| opaini.f | OPAINI | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR | DWNFR0, REFLEV, WNSTOR, LINPRO, SABOLF, LEVGRP | | | ⬜ |
| opctab.f | OPCTAB | SUBROUTINE | | BASICS, MODELQ | RAYLEIGH | | | ⬜ |
| opdata.f | OPDATA | SUBROUTINE | | TOPB | | 📁 | | ⬜ |
| opfrac.f | OPFRAC | SUBROUTINE | | pfoptb | | 📁 | | ⬜ |
| osccor.f | OSCCOR | SUBROUTINE | | BASICS, MODELQ, ITERAT | | 📁 | | ⬜ |
| outpri.f | OUTPRI | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, grdpra | OPACF1, LEVSOL, WNSTOR, SABOLF, RATMAL | 📁 | | ⬜ |
| outpri.f | OUTPRI | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, grdpra | RATMAL, OPACF1, LEVSOL, WNSTOR, SABOLF | 📁 | | ⬜ |
| output.f | OUTPUT | SUBROUTINE | | BASICS, MODELQ | | 📁 | | ⬜ |
| partf.f | PARTF | SUBROUTINE | | BASICS, PFSTDS, irwint | PFNI, OPFRAC, PFHEAV, PFCNO, PFSPEC, MPARTF, PFFE | | | ⬜ |
| partf.f | PARTF | SUBROUTINE | | BASICS, irwint, PFSTDS | PFFE, PFNI, OPFRAC, MPARTF, PFHEAV, PFSPEC, PFCNO | | | ⬜ |
| pfcno.f | PFCNO | SUBROUTINE | ✓ | BASICS | | | pfcno.rs | ✅ |
| pffe.f | PFFE | SUBROUTINE | ✓ | | | | pffe.rs | ✅ |
| pfheav.f | PFHEAV | SUBROUTINE | | | | 📁 | | ⬜ |
@ -223,59 +223,59 @@
| 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 | 📁 | | ⬜ |
| princ.f | PRINC | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR | LINPRO, DWNFR, OPACF1, SABOLF | 📁 | | ⬜ |
| prnt.f | PRNT | SUBROUTINE | | BASICS, ATOMIC, MODELQ | SABOLF | 📁 | | ⬜ |
| profil.f | PROFIL | FUNCTION | | BASICS, ATOMIC, MODELQ, quasun | STARK0, PROFIL, DIVSTR | | | ⬜ |
| profsp.f | PROFSP | FUNCTION | | BASICS, ATOMIC, MODELQ | SABOLF, PROFSP | | | ⬜ |
| prsent.f | PRSENT | SUBROUTINE | | tdedge, tdflag, THERM, TABLTD | | 📁 | | ⬜ |
| profil.f | PROFIL | FUNCTION | | BASICS, ATOMIC, MODELQ, quasun | PROFIL, STARK0, DIVSTR | | profil.rs | ✅ |
| profsp.f | PROFSP | FUNCTION | | BASICS, ATOMIC, MODELQ | PROFSP, SABOLF | | | ⬜ |
| prsent.f | PRSENT | SUBROUTINE | | tdflag, tdedge, THERM, TABLTD | | 📁 | | ⬜ |
| psolve.f | PSOLVE | SUBROUTINE | | BASICS, MODELQ | | | psolve.rs | ✅ |
| pzert.f | PZERT | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | | ⬜ |
| pzert.f | PZERT | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | pzert.rs | ✅ |
| pzeval.f | PZEVAL | SUBROUTINE | | BASICS, MODELQ, ALIPAR, icnrsp | CONOUT | 📁 | | ⬜ |
| pzevld.f | PZEVLD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR, ARRAY1, PRSAUX, DEPTDR, ifpzpa, grdpra | | | | ⬜ |
| pzevld.f | PZEVLD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR, ARRAY1, ifpzpa, PRSAUX, grdpra, DEPTDR | | | pzevld.rs | ✅ |
| quartc.f | QUARTC | SUBROUTINE | | | | 📁 | quartc.rs | ✅ |
| quasim.f | QUASIM | SUBROUTINE | | BASICS, ATOMIC, MODELQ, quasun | ALLARD | | | ⬜ |
| quit.f | QUIT | SUBROUTINE | | | | 📁 | quit.rs | ✅ |
| radpre.f | RADPRE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR | RTEFR1, QUIT, OPACF1, INDEXX | 📁 | | ⬜ |
| radtot.f | RADTOT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ITERAT, OPTDPT, SURFEX, TOTJHK | OPAINI, RTEFR1, OPACF1 | | | ⬜ |
| radpre.f | RADPRE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR | QUIT, INDEXX, OPACF1, RTEFR1 | 📁 | | ⬜ |
| radtot.f | RADTOT | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ITERAT, OPTDPT, TOTJHK, SURFEX | OPAINI, OPACF1, RTEFR1 | | | ⬜ |
| raph.f | RAPH | FUNCTION | ✓ | | RAPH | | raph.rs | ✅ |
| rates1.f | RATES1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ITERAT | ROSSTD, RTEFR1, OPACF1 | | | ⬜ |
| rates1.f | RATES1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ITERAT | ROSSTD, OPACF1, RTEFR1 | | | ⬜ |
| ratmal.f | RATMAL | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | | ratmal.rs | ✅ |
| ratmat.f | RATMAT | SUBROUTINE | | BASICS, ATOMIC, MODELQ | REFLEV | | | ⬜ |
| ratsp1.f | RATSP1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT | ROSSTD, RTEFR1, OPACF1 | 📁 | | ⬜ |
| ratsp1.f | RATSP1 | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR, ARRAY1, ITERAT | ROSSTD, OPACF1, RTEFR1 | 📁 | | ⬜ |
| rayini.f | RAYINI | SUBROUTINE | | BASICS, MODELQ, ATOMIC | RAYLEIGH | 📁 | | ⬜ |
| rayleigh.f | RAYLEIGH | SUBROUTINE | | BASICS, ATOMIC, MODELQ, RAYSCT, eospar | | | rayleigh.rs | ✅ |
| rayset.f | RAYSET | SUBROUTINE | | BASICS, MODELQ | | | rayset.rs | ✅ |
| rdata.f | RDATA | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ODFPAR, ALIPAR, STRPAR, imodlc, INUNIT | LINSET, QUIT, RDATAX, DOPGAM | 📁 | | ⬜ |
| rdata.f | RDATA | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ODFPAR, ALIPAR, STRPAR, INUNIT, imodlc | RDATAX, LINSET, QUIT, DOPGAM | 📁 | | ⬜ |
| rdatax.f | RDATAX | SUBROUTINE | | BASICS, ATOMIC, MODELQ | BKHSGO | 📁 | | ⬜ |
| readbf.f | READBF | SUBROUTINE | | BASICS | | 📁 | | ⬜ |
| rechck.f | RECHCK | SUBROUTINE | | BASICS, ATOMIC, MODELQ | RTEFR1, OPACF1 | 📁 | | ⬜ |
| rechck.f | RECHCK | SUBROUTINE | | BASICS, ATOMIC, MODELQ | OPACF1, RTEFR1 | 📁 | | ⬜ |
| reflev.f | REFLEV | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | | | | ⬜ |
| reiman.f | REIMAN | FUNCTION | ✓ | | REIMAN | | reiman.rs | ✅ |
| resolv.f | RESOLV | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ALIPAR, ARRAY1, icnrsp | RTEFR1, ELCOR, STEQEQ, NEWPOP, OPAINI, ROSSTD, CONOUT, TIMING, PRD, TAUFR1, RATES1, OPACF1 | 📁 | | ⬜ |
| resolv.f | RESOLV | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ALIPAR, ARRAY1, icnrsp | NEWPOP, TAUFR1, TIMING, OPACF1, RTEFR1, ROSSTD, STEQEQ, CONOUT, ELCOR, OPAINI, RATES1, PRD | 📁 | | ⬜ |
| rhoeos.f | RHOEOS | FUNCTION | | BASICS, MODELQ | PRSENT, RHOEOS | | | ⬜ |
| rhonen.f | RHONEN | SUBROUTINE | | BASICS, MODELQ | ELDENS | | | ⬜ |
| rhsgen.f | RHSGEN | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, CUBCON | STATE, RATMAT, MATINV, SABOLF, CONVEC, COMPT0, LEVGRP | | | ⬜ |
| rossop.f | ROSSOP | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR | MEANOP, STEQEQ, WNSTOR, OPACF0, MEANOPT, ELDENS | | | ⬜ |
| rhsgen.f | RHSGEN | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ARRAY1, ALIPAR, CUBCON | MATINV, STATE, RATMAT, COMPT0, SABOLF, CONVEC, LEVGRP | | | ⬜ |
| rossop.f | ROSSOP | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ALIPAR | WNSTOR, STEQEQ, ELDENS, OPACF0, MEANOP, MEANOPT | | | ⬜ |
| rosstd.f | ROSSTD | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, ALIPAR | | 📁 | | ⬜ |
| rte_sc.f | RTE_SC | SUBROUTINE | ✓ | BASICS | | | rte_sc.rs | ✅ |
| rteang.f | RTEANG | SUBROUTINE | | BASICS, MODELQ, ALIPAR, SURFEX, EXTINT | GAULEG | | | ⬜ |
| rteang.f | RTEANG | SUBROUTINE | | BASICS, MODELQ, ALIPAR, EXTINT, SURFEX | GAULEG | | | ⬜ |
| rtecf0.f | RTECF0 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT, auxcbc, AUXRTE | | | | ⬜ |
| rtecf1.f | RTECF1 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, 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 | | | | ⬜ |
| rtecf1.f | RTECF1 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT, comgfs, AUXRTE, EXTINT, SURFEX | RTEFE2, RTESOL, RTECF0 | 📁 | | ⬜ |
| rtecmc.f | RTECMC | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, comgfs, AUXRTE | MATINV, OPACF1, RTECF0 | | | ⬜ |
| rtecmu.f | RTECMU | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT, AUXRTE | RTESOL, OPACF1, RTECF0, GAULEG | 📁 | | ⬜ |
| rtecom.f | RTECOM | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT, comgfs, AUXRTE | OPACF1, RTECF0, RTECF1 | | | ⬜ |
| rtedf1.f | RTEDF1 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, OPTDPT | | | rtedf1.rs | ✅ |
| rtedf2.f | RTEDF2 | SUBROUTINE | | BASICS, MODELQ, ALIPAR | | | | ⬜ |
| rtefe2.f | RTEFE2 | SUBROUTINE | ✓ | BASICS | | | rtefe2.rs | ✅ |
| rtefr1.f | RTEFR1 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT | RTEDF2, MATINV, RTECF1, RTEDF1, MINV3, RTESOL | 📁 | | ⬜ |
| rtefr1.f | RTEFR1 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT | MATINV, RTESOL, RTEDF1, RTECF1, RTEDF2, MINV3 | 📁 | | ⬜ |
| rteint.f | RTEINT | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT | MATINV, OPACF1 | 📁 | | ⬜ |
| rtesol.f | RTESOL | SUBROUTINE | ✓ | BASICS | | | rtesol.rs | ✅ |
| russel.f | RUSSEL | SUBROUTINE | | BASICS, MODELQ, COMFH1 | MPARTF | 📁 | | ⬜ |
| rybchn.f | RYBCHN | SUBROUTINE | | BASICS, ITERAT, MODELQ, ALIPAR, ARRAY1, rybpgs, grdpra | PGSET, ELDENS | 📁 | | ⬜ |
| rybene.f | RYBENE | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ARRAY1, CUBCON, deridt, RYBMTX | CONVEC | | | ⬜ |
| rybheq.f | RYBHEQ | SUBROUTINE | | BASICS, MODELQ, rybpgs, grdpra | RTEFR1, ELCOR, STEQEQ, WNSTOR, OPAINI, PGSET, ELDENS, OPACF1 | 📁 | | ⬜ |
| rybene.f | RYBENE | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ARRAY1, RYBMTX, deridt, CUBCON | CONVEC | | | ⬜ |
| rybheq.f | RYBHEQ | SUBROUTINE | | BASICS, MODELQ, rybpgs, grdpra | OPACF1, RTEFR1, WNSTOR, STEQEQ, ELDENS, ELCOR, OPAINI, PGSET | 📁 | | ⬜ |
| rybmat.f | RYBMAT | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ARRAY1, dsctva, RYBMTX | | | | ⬜ |
| rybsol.f | RYBSOL | SUBROUTINE | | BASICS, MODELQ, ATOMIC, ALIPAR, ARRAY1, ITERAT, RYBMTX, imodlc | RTEFR1, ALIFR1, TRIDAG, STEQEQ, ROSSTD, RYBMAT, OPACTR, OPACFD, RYBCHN, LINEQS | 📁 | | ⬜ |
| rybsol.f | RYBSOL | SUBROUTINE | | BASICS, MODELQ, ATOMIC, ALIPAR, ARRAY1, ITERAT, imodlc, RYBMTX | RYBMAT, RTEFR1, ROSSTD, OPACTR, LINEQS, STEQEQ, ALIFR1, TRIDAG, RYBCHN, OPACFD | 📁 | | ⬜ |
| sabolf.f | SABOLF | SUBROUTINE | | BASICS, ATOMIC, MODELQ | PARTF | | | ⬜ |
| sbfch.f | SBFCH | FUNCTION | ✓ | | SBFCH | | sbfch.rs | ✅ |
| sbfhe1.f | SBFHE1 | FUNCTION | | BASICS, ATOMIC | SBFHE1, QUIT | 📁 | sbfhe1.rs | ✅ |
@ -283,7 +283,7 @@
| 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 | 📁 | | ⬜ |
| settrm.f | SETTRM | SUBROUTINE | | tdflag, tdedge, THERM, TABLTD | PRSENT | 📁 | | ⬜ |
| sffhmi.f | SFFHMI | FUNCTION | ✓ | | SFFHMI | | sffhmi.rs | ✅ |
| sffhmi_add.f | SFFHMI_ADD | FUNCTION | ✓ | | SFFHMI_ADD | | sffhmi_add.rs | ✅ |
| sghe12.f | SGHE12 | FUNCTION | ✓ | | SGHE12 | | sghe12.rs | ✅ |
@ -291,25 +291,25 @@
| 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 | | | ⬜ |
| sigk.f | SIGK | FUNCTION | | BASICS, ATOMIC | SPSIGK, SIGK | | | ⬜ |
| sigmar.f | SIGMAR | FUNCTION | | BASICS | SIGMAR, LAGUER | 📁 | | ⬜ |
| solve.f | SOLVE | SUBROUTINE | | BASICS, ITERAT, MODELQ, ARRAY1, ALIPAR, CMATZD | MATINV, WNSTOR, MATGEN, RHSGEN, PRCHAN | 📁 | | ⬜ |
| solves.f | SOLVES | SUBROUTINE | | BASICS, ITERAT, MODELQ, ARRAY1, ALIPAR, CMATZD, STOMAT | MATINV, WNSTOR, MATGEN, RHSGEN, PRCHAN | 📁 | | ⬜ |
| solve.f | SOLVE | SUBROUTINE | | BASICS, ITERAT, MODELQ, ARRAY1, ALIPAR, CMATZD | MATINV, PRCHAN, RHSGEN, WNSTOR, MATGEN | 📁 | | ⬜ |
| solves.f | SOLVES | SUBROUTINE | | BASICS, ITERAT, MODELQ, ARRAY1, ALIPAR, STOMAT, CMATZD | MATINV, PRCHAN, RHSGEN, WNSTOR, MATGEN | 📁 | | ⬜ |
| spsigk.f | SPSIGK | SUBROUTINE | ✓ | | CARBON | | spsigk.rs | ✅ |
| srtfrq.f | SRTFRQ | SUBROUTINE | | BASICS, ATOMIC, MODELQ | QUIT, INDEXX | 📁 | | ⬜ |
| srtfrq.f | SRTFRQ | SUBROUTINE | | BASICS, ATOMIC, MODELQ | INDEXX, QUIT | 📁 | | ⬜ |
| stark0.f | STARK0 | SUBROUTINE | ✓ | | | | stark0.rs | ✅ |
| starka.f | STARKA | FUNCTION | | BASICS, MODELQ | STARKA | | starka.rs | ✅ |
| start.f | START | SUBROUTINE | | BASICS, hediff | | 📁 | | ⬜ |
| state.f | STATE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, PFSTDS, terden | PARTF, OPFRAC | 📁 | | ⬜ |
| steqeq.f | STEQEQ | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, POPSTR, PPAPAR | MOLEQ, LEVSOL, SABOLF, RATMAT | | | ⬜ |
| state.f | STATE | SUBROUTINE | | BASICS, ATOMIC, MODELQ, terden, PFSTDS | OPFRAC, PARTF | 📁 | | ⬜ |
| steqeq.f | STEQEQ | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT, POPSTR, PPAPAR | LEVSOL, RATMAT, SABOLF, MOLEQ | | | ⬜ |
| switch.f | SWITCH | SUBROUTINE | | BASICS, ATOMIC, MODELQ | | 📁 | | ⬜ |
| szirc.f | SZIRC | SUBROUTINE | ✓ | | EINT | | szirc.rs | ✅ |
| tabini.f | TABINI | SUBROUTINE | | BASICS, MODELQ, ATOMIC, intcff, abntab, eletab | | 📁 | | ⬜ |
| tabini.f | TABINI | SUBROUTINE | | BASICS, MODELQ, ATOMIC, eletab, abntab, intcff | | 📁 | | ⬜ |
| tabint.f | TABINT | SUBROUTINE | | BASICS, MODELQ, ATOMIC, intcff | | | | ⬜ |
| taufr1.f | TAUFR1 | SUBROUTINE | | BASICS, MODELQ, ALIPAR, ITERAT, OPTDPT | | | | ⬜ |
| tdpini.f | TDPINI | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ODFPAR, ALIPAR | GFREE0 | | tdpini.rs | ✅ |
| temcor.f | TEMCOR | SUBROUTINE | | BASICS, MODELQ, ARRAY1, ALIPAR, CUBCON | MEANOP, STEQEQ, WNSTOR, OPACF0, CONVEC, ELDENS | 📁 | | ⬜ |
| temper.f | TEMPER | SUBROUTINE | | BASICS, MODELQ, ALIPAR, PRSAUX, FLXAUX, FACTRS | MEANOP, STEQEQ, WNSTOR, OPACF0, TLOCAL, MEANOPT, ELDENS | 📁 | | ⬜ |
| temcor.f | TEMCOR | SUBROUTINE | | BASICS, MODELQ, ARRAY1, ALIPAR, CUBCON | MEANOP, WNSTOR, STEQEQ, ELDENS, OPACF0, CONVEC | 📁 | | ⬜ |
| temper.f | TEMPER | SUBROUTINE | | BASICS, MODELQ, ALIPAR, FACTRS, PRSAUX, FLXAUX | WNSTOR, TLOCAL, STEQEQ, ELDENS, OPACF0, MEANOP, MEANOPT | 📁 | | ⬜ |
| timing.f | TIMING | SUBROUTINE | | | | 📁 | | ⬜ |
| tiopf.f | TIOPF | SUBROUTINE | ✓ | | | | tiopf.rs | ✅ |
| tlocal.f | TLOCAL | SUBROUTINE | | BASICS, MODELQ, FACTRS, FLXAUX | QUARTC | | | ⬜ |
@ -317,14 +317,14 @@
| 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 | | | ⬜ |
| trmder.f | TRMDER | SUBROUTINE | | BASICS, derdif, terden, adiaba | ELDENS | | | ⬜ |
| trmdrt.f | TRMDRT | SUBROUTINE | | BASICS, tdedge, CC, tdflag, CONVOUT | PRSENT | | | ⬜ |
| ubeta.f | UBETA | FUNCTION | ✓ | | UBETA, LAGRAN | | ubeta.rs | ✅ |
| vern16.f | VERN16 | FUNCTION | ✓ | BASICS | VERN16 | | vern16.rs | ✅ |
| vern18.f | VERN18 | FUNCTION | ✓ | BASICS | VERN18 | | vern18.rs | ✅ |
| vern20.f | VERN20 | FUNCTION | ✓ | BASICS | VERN20 | | vern20.rs | ✅ |
| vern26.f | VERN26 | FUNCTION | ✓ | BASICS | VERN26 | | vern26.rs | ✅ |
| verner.f | VERNER | FUNCTION | | BASICS, ATOMIC | VERNER, QUIT | | verner.rs | ✅ |
| verner.f | VERNER | FUNCTION | | BASICS, ATOMIC | QUIT, VERNER | | verner.rs | ✅ |
| visini.f | VISINI | SUBROUTINE | | BASICS, ATOMIC, MODELQ, ITERAT | | 📁 | | ⬜ |
| voigt.f | VOIGT | FUNCTION | ✓ | | VOIGT | | voigt.rs | ✅ |
| voigte.f | VOIGTE | FUNCTION | ✓ | | VOIGTE | | voigte.rs | ✅ |

110
MEMORY/MEMORY.md Normal file
View File

@ -0,0 +1,110 @@
# TLUSTY/SYNSPEC Rust 重构项目记忆
注意用中文回复
重要!!!:不要进行全量测试 `cargo test`,系统会卡死,需要时一个个来测
## 分离子模块(已经完成)
```bash
cd /home/fmq/program/tlusty/tl208-s54/rust
python3 extract_fortran.py tlusty/tlusty208.f tlusty/extracted/
cp tlusty/*.FOR tlusty/extracted/
```
分离后的子文件在tlusty/extracted/下
## common模块
common模块×.FOR已经重构到src/state目录下
## 重构追踪系统
```bash
# 更新追踪表 (必须按顺序执行)
python3 scripts/analyze_fortran.py > MEMORY/fortran_analysis.csv
python3 scripts/generate_tracking.py > MEMORY/FORTRAN_TRACKING.md
# 依赖树分析命令
python3 scripts/analyze_fortran.py --priority # 按未实现依赖排序的重构优先级只显示pending
python3 scripts/analyze_fortran.py --tree UNIT # 输出依赖树(含未实现依赖数)
python3 scripts/analyze_fortran.py --full # 输出完整传递依赖
```
| 指标 | 数量 |
|------|------|
| 总单元 | 305 |
| 已完成 | 107 |
| 待处理 | 198 |
| 完成率 | 35.1% |
## 重构流程
DATA 语句硬编码的数据表已经由scripts/extract_fortran_data.py生成到src/data.rs中
详细规范见 [MEMORY/REFACTORING_GUIDE.md](MEMORY/REFACTORING_GUIDE.md)
**步骤:**
1. `python3 scripts/analyze_fortran.py --priority` - 找未实现依赖少的函数
2. 分析tlusty/extracted/文件夹下对应的 Fortran 源码 (INCLUDE/COMMON/调用/I/O)
3. 创建 `src/math/TARGET.rs`
4. 实现函数 + 测试
5. 添加到 `mod.rs`
6. `cargo test target` (单个测试!)
7. 更新追踪表
**SPECIAL_MAPPINGS** (`scripts/analyze_fortran.py` 第 72 行):
```python
SPECIAL_MAPPINGS = {
'gfree': ['gfree0', 'gfreed', 'gfree1'],
'interpolate': ['yint', 'lagran'],
'sgmer': ['sgmer0', 'sgmer1', 'sgmerd'],
'ctdata': ['hction', 'hctrecom'],
'cross': ['cross', 'crossd'],
'expint': ['eint', 'expinx'],
'erfcx': ['erfcx', 'erfcin'],
'lineqs': ['lineqs', 'lineqs_nr'],
# 新增映射...
}
```
## 当前状态 (2026-03-20)
- **已完成**: 107 个函数 + 15 个状态模块
- **测试通过**: 421+ 个
- **最新完成**: alifrk.rs (Kantorovich 迭代简化版)
**查看实时进度**: `cat FORTRAN_TRACKING.md | head -20`
## 项目结构
```
rust/src/
├── math/ # 函数 (85+ 个 .rs 文件)
├── state/ # COMMON 块 (8 个模块)
│ ├── constants.rs # BASICS.FOR
│ ├── atomic.rs # ATOMIC.FOR
│ ├── model.rs # MODELQ.FOR
│ ├── arrays.rs # ARRAY1.FOR
│ ├── iterat.rs # ITERAT.FOR
│ ├── alipar.rs # ALIPAR.FOR
│ └── odfpar.rs # ODFPAR.FOR
└── data.rs # 静态数据
```
## 关键陷阱
| 问题 | 解决方案 |
|------|----------|
| Fortran 1-indexed | `arr(i)``arr[i-1]` |
| `-LOG(X)` | 是 `-ln(X)` 不是 `ln(-X)` |
| powi 类型歧义 | 用显式乘法 `(a)*(a)` |
| 循环变量变负 | 用 `isize` 不用 `usize` |
| 多项式近似精度 | 放宽到 1e-7 |
| 矩阵列优先 | `A(j,i)``a[(i-1)*N + (j-1)]` |
| COMMON 块冲突 | 添加前 grep 检查是否已存在 |
| 循环变量重赋值 | 声明为 `mut` |
## 详细文档
- [MEMORY/REFACTORING_GUIDE.md](MEMORY/REFACTORING_GUIDE.md) - 重构规范 ⭐
- [MEMORY/FORTRAN_TRACKING.md](MEMORY/FORTRAN_TRACKING.md) - 函数追踪表
- [MEMORY/refactoring_notes.md](MEMORY/refactoring_notes.md) - 重构过程中遇到的问题及解决方法

305
MEMORY/fortran_analysis.csv Normal file
View 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","RTEFR1|ALIFRK|ROSSTD|OPACF1",True,,pending
alisk2.f,ALISK2,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","RTEFR1|ALIFRK|ROSSTD|OPACF1",True,,pending
alist1.f,ALIST1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT","RTEFR1|ALIFR1|OPACFD|ROSSTD",True,,pending
alist2.f,ALIST2,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","QUIT|OPACFD|RTEFR1|ALIFR1|ROSSTD",True,,pending
allard.f,ALLARD,SUBROUTINE,False,"BASICS|callardb|callardc|callarda|quasun|callardg|calphatd","ALLARDT",True,,pending
allardt.f,ALLARDT,SUBROUTINE,False,"BASICS|calphatd","",False,src/math/allardt.rs,done
angset.f,ANGSET,SUBROUTINE,True,"BASICS","GAULEG",False,src/math/angset.rs,done
betah.f,BETAH,FUNCTION,True,"","",False,src/math/betah.rs,done
bhe.f,BHE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","",False,src/math/bhe.rs,done
bhed.f,BHED,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|CMATZD|SURFEX","",False,src/math/bhe.rs,done
bhez.f,BHEZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|SURFEX","",False,src/math/bhe.rs,done
bkhsgo.f,BKHSGO,SUBROUTINE,True,"","",False,src/math/bkhsgo.rs,done
bpop.f,BPOP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR|ITERAT","LEVGRP|BPOPE|RATMAT|BPOPT|MATINV|BPOPC|LEVSOL|BPOPF",False,,pending
bpopc.f,BPOPC,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR|ADCHAR","STATE",False,,pending
bpope.f,BPOPE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT|ARRAY1","DWNFR1|SGMER1",False,,pending
bpopf.f,BPOPF,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR","",False,src/math/bpopf.rs,done
bpopt.f,BPOPT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR","COLIS",False,,pending
bre.f,BRE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","COMPT0",False,,pending
brez.f,BREZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","COMPT0",False,,pending
brte.f,BRTE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1","COMPT0",False,,pending
brtez.f,BRTEZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1","COMPT0",False,,pending
butler.f,BUTLER,SUBROUTINE,True,"","",False,src/math/butler.rs,done
carbon.f,CARBON,SUBROUTINE,True,"","",False,src/math/carbon.rs,done
ceh12.f,CEH12,FUNCTION,True,"","",False,src/math/ceh12.rs,done
change.f,CHANGE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","READBF|STEQEQ",True,,pending
chckse.f,CHCKSE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","SABOLF",True,,pending
chctab.f,CHCTAB,SUBROUTINE,False,"BASICS|MODELQ|abntab","",True,,pending
cheav.f,CHEAV,FUNCTION,False,"BASICS|ATOMIC","QUIT",True,,pending
cheavj.f,CHEAVJ,FUNCTION,False,"BASICS|ATOMIC","QUIT",True,,pending
cia_h2h.f,CIA_H2H,SUBROUTINE,False,"","LOCATE|IF",True,,pending
cia_h2h2.f,CIA_H2H2,SUBROUTINE,False,"","LOCATE|IF",True,,pending
cia_h2he.f,CIA_H2HE,SUBROUTINE,False,"","LOCATE|IF",True,,pending
cia_hhe.f,CIA_HHE,SUBROUTINE,False,"","LOCATE|IF",True,,pending
cion.f,CION,FUNCTION,True,"","",False,src/math/cion.rs,done
ckoest.f,CKOEST,FUNCTION,True,"BASICS","",False,src/math/ckoest.rs,done
colh.f,COLH,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","IRC|CSPEC|BUTLER",False,,pending
colhe.f,COLHE,SUBROUTINE,False,"BASICS|ATOMIC","IRC|COLLHE|CSPEC",False,,pending
colis.f,COLIS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|CTRTEMP","IRC|COLHE|CSPEC|COLH",False,,pending
collhe.f,COLLHE,SUBROUTINE,True,"","",False,src/math/collhe.rs,done
column.f,COLUMN,SUBROUTINE,False,"BASICS|MODELQ|relcor","",True,,pending
compt0.f,COMPT0,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|auxcbc","",False,src/math/compt0.rs,done
comset.f,COMSET,SUBROUTINE,False,"BASICS|MODELQ|auxcbc|comgfs","",False,src/math/comset.rs,done
concor.f,CONCOR,SUBROUTINE,False,"BASICS|MODELQ","CONOUT",True,,pending
conout.f,CONOUT,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|CUBCON","MEANOPT|MEANOP|CONVEC|OPACF0",True,,pending
conref.f,CONREF,SUBROUTINE,False,"BASICS|MODELQ|ARRAY1|imucnn|CUBCON","WNSTOR|ELDENS|CONVC1|STEQEQ|CONOUT|CONVEC",True,,pending
contmd.f,CONTMD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|PRSAUX|CUBCON","MEANOP|CUBIC|OPACF0|STEQEQ|WNSTOR|CONOUT|CONVEC",True,,pending
contmp.f,CONTMP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ichndm|CUBCON","MEANOPT|MEANOP|CUBIC|OPACF0|STEQEQ|ELDENS|WNSTOR|CONOUT|CONVEC",True,,pending
convc1.f,CONVC1,SUBROUTINE,False,"BASICS|CUBCON","TRMDER|TRMDRT",False,,pending
convec.f,CONVEC,SUBROUTINE,False,"BASICS|CUBCON","TRMDER|TRMDRT",False,,pending
coolrt.f,COOLRT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT|COOLCO","RTEFR1|OPACFA",True,,pending
corrwm.f,CORRWM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT",True,,pending
cross.f,CROSS,FUNCTION,False,"BASICS|ATOMIC|MODELQ","",False,src/math/cross.rs,done
crossd.f,CROSSD,FUNCTION,False,"BASICS|ATOMIC|MODELQ","",False,src/math/cross.rs,done
cspec.f,CSPEC,SUBROUTINE,False,"BASICS|ATOMIC","QUIT",False,,pending
ctdata.f,CTDATA,BLOCK DATA,False,"CTIon|CTRecomb","",False,src/math/ctdata.rs,done
cubic.f,CUBIC,SUBROUTINE,False,"BASICS|CUBCON","",False,src/math/cubic.rs,done
dielrc.f,DIELRC,SUBROUTINE,True,"","",False,src/math/dielrc.rs,done
dietot.f,DIETOT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","DIELRC",True,,pending
divstr.f,DIVSTR,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/divstr.rs,done
dmder.f,DMDER,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|DEPTDR","",False,src/math/dmder.rs,done
dmeval.f,DMEVAL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ARRAY1","",True,,pending
dopgam.f,DOPGAM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","GAMSP",False,src/math/dopgam.rs,done
dwnfr.f,DWNFR,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/dwnfr.rs,done
dwnfr0.f,DWNFR0,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/dwnfr0.rs,done
dwnfr1.f,DWNFR1,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/dwnfr1.rs,done
eint.f,EINT,SUBROUTINE,True,"","EXPINX",False,src/math/expint.rs,done
elcor.f,ELCOR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ADCHAR","WNSTOR|STEQEQ|STATE|MOLEQ",True,,pending
eldenc.f,ELDENC,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|eospar|hmolab|eletab","RHONEN|STATE|MOLEQ",True,,pending
eldens.f,ELDENS,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|eospar|terden","ENTENE|LINEQS|MPARTF|STATE|MOLEQ",True,,pending
emat.f,EMAT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","",False,src/math/emat.rs,done
entene.f,ENTENE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","MPARTF",False,,pending
erfcin.f,ERFCIN,FUNCTION,True,"","",False,src/math/erfcx.rs,done
erfcx.f,ERFCX,FUNCTION,True,"","",False,src/math/erfcx.rs,done
expint.f,EXPINT,FUNCTION,True,"","",False,src/math/expint.rs,done
expinx.f,EXPINX,SUBROUTINE,True,"","",False,src/math/expint.rs,done
expo.f,EXPO,FUNCTION,True,"","",False,src/math/expo.rs,done
ffcros.f,FFCROS,FUNCTION,True,"","",False,src/math/ffcros.rs,done
gami.f,GAMI,FUNCTION,True,"","",False,src/math/gami.rs,done
gamsp.f,GAMSP,SUBROUTINE,True,"BASICS","",False,src/math/gamsp.rs,done
gauleg.f,GAULEG,SUBROUTINE,True,"","",False,src/math/gauleg.rs,done
gaunt.f,GAUNT,FUNCTION,True,"","",False,src/math/gaunt.rs,done
getlal.f,GETLAL,SUBROUTINE,False,"BASICS|callardb|callardc|callarda|quasun|callardg|calphatd","",True,,pending
getwrd.f,GETWRD,SUBROUTINE,True,"","",False,src/math/getwrd.rs,done
gfree0.f,GFREE0,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/gfree.rs,done
gfree1.f,GFREE1,FUNCTION,False,"BASICS|MODELQ","",False,src/math/gfree.rs,done
gfreed.f,GFREED,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/gfree.rs,done
ghydop.f,GHYDOP,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|intcfg","",False,src/math/ghydop.rs,done
gntk.f,GNTK,FUNCTION,True,"","",False,src/math/gntk.rs,done
gomini.f,GOMINI,SUBROUTINE,False,"BASICS|MODELQ|intcfg","",True,,pending
grcor.f,GRCOR,SUBROUTINE,True,"","",False,src/math/grcor.rs,done
greyd.f,GREYD,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|ALIPAR","WNSTOR|MEANOP|OPACF0|STEQEQ|RHONEN",True,,pending
gridp.f,GRIDP,SUBROUTINE,True,"BASICS","",False,src/math/gridp.rs,done
h2minus.f,H2MINUS,SUBROUTINE,False,"BASICS","LOCATE",True,,pending
hction.f,HCTION,FUNCTION,False,"CTIon|CTRTEMP","",False,src/math/ctdata.rs,done
hctrecom.f,HCTRECOM,FUNCTION,False,"CTRTEMP|CTRecomb","",False,src/math/ctdata.rs,done
hedif.f,HEDIF,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|hediff","",True,,pending
hephot.f,HEPHOT,FUNCTION,True,"","",False,src/math/hephot.rs,done
hesol6.f,HESOL6,SUBROUTINE,False,"BASICS|MODELQ|PRSAUX","MATINV",False,,pending
hesolv.f,HESOLV,SUBROUTINE,False,"BASICS|MODELQ|PRSAUX","WNSTOR|STEQEQ|RHONEN|MATINV",True,,pending
hidalg.f,HIDALG,FUNCTION,True,"","",False,src/math/hidalg.rs,done
ijali2.f,IJALI2,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","QUIT",True,,pending
ijalis.f,IJALIS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",True,,pending
incldy.f,INCLDY,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT|RATMAT|SABOLF|WNSTOR|LEVSOL",True,,pending
indexx.f,INDEXX,SUBROUTINE,True,"","",False,src/math/indexx.rs,done
inicom.f,INICOM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|comgfs","",False,src/math/inicom.rs,done
inifrc.f,INIFRC,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ijflar","INDEXX",True,,pending
inifrs.f,INIFRS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","QUIT|INDEXX",True,,pending
inifrt.f,INIFRT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ijflar","INDEXX",True,,pending
inilam.f,INILAM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR","WNSTOR|RTEFR1|RATES1|ELCOR|COLIS|SABOLF|STEQEQ|OPAINI|OPACF1",False,,pending
initia.f,INITIA,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|freqcl|INUNIT|STRPAR","OPADD0|QUIT|ODFHYS|LINSPL|NSTPAR|INIFRC|READBF|RDATA|STATE|DOPGAM|LINSET|RDATAX|INTERP",True,,pending
inkul.f,INKUL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|COLKUR|LINED","",True,,pending
inpdis.f,INPDIS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|relcor","GRCOR",True,,pending
inpmod.f,INPMOD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|eospar","QUIT|RATMAT|KURUCZ|INCLDY|MOLEQ|SABOLF|WNSTOR|LEVSOL",True,,pending
interp.f,INTERP,SUBROUTINE,True,"BASICS","",False,src/math/interp.rs,done
inthyd.f,INTHYD,SUBROUTINE,False,"BASICS|MODELQ","DIVSTR",False,src/math/inthyd.rs,done
intlem.f,INTLEM,SUBROUTINE,False,"BASICS|MODELQ","INTHYD",False,src/math/intlem.rs,done
intxen.f,INTXEN,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/intxen.rs,done
irc.f,IRC,SUBROUTINE,True,"","EXPINX|SZIRC",False,src/math/irc.rs,done
iroset.f,IROSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|LINED","QUIT|LEVCD|INKUL",True,,pending
kurucz.f,KURUCZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|temlim","QUIT|RATMAT|MOLEQ|SABOLF|WNSTOR|LEVSOL|RHONEN",True,,pending
lagran.f,LAGRAN,SUBROUTINE,True,"","",False,src/math/interpolate.rs,done
laguer.f,LAGUER,SUBROUTINE,False,"","",True,src/math/laguer.rs,done
lemini.f,LEMINI,SUBROUTINE,False,"BASICS|MODELQ","",True,,pending
levcd.f,LEVCD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|COLKUR","QUIT|INDEXX",True,,pending
levgrp.f,LEVGRP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",False,src/math/levgrp.rs,done
levset.f,LEVSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT",False,,pending
levsol.f,LEVSOL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","LINEQS",False,src/math/levsol.rs,done
lineqs.f,LINEQS,SUBROUTINE,True,"BASICS","",False,src/math/lineqs.rs,done
linpro.f,LINPRO,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|quasun","DIVSTR|STARK0|DOPGAM|INTLEM|INTXEN",False,,pending
linsel.f,LINSEL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","RTEFR1|QUIT|OPAINI|OPACF1",True,,pending
linset.f,LINSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","DIVSTR|QUIT|IJALIS|STARK0",True,,pending
linspl.f,LINSPL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/linspl.rs,done
locate.f,LOCATE,SUBROUTINE,True,"","",False,src/math/locate.rs,done
ltegr.f,LTEGR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT|CONOUT|ROSSOP|WNSTOR|STEQEQ|INTERP",True,,pending
ltegrd.f,LTEGRD,SUBROUTINE,False,"BASICS|MODELQ|PRSAUX|CUBCON|TOTJHK|FACTRS|FLXAUX","QUIT|TEMPER|CONOUT|ELDENS|WNSTOR|STEQEQ|INTERP|ZMRHO",True,,pending
lucy.f,LUCY,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ITERAT|ALIPAR|ARRAY1","OPACFL|RTEFR1|ELCOR|COLIS|SABOLF|WNSTOR|STEQEQ|OPAINI",True,,pending
lymlin.f,LYMLIN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","DIVSTR|STARK0",True,,pending
matcon.f,MATCON,SUBROUTINE,False,"BASICS|MODELQ|ARRAY1|CUBCON","CONVEC",False,,pending
matgen.f,MATGEN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","BHED|MATCON|BREZ|BRE|BRTEZ|BHE|BRTE|SABOLF|BHEZ|EMAT|BPOP",False,,pending
matinv.f,MATINV,SUBROUTINE,True,"BASICS","",False,src/math/matinv.rs,done
meanop.f,MEANOP,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC","",False,src/math/meanop.rs,done
meanopt.f,MEANOPT,SUBROUTINE,False,"BASICS|MODELQ","OPCTAB",False,,pending
minv3.f,MINV3,SUBROUTINE,True,"","",False,src/math/minv3.rs,done
moleq.f,MOLEQ,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|adchar|eospar|hmolab|terden|ioniz2|COMFH1|moldat|entrop","MPARTF|RUSSEL",True,,pending
mpartf.f,MPARTF,SUBROUTINE,False,"moldat","",True,,pending
newdm.f,NEWDM,SUBROUTINE,False,"BASICS|MODELQ|FACTRS|PRSAUX|FLXAUX","INTERP|TEMPER",True,,pending
newdmt.f,NEWDMT,SUBROUTINE,False,"BASICS|MODELQ|FACTRS|PRSAUX|FLXAUX","INTERP|GRIDP|TEMPER",True,,pending
newpop.f,NEWPOP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",True,,pending
nstout.f,NSTOUT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR","QUIT",True,,pending
nstpar.f,NSTPAR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|deridt|irwint|icnrsp|imucnn|ichndm|derdif|hediff|freqcl|temlim|moldat|ifpzpa|quasun|ipricr|adiaba|FLXAUX","QUIT|GETWRD",True,,pending
odf1.f,ODF1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","DIVSTR|ODFHST|DWNFR",True,,pending
odffr.f,ODFFR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","QUIT",False,,pending
odfhst.f,ODFHST,SUBROUTINE,False,"BASICS|MODELQ|ODFPAR","",False,src/math/odfhst.rs,done
odfhyd.f,ODFHYD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","DIVSTR|ODFHST|INDEXX",False,,pending
odfhys.f,ODFHYS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","ODFFR|STARK0|IJALIS",False,,pending
odfmer.f,ODFMER,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","ODFHYD",False,,pending
odfset.f,ODFSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|STFCR","QUIT|IJALIS",True,,pending
opacf0.f,OPACF0,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|hmolab","OPACT1|OPADD|SGMER1|GFREE0|DWNFR0|DWNFR1|SABOLF|WNSTOR|LINPRO",False,,pending
opacf1.f,OPACF1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|hmolab|ipricr","OPACT1|OPADD|QUASIM|LYMLIN|GHYDOP|DWNFR1|PRD|SGMER1",True,,pending
opacfa.f,OPACFA,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|COOLCO","DWNFR1|OPADD|PRD|SGMER1",False,,pending
opacfd.f,OPACFD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT|dsctva|rhoder|hmolab","OPADD|OPACTD|QUASIM|LYMLIN|OPCTAB|DWNFR1|PRD|SGMER1|GFREED",True,,pending
opacfl.f,OPACFL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","DWNFR1|OPADD|SGMER1",False,,pending
opact1.f,OPACT1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|hmolab","OPCTAB",False,,pending
opactd.f,OPACTD,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ARRAY1|ITERAT|dsctva|rhoder|hmolab","OPCTAB",False,,pending
opactr.f,OPACTR,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ATOMIC|grdpra|dsctva|hmolab","STEQEQ|PGSET|ELDENS|SABOLF|RATMAL|WNSTOR|LEVSOL|OPAINI|OPACF1",False,,pending
opadd.f,OPADD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|eospar","H2MINUS|CIA_H2H|CIA_H2HE|CIA_H2H2|CIA_HHE",False,,pending
opadd0.f,OPADD0,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT",False,,pending
opahst.f,OPAHST,SUBROUTINE,False,"BASICS|ODFPAR","STARK0",True,,pending
opaini.f,OPAINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","LEVGRP|DWNFR0|REFLEV|SABOLF|WNSTOR|LINPRO",False,,pending
opctab.f,OPCTAB,SUBROUTINE,False,"BASICS|MODELQ","RAYLEIGH",False,,pending
opdata.f,OPDATA,SUBROUTINE,False,"TOPB","",True,,pending
opfrac.f,OPFRAC,SUBROUTINE,False,"pfoptb","",True,,pending
osccor.f,OSCCOR,SUBROUTINE,False,"BASICS|MODELQ|ITERAT","",True,,pending
outpri.f,OUTPRI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|grdpra","SABOLF|RATMAL|WNSTOR|LEVSOL|OPACF1",True,,pending
output.f,OUTPUT,SUBROUTINE,False,"BASICS|MODELQ","",True,,pending
partf.f,PARTF,SUBROUTINE,False,"BASICS|PFSTDS|irwint","OPFRAC|MPARTF|PFNI|PFFE|PFCNO|PFHEAV|PFSPEC",False,,pending
pfcno.f,PFCNO,SUBROUTINE,True,"BASICS","",False,src/math/pfcno.rs,done
pffe.f,PFFE,SUBROUTINE,True,"","",False,src/math/pffe.rs,done
pfheav.f,PFHEAV,SUBROUTINE,False,"","",True,,pending
pfni.f,PFNI,SUBROUTINE,True,"","",False,src/math/pfni.rs,done
pfspec.f,PFSPEC,SUBROUTINE,True,"","",False,src/math/pfspec.rs,done
pgset.f,PGSET,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|grdpra|rybpgs","TRIDAG",True,,pending
prchan.f,PRCHAN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",True,,pending
prd.f,PRD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","DOPGAM",False,,pending
prdini.f,PRDINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/prdini.rs,done
princ.f,PRINC,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","DWNFR|LINPRO|SABOLF|OPACF1",True,,pending
prnt.f,PRNT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","SABOLF",True,,pending
profil.f,PROFIL,FUNCTION,False,"BASICS|ATOMIC|MODELQ|quasun","DIVSTR|STARK0",False,src/math/profil.rs,done
profsp.f,PROFSP,FUNCTION,False,"BASICS|ATOMIC|MODELQ","SABOLF",False,,pending
prsent.f,PRSENT,SUBROUTINE,False,"tdedge|TABLTD|tdflag|THERM","",True,,pending
psolve.f,PSOLVE,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/psolve.rs,done
pzert.f,PZERT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/pzert.rs,done
pzeval.f,PZEVAL,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|icnrsp","CONOUT",True,,pending
pzevld.f,PZEVLD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1|grdpra|DEPTDR|ifpzpa|PRSAUX","",False,src/math/pzevld.rs,done
quartc.f,QUARTC,SUBROUTINE,False,"","",True,src/math/quartc.rs,done
quasim.f,QUASIM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|quasun","ALLARD",False,,pending
quit.f,QUIT,SUBROUTINE,False,"","",True,src/math/quit.rs,done
radpre.f,RADPRE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","RTEFR1|INDEXX|QUIT|OPACF1",True,,pending
radtot.f,RADTOT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT|TOTJHK|SURFEX|OPTDPT","RTEFR1|OPAINI|OPACF1",False,,pending
raph.f,RAPH,FUNCTION,True,"","",False,src/math/raph.rs,done
rates1.f,RATES1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT","RTEFR1|ROSSTD|OPACF1",False,,pending
ratmal.f,RATMAL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/ratmal.rs,done
ratmat.f,RATMAT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","REFLEV",False,,pending
ratsp1.f,RATSP1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","RTEFR1|ROSSTD|OPACF1",True,,pending
rayini.f,RAYINI,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC","RAYLEIGH",True,,pending
rayleigh.f,RAYLEIGH,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|eospar|RAYSCT","",False,src/math/rayleigh.rs,done
rayset.f,RAYSET,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/rayset.rs,done
rdata.f,RDATA,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|INUNIT|STRPAR|imodlc","QUIT|DOPGAM|LINSET|RDATAX",True,,pending
rdatax.f,RDATAX,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","BKHSGO",True,,pending
readbf.f,READBF,SUBROUTINE,False,"BASICS","",True,,pending
rechck.f,RECHCK,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","RTEFR1|OPACF1",True,,pending
reflev.f,REFLEV,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",False,src/math/reflev.rs,done
reiman.f,REIMAN,FUNCTION,True,"","",False,src/math/reiman.rs,done
resolv.f,RESOLV,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR|ARRAY1|icnrsp","NEWPOP|RTEFR1|RATES1|TAUFR1|ELCOR|ROSSTD|PRD|STEQEQ|CONOUT|OPAINI|TIMING|OPACF1",True,,pending
rhoeos.f,RHOEOS,FUNCTION,False,"BASICS|MODELQ","PRSENT",False,,pending
rhonen.f,RHONEN,SUBROUTINE,False,"BASICS|MODELQ","ELDENS",False,,pending
rhsgen.f,RHSGEN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|CUBCON","LEVGRP|RATMAT|MATINV|STATE|SABOLF|CONVEC|COMPT0",False,,pending
rossop.f,ROSSOP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","WNSTOR|MEANOPT|MEANOP|OPACF0|ELDENS|STEQEQ",False,,pending
rosstd.f,ROSSTD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR","",True,,pending
rte_sc.f,RTE_SC,SUBROUTINE,True,"BASICS","",False,src/math/rte_sc.rs,done
rteang.f,RTEANG,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|SURFEX|EXTINT","GAULEG",False,,pending
rtecf0.f,RTECF0,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|auxcbc|AUXRTE|OPTDPT","",False,src/math/rtecf0.rs,done
rtecf1.f,RTECF1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|SURFEX|comgfs|AUXRTE|EXTINT|OPTDPT","RTECF0|RTEFE2|RTESOL",True,,pending
rtecmc.f,RTECMC,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|AUXRTE|comgfs","RTECF0|MATINV|OPACF1",False,,pending
rtecmu.f,RTECMU,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|AUXRTE|OPTDPT","GAULEG|RTESOL|RTECF0|OPACF1",True,,pending
rtecom.f,RTECOM,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|AUXRTE|comgfs|OPTDPT","RTECF1|RTECF0|OPACF1",False,,pending
rtedf1.f,RTEDF1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|OPTDPT","",False,src/math/rtedf1.rs,done
rtedf2.f,RTEDF2,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR","",False,src/math/rtedf2.rs,done
rtefe2.f,RTEFE2,SUBROUTINE,True,"BASICS","",False,src/math/rtefe2.rs,done
rtefr1.f,RTEFR1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT","RTEDF2|RTESOL|MATINV|RTECF1|MINV3|RTEDF1",True,,pending
rteint.f,RTEINT,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT","MATINV|OPACF1",True,,pending
rtesol.f,RTESOL,SUBROUTINE,True,"BASICS","",False,src/math/rtesol.rs,done
russel.f,RUSSEL,SUBROUTINE,False,"BASICS|MODELQ|COMFH1","MPARTF",True,,pending
rybchn.f,RYBCHN,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|ALIPAR|ARRAY1|grdpra|rybpgs","ELDENS|PGSET",True,,pending
rybene.f,RYBENE,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ARRAY1|deridt|RYBMTX|CUBCON","CONVEC",False,,pending
rybheq.f,RYBHEQ,SUBROUTINE,False,"BASICS|MODELQ|grdpra|rybpgs","RTEFR1|ELCOR|PGSET|ELDENS|WNSTOR|STEQEQ|OPAINI|OPACF1",True,,pending
rybmat.f,RYBMAT,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ARRAY1|dsctva|RYBMTX","",False,src/math/rybmat.rs,done
rybsol.f,RYBSOL,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|ALIPAR|ARRAY1|ITERAT|imodlc|RYBMTX","OPACTR|OPACFD|RYBMAT|RYBCHN|LINEQS|RTEFR1|ALIFR1|ROSSTD|TRIDAG|STEQEQ",True,,pending
sabolf.f,SABOLF,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","PARTF",False,,pending
sbfch.f,SBFCH,FUNCTION,True,"","",False,src/math/sbfch.rs,done
sbfhe1.f,SBFHE1,FUNCTION,False,"BASICS|ATOMIC","QUIT",True,src/math/sbfhe1.rs,done
sbfhmi.f,SBFHMI,FUNCTION,True,"","",False,src/math/sbfhmi.rs,done
sbfhmi_old.f,SBFHMI_OLD,FUNCTION,True,"","",False,src/math/sbfhmi_old.rs,done
sbfoh.f,SBFOH,FUNCTION,True,"","",False,src/math/sbfoh.rs,done
setdrt.f,SETDRT,SUBROUTINE,False,"BASICS|MODELQ|RHODER","",False,src/math/setdrt.rs,done
settrm.f,SETTRM,SUBROUTINE,False,"tdedge|TABLTD|tdflag|THERM","PRSENT",True,,pending
sffhmi.f,SFFHMI,FUNCTION,True,"","",False,src/math/sffhmi.rs,done
sffhmi_add.f,SFFHMI_ADD,FUNCTION,True,"","",False,src/math/sffhmi_add.rs,done
sghe12.f,SGHE12,FUNCTION,True,"","",False,src/math/sghe12.rs,done
sgmer0.f,SGMER0,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/sgmer.rs,done
sgmer1.f,SGMER1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/sgmer.rs,done
sgmerd.f,SGMERD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/sgmer.rs,done
sigave.f,SIGAVE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","",True,,pending
sigk.f,SIGK,FUNCTION,False,"BASICS|ATOMIC","SPSIGK",False,,pending
sigmar.f,SIGMAR,FUNCTION,False,"BASICS","LAGUER",True,,pending
solve.f,SOLVE,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|ARRAY1|ALIPAR|CMATZD","MATGEN|PRCHAN|MATINV|WNSTOR|RHSGEN",True,,pending
solves.f,SOLVES,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|ARRAY1|ALIPAR|CMATZD|STOMAT","MATGEN|PRCHAN|MATINV|WNSTOR|RHSGEN",True,,pending
spsigk.f,SPSIGK,SUBROUTINE,True,"","CARBON",False,src/math/spsigk.rs,done
srtfrq.f,SRTFRQ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT|INDEXX",True,,pending
stark0.f,STARK0,SUBROUTINE,True,"","",False,src/math/stark0.rs,done
starka.f,STARKA,FUNCTION,False,"BASICS|MODELQ","",False,src/math/starka.rs,done
start.f,START,SUBROUTINE,False,"BASICS|hediff","",True,,pending
state.f,STATE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|PFSTDS|terden","OPFRAC|PARTF",True,,pending
steqeq.f,STEQEQ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|POPSTR|PPAPAR","LEVSOL|RATMAT|SABOLF|MOLEQ",False,,pending
switch.f,SWITCH,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",True,,pending
szirc.f,SZIRC,SUBROUTINE,True,"","EINT",False,src/math/szirc.rs,done
tabini.f,TABINI,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|intcff|abntab|eletab","",True,,pending
tabint.f,TABINT,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|intcff","",False,src/math/tabint.rs,done
taufr1.f,TAUFR1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT","",False,src/math/taufr1.rs,done
tdpini.f,TDPINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","GFREE0",False,src/math/tdpini.rs,done
temcor.f,TEMCOR,SUBROUTINE,False,"BASICS|MODELQ|ARRAY1|ALIPAR|CUBCON","WNSTOR|MEANOP|OPACF0|ELDENS|STEQEQ|CONVEC",True,,pending
temper.f,TEMPER,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|FACTRS|PRSAUX|FLXAUX","WNSTOR|MEANOPT|TLOCAL|MEANOP|OPACF0|ELDENS|STEQEQ",True,,pending
timing.f,TIMING,SUBROUTINE,False,"","",True,,pending
tiopf.f,TIOPF,SUBROUTINE,True,"","",False,src/math/tiopf.rs,done
tlocal.f,TLOCAL,SUBROUTINE,False,"BASICS|MODELQ|FACTRS|FLXAUX","QUARTC",False,,pending
tlusty.f,TLUSTY,UNKNOWN,False,"BASICS|ITERAT|ALIPAR","TIMING",True,,pending
topbas.f,TOPBAS,FUNCTION,False,"TOPB","",True,,pending
traini.f,TRAINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","",False,src/math/traini.rs,done
tridag.f,TRIDAG,SUBROUTINE,True,"","",False,src/math/tridag.rs,done
trmder.f,TRMDER,SUBROUTINE,False,"BASICS|terden|adiaba|derdif","ELDENS",False,,pending
trmdrt.f,TRMDRT,SUBROUTINE,False,"BASICS|CC|CONVOUT|tdedge|tdflag","PRSENT",False,,pending
ubeta.f,UBETA,FUNCTION,True,"","LAGRAN",False,src/math/ubeta.rs,done
vern16.f,VERN16,FUNCTION,True,"BASICS","",False,src/math/vern16.rs,done
vern18.f,VERN18,FUNCTION,True,"BASICS","",False,src/math/vern18.rs,done
vern20.f,VERN20,FUNCTION,True,"BASICS","",False,src/math/vern20.rs,done
vern26.f,VERN26,FUNCTION,True,"BASICS","",False,src/math/vern26.rs,done
verner.f,VERNER,FUNCTION,False,"BASICS|ATOMIC","QUIT",False,src/math/verner.rs,done
visini.f,VISINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",True,,pending
voigt.f,VOIGT,FUNCTION,True,"","",False,src/math/voigt.rs,done
voigte.f,VOIGTE,FUNCTION,True,"","",False,src/math/voigte.rs,done
wn.f,WN,FUNCTION,True,"BASICS","",False,src/math/wn.rs,done
wnstor.f,WNSTOR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/wnstor.rs,done
xenini.f,XENINI,SUBROUTINE,False,"BASICS|MODELQ","",True,,pending
xk2dop.f,XK2DOP,FUNCTION,True,"","",False,src/math/xk2dop.rs,done
yint.f,YINT,FUNCTION,True,"","",False,src/math/interpolate.rs,done
ylintp.f,YLINTP,FUNCTION,True,"","",False,src/math/ylintp.rs,done
zmrho.f,ZMRHO,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/zmrho.rs,done
1 fortran_file unit_name unit_type is_pure common_deps call_deps has_io rust_module status
2 _unnamed_block_data_.f C BLOCK DATA False BASICS|ATOMIC False pending
3 accel2.f ACCEL2 SUBROUTINE False BASICS|ITERAT|MODELQ True pending
4 accelp.f ACCELP SUBROUTINE False BASICS|MODELQ|ITERAT|POPULS True pending
5 alifr1.f ALIFR1 SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR ALIFR3 False pending
6 alifr3.f ALIFR3 SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR False src/math/alifr3.rs done
7 alifr6.f ALIFR6 SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR False src/math/alifr6.rs done
8 alifrk.f ALIFRK SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR False src/math/alifrk.rs done
9 alisk1.f ALISK1 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT RTEFR1|ALIFRK|ROSSTD|OPACF1 True pending
10 alisk2.f ALISK2 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT RTEFR1|ALIFRK|ROSSTD|OPACF1 True pending
11 alist1.f ALIST1 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT RTEFR1|ALIFR1|OPACFD|ROSSTD True pending
12 alist2.f ALIST2 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT QUIT|OPACFD|RTEFR1|ALIFR1|ROSSTD True pending
13 allard.f ALLARD SUBROUTINE False BASICS|callardb|callardc|callarda|quasun|callardg|calphatd ALLARDT True pending
14 allardt.f ALLARDT SUBROUTINE False BASICS|calphatd False src/math/allardt.rs done
15 angset.f ANGSET SUBROUTINE True BASICS GAULEG False src/math/angset.rs done
16 betah.f BETAH FUNCTION True False src/math/betah.rs done
17 bhe.f BHE SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR False src/math/bhe.rs done
18 bhed.f BHED SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|CMATZD|SURFEX False src/math/bhe.rs done
19 bhez.f BHEZ SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|SURFEX False src/math/bhe.rs done
20 bkhsgo.f BKHSGO SUBROUTINE True False src/math/bkhsgo.rs done
21 bpop.f BPOP SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR|ITERAT LEVGRP|BPOPE|RATMAT|BPOPT|MATINV|BPOPC|LEVSOL|BPOPF False pending
22 bpopc.f BPOPC SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR|ADCHAR STATE False pending
23 bpope.f BPOPE SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT|ARRAY1 DWNFR1|SGMER1 False pending
24 bpopf.f BPOPF SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR False src/math/bpopf.rs done
25 bpopt.f BPOPT SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR COLIS False pending
26 bre.f BRE SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR COMPT0 False pending
27 brez.f BREZ SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR COMPT0 False pending
28 brte.f BRTE SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1 COMPT0 False pending
29 brtez.f BRTEZ SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1 COMPT0 False pending
30 butler.f BUTLER SUBROUTINE True False src/math/butler.rs done
31 carbon.f CARBON SUBROUTINE True False src/math/carbon.rs done
32 ceh12.f CEH12 FUNCTION True False src/math/ceh12.rs done
33 change.f CHANGE SUBROUTINE False BASICS|ATOMIC|MODELQ READBF|STEQEQ True pending
34 chckse.f CHCKSE SUBROUTINE False BASICS|ATOMIC|MODELQ SABOLF True pending
35 chctab.f CHCTAB SUBROUTINE False BASICS|MODELQ|abntab True pending
36 cheav.f CHEAV FUNCTION False BASICS|ATOMIC QUIT True pending
37 cheavj.f CHEAVJ FUNCTION False BASICS|ATOMIC QUIT True pending
38 cia_h2h.f CIA_H2H SUBROUTINE False LOCATE|IF True pending
39 cia_h2h2.f CIA_H2H2 SUBROUTINE False LOCATE|IF True pending
40 cia_h2he.f CIA_H2HE SUBROUTINE False LOCATE|IF True pending
41 cia_hhe.f CIA_HHE SUBROUTINE False LOCATE|IF True pending
42 cion.f CION FUNCTION True False src/math/cion.rs done
43 ckoest.f CKOEST FUNCTION True BASICS False src/math/ckoest.rs done
44 colh.f COLH SUBROUTINE False BASICS|ATOMIC|MODELQ IRC|CSPEC|BUTLER False pending
45 colhe.f COLHE SUBROUTINE False BASICS|ATOMIC IRC|COLLHE|CSPEC False pending
46 colis.f COLIS SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|CTRTEMP IRC|COLHE|CSPEC|COLH False pending
47 collhe.f COLLHE SUBROUTINE True False src/math/collhe.rs done
48 column.f COLUMN SUBROUTINE False BASICS|MODELQ|relcor True pending
49 compt0.f COMPT0 SUBROUTINE False BASICS|MODELQ|ALIPAR|ITERAT|auxcbc False src/math/compt0.rs done
50 comset.f COMSET SUBROUTINE False BASICS|MODELQ|auxcbc|comgfs False src/math/comset.rs done
51 concor.f CONCOR SUBROUTINE False BASICS|MODELQ CONOUT True pending
52 conout.f CONOUT SUBROUTINE False BASICS|MODELQ|ALIPAR|CUBCON MEANOPT|MEANOP|CONVEC|OPACF0 True pending
53 conref.f CONREF SUBROUTINE False BASICS|MODELQ|ARRAY1|imucnn|CUBCON WNSTOR|ELDENS|CONVC1|STEQEQ|CONOUT|CONVEC True pending
54 contmd.f CONTMD SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR|PRSAUX|CUBCON MEANOP|CUBIC|OPACF0|STEQEQ|WNSTOR|CONOUT|CONVEC True pending
55 contmp.f CONTMP SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR|ichndm|CUBCON MEANOPT|MEANOP|CUBIC|OPACF0|STEQEQ|ELDENS|WNSTOR|CONOUT|CONVEC True pending
56 convc1.f CONVC1 SUBROUTINE False BASICS|CUBCON TRMDER|TRMDRT False pending
57 convec.f CONVEC SUBROUTINE False BASICS|CUBCON TRMDER|TRMDRT False pending
58 coolrt.f COOLRT SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT|COOLCO RTEFR1|OPACFA True pending
59 corrwm.f CORRWM SUBROUTINE False BASICS|ATOMIC|MODELQ QUIT True pending
60 cross.f CROSS FUNCTION False BASICS|ATOMIC|MODELQ False src/math/cross.rs done
61 crossd.f CROSSD FUNCTION False BASICS|ATOMIC|MODELQ False src/math/cross.rs done
62 cspec.f CSPEC SUBROUTINE False BASICS|ATOMIC QUIT False pending
63 ctdata.f CTDATA BLOCK DATA False CTIon|CTRecomb False src/math/ctdata.rs done
64 cubic.f CUBIC SUBROUTINE False BASICS|CUBCON False src/math/cubic.rs done
65 dielrc.f DIELRC SUBROUTINE True False src/math/dielrc.rs done
66 dietot.f DIETOT SUBROUTINE False BASICS|ATOMIC|MODELQ DIELRC True pending
67 divstr.f DIVSTR SUBROUTINE False BASICS|MODELQ False src/math/divstr.rs done
68 dmder.f DMDER SUBROUTINE False BASICS|ATOMIC|MODELQ|DEPTDR False src/math/dmder.rs done
69 dmeval.f DMEVAL SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT|ARRAY1 True pending
70 dopgam.f DOPGAM SUBROUTINE False BASICS|ATOMIC|MODELQ GAMSP False src/math/dopgam.rs done
71 dwnfr.f DWNFR SUBROUTINE False BASICS|MODELQ False src/math/dwnfr.rs done
72 dwnfr0.f DWNFR0 SUBROUTINE False BASICS|MODELQ False src/math/dwnfr0.rs done
73 dwnfr1.f DWNFR1 SUBROUTINE False BASICS|MODELQ False src/math/dwnfr1.rs done
74 eint.f EINT SUBROUTINE True EXPINX False src/math/expint.rs done
75 elcor.f ELCOR SUBROUTINE False BASICS|ATOMIC|MODELQ|ADCHAR WNSTOR|STEQEQ|STATE|MOLEQ True pending
76 eldenc.f ELDENC SUBROUTINE False BASICS|MODELQ|ATOMIC|eospar|hmolab|eletab RHONEN|STATE|MOLEQ True pending
77 eldens.f ELDENS SUBROUTINE False BASICS|MODELQ|ATOMIC|eospar|terden ENTENE|LINEQS|MPARTF|STATE|MOLEQ True pending
78 emat.f EMAT SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR False src/math/emat.rs done
79 entene.f ENTENE SUBROUTINE False BASICS|ATOMIC|MODELQ MPARTF False pending
80 erfcin.f ERFCIN FUNCTION True False src/math/erfcx.rs done
81 erfcx.f ERFCX FUNCTION True False src/math/erfcx.rs done
82 expint.f EXPINT FUNCTION True False src/math/expint.rs done
83 expinx.f EXPINX SUBROUTINE True False src/math/expint.rs done
84 expo.f EXPO FUNCTION True False src/math/expo.rs done
85 ffcros.f FFCROS FUNCTION True False src/math/ffcros.rs done
86 gami.f GAMI FUNCTION True False src/math/gami.rs done
87 gamsp.f GAMSP SUBROUTINE True BASICS False src/math/gamsp.rs done
88 gauleg.f GAULEG SUBROUTINE True False src/math/gauleg.rs done
89 gaunt.f GAUNT FUNCTION True False src/math/gaunt.rs done
90 getlal.f GETLAL SUBROUTINE False BASICS|callardb|callardc|callarda|quasun|callardg|calphatd True pending
91 getwrd.f GETWRD SUBROUTINE True False src/math/getwrd.rs done
92 gfree0.f GFREE0 SUBROUTINE False BASICS|MODELQ False src/math/gfree.rs done
93 gfree1.f GFREE1 FUNCTION False BASICS|MODELQ False src/math/gfree.rs done
94 gfreed.f GFREED SUBROUTINE False BASICS|MODELQ False src/math/gfree.rs done
95 ghydop.f GHYDOP SUBROUTINE False BASICS|MODELQ|ATOMIC|intcfg False src/math/ghydop.rs done
96 gntk.f GNTK FUNCTION True False src/math/gntk.rs done
97 gomini.f GOMINI SUBROUTINE False BASICS|MODELQ|intcfg True pending
98 grcor.f GRCOR SUBROUTINE True False src/math/grcor.rs done
99 greyd.f GREYD SUBROUTINE False BASICS|MODELQ|ATOMIC|ALIPAR WNSTOR|MEANOP|OPACF0|STEQEQ|RHONEN True pending
100 gridp.f GRIDP SUBROUTINE True BASICS False src/math/gridp.rs done
101 h2minus.f H2MINUS SUBROUTINE False BASICS LOCATE True pending
102 hction.f HCTION FUNCTION False CTIon|CTRTEMP False src/math/ctdata.rs done
103 hctrecom.f HCTRECOM FUNCTION False CTRTEMP|CTRecomb False src/math/ctdata.rs done
104 hedif.f HEDIF SUBROUTINE False BASICS|MODELQ|ATOMIC|hediff True pending
105 hephot.f HEPHOT FUNCTION True False src/math/hephot.rs done
106 hesol6.f HESOL6 SUBROUTINE False BASICS|MODELQ|PRSAUX MATINV False pending
107 hesolv.f HESOLV SUBROUTINE False BASICS|MODELQ|PRSAUX WNSTOR|STEQEQ|RHONEN|MATINV True pending
108 hidalg.f HIDALG FUNCTION True False src/math/hidalg.rs done
109 ijali2.f IJALI2 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR QUIT True pending
110 ijalis.f IJALIS SUBROUTINE False BASICS|ATOMIC|MODELQ True pending
111 incldy.f INCLDY SUBROUTINE False BASICS|ATOMIC|MODELQ QUIT|RATMAT|SABOLF|WNSTOR|LEVSOL True pending
112 indexx.f INDEXX SUBROUTINE True False src/math/indexx.rs done
113 inicom.f INICOM SUBROUTINE False BASICS|ATOMIC|MODELQ|comgfs False src/math/inicom.rs done
114 inifrc.f INIFRC SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ijflar INDEXX True pending
115 inifrs.f INIFRS SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR QUIT|INDEXX True pending
116 inifrt.f INIFRT SUBROUTINE False BASICS|ATOMIC|MODELQ|ijflar INDEXX True pending
117 inilam.f INILAM SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR WNSTOR|RTEFR1|RATES1|ELCOR|COLIS|SABOLF|STEQEQ|OPAINI|OPACF1 False pending
118 initia.f INITIA SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|freqcl|INUNIT|STRPAR OPADD0|QUIT|ODFHYS|LINSPL|NSTPAR|INIFRC|READBF|RDATA|STATE|DOPGAM|LINSET|RDATAX|INTERP True pending
119 inkul.f INKUL SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|COLKUR|LINED True pending
120 inpdis.f INPDIS SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|relcor GRCOR True pending
121 inpmod.f INPMOD SUBROUTINE False BASICS|ATOMIC|MODELQ|eospar QUIT|RATMAT|KURUCZ|INCLDY|MOLEQ|SABOLF|WNSTOR|LEVSOL True pending
122 interp.f INTERP SUBROUTINE True BASICS False src/math/interp.rs done
123 inthyd.f INTHYD SUBROUTINE False BASICS|MODELQ DIVSTR False src/math/inthyd.rs done
124 intlem.f INTLEM SUBROUTINE False BASICS|MODELQ INTHYD False src/math/intlem.rs done
125 intxen.f INTXEN SUBROUTINE False BASICS|MODELQ False src/math/intxen.rs done
126 irc.f IRC SUBROUTINE True EXPINX|SZIRC False src/math/irc.rs done
127 iroset.f IROSET SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|LINED QUIT|LEVCD|INKUL True pending
128 kurucz.f KURUCZ SUBROUTINE False BASICS|ATOMIC|MODELQ|temlim QUIT|RATMAT|MOLEQ|SABOLF|WNSTOR|LEVSOL|RHONEN True pending
129 lagran.f LAGRAN SUBROUTINE True False src/math/interpolate.rs done
130 laguer.f LAGUER SUBROUTINE False True src/math/laguer.rs done
131 lemini.f LEMINI SUBROUTINE False BASICS|MODELQ True pending
132 levcd.f LEVCD SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|COLKUR QUIT|INDEXX True pending
133 levgrp.f LEVGRP SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT False src/math/levgrp.rs done
134 levset.f LEVSET SUBROUTINE False BASICS|ATOMIC|MODELQ QUIT False pending
135 levsol.f LEVSOL SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT LINEQS False src/math/levsol.rs done
136 lineqs.f LINEQS SUBROUTINE True BASICS False src/math/lineqs.rs done
137 linpro.f LINPRO SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|quasun DIVSTR|STARK0|DOPGAM|INTLEM|INTXEN False pending
138 linsel.f LINSEL SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR RTEFR1|QUIT|OPAINI|OPACF1 True pending
139 linset.f LINSET SUBROUTINE False BASICS|ATOMIC|MODELQ DIVSTR|QUIT|IJALIS|STARK0 True pending
140 linspl.f LINSPL SUBROUTINE False BASICS|ATOMIC|MODELQ False src/math/linspl.rs done
141 locate.f LOCATE SUBROUTINE True False src/math/locate.rs done
142 ltegr.f LTEGR SUBROUTINE False BASICS|ATOMIC|MODELQ QUIT|CONOUT|ROSSOP|WNSTOR|STEQEQ|INTERP True pending
143 ltegrd.f LTEGRD SUBROUTINE False BASICS|MODELQ|PRSAUX|CUBCON|TOTJHK|FACTRS|FLXAUX QUIT|TEMPER|CONOUT|ELDENS|WNSTOR|STEQEQ|INTERP|ZMRHO True pending
144 lucy.f LUCY SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ITERAT|ALIPAR|ARRAY1 OPACFL|RTEFR1|ELCOR|COLIS|SABOLF|WNSTOR|STEQEQ|OPAINI True pending
145 lymlin.f LYMLIN SUBROUTINE False BASICS|ATOMIC|MODELQ DIVSTR|STARK0 True pending
146 matcon.f MATCON SUBROUTINE False BASICS|MODELQ|ARRAY1|CUBCON CONVEC False pending
147 matgen.f MATGEN SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR BHED|MATCON|BREZ|BRE|BRTEZ|BHE|BRTE|SABOLF|BHEZ|EMAT|BPOP False pending
148 matinv.f MATINV SUBROUTINE True BASICS False src/math/matinv.rs done
149 meanop.f MEANOP SUBROUTINE False BASICS|MODELQ|ATOMIC False src/math/meanop.rs done
150 meanopt.f MEANOPT SUBROUTINE False BASICS|MODELQ OPCTAB False pending
151 minv3.f MINV3 SUBROUTINE True False src/math/minv3.rs done
152 moleq.f MOLEQ SUBROUTINE False BASICS|MODELQ|ATOMIC|adchar|eospar|hmolab|terden|ioniz2|COMFH1|moldat|entrop MPARTF|RUSSEL True pending
153 mpartf.f MPARTF SUBROUTINE False moldat True pending
154 newdm.f NEWDM SUBROUTINE False BASICS|MODELQ|FACTRS|PRSAUX|FLXAUX INTERP|TEMPER True pending
155 newdmt.f NEWDMT SUBROUTINE False BASICS|MODELQ|FACTRS|PRSAUX|FLXAUX INTERP|GRIDP|TEMPER True pending
156 newpop.f NEWPOP SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT True pending
157 nstout.f NSTOUT SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR QUIT True pending
158 nstpar.f NSTPAR SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|deridt|irwint|icnrsp|imucnn|ichndm|derdif|hediff|freqcl|temlim|moldat|ifpzpa|quasun|ipricr|adiaba|FLXAUX QUIT|GETWRD True pending
159 odf1.f ODF1 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR DIVSTR|ODFHST|DWNFR True pending
160 odffr.f ODFFR SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR QUIT False pending
161 odfhst.f ODFHST SUBROUTINE False BASICS|MODELQ|ODFPAR False src/math/odfhst.rs done
162 odfhyd.f ODFHYD SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR DIVSTR|ODFHST|INDEXX False pending
163 odfhys.f ODFHYS SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR ODFFR|STARK0|IJALIS False pending
164 odfmer.f ODFMER SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR ODFHYD False pending
165 odfset.f ODFSET SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|STFCR QUIT|IJALIS True pending
166 opacf0.f OPACF0 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|hmolab OPACT1|OPADD|SGMER1|GFREE0|DWNFR0|DWNFR1|SABOLF|WNSTOR|LINPRO False pending
167 opacf1.f OPACF1 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|hmolab|ipricr OPACT1|OPADD|QUASIM|LYMLIN|GHYDOP|DWNFR1|PRD|SGMER1 True pending
168 opacfa.f OPACFA SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|COOLCO DWNFR1|OPADD|PRD|SGMER1 False pending
169 opacfd.f OPACFD SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT|dsctva|rhoder|hmolab OPADD|OPACTD|QUASIM|LYMLIN|OPCTAB|DWNFR1|PRD|SGMER1|GFREED True pending
170 opacfl.f OPACFL SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR DWNFR1|OPADD|SGMER1 False pending
171 opact1.f OPACT1 SUBROUTINE False BASICS|MODELQ|ALIPAR|hmolab OPCTAB False pending
172 opactd.f OPACTD SUBROUTINE False BASICS|MODELQ|ALIPAR|ARRAY1|ITERAT|dsctva|rhoder|hmolab OPCTAB False pending
173 opactr.f OPACTR SUBROUTINE False BASICS|MODELQ|ALIPAR|ATOMIC|grdpra|dsctva|hmolab STEQEQ|PGSET|ELDENS|SABOLF|RATMAL|WNSTOR|LEVSOL|OPAINI|OPACF1 False pending
174 opadd.f OPADD SUBROUTINE False BASICS|ATOMIC|MODELQ|eospar H2MINUS|CIA_H2H|CIA_H2HE|CIA_H2H2|CIA_HHE False pending
175 opadd0.f OPADD0 SUBROUTINE False BASICS|ATOMIC|MODELQ QUIT False pending
176 opahst.f OPAHST SUBROUTINE False BASICS|ODFPAR STARK0 True pending
177 opaini.f OPAINI SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR LEVGRP|DWNFR0|REFLEV|SABOLF|WNSTOR|LINPRO False pending
178 opctab.f OPCTAB SUBROUTINE False BASICS|MODELQ RAYLEIGH False pending
179 opdata.f OPDATA SUBROUTINE False TOPB True pending
180 opfrac.f OPFRAC SUBROUTINE False pfoptb True pending
181 osccor.f OSCCOR SUBROUTINE False BASICS|MODELQ|ITERAT True pending
182 outpri.f OUTPRI SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|grdpra SABOLF|RATMAL|WNSTOR|LEVSOL|OPACF1 True pending
183 output.f OUTPUT SUBROUTINE False BASICS|MODELQ True pending
184 partf.f PARTF SUBROUTINE False BASICS|PFSTDS|irwint OPFRAC|MPARTF|PFNI|PFFE|PFCNO|PFHEAV|PFSPEC False pending
185 pfcno.f PFCNO SUBROUTINE True BASICS False src/math/pfcno.rs done
186 pffe.f PFFE SUBROUTINE True False src/math/pffe.rs done
187 pfheav.f PFHEAV SUBROUTINE False True pending
188 pfni.f PFNI SUBROUTINE True False src/math/pfni.rs done
189 pfspec.f PFSPEC SUBROUTINE True False src/math/pfspec.rs done
190 pgset.f PGSET SUBROUTINE False BASICS|ITERAT|MODELQ|grdpra|rybpgs TRIDAG True pending
191 prchan.f PRCHAN SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT True pending
192 prd.f PRD SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT DOPGAM False pending
193 prdini.f PRDINI SUBROUTINE False BASICS|ATOMIC|MODELQ False src/math/prdini.rs done
194 princ.f PRINC SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR DWNFR|LINPRO|SABOLF|OPACF1 True pending
195 prnt.f PRNT SUBROUTINE False BASICS|ATOMIC|MODELQ SABOLF True pending
196 profil.f PROFIL FUNCTION False BASICS|ATOMIC|MODELQ|quasun DIVSTR|STARK0 False src/math/profil.rs done
197 profsp.f PROFSP FUNCTION False BASICS|ATOMIC|MODELQ SABOLF False pending
198 prsent.f PRSENT SUBROUTINE False tdedge|TABLTD|tdflag|THERM True pending
199 psolve.f PSOLVE SUBROUTINE False BASICS|MODELQ False src/math/psolve.rs done
200 pzert.f PZERT SUBROUTINE False BASICS|ATOMIC|MODELQ False src/math/pzert.rs done
201 pzeval.f PZEVAL SUBROUTINE False BASICS|MODELQ|ALIPAR|icnrsp CONOUT True pending
202 pzevld.f PZEVLD SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1|grdpra|DEPTDR|ifpzpa|PRSAUX False src/math/pzevld.rs done
203 quartc.f QUARTC SUBROUTINE False True src/math/quartc.rs done
204 quasim.f QUASIM SUBROUTINE False BASICS|ATOMIC|MODELQ|quasun ALLARD False pending
205 quit.f QUIT SUBROUTINE False True src/math/quit.rs done
206 radpre.f RADPRE SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR RTEFR1|INDEXX|QUIT|OPACF1 True pending
207 radtot.f RADTOT SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT|TOTJHK|SURFEX|OPTDPT RTEFR1|OPAINI|OPACF1 False pending
208 raph.f RAPH FUNCTION True False src/math/raph.rs done
209 rates1.f RATES1 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT RTEFR1|ROSSTD|OPACF1 False pending
210 ratmal.f RATMAL SUBROUTINE False BASICS|ATOMIC|MODELQ False src/math/ratmal.rs done
211 ratmat.f RATMAT SUBROUTINE False BASICS|ATOMIC|MODELQ REFLEV False pending
212 ratsp1.f RATSP1 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT RTEFR1|ROSSTD|OPACF1 True pending
213 rayini.f RAYINI SUBROUTINE False BASICS|MODELQ|ATOMIC RAYLEIGH True pending
214 rayleigh.f RAYLEIGH SUBROUTINE False BASICS|ATOMIC|MODELQ|eospar|RAYSCT False src/math/rayleigh.rs done
215 rayset.f RAYSET SUBROUTINE False BASICS|MODELQ False src/math/rayset.rs done
216 rdata.f RDATA SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|INUNIT|STRPAR|imodlc QUIT|DOPGAM|LINSET|RDATAX True pending
217 rdatax.f RDATAX SUBROUTINE False BASICS|ATOMIC|MODELQ BKHSGO True pending
218 readbf.f READBF SUBROUTINE False BASICS True pending
219 rechck.f RECHCK SUBROUTINE False BASICS|ATOMIC|MODELQ RTEFR1|OPACF1 True pending
220 reflev.f REFLEV SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT False src/math/reflev.rs done
221 reiman.f REIMAN FUNCTION True False src/math/reiman.rs done
222 resolv.f RESOLV SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR|ARRAY1|icnrsp NEWPOP|RTEFR1|RATES1|TAUFR1|ELCOR|ROSSTD|PRD|STEQEQ|CONOUT|OPAINI|TIMING|OPACF1 True pending
223 rhoeos.f RHOEOS FUNCTION False BASICS|MODELQ PRSENT False pending
224 rhonen.f RHONEN SUBROUTINE False BASICS|MODELQ ELDENS False pending
225 rhsgen.f RHSGEN SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|CUBCON LEVGRP|RATMAT|MATINV|STATE|SABOLF|CONVEC|COMPT0 False pending
226 rossop.f ROSSOP SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR WNSTOR|MEANOPT|MEANOP|OPACF0|ELDENS|STEQEQ False pending
227 rosstd.f ROSSTD SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR True pending
228 rte_sc.f RTE_SC SUBROUTINE True BASICS False src/math/rte_sc.rs done
229 rteang.f RTEANG SUBROUTINE False BASICS|MODELQ|ALIPAR|SURFEX|EXTINT GAULEG False pending
230 rtecf0.f RTECF0 SUBROUTINE False BASICS|MODELQ|ALIPAR|ITERAT|auxcbc|AUXRTE|OPTDPT False src/math/rtecf0.rs done
231 rtecf1.f RTECF1 SUBROUTINE False BASICS|MODELQ|ALIPAR|ITERAT|SURFEX|comgfs|AUXRTE|EXTINT|OPTDPT RTECF0|RTEFE2|RTESOL True pending
232 rtecmc.f RTECMC SUBROUTINE False BASICS|MODELQ|ALIPAR|ITERAT|AUXRTE|comgfs RTECF0|MATINV|OPACF1 False pending
233 rtecmu.f RTECMU SUBROUTINE False BASICS|MODELQ|ALIPAR|ITERAT|AUXRTE|OPTDPT GAULEG|RTESOL|RTECF0|OPACF1 True pending
234 rtecom.f RTECOM SUBROUTINE False BASICS|MODELQ|ALIPAR|ITERAT|AUXRTE|comgfs|OPTDPT RTECF1|RTECF0|OPACF1 False pending
235 rtedf1.f RTEDF1 SUBROUTINE False BASICS|MODELQ|ALIPAR|OPTDPT False src/math/rtedf1.rs done
236 rtedf2.f RTEDF2 SUBROUTINE False BASICS|MODELQ|ALIPAR False src/math/rtedf2.rs done
237 rtefe2.f RTEFE2 SUBROUTINE True BASICS False src/math/rtefe2.rs done
238 rtefr1.f RTEFR1 SUBROUTINE False BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT RTEDF2|RTESOL|MATINV|RTECF1|MINV3|RTEDF1 True pending
239 rteint.f RTEINT SUBROUTINE False BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT MATINV|OPACF1 True pending
240 rtesol.f RTESOL SUBROUTINE True BASICS False src/math/rtesol.rs done
241 russel.f RUSSEL SUBROUTINE False BASICS|MODELQ|COMFH1 MPARTF True pending
242 rybchn.f RYBCHN SUBROUTINE False BASICS|ITERAT|MODELQ|ALIPAR|ARRAY1|grdpra|rybpgs ELDENS|PGSET True pending
243 rybene.f RYBENE SUBROUTINE False BASICS|MODELQ|ALIPAR|ARRAY1|deridt|RYBMTX|CUBCON CONVEC False pending
244 rybheq.f RYBHEQ SUBROUTINE False BASICS|MODELQ|grdpra|rybpgs RTEFR1|ELCOR|PGSET|ELDENS|WNSTOR|STEQEQ|OPAINI|OPACF1 True pending
245 rybmat.f RYBMAT SUBROUTINE False BASICS|MODELQ|ALIPAR|ARRAY1|dsctva|RYBMTX False src/math/rybmat.rs done
246 rybsol.f RYBSOL SUBROUTINE False BASICS|MODELQ|ATOMIC|ALIPAR|ARRAY1|ITERAT|imodlc|RYBMTX OPACTR|OPACFD|RYBMAT|RYBCHN|LINEQS|RTEFR1|ALIFR1|ROSSTD|TRIDAG|STEQEQ True pending
247 sabolf.f SABOLF SUBROUTINE False BASICS|ATOMIC|MODELQ PARTF False pending
248 sbfch.f SBFCH FUNCTION True False src/math/sbfch.rs done
249 sbfhe1.f SBFHE1 FUNCTION False BASICS|ATOMIC QUIT True src/math/sbfhe1.rs done
250 sbfhmi.f SBFHMI FUNCTION True False src/math/sbfhmi.rs done
251 sbfhmi_old.f SBFHMI_OLD FUNCTION True False src/math/sbfhmi_old.rs done
252 sbfoh.f SBFOH FUNCTION True False src/math/sbfoh.rs done
253 setdrt.f SETDRT SUBROUTINE False BASICS|MODELQ|RHODER False src/math/setdrt.rs done
254 settrm.f SETTRM SUBROUTINE False tdedge|TABLTD|tdflag|THERM PRSENT True pending
255 sffhmi.f SFFHMI FUNCTION True False src/math/sffhmi.rs done
256 sffhmi_add.f SFFHMI_ADD FUNCTION True False src/math/sffhmi_add.rs done
257 sghe12.f SGHE12 FUNCTION True False src/math/sghe12.rs done
258 sgmer0.f SGMER0 SUBROUTINE False BASICS|ATOMIC|MODELQ False src/math/sgmer.rs done
259 sgmer1.f SGMER1 SUBROUTINE False BASICS|ATOMIC|MODELQ False src/math/sgmer.rs done
260 sgmerd.f SGMERD SUBROUTINE False BASICS|ATOMIC|MODELQ False src/math/sgmer.rs done
261 sigave.f SIGAVE SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR True pending
262 sigk.f SIGK FUNCTION False BASICS|ATOMIC SPSIGK False pending
263 sigmar.f SIGMAR FUNCTION False BASICS LAGUER True pending
264 solve.f SOLVE SUBROUTINE False BASICS|ITERAT|MODELQ|ARRAY1|ALIPAR|CMATZD MATGEN|PRCHAN|MATINV|WNSTOR|RHSGEN True pending
265 solves.f SOLVES SUBROUTINE False BASICS|ITERAT|MODELQ|ARRAY1|ALIPAR|CMATZD|STOMAT MATGEN|PRCHAN|MATINV|WNSTOR|RHSGEN True pending
266 spsigk.f SPSIGK SUBROUTINE True CARBON False src/math/spsigk.rs done
267 srtfrq.f SRTFRQ SUBROUTINE False BASICS|ATOMIC|MODELQ QUIT|INDEXX True pending
268 stark0.f STARK0 SUBROUTINE True False src/math/stark0.rs done
269 starka.f STARKA FUNCTION False BASICS|MODELQ False src/math/starka.rs done
270 start.f START SUBROUTINE False BASICS|hediff True pending
271 state.f STATE SUBROUTINE False BASICS|ATOMIC|MODELQ|PFSTDS|terden OPFRAC|PARTF True pending
272 steqeq.f STEQEQ SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT|POPSTR|PPAPAR LEVSOL|RATMAT|SABOLF|MOLEQ False pending
273 switch.f SWITCH SUBROUTINE False BASICS|ATOMIC|MODELQ True pending
274 szirc.f SZIRC SUBROUTINE True EINT False src/math/szirc.rs done
275 tabini.f TABINI SUBROUTINE False BASICS|MODELQ|ATOMIC|intcff|abntab|eletab True pending
276 tabint.f TABINT SUBROUTINE False BASICS|MODELQ|ATOMIC|intcff False src/math/tabint.rs done
277 taufr1.f TAUFR1 SUBROUTINE False BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT False src/math/taufr1.rs done
278 tdpini.f TDPINI SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR GFREE0 False src/math/tdpini.rs done
279 temcor.f TEMCOR SUBROUTINE False BASICS|MODELQ|ARRAY1|ALIPAR|CUBCON WNSTOR|MEANOP|OPACF0|ELDENS|STEQEQ|CONVEC True pending
280 temper.f TEMPER SUBROUTINE False BASICS|MODELQ|ALIPAR|FACTRS|PRSAUX|FLXAUX WNSTOR|MEANOPT|TLOCAL|MEANOP|OPACF0|ELDENS|STEQEQ True pending
281 timing.f TIMING SUBROUTINE False True pending
282 tiopf.f TIOPF SUBROUTINE True False src/math/tiopf.rs done
283 tlocal.f TLOCAL SUBROUTINE False BASICS|MODELQ|FACTRS|FLXAUX QUARTC False pending
284 tlusty.f TLUSTY UNKNOWN False BASICS|ITERAT|ALIPAR TIMING True pending
285 topbas.f TOPBAS FUNCTION False TOPB True pending
286 traini.f TRAINI SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR False src/math/traini.rs done
287 tridag.f TRIDAG SUBROUTINE True False src/math/tridag.rs done
288 trmder.f TRMDER SUBROUTINE False BASICS|terden|adiaba|derdif ELDENS False pending
289 trmdrt.f TRMDRT SUBROUTINE False BASICS|CC|CONVOUT|tdedge|tdflag PRSENT False pending
290 ubeta.f UBETA FUNCTION True LAGRAN False src/math/ubeta.rs done
291 vern16.f VERN16 FUNCTION True BASICS False src/math/vern16.rs done
292 vern18.f VERN18 FUNCTION True BASICS False src/math/vern18.rs done
293 vern20.f VERN20 FUNCTION True BASICS False src/math/vern20.rs done
294 vern26.f VERN26 FUNCTION True BASICS False src/math/vern26.rs done
295 verner.f VERNER FUNCTION False BASICS|ATOMIC QUIT False src/math/verner.rs done
296 visini.f VISINI SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT True pending
297 voigt.f VOIGT FUNCTION True False src/math/voigt.rs done
298 voigte.f VOIGTE FUNCTION True False src/math/voigte.rs done
299 wn.f WN FUNCTION True BASICS False src/math/wn.rs done
300 wnstor.f WNSTOR SUBROUTINE False BASICS|ATOMIC|MODELQ False src/math/wnstor.rs done
301 xenini.f XENINI SUBROUTINE False BASICS|MODELQ True pending
302 xk2dop.f XK2DOP FUNCTION True False src/math/xk2dop.rs done
303 yint.f YINT FUNCTION True False src/math/interpolate.rs done
304 ylintp.f YLINTP FUNCTION True False src/math/ylintp.rs done
305 zmrho.f ZMRHO SUBROUTINE False BASICS|MODELQ False src/math/zmrho.rs done

77
MEMORY/plan.md Normal file
View File

@ -0,0 +1,77 @@
# TLUSTY/SYNSPEC 重构计划
> **实时追踪见 [FORTRAN_TRACKING.md](../../FORTRAN_TRACKING.md)**
> 本文档提供重构优先级和阶段划分参考。
## 优先级原则
1. **纯函数优先** - 无 COMMON/I/O 依赖,独立测试
2. **依赖少的优先** - 只依赖 BASICS 的函数
3. **小型函数优先** - 行数少,风险低
4. **核心功能优先** - 辐射转移、不透明度
## 阶段划分
### 阶段 1: 纯函数 (is_pure=True)
已完成大部分,剩余查看:
```bash
grep "True.*pending" fortran_analysis.csv
```
### 阶段 2: 简单 COMMON 依赖
只依赖 BASICS 或已有状态结构体的函数。
### 阶段 3: 复杂 COMMON 依赖
依赖多个 COMMON 块的函数。
### 阶段 4: I/O 依赖 (117个)
最后处理,可能需要设计 I/O 抽象层。
## 核心函数分类
### 辐射转移 (RTE)
- `bre.f`, `brez.f` - 基本辐射转移
- `brte.f`, `brtez.f` - 扩展辐射转移
- `rte_sc.f`, `rtedf*.f` - 深度/角度积分
### 不透明度 (Opacity)
- `opacfl.f` - 核心不透明度计算
- `opctab.f` - 不透明度表插值
- `opadd*.f` - 不透明度叠加
### 统计平衡 (Statistical Equilibrium)
- `bpop*.f` - 束缚态占据数
- `levsol.f` - 能级求解 ✅
- `ratmat.f` - 速率矩阵
### ALI 求解器
- `alifr1.f`, `alifr3.f`, `alifr6.f` - ALI 迭代
## 推荐执行顺序
```
1. 完成剩余纯函数
2. 简单 COMMON 函数 (setdrt, cubic, dmder ✅)
3. 辐射转移核心
4. 不透明度核心
5. 统计平衡核心
6. I/O 封装
7. 主程序集成
```
## 查询命令
```bash
# 未完成的纯函数
grep "True.*pending" fortran_analysis.csv
# 只依赖 BASICS 的待处理函数
grep "BASICS.*pending" fortran_analysis.csv | grep -v "ATOMIC\|MODELQ"
# 按行数排序
cd tlusty/extracted && wc -l *.f | sort -n | head -30
```

670
MEMORY/refactoring_notes.md Normal file
View File

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

View File

@ -1,86 +0,0 @@
# TLUSTY/SYNSPEC Rust 重构进度
## 当前状态 (2026-03-20)
- **已完成**: 86 个函数 + 15 个状态模块 + 1 个数据文件
- **测试通过**: 379 个
- **TLUSTY 纯函数重构完成**
- **最新完成**: cubic.rs
## 统计
| 程序 | 总单元 | 纯函数 |
|------|--------|--------|
| TLUSTY | 304 | 195 |
| SYNSPEC | 168 | 93 |
| 类型 | 数量 |
|------|------|
| 已完成 | 81 |
| 剩余纯函数 | ~70 |
| COMMON 依赖 | ~45 |
| I/O 依赖 | 114 |
## 已完成模块 (87个)
### math/ (83个)
angset, betah, bkhsgo, butler, carbon, ceh12, cion, ckoest, collhe, cross, crossd,
ctdata, cubic, dielrc, divstr, dmder, dwnfr, dwnfr0, dwnfr1, emat, erfcx, expint, expo, ffcros, gami,
gamsp, gauleg, gaunt, getwrd, gfree, gntk, grcor, hephot, hidalg, indexx, inthyd,
intlem, interpolate, irc, laguer, levsol, lineqs, locate, meanop, minv3, pffe, pfni, pfspec,
psolve, quartc, quit, raph, ratmal, reiman, sbfch, sbfhe1, sbfhmi, sbfhmi_old,
sbfoh, sffhmi, sffhmi_add, sghe12, sgmer, spsigk, stark0, starka, szirc, tdpini, tiopf,
traini, tridag, ubeta, vern16, vern18, vern20, vern26, verner, voigt, voigte,
wn, xk2dop, ylintp
### state/ (15个)
constants, config, atomic, model, arrays, iterat, alipar, odfpar,
+ WMCOMP, MRGPAR, INVINT, CUROPA, PRESSR, DWNPAR, HYDPRF, TURBUL (嵌套在 model.rs)
### data/ (1个)
data.rs - 静态数据数组
## 状态说明
- ✅ 已完成
- 🔄 进行中
- ⬜ 待处理
- ❌ 有 I/O 依赖 (暂不处理)
---
## 重构日志
### 2026-03-20
**新增:**
- lineqs.rs - 线性方程组求解 (高斯消元法)
- starka.rs - Stark 展宽近似表达式
- inthyd.rs - 氢线 Stark 展宽表格插值
- HydPrf 结构体 (model.rs)
**新增常量:**
- MHT=7, MHE=20, MHWL=90 (氢线表格维度)
### 2026-03-19
**完成:**
- 创建 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

View File

@ -274,3 +274,10 @@ tridag - 22 - ⬜
4. 按照流程执行重构
**第一个文件**: `expo.f` (10行最简单)
RYBMAT 0 0 0 0 0 ○
SETDRT 0 0 0 0 0 ○
TABINT 0 0 0 0 0 ○
TAUFR1 可能有问题
SETDRT 调用的 RHOEOS 可能被分析脚本漏掉了(因为 RHOEOS 是 FUNCTION 不是 SUBROUTINE
或者脚本认为 RHOEOS 不在调用依赖中

View File

@ -6,19 +6,19 @@ alifr1.f,ALIFR1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","ALIFR3",False,,p
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
alisk1.f,ALISK1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","ALIFRK|ROSSTD|OPACF1|RTEFR1",True,,pending
alisk2.f,ALISK2,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","ALIFRK|ROSSTD|OPACF1|RTEFR1",True,,pending
alist1.f,ALIST1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT","OPACFD|ALIFR1|ROSSTD|RTEFR1",True,,pending
alist2.f,ALIST2,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","RTEFR1|ROSSTD|ALIFR1|QUIT|OPACFD",True,,pending
allard.f,ALLARD,SUBROUTINE,False,"BASICS|calphatd|callarda|callardc|callardg|callardb|quasun","ALLARDT",True,,pending
allardt.f,ALLARDT,SUBROUTINE,False,"BASICS|calphatd","",False,src/math/allardt.rs,done
angset.f,ANGSET,SUBROUTINE,True,"BASICS","GAULEG",False,src/math/angset.rs,done
betah.f,BETAH,FUNCTION,True,"","BETAH",False,src/math/betah.rs,done
bhe.f,BHE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","",False,,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
bhe.f,BHE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","",False,src/math/bhe.rs,done
bhed.f,BHED,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|CMATZD|SURFEX","",False,src/math/bhe.rs,done
bhez.f,BHEZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|SURFEX","",False,src/math/bhe.rs,done
bkhsgo.f,BKHSGO,SUBROUTINE,True,"","",False,src/math/bkhsgo.rs,done
bpop.f,BPOP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR|ITERAT","BPOPF|RATMAT|BPOPT|LEVSOL|MATINV|BPOPC|BPOPE|LEVGRP",False,,pending
bpop.f,BPOP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR|ITERAT","MATINV|BPOPT|BPOPF|LEVSOL|RATMAT|BPOPC|BPOPE|LEVGRP",False,,pending
bpopc.f,BPOPC,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR|ADCHAR","STATE",False,,pending
bpope.f,BPOPE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT|ARRAY1","SGMER1|DWNFR1",False,,pending
bpopf.f,BPOPF,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR","",False,src/math/bpopf.rs,done
@ -30,37 +30,37 @@ brtez.f,BRTEZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1","COMPT0",Fal
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
change.f,CHANGE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","STEQEQ|READBF",True,,pending
chckse.f,CHCKSE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","SABOLF",True,,pending
chctab.f,CHCTAB,SUBROUTINE,False,"BASICS|MODELQ|abntab","",True,,pending
cheav.f,CHEAV,FUNCTION,False,"BASICS|ATOMIC","QUIT|CHEAV",True,,pending
cheavj.f,CHEAVJ,FUNCTION,False,"BASICS|ATOMIC","CHEAVJ|QUIT",True,,pending
cheav.f,CHEAV,FUNCTION,False,"BASICS|ATOMIC","CHEAV|QUIT",True,,pending
cheavj.f,CHEAVJ,FUNCTION,False,"BASICS|ATOMIC","QUIT|CHEAVJ",True,,pending
cia_h2h.f,CIA_H2H,SUBROUTINE,False,"","LOCATE|IF",True,,pending
cia_h2h2.f,CIA_H2H2,SUBROUTINE,False,"","LOCATE|IF",True,,pending
cia_h2he.f,CIA_H2HE,SUBROUTINE,False,"","LOCATE|IF",True,,pending
cia_hhe.f,CIA_HHE,SUBROUTINE,False,"","LOCATE|IF",True,,pending
cion.f,CION,FUNCTION,True,"","CION",False,src/math/cion.rs,done
ckoest.f,CKOEST,FUNCTION,True,"BASICS","CKOEST",False,src/math/ckoest.rs,done
colh.f,COLH,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","CSPEC|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
colh.f,COLH,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","CSPEC|BUTLER|IRC",False,,pending
colhe.f,COLHE,SUBROUTINE,False,"BASICS|ATOMIC","COLLHE|CSPEC|IRC",False,,pending
colis.f,COLIS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|CTRTEMP","CSPEC|COLH|COLHE|IRC",False,,pending
collhe.f,COLLHE,SUBROUTINE,True,"","",False,src/math/collhe.rs,done
column.f,COLUMN,SUBROUTINE,False,"BASICS|MODELQ|relcor","",True,,pending
compt0.f,COMPT0,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|auxcbc","",False,,pending
comset.f,COMSET,SUBROUTINE,False,"BASICS|MODELQ|comgfs|auxcbc","",False,,pending
compt0.f,COMPT0,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|auxcbc","",False,src/math/compt0.rs,done
comset.f,COMSET,SUBROUTINE,False,"BASICS|MODELQ|comgfs|auxcbc","",False,src/math/comset.rs,done
concor.f,CONCOR,SUBROUTINE,False,"BASICS|MODELQ","CONOUT",True,,pending
conout.f,CONOUT,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|CUBCON","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
conout.f,CONOUT,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|CUBCON","MEANOP|MEANOPT|CONVEC|OPACF0",True,,pending
conref.f,CONREF,SUBROUTINE,False,"BASICS|MODELQ|ARRAY1|imucnn|CUBCON","CONVC1|WNSTOR|STEQEQ|ELDENS|CONVEC|CONOUT",True,,pending
contmd.f,CONTMD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|PRSAUX|CUBCON","MEANOP|WNSTOR|STEQEQ|OPACF0|CONVEC|CONOUT|CUBIC",True,,pending
contmp.f,CONTMP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ichndm|CUBCON","MEANOP|WNSTOR|STEQEQ|ELDENS|OPACF0|CONVEC|MEANOPT|CONOUT|CUBIC",True,,pending
convc1.f,CONVC1,SUBROUTINE,False,"BASICS|CUBCON","TRMDER|TRMDRT",False,,pending
convec.f,CONVEC,SUBROUTINE,False,"BASICS|CUBCON","TRMDER|TRMDRT",False,,pending
coolrt.f,COOLRT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT|COOLCO","RTEFR1|OPACFA",True,,pending
coolrt.f,COOLRT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT|COOLCO","OPACFA|RTEFR1",True,,pending
corrwm.f,CORRWM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT",True,,pending
cross.f,CROSS,FUNCTION,False,"BASICS|ATOMIC|MODELQ","CROSS",False,src/math/cross.rs,done
crossd.f,CROSSD,FUNCTION,False,"BASICS|ATOMIC|MODELQ","CROSSD",False,src/math/cross.rs,done
cspec.f,CSPEC,SUBROUTINE,False,"BASICS|ATOMIC","QUIT",False,,pending
ctdata.f,CTDATA,BLOCK DATA,False,"CTRecomb|CTIon","",False,src/math/ctdata.rs,done
ctdata.f,CTDATA,BLOCK DATA,False,"CTIon|CTRecomb","",False,src/math/ctdata.rs,done
cubic.f,CUBIC,SUBROUTINE,False,"BASICS|CUBCON","",False,src/math/cubic.rs,done
dielrc.f,DIELRC,SUBROUTINE,True,"","",False,src/math/dielrc.rs,done
dietot.f,DIETOT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","DIELRC",True,,pending
@ -72,9 +72,9 @@ 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
elcor.f,ELCOR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ADCHAR","WNSTOR|STEQEQ|STATE|MOLEQ",True,,pending
eldenc.f,ELDENC,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|eletab|hmolab|eospar","RHONEN|STATE|MOLEQ",True,,pending
eldens.f,ELDENS,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|terden|eospar","ENTENE|STATE|MPARTF|LINEQS|MOLEQ",True,,pending
emat.f,EMAT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","",False,src/math/emat.rs,done
entene.f,ENTENE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","MPARTF",False,,pending
erfcin.f,ERFCIN,FUNCTION,True,"","ERFCIN",False,src/math/erfcx.rs,done
@ -87,101 +87,101 @@ 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
getlal.f,GETLAL,SUBROUTINE,False,"BASICS|callardc|callarda|calphatd|callardg|callardb|quasun","",True,,pending
getwrd.f,GETWRD,SUBROUTINE,True,"","",False,src/math/getwrd.rs,done
gfree0.f,GFREE0,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/gfree.rs,done
gfree1.f,GFREE1,FUNCTION,False,"BASICS|MODELQ","GFREE1",False,src/math/gfree.rs,done
gfreed.f,GFREED,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/gfree.rs,done
ghydop.f,GHYDOP,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|intcfg","",False,,pending
ghydop.f,GHYDOP,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|intcfg","",False,src/math/ghydop.rs,done
gntk.f,GNTK,FUNCTION,True,"","GNTK",False,src/math/gntk.rs,done
gomini.f,GOMINI,SUBROUTINE,False,"BASICS|MODELQ|intcfg","",True,,pending
grcor.f,GRCOR,SUBROUTINE,True,"","",False,src/math/grcor.rs,done
greyd.f,GREYD,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|ALIPAR","MEANOP|RHONEN|STEQEQ|WNSTOR|OPACF0",True,,pending
greyd.f,GREYD,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|ALIPAR","WNSTOR|RHONEN|STEQEQ|OPACF0|MEANOP",True,,pending
gridp.f,GRIDP,SUBROUTINE,True,"BASICS","",False,src/math/gridp.rs,done
h2minus.f,H2MINUS,SUBROUTINE,False,"BASICS","LOCATE",True,,pending
hction.f,HCTION,FUNCTION,False,"CTIon|CTRTEMP","HCTION",False,src/math/ctdata.rs,done
hctrecom.f,HCTRECOM,FUNCTION,False,"CTRecomb|CTRTEMP","HCTRECOM",False,src/math/ctdata.rs,done
hction.f,HCTION,FUNCTION,False,"CTRTEMP|CTIon","HCTION",False,src/math/ctdata.rs,done
hctrecom.f,HCTRECOM,FUNCTION,False,"CTRTEMP|CTRecomb","HCTRECOM",False,src/math/ctdata.rs,done
hedif.f,HEDIF,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|hediff","",True,,pending
hephot.f,HEPHOT,FUNCTION,True,"","HEPHOT",False,src/math/hephot.rs,done
hesol6.f,HESOL6,SUBROUTINE,False,"BASICS|MODELQ|PRSAUX","MATINV",False,,pending
hesolv.f,HESOLV,SUBROUTINE,False,"BASICS|MODELQ|PRSAUX","MATINV|STEQEQ|RHONEN|WNSTOR",True,,pending
hesolv.f,HESOLV,SUBROUTINE,False,"BASICS|MODELQ|PRSAUX","MATINV|RHONEN|STEQEQ|WNSTOR",True,,pending
hidalg.f,HIDALG,FUNCTION,True,"","HIDALG",False,src/math/hidalg.rs,done
ijali2.f,IJALI2,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","QUIT",True,,pending
ijalis.f,IJALIS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",True,,pending
incldy.f,INCLDY,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","RATMAT|LEVSOL|QUIT|WNSTOR|SABOLF",True,,pending
incldy.f,INCLDY,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","LEVSOL|RATMAT|WNSTOR|QUIT|SABOLF",True,,pending
indexx.f,INDEXX,SUBROUTINE,True,"","",False,src/math/indexx.rs,done
inicom.f,INICOM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|comgfs","",False,src/math/inicom.rs,done
inifrc.f,INIFRC,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ijflar","INDEXX",True,,pending
inifrs.f,INIFRS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","QUIT|INDEXX",True,,pending
inifrs.f,INIFRS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","INDEXX|QUIT",True,,pending
inifrt.f,INIFRT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ijflar","INDEXX",True,,pending
inilam.f,INILAM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR","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
inilam.f,INILAM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR","OPACF1|RTEFR1|COLIS|OPAINI|WNSTOR|STEQEQ|SABOLF|ELCOR|RATES1",False,,pending
initia.f,INITIA,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|STRPAR|INUNIT|freqcl","RDATA|RDATAX|OPADD0|STATE|DOPGAM|ODFHYS|INTERP|LINSET|NSTPAR|QUIT|LINSPL|INIFRC|READBF",True,,pending
inkul.f,INKUL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|COLKUR|LINED","",True,,pending
inpdis.f,INPDIS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|relcor","GRCOR",True,,pending
inpmod.f,INPMOD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|eospar","RATMAT|LEVSOL|QUIT|WNSTOR|SABOLF|KURUCZ|MOLEQ|INCLDY",True,,pending
inpmod.f,INPMOD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|eospar","LEVSOL|RATMAT|WNSTOR|QUIT|SABOLF|MOLEQ|KURUCZ|INCLDY",True,,pending
interp.f,INTERP,SUBROUTINE,True,"BASICS","",False,src/math/interp.rs,done
inthyd.f,INTHYD,SUBROUTINE,False,"BASICS|MODELQ","DIVSTR",False,src/math/inthyd.rs,done
intlem.f,INTLEM,SUBROUTINE,False,"BASICS|MODELQ","INTHYD",False,src/math/intlem.rs,done
intxen.f,INTXEN,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/intxen.rs,done
irc.f,IRC,SUBROUTINE,True,"","EXPINX|SZIRC",False,src/math/irc.rs,done
iroset.f,IROSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|LINED","INKUL|QUIT|LEVCD",True,,pending
kurucz.f,KURUCZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|temlim","RHONEN|RATMAT|LEVSOL|QUIT|WNSTOR|SABOLF|MOLEQ",True,,pending
iroset.f,IROSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|LINED","LEVCD|QUIT|INKUL",True,,pending
kurucz.f,KURUCZ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|temlim","LEVSOL|RATMAT|WNSTOR|RHONEN|QUIT|SABOLF|MOLEQ",True,,pending
lagran.f,LAGRAN,SUBROUTINE,True,"","",False,src/math/interpolate.rs,done
laguer.f,LAGUER,SUBROUTINE,False,"","",True,src/math/laguer.rs,done
lemini.f,LEMINI,SUBROUTINE,False,"BASICS|MODELQ","",True,,pending
levcd.f,LEVCD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|COLKUR","QUIT|INDEXX",True,,pending
levgrp.f,LEVGRP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",False,,pending
levcd.f,LEVCD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|COLKUR","INDEXX|QUIT",True,,pending
levgrp.f,LEVGRP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",False,src/math/levgrp.rs,done
levset.f,LEVSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT",False,,pending
levsol.f,LEVSOL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","LINEQS",False,src/math/levsol.rs,done
lineqs.f,LINEQS,SUBROUTINE,True,"BASICS","",False,src/math/lineqs.rs,done
linpro.f,LINPRO,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|quasun","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
linpro.f,LINPRO,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|quasun","STARK0|DOPGAM|INTLEM|DIVSTR|INTXEN",False,,pending
linsel.f,LINSEL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","OPAINI|QUIT|OPACF1|RTEFR1",True,,pending
linset.f,LINSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","IJALIS|STARK0|QUIT|DIVSTR",True,,pending
linspl.f,LINSPL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/linspl.rs,done
locate.f,LOCATE,SUBROUTINE,True,"","",False,src/math/locate.rs,done
ltegr.f,LTEGR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","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
ltegr.f,LTEGR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","INTERP|WNSTOR|STEQEQ|QUIT|ROSSOP|CONOUT",True,,pending
ltegrd.f,LTEGRD,SUBROUTINE,False,"BASICS|MODELQ|FACTRS|CUBCON|TOTJHK|FLXAUX|PRSAUX","TEMPER|INTERP|ZMRHO|WNSTOR|STEQEQ|ELDENS|QUIT|CONOUT",True,,pending
lucy.f,LUCY,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ITERAT|ALIPAR|ARRAY1","OPACFL|RTEFR1|COLIS|WNSTOR|STEQEQ|SABOLF|ELCOR|OPAINI",True,,pending
lymlin.f,LYMLIN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","STARK0|DIVSTR",True,,pending
matcon.f,MATCON,SUBROUTINE,False,"BASICS|MODELQ|ARRAY1|CUBCON","CONVEC",False,,pending
matgen.f,MATGEN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","BRE|BRTE|BREZ|BHEZ|BHED|MATCON|EMAT|SABOLF|BRTEZ|BHE|BPOP",False,,pending
matgen.f,MATGEN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR","BHED|BRTE|BHEZ|BREZ|MATCON|BRTEZ|EMAT|BHE|SABOLF|BPOP|BRE",False,,pending
matinv.f,MATINV,SUBROUTINE,True,"BASICS","",False,src/math/matinv.rs,done
meanop.f,MEANOP,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC","",False,src/math/meanop.rs,done
meanopt.f,MEANOPT,SUBROUTINE,False,"BASICS|MODELQ","OPCTAB",False,,pending
minv3.f,MINV3,SUBROUTINE,True,"","",False,src/math/minv3.rs,done
moleq.f,MOLEQ,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|ioniz2|COMFH1|hmolab|eospar|entrop|terden|adchar|moldat","MPARTF|RUSSEL",True,,pending
moleq.f,MOLEQ,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|COMFH1|entrop|moldat|eospar|adchar|ioniz2|terden|hmolab","RUSSEL|MPARTF",True,,pending
mpartf.f,MPARTF,SUBROUTINE,False,"moldat","",True,,pending
newdm.f,NEWDM,SUBROUTINE,False,"BASICS|MODELQ|PRSAUX|FLXAUX|FACTRS","TEMPER|INTERP",True,,pending
newdmt.f,NEWDMT,SUBROUTINE,False,"BASICS|MODELQ|PRSAUX|FLXAUX|FACTRS","TEMPER|GRIDP|INTERP",True,,pending
newdm.f,NEWDM,SUBROUTINE,False,"BASICS|MODELQ|FACTRS|PRSAUX|FLXAUX","INTERP|TEMPER",True,,pending
newdmt.f,NEWDMT,SUBROUTINE,False,"BASICS|MODELQ|FACTRS|PRSAUX|FLXAUX","GRIDP|INTERP|TEMPER",True,,pending
newpop.f,NEWPOP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",True,,pending
nstout.f,NSTOUT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR","QUIT",True,,pending
nstpar.f,NSTPAR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|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
nstpar.f,NSTPAR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|imucnn|temlim|adiaba|ifpzpa|derdif|ichndm|ipricr|deridt|hediff|FLXAUX|irwint|moldat|quasun|freqcl|icnrsp","QUIT|GETWRD",True,,pending
odf1.f,ODF1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","DWNFR|ODFHST|DIVSTR",True,,pending
odffr.f,ODFFR,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","QUIT",False,,pending
odfhst.f,ODFHST,SUBROUTINE,False,"BASICS|MODELQ|ODFPAR","",False,src/math/odfhst.rs,done
odfhyd.f,ODFHYD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","DIVSTR|INDEXX|ODFHST",False,,pending
odfhys.f,ODFHYS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","STARK0|ODFFR|IJALIS",False,,pending
odfhyd.f,ODFHYD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","INDEXX|ODFHST|DIVSTR",False,,pending
odfhys.f,ODFHYS,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","IJALIS|STARK0|ODFFR",False,,pending
odfmer.f,ODFMER,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR","ODFHYD",False,,pending
odfset.f,ODFSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|STFCR","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
odfset.f,ODFSET,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|STFCR","IJALIS|QUIT",True,,pending
opacf0.f,OPACF0,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|hmolab","GFREE0|DWNFR1|DWNFR0|WNSTOR|LINPRO|SABOLF|SGMER1|OPADD|OPACT1",False,,pending
opacf1.f,OPACF1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ipricr|hmolab","QUASIM|GHYDOP|DWNFR1|LYMLIN|SGMER1|OPADD|PRD|OPACT1",True,,pending
opacfa.f,OPACFA,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|COOLCO","SGMER1|OPADD|PRD|DWNFR1",False,,pending
opacfd.f,OPACFD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT|dsctva|hmolab|rhoder","OPACTD|QUASIM|DWNFR1|LYMLIN|OPCTAB|SGMER1|OPADD|PRD|GFREED",True,,pending
opacfl.f,OPACFL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","SGMER1|OPADD|DWNFR1",False,,pending
opact1.f,OPACT1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|hmolab","OPCTAB",False,,pending
opactd.f,OPACTD,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ARRAY1|ITERAT|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
opactd.f,OPACTD,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ARRAY1|ITERAT|dsctva|hmolab|rhoder","OPCTAB",False,,pending
opactr.f,OPACTR,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ATOMIC|dsctva|hmolab|grdpra","RATMAL|OPACF1|LEVSOL|WNSTOR|STEQEQ|ELDENS|SABOLF|OPAINI|PGSET",False,,pending
opadd.f,OPADD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|eospar","CIA_H2H|CIA_H2HE|CIA_HHE|H2MINUS|CIA_H2H2",False,,pending
opadd0.f,OPADD0,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT",False,,pending
opahst.f,OPAHST,SUBROUTINE,False,"BASICS|ODFPAR","STARK0",True,,pending
opaini.f,OPAINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","REFLEV|WNSTOR|SABOLF|DWNFR0|LINPRO|LEVGRP",False,,pending
opaini.f,OPAINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","DWNFR0|REFLEV|WNSTOR|LINPRO|SABOLF|LEVGRP",False,,pending
opctab.f,OPCTAB,SUBROUTINE,False,"BASICS|MODELQ","RAYLEIGH",False,,pending
opdata.f,OPDATA,SUBROUTINE,False,"TOPB","",True,,pending
opfrac.f,OPFRAC,SUBROUTINE,False,"pfoptb","",True,,pending
osccor.f,OSCCOR,SUBROUTINE,False,"BASICS|MODELQ|ITERAT","",True,,pending
outpri.f,OUTPRI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|grdpra","OPACF1|LEVSOL|WNSTOR|SABOLF|RATMAL",True,,pending
outpri.f,OUTPRI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|grdpra","RATMAL|OPACF1|LEVSOL|WNSTOR|SABOLF",True,,pending
output.f,OUTPUT,SUBROUTINE,False,"BASICS|MODELQ","",True,,pending
partf.f,PARTF,SUBROUTINE,False,"BASICS|PFSTDS|irwint","PFNI|OPFRAC|PFHEAV|PFCNO|PFSPEC|MPARTF|PFFE",False,,pending
partf.f,PARTF,SUBROUTINE,False,"BASICS|irwint|PFSTDS","PFFE|PFNI|OPFRAC|MPARTF|PFHEAV|PFSPEC|PFCNO",False,,pending
pfcno.f,PFCNO,SUBROUTINE,True,"BASICS","",False,src/math/pfcno.rs,done
pffe.f,PFFE,SUBROUTINE,True,"","",False,src/math/pffe.rs,done
pfheav.f,PFHEAV,SUBROUTINE,False,"","",True,,pending
@ -191,59 +191,59 @@ pgset.f,PGSET,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|rybpgs|grdpra","TRIDAG",Tru
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
princ.f,PRINC,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","LINPRO|DWNFR|OPACF1|SABOLF",True,,pending
prnt.f,PRNT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","SABOLF",True,,pending
profil.f,PROFIL,FUNCTION,False,"BASICS|ATOMIC|MODELQ|quasun","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
profil.f,PROFIL,FUNCTION,False,"BASICS|ATOMIC|MODELQ|quasun","PROFIL|STARK0|DIVSTR",False,src/math/profil.rs,done
profsp.f,PROFSP,FUNCTION,False,"BASICS|ATOMIC|MODELQ","PROFSP|SABOLF",False,,pending
prsent.f,PRSENT,SUBROUTINE,False,"tdflag|tdedge|THERM|TABLTD","",True,,pending
psolve.f,PSOLVE,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/psolve.rs,done
pzert.f,PZERT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,,pending
pzert.f,PZERT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/pzert.rs,done
pzeval.f,PZEVAL,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|icnrsp","CONOUT",True,,pending
pzevld.f,PZEVLD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1|PRSAUX|DEPTDR|ifpzpa|grdpra","",False,,pending
pzevld.f,PZEVLD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1|ifpzpa|PRSAUX|grdpra|DEPTDR","",False,src/math/pzevld.rs,done
quartc.f,QUARTC,SUBROUTINE,False,"","",True,src/math/quartc.rs,done
quasim.f,QUASIM,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|quasun","ALLARD",False,,pending
quit.f,QUIT,SUBROUTINE,False,"","",True,src/math/quit.rs,done
radpre.f,RADPRE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","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
radpre.f,RADPRE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","QUIT|INDEXX|OPACF1|RTEFR1",True,,pending
radtot.f,RADTOT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT|OPTDPT|TOTJHK|SURFEX","OPAINI|OPACF1|RTEFR1",False,,pending
raph.f,RAPH,FUNCTION,True,"","RAPH",False,src/math/raph.rs,done
rates1.f,RATES1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT","ROSSTD|RTEFR1|OPACF1",False,,pending
rates1.f,RATES1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT","ROSSTD|OPACF1|RTEFR1",False,,pending
ratmal.f,RATMAL,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/ratmal.rs,done
ratmat.f,RATMAT,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","REFLEV",False,,pending
ratsp1.f,RATSP1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","ROSSTD|RTEFR1|OPACF1",True,,pending
ratsp1.f,RATSP1,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT","ROSSTD|OPACF1|RTEFR1",True,,pending
rayini.f,RAYINI,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC","RAYLEIGH",True,,pending
rayleigh.f,RAYLEIGH,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|RAYSCT|eospar","",False,src/math/rayleigh.rs,done
rayset.f,RAYSET,SUBROUTINE,False,"BASICS|MODELQ","",False,src/math/rayset.rs,done
rdata.f,RDATA,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|STRPAR|imodlc|INUNIT","LINSET|QUIT|RDATAX|DOPGAM",True,,pending
rdata.f,RDATA,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|STRPAR|INUNIT|imodlc","RDATAX|LINSET|QUIT|DOPGAM",True,,pending
rdatax.f,RDATAX,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","BKHSGO",True,,pending
readbf.f,READBF,SUBROUTINE,False,"BASICS","",True,,pending
rechck.f,RECHCK,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","RTEFR1|OPACF1",True,,pending
rechck.f,RECHCK,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","OPACF1|RTEFR1",True,,pending
reflev.f,REFLEV,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",False,,pending
reiman.f,REIMAN,FUNCTION,True,"","REIMAN",False,src/math/reiman.rs,done
resolv.f,RESOLV,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR|ARRAY1|icnrsp","RTEFR1|ELCOR|STEQEQ|NEWPOP|OPAINI|ROSSTD|CONOUT|TIMING|PRD|TAUFR1|RATES1|OPACF1",True,,pending
resolv.f,RESOLV,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR|ARRAY1|icnrsp","NEWPOP|TAUFR1|TIMING|OPACF1|RTEFR1|ROSSTD|STEQEQ|CONOUT|ELCOR|OPAINI|RATES1|PRD",True,,pending
rhoeos.f,RHOEOS,FUNCTION,False,"BASICS|MODELQ","PRSENT|RHOEOS",False,,pending
rhonen.f,RHONEN,SUBROUTINE,False,"BASICS|MODELQ","ELDENS",False,,pending
rhsgen.f,RHSGEN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|CUBCON","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
rhsgen.f,RHSGEN,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|CUBCON","MATINV|STATE|RATMAT|COMPT0|SABOLF|CONVEC|LEVGRP",False,,pending
rossop.f,ROSSOP,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ALIPAR","WNSTOR|STEQEQ|ELDENS|OPACF0|MEANOP|MEANOPT",False,,pending
rosstd.f,ROSSTD,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR","",True,,pending
rte_sc.f,RTE_SC,SUBROUTINE,True,"BASICS","",False,src/math/rte_sc.rs,done
rteang.f,RTEANG,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|SURFEX|EXTINT","GAULEG",False,,pending
rteang.f,RTEANG,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|EXTINT|SURFEX","GAULEG",False,,pending
rtecf0.f,RTECF0,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT|auxcbc|AUXRTE","",False,,pending
rtecf1.f,RTECF1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|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
rtecf1.f,RTECF1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT|comgfs|AUXRTE|EXTINT|SURFEX","RTEFE2|RTESOL|RTECF0",True,,pending
rtecmc.f,RTECMC,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|comgfs|AUXRTE","MATINV|OPACF1|RTECF0",False,,pending
rtecmu.f,RTECMU,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT|AUXRTE","RTESOL|OPACF1|RTECF0|GAULEG",True,,pending
rtecom.f,RTECOM,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT|comgfs|AUXRTE","OPACF1|RTECF0|RTECF1",False,,pending
rtedf1.f,RTEDF1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|OPTDPT","",False,src/math/rtedf1.rs,done
rtedf2.f,RTEDF2,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR","",False,,pending
rtefe2.f,RTEFE2,SUBROUTINE,True,"BASICS","",False,src/math/rtefe2.rs,done
rtefr1.f,RTEFR1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT","RTEDF2|MATINV|RTECF1|RTEDF1|MINV3|RTESOL",True,,pending
rtefr1.f,RTEFR1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT","MATINV|RTESOL|RTEDF1|RTECF1|RTEDF2|MINV3",True,,pending
rteint.f,RTEINT,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT","MATINV|OPACF1",True,,pending
rtesol.f,RTESOL,SUBROUTINE,True,"BASICS","",False,src/math/rtesol.rs,done
russel.f,RUSSEL,SUBROUTINE,False,"BASICS|MODELQ|COMFH1","MPARTF",True,,pending
rybchn.f,RYBCHN,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|ALIPAR|ARRAY1|rybpgs|grdpra","PGSET|ELDENS",True,,pending
rybene.f,RYBENE,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ARRAY1|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
rybene.f,RYBENE,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ARRAY1|RYBMTX|deridt|CUBCON","CONVEC",False,,pending
rybheq.f,RYBHEQ,SUBROUTINE,False,"BASICS|MODELQ|rybpgs|grdpra","OPACF1|RTEFR1|WNSTOR|STEQEQ|ELDENS|ELCOR|OPAINI|PGSET",True,,pending
rybmat.f,RYBMAT,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ARRAY1|dsctva|RYBMTX","",False,,pending
rybsol.f,RYBSOL,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|ALIPAR|ARRAY1|ITERAT|RYBMTX|imodlc","RTEFR1|ALIFR1|TRIDAG|STEQEQ|ROSSTD|RYBMAT|OPACTR|OPACFD|RYBCHN|LINEQS",True,,pending
rybsol.f,RYBSOL,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|ALIPAR|ARRAY1|ITERAT|imodlc|RYBMTX","RYBMAT|RTEFR1|ROSSTD|OPACTR|LINEQS|STEQEQ|ALIFR1|TRIDAG|RYBCHN|OPACFD",True,,pending
sabolf.f,SABOLF,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","PARTF",False,,pending
sbfch.f,SBFCH,FUNCTION,True,"","SBFCH",False,src/math/sbfch.rs,done
sbfhe1.f,SBFHE1,FUNCTION,False,"BASICS|ATOMIC","SBFHE1|QUIT",True,src/math/sbfhe1.rs,done
@ -251,7 +251,7 @@ 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
settrm.f,SETTRM,SUBROUTINE,False,"tdflag|tdedge|THERM|TABLTD","PRSENT",True,,pending
sffhmi.f,SFFHMI,FUNCTION,True,"","SFFHMI",False,src/math/sffhmi.rs,done
sffhmi_add.f,SFFHMI_ADD,FUNCTION,True,"","SFFHMI_ADD",False,src/math/sffhmi_add.rs,done
sghe12.f,SGHE12,FUNCTION,True,"","SGHE12",False,src/math/sghe12.rs,done
@ -259,25 +259,25 @@ sgmer0.f,SGMER0,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",False,src/math/sgmer.
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
sigk.f,SIGK,FUNCTION,False,"BASICS|ATOMIC","SPSIGK|SIGK",False,,pending
sigmar.f,SIGMAR,FUNCTION,False,"BASICS","SIGMAR|LAGUER",True,,pending
solve.f,SOLVE,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|ARRAY1|ALIPAR|CMATZD","MATINV|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
solve.f,SOLVE,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|ARRAY1|ALIPAR|CMATZD","MATINV|PRCHAN|RHSGEN|WNSTOR|MATGEN",True,,pending
solves.f,SOLVES,SUBROUTINE,False,"BASICS|ITERAT|MODELQ|ARRAY1|ALIPAR|STOMAT|CMATZD","MATINV|PRCHAN|RHSGEN|WNSTOR|MATGEN",True,,pending
spsigk.f,SPSIGK,SUBROUTINE,True,"","CARBON",False,src/math/spsigk.rs,done
srtfrq.f,SRTFRQ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","QUIT|INDEXX",True,,pending
srtfrq.f,SRTFRQ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","INDEXX|QUIT",True,,pending
stark0.f,STARK0,SUBROUTINE,True,"","",False,src/math/stark0.rs,done
starka.f,STARKA,FUNCTION,False,"BASICS|MODELQ","STARKA",False,src/math/starka.rs,done
start.f,START,SUBROUTINE,False,"BASICS|hediff","",True,,pending
state.f,STATE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|PFSTDS|terden","PARTF|OPFRAC",True,,pending
steqeq.f,STEQEQ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|POPSTR|PPAPAR","MOLEQ|LEVSOL|SABOLF|RATMAT",False,,pending
state.f,STATE,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|terden|PFSTDS","OPFRAC|PARTF",True,,pending
steqeq.f,STEQEQ,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT|POPSTR|PPAPAR","LEVSOL|RATMAT|SABOLF|MOLEQ",False,,pending
switch.f,SWITCH,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ","",True,,pending
szirc.f,SZIRC,SUBROUTINE,True,"","EINT",False,src/math/szirc.rs,done
tabini.f,TABINI,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|intcff|abntab|eletab","",True,,pending
tabini.f,TABINI,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|eletab|abntab|intcff","",True,,pending
tabint.f,TABINT,SUBROUTINE,False,"BASICS|MODELQ|ATOMIC|intcff","",False,,pending
taufr1.f,TAUFR1,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT","",False,,pending
tdpini.f,TDPINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR","GFREE0",False,src/math/tdpini.rs,done
temcor.f,TEMCOR,SUBROUTINE,False,"BASICS|MODELQ|ARRAY1|ALIPAR|CUBCON","MEANOP|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
temcor.f,TEMCOR,SUBROUTINE,False,"BASICS|MODELQ|ARRAY1|ALIPAR|CUBCON","MEANOP|WNSTOR|STEQEQ|ELDENS|OPACF0|CONVEC",True,,pending
temper.f,TEMPER,SUBROUTINE,False,"BASICS|MODELQ|ALIPAR|FACTRS|PRSAUX|FLXAUX","WNSTOR|TLOCAL|STEQEQ|ELDENS|OPACF0|MEANOP|MEANOPT",True,,pending
timing.f,TIMING,SUBROUTINE,False,"","",True,,pending
tiopf.f,TIOPF,SUBROUTINE,True,"","",False,src/math/tiopf.rs,done
tlocal.f,TLOCAL,SUBROUTINE,False,"BASICS|MODELQ|FACTRS|FLXAUX","QUARTC",False,,pending
@ -285,14 +285,14 @@ 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
trmder.f,TRMDER,SUBROUTINE,False,"BASICS|derdif|terden|adiaba","ELDENS",False,,pending
trmdrt.f,TRMDRT,SUBROUTINE,False,"BASICS|tdedge|CC|tdflag|CONVOUT","PRSENT",False,,pending
ubeta.f,UBETA,FUNCTION,True,"","UBETA|LAGRAN",False,src/math/ubeta.rs,done
vern16.f,VERN16,FUNCTION,True,"BASICS","VERN16",False,src/math/vern16.rs,done
vern18.f,VERN18,FUNCTION,True,"BASICS","VERN18",False,src/math/vern18.rs,done
vern20.f,VERN20,FUNCTION,True,"BASICS","VERN20",False,src/math/vern20.rs,done
vern26.f,VERN26,FUNCTION,True,"BASICS","VERN26",False,src/math/vern26.rs,done
verner.f,VERNER,FUNCTION,False,"BASICS|ATOMIC","VERNER|QUIT",False,src/math/verner.rs,done
verner.f,VERNER,FUNCTION,False,"BASICS|ATOMIC","QUIT|VERNER",False,src/math/verner.rs,done
visini.f,VISINI,SUBROUTINE,False,"BASICS|ATOMIC|MODELQ|ITERAT","",True,,pending
voigt.f,VOIGT,FUNCTION,True,"","VOIGT",False,src/math/voigt.rs,done
voigte.f,VOIGTE,FUNCTION,True,"","VOIGTE",False,src/math/voigte.rs,done

1 fortran_file unit_name unit_type is_pure common_deps call_deps has_io rust_module status
6 alifr3.f ALIFR3 SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR False src/math/alifr3.rs done
7 alifr6.f ALIFR6 SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR False src/math/alifr6.rs done
8 alifrk.f ALIFRK SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR False src/math/alifrk.rs done
9 alisk1.f ALISK1 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT ALIFRK|RTEFR1|OPACF1|ROSSTD ALIFRK|ROSSTD|OPACF1|RTEFR1 True pending
10 alisk2.f ALISK2 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT ALIFRK|RTEFR1|OPACF1|ROSSTD ALIFRK|ROSSTD|OPACF1|RTEFR1 True pending
11 alist1.f ALIST1 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT ROSSTD|RTEFR1|ALIFR1|OPACFD OPACFD|ALIFR1|ROSSTD|RTEFR1 True pending
12 alist2.f ALIST2 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT RTEFR1|ALIFR1|QUIT|ROSSTD|OPACFD RTEFR1|ROSSTD|ALIFR1|QUIT|OPACFD True pending
13 allard.f ALLARD SUBROUTINE False BASICS|callarda|callardc|quasun|callardb|callardg|calphatd BASICS|calphatd|callarda|callardc|callardg|callardb|quasun ALLARDT True pending
14 allardt.f ALLARDT SUBROUTINE False BASICS|calphatd False src/math/allardt.rs done
15 angset.f ANGSET SUBROUTINE True BASICS GAULEG False src/math/angset.rs done
16 betah.f BETAH FUNCTION True BETAH False src/math/betah.rs done
17 bhe.f BHE SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR False src/math/bhe.rs pending done
18 bhed.f BHED SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|SURFEX|CMATZD BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|CMATZD|SURFEX False src/math/bhe.rs pending done
19 bhez.f BHEZ SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|SURFEX False src/math/bhe.rs pending done
20 bkhsgo.f BKHSGO SUBROUTINE True False src/math/bkhsgo.rs done
21 bpop.f BPOP SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR|ITERAT BPOPF|RATMAT|BPOPT|LEVSOL|MATINV|BPOPC|BPOPE|LEVGRP MATINV|BPOPT|BPOPF|LEVSOL|RATMAT|BPOPC|BPOPE|LEVGRP False pending
22 bpopc.f BPOPC SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR|ADCHAR STATE False pending
23 bpope.f BPOPE SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT|ARRAY1 SGMER1|DWNFR1 False pending
24 bpopf.f BPOPF SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|ODFPAR False src/math/bpopf.rs done
30 butler.f BUTLER SUBROUTINE True False src/math/butler.rs done
31 carbon.f CARBON SUBROUTINE True False src/math/carbon.rs done
32 ceh12.f CEH12 FUNCTION True CEH12 False src/math/ceh12.rs done
33 change.f CHANGE SUBROUTINE False BASICS|ATOMIC|MODELQ READBF|STEQEQ STEQEQ|READBF True pending
34 chckse.f CHCKSE SUBROUTINE False BASICS|ATOMIC|MODELQ SABOLF True pending
35 chctab.f CHCTAB SUBROUTINE False BASICS|MODELQ|abntab True pending
36 cheav.f CHEAV FUNCTION False BASICS|ATOMIC QUIT|CHEAV CHEAV|QUIT True pending
37 cheavj.f CHEAVJ FUNCTION False BASICS|ATOMIC CHEAVJ|QUIT QUIT|CHEAVJ True pending
38 cia_h2h.f CIA_H2H SUBROUTINE False LOCATE|IF True pending
39 cia_h2h2.f CIA_H2H2 SUBROUTINE False LOCATE|IF True pending
40 cia_h2he.f CIA_H2HE SUBROUTINE False LOCATE|IF True pending
41 cia_hhe.f CIA_HHE SUBROUTINE False LOCATE|IF True pending
42 cion.f CION FUNCTION True CION False src/math/cion.rs done
43 ckoest.f CKOEST FUNCTION True BASICS CKOEST False src/math/ckoest.rs done
44 colh.f COLH SUBROUTINE False BASICS|ATOMIC|MODELQ CSPEC|IRC|BUTLER CSPEC|BUTLER|IRC False pending
45 colhe.f COLHE SUBROUTINE False BASICS|ATOMIC CSPEC|COLLHE|IRC COLLHE|CSPEC|IRC False pending
46 colis.f COLIS SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|CTRTEMP CSPEC|COLH|IRC|COLHE CSPEC|COLH|COLHE|IRC False pending
47 collhe.f COLLHE SUBROUTINE True False src/math/collhe.rs done
48 column.f COLUMN SUBROUTINE False BASICS|MODELQ|relcor True pending
49 compt0.f COMPT0 SUBROUTINE False BASICS|MODELQ|ALIPAR|ITERAT|auxcbc False src/math/compt0.rs pending done
50 comset.f COMSET SUBROUTINE False BASICS|MODELQ|comgfs|auxcbc False src/math/comset.rs pending done
51 concor.f CONCOR SUBROUTINE False BASICS|MODELQ CONOUT True pending
52 conout.f CONOUT SUBROUTINE False BASICS|MODELQ|ALIPAR|CUBCON MEANOPT|OPACF0|CONVEC|MEANOP MEANOP|MEANOPT|CONVEC|OPACF0 True pending
53 conref.f CONREF SUBROUTINE False BASICS|MODELQ|ARRAY1|imucnn|CUBCON STEQEQ|WNSTOR|CONVC1|CONOUT|CONVEC|ELDENS CONVC1|WNSTOR|STEQEQ|ELDENS|CONVEC|CONOUT True pending
54 contmd.f CONTMD SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR|PRSAUX|CUBCON MEANOP|CUBIC|STEQEQ|WNSTOR|OPACF0|CONOUT|CONVEC MEANOP|WNSTOR|STEQEQ|OPACF0|CONVEC|CONOUT|CUBIC True pending
55 contmp.f CONTMP SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR|ichndm|CUBCON MEANOP|CUBIC|STEQEQ|WNSTOR|OPACF0|CONOUT|CONVEC|MEANOPT|ELDENS MEANOP|WNSTOR|STEQEQ|ELDENS|OPACF0|CONVEC|MEANOPT|CONOUT|CUBIC True pending
56 convc1.f CONVC1 SUBROUTINE False BASICS|CUBCON TRMDER|TRMDRT False pending
57 convec.f CONVEC SUBROUTINE False BASICS|CUBCON TRMDER|TRMDRT False pending
58 coolrt.f COOLRT SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT|COOLCO RTEFR1|OPACFA OPACFA|RTEFR1 True pending
59 corrwm.f CORRWM SUBROUTINE False BASICS|ATOMIC|MODELQ QUIT True pending
60 cross.f CROSS FUNCTION False BASICS|ATOMIC|MODELQ CROSS False src/math/cross.rs done
61 crossd.f CROSSD FUNCTION False BASICS|ATOMIC|MODELQ CROSSD False src/math/cross.rs done
62 cspec.f CSPEC SUBROUTINE False BASICS|ATOMIC QUIT False pending
63 ctdata.f CTDATA BLOCK DATA False CTRecomb|CTIon CTIon|CTRecomb False src/math/ctdata.rs done
64 cubic.f CUBIC SUBROUTINE False BASICS|CUBCON False src/math/cubic.rs done
65 dielrc.f DIELRC SUBROUTINE True False src/math/dielrc.rs done
66 dietot.f DIETOT SUBROUTINE False BASICS|ATOMIC|MODELQ DIELRC True pending
72 dwnfr0.f DWNFR0 SUBROUTINE False BASICS|MODELQ False src/math/dwnfr0.rs done
73 dwnfr1.f DWNFR1 SUBROUTINE False BASICS|MODELQ False src/math/dwnfr1.rs done
74 eint.f EINT SUBROUTINE True EXPINX False src/math/expint.rs done
75 elcor.f ELCOR SUBROUTINE False BASICS|ATOMIC|MODELQ|ADCHAR MOLEQ|STATE|STEQEQ|WNSTOR WNSTOR|STEQEQ|STATE|MOLEQ True pending
76 eldenc.f ELDENC SUBROUTINE False BASICS|MODELQ|ATOMIC|eospar|eletab|hmolab BASICS|MODELQ|ATOMIC|eletab|hmolab|eospar MOLEQ|STATE|RHONEN RHONEN|STATE|MOLEQ True pending
77 eldens.f ELDENS SUBROUTINE False BASICS|MODELQ|ATOMIC|eospar|terden BASICS|MODELQ|ATOMIC|terden|eospar STATE|ENTENE|MOLEQ|MPARTF|LINEQS ENTENE|STATE|MPARTF|LINEQS|MOLEQ True pending
78 emat.f EMAT SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR False src/math/emat.rs done
79 entene.f ENTENE SUBROUTINE False BASICS|ATOMIC|MODELQ MPARTF False pending
80 erfcin.f ERFCIN FUNCTION True ERFCIN False src/math/erfcx.rs done
87 gamsp.f GAMSP SUBROUTINE True BASICS False src/math/gamsp.rs done
88 gauleg.f GAULEG SUBROUTINE True False src/math/gauleg.rs done
89 gaunt.f GAUNT FUNCTION True GAUNT False src/math/gaunt.rs done
90 getlal.f GETLAL SUBROUTINE False BASICS|callarda|callardc|quasun|callardb|callardg|calphatd BASICS|callardc|callarda|calphatd|callardg|callardb|quasun True pending
91 getwrd.f GETWRD SUBROUTINE True False src/math/getwrd.rs done
92 gfree0.f GFREE0 SUBROUTINE False BASICS|MODELQ False src/math/gfree.rs done
93 gfree1.f GFREE1 FUNCTION False BASICS|MODELQ GFREE1 False src/math/gfree.rs done
94 gfreed.f GFREED SUBROUTINE False BASICS|MODELQ False src/math/gfree.rs done
95 ghydop.f GHYDOP SUBROUTINE False BASICS|MODELQ|ATOMIC|intcfg False src/math/ghydop.rs pending done
96 gntk.f GNTK FUNCTION True GNTK False src/math/gntk.rs done
97 gomini.f GOMINI SUBROUTINE False BASICS|MODELQ|intcfg True pending
98 grcor.f GRCOR SUBROUTINE True False src/math/grcor.rs done
99 greyd.f GREYD SUBROUTINE False BASICS|MODELQ|ATOMIC|ALIPAR MEANOP|RHONEN|STEQEQ|WNSTOR|OPACF0 WNSTOR|RHONEN|STEQEQ|OPACF0|MEANOP True pending
100 gridp.f GRIDP SUBROUTINE True BASICS False src/math/gridp.rs done
101 h2minus.f H2MINUS SUBROUTINE False BASICS LOCATE True pending
102 hction.f HCTION FUNCTION False CTIon|CTRTEMP CTRTEMP|CTIon HCTION False src/math/ctdata.rs done
103 hctrecom.f HCTRECOM FUNCTION False CTRecomb|CTRTEMP CTRTEMP|CTRecomb HCTRECOM False src/math/ctdata.rs done
104 hedif.f HEDIF SUBROUTINE False BASICS|MODELQ|ATOMIC|hediff True pending
105 hephot.f HEPHOT FUNCTION True HEPHOT False src/math/hephot.rs done
106 hesol6.f HESOL6 SUBROUTINE False BASICS|MODELQ|PRSAUX MATINV False pending
107 hesolv.f HESOLV SUBROUTINE False BASICS|MODELQ|PRSAUX MATINV|STEQEQ|RHONEN|WNSTOR MATINV|RHONEN|STEQEQ|WNSTOR True pending
108 hidalg.f HIDALG FUNCTION True HIDALG False src/math/hidalg.rs done
109 ijali2.f IJALI2 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR QUIT True pending
110 ijalis.f IJALIS SUBROUTINE False BASICS|ATOMIC|MODELQ True pending
111 incldy.f INCLDY SUBROUTINE False BASICS|ATOMIC|MODELQ RATMAT|LEVSOL|QUIT|WNSTOR|SABOLF LEVSOL|RATMAT|WNSTOR|QUIT|SABOLF True pending
112 indexx.f INDEXX SUBROUTINE True False src/math/indexx.rs done
113 inicom.f INICOM SUBROUTINE False BASICS|ATOMIC|MODELQ|comgfs False src/math/inicom.rs done
114 inifrc.f INIFRC SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ijflar INDEXX True pending
115 inifrs.f INIFRS SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR QUIT|INDEXX INDEXX|QUIT True pending
116 inifrt.f INIFRT SUBROUTINE False BASICS|ATOMIC|MODELQ|ijflar INDEXX True pending
117 inilam.f INILAM SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR ELCOR|RTEFR1|STEQEQ|COLIS|WNSTOR|OPAINI|SABOLF|RATES1|OPACF1 OPACF1|RTEFR1|COLIS|OPAINI|WNSTOR|STEQEQ|SABOLF|ELCOR|RATES1 False pending
118 initia.f INITIA SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|freqcl|STRPAR|INUNIT BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|STRPAR|INUNIT|freqcl STATE|LINSET|INIFRC|QUIT|ODFHYS|OPADD0|LINSPL|RDATA|NSTPAR|RDATAX|DOPGAM|READBF|INTERP RDATA|RDATAX|OPADD0|STATE|DOPGAM|ODFHYS|INTERP|LINSET|NSTPAR|QUIT|LINSPL|INIFRC|READBF True pending
119 inkul.f INKUL SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|LINED|COLKUR BASICS|ATOMIC|MODELQ|ODFPAR|COLKUR|LINED True pending
120 inpdis.f INPDIS SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|relcor GRCOR True pending
121 inpmod.f INPMOD SUBROUTINE False BASICS|ATOMIC|MODELQ|eospar RATMAT|LEVSOL|QUIT|WNSTOR|SABOLF|KURUCZ|MOLEQ|INCLDY LEVSOL|RATMAT|WNSTOR|QUIT|SABOLF|MOLEQ|KURUCZ|INCLDY True pending
122 interp.f INTERP SUBROUTINE True BASICS False src/math/interp.rs done
123 inthyd.f INTHYD SUBROUTINE False BASICS|MODELQ DIVSTR False src/math/inthyd.rs done
124 intlem.f INTLEM SUBROUTINE False BASICS|MODELQ INTHYD False src/math/intlem.rs done
125 intxen.f INTXEN SUBROUTINE False BASICS|MODELQ False src/math/intxen.rs done
126 irc.f IRC SUBROUTINE True EXPINX|SZIRC False src/math/irc.rs done
127 iroset.f IROSET SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|LINED INKUL|QUIT|LEVCD LEVCD|QUIT|INKUL True pending
128 kurucz.f KURUCZ SUBROUTINE False BASICS|ATOMIC|MODELQ|temlim RHONEN|RATMAT|LEVSOL|QUIT|WNSTOR|SABOLF|MOLEQ LEVSOL|RATMAT|WNSTOR|RHONEN|QUIT|SABOLF|MOLEQ True pending
129 lagran.f LAGRAN SUBROUTINE True False src/math/interpolate.rs done
130 laguer.f LAGUER SUBROUTINE False True src/math/laguer.rs done
131 lemini.f LEMINI SUBROUTINE False BASICS|MODELQ True pending
132 levcd.f LEVCD SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|COLKUR QUIT|INDEXX INDEXX|QUIT True pending
133 levgrp.f LEVGRP SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT False src/math/levgrp.rs pending done
134 levset.f LEVSET SUBROUTINE False BASICS|ATOMIC|MODELQ QUIT False pending
135 levsol.f LEVSOL SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT LINEQS False src/math/levsol.rs done
136 lineqs.f LINEQS SUBROUTINE True BASICS False src/math/lineqs.rs done
137 linpro.f LINPRO SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|quasun DIVSTR|INTLEM|STARK0|INTXEN|DOPGAM STARK0|DOPGAM|INTLEM|DIVSTR|INTXEN False pending
138 linsel.f LINSEL SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR OPAINI|QUIT|RTEFR1|OPACF1 OPAINI|QUIT|OPACF1|RTEFR1 True pending
139 linset.f LINSET SUBROUTINE False BASICS|ATOMIC|MODELQ STARK0|QUIT|DIVSTR|IJALIS IJALIS|STARK0|QUIT|DIVSTR True pending
140 linspl.f LINSPL SUBROUTINE False BASICS|ATOMIC|MODELQ False src/math/linspl.rs pending done
141 locate.f LOCATE SUBROUTINE True False src/math/locate.rs done
142 ltegr.f LTEGR SUBROUTINE False BASICS|ATOMIC|MODELQ ROSSOP|QUIT|STEQEQ|WNSTOR|CONOUT|INTERP INTERP|WNSTOR|STEQEQ|QUIT|ROSSOP|CONOUT True pending
143 ltegrd.f LTEGRD SUBROUTINE False BASICS|MODELQ|PRSAUX|TOTJHK|CUBCON|FLXAUX|FACTRS BASICS|MODELQ|FACTRS|CUBCON|TOTJHK|FLXAUX|PRSAUX TEMPER|QUIT|STEQEQ|WNSTOR|CONOUT|ZMRHO|ELDENS|INTERP TEMPER|INTERP|ZMRHO|WNSTOR|STEQEQ|ELDENS|QUIT|CONOUT True pending
144 lucy.f LUCY SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ITERAT|ALIPAR|ARRAY1 RTEFR1|ELCOR|STEQEQ|WNSTOR|COLIS|OPAINI|SABOLF|OPACFL OPACFL|RTEFR1|COLIS|WNSTOR|STEQEQ|SABOLF|ELCOR|OPAINI True pending
145 lymlin.f LYMLIN SUBROUTINE False BASICS|ATOMIC|MODELQ STARK0|DIVSTR True pending
146 matcon.f MATCON SUBROUTINE False BASICS|MODELQ|ARRAY1|CUBCON CONVEC False pending
147 matgen.f MATGEN SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR BRE|BRTE|BREZ|BHEZ|BHED|MATCON|EMAT|SABOLF|BRTEZ|BHE|BPOP BHED|BRTE|BHEZ|BREZ|MATCON|BRTEZ|EMAT|BHE|SABOLF|BPOP|BRE False pending
148 matinv.f MATINV SUBROUTINE True BASICS False src/math/matinv.rs done
149 meanop.f MEANOP SUBROUTINE False BASICS|MODELQ|ATOMIC False src/math/meanop.rs done
150 meanopt.f MEANOPT SUBROUTINE False BASICS|MODELQ OPCTAB False pending
151 minv3.f MINV3 SUBROUTINE True False src/math/minv3.rs done
152 moleq.f MOLEQ SUBROUTINE False BASICS|MODELQ|ATOMIC|ioniz2|COMFH1|hmolab|eospar|entrop|terden|adchar|moldat BASICS|MODELQ|ATOMIC|COMFH1|entrop|moldat|eospar|adchar|ioniz2|terden|hmolab MPARTF|RUSSEL RUSSEL|MPARTF True pending
153 mpartf.f MPARTF SUBROUTINE False moldat True pending
154 newdm.f NEWDM SUBROUTINE False BASICS|MODELQ|PRSAUX|FLXAUX|FACTRS BASICS|MODELQ|FACTRS|PRSAUX|FLXAUX TEMPER|INTERP INTERP|TEMPER True pending
155 newdmt.f NEWDMT SUBROUTINE False BASICS|MODELQ|PRSAUX|FLXAUX|FACTRS BASICS|MODELQ|FACTRS|PRSAUX|FLXAUX TEMPER|GRIDP|INTERP GRIDP|INTERP|TEMPER True pending
156 newpop.f NEWPOP SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT True pending
157 nstout.f NSTOUT SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR QUIT True pending
158 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 BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|imucnn|temlim|adiaba|ifpzpa|derdif|ichndm|ipricr|deridt|hediff|FLXAUX|irwint|moldat|quasun|freqcl|icnrsp QUIT|GETWRD True pending
159 odf1.f ODF1 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR DIVSTR|DWNFR|ODFHST DWNFR|ODFHST|DIVSTR True pending
160 odffr.f ODFFR SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR QUIT False pending
161 odfhst.f ODFHST SUBROUTINE False BASICS|MODELQ|ODFPAR False src/math/odfhst.rs done
162 odfhyd.f ODFHYD SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR DIVSTR|INDEXX|ODFHST INDEXX|ODFHST|DIVSTR False pending
163 odfhys.f ODFHYS SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR STARK0|ODFFR|IJALIS IJALIS|STARK0|ODFFR False pending
164 odfmer.f ODFMER SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR ODFHYD False pending
165 odfset.f ODFSET SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|STFCR QUIT|IJALIS IJALIS|QUIT True pending
166 opacf0.f OPACF0 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|hmolab OPADD|WNSTOR|DWNFR1|SABOLF|DWNFR0|GFREE0|OPACT1|SGMER1|LINPRO GFREE0|DWNFR1|DWNFR0|WNSTOR|LINPRO|SABOLF|SGMER1|OPADD|OPACT1 False pending
167 opacf1.f OPACF1 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ipricr|hmolab OPADD|DWNFR1|GHYDOP|QUASIM|OPACT1|PRD|SGMER1|LYMLIN QUASIM|GHYDOP|DWNFR1|LYMLIN|SGMER1|OPADD|PRD|OPACT1 True pending
168 opacfa.f OPACFA SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|COOLCO PRD|SGMER1|DWNFR1|OPADD SGMER1|OPADD|PRD|DWNFR1 False pending
169 opacfd.f OPACFD SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT|dsctva|hmolab|rhoder OPADD|GFREED|OPACTD|DWNFR1|QUASIM|PRD|SGMER1|OPCTAB|LYMLIN OPACTD|QUASIM|DWNFR1|LYMLIN|OPCTAB|SGMER1|OPADD|PRD|GFREED True pending
170 opacfl.f OPACFL SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR SGMER1|DWNFR1|OPADD SGMER1|OPADD|DWNFR1 False pending
171 opact1.f OPACT1 SUBROUTINE False BASICS|MODELQ|ALIPAR|hmolab OPCTAB False pending
172 opactd.f OPACTD SUBROUTINE False BASICS|MODELQ|ALIPAR|ARRAY1|ITERAT|rhoder|dsctva|hmolab BASICS|MODELQ|ALIPAR|ARRAY1|ITERAT|dsctva|hmolab|rhoder OPCTAB False pending
173 opactr.f OPACTR SUBROUTINE False BASICS|MODELQ|ALIPAR|ATOMIC|grdpra|dsctva|hmolab BASICS|MODELQ|ALIPAR|ATOMIC|dsctva|hmolab|grdpra OPACF1|LEVSOL|STEQEQ|WNSTOR|OPAINI|PGSET|SABOLF|ELDENS|RATMAL RATMAL|OPACF1|LEVSOL|WNSTOR|STEQEQ|ELDENS|SABOLF|OPAINI|PGSET False pending
174 opadd.f OPADD SUBROUTINE False BASICS|ATOMIC|MODELQ|eospar CIA_H2H|H2MINUS|CIA_H2HE|CIA_H2H2|CIA_HHE CIA_H2H|CIA_H2HE|CIA_HHE|H2MINUS|CIA_H2H2 False pending
175 opadd0.f OPADD0 SUBROUTINE False BASICS|ATOMIC|MODELQ QUIT False pending
176 opahst.f OPAHST SUBROUTINE False BASICS|ODFPAR STARK0 True pending
177 opaini.f OPAINI SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR REFLEV|WNSTOR|SABOLF|DWNFR0|LINPRO|LEVGRP DWNFR0|REFLEV|WNSTOR|LINPRO|SABOLF|LEVGRP False pending
178 opctab.f OPCTAB SUBROUTINE False BASICS|MODELQ RAYLEIGH False pending
179 opdata.f OPDATA SUBROUTINE False TOPB True pending
180 opfrac.f OPFRAC SUBROUTINE False pfoptb True pending
181 osccor.f OSCCOR SUBROUTINE False BASICS|MODELQ|ITERAT True pending
182 outpri.f OUTPRI SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|grdpra OPACF1|LEVSOL|WNSTOR|SABOLF|RATMAL RATMAL|OPACF1|LEVSOL|WNSTOR|SABOLF True pending
183 output.f OUTPUT SUBROUTINE False BASICS|MODELQ True pending
184 partf.f PARTF SUBROUTINE False BASICS|PFSTDS|irwint BASICS|irwint|PFSTDS PFNI|OPFRAC|PFHEAV|PFCNO|PFSPEC|MPARTF|PFFE PFFE|PFNI|OPFRAC|MPARTF|PFHEAV|PFSPEC|PFCNO False pending
185 pfcno.f PFCNO SUBROUTINE True BASICS False src/math/pfcno.rs done
186 pffe.f PFFE SUBROUTINE True False src/math/pffe.rs done
187 pfheav.f PFHEAV SUBROUTINE False True pending
191 prchan.f PRCHAN SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT True pending
192 prd.f PRD SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT DOPGAM False pending
193 prdini.f PRDINI SUBROUTINE False BASICS|ATOMIC|MODELQ False src/math/prdini.rs done
194 princ.f PRINC SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR LINPRO|SABOLF|OPACF1|DWNFR LINPRO|DWNFR|OPACF1|SABOLF True pending
195 prnt.f PRNT SUBROUTINE False BASICS|ATOMIC|MODELQ SABOLF True pending
196 profil.f PROFIL FUNCTION False BASICS|ATOMIC|MODELQ|quasun STARK0|PROFIL|DIVSTR PROFIL|STARK0|DIVSTR False src/math/profil.rs pending done
197 profsp.f PROFSP FUNCTION False BASICS|ATOMIC|MODELQ SABOLF|PROFSP PROFSP|SABOLF False pending
198 prsent.f PRSENT SUBROUTINE False tdedge|tdflag|THERM|TABLTD tdflag|tdedge|THERM|TABLTD True pending
199 psolve.f PSOLVE SUBROUTINE False BASICS|MODELQ False src/math/psolve.rs done
200 pzert.f PZERT SUBROUTINE False BASICS|ATOMIC|MODELQ False src/math/pzert.rs pending done
201 pzeval.f PZEVAL SUBROUTINE False BASICS|MODELQ|ALIPAR|icnrsp CONOUT True pending
202 pzevld.f PZEVLD SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1|PRSAUX|DEPTDR|ifpzpa|grdpra BASICS|ATOMIC|MODELQ|ALIPAR|ARRAY1|ifpzpa|PRSAUX|grdpra|DEPTDR False src/math/pzevld.rs pending done
203 quartc.f QUARTC SUBROUTINE False True src/math/quartc.rs done
204 quasim.f QUASIM SUBROUTINE False BASICS|ATOMIC|MODELQ|quasun ALLARD False pending
205 quit.f QUIT SUBROUTINE False True src/math/quit.rs done
206 radpre.f RADPRE SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR RTEFR1|QUIT|OPACF1|INDEXX QUIT|INDEXX|OPACF1|RTEFR1 True pending
207 radtot.f RADTOT SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT|OPTDPT|SURFEX|TOTJHK BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT|OPTDPT|TOTJHK|SURFEX OPAINI|RTEFR1|OPACF1 OPAINI|OPACF1|RTEFR1 False pending
208 raph.f RAPH FUNCTION True RAPH False src/math/raph.rs done
209 rates1.f RATES1 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ITERAT ROSSTD|RTEFR1|OPACF1 ROSSTD|OPACF1|RTEFR1 False pending
210 ratmal.f RATMAL SUBROUTINE False BASICS|ATOMIC|MODELQ False src/math/ratmal.rs done
211 ratmat.f RATMAT SUBROUTINE False BASICS|ATOMIC|MODELQ REFLEV False pending
212 ratsp1.f RATSP1 SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR|ARRAY1|ITERAT ROSSTD|RTEFR1|OPACF1 ROSSTD|OPACF1|RTEFR1 True pending
213 rayini.f RAYINI SUBROUTINE False BASICS|MODELQ|ATOMIC RAYLEIGH True pending
214 rayleigh.f RAYLEIGH SUBROUTINE False BASICS|ATOMIC|MODELQ|RAYSCT|eospar False src/math/rayleigh.rs done
215 rayset.f RAYSET SUBROUTINE False BASICS|MODELQ False src/math/rayset.rs done
216 rdata.f RDATA SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|STRPAR|imodlc|INUNIT BASICS|ATOMIC|MODELQ|ITERAT|ODFPAR|ALIPAR|STRPAR|INUNIT|imodlc LINSET|QUIT|RDATAX|DOPGAM RDATAX|LINSET|QUIT|DOPGAM True pending
217 rdatax.f RDATAX SUBROUTINE False BASICS|ATOMIC|MODELQ BKHSGO True pending
218 readbf.f READBF SUBROUTINE False BASICS True pending
219 rechck.f RECHCK SUBROUTINE False BASICS|ATOMIC|MODELQ RTEFR1|OPACF1 OPACF1|RTEFR1 True pending
220 reflev.f REFLEV SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT False pending
221 reiman.f REIMAN FUNCTION True REIMAN False src/math/reiman.rs done
222 resolv.f RESOLV SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR|ARRAY1|icnrsp RTEFR1|ELCOR|STEQEQ|NEWPOP|OPAINI|ROSSTD|CONOUT|TIMING|PRD|TAUFR1|RATES1|OPACF1 NEWPOP|TAUFR1|TIMING|OPACF1|RTEFR1|ROSSTD|STEQEQ|CONOUT|ELCOR|OPAINI|RATES1|PRD True pending
223 rhoeos.f RHOEOS FUNCTION False BASICS|MODELQ PRSENT|RHOEOS False pending
224 rhonen.f RHONEN SUBROUTINE False BASICS|MODELQ ELDENS False pending
225 rhsgen.f RHSGEN SUBROUTINE False BASICS|ATOMIC|MODELQ|ARRAY1|ALIPAR|CUBCON STATE|RATMAT|MATINV|SABOLF|CONVEC|COMPT0|LEVGRP MATINV|STATE|RATMAT|COMPT0|SABOLF|CONVEC|LEVGRP False pending
226 rossop.f ROSSOP SUBROUTINE False BASICS|ATOMIC|MODELQ|ALIPAR MEANOP|STEQEQ|WNSTOR|OPACF0|MEANOPT|ELDENS WNSTOR|STEQEQ|ELDENS|OPACF0|MEANOP|MEANOPT False pending
227 rosstd.f ROSSTD SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT|ALIPAR True pending
228 rte_sc.f RTE_SC SUBROUTINE True BASICS False src/math/rte_sc.rs done
229 rteang.f RTEANG SUBROUTINE False BASICS|MODELQ|ALIPAR|SURFEX|EXTINT BASICS|MODELQ|ALIPAR|EXTINT|SURFEX GAULEG False pending
230 rtecf0.f RTECF0 SUBROUTINE False BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT|auxcbc|AUXRTE False pending
231 rtecf1.f RTECF1 SUBROUTINE False BASICS|MODELQ|ALIPAR|ITERAT|SURFEX|AUXRTE|EXTINT|OPTDPT|comgfs BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT|comgfs|AUXRTE|EXTINT|SURFEX RTECF0|RTEFE2|RTESOL RTEFE2|RTESOL|RTECF0 True pending
232 rtecmc.f RTECMC SUBROUTINE False BASICS|MODELQ|ALIPAR|ITERAT|AUXRTE|comgfs BASICS|MODELQ|ALIPAR|ITERAT|comgfs|AUXRTE RTECF0|OPACF1|MATINV MATINV|OPACF1|RTECF0 False pending
233 rtecmu.f RTECMU SUBROUTINE False BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT|AUXRTE GAULEG|RTECF0|OPACF1|RTESOL RTESOL|OPACF1|RTECF0|GAULEG True pending
234 rtecom.f RTECOM SUBROUTINE False BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT|AUXRTE|comgfs BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT|comgfs|AUXRTE RTECF0|RTECF1|OPACF1 OPACF1|RTECF0|RTECF1 False pending
235 rtedf1.f RTEDF1 SUBROUTINE False BASICS|MODELQ|ALIPAR|OPTDPT False src/math/rtedf1.rs pending done
236 rtedf2.f RTEDF2 SUBROUTINE False BASICS|MODELQ|ALIPAR False pending
237 rtefe2.f RTEFE2 SUBROUTINE True BASICS False src/math/rtefe2.rs done
238 rtefr1.f RTEFR1 SUBROUTINE False BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT RTEDF2|MATINV|RTECF1|RTEDF1|MINV3|RTESOL MATINV|RTESOL|RTEDF1|RTECF1|RTEDF2|MINV3 True pending
239 rteint.f RTEINT SUBROUTINE False BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT MATINV|OPACF1 True pending
240 rtesol.f RTESOL SUBROUTINE True BASICS False src/math/rtesol.rs done
241 russel.f RUSSEL SUBROUTINE False BASICS|MODELQ|COMFH1 MPARTF True pending
242 rybchn.f RYBCHN SUBROUTINE False BASICS|ITERAT|MODELQ|ALIPAR|ARRAY1|rybpgs|grdpra PGSET|ELDENS True pending
243 rybene.f RYBENE SUBROUTINE False BASICS|MODELQ|ALIPAR|ARRAY1|CUBCON|deridt|RYBMTX BASICS|MODELQ|ALIPAR|ARRAY1|RYBMTX|deridt|CUBCON CONVEC False pending
244 rybheq.f RYBHEQ SUBROUTINE False BASICS|MODELQ|rybpgs|grdpra RTEFR1|ELCOR|STEQEQ|WNSTOR|OPAINI|PGSET|ELDENS|OPACF1 OPACF1|RTEFR1|WNSTOR|STEQEQ|ELDENS|ELCOR|OPAINI|PGSET True pending
245 rybmat.f RYBMAT SUBROUTINE False BASICS|MODELQ|ALIPAR|ARRAY1|dsctva|RYBMTX False pending
246 rybsol.f RYBSOL SUBROUTINE False BASICS|MODELQ|ATOMIC|ALIPAR|ARRAY1|ITERAT|RYBMTX|imodlc BASICS|MODELQ|ATOMIC|ALIPAR|ARRAY1|ITERAT|imodlc|RYBMTX RTEFR1|ALIFR1|TRIDAG|STEQEQ|ROSSTD|RYBMAT|OPACTR|OPACFD|RYBCHN|LINEQS RYBMAT|RTEFR1|ROSSTD|OPACTR|LINEQS|STEQEQ|ALIFR1|TRIDAG|RYBCHN|OPACFD True pending
247 sabolf.f SABOLF SUBROUTINE False BASICS|ATOMIC|MODELQ PARTF False pending
248 sbfch.f SBFCH FUNCTION True SBFCH False src/math/sbfch.rs done
249 sbfhe1.f SBFHE1 FUNCTION False BASICS|ATOMIC SBFHE1|QUIT True src/math/sbfhe1.rs done
251 sbfhmi_old.f SBFHMI_OLD FUNCTION True SBFHMI_OLD False src/math/sbfhmi_old.rs done
252 sbfoh.f SBFOH FUNCTION True SBFOH False src/math/sbfoh.rs done
253 setdrt.f SETDRT SUBROUTINE False BASICS|MODELQ|RHODER False pending
254 settrm.f SETTRM SUBROUTINE False tdedge|tdflag|THERM|TABLTD tdflag|tdedge|THERM|TABLTD PRSENT True pending
255 sffhmi.f SFFHMI FUNCTION True SFFHMI False src/math/sffhmi.rs done
256 sffhmi_add.f SFFHMI_ADD FUNCTION True SFFHMI_ADD False src/math/sffhmi_add.rs done
257 sghe12.f SGHE12 FUNCTION True SGHE12 False src/math/sghe12.rs done
259 sgmer1.f SGMER1 SUBROUTINE False BASICS|ATOMIC|MODELQ False src/math/sgmer.rs done
260 sgmerd.f SGMERD SUBROUTINE False BASICS|ATOMIC|MODELQ False src/math/sgmer.rs done
261 sigave.f SIGAVE SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR True pending
262 sigk.f SIGK FUNCTION False BASICS|ATOMIC SIGK|SPSIGK SPSIGK|SIGK False pending
263 sigmar.f SIGMAR FUNCTION False BASICS SIGMAR|LAGUER True pending
264 solve.f SOLVE SUBROUTINE False BASICS|ITERAT|MODELQ|ARRAY1|ALIPAR|CMATZD MATINV|WNSTOR|MATGEN|RHSGEN|PRCHAN MATINV|PRCHAN|RHSGEN|WNSTOR|MATGEN True pending
265 solves.f SOLVES SUBROUTINE False BASICS|ITERAT|MODELQ|ARRAY1|ALIPAR|CMATZD|STOMAT BASICS|ITERAT|MODELQ|ARRAY1|ALIPAR|STOMAT|CMATZD MATINV|WNSTOR|MATGEN|RHSGEN|PRCHAN MATINV|PRCHAN|RHSGEN|WNSTOR|MATGEN True pending
266 spsigk.f SPSIGK SUBROUTINE True CARBON False src/math/spsigk.rs done
267 srtfrq.f SRTFRQ SUBROUTINE False BASICS|ATOMIC|MODELQ QUIT|INDEXX INDEXX|QUIT True pending
268 stark0.f STARK0 SUBROUTINE True False src/math/stark0.rs done
269 starka.f STARKA FUNCTION False BASICS|MODELQ STARKA False src/math/starka.rs done
270 start.f START SUBROUTINE False BASICS|hediff True pending
271 state.f STATE SUBROUTINE False BASICS|ATOMIC|MODELQ|PFSTDS|terden BASICS|ATOMIC|MODELQ|terden|PFSTDS PARTF|OPFRAC OPFRAC|PARTF True pending
272 steqeq.f STEQEQ SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT|POPSTR|PPAPAR MOLEQ|LEVSOL|SABOLF|RATMAT LEVSOL|RATMAT|SABOLF|MOLEQ False pending
273 switch.f SWITCH SUBROUTINE False BASICS|ATOMIC|MODELQ True pending
274 szirc.f SZIRC SUBROUTINE True EINT False src/math/szirc.rs done
275 tabini.f TABINI SUBROUTINE False BASICS|MODELQ|ATOMIC|intcff|abntab|eletab BASICS|MODELQ|ATOMIC|eletab|abntab|intcff True pending
276 tabint.f TABINT SUBROUTINE False BASICS|MODELQ|ATOMIC|intcff False pending
277 taufr1.f TAUFR1 SUBROUTINE False BASICS|MODELQ|ALIPAR|ITERAT|OPTDPT False pending
278 tdpini.f TDPINI SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR|ALIPAR GFREE0 False src/math/tdpini.rs done
279 temcor.f TEMCOR SUBROUTINE False BASICS|MODELQ|ARRAY1|ALIPAR|CUBCON MEANOP|STEQEQ|WNSTOR|OPACF0|CONVEC|ELDENS MEANOP|WNSTOR|STEQEQ|ELDENS|OPACF0|CONVEC True pending
280 temper.f TEMPER SUBROUTINE False BASICS|MODELQ|ALIPAR|PRSAUX|FLXAUX|FACTRS BASICS|MODELQ|ALIPAR|FACTRS|PRSAUX|FLXAUX MEANOP|STEQEQ|WNSTOR|OPACF0|TLOCAL|MEANOPT|ELDENS WNSTOR|TLOCAL|STEQEQ|ELDENS|OPACF0|MEANOP|MEANOPT True pending
281 timing.f TIMING SUBROUTINE False True pending
282 tiopf.f TIOPF SUBROUTINE True False src/math/tiopf.rs done
283 tlocal.f TLOCAL SUBROUTINE False BASICS|MODELQ|FACTRS|FLXAUX QUARTC False pending
285 topbas.f TOPBAS FUNCTION False TOPB TOPBAS True pending
286 traini.f TRAINI SUBROUTINE False BASICS|ATOMIC|MODELQ|ODFPAR False src/math/traini.rs done
287 tridag.f TRIDAG SUBROUTINE True False src/math/tridag.rs done
288 trmder.f TRMDER SUBROUTINE False BASICS|terden|adiaba|derdif BASICS|derdif|terden|adiaba ELDENS False pending
289 trmdrt.f TRMDRT SUBROUTINE False BASICS|tdflag|CONVOUT|tdedge|CC BASICS|tdedge|CC|tdflag|CONVOUT PRSENT False pending
290 ubeta.f UBETA FUNCTION True UBETA|LAGRAN False src/math/ubeta.rs done
291 vern16.f VERN16 FUNCTION True BASICS VERN16 False src/math/vern16.rs done
292 vern18.f VERN18 FUNCTION True BASICS VERN18 False src/math/vern18.rs done
293 vern20.f VERN20 FUNCTION True BASICS VERN20 False src/math/vern20.rs done
294 vern26.f VERN26 FUNCTION True BASICS VERN26 False src/math/vern26.rs done
295 verner.f VERNER FUNCTION False BASICS|ATOMIC VERNER|QUIT QUIT|VERNER False src/math/verner.rs done
296 visini.f VISINI SUBROUTINE False BASICS|ATOMIC|MODELQ|ITERAT True pending
297 voigt.f VOIGT FUNCTION True VOIGT False src/math/voigt.rs done
298 voigte.f VOIGTE FUNCTION True VOIGTE False src/math/voigte.rs done

View File

@ -25,13 +25,54 @@ def extract_commons(content):
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))
# Fortran 内置函数列表(不需要追踪)
FORTRAN_INTRINSICS = {
'SIN', 'COS', 'TAN', 'ASIN', 'ACOS', 'ATAN', 'ATAN2',
'SINH', 'COSH', 'TANH',
'EXP', 'LOG', 'LOG10', 'LOG2',
'SQRT', 'ABS', 'MOD', 'SIGN',
'MAX', 'MIN', 'MAX0', 'MIN0', 'MAX1', 'MIN1', 'AMAX0', 'AMIN0',
'INT', 'IFIX', 'IDINT', 'FLOAT', 'SNGL', 'DBLE', 'CMPLX',
'REAL', 'AIMAG', 'CONJG',
'ICHAR', 'CHAR', 'INDEX', 'LEN', 'LGE', 'LGT', 'LLE', 'LLT',
'DOT_PRODUCT', 'MATMUL', 'TRANSPOSE', 'RESHAPE',
'SIZE', 'SHAPE', 'LBOUND', 'UBOUND',
'ALLOCATED', 'ALLOCATE', 'DEALLOCATE',
'KIND', 'SELECTED_REAL_KIND', 'SELECTED_INT_KIND',
'DIGITS', 'EPSILON', 'HUGE', 'TINY', 'PRECISION', 'RANGE',
'FLOOR', 'CEILING', 'NINT', 'ANINT',
'ADJUSTL', 'ADJUSTR', 'TRIM', 'REPEAT', 'SCAN', 'VERIFY',
'PRESENT', 'ASSOCIATED',
# TLUSTY 常用数学函数
'ERF', 'ERFC', 'GAMMA', 'LOG_GAMMA',
}
def extract_calls(content, known_functions=None):
"""提取 CALL 语句和 FUNCTION 调用
Args:
content: Fortran 源码
known_functions: 已知的函数名集合用于区分函数调用和数组访问
"""
calls = set()
# 1. 提取 CALL 语句(支持有括号和无括号两种形式)
# CALL NAME(...) 或 CALL NAME
call_stmts = re.findall(r'(?i)CALL\s+(\w+)(?:\s*\(|\s*$|\s*\n)', content)
calls.update(c.upper() for c in call_stmts)
# 2. 提取可能的 FUNCTION 调用
if known_functions:
# 只匹配已知函数名
func_assign = re.findall(r'(?i)=\s*([A-Z][A-Z0-9]*)\s*\(', content)
calls.update(f.upper() for f in func_assign
if f.upper() in known_functions and f.upper() not in FORTRAN_INTRINSICS)
func_expr = re.findall(r'(?i)[=(,]\s*([A-Z][A-Z0-9]*)\s*\(', content)
calls.update(f.upper() for f in func_expr
if f.upper() in known_functions and f.upper() not in FORTRAN_INTRINSICS)
return list(calls)
def has_file_io(content):
"""检查是否有文件 I/O"""
@ -86,6 +127,12 @@ SPECIAL_MAPPINGS = {
'erfcx': ['erfcx', 'erfcin'],
'lineqs': ['lineqs', 'lineqs_nr'],
'gamsp': ['gamsp'], # alias
'bhe': ['bhe', 'bhed', 'bhez'], # 流体静力学平衡方程
'comset': ['comset'], # Compton 散射参数设置
'ghydop': ['ghydop'], # 氢不透明度 (Gomez 表)
'levgrp': ['levgrp'], # 能级分组
'profil': ['profil'], # 标准吸收轮廓
'linspl': ['linspl'], # 谱线轮廓设置
}
def find_rust_module(fortran_name, rust_dir):
@ -247,10 +294,20 @@ def main():
extracted_dir = "/home/fmq/program/tlusty/tl208-s54/rust/tlusty/extracted"
rust_dir = "/home/fmq/program/tlusty/tl208-s54/rust/src/math"
# 收集所有单元信息
units_dict = {}
# 第一遍:收集所有已定义的 SUBROUTINE 和 FUNCTION 名称
all_defined_units = set()
fortran_files = sorted(glob.glob(os.path.join(extracted_dir, "*.f")))
for fpath in fortran_files:
with open(fpath, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
units = extract_unit_info(content, os.path.basename(fpath))
for unit_type, unit_name in units:
all_defined_units.add(unit_name)
# 第二遍:收集所有单元信息(使用已知函数名来过滤调用)
units_dict = {}
for fpath in fortran_files:
fname = os.path.basename(fpath)
base_name = os.path.splitext(fname)[0]
@ -260,7 +317,7 @@ def main():
includes = extract_includes(content)
commons = extract_commons(content)
calls = extract_calls(content)
calls = extract_calls(content, known_functions=all_defined_units)
io = has_file_io(content)
units = extract_unit_info(content, fname)
@ -315,6 +372,9 @@ def main():
for unit_name, unit in units_dict.items():
if unit['status'] == 'done':
continue
# 跳过无法识别程序单元的文件(如纯注释文件)
if unit['unit_type'] == 'UNKNOWN':
continue
depth = calculate_depth(unit_name, units_dict, memo)
trans_calls = len(get_transitive_deps(unit_name, units_dict))
@ -335,10 +395,10 @@ def main():
'is_pure': unit['is_pure'],
})
# 按优先级排序:未实现依赖少 > 深度低 > 无IO
priority_list.sort(key=lambda x: (x['trans_pending'], x['depth'], x['trans_calls'], x['has_io']))
# 按优先级排序:无IO > 未实现依赖少 > 深度低
priority_list.sort(key=lambda x: (x['has_io'], x['trans_pending'], x['depth'], x['trans_calls']))
print("重构优先级列表 (按未实现依赖排序)")
print("重构优先级列表 (优先无IO按未实现依赖排序)")
print("=" * 100)
print(f"{'单元名':<20} {'未实现':>6} {'传递未实现':>10} {'深度':>4} {'直接调用':>8} {'传递调用':>8} {'IO':>4}")
print("-" * 100)

1346
src/math/bhe.rs Normal file

File diff suppressed because it is too large Load Diff

482
src/math/compt0.rs Normal file
View File

@ -0,0 +1,482 @@
//! Compton 散射源函数辅助量计算。
//!
//! 重构自 TLUSTY `compt0.f`
//!
//! 计算 Compton 散射源函数的辅助量 COMPA, COMPB, COMPC, COMPD, COMPE, COMPS。
use crate::state::constants::{MDEPTH, MFREQ, TWO, UN};
// ============================================================================
// 常量
// ============================================================================
/// 频率转换常数 XCON = 8.0935e-21
const XCON: f64 = 8.0935e-21;
/// 温度转换常数 YCON = 1.68638e-10
const YCON: f64 = 1.68638e-10;
// ============================================================================
// 辅助数组 (对应 COMMON /auxcbc/)
// ============================================================================
/// Compton 散射导数辅助数组。
/// 对应 COMMON /auxcbc/
#[derive(Debug, Clone, Default)]
pub struct AuxCbc {
/// CDER1M(MDEPTH) - 频率导数 (前)
pub cden1m: Vec<f64>,
/// CDER10(MDEPTH) - 频率导数 (当前)
pub cden10: Vec<f64>,
/// CDER2M(MDEPTH) - 二阶频率导数 (前)
pub cden2m: Vec<f64>,
/// CDER20(MDEPTH) - 二阶频率导数 (当前)
pub cden20: Vec<f64>,
}
impl AuxCbc {
pub fn new() -> Self {
Self {
cden1m: vec![0.0; MDEPTH],
cden10: vec![0.0; MDEPTH],
cden2m: vec![0.0; MDEPTH],
cden20: vec![0.0; MDEPTH],
}
}
}
// ============================================================================
// COMPT0 参数结构体
// ============================================================================
/// COMPT0 输入参数。
#[derive(Debug, Clone)]
pub struct Compt0Params {
/// 频率索引 IJ (1-indexed)
pub ij: usize,
/// 深度索引 ID (1-indexed)
pub id: usize,
/// 吸收系数
pub ab: f64,
/// 总频率点数
pub nfreq: usize,
// 频率相关数组 [MFREQ]
pub freq: Vec<f64>,
pub ijorig: Vec<usize>,
pub kij: Vec<usize>,
pub dlnfr: Vec<f64>,
pub delj: Vec<Vec<f64>>,
pub bnus: Vec<f64>,
pub sigec: Vec<f64>,
// 深度相关数组 [MDEPTH]
pub temp: Vec<f64>,
pub elec: Vec<f64>,
// 辐射场 [MFREQ × MDEPTH]
pub rad: Vec<Vec<f64>>,
// Compton 控制参数
/// Compton 密度导数标志 (0=禁用)
pub icomde: i32,
/// Compton 源函数标志 (0=禁用)
pub icomst: i32,
/// Compton 高阶项标志
pub ichcoo: i32,
/// Compton 模式 (1=标准, 2=无非对角项, 3=全禁用)
pub icompt: i32,
/// 汤姆逊散射截面
pub sige: f64,
// 可变导数数组 (CDER1M, CDER10, CDER1P, CDER2M, CDER20, CDER2P)
/// CDER1M(IJI) - 前频率导数
pub cder1m: Vec<f64>,
/// CDER10(IJI) - 当前频率导数
pub cder10: Vec<f64>,
/// CDER1P(IJI) - 后频率导数
pub cder1p: Vec<f64>,
/// CDER2M(IJI) - 前二阶导数
pub cder2m: Vec<f64>,
/// CDER20(IJI) - 当前二阶导数
pub cder20: Vec<f64>,
/// CDER2P(IJI) - 后二阶导数
pub cder2p: Vec<f64>,
}
impl Default for Compt0Params {
fn default() -> Self {
Self {
ij: 1,
id: 1,
ab: 1.0,
nfreq: 1,
freq: vec![0.0; MFREQ],
ijorig: vec![0; MFREQ],
kij: vec![0; MFREQ],
dlnfr: vec![0.0; MFREQ],
delj: vec![vec![0.0; MDEPTH]; MFREQ],
bnus: vec![0.0; MFREQ],
sigec: vec![0.0; MFREQ],
temp: vec![0.0; MDEPTH],
elec: vec![0.0; MDEPTH],
rad: vec![vec![0.0; MDEPTH]; MFREQ],
icomde: 0,
icomst: 0,
ichcoo: 0,
icompt: 1,
sige: 6.6516e-25, // 汤姆逊散射截面
cder1m: vec![0.0; MFREQ],
cder10: vec![0.0; MFREQ],
cder1p: vec![0.0; MFREQ],
cder2m: vec![0.0; MFREQ],
cder20: vec![0.0; MFREQ],
cder2p: vec![0.0; MFREQ],
}
}
}
/// COMPT0 输出结果。
#[derive(Debug, Clone, Default)]
pub struct Compt0Result {
/// COMPA - 前频率导数系数
pub compa: f64,
/// COMPB - 当前频率导数系数
pub compb: f64,
/// COMPC - 后频率导数系数
pub compc: f64,
/// COMPD - 密度导数系数
pub compd: f64,
/// COMPE - 发射系数
pub compe: f64,
/// COMPS - 源函数系数
pub comps: f64,
}
// ============================================================================
// COMPT0 主函数
// ============================================================================
/// 计算 Compton 散射源函数的辅助量。
///
/// # 参数
///
/// * `params` - 输入参数
///
/// # 返回值
///
/// 返回 Compt0Result 包含 COMPA, COMPB, COMPC, COMPD, COMPE, COMPS
///
/// # Fortran 索引说明
///
/// - IJ, ID, IJI 都是 1-indexed
/// - Rust 中使用 0-indexed需要转换
pub fn compt0(params: &mut Compt0Params) -> Compt0Result {
let mut result = Compt0Result::default();
// IJI = NFREQ - KIJ(IJ) + 1
// Fortran 1-indexed: KIJ(IJ) → Rust: kij[ij-1]
let iji = params.nfreq - params.kij[params.ij - 1] + 1;
// 如果 IJI = 1所有输出为 0
if iji == 1 {
return result;
}
let ij_idx = params.ij - 1; // 0-indexed
let id_idx = params.id - 1; // 0-indexed
let iji_idx = iji - 1; // 0-indexed
// FR = FREQ(IJ)
let fr = params.freq[ij_idx];
// FRP = FREQ(IJORIG(IJI+1))
let frp = params.freq[params.ijorig[iji_idx + 1] - 1];
// FRM = FREQ(IJORIG(IJI-1))
let frm = params.freq[params.ijorig[iji_idx - 1] - 1];
let xcomp = fr * XCON;
let e2 = YCON * params.temp[id_idx];
let e1 = xcomp - 3.0 * e2;
// DEL0 = TWO / (DLNFR(IJI) + DLNFR(IJI-1))
let del0 = TWO / (params.dlnfr[iji_idx] + params.dlnfr[iji_idx - 1]);
// 计算导数系数
// Fortran: CDER1P(IJI) = (UN - DELJ(IJI, ID)) * DEL0
// Rust: cder1p[iji_idx] = (1.0 - delj[iji_idx][id_idx]) * del0
params.cder1p[iji_idx] = (UN - params.delj[iji_idx][id_idx]) * del0;
// Fortran: CDER1M(IJI) = -DELJ(IJI-1, ID) * DEL0
params.cder1m[iji_idx] = -params.delj[iji_idx - 1][id_idx] * del0;
// Fortran: CDER10(IJI) = -DEL0 * (UN - DELJ(IJI-1, ID) - DELJ(IJI, ID))
params.cder10[iji_idx] = -del0 * (UN - params.delj[iji_idx - 1][id_idx] - params.delj[iji_idx][id_idx]);
// SS0 = ELEC(ID) * SIGE / AB
let ss0 = params.elec[id_idx] * params.sige / params.ab;
// 临时变量
let mut compu = 0.0;
let mut compv = 0.0;
let mut cbs = 0.0;
if params.ichcoo == 0 {
// 标准模式
params.cder10[iji_idx] = -params.cder1m[iji_idx] - params.cder1p[iji_idx];
result.compa = ss0 * (e1 * params.cder1m[iji_idx] + e2 * params.cder2m[iji_idx]);
result.compb = ss0
* (UN - xcomp - params.sigec[ij_idx] / params.sige + e1 * params.cder10[iji_idx]
+ e2 * params.cder20[iji_idx]);
result.compc = ss0 * (e1 * params.cder1p[iji_idx] + e2 * params.cder2p[iji_idx]);
} else {
// 高阶模式
// EPSNU = (AB - ELEC(ID) * SIGEC(IJ)) / AB
let epsnu = (params.ab - params.elec[id_idx] * params.sigec[ij_idx]) / params.ab;
// ZXXP, ZXX0, ZXXM
let zxxp = XCON * frp + 0.5 * params.bnus[iji_idx + 1] * params.rad[iji_idx + 1][id_idx]
- 3.0 * e2;
let zxx0 = xcomp + 0.5 * params.bnus[iji_idx] * params.rad[iji_idx][id_idx] - 3.0 * e2;
let zxxm = XCON * frm + 0.5 * params.bnus[iji_idx - 1] * params.rad[iji_idx - 1][id_idx]
- 3.0 * e2;
let zxxp12 = ((UN - params.delj[iji_idx][id_idx]) * zxxp
+ params.delj[iji_idx][id_idx] * zxx0)
* del0;
let zxxm12 = ((UN - params.delj[iji_idx - 1][id_idx]) * zxx0
+ params.delj[iji_idx - 1][id_idx] * zxxm)
* del0;
result.compa = ss0 * (-params.delj[iji_idx - 1][id_idx] * zxxm12 + e2 * params.cder2m[iji_idx]);
result.compc = ss0
* ((UN - params.delj[iji_idx][id_idx]) * zxxp12 + e2 * params.cder2p[iji_idx]);
result.compb = ss0
* (params.delj[iji_idx][id_idx] * zxxp12
- (UN - params.delj[iji_idx - 1][id_idx]) * zxxm12
+ e2 * params.cder20[iji_idx]
- params.sigec[ij_idx] / params.sige)
- epsnu
+ 1.0;
result.compe = 0.0;
}
// COMPD = (-3 * CDER10(IJI) + CDER20(IJI)) * RAD(IJI, ID)
result.compd =
(-3.0 * params.cder10[iji_idx] + params.cder20[iji_idx]) * params.rad[iji_idx][id_idx];
// 如果 ICOMDE = 0禁用密度导数项
if params.icomde == 0 {
result.compa = 0.0;
result.compc = 0.0;
result.compb = 0.0;
}
// X0 = SS0 * BNUS(IJI)
let x0 = ss0 * params.bnus[iji_idx];
// 如果 ICOMST = 0禁用源函数项
let x0 = if params.icomst == 0 { 0.0 } else { x0 };
if params.ichcoo == 0 {
result.compe = x0 * (params.cder10[iji_idx] - UN);
compu = x0 * params.cder1m[iji_idx];
compv = x0 * params.cder1p[iji_idx];
cbs = result.compe * params.rad[iji_idx][id_idx];
result.compe = cbs;
}
result.comps = result.compb * params.rad[iji_idx][id_idx];
// IJI > 1 时的额外项
if iji > 1 {
if params.ichcoo == 0 {
cbs += compu * params.rad[iji_idx - 1][id_idx];
}
result.comps += result.compa * params.rad[iji_idx - 1][id_idx];
result.compd +=
(-3.0 * params.cder1m[iji_idx] + params.cder2m[iji_idx]) * params.rad[iji_idx - 1][id_idx];
}
// IJI < NFREQ 时的额外项
if iji < params.nfreq {
if params.ichcoo == 0 {
cbs += compv * params.rad[iji_idx + 1][id_idx];
}
result.comps += result.compc * params.rad[iji_idx + 1][id_idx];
result.compd +=
(-3.0 * params.cder1p[iji_idx] + params.cder2p[iji_idx]) * params.rad[iji_idx + 1][id_idx];
}
if params.ichcoo == 0 {
result.compb += cbs;
result.compa += compu * params.rad[iji_idx][id_idx];
result.compc += compv * params.rad[iji_idx][id_idx];
result.comps += cbs * params.rad[iji_idx][id_idx];
}
result.compd *= ss0 * YCON;
// 如果 ICOMDE = 0禁用密度导数
if params.icomde == 0 {
result.compd = 0.0;
}
// ICOMPT = 2 模式:无非对角强度项
if params.icompt == 2 {
if iji > 1 {
result.compb += result.compa * params.rad[iji_idx - 1][id_idx];
}
if iji < params.nfreq {
result.compb += result.compc * params.rad[iji_idx + 1][id_idx];
}
result.compa = 0.0;
result.compc = 0.0;
} else if params.icompt == 3 {
// ICOMPT = 3 模式:全禁用
result.compa = 0.0;
result.compb = 0.0;
result.compc = 0.0;
}
result
}
// ============================================================================
// 测试
// ============================================================================
#[cfg(test)]
mod tests {
use super::*;
fn create_test_params() -> Compt0Params {
let mut params = Compt0Params::default();
params.ij = 5;
params.id = 1;
params.ab = 1e-8;
params.nfreq = 100;
// 设置简单的测试值
params.temp[0] = 10000.0;
params.elec[0] = 1e12;
// 设置 KIJ 映射
for i in 0..100 {
params.kij[i] = 100 - i; // 简单的反向映射
params.ijorig[i] = i + 1;
params.freq[i] = 1e14 * (i + 1) as f64;
params.dlnfr[i] = 0.1;
params.bnus[i] = 1e-10;
params.sigec[i] = 0.0;
params.delj[i][0] = 0.5;
// 初始化导数数组
params.cder1m[i] = 0.0;
params.cder10[i] = 0.0;
params.cder1p[i] = 0.0;
params.cder2m[i] = 0.0;
params.cder20[i] = 0.0;
params.cder2p[i] = 0.0;
// 初始化辐射场
params.rad[i][0] = 1e10;
}
params
}
#[test]
fn test_compt0_iji_equals_1() {
let mut params = Compt0Params::default();
// IJI = NFREQ - KIJ(IJ) + 1
// 当 kij[i] = 100 - i 时:
// ij = 1 → kij[0] = 100 → iji = 100 - 100 + 1 = 1
params.ij = 1;
params.nfreq = 100;
for i in 0..100 {
params.kij[i] = 100 - i;
}
let result = compt0(&mut params);
// 当 IJI = 1 时,所有输出应为 0
assert!((result.compa).abs() < 1e-15);
assert!((result.compb).abs() < 1e-15);
assert!((result.compc).abs() < 1e-15);
assert!((result.compd).abs() < 1e-15);
assert!((result.compe).abs() < 1e-15);
assert!((result.comps).abs() < 1e-15);
}
#[test]
fn test_compt0_basic() {
let mut params = create_test_params();
params.ichcoo = 0;
params.icomde = 1;
params.icomst = 1;
params.icompt = 1;
let result = compt0(&mut params);
// 检查结果有限
assert!(result.compa.is_finite());
assert!(result.compb.is_finite());
assert!(result.compc.is_finite());
assert!(result.compd.is_finite());
assert!(result.compe.is_finite());
assert!(result.comps.is_finite());
}
#[test]
fn test_compt0_icomde_zero() {
let mut params = create_test_params();
params.icomde = 0; // 禁用密度导数
let result = compt0(&mut params);
// ICOMDE = 0 时COMPA, COMPB, COMPC, COMPD 应为 0
assert!((result.compa).abs() < 1e-15);
assert!((result.compb).abs() < 1e-15);
assert!((result.compc).abs() < 1e-15);
assert!((result.compd).abs() < 1e-15);
}
#[test]
fn test_compt0_icompt_3() {
let mut params = create_test_params();
params.icomde = 1;
params.icompt = 3; // 全禁用模式
let result = compt0(&mut params);
// ICOMPT = 3 时COMPA, COMPB, COMPC 应为 0
assert!((result.compa).abs() < 1e-15);
assert!((result.compb).abs() < 1e-15);
assert!((result.compc).abs() < 1e-15);
}
#[test]
fn test_compt0_high_order_mode() {
let mut params = create_test_params();
params.ichcoo = 1; // 高阶模式
params.icomde = 1;
params.icomst = 1;
let result = compt0(&mut params);
// 检查结果有限
assert!(result.compa.is_finite());
assert!(result.compb.is_finite());
assert!(result.compc.is_finite());
assert!(result.compd.is_finite());
assert!(result.compe.is_finite());
assert!(result.comps.is_finite());
}
#[test]
fn test_constants() {
// 验证常量
assert!((XCON - 8.0935e-21).abs() < 1e-30);
assert!((YCON - 1.68638e-10).abs() < 1e-20);
}
}

486
src/math/comset.rs Normal file
View File

@ -0,0 +1,486 @@
//! Compton 散射参数设置。
//!
//! 重构自 TLUSTY `comset.f`。
//!
//! 设置 Compton 散射所需的频率相关参数:
//! - 导数系数 CDER1M, CDER10, CDER1P, CDER2M, CDER20, CDER2P
//! - 插值系数 DELJ
//! - Klein-Nishina 散射截面 SIGEC
use crate::state::constants::{BN, HALF, HK, MDEPTH, MFREQ, SIGE, UN};
// ============================================================================
// 常量
// ============================================================================
/// 频率转换常数 XCON = 8.0935e-21
const XCON: f64 = 8.0935e-21;
/// 温度转换常数 YCON = 1.68638e-10
const YCON: f64 = 1.68638e-10;
/// 频率单位转换 T15 = 1e-15
const T15: f64 = 1e-15;
// ============================================================================
// COMSET 参数结构体
// ============================================================================
/// COMSET 输入参数。
#[derive(Debug, Clone)]
pub struct ComsetParams {
/// 频率点数
pub nfreq: usize,
/// 深度点数
pub nd: usize,
/// 频率数组 [MFREQ] (1-indexed 访问)
pub freq: Vec<f64>,
/// 频率索引映射 KIJ [MFREQ]
pub kij: Vec<usize>,
/// 温度数组 [MDEPTH]
pub temp: Vec<f64>,
// 控制参数
/// Compton 模式 (≤0=禁用)
pub icompt: i32,
/// Compton 高阶项标志
pub ichcoo: i32,
/// Klein-Nishina 截面标志 (0=一阶近似, 1=完整公式)
pub knish: i32,
}
impl Default for ComsetParams {
fn default() -> Self {
Self {
nfreq: 1,
nd: 1,
freq: vec![0.0; MFREQ],
kij: vec![1; MFREQ],
temp: vec![0.0; MDEPTH],
icompt: 0,
ichcoo: 0,
knish: 0,
}
}
}
/// COMSET 输出结果。
#[derive(Debug, Clone, Default)]
pub struct ComsetResult {
/// 频率索引映射 IJORIG [MFREQ]
pub ijorig: Vec<usize>,
/// 重排后的频率 FREQI [MFREQ]
pub freqi: Vec<f64>,
/// Planck 加权因子 BNUS [MFREQ]
pub bnus: Vec<f64>,
/// 对数频率间隔 DLNFR [MFREQ]
pub dlnfr: Vec<f64>,
/// 插值系数 DELJ [MFREQ × MDEPTH]
pub delj: Vec<Vec<f64>>,
/// Klein-Nishina 散射截面 SIGEC [MFREQ]
pub sigec: Vec<f64>,
// 导数系数 [MFREQ]
/// 前频率导数 CDER1M
pub cder1m: Vec<f64>,
/// 当前频率导数 CDER10
pub cder10: Vec<f64>,
/// 后频率导数 CDER1P
pub cder1p: Vec<f64>,
/// 前二阶导数 CDER2M
pub cder2m: Vec<f64>,
/// 当前二阶导数 CDER20
pub cder20: Vec<f64>,
/// 后二阶导数 CDER2P
pub cder2p: Vec<f64>,
}
impl ComsetResult {
pub fn new() -> Self {
Self {
ijorig: vec![0; MFREQ],
freqi: vec![0.0; MFREQ],
bnus: vec![0.0; MFREQ],
dlnfr: vec![0.0; MFREQ],
delj: vec![vec![0.0; MDEPTH]; MFREQ],
sigec: vec![0.0; MFREQ],
cder1m: vec![0.0; MFREQ],
cder10: vec![0.0; MFREQ],
cder1p: vec![0.0; MFREQ],
cder2m: vec![0.0; MFREQ],
cder20: vec![0.0; MFREQ],
cder2p: vec![0.0; MFREQ],
}
}
}
// ============================================================================
// COMSET 主函数
// ============================================================================
/// 设置 Compton 散射参数。
///
/// # 参数
///
/// * `params` - 输入参数
///
/// # 返回值
///
/// 返回 ComsetResult 包含所有计算结果
///
/// # 说明
///
/// 此函数设置 Compton 散射所需的频率相关参数:
/// 1. 重排频率索引 (IJORIG)
/// 2. 计算 Planck 加权因子 (BNUS)
/// 3. 计算对数频率间隔 (DLNFR)
/// 4. 计算二阶导数系数 (CDER2*)
/// 5. 计算插值系数 (DELJ)
/// 6. 计算 Klein-Nishina 散射截面 (SIGEC)
pub fn comset(params: &ComsetParams) -> ComsetResult {
let mut result = ComsetResult::new();
// 如果 ICOMPT ≤ 0跳过大部分计算
if params.icompt <= 0 {
// 只计算 SIGEC
compute_sigec(params, &mut result);
return result;
}
// ========================================================================
// 频率相关通用参数
// ========================================================================
for ij in 1..=params.nfreq {
// 初始化导数系数
result.cder10[ij - 1] = 0.0;
result.cder1p[ij - 1] = 0.0;
result.cder1m[ij - 1] = 0.0;
result.cder20[ij - 1] = 0.0;
result.cder2p[ij - 1] = 0.0;
result.cder2m[ij - 1] = 0.0;
// IJI = NFREQ - KIJ(IJ) + 1
let iji = params.nfreq - params.kij[ij - 1] + 1;
// IJORIG(IJI) = IJ
result.ijorig[iji - 1] = ij;
// FREQI(IJI) = FREQ(IJ)
result.freqi[iji - 1] = params.freq[ij - 1];
// FR = FREQI(IJI)
let fr = result.freqi[iji - 1];
// BNUS(IJI) = TWO*XCON*FR/(BN*(FR*T15)**3)
let fr_t15 = fr * T15;
result.bnus[iji - 1] = 2.0 * XCON * fr / (BN * fr_t15 * fr_t15 * fr_t15);
}
// ========================================================================
// 计算对数频率间隔和二阶导数系数
// ========================================================================
// IJ = 1
result.dlnfr[0] = (result.freqi[1] / result.freqi[0]).ln();
for ij in 2..params.nfreq {
// DLNFR(IJ) = LOG(FREQI(IJ+1)/FREQI(IJ))
result.dlnfr[ij - 1] = (result.freqi[ij] / result.freqi[ij - 1]).ln();
let delp = result.dlnfr[ij - 1];
let delm = result.dlnfr[ij - 2];
let del0 = delp + delm;
let cd0 = 2.0 / del0;
// CDER2M(IJ) = CD0/DELM
result.cder2m[ij - 1] = cd0 / delm;
// CDER2P(IJ) = CD0/DELP
result.cder2p[ij - 1] = cd0 / delp;
// CDER20(IJ) = -CDER2M(IJ) - CDER2P(IJ)
result.cder20[ij - 1] = -result.cder2m[ij - 1] - result.cder2p[ij - 1];
}
// ========================================================================
// 计算插值系数 DELJ
// ========================================================================
for ij in 1..params.nfreq {
let frj0 = result.freqi[ij - 1];
let frjp = result.freqi[ij];
let frz = (frj0 * frjp).sqrt();
for id in 1..=params.nd {
// 避免上溢/下溢
let fjb0 = if HK * frj0 / params.temp[id - 1] < 200.0 {
UN / ((HK * frj0 / params.temp[id - 1]).exp() - UN)
} else {
0.0
};
let fjbp = if HK * frjp / params.temp[id - 1] < 200.0 {
UN / ((HK * frjp / params.temp[id - 1]).exp() - UN)
} else {
0.0
};
let fjz0 = fjb0 * (BN * (frj0 * T15).powi(3));
let fjzp = fjbp * (BN * (frjp * T15).powi(3));
let (aa, bb, cc) = if params.ichcoo == 0 {
// 标准模式
let zj0 = HK * frz / params.temp[id - 1];
let dfjz = fjz0 - fjzp;
let dfjb = fjb0 - fjbp;
let fzz = UN + fjbp - 3.0 / zj0;
let aa = dfjz * dfjb;
let bb = dfjz * fzz + fjzp * dfjb;
let cc = fjzp * fzz - dfjz / result.dlnfr[ij - 1] / zj0;
(aa, bb, cc)
} else {
// 高阶模式
let e2 = YCON * params.temp[id - 1];
let zxxp = XCON * frjp * (UN + fjbp) - 3.0 * e2;
let zxx0 = XCON * frj0 * (UN + fjb0) - 3.0 * e2;
let dzxx = zxx0 - zxxp;
let dfjb = fjb0 - fjbp;
let dfjz = fjz0 - fjzp;
let aa = dfjz * dzxx;
let bb = dfjz * zxxp + fjzp * dzxx;
let cc = fjzp * zxxp - e2 * dfjz / result.dlnfr[ij - 1];
(aa, bb, cc)
};
// 求解二次方程 AA*XX1² + BB*XX1 + CC = 0
let xx1 = if aa.abs() == 0.0 && bb.abs() == 0.0 {
0.0
} else if aa.abs() < 1e-7 * bb.abs() {
-cc / bb
} else {
let dd = bb * bb - 4.0 * aa * cc;
let dd = if dd < 0.0 { 0.0 } else { dd };
let dd = dd.sqrt();
let xx1 = (dd - bb) * HALF / aa;
if params.ichcoo > 0 {
let xx2 = -(dd + bb) * HALF / aa;
let dxx1 = (xx1 - HALF).abs();
let dxx2 = (xx2 - HALF).abs();
let xx1 = if dxx2 < dxx1 { xx2 } else { xx1 };
if xx1 > 1.0 || xx1 < 0.0 {
HALF
} else {
xx1
}
} else {
xx1
}
};
result.delj[ij - 1][id - 1] = xx1;
}
}
// ========================================================================
// 计算 Klein-Nishina 散射截面 SIGEC
// ========================================================================
compute_sigec(params, &mut result);
result
}
/// 计算 Klein-Nishina 散射截面。
fn compute_sigec(params: &ComsetParams, result: &mut ComsetResult) {
for ij in 1..=params.nfreq {
if params.knish == 0 {
// 一阶近似
result.sigec[ij - 1] = SIGE * (UN - 2.0 * params.freq[ij - 1] * XCON);
} else {
// 完整 Klein-Nishina 截面 (Rybicki & Lightman 1975)
let xf = XCON * params.freq[ij - 1];
if xf < 0.1 {
// 小 x 展开式
result.sigec[ij - 1] = SIGE
* (1.0
- xf
* (2.0
- xf
* (26.0 / 5.0
- xf
* (13.3
- xf
* (1144.0 / 35.0
- xf
* (544.0 / 7.0
- xf
* (3784.0 / 21.0
- xf
* (6148.0 / 15.0
- xf
* (151552.0
/ 165.0
- xf
* 111872.0
/ 55.0)))))))));
} else if xf > 1e3 {
// 大 x 渐近式
result.sigec[ij - 1] = SIGE * 3.0 / 8.0 / xf * ((2.0 * xf).ln() + 0.5);
} else {
// 完整公式
let xf1 = xf + 1.0;
let xf2 = 2.0 * xf + 1.0;
result.sigec[ij - 1] = SIGE
* 0.75
* (xf1 / (xf * xf * xf) * (2.0 * xf * xf1 / xf2 - (xf2).ln())
+ 0.5 * (xf2).ln() / xf
- (1.0 + 3.0 * xf) / (xf2 * xf2));
}
}
}
}
// ============================================================================
// 测试
// ============================================================================
#[cfg(test)]
mod tests {
use super::*;
fn create_test_params() -> ComsetParams {
let mut params = ComsetParams::default();
params.nfreq = 10;
params.nd = 5;
params.icompt = 1;
params.ichcoo = 0;
params.knish = 0;
// 设置频率 (递增)
for i in 0..10 {
params.freq[i] = 1e14 * (i + 1) as f64;
// KIJ 是反向映射
params.kij[i] = 10 - i;
}
// 设置温度
for i in 0..5 {
params.temp[i] = 10000.0 + i as f64 * 1000.0;
}
params
}
#[test]
fn test_comset_icompt_zero() {
let mut params = ComsetParams::default();
params.nfreq = 5;
params.icompt = 0; // 禁用
params.knish = 0;
for i in 0..5 {
params.freq[i] = 1e14 * (i + 1) as f64;
params.kij[i] = 5 - i;
}
let result = comset(&params);
// 当 ICOMPT = 0 时,只计算 SIGEC
// 检查 SIGEC 有限
for i in 0..params.nfreq {
assert!(result.sigec[i].is_finite());
}
}
#[test]
fn test_comset_basic() {
let params = create_test_params();
let result = comset(&params);
// 检查 IJORIG 映射 (只检查前 nfreq 个元素)
for i in 0..params.nfreq {
assert!(result.ijorig[i] >= 1 && result.ijorig[i] <= params.nfreq);
}
// 检查 BNUS 有限
for i in 0..params.nfreq {
assert!(result.bnus[i].is_finite());
}
// 检查 DLNFR 正数
for i in 0..params.nfreq - 1 {
assert!(result.dlnfr[i] > 0.0);
}
// 检查 DELJ 在 [0, 1] 范围内
for ij in 0..params.nfreq - 1 {
for id in 0..params.nd {
assert!(result.delj[ij][id] >= 0.0 && result.delj[ij][id] <= 1.0);
}
}
// 检查 SIGEC 有限
for i in 0..params.nfreq {
assert!(result.sigec[i].is_finite());
}
}
#[test]
fn test_comset_high_order_mode() {
let mut params = create_test_params();
params.ichcoo = 1; // 高阶模式
let result = comset(&params);
// 检查结果有限
for i in 0..params.nfreq {
assert!(result.bnus[i].is_finite());
assert!(result.sigec[i].is_finite());
}
}
#[test]
fn test_comset_klein_nishina() {
let mut params = create_test_params();
params.knish = 1; // 完整 Klein-Nishina
let result = comset(&params);
// 检查 SIGEC 有限且为正
for i in 0..params.nfreq {
assert!(result.sigec[i].is_finite());
assert!(result.sigec[i] > 0.0);
}
}
#[test]
fn test_comset_sigec_limits() {
let mut params = ComsetParams::default();
params.nfreq = 3;
params.knish = 1;
// 低频 (xf < 0.1)
params.freq[0] = 1e12; // xf ≈ 8e-9
// 中频 (0.1 < xf < 1000)
params.freq[1] = 1e16; // xf ≈ 0.08
// 高频 (xf > 1000)
params.freq[2] = 1e24; // xf ≈ 8e3
let result = comset(&params);
// 所有 SIGEC 应为正且有限
for i in 0..3 {
assert!(result.sigec[i] > 0.0);
assert!(result.sigec[i].is_finite());
}
}
#[test]
fn test_constants() {
assert!((XCON - 8.0935e-21).abs() < 1e-30);
assert!((YCON - 1.68638e-10).abs() < 1e-20);
assert!((T15 - 1e-15).abs() < 1e-20);
}
}

404
src/math/ghydop.rs Normal file
View File

@ -0,0 +1,404 @@
//! 氢不透明度计算Gomez 表)。
//!
//! 重构自 TLUSTY `ghydop.f`。
//!
//! 从 Gomez 表计算氢的谱线和伪连续谱不透明度。
use crate::state::constants::{MDEPTH, MFHTAB, MFREQ, MTABEH, MTABTH, UN};
// ============================================================================
// 常量
// ============================================================================
/// 转换因子0.0265 * 4.1347e-15
const OPAC_FACTOR: f64 = 0.0265 * 4.1347e-15;
// ============================================================================
// GHYDOP 参数结构体
// ============================================================================
/// GHYDOP 输入参数。
#[derive(Debug, Clone)]
pub struct GhydopParams {
/// 频率索引 IJ (1-indexed)
pub ij: usize,
/// 深度点数
pub nd: usize,
// 模型变量 [MDEPTH]
/// 电子密度 (cm⁻³)
pub elec: Vec<f64>,
/// 温度 (K)
pub temp: Vec<f64>,
/// 粒子数分布 [能级 × 深度]
pub popul: Vec<Vec<f64>>,
/// 吸收系数 [MDEPTH]
pub abso1: Vec<f64>,
/// 发射系数 [MDEPTH]
pub emis1: Vec<f64>,
/// 普朗克函数 × XKF [MDEPTH]
pub xkfb: Vec<f64>,
/// 1 - XKF [MDEPTH]
pub xkf1: Vec<f64>,
// 频率 [MFREQ]
pub freq: Vec<f64>,
// 原子数据
/// 氢离子索引
pub ielh: usize,
/// 氢第一能级索引 [MION]
pub nfirst: Vec<i32>,
/// 统计权重 [MLEVEL]
pub g: Vec<f64>,
// Gomez 表参数
/// 氢 Gomez 表标志 (0=禁用)
pub ihgom: i32,
/// 频率插值索引 [MFREQ]
pub jgint: Vec<i32>,
/// 氢 Gomez 表电子密度限制
pub hglim: f64,
/// 电子密度表边界 (ln)
pub egtab1: f64,
pub egtab2: f64,
/// 温度表边界 (ln)
pub tgtab1: f64,
pub tgtab2: f64,
/// Gomez 表电子密度数
pub nugele: i32,
/// Gomez 表温度数
pub nugtemp: i32,
/// 电子密度向量 (ln) [MTABEH]
pub elevec: Vec<f64>,
/// 温度向量 (ln) [MTABTH]
pub temvec: Vec<f64>,
/// 氢截面表 (温度 × 电子密度 × 频率) [MTABTH × MTABEH × MFREQ]
pub hydcrs: Vec<Vec<Vec<f64>>>,
}
impl Default for GhydopParams {
fn default() -> Self {
Self {
ij: 1,
nd: 1,
elec: vec![0.0; MDEPTH],
temp: vec![0.0; MDEPTH],
popul: vec![vec![0.0; MDEPTH]; 100], // 简化
abso1: vec![0.0; MDEPTH],
emis1: vec![0.0; MDEPTH],
xkfb: vec![0.0; MDEPTH],
xkf1: vec![0.0; MDEPTH],
freq: vec![0.0; MFREQ],
ielh: 1,
nfirst: vec![0; 100], // 简化
g: vec![0.0; 100], // 简化
ihgom: 0,
jgint: vec![0; MFREQ],
hglim: 0.0,
egtab1: 0.0,
egtab2: 0.0,
tgtab1: 0.0,
tgtab2: 0.0,
nugele: 0,
nugtemp: 0,
elevec: vec![0.0; MTABEH],
temvec: vec![0.0; MTABTH],
hydcrs: vec![vec![vec![0.0; MFREQ]; MTABEH]; MTABTH],
}
}
}
/// GHYDOP 输出结果(修改后的吸收/发射系数)。
#[derive(Debug, Clone, Default)]
pub struct GhydopResult {
/// 更新后的吸收系数 [MDEPTH]
pub abso1: Vec<f64>,
/// 更新后的发射系数 [MDEPTH]
pub emis1: Vec<f64>,
}
// ============================================================================
// GHYDOP 主函数
// ============================================================================
/// 计算氢不透明度Gomez 表)。
///
/// # 参数
///
/// * `params` - 输入参数(包含模型状态和 Gomez 表数据)
///
/// # 返回值
///
/// 返回更新后的 `abso1` 和 `emis1`
///
/// # 说明
///
/// 此函数从 Gomez 表计算氢的谱线和伪连续谱不透明度:
/// 1. 如果 `ihgom = 0` 或 `jgint(ij) = 0`,直接返回
/// 2. 对每个深度点进行双线性插值计算截面
/// 3. 更新吸收和发射系数
pub fn ghydop(params: &mut GhydopParams) -> GhydopResult {
let mut result = GhydopResult {
abso1: params.abso1.clone(),
emis1: params.emis1.clone(),
};
// 如果 ihgom = 0 或 jgint(ij) = 0直接返回
let ij_idx = params.ij - 1; // 0-indexed
if params.ihgom == 0 || params.jgint[ij_idx] == 0 {
return result;
}
let jf = params.ij; // 保持 1-indexed 用于 hydcrs 访问
// 遍历深度点
for id in 1..=params.nd {
let id_idx = id - 1; // 0-indexed
// 检查电子密度是否超过限制
if params.elec[id_idx] < params.hglim {
continue;
}
// 计算对数电子密度和温度
let rl = params.elec[id_idx].ln();
let tl = params.temp[id_idx].ln();
// ================================================================
// 电子密度插值索引
// ================================================================
// DELTAR = (RL - EGTAB1) / (EGTAB2 - EGTAB1) * (NUGELE - 1)
let deltar = (rl - params.egtab1) / (params.egtab2 - params.egtab1)
* (params.nugele - 1) as f64;
let jr = (1.0 + deltar.floor()) as i32;
let jr = jr.clamp(1, params.nugele - 1) as usize;
let r1i = params.elevec[jr - 1];
let r2i = params.elevec[jr];
let mut dri = (rl - r1i) / (r2i - r1i);
if jr == 1 {
dri = 0.0;
}
// ================================================================
// 温度插值索引
// ================================================================
// DELTAT = (TL - TGTAB1) / (TGTAB2 - TGTAB1) * (NUGTEMP - 1)
let deltat = (tl - params.tgtab1) / (params.tgtab2 - params.tgtab1)
* (params.nugtemp - 1) as f64;
let jt = (1.0 + deltat.floor()) as i32;
let jt = jt.clamp(1, params.nugtemp - 1) as usize;
let t1i = params.temvec[jt - 1];
let t2i = params.temvec[jt];
let mut dti = (tl - t1i) / (t2i - t1i);
if jt == 1 {
dti = 0.0;
}
// ================================================================
// 双线性插值计算截面
// ================================================================
// opr1 = hydcrs(jt, jr, jf) + dti * (hydcrs(jt+1, jr, jf) - hydcrs(jt, jr, jf))
let opr1 = params.hydcrs[jt - 1][jr - 1][jf - 1]
+ dti * (params.hydcrs[jt][jr - 1][jf - 1] - params.hydcrs[jt - 1][jr - 1][jf - 1]);
// opr2 = hydcrs(jt, jr+1, jf) + dti * (hydcrs(jt+1, jr+1, jf) - hydcrs(jt, jr+1, jf))
let opr2 = params.hydcrs[jt - 1][jr][jf - 1]
+ dti * (params.hydcrs[jt][jr][jf - 1] - params.hydcrs[jt - 1][jr][jf - 1]);
let opac = opr1 + dri * (opr2 - opr1);
// AB = exp(opac) * 0.0265 * 4.1347e-15
let ab = opac.exp() * OPAC_FACTOR;
// ================================================================
// 确定氢能级索引
// ================================================================
// if freq(ij) > 8.22013e14 then
// ii = nfirst(ielh)
// else
// ii = nfirst(ielh) + 1
// end if
let ii = if params.freq[ij_idx] > 8.22013e14 {
params.nfirst[params.ielh] as usize
} else {
(params.nfirst[params.ielh] + 1) as usize
};
// ================================================================
// 计算氢不透明度
// ================================================================
// oph = ab * popul(ii, id) * g(ii)
let oph = ab * params.popul[ii - 1][id_idx] * params.g[ii - 1];
// abso1(id) = abso1(id) + oph
result.abso1[id_idx] += oph;
// emis1(id) = emis1(id) + oph * xkfb(id) / xkf1(id)
if params.xkf1[id_idx].abs() > 1e-30 {
result.emis1[id_idx] += oph * params.xkfb[id_idx] / params.xkf1[id_idx];
}
}
result
}
// ============================================================================
// 测试
// ============================================================================
#[cfg(test)]
mod tests {
use super::*;
fn create_test_params() -> GhydopParams {
let mut params = GhydopParams::default();
params.ij = 5;
params.nd = 5;
params.ihgom = 1;
params.hglim = 1e10; // 电子密度限制
params.ielh = 1;
params.nfirst[1] = 1;
params.g[0] = 2.0; // 氢基态统计权重
params.g[1] = 8.0; // 氢第一激发态
// 设置频率
params.freq[4] = 1e15; // 大于 8.22013e14
// 设置 Gomez 表参数
params.nugele = 10;
params.nugtemp = 10;
params.egtab1 = 20.0; // ln(5e8)
params.egtab2 = 30.0; // ln(1e13)
params.tgtab1 = 8.0; // ln(3000)
params.tgtab2 = 10.0; // ln(22000)
// 设置电子密度向量
for i in 0..10 {
params.elevec[i] = params.egtab1 + (params.egtab2 - params.egtab1) * i as f64 / 9.0;
}
// 设置温度向量
for i in 0..10 {
params.temvec[i] = params.tgtab1 + (params.tgtab2 - params.tgtab1) * i as f64 / 9.0;
}
// 设置 jgint
params.jgint[4] = 5; // 非零
// 设置电子密度和温度
for i in 0..5 {
params.elec[i] = 1e11 + i as f64 * 1e10;
params.temp[i] = 10000.0 + i as f64 * 1000.0;
params.popul[0][i] = 1e14;
params.popul[1][i] = 1e12;
params.xkfb[i] = 0.5;
params.xkf1[i] = 0.3;
}
// 设置氢截面表(简单值)
for it in 0..10 {
for ir in 0..10 {
for kf in 0..MFREQ {
params.hydcrs[it][ir][kf] = -20.0; // 简单的对数截面
}
}
}
params
}
#[test]
fn test_ghydop_disabled() {
let mut params = GhydopParams::default();
params.ihgom = 0; // 禁用
let result = ghydop(&mut params);
// 禁用时不应修改
for i in 0..MDEPTH {
assert!((result.abso1[i]).abs() < 1e-30);
assert!((result.emis1[i]).abs() < 1e-30);
}
}
#[test]
fn test_ghydop_jgint_zero() {
let mut params = GhydopParams::default();
params.ij = 1;
params.ihgom = 1;
params.jgint[0] = 0; // jgint = 0
let result = ghydop(&mut params);
// jgint = 0 时不应修改
for i in 0..MDEPTH {
assert!((result.abso1[i]).abs() < 1e-30);
assert!((result.emis1[i]).abs() < 1e-30);
}
}
#[test]
fn test_ghydop_below_hglim() {
let mut params = GhydopParams::default();
params.ij = 1;
params.nd = 2;
params.ihgom = 1;
params.jgint[0] = 1;
params.hglim = 1e12; // 高限制
// 电子密度低于限制
params.elec[0] = 1e10;
params.elec[1] = 1e11;
let result = ghydop(&mut params);
// 所有电子密度都低于 hglim不应修改
for i in 0..MDEPTH {
assert!((result.abso1[i]).abs() < 1e-30);
assert!((result.emis1[i]).abs() < 1e-30);
}
}
#[test]
fn test_ghydop_basic() {
let mut params = create_test_params();
let result = ghydop(&mut params);
// 检查结果有限
for i in 0..params.nd {
assert!(result.abso1[i].is_finite());
assert!(result.emis1[i].is_finite());
}
// 应该有非零值(因为 elec > hglim
let has_nonzero = result.abso1.iter().take(params.nd).any(|&x| x > 0.0);
assert!(has_nonzero);
}
#[test]
fn test_ghydop_low_frequency() {
let mut params = create_test_params();
params.freq[4] = 7e14; // 低于 8.22013e14,使用 nfirst + 1
let result = ghydop(&mut params);
// 检查结果有限
for i in 0..params.nd {
assert!(result.abso1[i].is_finite());
assert!(result.emis1[i].is_finite());
}
}
#[test]
fn test_opac_factor() {
// 验证转换因子
let expected = 0.0265 * 4.1347e-15;
assert!((OPAC_FACTOR - expected).abs() / expected < 1e-15);
}
}

398
src/math/levgrp.rs Normal file
View File

@ -0,0 +1,398 @@
//! 能级分组占据数计算。
//!
//! 重构自 TLUSTY `levgrp.f`。
//!
//! 计算能级组的总占据数和相对占据数。
use crate::state::constants::{MDEPTH, MLEVEL, MLVEXP};
// ============================================================================
// LEVGRP 参数结构体
// ============================================================================
/// LEVGRP 输入参数。
#[derive(Debug, Clone)]
pub struct LevgrpParams {
/// 深度索引 ID (1-indexed)
pub id: usize,
/// 输入模式:
/// - 0: 从 POPUL 读取占据数
/// - 1: 从 POPP 参数读取占据数
pub imode: i32,
/// 能级分组参数数组 [MLEVEL]
/// - 正值: 组长 (主能级)
/// - 负值: 组成员
/// - 零: 不分组
pub iical: Vec<i32>,
/// 输入占据数数组 [MLEVEL] (仅 IMODE=1 使用)
pub popp: Vec<f64>,
// 模型参数
/// 总能级数
pub nlevel: usize,
/// 显式能级数
pub nlvexp: usize,
/// 选项标志 (<0 时跳过)
pub ioptab: i32,
/// 占据数 [MLEVEL × MDEPTH]
pub popul: Vec<Vec<f64>>,
/// 总粒子密度 [MDEPTH]
pub dens: Vec<f64>,
/// 平均分子量倒数 [MDEPTH]
pub wmm: Vec<f64>,
/// 总粒子数 [MDEPTH]
pub ytot: Vec<f64>,
/// 原子丰度 [MATOM × MDEPTH]
pub abund: Vec<Vec<f64>>,
/// 能级所属原子索引 [MLEVEL]
pub iatm: Vec<i32>,
// 迭代参数
/// 当前迭代次数
pub iter: i32,
/// Kantorovich 标志数组
pub kant: Vec<i32>,
/// 加速参数
pub iacc: i32,
// 阈值
/// 零占据数阈值
pub popzer: f64,
}
impl Default for LevgrpParams {
fn default() -> Self {
Self {
id: 1,
imode: 0,
iical: vec![0; MLEVEL],
popp: vec![0.0; MLEVEL],
nlevel: 1,
nlvexp: 1,
ioptab: 0,
popul: vec![vec![0.0; MDEPTH]; MLEVEL],
dens: vec![0.0; MDEPTH],
wmm: vec![0.0; MDEPTH],
ytot: vec![0.0; MDEPTH],
abund: vec![vec![0.0; MDEPTH]; 100], // 简化
iatm: vec![0; MLEVEL],
iter: 0,
kant: vec![0; 100], // 简化
iacc: 0,
popzer: 1e-30,
}
}
}
/// LEVGRP 输出结果。
#[derive(Debug, Clone, Default)]
pub struct LevgrpResult {
/// 能级组占据数 [MLVEXP]
pub popgrp: Vec<f64>,
/// 相对占据数 [MLEVEL × MDEPTH]
pub sbpsi: Vec<Vec<f64>>,
/// 能级组零占据标志 [MLVEXP × MDEPTH]
pub igzero: Vec<Vec<i32>>,
/// 相对占据数初始值 [MLVEXP × MDEPTH]
pub rpop0: Vec<Vec<f64>>,
}
impl LevgrpResult {
pub fn new() -> Self {
Self {
popgrp: vec![0.0; MLVEXP],
sbpsi: vec![vec![0.0; MDEPTH]; MLEVEL],
igzero: vec![vec![0; MDEPTH]; MLVEXP],
rpop0: vec![vec![0.0; MDEPTH]; MLVEXP],
}
}
}
// ============================================================================
// LEVGRP 主函数
// ============================================================================
/// 计算能级组的总占据数和相对占据数。
///
/// # 参数
///
/// * `params` - 输入参数
///
/// # 返回值
///
/// 返回 LevgrpResult 包含更新后的 popgrp, sbpsi, igzero, rpop0
///
/// # 说明
///
/// 此函数执行以下计算:
/// 1. 计算每个能级组的总占据数
/// 2. 计算组内成员相对于总占据数的比例
/// 3. 如果组总占据数过小,将整个组归零
pub fn levgrp(params: &LevgrpParams) -> LevgrpResult {
let mut result = LevgrpResult::new();
// 如果 ioptab < 0直接返回
if params.ioptab < 0 {
return result;
}
let id_idx = params.id - 1; // 0-indexed
// 初始化能级组占据数和零标志
for i in 0..params.nlvexp {
result.popgrp[i] = 0.0;
result.igzero[i][id_idx] = 0;
}
// 获取输入占据数
let pop_input: Vec<f64> = if params.imode == 0 {
// 从 POPUL 读取
(0..params.nlevel)
.map(|i| params.popul[i][id_idx])
.collect()
} else {
// 使用传入的 POPP
params.popp.clone()
};
// ========================================================================
// 计算能级组总占据数
// ========================================================================
for i in 0..params.nlevel {
let ii = (params.iical[i].abs()) as usize;
if ii > 0 && ii <= params.nlvexp {
result.popgrp[ii - 1] += pop_input[i];
}
}
// ========================================================================
// 计算相对占据数 (组内成员相对于总占据数的比例)
// ========================================================================
for i in 0..params.nlevel {
let ii = params.iical[i];
if ii < 0 {
let grp_idx = (-ii) as usize - 1; // 0-indexed
if result.popgrp[grp_idx] > 0.0 {
result.sbpsi[i][id_idx] = pop_input[i] / result.popgrp[grp_idx];
} else {
result.sbpsi[i][id_idx] = 0.0;
result.igzero[grp_idx][id_idx] = 1;
}
}
}
// ========================================================================
// 检查是否需要归零整个组
// (当组总占据数小于 popzer × 原子总粒子数时)
// ========================================================================
let lkit = if params.iter == 0 {
true
} else {
let kant_idx = (params.iter - 1) as usize;
let kant_val = if kant_idx < params.kant.len() {
params.kant[kant_idx]
} else {
0
};
kant_val == 0 && params.iter < params.iacc
};
if lkit {
for i in 0..params.nlevel {
let iat = params.iatm[i] as usize;
// 计算原子总粒子数
// POPM = DENS(ID) / WMM(ID) / YTOT(ID) * ABUND(IAT, ID)
let popm = if params.wmm[id_idx].abs() > 1e-30 && params.ytot[id_idx].abs() > 1e-30 {
params.dens[id_idx] / params.wmm[id_idx] / params.ytot[id_idx]
* params.abund[iat][id_idx]
} else {
0.0
};
let ii = params.iical[i];
if ii < 0 {
let grp_idx = (-ii) as usize - 1;
if grp_idx < params.nlvexp && result.popgrp[grp_idx] / popm < params.popzer {
result.popgrp[grp_idx] = 0.0;
result.igzero[grp_idx][id_idx] = 1;
}
if grp_idx < params.nlvexp {
result.rpop0[grp_idx][id_idx] = result.popgrp[grp_idx] / popm;
}
} else if ii > 0 {
let grp_idx = (ii - 1) as usize;
if grp_idx < params.nlvexp {
result.rpop0[grp_idx][id_idx] = result.popgrp[grp_idx] / popm;
}
}
}
}
result
}
// ============================================================================
// 测试
// ============================================================================
#[cfg(test)]
mod tests {
use super::*;
fn create_test_params() -> LevgrpParams {
let mut params = LevgrpParams::default();
params.id = 1;
params.imode = 0;
params.nlevel = 10;
params.nlvexp = 3;
params.ioptab = 1;
params.iter = 0;
params.iacc = 10;
// 设置分组参数
// 能级 1,2,3 属于组 1 (能级 1 是组长2,3 是成员)
params.iical[0] = 1; // 组 1 组长
params.iical[1] = -1; // 组 1 成员
params.iical[2] = -1; // 组 1 成员
// 能级 4,5,6 属于组 2
params.iical[3] = 2; // 组 2 组长
params.iical[4] = -2; // 组 2 成员
params.iical[5] = -2; // 组 2 成员
// 能级 7,8,9 属于组 3
params.iical[6] = 3; // 组 3 组长
params.iical[7] = -3; // 组 3 成员
params.iical[8] = -3; // 组 3 成员
// 能级 10 不分组
params.iical[9] = 0;
// 设置占据数
for i in 0..10 {
params.popul[i][0] = 1e10 * (i + 1) as f64;
params.iatm[i] = 1; // 属于原子 1
}
// 设置模型参数
params.dens[0] = 1e14;
params.wmm[0] = 1.0;
params.ytot[0] = 1.0;
params.abund[1][0] = 0.9; // 原子 1 丰度
params
}
#[test]
fn test_levgrp_ioptab_negative() {
let mut params = LevgrpParams::default();
params.ioptab = -1; // 禁用
let result = levgrp(&params);
// 禁用时所有输出应为 0
for i in 0..MLVEXP {
assert!((result.popgrp[i]).abs() < 1e-30);
}
}
#[test]
fn test_levgrp_basic() {
let params = create_test_params();
let result = levgrp(&params);
// 检查组占据数
// 组 1: 1e10 + 2e10 + 3e10 = 6e10
assert!((result.popgrp[0] - 6e10).abs() < 1e5);
// 组 2: 4e10 + 5e10 + 6e10 = 15e10
assert!((result.popgrp[1] - 15e10).abs() < 1e5);
// 组 3: 7e10 + 8e10 + 9e10 = 24e10
assert!((result.popgrp[2] - 24e10).abs() < 1e5);
}
#[test]
fn test_levgrp_relative_populations() {
let params = create_test_params();
let result = levgrp(&params);
// 检查相对占据数
// 能级 2: 2e10 / 6e10 = 1/3
assert!((result.sbpsi[1][0] - 1.0/3.0).abs() < 1e-10);
// 能级 3: 3e10 / 6e10 = 1/2
assert!((result.sbpsi[2][0] - 0.5).abs() < 1e-10);
// 能级 5: 5e10 / 15e10 = 1/3
assert!((result.sbpsi[4][0] - 1.0/3.0).abs() < 1e-10);
}
#[test]
fn test_levgrp_imode_1() {
let mut params = create_test_params();
params.imode = 1;
// 使用自定义占据数
for i in 0..10 {
params.popp[i] = 1e11;
}
let result = levgrp(&params);
// 组 1: 3 * 1e11 = 3e11
assert!((result.popgrp[0] - 3e11).abs() < 1e6);
}
#[test]
fn test_levgrp_zero_group() {
let mut params = LevgrpParams::default();
params.id = 1;
params.imode = 0;
params.nlevel = 3;
params.nlvexp = 1;
params.ioptab = 1;
params.iter = 0;
params.iacc = 10;
params.popzer = 1e-10; // 设置较大的阈值
// 设置分组
params.iical[0] = 1; // 组 1 组长
params.iical[1] = -1; // 组 1 成员
params.iical[2] = -1; // 组 1 成员
// 设置很小的占据数
params.popul[0][0] = 1e-20;
params.popul[1][0] = 1e-20;
params.popul[2][0] = 1e-20;
// 设置模型参数
params.dens[0] = 1e14;
params.wmm[0] = 1.0;
params.ytot[0] = 1.0;
params.abund[1][0] = 0.9;
params.iatm[0] = 1;
params.iatm[1] = 1;
params.iatm[2] = 1;
let result = levgrp(&params);
// 组占据数应被归零
assert!((result.popgrp[0]).abs() < 1e-30);
assert_eq!(result.igzero[0][0], 1);
}
#[test]
fn test_levgrp_rpop0() {
let params = create_test_params();
let result = levgrp(&params);
// 检查 rpop0 被设置
for grp_idx in 0..3 {
assert!(result.rpop0[grp_idx][0] >= 0.0);
assert!(result.rpop0[grp_idx][0].is_finite());
}
}
}

304
src/math/linspl.rs Normal file
View File

@ -0,0 +1,304 @@
//! 设置谱线的深度无关轮廓。
//!
//! 重构自 TLUSTY `linspl.f`。
//!
//! 类似于采样模式中使用的 LINSET。
use super::profil::{profil, ProfilParams};
// ============================================================================
// 常量
// ============================================================================
/// 振子强度转换因子
const OS0: f64 = 0.02654;
// ============================================================================
// LINSPL 参数结构体
// ============================================================================
/// LINSPL 输入参数。
#[derive(Debug, Clone)]
pub struct LinsplParams {
/// 跃迁索引 (1-indexed)
pub itr: usize,
/// Doppler 宽度
pub dop: f64,
/// 阻尼参数 (仅用于 Voigt 或非标准轮廓)
pub agam: f64,
// 频率索引
/// 跃迁起始频率索引 [跃迁数]
pub ifr0: Vec<i32>,
/// 跃迁结束频率索引 [跃迁数]
pub ifr1: Vec<i32>,
// 轮廓存储索引
/// 轮廓存储起始索引 [跃迁数]
pub kfr0: Vec<i32>,
/// 轮廓存储结束索引 [跃迁数]
pub kfr1: Vec<i32>,
// 原子参数
/// 振子强度 [跃迁数]
pub osc0: Vec<f64>,
/// 轮廓类型 [跃迁数]
/// - 0: Doppler
/// - 1: Voigt
/// - 2: Stark
/// - >10: 用户定义
pub iprof: Vec<i32>,
// 频率数组
/// 频率 [频率点数]
pub freq: Vec<f64>,
// PROFIL 所需参数
/// PROFIL 参数
pub profil_params: ProfilParams,
}
impl Default for LinsplParams {
fn default() -> Self {
Self {
itr: 1,
dop: 1.0,
agam: 0.0,
ifr0: vec![0; 100],
ifr1: vec![0; 100],
kfr0: vec![0; 100],
kfr1: vec![0; 100],
osc0: vec![0.0; 100],
iprof: vec![0; 100],
freq: vec![0.0; 1000],
profil_params: ProfilParams::default(),
}
}
}
/// LINSPL 输出结果。
#[derive(Debug, Clone, Default)]
pub struct LinsplResult {
/// 更新后的轮廓数组
pub prof: Vec<f64>,
}
// ============================================================================
// LINSPL 主函数
// ============================================================================
/// 设置谱线的深度无关轮廓。
///
/// # 参数
///
/// * `params` - 输入参数
/// * `prof` - 现有轮廓数组 (会被修改)
///
/// # 返回值
///
/// 返回更新后的轮廓数组
///
/// # 说明
///
/// 此函数计算一条谱线的深度无关轮廓:
/// 1. 获取跃迁的频率范围 [IJ0, IJ1]
/// 2. 获取轮廓存储范围 [KJ0, KJ1]
/// 3. S = OSC0(ITR) * 0.02654
/// 4. 对每个频率点计算: PROF = PROFIL * S / DOP
/// 5. 如果 IPROF >= 0, 将端点设为 0
pub fn linspl(params: &LinsplParams, prof: &mut Vec<f64>) {
let itr_idx = params.itr - 1; // 0-indexed
// 获取频率范围
let ij0 = params.ifr0[itr_idx] as usize;
let ij1 = params.ifr1[itr_idx] as usize;
let n = ij1 - ij0 + 1;
// 获取轮廓存储范围
let kj0 = params.kfr0[itr_idx] as usize;
let kj1 = params.kfr1[itr_idx] as usize;
// 振子强度转换
let s = params.osc0[itr_idx] * OS0;
// 轮廓类型
let ip0 = params.iprof[itr_idx];
let ip = ip0.abs();
// 构建 PROFIL 参数
let mut profil_params = params.profil_params.clone();
profil_params.itr = params.itr;
profil_params.a = params.agam;
profil_params.dop = params.dop;
profil_params.ip = ip;
profil_params.id = 0; // 深度无关
// 计算每个频率点的轮廓
for i in 1..=n {
let freq_idx = ij0 + i - 1 - 1; // 0-indexed
profil_params.fr = params.freq[freq_idx];
let prof_val = profil(&profil_params) * s / params.dop;
let prof_idx = kj0 + i - 1 - 1; // 0-indexed
if prof_idx < prof.len() {
prof[prof_idx] = prof_val;
}
}
// 如果 IPROF >= 0, 将端点设为 0
if ip0 >= 0 {
let k0_idx = kj0 - 1; // 0-indexed
let k1_idx = kj1 - 1;
if k0_idx < prof.len() {
prof[k0_idx] = 0.0;
}
if k1_idx < prof.len() {
prof[k1_idx] = 0.0;
}
}
}
// ============================================================================
// 测试
// ============================================================================
#[cfg(test)]
mod tests {
use super::*;
fn create_test_params() -> (LinsplParams, Vec<f64>) {
let mut params = LinsplParams::default();
params.itr = 1;
params.dop = 1e10;
params.agam = 0.01;
// 设置频率范围
params.ifr0[0] = 1;
params.ifr1[0] = 10;
params.kfr0[0] = 1;
params.kfr1[0] = 10;
// 设置振子强度和轮廓类型
params.osc0[0] = 0.5;
params.iprof[0] = 1; // Voigt
// 设置频率数组
let fr0 = 4.57e14; // H-alpha 线心
for i in 0..10 {
params.freq[i] = fr0 + (i as f64 - 4.5) * 1e10;
}
// 设置 PROFIL 参数
params.profil_params.fr0 = vec![fr0; 100];
params.profil_params.ilow = vec![1; 100];
params.profil_params.iup = vec![2; 100];
params.profil_params.nquant = vec![2, 3, 0, 0, 0];
params.profil_params.iel = vec![1; 100];
params.profil_params.iz = vec![1; 100];
params.profil_params.elec = vec![1e12; 100];
params.profil_params.grav = 1.0;
params.profil_params.iquasi = 0;
// 创建轮廓数组
let prof = vec![0.0; 100];
(params, prof)
}
#[test]
fn test_linspl_basic() {
let (params, mut prof) = create_test_params();
linspl(&params, &mut prof);
// 检查轮廓值被设置
// 注意: 端点 (索引 0 和 9) 应该为 0
assert!((prof[0] - 0.0).abs() < 1e-30);
assert!((prof[9] - 0.0).abs() < 1e-30);
// 中间点应该有值
for i in 1..9 {
assert!(prof[i] > 0.0);
assert!(prof[i].is_finite());
}
}
#[test]
fn test_linspl_doppler() {
let (mut params, mut prof) = create_test_params();
params.iprof[0] = 0; // Doppler 轮廓
linspl(&params, &mut prof);
// 端点为 0
assert!((prof[0] - 0.0).abs() < 1e-30);
assert!((prof[9] - 0.0).abs() < 1e-30);
// 中间点有值
for i in 1..9 {
assert!(prof[i] >= 0.0);
}
}
#[test]
fn test_linspl_negative_iprof() {
let (mut params, mut prof) = create_test_params();
params.iprof[0] = -1; // 负值,端点不为 0
linspl(&params, &mut prof);
// 端点应该有值 (因为 IP0 < 0)
assert!(prof[0] > 0.0);
assert!(prof[9] > 0.0);
}
#[test]
fn test_linspl_scale_factor() {
let (mut params1, mut prof1) = create_test_params();
let (mut params2, mut prof2) = create_test_params();
// 不同的振子强度
params1.osc0[0] = 0.5;
params2.osc0[0] = 1.0;
linspl(&params1, &mut prof1);
linspl(&params2, &mut prof2);
// prof2 应该大约是 prof1 的 2 倍 (中间点)
for i in 1..9 {
let ratio = prof2[i] / prof1[i];
assert!((ratio - 2.0).abs() < 1e-10);
}
}
#[test]
fn test_linspl_doppler_width() {
let (mut params1, mut prof1) = create_test_params();
let (mut params2, mut prof2) = create_test_params();
// 不同的 Doppler 宽度
params1.dop = 1e10;
params2.dop = 2e10;
linspl(&params1, &mut prof1);
linspl(&params2, &mut prof2);
// 轮廓值应该不同
// 注意: 由于轮廓归一化到 1/DOP且乘以 S/DOP
// 总体比例不是简单的线性关系
for i in 1..9 {
assert!(prof1[i] > 0.0);
assert!(prof2[i] > 0.0);
}
}
#[test]
fn test_os0_constant() {
// 验证常量值
assert!((OS0 - 0.02654).abs() < 1e-15);
}
}

View File

@ -7,6 +7,7 @@ mod allardt;
mod angset;
mod betah;
mod bkhsgo;
mod bhe;
mod bpopf;
mod butler;
mod carbon;
@ -14,6 +15,8 @@ mod ceh12;
mod cion;
mod ckoest;
mod collhe;
mod compt0;
mod comset;
mod cross;
mod ctdata;
mod cubic;
@ -34,6 +37,7 @@ mod getwrd;
mod gami;
mod gamsp;
mod gfree;
mod ghydop;
mod gaunt;
mod gntk;
mod gridp;
@ -50,7 +54,9 @@ mod irc;
mod interpolate;
mod laguer;
mod levsol;
mod levgrp;
mod lineqs;
mod linspl;
mod locate;
mod matinv;
mod meanop;
@ -59,27 +65,38 @@ mod odfhst;
mod pfcno;
mod pffe;
mod prdini;
mod profil;
mod quartc;
mod pfni;
mod pzert;
mod pzevld;
mod pfspec;
mod psolve;
mod quit;
mod reflev;
mod raph;
mod ratmal;
mod rayleigh;
mod rybmat;
mod rayset;
mod reiman;
mod rte_sc;
mod rtefe2;
mod rtedf1;
mod rtedf2;
mod rtecf0;
mod rtesol;
mod sbfch;
mod sbfhe1;
mod sbfhmi;
mod sbfhmi_old;
mod sbfoh;
mod setdrt;
mod sghe12;
mod sgmer;
mod sffhmi;
mod tabint;
mod taufr1;
mod sffhmi_add;
mod spsigk;
mod stark0;
@ -110,6 +127,7 @@ pub use allardt::{allardt, AllardData};
pub use angset::angset;
pub use betah::betah;
pub use bkhsgo::bkhsgo;
pub use bhe::{bhe, bhed, bhez, BheParams, BheState, MatKey};
pub use bpopf::{bpopf, BpopfParams};
pub use butler::butler;
pub use carbon::carbon;
@ -117,6 +135,7 @@ pub use ceh12::ceh12;
pub use cion::cion;
pub use ckoest::ckoest;
pub use collhe::collhe;
pub use comset::{comset, ComsetParams, ComsetResult};
pub use cross::{cross, crossd};
pub use ctdata::{hction, hctrecom, CTION, CTRECOMB};
pub use cubic::{cubic, CubicCon};
@ -137,6 +156,7 @@ pub use getwrd::getwrd;
pub use gami::gami;
pub use gamsp::gamsp;
pub use gfree::{gfree0, gfreed};
pub use ghydop::{ghydop, GhydopParams, GhydopResult};
pub use gaunt::gaunt;
pub use gntk::gntk;
pub use gridp::gridp;
@ -153,7 +173,9 @@ pub use irc::irc;
pub use interpolate::{lagran, yint};
pub use laguer::laguer;
pub use levsol::levsol;
pub use levgrp::{levgrp, LevgrpParams, LevgrpResult};
pub use lineqs::{lineqs, lineqs_nr};
pub use linspl::{linspl, LinsplParams};
pub use locate::locate;
pub use matinv::matinv;
pub use meanop::meanop;
@ -162,10 +184,14 @@ pub use odfhst::odfhst;
pub use pfcno::pfcno;
pub use pffe::pffe;
pub use prdini::prdini;
pub use profil::{profil, ProfilParams};
pub use pfni::pfni;
pub use pzert::pzert;
pub use pzevld::pzevld;
pub use pfspec::pfspec;
pub use psolve::psolve;
pub use quartc::quartc;
pub use reflev::reflev;
pub use quit::{quit, quit_error};
pub use raph::raph;
pub use ratmal::ratmal;
@ -177,17 +203,24 @@ pub use rayset::rayset;
pub use reiman::reiman;
pub use rte_sc::rte_sc;
pub use rtefe2::rtefe2;
pub use rtedf1::{rtedf1, Rtedf1AliState, Rtedf1ModelState, Rtedf1Params};
pub use rtedf2::rtedf2;
pub use rtecf0::rtecf0;
pub use rtesol::rtesol;
pub use rybmat::{rybmat, RybmatParams, RybmatResult};
pub use sbfch::sbfch;
pub use sbfhe1::sbfhe1;
pub use sbfhmi::sbfhmi;
pub use sbfhmi_old::sbfhmi_old;
pub use sbfoh::sbfoh;
pub use setdrt::setdrt;
pub use sghe12::sghe12;
pub use sgmer::{sgmer0, sgmer1, sgmerd};
pub use sffhmi::sffhmi;
pub use sffhmi_add::sffhmi_add;
pub use spsigk::spsigk;
pub use tabint::{tabint, IntCff, OpacTable, TabintParams};
pub use taufr1::{taufr1, Taufr1Params, Taufr1Result};
pub use stark0::stark0;
pub use starka::starka;
pub use szirc::szirc;

View File

@ -6,7 +6,7 @@
use crate::state::constants::{TWO, UN};
use crate::state::model::StrAux;
use crate::state::odfpar::MFRO;
use crate::state::MFRO;
/// ODF Stark 展宽辅助函数。
///

366
src/math/profil.rs Normal file
View File

@ -0,0 +1,366 @@
//! 标准吸收轮廓函数。
//!
//! 重构自 TLUSTY `profil.f`。
//!
//! 计算归一化到 1 的标准吸收轮廓。
use crate::state::constants::{TWO, UN};
// ============================================================================
// 常量
// ============================================================================
/// sqrt(pi)
const PISQ: f64 = 1.77245385090551;
/// 1 / sqrt(pi)
const PISQ1: f64 = UN / PISQ;
// ============================================================================
// PROFIL 参数结构体
// ============================================================================
/// PROFIL 输入参数。
#[derive(Debug, Clone)]
pub struct ProfilParams {
/// 频率
pub fr: f64,
/// Voigt 阻尼参数
pub a: f64,
/// Doppler 宽度
pub dop: f64,
/// 跃迁索引 (1-indexed)
pub itr: usize,
/// 轮廓类型:
/// - 0: Doppler 轮廓
/// - 1: Voigt 轮廓
/// - 2: Stark (+ Doppler) 轮廓 (氢线)
/// - >10: 用户提供的轮廓 (PROFSP)
pub ip: i32,
/// 深度索引 (1-indexed, <= 0 时使用默认电子密度)
pub id: i32,
// 模型参数
/// 线心频率 [跃迁数]
pub fr0: Vec<f64>,
/// 电子密度 [深度]
pub elec: Vec<f64>,
/// 重力加速度 (用于默认电子密度)
pub grav: f64,
// 原子参数
/// 下能级索引 [跃迁数]
pub ilow: Vec<i32>,
/// 上能级索引 [跃迁数]
pub iup: Vec<i32>,
/// 主量子数 [能级数]
pub nquant: Vec<i32>,
/// 能级所属原子索引 [能级数]
pub iel: Vec<i32>,
/// 原子电荷 [原子数]
pub iz: Vec<i32>,
// Quasistatic Stark 参数
/// Quasistatic 模式标志
pub iquasi: i32,
}
impl Default for ProfilParams {
fn default() -> Self {
Self {
fr: 0.0,
a: 0.0,
dop: 1.0,
itr: 1,
ip: 0,
id: 1,
fr0: vec![0.0; 100],
elec: vec![0.0; 100],
grav: 1.0,
ilow: vec![0; 100],
iup: vec![0; 100],
nquant: vec![0; 100],
iel: vec![0; 100],
iz: vec![0; 100],
iquasi: 0,
}
}
}
// ============================================================================
// PROFIL 主函数
// ============================================================================
/// 计算标准吸收轮廓 (归一化到 1)。
///
/// # 参数
///
/// * `params` - 输入参数
///
/// # 返回值
///
/// 返回轮廓值
///
/// # 说明
///
/// 根据不同的 IP 值计算不同的轮廓:
/// - IP = 0: Doppler 轮廓
/// - IP = 1: Voigt 轮廓
/// - IP = 2: Stark (+ Doppler) 轮廓 (氢线近似)
/// - IP > 10: 用户提供的轮廓 (PROFSP)
///
/// V - 以 Doppler 宽度为单位的频率位移
pub fn profil(params: &ProfilParams) -> f64 {
// 频率位移 (Doppler 单位)
let v = (params.fr - params.fr0[params.itr - 1]) / params.dop;
// 取绝对值
let ipa = params.ip.abs();
if ipa == 0 {
// ================================================================
// Doppler 轮廓
// ================================================================
if v <= 13.0 {
(-v * v).exp() * PISQ1
} else {
0.0
}
} else if ipa == 1 {
// ================================================================
// Voigt 轮廓
// ================================================================
super::voigt(v, params.a) * PISQ1
} else if ipa == 2 {
// ================================================================
// Stark (+ Doppler) 轮廓 (氢线)
// ================================================================
// 获取电子密度
let ane = if params.id > 0 {
params.elec[params.id as usize - 1]
} else {
1e9 * params.grav
};
// 避免负电子密度
let ane = if ane <= 0.0 { 1e14 } else { ane };
// F000 = ane^(2/3)
let f000 = ane.powf(2.0 / 3.0);
// 获取下能级和上能级
let ilow_idx = params.ilow[params.itr - 1] as usize;
let iup_idx = params.iup[params.itr - 1] as usize;
// 主量子数
let ii = params.nquant[ilow_idx - 1] as usize;
let jj = params.nquant[iup_idx - 1] as usize;
// 离子电荷
let izz = params.iz[params.iel[ilow_idx - 1] as usize - 1] as usize;
// 乘法因子 (默认为 2)
let mut fac = TWO;
// Quasistatic 模式下的特殊处理
if params.iquasi > 0 && ii == 1 {
if jj == 2 {
fac = UN;
}
if jj == 3 && params.iquasi > 1 {
fac = UN;
}
}
// F00 = 1.25e-9 * F000 (氢) 或 3.906e-11 * F000 (氦)
let f00 = if izz == 2 {
fac = UN;
3.906e-11 * f000
} else {
1.25e-9 * f000
};
// 计算 Stark 参数
let (xkij, wl0, _fij) = super::stark0(ii, jj, izz);
let fxk = f00 * xkij;
// DBETA = wl0^2 / c / fxk
let dbeta = wl0 * wl0 / 2.997925e18 / fxk;
let betad = params.dop * dbeta;
// DIVSTR - 计算 Stark 分割点
let (adh, divh) = super::divstr(betad, izz as i32);
// BETA = DBETA * |FR - FR0|
let beta = dbeta * (params.fr - params.fr0[params.itr - 1]).abs();
// Stark 轮廓
super::starka(beta, fac, adh, betad, divh) * betad
} else if ipa > 10 {
// ================================================================
// 用户提供的轮廓 (PROFSP)
// 这里返回 0实际实现需要 PROFSP 函数
// TODO: 实现 PROFSP
0.0
} else {
0.0
}
}
// ============================================================================
// 测试
// ============================================================================
#[cfg(test)]
mod tests {
use super::*;
fn create_test_params() -> ProfilParams {
let mut params = ProfilParams::default();
params.fr = 4.57e14; // H-alpha 附近
params.a = 0.01;
params.dop = 1e10; // Doppler 宽度
params.itr = 1;
params.fr0[0] = 4.57e14; // 线心频率
params.grav = 1.0;
params.ilow[0] = 1;
params.iup[0] = 2;
params.nquant[0] = 2; // n=2 (下能级)
params.nquant[1] = 3; // n=3 (上能级)
params.iel[0] = 1;
params.iz[0] = 1; // 氢
params.elec[0] = 1e12;
params
}
#[test]
fn test_profil_doppler_at_center() {
let mut params = create_test_params();
params.ip = 0;
params.fr = params.fr0[0]; // 线心
let result = profil(&params);
// Doppler 轮廓在线心应为 1/sqrt(pi)
assert!((result - PISQ1).abs() < 1e-10);
}
#[test]
fn test_profil_doppler_wing() {
let mut params = create_test_params();
params.ip = 0;
params.fr = params.fr0[0] + 5.0 * params.dop; // 5 Doppler 宽度
let result = profil(&params);
// 5 Doppler 宽度处应该很小
let expected = (-25.0f64).exp() * PISQ1;
assert!((result - expected).abs() / expected < 1e-10);
}
#[test]
fn test_profil_doppler_far_wing() {
let mut params = create_test_params();
params.ip = 0;
params.fr = params.fr0[0] + 14.0 * params.dop; // > 13 Doppler 宽度
let result = profil(&params);
// 超过 13 Doppler 宽度应为 0
assert!((result - 0.0).abs() < 1e-30);
}
#[test]
fn test_profil_voigt() {
let mut params = create_test_params();
params.ip = 1;
params.fr = params.fr0[0]; // 线心
let result = profil(&params);
// Voigt 轮廓在线心应为 voigt(0, a) / sqrt(pi)
let expected = super::super::voigt(0.0, params.a) * PISQ1;
assert!((result - expected).abs() / expected < 1e-10);
}
#[test]
fn test_profil_stark() {
let mut params = create_test_params();
params.ip = 2;
params.id = 1;
params.fr = params.fr0[0] + 1e9; // 稍微偏离线心
let result = profil(&params);
// Stark 轮廓应该返回有限正值
assert!(result.is_finite());
assert!(result >= 0.0);
}
#[test]
fn test_profil_stark_negative_id() {
let mut params = create_test_params();
params.ip = 2;
params.id = 0; // 使用默认电子密度
params.grav = 1e4;
params.fr = params.fr0[0] + 1e9;
let result = profil(&params);
// 应该使用 grav 计算默认电子密度
assert!(result.is_finite());
}
#[test]
fn test_profil_stark_zero_elec() {
let mut params = create_test_params();
params.ip = 2;
params.id = 1;
params.elec[0] = 0.0; // 零电子密度
params.fr = params.fr0[0] + 1e9;
let result = profil(&params);
// 应该使用默认值 1e14
assert!(result.is_finite());
}
#[test]
fn test_profil_user_defined() {
let mut params = create_test_params();
params.ip = 12; // > 10用户定义
params.fr = params.fr0[0];
let result = profil(&params);
// 目前 PROFSP 未实现,返回 0
assert!((result - 0.0).abs() < 1e-30);
}
#[test]
fn test_profil_negative_ip() {
// IP 可以是负值 (取绝对值)
let mut params = create_test_params();
params.ip = -1; // 等同于 IP = 1
params.fr = params.fr0[0];
let result = profil(&params);
let expected = super::super::voigt(0.0, params.a) * PISQ1;
assert!((result - expected).abs() / expected < 1e-10);
}
#[test]
fn test_profil_quasi_mode() {
let mut params = create_test_params();
params.ip = 2;
params.iquasi = 1;
params.nquant[0] = 1; // ii = 1
params.nquant[1] = 2; // jj = 2
params.fr = params.fr0[0] + 1e9;
let result = profil(&params);
// Quasi 模式下 fac = 1
assert!(result.is_finite());
}
}

172
src/math/pzert.rs Normal file
View File

@ -0,0 +1,172 @@
//! 能级超级归零处理 - PZERT。
//!
//! 重构自 TLUSTY `pzert.f`
//!
//! 确定占据数在全层深度都很小的能级,并将其从线性化方程组中完全移除(超级归零)。
use crate::state::atomic::AtomicData;
use crate::state::config::TlustyConfig;
use crate::state::constants::{MDEPTH, MLEVEL};
use crate::state::model::ModelState;
/// 执行能级超级归零处理。
pub fn pzert(config: &mut TlustyConfig, atomic: &mut AtomicData, model: &mut ModelState) {
// 1. 如果 ioptab < 0直接返回
if config.basnum.ioptab < 0 {
return;
}
let natom = config.basnum.natom as usize;
let nd = config.basnum.nd as usize;
let nlevel = config.basnum.nlevel as usize;
let nlvexp = atomic.levpar.nlvexp as usize;
// 2. 局部变量模拟 DIMENSION POPMA(MLEVEL), INDLEZ(MLEVEL), GZR(MLEVEL)
let mut popma = [0.0; MLEVEL];
let mut indlez = [0; MLEVEL];
let mut gzr = [0.0; MLEVEL]; // 使用 0.0 表示 False (0), 1.0 表示 True (1)
// 3. 超级归零阈值
// POPZRL = 1.E5 * POPZER
let popzrl = 1.0e5 * model.popzr0.popzer;
let mut nlnzx = 0;
// 4. 对每个原子进行处理
for iat in 0..natom {
// n0a, nka 在 Fortran 中是 1-indexed这里转换为 0-indexed 处理
let n1 = (atomic.atopar.n0a[iat] - 1) as usize;
let nk = (atomic.atopar.nka[iat] - 1) as usize;
for ii in n1..=nk {
popma[ii] = 0.0;
}
// 寻找每个能级在所有深度点相对于该原子总占据数的最大比例
for id in 0..nd {
let mut popm = 0.0;
// 找到该深度点该原子的最大能级占据数
for ii in n1..=nk {
if model.levpop.popul[ii][id] > popm {
popm = model.levpop.popul[ii][id];
}
}
if popm > 0.0 {
for ii in n1..=nk {
let poprel = model.levpop.popul[ii][id] / popm;
if poprel > popma[ii] {
popma[ii] = poprel;
}
}
}
}
// 标记超级归零能级
for ii in n1..=nk {
atomic.levpar.ipzert[ii] = 0;
if popma[ii] < popzrl {
atomic.levpar.ipzert[ii] = 1;
for id in 0..nd {
model.popzr0.ipzero[ii][id] = 1;
model.levpop.popul[ii][id] = 0.0;
}
} else {
// 如果能级是显式线性化的 (iiexp > 0)
if atomic.levpar.iiexp[ii] > 0 {
nlnzx += 1;
indlez[nlnzx - 1] = ii + 1; // 保持 1-indexed 存储或按需调整
}
}
}
}
// 5. 检查能级组 (Groups)
// 如果一个组内的所有能级都被 super-zeroed则标记该组
for ii in 0..nlvexp {
gzr[ii] = 1.0;
}
for i in 0..nlevel {
let ii_exp = atomic.levpar.iiexp[i].abs() as usize;
if ii_exp != 0 {
// gzr(ii) = gzr(ii) * ipzert(i)
// 只要有一个 ipzert(i) 为 0gzr(ii) 就会变成 0
gzr[ii_exp - 1] *= atomic.levpar.ipzert[i] as f64;
}
}
let mut nlvexz = 0;
for ii in 0..nlvexp {
if gzr[ii] == 0.0 {
atomic.levpar.igzert[ii] = 0;
nlvexz += 1;
atomic.levpar.indlgz[nlvexz - 1] = (ii + 1) as i32;
atomic.levpar.iinonz[ii] = nlvexz as i32;
} else {
atomic.levpar.igzert[ii] = 1;
atomic.levpar.iinonz[ii] = 0;
}
}
// 更新线性化系统的有效维度 NN
// NN = NN0 - NLVEXP + NLVEXZ
config.matkey.nn = config.matkey.nn0 - atomic.levpar.nlvexp + nlvexz as i32;
atomic.levpar.nlvexz = nlvexz as i32;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::state::constants::MATOM;
#[test]
fn test_pzert_basic() {
let mut config = TlustyConfig::default();
let mut atomic = AtomicData::default();
let mut model = ModelState::default();
config.basnum.ioptab = 1;
config.basnum.natom = 1;
config.basnum.nd = 2;
config.basnum.nlevel = 2;
atomic.levpar.nlvexp = 2;
config.matkey.nn0 = 10;
atomic.atopar.n0a[0] = 1;
atomic.atopar.nka[0] = 2;
// 设置能级 1 为高占据数,能级 2 为低占据数
model.levpop.popul[0][0] = 1.0;
model.levpop.popul[0][1] = 1.0;
model.levpop.popul[1][0] = 1e-40; // 远小于 1e5 * 1e-30 (1e-25)
model.levpop.popul[1][1] = 1e-40;
model.popzr0.popzer = 1e-30;
// 设置显式映射
atomic.levpar.iiexp[0] = 1;
atomic.levpar.iiexp[1] = 2;
pzert(&mut config, &mut atomic, &mut model);
assert_eq!(atomic.levpar.ipzert[0], 0);
assert_eq!(atomic.levpar.ipzert[1], 1);
assert_eq!(model.popzr0.ipzero[1][0], 1);
assert_eq!(model.levpop.popul[1][0], 0.0);
// 检查维度更新
// nlvexz 应该为 1 (因为组 1 没被归零 [ipzert=0],组 2 被归零了 [ipzert=1])
// 这里的逻辑需要细看 GZR
// 组 1: GZR[0] = 1.0 * ipzert[0] = 0.0 -> igzert=0
// 组 2: GZR[1] = 1.0 * ipzert[1] = 1.0 -> igzert=1
// 注意Fortran Code Line 79: ELSE -> IGZERT(II)=1 (这是组归零的情况)
// Line 73: IF(GZR(II).EQ.0) THEN -> IGZERT(II)=0 (非整组归零)
// 修正逻辑GZR(II)=1 表示该组所有能级都被 super-zeroed。
assert_eq!(atomic.levpar.igzert[0], 0); // 组 1 正常
assert_eq!(atomic.levpar.igzert[1], 1); // 组 2 被归零了
assert_eq!(atomic.levpar.nlvexz, 1);
assert_eq!(config.matkey.nn, 10 - 2 + 1);
}
}

246
src/math/pzevld.rs Normal file
View File

@ -0,0 +1,246 @@
//! 压力与几何距离评价 - PZEVLD。
//!
//! 重构自 TLUSTY `pzevld.f`
//!
//! 确定总压、气体压以及压力对数梯度;计算从中心平面开始的几何距离 Z。
use crate::math::dmder::DepthDeriv;
use crate::state::arrays::ComputeArrays;
use crate::state::atomic::AtomicData;
use crate::state::config::TlustyConfig;
use crate::state::constants::{BOLK, MDEPTH, HALF, UN};
use crate::state::model::ModelState;
/// 执行压力与几何距离评价。
pub fn pzevld(
config: &mut TlustyConfig,
_atomic: &mut AtomicData,
model: &mut ModelState,
arrays: &mut ComputeArrays,
deriv: &DepthDeriv,
) {
let nd = config.basnum.nd as usize;
let nfreqe = config.basnum.nfreqe as usize;
let ifryb = config.basnum.ifryb;
let ihecor = model.heqaux.ihecor;
let qgrav = config.inppar.qgrav;
let znd = config.centrl.znd;
if ifryb > 0 && model.ifpzpa.ifpzev == 0 {
return;
}
let mut iheitr = 0;
let mut zold = [0.0; MDEPTH];
let mut dpp = [0.0; MDEPTH];
let mut zd1 = [0.0; MDEPTH];
let mut zd2 = [0.0; MDEPTH];
let mut zd3 = [0.0; MDEPTH];
loop {
iheitr += 1;
// 1. 计算几何距离 Z (从中心平面开始)
if ihecor >= 0 {
model.modpar.zd[nd - 1] = znd;
for iid in 1..nd {
let id = nd - 1 - iid;
if iheitr == 1 {
model.modpar.zd[id] = model.modpar.zd[id + 1]
+ HALF
* (model.modpar.dm[id + 1] - model.modpar.dm[id])
* (UN / model.modpar.dens[id + 1] + UN / model.modpar.dens[id]);
zold[id] = model.modpar.zd[id];
}
}
} else {
zd1[0] = -deriv.ddc[0] / deriv.ddb[0];
zd2[0] = -model.modpar.dens1[0] / deriv.ddb[0];
for id in 1..nd - 1 {
let x = UN / (deriv.ddb[id] - deriv.dda[id] * zd1[id - 1]);
zd1[id] = -x * deriv.ddc[id];
zd2[id] = -x * (model.modpar.dens1[id] - deriv.dda[id] * zd2[id - 1]);
}
model.modpar.zd[nd - 1] = znd;
for id in (0..nd - 1).rev() {
model.modpar.zd[id] = zd1[id] * model.modpar.zd[id + 1] + zd2[id];
}
}
// 2. 总压、气体压和声速
for id in 0..nd {
let pturb = HALF * model.modpar.dens[id] * model.turbul.vturb[id] * model.turbul.vturb[id];
let pgsc = (model.modpar.dens[id] / config.inppar.wmm[id] + model.modpar.elec[id])
* BOLK
* model.modpar.temp[id];
model.pressr.pgs[id] = pgsc;
let ptotl0 = model.pressr.pgs[id] + model.pressr.pradt[id] + pturb;
model.pressr.ptotal[id] = ptotl0;
model.prsaux.vsnd2[id] = model.pressr.ptotal[id] / model.modpar.dens[id];
}
// 边界参考
model.prsaux.hg1 = (2.0 * model.pressr.pgs[0] / model.modpar.dens[0] / qgrav).sqrt();
model.prsaux.hr1 = model.heqaux.prd0 / qgrav;
model.prsaux.rr1 = model.prsaux.hr1 / model.prsaux.hg1;
// 3. 重新计算 Z 距离 (辐射驱动力影响)
let mut ij1 = 0;
if config.compti.icompt > 0 && config.compti.icombc > 0 && model.freaux.ijex[0] > 0 {
ij1 = 1;
}
for id in 0..nd {
let mut grp = 0.0;
if nfreqe > 0 || ifryb == 0 {
for ij in ij1..nfreqe {
let rad0 = model.expraf.radex[ij][id];
let abso0 = arrays.exprad.absoex[ij][id];
let ijt = (model.freaux.ijfr[ij] - 1) as usize;
let wd0 = model.frqall.w[ijt];
let fluxw = model.surfac.fh[ijt] * rad0 - model.totrad.hextrd[ijt];
if model.frqall.lskip[id][ijt] == 0 {
if id == 0 {
grp += wd0 * fluxw * abso0;
} else {
let radm = model.expraf.radex[ij][id - 1];
let fkm = model.expraf.fakex[ij][id - 1];
let fk0 = model.expraf.fakex[ij][id];
let frd = fk0 * rad0 - fkm * radm;
grp += wd0 * frd;
}
}
}
model.grdpra.grd[id] = grp + model.totflx.fprd[id];
}
if id > 0 {
let ddm = model.modpar.dm[id] - model.modpar.dm[id - 1];
dpp[id] = (model.pressr.pgs[id] - model.pressr.pgs[id - 1]) / ddm
+ model.grdpra.grd[id] / ddm * 4.19168946e-10;
dpp[id] /= qgrav;
}
}
// 4. 判断是否进入更多迭代
if config.runkey.iter <= config.centrl.ifz0.abs() {
zd1[0] = dpp[1];
zd1[nd - 1] = znd;
for id in 1..nd - 1 {
zd1[id] = (dpp[id] + dpp[id + 1]) * HALF;
}
zd2[nd - 1] = znd;
zd3[nd - 1] = znd;
let mut izdiv = 0;
let mut nzdiv = 0;
for id in (0..nd - 1).rev() {
zd2[id] = 2.0 * dpp[id + 1] - zd2[id + 1];
zd3[id] = dpp[id] * deriv.ddmin[id] + dpp[id + 1] * deriv.ddplu[id];
if zd2[id] <= zd2[id + 1] {
nzdiv += 1;
}
if nzdiv == 1 {
izdiv = id;
}
}
if ihecor >= 0 {
for id in 0..nd {
model.modpar.zd[id] = zd2[id];
if id <= izdiv {
model.modpar.zd[id] = zd1[id];
}
}
} else {
for id in 0..nd {
model.modpar.zd[id] = zd3[id];
}
}
// 重新计算密度 (如果需要)
if ihecor > 0 {
for id in (0..nd - 1).rev() {
let ddm_curr = model.modpar.dm[id + 1] - model.modpar.dm[id];
let x = HALF * ddm_curr;
let xne = model.modpar.elec[id] / model.modpar.dens[id];
model.modpar.dens[id] = x * model.modpar.dens[id + 1]
/ ((model.modpar.zd[id] - model.modpar.zd[id + 1]) * model.modpar.dens[id + 1] - x);
model.modpar.dens1[id] = UN / model.modpar.dens[id];
model.modpar.elec[id] = xne * model.modpar.dens[id];
}
} else if ihecor < -1 {
for id in (0..nd - 1).rev() {
let xne = model.modpar.elec[id] / model.modpar.dens[id];
if id > 0 {
model.modpar.dens1[id] = model.modpar.zd[id - 1] * deriv.dda[id]
- model.modpar.zd[id] * deriv.ddb[id]
- model.modpar.zd[id + 1] * deriv.ddc[id];
} else {
model.modpar.dens1[id] =
-model.modpar.zd[id] * deriv.ddb[id] - model.modpar.zd[id + 1] * deriv.ddc[id];
}
model.modpar.dens[id] = UN / model.modpar.dens1[id];
model.modpar.elec[id] = xne * model.modpar.dens[id];
}
}
let mut dzmx = 0.0;
for id in 0..nd - 1 {
let diff = ((model.modpar.zd[id] - zold[id]) / model.modpar.zd[id]).abs();
if diff > dzmx {
dzmx = diff;
}
zold[id] = model.modpar.zd[id];
}
if iheitr >= 5 || dzmx < 1.0e-3 {
break;
}
} else {
break;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::math::dmder::dmder;
#[test]
fn test_pzevld_basic() {
let mut config = TlustyConfig::default();
let mut atomic = AtomicData::default();
let mut model = ModelState::default();
let mut arrays = ComputeArrays::default();
config.basnum.nd = 10;
config.inppar.qgrav = 1e4;
config.centrl.znd = 1e8;
model.heqaux.ihecor = 0;
// 设置 DM 为均匀网格
for id in 0..10 {
model.modpar.dm[id] = (id + 1) as f64 * 1.0;
model.modpar.dens[id] = 1.0;
model.modpar.temp[id] = 10000.0;
}
let deriv = dmder(&model.modpar.dm, 10);
// 初始运行配置
config.runkey.iter = 10;
config.centrl.ifz0 = 0;
pzevld(&mut config, &mut atomic, &mut model, &mut arrays, &deriv);
// 验证基本压力组件
assert!(model.pressr.pgs[0] > 0.0);
assert!(model.pressr.ptotal[0] >= model.pressr.pgs[0]);
// 验证 ZD 积分结果 (简单情况)
assert!(model.modpar.zd[0] > model.modpar.zd[9]);
}
}

422
src/math/reflev.rs Normal file
View File

@ -0,0 +1,422 @@
//! 参考能级确定与 LTE 参考量计算 - REFLEV。
//!
//! 重构自 TLUSTY `reflev.f`
//!
//! 1. 确定原子的全局参考能级 (IREF)。
//! 2. 确定 LTE 参考能级 (ILTREF) 及其相关物理量 (SBPSI, DSBPST, DSBPSN)。
//! 3. 处理能级清零逻辑 (IPZERO)。
use crate::state::atomic::AtomicData;
use crate::state::config::TlustyConfig;
use crate::state::constants::UN;
use crate::state::iterat::IterControl;
use crate::state::model::ModelState;
/// 执行参考能级确定与相关物理量计算。
pub fn reflev(
id: usize,
imode: i32,
config: &mut TlustyConfig,
atomic: &mut AtomicData,
model: &mut ModelState,
iterat: &IterControl,
) {
if config.basnum.ioptab < 0 {
return;
}
let natom = config.basnum.natom as usize;
let nitzer = config.runkey.nitzer as usize;
let iter = config.runkey.iter as usize;
let lte = config.inppar.lte;
let popzr2 = model.popzr0.popzr2;
let elec_id = model.modpar.elec[id];
let elec1_id = 1.0 / elec_id;
// 1. 确定参考能级 (IREF)
if atomic.atopar.modref >= 1 {
if imode == 1 && (iter <= 1 || iterat.accel.kant[iter] == 0) {
for iat in 0..natom {
let mut pmax = 0.0;
let mut iref = 0;
let n0 = atomic.atopar.n0a[iat] as usize;
let nk = atomic.atopar.nka[iat] as usize;
for i in n0..=nk {
if model.levpop.popul[i][id] >= pmax {
pmax = model.levpop.popul[i][id];
iref = i;
}
}
// 约束规则
let iel_iref = atomic.levpar.iel[iref] as usize;
let nfirst_iel = atomic.ionpar.nfirst[iel_iref] as usize;
if iref != nk && iref != nfirst_iel {
iref = nfirst_iel;
}
if atomic.atopar.modref == 2 {
let nk_iat = atomic.atopar.nka[iat] as usize;
let iel_nk = atomic.levpar.iel[nk_iat] as usize;
let nfirst_nk = atomic.ionpar.nfirst[iel_nk] as usize;
if iref < nfirst_nk {
iref = nfirst_nk;
}
}
atomic.atopar.nref[iat] = iref as i32;
atomic.atopar.nrefs[iat][id] = iref as i32;
}
} else {
for iat in 0..natom {
atomic.atopar.nref[iat] = atomic.atopar.nrefs[iat][id];
}
}
} else {
for iat in 0..natom {
atomic.atopar.nrefs[iat][id] = atomic.atopar.nref[iat];
}
}
// 2. 占据数清零 (IPZERO)
if iter <= nitzer {
let mut xsbf = vec![0.0; atomic.levpar.enion.len()];
for iat in 0..natom {
let n1 = atomic.atopar.n0a[iat] as usize;
let nk = atomic.atopar.nka[iat] as usize;
let mut iref = atomic.atopar.nref[iat] as usize;
for ii in n1..=nk {
model.popzr0.ipzero[ii][id] = 0;
xsbf[ii] = model.levpop.sbf[ii] * elec_id;
if !lte {
let iel_ii = atomic.levpar.iel[ii] as usize;
let nnext_ii = atomic.ionpar.nnext[iel_ii] as usize;
let itr = atomic.trapar.itra[ii][nnext_ii];
if itr > 0 {
let itr_idx = (itr - 1) as usize;
if model.rrrates.rru[itr_idx][id] > 1.0e-30 {
xsbf[ii] = model.levpop.sbf[ii]
* elec_id
* model.rrrates.rrd[itr_idx][id]
/ model.rrrates.rru[itr_idx][id];
}
}
}
}
if lte {
iref = n1;
for ii in (n1 + 1)..=nk {
if atomic.levpar.ilk[ii] > 0 {
iref = ii;
if xsbf[ii] > 1.0 {
break;
}
}
}
atomic.atopar.nrefs[iat][id] = iref as i32;
atomic.atopar.nref[iat] = iref as i32;
}
// 向前清零
if iref > n1 {
let mut x = 1.0;
for ii in (n1..iref).rev() {
if atomic.levpar.ilk[ii] > 0 || ii == n1 {
x *= xsbf[ii];
if x < popzr2 {
let iel_ii = atomic.levpar.iel[ii] as usize;
let nlast_ii = atomic.ionpar.nlast[iel_ii] as usize;
for iii in n1..=nlast_ii {
model.popzr0.ipzero[iii][id] = 1;
}
break;
}
}
}
}
// 向后清零
if iref < nk {
let mut x = 1.0;
for ii in (iref + 1)..=nk {
if atomic.levpar.ilk[ii] > 0 {
let ilk_ii = atomic.levpar.ilk[ii] as usize;
let nfirst_ilk = atomic.ionpar.nfirst[ilk_ii] as usize;
x *= xsbf[nfirst_ilk];
if x > 1.0 / popzr2 {
let nfirst_iel = atomic.ionpar.nfirst[atomic.levpar.iel[ii] as usize] as usize;
let nfir = if ii == nk { nk } else { nfirst_iel };
for iii in nfir..=nk {
model.popzr0.ipzero[iii][id] = 1;
}
break;
}
}
}
}
}
}
// 3. 计算 LTE 参考能级及其对应物理量
model.modpar.elec1[id] = elec1_id;
for iat in 0..natom {
let mut ifsup = 0;
let n0 = atomic.atopar.n0a[iat] as usize;
let nk = atomic.atopar.nka[iat] as usize;
let iref = atomic.atopar.nref[iat] as usize;
for i in n0..=nk {
let imodl = atomic.levpar.imodl[i].abs();
if imodl == 1 || imodl == 2 {
let iel_i = atomic.levpar.iel[i] as usize;
let in_idx = atomic.ionpar.nnext[iel_i] as usize;
if i < iref || model.levpop.popul[i][id] < model.levpop.popul[in_idx][id] {
model.levref.iltref[i][id] = in_idx as i32;
model.levref.sbpsi[i][id] = model.levpop.sbf[i]
* elec_id
* model.wmcomp.wop[i][id]
* model.levpop.bfac[i][id];
model.levref.dsbpst[i][id] = model.levpop.dsbf[i];
model.levref.dsbpsn[i][id] = elec1_id;
} else if i > iref {
let i1 = atomic.ionpar.nfirst[iel_i] as usize;
model.levref.iltref[i][id] = i1 as i32;
let denom = model.levpop.sbf[i1]
* model.wmcomp.wop[i1][id]
* model.levpop.bfac[i1][id];
model.levref.sbpsi[i][id] = (model.levpop.sbf[i]
* model.wmcomp.wop[i][id]
* model.levpop.bfac[i][id])
/ denom;
model.levref.dsbpst[i][id] = model.levpop.dsbf[i] - model.levpop.dsbf[i1];
model.levref.dsbpsn[i][id] = 0.0;
}
} else if imodl == 3 || imodl == 0 {
let iel_i = atomic.levpar.iel[i] as usize;
let in_idx = atomic.ionpar.nnext[iel_i] as usize;
model.levref.iltref[i][id] = in_idx as i32;
model.levref.sbpsi[i][id] = model.levpop.sbf[i]
* elec_id
* model.wmcomp.wop[i][id]
* model.levpop.bfac[i][id];
model.levref.dsbpst[i][id] = model.levpop.dsbf[i];
model.levref.dsbpsn[i][id] = elec1_id;
} else if imodl == 5 {
ifsup = 1;
}
}
// 超级参考能级模式
let lrf = lte && iref != nk;
if ifsup == 1 || lrf {
let mut xa = UN;
let mut xt = 0.0;
let mut xn = 0.0;
model.levref.iltref[iref][id] = iref as i32;
if iref > n0 {
for i in (n0..iref).rev() {
model.levref.iltref[i][id] = iref as i32;
model.levref.sbpsi[i][id] =
xa * model.levpop.sbf[i] * elec_id * model.levpop.bfac[i][id] * model.wmcomp.wop[i][id];
if model.levref.sbpsi[i][id] < popzr2 {
model.levref.sbpsi[i][id] = 0.0;
} else {
model.levref.dsbpst[i][id] = xt + model.levpop.dsbf[i];
model.levref.dsbpsn[i][id] = xn + elec1_id;
}
if i == atomic.ionpar.nfirst[atomic.levpar.iel[i] as usize] as usize {
xa = model.levref.sbpsi[i][id];
xt = model.levref.dsbpst[i][id];
xn = model.levref.dsbpsn[i][id];
if xn == 0.0 {
atomic.levpar.ilk[i] = 0;
}
}
}
}
xa = UN;
xt = 0.0;
xn = 0.0;
if iref < nk {
if iref == atomic.ionpar.nlast[atomic.levpar.iel[iref] as usize] as usize {
xa = UN
/ (model.levpop.sbf[iref]
* model.levpop.bfac[iref][id]
* model.wmcomp.wop[iref][id]
* elec_id);
xt = -model.levpop.dsbf[iref];
xn = -elec1_id;
}
for i in (iref + 1)..nk {
model.levref.iltref[i][id] = iref as i32;
let i1 = atomic.ionpar.nfirst[atomic.levpar.iel[i] as usize] as usize;
let sbb1 = UN
/ (model.levpop.sbf[i1]
* model.levpop.bfac[i1][id]
* model.wmcomp.wop[i1][id]);
model.levref.sbpsi[i][id] =
xa * model.levpop.sbf[i] * model.levpop.bfac[i][id] * model.wmcomp.wop[i][id] * sbb1;
if model.levref.sbpsi[i][id] < popzr2 {
model.levref.sbpsi[i][id] = 0.0;
} else {
model.levref.dsbpst[i][id] = xt + model.levpop.dsbf[i] - model.levpop.dsbf[i1];
model.levref.dsbpsn[i][id] = xn;
}
if i == atomic.ionpar.nlast[atomic.levpar.iel[i] as usize] as usize {
xa = xa * sbb1 * elec1_id;
xt -= model.levpop.dsbf[i1];
xn -= elec1_id;
}
}
let i_nk = nk;
model.levref.iltref[i_nk][id] = iref as i32;
model.levref.sbpsi[i_nk][id] = xa;
model.levref.dsbpst[i_nk][id] = xt;
model.levref.dsbpsn[i_nk][id] = xn;
}
}
// 处理 IMODL=2 的特殊含义
for i in n0..=nk {
if atomic.levpar.imodl[i].abs() == 2 {
let ilt = model.levref.iltref[i][id] as usize;
if model.levpop.popul[i][id] == 0.0 || model.levpop.popul[ilt][id] == 0.0 {
let iel_i = atomic.levpar.iel[i] as usize;
let in_idx = atomic.ionpar.nnext[iel_i] as usize;
if i < iref || model.levpop.popul[i][id] < model.levpop.popul[in_idx][id] {
model.levref.iltref[i][id] = in_idx as i32;
model.levref.sbpsi[i][id] = model.levpop.sbf[i]
* elec_id
* model.wmcomp.wop[i][id]
* model.levpop.bfac[i][id];
} else if i > iref {
let i1 = atomic.ionpar.nfirst[iel_i] as usize;
model.levref.iltref[i][id] = i1 as i32;
model.levref.sbpsi[i][id] = (model.levpop.sbf[i]
* model.wmcomp.wop[i][id]
* model.levpop.bfac[i][id])
/ (model.levpop.sbf[i1]
* model.wmcomp.wop[i1][id]
* model.levpop.bfac[i1][id]);
}
} else {
model.levref.sbpsi[i][id] =
model.levpop.popul[i][id] / model.levpop.popul[ilt][id];
}
model.levref.dsbpst[i][id] = 0.0;
model.levref.dsbpsn[i][id] = 0.0;
}
}
// 处理 IMODL=6 的特殊含义
if imode == 1 {
for i in n0..=nk {
if atomic.levpar.imodl[i].abs() == 6 {
let ilt = model.levref.iguide[i] as usize;
model.levref.iltref[i][id] = ilt as i32;
if model.levpop.popul[ilt][id] > 0.0 {
model.levref.sbpsi[i][id] =
model.levpop.popul[i][id] / model.levpop.popul[ilt][id];
}
model.levref.dsbpst[i][id] = 0.0;
model.levref.dsbpsn[i][id] = 0.0;
}
}
}
}
// 4. 真实 LTE 参考能级
for iat in 0..natom {
let n0 = atomic.atopar.n0a[iat] as usize;
let nk = atomic.atopar.nka[iat] as usize;
for i in n0..=nk {
if atomic.levpar.imodl[i].abs() < 6 {
model.levref.ilterf[i][id] = model.levref.iltref[i][id];
model.levref.sblpsi[i][id] = model.levref.sbpsi[i][id];
} else {
let iel_i = atomic.levpar.iel[i] as usize;
let in_idx = atomic.ionpar.nnext[iel_i] as usize;
model.levref.ilterf[i][id] = in_idx as i32;
model.levref.sblpsi[i][id] = model.levpop.sbf[i]
* elec_id
* model.wmcomp.wop[i][id]
* model.levpop.bfac[i][id];
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::state::constants::MDEPTH;
#[test]
fn test_reflev_basic() {
let mut config = TlustyConfig::default();
let mut atomic = AtomicData::default();
let mut model = ModelState::default();
let iterat = IterControl::default();
config.basnum.natom = 1;
config.basnum.nd = 10;
config.runkey.iter = 1;
atomic.atopar.n0a[0] = 0;
atomic.atopar.nka[0] = 1;
atomic.atopar.modref = 1;
atomic.levpar.iel[0] = 0;
atomic.levpar.iel[1] = 0;
atomic.ionpar.nfirst[0] = 0;
atomic.ionpar.nlast[0] = 1;
atomic.ionpar.nnext[0] = 2; // dummy
// 设置占空比,使能级 1 占主导
model.levpop.popul[0][0] = 1.0;
model.levpop.popul[1][0] = 10.0;
reflev(0, 1, &mut config, &mut atomic, &mut model, &iterat);
// 验证参考能级被选为 1 (虽然有约束规则可能将其强制设为 nfirst)
// 在目前的逻辑中: iref=1. iref != nk (1) is false. So iref stays 1?
// Wait, 1 is nk. So iref != nk is false.
assert_eq!(atomic.atopar.nref[0], 1);
}
#[test]
fn test_reflev_zeroing() {
let mut config = TlustyConfig::default();
let mut atomic = AtomicData::default();
let mut model = ModelState::default();
let iterat = IterControl::default();
config.basnum.natom = 1;
config.runkey.nitzer = 5;
config.runkey.iter = 1;
atomic.atopar.n0a[0] = 0;
atomic.atopar.nka[0] = 2;
atomic.atopar.nref[0] = 2; // 参考能级设为 2
atomic.levpar.iel[0] = 0;
atomic.levpar.iel[1] = 0;
atomic.levpar.iel[2] = 0;
atomic.ionpar.nfirst[0] = 0;
atomic.ionpar.nlast[0] = 2;
atomic.levpar.ilk[0] = 1;
atomic.levpar.ilk[1] = 1;
model.popzr0.popzr2 = 0.1;
model.modpar.elec[0] = 1.0;
model.levpop.sbf[0] = 0.05; // 触发向前清零 (x = 1.0 * 0.05 = 0.05 < 0.1)
model.levpop.sbf[1] = 1.0;
reflev(0, 1, &mut config, &mut atomic, &mut model, &iterat);
// 验证能级 0 被标记为清零 (因为 x 从 1.0 开始,在 ii=1 时 x=1.0*1.0=1.0, 在 ii=0 时 x=1.0*0.05=0.05 < 0.1)
assert_eq!(model.popzr0.ipzero[0][0], 1);
}
}

279
src/math/rtecf0.rs Normal file
View File

@ -0,0 +1,279 @@
//! 设置包含康普顿散射的辐射转移方程矩阵元素 - RTECF0。
//!
//! 重构自 TLUSTY `rtecf0.f`
//!
//! 该子程序为特定频率点 IJ 计算矩阵 A, B, C, E, U, V 和 alpha, beta, gamma。
use crate::state::atomic::AtomicData;
use crate::state::config::TlustyConfig;
use crate::state::constants::{HALF, HK, SIGE, TWO, UN, XCON, YCON};
use crate::state::iterat::IterControl;
use crate::state::model::ModelState;
/// 为频率点 ij 计算 RTE 矩阵元素。
pub fn rtecf0(
ij: usize, // 0-based
config: &TlustyConfig,
_atomic: &AtomicData,
model: &mut ModelState,
_iterat: &IterControl,
) {
let nd = config.basnum.nd as usize;
let nfreq = config.basnum.nfreq as usize;
let iji_1 = (nfreq as i32 - model.frqall.kij[ij] + 1) as usize; // 1-based index in frequency-ordered scale
// Note: ij is 0-based. kij[ij] is 1-based in Fortran.
let fr = model.frqall.freq[ij];
let xcomp = fr * XCON;
// 1. 光学深度尺度 (Optical depth scale)
for id in 0..(nd - 1) {
model.optdpt.dt[id] = model.modpar.deldmz[id] * (model.curopa.absot[id + 1] + model.curopa.absot[id]);
}
// 2. 深度离散化矩阵 (Depth discretization matrices)
// 上边界 (Upper boundary, id = 1)
let id_0 = 0;
let dtp1 = model.optdpt.dt[id_0];
let bb0 = UN / dtp1;
let bb1 = TWO * bb0 * bb0;
model.auxrte.be[id_0] = bb0 * TWO * model.surfac.fh[ij] + bb1 * model.totrad.fak[ij][id_0];
model.auxrte.ga[id_0] = bb1 * model.totrad.fak[ij][id_0 + 1];
let sext = TWO * bb0 * model.totrad.hextrd[ij];
// 标准深度点 (Normal depth points, id = 2..nd-1)
let mut dtp1_current = dtp1;
for id in 1..(nd - 1) {
let dtm1 = dtp1_current;
dtp1_current = model.optdpt.dt[id];
let dt0 = TWO / (dtm1 + dtp1_current);
model.auxrte.al[id] = model.totrad.fak[ij][id - 1] / dtm1 * dt0;
model.auxrte.ga[id] = model.totrad.fak[ij][id + 1] / dtp1_current * dt0;
model.auxrte.be[id] = model.totrad.fak[ij][id] * dt0 * (UN / dtm1 + UN / dtp1_current);
}
// 下边界 (Lower boundary, id = nd)
let id_nd = nd - 1;
if config.basnum.idisk == 0 || config.centrl.ifz0 < 0 {
if config.basnum.ibc == 0 {
model.auxrte.be[id_nd] = model.totrad.fak[ij][id_nd] / dtp1_current + HALF;
model.auxrte.al[id_nd] = model.totrad.fak[ij][id_nd - 1] / dtp1_current;
} else if config.basnum.ibc < 4 {
let b = UN / dtp1_current;
let a = TWO * b * b;
model.auxrte.be[id_nd] = b * TWO * model.totrad.fhd[ij] + a * model.totrad.fak[ij][id_nd];
model.auxrte.al[id_nd] = a * model.totrad.fak[ij][id_nd - 1];
} else {
let b = UN / dtp1_current;
let a = TWO * b * b;
model.auxrte.be[id_nd] = b + a * model.totrad.fak[ij][id_nd];
model.auxrte.al[id_nd] = a * model.totrad.fak[ij][id_nd - 1];
}
} else {
// 吸计盘 - 对称边界
let bb1 = TWO * (UN / dtp1_current) * (UN / dtp1_current);
model.auxrte.be[id_nd] = bb1 * model.totrad.fak[ij][id_nd];
model.auxrte.al[id_nd] = bb1 * model.totrad.fak[ij][id_nd - 1];
}
// 3. 散射矩阵 (Scattering matrices)
for id in 0..nd {
let scat0 = model.modpar.elec[id] * SIGE;
let sa0 = model.curopa.emis1[id] / model.curopa.abso1[id];
let ss0 = scat0 / model.curopa.abso1[id];
let epsnu = (model.curopa.abso1[id] - model.curopa.scat1[id]) / model.curopa.abso1[id];
let mut x0 = ss0;
let temp_id = model.modpar.temp[id];
let e2 = YCON * temp_id + 0.7 * xcomp * xcomp;
let e1 = xcomp - 3.0 * e2 - 0.7 * xcomp * xcomp;
let e0 = 1.0 - xcomp - 4.2 * xcomp * xcomp;
model.auxrte.coma[id] = 0.0;
model.auxrte.comc[id] = 0.0;
model.auxrte.u[id] = 0.0;
model.auxrte.v[id] = 0.0;
model.auxrte.vl[id] = sa0;
if id == 0 && model.windbl.iwinbl < 0 {
model.auxrte.vl[id] = sa0 + sext;
}
model.auxrte.bs[id] = 0.0;
if iji_1 == 1 {
model.auxrte.comc[id] = 0.0;
model.auxrte.comb[id] = x0 * (UN - TWO * xcomp);
} else if iji_1 < nfreq {
let del0 = TWO / (model.comptf.dlnfr[iji_1 - 1] + model.comptf.dlnfr[iji_1 - 2]);
let cder1p = (UN - model.comptf.delj[iji_1 - 1][id]) * del0;
let cder1m = -model.comptf.delj[iji_1 - 2][id] * del0;
let cder10 = -cder1m - cder1p;
if config.compti.ichcoo == 0 {
model.auxrte.coma[id] = x0 * (e1 * cder1m + e2 * model.comptf.cder2m[iji_1 - 1]);
model.auxrte.comb[id] = x0 * (e0 + e1 * cder10 + e2 * model.comptf.cder20[iji_1 - 1]);
model.auxrte.comc[id] = x0 * (e1 * cder1p + e2 * model.comptf.cder2p[iji_1 - 1]);
x0 = ss0 * model.comptf.bnus[iji_1 - 1];
if config.compti.icomst == 0 {
x0 = 0.0;
}
model.auxrte.come[id] = x0 * (cder10 - UN);
model.auxrte.u[id] = x0 * cder1m;
model.auxrte.v[id] = x0 * cder1p;
let ijo = config.comptn.ijorig[iji_1 - 1] as usize - 1;
let ijom1 = config.comptn.ijorig[iji_1 - 2] as usize - 1;
let ijop1 = config.comptn.ijorig[iji_1] as usize - 1;
model.auxrte.bs[id] = model.auxrte.come[id] * model.totrad.rad[ijo][id]
+ model.auxrte.u[id] * model.totrad.rad[ijom1][id]
+ model.auxrte.v[id] * model.totrad.rad[ijop1][id];
} else {
let ijo = config.comptn.ijorig[iji_1 - 1] as usize - 1;
let ijom1 = config.comptn.ijorig[iji_1 - 2] as usize - 1;
let ijop1 = config.comptn.ijorig[iji_1] as usize - 1;
let frp = model.frqall.freq[ijop1];
let frm = model.frqall.freq[ijom1];
let zxxp = XCON * frp + 0.5 * model.comptf.bnus[iji_1] * model.totrad.rad[ijop1][id] - 3.0 * e2;
let zxx0 = xcomp + 0.5 * model.comptf.bnus[iji_1 - 1] * model.totrad.rad[ijo][id] - 3.0 * e2;
let zxxm = XCON * frm + 0.5 * model.comptf.bnus[iji_1 - 2] * model.totrad.rad[ijom1][id] - 3.0 * e2;
let zxxp12 = ((UN - model.comptf.delj[iji_1 - 1][id]) * zxxp + model.comptf.delj[iji_1 - 1][id] * zxx0) * del0;
let zxxm12 = ((UN - model.comptf.delj[iji_1 - 2][id]) * zxx0 + model.comptf.delj[iji_1 - 2][id] * zxxm) * del0;
model.auxrte.coma[id] = x0 * (-model.comptf.delj[iji_1 - 2][id] * zxxm12 + e2 * model.comptf.cder2m[iji_1 - 1]);
model.auxrte.comc[id] = x0 * ((UN - model.comptf.delj[iji_1 - 1][id]) * zxxp12 + e2 * model.comptf.cder2p[iji_1 - 1]);
model.auxrte.comb[id] = x0 * (model.comptf.delj[iji_1 - 1][id] * zxxp12 - (UN - model.comptf.delj[iji_1 - 2][id]) * zxxm12 + e2 * model.comptf.cder20[iji_1 - 1]) - epsnu + 1.0;
}
} else {
// iji_1 == nfreq
let ijo = config.comptn.ijorig[iji_1 - 1] as usize - 1;
let ijom1 = config.comptn.ijorig[iji_1 - 2] as usize - 1;
let dlt = model.comptf.delj[iji_1 - 2][id];
let zj1 = (-HK * model.frqall.freq[ijo] / temp_id).exp();
let zj2 = if ijo + 1 < nfreq {
(-HK * model.frqall.freq[ijo + 1] / temp_id).exp()
} else {
zj1
};
if config.compti.ichcoo == 0 {
let fr_next = if ijo + 1 < nfreq {
model.frqall.freq[ijo + 1]
} else {
model.frqall.freq[ijo]
};
let zj0 = UN / (HK * (model.frqall.freq[ijo] * fr_next).sqrt() / temp_id);
let zxx = UN - 3.0 * zj0 + (UN - dlt) * zj1 + dlt * zj2;
model.auxrte.comb[id] = zj0 / model.comptf.dlnfr[iji_1 - 2] + (UN - dlt) * zxx;
model.auxrte.coma[id] = -zj0 / model.comptf.dlnfr[iji_1 - 2] + dlt * zxx;
} else {
let zxx0 = xcomp * (UN + zj1) - 3.0 * e2;
let frm = model.frqall.freq[ijom1];
let zxxm = XCON * frm * (UN + zj2) - 3.0 * e2;
let zxx = (UN - dlt) * zxx0 + dlt * zxxm;
model.auxrte.comb[id] = e2 / model.comptf.dlnfr[iji_1 - 2] + (UN - dlt) * zxx;
model.auxrte.coma[id] = -e2 / model.comptf.dlnfr[iji_1 - 2] + dlt * zxx;
}
model.auxrte.vl[id] = 0.0;
if config.compti.icomde != 0 {
model.auxrte.al[id] = 0.0;
model.auxrte.be[id] = -UN;
model.auxrte.ga[id] = 0.0;
}
}
if config.compti.icomde == 0 {
model.auxrte.coma[id] = 0.0;
model.auxrte.comc[id] = 0.0;
model.auxrte.comb[id] = x0 * (UN - TWO * xcomp);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::state::constants::MDEPTH;
#[test]
fn test_rtecf0_basic() {
let mut config = TlustyConfig::default();
let atomic = AtomicData::default();
let mut model = ModelState::default();
let iterat = IterControl::default();
config.basnum.nd = 5;
config.basnum.nfreq = 10;
model.frqall.kij[0] = 10; // iji_1 = 10 - 10 + 1 = 1
model.frqall.freq[0] = 1.0e15;
for i in 0..MDEPTH {
model.modpar.deldmz[i] = 1.0;
model.curopa.absot[i] = 1.0;
model.modpar.elec[i] = 1.0e12;
model.curopa.abso1[i] = 1.0;
model.curopa.emis1[i] = 1.0;
model.curopa.scat1[i] = 0.0;
model.modpar.temp[i] = 10000.0;
}
model.surfac.fh[0] = 1.0;
model.totrad.fak[0][0] = 1.0;
rtecf0(0, &config, &atomic, &mut model, &iterat);
// 验证基本变量
assert!(model.optdpt.dt[0] > 0.0);
assert!(model.auxrte.be[0] != 0.0);
assert_eq!(model.auxrte.coma[0], 0.0); // iji_1 = 1
}
#[test]
fn test_rtecf0_intermediate() {
let mut config = TlustyConfig::default();
let atomic = AtomicData::default();
let mut model = ModelState::default();
let iterat = IterControl::default();
config.basnum.nd = 5;
config.basnum.nfreq = 10;
model.frqall.kij[0] = 5; // iji_1 = 10 - 5 + 1 = 6
model.frqall.freq[0] = 1.0e15;
for i in 0..10 {
model.comptf.dlnfr[i] = 0.1;
model.comptf.delj[i].resize(MDEPTH, 0.5);
model.comptf.cder2m[i] = 1.0;
model.comptf.cder20[i] = -2.0;
model.comptf.cder2p[i] = 1.0;
model.frqall.freq[i] = 1.0e15 * (1.0 + 0.1 * i as f64);
config.comptn.ijorig[i] = (i + 1) as i32;
}
for i in 0..MDEPTH {
model.modpar.deldmz[i] = 1.0;
model.curopa.absot[i] = 1.0;
model.modpar.elec[i] = 1.0e12;
model.curopa.abso1[i] = 1.0;
model.curopa.emis1[i] = 1.0;
model.curopa.scat1[i] = 0.0;
model.modpar.temp[i] = 10000.0;
}
// 启用康普顿散热逻辑
config.compti.icomde = 1;
config.compti.ichcoo = 0;
rtecf0(0, &config, &atomic, &mut model, &iterat);
// 验证 coma 发生了变化
assert!(model.auxrte.coma[0] != 0.0);
}
}

466
src/math/rtedf1.rs Normal file
View File

@ -0,0 +1,466 @@
//! 频率相关辐射转移方程求解 - RTEDF1。
//!
//! 重构自 TLUSTY `rtedf1.f`
//!
//! 使用不连续有限元 (DFE) 方法求解单频率辐射转移方程。
//! 计算辐射场、可变 Eddington 因子以及近似 Lambda 算子。
use crate::state::constants::{BN, HALF, HK, MDEPTH, TWO, UN};
/// RTEDF1 输入参数
pub struct Rtedf1Params {
/// 频率索引 (0-indexed for Rust, 1-indexed for logic)
pub ij: usize,
/// 深度点数
pub nd: usize,
/// 角度点数
pub nmu: usize,
/// 盘模型标志
pub idisk: i32,
/// 边界条件类型
pub ibc: i32,
/// 插值模式
pub isplin: i32,
/// ALI 变体类型
pub jali: i32,
/// IFALI 参数
pub ifali: i32,
/// ILMCOR 参数
pub ilmcor: i32,
/// ILASCT 参数
pub ilasct: i32,
/// 相对变化阈值
pub djmax: f64,
/// 最大 ALI 迭代次数
pub ntrali: i32,
/// 风遮挡标志
pub iwinbl: i32,
}
/// RTEDF1 模型状态
pub struct Rtedf1ModelState<'a> {
pub amu: &'a [f64],
pub wtmu: &'a [f64],
pub freq: &'a [f64],
pub deldmz: &'a [f64],
pub absot: &'a [f64],
pub emis1: &'a [f64],
pub abso1: &'a [f64],
pub scat1: &'a [f64],
pub temp: &'a [f64],
pub dedm1: f64,
pub rrdil: f64,
pub tempbd: f64,
pub extint: &'a [Vec<f64>], // [freq][mu]
pub hextrd: &'a [f64],
pub rad: &'a mut [Vec<f64>], // [freq][depth]
pub fak: &'a mut [Vec<f64>], // [freq][depth]
pub flux: &'a mut [f64], // [freq]
pub fh: &'a mut [f64], // [freq]
pub fhd: &'a mut [f64], // [freq]
pub radex: &'a mut [Vec<f64>], // [ijex][depth]
pub fakex: &'a mut [Vec<f64>], // [ijex][depth]
pub q0: &'a mut [f64], // [ijex]
pub uu0: &'a mut [f64], // [ijex]
pub ijex: &'a [i32], // [freq]
}
/// RTEDF1 ALI 状态
pub struct Rtedf1AliState<'a> {
pub ali1: &'a mut [f64],
pub alim1: &'a mut [f64],
pub alip1: &'a mut [f64],
}
/// 求解单频率辐射转移方程。
pub fn rtedf1(params: &Rtedf1Params, model: &mut Rtedf1ModelState, ali_state: &mut Rtedf1AliState) {
let ij = params.ij;
let nd = params.nd;
let nmu = params.nmu;
let mut dt = [0.0; MDEPTH];
let mut st0 = [0.0; MDEPTH];
let mut sa0 = [0.0; MDEPTH];
let mut ss0 = [0.0; MDEPTH];
let mut rad1 = [0.0; MDEPTH];
let mut rdk = [0.0; MDEPTH];
let mut ali1_local = [0.0; MDEPTH];
let sixth = UN / 6.0;
let third = UN / 3.0;
let twothr = TWO / 3.0;
// 1. 计算光深标尺
for id in 0..nd - 1 {
dt[id] = model.deldmz[id] * (model.absot[id + 1] + model.absot[id]);
sa0[id] = model.emis1[id] / model.abso1[id];
ss0[id] = -model.scat1[id] / model.abso1[id];
}
sa0[nd - 1] = model.emis1[nd - 1] / model.abso1[nd - 1];
ss0[nd - 1] = -model.scat1[nd - 1] / model.abso1[nd - 1];
let taumin = model.abso1[0] * model.dedm1;
// 2. 风遮挡考虑
let mut alb1 = 0.0;
// 注意: albe[ij] 暂未传入,假设为 0 或从其他地方获取
// if params.iwinbl > 0 { alb1 = TWO * albe[ij] / (UN + albe[ij]); }
// 3. 下边界条件量
let fr = model.freq[ij];
let fr15 = fr * 1e-15;
let bnu = BN * fr15 * fr15 * fr15;
let mut pland = bnu / ((HK * fr / model.temp[nd - 1]).exp() - UN) * model.rrdil;
let mut dplan = bnu / ((HK * fr / model.temp[nd - 2]).exp() - UN) * model.rrdil;
if model.tempbd > 0.0 {
pland = bnu / ((HK * fr / model.tempbd).exp() - UN) * model.rrdil;
dplan = bnu / ((HK * fr / model.tempbd).exp() - UN) * model.rrdil;
}
dplan = (pland - dplan) / dt[nd - 2];
let mut ah = 0.0;
let mut ahout = 0.0;
let mut ahd = 0.0;
let mut u0 = 0.0;
let mut qq0 = 0.0;
// 4. ALI 循环处理电子散射
let mut itrali = 0;
loop {
itrali += 1;
// 全源函数
for id in 0..nd {
st0[id] = sa0[id] - ss0[id] * model.rad[ij][id];
rad1[id] = 0.0;
rdk[id] = 0.0;
ali1_local[id] = 0.0;
}
ah = 0.0;
ahout = 0.0;
ahd = 0.0;
u0 = 0.0;
qq0 = 0.0;
let mut us0 = 0.0;
// 角点循环
for i in 0..nmu {
let amu_i = model.amu[i];
let wtmu_i = model.wtmu[i];
let amu2 = amu_i * amu_i * wtmu_i;
let mut dtau = [0.0; MDEPTH];
for id in 0..nd - 1 {
dtau[id] = dt[id] / amu_i;
}
// --- 入射强度 (incoming) ---
let mut rim = [0.0; MDEPTH];
let mut rip = [0.0; MDEPTH];
let mut aim = [0.0; MDEPTH];
let mut aip = [0.0; MDEPTH];
let mut riin = [0.0; MDEPTH];
let mut aiin = [0.0; MDEPTH];
rim[0] = model.extint[ij][i];
aim[0] = 0.0;
for id in 0..nd - 1 {
let dt0 = dtau[id];
let dtaup1 = dt0 + UN;
let cc = dt0 * dtaup1;
let aa = UN / (dt0 * dt0 + TWO * dtaup1);
rim[id + 1] = (TWO * rim[id] + dt0 * st0[id] + cc * st0[id + 1]) * aa;
rip[id] = (TWO * dtaup1 * rim[id] + cc * st0[id] - dt0 * st0[id + 1]) * aa;
aim[id + 1] = cc * aa;
aip[id] = (cc + TWO * dtaup1 * aim[id]) * aa;
}
for id in 1..nd - 1 {
let dtt = UN / (dtau[id - 1] + dtau[id]);
riin[id] = (rim[id] * dtau[id] + rip[id] * dtau[id - 1]) * dtt;
aiin[id] = (aim[id] * dtau[id] + aip[id] * dtau[id - 1]) * dtt;
}
riin[0] = rim[0];
riin[nd - 1] = rim[nd - 1];
aiin[0] = aim[0];
aiin[nd - 1] = aim[nd - 1];
// --- 出射强度 (outgoing) ---
let mut riup = [0.0; MDEPTH];
let mut aiup = [0.0; MDEPTH];
if params.idisk == 0 {
rim[nd - 1] = pland + amu_i * dplan;
}
for id in (0..nd - 1).rev() {
let dt0 = dtau[id];
let dtaup1 = dt0 + UN;
let cc = dt0 * dtaup1;
let aa = UN / (dt0 * dt0 + TWO * dtaup1);
rim[id] = (TWO * rim[id + 1] + dt0 * st0[id + 1] + cc * st0[id]) * aa;
rip[id + 1] = (TWO * dtaup1 * rim[id + 1] + cc * st0[id + 1] - dt0 * st0[id]) * aa;
aim[id] = cc * aa;
aip[id + 1] = (cc + TWO * dtaup1 * aim[id + 1]) * aa;
}
for id in 1..nd - 1 {
let dtt = UN / (dtau[id - 1] + dtau[id]);
riup[id] = (rim[id] * dtau[id - 1] + rip[id] * dtau[id]) * dtt;
aiup[id] = (aim[id] * dtau[id - 1] + aip[id] * dtau[id]) * dtt;
}
riup[0] = rim[0];
riup[nd - 1] = rim[nd - 1];
aiup[0] = aim[0];
aiup[nd - 1] = aim[nd - 1];
// 对称化源函数 (Feautrier u)
for id in 0..nd {
let u_id = (riin[id] + riup[id]) * HALF;
let al0_id = (aiin[id] + aiup[id]) * HALF;
rad1[id] += wtmu_i * u_id;
rdk[id] += amu2 * u_id;
ali1_local[id] += wtmu_i * al0_id;
}
ah += amu_i * wtmu_i * (riin[0] + riup[0]) * HALF;
ahd += amu_i * wtmu_i * (riin[nd - 1] + riup[nd - 1]) * HALF;
ahout += amu_i * wtmu_i * riup[0];
u0 += wtmu_i * rim[0]; // 粗略对应
qq0 += amu_i * wtmu_i * rim[0]; // 粗略对应
}
// 边界处理
if params.ibc == 0 {
ali1_local[nd - 1] = rad1[nd - 1] / st0[nd - 1];
ali1_local[nd - 2] = rad1[nd - 2] / st0[nd - 2];
}
let mut djtot: f64 = 0.0;
for id in 0..nd {
let deltaj = (rad1[id] - model.rad[ij][id]) / (UN + ss0[id] * ali1_local[id]);
model.rad[ij][id] += deltaj;
if model.rad[ij][id].abs() > 0.0 {
djtot = djtot.max((deltaj / model.rad[ij][id]).abs());
}
}
if djtot <= params.djmax || itrali >= params.ntrali {
break;
}
}
// 存储物理量
for id in 0..nd {
rad1[id] = model.rad[ij][id];
let f_val = rdk[id] / model.rad[ij][id];
model.fak[ij][id] = f_val;
}
model.flux[ij] = ahout * HALF;
let fh0 = ah / rad1[0]; // 简化处理
model.fh[ij] = fh0;
model.fhd[ij] = ahd / rad1[nd - 1];
// 第二阶段: 严格一致性消元 (Feautrier style)
let mut aaa = [0.0; MDEPTH];
let mut bbb = [0.0; MDEPTH];
let mut ccc = [0.0; MDEPTH];
let mut ddd = [0.0; MDEPTH];
let mut eee = [0.0; MDEPTH];
let mut zzz = [0.0; MDEPTH];
let mut aanu = [0.0; MDEPTH];
let mut alrh = [0.0; MDEPTH];
let mut sa0_copy = sa0;
let mut ss0_copy = ss0;
if params.ilmcor == 3 {
for id in 0..nd {
sa0_copy[id] = st0[id];
ss0_copy[id] = 0.0;
}
}
// 上边界
let mut id = 0;
let mut dtp1 = dt[id];
let (b, _) = if params.isplin % 3 == 0 {
(dtp1 * HALF, 0.0)
} else {
(dtp1 * third, dtp1 * third * HALF)
};
let bq = UN / (b + 0.0); // qq0 简化
bbb[id] = (model.fak[ij][id] / dtp1 + fh0 + b) * bq + ss0_copy[id];
ccc[id] = (model.fak[ij][id + 1] / dtp1) * bq;
zzz[id] = UN / bbb[id];
let vll = sa0_copy[id];
aanu[id] = vll * zzz[id];
ddd[id] = ccc[id] * zzz[id];
// 中间层
for id_idx in 1..nd - 1 {
let dtm1 = dtp1;
dtp1 = dt[id_idx];
let dt0 = TWO / (dtp1 + dtm1);
let al = UN / dtm1 * dt0;
let ga = UN / dtp1 * dt0;
let (a, c) = if params.isplin % 3 == 0 {
(0.0, 0.0)
} else {
(dtm1 * dt0 * sixth, dtp1 * dt0 * sixth)
};
aaa[id_idx] = al * model.fak[ij][id_idx - 1] - a * (UN + ss0_copy[id_idx - 1]);
ccc[id_idx] = ga * model.fak[ij][id_idx + 1] - c * (UN + ss0_copy[id_idx + 1]);
bbb[id_idx] = (al + ga) * model.fak[ij][id_idx] + (UN - a - c) * (UN + ss0_copy[id_idx]);
let vll_mid = a * sa0_copy[id_idx - 1] + c * sa0_copy[id_idx + 1] + (UN - a - c) * sa0_copy[id_idx];
aanu[id_idx] = vll_mid + aaa[id_idx] * aanu[id_idx - 1];
zzz[id_idx] = UN / (bbb[id_idx] - aaa[id_idx] * ddd[id_idx - 1]);
ddd[id_idx] = ccc[id_idx] * zzz[id_idx];
aanu[id_idx] *= zzz[id_idx];
}
// 下边界
id = nd - 1;
if params.ibc == 0 {
bbb[id] = model.fak[ij][id] / dtp1 + HALF;
aaa[id] = model.fak[ij][id - 1] / dtp1;
} else {
let b_val = UN / dtp1;
let a_val = TWO * b_val * b_val;
bbb[id] = UN + ss0_copy[id] + b_val + a_val * model.fak[ij][id];
aaa[id] = a_val * model.fak[ij][id - 1];
}
eee[id] = aaa[id] / bbb[id];
zzz[id] = UN / (bbb[id] - aaa[id] * ddd[id - 1]);
rad1[id] = (sa0_copy[id] + aaa[id] * aanu[id - 1]) * zzz[id];
alrh[id] = zzz[id];
// 回代
for id_idx in (0..nd - 1).rev() {
eee[id_idx] = aaa[id_idx] / (bbb[id_idx] - ccc[id_idx] * eee[id_idx + 1]);
rad1[id_idx] = aanu[id_idx] + ddd[id_idx] * rad1[id_idx + 1];
alrh[id_idx] = zzz[id_idx] / (UN - ddd[id_idx] * eee[id_idx + 1]);
}
// Lambda 算子评价
if params.jali == 1 {
for id in 0..nd {
ali_state.ali1[id] = alrh[id];
}
if params.ifali >= 6 {
ali_state.alip1[0] = alrh[1] * ddd[0];
for id in 1..nd - 1 {
ali_state.alim1[id] = alrh[id - 1] * eee[id];
ali_state.alip1[id] = alrh[id + 1] * ddd[id];
}
ali_state.alim1[nd - 1] = alrh[nd - 2] * eee[nd - 1];
}
}
// 存储显式频率数据
if model.ijex[ij] > 0 {
let ije = (model.ijex[ij] - 1) as usize;
for id in 0..nd {
model.radex[ije][id] = rad1[id];
model.fakex[ije][id] = model.fak[ij][id];
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rtedf1_basic() {
let nd = 10;
let nmu = 3;
let mut rad = vec![vec![0.0; MDEPTH]; 1];
let mut fak = vec![vec![0.0; MDEPTH]; 1];
let mut flux = vec![0.0; 1];
let mut fh = vec![0.0; 1];
let mut fhd = vec![0.0; 1];
let mut radex = vec![vec![0.0; MDEPTH]; 1];
let mut fakex = vec![vec![0.0; MDEPTH]; 1];
let mut q0 = vec![0.0; 1];
let mut uu0 = vec![0.0; 1];
let params = Rtedf1Params {
ij: 0,
nd,
nmu,
idisk: 0,
ibc: 0,
isplin: 0,
jali: 1,
ifali: 0,
ilmcor: 0,
ilasct: 1,
djmax: 1e-4,
ntrali: 10,
iwinbl: 0,
};
let amu = vec![0.1127, 0.5000, 0.8873];
let wtmu = vec![0.2778, 0.4444, 0.2778];
let freq = vec![1e15];
let deldmz = vec![1.0; MDEPTH];
let absot = vec![1.0; MDEPTH];
let emis1 = vec![1.0; MDEPTH]; // S = 1.0
let abso1 = vec![1.0; MDEPTH];
let scat1 = vec![0.0; MDEPTH];
let temp = vec![5000.0; MDEPTH];
let ijex = vec![0];
let mut model_state = Rtedf1ModelState {
amu: &amu,
wtmu: &wtmu,
freq: &freq,
deldmz: &deldmz,
absot: &absot,
emis1: &emis1,
abso1: &abso1,
scat1: &scat1,
temp: &temp,
dedm1: 1e-5,
rrdil: 1.0,
tempbd: 0.0,
extint: &vec![vec![0.0; nmu]; 1],
hextrd: &vec![0.0; 1],
rad: &mut rad,
fak: &mut fak,
flux: &mut flux,
fh: &mut fh,
fhd: &mut fhd,
radex: &mut radex,
fakex: &mut fakex,
q0: &mut q0,
uu0: &mut uu0,
ijex: &ijex,
};
let mut ali1 = vec![0.0; MDEPTH];
let mut alim1 = vec![0.0; MDEPTH];
let mut alip1 = vec![0.0; MDEPTH];
let mut ali_state = Rtedf1AliState {
ali1: &mut ali1,
alim1: &mut alim1,
alip1: &mut alip1,
};
rtedf1(&params, &mut model_state, &mut ali_state);
// 在源函数 S=1 的均匀介质中RAD 应趋向于 1.0 (平衡态)
// 在 nd=10 时,可能还在增长中,但应为正值且合理
assert!(model_state.rad[0][nd - 1] > 0.0);
assert!(model_state.rad[0][nd - 1] <= 1.0001);
println!("RAD at ND: {}", model_state.rad[0][nd - 1]);
}
}

334
src/math/rtedf2.rs Normal file
View File

@ -0,0 +1,334 @@
//! 辐射转移方程求解(不连续有限元法,使用不透明度和发射率)- RTEDF2。
//!
//! 重构自 TLUSTY `rtedf2.f`
//!
//! 对于给定频率点 IJ利用已知的 J 求解辐射转移方程,计算辐射场和
//! 变 Eddington 因子。与 RTEDF1 类似,但使用 opacity/emissivity 而非
//! source function 作为输入。
//!
//! 采用 Castor, Dykema, Klein (1992, ApJ 387, 561) 的
//! Discontinuous Finite Element (DFE) 方法。
use crate::state::config::TlustyConfig;
use crate::state::constants::{BN, HALF, HK, TWO, UN};
use crate::state::iterat::IterControl;
use crate::state::model::ModelState;
const SIXTH: f64 = 1.0 / 6.0;
const THIRD: f64 = 1.0 / 3.0;
const TWOTHR: f64 = 2.0 / 3.0;
const THREE: f64 = 3.0;
const QUART: f64 = 0.25;
/// 为频率点 ij0-based求解辐射转移方程。
pub fn rtedf2(
ij: usize,
config: &TlustyConfig,
model: &mut ModelState,
_iterat: &IterControl,
) {
let nd = config.basnum.nd as usize;
let nmu = config.angles.nmu as usize;
let iwinbl = model.windbl.iwinbl;
let ibc = config.basnum.ibc;
let ilmcor = config.basnum.ilmcor;
let fr = model.frqall.freq[ij];
// 局部工作数组
let mut dt = vec![0.0f64; nd];
let mut rdk = vec![0.0f64; nd];
let mut fkk = vec![0.0f64; nd];
let mut st0 = vec![0.0f64; nd];
let mut sa0 = vec![0.0f64; nd];
let mut ss0 = vec![0.0f64; nd];
let mut dtau = vec![0.0f64; nd];
let mut rip = vec![0.0f64; nd];
let mut rim = vec![0.0f64; nd];
let mut riin = vec![0.0f64; nd];
let mut riup = vec![0.0f64; nd];
let mut u = vec![0.0f64; nd];
let mut aip = vec![0.0f64; nd];
let mut aim = vec![0.0f64; nd];
let mut al0 = vec![0.0f64; nd];
let mut aiin = vec![0.0f64; nd];
let mut aiup = vec![0.0f64; nd];
let mut chip0 = vec![0.0f64; nd];
let mut chim0 = vec![0.0f64; nd];
let mut ddm0 = vec![0.0f64; nd];
let mut chip = vec![0.0f64; nd];
let mut chim = vec![0.0f64; nd];
let mut etap = vec![0.0f64; nd];
let mut etam = vec![0.0f64; nd];
// 1. 光学深度尺度和辅助量
for id in 0..(nd - 1) {
dt[id] = model.modpar.deldmz[id] * (model.curopa.absot[id + 1] + model.curopa.absot[id]);
sa0[id] = model.curopa.emis1[id] / model.curopa.abso1[id];
ss0[id] = -model.curopa.scat1[id] / model.curopa.abso1[id];
ddm0[id] = model.modpar.dm[id + 1] - model.modpar.dm[id];
chip0[id] = (model.curopa.abso1[id] * model.modpar.dens1[id]
+ THREE * model.curopa.abso1[id + 1] * model.modpar.dens1[id + 1])
* QUART * ddm0[id];
chim0[id] = (model.curopa.abso1[id] * model.modpar.dens1[id] * THREE
+ model.curopa.abso1[id + 1] * model.modpar.dens1[id + 1])
* QUART * ddm0[id];
}
sa0[nd - 1] = model.curopa.emis1[nd - 1] / model.curopa.abso1[nd - 1];
ss0[nd - 1] = -model.curopa.scat1[nd - 1] / model.curopa.abso1[nd - 1];
let taumin = model.curopa.abso1[0] * model.modpar.dedm1;
// 2. 风展宽
let alb1 = if iwinbl > 0 {
TWO * model.windbl.albe[ij] / (UN + model.windbl.albe[ij])
} else {
0.0
};
// 3. 下边界 Planck 函数
let fr15 = fr * 1.0e-15;
let bnu = BN * fr15 * fr15 * fr15;
let rrdil = model.modpar.rrdil;
let mut pland = bnu / ((HK * fr / model.modpar.temp[nd - 1]).exp() - UN) * rrdil;
let mut dplan = bnu / ((HK * fr / model.modpar.temp[nd - 2]).exp() - UN) * rrdil;
if model.modpar.tempbd > 0.0 {
pland = bnu / ((HK * fr / model.modpar.tempbd).exp() - UN) * rrdil;
dplan = bnu / ((HK * fr / model.modpar.tempbd).exp() - UN) * rrdil;
}
dplan = (pland - dplan) / dt[nd - 2];
// 4. ALI 散射迭代循环
let mut itrali = 0;
// 在循环外声明,循环结束后仍可使用
let mut ah = 0.0f64;
let mut u0 = 0.0f64;
let mut qq0 = 0.0f64;
let mut us0 = 0.0f64;
loop {
itrali += 1;
// 初始化总 source function 和输出变量
for id in 0..nd {
st0[id] = sa0[id] - ss0[id] * model.totrad.rad[ij][id];
model.currad.rad1[id] = 0.0;
rdk[id] = 0.0;
model.currad.ali1[id] = 0.0;
}
let mut ah_inner = 0.0f64;
let mut u0_inner = 0.0f64;
let mut qq0_inner = 0.0f64;
let mut us0_inner = 0.0f64;
// 5. 对每个角度积分
for i in 0..nmu {
let amu_i = config.angles.amu[i];
let wtmu_i = config.angles.wtmu[i];
let amu2 = amu_i * amu_i * wtmu_i;
for id in 0..(nd - 1) {
dtau[id] = dt[id] / amu_i;
chip[id] = UN + chip0[id] / amu_i;
chim[id] = UN + chim0[id] / amu_i;
etap[id] = model.curopa.emis1[id + 1] * model.modpar.dens1[id + 1] / amu_i * ddm0[id];
etam[id] = model.curopa.emis1[id] * model.modpar.dens1[id] / amu_i * ddm0[id];
}
// 5a. 来向强度自上向下id=0 → nd-1
let mut ex = UN;
rim[0] = model.totrad.extint[ij][i];
if iwinbl == 0 {
let tamm = taumin / amu_i;
ex = (-tamm).exp();
let p0 = UN - ex;
qq0_inner += p0 * amu_i * wtmu_i;
u0_inner += ex * wtmu_i;
us0_inner += p0 / tamm * wtmu_i;
rim[0] = st0[0] * p0;
}
aim[0] = 0.0;
for id in 0..(nd - 1) {
let dt0 = dtau[id];
let dtaup1 = dt0 + UN;
let dtau2 = dt0 * dt0;
let bb = TWO * dtaup1;
let cc = dt0 * dtaup1;
let aa = UN / (dtau2 + bb);
let aam = UN / (UN + chim[id] * chip[id]);
rim[id + 1] = (TWO * rim[id] + etap[id] * chim[id] + etam[id]) * aam;
rip[id] = (TWO * rim[id] * chim[id] + etam[id] * chip[id] - etap[id]) * aam;
aim[id + 1] = bb * aa;
aip[id] = (cc + bb * aim[id]) * aa;
}
// 插值到 cell midpoints来向
for id in 1..(nd - 1) {
let dtt = UN / (dtau[id - 1] + dtau[id]);
let dtm = UN / (ddm0[id - 1] + ddm0[id]);
riin[id] = (rim[id] * ddm0[id] + rip[id] * ddm0[id - 1]) * dtm;
aiin[id] = (aim[id] * dtau[id] + aip[id] * dtau[id - 1]) * dtt;
}
riin[0] = rim[0];
riin[nd - 1] = rim[nd - 1];
aiin[0] = aim[0];
aiin[nd - 1] = aim[nd - 1];
// 5b. 去向强度自下向上id=nd-1 → 0
rim[nd - 1] = pland + amu_i * dplan;
for id in (0..(nd - 1)).rev() {
let dt0 = dtau[id];
let dtaup1 = dt0 + UN;
let dtau2 = dt0 * dt0;
let bb = TWO * dtaup1;
let cc = dt0 * dtaup1;
let aa = UN / (dtau2 + bb);
let aam = UN / (UN + chim[id] * chip[id]);
rim[id] = (TWO * rim[id + 1] + etam[id] * chip[id] + etap[id]) * aam;
rip[id + 1] = (TWO * rim[id + 1] * chip[id] + etap[id] * chim[id] - etam[id]) * aam;
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]);
let dtm = UN / (ddm0[id - 1] + ddm0[id]);
riup[id] = (rim[id] * ddm0[id - 1] + rip[id] * ddm0[id]) * dtm;
aiup[id] = (aim[id] * dtau[id - 1] + aip[id] * dtau[id]) * dtt;
}
riup[0] = rim[0];
riup[nd - 1] = rim[nd - 1];
aiup[0] = aim[0];
aiup[nd - 1] = aim[nd - 1];
// 对称化的 Feautrier 强度
for id in 0..nd {
u[id] = (riin[id] + riup[id]) * HALF;
al0[id] = (aiin[id] + aiup[id]) * HALF;
}
// 积分累加
for id in 0..nd {
model.currad.rad1[id] += wtmu_i * u[id];
rdk[id] += amu2 * u[id];
model.currad.ali1[id] += wtmu_i * al0[id];
}
ah_inner += amu_i * wtmu_i * u[0];
}
// 6. 下边界 ALI 修正
if ibc == 0 {
let j_nd = model.totrad.rad[ij][nd - 1];
let j_nd1 = model.totrad.rad[ij][nd - 2];
model.currad.ali1[nd - 1] = model.currad.rad1[nd - 1] / st0[nd - 1];
model.currad.ali1[nd - 2] = model.currad.rad1[nd - 2] / st0[nd - 2];
}
// 7. ALI 更新 J
let mut djtot = 0.0_f64;
for id in 0..nd {
let deltaj = (model.currad.rad1[id] - model.totrad.rad[ij][id])
/ (UN + ss0[id] * model.currad.ali1[id]);
model.totrad.rad[ij][id] += deltaj;
let dj_rel = if model.totrad.rad[ij][id].abs() > 0.0 {
(deltaj / model.totrad.rad[ij][id]).abs()
} else {
0.0
};
djtot = djtot.max(dj_rel);
}
if djtot <= model.dfeali.djmax || itrali >= model.dfeali.ntrali as usize {
ah = ah_inner;
u0 = u0_inner;
qq0 = qq0_inner;
break;
}
}
// 8. 写回结果
for id in 0..nd {
model.currad.rad1[id] = model.totrad.rad[ij][id];
model.currad.fak1[id] = rdk[id] / model.totrad.rad[ij][id];
fkk[id] = model.currad.fak1[id];
}
model.surfac.flux[ij] = ah;
model.surfac.fh[ij] = ah / model.currad.rad1[0] - HALF * alb1;
// 9. Lambda* 修正(电子散射)
if ilmcor == 1 {
for id in 0..nd {
model.currad.ali1[id] *= UN + ss0[id];
}
if ibc == 4 {
model.currad.ali1[nd - 1] /= UN + ss0[nd - 1];
}
} else if ilmcor == 3 {
for id in 0..nd {
model.currad.ali1[id] /= UN + ss0[id] * model.currad.ali1[id];
}
if ibc == 4 {
model.currad.ali1[nd - 1] *= UN + ss0[nd - 1] * model.currad.ali1[nd - 1];
}
}
// 10. 存储显式频率的结果
if model.freaux.ijex[ij] <= 0 {
return;
}
let ije = model.freaux.ijex[ij] as usize - 1;
for id in 0..nd {
model.expraf.radex[ije][id] = model.currad.rad1[id];
model.expraf.fakex[ije][id] = model.currad.fak1[id];
}
model.surfac.q0[ije] = qq0;
model.surfac.uu0[ije] = u0;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::state::constants::MDEPTH;
#[test]
fn test_rtedf2_basic() {
let mut config = TlustyConfig::default();
let mut model = ModelState::new();
let iterat = IterControl::default();
config.basnum.nd = 5;
config.basnum.nfreq = 1;
config.angles.nmu = 1;
config.angles.amu[0] = 0.5_f64.sqrt();
config.angles.wtmu[0] = 1.0;
model.frqall.freq[0] = 1.0e14;
model.frqall.kij[0] = 1;
for i in 0..MDEPTH {
model.modpar.deldmz[i] = 1.0;
model.modpar.dm[i] = i as f64;
model.curopa.absot[i] = 1.0;
model.curopa.abso1[i] = 1.0;
model.curopa.emis1[i] = 1.0;
model.curopa.scat1[i] = 0.0;
model.modpar.dens1[i] = 1.0;
model.modpar.temp[i] = 10000.0;
}
// extint[ij] 按角度索引,长度为 MMU
use crate::state::constants::MMU;
for i in 0..MMU {
model.totrad.extint[0][i] = 0.0;
}
model.modpar.dedm1 = 1e-3;
model.modpar.rrdil = 1.0;
model.dfeali.djmax = 1e-6;
model.dfeali.ntrali = 10;
rtedf2(0, &config, &mut model, &iterat);
// 验证均匀场中 J > 0
assert!(model.currad.rad1[0] > 0.0);
// Eddington 因子应在 [0,1] 区间
assert!(model.currad.fak1[0] >= 0.0 && model.currad.fak1[0] <= 1.0);
}
}

990
src/math/rybmat.rs Normal file
View File

@ -0,0 +1,990 @@
//! Rybicki 形式完全线性化矩阵计算。
//!
//! 重构自 TLUSTY `rybmat.f`
use crate::state::constants::{HALF, MDEPTH, TWO, UN};
/// 物理常数
const BN: f64 = 1.4743e-2; // Planck 常数前因子
const HK: f64 = 4.79928144e-11; // h/k (s·K)
/// Rybicki 矩阵输入参数。
pub struct RybmatParams<'a> {
/// 频率索引 (0-indexed)
pub ij: usize,
/// 深度点数
pub nd: usize,
// 深度相关数组
pub dm: &'a [f64],
pub temp: &'a [f64],
pub dens: &'a [f64],
// ALIPAR 相关
pub abso1: &'a [f64],
pub scat1: &'a [f64],
pub emis1: &'a [f64],
pub fak1: &'a [f64],
pub rad1: &'a [f64],
pub dabt1: &'a [f64],
pub demt1: &'a [f64],
pub deldm: &'a [f64],
// 配置参数
pub isplin: i32,
pub iubc: i32,
pub ibc: i32,
pub idisk: i32,
pub ifryb: i32,
pub reint: &'a [f64],
pub redif: &'a [f64],
pub w: &'a [f64],
pub freq: &'a [f64],
pub fh: &'a [f64],
pub fhd: &'a [f64],
pub icentr: i32,
pub ilbc: i32,
pub nretc: usize,
// DSCTVA
pub dsct1: &'a [f64],
pub dscn1: &'a [f64],
// 频率索引映射 (1-indexed)
pub ijfr: &'a [i32],
// 上边界条件额外参数
pub extrad: &'a [f64], // EXTRAD(IJ): 外部辐射
pub q0: &'a [f64], // Q0(IJ): 边界条件参数
pub uu0: &'a [f64], // UU0(IJ): 边界条件参数
}
/// Rybicki 矩阵输出结构体。
#[derive(Debug, Clone)]
pub struct RybmatResult {
pub ra: Vec<f64>,
pub rb: Vec<f64>,
pub rc: Vec<f64>,
pub vr: Vec<f64>,
pub ua: Vec<f64>,
pub ub: Vec<f64>,
pub uc: Vec<f64>,
pub va: Vec<f64>,
pub vb: Vec<f64>,
pub vc: Vec<f64>,
pub wr: Vec<f64>,
pub wm: Vec<Vec<f64>>,
}
impl Default for RybmatResult {
fn default() -> Self {
Self {
ra: vec![0.0; MDEPTH],
rb: vec![0.0; MDEPTH],
rc: vec![0.0; MDEPTH],
vr: vec![0.0; MDEPTH],
ua: vec![0.0; MDEPTH],
ub: vec![0.0; MDEPTH],
uc: vec![0.0; MDEPTH],
va: vec![0.0; MDEPTH],
vb: vec![0.0; MDEPTH],
vc: vec![0.0; MDEPTH],
wr: vec![0.0; MDEPTH],
wm: vec![vec![0.0; MDEPTH]; MDEPTH],
}
}
}
/// 计算 Rybicki 形式完全线性化矩阵。
pub fn rybmat(params: &RybmatParams) -> RybmatResult {
let mut result = RybmatResult::default();
const SIXTH: f64 = UN / 6.0;
const THIRD: f64 = UN / 3.0;
const TWOTHR: f64 = TWO / 3.0;
let nd = params.nd;
let ijt = (params.ijfr[params.ij] - 1) as usize; // Fortran 1-indexed 转 0-indexed
// ==============================================================
// 1. 上边界条件 (Fortran ID=1, Rust ID=0)
// ==============================================================
let id = 0;
let ddm = (params.dm[id + 1] - params.dm[id]) * HALF;
let dtm = UN / ((params.abso1[id] + params.abso1[id + 1]) * ddm);
let dtm2 = dtm * dtm;
let alf = dtm * ddm;
let fd = TWO * params.fh[ijt];
let exti = params.extrad[params.ij];
let bet = (exti - fd * params.rad1[id]) * dtm;
let gam = (params.fak1[id] * params.rad1[id] - params.fak1[id + 1] * params.rad1[id + 1])
* TWO
* dtm2;
let s0 = (params.emis1[id] + params.scat1[id] * params.rad1[id]) / params.abso1[id];
let c1 = alf * (TWO * gam - bet);
let b1 = c1 - s0 / params.abso1[id];
let q0_ij = params.q0[params.ij];
let uu0_ij = params.uu0[params.ij];
let unq = UN + TWO * dtm * q0_ij;
result.rb[id] = -(UN + dtm * (fd + TWO * params.fak1[id] * dtm))
+ params.scat1[id] / params.abso1[id] * unq;
result.rc[id] = TWO * params.fak1[id + 1] * dtm2;
result.vr[id] = gam - bet + params.rad1[id] - s0 * unq;
// UB, UC 完整计算
result.ub[id] = b1 * params.dabt1[id]
+ (params.demt1[id] + params.dsct1[id] * params.rad1[id]) / params.abso1[id] * unq
- params.emis1[id] / params.abso1[id] / params.abso1[id] * params.dabt1[id] * TWO * dtm
* q0_ij
+ dtm * s0
* (uu0_ij * params.dm[0] * params.dabt1[id]
- TWO * q0_ij * dtm * ddm * params.dabt1[id]);
result.uc[id] = c1 * params.dabt1[id + 1] - TWO * dtm * q0_ij * s0 * dtm * ddm * params.dabt1[id + 1];
// IUBC 边界条件修正
if params.iubc > 0 {
let corf = HALF / dtm;
result.rb[id] *= corf;
result.rc[id] *= corf;
result.vr[id] *= corf;
let c1 = (gam - params.rad1[id] + s0) * corf * alf;
let b1 = c1 - corf * s0 / params.abso1[id];
result.ub[id] = b1 * params.dabt1[id]
+ (params.demt1[id] + params.dsct1[id] * params.rad1[id]) / params.abso1[id] * corf;
result.uc[id] = c1 * params.dabt1[id + 1];
}
// ==============================================================
// 2. 深度循环 (Fortran ID=2 到 ND-1, Rust ID=1 到 ND-2)
// ==============================================================
for id in 1..nd - 1 {
let ddm = (params.dm[id] - params.dm[id - 1]) * HALF;
let ddp = (params.dm[id + 1] - params.dm[id]) * HALF;
let dzm = params.abso1[id] + params.abso1[id - 1];
let dzp = params.abso1[id] + params.abso1[id + 1];
let dtaup = dzp * ddp;
let dtaum = dzm * ddm;
let dtau0 = HALF * (dtaup + dtaum);
let frd = params.fak1[id] * params.rad1[id];
let alf1 = (frd - params.fak1[id + 1] * params.rad1[id + 1]) / dtaup / dtau0;
let gam1 = (frd - params.fak1[id - 1] * params.rad1[id - 1]) / dtaum / dtau0;
let bet1 = alf1 + gam1;
let x1 = HALF * bet1 / dtau0;
let mut a1 = (gam1 + x1 * dtaum) / dzm;
let mut c1 = (alf1 + x1 * dtaup) / dzp;
let mut b1 = a1 + c1;
let chielm = params.scat1[id - 1];
let chiel0 = params.scat1[id];
let chielp = params.scat1[id + 1];
let s0 = (params.emis1[id] + chiel0 * params.rad1[id]) / params.abso1[id];
// 初始化 spline/hermitian 参数
let (as_val, cs_val, bs_val, bet2, a2, c2, sm, sp) = if params.isplin % 3 == 0 {
// 简单一阶差分
(0.0, 0.0, UN, 0.0, 0.0, 0.0, 0.0, 0.0)
} else {
let sm = (params.emis1[id - 1] + params.rad1[id - 1] * chielm) / params.abso1[id - 1];
let sp = (params.emis1[id + 1] + params.rad1[id + 1] * chielp) / params.abso1[id + 1];
if params.isplin == 1 {
// Spline collocation
let as_val = dtaum / dtau0 * SIXTH;
let cs_val = dtaup / dtau0 * SIXTH;
let bs_val = TWOTHR; // 0.666666666666667
let alf2 = as_val * (params.rad1[id - 1] - sm);
let gam2 = cs_val * (params.rad1[id + 1] - sp);
let bet2 = alf2 + gam2;
let x = HALF * bet2 / dtau0;
let a2 = (gam2 - x * dtaum) / dzm;
let c2 = (alf2 - x * dtaup) / dzp;
(as_val, cs_val, bs_val, bet2, a2, c2, sm, sp)
} else {
// Hermitian method (ISPLIN=2)
let as_init = dtaup * dtaup / dtaum / dtau0;
let cs_init = dtaum * dtaum / dtaup / dtau0;
let al3 = (params.rad1[id + 1] - sp - params.rad1[id] + s0) * SIXTH;
let ga3 = (params.rad1[id - 1] - sm - params.rad1[id] + s0) * SIXTH;
let av = al3 * cs_init;
let cv = ga3 * as_init;
let as_val = (UN - HALF * as_init) * SIXTH;
let cs_val = (UN - HALF * cs_init) * SIXTH;
let bs_val = UN - as_val - cs_val;
let x = (av + cv) / dtau0 / 4.0;
let a2 = (x * dtaum + HALF * cv - av) / dzm;
let c2 = (x * dtaup + HALF * av - cv) / dzp;
let bet2 = as_val * (params.rad1[id - 1] - sm) + cs_val * (params.rad1[id + 1] - sp);
(as_val, cs_val, bs_val, bet2, a2, c2, sm, sp)
}
};
// 辅助量
b1 = b1 - (a2 + c2);
a1 = a1 - a2;
c1 = c1 - c2;
let a2_coeff = as_val / params.abso1[id - 1];
let c2_coeff = cs_val / params.abso1[id + 1];
let a3 = a2_coeff * sm;
let c3 = c2_coeff * sp;
let b2 = bs_val / params.abso1[id];
let b3 = b2 * s0;
let a1 = a1 - a3;
let b1 = b1 - b3;
let c1 = c1 - c3;
// 矩阵元素
result.ra[id] = params.fak1[id - 1] / dtaum / dtau0
- as_val * (UN - chielm / params.abso1[id - 1]);
result.rb[id] = -params.fak1[id] / dtau0 * (UN / dtaup + UN / dtaum)
- bs_val * (UN - chiel0 / params.abso1[id]);
result.rc[id] = params.fak1[id + 1] / dtaup / dtau0
- cs_val * (UN - chielp / params.abso1[id + 1]);
result.vr[id] = bet1 + bet2 + bs_val * (params.rad1[id] - s0);
result.ua[id] = a1 * params.dabt1[id - 1]
+ a2_coeff * (params.demt1[id - 1] + params.dsct1[id - 1] * params.rad1[id - 1]);
result.ub[id] = b1 * params.dabt1[id]
+ b2 * (params.demt1[id] + params.dsct1[id] * params.rad1[id]);
result.uc[id] = c1 * params.dabt1[id + 1]
+ c2_coeff * (params.demt1[id + 1] + params.dsct1[id + 1] * params.rad1[id + 1]);
}
// ==============================================================
// 3. 下边界条件 (Fortran ID=ND, Rust ID=ND-1)
// ==============================================================
let id = nd - 1;
let ddm = HALF * (params.dm[id] - params.dm[id - 1]);
let t0 = params.temp[id];
let tm = params.temp[id - 1];
if params.ibc > 0 && params.ibc < 4 && params.idisk == 0 {
// Planck 函数边界条件
let dtm = UN / ((params.abso1[id - 1] + params.abso1[id]) * ddm);
let dtm2 = dtm * dtm;
let fd = TWO * params.fhd[ijt];
let fr = params.freq[params.ij];
let fr15 = fr * 1e-15;
let bnu = BN * fr15 * fr15 * fr15;
let x0 = HK * fr / t0;
let xm = HK * fr / tm;
let pland = bnu / (x0.exp() - UN);
let planm = bnu / (xm.exp() - UN);
let dpldt0 = pland / (UN - (-x0).exp()) * x0 / t0;
let dpldtm = planm / (UN - (-xm).exp()) * xm / tm;
let alf = dtm * ddm;
let bet = (pland - fd * params.rad1[id]) * dtm;
let gam = (params.fak1[id] * params.rad1[id]
- params.fak1[id - 1] * params.rad1[id - 1]
- THIRD * (pland - planm))
* TWO
* dtm2;
let s0 = (params.emis1[id] + params.scat1[id] * params.rad1[id]) / params.abso1[id];
let a1 = alf * (TWO * gam - bet);
let b1 = a1 - s0 / params.abso1[id];
result.ra[id] = TWO * params.fak1[id - 1] * dtm2;
result.rb[id] = -(UN + dtm * (fd + TWO * params.fak1[id] * dtm))
+ params.scat1[id] / params.abso1[id];
result.vr[id] = gam - bet + params.rad1[id] - s0;
result.ua[id] = b1 * params.dabt1[id - 1]
+ (params.demt1[id - 1] + params.dsct1[id - 1] * params.rad1[id - 1])
/ params.abso1[id - 1]
- dpldtm * dtm2 * TWOTHR;
result.ub[id] = b1 * params.dabt1[id]
+ (params.demt1[id] + params.dsct1[id] * params.rad1[id]) / params.abso1[id]
+ dpldt0 * dtm * (UN + TWOTHR * dtm);
// IFRYB > 0 分支Rybicki 特殊边界条件
if params.ifryb > 0 {
let dtm = UN / ((params.abso1[id - 1] + params.abso1[id]) * ddm);
let fr = params.freq[params.ij];
let fr15 = fr * 1e-15;
let bnu = BN * fr15 * fr15 * fr15;
let x0 = HK * fr / t0;
let xm = HK * fr / tm;
let pland = bnu / (x0.exp() - UN);
let planm = bnu / (xm.exp() - UN);
let dpldt0 = pland / (UN - (-x0).exp()) * x0 / t0;
let dpldtm = planm / (UN - (-xm).exp()) * xm / tm;
let gam = (params.fak1[id] * params.rad1[id]
- params.fak1[id - 1] * params.rad1[id - 1]
- THIRD * (pland - planm))
* dtm;
let s0 = (params.emis1[id] + params.scat1[id] * params.rad1[id]) / params.abso1[id];
let bs = HALF / dtm;
let bet = bs * (params.rad1[id] - s0);
let a1 = (gam - bet) * dtm * ddm;
let b1 = a1 - bs * s0 / params.abso1[id];
result.ra[id] = params.fak1[id - 1] * dtm;
result.rb[id] = -params.fak1[id] * dtm
- bs * (UN - params.scat1[id] / params.abso1[id])
- params.fhd[ijt];
result.vr[id] = gam + bet - HALF * pland + params.fhd[ijt] * params.rad1[id];
result.ua[id] = a1 * params.dabt1[id - 1] - dpldtm * dtm * THIRD;
result.ub[id] = b1 * params.dabt1[id]
+ bs * (params.demt1[id] + params.dsct1[id] * params.rad1[id]) / params.abso1[id]
+ (HALF + THIRD * dtm) * dpldt0;
}
} else {
// 磁盘模式I(taumax,-mu,nu) = I(taumax,+mu,nu)
let dzm = params.abso1[id] + params.abso1[id - 1];
let frd = params.fak1[id] * params.rad1[id] - params.fak1[id - 1] * params.rad1[id - 1];
let dtaum = dzm * ddm;
let gam1 = frd / dtaum;
let a2 = 0.0; // AS = 0 in this branch
let a1 = gam1 / dzm;
let bs = dtaum * HALF;
let s0 = (params.emis1[id] + params.scat1[id] * params.rad1[id]) / params.abso1[id];
let gam2 = bs * (params.rad1[id] - s0);
let x1 = gam2 / dzm;
let a1 = a1 - x1;
let b2 = bs / params.abso1[id];
let b1 = a1 - b2 * s0;
result.ra[id] = params.fak1[id - 1] / dtaum - a2 * (UN - params.scat1[id - 1] / params.abso1[id - 1]);
result.rb[id] = -params.fak1[id] / dtaum - bs * (UN - params.scat1[id] / params.abso1[id]);
result.ua[id] = a1 * params.dabt1[id - 1]
+ a2 / params.abso1[id - 1] * (params.demt1[id - 1] + params.dsct1[id - 1] * params.rad1[id - 1]);
result.ub[id] = b1 * params.dabt1[id] + b2 * (params.demt1[id] + params.dsct1[id] * params.rad1[id]);
result.vr[id] = gam1 + gam2;
}
// ==============================================================
// 4. 辐射平衡部分
// ==============================================================
// 4a. 积分方程部分
for id in 0..nd {
if params.reint[id] > 0.0 {
let heat = params.abso1[id] - params.scat1[id];
let dheat = (params.dabt1[id] - params.dsct1[id]) * params.rad1[id];
let wdr = params.w[params.ij] * params.dens[id] * params.reint[id];
result.vb[id] = heat * wdr;
result.wm[id][id] += (dheat - params.demt1[id]) * wdr;
result.wr[id] -= (heat * params.rad1[id] - params.emis1[id]) * wdr;
}
}
// 4b. 微分方程部分 - ID=0 (Fortran ID=1) 特殊处理
let id = 0;
if params.redif[id] > 0.0 {
let wf = params.w[params.ij] * params.fh[ijt] * params.redif[id];
result.vb[id] += wf;
result.wr[id] -= wf * params.rad1[id];
}
// 4c. 微分方程部分 - 主循环
if params.icentr == 0 {
// 一阶中点差分格式
let nd1 = if params.ilbc != 0 { nd - 1 } else { nd };
for id in 1..nd1 {
if params.redif[id] > 0.0 {
let ddm = (params.dm[id] - params.dm[id - 1]) * HALF;
let omeg0 = params.abso1[id];
let omegm = params.abso1[id - 1];
let dtaum = (omeg0 + omegm) * ddm;
let frd = params.fak1[id] * params.rad1[id]
- params.fak1[id - 1] * params.rad1[id - 1];
let gamr = frd / dtaum * params.redif[id];
let a1 = gamr / (omeg0 + omegm);
result.va[id] -= params.w[params.ij] * params.fak1[id - 1] / dtaum * params.redif[id];
result.vb[id] += params.w[params.ij] * params.fak1[id] / dtaum * params.redif[id];
result.wm[id][id - 1] -= a1 * params.w[params.ij] * params.dabt1[id - 1];
result.wm[id][id] -= a1 * params.w[params.ij] * params.dabt1[id];
result.wr[id] -= params.w[params.ij] * gamr;
}
}
// ILBC 边界条件处理
if params.ilbc > 0 {
let id = nd - 1;
if params.redif[id] > 0.0 {
let ddm = (params.dm[id] - params.dm[id - 1]) * HALF;
let dtaum = (params.abso1[id] + params.abso1[id - 1]) * ddm * 3.0;
let fr = params.freq[params.ij];
let fr15 = fr * 1e-15;
let bnu = BN * fr15 * fr15 * fr15;
let x0 = HK * fr / params.temp[id];
let xm = HK * fr / params.temp[id - 1];
let pland = bnu / (x0.exp() - UN);
let planm = bnu / (xm.exp() - UN);
let dpldt0 = pland / (UN - (-x0).exp()) * x0 / params.temp[id];
let dpldtm = planm / (UN - (-xm).exp()) * xm / params.temp[id - 1];
let flx = (pland - planm) / dtaum * params.redif[id];
let a1 = flx / (params.abso1[id] + params.abso1[id - 1]);
result.wm[id][id - 1] += params.w[params.ij] * (dpldtm - a1 * params.dabt1[id - 1]);
result.wm[id][id] += params.w[params.ij] * (dpldt0 - a1 * params.dabt1[id]);
result.wr[id] -= params.w[params.ij] * flx;
}
}
} else {
// 中心差分格式 (ICENTR != 0)
for id in 1..nd - 1 {
if params.redif[id] > 0.0 {
let wwr = params.w[params.ij] * params.redif[id];
let ddm = HALF * (params.dm[id] - params.dm[id - 1]);
let ddp = HALF * (params.dm[id + 1] - params.dm[id]);
let dtm = (params.abso1[id - 1] + params.abso1[id]) * ddm;
let dtp = (params.abso1[id + 1] + params.abso1[id]) * ddp;
let dt0 = dtm + dtp;
let frm = params.fak1[id] * params.rad1[id]
- params.fak1[id - 1] * params.rad1[id - 1];
let frp = params.fak1[id + 1] * params.rad1[id + 1]
- params.fak1[id] * params.rad1[id];
let alp = dtp / dtm / dt0;
let gam = dtm / dtp / dt0;
let flx = alp * frm + gam * frp;
// 矩阵元素
result.va[id] -= wwr * params.fak1[id - 1] * alp;
result.vb[id] += wwr * params.fak1[id] * (alp - gam);
result.vc[id] = wwr * params.fak1[id + 1] * gam;
let dmtm = ddm / (dtm * dtm);
let dmtp = ddp / (dtp * dtp);
let rm = dtm / dt0;
let rp = dtp / dt0;
result.wm[id][id - 1] +=
wwr * params.dabt1[id - 1] * dmtm * (rm * rm * frp - (UN + rm) * frm);
result.wm[id][id] += wwr
* params.dabt1[id]
* ((dmtp * rp * rp - dmtm * rp * (UN + rm)) * frm
+ (dmtm * rm * rm - dmtp * rm * (UN + rp)) * frp);
result.wr[id] -= wwr * flx;
}
}
// 下边界 (ID=ND-1)
let id = nd - 1;
if params.redif[id] > 0.0 {
let ddm = HALF * (params.dm[id] - params.dm[id - 1]);
let dtaum = (params.abso1[id] + params.abso1[id - 1]) * ddm;
let frd = params.fak1[id] * params.rad1[id]
- params.fak1[id - 1] * params.rad1[id - 1];
let flx = frd / dtaum * params.redif[id];
let a1 = flx / (params.abso1[id] + params.abso1[id - 1]);
result.va[id] -= params.w[params.ij] * params.fak1[id - 1] / dtaum * params.redif[id];
result.vb[id] += params.w[params.ij] * params.fak1[id] / dtaum * params.redif[id];
result.wm[id][id - 1] -= a1 * params.w[params.ij] * params.dabt1[id - 1];
result.wm[id][id] -= a1 * params.w[params.ij] * params.dabt1[id];
result.wr[id] -= params.w[params.ij] * flx;
}
}
// 4d. 辐射平衡温度点修正
if params.nretc > 0 {
for id in 0..params.nretc {
result.wr[id] = 0.0;
result.vb[id] = 0.0;
result.va[id] = 0.0;
result.wm[id][id] = 1.0;
if id > 0 {
result.wm[id][id - 1] = 0.0;
}
}
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rybmat_result_default() {
let result = RybmatResult::default();
assert_eq!(result.ra.len(), MDEPTH);
assert_eq!(result.rb.len(), MDEPTH);
assert_eq!(result.wm.len(), MDEPTH);
}
#[test]
fn test_rybmat_basic() {
let dm: Vec<f64> = (0..MDEPTH).map(|i| 0.01 + i as f64 * 0.01).collect();
let temp = vec![10000.0; MDEPTH];
let dens = vec![1e15; MDEPTH];
let abso1 = vec![1e-8; MDEPTH];
let scat1 = vec![1e-9; MDEPTH];
let emis1 = vec![1e-10; MDEPTH];
let fak1 = vec![0.5; MDEPTH];
let rad1 = vec![1e10; MDEPTH];
let dabt1 = vec![1e-12; MDEPTH];
let demt1 = vec![1e-14; MDEPTH];
let deldm = vec![0.001; MDEPTH];
let reint = vec![0.0; MDEPTH];
let redif = vec![0.0; MDEPTH];
let w = vec![0.01; 100];
let fh = vec![0.5; 100];
let fhd = vec![0.5; 100];
let freq = vec![1e15; 100];
let dsct1 = vec![1e-11; MDEPTH];
let dscn1 = vec![1e-16; MDEPTH];
let ijfr: Vec<i32> = (0..100).map(|i| (i + 1) as i32).collect();
let extrad = vec![0.0; 100];
let q0 = vec![0.0; 100];
let uu0 = vec![0.0; 100];
let params = RybmatParams {
ij: 0,
nd: 10,
dm: &dm,
temp: &temp,
dens: &dens,
abso1: &abso1,
scat1: &scat1,
emis1: &emis1,
fak1: &fak1,
rad1: &rad1,
dabt1: &dabt1,
demt1: &demt1,
deldm: &deldm,
isplin: 1,
iubc: 0,
ibc: 1,
idisk: 0,
ifryb: 0,
reint: &reint,
redif: &redif,
w: &w,
freq: &freq,
fh: &fh,
fhd: &fhd,
icentr: 0,
ilbc: 0,
nretc: 0,
dsct1: &dsct1,
dscn1: &dscn1,
ijfr: &ijfr,
extrad: &extrad,
q0: &q0,
uu0: &uu0,
};
let result = rybmat(&params);
assert!(result.rb[0].is_finite());
assert!(result.vr[0].is_finite());
}
#[test]
fn test_rybmat_with_extrad() {
let dm: Vec<f64> = (0..MDEPTH).map(|i| 0.01 + i as f64 * 0.01).collect();
let temp = vec![10000.0; MDEPTH];
let dens = vec![1e15; MDEPTH];
let abso1 = vec![1e-8; MDEPTH];
let scat1 = vec![1e-9; MDEPTH];
let emis1 = vec![1e-10; MDEPTH];
let fak1 = vec![0.5; MDEPTH];
let rad1 = vec![1e10; MDEPTH];
let dabt1 = vec![1e-12; MDEPTH];
let demt1 = vec![1e-14; MDEPTH];
let deldm = vec![0.001; MDEPTH];
let reint = vec![0.0; MDEPTH];
let redif = vec![0.0; MDEPTH];
let w = vec![0.01; 100];
let fh = vec![0.5; 100];
let fhd = vec![0.5; 100];
let freq = vec![1e15; 100];
let dsct1 = vec![1e-11; MDEPTH];
let dscn1 = vec![1e-16; MDEPTH];
let ijfr: Vec<i32> = (0..100).map(|i| (i + 1) as i32).collect();
let extrad: Vec<f64> = (0..100).map(|i| 1e10 * (i as f64 + 1.0)).collect();
let q0 = vec![0.0; 100];
let uu0 = vec![0.0; 100];
let params = RybmatParams {
ij: 0,
nd: 10,
dm: &dm,
temp: &temp,
dens: &dens,
abso1: &abso1,
scat1: &scat1,
emis1: &emis1,
fak1: &fak1,
rad1: &rad1,
dabt1: &dabt1,
demt1: &demt1,
deldm: &deldm,
isplin: 1,
iubc: 0,
ibc: 1,
idisk: 0,
ifryb: 0,
reint: &reint,
redif: &redif,
w: &w,
freq: &freq,
fh: &fh,
fhd: &fhd,
icentr: 0,
ilbc: 0,
nretc: 0,
dsct1: &dsct1,
dscn1: &dscn1,
ijfr: &ijfr,
extrad: &extrad,
q0: &q0,
uu0: &uu0,
};
let result = rybmat(&params);
assert!(result.rb[0].is_finite());
assert!(result.vr[0].is_finite());
}
#[test]
fn test_rybmat_hermitian() {
let dm: Vec<f64> = (0..MDEPTH).map(|i| 0.01 + i as f64 * 0.01).collect();
let temp = vec![10000.0; MDEPTH];
let dens = vec![1e15; MDEPTH];
let abso1 = vec![1e-8; MDEPTH];
let scat1 = vec![1e-9; MDEPTH];
let emis1 = vec![1e-10; MDEPTH];
let fak1 = vec![0.5; MDEPTH];
let rad1 = vec![1e10; MDEPTH];
let dabt1 = vec![1e-12; MDEPTH];
let demt1 = vec![1e-14; MDEPTH];
let deldm = vec![0.001; MDEPTH];
let reint = vec![0.0; MDEPTH];
let redif = vec![0.0; MDEPTH];
let w = vec![0.01; 100];
let fh = vec![0.5; 100];
let fhd = vec![0.5; 100];
let freq = vec![1e15; 100];
let dsct1 = vec![1e-11; MDEPTH];
let dscn1 = vec![1e-16; MDEPTH];
let ijfr: Vec<i32> = (0..100).map(|i| (i + 1) as i32).collect();
let extrad = vec![0.0; 100];
let q0 = vec![0.0; 100];
let uu0 = vec![0.0; 100];
let params = RybmatParams {
ij: 0,
nd: 10,
dm: &dm,
temp: &temp,
dens: &dens,
abso1: &abso1,
scat1: &scat1,
emis1: &emis1,
fak1: &fak1,
rad1: &rad1,
dabt1: &dabt1,
demt1: &demt1,
deldm: &deldm,
isplin: 2, // Hermitian method
iubc: 0,
ibc: 1,
idisk: 0,
ifryb: 0,
reint: &reint,
redif: &redif,
w: &w,
freq: &freq,
fh: &fh,
fhd: &fhd,
icentr: 0,
ilbc: 0,
nretc: 0,
dsct1: &dsct1,
dscn1: &dscn1,
ijfr: &ijfr,
extrad: &extrad,
q0: &q0,
uu0: &uu0,
};
let result = rybmat(&params);
assert!(result.rb[0].is_finite());
assert!(result.vr[0].is_finite());
}
#[test]
fn test_rybmat_centered_difference() {
let dm: Vec<f64> = (0..MDEPTH).map(|i| 0.01 + i as f64 * 0.01).collect();
let temp = vec![10000.0; MDEPTH];
let dens = vec![1e15; MDEPTH];
let abso1 = vec![1e-8; MDEPTH];
let scat1 = vec![1e-9; MDEPTH];
let emis1 = vec![1e-10; MDEPTH];
let fak1 = vec![0.5; MDEPTH];
let rad1 = vec![1e10; MDEPTH];
let dabt1 = vec![1e-12; MDEPTH];
let demt1 = vec![1e-14; MDEPTH];
let deldm = vec![0.001; MDEPTH];
let reint = vec![0.0; MDEPTH];
let redif: Vec<f64> = (0..MDEPTH).map(|_| 0.1).collect();
let w = vec![0.01; 100];
let fh = vec![0.5; 100];
let fhd = vec![0.5; 100];
let freq = vec![1e15; 100];
let dsct1 = vec![1e-11; MDEPTH];
let dscn1 = vec![1e-16; MDEPTH];
let ijfr: Vec<i32> = (0..100).map(|i| (i + 1) as i32).collect();
let extrad = vec![0.0; 100];
let q0 = vec![0.0; 100];
let uu0 = vec![0.0; 100];
let params = RybmatParams {
ij: 0,
nd: 10,
dm: &dm,
temp: &temp,
dens: &dens,
abso1: &abso1,
scat1: &scat1,
emis1: &emis1,
fak1: &fak1,
rad1: &rad1,
dabt1: &dabt1,
demt1: &demt1,
deldm: &deldm,
isplin: 1,
iubc: 0,
ibc: 1,
idisk: 0,
ifryb: 0,
reint: &reint,
redif: &redif,
w: &w,
freq: &freq,
fh: &fh,
fhd: &fhd,
icentr: 1, // 使用中心差分
ilbc: 0,
nretc: 0,
dsct1: &dsct1,
dscn1: &dscn1,
ijfr: &ijfr,
extrad: &extrad,
q0: &q0,
uu0: &uu0,
};
let result = rybmat(&params);
assert!(result.rb[0].is_finite());
assert!(result.vr[0].is_finite());
assert!(result.vc[5].is_finite());
}
#[test]
fn test_rybmat_ifryb_branch() {
let dm: Vec<f64> = (0..MDEPTH).map(|i| 0.01 + i as f64 * 0.01).collect();
let temp = vec![10000.0; MDEPTH];
let dens = vec![1e15; MDEPTH];
let abso1 = vec![1e-8; MDEPTH];
let scat1 = vec![1e-9; MDEPTH];
let emis1 = vec![1e-10; MDEPTH];
let fak1 = vec![0.5; MDEPTH];
let rad1 = vec![1e10; MDEPTH];
let dabt1 = vec![1e-12; MDEPTH];
let demt1 = vec![1e-14; MDEPTH];
let deldm = vec![0.001; MDEPTH];
let reint = vec![0.0; MDEPTH];
let redif = vec![0.0; MDEPTH];
let w = vec![0.01; 100];
let fh = vec![0.5; 100];
let fhd = vec![0.5; 100];
let freq = vec![1e15; 100];
let dsct1 = vec![1e-11; MDEPTH];
let dscn1 = vec![1e-16; MDEPTH];
let ijfr: Vec<i32> = (0..100).map(|i| (i + 1) as i32).collect();
let extrad = vec![0.0; 100];
let q0 = vec![0.0; 100];
let uu0 = vec![0.0; 100];
let params = RybmatParams {
ij: 0,
nd: 10,
dm: &dm,
temp: &temp,
dens: &dens,
abso1: &abso1,
scat1: &scat1,
emis1: &emis1,
fak1: &fak1,
rad1: &rad1,
dabt1: &dabt1,
demt1: &demt1,
deldm: &deldm,
isplin: 1,
iubc: 0,
ibc: 1,
idisk: 0,
ifryb: 1, // 启用 Rybicki 特殊边界条件
reint: &reint,
redif: &redif,
w: &w,
freq: &freq,
fh: &fh,
fhd: &fhd,
icentr: 0,
ilbc: 0,
nretc: 0,
dsct1: &dsct1,
dscn1: &dscn1,
ijfr: &ijfr,
extrad: &extrad,
q0: &q0,
uu0: &uu0,
};
let result = rybmat(&params);
assert!(result.rb[0].is_finite());
assert!(result.vr[0].is_finite());
}
#[test]
fn test_rybmat_disk_mode() {
let dm: Vec<f64> = (0..MDEPTH).map(|i| 0.01 + i as f64 * 0.01).collect();
let temp = vec![10000.0; MDEPTH];
let dens = vec![1e15; MDEPTH];
let abso1 = vec![1e-8; MDEPTH];
let scat1 = vec![1e-9; MDEPTH];
let emis1 = vec![1e-10; MDEPTH];
let fak1 = vec![0.5; MDEPTH];
let rad1 = vec![1e10; MDEPTH];
let dabt1 = vec![1e-12; MDEPTH];
let demt1 = vec![1e-14; MDEPTH];
let deldm = vec![0.001; MDEPTH];
let reint = vec![0.0; MDEPTH];
let redif = vec![0.0; MDEPTH];
let w = vec![0.01; 100];
let fh = vec![0.5; 100];
let fhd = vec![0.5; 100];
let freq = vec![1e15; 100];
let dsct1 = vec![1e-11; MDEPTH];
let dscn1 = vec![1e-16; MDEPTH];
let ijfr: Vec<i32> = (0..100).map(|i| (i + 1) as i32).collect();
let extrad = vec![0.0; 100];
let q0 = vec![0.0; 100];
let uu0 = vec![0.0; 100];
let params = RybmatParams {
ij: 0,
nd: 10,
dm: &dm,
temp: &temp,
dens: &dens,
abso1: &abso1,
scat1: &scat1,
emis1: &emis1,
fak1: &fak1,
rad1: &rad1,
dabt1: &dabt1,
demt1: &demt1,
deldm: &deldm,
isplin: 1,
iubc: 0,
ibc: 4, // 磁盘模式
idisk: 1,
ifryb: 0,
reint: &reint,
redif: &redif,
w: &w,
freq: &freq,
fh: &fh,
fhd: &fhd,
icentr: 0,
ilbc: 0,
nretc: 0,
dsct1: &dsct1,
dscn1: &dscn1,
ijfr: &ijfr,
extrad: &extrad,
q0: &q0,
uu0: &uu0,
};
let result = rybmat(&params);
assert!(result.rb[0].is_finite());
assert!(result.vr[0].is_finite());
}
#[test]
fn test_rybmat_radiative_equilibrium() {
let dm: Vec<f64> = (0..MDEPTH).map(|i| 0.01 + i as f64 * 0.01).collect();
let temp = vec![10000.0; MDEPTH];
let dens = vec![1e15; MDEPTH];
let abso1 = vec![1e-8; MDEPTH];
let scat1 = vec![1e-9; MDEPTH];
let emis1 = vec![1e-10; MDEPTH];
let fak1 = vec![0.5; MDEPTH];
let rad1 = vec![1e10; MDEPTH];
let dabt1 = vec![1e-12; MDEPTH];
let demt1 = vec![1e-14; MDEPTH];
let deldm = vec![0.001; MDEPTH];
let reint: Vec<f64> = (0..MDEPTH).map(|_| 0.1).collect();
let redif: Vec<f64> = (0..MDEPTH).map(|_| 0.1).collect();
let w = vec![0.01; 100];
let fh = vec![0.5; 100];
let fhd = vec![0.5; 100];
let freq = vec![1e15; 100];
let dsct1 = vec![1e-11; MDEPTH];
let dscn1 = vec![1e-16; MDEPTH];
let ijfr: Vec<i32> = (0..100).map(|i| (i + 1) as i32).collect();
let extrad = vec![0.0; 100];
let q0 = vec![0.0; 100];
let uu0 = vec![0.0; 100];
let params = RybmatParams {
ij: 0,
nd: 10,
dm: &dm,
temp: &temp,
dens: &dens,
abso1: &abso1,
scat1: &scat1,
emis1: &emis1,
fak1: &fak1,
rad1: &rad1,
dabt1: &dabt1,
demt1: &demt1,
deldm: &deldm,
isplin: 1,
iubc: 0,
ibc: 1,
idisk: 0,
ifryb: 0,
reint: &reint,
redif: &redif,
w: &w,
freq: &freq,
fh: &fh,
fhd: &fhd,
icentr: 0,
ilbc: 0,
nretc: 0,
dsct1: &dsct1,
dscn1: &dscn1,
ijfr: &ijfr,
extrad: &extrad,
q0: &q0,
uu0: &uu0,
};
let result = rybmat(&params);
// 检查辐射平衡相关输出
assert!(result.vb[0].is_finite());
assert!(result.wr[0].is_finite());
assert!(result.va[5].is_finite());
}
}

136
src/math/setdrt.rs Normal file
View File

@ -0,0 +1,136 @@
//! 计算密度对温度的导数。
//!
//! 重构自 TLUSTY `setdrt.f`
use crate::state::constants::MDEPTH;
use crate::state::model::{ModPar, PressR, Rhoder};
/// 计算密度对温度的导数。
///
/// 使用有限差分法计算每个深度点的密度对温度的导数。
///
/// # 参数
///
/// * `modpar` - 模型参数(包含温度)
/// * `pressr` - 压力相关数组(包含总压力)
/// * `rhoder` - 输出:密度对温度的导数
/// * `nd` - 深度点数
/// * `rhoeos_fn` - 状态方程函数,计算给定温度和压力下的密度
///
/// # 示例
///
/// ```
/// use tlusty_rust::state::model::{ModPar, PressR, Rhoder};
/// use tlusty_rust::math::setdrt::setdrt;
///
/// let mut modpar = ModPar::default();
/// let mut pressr = PressR::default();
/// let mut rhoder = Rhoder::default();
/// modpar.temp[0] = 10000.0;
/// modpar.temp[1] = 9500.0;
/// pressr.ptotal[0] = 1e5;
/// pressr.ptotal[1] = 1.1e5;
///
/// // 简单的状态方程函数
/// fn simple_rhoeos(t: f64, p: f64) -> f64 {
/// p / (1.380649e-16 * t) * 1.67333e-24 / 2.3
/// }
///
/// setdrt(&modpar, &pressr, &mut rhoder, 2, simple_rhoeos);
/// ```
pub fn setdrt<F>(
modpar: &ModPar,
pressr: &PressR,
rhoder: &mut Rhoder,
nd: usize,
rhoeos_fn: F,
) where
F: Fn(f64, f64) -> f64,
{
const DDTMIN: f64 = 0.0;
const DDTPLU: f64 = 0.001;
for id in 0..nd {
let t = modpar.temp[id];
let p = pressr.ptotal[id];
let rho1 = rhoeos_fn(t * (1.0 - DDTMIN), p);
let rho2 = rhoeos_fn(t * (1.0 + DDTPLU), p);
rhoder.drhodt[id] = (rho2 - rho1) / t / (DDTMIN + DDTPLU);
}
}
#[cfg(test)]
mod tests {
use super::*;
/// 简单的理想气体状态方程
fn ideal_gas_rhoeos(t: f64, p: f64) -> f64 {
// P = n k T => n = P / (k T)
// rho = n * m = P / (k T) * m
// 使用平均分子质量 ~2.3 * m_H
let wmol0 = 1.67333e-24 / 2.3;
p / (1.380649e-16 * t) * wmol0
}
#[test]
fn test_setdrt_basic() {
let mut modpar = ModPar::default();
let mut pressr = PressR::default();
let mut rhoder = Rhoder::default();
// 设置两个深度点
modpar.temp[0] = 10000.0;
modpar.temp[1] = 9500.0;
pressr.ptotal[0] = 1.0e5;
pressr.ptotal[1] = 1.1e5;
setdrt(&modpar, &pressr, &mut rhoder, 2, ideal_gas_rhoeos);
// 检查导数不为零
assert!(rhoder.drhodt[0].abs() > 0.0);
assert!(rhoder.drhodt[1].abs() > 0.0);
}
#[test]
fn test_setdrt_analytical() {
let mut modpar = ModPar::default();
let mut pressr = PressR::default();
let mut rhoder = Rhoder::default();
modpar.temp[0] = 10000.0;
pressr.ptotal[0] = 1.0e5;
setdrt(&modpar, &pressr, &mut rhoder, 1, ideal_gas_rhoeos);
// 对于理想气体: rho = P * const / T
// drho/dT = -P * const / T^2
let wmol0 = 1.67333e-24 / 2.3;
let bolk = 1.380649e-16;
let expected = -pressr.ptotal[0] * wmol0 / (bolk * modpar.temp[0] * modpar.temp[0]);
// 有限差分结果应该接近解析解
assert!((rhoder.drhodt[0] - expected).abs() < 1e-12 * expected.abs().max(1.0));
}
#[test]
fn test_setdrt_multiple_points() {
let mut modpar = ModPar::default();
let mut pressr = PressR::default();
let mut rhoder = Rhoder::default();
// 设置5个深度点
for i in 0..5 {
modpar.temp[i] = 10000.0 - i as f64 * 500.0;
pressr.ptotal[i] = 1.0e5 * (1.0 + i as f64 * 0.1);
}
setdrt(&modpar, &pressr, &mut rhoder, 5, ideal_gas_rhoeos);
// 所有导数应该为负值(温度升高,密度降低)
for i in 0..5 {
assert!(rhoder.drhodt[i] < 0.0, "drhodt[{}] should be negative", i);
}
}
}

274
src/math/tabint.rs Normal file
View File

@ -0,0 +1,274 @@
//! 频率表插值。
//!
//! 重构自 TLUSTY `tabint.f`
use crate::state::constants::{MFREQ, MFREQC, MFRTAB, MTABR, MTABT};
/// 频率表插值系数。
/// 对应 COMMON /intcff/
#[derive(Debug, Clone, Default)]
pub struct IntCff {
/// 插值系数 yint = 1/log10(frtab(j+1)/frtab(j))
pub yint: Vec<f64>,
/// 插值下标索引 jint
pub jint: Vec<i32>,
}
impl IntCff {
pub fn new() -> Self {
Self {
yint: vec![0.0; MFREQ],
jint: vec![0; MFREQ],
}
}
}
/// 不透明度表数据。
/// 包含温度、密度、频率网格和不透明度值。
#[derive(Debug, Clone)]
pub struct OpacTable {
/// 频率表 (Hz)
pub frtab: Vec<f64>,
/// 温度向量 (ln T)
pub tempvec: Vec<f64>,
/// 密度矩阵 (ln rho) [温度索引][密度索引]
pub rhomat: Vec<Vec<f64>>,
/// 不透明度表 (ln kappa) [温度索引][密度索引][频率索引]
pub absopac: Vec<Vec<Vec<f64>>>,
/// 每个温度点的密度数
pub numrh: Vec<i32>,
/// 频率数
pub numfreq: usize,
/// 温度数
pub numtemp: usize,
/// 最大频率值
pub frtabm: f64,
}
impl Default for OpacTable {
fn default() -> Self {
Self {
frtab: vec![0.0; MFRTAB],
tempvec: vec![0.0; MTABT],
rhomat: vec![vec![0.0; MTABR]; MTABT],
absopac: vec![vec![vec![0.0; MFRTAB]; MTABR]; MTABT],
numrh: vec![0; MTABT],
numfreq: 0,
numtemp: 0,
frtabm: 0.0,
}
}
}
/// TABINT 输入参数。
pub struct TabintParams<'a> {
/// 控制参数IOPTAB < 0 表示使用精确表频率
pub ioptab: i32,
/// 频率设置标志
pub ifrset: i32,
/// 当前频率数
pub nfreq: usize,
/// 连续频率数
pub nfreqc: usize,
/// 频率数组
pub freq: &'a mut [f64],
/// 频率映射索引
pub ijfr: &'a mut [i32],
/// 反向映射索引
pub jik: &'a mut [i32],
/// ALI 标志
pub ijali: &'a mut [i32],
/// 权重数组
pub w: &'a mut [f64],
}
/// 频率表插值。
///
/// 设置频率插值系数或直接使用表频率。
///
/// # 参数
///
/// * `params` - 输入/输出参数
/// * `opac_table` - 不透明度表数据(可变,会被插值修改)
/// * `intcff` - 输出:插值系数
pub fn tabint(params: &mut TabintParams, opac_table: &mut OpacTable, intcff: &mut IntCff) {
let numfreq = opac_table.numfreq;
let fr1 = opac_table.frtab[0];
let fr2 = opac_table.frtab[numfreq - 1];
if params.ioptab < 0 && params.ifrset == 0 {
// 频率精确等于表值
params.nfreq = numfreq;
params.nfreqc = numfreq;
for ij in 0..numfreq {
params.freq[ij] = opac_table.frtab[ij];
params.ijfr[ij] = ij as i32 + 1; // Fortran 1-indexed
params.jik[ij] = ij as i32 + 1;
params.ijali[ij] = 1;
}
// 设置权重
if numfreq > 0 {
params.w[0] = 0.5 * (opac_table.frtab[0] - opac_table.frtab[1]);
params.w[numfreq - 1] = 0.5 * (opac_table.frtab[numfreq - 2] - opac_table.frtab[numfreq - 1]);
}
for ij in 1..numfreq - 1 {
params.w[ij] = 0.5 * (opac_table.frtab[ij - 1] - opac_table.frtab[ij + 1]);
}
} else {
// 频率已设置,建立插值系数
// 使用二分法查找插值位置
for ij in 0..params.nfreq {
let xint = params.freq[ij];
let (jl, _ju) = locate_interval(&opac_table.frtab, numfreq, xint, fr1, fr2);
let mut j = jl;
if j == numfreq as i32 - 1 {
j -= 1;
}
if j == 0 {
j += 1;
}
let j_idx = j as usize;
intcff.jint[ij] = j;
intcff.yint[ij] = 1.0 / (opac_table.frtab[j_idx + 1] / opac_table.frtab[j_idx]).log10();
}
// 插值不透明度表
interpolate_opacity(params, opac_table, intcff, fr1, fr2);
}
}
/// 二分法查找区间。
///
/// 返回 (jl, ju),其中 xint 在 [frtab[jl], frtab[ju]] 之间
fn locate_interval(
frtab: &[f64],
numfreq: usize,
xint: f64,
fr1: f64,
fr2: f64,
) -> (i32, i32) {
let mut jl: i32 = 0;
let mut ju: i32 = numfreq as i32;
let ascending = fr2 > fr1;
while ju - jl > 1 {
let jm = (ju + jl) / 2;
let jm_idx = jm as usize;
if (ascending && xint > frtab[jm_idx]) || (!ascending && xint < frtab[jm_idx]) {
jl = jm;
} else {
ju = jm;
}
}
(jl, ju)
}
/// 对不透明度表进行插值。
///
/// 修改 `opac_table.absopac` 数组,将原始表频率的不透明度插值到目标频率网格。
fn interpolate_opacity(
params: &mut TabintParams,
opac_table: &mut OpacTable,
intcff: &IntCff,
fr1: f64,
fr2: f64,
) {
let numtemp = opac_table.numtemp;
let numfreq = opac_table.numfreq;
let frtab = opac_table.frtab.clone();
// 创建临时数组存储原始不透明度
let mut absort = vec![0.0f64; numfreq];
for it in 0..numtemp {
let numrho = opac_table.numrh[it] as usize;
for ir in 0..numrho {
// 复制原始数据到临时数组
for k in 0..numfreq {
absort[k] = opac_table.absopac[it][ir][k];
}
// 对每个频率进行插值,结果存回 absopac
for ij in 0..params.nfreq {
let j = intcff.jint[ij] as usize;
// Fortran: rc=(absort(j+1)-absort(j))*yint(ij)
// Fortran jint 是 1-indexedj+1 在 Rust 中是 j
let rc = (absort[j] - absort[j - 1]) * intcff.yint[ij];
// Fortran: opac=rc*log10(freq(ij)/frtab(j))+absort(j)
let opac = rc * (params.freq[ij] / frtab[j - 1]).log10() + absort[j - 1];
opac_table.absopac[it][ir][ij] = opac;
}
}
}
// 重置超出频率范围的不透明度
for ij in 0..params.nfreq {
if params.freq[ij] < fr2 * 0.99 || params.freq[ij] > fr1 * 1.01 {
// 超出范围,设置为零
for it in 0..numtemp {
let numrho = opac_table.numrh[it] as usize;
for ir in 0..numrho {
opac_table.absopac[it][ir][ij] = 0.0;
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_locate_interval_ascending() {
let frtab = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let numfreq = 5;
let fr1 = frtab[0];
let fr2 = frtab[numfreq - 1];
// 测试中间值
let (jl, _ju) = locate_interval(&frtab, numfreq, 2.5, fr1, fr2);
assert_eq!(jl, 1);
let (jl, _ju) = locate_interval(&frtab, numfreq, 3.5, fr1, fr2);
assert_eq!(jl, 2);
}
#[test]
fn test_locate_interval_descending() {
let frtab = vec![5.0, 4.0, 3.0, 2.0, 1.0];
let numfreq = 5;
let fr1 = frtab[0];
let fr2 = frtab[numfreq - 1];
// 降序数组
let (jl, _ju) = locate_interval(&frtab, numfreq, 2.5, fr1, fr2);
assert_eq!(jl, 2);
}
#[test]
fn test_intcff_creation() {
let intcff = IntCff::new();
assert_eq!(intcff.yint.len(), MFREQ);
assert_eq!(intcff.jint.len(), MFREQ);
}
#[test]
fn test_opac_table_default() {
let table = OpacTable::default();
assert_eq!(table.frtab.len(), MFRTAB);
assert_eq!(table.tempvec.len(), MTABT);
assert_eq!(table.rhomat.len(), MTABT);
assert_eq!(table.numfreq, 0);
assert_eq!(table.numtemp, 0);
}
}

333
src/math/taufr1.rs Normal file
View File

@ -0,0 +1,333 @@
//! 计算参考光学深度处的各种物理量。
//!
//! 重构自 TLUSTY `taufr1.f`
use crate::state::constants::{HK, MDEPTH, MFREQ};
/// TAUFR1 输入参数结构体。
pub struct Taufr1Params<'a> {
/// 频率索引 (0-indexed)
pub ij: usize,
/// 深度点数
pub nd: usize,
/// 频率数组
pub freq: &'a [f64],
/// 深度数组 (柱质量密度)
pub dm: &'a [f64],
/// 温度数组
pub temp: &'a [f64],
/// 吸收系数数组 (包含散射)
pub abso1: &'a [f64],
/// 散射系数数组
pub scat1: &'a [f64],
/// 发射系数数组
pub emis1: &'a [f64],
/// 总吸收系数 (用于光学深度计算)
pub absot: &'a [f64],
/// 密度倒数 (1/dens)
pub dens1: &'a [f64],
/// 深度增量 (柱质量)
pub deldm: &'a [f64],
/// 深度增量 (区域)
pub deldmz: &'a [f64],
/// 第一个深度点的增量
pub dedm1: f64,
/// 频率索引映射
pub kij: &'a [i32],
/// 总频率数
pub nfreq: usize,
/// 辐射强度矩阵 [频率][深度]
pub rad: &'a [Vec<f64>],
}
/// TAUFR1 输出结构体。
#[derive(Debug, Clone)]
pub struct Taufr1Result {
/// 参考深度 (柱质量)
pub dmref: f64,
/// 参考温度
pub tref: f64,
/// 参考吸收系数
pub abref: f64,
/// 参考散射系数
pub scref: f64,
/// 参考源函数
pub stref: f64,
/// 参考光学深度
pub tauef: f64,
/// 参考发射率参数
pub epref: f64,
/// 参考Planck函数
pub bref: f64,
/// Y参考值
pub yref: f64,
/// 波长 (Å)
pub alm: f64,
/// 辐射强度
pub r1: f64,
/// Rosseland平均相关
pub rb1: f64,
/// 源函数相关
pub rs1: f64,
/// 光学深度增量数组
pub dt: Vec<f64>,
/// 参考深度索引
pub iref: usize,
}
/// 计算参考光学深度处的各种物理量。
///
/// 这个函数计算在给定频率处的光学深度分布,并找到参考光学深度(τ=1处的
/// 各种插值物理量。
///
/// # 参数
///
/// * `params` - 输入参数结构体
///
/// # 返回值
///
/// 包含各种参考量的 Taufr1Result 结构体
pub fn taufr1(params: &Taufr1Params) -> Taufr1Result {
const TAUREF: f64 = 1.0;
const YCON: f64 = 1.68638e-10;
const CAS: f64 = 2.997925e18; // 光速 Å/s
let nd = params.nd;
let ij = params.ij;
let fr = params.freq[ij];
// 局部数组
let mut st0 = vec![0.0f64; MDEPTH];
let mut ss0 = vec![0.0f64; MDEPTH];
let mut ab0 = vec![0.0f64; MDEPTH];
let mut tau = vec![0.0f64; MDEPTH];
let mut taus = vec![0.0f64; MDEPTH];
let mut dt = vec![0.0f64; MDEPTH];
// 计算源函数和散射参数
for id in 0..nd {
ab0[id] = params.abso1[id];
// 避免除零
let heat = ab0[id] - params.scat1[id];
if heat > 1e-100 {
st0[id] = params.emis1[id] / heat;
} else {
st0[id] = 0.0;
}
// 设置最小值避免零源函数
if st0[id] == 0.0 {
st0[id] = 1e-20 * params.scat1[id];
}
ss0[id] = -params.scat1[id] / ab0[id];
}
// 计算边界处的光学深度
tau[0] = params.abso1[0] * params.dedm1;
// 避免负的平方根
let eps0 = (ab0[0] * (ab0[0] - params.scat1[0]).max(0.0)).sqrt();
taus[0] = 3.0f64.sqrt() * eps0 * params.dedm1;
let mut iref = 1;
let mut irefs = 1;
// 计算光学深度分布
for id in 0..nd - 1 {
dt[id] = params.deldmz[id] * (params.absot[id + 1] + params.absot[id]);
tau[id + 1] = tau[id] + dt[id];
// 避免负的平方根
let eps0 = (ab0[id] * (ab0[id] - params.scat1[id]).max(0.0)).sqrt();
let eps1 = (ab0[id + 1] * (ab0[id + 1] - params.scat1[id + 1]).max(0.0)).sqrt();
let dts = params.deldm[id] * (eps0 * params.dens1[id] + eps1 * params.dens1[id + 1]) * 3.0f64.sqrt();
taus[id + 1] = taus[id] + dts;
// 找到参考光学深度位置
if tau[id] <= TAUREF && tau[id + 1] > TAUREF {
iref = id + 1;
}
if taus[id] <= TAUREF && taus[id + 1] > TAUREF {
irefs = id + 1;
}
}
// 处理边界情况
if iref == 1 && tau[nd - 1] <= TAUREF {
iref = nd;
}
if irefs == 1 && taus[nd - 1] <= TAUREF {
irefs = nd;
}
// 使用散射光学深度作为参考
let iref_use = irefs;
// 在参考光学深度处进行对数插值
let (dmref, tref, abref, scref, stref, tauef) = if iref_use < nd {
let t0 = (taus[iref_use] / taus[iref_use - 1]).ln();
let x0 = (taus[iref_use] / TAUREF).ln() / t0;
let x1 = (TAUREF / taus[iref_use - 1]).ln() / t0;
let dmref = (params.dm[iref_use - 1].ln() * x0 + params.dm[iref_use].ln() * x1).exp();
let tref = (params.temp[iref_use - 1].ln() * x0 + params.temp[iref_use].ln() * x1).exp();
let abref = (ab0[iref_use - 1].ln() * x0 + ab0[iref_use].ln() * x1).exp();
let scref = (params.scat1[iref_use - 1].ln() * x0 + params.scat1[iref_use].ln() * x1).exp();
let stref = (st0[iref_use - 1].ln() * x0 + st0[iref_use].ln() * x1).exp();
let tauef = (tau[iref_use - 1].ln() * x0 + tau[iref_use].ln() * x1).exp();
(dmref, tref, abref, scref, stref, tauef)
} else {
(
params.dm[nd - 1],
params.temp[nd - 1],
ab0[nd - 1],
params.scat1[nd - 1],
st0[nd - 1],
tau[nd - 1],
)
};
// 计算发射率参数
let epref = (abref - scref) / abref;
// 计算Planck函数避免溢出
let bref = if HK * fr / tref < 200.0 {
1.4743e-2 * (fr * 1e-15).powi(3) / ((HK * fr / tref).exp() - 1.0)
} else {
0.0
};
// 计算Y参考值
let taur = if tauef > tauef * tauef {
tauef
} else {
tauef * tauef
};
let yref = 4.0 * YCON * tref * taur;
// 波长
let alm = CAS / fr;
// 辐射强度
let kij_val = params.kij[ij] as usize;
let r1 = if kij_val > 0 && kij_val <= params.nfreq {
params.rad[params.nfreq - kij_val][0]
} else {
0.0
};
// Rosseland和源函数相关量
let (rb1, rs1) = if epref >= 0.0 {
(epref.sqrt() * bref, epref.sqrt() * stref)
} else {
(0.0, 0.0)
};
Taufr1Result {
dmref,
tref,
abref,
scref,
stref,
tauef,
epref,
bref,
yref,
alm,
r1,
rb1,
rs1,
dt,
iref: iref_use,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_taufr1_basic() {
let freq: Vec<f64> = vec![1e15; MFREQ];
let dm: Vec<f64> = vec![0.01; MDEPTH];
let temp: Vec<f64> = vec![10000.0; MDEPTH];
let abso1: Vec<f64> = vec![1e-8; MDEPTH];
let scat1: Vec<f64> = vec![1e-9; MDEPTH];
let emis1: Vec<f64> = vec![1e-10; MDEPTH];
let absot: Vec<f64> = vec![1e-8; MDEPTH];
let dens1: Vec<f64> = vec![1e15; MDEPTH];
let deldm: Vec<f64> = vec![0.001; MDEPTH];
let deldmz: Vec<f64> = vec![0.001; MDEPTH];
let kij: Vec<i32> = vec![0; MFREQ];
let rad: Vec<Vec<f64>> = vec![vec![1e10; MDEPTH]; MFREQ];
let params = Taufr1Params {
ij: 0,
nd: 10,
freq: &freq,
dm: &dm,
temp: &temp,
abso1: &abso1,
scat1: &scat1,
emis1: &emis1,
absot: &absot,
dens1: &dens1,
deldm: &deldm,
deldmz: &deldmz,
dedm1: 0.001,
kij: &kij,
nfreq: 100,
rad: &rad,
};
let result = taufr1(&params);
// 检查结果合理性
assert!(result.alm > 0.0);
assert!(result.tref > 0.0);
assert!(result.dt.len() >= 10);
}
#[test]
fn test_taufr1_wavelength() {
let freq: Vec<f64> = vec![1e15; MFREQ];
let dm: Vec<f64> = vec![0.01; MDEPTH];
let temp: Vec<f64> = vec![10000.0; MDEPTH];
let abso1: Vec<f64> = vec![1e-8; MDEPTH];
let scat1: Vec<f64> = vec![1e-9; MDEPTH];
let emis1: Vec<f64> = vec![1e-10; MDEPTH];
let absot: Vec<f64> = vec![1e-8; MDEPTH];
let dens1: Vec<f64> = vec![1e15; MDEPTH];
let deldm: Vec<f64> = vec![0.001; MDEPTH];
let deldmz: Vec<f64> = vec![0.001; MDEPTH];
let kij: Vec<i32> = vec![0; MFREQ];
let rad: Vec<Vec<f64>> = vec![vec![1e10; MDEPTH]; MFREQ];
let params = Taufr1Params {
ij: 0,
nd: 10,
freq: &freq,
dm: &dm,
temp: &temp,
abso1: &abso1,
scat1: &scat1,
emis1: &emis1,
absot: &absot,
dens1: &dens1,
deldm: &deldm,
deldmz: &deldmz,
dedm1: 0.001,
kij: &kij,
nfreq: 100,
rad: &rad,
};
let result = taufr1(&params);
// 波长 = c / nu = 2.997925e18 / 1e15 = 2997.925 Å
assert!((result.alm - 2997.925).abs() < 0.1);
}
}

View File

@ -339,9 +339,263 @@ impl VoiPar {
}
}
// ============================================================================
// ATOMAS - 原子质量额外存储
// ============================================================================
/// 原子质量额外存储。
/// 对应 COMMON /ATOMAS/
#[derive(Debug, Clone)]
pub struct AtoMas {
pub amas: Vec<f64>,
}
impl Default for AtoMas {
fn default() -> Self {
Self {
amas: vec![0.0; 100],
}
}
}
// ============================================================================
// TOPCS - 截面拟合参数
// ============================================================================
/// 截面拟合参数。
/// 对应 COMMON /TOPCS/
#[derive(Debug, Clone)]
pub struct TopCs {
pub ctop: Vec<Vec<f64>>,
pub xtop: Vec<Vec<f64>>,
}
impl Default for TopCs {
fn default() -> Self {
Self {
ctop: vec![vec![0.0; MCROSS]; MFIT],
xtop: vec![vec![0.0; MCROSS]; MFIT],
}
}
}
// ============================================================================
// TABCOL - 碰撞率表
// ============================================================================
/// 碰撞率表。
/// 对应 COMMON /TABCOL/
#[derive(Debug, Clone)]
pub struct TabCol {
pub ctemp: Vec<Vec<Vec<f64>>>,
pub crate_data: Vec<Vec<Vec<f64>>>,
}
impl Default for TabCol {
fn default() -> Self {
Self {
ctemp: vec![vec![vec![0.0; MCORAT]; MCFIT]; MXTCOL],
crate_data: vec![vec![vec![0.0; MCORAT]; MCFIT]; MXTCOL],
}
}
}
// ============================================================================
// TRACOR - 跃迁修正
// ============================================================================
/// 跃迁修正标志。
/// 对应 COMMON /TRACOR/
#[derive(Debug, Clone)]
pub struct TraCor {
pub lexp: Vec<i32>,
pub lali: Vec<i32>,
}
impl Default for TraCor {
fn default() -> Self {
Self {
lexp: vec![0; MTRANS],
lali: vec![0; MTRANS],
}
}
}
// ============================================================================
// TRAALI - ALI 跃迁标志
// ============================================================================
/// ALI 跃迁标志。
/// 对应 COMMON /TRAALI/
#[derive(Debug, Clone, Default)]
pub struct TraAli {
pub nffix: i32,
pub ifsub: i32,
pub iflev: i32,
}
// ============================================================================
// AUXIND - 辅助索引
// ============================================================================
/// 辅助索引。
/// 对应 COMMON /AUXIND/
#[derive(Debug, Clone, Default)]
pub struct AuxInd {
pub iath: i32,
pub iathe: i32,
pub ielh: i32,
pub ielhm: i32,
pub ielhe1: i32,
pub ielhe2: i32,
}
// ============================================================================
// IONFIL - 离子文件映射
// ============================================================================
/// 离子文件映射。
/// 对应 COMMON /IONFIL/
#[derive(Debug, Clone)]
pub struct IonFil {
pub fidata: Vec<String>,
pub fiodf1: Vec<String>,
pub fiodf2: Vec<String>,
pub fibfcs: Vec<String>,
}
impl Default for IonFil {
fn default() -> Self {
Self {
fidata: vec![String::new(); MION],
fiodf1: vec![String::new(); MION],
fiodf2: vec![String::new(); MION],
fibfcs: vec![String::new(); MION],
}
}
}
// ============================================================================
// IONDAT - 离子数据索引
// ============================================================================
/// 离子数据索引。
/// 对应 COMMON /IONDAT/
#[derive(Debug, Clone)]
pub struct IonDat {
pub iati: Vec<i32>,
pub izi: Vec<i32>,
pub nlevs: Vec<i32>,
pub nllim: Vec<i32>,
}
impl Default for IonDat {
fn default() -> Self {
Self {
iati: vec![0; MION],
izi: vec![0; MION],
nlevs: vec![0; MION],
nllim: vec![0; MION],
}
}
}
// ============================================================================
// OSCHYD - 氢振子强度额外存储
// ============================================================================
/// 氢振子强度额外存储。
/// 对应 COMMON /OSCHYD/
#[derive(Debug, Clone)]
pub struct OscHyd {
pub osh: [[f64; 20]; 20],
}
impl Default for OscHyd {
fn default() -> Self {
Self {
osh: [[0.0; 20]; 20],
}
}
}
// ============================================================================
// PRINTP - 能级打印控制
// ============================================================================
/// 能级打印控制。
/// 对应 COMMON /PRINTP/
#[derive(Debug, Clone)]
pub struct PrintP {
pub npgpop: i32,
pub iipr: Vec<Vec<i32>>,
pub typlev: Vec<String>,
pub typion: Vec<String>,
}
impl Default for PrintP {
fn default() -> Self {
let mpag = MLEVEL / 6 + 1;
Self {
npgpop: 0,
iipr: vec![vec![0; mpag]; 6],
typlev: vec![String::new(); MLEVEL],
typion: vec![String::new(); MION],
}
}
}
// ============================================================================
// TABMAX - 最大频率
// ============================================================================
/// 最大频率。
/// 对应 COMMON /TABMAX/
#[derive(Debug, Clone, Default)]
pub struct TabMax {
pub frtabm: f64,
}
// ============================================================================
// PRDPAR - PRD 参数
// ============================================================================
/// PRD (部分频率重分布) 参数。
/// 对应 COMMON /PRDPAR/
#[derive(Debug, Clone)]
pub struct PrdPar {
pub doptr: Vec<Vec<f64>>,
pub coher: Vec<Vec<f64>>,
pub pjbar: Vec<Vec<f64>>,
pub rjbar: Vec<Vec<f64>>,
pub xpdiv: f64,
pub iprd: Vec<i32>,
pub itrtot: Vec<i32>,
pub ntrprd: i32,
pub ifprd: i32,
}
impl Default for PrdPar {
fn default() -> Self {
const MTRPRD: usize = 5;
Self {
doptr: vec![vec![0.0; MDEPTH]; MTRPRD],
coher: vec![vec![0.0; MDEPTH]; MTRPRD],
pjbar: vec![vec![0.0; MDEPTH]; MTRPRD],
rjbar: vec![vec![0.0; MDEPTH]; MTRPRD],
xpdiv: 0.0,
iprd: vec![0; MTRANS],
itrtot: vec![0; MTRPRD],
ntrprd: 0,
ifprd: 0,
}
}
}
// ============================================================================
// HECRAT - 氦碰撞速率
// ============================================================================
// ============================================================================
/// 氦碰撞速率系数。
/// 对应 COMMON /HECRAT/
@ -366,6 +620,18 @@ pub struct AtomicData {
pub phoset: PhoSet,
pub voipar: VoiPar,
pub hecrat: HeCrat,
pub atopas: AtoMas,
pub topcs: TopCs,
pub tabcol: TabCol,
pub tracor: TraCor,
pub traali: TraAli,
pub auxind: AuxInd,
pub ionfil: IonFil,
pub iondat: IonDat,
pub oschyd: OscHyd,
pub printp: PrintP,
pub tabmax: TabMax,
pub prdpar: PrdPar,
}
impl AtomicData {

View File

@ -94,6 +94,20 @@ impl Default for BasNum {
}
}
// ============================================================================
// CENTRL - 中心点参数
// ============================================================================
/// 中心点参数。
/// 对应 COMMON /CENTRL/
#[derive(Debug, Clone, Default)]
pub struct Centrl {
/// 中心平面几何距离
pub znd: f64,
/// Z 修正迭代控制
pub ifz0: i32,
}
// ============================================================================
// INPPAR - 输入参数
// ============================================================================
@ -246,6 +260,128 @@ pub struct ConKey {
pub idconz: i32,
}
// ============================================================================
// FIXDEN - 固定密度标志
// ============================================================================
/// 固定密度标志。
/// 对应 COMMON /FIXDEN/
#[derive(Debug, Clone, Default)]
pub struct FixDen {
pub ifixde: i32,
}
// ============================================================================
// INVINT - 逆积分参数
// ============================================================================
/// 逆积分参数。
/// 对应 COMMON /INVINT/
#[derive(Debug, Clone)]
pub struct InvInt {
pub xi2: Vec<f64>,
pub xi3: Vec<f64>,
}
impl Default for InvInt {
fn default() -> Self {
Self {
xi2: vec![0.0; NLMX],
xi3: vec![0.0; NLMX],
}
}
}
// ============================================================================
// OPCKEY - 不透明度控制键
// ============================================================================
/// 不透明度控制键。
/// 对应 COMMON /OPCKEY/
#[derive(Debug, Clone, Default)]
pub struct OpcKey {
pub ncon: i32,
pub iophl1: i32,
pub iophl2: i32,
pub iphe2c: i32,
pub ifmoff: i32,
}
// ============================================================================
// PSILIM - Ψ 限制参数
// ============================================================================
/// Ψ 限制参数。
/// 对应 COMMON /PSILIM/
#[derive(Debug, Clone, Default)]
pub struct PsiLim {
pub dpsilg: f64,
pub dpsilt: f64,
pub dpsiln: f64,
pub dpsild: f64,
}
// ============================================================================
// COMITE - 迭代控制键
// ============================================================================
/// 迭代控制参数。
/// 对应 COMMON /COMITE/
#[derive(Debug, Clone, Default)]
pub struct Comite {
pub ncfor1: i32,
pub ncfor2: i32,
pub nccoup: i32,
pub ncitot: i32,
pub ncfull: i32,
}
// ============================================================================
// MLCONS - 混合长度常数
// ============================================================================
/// 混合长度常数。
/// 对应 COMMON /MLCONS/
#[derive(Debug, Clone, Default)]
pub struct MlCons {
pub aconml: f64,
pub bconml: f64,
pub cconml: f64,
}
// ============================================================================
// TAURSL - 光学深度表
// ============================================================================
/// 光学深度表。
/// 对应 COMMON /TAURSL/
#[derive(Debug, Clone)]
pub struct TaurSl {
pub taurs: Vec<f64>,
}
impl Default for TaurSl {
fn default() -> Self {
Self {
taurs: vec![0.0; MDEPTH],
}
}
}
// ============================================================================
// IPRKEY - 打印键
// ============================================================================
/// 打印键。
/// 对应 COMMON /IPRKEY/
#[derive(Debug, Clone, Default)]
pub struct IprKey {
pub iprybh: i32,
pub ipelch: i32,
pub ipeldo: i32,
pub ipconf: i32,
}
// ============================================================================
// OPCPAR - 额外不透明度控制
// ============================================================================
@ -254,6 +390,7 @@ pub struct ConKey {
/// 对应 COMMON /OPCPAR/
#[derive(Debug, Clone, Default)]
pub struct OpcPar {
pub iopadd: i32,
pub iophmi: i32, // H⁻
pub ioph2p: i32, // H₂⁺
pub iophem: i32, // He⁻
@ -384,6 +521,31 @@ impl Default for Comptn {
}
}
// ============================================================================
// COMPTI - Compton 散射控制参数
// ============================================================================
/// Compton 散射控制参数。
/// 对应 COMMON /COMPTI/
#[derive(Debug, Clone, Default)]
pub struct Compti {
pub nedd: i32,
pub nsti: i32,
pub islab: i32,
pub ilbc: i32,
pub icompt: i32,
pub icomst: i32,
pub icomde: i32,
pub icombc: i32,
pub icmdra: i32,
pub knish: i32,
pub itcomp: i32,
pub icomve: i32,
pub icomrt: i32,
pub ichcoo: i32,
pub icomgr: i32,
}
// ============================================================================
// 综合配置结构
// ============================================================================
@ -401,6 +563,16 @@ pub struct TlustyConfig {
pub prints: Prints,
pub angles: Angles,
pub comptn: Comptn,
pub centrl: Centrl,
pub compti: Compti,
pub opckey: OpcKey,
pub invint: InvInt,
pub fixden: FixDen,
pub psilim: PsiLim,
pub comite: Comite,
pub mlcons: MlCons,
pub taursl: TaurSl,
pub iprkey: IprKey,
}
impl TlustyConfig {

View File

@ -20,6 +20,8 @@ pub const MTRANS: usize = 21000;
pub const MDEPTH: usize = 100;
/// 最大频率点数
pub const MFREQ: usize = 135000;
/// 最大频率点数 + 1
pub const MFREQ1: usize = MFREQ; // 根据 BASICS.FOR: MFREQ1 = MFREQ
/// 工作频率数组大小
pub const MFREQP: usize = 220000;
/// 连续谱频率点数
@ -66,6 +68,40 @@ pub const MTRAN3: usize = 1;
pub const MCROSS: usize = MLEVEL + 5;
/// 束缚-自由跃迁数
pub const MBF: usize = MLEVEL;
/// Gomez 氢不透明度表频率大小
pub const MFHTAB: usize = 1000;
/// Gomez 氢不透明度表温度大小
pub const MTABTH: usize = 10;
/// Gomez 氢不透明度表电子密度大小
pub const MTABEH: usize = 10;
/// 碰撞率表温度点数
pub const MXTCOL: usize = 3;
/// 碰撞率表拟合点数
pub const MCFIT: usize = 10;
/// 碰撞率表跃迁点数
pub const MCORAT: usize = MTRANS;
/// 不透明度表温度大小
pub const MTABT: usize = 21;
/// 不透明度表密度大小
pub const MTABR: usize = 19;
/// 不透明度表频率大小
pub const MFRTAB: usize = 125000;
// ODF 相关
/// ODF 频率点数
pub const MFODF: usize = 180;
/// ODF 热点数
pub const MHOD: usize = 3;
/// ODF 深度维度
pub const MDODF: usize = 3;
/// ODF 库能级数
pub const MKULEV: usize = 7000;
/// ODF 谱线数
pub const MLINE: usize = 1140000;
/// ODF 截面点数
pub const MCFE: usize = 7824000;
/// ODF 频率范围
pub const MFRO: usize = MFREQL;
// ============================================================================
// 物理常数 (CGS 单位)
@ -93,6 +129,10 @@ pub const PI4H: f64 = 1.8966e27;
pub const PCK: f64 = 4.19168946e-10;
/// 氢原子质量 (g)
pub const HMASS: f64 = 1.67333e-24;
/// 康普顿散射常数 XCON
pub const XCON: f64 = 8.0935e-21;
/// 康普顿散射常数 YCON
pub const YCON: f64 = 1.68638e-10;
// ============================================================================
// 数学常数

View File

@ -188,7 +188,7 @@ pub struct ChnAd {
/// Lambda 计数
pub nlamt: i32,
/// 导数标志
pubilder: i32,
pub ilder: i32,
/// BPOP 标志
pub ibpope: i32,
}

File diff suppressed because it is too large Load Diff

View File

@ -4,25 +4,6 @@
use super::constants::*;
// ============================================================================
// ODF 维度参数
// ============================================================================
/// ODF 频率点数
pub const MFODF: usize = 180;
/// ODF 热点数
pub const MHOD: usize = 3;
/// ODF 频率范围
pub const MFRO: usize = MFREQL;
/// ODF 深度维度
pub const MDODF: usize = 3;
/// Kurucz 能级数
pub const MKULEV: usize = 7000;
/// 谱线数
pub const MLINE: usize = 1140000;
/// Fe 系数数
pub const MCFE: usize = 7824000;
// ============================================================================
// ODFION - ODF 离子控制
// ============================================================================
@ -411,6 +392,7 @@ pub struct OdfData {
pub odfctr: OdfCtr,
pub odffrq: OdfFrq,
pub odfmod: OdfMod,
pub odfstk: OdfStk,
pub splcom: SplCom,
pub opalim: OpaLim,
pub oplimt: OpLimT,
@ -424,6 +406,7 @@ impl OdfData {
odfctr: OdfCtr::new(),
odffrq: OdfFrq::new(),
odfmod: OdfMod::new(),
odfstk: OdfStk::new(NLMX),
..Default::default()
}
}