SpectraRust/src/tlusty/math/solvers/rhsgen.rs
2026-04-01 16:35:36 +08:00

1313 lines
44 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 线性化方程组右端向量计算。
//!
//! 重构自 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;
/// 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,
}
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,
}
}
}
/// 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
}
}
/// 默认的空回调实现
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];
// 计算行索引
let nhe = nfreqe + inhe as usize;
let nre = nfreqe + inre as usize;
let npc = nfreqe + inpc as usize;
let ndel = nfreqe + indl as usize;
let nse = nfreqe + inse as usize;
let nzd = nfreqe + inzd as usize;
// ========================================================================
// 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)
// ========================================================================
if inse > 0 && params.config.ifpopr >= 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 = &params.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 = &params.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 = &params.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)
if cfg.idisk == 0 || cfg.ibche < 0 {
// ... 完整的下边界条件逻辑
for ij in (ij1 - 1)..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 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;
if ij < vecl.len() {
// Fortran lines 346-350
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 = &params.config;
if id == 1 {
// 上边界条件 (Fortran lines 390-466)
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(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 line 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);
if nhe < vecl.len() {
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 {
// 内部点 (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];
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);
let psim_nhe = params.callbacks.psim(nhe);
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];
}
}
}
/// 计算辐射平衡 (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 = &params.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 = &params.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() {
vecl[ndel] = 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);
}
}