//! NLTE Lucy-Unsöld 温度修正方案。 //! //! 重构自 TLUSTY `lucy.f`。 //! //! # 功能 //! //! 实现 Werner & Dreizler 的 NLTE 温度修正方案: //! 1. 计算辐射加热/冷却率 //! 2. 计算 Eddington 因子 //! 3. 应用温度修正 //! 4. 积分流体静力学平衡 //! 5. 更新粒子数 //! //! # 算法 //! //! 基于辐射平衡方程: //! - HEAT = 加热率(吸收 - 发射) //! - DELH = 辐射流偏离 //! - DELTAT = 温度修正 use crate::tlusty::state::constants::{BN, BOLK, HALF, HK, MDEPTH, MFREQ, MTRANS, SIG4P, UN}; // f2r_depends: COLIS, CONCOR, ELCOR, ODFMER, OPACFL, OPAINI, SABOLF, STEQEQ, TDPINI, WNSTOR // ============================================================================ // 常量 // ============================================================================ /// 1/3 const THIRD: f64 = 1.0 / 3.0; // ============================================================================ // 配置结构体 // ============================================================================ /// LUCY 配置参数。 #[derive(Debug, Clone)] pub struct LucyConfig { /// Lucy 迭代次数上限 (ITLUCY) pub itlucy: i32, /// 加速开始迭代 (IACLT) pub iaclt: i32, /// 加速间隔 (IACLDT) pub iacldt: i32, /// 流体静力学修正标志 (IHECOR) pub ihecor: i32, /// 是否 LTE (LTE) pub lte: bool, /// LCHC 标志 pub lchc: bool, /// 电子密度修正起始迭代 (IELCOR) pub ielcor: i32, /// 当前迭代次数 (ITER) pub iter: i32, /// Lucy 跃迁列表 (NTRL) pub ntrl: i32, /// Lucy 跃迁标志 (ILUCTR) pub iluctr: Vec, } impl Default for LucyConfig { fn default() -> Self { Self { itlucy: 0, iaclt: 10, iacldt: 1, ihecor: 1, lte: false, lchc: false, ielcor: 10, iter: 0, ntrl: 0, iluctr: vec![0; MTRANS], } } } // ============================================================================ // 输入参数结构体 // ============================================================================ /// LUCY 模型参数(只读引用)。 pub struct LucyModelParams<'a> { /// 深度点数 (ND) pub nd: usize, /// 频率点数 (NFREQ) pub nfreq: usize, /// 跃迁数 (NTRANS) pub ntrans: usize, /// 有效温度 (TEFF) pub teff: f64, /// 表面重力加速度 (GRAV) pub grav: f64, /// 参考辐射压 (PRD0) pub prd0: f64, // 深度相关数组 [nd] /// 深度 (柱质量密度, g/cm²) pub dm: &'a [f64], /// 温度 (K) pub temp: &'a [f64], /// 电子密度 (cm⁻³) pub elec: &'a [f64], /// 总粒子密度 (cm⁻³) pub dens: &'a [f64], /// 密度倒数 (1/DENS) pub dens1: &'a [f64], /// 平均分子量 pub wmm: &'a [f64], /// 深度间隔 [nd-1] pub deldm: &'a [f64], /// 湍流速度 pub vturb: &'a [f64], /// 辐射压力梯度 pub pradt: &'a [f64], /// 气体压力 [nd] pub pgs: &'a mut [f64], // 频率相关数组 [nfreq] /// 频率 (Hz) pub freq: &'a [f64], /// 频率权重 pub w: &'a [f64], /// H 函数 pub fh: &'a [f64], /// 外辐射场 pub hextrd: &'a [f64], // ALI 相关数组 /// LAC2T 标志 pub lac2t: bool, } /// LUCY 输出结果。 #[derive(Debug, Clone)] pub struct LucyOutput { /// 新温度 [nd] pub temp: Vec, /// 新电子密度 [nd] pub elec: Vec, /// 新总密度 [nd] pub dens: Vec, /// 密度倒数 [nd] pub dens1: Vec, /// 气体压力 [nd] pub pgs: Vec, /// Lucy 迭代计数 pub ilucy: i32, /// LAC2T 标志 pub lac2t: bool, /// 最大加热偏差 pub dhhmx1: f64, } // ============================================================================ // 辅助结构体 - 迭代状态 // ============================================================================ /// Lucy 迭代内部状态。 struct LucyState { /// 加热率 [nd] heat: Vec, /// 加热/普朗克比 [nd] heab: Vec, /// 零深度吸收 [nd] absz: Vec, /// 普朗克加权吸收 [nd] absp: Vec, /// 流加权吸收 [nd] absh: Vec, /// 总 J 积分 [nd] totj: Vec, /// 总 H 积分 [nd] toth: Vec, /// 总 B 积分 [nd] totb: Vec, /// Eddington 因子 [nd] eddf: Vec, /// 流偏离 [nd] delh: Vec, /// 光学深度 [nd] tau: Vec, /// 温度修正 [nd] deltat: Vec, /// 温度修正分量1 [nd] dt1: Vec, /// 温度修正分量2 [nd] dt2: Vec, /// 粒子数密度 [nd] antc: Vec, /// 电子分数 [nd] xe: Vec, /// 温度历史 0 [nd] tem0: Vec, /// 温度历史 1 [nd] tem1: Vec, /// 温度历史 2 [nd] tem2: Vec, /// 温度历史 3 [nd] tem3: Vec, /// Eddington H 比值 eddh: f64, } impl LucyState { fn new(nd: usize) -> Self { Self { heat: vec![0.0; nd], heab: vec![0.0; nd], absz: vec![0.0; nd], absp: vec![0.0; nd], absh: vec![0.0; nd], totj: vec![0.0; nd], toth: vec![0.0; nd], totb: vec![0.0; nd], eddf: vec![0.0; nd], delh: vec![0.0; nd], tau: vec![0.0; nd], deltat: vec![0.0; nd], dt1: vec![0.0; nd], dt2: vec![0.0; nd], antc: vec![0.0; nd], xe: vec![0.0; nd], tem0: vec![0.0; nd], tem1: vec![0.0; nd], tem2: vec![0.0; nd], tem3: vec![0.0; nd], eddh: 0.0, } } } // ============================================================================ // 纯计算函数 // ============================================================================ /// Lucy 温度修正方案核心计算。 /// /// 这是一个简化版本,仅实现核心温度修正逻辑。 /// 完整版本需要传入完整的模型状态和所有依赖函数。 /// /// # 参数 /// - `config`: 配置参数 /// - `model`: 模型参数 /// - `opacfl_data`: 不透明度数据 (ABSO1, EMIS1L, SCAT1, ABSO1L) /// - `rad1_data`: 辐射场数据 (RAD1, FAK1) /// /// # 返回值 /// 温度修正后的模型状态 /// /// # Fortran 原始代码 /// /// ```fortran /// SUBROUTINE LUCY /// ! NLTE Lucy-Unsold temperature correction scheme /// DO IJ=1,NFREQ /// CALL OPACFL(IJ) /// CALL RTEFR1(IJ) /// ! 计算加热率、Eddington 因子等 /// END DO /// ! 计算温度修正 DELTAT /// ! 应用加速方案 /// ! 积分流体静力学平衡 /// END /// ``` pub fn lucy_pure( config: &LucyConfig, model: &LucyModelParams, opacfl_data: &[OpacflPointData], rad1_data: &[Rad1PointData], ) -> LucyOutput { let nd = model.nd; let nfreq = model.nfreq; // 如果 ITLUCY <= 0,直接返回 if config.itlucy <= 0 { return LucyOutput { temp: model.temp.to_vec(), elec: model.elec.to_vec(), dens: model.dens.to_vec(), dens1: model.dens1.to_vec(), pgs: model.pgs.to_vec(), ilucy: 0, lac2t: false, dhhmx1: 0.0, }; } // 初始化状态 let mut state = LucyState::new(nd); let mut lac2t = false; let iacc0t = config.iaclt - 3; let mut ilucy = 1; // 迭代循环 while ilucy <= config.itlucy { // 重置累积量 for id in 0..nd { state.heat[id] = 0.0; state.heab[id] = 0.0; state.absz[id] = 0.0; state.absp[id] = 0.0; state.absh[id] = 0.0; state.totj[id] = 0.0; state.toth[id] = 0.0; state.totb[id] = 0.0; state.eddf[id] = 0.0; } state.eddh = 0.0; // 频率循环 for ij in 0..nfreq { let w0 = model.w[ij]; let fr = model.freq[ij]; let fr15 = fr * 1e-15; let bnu = BN * fr15 * fr15 * fr15; // 获取预计算的不透明度和辐射数据 let opac = &opacfl_data[ij]; let rad = &rad1_data[ij]; // 计算光学深度 (第一个点) if ij == 0 { state.tau[0] = opac.abso1[0] / model.dens[0] * model.dm[0]; } for id in 0..nd { // 计算光学深度 if id > 0 { state.tau[id] = state.tau[id - 1] + HALF * (opac.abso1[id] / model.dens[id] + opac.abso1[id - 1] / model.dens[id - 1]) * (model.dm[id] - model.dm[id - 1]); } let pland = bnu / ((HK * fr / model.temp[id]).exp() - UN); let absot0 = opac.abso1l[id] - opac.scat1[id]; // 加热率累积 state.heat[id] += w0 * (absot0 * rad.rad1[id] - opac.emis1l[id]); state.heab[id] += w0 * (absot0 * rad.rad1[id] - opac.emis1l[id]) / (absot0 * pland); state.absp[id] += w0 * absot0 * pland; state.totj[id] += w0 * rad.rad1[id]; state.totb[id] += w0 * pland; state.eddf[id] += w0 * rad.fak1[id] * rad.rad1[id]; } // 表面 Eddington H state.eddh += w0 * rad.rad1[0] * model.fh[ij]; state.toth[0] += w0 * (rad.rad1[0] * model.fh[ij] - model.hextrd[ij]); let absot0 = opac.abso1[0]; state.absh[0] += w0 * absot0 * (rad.rad1[0] * model.fh[ij] - model.hextrd[ij]); // 深度积分 for id in 1..nd { let absot0 = HALF * (opac.abso1[id] + opac.abso1[id - 1]); let dtm = model.deldm[id - 1] * (opac.abso1[id] * model.dens1[id] + opac.abso1[id - 1] * model.dens1[id - 1]); let fluz = (rad.rad1[id] * rad.fak1[id] - rad.rad1[id - 1] * rad.fak1[id - 1]) / dtm; state.toth[id] += w0 * fluz; state.absh[id] += w0 * absot0 * fluz; } } // 归一化 state.eddh /= state.totj[0]; let tef4 = SIG4P * model.teff.powi(4); state.toth[nd - 1] = tef4; state.heat[nd - 1] = 0.0; state.heat[nd - 2] = 0.0; // 计算各种吸收系数和流偏离 for id in 0..nd { state.absz[id] = (state.heat[id] + state.absp[id]) / state.totj[id]; state.absp[id] /= state.totb[id]; state.absh[id] /= state.toth[id]; state.eddf[id] /= state.totj[id]; state.delh[id] = -state.toth[id] + tef4; } // 计算最大流偏离 let mut dhhmx1 = 0.0; for id in 0..(nd - 1) { let dhh = (state.delh[id] / state.toth[id]).abs(); if dhh > dhhmx1 { dhhmx1 = dhh; } } // 计算温度修正 let mut xx = 0.0; let mut xx1 = 0.0; // 表面点 let tp3 = model.temp[0].powi(3); xx = state.eddf[0] / state.eddh * state.delh[0]; xx1 = xx; state.dt1[0] = state.heat[0] / 16.0 / SIG4P * tp3 / state.absp[0]; state.dt2[0] = state.absz[0] / state.eddf[0] * xx / 16.0 / SIG4P * tp3 / state.absp[0]; state.deltat[0] = state.dt1[0] + state.dt2[0]; // 内部点 for id in 1..nd { let tp3 = model.temp[id].powi(3); xx += model.deldm[id - 1] * (state.absh[id - 1] * model.dens1[id - 1] * state.delh[id - 1] + state.absh[id] * model.dens1[id] * state.delh[id]); state.dt1[id] = state.heat[id] / 16.0 / SIG4P * tp3 / state.absp[id]; state.dt2[id] = state.absz[id] / state.eddf[id] * xx / 16.0 / SIG4P * tp3 / state.absp[id]; state.deltat[id] = state.dt1[id] + state.dt2[id]; } // 应用温度修正 let mut new_temp = model.temp.to_vec(); let mut new_elec = model.elec.to_vec(); let mut new_dens = model.dens.to_vec(); let mut new_dens1 = model.dens1.to_vec(); let mut new_pgs = model.pgs.to_vec(); for id in 0..nd { new_temp[id] += state.deltat[id]; state.tem0[id] = new_temp[id]; let aold = new_dens[id] / model.wmm[id] + new_elec[id]; state.xe[id] = UN - new_elec[id] / aold; } // 加速方案 if ilucy >= config.iaclt && ilucy >= iacc0t { let ipng = if config.iacldt > 0 { (ilucy - config.iaclt) % config.iacldt } else { 0 }; if !lac2t { let ipt = ilucy % 3; let ipt0 = config.iaclt % 3; let ipt1 = (config.iaclt + 1) % 3; let ipt2 = (config.iaclt + 2) % 3; if ilucy == iacc0t { for id in 0..nd { state.tem3[id] = state.tem0[id]; } } else if ipt == ipt1 { for id in 0..nd { state.tem2[id] = state.tem0[id]; } } else if ipt == ipt2 { for id in 0..nd { state.tem1[id] = state.tem0[id]; } } } else if ipng != 0 { // 滚动温度历史 for id in 0..nd { state.tem3[id] = state.tem2[id]; state.tem2[id] = state.tem1[id]; state.tem1[id] = state.tem0[id]; } } else { // 应用加速度 if ilucy >= config.iaclt { let (a1, b1, b2, c1, c2) = compute_acceleration(&state, nd); let ab = b2 * a1 - b1 * b1; if ab != 0.0 { let a0 = (b2 * c1 - b1 * c2) / ab; let b0 = (a1 * c2 - b1 * c1) / ab; for id in 0..nd { state.tem0[id] = (1.0 - a0 - b0) * state.tem0[id] + a0 * state.tem1[id] + b0 * state.tem2[id]; new_temp[id] = state.tem0[id]; } lac2t = true; } else { // 对应 Fortran: WRITE(6,601) ILUCY,AB // FORMAT(/,' **** ACCELT, ITER=',I4,' AB = ',F7.3,/) eprintln!("\n **** ACCELT, ITER={:4} AB = {:7.3}\n", ilucy, ab); } } } } // 流体静力学平衡积分 if config.ihecor >= 1 { // 表面边界条件 let ptur = HALF * model.vturb[0] * model.vturb[0] * new_dens[0]; state.antc[0] = (model.dm[0] * model.grav - model.prd0 - ptur) / BOLK / new_temp[0]; if state.antc[0] <= 0.0 { state.antc[0] = new_dens[0] / model.wmm[0] + new_elec[0]; } // 向内积分 for id in 1..nd { let ptur = HALF * model.vturb[id] * model.vturb[id] * new_dens[id]; let pturm = HALF * model.vturb[id - 1] * model.vturb[id - 1] * new_dens[id - 1]; state.antc[id] = (model.grav * (model.dm[id] - model.dm[id - 1]) + BOLK * new_temp[id - 1] * state.antc[id - 1] - model.pradt[id] + model.pradt[id - 1] - ptur + pturm) / BOLK / new_temp[id]; } // 更新密度 for id in 0..nd { new_elec[id] = (UN - state.xe[id]) * state.antc[id]; new_dens[id] = model.wmm[id] * (state.antc[id] - new_elec[id]); new_dens1[id] = UN / new_dens[id]; } } // 更新气体压力 for id in 0..nd { new_pgs[id] = (new_dens[id] / model.wmm[id] + new_elec[id]) * BOLK * new_temp[id]; } ilucy += 1; // 单次迭代后返回(完整版本会循环) return LucyOutput { temp: new_temp, elec: new_elec, dens: new_dens, dens1: new_dens1, pgs: new_pgs, ilucy, lac2t, dhhmx1, }; } // 不应该到达这里 LucyOutput { temp: model.temp.to_vec(), elec: model.elec.to_vec(), dens: model.dens.to_vec(), dens1: model.dens1.to_vec(), pgs: model.pgs.to_vec(), ilucy, lac2t, dhhmx1: 0.0, } } // ============================================================================ // 辅助函数 // ============================================================================ /// 计算加速度系数。 fn compute_acceleration(state: &LucyState, nd: usize) -> (f64, f64, f64, f64, f64) { let mut a1 = 0.0; let mut b1 = 0.0; let mut b2 = 0.0; let mut c1 = 0.0; let mut c2 = 0.0; for id in 0..nd { let mut wt = 0.0; if state.tem0[id] != 0.0 { wt = 1.0 / state.tem0[id].abs(); } let d0 = state.tem0[id] - state.tem1[id]; let d1 = d0 - state.tem1[id] + state.tem2[id]; let d2 = d0 - state.tem2[id] + state.tem3[id]; a1 += wt * d1 * d1; b1 += wt * d1 * d2; b2 += wt * d2 * d2; c1 += wt * d0 * d1; c2 += wt * d0 * d2; } (a1, b1, b2, c1, c2) } // ============================================================================ // 数据结构体 // ============================================================================ /// 单个频率点的不透明度数据。 #[derive(Debug, Clone, Default)] pub struct OpacflPointData { /// 吸收系数 [nd] pub abso1: Vec, /// 谱线吸收系数 [nd] pub abso1l: Vec, /// 发射系数 [nd] pub emis1l: Vec, /// 散射系数 [nd] pub scat1: Vec, } /// 单个频率点的辐射场数据。 #[derive(Debug, Clone, Default)] pub struct Rad1PointData { /// 辐射强度 [nd] pub rad1: Vec, /// Eddington 因子 [nd] pub fak1: Vec, } // ============================================================================ // 测试 // ============================================================================ #[cfg(test)] mod tests { use super::*; fn create_test_model(nd: usize, nfreq: usize) -> (LucyConfig, LucyModelParams<'static>) { let config = LucyConfig { itlucy: 1, iaclt: 10, iacldt: 1, ihecor: 1, lte: false, lchc: false, ielcor: 10, iter: 0, ntrl: 0, iluctr: vec![0; MTRANS], }; // 注意:这里需要使用静态生命周期,实际测试中需要用 Box::leak 或其他方式 let dm: &'static [f64] = Box::leak((0..nd).map(|i| 0.01 * (i + 1) as f64).collect::>().into_boxed_slice()); let temp: &'static [f64] = Box::leak((0..nd).map(|i| 10000.0 - 100.0 * i as f64).collect::>().into_boxed_slice()); let elec: &'static [f64] = Box::leak(vec![1e12; nd].into_boxed_slice()); let dens: &'static [f64] = Box::leak(vec![1e14; nd].into_boxed_slice()); let dens1: &'static [f64] = Box::leak(vec![1e-14; nd].into_boxed_slice()); let wmm: &'static [f64] = Box::leak(vec![1.0; nd].into_boxed_slice()); let deldm: &'static [f64] = Box::leak(vec![0.005; nd - 1].into_boxed_slice()); let vturb: &'static [f64] = Box::leak(vec![0.0; nd].into_boxed_slice()); let pradt: &'static [f64] = Box::leak(vec![0.0; nd].into_boxed_slice()); let pgs: &'static mut [f64] = Box::leak(vec![0.0; nd].into_boxed_slice()); let freq: &'static [f64] = Box::leak(vec![1e15; nfreq].into_boxed_slice()); let w: &'static [f64] = Box::leak(vec![1.0 / nfreq as f64; nfreq].into_boxed_slice()); let fh: &'static [f64] = Box::leak(vec![0.5; nfreq].into_boxed_slice()); let hextrd: &'static [f64] = Box::leak(vec![0.0; nfreq].into_boxed_slice()); let model = LucyModelParams { nd, nfreq, ntrans: 10, teff: 10000.0, grav: 1e4, // 典型表面重力加速度 prd0: 0.0, // 参考辐射压 dm, temp, elec, dens, dens1, wmm, deldm, vturb, pradt, pgs, freq, w, fh, hextrd, lac2t: false, }; (config, model) } #[test] fn test_lucy_config_default() { let config = LucyConfig::default(); assert_eq!(config.itlucy, 0); assert_eq!(config.iaclt, 10); assert_eq!(config.ihecor, 1); assert!(!config.lte); } #[test] fn test_lucy_state_creation() { let state = LucyState::new(10); assert_eq!(state.heat.len(), 10); assert_eq!(state.tau.len(), 10); assert_eq!(state.eddh, 0.0); } #[test] fn test_lucy_zero_iterations() { let (config, model) = create_test_model(5, 3); let opacfl_data = vec![OpacflPointData::default(); 3]; let rad1_data = vec![Rad1PointData::default(); 3]; // 当 itlucy = 0 时,应该直接返回 let config_zero = LucyConfig { itlucy: 0, ..config.clone() }; let output = lucy_pure(&config_zero, &model, &opacfl_data, &rad1_data); assert_eq!(output.ilucy, 0); } #[test] fn test_compute_acceleration() { let nd = 5; let mut state = LucyState::new(nd); // 设置一些测试值 for i in 0..nd { state.tem0[i] = 10000.0 + i as f64; state.tem1[i] = 9900.0 + i as f64; state.tem2[i] = 9800.0 + i as f64; state.tem3[i] = 9700.0 + i as f64; } let (a1, b1, b2, c1, c2) = compute_acceleration(&state, nd); // 加速度系数应该是正数 assert!(a1 >= 0.0); assert!(b2 >= 0.0); } }