feat: 添加更多重构模块 (第7批)

包含 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>
This commit is contained in:
fmq
2026-03-25 01:46:29 +08:00
co-authored by Claude Opus 4.6
parent 21cb6af16c
commit a086e313cb
85 changed files with 48464 additions and 6 deletions
+729
View File
@@ -0,0 +1,729 @@
//! 对流诊断输出模块。
//!
//! 重构自 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,
&params.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"));
}
}