feat: 添加更多重构模块 (第7批)
包含 IO 和 math 模块的实现: - IO: initia, levcd, linset, ltegr, ltegrd, odfset, outpri, resolv, srtfrq, start, tabini, xenini - Math: accel2, alisk1, alisk2, alist1, alist2, concor, conout, conref, contmd, contmp, coolrt, greyd, inilam, linsel, lucy, lymlin, matcon, matgen, moleq, newdm, newdmt, odf1, opacf0, opacf1, opacfa, opacfd, opacfl, opactr, opadd, opahst, pgset, princ, prnt, pzeval, quasim, radpre, radtot, rates1, ratsp1, rdata, rdatax, rechck, rhoeos, rhonen, rhsgen, rossop, rtecf1, rtecmc, rtecmu, rtecom, rtefr1, rteint, russel, rybchn, rybene, rybheq, rybsol, sgmer1, sigave, sigk, solve, solves, state, steqeq, temcor, temper, topbas, trmder, trmdrt Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,847 @@
|
||||
//! 模型参数初始化,用于 RESOLV。
|
||||
//!
|
||||
//! 重构自 TLUSTY `inilam.f`
|
||||
//!
|
||||
//! # 功能
|
||||
//!
|
||||
//! RESOLV 的辅助过程,初始化模型参数以供后续使用。
|
||||
//!
|
||||
//! # 两个主要分支
|
||||
//!
|
||||
//! 1. **INIT=1** (第一次完全线性化迭代前):
|
||||
//! - 计算所有跃迁、所有深度的碰撞速率
|
||||
//! - 初始化 b 因子、固定电荷等
|
||||
//!
|
||||
//! 2. **INIT≠1** (完全线性化迭代完成后):
|
||||
//! - 更新温度、电子密度、密度等
|
||||
//! - 计算新的占据数
|
||||
//! - 求解辐射转移方程
|
||||
|
||||
use crate::state::constants::{BOLK, HALF, MDEPTH, MFREQ, MLEVEL, MTRANS, PCK, SIG4P, UN};
|
||||
|
||||
// ============================================================================
|
||||
// 配置参数
|
||||
// ============================================================================
|
||||
|
||||
/// 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(在函数内部设置)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user