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
+617
View File
@@ -0,0 +1,617 @@
//! 基于温度变化的新深度网格计算。
//!
//! 重构自 TLUSTY `newdmt.f`
//!
//! # 功能
//!
//! 计算新的 m-scale(柱质量密度),基于新网格更好地表示温度变化。
//! 使用 GRIDP 函数找到最优网格点分布。
use crate::math::gridp::gridp;
use crate::math::interp::interp;
use crate::state::constants::{HALF, MDEPTH, TWO, UN};
// ============================================================================
// 常量
// ============================================================================
/// ln(10) = 2.3025851...
const LN10: f64 = 2.30258509299404568402;
// ============================================================================
// 辅助参数结构体
// ============================================================================
/// PRSAUX COMMON 块参数
#[derive(Debug, Clone)]
pub struct NewdmtPrsAux {
/// 声速平方 [深度]
pub vsnd2: Vec<f64>,
/// 表面气压标高
pub hg1: f64,
/// 辐射气压标高
pub hr1: f64,
/// 辐射/气压标高比
pub rr1: f64,
}
impl Default for NewdmtPrsAux {
fn default() -> Self {
Self {
vsnd2: vec![0.0; MDEPTH],
hg1: 0.0,
hr1: 0.0,
rr1: 0.0,
}
}
}
/// FACTRS COMMON 块参数
#[derive(Debug, Clone)]
pub struct NewdmtFactrs {
/// 辐射因子 [深度]
pub gamj: Vec<f64>,
/// 几何稀释因子
pub gamh: f64,
pub fak0: f64,
}
impl Default for NewdmtFactrs {
fn default() -> Self {
Self {
gamj: vec![1.0; MDEPTH],
gamh: 1.0,
fak0: 0.0,
}
}
}
// ============================================================================
// 配置结构体
// ============================================================================
/// NEWDMT 配置参数。
#[derive(Debug, Clone)]
pub struct NewdmtConfig {
/// 深度点数
pub nd: usize,
/// 粘性深度参数
pub dmvisc: f64,
/// ZETA0 参数
pub zeta0: f64,
/// ZETA1 参数
pub zeta1: f64,
/// 粘性分数
pub fractv: f64,
/// 收敛迭代控制
pub nconit: i32,
/// 打印控制
pub ipring: i32,
}
impl Default for NewdmtConfig {
fn default() -> Self {
Self {
nd: 50,
dmvisc: 0.0,
zeta0: 0.0,
zeta1: 0.0,
fractv: 0.0,
nconit: 0,
ipring: 0,
}
}
}
// ============================================================================
// 模型状态结构体
// ============================================================================
/// NEWDMT 模型状态(可变)。
#[derive(Debug, Clone)]
pub struct NewdmtModelState {
/// 深度 (柱质量密度, g/cm²) [深度]
pub dm: Vec<f64>,
/// 温度 (K) [深度]
pub temp: Vec<f64>,
/// 电子密度 (cm⁻³) [深度]
pub elec: Vec<f64>,
/// 总粒子密度 (cm⁻³) [深度]
pub dens: Vec<f64>,
/// 深度变量 (cm) [深度]
pub zd: Vec<f64>,
/// Rosseland 光学深度 [深度]
pub tauros: Vec<f64>,
/// 热光学深度 [深度]
pub tauthe: Vec<f64>,
/// Theta 函数 [深度]
pub theta: Vec<f64>,
/// 粘性系数 [深度]
pub viscd: Vec<f64>,
/// Rosseland 平均不透明度 [深度]
pub abrosd: Vec<f64>,
/// Planck 平均不透明度 [深度]
pub abplad: Vec<f64>,
/// 平均分子量 [深度]
pub wmm: Vec<f64>,
/// 总压力 [深度]
pub ptotal: Vec<f64>,
}
impl Default for NewdmtModelState {
fn default() -> Self {
Self {
dm: vec![0.0; MDEPTH],
temp: vec![0.0; MDEPTH],
elec: vec![0.0; MDEPTH],
dens: vec![0.0; MDEPTH],
zd: vec![0.0; MDEPTH],
tauros: vec![0.0; MDEPTH],
tauthe: vec![0.0; MDEPTH],
theta: vec![0.0; MDEPTH],
viscd: vec![0.0; MDEPTH],
abrosd: vec![0.0; MDEPTH],
abplad: vec![0.0; MDEPTH],
wmm: vec![1.0; MDEPTH],
ptotal: vec![0.0; MDEPTH],
}
}
}
// ============================================================================
// 主计算函数
// ============================================================================
/// NEWDMT 纯计算函数(网格计算部分)。
///
/// 计算新的深度网格,基于温度变化使用 GRIDP。
///
/// # 参数
///
/// * `config` - 配置参数
/// * `state` - 模型状态(输入/输出)
/// * `prs_aux` - PRSAUX 参数(输出)
/// * `factrs` - FACTRS 参数(输出)
///
/// # 注意
///
/// 完整实现需要调用 TEMPER 和 HESOLV,这里只实现网格计算部分。
pub fn newdmt_pure(
config: &NewdmtConfig,
state: &mut NewdmtModelState,
prs_aux: &mut NewdmtPrsAux,
factrs: &mut NewdmtFactrs,
) {
let nd = config.nd;
let nd1 = nd - 1;
// 工作数组
let mut dm0 = vec![0.0; MDEPTH]; // log10(dm) 原始
let mut dm11 = vec![0.0; MDEPTH]; // 新 log10(dm)
let mut t0 = vec![0.0; MDEPTH]; // log10(tauros) 原始
let mut t1 = vec![0.0; MDEPTH]; // 新 log10(tauros)
let mut elec0 = vec![0.0; MDEPTH]; // 原始 elec
let mut dens0 = vec![0.0; MDEPTH]; // 原始 dens
let mut zd0 = vec![0.0; MDEPTH]; // 原始 zd
let mut pt0 = vec![0.0; MDEPTH]; // 原始 ptotal
let mut abrs0 = vec![0.0; MDEPTH]; // 原始 abrosd
let mut abpl0 = vec![0.0; MDEPTH]; // 原始 abplad
// ========================================================================
// Step 1: 保存原始值并计算 log10
// ========================================================================
for id in 0..nd {
dm0[id] = if state.dm[id] > 0.0 {
state.dm[id].log10()
} else {
-10.0
};
t0[id] = if state.tauros[id] > 0.0 {
state.tauros[id].log10()
} else {
-10.0
};
elec0[id] = state.elec[id];
dens0[id] = state.dens[id];
pt0[id] = state.ptotal[id];
zd0[id] = state.zd[id];
abrs0[id] = state.abrosd[id];
abpl0[id] = state.abplad[id];
}
// ========================================================================
// Step 2: 使用 GRIDP 计算新网格
// ========================================================================
// GRIDP 需要 nd1 个点(不包括最后一个点)
gridp(&dm0, &t0, &mut dm11, &mut t1, nd1);
// 最后一个点保持不变
dm11[nd - 1] = dm0[nd - 1];
t1[nd - 1] = t0[nd - 1];
// ========================================================================
// Step 3: 转换回线性尺度
// ========================================================================
for id in 0..nd {
state.dm[id] = (LN10 * dm11[id]).exp();
state.tauros[id] = (LN10 * t1[id]).exp();
}
// ========================================================================
// Step 4: 插值各种物理量到新网格
// ========================================================================
// 需要可变数组用于 INTERP(INTERP 会修改输入数组)
let mut dm0_mut = dm0.clone();
let mut elec0_mut = elec0.clone();
let mut dm11_mut = dm11.clone();
let mut elec_out = vec![0.0; MDEPTH];
interp(
&mut dm0_mut,
&mut elec0_mut,
&mut dm11_mut,
&mut elec_out,
nd,
nd,
2, // 线性插值
0, // 不对 x 取对数(已经是对数)
1, // 对 y 取对数
);
for id in 0..nd {
state.elec[id] = elec_out[id];
}
// INTERP(DM0, DENS0, DM11, DENS, ND, ND, 2, 0, 1)
let mut dm0_mut = dm0.clone();
let mut dens0_mut = dens0.clone();
let mut dm11_mut = dm11.clone();
let mut dens_out = vec![0.0; MDEPTH];
interp(
&mut dm0_mut,
&mut dens0_mut,
&mut dm11_mut,
&mut dens_out,
nd,
nd,
2,
0,
1,
);
for id in 0..nd {
state.dens[id] = dens_out[id];
}
// INTERP(DM0, PT0, DM11, PTOTAL, ND, ND, 2, 0, 1)
let mut dm0_mut = dm0.clone();
let mut pt0_mut = pt0.clone();
let mut dm11_mut = dm11.clone();
let mut ptotal_out = vec![0.0; MDEPTH];
interp(
&mut dm0_mut,
&mut pt0_mut,
&mut dm11_mut,
&mut ptotal_out,
nd,
nd,
2,
0,
1,
);
for id in 0..nd {
state.ptotal[id] = ptotal_out[id];
}
// INTERP(DM0, ZD0, DM11, ZD, ND, ND, 2, 0, 0)
let mut dm0_mut = dm0.clone();
let mut zd0_mut = zd0.clone();
let mut dm11_mut = dm11.clone();
let mut zd_out = vec![0.0; MDEPTH];
interp(
&mut dm0_mut,
&mut zd0_mut,
&mut dm11_mut,
&mut zd_out,
nd,
nd,
2,
0,
0, // 不对 y 取对数
);
for id in 0..nd {
state.zd[id] = zd_out[id];
}
// INTERP(DM0, ABRS0, DM11, ABROSD, ND, ND, 2, 0, 1)
let mut dm0_mut = dm0.clone();
let mut abrs0_mut = abrs0.clone();
let mut dm11_mut = dm11.clone();
let mut abrosd_out = vec![0.0; MDEPTH];
interp(
&mut dm0_mut,
&mut abrs0_mut,
&mut dm11_mut,
&mut abrosd_out,
nd,
nd,
2,
0,
1,
);
for id in 0..nd {
state.abrosd[id] = abrosd_out[id];
}
// INTERP(DM0, ABPL0, DM11, ABPLAD, ND, ND, 2, 0, 1)
let mut dm0_mut = dm0.clone();
let mut abpl0_mut = abpl0.clone();
let mut dm11_mut = dm11.clone();
let mut abplad_out = vec![0.0; MDEPTH];
interp(
&mut dm0_mut,
&mut abpl0_mut,
&mut dm11_mut,
&mut abplad_out,
nd,
nd,
2,
0,
1,
);
for id in 0..nd {
state.abplad[id] = abplad_out[id];
}
// ========================================================================
// Step 5: 计算声速平方 VSND2 = PTOTAL / DENS
// ========================================================================
for id in 0..nd {
if state.dens[id] > 0.0 {
prs_aux.vsnd2[id] = state.ptotal[id] / state.dens[id];
}
}
// ========================================================================
// Step 6: 计算新的 Rosseland 不透明度和 theta/tauthe 函数
// ========================================================================
let amuv0 = if config.dmvisc > 0.0 && state.dm[nd - 1] > 0.0 {
config.dmvisc.powf(config.zeta0 + UN)
} else {
0.0
};
let amuv1 = UN - amuv0;
for id in 0..nd {
// 计算 viscd 和 theta
if config.dmvisc > 0.0 && state.dm[nd - 1] > 0.0 {
let ratio = state.dm[id] / state.dm[nd - 1];
if ratio <= config.dmvisc {
// 内部区域:dm <= dmvisc * dm[nd-1]
state.viscd[id] = (UN - config.fractv) * (config.zeta1 + UN)
/ config.dmvisc.powf(config.zeta1 + UN)
* ratio.powf(config.zeta1);
state.theta[id] =
(UN - config.fractv) * (ratio / config.dmvisc).powf(config.zeta1 + UN);
} else {
// 外部区域:dm > dmvisc * dm[nd-1]
state.viscd[id] =
config.fractv * (config.zeta0 + UN) / amuv1 * ratio.powf(config.zeta0);
state.theta[id] = (UN - config.fractv)
+ config.fractv * (ratio.powf(config.zeta0 + UN) - amuv0) / amuv1;
}
} else {
// 当 dmvisc = 0 或 dm[nd-1] = 0 时,theta = 1.0(无粘性)
state.theta[id] = UN;
state.viscd[id] = 0.0;
}
factrs.gamj[id] = UN;
// 计算 tauros 和 tauthe
if id == 0 {
state.tauros[id] = state.dm[id] * state.abrosd[id];
state.tauthe[id] = state.tauros[id] * state.theta[id] / (config.zeta1 + TWO);
// 原始代码中计算 ANEREL,但不使用
} else {
let ddm = state.dm[id] - state.dm[id - 1];
state.tauros[id] =
state.tauros[id - 1] + ddm * HALF * (state.abrosd[id - 1] + state.abrosd[id]);
let zetad = if config.dmvisc > 0.0
&& state.dm[nd - 1] > 0.0
&& state.dm[id] <= config.dmvisc * state.dm[nd - 1]
{
config.zeta1
} else {
config.zeta0
};
let a0 = (state.abrosd[id - 1] * state.dm[id] - state.abrosd[id] * state.dm[id - 1])
/ ddm
/ (zetad + TWO);
let a1 =
(state.abrosd[id] - state.abrosd[id - 1]) / ddm / (zetad + 3.0);
state.tauthe[id] = state.tauthe[id - 1]
+ a0 * (state.theta[id] * state.dm[id] - state.theta[id - 1] * state.dm[id - 1])
+ a1 * (state.theta[id] * state.dm[id] * state.dm[id]
- state.theta[id - 1] * state.dm[id - 1] * state.dm[id - 1]);
}
// 注意:原始代码中这里调用 TEMPER
// CALL TEMPER(ID, TAUR, 1)
}
// ========================================================================
// Step 7: 调用 HESOLV(如果 nconit >= 0
// ========================================================================
// 注意:原始代码中这里调用 HESOLV
// if(nconit.ge.0) CALL HESOLV
// ========================================================================
// Step 8: 再次计算温度和平均不透明度
// ========================================================================
// 注意:原始代码中再次调用 TEMPER 和 HESOLV
// ========================================================================
// Step 9: 打印输出(如果 ipring >= 1
// ========================================================================
// 注意:原始代码中有 WRITE(6,...) 输出
}
// ============================================================================
// 测试
// ============================================================================
#[cfg(test)]
mod tests {
use super::*;
fn create_test_state(nd: usize) -> NewdmtModelState {
let mut state = NewdmtModelState::default();
// 创建简单的测试网格
for i in 0..nd {
let x = (i + 1) as f64 / nd as f64;
state.dm[i] = 1e-4 * x.powf(2.0);
state.dens[i] = 1e13 * (1.0 - 0.5 * x);
state.abrosd[i] = 1e-4 * (1.0 + x);
state.abplad[i] = 2e-4 * (1.0 + x);
state.tauros[i] = state.dm[i] * state.abrosd[i];
state.elec[i] = 1e10 * x;
state.ptotal[i] = 1e4 * (1.0 + x);
state.zd[i] = 1e8 * x;
state.wmm[i] = 1.0;
}
state
}
#[test]
fn test_newdmt_basic() {
let config = NewdmtConfig {
nd: 50,
dmvisc: 0.0,
zeta0: 0.0,
zeta1: 0.0,
fractv: 0.0,
nconit: 0,
ipring: 0,
};
let mut state = create_test_state(config.nd);
let mut prs_aux = NewdmtPrsAux::default();
let mut factrs = NewdmtFactrs::default();
newdmt_pure(&config, &mut state, &mut prs_aux, &mut factrs);
// 验证基本属性
assert!(state.dm[0] > 0.0, "dm[0] should be positive");
assert!(state.tauros[0] >= 0.0, "tauros[0] should be non-negative");
// 验证 dm 是单调递增的
for i in 1..config.nd {
assert!(
state.dm[i] >= state.dm[i - 1],
"dm should be monotonically increasing at i={}",
i
);
}
// 验证 theta = 1.0(无粘性时)
for i in 0..config.nd {
assert!(
(state.theta[i] - 1.0).abs() < 1e-10,
"theta[{}] should be 1.0 when dmvisc=0",
i
);
}
}
#[test]
fn test_newdmt_with_viscosity() {
let config = NewdmtConfig {
nd: 50,
dmvisc: 0.5,
zeta0: 0.5,
zeta1: 1.0,
fractv: 0.1,
nconit: 0,
ipring: 0,
};
let mut state = create_test_state(config.nd);
let mut prs_aux = NewdmtPrsAux::default();
let mut factrs = NewdmtFactrs::default();
newdmt_pure(&config, &mut state, &mut prs_aux, &mut factrs);
// 验证 theta 在有效范围内
for i in 0..config.nd {
assert!(
state.theta[i] >= 0.0,
"theta[{}] = {} is negative",
i,
state.theta[i]
);
assert!(
state.theta[i] <= 2.0,
"theta[{}] = {} is too large",
i,
state.theta[i]
);
}
// 验证 vsnd2 已计算
for i in 0..config.nd {
if state.dens[i] > 0.0 {
assert!(
prs_aux.vsnd2[i] > 0.0,
"vsnd2[{}] should be positive",
i
);
}
}
}
#[test]
fn test_newdmt_tauros_consistency() {
let config = NewdmtConfig {
nd: 50,
dmvisc: 0.0,
zeta0: 0.0,
zeta1: 0.0,
fractv: 0.0,
nconit: 0,
ipring: 0,
};
let mut state = create_test_state(config.nd);
let mut prs_aux = NewdmtPrsAux::default();
let mut factrs = NewdmtFactrs::default();
newdmt_pure(&config, &mut state, &mut prs_aux, &mut factrs);
// 验证 tauros 是单调递增的
for i in 1..config.nd {
assert!(
state.tauros[i] >= state.tauros[i - 1],
"tauros should be monotonically increasing at i={}",
i
);
}
// 验证 tauthe 是单调递增的
for i in 1..config.nd {
assert!(
state.tauthe[i] >= state.tauthe[i - 1],
"tauthe should be monotonically increasing at i={}",
i
);
}
}
}