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

526 lines
16 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 `inpdis.f`
//!
//! 处理盘模型的输入参数,包括:
//! - 恒星质量、半径、吸积率
//! - 广义相对论修正
//! - 总柱质量计算
//! - 有效温度和引力参数设置
use crate::tlusty::io::{FortranWriter, Result};
use crate::tlusty::math::{column, ColumnParams};
use crate::tlusty::math::grcor;
use crate::tlusty::math::sigmar;
use crate::tlusty::state::constants::{SIG4P, TWO};
// 物理常数
const VELC: f64 = 2.997925e10; // 光速 (cm/s)
const PI4: f64 = 12.5663706; // 4π
const GRCON: f64 = 6.668e-8; // 引力常数
const XMSUN: f64 = 1.989e33; // 太阳质量 (g)
const XMDSUN: f64 = 6.3029e25; // 太阳质量/年 转 g/s
const RSUN: f64 = 6.9598e10; // 太阳半径 (cm)
/// INPDIS 输入参数。
pub struct InpDisParams {
/// 恒星质量 (太阳质量或克,负值启用 GR 修正)
pub xmstar: f64,
/// 吸积率 (太阳质量/年或 g/s)
pub xmdot: f64,
/// 恒星半径 (太阳半径或 cm)
pub rstar: f64,
/// R/R(star) 相对距离
pub reldst: f64,
/// Alpha 粘滞参数
pub alphav: f64,
/// Zeta0 参数
pub zeta0: f64,
/// Zeta1 参数
pub zeta1: f64,
/// 分数参数
pub fractv: f64,
/// 粘性参数
pub dmvisc: f64,
/// 雷诺数
pub reynum: f64,
}
/// INPDIS 输出结果。
#[derive(Debug, Clone)]
pub struct InpDisResult {
/// 有效温度 (K)
pub teff: f64,
/// 引力参数 (cm/s²)
pub qgrav: f64,
/// 总柱质量 (g/cm²)
pub dmtot: f64,
/// 盘辐射参数
pub edisc: f64,
/// 平均分子量
pub wbarm: f64,
/// 雷诺数 (计算后)
pub reynum: f64,
/// Alpha 参数 (计算后)
pub alpav: f64,
/// 粘性系数
pub visc: f64,
/// T 修正因子
pub tcor: f64,
/// Q 修正因子
pub qcor: f64,
/// 相对论因子 A
pub arh: f64,
/// 相对论因子 B
pub brh: f64,
/// 相对论因子 C
pub crh: f64,
/// 相对论因子 D
pub drh: f64,
/// 角速度
pub omeg32: f64,
/// 最大频率
pub frlmax: f64,
}
impl Default for InpDisParams {
fn default() -> Self {
Self {
xmstar: 0.0,
xmdot: 0.0,
rstar: 0.0,
reldst: 0.0,
alphav: 0.0,
zeta0: 0.0,
zeta1: 0.0,
fractv: 0.0,
dmvisc: 0.0,
reynum: 0.0,
}
}
}
/// 盘模型输入处理(纯计算版本)。
///
/// 根据输入参数计算盘模型的物理量,包括:
/// - 单位转换(太阳质量/半径等)
/// - 广义相对论修正
/// - 总柱质量计算
/// - 有效温度和引力参数
///
/// # 参数
/// * `params` - 输入参数
/// * `cnu1` - 频率参数(用于计算最大频率)
///
/// # 返回值
/// 返回计算结果 InpDisResult
pub fn inpdis_pure(params: &mut InpDisParams, cnu1: f64) -> InpDisResult {
let un = 1.0;
// 处理 fractv 和 dmvisc
if params.fractv < 0.0 {
let amuv0 = params.dmvisc.powf(params.zeta0 + un);
params.fractv =
un / (un + (params.zeta0 + un) / (params.zeta1 + un) * amuv0 / (un - amuv0));
}
if params.dmvisc < 0.0 {
params.dmvisc = (un
/ (un
+ (params.zeta0 + un) / (params.zeta1 + un) * params.fractv / (un - params.fractv)))
.powf(un / (params.zeta0 + un));
}
let alpha0 = params.alphav;
// 主计算分支
if params.xmstar != 0.0 {
// 处理恒星参数
let (aa, rstar, xmstar) = if params.xmstar < 0.0 {
// GR 修正模式
let aa = params.rstar; // 角动量
let rstar = -params.xmstar * XMSUN * GRCON / VELC / VELC;
(aa, rstar, params.xmstar)
} else {
(0.0, params.rstar, params.xmstar)
};
// 单位转换
let xmstar_abs = xmstar.abs();
let xmstar_conv = if xmstar_abs > 1e16 {
xmstar / XMSUN
} else {
xmstar
};
let xmdot_conv = if params.xmdot > 1e3 {
params.xmdot / XMDSUN
} else {
params.xmdot
};
let rstar_conv = if params.rstar > 1e3 {
params.rstar / RSUN
} else {
params.rstar
};
let r = rstar_conv * params.reldst.abs();
let qgrav = 5.9 * GRCON * xmstar_conv.abs() / r.powi(3);
let omeg32 = qgrav.sqrt() * 1.5;
// 广义相对论修正
let mut rr0 = params.reldst;
let (qcor, tcor, arh, brh, crh, drh) = grcor(aa, rr0, xmstar);
// 计算有效温度
let teff0 = (0.179049311 * qgrav * 3.34379e24 * xmdot_conv / SIG4P).powf(0.25);
let teff = teff0 * tcor;
let qgrav_corr = qgrav * qcor;
let omeg32_corr = omeg32 * arh / brh;
rr0 = rr0.abs();
let xmas9 = xmstar_conv.abs() * 1e-9;
let xmdt = xmdot_conv / xmas9 / 2.22;
let xmd = xmdot_conv * XMDSUN;
let alpav = params.alphav.abs();
// 计算总柱质量
let dmtot = if alpav <= 0.0 {
// 旧方法
let chih = 0.39;
let reynum = if params.reynum <= 0.0 {
(rr0 / xmdt).powi(2) / alpav * arh * crh / drh / drh
} else {
params.reynum
};
let alpav_calc = if params.reynum <= 0.0 {
alpav
} else {
(rr0 / xmdt).powi(2) / reynum * arh * crh / drh / drh
};
let visc = 1.176565e22 * (GRCON * xmstar_conv.abs() * r).sqrt() / reynum;
let dmtot_calc =
3.34379e24 * xmdot_conv / visc * brh * drh / arh / arh;
if alpav_calc < 0.0 {
// 使用 SIGMAR 计算
let re = rr0.abs();
let omega = VELC / rstar_conv / 6.9698e10 / re.powf(1.5);
let relt = drh / arh;
let relr = drh / brh;
let rl2 = re * (1.0 - 2.0 * aa / re.powf(1.5) + aa * aa / re / re).powi(2) / brh;
let einf = (1.0 - 2.0 / re + aa / re.powf(1.5)) / brh.sqrt();
let relz = (rl2 - aa * aa * (einf - 1.0)) / re;
0.5 * sigmar(alpav, xmd, teff, omega, relr, relt, relz)
} else {
dmtot_calc
}
} else {
// 新方法 - 使用 COLUMN
let column_params = ColumnParams {
alphav: params.alphav,
reldst: params.reldst,
rstar: rstar_conv,
xmstar: xmstar_conv.abs(),
teff,
xmdot: xmdot_conv,
qgrav: qgrav_corr,
fractv: params.fractv,
arh,
brh,
drh,
};
let result = column(&column_params);
result.dmtot
};
// 计算其他输出量
let edisc = SIG4P * teff.powi(4) / dmtot;
let wbarm = xmdot_conv * XMDSUN / 6.0 / std::f64::consts::PI * brh * drh / arh / arh;
let reynum_out = dmtot / wbarm * (xmstar_conv * r).sqrt() * 3.03818e18;
// 计算最大频率
let frlmax = if cnu1 > 0.0 {
1e11 * cnu1 * teff
} else {
0.0
};
InpDisResult {
teff,
qgrav: qgrav_corr,
dmtot,
edisc,
wbarm,
reynum: reynum_out,
alpav: alpav,
visc: 0.0, // 在 I/O 版本中计算
tcor,
qcor,
arh,
brh,
crh,
drh,
omeg32: omeg32_corr,
frlmax,
}
} else {
// 直接使用 TEFF, QGRAV, DMTOT 模式
let teff = params.xmdot;
let qgrav = params.rstar;
let dmtot = params.reldst;
let edisc = SIG4P * teff.powi(4) / dmtot;
let omeg32 = qgrav.sqrt() * 1.5;
let frlmax = if cnu1 > 0.0 {
1e11 * cnu1 * teff
} else {
0.0
};
InpDisResult {
teff,
qgrav,
dmtot,
edisc,
wbarm: 0.0,
reynum: 0.0,
alpav: params.alphav,
visc: 0.0,
tcor: 1.0,
qcor: 1.0,
arh: 1.0,
brh: 1.0,
crh: 1.0,
drh: 1.0,
omeg32,
frlmax,
}
}
}
/// 盘模型输入处理(带 I/O 版本)。
///
/// # 参数
/// * `params` - 输入参数
/// * `cnu1` - 频率参数
/// * `writer` - 输出写入器
///
/// # 返回值
/// 返回计算结果
pub fn inpdis_io<W: std::io::Write>(
params: &mut InpDisParams,
cnu1: f64,
writer: &mut FortranWriter<W>,
) -> Result<InpDisResult> {
// 写入标题
writer.write_newline()?;
writer.write_raw("***************************************")?;
writer.write_newline()?;
writer.write_newline()?;
writer.write_raw("M O D E L O F A D I S K R I N G")?;
writer.write_newline()?;
writer.write_newline()?;
writer.write_raw(" ***************************************")?;
writer.write_newline()?;
writer.write_newline()?;
// 写入输入参数
writer.write_raw(&format!("M(STAR) ={:#12.3}", params.xmstar))?;
writer.write_newline()?;
writer.write_raw(&format!("M(DOT) ={:#12.3}", params.xmdot))?;
writer.write_newline()?;
writer.write_raw(&format!("R(STAR) ={:#12.3}", params.rstar))?;
writer.write_newline()?;
writer.write_raw(&format!("R/R(STAR) ={:#12.3}", params.reldst))?;
writer.write_newline()?;
writer.write_raw(&format!("ALPHA ={:#12.3}", params.alphav))?;
writer.write_newline()?;
writer.write_newline()?;
// 调用纯计算函数
let result = inpdis_pure(params, cnu1);
// 写入输出结果
writer.write_raw(&format!("TEFF ={:#10.0}", result.teff))?;
writer.write_newline()?;
writer.write_raw(&format!("QGRAV ={:#12.3}", result.qgrav))?;
writer.write_newline()?;
writer.write_raw(&format!("DMTOT ={:#12.3}", result.dmtot))?;
writer.write_newline()?;
writer.write_raw(&format!("ZETA0 ={:#12.3}", params.zeta0))?;
writer.write_newline()?;
writer.write_raw(&format!("ZETA1 ={:#12.3}", params.zeta1))?;
writer.write_newline()?;
writer.write_raw(&format!("FRACTV ={:#12.3}", params.fractv))?;
writer.write_newline()?;
writer.write_raw(&format!("DMVISC ={:#12.3}", params.dmvisc))?;
writer.write_newline()?;
writer.write_raw(&format!("TSTAR ={:#10.0}", result.teff))?;
writer.write_newline()?;
writer.write_newline()?;
// 写入 GR 修正参数
writer.write_raw(&format!("tcor ={:#12.3}", result.tcor))?;
writer.write_newline()?;
writer.write_raw(&format!("qcor ={:#12.3}", result.qcor))?;
writer.write_newline()?;
writer.write_raw(&format!("A(RH) ={:#12.3}", result.arh))?;
writer.write_newline()?;
writer.write_raw(&format!("B(RB) ={:#12.3}", result.brh))?;
writer.write_newline()?;
writer.write_raw(&format!("C(RH) ={:#12.3}", result.crh))?;
writer.write_newline()?;
writer.write_raw(&format!("D(RH) ={:#12.3}", result.drh))?;
writer.write_newline()?;
writer.write_raw(&format!("Re ={:#12.3}", result.reynum))?;
writer.write_newline()?;
writer.write_raw(&format!("alpha ={:#12.3}", result.alpav))?;
writer.write_newline()?;
writer.write_newline()?;
writer.write_raw(&format!("DMTOT ={:#12.3}", result.dmtot))?;
writer.write_newline()?;
writer.write_raw(&format!("EDISC ={:#12.3}", result.edisc))?;
writer.write_newline()?;
writer.write_raw(&format!("WBARM ={:#12.3}", result.wbarm))?;
writer.write_newline()?;
writer.write_newline()?;
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_inpdis_classical() {
// 测试经典(无 GR 修正)情况
let mut params = InpDisParams {
xmstar: 10.0, // 10 太阳质量
xmdot: 1e-8, // 1e-8 太阳质量/年
rstar: 10.0, // 10 太阳半径
reldst: 3.0, // R/R(star) = 3
alphav: 0.1, // alpha = 0.1
zeta0: 0.0,
zeta1: 0.0,
fractv: 0.5,
dmvisc: 0.0,
reynum: 0.0,
};
let result = inpdis_pure(&mut params, 1e15);
// 验证基本物理量
assert!(result.teff > 0.0, "TEFF should be positive");
assert!(result.qgrav > 0.0, "QGRAV should be positive");
assert!(result.dmtot > 0.0, "DMTOT should be positive");
assert!(result.edisc > 0.0, "EDISC should be positive");
// 经典情况下qcor = 1.0
assert_relative_eq!(result.qcor, 1.0, epsilon = 1e-6);
// tcor = (1 - 1/sqrt(rr))^0.25,对于 rr=3tcor ≈ 0.806
assert!(result.tcor > 0.0 && result.tcor < 1.0);
// arh, brh, crh 应该为 1
assert_relative_eq!(result.arh, 1.0, epsilon = 1e-6);
assert_relative_eq!(result.brh, 1.0, epsilon = 1e-6);
assert_relative_eq!(result.crh, 1.0, epsilon = 1e-6);
}
#[test]
fn test_inpdis_gr_correction() {
// 测试 GR 修正情况
let mut params = InpDisParams {
xmstar: -10.0, // 负值启用 GR 修正
xmdot: 1e-8,
rstar: 0.5, // 角动量参数
reldst: 6.0, // 最小稳定轨道
alphav: 0.1,
zeta0: 0.0,
zeta1: 0.0,
fractv: 0.5,
dmvisc: 0.0,
reynum: 0.0,
};
let result = inpdis_pure(&mut params, 1e15);
// GR 修正应该生效
assert!(result.teff > 0.0);
assert!(result.qgrav > 0.0);
assert!(result.arh > 0.0);
assert!(result.brh > 0.0);
}
#[test]
fn test_inpdis_direct_mode() {
// 测试直接输入模式 (xmstar = 0)
let mut params = InpDisParams {
xmstar: 0.0,
xmdot: 35000.0, // 直接作为 TEFF
rstar: 1e4, // 直接作为 QGRAV
reldst: 1e2, // 直接作为 DMTOT
alphav: 0.0,
zeta0: 0.0,
zeta1: 0.0,
fractv: 0.0,
dmvisc: 0.0,
reynum: 0.0,
};
let result = inpdis_pure(&mut params, 1e15);
assert_relative_eq!(result.teff, 35000.0, epsilon = 1e-6);
assert_relative_eq!(result.qgrav, 1e4, epsilon = 1e-6);
assert_relative_eq!(result.dmtot, 1e2, epsilon = 1e-6);
}
#[test]
fn test_fractv_dmvisc_calculation() {
// 测试 fractv 和 dmvisc 的计算
let mut params = InpDisParams {
xmstar: 0.0,
xmdot: 10000.0,
rstar: 1e4,
reldst: 1e2,
alphav: 0.0,
zeta0: 1.0,
zeta1: 2.0,
fractv: -1.0, // 触发计算
dmvisc: 0.5,
reynum: 0.0,
};
let result = inpdis_pure(&mut params, 0.0);
// fractv 应该被重新计算
assert!(params.fractv > 0.0);
assert!(params.fractv < 1.0);
}
#[test]
fn test_frlmax_calculation() {
let mut params = InpDisParams {
xmstar: 0.0,
xmdot: 30000.0,
rstar: 1e4,
reldst: 1e2,
alphav: 0.0,
zeta0: 0.0,
zeta1: 0.0,
fractv: 0.0,
dmvisc: 0.0,
reynum: 0.0,
};
let cnu1 = 2.0e15;
let result = inpdis_pure(&mut params, cnu1);
// FRLMAX = 1e11 * cnu1 * teff
let expected_frlmax = 1e11 * cnu1 * 30000.0;
assert_relative_eq!(result.frlmax, expected_frlmax, epsilon = 1e-3);
}
}