包含 IO 和 math 模块的实现: - IO: initia, levcd, linset, ltegr, ltegrd, odfset, outpri, resolv, srtfrq, start, tabini, xenini - Math: accel2, alisk1, alisk2, alist1, alist2, concor, conout, conref, contmd, contmp, coolrt, greyd, inilam, linsel, lucy, lymlin, matcon, matgen, moleq, newdm, newdmt, odf1, opacf0, opacf1, opacfa, opacfd, opacfl, opactr, opadd, opahst, pgset, princ, prnt, pzeval, quasim, radpre, radtot, rates1, ratsp1, rdata, rdatax, rechck, rhoeos, rhonen, rhsgen, rossop, rtecf1, rtecmc, rtecmu, rtecom, rtefr1, rteint, russel, rybchn, rybene, rybheq, rybsol, sgmer1, sigave, sigk, solve, solves, state, steqeq, temcor, temper, topbas, trmder, trmdrt Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
730 lines
20 KiB
Rust
730 lines
20 KiB
Rust
//! 对流诊断输出模块。
|
|
//!
|
|
//! 重构自 TLUSTY `conout.f`
|
|
//!
|
|
//! # 功能
|
|
//!
|
|
//! 计算并输出温度梯度、对流通量及其导数的诊断信息:
|
|
//! - 计算各深度点的 DELTA (温度梯度参数)
|
|
//! - 调用 CONVEC 计算对流通量
|
|
//! - 确定对流区的起始和结束深度
|
|
//! - 根据 ICONV 参数调整 NDRE 和 REDIF/REINT 数组
|
|
|
|
use crate::state::constants::{HALF, SIG4P, UN};
|
|
|
|
// ============================================================================
|
|
// 配置结构体
|
|
// ============================================================================
|
|
|
|
/// CONOUT 配置参数。
|
|
#[derive(Debug, Clone)]
|
|
pub struct ConoutConfig {
|
|
/// 混合长度参数 (HMIX0)
|
|
pub hmix0: f64,
|
|
/// 对流模式 (ICONV)
|
|
/// - 2: 在对流区使用 REDIF=1
|
|
/// - 3: 在对流区使用 REDIF=1, REINT=0 (差分形式)
|
|
pub iconv: i32,
|
|
/// 盘模式标志 (IDISK)
|
|
pub idisk: i32,
|
|
/// 不透明度表标志 (IOPTAB)
|
|
pub ioptab: i32,
|
|
/// 对数梯度标志 (ILGDER)
|
|
/// - 0: 线性平均
|
|
/// - 1: 对数平均
|
|
pub ilgder: i32,
|
|
/// 表面重力加速度 (GRAV)
|
|
pub grav: f64,
|
|
/// 对流常数 A (ACONML)
|
|
pub aconml: f64,
|
|
/// 对流常数 B (BCONML)
|
|
pub bconml: f64,
|
|
/// 对流常数 C (CCONML)
|
|
pub cconml: f64,
|
|
}
|
|
|
|
impl Default for ConoutConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
hmix0: 1.0,
|
|
iconv: 0,
|
|
idisk: 0,
|
|
ioptab: 0,
|
|
ilgder: 0,
|
|
grav: 1e4,
|
|
aconml: 1.0,
|
|
bconml: 1.0,
|
|
cconml: 1.0,
|
|
}
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// 输入/输出结构体
|
|
// ============================================================================
|
|
|
|
/// CONOUT 输入参数。
|
|
pub struct ConoutParams<'a> {
|
|
/// 模式标志 (IMOD)
|
|
/// - 2: 计算平均不透明度
|
|
pub imod: i32,
|
|
/// 打印标志 (IPRIN)
|
|
/// - >0: 输出诊断信息
|
|
pub iprin: i32,
|
|
/// 深度点数 (ND)
|
|
pub nd: usize,
|
|
/// 有效温度 (TEFF)
|
|
pub teff: f64,
|
|
/// 配置
|
|
pub config: ConoutConfig,
|
|
// 深度相关数组 (nd)
|
|
/// 温度 (TEMP)
|
|
pub temp: &'a [f64],
|
|
/// 电子密度 (ELEC)
|
|
pub elec: &'a [f64],
|
|
/// 总粒子密度 (DENS)
|
|
pub dens: &'a [f64],
|
|
/// 分子质量 (WMM)
|
|
pub wmm: &'a [f64],
|
|
/// 深度 (柱质量密度, DM)
|
|
pub dm: &'a [f64],
|
|
/// 深度变量 (ZD) - 盘模式使用
|
|
pub zd: &'a [f64],
|
|
/// 总压力 (PTOTAL)
|
|
pub ptotal: &'a [f64],
|
|
/// 气压 (PGS)
|
|
pub pgs: &'a [f64],
|
|
/// 湍流速度 (VTURB)
|
|
pub vturb: &'a [f64],
|
|
/// Rosseland 不透明度/密度 (ABROSD)
|
|
pub abrosd: &'a mut [f64],
|
|
/// 辐射通量 (FLRD)
|
|
pub flrd: &'a [f64],
|
|
/// 对流通量 (FLXC) - 输出
|
|
pub flxc: &'a mut [f64],
|
|
/// Delta 温度梯度 (DELTA) - 输出
|
|
pub delta: &'a mut [f64],
|
|
/// 辐射等效积分 (REINT) - 输出
|
|
pub reint: &'a mut [f64],
|
|
/// 辐射等效差分 (REDIF) - 输出
|
|
pub redif: &'a mut [f64],
|
|
// 盘模式特定
|
|
/// 角速度参数 (THETAV)
|
|
pub thetav: &'a [f64],
|
|
/// 引力参数 (QGRAV)
|
|
pub qgrav: f64,
|
|
/// 辐射压 (PRADT) - 盘模式
|
|
pub pradt: &'a [f64],
|
|
}
|
|
|
|
/// 单深度点计算结果。
|
|
#[derive(Debug, Clone)]
|
|
pub struct DepthResult {
|
|
/// 深度索引 (1-based)
|
|
pub id: usize,
|
|
/// Rosseland 光学深度
|
|
pub tau: f64,
|
|
/// 温度
|
|
pub t: f64,
|
|
/// Delta 温度梯度
|
|
pub delta: f64,
|
|
/// 绝热梯度 (GRDADB)
|
|
pub grdadb: f64,
|
|
/// 对流/总通量比
|
|
pub conrel: f64,
|
|
/// 辐射/总通量比
|
|
pub radrel: f64,
|
|
}
|
|
|
|
/// CONOUT 输出结果。
|
|
#[derive(Debug, Clone)]
|
|
pub struct ConoutOutput {
|
|
/// 各深度点计算结果
|
|
pub depth_results: Vec<DepthResult>,
|
|
/// 对流区起始深度 (ICBEG, 1-based)
|
|
pub icbeg: usize,
|
|
/// 对流区结束深度 (ICEND, 1-based)
|
|
pub icend: usize,
|
|
/// 更新后的 NDRE
|
|
pub ndre: usize,
|
|
}
|
|
|
|
/// CUBCON 通用块数据 (对流计算中间量)。
|
|
#[derive(Debug, Clone, Default)]
|
|
pub struct CubconData {
|
|
pub a: f64,
|
|
pub b: f64,
|
|
pub del: f64,
|
|
pub grdadb: f64,
|
|
pub delmde: f64,
|
|
pub rho: f64,
|
|
pub flxtot: f64,
|
|
pub gravd: f64,
|
|
}
|
|
|
|
// ============================================================================
|
|
// 核心计算函数
|
|
// ============================================================================
|
|
|
|
/// 计算对流诊断信息 (CONOUT)。
|
|
///
|
|
/// # 参数
|
|
///
|
|
/// * `params` - 输入参数
|
|
///
|
|
/// # 返回值
|
|
///
|
|
/// 返回 `ConoutOutput`,包含各深度点的诊断信息和对流区范围。
|
|
///
|
|
/// # Fortran 原始代码
|
|
///
|
|
/// ```fortran
|
|
/// SUBROUTINE CONOUT(IMOD,IPRIN)
|
|
/// INCLUDE 'IMPLIC.FOR'
|
|
/// INCLUDE 'BASICS.FOR'
|
|
/// INCLUDE 'MODELQ.FOR'
|
|
/// INCLUDE 'ALIPAR.FOR'
|
|
/// COMMON/CUBCON/A,B,DEL,GRDADB,DELMDE,RHO,FLXTOT,GRAVD
|
|
/// ...
|
|
/// END
|
|
/// ```
|
|
pub fn conout_pure(params: &mut ConoutParams) -> ConoutOutput {
|
|
let nd = params.nd;
|
|
let mut depth_results = Vec::with_capacity(nd);
|
|
let mut icbeg: usize = 0;
|
|
let mut icend: usize = 0;
|
|
let mut ndre = 0;
|
|
|
|
// 计算总通量
|
|
let flxto0 = SIG4P * params.teff.powi(4);
|
|
|
|
// 初始化变量
|
|
let mut taum = 0.0;
|
|
let mut grdadb = 0.0;
|
|
|
|
// 遍历所有深度点
|
|
for id in 0..nd {
|
|
let t = params.temp[id];
|
|
let ptot = params.ptotal[id];
|
|
let pg = params.pgs[id];
|
|
|
|
// 计算辐射压
|
|
let mut prad = ptot - pg - HALF * params.dens[id] * params.vturb[id].powi(2);
|
|
if prad < 0.0 {
|
|
prad = 0.0;
|
|
}
|
|
|
|
// 计算总通量和引力
|
|
let mut flxtot = flxto0;
|
|
let mut gravd = 0.0;
|
|
if params.config.idisk == 1 {
|
|
flxtot = flxto0 * (UN - params.thetav[id]);
|
|
gravd = params.zd[id] * params.qgrav;
|
|
prad = params.pradt[id];
|
|
}
|
|
|
|
// 第一个深度点特殊处理
|
|
let (delta_val, flxcnv) = if id == 0 {
|
|
let tau = params.dm[0] * params.abrosd[0];
|
|
params.delta[0] = 0.0;
|
|
params.flxc[0] = 0.0;
|
|
taum = tau;
|
|
|
|
depth_results.push(DepthResult {
|
|
id: 1,
|
|
tau,
|
|
t,
|
|
delta: 0.0,
|
|
grdadb: 0.0,
|
|
conrel: 0.0,
|
|
radrel: if flxtot > 0.0 { params.flrd[0] / flxtot } else { 1.0 },
|
|
});
|
|
(0.0, 0.0)
|
|
} else {
|
|
// 计算光学深度和温度梯度
|
|
let tm = params.temp[id - 1];
|
|
let tau = taum + HALF * (params.dm[id] - params.dm[id - 1])
|
|
* (params.abrosd[id] + params.abrosd[id - 1]);
|
|
|
|
let ptotm = params.ptotal[id - 1];
|
|
let pgm = params.pgs[id - 1];
|
|
let mut pradm = ptotm - pgm - HALF * params.dens[id - 1] * params.vturb[id - 1].powi(2);
|
|
if params.config.idisk == 1 {
|
|
pradm = params.pradt[id - 1];
|
|
}
|
|
if pradm < 0.0 {
|
|
pradm = 0.0;
|
|
}
|
|
|
|
// 计算中间点值
|
|
let (t0, pt0, pg0, pr0, ab0, dlt) = if params.config.ilgder == 0 {
|
|
// 线性平均
|
|
let t0 = HALF * (t + tm);
|
|
let pt0 = HALF * (ptot + ptotm);
|
|
let pg0 = HALF * (pg + pgm);
|
|
let pr0 = HALF * (prad + pradm);
|
|
let ab0 = HALF * (params.abrosd[id] + params.abrosd[id - 1]);
|
|
let dlt = (t - tm) / (ptot - ptotm) * pt0 / t0;
|
|
(t0, pt0, pg0, pr0, ab0, dlt)
|
|
} else {
|
|
// 对数平均
|
|
let t0 = (t * tm).sqrt();
|
|
let pt0 = (ptot * ptotm).sqrt();
|
|
let pg0 = (pg * pgm).sqrt();
|
|
let pr0 = (prad * pradm).sqrt();
|
|
let ab0 = (params.abrosd[id] * params.abrosd[id - 1]).sqrt();
|
|
let dlt = if t > 0.0 && tm > 0.0 && ptot > 0.0 && ptotm > 0.0 {
|
|
(t / tm).ln() / (ptot / ptotm).ln()
|
|
} else {
|
|
0.0
|
|
};
|
|
(t0, pt0, pg0, pr0, ab0, dlt)
|
|
};
|
|
|
|
params.delta[id] = dlt;
|
|
|
|
// 计算对流通量
|
|
let mut flxcnv = 0.0;
|
|
let mut vcon = 0.0;
|
|
|
|
if params.config.idisk != 1 || id < nd - 1 {
|
|
// 调用简化对流计算
|
|
let convec_result = compute_convection(
|
|
id + 1, // 1-based
|
|
t0,
|
|
pt0,
|
|
pg0,
|
|
pr0,
|
|
ab0,
|
|
dlt,
|
|
¶ms.config,
|
|
flxtot,
|
|
gravd,
|
|
);
|
|
flxcnv = convec_result.0;
|
|
vcon = convec_result.1;
|
|
grdadb = convec_result.2;
|
|
}
|
|
|
|
if params.config.hmix0 > 0.0 {
|
|
params.flxc[id] = flxcnv;
|
|
}
|
|
|
|
// 检测对流区起始
|
|
if icbeg == 0
|
|
&& params.flxc[id] > 0.0
|
|
&& params.flxc[id - 1] == 0.0
|
|
&& id > 24
|
|
{
|
|
icbeg = id + 1; // 1-based
|
|
}
|
|
if icbeg > 0 && params.flxc[id] > 0.0 {
|
|
icend = id + 1; // 1-based
|
|
}
|
|
|
|
// 计算通量比
|
|
let (conrel, radrel) = if flxtot > 0.0 {
|
|
(flxcnv / flxtot, params.flrd[id] / flxtot)
|
|
} else {
|
|
(0.0, 1.0)
|
|
};
|
|
|
|
// 记录结果
|
|
depth_results.push(DepthResult {
|
|
id: id + 1,
|
|
tau,
|
|
t,
|
|
delta: dlt,
|
|
grdadb,
|
|
conrel,
|
|
radrel,
|
|
});
|
|
|
|
taum = tau;
|
|
(dlt, flxcnv)
|
|
};
|
|
}
|
|
|
|
// 根据 ICONV 调整 NDRE 和 REDIF/REINT
|
|
if icbeg > 3 {
|
|
if params.config.iconv == 3 {
|
|
ndre = icbeg - 1;
|
|
for id in 0..nd {
|
|
if id >= ndre - 1 {
|
|
params.reint[id] = 0.0;
|
|
params.redif[id] = 1.0;
|
|
} else {
|
|
params.reint[id] = 1.0;
|
|
params.redif[id] = 0.0;
|
|
}
|
|
}
|
|
} else if params.config.iconv == 2 {
|
|
ndre = icbeg - 1;
|
|
for id in 0..nd {
|
|
if id >= ndre - 1 {
|
|
params.redif[id] = 1.0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ConoutOutput {
|
|
depth_results,
|
|
icbeg,
|
|
icend,
|
|
ndre,
|
|
}
|
|
}
|
|
|
|
/// 简化的对流计算 (内部使用)。
|
|
///
|
|
/// 返回 (flxcnv, vcon, grdadb)
|
|
fn compute_convection(
|
|
_id: usize,
|
|
t0: f64,
|
|
pt0: f64,
|
|
pg0: f64,
|
|
pr0: f64,
|
|
ab0: f64,
|
|
dlt: f64,
|
|
config: &ConoutConfig,
|
|
flxtot: f64,
|
|
gravd: f64,
|
|
) -> (f64, f64, f64) {
|
|
// 如果对流被禁用
|
|
if config.hmix0 < 0.0 {
|
|
return (0.0, 0.0, 0.0);
|
|
}
|
|
|
|
// 绝热梯度近似 (单原子理想气体 = 0.4)
|
|
let grdadb = 0.4;
|
|
|
|
// 检查对流不稳定性
|
|
let ddel = dlt - grdadb;
|
|
if ddel < 0.0 {
|
|
return (0.0, 0.0, grdadb);
|
|
}
|
|
|
|
// 简化的对流计算
|
|
let grav = if config.idisk == 1 { gravd } else { config.grav };
|
|
if grav == 0.0 {
|
|
return (0.0, 0.0, grdadb);
|
|
}
|
|
|
|
// 粗略估计密度
|
|
let rho = if t0 > 0.0 {
|
|
pt0 / (t0 * 1.38e-16 * grav)
|
|
} else {
|
|
1e-7
|
|
};
|
|
|
|
// 混合长度
|
|
let hmix = if config.hmix0 == 0.0 { 1.0 } else { config.hmix0 };
|
|
|
|
// 压力标高
|
|
let hscale = pt0 / rho / grav;
|
|
|
|
// 简化的对流速度 (基于混合长度理论)
|
|
// vco ~ hmix * sqrt(aconml * pt0 / rho * dlrdlt)
|
|
// 这里简化处理,假设 dlrdlt ~ 1.0
|
|
let vco = hmix * (config.aconml * pt0 / rho).abs().sqrt();
|
|
|
|
// 简化的对流系数
|
|
// flco ~ bconml * rho * heatcp * t0 * hmix / 4pi
|
|
// 这里假设 heatcp ~ 1.0
|
|
let flco = config.bconml * rho * t0 * hmix / 12.5664;
|
|
|
|
// 光学厚度
|
|
let taue = hmix * ab0 * rho * hscale;
|
|
|
|
// 辐射耗散因子
|
|
let fac = taue / (UN + HALF * taue * taue);
|
|
|
|
// 参数 B (参考 Mihalas)
|
|
let b = 5.67e-5 * t0.powi(3) / (rho * vco) * fac * config.cconml * HALF;
|
|
|
|
// 参数 D
|
|
let d = b * b / 2.0;
|
|
let disc = d / 2.0 + ddel;
|
|
|
|
// 计算有效 DLT
|
|
let dlt_eff = if disc >= 0.0 {
|
|
let val = d + ddel - b * disc.sqrt();
|
|
if val < 0.0 { 0.0 } else { val }
|
|
} else {
|
|
0.0
|
|
};
|
|
|
|
// 对流速度和通量
|
|
let vconv = vco * dlt_eff.sqrt();
|
|
let flxcnv = flco * vconv * dlt_eff;
|
|
|
|
(flxcnv, vconv, grdadb)
|
|
}
|
|
|
|
// ============================================================================
|
|
// I/O 函数
|
|
// ============================================================================
|
|
|
|
/// 格式化输出诊断信息表头。
|
|
pub fn format_conout_header() -> String {
|
|
"\n\n ID TAUR TEMP DELTA DELTA(AD) CON/TOT RAD/TOT (C+R)/TOT\n\n".to_string()
|
|
}
|
|
|
|
/// 格式化单行输出。
|
|
pub fn format_depth_line(result: &DepthResult) -> String {
|
|
format!(
|
|
"{:4}{:9.2}{:9.1}{:10.2}{:10.2}{:10.2}{:10.2}{:10.2}\n",
|
|
result.id,
|
|
result.tau,
|
|
result.t,
|
|
result.delta,
|
|
result.grdadb,
|
|
result.conrel,
|
|
result.radrel,
|
|
result.conrel + result.radrel
|
|
)
|
|
}
|
|
|
|
/// 格式化对流区信息。
|
|
pub fn format_convective_zone(icbeg: usize, icend: usize) -> String {
|
|
format!(
|
|
"\n convective zone between depths (inclusive) {:4}{:4}\n",
|
|
icbeg, icend
|
|
)
|
|
}
|
|
|
|
/// 格式化 NDRE 重置信息。
|
|
pub fn format_ndre_reset(ndre: usize) -> String {
|
|
format!(
|
|
"\n\n NDRE IS RESET IN CONOUT DUE TO THE EXISTENCE OF CONVECTIVE ZONE\n NDRE= {:3}\n",
|
|
ndre
|
|
)
|
|
}
|
|
|
|
// ============================================================================
|
|
// 测试
|
|
// ============================================================================
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
/// 测试用的参数构建器
|
|
struct TestParamsBuilder {
|
|
nd: usize,
|
|
imod: i32,
|
|
iprin: i32,
|
|
teff: f64,
|
|
config: ConoutConfig,
|
|
}
|
|
|
|
impl TestParamsBuilder {
|
|
fn new(nd: usize) -> Self {
|
|
Self {
|
|
nd,
|
|
imod: 0,
|
|
iprin: 1,
|
|
teff: 35000.0,
|
|
config: ConoutConfig::default(),
|
|
}
|
|
}
|
|
|
|
fn config(mut self, config: ConoutConfig) -> Self {
|
|
self.config = config;
|
|
self
|
|
}
|
|
|
|
fn build(self) -> ConoutParams<'static> {
|
|
let nd = self.nd;
|
|
let mut temp = vec![0.0; nd];
|
|
let mut elec = vec![0.0; nd];
|
|
let mut dens = vec![0.0; nd];
|
|
let mut wmm = vec![0.0; nd];
|
|
let mut dm = vec![0.0; nd];
|
|
let mut zd = vec![0.0; nd];
|
|
let mut ptotal = vec![0.0; nd];
|
|
let mut pgs = vec![0.0; nd];
|
|
let mut vturb = vec![0.0; nd];
|
|
let mut abrosd = vec![0.0; nd];
|
|
let mut flrd = vec![0.0; nd];
|
|
let mut flxc = vec![0.0; nd];
|
|
let mut delta = vec![0.0; nd];
|
|
let mut reint = vec![0.0; nd];
|
|
let mut redif = vec![0.0; nd];
|
|
let mut thetav = vec![0.0; nd];
|
|
let mut pradt = vec![0.0; nd];
|
|
|
|
for i in 0..nd {
|
|
temp[i] = 10000.0 - i as f64 * 100.0;
|
|
elec[i] = 1e12;
|
|
dens[i] = 1e-7;
|
|
wmm[i] = 1.0;
|
|
dm[i] = 1e-2 * (i + 1) as f64;
|
|
zd[i] = 1e10 * (i + 1) as f64;
|
|
ptotal[i] = 1e5;
|
|
pgs[i] = 1e5;
|
|
vturb[i] = 0.0;
|
|
abrosd[i] = 0.1;
|
|
flrd[i] = 1e10;
|
|
flxc[i] = 0.0;
|
|
delta[i] = 0.0;
|
|
reint[i] = 1.0;
|
|
redif[i] = 0.0;
|
|
thetav[i] = 0.0;
|
|
pradt[i] = 0.0;
|
|
}
|
|
|
|
// 使用 Box::leak 来创建 'static 引用
|
|
ConoutParams {
|
|
imod: self.imod,
|
|
iprin: self.iprin,
|
|
nd,
|
|
teff: self.teff,
|
|
config: self.config,
|
|
temp: Box::leak(temp.into_boxed_slice()),
|
|
elec: Box::leak(elec.into_boxed_slice()),
|
|
dens: Box::leak(dens.into_boxed_slice()),
|
|
wmm: Box::leak(wmm.into_boxed_slice()),
|
|
dm: Box::leak(dm.into_boxed_slice()),
|
|
zd: Box::leak(zd.into_boxed_slice()),
|
|
ptotal: Box::leak(ptotal.into_boxed_slice()),
|
|
pgs: Box::leak(pgs.into_boxed_slice()),
|
|
vturb: Box::leak(vturb.into_boxed_slice()),
|
|
abrosd: Box::leak(abrosd.into_boxed_slice()),
|
|
flrd: Box::leak(flrd.into_boxed_slice()),
|
|
flxc: Box::leak(flxc.into_boxed_slice()),
|
|
delta: Box::leak(delta.into_boxed_slice()),
|
|
reint: Box::leak(reint.into_boxed_slice()),
|
|
redif: Box::leak(redif.into_boxed_slice()),
|
|
thetav: Box::leak(thetav.into_boxed_slice()),
|
|
qgrav: 1e-10,
|
|
pradt: Box::leak(pradt.into_boxed_slice()),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_conout_basic() {
|
|
let mut params = TestParamsBuilder::new(50).build();
|
|
let output = conout_pure(&mut params);
|
|
|
|
// 验证基本输出
|
|
assert_eq!(output.depth_results.len(), 50);
|
|
}
|
|
|
|
#[test]
|
|
fn test_format_output() {
|
|
let header = format_conout_header();
|
|
assert!(header.contains("TAUR"));
|
|
assert!(header.contains("TEMP"));
|
|
|
|
let result = DepthResult {
|
|
id: 1,
|
|
tau: 1e-4,
|
|
t: 10000.0,
|
|
delta: 0.3,
|
|
grdadb: 0.4,
|
|
conrel: 0.1,
|
|
radrel: 0.9,
|
|
};
|
|
let line = format_depth_line(&result);
|
|
assert!(line.contains("1"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_conout_no_convection() {
|
|
let config = ConoutConfig {
|
|
hmix0: -1.0, // 禁用对流
|
|
..Default::default()
|
|
};
|
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
|
let output = conout_pure(&mut params);
|
|
|
|
// 禁用对流时不应该有对流区
|
|
assert_eq!(output.icbeg, 0);
|
|
assert_eq!(output.icend, 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_conout_iconv_mode_2() {
|
|
let config = ConoutConfig {
|
|
iconv: 2,
|
|
hmix0: 1.0,
|
|
..Default::default()
|
|
};
|
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
|
let output = conout_pure(&mut params);
|
|
|
|
// 验证基本功能
|
|
assert_eq!(output.depth_results.len(), 50);
|
|
}
|
|
|
|
#[test]
|
|
fn test_conout_iconv_mode_3() {
|
|
let config = ConoutConfig {
|
|
iconv: 3,
|
|
hmix0: 1.0,
|
|
..Default::default()
|
|
};
|
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
|
let output = conout_pure(&mut params);
|
|
|
|
// 验证基本功能
|
|
assert_eq!(output.depth_results.len(), 50);
|
|
}
|
|
|
|
#[test]
|
|
fn test_conout_disk_mode() {
|
|
let config = ConoutConfig {
|
|
idisk: 1,
|
|
hmix0: 1.0,
|
|
..Default::default()
|
|
};
|
|
let mut params = TestParamsBuilder::new(50).config(config).build();
|
|
let output = conout_pure(&mut params);
|
|
|
|
// 盘模式应该正常工作
|
|
assert_eq!(output.depth_results.len(), 50);
|
|
}
|
|
|
|
#[test]
|
|
fn test_compute_convection_disabled() {
|
|
let config = ConoutConfig {
|
|
hmix0: -1.0,
|
|
..Default::default()
|
|
};
|
|
let (flxcnv, vconv, _) = compute_convection(
|
|
1, 10000.0, 1e5, 1e5, 0.0, 0.1, 0.3, &config, 1e10, 0.0
|
|
);
|
|
assert_eq!(flxcnv, 0.0);
|
|
assert_eq!(vconv, 0.0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_compute_convection_stable() {
|
|
let config = ConoutConfig::default();
|
|
let (flxcnv, vconv, grdadb) = compute_convection(
|
|
1, 10000.0, 1e5, 1e5, 0.0, 0.1, 0.1, &config, 1e10, 0.0
|
|
);
|
|
// dlt < grdadb (0.1 < 0.4),稳定,无对流
|
|
assert_eq!(flxcnv, 0.0);
|
|
assert_eq!(vconv, 0.0);
|
|
assert!((grdadb - 0.4).abs() < 1e-10);
|
|
}
|
|
|
|
#[test]
|
|
fn test_format_convective_zone() {
|
|
let msg = format_convective_zone(10, 40);
|
|
assert!(msg.contains("10"));
|
|
assert!(msg.contains("40"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_format_ndre_reset() {
|
|
let msg = format_ndre_reset(15);
|
|
assert!(msg.contains("15"));
|
|
assert!(msg.contains("NDRE"));
|
|
}
|
|
}
|