//! 流体静力学平衡方程矩阵填充。 //! //! 重构自 TLUSTY 的 BHE, BHED, BHEZ 子程序。 //! //! 这些函数填充矩阵 A, B, C 的流体静力学平衡行 //! (NFREQE+INHE)-th row。 use crate::tlusty::state::constants::{BOLK, HALF, MDEPTH, MFREQ, MLEVEL, MTOT, TWO, UN}; // ============================================================================ // 控制参数 // ============================================================================ /// 矩阵索引控制参数。 /// 对应 COMMON /MATKEY/ #[derive(Debug, Clone, Default)] pub struct MatKey { /// 方程总数 pub nn: usize, /// NN0 pub nn0: usize, /// 流体静力学平衡方程索引偏移 pub inhe: isize, /// 辐射平衡方程索引偏移 pub inre: isize, /// 粒子守恒方程索引偏移 pub inpc: isize, /// 统计平衡方程索引偏移 pub inse: isize, /// z-距离方程索引偏移 pub inzd: isize, /// 虚拟大质量粒子密度索引偏移 pub inmp: isize, /// 辐射平衡深度点数 pub ndre: usize, } /// 输入参数控制。 /// 对应部分 COMMON /INPPAR/ 和 /FIXDEN/ #[derive(Debug, Clone, Default)] pub struct InputControl { /// 边界条件类型 (0: 恒星大气, 1: 盘新版, 2: 盘旧版, 3: 简单气压边界) pub ibche: i32, /// 固定密度标志 pub ifixde: i32, /// 辐射压力标志 pub ifprad: i32, /// 表面气压 (用于 ibche=3) pub pgas0: f64, /// 重力加速度缩放因子 pub qgrav: f64, } /// 模型维度参数。 #[derive(Debug, Clone)] pub struct ModelDims { /// 显式频率数 pub nfreqe: usize, /// 深度点数 pub nd: usize, /// 线性化能级数 pub nlvexp: usize, } impl Default for ModelDims { fn default() -> Self { Self { nfreqe: 0, nd: 1, nlvexp: 0, } } } // ============================================================================ // BHE 参数结构体 // ============================================================================ /// BHE 输入参数。 #[derive(Debug, Clone)] pub struct BheParams { /// 深度索引 (1-based in Fortran) pub id: usize, /// 模型维度 pub dims: ModelDims, /// 矩阵索引控制 pub matkey: MatKey, /// 输入控制 pub ctrl: InputControl, // 模型状态 (深度相关) /// 温度 (K) [MDEPTH] pub temp: Vec, /// 总粒子密度 (cm⁻³) [MDEPTH] pub dens: Vec, /// 总粒子数 [MDEPTH] pub totn: Vec, /// 电子密度 (cm⁻³) [MDEPTH] pub elec: Vec, /// 平均分子量倒数 [MDEPTH] pub wmm: Vec, /// 柱质量密度 (g/cm²) [MDEPTH] pub dm: Vec, /// 湍流速度 [MDEPTH] pub vturb: Vec, /// z-距离 [MDEPTH] pub zd: Vec, // 频率相关 /// 频率权重 [MFREQ] pub w: Vec, /// 频率索引映射 [MFREQ] pub ijfr: Vec, /// 跳过标志 [MDEPTH][MFREQ] pub lskip: Vec>, /// 表面通量权重 [MFREQ] pub fh: Vec, /// 外辐射 (氦) [MFREQ] pub hextrd: Vec, // 辐射相关 (显式频率) /// 辐射场 (当前) [MFREQ] pub rad0: Vec, /// 辐射场 (前) [MFREQ] pub radm: Vec, /// FK 算子 (当前) [MFREQ] pub fk0: Vec, /// FK 算子 (前) [MFREQ] pub fkm: Vec, /// 吸收系数 (当前) [MFREQ] pub abso0: Vec, /// 吸收系数温度导数 (当前) [MFREQ] pub dabt0: Vec, /// 吸收系数密度导数 (当前) [MFREQ] pub dabn0: Vec, /// 发射系数温度导数 (当前) [MFREQ] pub demt0: Vec, /// 深度权重 (当前) [MFREQ] pub wdep0: Vec, /// 能级导数 [MLEVEL][MFREQ] pub drch0: Vec>, // HE 相关数组 (深度相关) /// HEIT - 氦温度相关 [MDEPTH] pub heit: Vec, /// HEIN - 氦密度相关 [MDEPTH] pub hein: Vec, /// HEITM - 氦温度相关 (前) [MDEPTH] pub heitm: Vec, /// HEINM - 氦密度相关 (前) [MDEPTH] pub heinm: Vec, /// HEITP - 氦温度相关导数 [MDEPTH] pub heitp: Vec, /// HEINP - 氦密度相关导数 [MDEPTH] pub heinp: Vec, // HE 能级相关 [nlvexp][MDEPTH] pub heip: Vec>, pub heipm: Vec>, pub heipp: Vec>, // 辐射压力 /// 固定频率辐射压力 [MDEPTH] pub fprd: Vec, // Psi 向量 /// Ψ (当前) [MTOT] pub psi0: Vec, /// Ψ (前) [MTOT] pub psim: Vec, /// Ψ (后) [MTOT] pub psip: Vec, } impl Default for BheParams { fn default() -> Self { Self { id: 1, dims: ModelDims::default(), matkey: MatKey::default(), ctrl: InputControl::default(), temp: vec![0.0; MDEPTH], dens: vec![0.0; MDEPTH], totn: vec![0.0; MDEPTH], elec: vec![0.0; MDEPTH], wmm: vec![0.0; MDEPTH], dm: vec![0.0; MDEPTH], vturb: vec![0.0; MDEPTH], zd: vec![0.0; MDEPTH], w: vec![0.0; MFREQ], ijfr: vec![0; MFREQ], lskip: vec![vec![false; MFREQ]; MDEPTH], fh: vec![0.0; MFREQ], hextrd: vec![0.0; MFREQ], rad0: vec![0.0; MFREQ], radm: vec![0.0; MFREQ], fk0: vec![0.0; MFREQ], fkm: vec![0.0; MFREQ], abso0: vec![0.0; MFREQ], dabt0: vec![0.0; MFREQ], dabn0: vec![0.0; MFREQ], demt0: vec![0.0; MFREQ], wdep0: vec![0.0; MFREQ], drch0: vec![vec![0.0; MFREQ]; MLEVEL], heit: vec![0.0; MDEPTH], hein: vec![0.0; MDEPTH], heitm: vec![0.0; MDEPTH], heinm: vec![0.0; MDEPTH], heitp: vec![0.0; MDEPTH], heinp: vec![0.0; MDEPTH], heip: vec![vec![0.0; MDEPTH]; MLEVEL], heipm: vec![vec![0.0; MDEPTH]; MLEVEL], heipp: vec![vec![0.0; MDEPTH]; MLEVEL], fprd: vec![0.0; MDEPTH], psi0: vec![0.0; MTOT], psim: vec![0.0; MTOT], psip: vec![0.0; MTOT], } } } // ============================================================================ // BHE 可变状态 // ============================================================================ /// BHE 可变状态 (矩阵和向量)。 #[derive(Debug, Clone, Default)] pub struct BheState { /// A 矩阵 [MTOT][MTOT] pub a: Vec>, /// B 矩阵 [MTOT][MTOT] pub b: Vec>, /// C 矩阵 [MTOT][MTOT] pub c: Vec>, /// 右端向量 [MTOT] pub vecl: Vec, // CMATZD - z-m 关系的 C 矩阵元素 (BHED 专用) pub czz: f64, pub czn: f64, pub cze: f64, pub czm: f64, } impl BheState { pub fn new() -> Self { Self { a: vec![vec![0.0; MTOT]; MTOT], b: vec![vec![0.0; MTOT]; MTOT], c: vec![vec![0.0; MTOT]; MTOT], vecl: vec![0.0; MTOT], czz: 0.0, czn: 0.0, cze: 0.0, czm: 0.0, } } } // ============================================================================ // 物理常量 // ============================================================================ /// GN = 重力加速度相关常数 const GN: f64 = 1.0; /// GP = 重力加速度相关常数 (大质量粒子) const GP: f64 = 1.0; /// PCK = 辐射压力常数 const PCK: f64 = 4.0 * std::f64::consts::PI / 3.0; // ============================================================================ // 辅助函数 // ============================================================================ /// 计算 erfcx(x) = exp(x²) * erfc(x) /// 使用 Fortran 代码中的系数 fn erfcx(x: f64) -> f64 { if x < 3.0 { // 使用近似公式 8.86226925e-1 * (x * x).exp() * erfc_approx(x) } else { // 渐近展开 HALF * (UN - HALF / (x * x)) / x } } /// erfc 近似 fn erfc_approx(x: f64) -> f64 { // 简单近似,精度足够 if x < 0.0 { 2.0 - erfc_approx(-x) } else if x < 6.0 { let t = 1.0 / (1.0 + 0.5 * x); let poly = t * (0.17087277 + t * (-0.82215223 + t * (1.48851587 + t * (-1.13520398 + t * (0.27886807 + t * (-0.18628806 + t * 0.4206475)))))); t * (-x * x).exp() * poly } else { 0.0 } } // ============================================================================ // BHE 主函数 // ============================================================================ /// 流体静力学平衡方程矩阵填充。 /// /// 填充矩阵 A 和 B 的 (NFREQE+INHE)-th 行, /// 对应流体静力学平衡方程。 /// /// # 参数 /// - `params`: 输入参数 /// - `state`: 可变状态 (矩阵和向量) pub fn bhe(params: &BheParams, state: &mut BheState) { let id = params.id; let dims = ¶ms.dims; let matkey = ¶ms.matkey; // 计算行索引 (0-based) let nhe = dims.nfreqe + matkey.inhe as usize; let nre = dims.nfreqe + matkey.inre as usize; let npc = dims.nfreqe + matkey.inpc as usize; let nse = dims.nfreqe + matkey.inse as usize - 1; // Fortran: NSE = NFREQE+INSE-1 // 固定密度情况 if params.ctrl.ifixde > 0 { state.b[nhe][nhe] = UN; state.b[nhe][npc] = -UN; state.vecl[nhe] = params.dens[id - 1] * params.wmm[id - 1] + params.elec[id - 1] - params.totn[id - 1]; return; } // 虚拟大质量粒子密度方程 if matkey.inmp > 0 { let nmp = dims.nfreqe + matkey.inmp as usize; state.b[nmp][nmp] = -UN; state.b[nmp][nhe] = UN; if matkey.inpc > 0 { state.b[nmp][npc] = -UN; } } // 初始化累积变量 let mut hext: f64 = 0.0; let mut hexn: f64 = 0.0; let mut grd: f64 = 0.0; let mut hex = vec![0.0; dims.nlvexp]; // 上边界条件 (id = 1) if id > 1 { // 正常深度点 - 跳转到标号 50 fill_normal_depth(params, state, id, nhe, nre, npc, nse, &mut grd); return; } // === 上边界条件 (ID = 1) === // 基于 Mihalas (1978) 的线性化方程 (7-10) let mut x1 = 0.0; if dims.nfreqe > 0 && params.ctrl.ifprad > 0 { x1 = PCK / params.dens[id - 1]; for ij in 1..=dims.nfreqe { let ijt = params.ijfr[ij - 1]; if !params.lskip[id - 1][ijt] { let fluxw = params.w[ijt] * (params.fh[ijt] * params.rad0[ij - 1] - params.hextrd[ijt]); grd += fluxw * params.abso0[ij - 1]; hexn += fluxw * params.dabn0[ij - 1]; hext += fluxw * params.dabt0[ij - 1]; for i in 0..dims.nlvexp { hex[i] += fluxw * params.drch0[i][ij - 1]; } // 平均强度列 state.b[nhe][ij - 1] = x1 * params.w[ijt] * params.fh[ijt] * params.abso0[ij - 1]; } } } let rtn = x1 * params.wmm[id - 1] / params.dens[id - 1] * (grd + params.fprd[id - 1]); let vt0 = HALF * params.vturb[id - 1] * params.vturb[id - 1] / params.dm[id - 1] * params.wmm[id - 1]; // 总粒子密度、虚拟大质量粒子密度、温度、电子密度列 state.b[nhe][nhe] = BOLK * params.temp[id - 1] / params.dm[id - 1] - GN * (rtn - vt0); if matkey.inmp > 0 { let nmp = dims.nfreqe + matkey.inmp as usize; state.b[nhe][nmp] = GP * (vt0 - rtn); } if matkey.inre > 0 { state.b[nhe][nre] = BOLK * params.totn[id - 1] / params.dm[0] + x1 * (hext + params.heit[id - 1]); state.c[nhe][nre] = x1 * params.heitp[id - 1]; } if matkey.inpc > 0 { state.b[nhe][npc] = x1 * (hexn + params.hein[id - 1]) + GN * (rtn - vt0); state.c[nhe][npc] = x1 * params.heinp[id - 1]; } // 能级占据数列 for ii in 0..dims.nlvexp { state.b[nhe][nse + ii] += x1 * (hex[ii] + params.heip[ii][id - 1]); state.c[nhe][nse + ii] += x1 * params.heipp[ii][id - 1]; } // 右端向量 let grav = params.ctrl.qgrav * params.zd[id - 1]; state.vecl[nhe] = grav - BOLK * params.temp[id - 1] * params.totn[id - 1] / params.dm[id - 1] - x1 * (grd + params.fprd[id - 1]) - vt0 / params.wmm[id - 1] * params.dens[id - 1]; } /// 填充正常深度点 (ID > 1) 的矩阵 fn fill_normal_depth( params: &BheParams, state: &mut BheState, id: usize, nhe: usize, nre: usize, npc: usize, nse: usize, grd: &mut f64, ) { let dims = ¶ms.dims; let matkey = ¶ms.matkey; // 平均强度列 if dims.nfreqe > 0 && params.ctrl.ifprad > 0 { for ij in 1..=dims.nfreqe { if !params.lskip[id - 1][params.ijfr[ij - 1]] { *grd += (params.fk0[ij - 1] * params.rad0[ij - 1] - params.fkm[ij - 1] * params.radm[ij - 1]) * params.w[params.ijfr[ij - 1]]; state.a[nhe][ij - 1] = -PCK * params.w[params.ijfr[ij - 1]] * params.fkm[ij - 1]; state.b[nhe][ij - 1] = PCK * params.w[params.ijfr[ij - 1]] * params.fk0[ij - 1]; } } } let vt0 = HALF * params.vturb[id - 1] * params.vturb[id - 1] * params.wmm[id - 1]; let vtm = HALF * params.vturb[id - 2] * params.vturb[id - 2] * params.wmm[id - 2]; // 总粒子密度列 state.a[nhe][nhe] = -BOLK * params.temp[id - 2] - GN * vtm; state.b[nhe][nhe] = BOLK * params.temp[id - 1] + GN * vt0; // 温度列 if matkey.inre > 0 { state.a[nhe][nre] = -BOLK * params.totn[id - 2] + PCK * params.heitm[id - 1]; state.b[nhe][nre] = BOLK * params.totn[id - 1] + PCK * params.heit[id - 1]; state.c[nhe][nre] = PCK * params.heitp[id - 1]; } // 电子密度列 if matkey.inpc > 0 { state.a[nhe][npc] = GN * vtm + PCK * params.heinm[id - 1]; state.b[nhe][npc] = -GN * vt0 + PCK * params.hein[id - 1]; state.c[nhe][npc] = PCK * params.heinp[id - 1]; } // 虚拟大质量粒子密度列 if matkey.inmp > 0 { let nmp = dims.nfreqe + matkey.inmp as usize; state.a[nhe][nmp] = -GP * vtm; state.b[nhe][nmp] = GP * vt0; } // 能级占据数列 for ii in 0..dims.nlvexp { state.a[nhe][nse + ii] += PCK * params.heipm[ii][id - 1]; state.b[nhe][nse + ii] += PCK * params.heip[ii][id - 1]; state.c[nhe][nse + ii] += PCK * params.heipp[ii][id - 1]; } // 右端向量 let grav = params.ctrl.qgrav * (params.dm[id - 1] - params.dm[id - 2]); state.vecl[nhe] = grav - BOLK * (params.temp[id - 1] * params.totn[id - 1] - params.temp[id - 2] * params.totn[id - 2]) - PCK * (*grd + params.fprd[id - 1]) - vt0 / params.wmm[id - 1] * params.dens[id - 1] + vtm / params.wmm[id - 2] * params.dens[id - 2]; } // ============================================================================ // BHED 函数 // ============================================================================ /// 流体静力学平衡方程矩阵填充 (扩展版,含 z-m 关系)。 /// /// 与 BHE 类似,但增加: /// - z-距离与质量深度关系 (NFREQE+INZD)-th 行 /// - 支持盘模型的边界条件 pub fn bhed(params: &BheParams, state: &mut BheState) { let id = params.id; let dims = ¶ms.dims; let matkey = ¶ms.matkey; let nhe = dims.nfreqe + matkey.inhe as usize; let nre = dims.nfreqe + matkey.inre as usize; let npc = dims.nfreqe + matkey.inpc as usize; let nse = dims.nfreqe + matkey.inse as usize - 1; if matkey.inhe <= 0 { // 跳过流体静力学平衡,只处理 z-m 关系 fill_zm_relation(params, state, id, dims, matkey); return; } // 虚拟大质量粒子密度方程 if matkey.inmp > 0 { let nmp = dims.nfreqe + matkey.inmp as usize; state.b[nmp][nmp] = -UN; state.b[nmp][nhe] = UN; if matkey.inpc > 0 { state.b[nmp][npc] = -UN; } } // 初始化 let mut hext: f64 = 0.0; let mut hexn: f64 = 0.0; let mut grd: f64 = 0.0; let mut hex = vec![0.0; dims.nlvexp]; if id > 1 { // 正常深度点 fill_normal_depth_bhed(params, state, id, nhe, nre, npc, nse, &mut grd); fill_zm_relation(params, state, id, dims, matkey); return; } // === 上边界条件 (ID = 1) === let ibche = params.ctrl.ibche; if ibche <= 0 { // 1. 恒星大气边界条件 (Mihalas 1978) fill_upper_boundary_stellar(params, state, nhe, nre, npc, nse, &mut hext, &mut hexn, &mut grd, &mut hex); } else if ibche == 1 { // 2. 盘模型边界条件 (Hubeny 1990 新版) fill_upper_boundary_disk_new(params, state, nhe, nre, npc, nse, &mut hext, &mut hexn, &mut grd, &mut hex); } else if ibche == 2 { // 3. 盘模型边界条件 (Hubeny 1990 旧版) fill_upper_boundary_disk_old(params, state, nhe, nre, &mut grd); } else { // 4. 简单气压边界条件 P_gas(ID=1) = PGAS0 fill_upper_boundary_simple(params, state, nhe, nre); } // z-m 关系 fill_zm_relation(params, state, id, dims, matkey); } /// 填充恒星大气上边界条件 fn fill_upper_boundary_stellar( params: &BheParams, state: &mut BheState, nhe: usize, nre: usize, npc: usize, nse: usize, hext: &mut f64, hexn: &mut f64, grd: &mut f64, hex: &mut [f64], ) { let dims = ¶ms.dims; let matkey = ¶ms.matkey; let x1 = PCK / params.dens[0]; if dims.nfreqe > 0 { for ij in 1..=dims.nfreqe { let ijt = params.ijfr[ij - 1]; if !params.lskip[0][ijt] { let fluxw = params.w[ijt] * (params.fh[ijt] * params.rad0[ij - 1] - params.hextrd[ijt]); *grd += fluxw * params.abso0[ij - 1]; *hexn += fluxw * params.dabn0[ij - 1]; *hext += fluxw * params.dabt0[ij - 1]; for i in 0..dims.nlvexp { hex[i] += fluxw * params.drch0[i][ij - 1]; } state.b[nhe][ij - 1] = x1 * params.wdep0[ij - 1] * params.fh[ijt] * params.abso0[ij - 1]; } } } let rtn = x1 * params.wmm[0] / params.dens[0] * (*grd + params.fprd[0]); let vt0 = HALF * params.vturb[0] * params.vturb[0] / params.dm[0] * params.wmm[0]; state.b[nhe][nhe] = BOLK * params.temp[0] / params.dm[0] - GN * (rtn - vt0); if matkey.inmp > 0 { let nmp = dims.nfreqe + matkey.inmp as usize; state.b[nhe][nmp] = GP * (vt0 - rtn); } if matkey.inre > 0 { state.b[nhe][nre] = BOLK * params.psi0[nhe] / params.dm[0] + x1 * (*hext + params.heit[0]); state.c[nhe][nre] = x1 * params.heitp[0]; } if matkey.inpc > 0 { state.b[nhe][npc] = x1 * (*hexn + params.hein[0]) + GN * (rtn - vt0); state.c[nhe][npc] = x1 * params.heinp[0]; } for ii in 0..dims.nlvexp { state.b[nhe][nse + ii] += x1 * (hex[ii] + params.heip[ii][0]); state.c[nhe][nse + ii] += x1 * params.heipp[ii][0]; } let grav = params.ctrl.qgrav * params.zd[0]; state.vecl[nhe] = grav - BOLK * params.temp[0] * params.psi0[nhe] / params.dm[0] - x1 * (*grd + params.fprd[0]) - vt0 / params.wmm[0] * params.dens[0]; } /// 填充盘模型上边界条件 (新版) fn fill_upper_boundary_disk_new( params: &BheParams, state: &mut BheState, nhe: usize, nre: usize, npc: usize, nse: usize, hext: &mut f64, hexn: &mut f64, grd: &mut f64, hex: &mut [f64], ) { let dims = ¶ms.dims; let matkey = ¶ms.matkey; if dims.nfreqe > 0 { for ij in 1..=dims.nfreqe { let ijt = params.ijfr[ij - 1]; if !params.lskip[0][ijt] { let fluxw = params.w[ijt] * (params.fh[ijt] * params.rad0[ij - 1] - params.hextrd[ijt]); *grd += fluxw * params.abso0[ij - 1]; *hexn += fluxw * params.dabn0[ij - 1]; *hext += fluxw * params.dabt0[ij - 1]; for i in 0..dims.nlvexp { hex[i] += fluxw * params.drch0[i][ij - 1]; } } } } let ccc = PCK / params.ctrl.qgrav; let hr1 = ccc * (*grd + params.fprd[0]) / params.dens[0]; let pg1 = BOLK * params.psi0[nhe] * params.temp[0]; let hg1 = (TWO * pg1 / params.dens[0] / params.ctrl.qgrav).sqrt(); let mut x = (params.zd[0] - hr1) / hg1; let f1 = if x < 3.0 { if x < 0.0 { x = 0.0; } erfcx(x) } else { HALF * (UN - HALF / (x * x)) / x }; let x1 = x * 1.01; let f1d = if x1 < 3.0 { erfcx(x1) } else { HALF * (UN - HALF / (x1 * x1)) / x1 }; let f1d = if x > 0.0 { (f1d - f1) * 100.0 / x } else { 0.0 }; let ggg = params.dens[0] * hg1 * f1; let rf1 = params.dens[0] * f1d; let ccd = ccc * f1d; for ij in 1..=dims.nfreqe { state.b[nhe][ij - 1] = -ccd * params.wdep0[ij - 1] * params.fh[params.ijfr[ij - 1]] * params.abso0[ij - 1]; } state.b[nhe][nhe] += (ggg + hr1 * rf1) / params.psi0[nhe]; if matkey.inre > 0 { state.b[nhe][nre] = (ggg - rf1 * params.zd[0] + rf1 * hr1) * HALF / params.temp[0] - ccd * (*hext + params.heit[0]); } if matkey.inzd > 0 { let nzd = dims.nfreqe + matkey.inzd as usize; state.b[nhe][nzd] = rf1; } if matkey.inpc > 0 { state.b[nhe][npc] = -ccd * (*hexn + params.hein[0]); } for ii in 0..dims.nlvexp { state.b[nhe][nse + ii] = -ccd * (hex[ii] + params.heip[ii][0]); } state.vecl[nhe] = params.dm[0] - ggg; } /// 填充盘模型上边界条件 (旧版) fn fill_upper_boundary_disk_old( params: &BheParams, state: &mut BheState, nhe: usize, nre: usize, grd: &mut f64, ) { let dims = ¶ms.dims; let matkey = ¶ms.matkey; if dims.nfreqe > 0 { for ij in 1..=dims.nfreqe { let ijt = params.ijfr[ij - 1]; if !params.lskip[0][ijt] { let fluxw = params.w[ijt] * (params.fh[ijt] * params.rad0[ij - 1] - params.hextrd[ijt]); *grd += fluxw * params.abso0[ij - 1]; } } } let ccc = PCK / params.ctrl.qgrav; let pr1 = ccc * (*grd + params.fprd[0]) / params.dens[0]; let pg1 = BOLK * params.psi0[nhe] * params.temp[0]; let hg1 = (TWO * pg1 / params.dens[0] / params.ctrl.qgrav).sqrt(); let mut x = (params.zd[0] - pr1) / hg1; let f1 = if x < 3.0 { if x < 0.0 { x = 0.0; } erfcx(x) } else { HALF * (UN - HALF / (x * x)) / x }; let ggg = hg1 * params.ctrl.qgrav * HALF / f1; state.b[nhe][nhe] = BOLK * params.temp[0]; if matkey.inre > 0 { state.b[nhe][dims.nfreqe + matkey.inre as usize] = pg1 / params.temp[0]; } state.vecl[nhe] = params.dm[0] * ggg - pg1; } /// 填充简单气压边界条件 fn fill_upper_boundary_simple( params: &BheParams, state: &mut BheState, nhe: usize, nre: usize, ) { let matkey = ¶ms.matkey; state.b[nhe][nhe] = BOLK * params.temp[0]; if matkey.inre > 0 { state.b[nhe][nre] = BOLK * params.psi0[nhe]; } state.vecl[nhe] = params.ctrl.pgas0 - BOLK * params.temp[0] * params.psi0[nhe]; } /// 填充 BHED 正常深度点 fn fill_normal_depth_bhed( params: &BheParams, state: &mut BheState, id: usize, nhe: usize, nre: usize, npc: usize, nse: usize, grd: &mut f64, ) { let dims = ¶ms.dims; let matkey = ¶ms.matkey; if dims.nfreqe > 0 { for ij in 1..=dims.nfreqe { if !params.lskip[id - 1][params.ijfr[ij - 1]] { *grd += (params.fk0[ij - 1] * params.rad0[ij - 1] - params.fkm[ij - 1] * params.radm[ij - 1]) * params.w[params.ijfr[ij - 1]]; state.a[nhe][ij - 1] = -PCK * params.w[params.ijfr[ij - 1]] * params.fkm[ij - 1]; state.b[nhe][ij - 1] = PCK * params.w[params.ijfr[ij - 1]] * params.fk0[ij - 1]; } } } let vt0 = HALF * params.vturb[id - 1] * params.vturb[id - 1] * params.wmm[id - 1]; let vtm = HALF * params.vturb[id - 2] * params.vturb[id - 2] * params.wmm[id - 2]; state.a[nhe][nhe] = -BOLK * params.temp[id - 2] - GN * vtm; state.b[nhe][nhe] = BOLK * params.temp[id - 1] + GN * vt0; if matkey.inre > 0 { state.a[nhe][nre] = -BOLK * params.psim[nhe] + PCK * params.heitm[id - 1]; state.b[nhe][nre] = BOLK * params.psi0[nhe] + PCK * params.heit[id - 1]; state.c[nhe][nre] = PCK * params.heitp[id - 1]; } if matkey.inpc > 0 { state.a[nhe][npc] = GN * vtm + PCK * params.heinm[id - 1]; state.b[nhe][npc] = -GN * vt0 + PCK * params.hein[id - 1]; state.c[nhe][npc] = PCK * params.heinp[id - 1]; } if matkey.inmp > 0 { let nmp = dims.nfreqe + matkey.inmp as usize; state.a[nhe][nmp] = -GP * vtm; state.b[nhe][nmp] = GP * vt0; } if matkey.inzd > 0 { let nzd = dims.nfreqe + matkey.inzd as usize; let dgrav = -params.ctrl.qgrav * (params.dm[id - 1] - params.dm[id - 2]) * HALF; state.a[nhe][nzd] = dgrav; state.b[nhe][nzd] = dgrav; } for ii in 0..dims.nlvexp { state.a[nhe][nse + ii] += PCK * params.heipm[ii][id - 1]; state.b[nhe][nse + ii] += PCK * params.heip[ii][id - 1]; state.c[nhe][nse + ii] += PCK * params.heipp[ii][id - 1]; } let grav = params.ctrl.qgrav * (params.zd[id - 1] + params.zd[id - 2]) * HALF; state.vecl[nhe] = grav * (params.dm[id - 1] - params.dm[id - 2]) - BOLK * (params.temp[id - 1] * params.psi0[nhe] - params.temp[id - 2] * params.psim[nhe]) - PCK * (*grd + params.fprd[id - 1]) - vt0 / params.wmm[id - 1] * params.dens[id - 1] + vtm / params.wmm[id - 2] * params.dens[id - 2]; } /// 填充 z-m 关系方程 /// /// # Fortran 索引说明 /// /// Fortran 使用 1-indexed,此函数中的 `id` 参数也是 1-indexed。 /// /// Fortran 代码使用向前差分: /// - `DDP = (DM(ID+1) - DM(ID)) * HALF` /// - `VECL(NZD) = ZD(ID+1) - ZD(ID) + ...` /// /// 转换为 Rust 0-indexed: /// - Fortran `DM(ID)` → Rust `dm[id-1]` /// - Fortran `DM(ID+1)` → Rust `dm[id]` fn fill_zm_relation( params: &BheParams, state: &mut BheState, id: usize, dims: &ModelDims, matkey: &MatKey, ) { if matkey.inzd <= 0 { return; } let nzd = dims.nfreqe + matkey.inzd as usize; // 下边界条件 z(ND) = 0 state.b[nzd][nzd] = UN; if id == dims.nd { return; } // 正常深度点 - 使用向前差分 // Fortran: DDP = (DM(ID+1) - DM(ID)) * HALF // Rust: ddp = (dm[id] - dm[id-1]) * HALF (因为 Fortran ID 是 1-indexed) let ddp = (params.dm[id] - params.dm[id - 1]) * HALF; state.b[nzd][nzd] = UN; state.czz = -UN; // Fortran: X1 = GN * WMM(ID) * DDP // Rust: wmm[id-1] (0-indexed) let x1 = GN * params.wmm[id - 1] * ddp; if matkey.inhe > 0 { let nhe = dims.nfreqe + matkey.inhe as usize; // Fortran: B(NZD,NHE) = X1 / DENS(ID) / DENS(ID) // Fortran: CZN = X1 / DENS(ID+1) / DENS(ID+1) state.b[nzd][nhe] = x1 / params.dens[id - 1] / params.dens[id - 1]; state.czn = x1 / params.dens[id] / params.dens[id]; } if matkey.inpc > 0 { let npc = dims.nfreqe + matkey.inpc as usize; state.b[nzd][npc] = -x1 / params.dens[id - 1] / params.dens[id - 1]; state.cze = -x1 / params.dens[id] / params.dens[id]; } if matkey.inmp > 0 { let nmp = dims.nfreqe + matkey.inmp as usize; // Fortran: PSI0(NFREQE+INMP) 和 PSIP(NFREQE+INMP) state.b[nzd][nmp] = ddp / params.dens[id - 1] / params.psi0[nmp]; state.czm = ddp / params.dens[id] / params.psip[nmp]; } // Fortran: VECL(NZD) = ZD(ID+1) - ZD(ID) + DDP/DENS(ID) + DDP/DENS(ID+1) state.vecl[nzd] = params.zd[id] - params.zd[id - 1] + ddp / params.dens[id - 1] + ddp / params.dens[id]; } // ============================================================================ // BHEZ 函数 // ============================================================================ /// 流体静力学平衡方程矩阵填充 (z 坐标版本)。 /// /// 专门用于 z 坐标情况, /// 边界条件处理略有不同。 pub fn bhez(params: &BheParams, state: &mut BheState) { let id = params.id; let dims = ¶ms.dims; let matkey = ¶ms.matkey; let nhe = dims.nfreqe + matkey.inhe as usize; let nre = dims.nfreqe + matkey.inre as usize; let npc = dims.nfreqe + matkey.inpc as usize; let nzd = dims.nfreqe + matkey.inzd as usize; let nse = dims.nfreqe + matkey.inse as usize - 1; if matkey.inhe <= 0 { return; } // 虚拟大质量粒子密度方程 if matkey.inmp > 0 { let nmp = dims.nfreqe + matkey.inmp as usize; state.b[nmp][nmp] = -UN; state.b[nmp][nhe] = UN; if matkey.inpc > 0 { state.b[nmp][npc] = -UN; } } // 初始化 let mut hext: f64 = 0.0; let mut hexn: f64 = 0.0; let mut grd: f64 = 0.0; let mut hex = vec![0.0; dims.nlvexp]; if id > 1 { // 正常深度点 fill_normal_depth_bhez(params, state, id, nhe, nre, npc, nse, nzd, &mut grd); return; } // === 上边界条件 (ID = 1) === let ibche = params.ctrl.ibche; if ibche == 0 { // 恒星大气边界条件 fill_upper_boundary_stellar_bhez(params, state, nhe, nre, npc, nse, nzd, &mut hext, &mut hexn, &mut grd, &mut hex); } else if ibche == 1 { // 盘模型边界条件 (新版) fill_upper_boundary_disk_new_bhez(params, state, nhe, nre, npc, nse, nzd, &mut hext, &mut hexn, &mut grd, &mut hex); } else if ibche == 2 { // 盘模型边界条件 (旧版) fill_upper_boundary_disk_old_bhez(params, state, nhe, nre, nzd, &mut grd); } else { // 简单气压边界条件 fill_upper_boundary_simple(params, state, nhe, nre); } } /// BHEZ 恒星大气上边界 fn fill_upper_boundary_stellar_bhez( params: &BheParams, state: &mut BheState, nhe: usize, nre: usize, npc: usize, nse: usize, nzd: usize, hext: &mut f64, hexn: &mut f64, grd: &mut f64, hex: &mut [f64], ) { // 与 BHED 的恒星大气边界相同 fill_upper_boundary_stellar(params, state, nhe, nre, npc, nse, hext, hexn, grd, hex); } /// BHEZ 盘模型上边界 (新版) fn fill_upper_boundary_disk_new_bhez( params: &BheParams, state: &mut BheState, nhe: usize, nre: usize, npc: usize, nse: usize, nzd: usize, hext: &mut f64, hexn: &mut f64, grd: &mut f64, hex: &mut [f64], ) { let dims = ¶ms.dims; let matkey = ¶ms.matkey; if dims.nfreqe > 0 { for ij in 1..=dims.nfreqe { let ijt = params.ijfr[ij - 1]; if !params.lskip[0][ijt] { let fluxw = params.w[ijt] * (params.fh[ijt] * params.rad0[ij - 1] - params.hextrd[ijt]); *grd += fluxw * params.abso0[ij - 1]; *hexn += fluxw * params.dabn0[ij - 1]; *hext += fluxw * params.dabt0[ij - 1]; for i in 0..dims.nlvexp { hex[i] += fluxw * params.drch0[i][ij - 1]; } } } } let ccc = PCK / params.ctrl.qgrav; let hr1 = ccc * (*grd + params.fprd[0]) / params.dens[0]; let pg1 = BOLK * params.psi0[nhe] * params.temp[0]; let hg1 = (TWO * pg1 / params.dens[0] / params.ctrl.qgrav).sqrt(); let mut x = (params.zd[0] - hr1) / hg1; let f1 = if x < 3.0 { if x < 0.0 { x = 0.0; } erfcx(x) } else { HALF * (UN - HALF / (x * x)) / x }; let x1 = x * 1.01; let f1d = if x1 < 3.0 { erfcx(x1) } else { HALF * (UN - HALF / (x1 * x1)) / x1 }; let f1d = if x > 0.0 { (f1d - f1) * 100.0 / x } else { 0.0 }; let ggg = params.dens[0] * hg1 * f1; let rf1 = params.dens[0] * f1d; let ccd = ccc * f1d; for ij in 1..=dims.nfreqe { state.b[nhe][ij - 1] = -ccd * params.wdep0[ij - 1] * params.fh[params.ijfr[ij - 1]] * params.abso0[ij - 1]; } state.b[nhe][nhe] += (ggg + hr1 * rf1) / params.psi0[nhe]; if matkey.inre > 0 { state.b[nhe][nre] = (ggg - rf1 * params.zd[0] + rf1 * hr1) * HALF / params.temp[0] - ccd * (*hext + params.heit[0]); } state.b[nhe][nzd] = rf1; if matkey.inpc > 0 { state.b[nhe][npc] = -ccd * (*hexn + params.hein[0]); } for ii in 0..dims.nlvexp { state.b[nhe][nse + ii] = -ccd * (hex[ii] + params.heip[ii][0]); } state.vecl[nhe] = params.dm[0] - ggg; } /// BHEZ 盘模型上边界 (旧版) fn fill_upper_boundary_disk_old_bhez( params: &BheParams, state: &mut BheState, nhe: usize, nre: usize, nzd: usize, grd: &mut f64, ) { let dims = ¶ms.dims; let matkey = ¶ms.matkey; if dims.nfreqe > 0 { for ij in 1..=dims.nfreqe { let ijt = params.ijfr[ij - 1]; if !params.lskip[0][ijt] { let fluxw = params.w[ijt] * (params.fh[ijt] * params.rad0[ij - 1] - params.hextrd[ijt]); *grd += fluxw * params.abso0[ij - 1]; } } } let ccc = PCK / params.ctrl.qgrav; let pr1 = ccc * (*grd + params.fprd[0]) / params.dens[0]; let pg1 = BOLK * params.psi0[nhe] * params.temp[0]; let hg1 = (TWO * pg1 / params.dens[0] / params.ctrl.qgrav).sqrt(); let mut x = (params.zd[0] - pr1) / hg1; let f1 = if x < 3.0 { if x < 0.0 { x = 0.0; } erfcx(x) } else { HALF * (UN - HALF / (x * x)) / x }; let ggg = hg1 * params.ctrl.qgrav * HALF / f1; state.b[nhe][nhe] = BOLK * params.temp[0]; if matkey.inre > 0 { state.b[nhe][dims.nfreqe + matkey.inre as usize] = pg1 / params.temp[0]; } state.vecl[nhe] = params.dm[0] * ggg - pg1; } /// BHEZ 正常深度点 fn fill_normal_depth_bhez( params: &BheParams, state: &mut BheState, id: usize, nhe: usize, nre: usize, npc: usize, nse: usize, nzd: usize, grd: &mut f64, ) { let dims = ¶ms.dims; let matkey = ¶ms.matkey; let grav = params.ctrl.qgrav * (params.zd[id - 1] + params.zd[id - 2]) * HALF; let gravz = grav * (params.zd[id - 1] - params.zd[id - 2]); let dgrv = gravz * HALF * params.wmm[id - 1]; if dims.nfreqe > 0 { for ij in 1..=dims.nfreqe { if !params.lskip[id - 1][params.ijfr[ij - 1]] { *grd += (params.fk0[ij - 1] * params.rad0[ij - 1] - params.fkm[ij - 1] * params.radm[ij - 1]) * params.w[params.ijfr[ij - 1]]; state.a[nhe][ij - 1] = -PCK * params.w[params.ijfr[ij - 1]] * params.fkm[ij - 1]; state.b[nhe][ij - 1] = PCK * params.w[params.ijfr[ij - 1]] * params.fk0[ij - 1]; } } } let vt0 = HALF * params.vturb[id - 1] * params.vturb[id - 1] * params.wmm[id - 1]; let vtm = HALF * params.vturb[id - 2] * params.vturb[id - 2] * params.wmm[id - 2]; state.a[nhe][nhe] = -BOLK * params.temp[id - 2] - GN * (vtm + dgrv); state.b[nhe][nhe] = BOLK * params.temp[id - 1] + GN * (vt0 + dgrv); if matkey.inre > 0 { state.a[nhe][nre] = -BOLK * params.psim[nhe] + PCK * params.heitm[id - 1]; state.b[nhe][nre] = BOLK * params.psi0[nhe] + PCK * params.heit[id - 1]; state.c[nhe][nre] = PCK * params.heitp[id - 1]; } if matkey.inpc > 0 { state.a[nhe][npc] = GN * (vtm + dgrv) + PCK * params.heinm[id - 1]; state.b[nhe][npc] = -GN * (vt0 + dgrv) + PCK * params.hein[id - 1]; state.c[nhe][npc] = PCK * params.heinp[id - 1]; } if matkey.inmp > 0 { let nmp = dims.nfreqe + matkey.inmp as usize; state.a[nhe][nmp] = -GP * (vtm + dgrv); state.b[nhe][nmp] = GP * (vt0 + dgrv); } for ii in 0..dims.nlvexp { state.a[nhe][nse + ii] += PCK * params.heipm[ii][id - 1]; state.b[nhe][nse + ii] += PCK * params.heip[ii][id - 1]; state.c[nhe][nse + ii] += PCK * params.heipp[ii][id - 1]; } state.vecl[nhe] = -gravz * (params.dens[id - 1] + params.dens[id - 2]) * HALF - BOLK * (params.temp[id - 1] * params.psi0[nhe] - params.temp[id - 2] * params.psim[nhe]) - PCK * (*grd + params.fprd[id - 1]) - vt0 / params.wmm[id - 1] * params.dens[id - 1] + vtm / params.wmm[id - 2] * params.dens[id - 2]; } // ============================================================================ // 测试 // ============================================================================ #[cfg(test)] mod tests { use super::*; fn create_test_params() -> BheParams { let mut params = BheParams::default(); params.id = 1; params.dims = ModelDims { nfreqe: 10, nd: 5, nlvexp: 3, }; params.matkey = MatKey { nn: 20, nn0: 20, inhe: 10, inre: 11, inpc: 12, inse: 14, inzd: 0, inmp: 0, ndre: 5, }; params.ctrl = InputControl { ibche: 0, ifixde: 0, ifprad: 1, pgas0: 0.0, qgrav: 1.0, }; // 设置简单的测试值 params.temp[0] = 10000.0; params.dens[0] = 1e12; params.totn[0] = 1e12; params.elec[0] = 1e10; params.wmm[0] = 1.0; params.dm[0] = 1e-4; params.vturb[0] = 5e5; params.zd[0] = 1e8; for i in 0..10 { params.w[i] = 1.0; params.ijfr[i] = i; params.fh[i] = 0.5; params.hextrd[i] = 0.0; params.rad0[i] = 1e10; params.abso0[i] = 1e-8; params.dabt0[i] = 1e-10; params.dabn0[i] = 1e-10; params.wdep0[i] = 1.0; } for i in 0..3 { for j in 0..10 { params.drch0[i][j] = 1e-12; } params.heip[i][0] = 0.0; params.heipp[i][0] = 0.0; } params.heit[0] = 0.0; params.hein[0] = 0.0; params.heitp[0] = 0.0; params.heinp[0] = 0.0; params.fprd[0] = 0.0; params.psi0[20] = 1e12; // nhe index params } #[test] fn test_bhe_fixed_density() { let mut params = create_test_params(); params.ctrl.ifixde = 1; params.ctrl.ifprad = 0; let mut state = BheState::new(); bhe(¶ms, &mut state); let nhe = params.dims.nfreqe + params.matkey.inhe as usize; let npc = params.dims.nfreqe + params.matkey.inpc as usize; assert!((state.b[nhe][nhe] - UN).abs() < 1e-10); assert!((state.b[nhe][npc] - (-UN)).abs() < 1e-10); } #[test] fn test_bhe_upper_boundary() { let params = create_test_params(); let mut state = BheState::new(); bhe(¶ms, &mut state); let nhe = params.dims.nfreqe + params.matkey.inhe as usize; // 检查矩阵元素被填充 assert!(state.b[nhe][nhe].abs() > 0.0 || state.vecl[nhe].abs() > 0.0); } #[test] fn test_bhed_simple() { let params = create_test_params(); let mut state = BheState::new(); bhed(¶ms, &mut state); let nhe = params.dims.nfreqe + params.matkey.inhe as usize; // 检查矩阵元素被填充 assert!(state.b[nhe][nhe].abs() > 0.0 || state.vecl[nhe].abs() > 0.0); } #[test] fn test_bhez_simple() { let params = create_test_params(); let mut state = BheState::new(); bhez(¶ms, &mut state); let nhe = params.dims.nfreqe + params.matkey.inhe as usize; // 检查矩阵元素被填充 assert!(state.b[nhe][nhe].abs() > 0.0 || state.vecl[nhe].abs() > 0.0); } #[test] fn test_erfcx() { // 测试 erfcx 函数 let x = 1.0; let result = erfcx(x); assert!(result > 0.0); assert!(result < 2.0); } }