SpectraRust/src/tlusty/math/radiative/trmdrt.rs
2026-03-25 18:34:41 +08:00

335 lines
8.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 热力学导数计算。
//!
//! 重构自 TLUSTY `TRMDRT` 子程序。
//!
//! # 功能
//!
//! 基于状态方程和熵表计算热力学导数:
//! - 定压比热 (HEATCP)
//! - 密度对数导数 d(ln rho)/d(ln T) (DLRDLT)
//! - 绝热梯度 d(ln T)/d(ln P)_ad (GRDADB)
//!
//! # 算法
//!
//! 使用数值微分方法计算热力学导数:
//! 1. 调用 RHOEOS 计算密度
//! 2. 在密度和温度点附近做数值微分
//! 3. 计算各种热力学量
use crate::tlusty::math::{prsent, rhoeos, PrsentParams, RhoeosParams, ThermTables};
/// 气体常数 R (erg/K/mol)
const RCON: f64 = 8.31434e7;
// ============================================================================
// 输入/输出结构体
// ============================================================================
/// TRMDRT 输入参数。
pub struct TrmdrtParams<'a> {
/// 深度索引 (1-based)
pub id: usize,
/// 温度 (K)
pub t: f64,
/// 气压 (cgs)
pub p: f64,
/// 热力学表引用
pub tables: &'a ThermTables,
}
/// TRMDRT 输出结果。
#[derive(Debug, Clone)]
pub struct TrmdrtOutput {
/// 定压比热
pub heatcp: f64,
/// d(ln rho)/d(ln T)
pub dlrdlt: f64,
/// 绝热梯度 d(ln T)/d(ln P)_ad
pub grdadb: f64,
/// 密度 (g/cm³)
pub rho: f64,
/// 熵 (参考值)
pub entropy: f64,
/// 定容比热
pub heatcv: f64,
/// 比热比 gamma = Cp/Cv
pub gamma: f64,
/// 热力学导数项
pub dpdr: f64,
pub dpdt: f64,
pub dsdr: f64,
pub dsdt: f64,
}
// ============================================================================
// 核心计算函数
// ============================================================================
/// 计算热力学导数。
///
/// # 参数
///
/// * `params` - 输入参数,包含深度索引、温度、气压和热力学表
///
/// # 返回值
///
/// 返回 `TrmdrtOutput`,包含各种热力学量。
///
/// # 示例
///
/// ```ignore
/// use tlusty::math::{trmdrt, TrmdrtParams, ThermTables};
///
/// let tables = ThermTables::default();
/// let params = TrmdrtParams { id: 1, t: 10000.0, p: 1e4, tables: &tables };
/// let result = trmdrt(&params);
/// println!("绝热梯度: {}", result.grdadb);
/// ```
pub fn trmdrt(params: &TrmdrtParams) -> TrmdrtOutput {
let id = params.id;
let t = params.t;
let p = params.p;
let tables = params.tables;
// 1. 计算密度
let rhoeos_params = RhoeosParams { t, p, tables };
let rho = rhoeos(&rhoeos_params).rho;
// 2. 数值微分的步长
let drho = 0.01 * rho;
let dt = 0.01 * t;
// 3. 在多个点计算压力和熵
// 中心点
let prsent_params = PrsentParams { r: rho, t, tables };
let result0 = prsent(&prsent_params);
let p0 = result0.fp;
let s0 = result0.fs;
let jon = result0.jon;
// 密度 +drho
let prsent_params = PrsentParams {
r: rho + drho,
t,
tables,
};
let result1 = prsent(&prsent_params);
let p1 = result1.fp;
let s1 = result1.fs;
// 密度 -drho
let prsent_params = PrsentParams {
r: rho - drho,
t,
tables,
};
let result2 = prsent(&prsent_params);
let p2 = result2.fp;
let s2 = result2.fs;
// 温度 +dt
let prsent_params = PrsentParams {
r: rho,
t: t + dt,
tables,
};
let result3 = prsent(&prsent_params);
let p3 = result3.fp;
let s3 = result3.fs;
// 温度 -dt
let prsent_params = PrsentParams {
r: rho,
t: t - dt,
tables,
};
let result4 = prsent(&prsent_params);
let p4 = result4.fp;
let s4 = result4.fs;
// 4. 计算数值微分
let dpdr = (p1 - p2) / (2.0 * drho);
let dpdt = (p3 - p4) / (2.0 * dt);
let dsdr = (s1 - s2) / (2.0 * drho) * RCON;
let dsdt = (s3 - s4) / (2.0 * dt) * RCON;
// 5. 计算热力学量
let (heatcp, dlrdlt, grdadb, heatcv, gamma);
if jon == 0 {
// 正常情况(在表内)
heatcv = t * dsdt;
let den = dpdr * dsdt - dpdt * dsdr;
heatcp = t * den / dpdr;
// d(ln rho)/d(ln T) = -rho * dpdr / (t * dpdt)
// Fortran: DLRDLT = -RHO*DPDR/(T*DPDT); DLRDLT = 1.D0/DLRDLT
let dlrdlt_raw = -rho * dpdr / (t * dpdt);
dlrdlt = 1.0 / dlrdlt_raw;
// 绝热梯度
// Fortran: GRDADB = -P/(HEATCP*RHO*T)*DLRDLT
grdadb = -p / (heatcp * rho * t) * dlrdlt;
// Gamma
let dq = dsdt * p / (den * rho);
gamma = 1.0 / dq;
} else {
// 在表外,使用理想气体近似
// Fortran 使用 cvedge(JON), cpedge(JON), gammaedge(JON)
// 简化处理:使用理想气体值
heatcv = 1.5 * RCON; // 单原子理想气体 Cv
heatcp = 2.5 * RCON; // 单原子理想气体 Cp
dlrdlt = -1.0;
grdadb = -p / (heatcp * rho * t) * dlrdlt;
gamma = tables.gammaedge.get(jon).copied().unwrap_or(5.0 / 3.0);
}
// 6. 重新计算绝热梯度Fortran 最后覆盖)
// grdadb = p/t*(dsdr/(dsdr*dpdt-dsdt*dpdr))
let den2 = dsdr * dpdt - dsdt * dpdr;
let grdadb_final = if den2.abs() > 1e-30 {
p / t * (dsdr / den2)
} else {
grdadb
};
TrmdrtOutput {
heatcp,
dlrdlt,
grdadb: grdadb_final,
rho,
entropy: s0,
heatcv,
gamma,
dpdr,
dpdt,
dsdr,
dsdt,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_tables() -> ThermTables {
let mut tables = ThermTables::default();
// 填充一些合理的测试数据
// 这些数据模拟真实的热力学表行为
for i in 0..330 {
for j in 0..100 {
// 熵和压力使用对数空间的值
tables.sl[i][j] = 10.0 + 0.01 * i as f64 + 0.1 * j as f64;
tables.pl[i][j] = 5.0 + 0.01 * i as f64 + 0.05 * j as f64;
}
}
// 边缘数据必须设置合理值(用于表外插值)
for j in 0..100 {
tables.pedge[j] = 1.0e5;
tables.sedge[j] = 1.0e8;
tables.tedge[j] = 1.0e4;
tables.gammaedge[j] = 1.6667;
}
tables.redge = 1.0e-5;
tables
}
#[test]
fn test_trmdrt_basic() {
let tables = create_test_tables();
let params = TrmdrtParams {
id: 1,
t: 10000.0,
p: 1e4,
tables: &tables,
};
let result = trmdrt(&params);
// 验证基本属性
assert!(result.rho > 0.0, "密度应该为正");
assert!(result.heatcp > 0.0, "定压比热应该为正");
assert!(result.gamma > 1.0, "gamma 应该大于 1");
}
#[test]
fn test_trmdrt_higher_temperature() {
let tables = create_test_tables();
let params1 = TrmdrtParams {
id: 1,
t: 10000.0,
p: 1e4,
tables: &tables,
};
let result1 = trmdrt(&params1);
let params2 = TrmdrtParams {
id: 2,
t: 20000.0,
p: 1e4,
tables: &tables,
};
let result2 = trmdrt(&params2);
// 更高温度下密度应该更低
assert!(
result2.rho < result1.rho,
"更高温度下密度应该更低: {} vs {}",
result2.rho,
result1.rho
);
}
#[test]
fn test_trmdrt_different_pressure() {
let tables = create_test_tables();
let params1 = TrmdrtParams {
id: 1,
t: 10000.0,
p: 1e4,
tables: &tables,
};
let result1 = trmdrt(&params1);
let params2 = TrmdrtParams {
id: 2,
t: 10000.0,
p: 1e5,
tables: &tables,
};
let result2 = trmdrt(&params2);
// 验证两个结果都是有效的正数
assert!(result1.rho > 0.0 && result1.rho.is_finite(), "密度1应该是有效的正数");
assert!(result2.rho > 0.0 && result2.rho.is_finite(), "密度2应该是有效的正数");
// 注意:由于测试表数据不是真实的热力学数据,
// 物理关系可能不正确,所以我们只验证结果有限且为正
}
#[test]
fn test_trmdrt_derivatives() {
let tables = create_test_tables();
let params = TrmdrtParams {
id: 1,
t: 10000.0,
p: 1e4,
tables: &tables,
};
let result = trmdrt(&params);
// 检查导数值是有限的
assert!(result.dpdr.is_finite(), "dpdr 应该是有限的");
assert!(result.dpdt.is_finite(), "dpdt 应该是有限的");
assert!(result.dsdr.is_finite(), "dsdr 应该是有限的");
assert!(result.dsdt.is_finite(), "dsdt 应该是有限的");
}
}