643 lines
23 KiB
Rust
643 lines
23 KiB
Rust
//! 所有深度点的吸收、发射和散射系数计算 (含离子贡献)。
|
||
//!
|
||
//! 重构自 TLUSTY `opacfa.f`
|
||
//!
|
||
//! 对于给定频率点 IJ,计算所有深度点的吸收、发射和散射系数,
|
||
//! 并保存每个离子的贡献(用于计算冷却和加热率)。
|
||
//!
|
||
//! # 算法流程
|
||
//!
|
||
//! 1. 初始化电子散射贡献
|
||
//! 2. 计算频率和深度相关的基础量 (XKF, XKFB 等)
|
||
//! 3. 计算束缚-自由 (bound-free) 贡献 (DWNFR1, SGMER1)
|
||
//! 4. 计算自由-自由 (free-free) 贡献 (GFREE1, SFFHMI, FFCROS)
|
||
//! 5. 计算附加不透明度 (OPADD)
|
||
//! 6. 保存连续谱系数
|
||
//! 7. 计算谱线贡献 (如果 icoolp != 0)
|
||
//! 8. 最终不透明度计算
|
||
//! 9. PRD 修正
|
||
|
||
// f2r_depends: DWNFR1, OPADD, PRD, SGMER1
|
||
|
||
use crate::tlusty::state::constants::UN;
|
||
use crate::tlusty::state::{GffPar, DwnPar, InpPar};
|
||
use crate::tlusty::math::opacity::dwnfr1;
|
||
use crate::tlusty::math::sgmer1_simple;
|
||
use crate::tlusty::math::gfree1;
|
||
use crate::tlusty::math::sffhmi;
|
||
use crate::tlusty::math::ffcros;
|
||
|
||
// 物理常数
|
||
const C14: f64 = 2.99793e14;
|
||
|
||
// ============================================================================
|
||
// 回调接口
|
||
// ============================================================================
|
||
|
||
/// OPADD 回调结果
|
||
#[derive(Debug, Clone, Default)]
|
||
pub struct OpaddResult {
|
||
pub abad: f64,
|
||
pub emad: f64,
|
||
pub scad: f64,
|
||
}
|
||
|
||
/// OPACFA 回调 trait - 用于 OPADD 和 PRD 外部调用
|
||
pub trait OpacfaCallbacks {
|
||
fn call_opadd(&mut self, mode: i32, icall: i32, ij: usize, id: usize) -> OpaddResult;
|
||
fn call_prd(&mut self, ij: usize);
|
||
}
|
||
|
||
/// 空回调实现
|
||
#[derive(Debug, Clone, Default)]
|
||
pub struct NoOpCallbacks;
|
||
|
||
impl OpacfaCallbacks for NoOpCallbacks {
|
||
fn call_opadd(&mut self, _mode: i32, _icall: i32, _ij: usize, _id: usize) -> OpaddResult {
|
||
OpaddResult::default()
|
||
}
|
||
fn call_prd(&mut self, _ij: usize) {}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 参数结构体
|
||
// ============================================================================
|
||
|
||
/// OPACFA 配置参数
|
||
#[derive(Debug, Clone)]
|
||
pub struct OpacfaConfig {
|
||
pub icompt: i32,
|
||
pub icoolp: i32,
|
||
pub ispodf: i32,
|
||
pub ifdiel: i32,
|
||
pub iopadd: i32,
|
||
pub ifprd: i32,
|
||
pub izscal: i32,
|
||
pub frtabm: f64,
|
||
}
|
||
|
||
impl Default for OpacfaConfig {
|
||
fn default() -> Self {
|
||
Self {
|
||
icompt: 0, icoolp: 0, ispodf: 0, ifdiel: 0,
|
||
iopadd: 0, ifprd: 0, izscal: 1, frtabm: 1e16,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// OPACFA 外部依赖上下文
|
||
pub struct OpacfaContext<'a> {
|
||
pub inppar: &'a InpPar,
|
||
pub dwnpar: &'a DwnPar,
|
||
pub gffpar: &'a GffPar,
|
||
pub frch: &'a [f64],
|
||
pub sgmsum: &'a [Vec<Vec<f64>>],
|
||
pub nlmx: usize,
|
||
}
|
||
|
||
/// OPACFA 模型数据 (深度相关)
|
||
pub struct OpacfaModelData<'a> {
|
||
pub nd: usize,
|
||
pub nfreq: usize,
|
||
pub freq: &'a [f64],
|
||
pub bnue: &'a [f64],
|
||
pub temp: &'a [f64],
|
||
pub elec: &'a [f64],
|
||
pub dens1: &'a [f64],
|
||
pub popul: &'a [f64],
|
||
pub elscat: &'a mut [f64],
|
||
pub sigec: &'a [f64],
|
||
pub hkt1: &'a mut [f64],
|
||
pub xkf: &'a mut [f64],
|
||
pub xkf1: &'a mut [f64],
|
||
pub xkfb: &'a mut [f64],
|
||
}
|
||
|
||
/// OPACFA 原子数据
|
||
pub struct OpacfaAtomicData<'a> {
|
||
pub nion: usize,
|
||
pub ntranc: usize,
|
||
pub itrbf: &'a [i32],
|
||
pub ilow: &'a [i32],
|
||
pub iup: &'a [i32],
|
||
pub ifr0: &'a [i32],
|
||
pub ifr1: &'a [i32],
|
||
pub mcdw: &'a [i32],
|
||
pub fr0: &'a [f64],
|
||
pub ifwop: &'a [i32],
|
||
pub imrg: &'a [i32],
|
||
pub itra: &'a [i32],
|
||
pub nnext: &'a [i32],
|
||
pub nfirst: &'a [i32],
|
||
pub ff: &'a [f64],
|
||
pub charg2: &'a [i32],
|
||
pub iz: &'a [i32],
|
||
pub ielh: i32,
|
||
pub iel: &'a [i32],
|
||
pub iadop: &'a [i32],
|
||
pub iatm: &'a [i32],
|
||
pub abtra: &'a [f64],
|
||
pub emtra: &'a [f64],
|
||
pub sff2: &'a [f64],
|
||
pub sff3: &'a [f64],
|
||
pub cross_bf: &'a [f64],
|
||
pub cross_di: &'a [f64],
|
||
pub ijlin: &'a [i32],
|
||
pub nlines: &'a [i32],
|
||
pub itrlin: &'a [i32],
|
||
pub linexp: &'a [bool],
|
||
pub prflin: &'a [f64],
|
||
pub indexp: &'a [i32],
|
||
pub kfr0: &'a [i32],
|
||
pub xjid: &'a [f64],
|
||
pub jidi: &'a [i32],
|
||
pub sigfe: &'a [f64],
|
||
pub dwf1: &'a mut [f64],
|
||
pub sgmg: &'a mut [f64],
|
||
}
|
||
|
||
impl OpacfaAtomicData<'_> {
|
||
pub fn empty<'a>() -> OpacfaAtomicData<'a> {
|
||
OpacfaAtomicData {
|
||
nion: 0, ntranc: 0,
|
||
itrbf: &[], ilow: &[], iup: &[], ifr0: &[], ifr1: &[],
|
||
mcdw: &[], fr0: &[], ifwop: &[], imrg: &[], itra: &[],
|
||
nnext: &[], nfirst: &[], ff: &[], charg2: &[], iz: &[],
|
||
ielh: 0, iel: &[], iadop: &[], iatm: &[],
|
||
abtra: &[], emtra: &[], sff2: &[], sff3: &[],
|
||
cross_bf: &[], cross_di: &[], ijlin: &[], nlines: &[],
|
||
itrlin: &[], linexp: &[], prflin: &[],
|
||
indexp: &[], kfr0: &[], xjid: &[], jidi: &[], sigfe: &[],
|
||
dwf1: &mut [], sgmg: &mut [],
|
||
}
|
||
}
|
||
}
|
||
|
||
/// OPACFA 输出
|
||
pub struct OpacfaOutput<'a> {
|
||
pub abso1: &'a mut [f64],
|
||
pub emis1: &'a mut [f64],
|
||
pub scat1: &'a mut [f64],
|
||
pub absot: &'a mut [f64],
|
||
pub absoc1: &'a mut [f64],
|
||
pub emisc1: &'a mut [f64],
|
||
pub absoti: &'a mut [f64],
|
||
pub emisti: &'a mut [f64],
|
||
}
|
||
|
||
// ============================================================================
|
||
// 主函数
|
||
// ============================================================================
|
||
|
||
/// 计算给定频率点 IJ 下所有深度点的吸收、发射和散射系数。
|
||
pub fn opacfa<C: OpacfaCallbacks>(
|
||
ij: usize,
|
||
config: &OpacfaConfig,
|
||
model: &mut OpacfaModelData,
|
||
atomic: &mut OpacfaAtomicData,
|
||
output: &mut OpacfaOutput,
|
||
context: &OpacfaContext,
|
||
callbacks: &mut C,
|
||
) {
|
||
let ij_idx = ij - 1; // 0-indexed
|
||
let nd = model.nd;
|
||
let nfreq = model.nfreq;
|
||
|
||
// ========================================================================
|
||
// 1. 初始化
|
||
// ========================================================================
|
||
|
||
if config.icompt > 0 {
|
||
for id in 0..nd {
|
||
model.elscat[id] = model.elec[id] * model.sigec[ij_idx];
|
||
}
|
||
}
|
||
|
||
for id in 0..nd {
|
||
output.abso1[id] = model.elscat[id];
|
||
output.emis1[id] = 0.0;
|
||
output.scat1[id] = model.elscat[id];
|
||
output.absoc1[id] = output.abso1[id];
|
||
output.emisc1[id] = 0.0;
|
||
let nion = output.absoti.len() / nd;
|
||
for ion in 0..nion {
|
||
output.absoti[ion * nd + id] = 0.0;
|
||
output.emisti[ion * nd + id] = 0.0;
|
||
}
|
||
}
|
||
|
||
// ========================================================================
|
||
// 2. 频率和深度相关的基础量
|
||
// ========================================================================
|
||
|
||
let fr = model.freq[ij_idx];
|
||
let frinv = UN / fr;
|
||
let fr3inv = frinv * frinv * frinv;
|
||
let lfre = fr > config.frtabm;
|
||
|
||
for id in 0..nd {
|
||
model.hkt1[id] = 6.626176e-27 / 1.380662e-16 / model.temp[id];
|
||
model.xkf[id] = (-model.hkt1[id] * fr).exp();
|
||
model.xkf1[id] = UN - model.xkf[id];
|
||
model.xkfb[id] = model.xkf[id] * model.bnue[ij_idx];
|
||
}
|
||
|
||
// ========================================================================
|
||
// 3. 束缚-自由贡献
|
||
// ========================================================================
|
||
|
||
if config.ifdiel == 0 {
|
||
bound_free_no_diel(ij_idx, nd, nfreq, fr, frinv, fr3inv, lfre, atomic, output, context);
|
||
} else {
|
||
bound_free_with_diel(ij_idx, nd, nfreq, fr, frinv, fr3inv, lfre, atomic, output, context);
|
||
}
|
||
|
||
// ========================================================================
|
||
// 4. 自由-自由贡献
|
||
// ========================================================================
|
||
|
||
free_free(nd, nfreq, fr, fr3inv, lfre, atomic, model, output, context);
|
||
|
||
// ========================================================================
|
||
// 5. 附加不透明度 (OPADD)
|
||
// ========================================================================
|
||
|
||
if config.iopadd != 0 {
|
||
let icall: i32 = 1;
|
||
for id in 0..nd {
|
||
let r = callbacks.call_opadd(0, icall, ij, id + 1);
|
||
output.abso1[id] += r.abad;
|
||
output.emis1[id] += r.emad;
|
||
output.scat1[id] += r.scad;
|
||
let ielh = atomic.ielh as usize;
|
||
if ielh > 0 && ielh - 1 < output.absoti.len() / nd {
|
||
output.absoti[(ielh - 1) * nd + id] += r.abad;
|
||
output.emisti[(ielh - 1) * nd + id] += r.emad;
|
||
}
|
||
}
|
||
}
|
||
|
||
// ========================================================================
|
||
// 6. 保存连续谱系数
|
||
// ========================================================================
|
||
|
||
for id in 0..nd {
|
||
output.absoc1[id] = output.abso1[id];
|
||
output.emisc1[id] = output.emis1[id];
|
||
}
|
||
|
||
if config.icoolp == 0 {
|
||
finalize_opacities(config.izscal, model, output, nd);
|
||
return;
|
||
}
|
||
|
||
// ========================================================================
|
||
// 7. 谱线贡献
|
||
// ========================================================================
|
||
|
||
if config.ispodf == 0 {
|
||
line_contributions_standard(ij_idx, nd, nfreq, fr, lfre, atomic, model, output);
|
||
} else {
|
||
line_contributions_odf(ij_idx, nd, nfreq, lfre, atomic, model, output);
|
||
}
|
||
|
||
// ========================================================================
|
||
// 8. 最终不透明度计算
|
||
// ========================================================================
|
||
|
||
finalize_opacities(config.izscal, model, output, nd);
|
||
|
||
// ========================================================================
|
||
// 9. PRD 修正
|
||
// ========================================================================
|
||
|
||
if config.ifprd > 0 {
|
||
callbacks.call_prd(ij);
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 束缚-自由 (无双电子复合): SG=CROSS(IBFT,IJ)
|
||
// ============================================================================
|
||
|
||
fn bound_free_no_diel(
|
||
ij_idx: usize, nd: usize, nfreq: usize,
|
||
fr: f64, frinv: f64, fr3inv: f64, lfre: bool,
|
||
atomic: &mut OpacfaAtomicData, output: &mut OpacfaOutput, context: &OpacfaContext,
|
||
) {
|
||
for ibft in 0..atomic.ntranc {
|
||
let itr = (atomic.itrbf[ibft] - 1) as usize;
|
||
let sg = atomic.cross_bf[ibft * nfreq + ij_idx];
|
||
let ii = (atomic.ilow[itr] - 1) as usize;
|
||
let iad = if ii < atomic.iatm.len() {
|
||
atomic.iadop[(atomic.iatm[ii] - 1) as usize]
|
||
} else { 0 };
|
||
|
||
if !(sg > 0.0 && (iad == 0 || (iad > 0 && lfre))) { continue; }
|
||
|
||
let izz = (atomic.iz[ii] - 1) as usize;
|
||
let imer = (atomic.imrg[ii] - 1) as usize;
|
||
|
||
for id in 0..nd {
|
||
let mut sgd = sg;
|
||
if atomic.mcdw[itr] > 0 {
|
||
let mcdw_idx = (atomic.mcdw[itr] - 1) as usize;
|
||
let dw1 = dwnfr1(fr, atomic.fr0[itr], id, izz, context.inppar, context.dwnpar);
|
||
atomic.dwf1[mcdw_idx * nd + id] = dw1;
|
||
sgd = sg * dw1;
|
||
}
|
||
if atomic.ifwop[ii] < 0 {
|
||
let sgme1 = sgmer1_simple(frinv, fr3inv, imer, id,
|
||
context.frch, context.sgmsum, context.nlmx);
|
||
atomic.sgmg[imer * nd + id] = sgme1;
|
||
sgd = sgme1;
|
||
}
|
||
let emisbf = sgd * atomic.emtra[itr * nd + id];
|
||
output.abso1[id] += sgd * atomic.abtra[itr * nd + id];
|
||
output.emis1[id] += emisbf;
|
||
let ion = (atomic.iel[ii] - 1) as usize;
|
||
if ion < output.absoti.len() / nd {
|
||
output.absoti[ion * nd + id] += sgd * atomic.abtra[itr * nd + id];
|
||
output.emisti[ion * nd + id] += emisbf;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 束缚-自由 (有双电子复合): SG=CROSSD(IBFT,IJ,ID)
|
||
// ============================================================================
|
||
|
||
fn bound_free_with_diel(
|
||
ij_idx: usize, nd: usize, nfreq: usize,
|
||
fr: f64, frinv: f64, fr3inv: f64, lfre: bool,
|
||
atomic: &mut OpacfaAtomicData, output: &mut OpacfaOutput, context: &OpacfaContext,
|
||
) {
|
||
for ibft in 0..atomic.ntranc {
|
||
let itr = (atomic.itrbf[ibft] - 1) as usize;
|
||
let ii = (atomic.ilow[itr] - 1) as usize;
|
||
let iad = if ii < atomic.iatm.len() {
|
||
atomic.iadop[(atomic.iatm[ii] - 1) as usize]
|
||
} else { 0 };
|
||
|
||
if !(iad == 0 || (iad > 0 && lfre)) { continue; }
|
||
|
||
let izz = (atomic.iz[ii] - 1) as usize;
|
||
let imer = (atomic.imrg[ii] - 1) as usize;
|
||
|
||
for id in 0..nd {
|
||
let sg = atomic.cross_di[ibft * nfreq * nd + ij_idx * nd + id];
|
||
if sg <= 0.0 { continue; }
|
||
|
||
let mut sgd = sg;
|
||
if atomic.mcdw[itr] > 0 {
|
||
let mcdw_idx = (atomic.mcdw[itr] - 1) as usize;
|
||
let dw1 = dwnfr1(fr, atomic.fr0[itr], id, izz, context.inppar, context.dwnpar);
|
||
atomic.dwf1[mcdw_idx * nd + id] = dw1;
|
||
sgd = sg * dw1;
|
||
}
|
||
if atomic.ifwop[ii] < 0 {
|
||
let sgme1 = sgmer1_simple(frinv, fr3inv, imer, id,
|
||
context.frch, context.sgmsum, context.nlmx);
|
||
atomic.sgmg[imer * nd + id] = sgme1;
|
||
sgd = sgme1;
|
||
}
|
||
let emisbf = sgd * atomic.emtra[itr * nd + id];
|
||
output.abso1[id] += sgd * atomic.abtra[itr * nd + id];
|
||
output.emis1[id] += emisbf;
|
||
let ion = (atomic.iel[ii] - 1) as usize;
|
||
if ion < output.absoti.len() / nd {
|
||
output.absoti[ion * nd + id] += sgd * atomic.abtra[itr * nd + id];
|
||
output.emisti[ion * nd + id] += emisbf;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 自由-自由贡献
|
||
// ============================================================================
|
||
|
||
fn free_free(
|
||
nd: usize, nfreq: usize, fr: f64, fr3inv: f64, lfre: bool,
|
||
atomic: &mut OpacfaAtomicData, model: &OpacfaModelData,
|
||
output: &mut OpacfaOutput, context: &OpacfaContext,
|
||
) {
|
||
for ion in 0..atomic.nion {
|
||
let nnext_1 = atomic.nnext[ion] as usize; // 1-indexed → usize
|
||
let itra_dim = atomic.nion.max(1); // ITRA 维度估计
|
||
let it = if nnext_1 > 0 && (nnext_1 - 1) < itra_dim {
|
||
atomic.itra[(nnext_1 - 1) * itra_dim + (nnext_1 - 1)]
|
||
} else { 0 };
|
||
|
||
let nnext_0 = nnext_1 - 1; // 0-indexed
|
||
let iad = if nnext_0 < atomic.iatm.len() && nnext_0 > 0 {
|
||
atomic.iadop[(atomic.iatm[nnext_0] - 1) as usize]
|
||
} else { 0 };
|
||
|
||
if iad > 0 && !lfre { continue; }
|
||
|
||
match it {
|
||
1 => { // 氢型, Gaunt=1
|
||
for id in 0..nd {
|
||
let sf1 = atomic.sff3[ion * nd + id] * fr3inv;
|
||
let mut sf2 = atomic.sff2[ion * nd + id];
|
||
if fr < atomic.ff[ion] { sf2 = UN / model.xkf[id]; }
|
||
let absoff = sf1 * sf2;
|
||
output.abso1[id] += absoff;
|
||
output.emis1[id] += absoff;
|
||
add_ion(ion, id, nd, absoff, absoff, output);
|
||
}
|
||
}
|
||
2 => { // 氢型, 精确 Gaunt
|
||
for id in 0..nd {
|
||
let sf1 = atomic.sff3[ion * nd + id] * fr3inv;
|
||
let mut sf2 = atomic.sff2[ion * nd + id];
|
||
if fr < atomic.ff[ion] { sf2 = UN / model.xkf[id]; }
|
||
let x = C14 * (atomic.charg2[ion] as f64) / fr;
|
||
sf2 = sf2 - UN + gfree1(id, x, context.gffpar);
|
||
let absoff = sf1 * sf2;
|
||
output.abso1[id] += absoff;
|
||
output.emis1[id] += absoff;
|
||
add_ion(ion, id, nd, absoff, absoff, output);
|
||
}
|
||
}
|
||
3 => { // H⁻ 自由-自由
|
||
let ielh = atomic.ielh as usize;
|
||
if ielh > 0 && ielh - 1 < atomic.nfirst.len() {
|
||
let nfirst_elh = (atomic.nfirst[ielh - 1] - 1) as usize;
|
||
for id in 0..nd {
|
||
let popi = model.popul[nfirst_elh * nd + id];
|
||
let absoff = sffhmi(popi, fr, model.temp[id]) * model.elec[id];
|
||
output.abso1[id] += absoff;
|
||
output.emis1[id] += absoff;
|
||
add_ion(ion, id, nd, absoff, absoff, output);
|
||
}
|
||
}
|
||
}
|
||
it if it < 0 => { // 特殊截面
|
||
for id in 0..nd {
|
||
let absoff = ffcros(ion as i32, it, model.temp[id], fr)
|
||
* model.popul[nnext_0 * nd + id] * model.elec[id];
|
||
output.abso1[id] += absoff;
|
||
output.emis1[id] += absoff;
|
||
add_ion(ion, id, nd, absoff, absoff, output);
|
||
}
|
||
}
|
||
_ => {}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 添加离子贡献 (内联辅助)
|
||
#[inline(always)]
|
||
fn add_ion(ion: usize, id: usize, nd: usize, abs_val: f64, emis_val: f64, output: &mut OpacfaOutput) {
|
||
if ion < output.absoti.len() / nd {
|
||
let idx = ion * nd + id;
|
||
output.absoti[idx] += abs_val;
|
||
output.emisti[idx] += emis_val;
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 谱线贡献 (标准模式, ISPODF=0)
|
||
// ============================================================================
|
||
|
||
fn line_contributions_standard(
|
||
ij_idx: usize, nd: usize, nfreq: usize, fr: f64, lfre: bool,
|
||
atomic: &mut OpacfaAtomicData, model: &OpacfaModelData, output: &mut OpacfaOutput,
|
||
) {
|
||
// 主谱线
|
||
if atomic.ijlin[ij_idx] > 0 {
|
||
let itr = (atomic.ijlin[ij_idx] - 1) as usize;
|
||
let ilow_itr = (atomic.ilow[itr] - 1) as usize;
|
||
let iad = if ilow_itr < atomic.iatm.len() {
|
||
atomic.iadop[(atomic.iatm[ilow_itr] - 1) as usize]
|
||
} else { 0 };
|
||
|
||
if iad == 0 || (lfre && iad > 0) {
|
||
let ion = (atomic.iel[ilow_itr] - 1) as usize;
|
||
for id in 0..nd {
|
||
let sg = atomic.prflin[id * nfreq + ij_idx];
|
||
output.abso1[id] += sg * atomic.abtra[itr * nd + id];
|
||
output.emis1[id] += sg * atomic.emtra[itr * nd + id];
|
||
add_ion(ion, id, nd, sg * atomic.abtra[itr * nd + id],
|
||
sg * atomic.emtra[itr * nd + id], output);
|
||
}
|
||
}
|
||
}
|
||
|
||
if atomic.nlines[ij_idx] <= 0 { return; }
|
||
|
||
let nlines_ij = atomic.nlines[ij_idx] as usize;
|
||
for ilint in 0..nlines_ij {
|
||
let itr = (atomic.itrlin[ilint * nfreq + ij_idx] - 1) as usize;
|
||
let ilow_itr = (atomic.ilow[itr] - 1) as usize;
|
||
let iad = if ilow_itr < atomic.iatm.len() {
|
||
atomic.iadop[(atomic.iatm[ilow_itr] - 1) as usize]
|
||
} else { 0 };
|
||
|
||
if iad > 0 && !lfre { continue; }
|
||
if atomic.linexp[itr] { continue; }
|
||
|
||
// 寻找 IJ0: DO IJT=IJ0,IFR1(ITR) IF(FREQ(IJT)<=FR) IJ0=IJT
|
||
let ifr0_itr = (atomic.ifr0[itr] - 1) as usize;
|
||
let ifr1_itr = (atomic.ifr1[itr] - 1) as usize;
|
||
let mut ij0 = ifr0_itr;
|
||
for ijt in ifr0_itr..=ifr1_itr {
|
||
if model.freq[ijt] <= fr {
|
||
ij0 = ijt;
|
||
break;
|
||
}
|
||
}
|
||
if ij0 == 0 { continue; }
|
||
let ij1 = ij0 - 1;
|
||
|
||
let a1 = (fr - model.freq[ij0]) / (model.freq[ij1] - model.freq[ij0]);
|
||
let a2 = UN - a1;
|
||
let ion = (atomic.iel[ilow_itr] - 1) as usize;
|
||
|
||
for id in 0..nd {
|
||
let sg = a1 * atomic.prflin[id * nfreq + ij1]
|
||
+ a2 * atomic.prflin[id * nfreq + ij0];
|
||
output.abso1[id] += sg * atomic.abtra[itr * nd + id];
|
||
output.emis1[id] += sg * atomic.emtra[itr * nd + id];
|
||
add_ion(ion, id, nd, sg * atomic.abtra[itr * nd + id],
|
||
sg * atomic.emtra[itr * nd + id], output);
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 谱线贡献 (ODF 采样模式, ISPODF>0)
|
||
// ============================================================================
|
||
|
||
fn line_contributions_odf(
|
||
ij_idx: usize, nd: usize, nfreq: usize, lfre: bool,
|
||
atomic: &mut OpacfaAtomicData, model: &OpacfaModelData, output: &mut OpacfaOutput,
|
||
) {
|
||
if atomic.nlines[ij_idx] <= 0 { return; }
|
||
let nlines_ij = atomic.nlines[ij_idx] as usize;
|
||
|
||
for ilint in 0..nlines_ij {
|
||
let itr = (atomic.itrlin[ilint * nfreq + ij_idx] - 1) as usize;
|
||
let ilow_itr = (atomic.ilow[itr] - 1) as usize;
|
||
let iad = if ilow_itr < atomic.iatm.len() {
|
||
atomic.iadop[(atomic.iatm[ilow_itr] - 1) as usize]
|
||
} else { 0 };
|
||
|
||
if iad > 0 && !lfre { continue; }
|
||
let ion = (atomic.iel[ilow_itr] - 1) as usize;
|
||
|
||
let _ij = ij_idx + 1; // 1-indexed for computing KJ
|
||
let kj = (_ij as i32 - atomic.ifr0[itr] + atomic.kfr0[itr]) as usize;
|
||
let indxpa = atomic.indexp[itr].abs();
|
||
|
||
if indxpa != 3 && indxpa != 4 {
|
||
for id in 0..nd {
|
||
let sg = atomic.prflin[id * nfreq + kj];
|
||
output.abso1[id] += sg * atomic.abtra[itr * nd + id];
|
||
output.emis1[id] += sg * atomic.emtra[itr * nd + id];
|
||
add_ion(ion, id, nd, sg * atomic.abtra[itr * nd + id],
|
||
sg * atomic.emtra[itr * nd + id], output);
|
||
}
|
||
} else {
|
||
// Fe 不透明度采样
|
||
for id in 0..nd {
|
||
let kjid = atomic.jidi[id] as usize;
|
||
let xjid_val = atomic.xjid[id];
|
||
let sg = (xjid_val * atomic.sigfe[kjid * nfreq + kj]
|
||
+ (UN - xjid_val) * atomic.sigfe[(kjid + 1) * nfreq + kj]).exp();
|
||
output.abso1[id] += sg * atomic.abtra[itr * nd + id];
|
||
output.emis1[id] += sg * atomic.emtra[itr * nd + id];
|
||
add_ion(ion, id, nd, sg * atomic.abtra[itr * nd + id],
|
||
sg * atomic.emtra[itr * nd + id], output);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 最终不透明度计算
|
||
// ============================================================================
|
||
|
||
fn finalize_opacities(izscal: i32, model: &OpacfaModelData, output: &mut OpacfaOutput, nd: usize) {
|
||
let nion = output.absoti.len() / nd;
|
||
for id in 0..nd {
|
||
output.abso1[id] -= output.emis1[id] * model.xkf[id];
|
||
output.absoc1[id] -= output.emisc1[id] * model.xkf[id];
|
||
for ion in 0..nion {
|
||
output.absoti[ion * nd + id] -= output.emisti[ion * nd + id] * model.xkf[id];
|
||
}
|
||
output.emis1[id] *= model.xkfb[id];
|
||
output.emisc1[id] *= model.xkfb[id];
|
||
for ion in 0..nion {
|
||
output.emisti[ion * nd + id] *= model.xkfb[id];
|
||
}
|
||
output.absot[id] = output.abso1[id];
|
||
if izscal == 0 {
|
||
output.absot[id] = output.abso1[id] * model.dens1[id];
|
||
}
|
||
}
|
||
}
|