//! 盘模型输入参数处理。 //! //! 重构自 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 rstar > 1e3 { rstar / RSUN } else { 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 params.alphav <= 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( params: &mut InpDisParams, cnu1: f64, writer: &mut FortranWriter, ) -> Result { // 写入标题 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); } }