1438 lines
50 KiB
Rust
1438 lines
50 KiB
Rust
//! 线性化方程组右端向量计算。
|
||
//!
|
||
//! 重构自 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<f64>,
|
||
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<f64>,
|
||
}
|
||
|
||
/// 计算 RHS 向量。
|
||
///
|
||
/// 完整实现对应 Fortran rhsgen.f (lines 1-722)
|
||
pub fn rhsgen<C: RhsgenCallbacks>(params: &mut RhsgenParams<C>) -> 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<ND 且 IDISK!=0 (盘模式) 时执行
|
||
// ========================================================================
|
||
if inzd > 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<C: RhsgenCallbacks>(params: &mut RhsgenParams<C>, 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<C: RhsgenCallbacks>(params: &mut RhsgenParams<C>, 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<C: RhsgenCallbacks>(params: &mut RhsgenParams<C>, 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<C: RhsgenCallbacks>(
|
||
params: &mut RhsgenParams<C>, 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<C: RhsgenCallbacks>(params: &mut RhsgenParams<C>, 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<C: RhsgenCallbacks>(params: &mut RhsgenParams<C>, 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<f64>, Vec<f64>, Vec<f64>, Vec<f64>, Vec<f64>, Vec<f64>) {
|
||
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);
|
||
}
|
||
}
|