295 lines
7.7 KiB
Rust
295 lines
7.7 KiB
Rust
//! 迭代求解粒子密度和电子密度。
|
||
//!
|
||
//! 重构自 TLUSTY `rhonen.f`。
|
||
//!
|
||
//! 功能:
|
||
//! - 从给定的温度和质量密度迭代求解总粒子密度和电子密度
|
||
//! - 使用 eldens 计算电子密度
|
||
|
||
use crate::tlusty::math::{eldens_pure, EldensConfig, EldensOutput, EldensParams};
|
||
use crate::tlusty::state::constants::{HMASS, UN};
|
||
|
||
/// RHONEN 输入参数
|
||
pub struct RhonenParams<'a> {
|
||
/// 深度点索引 (1-indexed)
|
||
pub id: usize,
|
||
/// 温度 [K]
|
||
pub t: f64,
|
||
/// 质量密度 [g/cm³]
|
||
pub rho: f64,
|
||
/// 平均分子量数组
|
||
pub wmm: &'a [f64],
|
||
/// 初始电子相对密度 (0 表示自动估计)
|
||
pub anerel: f64,
|
||
/// eldens 配置
|
||
pub eldens_config: EldensConfig,
|
||
/// eldens 需要的额外参数
|
||
pub eldens_ytot: f64,
|
||
pub eldens_qref: f64,
|
||
pub eldens_dqnr: f64,
|
||
pub eldens_wmy: f64,
|
||
}
|
||
|
||
/// RHONEN 输出结果
|
||
#[derive(Debug, Clone)]
|
||
pub struct RhonenOutput {
|
||
/// 总粒子密度 [cm⁻³]
|
||
pub an: f64,
|
||
/// 电子密度 [cm⁻³]
|
||
pub ane: f64,
|
||
/// 更新后的电子相对密度
|
||
pub anerel: f64,
|
||
/// 更新后的平均分子量
|
||
pub wm: f64,
|
||
/// 内能 [erg]
|
||
pub enrgi: f64,
|
||
/// 熵 [erg/K]
|
||
pub entt: f64,
|
||
/// 迭代次数
|
||
pub iterations: i32,
|
||
/// 是否收敛
|
||
pub converged: bool,
|
||
}
|
||
|
||
/// 迭代求解 N 和 Ne。
|
||
///
|
||
/// # 参数
|
||
/// * `params` - 输入参数
|
||
///
|
||
/// # 返回值
|
||
/// 包含粒子密度、电子密度等的输出结构体
|
||
pub fn rhonen_pure(params: &RhonenParams) -> RhonenOutput {
|
||
let id = params.id;
|
||
let t = params.t;
|
||
let rho = params.rho;
|
||
|
||
// 初始化电子相对密度
|
||
let mut anerel = if id == 1 && params.anerel == 0.0 {
|
||
// 根据温度估计初始值
|
||
if t < 4000.0 {
|
||
1e-6
|
||
} else if t < 5000.0 {
|
||
1e-5
|
||
} else if t < 5500.0 {
|
||
1e-4
|
||
} else if t < 6000.0 {
|
||
1e-3
|
||
} else if t < 7000.0 {
|
||
0.01
|
||
} else if t < 8000.0 {
|
||
0.1
|
||
} else if t < 9000.0 {
|
||
0.4
|
||
} else {
|
||
0.5
|
||
}
|
||
} else {
|
||
params.anerel
|
||
};
|
||
|
||
// 计算初始平均分子量
|
||
// Fortran: wm = wmm(id) * (un - anerel) / hmass
|
||
// 注意:wmm 数组是 0-indexed
|
||
let wmm_id = params.wmm[id - 1];
|
||
let mut wm = wmm_id * (UN - anerel) / HMASS;
|
||
let mut wm0 = wm;
|
||
|
||
let mut an = 0.0;
|
||
let mut ane = 0.0;
|
||
let mut enrgi = 0.0;
|
||
let mut entt = 0.0;
|
||
let mut converged = false;
|
||
let max_iterations = 30;
|
||
|
||
for it in 1..=max_iterations {
|
||
// 计算粒子密度
|
||
// Fortran: an = rho / wm / hmass
|
||
an = rho / wm / HMASS;
|
||
|
||
// 保存旧值用于收敛检查
|
||
let ane0 = anerel * an;
|
||
wm0 = wm;
|
||
|
||
// 调用 eldens 计算电子密度
|
||
let eldens_params = EldensParams {
|
||
id,
|
||
t,
|
||
an,
|
||
ytot: params.eldens_ytot,
|
||
qref: params.eldens_qref,
|
||
dqnr: params.eldens_dqnr,
|
||
wmy: params.eldens_wmy,
|
||
config: params.eldens_config.clone(),
|
||
state_params: None,
|
||
molecule_data: None,
|
||
anato_data: None,
|
||
};
|
||
|
||
let eldens_output = eldens_pure(&eldens_params, 0);
|
||
|
||
ane = eldens_output.ane;
|
||
enrgi = eldens_output.energ;
|
||
entt = eldens_output.entt;
|
||
wm = eldens_output.wm;
|
||
|
||
// 更新电子相对密度
|
||
if an > 0.0 {
|
||
anerel = ane / an;
|
||
}
|
||
|
||
// 收敛检查
|
||
// Fortran: abs((ane-ane0)/ane0).lt.1.e-5 .and. abs((wm-wm0)/wm0).lt.1.e-5
|
||
let ane_converged = if ane0.abs() > 1e-30 {
|
||
((ane - ane0) / ane0).abs() < 1e-5
|
||
} else {
|
||
ane.abs() < 1e-30
|
||
};
|
||
|
||
let wm_converged = if wm0.abs() > 1e-30 {
|
||
((wm - wm0) / wm0).abs() < 1e-5
|
||
} else {
|
||
wm.abs() < 1e-30
|
||
};
|
||
|
||
if ane_converged && wm_converged {
|
||
converged = true;
|
||
return RhonenOutput {
|
||
an,
|
||
ane,
|
||
anerel,
|
||
wm,
|
||
enrgi,
|
||
entt,
|
||
iterations: it,
|
||
converged,
|
||
};
|
||
}
|
||
}
|
||
|
||
// 未收敛,返回最后一次迭代结果
|
||
RhonenOutput {
|
||
an,
|
||
ane,
|
||
anerel,
|
||
wm,
|
||
enrgi,
|
||
entt,
|
||
iterations: max_iterations,
|
||
converged,
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_rhonen_hot_star() {
|
||
// 高温恒星情况 (T > 9000 K)
|
||
let wmm = vec![1.4e-24; 10];
|
||
let config = EldensConfig::default();
|
||
|
||
let params = RhonenParams {
|
||
id: 1,
|
||
t: 15000.0, // 高温
|
||
rho: 1e-7, // 典型光球层密度
|
||
wmm: &wmm,
|
||
anerel: 0.0, // 自动估计
|
||
eldens_config: config,
|
||
eldens_ytot: 1.0,
|
||
eldens_qref: 1.0,
|
||
eldens_dqnr: 0.0,
|
||
eldens_wmy: 1.0,
|
||
};
|
||
|
||
let result = rhonen_pure(¶ms);
|
||
|
||
// 验证基本物理约束
|
||
assert!(result.an > 0.0, "粒子密度应为正");
|
||
assert!(result.ane > 0.0, "电子密度应为正");
|
||
assert!(result.anerel > 0.0 && result.anerel < 1.0, "电子相对密度应在 (0, 1) 范围内");
|
||
assert!(result.iterations > 0, "应至少迭代一次");
|
||
}
|
||
|
||
#[test]
|
||
fn test_rhonen_cool_star() {
|
||
// 低温恒星情况 (T < 6000 K)
|
||
let wmm = vec![1.4e-24; 10];
|
||
let config = EldensConfig::default();
|
||
|
||
let params = RhonenParams {
|
||
id: 1,
|
||
t: 5000.0, // 低温
|
||
rho: 1e-6, // 较高密度
|
||
wmm: &wmm,
|
||
anerel: 0.0, // 自动估计
|
||
eldens_config: config,
|
||
eldens_ytot: 1.0,
|
||
eldens_qref: 1.0,
|
||
eldens_dqnr: 0.0,
|
||
eldens_wmy: 1.0,
|
||
};
|
||
|
||
let result = rhonen_pure(¶ms);
|
||
|
||
// 验证基本物理约束
|
||
assert!(result.an > 0.0, "粒子密度应为正");
|
||
assert!(result.ane > 0.0, "电子密度应为正");
|
||
// 低温时电子相对密度应该较低
|
||
assert!(result.anerel < 0.1, "低温时电子相对密度应较低");
|
||
}
|
||
|
||
#[test]
|
||
fn test_rhonen_initial_anerel() {
|
||
// 测试提供初始 anerel 的情况
|
||
let wmm = vec![1.4e-24; 10];
|
||
let config = EldensConfig::default();
|
||
|
||
let params = RhonenParams {
|
||
id: 2, // 非 1,所以不会重新估计
|
||
t: 8000.0,
|
||
rho: 1e-7,
|
||
wmm: &wmm,
|
||
anerel: 0.3, // 提供初始值
|
||
eldens_config: config,
|
||
eldens_ytot: 1.0,
|
||
eldens_qref: 1.0,
|
||
eldens_dqnr: 0.0,
|
||
eldens_wmy: 1.0,
|
||
};
|
||
|
||
let result = rhonen_pure(¶ms);
|
||
|
||
assert!(result.an > 0.0);
|
||
assert!(result.ane > 0.0);
|
||
}
|
||
|
||
#[test]
|
||
fn test_anerel_temperature_estimate() {
|
||
// 测试温度对初始 anerel 估计的影响
|
||
let wmm = vec![1.4e-24; 10];
|
||
let config = EldensConfig::default();
|
||
|
||
// 测试不同温度
|
||
let temps = [3500.0, 4500.0, 5500.0, 6500.0, 7500.0, 8500.0, 10000.0];
|
||
|
||
for &t in &temps {
|
||
let params = RhonenParams {
|
||
id: 1,
|
||
t,
|
||
rho: 1e-7,
|
||
wmm: &wmm,
|
||
anerel: 0.0,
|
||
eldens_config: config.clone(),
|
||
eldens_ytot: 1.0,
|
||
eldens_qref: 1.0,
|
||
eldens_dqnr: 0.0,
|
||
eldens_wmy: 1.0,
|
||
};
|
||
|
||
let result = rhonen_pure(¶ms);
|
||
assert!(result.ane >= 0.0, "温度 {} K 时电子密度应为非负", t);
|
||
}
|
||
}
|
||
}
|