1006 lines
34 KiB
Rust
1006 lines
34 KiB
Rust
//! 辐射转移方程矩阵(几何深度版本)。
|
||
//!
|
||
//! 重构自 TLUSTY `brtez.f`。
|
||
//!
|
||
//! 使用 ZD(几何深度)而不是 DM(柱质量密度)来计算矩阵元素。
|
||
|
||
const UN: f64 = 1.0;
|
||
const HALF: f64 = 0.5;
|
||
const THIRD: f64 = 1.0 / 3.0;
|
||
const SIXTH: f64 = 1.0 / 6.0;
|
||
const XCON: f64 = 8.0935e-21;
|
||
const YCON: f64 = 1.68638e-10;
|
||
const SIGE: f64 = 6.6516e-25; // Thomson 散射截面
|
||
|
||
/// 简化版 Compton 计算(BRTEZ 使用)。
|
||
///
|
||
/// 返回 (cma, cmb, cmc, cme, cms, cmd)
|
||
fn compt0_brtez(
|
||
ij: usize,
|
||
id: usize,
|
||
ab: f64,
|
||
nfreq: usize,
|
||
kij: &[usize],
|
||
elec: &[f64],
|
||
) -> (f64, f64, f64, f64, f64, f64) {
|
||
let iji = nfreq - kij[ij - 1] + 1;
|
||
if iji == 1 {
|
||
return (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
|
||
}
|
||
let ss0 = elec[id - 1] * SIGE / ab;
|
||
(0.0, 0.0, 0.0, 0.0, ss0, 0.0)
|
||
}
|
||
|
||
/// BRTEZ 参数结构体
|
||
#[allow(non_snake_case)]
|
||
pub struct BrtezParams<'a> {
|
||
// 标量参数
|
||
pub id: usize,
|
||
pub nfreqe: usize,
|
||
pub nd: usize,
|
||
pub ndre: usize,
|
||
pub inhe: usize,
|
||
pub inre: usize,
|
||
pub inpc: usize,
|
||
pub inse: usize,
|
||
pub inmp: usize,
|
||
pub nlvexp: usize,
|
||
pub nn0: usize,
|
||
pub isplin: i32,
|
||
pub icompt: i32,
|
||
pub icombc: i32,
|
||
pub ichcoo: i32,
|
||
pub idisk: i32,
|
||
pub ifz0: i32,
|
||
pub ibc: i32,
|
||
pub iwinbl: i32,
|
||
pub radzer: f64,
|
||
pub nfreq: usize,
|
||
pub dst: f64,
|
||
pub dsn: f64,
|
||
pub bn: f64,
|
||
pub hk: f64,
|
||
|
||
// 数组参数
|
||
pub zd: &'a [f64],
|
||
pub temp: &'a [f64],
|
||
pub dens: &'a [f64],
|
||
pub elec: &'a [f64],
|
||
pub freq: &'a [f64],
|
||
pub dlnfr: &'a [f64],
|
||
pub delj: &'a [f64],
|
||
pub kij: &'a [usize],
|
||
pub ijex: &'a [usize],
|
||
pub ijorig: &'a [usize],
|
||
pub ijfr: &'a [usize],
|
||
pub sigec: &'a [f64],
|
||
pub fhd: &'a [f64],
|
||
pub hextrd: &'a [f64],
|
||
|
||
// 辐射场和相关量
|
||
pub rad: &'a [f64], // nfreq x nd
|
||
pub radex: &'a [f64], // nfreqe x nd
|
||
pub fk0: &'a [f64],
|
||
pub fkp: &'a [f64],
|
||
pub fkm: &'a [f64],
|
||
pub abso0: &'a [f64],
|
||
pub absop: &'a [f64],
|
||
pub absom: &'a [f64],
|
||
pub scat0: &'a [f64],
|
||
pub scatp: &'a [f64],
|
||
pub scatm: &'a [f64],
|
||
pub emis0: &'a [f64],
|
||
pub emisp: &'a [f64],
|
||
pub emism: &'a [f64],
|
||
pub q0: &'a [f64],
|
||
pub uu0: &'a [f64],
|
||
pub wmm: &'a [f64],
|
||
|
||
// 矩阵导数项
|
||
pub dabt0: &'a [f64],
|
||
pub dabtp: &'a [f64],
|
||
pub dabtm: &'a [f64],
|
||
pub dabn0: &'a [f64],
|
||
pub dabnp: &'a [f64],
|
||
pub dabnm: &'a [f64],
|
||
pub dabm0: &'a [f64],
|
||
pub dabmp: &'a [f64],
|
||
pub dabmm: &'a [f64],
|
||
pub demt0: &'a [f64],
|
||
pub demtp: &'a [f64],
|
||
pub demtm: &'a [f64],
|
||
pub demn0: &'a [f64],
|
||
pub demnp: &'a [f64],
|
||
pub demnm: &'a [f64],
|
||
pub demm0: &'a [f64],
|
||
pub demmp: &'a [f64],
|
||
pub demmm: &'a [f64],
|
||
pub drch0: &'a [f64], // nlvexp x nfreqe
|
||
pub drchp: &'a [f64],
|
||
pub drchm: &'a [f64],
|
||
pub dret0: &'a [f64],
|
||
pub dretp: &'a [f64],
|
||
pub dretm: &'a [f64],
|
||
|
||
// 辐射场指针
|
||
pub rad0: &'a [f64],
|
||
pub radp: &'a [f64],
|
||
pub radm: &'a [f64],
|
||
}
|
||
|
||
/// BRTEZ 状态结构体(可变矩阵)
|
||
#[allow(non_snake_case)]
|
||
pub struct BrtezState<'a> {
|
||
pub a: &'a mut [&'a mut [f64]],
|
||
pub b: &'a mut [&'a mut [f64]],
|
||
pub c: &'a mut [&'a mut [f64]],
|
||
pub vecl: &'a mut [f64],
|
||
}
|
||
|
||
/// 辐射转移方程矩阵(几何深度版本)。
|
||
///
|
||
/// # 参数
|
||
/// * `params` - 输入参数
|
||
/// * `state` - 可变矩阵状态
|
||
#[allow(non_snake_case)]
|
||
pub fn brtez(params: &BrtezParams, state: &mut BrtezState) {
|
||
if params.nfreqe == 0 {
|
||
return;
|
||
}
|
||
|
||
let id = params.id;
|
||
let id_idx = id - 1; // 0-indexed
|
||
|
||
let mut ispl = params.isplin;
|
||
if ispl >= 5 {
|
||
ispl -= 5;
|
||
}
|
||
|
||
let nhe = params.nfreqe + params.inhe;
|
||
let nre = params.nfreqe + params.inre;
|
||
let npc = params.nfreqe + params.inpc;
|
||
let nse = params.nfreqe + params.inse - 1;
|
||
let nmp = params.nfreqe + params.inmp;
|
||
|
||
let mut gp = 0.0;
|
||
let mut gn = UN;
|
||
if params.inmp > 0 {
|
||
gp = UN;
|
||
gn = 0.0;
|
||
}
|
||
|
||
// Compton 散射边界条件
|
||
let mut ij1 = 1;
|
||
if params.icompt > 0 && params.icombc > 0 && params.ijex[0] > 0 {
|
||
ij1 = 2;
|
||
let ij = 1;
|
||
let iji = params.nfreq;
|
||
let zj1 = (-params.hk * params.freq[ij - 1] / params.temp[id_idx]).exp();
|
||
let zj2 = (-params.hk * params.freq[ij] / params.temp[id_idx]).exp();
|
||
let dlt = params.delj[iji - 2 + id_idx * (params.nfreq - 1)];
|
||
|
||
let (combid, comaid) = if params.ichcoo == 0 {
|
||
let zj0 = UN / (params.hk * (params.freq[ij - 1] * params.freq[ij]).sqrt() / params.temp[id_idx]);
|
||
let zxx = UN - 3.0 * zj0 + (UN - dlt) * zj1 + dlt * zj2;
|
||
let combid = zj0 / params.dlnfr[iji - 2] + (UN - dlt) * zxx;
|
||
let comaid = -zj0 / params.dlnfr[iji - 2] + dlt * zxx;
|
||
(combid, comaid)
|
||
} else {
|
||
let e2 = YCON * params.temp[id_idx];
|
||
let zxx0 = XCON * params.freq[ij - 1] * (UN + zj1) - 3.0 * e2;
|
||
let zxxm = XCON * params.freq[ij] * (UN + zj2) - 3.0 * e2;
|
||
let zxx = (UN - dlt) * zxx0 + dlt * zxxm;
|
||
let combid = e2 / params.dlnfr[iji - 2] + (UN - dlt) * zxx;
|
||
let comaid = -e2 / params.dlnfr[iji - 2] + dlt * zxx;
|
||
(combid, comaid)
|
||
};
|
||
|
||
state.b[ij - 1][ij - 1] = combid;
|
||
state.b[ij - 1][ij] = comaid;
|
||
// RAD 数组索引: rad(iji, id) -> rad[(iji-1) + id_idx * nfreq]
|
||
let rad_idx = (iji - 1) + id_idx * params.nfreq;
|
||
let rad_idx_prev = (iji - 2) + id_idx * params.nfreq;
|
||
state.vecl[ij - 1] = -state.b[ij - 1][ij - 1] * params.rad[rad_idx]
|
||
- state.b[ij - 1][ij] * params.rad[rad_idx_prev];
|
||
}
|
||
|
||
// ID = 1: 上边界条件
|
||
if id > 1 {
|
||
brtez_internal(params, state, id, ij1, nhe, nre, npc, nse, nmp, gn, gp, ispl);
|
||
return;
|
||
}
|
||
|
||
brtez_upper_boundary(params, state, id_idx, ij1, nhe, nre, npc, nse, nmp, gn, gp, ispl);
|
||
}
|
||
|
||
/// 上边界条件 (ID = 1)
|
||
#[allow(non_snake_case)]
|
||
fn brtez_upper_boundary(
|
||
params: &BrtezParams,
|
||
state: &mut BrtezState,
|
||
id_idx: usize,
|
||
ij1: usize,
|
||
nhe: usize,
|
||
nre: usize,
|
||
npc: usize,
|
||
nse: usize,
|
||
nmp: usize,
|
||
gn: f64,
|
||
gp: f64,
|
||
ispl: i32,
|
||
) {
|
||
let ddp = (params.zd[0] - params.zd[1]) * HALF;
|
||
|
||
for ij in ij1..=params.nfreqe {
|
||
let ij_idx = ij - 1;
|
||
let ijt = params.ijfr[ij_idx] - 1; // 0-indexed
|
||
|
||
let omeg0 = params.abso0[ij_idx];
|
||
let omegp = params.absop[ij_idx];
|
||
let dzp = omeg0 + omegp;
|
||
let dtaup = dzp * ddp;
|
||
|
||
let alf1 = (params.fk0[ij_idx] * params.rad0[ij_idx]
|
||
- params.fkp[ij_idx] * params.radp[ij_idx]) / dtaup;
|
||
|
||
let chiel0 = params.scat0[ij_idx];
|
||
let chielp = params.scatp[ij_idx];
|
||
let s0 = (params.emis0[ij_idx] + chiel0 * params.rad0[ij_idx]) / params.abso0[ij_idx];
|
||
|
||
let mut bs = HALF * dtaup;
|
||
let mut cs = 0.0;
|
||
let mut c2 = 0.0;
|
||
let mut gam2 = 0.0;
|
||
let mut sp = 0.0;
|
||
|
||
// Compton 散射
|
||
let (cma, cmb, cmc, cme, cms, cmd) = if params.icompt > 0 {
|
||
compt0_brtez(ijt + 1, id_idx + 1, params.abso0[ij_idx], params.nfreq, params.kij, params.elec)
|
||
} else {
|
||
(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
|
||
};
|
||
|
||
let s0 = if params.icompt > 0 { s0 + cms } else { s0 };
|
||
|
||
if ispl % 3 > 0 {
|
||
// Spline 配点法或 Hermite 方法
|
||
bs = dtaup * THIRD;
|
||
cs = HALF * bs;
|
||
sp = (params.emisp[ij_idx] + chielp * params.radp[ij_idx]) / params.absop[ij_idx];
|
||
c2 = cs / params.absop[ij_idx];
|
||
gam2 = cs * (params.radp[ij_idx] - sp);
|
||
}
|
||
|
||
// 辅助量
|
||
let alf2 = bs * (params.rad0[ij_idx] - s0);
|
||
let bet2 = alf2 + gam2;
|
||
let x1 = (alf1 - bet2) / dzp;
|
||
let b2 = (bs + params.q0[ij_idx]) / params.abso0[ij_idx];
|
||
let mut b1 = x1;
|
||
b1 = b1 + params.uu0[ij_idx] * s0 * params.zd[0] / params.dens[0]; // 使用 ZD(1) 替代 DM(1)
|
||
let mut c1 = x1;
|
||
b1 = b1 - b2 * s0;
|
||
c1 = c1 - c2 * sp;
|
||
|
||
// 矩阵元素
|
||
let rtn = omeg0 * params.wmm[id_idx] * b1;
|
||
let rtnc = omegp * params.wmm[id_idx + 1] * c1;
|
||
|
||
state.b[ij_idx][nre - 1] = b1 * params.dabt0[ij_idx]
|
||
+ b2 * (params.demt0[ij_idx] + params.dst * params.rad0[ij_idx]);
|
||
state.c[ij_idx][nre - 1] = c1 * params.dabtp[ij_idx]
|
||
+ c2 * (params.demtp[ij_idx] + params.dst * params.radp[ij_idx]);
|
||
|
||
let sigec_val = if ijt < params.sigec.len() { params.sigec[ijt] } else { 0.0 };
|
||
|
||
state.b[ij_idx][npc - 1] = b1 * params.dabn0[ij_idx]
|
||
+ b2 * (params.demn0[ij_idx] + (params.dsn + sigec_val) * params.rad0[ij_idx]);
|
||
state.c[ij_idx][npc - 1] = c1 * params.dabnp[ij_idx]
|
||
+ c2 * (params.demnp[ij_idx] + (params.dsn + sigec_val) * params.radp[ij_idx]);
|
||
|
||
state.b[ij_idx][nmp - 1] = b1 * params.dabm0[ij_idx] + b2 * params.demm0[ij_idx] - gp * rtn;
|
||
state.c[ij_idx][nmp - 1] = c1 * params.dabmp[ij_idx] + c2 * params.demmp[ij_idx] - gp * rtnc;
|
||
|
||
// 能级列
|
||
for ii in 0..params.nlvexp {
|
||
let drch0_val = params.drch0[ii * params.nfreqe + ij_idx];
|
||
let drchp_val = params.drchp[ii * params.nfreqe + ij_idx];
|
||
let dret0_val = params.dret0[ii * params.nfreqe + ij_idx];
|
||
let dretp_val = params.dretp[ii * params.nfreqe + ij_idx];
|
||
state.b[ij_idx][nse + ii] = state.b[ij_idx][nse + ii] + b1 * drch0_val + b2 * dret0_val;
|
||
state.c[ij_idx][nse + ii] = state.c[ij_idx][nse + ii] + c1 * drchp_val + c2 * dretp_val;
|
||
}
|
||
|
||
state.b[ij_idx][params.nfreqe - 1] = 0.0;
|
||
state.b[ij_idx][ij_idx] = -params.fk0[ij_idx] / dtaup - params.fhd[ijt]
|
||
- bs * (UN - chiel0 / params.abso0[ij_idx])
|
||
+ params.q0[ij_idx] * chiel0 / params.abso0[ij_idx];
|
||
state.c[ij_idx][params.nfreqe - 1] = 0.0;
|
||
state.c[ij_idx][ij_idx] = params.fkp[ij_idx] / dtaup - cs * (UN - chielp / params.absop[ij_idx]);
|
||
|
||
// RHS 向量
|
||
state.vecl[ij_idx] = alf1 + bet2 + params.fhd[ijt] * params.rad0[ij_idx] - s0 * params.q0[ij_idx];
|
||
if params.iwinbl < 0 {
|
||
state.vecl[ij_idx] -= params.hextrd[ijt];
|
||
}
|
||
|
||
// Compton 散射附加项
|
||
if params.icompt > 4 {
|
||
let iji = params.nfreq - params.kij[ijt] + 1;
|
||
state.b[ij_idx][ij_idx] += bs * (cmb + cme);
|
||
if iji > 1 {
|
||
let ijm = params.ijex[params.ijorig[iji - 2] - 1];
|
||
if ijm > 0 {
|
||
state.b[ij_idx][ijm - 1] += bs * cma;
|
||
}
|
||
}
|
||
if iji < params.nfreq {
|
||
let ijp_idx = params.ijex[params.ijorig[iji] - 1];
|
||
if ijp_idx > 0 {
|
||
state.b[ij_idx][ijp_idx - 1] += bs * cmc;
|
||
}
|
||
}
|
||
if params.inre > 0 {
|
||
state.b[ij_idx][nre - 1] += cmd * bs;
|
||
}
|
||
if params.inpc > 0 {
|
||
state.b[ij_idx][npc - 1] += cms * bs / params.elec[id_idx];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 内部深度点 (1 < ID < ND)
|
||
#[allow(non_snake_case)]
|
||
fn brtez_internal(
|
||
params: &BrtezParams,
|
||
state: &mut BrtezState,
|
||
id: usize,
|
||
ij1: usize,
|
||
nhe: usize,
|
||
nre: usize,
|
||
npc: usize,
|
||
nse: usize,
|
||
nmp: usize,
|
||
gn: f64,
|
||
gp: f64,
|
||
ispl: i32,
|
||
) {
|
||
let id_idx = id - 1;
|
||
let ddm = (params.zd[id_idx - 1] - params.zd[id_idx]) * HALF;
|
||
|
||
if id == params.nd {
|
||
brtez_lower_boundary(params, state, id_idx, ij1, nhe, nre, npc, nse, nmp, gn, gp, ddm, ispl);
|
||
return;
|
||
}
|
||
|
||
let ddp = (params.zd[id_idx] - params.zd[id_idx + 1]) * HALF;
|
||
|
||
for ij in ij1..=params.nfreqe {
|
||
let ij_idx = ij - 1;
|
||
let ijt = params.ijfr[ij_idx] - 1;
|
||
|
||
let omeg0 = params.abso0[ij_idx];
|
||
let omegp = params.absop[ij_idx];
|
||
let omegm = params.absom[ij_idx];
|
||
let dzp = omeg0 + omegp;
|
||
let dzm = omeg0 + omegm;
|
||
let dtaup = dzp * ddp;
|
||
let dtaum = dzm * ddm;
|
||
let dtau0 = HALF * (dtaup + dtaum);
|
||
|
||
let frd = params.fk0[ij_idx] * params.rad0[ij_idx];
|
||
let alf1 = (frd - params.fkp[ij_idx] * params.radp[ij_idx]) / dtaup / dtau0;
|
||
let gam1 = (frd - params.fkm[ij_idx] * params.radm[ij_idx]) / 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 mut bs = UN;
|
||
let chielm = params.scatm[ij_idx];
|
||
let chiel0 = params.scat0[ij_idx];
|
||
let chielp = params.scatp[ij_idx];
|
||
let s0 = (params.emis0[ij_idx] + chiel0 * params.rad0[ij_idx]) / params.abso0[ij_idx];
|
||
|
||
let mut as_val = 0.0;
|
||
let mut cs = 0.0;
|
||
let mut a2 = 0.0;
|
||
let mut c2 = 0.0;
|
||
let mut a3 = 0.0;
|
||
let mut c3 = 0.0;
|
||
let mut bet2 = 0.0;
|
||
let mut sm = 0.0;
|
||
let mut sp = 0.0;
|
||
|
||
// Compton 散射
|
||
let (cma, cmb, cmc, cme, cms, cmd) = if params.icompt > 0 {
|
||
compt0_brtez(ijt + 1, id, params.abso0[ij_idx], params.nfreq, params.kij, params.elec)
|
||
} else {
|
||
(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
|
||
};
|
||
|
||
let s0 = if params.icompt > 0 { s0 + cms } else { s0 };
|
||
|
||
if ispl % 3 != 0 {
|
||
sm = (params.emism[ij_idx] + params.radm[ij_idx] * chielm) / params.absom[ij_idx];
|
||
sp = (params.emisp[ij_idx] + params.radp[ij_idx] * chielp) / params.absop[ij_idx];
|
||
|
||
if ispl == 1 {
|
||
// Spline 配点法
|
||
as_val = dtaum / dtau0 * SIXTH;
|
||
cs = dtaup / dtau0 * SIXTH;
|
||
bs = 0.666666666666667_f64;
|
||
let alf2 = as_val * (params.radm[ij_idx] - sm);
|
||
let gam2 = cs * (params.radp[ij_idx] - sp);
|
||
bet2 = alf2 + gam2;
|
||
let x = HALF * bet2 / dtau0;
|
||
a2 = (gam2 - x * dtaum) / dzm;
|
||
c2 = (alf2 - x * dtaup) / dzp;
|
||
} else {
|
||
// Hermite 方法
|
||
as_val = dtaup * dtaup / dtaum / dtau0;
|
||
cs = dtaum * dtaum / dtaup / dtau0;
|
||
let al3 = (params.radp[ij_idx] - sp - params.rad0[ij_idx] + s0) * SIXTH;
|
||
let ga3 = (params.radm[ij_idx] - sm - params.rad0[ij_idx] + s0) * SIXTH;
|
||
let av = al3 * cs;
|
||
let cv = ga3 * as_val;
|
||
as_val = (UN - HALF * as_val) * SIXTH;
|
||
cs = (UN - HALF * cs) * SIXTH;
|
||
bs = UN - as_val - cs;
|
||
let x = (av + cv) / dtau0 / 4.0;
|
||
a2 = (x * dtaum + HALF * cv - av) / dzm;
|
||
c2 = (x * dtaup + HALF * av - cv) / dzp;
|
||
bet2 = as_val * (params.radm[ij_idx] - sm) + cs * (params.radp[ij_idx] - sp);
|
||
}
|
||
}
|
||
|
||
// 辅助量
|
||
b1 = b1 - (a2 + c2);
|
||
a1 = a1 - a2;
|
||
c1 = c1 - c2;
|
||
a2 = as_val / params.absom[ij_idx];
|
||
c2 = cs / params.absop[ij_idx];
|
||
a3 = a2 * sm;
|
||
c3 = c2 * sp;
|
||
|
||
let b2 = bs / params.abso0[ij_idx];
|
||
let b3 = b2 * s0;
|
||
|
||
a1 = a1 - a3;
|
||
b1 = b1 - b3;
|
||
c1 = c1 - c3;
|
||
|
||
// 矩阵元素
|
||
let rtna = omegm * params.wmm[id_idx - 1] * a1;
|
||
let rtn = omeg0 * params.wmm[id_idx] * b1;
|
||
let rtnc = omegp * params.wmm[id_idx + 1] * c1;
|
||
|
||
state.a[ij_idx][nhe - 1] = -gn * rtna;
|
||
state.b[ij_idx][nhe - 1] = -gn * rtn;
|
||
state.c[ij_idx][nhe - 1] = -gn * rtnc;
|
||
|
||
let a1_adj = a1 - a3;
|
||
let b1_adj = b1 - b3;
|
||
let c1_adj = c1 - c3;
|
||
|
||
state.a[ij_idx][nre - 1] = a1_adj * params.dabtm[ij_idx]
|
||
+ a2 * (params.demtm[ij_idx] + params.dst * params.radm[ij_idx]);
|
||
state.b[ij_idx][nre - 1] = b1_adj * params.dabt0[ij_idx]
|
||
+ b2 * (params.demt0[ij_idx] + params.dst * params.rad0[ij_idx]);
|
||
state.c[ij_idx][nre - 1] = c1_adj * params.dabtp[ij_idx]
|
||
+ c2 * (params.demtp[ij_idx] + params.dst * params.radp[ij_idx]);
|
||
|
||
let sigec_val = if ijt < params.sigec.len() { params.sigec[ijt] } else { 0.0 };
|
||
|
||
state.a[ij_idx][npc - 1] = a1_adj * params.dabnm[ij_idx]
|
||
+ a2 * (params.demnm[ij_idx] + (params.dsn + sigec_val) * params.radm[ij_idx]);
|
||
state.b[ij_idx][npc - 1] = b1_adj * params.dabn0[ij_idx]
|
||
+ b2 * (params.demn0[ij_idx] + (params.dsn + sigec_val) * params.rad0[ij_idx]);
|
||
state.c[ij_idx][npc - 1] = c1_adj * params.dabnp[ij_idx]
|
||
+ c2 * (params.demnp[ij_idx] + (params.dsn + sigec_val) * params.radp[ij_idx]);
|
||
|
||
state.a[ij_idx][nmp - 1] = a1_adj * params.dabmm[ij_idx] + a2 * params.demmm[ij_idx] - gp * rtna;
|
||
state.b[ij_idx][nmp - 1] = b1_adj * params.dabm0[ij_idx] + b2 * params.demm0[ij_idx] - gp * rtn;
|
||
state.c[ij_idx][nmp - 1] = c1_adj * params.dabmp[ij_idx] + c2 * params.demmp[ij_idx] - gp * rtnc;
|
||
|
||
// 能级列
|
||
for ii in 0..params.nlvexp {
|
||
let drchm_val = params.drchm[ii * params.nfreqe + ij_idx];
|
||
let drch0_val = params.drch0[ii * params.nfreqe + ij_idx];
|
||
let drchp_val = params.drchp[ii * params.nfreqe + ij_idx];
|
||
let dretm_val = params.dretm[ii * params.nfreqe + ij_idx];
|
||
let dret0_val = params.dret0[ii * params.nfreqe + ij_idx];
|
||
let dretp_val = params.dretp[ii * params.nfreqe + ij_idx];
|
||
|
||
state.a[ij_idx][nse + ii] = state.a[ij_idx][nse + ii] + a1_adj * drchm_val + a2 * dretm_val;
|
||
state.b[ij_idx][nse + ii] = state.b[ij_idx][nse + ii] + b1_adj * drch0_val + b2 * dret0_val;
|
||
state.c[ij_idx][nse + ii] = state.c[ij_idx][nse + ii] + c1_adj * drchp_val + c2 * dretp_val;
|
||
}
|
||
|
||
state.a[ij_idx][params.nfreqe - 1] = 0.0;
|
||
state.a[ij_idx][ij_idx] = params.fkm[ij_idx] / dtaum / dtau0 - as_val * (UN - chielm / params.absom[ij_idx]);
|
||
state.b[ij_idx][params.nfreqe - 1] = 0.0;
|
||
state.b[ij_idx][ij_idx] = -params.fk0[ij_idx] / dtau0 * (UN / dtaup + UN / dtaum)
|
||
- bs * (UN - chiel0 / params.abso0[ij_idx]);
|
||
state.c[ij_idx][params.nfreqe - 1] = 0.0;
|
||
state.c[ij_idx][ij_idx] = params.fkp[ij_idx] / dtaup / dtau0 - cs * (UN - chielp / params.absop[ij_idx]);
|
||
|
||
// RHS 向量
|
||
state.vecl[ij_idx] = bet1 + bet2 + bs * (params.rad0[ij_idx] - s0);
|
||
|
||
// Compton 散射附加项
|
||
if params.icompt > 4 {
|
||
let iji = params.nfreq - params.kij[ijt] + 1;
|
||
state.b[ij_idx][ij_idx] += bs * (cmb + cme);
|
||
if iji > 1 {
|
||
let ijm = params.ijex[params.ijorig[iji - 2] - 1];
|
||
if ijm > 0 {
|
||
state.b[ij_idx][ijm - 1] += bs * cma;
|
||
}
|
||
}
|
||
if iji < params.nfreq {
|
||
let ijp_idx = params.ijex[params.ijorig[iji] - 1];
|
||
if ijp_idx > 0 {
|
||
state.b[ij_idx][ijp_idx - 1] += bs * cmc;
|
||
}
|
||
}
|
||
if params.inre > 0 {
|
||
state.b[ij_idx][nre - 1] += cmd * bs;
|
||
}
|
||
if params.inpc > 0 {
|
||
state.b[ij_idx][npc - 1] += cms * bs / params.elec[id_idx];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 下边界条件 (ID = ND)
|
||
#[allow(non_snake_case)]
|
||
fn brtez_lower_boundary(
|
||
params: &BrtezParams,
|
||
state: &mut BrtezState,
|
||
id_idx: usize,
|
||
ij1: usize,
|
||
_nhe: usize,
|
||
nre: usize,
|
||
npc: usize,
|
||
nse: usize,
|
||
nmp: usize,
|
||
gn: f64,
|
||
gp: f64,
|
||
ddm: f64,
|
||
ispl: i32,
|
||
) {
|
||
if params.idisk == 0 || params.ifz0 < 0 {
|
||
// 非盘模型
|
||
brtez_lower_nodisk(params, state, id_idx, ij1, nre, npc, nse, nmp, gn, gp, ddm, ispl);
|
||
} else {
|
||
// 盘模型
|
||
brtez_lower_disk(params, state, id_idx, ij1, nre, npc, nse, nmp, gn, gp, ddm);
|
||
}
|
||
}
|
||
|
||
/// 下边界条件(非盘模型)
|
||
#[allow(non_snake_case)]
|
||
fn brtez_lower_nodisk(
|
||
params: &BrtezParams,
|
||
state: &mut BrtezState,
|
||
id_idx: usize,
|
||
ij1: usize,
|
||
nre: usize,
|
||
npc: usize,
|
||
nse: usize,
|
||
nmp: usize,
|
||
gn: f64,
|
||
gp: f64,
|
||
ddm: f64,
|
||
_ispl: i32,
|
||
) {
|
||
let t = params.temp[id_idx];
|
||
let tm = params.temp[id_idx - 1];
|
||
let hkt = params.hk / t;
|
||
let hktm = params.hk / tm;
|
||
|
||
for ij in ij1..=params.nfreqe {
|
||
let ij_idx = ij - 1;
|
||
let ijt = params.ijfr[ij_idx] - 1;
|
||
|
||
let chielm = params.scatm[ij_idx];
|
||
let chiel0 = params.scat0[ij_idx];
|
||
let omegm = params.absom[ij_idx];
|
||
let omeg0 = params.abso0[ij_idx];
|
||
let dzm = omeg0 + omegm;
|
||
let dtaum = dzm * ddm;
|
||
|
||
let frd = params.fk0[ij_idx] * params.rad0[ij_idx] - params.fkm[ij_idx] * params.radm[ij_idx];
|
||
let gam1 = frd / dtaum;
|
||
let mut a1 = gam1 / dzm;
|
||
|
||
let mut as_val = 0.0;
|
||
let mut bs = 0.0;
|
||
let mut a2 = 0.0;
|
||
let mut b2 = 0.0;
|
||
let mut a3 = 0.0;
|
||
let mut b3 = 0.0;
|
||
let mut alf2 = 0.0;
|
||
let mut bet2 = 0.0;
|
||
let mut gam2 = 0.0;
|
||
|
||
// 二阶边界条件
|
||
if params.ibc > 0 && params.ibc < 4 {
|
||
bs = dtaum * HALF;
|
||
let s0 = (params.emis0[ij_idx] + chiel0 * params.rad0[ij_idx]) / params.abso0[ij_idx];
|
||
|
||
// Compton 散射
|
||
let s0 = if params.icompt > 0 {
|
||
let (_, _, _, _, cms, _) = compt0_brtez(ijt + 1, id_idx + 1, params.abso0[ij_idx], params.nfreq, params.kij, params.elec);
|
||
s0 + cms
|
||
} else {
|
||
s0
|
||
};
|
||
|
||
gam2 = bs * (params.rad0[ij_idx] - s0);
|
||
bet2 = gam2;
|
||
let x1 = bet2 / dzm;
|
||
a1 = a1 - x1;
|
||
b2 = bs / params.abso0[ij_idx];
|
||
b3 = b2 * s0;
|
||
}
|
||
|
||
// 辅助参数
|
||
let fr = params.freq[ijt];
|
||
let fr15 = fr * 1e-15;
|
||
let x = hkt * fr;
|
||
let ex = x.exp();
|
||
let xm = hktm * fr;
|
||
let exm = xm.exp();
|
||
let plan = params.bn * fr15 * fr15 * fr15 / (ex - UN);
|
||
|
||
let mut planm = params.bn * fr15 * fr15 * fr15 / (exm - UN);
|
||
|
||
if params.inre == 0 || params.id >= params.ndre {
|
||
planm = params.bn * fr15 * fr15 * fr15 / (exm - UN);
|
||
let gam3 = (plan - planm) / dtaum * THIRD;
|
||
a1 = a1 - gam3 / dzm;
|
||
// gam1 -= gam3; // 在原代码中 gam1 未使用
|
||
}
|
||
|
||
let c1 = a1;
|
||
let b1 = c1;
|
||
let a1_adj = a1 - a3;
|
||
let b1_adj = b1 - b3;
|
||
|
||
// 矩阵元素
|
||
if params.inre == 0 || params.id >= params.ndre {
|
||
let dplanm = planm * xm / tm / (UN - UN / exm);
|
||
state.a[ij_idx][nre - 1] = a1_adj * params.dabtm[ij_idx]
|
||
+ a2 * (params.demtm[ij_idx] + params.dst * params.radm[ij_idx])
|
||
- dplanm / dtaum * THIRD;
|
||
|
||
let sigec_val = if ijt < params.sigec.len() { params.sigec[ijt] } else { 0.0 };
|
||
state.a[ij_idx][npc - 1] = a1_adj * params.dabnm[ij_idx]
|
||
+ a2 * (params.demnm[ij_idx] + (params.dsn + sigec_val) * params.radm[ij_idx]);
|
||
|
||
let bb = HALF + THIRD / dtaum;
|
||
let dplan = plan * x / t / (UN - UN / ex);
|
||
state.b[ij_idx][nre - 1] = b1_adj * params.dabt0[ij_idx]
|
||
+ b2 * (params.demt0[ij_idx] + params.dst * params.rad0[ij_idx])
|
||
+ bb * dplan;
|
||
state.b[ij_idx][npc - 1] = b1_adj * params.dabn0[ij_idx]
|
||
+ b2 * (params.demn0[ij_idx] + (params.dsn + sigec_val) * params.rad0[ij_idx]);
|
||
|
||
let rtna = omegm * params.wmm[id_idx - 1] * a1_adj;
|
||
let rtn = omeg0 * params.wmm[id_idx] * b1_adj;
|
||
|
||
state.a[ij_idx][nmp - 1] = a1_adj * params.dabmm[ij_idx] + a2 * params.demmm[ij_idx] - gp * rtna;
|
||
state.b[ij_idx][nmp - 1] = b1_adj * params.dabm0[ij_idx] + b2 * params.demm0[ij_idx] - gp * rtn;
|
||
|
||
// 能级列
|
||
for ii in 0..params.nlvexp {
|
||
let drchm_val = params.drchm[ii * params.nfreqe + ij_idx];
|
||
let drch0_val = params.drch0[ii * params.nfreqe + ij_idx];
|
||
let dretm_val = params.dretm[ii * params.nfreqe + ij_idx];
|
||
let dret0_val = params.dret0[ii * params.nfreqe + ij_idx];
|
||
state.a[ij_idx][nse + ii] = state.a[ij_idx][nse + ii] + a1_adj * drchm_val + a2 * dretm_val;
|
||
state.b[ij_idx][nse + ii] = state.b[ij_idx][nse + ii] + b1_adj * drch0_val + b2 * dret0_val;
|
||
}
|
||
|
||
state.a[ij_idx][params.nfreqe - 1] = 0.0;
|
||
state.a[ij_idx][ij_idx] = params.fkm[ij_idx] / dtaum - as_val * (UN - chielm / params.absom[ij_idx]);
|
||
state.b[ij_idx][params.nfreqe - 1] = 0.0;
|
||
}
|
||
|
||
// RHS 向量
|
||
if params.ibc == 0 || params.ibc == 4 {
|
||
state.b[ij_idx][ij_idx] = state.b[ij_idx][ij_idx] - params.fk0[ij_idx] / dtaum
|
||
- bs * (UN - chiel0 / params.abso0[ij_idx]) - HALF;
|
||
state.vecl[ij_idx] = gam1 + bet2 - HALF * (plan - params.rad0[ij_idx]);
|
||
} else {
|
||
state.b[ij_idx][ij_idx] = state.b[ij_idx][ij_idx] - params.fk0[ij_idx] / dtaum
|
||
- bs * (UN - chiel0 / params.abso0[ij_idx]) - params.fhd[ijt];
|
||
state.vecl[ij_idx] = gam1 + bet2 - HALF * plan + params.fhd[ijt] * params.rad0[ij_idx];
|
||
}
|
||
|
||
// Compton 散射附加项
|
||
if params.icompt > 4 {
|
||
let (cma, cmb, cmc, cme, cms, cmd) = compt0_brtez(ijt + 1, id_idx + 1, params.abso0[ij_idx], params.nfreq, params.kij, params.elec);
|
||
let iji = params.nfreq - params.kij[ijt] + 1;
|
||
state.b[ij_idx][ij_idx] += bs * (cmb + cme);
|
||
if iji > 1 {
|
||
let ijm = params.ijex[params.ijorig[iji - 2] - 1];
|
||
if ijm > 0 {
|
||
state.b[ij_idx][ijm - 1] += bs * cma;
|
||
}
|
||
}
|
||
if iji < params.nfreq {
|
||
let ijp_idx = params.ijex[params.ijorig[iji] - 1];
|
||
if ijp_idx > 0 {
|
||
state.b[ij_idx][ijp_idx - 1] += bs * cmc;
|
||
}
|
||
}
|
||
if params.inre > 0 {
|
||
state.b[ij_idx][nre - 1] += cmd * bs;
|
||
}
|
||
if params.inpc > 0 {
|
||
state.b[ij_idx][npc - 1] += cms * bs / params.elec[id_idx];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 下边界条件(盘模型)
|
||
#[allow(non_snake_case)]
|
||
fn brtez_lower_disk(
|
||
params: &BrtezParams,
|
||
state: &mut BrtezState,
|
||
id_idx: usize,
|
||
ij1: usize,
|
||
nre: usize,
|
||
npc: usize,
|
||
nse: usize,
|
||
nmp: usize,
|
||
gn: f64,
|
||
gp: f64,
|
||
ddm: f64,
|
||
) {
|
||
for ij in ij1..=params.nfreqe {
|
||
let ij_idx = ij - 1;
|
||
let ijt = params.ijfr[ij_idx] - 1;
|
||
|
||
let chielm = params.scatm[ij_idx];
|
||
let chiel0 = params.scat0[ij_idx];
|
||
let omegm = params.absom[ij_idx];
|
||
let omeg0 = params.abso0[ij_idx];
|
||
let dzm = omeg0 + omegm;
|
||
let dtaum = dzm * ddm;
|
||
|
||
let frd = params.fk0[ij_idx] * params.rad0[ij_idx] - params.fkm[ij_idx] * params.radm[ij_idx];
|
||
let gam1 = frd / dtaum;
|
||
let mut a1 = gam1 / dzm;
|
||
|
||
let mut as_val = 0.0;
|
||
let mut a2 = 0.0;
|
||
let mut a3 = 0.0;
|
||
let mut alf2 = 0.0;
|
||
let mut gam2 = 0.0;
|
||
|
||
let bs = dtaum * HALF;
|
||
let s0 = (params.emis0[ij_idx] + chiel0 * params.rad0[ij_idx]) / params.abso0[ij_idx];
|
||
|
||
// Compton 散射
|
||
let s0 = if params.icompt > 0 {
|
||
let (_, _, _, _, cms, _) = compt0_brtez(ijt + 1, id_idx + 1, params.abso0[ij_idx], params.nfreq, params.kij, params.elec);
|
||
s0 + cms
|
||
} else {
|
||
s0
|
||
};
|
||
|
||
gam2 = bs * (params.rad0[ij_idx] - s0);
|
||
let bet2 = alf2 + gam2;
|
||
let x1 = bet2 / dzm;
|
||
a1 = a1 - x1;
|
||
let b2 = bs / params.abso0[ij_idx];
|
||
let b3 = b2 * s0;
|
||
let b1 = a1;
|
||
let a1_adj = a1 - a3;
|
||
let b1_adj = b1 - b3;
|
||
|
||
// 矩阵 A 元素
|
||
state.a[ij_idx][nre - 1] = a1_adj * params.dabtm[ij_idx]
|
||
+ a2 * (params.demtm[ij_idx] + params.dst * params.radm[ij_idx]);
|
||
|
||
let sigec_val = if ijt < params.sigec.len() { params.sigec[ijt] } else { 0.0 };
|
||
state.a[ij_idx][npc - 1] = a1_adj * params.dabnm[ij_idx]
|
||
+ a2 * (params.demnm[ij_idx] + (params.dsn + sigec_val) * params.radm[ij_idx]);
|
||
|
||
let rtna = omegm * params.wmm[id_idx - 1] * a1_adj;
|
||
state.a[ij_idx][nmp - 1] = a1_adj * params.dabmm[ij_idx] + a2 * params.demmm[ij_idx] - gp * rtna;
|
||
|
||
// 能级列
|
||
for ii in 0..params.nlvexp {
|
||
let drchm_val = params.drchm[ii * params.nfreqe + ij_idx];
|
||
let dretm_val = params.dretm[ii * params.nfreqe + ij_idx];
|
||
state.a[ij_idx][nse + ii] = a1_adj * drchm_val + a2 * dretm_val;
|
||
}
|
||
|
||
state.a[ij_idx][params.nfreqe - 1] = 0.0;
|
||
state.a[ij_idx][ij_idx] = params.fkm[ij_idx] / dtaum - as_val * (UN - chielm / params.absom[ij_idx]);
|
||
|
||
// 矩阵 B 元素
|
||
state.b[ij_idx][nre - 1] = b1_adj * params.dabt0[ij_idx]
|
||
+ b2 * (params.demt0[ij_idx] + params.dst * params.rad0[ij_idx]);
|
||
state.b[ij_idx][npc - 1] = b1_adj * params.dabn0[ij_idx]
|
||
+ b2 * (params.demn0[ij_idx] + (params.dsn + sigec_val) * params.rad0[ij_idx]);
|
||
|
||
let rtn = omeg0 * params.wmm[id_idx] * b1_adj;
|
||
state.b[ij_idx][nmp - 1] = b1_adj * params.dabm0[ij_idx] + b2 * params.demm0[ij_idx] - gp * rtn;
|
||
|
||
// 能级列
|
||
for ii in 0..params.nlvexp {
|
||
let drch0_val = params.drch0[ii * params.nfreqe + ij_idx];
|
||
let dret0_val = params.dret0[ii * params.nfreqe + ij_idx];
|
||
state.b[ij_idx][nse + ii] = b1_adj * drch0_val + b2 * dret0_val;
|
||
}
|
||
|
||
state.b[ij_idx][params.nfreqe - 1] = 0.0;
|
||
state.b[ij_idx][ij_idx] = -params.fk0[ij_idx] / dtaum - bs * (UN - chiel0 / params.abso0[ij_idx]);
|
||
|
||
// RHS 向量
|
||
state.vecl[ij_idx] = gam1 + bet2;
|
||
|
||
// Compton 散射附加项
|
||
if params.icompt > 4 {
|
||
let (cma, cmb, cmc, cme, cms, cmd) = compt0_brtez(ijt + 1, id_idx + 1, params.abso0[ij_idx], params.nfreq, params.kij, params.elec);
|
||
let iji = params.nfreq - params.kij[ijt] + 1;
|
||
state.b[ij_idx][ij_idx] += bs * (cmb + cme);
|
||
if iji > 1 {
|
||
let ijm = params.ijex[params.ijorig[iji - 2] - 1];
|
||
if ijm > 0 {
|
||
state.b[ij_idx][ijm - 1] += bs * cma;
|
||
}
|
||
}
|
||
if iji < params.nfreq {
|
||
let ijp_idx = params.ijex[params.ijorig[iji] - 1];
|
||
if ijp_idx > 0 {
|
||
state.b[ij_idx][ijp_idx - 1] += bs * cmc;
|
||
}
|
||
}
|
||
if params.inre > 0 {
|
||
state.b[ij_idx][nre - 1] += cmd * bs;
|
||
}
|
||
if params.inpc > 0 {
|
||
state.b[ij_idx][npc - 1] += cms * bs / params.elec[id_idx];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
use approx::assert_relative_eq;
|
||
|
||
#[test]
|
||
fn test_brtez_constants() {
|
||
assert_relative_eq!(UN, 1.0, epsilon = 1e-15);
|
||
assert_relative_eq!(HALF, 0.5, epsilon = 1e-15);
|
||
assert_relative_eq!(THIRD, 1.0 / 3.0, epsilon = 1e-15);
|
||
assert_relative_eq!(SIXTH, 1.0 / 6.0, epsilon = 1e-15);
|
||
assert_relative_eq!(XCON, 8.0935e-21, epsilon = 1e-25);
|
||
assert_relative_eq!(YCON, 1.68638e-10, epsilon = 1e-15);
|
||
}
|
||
|
||
#[test]
|
||
fn test_brtez_early_return() {
|
||
// 当 nfreqe = 0 时应直接返回
|
||
let params = BrtezParams {
|
||
id: 1,
|
||
nfreqe: 0,
|
||
nd: 10,
|
||
ndre: 5,
|
||
inhe: 1,
|
||
inre: 1,
|
||
inpc: 1,
|
||
inse: 1,
|
||
inmp: 0,
|
||
nlvexp: 0,
|
||
nn0: 10,
|
||
isplin: 0,
|
||
icompt: 0,
|
||
icombc: 0,
|
||
ichcoo: 0,
|
||
idisk: 0,
|
||
ifz0: 0,
|
||
ibc: 0,
|
||
iwinbl: 0,
|
||
radzer: 0.0,
|
||
nfreq: 100,
|
||
dst: 0.0,
|
||
dsn: 0.0,
|
||
bn: 0.0,
|
||
hk: 0.0,
|
||
zd: &[],
|
||
temp: &[],
|
||
dens: &[],
|
||
elec: &[],
|
||
freq: &[],
|
||
dlnfr: &[],
|
||
delj: &[],
|
||
kij: &[],
|
||
ijex: &[],
|
||
ijorig: &[],
|
||
ijfr: &[],
|
||
sigec: &[],
|
||
fhd: &[],
|
||
hextrd: &[],
|
||
rad: &[],
|
||
radex: &[],
|
||
fk0: &[],
|
||
fkp: &[],
|
||
fkm: &[],
|
||
abso0: &[],
|
||
absop: &[],
|
||
absom: &[],
|
||
scat0: &[],
|
||
scatp: &[],
|
||
scatm: &[],
|
||
emis0: &[],
|
||
emisp: &[],
|
||
emism: &[],
|
||
q0: &[],
|
||
uu0: &[],
|
||
wmm: &[],
|
||
dabt0: &[],
|
||
dabtp: &[],
|
||
dabtm: &[],
|
||
dabn0: &[],
|
||
dabnp: &[],
|
||
dabnm: &[],
|
||
dabm0: &[],
|
||
dabmp: &[],
|
||
dabmm: &[],
|
||
demt0: &[],
|
||
demtp: &[],
|
||
demtm: &[],
|
||
demn0: &[],
|
||
demnp: &[],
|
||
demnm: &[],
|
||
demm0: &[],
|
||
demmp: &[],
|
||
demmm: &[],
|
||
drch0: &[],
|
||
drchp: &[],
|
||
drchm: &[],
|
||
dret0: &[],
|
||
dretp: &[],
|
||
dretm: &[],
|
||
rad0: &[],
|
||
radp: &[],
|
||
radm: &[],
|
||
};
|
||
|
||
// 空矩阵
|
||
let mut a_data: Vec<Vec<f64>> = vec![];
|
||
let mut b_data: Vec<Vec<f64>> = vec![];
|
||
let mut c_data: Vec<Vec<f64>> = vec![];
|
||
let mut vecl_data: Vec<f64> = vec![];
|
||
|
||
let mut a_rows: Vec<&mut [f64]> = vec![];
|
||
let mut b_rows: Vec<&mut [f64]> = vec![];
|
||
let mut c_rows: Vec<&mut [f64]> = vec![];
|
||
|
||
let mut state = BrtezState {
|
||
a: &mut a_rows,
|
||
b: &mut b_rows,
|
||
c: &mut c_rows,
|
||
vecl: &mut vecl_data,
|
||
};
|
||
|
||
brtez(¶ms, &mut state);
|
||
// 应该不崩溃
|
||
}
|
||
}
|