526 lines
16 KiB
Rust
526 lines
16 KiB
Rust
//! 盘模型输入参数处理。
|
||
//!
|
||
//! 重构自 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=3,tcor ≈ 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);
|
||
}
|
||
}
|