728 lines
22 KiB
Rust
728 lines
22 KiB
Rust
//! NLTE Lucy-Unsöld 温度修正方案。
|
||
//!
|
||
//! 重构自 TLUSTY `lucy.f`。
|
||
//!
|
||
//! # 功能
|
||
//!
|
||
//! 实现 Werner & Dreizler 的 NLTE 温度修正方案:
|
||
//! 1. 计算辐射加热/冷却率
|
||
//! 2. 计算 Eddington 因子
|
||
//! 3. 应用温度修正
|
||
//! 4. 积分流体静力学平衡
|
||
//! 5. 更新粒子数
|
||
//!
|
||
//! # 算法
|
||
//!
|
||
//! 基于辐射平衡方程:
|
||
//! - HEAT = 加热率(吸收 - 发射)
|
||
//! - DELH = 辐射流偏离
|
||
//! - DELTAT = 温度修正
|
||
|
||
use crate::tlusty::state::constants::{BN, BOLK, HALF, HK, MDEPTH, MFREQ, MTRANS, SIG4P, UN};
|
||
// f2r_depends: COLIS, CONCOR, ELCOR, ODFMER, OPACFL, OPAINI, SABOLF, STEQEQ, TDPINI, WNSTOR
|
||
|
||
// ============================================================================
|
||
// 常量
|
||
// ============================================================================
|
||
|
||
/// 1/3
|
||
const THIRD: f64 = 1.0 / 3.0;
|
||
|
||
// ============================================================================
|
||
// 配置结构体
|
||
// ============================================================================
|
||
|
||
/// LUCY 配置参数。
|
||
#[derive(Debug, Clone)]
|
||
pub struct LucyConfig {
|
||
/// Lucy 迭代次数上限 (ITLUCY)
|
||
pub itlucy: i32,
|
||
/// 加速开始迭代 (IACLT)
|
||
pub iaclt: i32,
|
||
/// 加速间隔 (IACLDT)
|
||
pub iacldt: i32,
|
||
/// 流体静力学修正标志 (IHECOR)
|
||
pub ihecor: i32,
|
||
/// 是否 LTE (LTE)
|
||
pub lte: bool,
|
||
/// LCHC 标志
|
||
pub lchc: bool,
|
||
/// 电子密度修正起始迭代 (IELCOR)
|
||
pub ielcor: i32,
|
||
/// 当前迭代次数 (ITER)
|
||
pub iter: i32,
|
||
/// Lucy 跃迁列表 (NTRL)
|
||
pub ntrl: i32,
|
||
/// Lucy 跃迁标志 (ILUCTR)
|
||
pub iluctr: Vec<i32>,
|
||
}
|
||
|
||
impl Default for LucyConfig {
|
||
fn default() -> Self {
|
||
Self {
|
||
itlucy: 0,
|
||
iaclt: 10,
|
||
iacldt: 1,
|
||
ihecor: 1,
|
||
lte: false,
|
||
lchc: false,
|
||
ielcor: 10,
|
||
iter: 0,
|
||
ntrl: 0,
|
||
iluctr: vec![0; MTRANS],
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 输入参数结构体
|
||
// ============================================================================
|
||
|
||
/// LUCY 模型参数(只读引用)。
|
||
pub struct LucyModelParams<'a> {
|
||
/// 深度点数 (ND)
|
||
pub nd: usize,
|
||
/// 频率点数 (NFREQ)
|
||
pub nfreq: usize,
|
||
/// 跃迁数 (NTRANS)
|
||
pub ntrans: usize,
|
||
/// 有效温度 (TEFF)
|
||
pub teff: f64,
|
||
/// 表面重力加速度 (GRAV)
|
||
pub grav: f64,
|
||
/// 参考辐射压 (PRD0)
|
||
pub prd0: f64,
|
||
|
||
// 深度相关数组 [nd]
|
||
/// 深度 (柱质量密度, g/cm²)
|
||
pub dm: &'a [f64],
|
||
/// 温度 (K)
|
||
pub temp: &'a [f64],
|
||
/// 电子密度 (cm⁻³)
|
||
pub elec: &'a [f64],
|
||
/// 总粒子密度 (cm⁻³)
|
||
pub dens: &'a [f64],
|
||
/// 密度倒数 (1/DENS)
|
||
pub dens1: &'a [f64],
|
||
/// 平均分子量
|
||
pub wmm: &'a [f64],
|
||
/// 深度间隔 [nd-1]
|
||
pub deldm: &'a [f64],
|
||
/// 湍流速度
|
||
pub vturb: &'a [f64],
|
||
/// 辐射压力梯度
|
||
pub pradt: &'a [f64],
|
||
/// 气体压力 [nd]
|
||
pub pgs: &'a mut [f64],
|
||
|
||
// 频率相关数组 [nfreq]
|
||
/// 频率 (Hz)
|
||
pub freq: &'a [f64],
|
||
/// 频率权重
|
||
pub w: &'a [f64],
|
||
/// H 函数
|
||
pub fh: &'a [f64],
|
||
/// 外辐射场
|
||
pub hextrd: &'a [f64],
|
||
|
||
// ALI 相关数组
|
||
/// LAC2T 标志
|
||
pub lac2t: bool,
|
||
}
|
||
|
||
/// LUCY 输出结果。
|
||
#[derive(Debug, Clone)]
|
||
pub struct LucyOutput {
|
||
/// 新温度 [nd]
|
||
pub temp: Vec<f64>,
|
||
/// 新电子密度 [nd]
|
||
pub elec: Vec<f64>,
|
||
/// 新总密度 [nd]
|
||
pub dens: Vec<f64>,
|
||
/// 密度倒数 [nd]
|
||
pub dens1: Vec<f64>,
|
||
/// 气体压力 [nd]
|
||
pub pgs: Vec<f64>,
|
||
/// Lucy 迭代计数
|
||
pub ilucy: i32,
|
||
/// LAC2T 标志
|
||
pub lac2t: bool,
|
||
/// 最大加热偏差
|
||
pub dhhmx1: f64,
|
||
}
|
||
|
||
// ============================================================================
|
||
// 辅助结构体 - 迭代状态
|
||
// ============================================================================
|
||
|
||
/// Lucy 迭代内部状态。
|
||
struct LucyState {
|
||
/// 加热率 [nd]
|
||
heat: Vec<f64>,
|
||
/// 加热/普朗克比 [nd]
|
||
heab: Vec<f64>,
|
||
/// 零深度吸收 [nd]
|
||
absz: Vec<f64>,
|
||
/// 普朗克加权吸收 [nd]
|
||
absp: Vec<f64>,
|
||
/// 流加权吸收 [nd]
|
||
absh: Vec<f64>,
|
||
/// 总 J 积分 [nd]
|
||
totj: Vec<f64>,
|
||
/// 总 H 积分 [nd]
|
||
toth: Vec<f64>,
|
||
/// 总 B 积分 [nd]
|
||
totb: Vec<f64>,
|
||
/// Eddington 因子 [nd]
|
||
eddf: Vec<f64>,
|
||
/// 流偏离 [nd]
|
||
delh: Vec<f64>,
|
||
/// 光学深度 [nd]
|
||
tau: Vec<f64>,
|
||
/// 温度修正 [nd]
|
||
deltat: Vec<f64>,
|
||
/// 温度修正分量1 [nd]
|
||
dt1: Vec<f64>,
|
||
/// 温度修正分量2 [nd]
|
||
dt2: Vec<f64>,
|
||
/// 粒子数密度 [nd]
|
||
antc: Vec<f64>,
|
||
/// 电子分数 [nd]
|
||
xe: Vec<f64>,
|
||
/// 温度历史 0 [nd]
|
||
tem0: Vec<f64>,
|
||
/// 温度历史 1 [nd]
|
||
tem1: Vec<f64>,
|
||
/// 温度历史 2 [nd]
|
||
tem2: Vec<f64>,
|
||
/// 温度历史 3 [nd]
|
||
tem3: Vec<f64>,
|
||
/// Eddington H 比值
|
||
eddh: f64,
|
||
}
|
||
|
||
impl LucyState {
|
||
fn new(nd: usize) -> Self {
|
||
Self {
|
||
heat: vec![0.0; nd],
|
||
heab: vec![0.0; nd],
|
||
absz: vec![0.0; nd],
|
||
absp: vec![0.0; nd],
|
||
absh: vec![0.0; nd],
|
||
totj: vec![0.0; nd],
|
||
toth: vec![0.0; nd],
|
||
totb: vec![0.0; nd],
|
||
eddf: vec![0.0; nd],
|
||
delh: vec![0.0; nd],
|
||
tau: vec![0.0; nd],
|
||
deltat: vec![0.0; nd],
|
||
dt1: vec![0.0; nd],
|
||
dt2: vec![0.0; nd],
|
||
antc: vec![0.0; nd],
|
||
xe: vec![0.0; nd],
|
||
tem0: vec![0.0; nd],
|
||
tem1: vec![0.0; nd],
|
||
tem2: vec![0.0; nd],
|
||
tem3: vec![0.0; nd],
|
||
eddh: 0.0,
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 纯计算函数
|
||
// ============================================================================
|
||
|
||
/// Lucy 温度修正方案核心计算。
|
||
///
|
||
/// 这是一个简化版本,仅实现核心温度修正逻辑。
|
||
/// 完整版本需要传入完整的模型状态和所有依赖函数。
|
||
///
|
||
/// # 参数
|
||
/// - `config`: 配置参数
|
||
/// - `model`: 模型参数
|
||
/// - `opacfl_data`: 不透明度数据 (ABSO1, EMIS1L, SCAT1, ABSO1L)
|
||
/// - `rad1_data`: 辐射场数据 (RAD1, FAK1)
|
||
///
|
||
/// # 返回值
|
||
/// 温度修正后的模型状态
|
||
///
|
||
/// # Fortran 原始代码
|
||
///
|
||
/// ```fortran
|
||
/// SUBROUTINE LUCY
|
||
/// ! NLTE Lucy-Unsold temperature correction scheme
|
||
/// DO IJ=1,NFREQ
|
||
/// CALL OPACFL(IJ)
|
||
/// CALL RTEFR1(IJ)
|
||
/// ! 计算加热率、Eddington 因子等
|
||
/// END DO
|
||
/// ! 计算温度修正 DELTAT
|
||
/// ! 应用加速方案
|
||
/// ! 积分流体静力学平衡
|
||
/// END
|
||
/// ```
|
||
pub fn lucy_pure(
|
||
config: &LucyConfig,
|
||
model: &LucyModelParams,
|
||
opacfl_data: &[OpacflPointData],
|
||
rad1_data: &[Rad1PointData],
|
||
) -> LucyOutput {
|
||
let nd = model.nd;
|
||
let nfreq = model.nfreq;
|
||
|
||
// 如果 ITLUCY <= 0,直接返回
|
||
if config.itlucy <= 0 {
|
||
return LucyOutput {
|
||
temp: model.temp.to_vec(),
|
||
elec: model.elec.to_vec(),
|
||
dens: model.dens.to_vec(),
|
||
dens1: model.dens1.to_vec(),
|
||
pgs: model.pgs.to_vec(),
|
||
ilucy: 0,
|
||
lac2t: false,
|
||
dhhmx1: 0.0,
|
||
};
|
||
}
|
||
|
||
// 初始化状态
|
||
let mut state = LucyState::new(nd);
|
||
let mut lac2t = false;
|
||
let iacc0t = config.iaclt - 3;
|
||
let mut ilucy = 1;
|
||
|
||
// 迭代循环
|
||
while ilucy <= config.itlucy {
|
||
// 重置累积量
|
||
for id in 0..nd {
|
||
state.heat[id] = 0.0;
|
||
state.heab[id] = 0.0;
|
||
state.absz[id] = 0.0;
|
||
state.absp[id] = 0.0;
|
||
state.absh[id] = 0.0;
|
||
state.totj[id] = 0.0;
|
||
state.toth[id] = 0.0;
|
||
state.totb[id] = 0.0;
|
||
state.eddf[id] = 0.0;
|
||
}
|
||
state.eddh = 0.0;
|
||
|
||
// 频率循环
|
||
for ij in 0..nfreq {
|
||
let w0 = model.w[ij];
|
||
let fr = model.freq[ij];
|
||
let fr15 = fr * 1e-15;
|
||
let bnu = BN * fr15 * fr15 * fr15;
|
||
|
||
// 获取预计算的不透明度和辐射数据
|
||
let opac = &opacfl_data[ij];
|
||
let rad = &rad1_data[ij];
|
||
|
||
// 计算光学深度 (第一个点)
|
||
if ij == 0 {
|
||
state.tau[0] = opac.abso1[0] / model.dens[0] * model.dm[0];
|
||
}
|
||
|
||
for id in 0..nd {
|
||
// 计算光学深度
|
||
if id > 0 {
|
||
state.tau[id] = state.tau[id - 1]
|
||
+ HALF
|
||
* (opac.abso1[id] / model.dens[id]
|
||
+ opac.abso1[id - 1] / model.dens[id - 1])
|
||
* (model.dm[id] - model.dm[id - 1]);
|
||
}
|
||
|
||
let pland = bnu / ((HK * fr / model.temp[id]).exp() - UN);
|
||
let absot0 = opac.abso1l[id] - opac.scat1[id];
|
||
|
||
// 加热率累积
|
||
state.heat[id] += w0 * (absot0 * rad.rad1[id] - opac.emis1l[id]);
|
||
state.heab[id] +=
|
||
w0 * (absot0 * rad.rad1[id] - opac.emis1l[id]) / (absot0 * pland);
|
||
state.absp[id] += w0 * absot0 * pland;
|
||
state.totj[id] += w0 * rad.rad1[id];
|
||
state.totb[id] += w0 * pland;
|
||
state.eddf[id] += w0 * rad.fak1[id] * rad.rad1[id];
|
||
}
|
||
|
||
// 表面 Eddington H
|
||
state.eddh += w0 * rad.rad1[0] * model.fh[ij];
|
||
state.toth[0] += w0 * (rad.rad1[0] * model.fh[ij] - model.hextrd[ij]);
|
||
let absot0 = opac.abso1[0];
|
||
state.absh[0] += w0 * absot0 * (rad.rad1[0] * model.fh[ij] - model.hextrd[ij]);
|
||
|
||
// 深度积分
|
||
for id in 1..nd {
|
||
let absot0 = HALF * (opac.abso1[id] + opac.abso1[id - 1]);
|
||
let dtm = model.deldm[id - 1]
|
||
* (opac.abso1[id] * model.dens1[id] + opac.abso1[id - 1] * model.dens1[id - 1]);
|
||
let fluz = (rad.rad1[id] * rad.fak1[id] - rad.rad1[id - 1] * rad.fak1[id - 1]) / dtm;
|
||
state.toth[id] += w0 * fluz;
|
||
state.absh[id] += w0 * absot0 * fluz;
|
||
}
|
||
}
|
||
|
||
// 归一化
|
||
state.eddh /= state.totj[0];
|
||
let tef4 = SIG4P * model.teff.powi(4);
|
||
state.toth[nd - 1] = tef4;
|
||
state.heat[nd - 1] = 0.0;
|
||
state.heat[nd - 2] = 0.0;
|
||
|
||
// 计算各种吸收系数和流偏离
|
||
for id in 0..nd {
|
||
state.absz[id] = (state.heat[id] + state.absp[id]) / state.totj[id];
|
||
state.absp[id] /= state.totb[id];
|
||
state.absh[id] /= state.toth[id];
|
||
state.eddf[id] /= state.totj[id];
|
||
state.delh[id] = -state.toth[id] + tef4;
|
||
}
|
||
|
||
// 计算最大流偏离
|
||
let mut dhhmx1 = 0.0;
|
||
for id in 0..(nd - 1) {
|
||
let dhh = (state.delh[id] / state.toth[id]).abs();
|
||
if dhh > dhhmx1 {
|
||
dhhmx1 = dhh;
|
||
}
|
||
}
|
||
|
||
// 计算温度修正
|
||
let mut xx = 0.0;
|
||
let mut xx1 = 0.0;
|
||
|
||
// 表面点
|
||
let tp3 = model.temp[0].powi(3);
|
||
xx = state.eddf[0] / state.eddh * state.delh[0];
|
||
xx1 = xx;
|
||
state.dt1[0] = state.heat[0] / 16.0 / SIG4P * tp3 / state.absp[0];
|
||
state.dt2[0] = state.absz[0] / state.eddf[0] * xx / 16.0 / SIG4P * tp3 / state.absp[0];
|
||
state.deltat[0] = state.dt1[0] + state.dt2[0];
|
||
|
||
// 内部点
|
||
for id in 1..nd {
|
||
let tp3 = model.temp[id].powi(3);
|
||
xx += model.deldm[id - 1]
|
||
* (state.absh[id - 1] * model.dens1[id - 1] * state.delh[id - 1]
|
||
+ state.absh[id] * model.dens1[id] * state.delh[id]);
|
||
state.dt1[id] = state.heat[id] / 16.0 / SIG4P * tp3 / state.absp[id];
|
||
state.dt2[id] = state.absz[id] / state.eddf[id] * xx / 16.0 / SIG4P * tp3 / state.absp[id];
|
||
state.deltat[id] = state.dt1[id] + state.dt2[id];
|
||
}
|
||
|
||
// 应用温度修正
|
||
let mut new_temp = model.temp.to_vec();
|
||
let mut new_elec = model.elec.to_vec();
|
||
let mut new_dens = model.dens.to_vec();
|
||
let mut new_dens1 = model.dens1.to_vec();
|
||
let mut new_pgs = model.pgs.to_vec();
|
||
|
||
for id in 0..nd {
|
||
new_temp[id] += state.deltat[id];
|
||
state.tem0[id] = new_temp[id];
|
||
let aold = new_dens[id] / model.wmm[id] + new_elec[id];
|
||
state.xe[id] = UN - new_elec[id] / aold;
|
||
}
|
||
|
||
// 加速方案
|
||
if ilucy >= config.iaclt && ilucy >= iacc0t {
|
||
let ipng = if config.iacldt > 0 {
|
||
(ilucy - config.iaclt) % config.iacldt
|
||
} else {
|
||
0
|
||
};
|
||
|
||
if !lac2t {
|
||
let ipt = ilucy % 3;
|
||
let ipt0 = config.iaclt % 3;
|
||
let ipt1 = (config.iaclt + 1) % 3;
|
||
let ipt2 = (config.iaclt + 2) % 3;
|
||
|
||
if ilucy == iacc0t {
|
||
for id in 0..nd {
|
||
state.tem3[id] = state.tem0[id];
|
||
}
|
||
} else if ipt == ipt1 {
|
||
for id in 0..nd {
|
||
state.tem2[id] = state.tem0[id];
|
||
}
|
||
} else if ipt == ipt2 {
|
||
for id in 0..nd {
|
||
state.tem1[id] = state.tem0[id];
|
||
}
|
||
}
|
||
} else if ipng != 0 {
|
||
// 滚动温度历史
|
||
for id in 0..nd {
|
||
state.tem3[id] = state.tem2[id];
|
||
state.tem2[id] = state.tem1[id];
|
||
state.tem1[id] = state.tem0[id];
|
||
}
|
||
} else {
|
||
// 应用加速度
|
||
if ilucy >= config.iaclt {
|
||
let (a1, b1, b2, c1, c2) = compute_acceleration(&state, nd);
|
||
|
||
let ab = b2 * a1 - b1 * b1;
|
||
if ab != 0.0 {
|
||
let a0 = (b2 * c1 - b1 * c2) / ab;
|
||
let b0 = (a1 * c2 - b1 * c1) / ab;
|
||
|
||
for id in 0..nd {
|
||
state.tem0[id] = (1.0 - a0 - b0) * state.tem0[id]
|
||
+ a0 * state.tem1[id]
|
||
+ b0 * state.tem2[id];
|
||
new_temp[id] = state.tem0[id];
|
||
}
|
||
lac2t = true;
|
||
} else {
|
||
// 对应 Fortran: WRITE(6,601) ILUCY,AB
|
||
// FORMAT(/,' **** ACCELT, ITER=',I4,' AB = ',F7.3,/)
|
||
eprintln!("\n **** ACCELT, ITER={:4} AB = {:7.3}\n", ilucy, ab);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 流体静力学平衡积分
|
||
if config.ihecor >= 1 {
|
||
// 表面边界条件
|
||
let ptur = HALF * model.vturb[0] * model.vturb[0] * new_dens[0];
|
||
state.antc[0] = (model.dm[0] * model.grav - model.prd0 - ptur) / BOLK / new_temp[0];
|
||
if state.antc[0] <= 0.0 {
|
||
state.antc[0] = new_dens[0] / model.wmm[0] + new_elec[0];
|
||
}
|
||
|
||
// 向内积分
|
||
for id in 1..nd {
|
||
let ptur = HALF * model.vturb[id] * model.vturb[id] * new_dens[id];
|
||
let pturm = HALF * model.vturb[id - 1] * model.vturb[id - 1] * new_dens[id - 1];
|
||
state.antc[id] = (model.grav * (model.dm[id] - model.dm[id - 1])
|
||
+ BOLK * new_temp[id - 1] * state.antc[id - 1]
|
||
- model.pradt[id]
|
||
+ model.pradt[id - 1]
|
||
- ptur
|
||
+ pturm)
|
||
/ BOLK
|
||
/ new_temp[id];
|
||
}
|
||
|
||
// 更新密度
|
||
for id in 0..nd {
|
||
new_elec[id] = (UN - state.xe[id]) * state.antc[id];
|
||
new_dens[id] = model.wmm[id] * (state.antc[id] - new_elec[id]);
|
||
new_dens1[id] = UN / new_dens[id];
|
||
}
|
||
}
|
||
|
||
// 更新气体压力
|
||
for id in 0..nd {
|
||
new_pgs[id] = (new_dens[id] / model.wmm[id] + new_elec[id]) * BOLK * new_temp[id];
|
||
}
|
||
|
||
ilucy += 1;
|
||
|
||
// 单次迭代后返回(完整版本会循环)
|
||
return LucyOutput {
|
||
temp: new_temp,
|
||
elec: new_elec,
|
||
dens: new_dens,
|
||
dens1: new_dens1,
|
||
pgs: new_pgs,
|
||
ilucy,
|
||
lac2t,
|
||
dhhmx1,
|
||
};
|
||
}
|
||
|
||
// 不应该到达这里
|
||
LucyOutput {
|
||
temp: model.temp.to_vec(),
|
||
elec: model.elec.to_vec(),
|
||
dens: model.dens.to_vec(),
|
||
dens1: model.dens1.to_vec(),
|
||
pgs: model.pgs.to_vec(),
|
||
ilucy,
|
||
lac2t,
|
||
dhhmx1: 0.0,
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 辅助函数
|
||
// ============================================================================
|
||
|
||
/// 计算加速度系数。
|
||
fn compute_acceleration(state: &LucyState, nd: usize) -> (f64, f64, f64, f64, f64) {
|
||
let mut a1 = 0.0;
|
||
let mut b1 = 0.0;
|
||
let mut b2 = 0.0;
|
||
let mut c1 = 0.0;
|
||
let mut c2 = 0.0;
|
||
|
||
for id in 0..nd {
|
||
let mut wt = 0.0;
|
||
if state.tem0[id] != 0.0 {
|
||
wt = 1.0 / state.tem0[id].abs();
|
||
}
|
||
|
||
let d0 = state.tem0[id] - state.tem1[id];
|
||
let d1 = d0 - state.tem1[id] + state.tem2[id];
|
||
let d2 = d0 - state.tem2[id] + state.tem3[id];
|
||
|
||
a1 += wt * d1 * d1;
|
||
b1 += wt * d1 * d2;
|
||
b2 += wt * d2 * d2;
|
||
c1 += wt * d0 * d1;
|
||
c2 += wt * d0 * d2;
|
||
}
|
||
|
||
(a1, b1, b2, c1, c2)
|
||
}
|
||
|
||
// ============================================================================
|
||
// 数据结构体
|
||
// ============================================================================
|
||
|
||
/// 单个频率点的不透明度数据。
|
||
#[derive(Debug, Clone, Default)]
|
||
pub struct OpacflPointData {
|
||
/// 吸收系数 [nd]
|
||
pub abso1: Vec<f64>,
|
||
/// 谱线吸收系数 [nd]
|
||
pub abso1l: Vec<f64>,
|
||
/// 发射系数 [nd]
|
||
pub emis1l: Vec<f64>,
|
||
/// 散射系数 [nd]
|
||
pub scat1: Vec<f64>,
|
||
}
|
||
|
||
/// 单个频率点的辐射场数据。
|
||
#[derive(Debug, Clone, Default)]
|
||
pub struct Rad1PointData {
|
||
/// 辐射强度 [nd]
|
||
pub rad1: Vec<f64>,
|
||
/// Eddington 因子 [nd]
|
||
pub fak1: Vec<f64>,
|
||
}
|
||
|
||
// ============================================================================
|
||
// 测试
|
||
// ============================================================================
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
fn create_test_model(nd: usize, nfreq: usize) -> (LucyConfig, LucyModelParams<'static>) {
|
||
let config = LucyConfig {
|
||
itlucy: 1,
|
||
iaclt: 10,
|
||
iacldt: 1,
|
||
ihecor: 1,
|
||
lte: false,
|
||
lchc: false,
|
||
ielcor: 10,
|
||
iter: 0,
|
||
ntrl: 0,
|
||
iluctr: vec![0; MTRANS],
|
||
};
|
||
|
||
// 注意:这里需要使用静态生命周期,实际测试中需要用 Box::leak 或其他方式
|
||
let dm: &'static [f64] = Box::leak((0..nd).map(|i| 0.01 * (i + 1) as f64).collect::<Vec<_>>().into_boxed_slice());
|
||
let temp: &'static [f64] = Box::leak((0..nd).map(|i| 10000.0 - 100.0 * i as f64).collect::<Vec<_>>().into_boxed_slice());
|
||
let elec: &'static [f64] = Box::leak(vec![1e12; nd].into_boxed_slice());
|
||
let dens: &'static [f64] = Box::leak(vec![1e14; nd].into_boxed_slice());
|
||
let dens1: &'static [f64] = Box::leak(vec![1e-14; nd].into_boxed_slice());
|
||
let wmm: &'static [f64] = Box::leak(vec![1.0; nd].into_boxed_slice());
|
||
let deldm: &'static [f64] = Box::leak(vec![0.005; nd - 1].into_boxed_slice());
|
||
let vturb: &'static [f64] = Box::leak(vec![0.0; nd].into_boxed_slice());
|
||
let pradt: &'static [f64] = Box::leak(vec![0.0; nd].into_boxed_slice());
|
||
let pgs: &'static mut [f64] = Box::leak(vec![0.0; nd].into_boxed_slice());
|
||
let freq: &'static [f64] = Box::leak(vec![1e15; nfreq].into_boxed_slice());
|
||
let w: &'static [f64] = Box::leak(vec![1.0 / nfreq as f64; nfreq].into_boxed_slice());
|
||
let fh: &'static [f64] = Box::leak(vec![0.5; nfreq].into_boxed_slice());
|
||
let hextrd: &'static [f64] = Box::leak(vec![0.0; nfreq].into_boxed_slice());
|
||
|
||
let model = LucyModelParams {
|
||
nd,
|
||
nfreq,
|
||
ntrans: 10,
|
||
teff: 10000.0,
|
||
grav: 1e4, // 典型表面重力加速度
|
||
prd0: 0.0, // 参考辐射压
|
||
dm,
|
||
temp,
|
||
elec,
|
||
dens,
|
||
dens1,
|
||
wmm,
|
||
deldm,
|
||
vturb,
|
||
pradt,
|
||
pgs,
|
||
freq,
|
||
w,
|
||
fh,
|
||
hextrd,
|
||
lac2t: false,
|
||
};
|
||
|
||
(config, model)
|
||
}
|
||
|
||
#[test]
|
||
fn test_lucy_config_default() {
|
||
let config = LucyConfig::default();
|
||
assert_eq!(config.itlucy, 0);
|
||
assert_eq!(config.iaclt, 10);
|
||
assert_eq!(config.ihecor, 1);
|
||
assert!(!config.lte);
|
||
}
|
||
|
||
#[test]
|
||
fn test_lucy_state_creation() {
|
||
let state = LucyState::new(10);
|
||
assert_eq!(state.heat.len(), 10);
|
||
assert_eq!(state.tau.len(), 10);
|
||
assert_eq!(state.eddh, 0.0);
|
||
}
|
||
|
||
#[test]
|
||
fn test_lucy_zero_iterations() {
|
||
let (config, model) = create_test_model(5, 3);
|
||
let opacfl_data = vec![OpacflPointData::default(); 3];
|
||
let rad1_data = vec![Rad1PointData::default(); 3];
|
||
|
||
// 当 itlucy = 0 时,应该直接返回
|
||
let config_zero = LucyConfig {
|
||
itlucy: 0,
|
||
..config.clone()
|
||
};
|
||
|
||
let output = lucy_pure(&config_zero, &model, &opacfl_data, &rad1_data);
|
||
assert_eq!(output.ilucy, 0);
|
||
}
|
||
|
||
#[test]
|
||
fn test_compute_acceleration() {
|
||
let nd = 5;
|
||
let mut state = LucyState::new(nd);
|
||
|
||
// 设置一些测试值
|
||
for i in 0..nd {
|
||
state.tem0[i] = 10000.0 + i as f64;
|
||
state.tem1[i] = 9900.0 + i as f64;
|
||
state.tem2[i] = 9800.0 + i as f64;
|
||
state.tem3[i] = 9700.0 + i as f64;
|
||
}
|
||
|
||
let (a1, b1, b2, c1, c2) = compute_acceleration(&state, nd);
|
||
|
||
// 加速度系数应该是正数
|
||
assert!(a1 >= 0.0);
|
||
assert!(b2 >= 0.0);
|
||
}
|
||
}
|