//! 线性化方程组右端向量计算。 //! //! 重构自 TLUSTY `rhsgen.f` (lines 1-722)。 //! //! 功能: //! - Compton 散射边界条件 (lines 30-51) //! - 辐射传输组件 (lines 112-379) //! - 流体静力学平衡 (lines 385-500) //! - z-d 关系 (lines 506-509) //! - 辐射平衡 (lines 515-581) //! - 统计平衡 (lines 587-614) //! - 电荷守恒 (lines 620-645) //! - 对流贡献 (lines 651-708) use crate::tlusty::math::{convec, ConvecConfig, ConvecParams}; use crate::tlusty::state::constants::{BOLK, HALF, UN, HK, SIG4P, BN, PCK}; /// 稀释因子 (RRDIL) - 平面平行大气为 1.0 const RRDIL: f64 = 1.0; /// 常量 (Fortran lines 16-18) const XCON: f64 = 8.0935e-21; const YCON: f64 = 1.68638e-10; const SIXTH: f64 = 1.0 / 6.0; const THIRD: f64 = 1.0 / 3.0; const TWO: f64 = 2.0; /// RHSGEN 配置参数 #[derive(Debug, Clone)] pub struct RhsgenConfig { /// 插值方法 (ISPLIN) - 0: 标准, 1: Spline, 2: Hermitian pub isplin: i32, /// 盘模式标志 (IDISK) - 0: 恒星大气, 1: 吸积盘 pub idisk: i32, /// 下边界条件类型 (IBC) - 0/4: 零阶, 1-3: 二阶 pub ibc: i32, /// 氦方程标志 (INHE) - 流体静力学平衡方程索引 pub inhe: i32, /// 能量方程标志 (INRE) - 辐射平衡方程索引 pub inre: i32, /// 压力方程标志 (INPC) - 电荷守恒方程索引 pub inpc: i32, /// DELTA 方程标志 (INDL) - 对流稳定性方程索引 pub indl: i32, /// 统计平衡标志 (INSE) - 统计平衡方程索引 pub inse: i32, /// z-d 关系标志 (INZD) - 几何深度方程索引 pub inzd: i32, /// 频率数 (NFREQE) pub nfreqe: usize, /// 总方程数 (NN) pub nn: usize, /// 不透明度缩放标志 (IZSCAL) - 0: 按质量, 1: 按电子 pub izscal: i32, /// Compton 散射标志 (ICOMPT) - >0 启用 pub icompt: i32, /// Compton 边界条件标志 (ICOMBC) - >0 启用最高频率边界条件 pub icombc: i32, /// 混合长度参数 (HMIX0) - >0 启用对流 pub hmix0: f64, /// 对流模式标志 (ICONV) pub iconv: i32, /// 有效温度 (TEFF) pub teff: f64, /// 辐射平衡差分方程权重模式 (NRETC) pub nretc: i32, /// 当前迭代次数 (ITER) pub iter: i32, /// NDRE - 辐射平衡从积分方程切换到微分方程的深度点 pub ndre: usize, /// 温度边界值 (TEMPBD) - 下边界温度 pub tempbd: f64, /// 上边界条件类型 (IBCHE) - 0: 标准, 1/2: 盘模式 pub ibche: i32, /// 不透明度表标志 (IOPTAB) pub ioptab: i32, /// 统计平衡模式 (IFPOPR) - 1-5: 不同处理方式 pub ifpopr: i32, /// 频率缩放坐标标志 (ICHCOO) - 0: 标准, 1: 线性频率 pub ichcoo: i32, /// 外部辐射场标志 (IWINBL) pub iwinbl: i32, /// 参考原子索引 (IATREF) pub iatref: usize, /// 重力加速度 (GRAV) pub grav: f64, /// 重力缩放因子 (QGRAV) pub qgrav: f64, /// z=0 标志 (IFZ0) - <0 使用标准边界, >=0 盘模式 pub ifz0: i32, } impl Default for RhsgenConfig { fn default() -> Self { Self { isplin: 0, idisk: 0, ibc: 1, inhe: 1, inre: 1, inpc: 0, indl: 0, inse: 0, inzd: 0, nfreqe: 10, nn: 15, izscal: 0, icompt: 0, icombc: 0, hmix0: -1.0, iconv: 0, teff: 10000.0, nretc: 0, iter: 0, ndre: 100, // 默认值,通常 > ND tempbd: 0.0, ibche: 0, ioptab: 0, ifpopr: 0, ichcoo: 0, iwinbl: 0, iatref: 0, grav: 1e4, qgrav: 1e4, ifz0: 0, } } } /// RHSGEN 频率数据 pub struct RhsgenFreqData<'a> { /// 频率权重 pub w: &'a [f64], /// 辐射强度 pub rad: &'a [f64], /// 吸收系数 pub abso: &'a [f64], /// 发射系数 pub emis: &'a [f64], /// 散射系数 pub scat: &'a [f64], /// FK 系数 pub fk: &'a [f64], } // ============================================================================ // 回调 trait (用于可选的高级功能) // ============================================================================ /// Compton 散射回调结果 #[derive(Debug, Clone, Default)] pub struct Compt0Result { pub cma: f64, pub cmb: f64, pub cmc: f64, pub cme: f64, pub cms: f64, pub cmd: f64, } /// STATE 回调结果 #[derive(Debug, Clone, Default)] pub struct StateResult { pub q: f64, } /// 统计平衡回调结果 #[derive(Debug, Clone, Default)] pub struct StatEqResult { pub popgrp: Vec, pub nlvexp: usize, } /// RHSGEN 回调 trait /// /// 用于实现可选的高级功能:Compton 散射、统计平衡、电荷守恒。 /// 调用者可以实现此 trait 来提供具体的计算逻辑。 pub trait RhsgenCallbacks { /// Compton 散射计算 (Fortran line 147, 210, 323, 544) /// 参数: (ijt, id, abso) /// 返回: (cma, cmb, cmc, cme, cms, cmd) fn call_compt0(&self, _ijt: usize, _id: usize, _abso: f64) -> Compt0Result { Compt0Result::default() } /// STATE 调用 (Fortran line 630) /// 参数: (mode, id, t, ane) /// 返回: q 值 fn call_state(&self, _mode: i32, _id: usize, _t: f64, _ane: f64) -> StateResult { StateResult::default() } /// 统计平衡计算 (Fortran lines 590-612) /// 参数: (id) /// 返回: 种群组和方程数 fn call_statistical_equilibrium(&self, _id: usize) -> StatEqResult { StatEqResult::default() } /// 检查是否跳过频率点 (Fortran line 399, 440, 475) fn lskip(&self, _id: usize, _ijt: usize) -> bool { false } /// 获取固定辐射压力 (Fortran line 413, 491) fn fprd(&self, _id: usize) -> f64 { 0.0 } /// 获取 FH 系数 (Fortran line 170, 349) fn fh(&self, _ijt: usize) -> f64 { 0.0 } /// 获取 FHD 系数 (Fortran line 349) fn fhd(&self, _ijt: usize) -> f64 { 0.0 } /// 获取 Q0 系数 (Fortran line 170) fn q0(&self, _ijt: usize) -> f64 { 0.0 } /// 获取外部辐射场 (Fortran line 171) fn hextrd(&self, _ijt: usize) -> f64 { 0.0 } /// 获取固定种群 (Fortran line 640) fn qfix(&self, _id: usize) -> f64 { 0.0 } /// 获取 IJEX 标志 (Fortran line 30) fn ijex(&self, _idx: usize) -> i32 { 0 } /// 获取频率 (Fortran line 330) fn freq(&self, _ijt: usize) -> f64 { 0.0 } /// 获取 DLNFR (Fortran line 40) fn dlnfr(&self, _ij: usize) -> f64 { 1.0 } /// 获取 DELJ (Fortran line 36) fn delj(&self, _ij: usize, _id: usize) -> f64 { 0.5 } /// 获取 IJFR 映射 (Fortran line 61) fn ijfr(&self, _ij: usize) -> usize { _ij } /// 获取 YTOT (Fortran line 632) fn ytot(&self, _id: usize) -> f64 { 1.0 } /// 获取 ABUND (Fortran line 633) fn abund(&self, _iat: usize, _id: usize) -> f64 { 1.0 } /// 获取原子数 (Fortran line 634) fn natom(&self) -> usize { 0 } /// 获取 N0A (Fortran line 636) fn n0a(&self, _iat: usize) -> usize { 0 } /// 获取 NKA (Fortran line 636) fn nka(&self, _iat: usize) -> usize { 0 } /// 获取 ILK (Fortran line 637) fn ilk(&self, _i: usize) -> usize { 0 } /// 获取 IEL (Fortran line 638) fn iel(&self, _i: usize) -> usize { 0 } /// 获取 IZ (Fortran line 638) fn iz(&self, _ion: usize) -> f64 { 0.0 } /// 获取 IMODL (Fortran line 640) fn imodl(&self, _i: usize) -> i32 { 0 } /// 获取 IIFIX (Fortran line 635) fn iifix(&self, _iat: usize) -> i32 { 0 } /// 获取 POPUL (Fortran line 640) fn popul(&self, _i: usize, _id: usize) -> f64 { 0.0 } /// 获取 USUMS (Fortran line 639) fn usums(&self, _il: usize, _id: usize) -> f64 { 0.0 } /// 获取 TVISC (Fortran line 532) fn tvisc(&self, _id: usize) -> f64 { 0.0 } /// 获取 THETAV (Fortran line 557) fn thetav(&self, _id: usize) -> f64 { 0.0 } /// 获取压力数组 fn ptotal(&self, _id: usize) -> f64 { 0.0 } /// 获取气体压力数组 fn pgs(&self, _id: usize) -> f64 { 0.0 } /// 获取 PSI0 (Fortran line 57) fn psi0(&self, _i: usize) -> f64 { 0.0 } /// 获取 PSIM (Fortran line 73) fn psim(&self, _i: usize) -> f64 { 0.0 } /// 获取 IGZERT (Fortran line 715) fn igzert(&self, _ii: usize) -> i32 { 0 } /// 获取 IGZERO (Fortran line 601) fn igzero(&self, _i: usize, _id: usize) -> i32 { 0 } /// 获取 ERFCX 缩放互补误差函数 (Fortran line 427, 454) fn erfcx(&self, _x: f64) -> f64 { crate::tlusty::math::special::erfcx(_x) } /// 获取参考气体压力 (Fortran line 464: PGAS0) fn pgas0(&self) -> f64 { 0.0 } } /// 默认的空回调实现 pub struct DefaultCallbacks; impl RhsgenCallbacks for DefaultCallbacks {} /// RHSGEN 输入参数 pub struct RhsgenParams<'a, C: RhsgenCallbacks = DefaultCallbacks> { /// 深度点索引 (1-indexed) pub id: usize, /// 总深度点数 pub nd: usize, /// 温度数组 pub temp: &'a [f64], /// 密度数组 pub dens: &'a [f64], /// 柱密度数组 pub dm: &'a [f64], /// 平均分子量数组 pub wmm: &'a [f64], /// 湍流速度数组 pub vturb: &'a [f64], /// 电子密度数组 pub elec: &'a [f64], /// 几何因子数组 pub zd: &'a [f64], /// Delta 参数数组 pub delta: &'a mut [f64], /// 对流通量数组 pub flxc: &'a mut [f64], /// 冷却通量数组 pub fcool: &'a [f64], /// Rosseland 不透明度数组 pub abrosd: &'a [f64], /// 微分方程权重 pub redif: &'a [f64], /// 积分方程权重 pub reint: &'a [f64], /// 光学深度增量数组 DELDMZ (Fortran lines 121, 178-180, 310, 561) pub deldmz: &'a [f64], /// 当前点频率数据 pub freq0: &'a RhsgenFreqData<'a>, /// 上一点频率数据 pub freqm: &'a RhsgenFreqData<'a>, /// 下一点频率数据 pub freqp: &'a RhsgenFreqData<'a>, /// 配置 pub config: RhsgenConfig, /// CONVEC 配置 pub convec_config: ConvecConfig, /// 回调函数 pub callbacks: &'a C, } /// RHSGEN 输出 pub struct RhsgenOutput { /// RHS 向量 pub vecl: Vec, } /// 计算 RHS 向量。 /// /// 完整实现对应 Fortran rhsgen.f (lines 1-722) pub fn rhsgen(params: &mut RhsgenParams) -> RhsgenOutput { let id = params.id; let nd = params.nd; // 提取配置值(避免借用冲突) let nfreqe = params.config.nfreqe; let nn = params.config.nn; let inhe = params.config.inhe; let inre = params.config.inre; let inpc = params.config.inpc; let indl = params.config.indl; let inse = params.config.inse; let inzd = params.config.inzd; let hmix0 = params.config.hmix0; let icompt = params.config.icompt; let icombc = params.config.icombc; let izscal = params.config.izscal; // Fortran lines 23-24: ISPLIN 调整 // ispl=isplin // if(isplin.ge.5) isplin=isplin-5 let isplin = if params.config.isplin >= 5 { params.config.isplin - 5 } else { params.config.isplin }; // 初始化 RHS 向量 (Fortran lines 108-110) let mut vecl = vec![0.0; nn]; // 计算行索引 (0-based for vecl array indexing) // Fortran: NHE = NFREQE + INHE (1-based), so 0-based = NFREQE + INHE - 1 let nhe = if inhe > 0 { nfreqe + inhe as usize - 1 } else { usize::MAX }; let nre = if inre > 0 { nfreqe + inre as usize - 1 } else { usize::MAX }; let npc = if inpc > 0 { nfreqe + inpc as usize - 1 } else { usize::MAX }; let ndel = if indl > 0 { nfreqe + indl as usize - 1 } else { usize::MAX }; let nse = if inse > 0 { nfreqe + inse as usize - 1 } else { usize::MAX }; let nzd = if inzd > 0 { nfreqe + inzd as usize - 1 } else { usize::MAX }; // ======================================================================== // Compton 散射边界条件 (Fortran lines 30-51) // ======================================================================== let ij1 = if icompt > 0 && icombc > 0 && params.callbacks.ijex(1) > 0 { // 计算 Compton 边界条件 let ij = 1; let iji = nfreqe; // 假设 nfreqe 对应 NFREQ let temp_id = params.temp[id - 1]; let zj1 = (-HK * params.callbacks.freq(ij) / temp_id).exp(); let zj2 = (-HK * params.callbacks.freq(ij + 1) / temp_id).exp(); let dlt = params.callbacks.delj(iji - 1, id); // 根据 ICHCOO 选择公式 if params.config.ichcoo == 0 { // Fortran lines 37-41 let zj0 = UN / (HK * (params.callbacks.freq(ij) * params.callbacks.freq(ij + 1)).sqrt() / temp_id); let zxx = UN - 3.0 * zj0 + (UN - dlt) * zj1 + dlt * zj2; let combid = zj0 / params.callbacks.dlnfr(iji - 1) + (UN - dlt) * zxx; let comaid = -zj0 / params.callbacks.dlnfr(iji - 1) + dlt * zxx; vecl[ij - 1] = -combid * params.freq0.rad[iji - 1] - comaid * params.freq0.rad[iji - 2]; } else { // Fortran lines 43-50 let e2 = YCON * temp_id; let zxx0 = XCON * params.callbacks.freq(ij) * (UN + zj1) - 3.0 * e2; let zxxm = XCON * params.callbacks.freq(ij + 1) * (UN + zj2) - 3.0 * e2; let zxx = (UN - dlt) * zxx0 + dlt * zxxm; let combid = e2 / params.callbacks.dlnfr(iji - 1) + (UN - dlt) * zxx; let comaid = -e2 / params.callbacks.dlnfr(iji - 1) + dlt * zxx; vecl[ij - 1] = -combid * params.freq0.rad[iji - 1] - comaid * params.freq0.rad[iji - 2]; } 2 // IJ1 = 2 } else { 1 // IJ1 = 1 }; // ======================================================================== // 1. 辐射传输组件 (Fortran lines 112-379) // ======================================================================== if nfreqe > 0 { if id == 1 { // 上边界条件 (Fortran lines 120-174) compute_upper_boundary(params, &mut vecl); } else if id < nd { // 内部点 (Fortran lines 178-245) compute_interior_point(params, &mut vecl); } else { // 下边界条件 (Fortran lines 249-379) compute_lower_boundary(params, &mut vecl, ij1); } } // ======================================================================== // 2. 流体静力学平衡 (Fortran lines 385-500) // ======================================================================== if inhe > 0 && nhe < vecl.len() { compute_hydrostatic(params, &mut vecl, nhe); } // ======================================================================== // 2a. z-d 关系 (Fortran lines 506-509) // 注意: Fortran 条件是 IF(INZD.LE.0.OR.ID.EQ.ND.OR.IDISK.EQ.0) GO TO 200 // 即: 仅在 INZD>0 且 ID 0 && id < nd && params.config.idisk != 0 { let ddp = (params.dm[id] - params.dm[id - 1]) * HALF; vecl[nzd] = params.zd[id] - params.zd[id - 1] + ddp / params.dens[id - 1] + ddp / params.dens[id]; } // ======================================================================== // 3. 辐射平衡 (Fortran lines 515-581) // ======================================================================== if inre > 0 { compute_radiative_equilibrium(params, &mut vecl, nre); } // ======================================================================== // 4. 统计平衡 (Fortran lines 587-614) // ======================================================================== // Fortran: IF(IABS(IFPOPR).GE.3.and.ifpopr.le.5) if inse > 0 && params.config.ifpopr.abs() >= 3 && params.config.ifpopr <= 5 { let stat_result = params.callbacks.call_statistical_equilibrium(id); let nlvexp = stat_result.nlvexp; for i in 0..nlvexp { let nse_i = nse + i; if nse_i < vecl.len() { if i < stat_result.popgrp.len() { vecl[nse_i] = stat_result.popgrp[i]; } // 设置零行 (Fortran lines 601, 610) if params.callbacks.igzero(i + 1, id) > 0 { vecl[nse_i] = 0.0; } } } } // ======================================================================== // 5. 电荷守恒 (Fortran lines 620-645) // ======================================================================== if inpc > 0 && npc < vecl.len() { let t = params.temp[id - 1]; let ane = params.elec[id - 1]; // 调用 STATE (Fortran line 630) let state_result = params.callbacks.call_state(2, id, t, ane); // 计算电荷守恒 (Fortran lines 632-645) let mut vpc = params.callbacks.qfix(id) + state_result.q * params.dens[id - 1] / params.wmm[id - 1] / params.callbacks.ytot(id); if params.config.ioptab == 0 { vpc *= params.callbacks.abund(params.config.iatref, id); } // 遍历原子 (Fortran lines 634-643) for iat in 0..params.callbacks.natom() { if params.callbacks.iifix(iat) != 1 { for i in params.callbacks.n0a(iat)..=params.callbacks.nka(iat) { let il = params.callbacks.ilk(i); let ch = params.callbacks.iz(params.callbacks.iel(i)) - 1.0; let ch = if il > 0 { params.callbacks.iz(il) + (params.callbacks.iz(il) - 1.0) * ane * params.callbacks.usums(il, id) } else { ch }; if params.callbacks.imodl(i) >= 0 { vpc += ch * params.callbacks.popul(i, id); } } } } vecl[npc] = ane - vpc; } // ======================================================================== // 6. 对流贡献 (Fortran lines 651-708) // ======================================================================== if hmix0 > 0.0 && id > 1 && id < nd { compute_convection(params, &mut vecl, nre, ndel); } // ======================================================================== // 跳过零种群行 (Fortran lines 712-719) // ======================================================================== let nn0 = nse + 100; // 假设 NN0 是一个合理的上限 let mut inonz = nse; for ii in nse..nn0.min(vecl.len()) { if params.callbacks.igzert(ii - nse + 1) == 0 { if inonz != ii { vecl[inonz] = vecl[ii]; } inonz += 1; } } RhsgenOutput { vecl } } /// 计算上边界条件 (Fortran lines 120-174) fn compute_upper_boundary(params: &mut RhsgenParams, vecl: &mut [f64]) { let cfg = ¶ms.config; let ddp = if !params.deldmz.is_empty() { params.deldmz[0] } else { 1e5 }; for ij in 0..cfg.nfreqe.min(params.freq0.abso.len()) { let ijt = params.callbacks.ijfr(ij + 1) - 1; // 转换为 0-indexed let abso0 = params.freq0.abso[ij]; let absop = params.freqp.abso[ij]; let dens = params.dens[0]; let omeg0 = if cfg.izscal == 0 { abso0 / dens } else { abso0 }; let omegp = if cfg.izscal == 0 { absop / dens } else { absop }; let dzp = omeg0 + omegp; let dtaup = dzp * ddp; let fk0 = if ij < params.freq0.fk.len() { params.freq0.fk[ij] } else { 1.0 }; let fkp = if ij < params.freqp.fk.len() { params.freqp.fk[ij] } else { 1.0 }; let rad0 = if ij < params.freq0.rad.len() { params.freq0.rad[ij] } else { 0.0 }; let radp = if ij < params.freqp.rad.len() { params.freqp.rad[ij] } else { 0.0 }; let alf1 = (fk0 * rad0 - fkp * radp) / dtaup; let scat0 = if ij < params.freq0.scat.len() { params.freq0.scat[ij] } else { 0.0 }; let scatp = if ij < params.freqp.scat.len() { params.freqp.scat[ij] } else { 0.0 }; let emis0 = if ij < params.freq0.emis.len() { params.freq0.emis[ij] } else { 0.0 }; let emisp = if ij < params.freqp.emis.len() { params.freqp.emis[ij] } else { 0.0 }; let mut s0 = if abso0.abs() > 1e-30 { (emis0 + scat0 * rad0) / abso0 } else { 0.0 }; let mut bs = HALF * dtaup; let mut cs = 0.0; let mut c2 = 0.0; let mut gam2 = 0.0; // Compton 散射贡献 (Fortran lines 146-149) if cfg.icompt > 0 { let compt_result = params.callbacks.call_compt0(ijt, 1, abso0); s0 += compt_result.cms; } // Spline/Hermitian 方法 (Fortran lines 151-161) if cfg.isplin > 0 { bs = dtaup * THIRD; cs = HALF * bs; let sp = if absop.abs() > 1e-30 { (emisp + scatp * radp) / absop } else { 0.0 }; c2 = cs / absop; gam2 = cs * (radp - sp); } let alf2 = bs * (rad0 - s0); let bet2 = alf2 + gam2; // RHS 元素 (Fortran line 170-171) if ij < vecl.len() { vecl[ij] = alf1 + bet2 + params.callbacks.fh(ijt) * rad0 - s0 * params.callbacks.q0(ijt); if cfg.iwinbl < 0 { vecl[ij] -= params.callbacks.hextrd(ijt); } } } } /// 计算内部点 (Fortran lines 178-245) fn compute_interior_point(params: &mut RhsgenParams, vecl: &mut [f64]) { let id = params.id; let cfg = ¶ms.config; let ddm = if id > 1 && params.deldmz.len() >= id - 1 { params.deldmz[id - 2] } else { 1e5 }; let ddp = if params.deldmz.len() >= id { params.deldmz[id - 1] } else { 1e5 }; for ij in 0..cfg.nfreqe.min(params.freq0.abso.len()) { let ijt = params.callbacks.ijfr(ij + 1) - 1; let dens_id = params.dens[id - 1]; let dens_im = params.dens[id - 2]; let dens_ip = params.dens[id]; let abso0 = params.freq0.abso[ij]; let absom = params.freqm.abso[ij]; let absop = params.freqp.abso[ij]; let omeg0 = if cfg.izscal == 0 { abso0 / dens_id } else { abso0 }; let omegm = if cfg.izscal == 0 { absom / dens_im } else { absom }; let omegp = if cfg.izscal == 0 { absop / dens_ip } else { absop }; let dzp = omeg0 + omegp; let dzm = omeg0 + omegm; let dtaup = dzp * ddp; let dtaum = dzm * ddm; let dtau0 = HALF * (dtaup + dtaum); let fk0 = if ij < params.freq0.fk.len() { params.freq0.fk[ij] } else { 1.0 }; let fkm = if ij < params.freqm.fk.len() { params.freqm.fk[ij] } else { 1.0 }; let fkp = if ij < params.freqp.fk.len() { params.freqp.fk[ij] } else { 1.0 }; let rad0 = if ij < params.freq0.rad.len() { params.freq0.rad[ij] } else { 0.0 }; let radm = if ij < params.freqm.rad.len() { params.freqm.rad[ij] } else { 0.0 }; let radp = if ij < params.freqp.rad.len() { params.freqp.rad[ij] } else { 0.0 }; let frd = fk0 * rad0; let alf1 = (frd - fkp * radp) / dtaup / dtau0; let gam1 = (frd - fkm * radm) / dtaum / dtau0; let bet1 = alf1 + gam1; let scatm = if ij < params.freqm.scat.len() { params.freqm.scat[ij] } else { 0.0 }; let scat0 = if ij < params.freq0.scat.len() { params.freq0.scat[ij] } else { 0.0 }; let scatp = if ij < params.freqp.scat.len() { params.freqp.scat[ij] } else { 0.0 }; let emism = if ij < params.freqm.emis.len() { params.freqm.emis[ij] } else { 0.0 }; let emis0 = if ij < params.freq0.emis.len() { params.freq0.emis[ij] } else { 0.0 }; let emisp = if ij < params.freqp.emis.len() { params.freqp.emis[ij] } else { 0.0 }; let mut s0 = if abso0.abs() > 1e-30 { (emis0 + scat0 * rad0) / abso0 } else { 0.0 }; // Fortran line 200: BS=UN (标准方法的默认值) let mut bs = UN; let mut bet2 = 0.0; // Compton 散射贡献 (Fortran lines 209-212) if cfg.icompt > 0 { let compt_result = params.callbacks.call_compt0(ijt, id, abso0); s0 += compt_result.cms; } // Spline 方法 (Fortran lines 214-225) if cfg.isplin == 1 { let as_ = dtaum / dtau0 * SIXTH; let cs = dtaup / dtau0 * SIXTH; bs = 0.666666666666667_f64; // Fortran line 220 let sm = if absom.abs() > 1e-30 { (emism + radm * scatm) / absom } else { 0.0 }; let sp = if absop.abs() > 1e-30 { (emisp + radp * scatp) / absop } else { 0.0 }; let alf2 = as_ * (radm - sm); let gam2_val = cs * (radp - sp); bet2 = alf2 + gam2_val; } // Hermitian 方法 (Fortran lines 226-238) else if cfg.isplin == 2 { let mut as_ = dtaup * dtaup / dtaum / dtau0; let mut cs = dtaum * dtaum / dtaup / dtau0; let sm = if absom.abs() > 1e-30 { (emism + radm * scatm) / absom } else { 0.0 }; let sp = if absop.abs() > 1e-30 { (emisp + radp * scatp) / absop } else { 0.0 }; as_ = (UN - HALF * as_) * SIXTH; cs = (UN - HALF * cs) * SIXTH; bs = UN - as_ - cs; // Fortran line 236 bet2 = as_ * (radm - sm) + cs * (radp - sp); } // RHS 元素 (Fortran line 242) // 注意: bs 在上面根据 ISPLIN 值设置,这里直接使用 if ij < vecl.len() { vecl[ij] = bet1 + bet2 + bs * (rad0 - s0); } } } /// 计算下边界条件 (Fortran lines 249-379) fn compute_lower_boundary(params: &mut RhsgenParams, vecl: &mut [f64], ij1: usize) { let id = params.id; let cfg = ¶ms.config; let mut t = params.temp[id - 1]; if cfg.tempbd > 0.0 { t = cfg.tempbd; } let hkt = HK / t; let ddm = if id > 1 && params.deldmz.len() >= id - 1 { params.deldmz[id - 2] } else { 1e5 }; // 恒星大气模式 (Fortran lines 255-351) // Fortran: IF(IDISK.EQ.0.OR.IFZ0.LT.0) THEN if cfg.idisk == 0 || cfg.ifz0 < 0 { // 辅助量: SUMB/SUMF/ZZ for integral form (Fortran lines 276-294) let mut zz = 0.0; if id < cfg.ndre && cfg.inre != 0 { let mut sumb = 0.0; let mut sumf = 0.0; for ij in 0..cfg.nfreqe.min(params.freq0.abso.len()) { let ijt = params.callbacks.ijfr(ij + 1); // 1-based for callback let fr = params.callbacks.freq(ijt); let fr15 = fr * 1e-15; let w = if ij < params.freq0.w.len() { params.freq0.w[ij] } else { 1.0 }; let x = hkt * fr; let ex = x.exp(); let plan = BN * fr15 * fr15 * fr15 / (ex - UN) * RRDIL; let dp = plan * x / t / (1.0 - x / ex) * w; sumb += (plan - params.freq0.rad[ij]) * w; let abso0 = params.freq0.abso[ij]; sumf += dp / abso0; } let fl = SIG4P * cfg.teff.powi(4); zz = (fl - HALF * sumb) / sumf; } // 主循环 (Fortran lines 298-351) for ij in (ij1 - 1)..cfg.nfreqe.min(params.freq0.abso.len()) { let ijt = params.callbacks.ijfr(ij + 1) - 1; // 0-based let dens_id = params.dens[id - 1]; let dens_im = params.dens[id - 2]; let abso0 = params.freq0.abso[ij]; let absom = params.freqm.abso[ij]; let omeg0 = if cfg.izscal == 0 { abso0 / dens_id } else { abso0 }; let omegm = if cfg.izscal == 0 { absom / dens_im } else { absom }; let dzm = omeg0 + omegm; let dtaum = dzm * ddm; let fk0 = if ij < params.freq0.fk.len() { params.freq0.fk[ij] } else { 1.0 }; let fkm = if ij < params.freqm.fk.len() { params.freqm.fk[ij] } else { 1.0 }; let rad0 = if ij < params.freq0.rad.len() { params.freq0.rad[ij] } else { 0.0 }; let radm = if ij < params.freqm.rad.len() { params.freqm.rad[ij] } else { 0.0 }; let gam1 = (fk0 * rad0 - fkm * radm) / dtaum; let mut bet2 = 0.0; let scatm = if ij < params.freqm.scat.len() { params.freqm.scat[ij] } else { 0.0 }; let scat0 = if ij < params.freq0.scat.len() { params.freq0.scat[ij] } else { 0.0 }; let emis0 = if ij < params.freq0.emis.len() { params.freq0.emis[ij] } else { 0.0 }; // 二阶边界条件 (Fortran lines 316-328) if cfg.ibc > 0 && cfg.ibc < 4 { let bs = dtaum * HALF; let mut s0 = if abso0.abs() > 1e-30 { (emis0 + scat0 * rad0) / abso0 } else { 0.0 }; // Compton 贡献 (Fortran lines 322-325) if cfg.icompt > 0 { let compt_result = params.callbacks.call_compt0(ijt, id, abso0); s0 += compt_result.cms; } bet2 = bs * (rad0 - s0); } // Planck 函数 (Fortran lines 330-342) let fr = params.callbacks.freq(ijt); let x = hkt * fr; let ex = x.exp(); let plan = BN * (fr * 1e-15).powi(3) / (ex - UN) * RRDIL; // INRE/NDRE 分支修改 GAM1 (Fortran lines 334-342) let mut gam1 = gam1; if cfg.inre == 0 || id >= cfg.ndre { // Fortran lines 335-336: 微分形式 let dplan = BN * (fr * 1e-15).powi(3) / ((HK * fr / params.temp[id - 2]).exp() - UN); gam1 = gam1 - (plan - dplan) / dtaum * THIRD; } else { // Fortran lines 338-342: 积分形式 let dp = plan * x / t / (1.0 - x / ex); let fi = dp / abso0; let x1 = fi * zz; gam1 = gam1 - x1; } // RHS 元素 (Fortran lines 346-350) if ij < vecl.len() { if cfg.ibc == 0 || cfg.ibc == 4 { vecl[ij] = gam1 + bet2 - HALF * (plan - rad0); } else { vecl[ij] = gam1 + bet2 - HALF * plan + params.callbacks.fhd(ijt) * rad0; } } } } else { // 吸积盘模式 (Fortran lines 355-378) for ij in (ij1 - 1)..cfg.nfreqe.min(params.freq0.abso.len()) { let dens_id = params.dens[id - 1]; let dens_im = params.dens[id - 2]; let abso0 = params.freq0.abso[ij]; let absom = params.freqm.abso[ij]; let omeg0 = if cfg.izscal == 0 { abso0 / dens_id } else { abso0 }; let omegm = if cfg.izscal == 0 { absom / dens_im } else { absom }; let dzm = omeg0 + omegm; let dtaum = dzm * ddm; let fk0 = if ij < params.freq0.fk.len() { params.freq0.fk[ij] } else { 1.0 }; let fkm = if ij < params.freqm.fk.len() { params.freqm.fk[ij] } else { 1.0 }; let rad0 = if ij < params.freq0.rad.len() { params.freq0.rad[ij] } else { 0.0 }; let radm = if ij < params.freqm.rad.len() { params.freqm.rad[ij] } else { 0.0 }; let scat0 = if ij < params.freq0.scat.len() { params.freq0.scat[ij] } else { 0.0 }; let emis0 = if ij < params.freq0.emis.len() { params.freq0.emis[ij] } else { 0.0 }; let frd = fk0 * rad0 - fkm * radm; let gam1 = frd / dtaum; let bs = dtaum * HALF; let s0 = if abso0.abs() > 1e-30 { (emis0 + scat0 * rad0) / abso0 } else { 0.0 }; let gam2 = bs * (rad0 - s0); if ij < vecl.len() { vecl[ij] = gam1 + gam2; } } } } /// 计算流体静力学平衡 (Fortran lines 385-500) fn compute_hydrostatic( params: &mut RhsgenParams, vecl: &mut [f64], nhe: usize, ) { let id = params.id; let cfg = ¶ms.config; if id == 1 { // 上边界条件 (Fortran lines 390-466) let mut grd = 0.0; if cfg.idisk == 0 || cfg.ibche == 0 { // 标准上边界 (Fortran lines 395-413) if cfg.nfreqe > 0 { for ij in 0..cfg.nfreqe.min(params.freq0.abso.len()) { let ijt = params.callbacks.ijfr(ij + 1); if !params.callbacks.lskip(1, ijt) { let w = if ij < params.freq0.w.len() { params.freq0.w[ij] } else { 1.0 }; let rad0 = if ij < params.freq0.rad.len() { params.freq0.rad[ij] } else { 0.0 }; let abso0 = params.freq0.abso[ij]; let fh_val = params.callbacks.fh(ijt); grd += w * fh_val * rad0 * abso0; } } } // Fortran lines 406-413 let x1 = PCK / params.dens[0]; let vt0 = HALF * params.vturb[0].powi(2) / params.dm[0] * params.wmm[0]; let psi0_nhe = params.callbacks.psi0(nhe + 1); // Fortran lines 412-413: VECL(NHE)=GRAV-BOLK*TEMP(ID)*PSI0(NHE)/DM(ID)-... vecl[nhe] = cfg.grav - BOLK * params.temp[0] * psi0_nhe / params.dm[0] - x1 * (grd + params.callbacks.fprd(1)) - vt0 / params.wmm[0] * params.dens[0]; } else if cfg.ibche == 1 { // 盘模式 - 新变体 (Fortran lines 416-432) let ccc = PCK / cfg.qgrav; let hr1 = ccc * SIG4P * cfg.teff.powi(4) * params.abrosd[0]; let psi0_nhe = params.callbacks.psi0(nhe + 1); let pg1 = BOLK * psi0_nhe * params.temp[0]; let hg1 = (TWO * pg1 / params.dens[0] / cfg.qgrav).sqrt(); let x = (params.zd[0] - hr1) / hg1; let f1 = if x < 3.0 { let x = if x < 0.0 { 0.0 } else { x }; 0.886226925_f64 * (x * x).exp() * params.callbacks.erfcx(x) } else { HALF * (UN - HALF / x / x) / x }; let ggg = params.dens[0] * hg1 * f1; vecl[nhe] = params.dm[0] - ggg; } else if cfg.ibche == 2 { // 盘模式 - 旧变体 (Fortran lines 434-462) if cfg.nfreqe > 0 { for ij in 0..cfg.nfreqe.min(params.freq0.abso.len()) { let ijt = params.callbacks.ijfr(ij + 1); if !params.callbacks.lskip(1, ijt) { let w = if ij < params.freq0.w.len() { params.freq0.w[ij] } else { 1.0 }; let rad0 = if ij < params.freq0.rad.len() { params.freq0.rad[ij] } else { 0.0 }; let abso0 = params.freq0.abso[ij]; let fh_val = params.callbacks.fh(ijt); grd += w * fh_val * rad0 * abso0; } } } let ccc = PCK / cfg.qgrav; let pr1 = ccc * (grd + params.callbacks.fprd(1)) / params.dens[0]; let psi0_nhe = params.callbacks.psi0(nhe + 1); let pg1 = BOLK * psi0_nhe * params.temp[0]; let hg1 = (TWO * pg1 / params.dens[0] / cfg.qgrav).sqrt(); let x = (params.zd[0] - pr1) / hg1; let f1 = if x < 3.0 { let x = if x < 0.0 { 0.0 } else { x }; 0.886226925_f64 * (x * x).exp() * params.callbacks.erfcx(x) } else { HALF * (UN - HALF / x / x) / x }; let ggg = hg1 * cfg.qgrav * HALF / f1; vecl[nhe] = params.dm[0] * ggg - pg1; } else { // 默认 (Fortran line 464) let psi0_nhe = params.callbacks.psi0(nhe + 1); vecl[nhe] = params.callbacks.pgas0() - BOLK * params.temp[0] * psi0_nhe; } } else { // 内部点 ID > 1 (Fortran lines 468-500) let mut grd = 0.0; if cfg.nfreqe > 0 { for ij in 0..cfg.nfreqe.min(params.freq0.abso.len()) { let ijt = params.callbacks.ijfr(ij + 1); if !params.callbacks.lskip(id, ijt) { let w = if ij < params.freq0.w.len() { params.freq0.w[ij] } else { 1.0 }; let fk0 = if ij < params.freq0.fk.len() { params.freq0.fk[ij] } else { 1.0 }; let fkm = if ij < params.freqm.fk.len() { params.freqm.fk[ij] } else { 1.0 }; let rad0 = if ij < params.freq0.rad.len() { params.freq0.rad[ij] } else { 0.0 }; let radm = if ij < params.freqm.rad.len() { params.freqm.rad[ij] } else { 0.0 }; grd += (fk0 * rad0 - fkm * radm) * w; } } } let vt0 = HALF * params.vturb[id - 1].powi(2) * params.wmm[id - 1]; let vtm = HALF * params.vturb[id - 2].powi(2) * params.wmm[id - 2]; // Fortran line 487: IF(IDISK.EQ.1) GRAV=QGRAV*(ZD(ID)+ZD(ID-1))*HALF let grav_val = if cfg.idisk == 1 { cfg.qgrav * (params.zd[id - 1] + params.zd[id - 2]) * HALF } else { cfg.grav }; if nhe < vecl.len() { let psi0_nhe = params.callbacks.psi0(nhe + 1); let psim_nhe = params.callbacks.psim(nhe + 1); // Fortran lines 488-499: IZSCAL 分支 if cfg.izscal == 0 { vecl[nhe] = grav_val * (params.dm[id - 1] - params.dm[id - 2]) - BOLK * (params.temp[id - 1] * psi0_nhe - params.temp[id - 2] * psim_nhe) - PCK * (grd + params.callbacks.fprd(id)) - vt0 / params.wmm[id - 1] * params.dens[id - 1] + vtm / params.wmm[id - 2] * params.dens[id - 2]; } else { let gravz = grav_val * (params.zd[id - 1] - params.zd[id - 2]); vecl[nhe] = -gravz * (params.dens[id - 1] + params.dens[id - 2]) * HALF - BOLK * (params.temp[id - 1] * psi0_nhe - params.temp[id - 2] * psim_nhe) - PCK * (grd + params.callbacks.fprd(id)) - vt0 / params.wmm[id - 1] * params.dens[id - 1] + vtm / params.wmm[id - 2] * params.dens[id - 2]; } } } } /// 计算辐射平衡 (Fortran lines 515-581) fn compute_radiative_equilibrium(params: &mut RhsgenParams, vecl: &mut [f64], nre: usize) { let id = params.id; let cfg = ¶ms.config; // 检查是否跳过 (Fortran lines 518-524) let ittc = (cfg.nretc.abs() / 100) as i32; if cfg.iter > ittc && id <= (cfg.nretc.abs() % 100) as usize { if cfg.nretc < 0 && nre < vecl.len() { vecl[nre] = params.temp[id] - params.temp[id - 1]; } return; } // 积分方程部分 (Fortran lines 531-549) if nre < vecl.len() { vecl[nre] = params.fcool[id - 1]; if cfg.idisk == 1 { vecl[nre] -= params.callbacks.tvisc(id) * params.reint[id - 1]; } if params.reint[id - 1] > 0.0 && cfg.nfreqe > 0 { for ij in 0..cfg.nfreqe.min(params.freq0.abso.len()) { let heat = params.freq0.abso[ij] - params.freq0.scat[ij]; let w = if ij < params.freq0.w.len() { params.freq0.w[ij] } else { 1.0 }; let rad0 = if ij < params.freq0.rad.len() { params.freq0.rad[ij] } else { 0.0 }; let emis0 = if ij < params.freq0.emis.len() { params.freq0.emis[ij] } else { 0.0 }; vecl[nre] -= (heat * rad0 - emis0) * w * params.reint[id - 1]; // Compton 贡献 (Fortran lines 542-546) if cfg.icompt > 5 { let ijt = params.callbacks.ijfr(ij + 1); let compt_result = params.callbacks.call_compt0(ijt, id, params.freq0.abso[ij]); vecl[nre] += params.freq0.abso[ij] * compt_result.cms * w * params.reint[id - 1]; } } } // 微分方程部分 (Fortran lines 555-581) if params.redif[id - 1] > 0.0 { let mut teffd = cfg.teff.powi(4); if cfg.idisk == 1 { teffd *= (UN - params.callbacks.thetav(id)); } vecl[nre] += SIG4P * teffd * params.redif[id - 1]; if id > 1 { let ddm = if params.deldmz.len() >= id - 1 { params.deldmz[id - 2] } else { 1e5 }; for ij in 0..cfg.nfreqe.min(params.freq0.abso.len()) { let dens_id = params.dens[id - 1]; let dens_im = params.dens[id - 2]; let abso0 = params.freq0.abso[ij]; let absom = params.freqm.abso[ij]; let omeg0 = abso0 / dens_id; let omegm = absom / dens_im; let dtaum = (omeg0 + omegm) * ddm; let fk0 = if ij < params.freq0.fk.len() { params.freq0.fk[ij] } else { 1.0 }; let fkm = if ij < params.freqm.fk.len() { params.freqm.fk[ij] } else { 1.0 }; let rad0 = if ij < params.freq0.rad.len() { params.freq0.rad[ij] } else { 0.0 }; let radm = if ij < params.freqm.rad.len() { params.freqm.rad[ij] } else { 0.0 }; let w = if ij < params.freq0.w.len() { params.freq0.w[ij] } else { 1.0 }; let frd = fk0 * rad0 - fkm * radm; let gamr = frd / dtaum; vecl[nre] -= w * gamr * params.redif[id - 1]; } } else { for ij in 0..cfg.nfreqe.min(params.freq0.abso.len()) { let ijt = params.callbacks.ijfr(ij + 1); let w = if ij < params.freq0.w.len() { params.freq0.w[ij] } else { 1.0 }; let rad0 = if ij < params.freq0.rad.len() { params.freq0.rad[ij] } else { 0.0 }; vecl[nre] -= w * params.callbacks.fh(ijt) * rad0 * params.redif[id - 1]; } } } } } /// 计算对流贡献 (Fortran lines 651-708) fn compute_convection(params: &mut RhsgenParams, vecl: &mut [f64], nre: usize, ndel: usize) { let id = params.id; let cfg = ¶ms.config; // 上边界条件 (Fortran lines 657-661) if id == 1 { params.delta[0] = 0.0; params.flxc[0] = 0.0; return; } // 内部点 (Fortran lines 665-706) let t = params.temp[id - 1]; let tm = params.temp[id - 2]; let p = params.callbacks.ptotal(id); let pm = params.callbacks.ptotal(id - 1); let pg = params.callbacks.pgs(id); let pgm = params.callbacks.pgs(id - 1); let prad = p - pg - HALF * params.dens[id - 1] * params.vturb[id - 1].powi(2); let pradm = pm - pgm - HALF * params.dens[id - 2] * params.vturb[id - 2].powi(2); let t0 = HALF * (t + tm); let p0 = HALF * (p + pm); let pg0 = HALF * (pg + pgm); let pr0 = HALF * (prad + pradm); let ab0 = HALF * (params.abrosd[id - 1] + params.abrosd[id - 2]); let dlt = if (p - pm).abs() > 1e-30 && t0.abs() > 1e-30 { (t - tm) / (p - pm) * p0 / t0 } else { 0.0 }; params.delta[id - 1] = dlt; if cfg.indl > 0 && ndel < vecl.len() { // Fortran line 680: VECL(NDEL)=DELTA(ID)-DLT // Note: DELTA(ID) was just set to DLT, so this is 0.0 vecl[ndel] = params.delta[id - 1] - dlt; } // 对流通量 (Fortran lines 685-686) let convec_params = ConvecParams { id, t: t0, ptot: p0, pg: pg0, prad: pr0, abros: ab0, delta: dlt, taurs: 0.0, config: params.convec_config.clone(), trmder_config: None, therm_tables: None, }; let convec_out = convec(&convec_params); let flxcnv = convec_out.flxcnv; params.flxc[id - 1] = flxcnv; if params.redif[id - 1] > 0.0 && nre < vecl.len() { vecl[nre] -= flxcnv * params.redif[id - 1]; } // 第二次 CONVEC 调用 (Fortran lines 691-706) if params.reint[id - 1] > 0.0 && cfg.iconv <= 2 && id < params.nd { let tp = params.temp[id]; let pmp = params.callbacks.ptotal(id + 1); let pgp = params.callbacks.pgs(id + 1); let pradp = pmp - pgp - HALF * params.dens[id] * params.vturb[id].powi(2); let t0p = HALF * (t + tp); let p0p = HALF * (p + pmp); let pg0p = HALF * (pg + pgp); let pr0p = HALF * (prad + pradp); let ab0p = HALF * (params.abrosd[id - 1] + params.abrosd[id]); let dltp = if (pmp - p).abs() > 1e-30 && (tp + t).abs() > 1e-30 { (tp - t) / (pmp - p) * (pmp + p) / (tp + t) } else { 0.0 }; let convec_params2 = ConvecParams { id: id + 1, t: t0p, ptot: p0p, pg: pg0p, prad: pr0p, abros: ab0p, delta: dltp, taurs: 0.0, config: params.convec_config.clone(), trmder_config: None, therm_tables: None, }; let convec_out2 = convec(&convec_params2); let flxcp = convec_out2.flxcnv; let delm = (params.dm[id] - params.dm[id - 1]) * HALF; let rdelm = params.dens[id - 1] / delm; vecl[nre] -= rdelm * (flxcp - flxcnv) * params.reint[id - 1]; } } #[cfg(test)] mod tests { use super::*; fn create_test_freq_data(n: usize) -> (Vec, Vec, Vec, Vec, Vec, Vec) { let w = vec![1.0; n]; let rad = vec![1e10; n]; let abso = vec![0.1; n]; let emis = vec![1e9; n]; let scat = vec![0.01; n]; let fk = vec![1.0; n]; (w, rad, abso, emis, scat, fk) } #[test] fn test_rhsgen_upper_boundary() { let nd = 5; let (w0, rad0, abso0, emis0, scat0, fk0) = create_test_freq_data(10); let (wp, radp, absop, emisp, scatp, fkp) = create_test_freq_data(10); let freq0 = RhsgenFreqData { w: &w0, rad: &rad0, abso: &abso0, emis: &emis0, scat: &scat0, fk: &fk0, }; let freqm = RhsgenFreqData { w: &[], rad: &[], abso: &[], emis: &[], scat: &[], fk: &[], }; let freqp = RhsgenFreqData { w: &wp, rad: &radp, abso: &absop, emis: &emisp, scat: &scatp, fk: &fkp, }; let temp = vec![10000.0, 9500.0, 9000.0, 8500.0, 8000.0]; let dens = vec![1e-7; nd]; let dm = vec![1e2; nd]; let wmm = vec![1.4e-24; nd]; let vturb = vec![2e5; nd]; let elec = vec![1e-8; nd]; let zd = vec![1.0; nd]; let mut delta = vec![0.0; nd]; let mut flxc = vec![0.0; nd]; let fcool = vec![0.0; nd]; let abrosd = vec![0.4; nd]; let redif = vec![1.0; nd]; let reint = vec![0.0; nd]; let deldmz = vec![1e5; nd]; let callbacks = DefaultCallbacks; let mut params = RhsgenParams { id: 1, nd, temp: &temp, dens: &dens, dm: &dm, wmm: &wmm, vturb: &vturb, elec: &elec, zd: &zd, delta: &mut delta, flxc: &mut flxc, fcool: &fcool, abrosd: &abrosd, redif: &redif, reint: &reint, deldmz: &deldmz, freq0: &freq0, freqm: &freqm, freqp: &freqp, config: RhsgenConfig { hmix0: -1.0, // 禁用对流 ..Default::default() }, convec_config: ConvecConfig::default(), callbacks: &callbacks, }; let result = rhsgen(&mut params); assert!(result.vecl.len() > 0); // 上边界时对流应被禁用 assert_eq!(params.flxc[0], 0.0); } #[test] fn test_rhsgen_interior_point() { let nd = 5; let (w0, rad0, abso0, emis0, scat0, fk0) = create_test_freq_data(10); let (wm, radm, absom, emism, scatm, fkm) = create_test_freq_data(10); let (wp, radp, absop, emisp, scatp, fkp) = create_test_freq_data(10); let freq0 = RhsgenFreqData { w: &w0, rad: &rad0, abso: &abso0, emis: &emis0, scat: &scat0, fk: &fk0, }; let freqm = RhsgenFreqData { w: &wm, rad: &radm, abso: &absom, emis: &emism, scat: &scatm, fk: &fkm, }; let freqp = RhsgenFreqData { w: &wp, rad: &radp, abso: &absop, emis: &emisp, scat: &scatp, fk: &fkp, }; let temp = vec![10000.0, 9500.0, 9000.0, 8500.0, 8000.0]; let dens = vec![1e-7; nd]; let dm = vec![1e2; nd]; let wmm = vec![1.4e-24; nd]; let vturb = vec![2e5; nd]; let elec = vec![1e-8; nd]; let zd = vec![1.0; nd]; let mut delta = vec![0.0; nd]; let mut flxc = vec![0.0; nd]; let fcool = vec![0.0; nd]; let abrosd = vec![0.4; nd]; let redif = vec![1.0; nd]; let reint = vec![0.0; nd]; let deldmz = vec![1e5; nd]; let callbacks = DefaultCallbacks; let mut params = RhsgenParams { id: 3, // 内部点 nd, temp: &temp, dens: &dens, dm: &dm, wmm: &wmm, vturb: &vturb, elec: &elec, zd: &zd, delta: &mut delta, flxc: &mut flxc, fcool: &fcool, abrosd: &abrosd, redif: &redif, reint: &reint, deldmz: &deldmz, freq0: &freq0, freqm: &freqm, freqp: &freqp, config: RhsgenConfig { hmix0: -1.0, ..Default::default() }, convec_config: ConvecConfig::default(), callbacks: &callbacks, }; let result = rhsgen(&mut params); assert!(result.vecl.len() > 0); } }