//! 预条件化辐射速率计算 - RATSP1。 //! //! 重构自 TLUSTY `ratsp1.f` //! //! 计算辐射跃迁的上下行速率,包括连续谱和谱线跃迁。 //! 支持标准模式和 ODF 采样模式。 use crate::state::constants::{UN, PCK, MDEPTH, MTRANS}; // 物理常数 /// 4π/c const PGRD: f64 = 4.1916825e-10; /// 谱线常数 const OSC_CONST: f64 = 0.02654; // ============================================================================ // 参数结构体 // ============================================================================ /// RATSP1 配置参数 #[derive(Debug, Clone)] pub struct Ratsp1Config { /// 深度点数 pub nd: usize, /// 频率点数 pub nfreq: usize, /// 束缚-自由跃迁数 pub ntranc: usize, /// 总跃迁数 pub ntrans: usize, /// PRD 跃迁数 pub ntrprd: usize, /// 当前迭代次数 pub iter: i32, /// 最后一次迭代标志 pub lfin: bool, /// ODF 采样标志 (0: 标准, >=1: ODF) pub ispodf: i32, /// 表格不透明度标志 (<0: 仅使用表格) pub ioptab: i32, /// 散射处理标志 (0: 包含散射, >0: 不包含) pub ilpsct: i32, /// 混合长度参数 (>0: 使用 Rosseland) pub hmix0: f64, /// 深度记录标志 (<=0: 计算完整 Rosseland) pub ndre: i32, } impl Default for Ratsp1Config { fn default() -> Self { Self { nd: 50, nfreq: 100, ntranc: 10, ntrans: 100, ntrprd: 0, iter: 1, lfin: false, ispodf: 0, ioptab: 0, ilpsct: 0, hmix0: 0.0, ndre: 0, } } } /// RATSP1 模型状态参数 #[derive(Debug)] pub struct Ratsp1ModelState<'a> { // 深度点数据 (nd 个元素) /// 温度倒数倒数 h/kT pub hkt1: &'a [f64], /// 深度差分 deldmz pub deldmz: &'a [f64], /// 密度倒数 dens1 pub dens1: &'a [f64], /// 柱质量密度 pub dm: &'a [f64], /// 总粒子密度 pub dens: &'a [f64], /// 散射修正因子 crsw pub crsw: &'a [f64], // 频率数据 (nfreq 个元素) /// 频率数组 pub freq: &'a [f64], /// 频率权重 pub w: &'a [f64], /// 归一化权重 pub w0e: &'a [f64], /// 频率索引标志 ijx pub ijx: &'a [i32], // 不透明度数据 (nd 个元素) /// 总吸收系数 pub absot: &'a [f64], /// 吸收系数 (单频率) - 可被回调函数修改 pub abso1: &'a mut [f64], /// 电子散射系数 pub elscat: &'a [f64], // 辐射场数据 /// 辐射强度 rad1 (nd) pub rad1: &'a [f64], /// 上边界强度 fh (nfreq) pub fh: &'a [f64], /// 氦外辐射 hextrd (nfreq) pub hextrd: &'a [f64], /// 辐射场积分 fak1 (nd) pub fak1: &'a [f64], /// 归一化普朗克函数 bnue (nfreq) pub bnue: &'a [f64], // Lambda 算子 /// Lambda 算子对角 ali1 (nd) pub ali1: &'a [f64], // 跃迁数据 /// 截面 cross (ntranc × nfreq) pub cross: &'a [f64], /// 低能级索引 ilow (ntrans) pub ilow: &'a [i32], /// 高能级索引 iup (ntrans) pub iup: &'a [i32], /// 跃迁索引 itra (mlevel × mlevel) pub itra: &'a [i32], /// 束缚-自由跃迁索引 itrbf (ntranc) pub itrbf: &'a [i32], /// 零占据标志 ipzero (mlevel × nd) pub ipzero: &'a [i32], /// Macfarlane 下沉修正索引 mcdw (ntrans) pub mcdw: &'a [i32], /// 合并能级索引 imrg (mlevel) pub imrg: &'a [i32], /// 占据概率修正标志 ifwop (mlevel) pub ifwop: &'a [i32], /// 下沉修正因子 dwf1 (mmcdw × nd) pub dwf1: &'a [f64], /// 合并能级截面 sgmrg (mmer × nd) pub sgmg: &'a [f64], /// 吸收系数 abtra (ntrans × nd) pub abtra: &'a [f64], /// 发射系数 emtra (ntrans × nd) pub emtra: &'a [f64], // 谱线数据 /// 主谱线索引 ijlin (nfreq) pub ijlin: &'a [i32], /// 谱线轮廓 prflin (nd × nfreq) pub prflin: &'a [f64], /// 重叠谱线数 nlines (nfreq) pub nlines: &'a [i32], /// 重叠谱线索引 itrlin (maxlines × nfreq) pub itrlin: &'a [i16], /// 频率起点 ifr0 (ntrans) pub ifr0: &'a [i32], /// 频率终点 ifr1 (ntrans) pub ifr1: &'a [i32], /// PRD 跃迁索引 iprd (ntrans) pub iprd: &'a [i32], /// 振子强度 osc0 (ntrans) pub osc0: &'a [f64], /// 显式谱线标志 linexp (ntrans) pub linexp: &'a [bool], // ODF 数据 /// 频率偏移 kfr0 (ntrans) pub kfr0: &'a [i32], /// 谱线索引 indexp (ntrans) pub indexp: &'a [i32], /// 铁截面 sigfe (ntab × nfreq) pub sigfe: &'a [f64], /// 深度相关索引 jidi (nd) pub jidi: &'a [i32], /// 插值因子 xjid (nd) pub xjid: &'a [f64], // 输出数组 /// 上行辐射速率 (ntrans × nd) pub rru: &'a mut [f64], /// 下行辐射速率 (ntrans × nd) pub rrd: &'a mut [f64], /// 总辐射压 (nd) pub pradt: &'a mut [f64], /// 吸收辐射压 (nd) pub prada: &'a mut [f64], /// 辐射通量 (nd) pub flrd: &'a mut [f64], /// PRD J 积分 (ntrprd × nd) pub pjbar: &'a mut [f64], /// Rosseland 平均不透明度 (nd) pub abrosd: &'a mut [f64], /// Rosseland 累积 (nd) pub sumdpl: &'a mut [f64], /// PRD 零点 pub prd0: &'a mut f64, } /// RATSP1 输出结构体 #[derive(Debug)] pub struct Ratsp1Output { /// 最小辐射压比 pub prdx: f64, } // ============================================================================ // 主函数 // ============================================================================ /// 计算预条件化辐射速率。 /// /// # 参数 /// - `config`: 配置参数 /// - `model`: 模型状态 /// - `opacf1_fn`: 不透明度计算函数 /// - `rtefr1_fn`: 辐射转移计算函数 /// - `rosstd_fn`: Rosseland 平均计算函数 (可选) /// /// # 返回值 /// - `Ratsp1Output`: 包含最小辐射压比 #[allow(clippy::too_many_arguments)] pub fn ratsp1( config: &Ratsp1Config, model: &mut Ratsp1ModelState, opacf1_fn: F, rtefr1_fn: G, rosstd_fn: Option, ) -> Ratsp1Output where F: Fn(usize, &mut Ratsp1ModelState), G: Fn(usize, &mut Ratsp1ModelState), H: Fn(usize, &mut Ratsp1ModelState), { let nd = config.nd; let nfreq = config.nfreq; let ntranc = config.ntranc; let ntrans = config.ntrans; let ntrprd = config.ntrprd; // 工作数组 let mut ehk = vec![0.0; nd]; let mut alab = vec![0.0; nd]; // 判断是否计算 Rosseland let lross = (config.ndre <= 0 && config.iter == 1) || config.lfin || config.hmix0 > 0.0; // ======================================================================== // 初始化输出数组 // ======================================================================== for id in 0..nd { model.pradt[id] = 0.0; model.prada[id] = 0.0; model.flrd[id] = 0.0; for itr in 0..ntrans { model.rru[itr * nd + id] = 0.0; model.rrd[itr * nd + id] = 0.0; } for itrp in 0..ntrprd { model.pjbar[itrp * nd + id] = 0.0; } } *model.prd0 = 0.0; // 初始化 Rosseland 数组 if lross { for id in 0..nd { model.abrosd[id] = 0.0; model.sumdpl[id] = 0.0; } } // ======================================================================== // 频率循环 // ======================================================================== for ij in 0..nfreq { // 跳过无效频率 if model.ijx[ij] == -1 { continue; } let fr = model.freq[ij]; let w0 = model.w0e[ij]; let ww = model.w[ij]; // 计算不透明度和辐射转移 opacf1_fn(ij, model); rtefr1_fn(ij, model); // 计算 Rosseland 平均 if lross { if let Some(ref ros_fn) = rosstd_fn { ros_fn(ij, model); } } // 通量梯度 let fluxw = model.w[ij] * model.rad1[0] * model.fh[ij]; // GRADF 在原代码中定义但未使用 // 上边界通量 model.flrd[0] += ww * model.fh[ij] * model.rad1[0] - ww * model.hextrd[ij]; // 深度点通量 for id in 1..nd { let dt = UN / (model.absot[id] + model.absot[id - 1]) / model.deldmz[id - 1]; let fl = (model.rad1[id] * model.fak1[id] - model.rad1[id - 1] * model.fak1[id - 1]) * dt; model.flrd[id] += ww * fl; } // 跳过仅表格模式 if config.ioptab < 0 { continue; } // ==================================================================== // 连续谱跃迁 // ==================================================================== for id in 0..nd { ehk[id] = (-model.hkt1[id] * fr).exp(); alab[id] = if config.ilpsct == 0 { model.ali1[id] / (model.abso1[id] - model.elscat[id]) } else { model.ali1[id] / model.abso1[id] }; for ibft in 0..ntranc { let itr = model.itrbf[ibft] as usize; if itr == 0 { continue; } let itr_idx = itr - 1; let sg = model.cross[ibft * nfreq + ij]; if sg <= 0.0 { continue; } let ii = model.ilow[itr_idx] as usize; let jj = model.iup[itr_idx] as usize; if ii == 0 || jj == 0 { continue; } let ii_idx = ii - 1; let jj_idx = jj - 1; // 检查零占据 if model.ipzero[ii_idx * nd + id] != 0 || model.ipzero[jj_idx * nd + id] != 0 { continue; } // 计算截面修正 let sg_final = if model.ifwop[ii_idx] >= 0 { let icdw = model.mcdw[itr_idx]; if icdw >= 1 { sg * model.dwf1[(icdw as usize - 1) * nd + id] } else { sg } } else { let imer = model.imrg[ii_idx] as usize; if imer > 0 { sg * model.sgmg[(imer - 1) * nd + id] } else { sg } }; let sgw0 = sg_final * w0; let rlam = sg_final * alab[id]; let elin = model.emtra[itr_idx * nd + id] * ehk[id]; let radres = model.rad1[id] - rlam * model.bnue[ij] * elin; let bnures = model.bnue[ij] * (UN - rlam * (model.abtra[itr_idx * nd + id] - elin)); model.rru[itr_idx * nd + id] += sgw0 * radres; model.rrd[itr_idx * nd + id] += sgw0 * (radres + bnures) * ehk[id]; } } // ==================================================================== // 谱线跃迁 // ==================================================================== if config.ispodf == 0 { // 标准模式 if model.ijlin[ij] > 0 { // 主谱线 let itr = (model.ijlin[ij] as usize) - 1; let ii = model.ilow[itr] as usize; let jj = model.iup[itr] as usize; if ii > 0 && jj > 0 { let ii_idx = ii - 1; let jj_idx = jj - 1; for id in 0..nd { if model.ipzero[ii_idx * nd + id] != 0 || model.ipzero[jj_idx * nd + id] != 0 { continue; } let sg = model.prflin[id * nfreq + ij]; let sgw0 = sg * w0; let rlam = sg * alab[id]; let elin = model.emtra[itr * nd + id] * ehk[id]; let radres = model.rad1[id] - rlam * model.bnue[ij] * elin; let bnures = model.bnue[ij] * (UN - rlam * (model.abtra[itr * nd + id] - elin)); model.rru[itr * nd + id] += sgw0 * radres; model.rrd[itr * nd + id] += sgw0 * (radres + bnures) * ehk[id]; } // PRD 贡献 let itrprd = model.iprd[itr]; if itrprd > 0 { let s = UN / (OSC_CONST * model.osc0[itr]); for id in 0..nd { let sg = model.prflin[id * nfreq + ij] * s; model.pjbar[(itrprd as usize - 1) * nd + id] += sg * model.w[ij] * model.rad1[id]; } } } } // 重叠谱线 let nlines = model.nlines[ij]; if nlines > 0 { for ilint in 0..nlines as usize { let itr = model.itrlin[ilint * nfreq + ij] as usize; if itr == 0 || model.linexp[itr - 1] { continue; } let itr_idx = itr - 1; // 频率插值 let ij0 = model.ifr0[itr_idx] as usize; let mut ij0_adj = ij0; for ijt in ij0..=model.ifr1[itr_idx] as usize { if model.freq[ijt] <= fr { ij0_adj = ijt; break; } } let ij1 = ij0_adj - 1; let a1 = if ij1 < nfreq && ij0_adj < nfreq { (fr - model.freq[ij0_adj]) / (model.freq[ij1] - model.freq[ij0_adj]) } else { 0.0 }; let a2 = UN - a1; let ii = model.ilow[itr_idx] as usize; let jj = model.iup[itr_idx] as usize; if ii == 0 || jj == 0 { continue; } let ii_idx = ii - 1; let jj_idx = jj - 1; for id in 0..nd { if model.ipzero[ii_idx * nd + id] != 0 || model.ipzero[jj_idx * nd + id] != 0 { continue; } let sg = if ij1 < nfreq && ij0_adj < nfreq { a1 * model.prflin[id * nfreq + ij1] + a2 * model.prflin[id * nfreq + ij0_adj] } else { 0.0 }; let sgw0 = sg * w0; let rlam = sg * alab[id]; let elin = model.emtra[itr_idx * nd + id] * ehk[id]; let radres = model.rad1[id] - rlam * model.bnue[ij] * elin; let bnures = model.bnue[ij] * (UN - rlam * (model.abtra[itr_idx * nd + id] - elin)); model.rru[itr_idx * nd + id] += sgw0 * radres; model.rrd[itr_idx * nd + id] += sgw0 * (radres + bnures) * ehk[id]; } // PRD 贡献 let itrprd = model.iprd[itr_idx]; if itrprd > 0 { let s = UN / (OSC_CONST * model.osc0[itr_idx]); for id in 0..nd { let sg = if ij1 < nfreq && ij0_adj < nfreq { (a1 * model.prflin[id * nfreq + ij1] + a2 * model.prflin[id * nfreq + ij0_adj]) * s } else { 0.0 }; model.pjbar[(itrprd as usize - 1) * nd + id] += sg * model.w[ij] * model.rad1[id]; } } } } } else { // ODF 采样模式 let nlines = model.nlines[ij]; if nlines > 0 { for ilint in 0..nlines as usize { let itr = model.itrlin[ilint * nfreq + ij] as usize; if itr == 0 { continue; } let itr_idx = itr - 1; let kj = ij as i32 - model.ifr0[itr_idx] + model.kfr0[itr_idx]; let indxpa = model.indexp[itr_idx].abs(); let ii = model.ilow[itr_idx] as usize; let jj = model.iup[itr_idx] as usize; if ii == 0 || jj == 0 { continue; } let ii_idx = ii - 1; let jj_idx = jj - 1; if indxpa != 3 && indxpa != 4 { // 标准截面 for id in 0..nd { if model.ipzero[ii_idx * nd + id] != 0 || model.ipzero[jj_idx * nd + id] != 0 { continue; } let sg = model.prflin[id * nfreq + kj as usize]; let sgw0 = sg * w0; let rlam = sg * alab[id]; let elin = model.emtra[itr_idx * nd + id] * ehk[id]; let radres = model.rad1[id] - rlam * model.bnue[ij] * elin; let bnures = model.bnue[ij] * (UN - rlam * (model.abtra[itr_idx * nd + id] - elin)); model.rru[itr_idx * nd + id] += sgw0 * radres; model.rrd[itr_idx * nd + id] += sgw0 * (radres + bnures) * ehk[id]; } } else { // 铁截面插值 for id in 0..nd { if model.ipzero[ii_idx * nd + id] != 0 || model.ipzero[jj_idx * nd + id] != 0 { continue; } let kjd = model.jidi[id] as usize; let xjid = model.xjid[id]; let sg = if kjd > 0 && (kjd + 1) < nd && (kj as usize) < nfreq { (xjid * model.sigfe[kjd * nfreq + kj as usize] + (UN - xjid) * model.sigfe[(kjd + 1) * nfreq + kj as usize]) .exp() } else { 1.0 }; let sgw0 = sg * w0; let rlam = sg * alab[id]; let elin = model.emtra[itr_idx * nd + id] * ehk[id]; let radres = model.rad1[id] - rlam * model.bnue[ij] * elin; let bnures = model.bnue[ij] * (UN - rlam * (model.abtra[itr_idx * nd + id] - elin)); model.rru[itr_idx * nd + id] += sgw0 * radres; model.rrd[itr_idx * nd + id] += sgw0 * (radres + bnures) * ehk[id]; } } } } } } // ======================================================================== // 散射修正 // ======================================================================== for id in 0..nd { if model.crsw[id] != UN { for itr in 0..ntrans { model.rru[itr * nd + id] *= model.crsw[id]; model.rrd[itr * nd + id] *= model.crsw[id]; } } } // ======================================================================== // 辐射压 // ======================================================================== let mut prdx = 1.0; for id in 0..nd { model.pradt[id] *= PCK; model.prada[id] *= PCK; if model.prada[id] > 0.0 { let prdr = model.pradt[id] / model.prada[id]; if prdr < prdx { prdx = prdr; } } } *model.prd0 = *model.prd0 / model.dens1[0] * model.dm[0] * PCK; // ======================================================================== // Rosseland 平均 // ======================================================================== if lross { for id in 0..nd { if model.abrosd[id] > 0.0 { model.abrosd[id] = model.sumdpl[id] / (model.abrosd[id] * model.dens[id]); } } } Ratsp1Output { prdx } } // ============================================================================ // 测试 // ============================================================================ #[cfg(test)] mod tests { use super::*; #[test] fn test_ratsp1_config_default() { let config = Ratsp1Config::default(); assert_eq!(config.nd, 50); assert_eq!(config.nfreq, 100); assert_eq!(config.ntrans, 100); } #[test] fn test_ratsp1_basic() { let nd = 5; let nfreq = 3; let ntrans = 2; let config = Ratsp1Config { nd, nfreq, ntranc: 0, ntrans, ntrprd: 0, iter: 1, lfin: false, ispodf: 0, ioptab: -1, // 跳过跃迁计算 ilpsct: 0, hmix0: 0.0, ndre: 0, }; // 创建简单的测试数据 let hkt1 = vec![1e-4; nd]; let deldmz = vec![0.01; nd]; let dens1 = vec![1e-14; nd]; let dm = vec![0.01; nd]; let dens = vec![1e14; nd]; let crsw = vec![1.0; nd]; let freq = vec![1e14, 2e14, 3e14]; let w = vec![0.5; nfreq]; let w0e = vec![1.0; nfreq]; let ijx = vec![0; nfreq]; let absot = vec![1e-8; nd]; let mut abso1 = vec![1e-8; nd]; let elscat = vec![1e-10; nd]; let rad1 = vec![1e-10; nd]; let fh = vec![0.0; nfreq]; let hextrd = vec![0.0; nfreq]; let fak1 = vec![1.0; nd]; let bnue = vec![1e-10; nfreq]; let ali1 = vec![1.0; nd]; let cross = vec![]; let ilow = vec![1, 2]; let iup = vec![2, 3]; let itra = vec![]; let itrbf = vec![]; let ipzero = vec![0; 100 * nd]; let mcdw = vec![0; ntrans]; let imrg = vec![0; 100]; let ifwop = vec![0; 100]; let dwf1 = vec![]; let sgmg = vec![]; let abtra = vec![1e-8; ntrans * nd]; let emtra = vec![1e-20; ntrans * nd]; let ijlin = vec![0; nfreq]; let prflin = vec![0.0; nd * nfreq]; let nlines = vec![0; nfreq]; let itrlin = vec![0; 100 * nfreq]; let ifr0 = vec![1; ntrans]; let ifr1 = vec![nfreq as i32; ntrans]; let iprd = vec![0; ntrans]; let osc0 = vec![0.1; ntrans]; let linexp = vec![false; ntrans]; let kfr0 = vec![0; ntrans]; let indexp = vec![0; ntrans]; let sigfe = vec![]; let jidi = vec![0; nd]; let xjid = vec![0.0; nd]; let mut rru = vec![0.0; ntrans * nd]; let mut rrd = vec![0.0; ntrans * nd]; let mut pradt = vec![0.0; nd]; let mut prada = vec![0.0; nd]; let mut flrd = vec![0.0; nd]; let mut pjbar = vec![]; let mut abrosd = vec![0.0; nd]; let mut sumdpl = vec![0.0; nd]; let mut prd0 = 0.0; let mut model = Ratsp1ModelState { hkt1: &hkt1, deldmz: &deldmz, dens1: &dens1, dm: &dm, dens: &dens, crsw: &crsw, freq: &freq, w: &w, w0e: &w0e, ijx: &ijx, absot: &absot, abso1: &mut abso1, elscat: &elscat, rad1: &rad1, fh: &fh, hextrd: &hextrd, fak1: &fak1, bnue: &bnue, ali1: &ali1, cross: &cross, ilow: &ilow, iup: &iup, itra: &itra, itrbf: &itrbf, ipzero: &ipzero, mcdw: &mcdw, imrg: &imrg, ifwop: &ifwop, dwf1: &dwf1, sgmg: &sgmg, abtra: &abtra, emtra: &emtra, ijlin: &ijlin, prflin: &prflin, nlines: &nlines, itrlin: &itrlin, ifr0: &ifr0, ifr1: &ifr1, iprd: &iprd, osc0: &osc0, linexp: &linexp, kfr0: &kfr0, indexp: &indexp, sigfe: &sigfe, jidi: &jidi, xjid: &xjid, rru: &mut rru, rrd: &mut rrd, pradt: &mut pradt, prada: &mut prada, flrd: &mut flrd, pjbar: &mut pjbar, abrosd: &mut abrosd, sumdpl: &mut sumdpl, prd0: &mut prd0, }; // 简单的回调函数 let opacf1_fn = |_: usize, m: &mut Ratsp1ModelState| { for id in 0..m.abso1.len() { m.abso1[id] = 1e-8; } }; let rtefr1_fn = |_: usize, _m: &mut Ratsp1ModelState| {}; let output = ratsp1(&config, &mut model, opacf1_fn, rtefr1_fn, None::); // 验证输出 assert_eq!(output.prdx, 1.0); // 由于所有 prada 为 0 assert_eq!(model.flrd.len(), nd); } }