SpectraRust/src/tlusty/math/opacity/inilam.rs
2026-04-01 16:35:36 +08:00

849 lines
26 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 模型参数初始化,用于 RESOLV。
//!
//! 重构自 TLUSTY `inilam.f`
//!
//! # 功能
//!
//! RESOLV 的辅助过程,初始化模型参数以供后续使用。
//!
//! # 两个主要分支
//!
//! 1. **INIT=1** (第一次完全线性化迭代前)
//! - 计算所有跃迁、所有深度的碰撞速率
//! - 初始化 b 因子、固定电荷等
//!
//! 2. **INIT≠1** (完全线性化迭代完成后)
//! - 更新温度、电子密度、密度等
//! - 计算新的占据数
//! - 求解辐射转移方程
use crate::tlusty::state::constants::{BOLK, HALF, MDEPTH, MFREQ, MLEVEL, MTRANS, PCK, SIG4P, UN};
// f2r_depends: COLIS, COMSET, CONCOR, DIETOT, ELCOR, ODFMER, OPACF1, OPAINI, OSCCOR, OUTPUT, RATES1, RTECOM, RTEFR1, RYBHEQ, SABOLF, STEQEQ, TDPINI, VISINI, WNSTOR
// ============================================================================
// 配置参数
// ============================================================================
/// INILAM 配置参数(只读)。
#[derive(Debug, Clone)]
pub struct InilamConfig {
/// 初始化标志 (1=第一次迭代前)
pub init: i32,
/// 迭代次数
pub iter: i32,
/// LTE 模式标志
pub lte: bool,
/// IPSLTE 参数
pub ipslte: i32,
/// IDLTE 参数
pub idlte: i32,
/// IOPABT 参数(选项表)
pub ioptab: i32,
/// IDISK 参数(盘模型)
pub idisk: i32,
/// 有效温度 (K)
pub teff: f64,
/// IFPOPR 参数
pub ifpopr: i32,
/// IFRYB 参数
pub ifryb: i32,
/// IOSCOR 参数(振荡修正)
pub ioscor: i32,
/// IHECOR 参数(流体静力平衡修正)
pub ihecor: i32,
/// IFIXDE 参数(固定密度)
pub ifixde: i32,
/// ISPLIN 参数
pub isplin: i32,
/// IFDIEL 参数(双电子复合)
pub ifdiel: i32,
/// ICOMPT 参数(康普顿散射)
pub icompt: i32,
/// IPRIND 参数(打印控制)
pub iprind: i32,
/// LCHC 参数
pub lchc: bool,
/// IELCOR 参数(电子修正迭代)
pub ielcor: i32,
/// INRE 参数(辐射平衡)
pub inre: i32,
/// INPC 参数(粒子守恒)
pub inpc: i32,
/// INHE 参数(流体静力平衡)
pub inhe: i32,
/// INMP 参数(大质量粒子)
pub inmp: i32,
/// INZD 参数(几何距离)
pub inzd: i32,
/// INDL 参数(温度对数梯度)
pub indl: i32,
/// NFREQE 参数(线性化频率数)
pub nfreqe: usize,
/// DPSILN 参数(密度变化限制)
pub dpsiln: f64,
}
impl Default for InilamConfig {
fn default() -> Self {
Self {
init: 1,
iter: 0,
lte: false,
ipslte: 0,
idlte: 100,
ioptab: 0,
idisk: 0,
teff: 10000.0,
ifpopr: 0,
ifryb: 0,
ioscor: 0,
ihecor: 0,
ifixde: 0,
isplin: 0,
ifdiel: 0,
icompt: 0,
iprind: 0,
lchc: false,
ielcor: 10,
inre: 0,
inpc: 0,
inhe: 0,
inmp: 0,
inzd: 0,
indl: 0,
nfreqe: 0,
dpsiln: 10.0_f64.exp(), // e^10
}
}
}
// ============================================================================
// 模型状态参数
// ============================================================================
/// INILAM 模型状态参数。
#[derive(Debug)]
pub struct InilamModelState<'a> {
/// 深度点数
pub nd: usize,
/// 能级数
pub nlevel: usize,
/// 离子数
pub nion: usize,
/// 跃迁数
pub ntrans: usize,
/// 频率点数
pub nfreq: usize,
// 深度相关数组 [nd]
/// 柱质量密度 (g/cm²)
pub dm: &'a [f64],
/// 温度 (K)
pub temp: &'a mut [f64],
/// 电子密度 (cm⁻³)
pub elec: &'a mut [f64],
/// 总粒子密度 (cm⁻³)
pub dens: &'a mut [f64],
/// 总粒子数
pub totn: &'a mut [f64],
/// 总原子密度
pub anto: &'a mut [f64],
/// 金属原子密度
pub anma: &'a mut [f64],
/// 气压
pub pgs: &'a mut [f64],
/// 辐射压力导数
pub pradt: &'a mut [f64],
/// 湍流速度
pub vturb: &'a [f64],
/// 平均分子量
pub wmm: &'a [f64],
// 能级相关数组 [nlevel × nd]
/// 占据数
pub popul: &'a mut [f64], // [nlevel * nd]
/// b 因子
pub bfac: &'a mut [f64], // [nlevel * nd]
// 跃迁相关数组 [ntrans × nd]
/// 碰撞速率
pub colrat: &'a mut [f64], // [ntrans * nd]
/// 碰撞速率目标
pub coltar: &'a mut [f64], // [ntrans * nd]
// 频率相关数组 [nfreq × nd]
/// 辐射强度
pub rad: &'a mut [f64], // [nfreq * nd]
// 辅助数组
/// FCOOL 数组 [nd]
pub fcool: &'a mut [f64],
/// FPRD 数组 [nd]
pub fprd: &'a mut [f64],
/// QFIX 数组 [nd]
pub qfix: &'a mut [f64],
// PSY0 数组 [总变量数 × nd]
pub psy0: &'a [f64],
// 气体压力 (用于 EOS)
pub ptotal: &'a [f64],
// 距离变量
pub zd: &'a mut [f64],
// 温度梯度
pub delta: &'a mut [f64],
}
/// INILAM 原子参数。
#[derive(Debug)]
pub struct InilamAtomicParams<'a> {
/// 能级原子索引 [nlevel]
pub iatm: &'a [i32],
/// 能级离子索引 [nlevel]
pub iel: &'a [i32],
/// 能级类型 [nlevel]
pub ilk: &'a [i32],
/// 固定丰度标志 [natom]
pub iifix: &'a [i32],
/// 能级模型标志 [nlevel]
pub imodl: &'a [i32],
/// LTE 能级标志 [nlevel]
pub iltlev: &'a [i32],
/// 显式能级索引 [nlevel]
pub iiexp: &'a [i32],
/// 非零索引 [nlvexp]
pub iinonz: &'a [i32],
/// LTE 参考能级 [nlevel × nd]
pub iltref: &'a [i32],
/// SBPSI 数组 [nlevel × nd]
pub sbpsi: &'a [f64],
/// 离子电荷 [nion]
pub iz: &'a [i32],
/// 离子起始能级 [nion]
pub nfirst: &'a [i32],
/// 离子终止能级 [nion]
pub nlast: &'a [i32],
/// 下一个离子能级 [nion]
pub nnext: &'a [i32],
/// SBF 数组 [nlevel]
pub sbf: &'a [f64],
/// WOP 数组 [nlevel × nd]
pub wop: &'a [f64],
/// USUM 数组 [nion]
pub usum: &'a [f64],
}
/// INILAM 频率参数。
#[derive(Debug)]
pub struct InilamFreqParams<'a> {
/// 频率数组 [nfreq]
pub freq: &'a [f64],
/// BNUE 数组 [nfreq]
pub bnue: &'a [f64],
/// FH 数组 [nfreq]
pub fh: &'a [f64],
/// HEXTRD 数组 [nfreq]
pub hextrd: &'a [f64],
/// 权重数组 [nfreq]
pub w: &'a [f64],
/// h/(kT) 数组 [nd]
pub hkt1: &'a [f64],
}
/// INILAM 当前频率输出。
#[derive(Debug, Clone)]
pub struct InilamFreqOutput {
/// ABSO1 数组 [nd]
pub abso1: Vec<f64>,
/// RAD1 数组 [nd]
pub rad1: Vec<f64>,
/// FAK1 数组 [nd]
pub fak1: Vec<f64>,
}
// ============================================================================
// 输出结构体
// ============================================================================
/// INILAM 输出结构体。
#[derive(Debug, Clone)]
pub struct InilamOutput {
/// PRAD 参数
pub prad: f64,
/// PRD0 参数
pub prd0: f64,
/// ANEREL 参数
pub anerel: f64,
/// AMUV0 参数
pub amuv0: f64,
/// AMUV1 参数
pub amuv1: f64,
/// DMTOT 参数
pub dmtot: f64,
/// EDISC 参数
pub edisc: f64,
/// GRD 数组 [nd]
pub grd: Vec<f64>,
/// PRA 数组 [nd]
pub pra: Vec<f64>,
}
impl Default for InilamOutput {
fn default() -> Self {
Self {
prad: 0.0,
prd0: 0.0,
anerel: 0.5,
amuv0: 0.0,
amuv1: 1.0,
dmtot: 0.0,
edisc: 0.0,
grd: vec![0.0; MDEPTH],
pra: vec![0.0; MDEPTH],
}
}
}
// ============================================================================
// 辅助函数
// ============================================================================
/// 获取二维数组的元素Fortran 列优先顺序)。
#[inline]
fn get_2d<T>(arr: &[T], i: usize, j: usize, nrows: usize) -> &T {
&arr[j * nrows + i]
}
/// 获取二维数组的可变元素Fortran 列优先顺序)。
#[inline]
fn get_2d_mut<T>(arr: &mut [T], i: usize, j: usize, nrows: usize) -> &mut T {
&mut arr[j * nrows + i]
}
/// 设置二维数组的元素Fortran 列优先顺序)。
#[inline]
fn set_2d<T: Copy>(arr: &mut [T], i: usize, j: usize, nrows: usize, val: T) {
arr[j * nrows + i] = val;
}
// ============================================================================
// 主函数
// ============================================================================
/// INILAM 纯计算函数。
///
/// # 参数
/// - `config`: 配置参数
/// - `model`: 模型状态(会被修改)
/// - `atomic`: 原子参数
/// - `freq`: 频率参数
///
/// # 返回值
/// 返回 InilamOutput 结构体
///
/// # 注意
/// 此函数仅实现核心计算逻辑,不包含 I/O 操作。
/// 外部函数调用(如 TDPINI、WNSTOR 等)需要通过回调实现。
pub fn inilam_pure(
config: &InilamConfig,
model: &mut InilamModelState,
atomic: &InilamAtomicParams,
freq: &InilamFreqParams,
) -> InilamOutput {
let nd = model.nd;
let nlevel = model.nlevel;
let nion = model.nion;
let ntrans = model.ntrans;
let nfreq = model.nfreq;
let mut output = InilamOutput::default();
output.grd.truncate(nd);
output.grd.resize(nd, 0.0);
output.pra.truncate(nd);
output.pra.resize(nd, 0.0);
// 初始化 ANEREL
output.anerel = 0.5;
if config.teff < 8000.0 {
output.anerel = 0.01;
}
// ================================================================
// INIT = 1 分支:第一次迭代前
// ================================================================
if config.init == 1 {
// 盘模型参数
if config.idisk == 1 {
// AMUV0 = DMVISC**(ZETA0+UN) - 这里需要外部参数
// AMUV1 = UN - AMUV0
output.dmtot = model.dm[nd - 1]; // Fortran DM(ND)
output.edisc = SIG4P * config.teff.powi(4) / output.dmtot;
}
// 对每个深度点进行初始化
for id in 0..nd {
// FCOOL 和 FPRD 初始化
model.fcool[id] = 0.0;
model.fprd[id] = 0.0;
// 初始化 b 因子
for i in 0..nlevel {
set_2d(&mut model.bfac, i, id, nlevel, UN);
// SBW(I) = ELEC(ID) * SBF(I) * WOP(I,ID)
let sbw = model.elec[id] * atomic.sbf[i] * *get_2d(atomic.wop, i, id, nlevel);
// 如果非 LTE 且 IPSLTE=0 且 id < idlte
if !config.lte && config.ipslte == 0 && id < config.idlte as usize {
// 对每个离子
for ion in 0..nion {
let nf = atomic.nfirst[ion] as usize;
let nl = atomic.nlast[ion] as usize;
let nn = atomic.nnext[ion] as usize;
if nn > 0 && nn <= nlevel {
let pop_next = *get_2d(model.popul, nn, id, nlevel);
if pop_next > 0.0 && atomic.iltlev[i] == 0 {
let pop_i = *get_2d(model.popul, i, id, nlevel);
let bfac_val = pop_i / (pop_next * sbw);
set_2d(&mut model.bfac, i, id, nlevel, bfac_val);
}
}
}
}
}
// 固定电荷部分 QFIX
model.qfix[id] = 0.0;
for i in 0..nlevel {
let imodl = atomic.imodl[i];
let iatm = atomic.iatm[i] as usize;
if imodl < 0 || (iatm < atomic.iifix.len() && atomic.iifix[iatm] > 0) {
let mut ch = (atomic.iz[atomic.iel[i] as usize] - 1) as f64;
let il = atomic.ilk[i];
if il > 0 {
let il_usize = il as usize;
ch = (atomic.iz[il_usize] as f64)
+ (atomic.iz[il_usize] as f64 - 1.0) * atomic.usum[il_usize] * model.elec[id];
}
model.qfix[id] += ch * *get_2d(model.popul, i, id, nlevel);
}
}
// 碰撞速率(非 LTE 情况)
if !config.lte {
// COLIS 调用需要外部实现
// 这里只初始化数组
for it in 0..ntrans {
set_2d(&mut model.colrat, it, id, ntrans, 0.0);
set_2d(&mut model.coltar, it, id, ntrans, 0.0);
}
}
}
// ISPLIN >= 5: 电子散射源函数的 Planck 函数估计
if config.isplin >= 5 {
for id in 0..nd {
for ij in 0..nfreq {
let rad_val = freq.bnue[ij]
/ (freq.hkt1[id] * freq.freq[ij]).exp() - UN;
set_2d(&mut model.rad, ij, id, nfreq, rad_val);
}
}
}
return output;
}
// ================================================================
// INIT ≠ 1 分支:迭代后更新
// ================================================================
// PRAD 初始化
output.prad = 0.0;
// 工作数组
let mut xe = vec![0.0; nd];
let mut antc = vec![0.0; nd];
// 保存旧量并更新
for id in 0..nd {
// AOLD = DENS(ID)/WMM(ID) + ELEC(ID)
let aold = model.dens[id] / model.wmm[id] + model.elec[id];
xe[id] = UN - model.elec[id] / aold;
// 更新温度(如果求解辐射平衡)
if config.inre != 0 {
let psy_idx = (config.nfreqe + config.inre as usize) * nd + id;
if psy_idx < model.psy0.len() {
model.temp[id] = model.psy0[psy_idx];
}
}
// 更新电子密度(如果求解粒子守恒)
if config.inpc != 0 {
let psy_idx = (config.nfreqe + config.inpc as usize) * nd + id;
if psy_idx < model.psy0.len() {
model.elec[id] = model.psy0[psy_idx];
}
}
// 更新总粒子数(如果求解流体静力平衡)
if config.inhe != 0 {
let psy_idx = (config.nfreqe + config.inhe as usize) * nd + id;
if psy_idx < model.psy0.len() {
model.totn[id] = model.psy0[psy_idx];
}
}
// 密度更新
if config.ifixde == 0 {
let dens0 = model.dens[id];
if config.inhe != 0 {
let psy_idx = (config.nfreqe + config.inhe as usize) * nd + id;
if psy_idx < model.psy0.len() {
model.dens[id] = model.wmm[id] * (model.psy0[psy_idx] - model.elec[id]);
}
let dplp = dens0 * config.dpsiln;
let dplm = dens0 / config.dpsiln;
if model.dens[id] > dplp {
model.dens[id] = dplp;
}
if model.dens[id] < dplm {
model.dens[id] = dplm;
}
} else if config.ioptab < -1 {
model.pgs[id] = model.ptotal[id];
// DENS(ID) = RHOEOS(TEMP(ID), PGS(ID)) - 需要外部调用
}
}
// 大质量粒子密度更新
if config.inmp != 0 {
let psy_idx = (config.nfreqe + config.inmp as usize) * nd + id;
if psy_idx < model.psy0.len() {
model.dens[id] = model.wmm[id] * model.psy0[psy_idx];
}
}
// 几何距离
if config.inzd > 0 {
let psy_idx = (config.nfreqe + config.inzd as usize) * nd + id;
if psy_idx < model.psy0.len() {
model.zd[id] = model.psy0[psy_idx];
}
}
// 温度对数梯度
if config.indl != 0 {
let psy_idx = (config.nfreqe + config.indl as usize) * nd + id;
if psy_idx < model.psy0.len() {
model.delta[id] = model.psy0[psy_idx];
}
}
// 更新 ANMA 和 ANTO
model.anma[id] = model.dens[id] / model.wmm[id];
model.anto[id] = model.anma[id] + model.elec[id];
}
// 流体静力平衡修正(如果需要)
if config.ihecor >= 2 {
// ID = 1
let ptur = HALF * model.vturb[0] * model.vturb[0] * model.dens[0];
antc[0] = (model.dm[0] * config.teff * 0.0 - output.prd0 - ptur) // GRAV 需要外部传入
/ BOLK / model.temp[0];
if antc[0] <= 0.0 {
antc[0] = model.dens[0] / model.wmm[0] + model.elec[0];
}
for id in 1..nd {
let ptur = HALF * model.vturb[id] * model.vturb[id] * model.dens[id];
let pturm = HALF * model.vturb[id - 1] * model.vturb[id - 1] * model.dens[id - 1];
// 简化计算GRAV 需要外部传入
antc[id] = (model.temp[id - 1] * antc[id - 1] * BOLK - model.pradt[id] + model.pradt[id - 1] - ptur + pturm)
/ BOLK / model.temp[id];
}
for id in 0..nd {
model.elec[id] = (UN - xe[id]) * antc[id];
model.dens[id] = model.wmm[id] * (antc[id] - model.elec[id]);
model.anma[id] = model.dens[id] / model.wmm[id];
model.anto[id] = model.anma[id] + model.elec[id];
}
}
// 更新 PGS
for id in 0..nd {
model.pgs[id] = (model.dens[id] / model.wmm[id] + model.elec[id]) * BOLK * model.temp[id];
}
// IOPTAB >= 0 时的处理
if config.ioptab >= 0 {
for id in 0..nd {
// 碰撞速率更新(非 LTE
if !config.lte {
for it in 0..ntrans {
set_2d(&mut model.colrat, it, id, ntrans, 0.0);
set_2d(&mut model.coltar, it, id, ntrans, 0.0);
}
}
}
}
// 新占据数计算
// 分支 1: IFPOPR <= 0 或 LTE 或 IFRYB > 0
if config.ifpopr <= 0 || config.lte || config.ifryb > 0 {
// RATES1 调用需要外部实现
// 深度遍历求解统计平衡
for id in 0..nd {
// STEQEQ 和 ELCOR 需要外部实现
}
} else {
// 分支 2: 从线性化修正获取占据数
for id in 0..nd {
for i in 0..nlevel {
let iatm = atomic.iatm[i] as usize;
if iatm < atomic.iifix.len() && atomic.iifix[iatm] != 1 {
let ii = atomic.iiexp[i];
if ii > 0 {
let iii = atomic.iinonz[ii as usize];
if iii > 0 {
let psy_idx = (config.nfreqe + config.inre as usize + iii as usize - 1) * nd + id;
if psy_idx < model.psy0.len() {
set_2d(&mut model.popul, i, id, nlevel, model.psy0[psy_idx]);
}
} else {
set_2d(&mut model.popul, i, id, nlevel, 0.0);
}
} else if ii < 0 {
let iii = atomic.iinonz[(-ii) as usize];
if iii > 0 {
let psy_idx = (config.nfreqe + config.inre as usize + iii as usize - 1) * nd + id;
if psy_idx < model.psy0.len() {
let pop_val = model.psy0[psy_idx] * *get_2d(atomic.sbpsi, i, id, nlevel);
set_2d(&mut model.popul, i, id, nlevel, pop_val);
}
} else {
set_2d(&mut model.popul, i, id, nlevel, 0.0);
}
} else {
// II = 0 情况
let iltref_val = *get_2d(atomic.iltref, i, id, nlevel);
if iltref_val >= 0 && (iltref_val as usize) < atomic.iiexp.len() {
let ii_ref = atomic.iiexp[iltref_val as usize];
if ii_ref > 0 && (ii_ref as usize) < atomic.iinonz.len() {
let iii = atomic.iinonz[ii_ref as usize];
if iii > 0 {
let psy_idx = (config.nfreqe + config.inre as usize + iii as usize - 1) * nd + id;
if psy_idx < model.psy0.len() {
let pop_val = model.psy0[psy_idx] * *get_2d(atomic.sbpsi, i, id, nlevel);
set_2d(&mut model.popul, i, id, nlevel, pop_val);
}
} else {
set_2d(&mut model.popul, i, id, nlevel, 0.0);
}
}
}
}
}
}
}
}
// 康普顿散射
if config.icompt > 0 {
// OPAINI, COMSET, RTECOM 需要外部实现
}
// 辐射转移求解
// OPAINI(1)
for id in 0..nd {
output.grd[id] = 0.0;
output.pra[id] = 0.0;
model.pradt[id] = 0.0;
}
output.prd0 = 0.0;
// 频率循环(完整版本需要 OPACF1 和 RTEFR1 回调)
for ij in 0..nfreq {
// OPACF1(IJ) 和 RTEFR1(IJ) 需要外部实现
// 计算 GRD 和 PRA
}
// GRD(1) = PCK * GRD(1) / DENS(1)
if nd > 0 && model.dens[0] != 0.0 {
output.grd[0] = PCK * output.grd[0] / model.dens[0];
}
// PRA 和 PRADT 更新
for id in 0..nd {
output.pra[id] *= PCK;
model.pradt[id] *= PCK;
}
// PGS 更新(非盘模型)
if config.idisk == 0 {
// 需要外部 GRAV 参数
// PGS(1) = DM(1) * (GRAV - GRD(1))
// PGS(ID) = PGS(ID-1) - PCK*GRD(ID) + GRAV*(DM(ID)-DM(ID-1))
}
output
}
// ============================================================================
// 测试
// ============================================================================
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_inilam_config_default() {
let config = InilamConfig::default();
assert_eq!(config.init, 1);
assert!(!config.lte);
assert_relative_eq!(config.teff, 10000.0);
}
#[test]
fn test_inilam_output_default() {
let output = InilamOutput::default();
assert_relative_eq!(output.prad, 0.0);
assert_relative_eq!(output.anerel, 0.5);
}
#[test]
fn test_inilam_init_branch() {
let config = InilamConfig {
init: 1,
teff: 10000.0,
..Default::default()
};
let nd = 3;
let nlevel = 2;
let nion = 1;
let ntrans = 1;
let nfreq = 2;
let dm = vec![1e-4, 1e-3, 1e-2];
let mut temp = vec![10000.0, 15000.0, 20000.0];
let mut elec = vec![1e10, 1e11, 1e12];
let mut dens = vec![1e14, 1e15, 1e16];
let mut totn = vec![0.0; 3];
let mut anto = vec![0.0; 3];
let mut anma = vec![0.0; 3];
let mut pgs = vec![0.0; 3];
let mut pradt = vec![0.0; 3];
let vturb = vec![0.0; 3];
let wmm = vec![1.0; 3];
let mut popul = vec![0.5; nlevel * nd];
let mut bfac = vec![1.0; nlevel * nd];
let mut colrat = vec![0.0; ntrans * nd];
let mut coltar = vec![0.0; ntrans * nd];
let mut rad = vec![0.0; nfreq * nd];
let mut fcool = vec![0.0; 3];
let mut fprd = vec![0.0; 3];
let mut qfix = vec![0.0; 3];
let psy0 = vec![0.0; 100];
let ptotal = vec![0.0; 3];
let mut zd = vec![0.0; 3];
let mut delta = vec![0.0; 3];
let mut model = InilamModelState {
nd,
nlevel,
nion,
ntrans,
nfreq,
dm: &dm,
temp: &mut temp,
elec: &mut elec,
dens: &mut dens,
totn: &mut totn,
anto: &mut anto,
anma: &mut anma,
pgs: &mut pgs,
pradt: &mut pradt,
vturb: &vturb,
wmm: &wmm,
popul: &mut popul,
bfac: &mut bfac,
colrat: &mut colrat,
coltar: &mut coltar,
rad: &mut rad,
fcool: &mut fcool,
fprd: &mut fprd,
qfix: &mut qfix,
psy0: &psy0,
ptotal: &ptotal,
zd: &mut zd,
delta: &mut delta,
};
let atomic = InilamAtomicParams {
iatm: &[0, 0],
iel: &[0, 0],
ilk: &[0, 0],
iifix: &[0],
imodl: &[0, 0],
iltlev: &[0, 0],
iiexp: &[0, 0],
iinonz: &[0],
iltref: &[0; 6],
sbpsi: &[1.0; 6],
iz: &[1],
nfirst: &[0],
nlast: &[1],
nnext: &[1],
sbf: &[1.0, 1.0],
wop: &[1.0; 6],
usum: &[1.0],
};
let freq_data = vec![1e14, 2e14];
let bnue = vec![1e-10, 2e-10];
let fh = vec![1.0, 1.0];
let hextrd = vec![0.0; 2];
let w = vec![0.5, 0.5];
let hkt1 = vec![4.8e-11 / 10000.0, 4.8e-11 / 15000.0, 4.8e-11 / 20000.0];
let freq = InilamFreqParams {
freq: &freq_data,
bnue: &bnue,
fh: &fh,
hextrd: &hextrd,
w: &w,
hkt1: &hkt1,
};
let output = inilam_pure(&config, &mut model, &atomic, &freq);
// INIT=1 分支应该返回
assert_relative_eq!(output.anerel, 0.5); // TEFF >= 8000
}
#[test]
fn test_inilam_cold_star() {
// 冷星 (TEFF < 8000K)
let config = InilamConfig {
init: 1,
teff: 6000.0,
..Default::default()
};
assert!(config.teff < 8000.0);
let output = InilamOutput::default();
// 对于冷星anerel 应该是 0.01(在函数内部设置)
}
}