SpectraRust/src/tlusty/math/radiative/radpre.rs
2026-06-06 14:24:50 +08:00

642 lines
18 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.

//! 辐射加速度计算 - RADPRE。
//!
//! 重构自 TLUSTY `radpre.f`
//!
//! 计算辐射加速度,自动排除对总辐射压力贡献最强的线。
//! 使用深度相关准则进行频率筛选。
use crate::tlusty::state::constants::{BOLK, HALF, MDEPTH, MFREQ, MLEVEL, MTRANS};
use crate::tlusty::math::indexx;
use crate::tlusty::math::quit;
// f2r_depends: OPACF1, RTEFR1
// ============================================================================
// 常量定义
// ============================================================================
/// PGRD 常量: 4.1916825e-10
const PGRD: f64 = 4.1916825e-10;
// ============================================================================
// XGRD 预设数组
// ============================================================================
/// XGRD0: 10 元素预设数组(用于 XGRAD = 0
const XGRD0: [f64; 10] = [
0.1, 0.3, 0.5, 0.7, 0.9, 0.92, 0.94, 0.96, 0.98, 0.99,
];
/// XGRD1: 20 元素预设数组(用于 XGRAD = -1
const XGRD1: [f64; 20] = [
0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.65, 0.7, 0.75, 0.8,
0.85, 0.9, 0.92, 0.94, 0.96, 0.98, 0.99, 0.99, 0.99, 0.99,
];
/// XGRD2: 20 元素预设数组(用于 XGRAD = -2
const XGRD2: [f64; 20] = [
0.1, 0.2, 0.3, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7,
0.75, 0.8, 0.84, 0.87, 0.9, 0.93, 0.95, 0.97, 0.98, 0.99,
];
// ============================================================================
// 参数结构体
// ============================================================================
/// RADPRE 配置参数。
#[derive(Debug, Clone)]
pub struct RadpreConfig {
/// XGRAD 参数(控制频率筛选)
/// = 0: 使用 XGRD0 预设
/// = -1: 使用 XGRD1 预设
/// = -2: 使用 XGRD2 预设
/// > 0: 使用固定值
pub xgrad: f64,
/// 重力加速度 (cm/s²)
pub grav: f64,
/// 辐射压力标志 (0: 不计算, >0: 计算)
pub ifprad: i32,
/// ODF 采样标志
pub ispodf: i32,
/// 最大显式频率数
pub mfrex: usize,
}
impl Default for RadpreConfig {
fn default() -> Self {
Self {
xgrad: 0.0,
grav: 0.0,
ifprad: 0,
ispodf: 0,
mfrex: 100,
}
}
}
/// RADPRE 模型状态参数。
pub struct RadpreModelState<'a> {
/// 深度点数
pub nd: usize,
/// 温度 [nd]
pub temp: &'a [f64],
/// 电子密度 [nd]
pub elec: &'a [f64],
/// 总粒子密度 [nd]
pub dens: &'a [f64],
/// 柱质量 [nd]
pub dm: &'a [f64],
/// 平均分子量倒数 [nd]
pub wmm: &'a [f64],
/// 湍流速度 [nd]
pub vturb: &'a [f64],
/// 深度间隔 [nd-1]
pub deldm: &'a [f64],
/// 柱质量梯度 [nd]
pub dedm1: f64,
/// Roseland 不透明度 [nd]
pub abrosd: &'a [f64],
/// 总吸收系数 [nd]
pub absot: &'a [f64],
/// 密度 [nd] (用于 dens1)
pub dens1: &'a [f64],
}
/// RADPRE 频率相关参数(可变)。
pub struct RadpreFreqParamsMut<'a> {
/// 频率总数
pub nfreq: usize,
/// 频率 [nfreq]
pub freq: &'a [f64],
/// 频率权重 [nfreq]
pub w: &'a [f64],
/// 主谱线索引 [nfreq], 0 表示无
pub ijlin: &'a [i32],
/// 每个频率的线数 [nfreq]
pub nlines: &'a [i32],
/// 跃迁中心频率 [mtrans]
pub fr0: &'a [f64],
/// 跃迁显式频率索引 [mtrans]
pub indexp: &'a mut [i32],
/// 跃迁显式频率标志 [mtrans]
pub lexp: &'a mut [bool],
}
/// RADPRE ALI 相关参数(可变)。
pub struct RadpreAliParamsMut<'a> {
/// ALI 索引 [nfreq]
pub ijali: &'a mut [i32],
/// 显式频率索引 [nfreq]
pub ijex: &'a mut [i32],
/// 显式频率对应的原始频率索引 [mfrex]
pub ijfr: &'a mut [i32],
/// 显式频率标志 [nfreq]
pub ijx: &'a mut [i32],
/// 连续性权重 [nfreq]
pub wc: &'a mut [f64],
/// 当前显式频率计数
pub nfreqe: &'a mut i32,
/// 跃迁线索引 [max_lines][nfreq]
pub itrlin: &'a [Vec<i32>],
}
/// RADPRE 辐射场参数(由 OPACF1 和 RTEFR1 计算)。
pub struct RadpreRadField<'a> {
/// 辐射强度 [nd]
pub rad1: &'a [f64],
/// Eddington 因子 [nd]
pub fak1: &'a [f64],
/// 吸收系数 [nd]
pub abso1: &'a [f64],
/// 表面 Eddington 因子 [nfreq]
pub fh: &'a [f64],
/// 外部辐射 [nfreq]
pub hextrd: &'a [f64],
}
/// RADPRE 输出状态(可变)。
pub struct RadpreOutputStateMut<'a> {
/// 跳过频率标志 [nd][nfreq]
pub lskip: &'a mut [Vec<bool>],
/// 辐射压力 [nd]
pub pradt: &'a mut [f64],
/// 累积辐射压力 [nd]
pub prada: &'a mut [f64],
/// 频率相关辐射加速度 [nd][nfreq]
pub gradf: &'a mut [Vec<f64>],
}
/// RADPRE 输出结果。
#[derive(Debug, Clone)]
pub struct RadpreOutput {
/// 深度相关阈值 [nd]
pub xgrd: Vec<f64>,
/// 气体+湍流压力加速度 [nd]
pub ggrt: Vec<f64>,
/// 辐射加速度 [nd]
pub grad: Vec<f64>,
/// 累积辐射加速度 [nd]
pub grada: Vec<f64>,
/// 表面辐射压力
pub prd0: f64,
/// 新增显式频率数
pub nfe: i32,
}
// ============================================================================
// 主函数
// ============================================================================
/// 计算辐射加速度RADPRE 主函数)。
///
/// # 参数
/// - `config`: 配置参数
/// - `model`: 模型状态
/// - `freq`: 频率参数(可变)
/// - `ali`: ALI 参数(可变)
/// - `output`: 输出状态(可变)
/// - `nn`: 跃迁计数(会被修改)
///
/// # 返回值
/// RADPRE 输出结果
#[allow(clippy::too_many_arguments)]
pub fn radpre(
config: &RadpreConfig,
model: &RadpreModelState,
freq: &mut RadpreFreqParamsMut,
ali: &mut RadpreAliParamsMut,
output: &mut RadpreOutputStateMut,
nn: &mut i32,
) -> RadpreOutput {
let nd = model.nd;
let nfreq = freq.nfreq;
// 输出数组初始化
let mut xgrd = vec![0.0; nd];
let mut ggrt = vec![0.0; nd];
let mut grad = vec![0.0; nd];
let mut grada = vec![0.0; nd];
let mut prid = vec![0.0; nd];
let mut pgt = vec![0.0; nd];
// 工作数组
let mut gradi = vec![0.0; nfreq];
// ========================================================================
// 步骤 1: 设置深度相关阈值 XGRD
// ========================================================================
setup_xgrd(config.xgrad, nd, &mut xgrd);
// ========================================================================
// 步骤 2: 计算气体和湍流压力的加速度
// ========================================================================
// PGAS = (DENS/WMM + ELEC) * BOLK * TEMP
for id in 0..nd {
let pgas = (model.dens[id] / model.wmm[id] + model.elec[id]) * BOLK * model.temp[id];
pgt[id] = pgas + HALF * model.dens[id] * model.vturb[id] * model.vturb[id];
}
// GGRT(ID) = (PGT(ID) - PGT(ID-1)) / (DM(ID) - DM(ID-1))
ggrt[0] = 0.0; // 会在下面设置为 ggrt[1]
for id in 1..nd {
let dm_diff = model.dm[id] - model.dm[id - 1];
if dm_diff.abs() > 1e-30 {
ggrt[id] = (pgt[id] - pgt[id - 1]) / dm_diff;
} else {
ggrt[id] = 0.0;
}
}
ggrt[0] = ggrt[1];
// ========================================================================
// 步骤 3: 初始化辐射相关数组
// ========================================================================
for id in 0..nd {
grad[id] = 0.0;
grada[id] = 0.0;
output.pradt[id] = 0.0;
}
// PRID(ID) = PGRD / (DM(ID) - DM(ID-1))
for id in 1..nd {
let dm_diff = model.dm[id] - model.dm[id - 1];
if dm_diff.abs() > 1e-30 {
prid[id] = PGRD / dm_diff;
} else {
prid[id] = 0.0;
}
}
let pgrd1 = PGRD / model.dens1[0];
let mut prd0 = 0.0;
// ========================================================================
// 步骤 4: 遍历所有频率,计算辐射加速度
// ========================================================================
// 注意:实际的 OPACF1 和 RTEFR1 调用需要在外部完成
// 这里只是框架grada 需要在外部通过 radpre_accumulate_frequency 累积
// ========================================================================
// 步骤 5: 深度相关的频率拒绝
// ========================================================================
let mut nfe = 0;
// 累积 Roseland 光学深度
let mut taur = HALF * model.dedm1 * model.abrosd[0] * model.dens[0];
for id in 0..nd {
// 更新光学深度
if id > 0 {
let dtaur = model.deldm[id - 1] * (model.abrosd[id] + model.abrosd[id - 1]);
taur += dtaur;
}
// 计算阈值
let xgr0 = config.grav * xgrd[id].abs();
// 初始化 gradi 数组
for ij in 0..nfreq {
gradi[ij] = output.gradf[id][ij];
// 如果不计算辐射压力,跳过所有频率
if config.ifprad == 0 {
output.lskip[id][ij] = true;
} else {
output.lskip[id][ij] = false;
}
}
// 对辐射加速度排序
let iigr = indexx(&gradi);
grad[id] = grada[id];
let mut ggrt0 = ggrt[id];
// 如果 XGRAD > 0 且 ID > 1继承上一层的 LSKIP
if config.xgrad > 0.0 && id > 0 {
for ij in 0..nfreq {
output.lskip[id][ij] = output.lskip[0][ij];
if output.lskip[id][ij] {
ggrt0 -= gradi[ij];
grad[id] -= gradi[ij];
}
}
continue;
}
// 对于 ID >= ND-1跳过频率拒绝
if id >= nd - 2 {
continue;
}
// 频率拒绝循环
let mut ijr = nfreq as i32 - 1;
while grad[id] > xgr0 && ijr >= 0 {
let ij = iigr[ijr as usize];
// 检查是否是连续谱或无谱线
if freq.ijlin[ij] == 0 && freq.nlines[ij] == 0 {
ijr -= 1;
continue;
}
// 标记跳过
output.lskip[id][ij] = true;
ggrt0 -= gradi[ij];
grad[id] -= gradi[ij];
// 处理显式频率
if xgrd[id] < 0.0 && nfe < 10 {
process_explicit_frequency(
id, ij, freq, ali, nn, &mut nfe, config.mfrex, config.ispodf,
);
}
ijr -= 1;
}
}
// 辐射压力单位转换
// PRADT(ID) = PRADT(ID) * PCK
// 注意PCK 常量需要从 constants 获取
// 这里暂时省略单位转换
RadpreOutput {
xgrd,
ggrt,
grad,
grada,
prd0,
nfe,
}
}
// ============================================================================
// 辅助函数
// ============================================================================
/// 设置深度相关阈值 XGRD。
fn setup_xgrd(xgrad: f64, nd: usize, xgrd: &mut [f64]) {
if xgrad == 0.0 {
// 使用 XGRD0 预设
for id in 0..nd.min(10) {
xgrd[id] = XGRD0[id];
}
for id in 10..nd {
xgrd[id] = xgrd[id - 1];
}
} else if xgrad == -1.0 {
// 使用 XGRD1 预设
for id in 0..nd.min(20) {
xgrd[id] = XGRD1[id];
}
for id in 20..nd {
xgrd[id] = xgrd[id - 1];
}
} else if xgrad == -2.0 {
// 使用 XGRD2 预设
for id in 0..nd.min(20) {
xgrd[id] = XGRD2[id];
}
for id in 20..nd {
xgrd[id] = xgrd[id - 1];
}
} else {
// 使用固定值
for id in 0..nd {
xgrd[id] = xgrad;
}
}
}
/// 处理显式频率。
#[allow(clippy::too_many_arguments)]
fn process_explicit_frequency(
id: usize,
ij: usize,
freq: &mut RadpreFreqParamsMut,
ali: &mut RadpreAliParamsMut,
nn: &mut i32,
nfe: &mut i32,
mfrex: usize,
ispodf: i32,
) {
if ispodf == 0 {
// 单谱线情况
let itr = freq.ijlin[ij];
if itr == 0 {
return;
}
let itr_idx = (itr.abs() - 1) as usize;
let indxpa = freq.indexp[itr_idx].abs();
let dx = (freq.freq[ij] - freq.freq.get(ij + 1).unwrap_or(&0.0)) * 0.25;
let dz = (freq.freq[ij] - freq.fr0[itr_idx]).abs();
if dz < dx && indxpa == 1 {
if freq.indexp[itr_idx] < 0 {
freq.indexp[itr_idx] = -9;
} else {
freq.indexp[itr_idx] = 9;
}
if !freq.lexp[itr_idx] {
freq.lexp[itr_idx] = true;
*ali.nfreqe += 1;
if *ali.nfreqe as usize > mfrex {
quit("nfreqe.gt.mfrex", *ali.nfreqe, mfrex as i32);
}
*nn += 1;
ali.ijali[ij] = 0;
ali.ijex[ij] = *ali.nfreqe;
ali.ijfr[(*ali.nfreqe - 1) as usize] = ij as i32;
ali.ijx[ij] = 1;
ali.wc[ij] = 0.0;
// Fortran: WRITE(10,612) FREQ(IJ),ITR,IJ,NFREQE
eprintln!(" AUTOMATIC EXPLICIT FREQ. {:12.6e}{:8}{:8}{:8}", freq.freq[ij], freq.ijlin[ij], ij, *ali.nfreqe);
*nfe += 1;
}
}
} else {
// 多谱线情况
let nlines_ij = freq.nlines[ij];
for ilint in 0..nlines_ij as usize {
let itr = ali.itrlin[ilint][ij];
if itr <= 0 {
continue;
}
let itr_idx = (itr - 1) as usize;
let indxpa = freq.indexp[itr_idx].abs();
let dx = (freq.freq[ij] - freq.freq.get(ij + 1).unwrap_or(&0.0)) * 0.25;
let dz = (freq.freq[ij] - freq.fr0[itr_idx]).abs();
if dz > dx || indxpa != 1 {
continue;
}
if freq.indexp[itr_idx] < 0 {
freq.indexp[itr_idx] = -9;
} else {
freq.indexp[itr_idx] = 9;
}
if !freq.lexp[itr_idx] {
freq.lexp[itr_idx] = true;
*ali.nfreqe += 1;
if *ali.nfreqe as usize > mfrex {
quit("nfreqe.gt.mfrex", *ali.nfreqe, mfrex as i32);
}
*nn += 1;
ali.ijali[ij] = 0;
ali.ijex[ij] = *ali.nfreqe;
ali.ijfr[(*ali.nfreqe - 1) as usize] = ij as i32;
ali.ijx[ij] = 1;
ali.wc[ij] = 0.0;
// Fortran: WRITE(10,612) FREQ(IJ),ITR,IJ,NFREQE
eprintln!(" AUTOMATIC EXPLICIT FREQ. {:12.6e}{:8}{:8}{:8}", freq.freq[ij], itr, ij, *ali.nfreqe);
*nfe += 1;
}
}
}
}
/// 计算单个频率的辐射加速度贡献。
///
/// 这个函数在频率循环中调用,用于累积辐射加速度。
pub fn radpre_accumulate_frequency(
ij: usize,
nd: usize,
rad: &RadpreRadField,
model: &RadpreModelState,
freq: &RadpreFreqParamsMut,
output: &mut RadpreOutputStateMut,
grada: &mut [f64],
prd0: &mut f64,
) {
let pgrd1 = PGRD / model.dens1[0];
let mut prid = vec![0.0; nd];
// 计算 PRID
for id in 1..nd {
let dm_diff = model.dm[id] - model.dm[id - 1];
if dm_diff.abs() > 1e-30 {
prid[id] = PGRD / dm_diff;
}
}
// 表面辐射加速度
let fluxw = freq.w[ij] * (rad.rad1[0] * rad.fh[ij] - rad.hextrd[ij]);
output.gradf[0][ij] = fluxw * rad.abso1[0] * pgrd1;
grada[0] += output.gradf[0][ij];
// 深度点辐射加速度
for id in 1..nd {
let frd = rad.fak1[id] * rad.rad1[id] - rad.fak1[id - 1] * rad.rad1[id - 1];
output.gradf[id][ij] = freq.w[ij] * frd * prid[id];
grada[id] += output.gradf[id][ij];
}
// 更新表面辐射压力
*prd0 += rad.abso1[0] * freq.w[ij] * (rad.rad1[0] * rad.fh[ij] - rad.hextrd[ij]);
}
// ============================================================================
// 测试
// ============================================================================
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_setup_xgrd_default() {
let nd = 15;
let mut xgrd = vec![0.0; nd];
setup_xgrd(0.0, nd, &mut xgrd);
// 检查前 10 个元素
for i in 0..10 {
assert!((xgrd[i] - XGRD0[i]).abs() < 1e-15);
}
// 检查后面的元素继承
for i in 10..nd {
assert!((xgrd[i] - xgrd[i - 1]).abs() < 1e-15);
}
}
#[test]
fn test_setup_xgrd_minus1() {
let nd = 25;
let mut xgrd = vec![0.0; nd];
setup_xgrd(-1.0, nd, &mut xgrd);
// 检查前 20 个元素
for i in 0..20 {
assert!((xgrd[i] - XGRD1[i]).abs() < 1e-15);
}
// 检查后面的元素继承
for i in 20..nd {
assert!((xgrd[i] - xgrd[i - 1]).abs() < 1e-15);
}
}
#[test]
fn test_setup_xgrd_minus2() {
let nd = 25;
let mut xgrd = vec![0.0; nd];
setup_xgrd(-2.0, nd, &mut xgrd);
// 检查前 20 个元素
for i in 0..20 {
assert!((xgrd[i] - XGRD2[i]).abs() < 1e-15);
}
}
#[test]
fn test_setup_xgrd_fixed() {
let nd = 10;
let mut xgrd = vec![0.0; nd];
setup_xgrd(0.5, nd, &mut xgrd);
// 所有元素应该等于固定值
for i in 0..nd {
assert!((xgrd[i] - 0.5).abs() < 1e-15);
}
}
#[test]
fn test_pgrd_constant() {
// 验证 PGRD 常量值
assert!((PGRD - 4.1916825e-10).abs() < 1e-20);
}
#[test]
fn test_xgrd_arrays() {
// 验证预设数组长度
assert_eq!(XGRD0.len(), 10);
assert_eq!(XGRD1.len(), 20);
assert_eq!(XGRD2.len(), 20);
// 验证数组是递增的
for i in 1..XGRD0.len() {
assert!(XGRD0[i] >= XGRD0[i - 1]);
}
for i in 1..XGRD1.len() {
assert!(XGRD1[i] >= XGRD1[i - 1]);
}
for i in 1..XGRD2.len() {
assert!(XGRD2[i] >= XGRD2[i - 1]);
}
}
}