991 lines
34 KiB
Rust
991 lines
34 KiB
Rust
//! Rybicki 形式完全线性化矩阵计算。
|
||
//!
|
||
//! 重构自 TLUSTY `rybmat.f`
|
||
|
||
use crate::tlusty::state::constants::{HALF, MDEPTH, TWO, UN};
|
||
|
||
/// 物理常数
|
||
const BN: f64 = 1.4743e-2; // Planck 常数前因子
|
||
const HK: f64 = 4.79928144e-11; // h/k (s·K)
|
||
|
||
/// Rybicki 矩阵输入参数。
|
||
pub struct RybmatParams<'a> {
|
||
/// 频率索引 (0-indexed)
|
||
pub ij: usize,
|
||
/// 深度点数
|
||
pub nd: usize,
|
||
|
||
// 深度相关数组
|
||
pub dm: &'a [f64],
|
||
pub temp: &'a [f64],
|
||
pub dens: &'a [f64],
|
||
|
||
// ALIPAR 相关
|
||
pub abso1: &'a [f64],
|
||
pub scat1: &'a [f64],
|
||
pub emis1: &'a [f64],
|
||
pub fak1: &'a [f64],
|
||
pub rad1: &'a [f64],
|
||
pub dabt1: &'a [f64],
|
||
pub demt1: &'a [f64],
|
||
pub deldm: &'a [f64],
|
||
|
||
// 配置参数
|
||
pub isplin: i32,
|
||
pub iubc: i32,
|
||
pub ibc: i32,
|
||
pub idisk: i32,
|
||
pub ifryb: i32,
|
||
pub reint: &'a [f64],
|
||
pub redif: &'a [f64],
|
||
pub w: &'a [f64],
|
||
pub freq: &'a [f64],
|
||
pub fh: &'a [f64],
|
||
pub fhd: &'a [f64],
|
||
pub icentr: i32,
|
||
pub ilbc: i32,
|
||
pub nretc: usize,
|
||
|
||
// DSCTVA
|
||
pub dsct1: &'a [f64],
|
||
pub dscn1: &'a [f64],
|
||
|
||
// 频率索引映射 (1-indexed)
|
||
pub ijfr: &'a [i32],
|
||
|
||
// 上边界条件额外参数
|
||
pub extrad: &'a [f64], // EXTRAD(IJ): 外部辐射
|
||
pub q0: &'a [f64], // Q0(IJ): 边界条件参数
|
||
pub uu0: &'a [f64], // UU0(IJ): 边界条件参数
|
||
}
|
||
|
||
/// Rybicki 矩阵输出结构体。
|
||
#[derive(Debug, Clone)]
|
||
pub struct RybmatResult {
|
||
pub ra: Vec<f64>,
|
||
pub rb: Vec<f64>,
|
||
pub rc: Vec<f64>,
|
||
pub vr: Vec<f64>,
|
||
pub ua: Vec<f64>,
|
||
pub ub: Vec<f64>,
|
||
pub uc: Vec<f64>,
|
||
pub va: Vec<f64>,
|
||
pub vb: Vec<f64>,
|
||
pub vc: Vec<f64>,
|
||
pub wr: Vec<f64>,
|
||
pub wm: Vec<Vec<f64>>,
|
||
}
|
||
|
||
impl Default for RybmatResult {
|
||
fn default() -> Self {
|
||
Self {
|
||
ra: vec![0.0; MDEPTH],
|
||
rb: vec![0.0; MDEPTH],
|
||
rc: vec![0.0; MDEPTH],
|
||
vr: vec![0.0; MDEPTH],
|
||
ua: vec![0.0; MDEPTH],
|
||
ub: vec![0.0; MDEPTH],
|
||
uc: vec![0.0; MDEPTH],
|
||
va: vec![0.0; MDEPTH],
|
||
vb: vec![0.0; MDEPTH],
|
||
vc: vec![0.0; MDEPTH],
|
||
wr: vec![0.0; MDEPTH],
|
||
wm: vec![vec![0.0; MDEPTH]; MDEPTH],
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 计算 Rybicki 形式完全线性化矩阵。
|
||
pub fn rybmat(params: &RybmatParams) -> RybmatResult {
|
||
let mut result = RybmatResult::default();
|
||
|
||
const SIXTH: f64 = UN / 6.0;
|
||
const THIRD: f64 = UN / 3.0;
|
||
const TWOTHR: f64 = TWO / 3.0;
|
||
|
||
let nd = params.nd;
|
||
let ijt = (params.ijfr[params.ij] - 1) as usize; // Fortran 1-indexed 转 0-indexed
|
||
|
||
// ==============================================================
|
||
// 1. 上边界条件 (Fortran ID=1, Rust ID=0)
|
||
// ==============================================================
|
||
let id = 0;
|
||
let ddm = (params.dm[id + 1] - params.dm[id]) * HALF;
|
||
let dtm = UN / ((params.abso1[id] + params.abso1[id + 1]) * ddm);
|
||
let dtm2 = dtm * dtm;
|
||
let alf = dtm * ddm;
|
||
let fd = TWO * params.fh[ijt];
|
||
let exti = params.extrad[params.ij];
|
||
let bet = (exti - fd * params.rad1[id]) * dtm;
|
||
let gam = (params.fak1[id] * params.rad1[id] - params.fak1[id + 1] * params.rad1[id + 1])
|
||
* TWO
|
||
* dtm2;
|
||
let s0 = (params.emis1[id] + params.scat1[id] * params.rad1[id]) / params.abso1[id];
|
||
let c1 = alf * (TWO * gam - bet);
|
||
let b1 = c1 - s0 / params.abso1[id];
|
||
let q0_ij = params.q0[params.ij];
|
||
let uu0_ij = params.uu0[params.ij];
|
||
let unq = UN + TWO * dtm * q0_ij;
|
||
|
||
result.rb[id] = -(UN + dtm * (fd + TWO * params.fak1[id] * dtm))
|
||
+ params.scat1[id] / params.abso1[id] * unq;
|
||
result.rc[id] = TWO * params.fak1[id + 1] * dtm2;
|
||
result.vr[id] = gam - bet + params.rad1[id] - s0 * unq;
|
||
|
||
// UB, UC 完整计算
|
||
result.ub[id] = b1 * params.dabt1[id]
|
||
+ (params.demt1[id] + params.dsct1[id] * params.rad1[id]) / params.abso1[id] * unq
|
||
- params.emis1[id] / params.abso1[id] / params.abso1[id] * params.dabt1[id] * TWO * dtm
|
||
* q0_ij
|
||
+ dtm * s0
|
||
* (uu0_ij * params.dm[0] * params.dabt1[id]
|
||
- TWO * q0_ij * dtm * ddm * params.dabt1[id]);
|
||
result.uc[id] = c1 * params.dabt1[id + 1] - TWO * dtm * q0_ij * s0 * dtm * ddm * params.dabt1[id + 1];
|
||
|
||
// IUBC 边界条件修正
|
||
if params.iubc > 0 {
|
||
let corf = HALF / dtm;
|
||
result.rb[id] *= corf;
|
||
result.rc[id] *= corf;
|
||
result.vr[id] *= corf;
|
||
let c1 = (gam - params.rad1[id] + s0) * corf * alf;
|
||
let b1 = c1 - corf * s0 / params.abso1[id];
|
||
result.ub[id] = b1 * params.dabt1[id]
|
||
+ (params.demt1[id] + params.dsct1[id] * params.rad1[id]) / params.abso1[id] * corf;
|
||
result.uc[id] = c1 * params.dabt1[id + 1];
|
||
}
|
||
|
||
// ==============================================================
|
||
// 2. 深度循环 (Fortran ID=2 到 ND-1, Rust ID=1 到 ND-2)
|
||
// ==============================================================
|
||
for id in 1..nd - 1 {
|
||
let ddm = (params.dm[id] - params.dm[id - 1]) * HALF;
|
||
let ddp = (params.dm[id + 1] - params.dm[id]) * HALF;
|
||
let dzm = params.abso1[id] + params.abso1[id - 1];
|
||
let dzp = params.abso1[id] + params.abso1[id + 1];
|
||
let dtaup = dzp * ddp;
|
||
let dtaum = dzm * ddm;
|
||
let dtau0 = HALF * (dtaup + dtaum);
|
||
|
||
let frd = params.fak1[id] * params.rad1[id];
|
||
let alf1 = (frd - params.fak1[id + 1] * params.rad1[id + 1]) / dtaup / dtau0;
|
||
let gam1 = (frd - params.fak1[id - 1] * params.rad1[id - 1]) / dtaum / dtau0;
|
||
let bet1 = alf1 + gam1;
|
||
let x1 = HALF * bet1 / dtau0;
|
||
let mut a1 = (gam1 + x1 * dtaum) / dzm;
|
||
let mut c1 = (alf1 + x1 * dtaup) / dzp;
|
||
let mut b1 = a1 + c1;
|
||
|
||
let chielm = params.scat1[id - 1];
|
||
let chiel0 = params.scat1[id];
|
||
let chielp = params.scat1[id + 1];
|
||
let s0 = (params.emis1[id] + chiel0 * params.rad1[id]) / params.abso1[id];
|
||
|
||
// 初始化 spline/hermitian 参数
|
||
let (as_val, cs_val, bs_val, bet2, a2, c2, sm, sp) = if params.isplin % 3 == 0 {
|
||
// 简单一阶差分
|
||
(0.0, 0.0, UN, 0.0, 0.0, 0.0, 0.0, 0.0)
|
||
} else {
|
||
let sm = (params.emis1[id - 1] + params.rad1[id - 1] * chielm) / params.abso1[id - 1];
|
||
let sp = (params.emis1[id + 1] + params.rad1[id + 1] * chielp) / params.abso1[id + 1];
|
||
|
||
if params.isplin == 1 {
|
||
// Spline collocation
|
||
let as_val = dtaum / dtau0 * SIXTH;
|
||
let cs_val = dtaup / dtau0 * SIXTH;
|
||
let bs_val = TWOTHR; // 0.666666666666667
|
||
let alf2 = as_val * (params.rad1[id - 1] - sm);
|
||
let gam2 = cs_val * (params.rad1[id + 1] - sp);
|
||
let bet2 = alf2 + gam2;
|
||
let x = HALF * bet2 / dtau0;
|
||
let a2 = (gam2 - x * dtaum) / dzm;
|
||
let c2 = (alf2 - x * dtaup) / dzp;
|
||
(as_val, cs_val, bs_val, bet2, a2, c2, sm, sp)
|
||
} else {
|
||
// Hermitian method (ISPLIN=2)
|
||
let as_init = dtaup * dtaup / dtaum / dtau0;
|
||
let cs_init = dtaum * dtaum / dtaup / dtau0;
|
||
let al3 = (params.rad1[id + 1] - sp - params.rad1[id] + s0) * SIXTH;
|
||
let ga3 = (params.rad1[id - 1] - sm - params.rad1[id] + s0) * SIXTH;
|
||
let av = al3 * cs_init;
|
||
let cv = ga3 * as_init;
|
||
let as_val = (UN - HALF * as_init) * SIXTH;
|
||
let cs_val = (UN - HALF * cs_init) * SIXTH;
|
||
let bs_val = UN - as_val - cs_val;
|
||
let x = (av + cv) / dtau0 / 4.0;
|
||
let a2 = (x * dtaum + HALF * cv - av) / dzm;
|
||
let c2 = (x * dtaup + HALF * av - cv) / dzp;
|
||
let bet2 = as_val * (params.rad1[id - 1] - sm) + cs_val * (params.rad1[id + 1] - sp);
|
||
(as_val, cs_val, bs_val, bet2, a2, c2, sm, sp)
|
||
}
|
||
};
|
||
|
||
// 辅助量
|
||
b1 = b1 - (a2 + c2);
|
||
a1 = a1 - a2;
|
||
c1 = c1 - c2;
|
||
let a2_coeff = as_val / params.abso1[id - 1];
|
||
let c2_coeff = cs_val / params.abso1[id + 1];
|
||
let a3 = a2_coeff * sm;
|
||
let c3 = c2_coeff * sp;
|
||
|
||
let b2 = bs_val / params.abso1[id];
|
||
let b3 = b2 * s0;
|
||
let a1 = a1 - a3;
|
||
let b1 = b1 - b3;
|
||
let c1 = c1 - c3;
|
||
|
||
// 矩阵元素
|
||
result.ra[id] = params.fak1[id - 1] / dtaum / dtau0
|
||
- as_val * (UN - chielm / params.abso1[id - 1]);
|
||
result.rb[id] = -params.fak1[id] / dtau0 * (UN / dtaup + UN / dtaum)
|
||
- bs_val * (UN - chiel0 / params.abso1[id]);
|
||
result.rc[id] = params.fak1[id + 1] / dtaup / dtau0
|
||
- cs_val * (UN - chielp / params.abso1[id + 1]);
|
||
result.vr[id] = bet1 + bet2 + bs_val * (params.rad1[id] - s0);
|
||
result.ua[id] = a1 * params.dabt1[id - 1]
|
||
+ a2_coeff * (params.demt1[id - 1] + params.dsct1[id - 1] * params.rad1[id - 1]);
|
||
result.ub[id] = b1 * params.dabt1[id]
|
||
+ b2 * (params.demt1[id] + params.dsct1[id] * params.rad1[id]);
|
||
result.uc[id] = c1 * params.dabt1[id + 1]
|
||
+ c2_coeff * (params.demt1[id + 1] + params.dsct1[id + 1] * params.rad1[id + 1]);
|
||
}
|
||
|
||
// ==============================================================
|
||
// 3. 下边界条件 (Fortran ID=ND, Rust ID=ND-1)
|
||
// ==============================================================
|
||
let id = nd - 1;
|
||
let ddm = HALF * (params.dm[id] - params.dm[id - 1]);
|
||
let t0 = params.temp[id];
|
||
let tm = params.temp[id - 1];
|
||
|
||
if params.ibc > 0 && params.ibc < 4 && params.idisk == 0 {
|
||
// Planck 函数边界条件
|
||
let dtm = UN / ((params.abso1[id - 1] + params.abso1[id]) * ddm);
|
||
let dtm2 = dtm * dtm;
|
||
let fd = TWO * params.fhd[ijt];
|
||
let fr = params.freq[ijt];
|
||
let fr15 = fr * 1e-15;
|
||
let bnu = BN * fr15 * fr15 * fr15;
|
||
|
||
let x0 = HK * fr / t0;
|
||
let xm = HK * fr / tm;
|
||
let pland = bnu / (x0.exp() - UN);
|
||
let planm = bnu / (xm.exp() - UN);
|
||
let dpldt0 = pland / (UN - (-x0).exp()) * x0 / t0;
|
||
let dpldtm = planm / (UN - (-xm).exp()) * xm / tm;
|
||
|
||
let alf = dtm * ddm;
|
||
let bet = (pland - fd * params.rad1[id]) * dtm;
|
||
let gam = (params.fak1[id] * params.rad1[id]
|
||
- params.fak1[id - 1] * params.rad1[id - 1]
|
||
- THIRD * (pland - planm))
|
||
* TWO
|
||
* dtm2;
|
||
let s0 = (params.emis1[id] + params.scat1[id] * params.rad1[id]) / params.abso1[id];
|
||
let a1 = alf * (TWO * gam - bet);
|
||
let b1 = a1 - s0 / params.abso1[id];
|
||
|
||
result.ra[id] = TWO * params.fak1[id - 1] * dtm2;
|
||
result.rb[id] = -(UN + dtm * (fd + TWO * params.fak1[id] * dtm))
|
||
+ params.scat1[id] / params.abso1[id];
|
||
result.vr[id] = gam - bet + params.rad1[id] - s0;
|
||
result.ua[id] = b1 * params.dabt1[id - 1]
|
||
+ (params.demt1[id - 1] + params.dsct1[id - 1] * params.rad1[id - 1])
|
||
/ params.abso1[id - 1]
|
||
- dpldtm * dtm2 * TWOTHR;
|
||
result.ub[id] = b1 * params.dabt1[id]
|
||
+ (params.demt1[id] + params.dsct1[id] * params.rad1[id]) / params.abso1[id]
|
||
+ dpldt0 * dtm * (UN + TWOTHR * dtm);
|
||
|
||
// IFRYB > 0 分支:Rybicki 特殊边界条件
|
||
if params.ifryb > 0 {
|
||
let dtm = UN / ((params.abso1[id - 1] + params.abso1[id]) * ddm);
|
||
let fr = params.freq[ijt];
|
||
let fr15 = fr * 1e-15;
|
||
let bnu = BN * fr15 * fr15 * fr15;
|
||
|
||
let x0 = HK * fr / t0;
|
||
let xm = HK * fr / tm;
|
||
let pland = bnu / (x0.exp() - UN);
|
||
let planm = bnu / (xm.exp() - UN);
|
||
let dpldt0 = pland / (UN - (-x0).exp()) * x0 / t0;
|
||
let dpldtm = planm / (UN - (-xm).exp()) * xm / tm;
|
||
|
||
let gam = (params.fak1[id] * params.rad1[id]
|
||
- params.fak1[id - 1] * params.rad1[id - 1]
|
||
- THIRD * (pland - planm))
|
||
* dtm;
|
||
let s0 = (params.emis1[id] + params.scat1[id] * params.rad1[id]) / params.abso1[id];
|
||
let bs = HALF / dtm;
|
||
let bet = bs * (params.rad1[id] - s0);
|
||
let a1 = (gam - bet) * dtm * ddm;
|
||
let b1 = a1 - bs * s0 / params.abso1[id];
|
||
|
||
result.ra[id] = params.fak1[id - 1] * dtm;
|
||
result.rb[id] = -params.fak1[id] * dtm
|
||
- bs * (UN - params.scat1[id] / params.abso1[id])
|
||
- params.fhd[ijt];
|
||
result.vr[id] = gam + bet - HALF * pland + params.fhd[ijt] * params.rad1[id];
|
||
result.ua[id] = a1 * params.dabt1[id - 1] - dpldtm * dtm * THIRD;
|
||
result.ub[id] = b1 * params.dabt1[id]
|
||
+ bs * (params.demt1[id] + params.dsct1[id] * params.rad1[id]) / params.abso1[id]
|
||
+ (HALF + THIRD * dtm) * dpldt0;
|
||
}
|
||
} else {
|
||
// 磁盘模式:I(taumax,-mu,nu) = I(taumax,+mu,nu)
|
||
let dzm = params.abso1[id] + params.abso1[id - 1];
|
||
let frd = params.fak1[id] * params.rad1[id] - params.fak1[id - 1] * params.rad1[id - 1];
|
||
let dtaum = dzm * ddm;
|
||
let gam1 = frd / dtaum;
|
||
let a2 = 0.0; // AS = 0 in this branch
|
||
let a1 = gam1 / dzm;
|
||
let bs = dtaum * HALF;
|
||
let s0 = (params.emis1[id] + params.scat1[id] * params.rad1[id]) / params.abso1[id];
|
||
let gam2 = bs * (params.rad1[id] - s0);
|
||
let x1 = gam2 / dzm;
|
||
let a1 = a1 - x1;
|
||
let b2 = bs / params.abso1[id];
|
||
let b1 = a1 - b2 * s0;
|
||
|
||
result.ra[id] = params.fak1[id - 1] / dtaum - a2 * (UN - params.scat1[id - 1] / params.abso1[id - 1]);
|
||
result.rb[id] = -params.fak1[id] / dtaum - bs * (UN - params.scat1[id] / params.abso1[id]);
|
||
result.ua[id] = a1 * params.dabt1[id - 1]
|
||
+ a2 / params.abso1[id - 1] * (params.demt1[id - 1] + params.dsct1[id - 1] * params.rad1[id - 1]);
|
||
result.ub[id] = b1 * params.dabt1[id] + b2 * (params.demt1[id] + params.dsct1[id] * params.rad1[id]);
|
||
result.vr[id] = gam1 + gam2;
|
||
}
|
||
|
||
// ==============================================================
|
||
// 4. 辐射平衡部分
|
||
// ==============================================================
|
||
|
||
// 4a. 积分方程部分
|
||
for id in 0..nd {
|
||
if params.reint[id] > 0.0 {
|
||
let heat = params.abso1[id] - params.scat1[id];
|
||
let dheat = (params.dabt1[id] - params.dsct1[id]) * params.rad1[id];
|
||
let wdr = params.w[params.ij] * params.dens[id] * params.reint[id];
|
||
result.vb[id] = heat * wdr;
|
||
result.wm[id][id] += (dheat - params.demt1[id]) * wdr;
|
||
result.wr[id] -= (heat * params.rad1[id] - params.emis1[id]) * wdr;
|
||
}
|
||
}
|
||
|
||
// 4b. 微分方程部分 - ID=0 (Fortran ID=1) 特殊处理
|
||
let id = 0;
|
||
if params.redif[id] > 0.0 {
|
||
let wf = params.w[params.ij] * params.fh[ijt] * params.redif[id];
|
||
result.vb[id] += wf;
|
||
result.wr[id] -= wf * params.rad1[id];
|
||
}
|
||
|
||
// 4c. 微分方程部分 - 主循环
|
||
if params.icentr == 0 {
|
||
// 一阶中点差分格式
|
||
let nd1 = if params.ilbc != 0 { nd - 1 } else { nd };
|
||
for id in 1..nd1 {
|
||
if params.redif[id] > 0.0 {
|
||
let ddm = (params.dm[id] - params.dm[id - 1]) * HALF;
|
||
let omeg0 = params.abso1[id];
|
||
let omegm = params.abso1[id - 1];
|
||
let dtaum = (omeg0 + omegm) * ddm;
|
||
let frd = params.fak1[id] * params.rad1[id]
|
||
- params.fak1[id - 1] * params.rad1[id - 1];
|
||
let gamr = frd / dtaum * params.redif[id];
|
||
let a1 = gamr / (omeg0 + omegm);
|
||
|
||
result.va[id] -= params.w[params.ij] * params.fak1[id - 1] / dtaum * params.redif[id];
|
||
result.vb[id] += params.w[params.ij] * params.fak1[id] / dtaum * params.redif[id];
|
||
result.wm[id][id - 1] -= a1 * params.w[params.ij] * params.dabt1[id - 1];
|
||
result.wm[id][id] -= a1 * params.w[params.ij] * params.dabt1[id];
|
||
result.wr[id] -= params.w[params.ij] * gamr;
|
||
}
|
||
}
|
||
|
||
// ILBC 边界条件处理
|
||
if params.ilbc > 0 {
|
||
let id = nd - 1;
|
||
if params.redif[id] > 0.0 {
|
||
let ddm = (params.dm[id] - params.dm[id - 1]) * HALF;
|
||
let dtaum = (params.abso1[id] + params.abso1[id - 1]) * ddm * 3.0;
|
||
let fr = params.freq[ijt];
|
||
let fr15 = fr * 1e-15;
|
||
let bnu = BN * fr15 * fr15 * fr15;
|
||
let x0 = HK * fr / params.temp[id];
|
||
let xm = HK * fr / params.temp[id - 1];
|
||
let pland = bnu / (x0.exp() - UN);
|
||
let planm = bnu / (xm.exp() - UN);
|
||
let dpldt0 = pland / (UN - (-x0).exp()) * x0 / params.temp[id];
|
||
let dpldtm = planm / (UN - (-xm).exp()) * xm / params.temp[id - 1];
|
||
let flx = (pland - planm) / dtaum * params.redif[id];
|
||
let a1 = flx / (params.abso1[id] + params.abso1[id - 1]);
|
||
|
||
result.wm[id][id - 1] += params.w[params.ij] * (dpldtm - a1 * params.dabt1[id - 1]);
|
||
result.wm[id][id] += params.w[params.ij] * (dpldt0 - a1 * params.dabt1[id]);
|
||
result.wr[id] -= params.w[params.ij] * flx;
|
||
}
|
||
}
|
||
} else {
|
||
// 中心差分格式 (ICENTR != 0)
|
||
for id in 1..nd - 1 {
|
||
if params.redif[id] > 0.0 {
|
||
let wwr = params.w[params.ij] * params.redif[id];
|
||
let ddm = HALF * (params.dm[id] - params.dm[id - 1]);
|
||
let ddp = HALF * (params.dm[id + 1] - params.dm[id]);
|
||
let dtm = (params.abso1[id - 1] + params.abso1[id]) * ddm;
|
||
let dtp = (params.abso1[id + 1] + params.abso1[id]) * ddp;
|
||
let dt0 = dtm + dtp;
|
||
let frm = params.fak1[id] * params.rad1[id]
|
||
- params.fak1[id - 1] * params.rad1[id - 1];
|
||
let frp = params.fak1[id + 1] * params.rad1[id + 1]
|
||
- params.fak1[id] * params.rad1[id];
|
||
let alp = dtp / dtm / dt0;
|
||
let gam = dtm / dtp / dt0;
|
||
let flx = alp * frm + gam * frp;
|
||
|
||
// 矩阵元素
|
||
result.va[id] -= wwr * params.fak1[id - 1] * alp;
|
||
result.vb[id] += wwr * params.fak1[id] * (alp - gam);
|
||
result.vc[id] = wwr * params.fak1[id + 1] * gam;
|
||
|
||
let dmtm = ddm / (dtm * dtm);
|
||
let dmtp = ddp / (dtp * dtp);
|
||
let rm = dtm / dt0;
|
||
let rp = dtp / dt0;
|
||
result.wm[id][id - 1] +=
|
||
wwr * params.dabt1[id - 1] * dmtm * (rm * rm * frp - (UN + rm) * frm);
|
||
result.wm[id][id] += wwr
|
||
* params.dabt1[id]
|
||
* ((dmtp * rp * rp - dmtm * rp * (UN + rm)) * frm
|
||
+ (dmtm * rm * rm - dmtp * rm * (UN + rp)) * frp);
|
||
result.wr[id] -= wwr * flx;
|
||
}
|
||
}
|
||
|
||
// 下边界 (ID=ND-1)
|
||
let id = nd - 1;
|
||
if params.redif[id] > 0.0 {
|
||
let ddm = HALF * (params.dm[id] - params.dm[id - 1]);
|
||
let dtaum = (params.abso1[id] + params.abso1[id - 1]) * ddm;
|
||
let frd = params.fak1[id] * params.rad1[id]
|
||
- params.fak1[id - 1] * params.rad1[id - 1];
|
||
let flx = frd / dtaum * params.redif[id];
|
||
let a1 = flx / (params.abso1[id] + params.abso1[id - 1]);
|
||
|
||
result.va[id] -= params.w[params.ij] * params.fak1[id - 1] / dtaum * params.redif[id];
|
||
result.vb[id] += params.w[params.ij] * params.fak1[id] / dtaum * params.redif[id];
|
||
result.wm[id][id - 1] -= a1 * params.w[params.ij] * params.dabt1[id - 1];
|
||
result.wm[id][id] -= a1 * params.w[params.ij] * params.dabt1[id];
|
||
result.wr[id] -= params.w[params.ij] * flx;
|
||
}
|
||
}
|
||
|
||
// 4d. 辐射平衡温度点修正
|
||
if params.nretc > 0 {
|
||
for id in 0..params.nretc {
|
||
result.wr[id] = 0.0;
|
||
result.vb[id] = 0.0;
|
||
result.va[id] = 0.0;
|
||
result.wm[id][id] = 1.0;
|
||
if id > 0 {
|
||
result.wm[id][id - 1] = 0.0;
|
||
}
|
||
}
|
||
}
|
||
|
||
result
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_rybmat_result_default() {
|
||
let result = RybmatResult::default();
|
||
assert_eq!(result.ra.len(), MDEPTH);
|
||
assert_eq!(result.rb.len(), MDEPTH);
|
||
assert_eq!(result.wm.len(), MDEPTH);
|
||
}
|
||
|
||
#[test]
|
||
fn test_rybmat_basic() {
|
||
let dm: Vec<f64> = (0..MDEPTH).map(|i| 0.01 + i as f64 * 0.01).collect();
|
||
let temp = vec![10000.0; MDEPTH];
|
||
let dens = vec![1e15; MDEPTH];
|
||
let abso1 = vec![1e-8; MDEPTH];
|
||
let scat1 = vec![1e-9; MDEPTH];
|
||
let emis1 = vec![1e-10; MDEPTH];
|
||
let fak1 = vec![0.5; MDEPTH];
|
||
let rad1 = vec![1e10; MDEPTH];
|
||
let dabt1 = vec![1e-12; MDEPTH];
|
||
let demt1 = vec![1e-14; MDEPTH];
|
||
let deldm = vec![0.001; MDEPTH];
|
||
let reint = vec![0.0; MDEPTH];
|
||
let redif = vec![0.0; MDEPTH];
|
||
let w = vec![0.01; 100];
|
||
let fh = vec![0.5; 100];
|
||
let fhd = vec![0.5; 100];
|
||
let freq = vec![1e15; 100];
|
||
let dsct1 = vec![1e-11; MDEPTH];
|
||
let dscn1 = vec![1e-16; MDEPTH];
|
||
let ijfr: Vec<i32> = (0..100).map(|i| (i + 1) as i32).collect();
|
||
let extrad = vec![0.0; 100];
|
||
let q0 = vec![0.0; 100];
|
||
let uu0 = vec![0.0; 100];
|
||
|
||
let params = RybmatParams {
|
||
ij: 0,
|
||
nd: 10,
|
||
dm: &dm,
|
||
temp: &temp,
|
||
dens: &dens,
|
||
abso1: &abso1,
|
||
scat1: &scat1,
|
||
emis1: &emis1,
|
||
fak1: &fak1,
|
||
rad1: &rad1,
|
||
dabt1: &dabt1,
|
||
demt1: &demt1,
|
||
deldm: &deldm,
|
||
isplin: 1,
|
||
iubc: 0,
|
||
ibc: 1,
|
||
idisk: 0,
|
||
ifryb: 0,
|
||
reint: &reint,
|
||
redif: &redif,
|
||
w: &w,
|
||
freq: &freq,
|
||
fh: &fh,
|
||
fhd: &fhd,
|
||
icentr: 0,
|
||
ilbc: 0,
|
||
nretc: 0,
|
||
dsct1: &dsct1,
|
||
dscn1: &dscn1,
|
||
ijfr: &ijfr,
|
||
extrad: &extrad,
|
||
q0: &q0,
|
||
uu0: &uu0,
|
||
};
|
||
|
||
let result = rybmat(¶ms);
|
||
|
||
assert!(result.rb[0].is_finite());
|
||
assert!(result.vr[0].is_finite());
|
||
}
|
||
|
||
#[test]
|
||
fn test_rybmat_with_extrad() {
|
||
let dm: Vec<f64> = (0..MDEPTH).map(|i| 0.01 + i as f64 * 0.01).collect();
|
||
let temp = vec![10000.0; MDEPTH];
|
||
let dens = vec![1e15; MDEPTH];
|
||
let abso1 = vec![1e-8; MDEPTH];
|
||
let scat1 = vec![1e-9; MDEPTH];
|
||
let emis1 = vec![1e-10; MDEPTH];
|
||
let fak1 = vec![0.5; MDEPTH];
|
||
let rad1 = vec![1e10; MDEPTH];
|
||
let dabt1 = vec![1e-12; MDEPTH];
|
||
let demt1 = vec![1e-14; MDEPTH];
|
||
let deldm = vec![0.001; MDEPTH];
|
||
let reint = vec![0.0; MDEPTH];
|
||
let redif = vec![0.0; MDEPTH];
|
||
let w = vec![0.01; 100];
|
||
let fh = vec![0.5; 100];
|
||
let fhd = vec![0.5; 100];
|
||
let freq = vec![1e15; 100];
|
||
let dsct1 = vec![1e-11; MDEPTH];
|
||
let dscn1 = vec![1e-16; MDEPTH];
|
||
let ijfr: Vec<i32> = (0..100).map(|i| (i + 1) as i32).collect();
|
||
let extrad: Vec<f64> = (0..100).map(|i| 1e10 * (i as f64 + 1.0)).collect();
|
||
let q0 = vec![0.0; 100];
|
||
let uu0 = vec![0.0; 100];
|
||
|
||
let params = RybmatParams {
|
||
ij: 0,
|
||
nd: 10,
|
||
dm: &dm,
|
||
temp: &temp,
|
||
dens: &dens,
|
||
abso1: &abso1,
|
||
scat1: &scat1,
|
||
emis1: &emis1,
|
||
fak1: &fak1,
|
||
rad1: &rad1,
|
||
dabt1: &dabt1,
|
||
demt1: &demt1,
|
||
deldm: &deldm,
|
||
isplin: 1,
|
||
iubc: 0,
|
||
ibc: 1,
|
||
idisk: 0,
|
||
ifryb: 0,
|
||
reint: &reint,
|
||
redif: &redif,
|
||
w: &w,
|
||
freq: &freq,
|
||
fh: &fh,
|
||
fhd: &fhd,
|
||
icentr: 0,
|
||
ilbc: 0,
|
||
nretc: 0,
|
||
dsct1: &dsct1,
|
||
dscn1: &dscn1,
|
||
ijfr: &ijfr,
|
||
extrad: &extrad,
|
||
q0: &q0,
|
||
uu0: &uu0,
|
||
};
|
||
|
||
let result = rybmat(¶ms);
|
||
|
||
assert!(result.rb[0].is_finite());
|
||
assert!(result.vr[0].is_finite());
|
||
}
|
||
|
||
#[test]
|
||
fn test_rybmat_hermitian() {
|
||
let dm: Vec<f64> = (0..MDEPTH).map(|i| 0.01 + i as f64 * 0.01).collect();
|
||
let temp = vec![10000.0; MDEPTH];
|
||
let dens = vec![1e15; MDEPTH];
|
||
let abso1 = vec![1e-8; MDEPTH];
|
||
let scat1 = vec![1e-9; MDEPTH];
|
||
let emis1 = vec![1e-10; MDEPTH];
|
||
let fak1 = vec![0.5; MDEPTH];
|
||
let rad1 = vec![1e10; MDEPTH];
|
||
let dabt1 = vec![1e-12; MDEPTH];
|
||
let demt1 = vec![1e-14; MDEPTH];
|
||
let deldm = vec![0.001; MDEPTH];
|
||
let reint = vec![0.0; MDEPTH];
|
||
let redif = vec![0.0; MDEPTH];
|
||
let w = vec![0.01; 100];
|
||
let fh = vec![0.5; 100];
|
||
let fhd = vec![0.5; 100];
|
||
let freq = vec![1e15; 100];
|
||
let dsct1 = vec![1e-11; MDEPTH];
|
||
let dscn1 = vec![1e-16; MDEPTH];
|
||
let ijfr: Vec<i32> = (0..100).map(|i| (i + 1) as i32).collect();
|
||
let extrad = vec![0.0; 100];
|
||
let q0 = vec![0.0; 100];
|
||
let uu0 = vec![0.0; 100];
|
||
|
||
let params = RybmatParams {
|
||
ij: 0,
|
||
nd: 10,
|
||
dm: &dm,
|
||
temp: &temp,
|
||
dens: &dens,
|
||
abso1: &abso1,
|
||
scat1: &scat1,
|
||
emis1: &emis1,
|
||
fak1: &fak1,
|
||
rad1: &rad1,
|
||
dabt1: &dabt1,
|
||
demt1: &demt1,
|
||
deldm: &deldm,
|
||
isplin: 2, // Hermitian method
|
||
iubc: 0,
|
||
ibc: 1,
|
||
idisk: 0,
|
||
ifryb: 0,
|
||
reint: &reint,
|
||
redif: &redif,
|
||
w: &w,
|
||
freq: &freq,
|
||
fh: &fh,
|
||
fhd: &fhd,
|
||
icentr: 0,
|
||
ilbc: 0,
|
||
nretc: 0,
|
||
dsct1: &dsct1,
|
||
dscn1: &dscn1,
|
||
ijfr: &ijfr,
|
||
extrad: &extrad,
|
||
q0: &q0,
|
||
uu0: &uu0,
|
||
};
|
||
|
||
let result = rybmat(¶ms);
|
||
|
||
assert!(result.rb[0].is_finite());
|
||
assert!(result.vr[0].is_finite());
|
||
}
|
||
|
||
#[test]
|
||
fn test_rybmat_centered_difference() {
|
||
let dm: Vec<f64> = (0..MDEPTH).map(|i| 0.01 + i as f64 * 0.01).collect();
|
||
let temp = vec![10000.0; MDEPTH];
|
||
let dens = vec![1e15; MDEPTH];
|
||
let abso1 = vec![1e-8; MDEPTH];
|
||
let scat1 = vec![1e-9; MDEPTH];
|
||
let emis1 = vec![1e-10; MDEPTH];
|
||
let fak1 = vec![0.5; MDEPTH];
|
||
let rad1 = vec![1e10; MDEPTH];
|
||
let dabt1 = vec![1e-12; MDEPTH];
|
||
let demt1 = vec![1e-14; MDEPTH];
|
||
let deldm = vec![0.001; MDEPTH];
|
||
let reint = vec![0.0; MDEPTH];
|
||
let redif: Vec<f64> = (0..MDEPTH).map(|_| 0.1).collect();
|
||
let w = vec![0.01; 100];
|
||
let fh = vec![0.5; 100];
|
||
let fhd = vec![0.5; 100];
|
||
let freq = vec![1e15; 100];
|
||
let dsct1 = vec![1e-11; MDEPTH];
|
||
let dscn1 = vec![1e-16; MDEPTH];
|
||
let ijfr: Vec<i32> = (0..100).map(|i| (i + 1) as i32).collect();
|
||
let extrad = vec![0.0; 100];
|
||
let q0 = vec![0.0; 100];
|
||
let uu0 = vec![0.0; 100];
|
||
|
||
let params = RybmatParams {
|
||
ij: 0,
|
||
nd: 10,
|
||
dm: &dm,
|
||
temp: &temp,
|
||
dens: &dens,
|
||
abso1: &abso1,
|
||
scat1: &scat1,
|
||
emis1: &emis1,
|
||
fak1: &fak1,
|
||
rad1: &rad1,
|
||
dabt1: &dabt1,
|
||
demt1: &demt1,
|
||
deldm: &deldm,
|
||
isplin: 1,
|
||
iubc: 0,
|
||
ibc: 1,
|
||
idisk: 0,
|
||
ifryb: 0,
|
||
reint: &reint,
|
||
redif: &redif,
|
||
w: &w,
|
||
freq: &freq,
|
||
fh: &fh,
|
||
fhd: &fhd,
|
||
icentr: 1, // 使用中心差分
|
||
ilbc: 0,
|
||
nretc: 0,
|
||
dsct1: &dsct1,
|
||
dscn1: &dscn1,
|
||
ijfr: &ijfr,
|
||
extrad: &extrad,
|
||
q0: &q0,
|
||
uu0: &uu0,
|
||
};
|
||
|
||
let result = rybmat(¶ms);
|
||
|
||
assert!(result.rb[0].is_finite());
|
||
assert!(result.vr[0].is_finite());
|
||
assert!(result.vc[5].is_finite());
|
||
}
|
||
|
||
#[test]
|
||
fn test_rybmat_ifryb_branch() {
|
||
let dm: Vec<f64> = (0..MDEPTH).map(|i| 0.01 + i as f64 * 0.01).collect();
|
||
let temp = vec![10000.0; MDEPTH];
|
||
let dens = vec![1e15; MDEPTH];
|
||
let abso1 = vec![1e-8; MDEPTH];
|
||
let scat1 = vec![1e-9; MDEPTH];
|
||
let emis1 = vec![1e-10; MDEPTH];
|
||
let fak1 = vec![0.5; MDEPTH];
|
||
let rad1 = vec![1e10; MDEPTH];
|
||
let dabt1 = vec![1e-12; MDEPTH];
|
||
let demt1 = vec![1e-14; MDEPTH];
|
||
let deldm = vec![0.001; MDEPTH];
|
||
let reint = vec![0.0; MDEPTH];
|
||
let redif = vec![0.0; MDEPTH];
|
||
let w = vec![0.01; 100];
|
||
let fh = vec![0.5; 100];
|
||
let fhd = vec![0.5; 100];
|
||
let freq = vec![1e15; 100];
|
||
let dsct1 = vec![1e-11; MDEPTH];
|
||
let dscn1 = vec![1e-16; MDEPTH];
|
||
let ijfr: Vec<i32> = (0..100).map(|i| (i + 1) as i32).collect();
|
||
let extrad = vec![0.0; 100];
|
||
let q0 = vec![0.0; 100];
|
||
let uu0 = vec![0.0; 100];
|
||
|
||
let params = RybmatParams {
|
||
ij: 0,
|
||
nd: 10,
|
||
dm: &dm,
|
||
temp: &temp,
|
||
dens: &dens,
|
||
abso1: &abso1,
|
||
scat1: &scat1,
|
||
emis1: &emis1,
|
||
fak1: &fak1,
|
||
rad1: &rad1,
|
||
dabt1: &dabt1,
|
||
demt1: &demt1,
|
||
deldm: &deldm,
|
||
isplin: 1,
|
||
iubc: 0,
|
||
ibc: 1,
|
||
idisk: 0,
|
||
ifryb: 1, // 启用 Rybicki 特殊边界条件
|
||
reint: &reint,
|
||
redif: &redif,
|
||
w: &w,
|
||
freq: &freq,
|
||
fh: &fh,
|
||
fhd: &fhd,
|
||
icentr: 0,
|
||
ilbc: 0,
|
||
nretc: 0,
|
||
dsct1: &dsct1,
|
||
dscn1: &dscn1,
|
||
ijfr: &ijfr,
|
||
extrad: &extrad,
|
||
q0: &q0,
|
||
uu0: &uu0,
|
||
};
|
||
|
||
let result = rybmat(¶ms);
|
||
|
||
assert!(result.rb[0].is_finite());
|
||
assert!(result.vr[0].is_finite());
|
||
}
|
||
|
||
#[test]
|
||
fn test_rybmat_disk_mode() {
|
||
let dm: Vec<f64> = (0..MDEPTH).map(|i| 0.01 + i as f64 * 0.01).collect();
|
||
let temp = vec![10000.0; MDEPTH];
|
||
let dens = vec![1e15; MDEPTH];
|
||
let abso1 = vec![1e-8; MDEPTH];
|
||
let scat1 = vec![1e-9; MDEPTH];
|
||
let emis1 = vec![1e-10; MDEPTH];
|
||
let fak1 = vec![0.5; MDEPTH];
|
||
let rad1 = vec![1e10; MDEPTH];
|
||
let dabt1 = vec![1e-12; MDEPTH];
|
||
let demt1 = vec![1e-14; MDEPTH];
|
||
let deldm = vec![0.001; MDEPTH];
|
||
let reint = vec![0.0; MDEPTH];
|
||
let redif = vec![0.0; MDEPTH];
|
||
let w = vec![0.01; 100];
|
||
let fh = vec![0.5; 100];
|
||
let fhd = vec![0.5; 100];
|
||
let freq = vec![1e15; 100];
|
||
let dsct1 = vec![1e-11; MDEPTH];
|
||
let dscn1 = vec![1e-16; MDEPTH];
|
||
let ijfr: Vec<i32> = (0..100).map(|i| (i + 1) as i32).collect();
|
||
let extrad = vec![0.0; 100];
|
||
let q0 = vec![0.0; 100];
|
||
let uu0 = vec![0.0; 100];
|
||
|
||
let params = RybmatParams {
|
||
ij: 0,
|
||
nd: 10,
|
||
dm: &dm,
|
||
temp: &temp,
|
||
dens: &dens,
|
||
abso1: &abso1,
|
||
scat1: &scat1,
|
||
emis1: &emis1,
|
||
fak1: &fak1,
|
||
rad1: &rad1,
|
||
dabt1: &dabt1,
|
||
demt1: &demt1,
|
||
deldm: &deldm,
|
||
isplin: 1,
|
||
iubc: 0,
|
||
ibc: 4, // 磁盘模式
|
||
idisk: 1,
|
||
ifryb: 0,
|
||
reint: &reint,
|
||
redif: &redif,
|
||
w: &w,
|
||
freq: &freq,
|
||
fh: &fh,
|
||
fhd: &fhd,
|
||
icentr: 0,
|
||
ilbc: 0,
|
||
nretc: 0,
|
||
dsct1: &dsct1,
|
||
dscn1: &dscn1,
|
||
ijfr: &ijfr,
|
||
extrad: &extrad,
|
||
q0: &q0,
|
||
uu0: &uu0,
|
||
};
|
||
|
||
let result = rybmat(¶ms);
|
||
|
||
assert!(result.rb[0].is_finite());
|
||
assert!(result.vr[0].is_finite());
|
||
}
|
||
|
||
#[test]
|
||
fn test_rybmat_radiative_equilibrium() {
|
||
let dm: Vec<f64> = (0..MDEPTH).map(|i| 0.01 + i as f64 * 0.01).collect();
|
||
let temp = vec![10000.0; MDEPTH];
|
||
let dens = vec![1e15; MDEPTH];
|
||
let abso1 = vec![1e-8; MDEPTH];
|
||
let scat1 = vec![1e-9; MDEPTH];
|
||
let emis1 = vec![1e-10; MDEPTH];
|
||
let fak1 = vec![0.5; MDEPTH];
|
||
let rad1 = vec![1e10; MDEPTH];
|
||
let dabt1 = vec![1e-12; MDEPTH];
|
||
let demt1 = vec![1e-14; MDEPTH];
|
||
let deldm = vec![0.001; MDEPTH];
|
||
let reint: Vec<f64> = (0..MDEPTH).map(|_| 0.1).collect();
|
||
let redif: Vec<f64> = (0..MDEPTH).map(|_| 0.1).collect();
|
||
let w = vec![0.01; 100];
|
||
let fh = vec![0.5; 100];
|
||
let fhd = vec![0.5; 100];
|
||
let freq = vec![1e15; 100];
|
||
let dsct1 = vec![1e-11; MDEPTH];
|
||
let dscn1 = vec![1e-16; MDEPTH];
|
||
let ijfr: Vec<i32> = (0..100).map(|i| (i + 1) as i32).collect();
|
||
let extrad = vec![0.0; 100];
|
||
let q0 = vec![0.0; 100];
|
||
let uu0 = vec![0.0; 100];
|
||
|
||
let params = RybmatParams {
|
||
ij: 0,
|
||
nd: 10,
|
||
dm: &dm,
|
||
temp: &temp,
|
||
dens: &dens,
|
||
abso1: &abso1,
|
||
scat1: &scat1,
|
||
emis1: &emis1,
|
||
fak1: &fak1,
|
||
rad1: &rad1,
|
||
dabt1: &dabt1,
|
||
demt1: &demt1,
|
||
deldm: &deldm,
|
||
isplin: 1,
|
||
iubc: 0,
|
||
ibc: 1,
|
||
idisk: 0,
|
||
ifryb: 0,
|
||
reint: &reint,
|
||
redif: &redif,
|
||
w: &w,
|
||
freq: &freq,
|
||
fh: &fh,
|
||
fhd: &fhd,
|
||
icentr: 0,
|
||
ilbc: 0,
|
||
nretc: 0,
|
||
dsct1: &dsct1,
|
||
dscn1: &dscn1,
|
||
ijfr: &ijfr,
|
||
extrad: &extrad,
|
||
q0: &q0,
|
||
uu0: &uu0,
|
||
};
|
||
|
||
let result = rybmat(¶ms);
|
||
|
||
// 检查辐射平衡相关输出
|
||
assert!(result.vb[0].is_finite());
|
||
assert!(result.wr[0].is_finite());
|
||
assert!(result.va[5].is_finite());
|
||
}
|
||
}
|