//! 碰撞-辐射切换参数评估。 //! //! 重构自 TLUSTY `SWITCH` 子程序。 //! //! # 功能 //! //! - 计算碰撞-辐射切换参数 lambda(R) = CRSW //! - 基于 Hummer & Voels (1988) 方法 //! - 支持深度依赖的切换参数 // ============================================================================ // 常量 // ============================================================================ /// 1.0 const UN: f64 = 1.0; /// h/k (Planck/Boltzmann) const HK: f64 = 4.7994e0; // ============================================================================ // 输入/输出结构体 // ============================================================================ /// SWITCH 输入参数(初始化模式)。 pub struct SwitchInitParams<'a> { /// 深度点数 pub nd: usize, /// 跃迁数 pub ntrans: usize, /// 碰撞速率 COLRAT(ITR, ID) pub colrat: &'a [Vec], /// 向上辐射速率 RRU(ITR, ID) pub rru: &'a [Vec], /// 向下辐射速率 RRD(ITR, ID) pub rrd: &'a [Vec], /// 参考频率 FR0(ITR) pub fr0: &'a [f64], /// 温度 TEMP(ID) pub temp: &'a [f64], /// 是否是谱线 LINE(ITR) pub line: &'a [bool], /// ICRSW 模式 (0=禁用, 1=全局最小, 2=深度依赖) pub icrsw: i32, /// SWPFAC 因子 pub swpfac: f64, /// SWPLIM 限制 pub swplim: f64, } /// SWITCH 输入参数(更新模式)。 pub struct SwitchUpdateParams<'a> { /// 深度点数 pub nd: usize, /// 当前 CRSW 值 pub crsw: &'a mut [f64], /// SWPINC 增量因子 pub swpinc: f64, /// SWPLIM 限制 pub swplim: f64, } /// SWITCH 输出结果。 #[derive(Debug, Clone)] pub struct SwitchOutput { /// CRSW 数组 pub crsw: Vec, /// 全局最小值(仅在初始化模式) pub swmin: Option, } // ============================================================================ // 核心计算函数 // ============================================================================ /// 执行 SWITCH 初始化计算(INITM = 1)。 /// /// # 参数 /// - `params`: 输入参数 /// /// # 返回 /// CRSW 数组 pub fn switch_init(params: &SwitchInitParams) -> SwitchOutput { if params.icrsw == 0 { // 碰撞-辐射切换未启用 return SwitchOutput { crsw: vec![UN; params.nd], swmin: None, }; } let nd = params.nd; let ntrans = params.ntrans; let mut swtch = vec![UN; nd]; let mut swmin = UN; // 遍历每个深度点 for id in 0..nd { swtch[id] = UN; // 遍历每个跃迁 for itr in 0..ntrans { let c = params.colrat.get(itr).and_then(|v| v.get(id)).copied().unwrap_or(0.0); let rru_val = params.rru.get(itr).and_then(|v| v.get(id)).copied().unwrap_or(0.0); let rrd_val = params.rrd.get(itr).and_then(|v| v.get(id)).copied().unwrap_or(0.0); if rru_val.abs() > 1e-30 { // 向上速率 let swu = c / rru_val; // 向下速率 let swd = if params.line.get(itr).copied().unwrap_or(false) { // 谱线 let fr0 = params.fr0.get(itr).copied().unwrap_or(0.0); let temp = params.temp.get(id).copied().unwrap_or(10000.0); c * (HK * fr0 / temp).exp() / rrd_val } else { // 连续谱 c / rrd_val }; // 取最小值 if swu < swtch[id] { swtch[id] = swu; } if swd < swtch[id] { swtch[id] = swd; } } } // 更新全局最小值 if swtch[id] < swmin { swmin = swtch[id]; } } // 计算 CRSW let mut crsw = vec![0.0; nd]; for id in 0..nd { crsw[id] = if params.icrsw == 1 { // 使用全局最小值 swmin * params.swpfac } else { // 使用深度依赖值 swtch[id] * params.swpfac }; // 限制 if crsw[id] > params.swplim { crsw[id] = UN; } } SwitchOutput { crsw, swmin: Some(swmin), } } /// 执行 SWITCH 更新计算(INITM = 0)。 /// /// # 参数 /// - `params`: 输入参数(crsw 会被修改) /// /// # 返回 /// 更新后的 CRSW 数组 pub fn switch_update(params: &mut SwitchUpdateParams) -> SwitchOutput { for id in 0..params.nd { params.crsw[id] *= params.swpinc; // 限制 if params.crsw[id] > params.swplim { params.crsw[id] = UN; } } SwitchOutput { crsw: params.crsw.to_vec(), swmin: None, } } /// 生成 CRSW 输出消息。 pub fn format_crsw_message(crsw: &[f64]) -> String { let mut msg = String::new(); for (i, &val) in crsw.iter().enumerate() { msg.push_str(&format!("{:10.3e}", val)); if (i + 1) % 8 == 0 { msg.push('\n'); } } if !crsw.is_empty() && crsw.len() % 8 != 0 { msg.push('\n'); } msg } // ============================================================================ // 测试 // ============================================================================ #[cfg(test)] mod tests { use super::*; #[test] fn test_switch_disabled() { let params = SwitchInitParams { nd: 3, ntrans: 2, colrat: &vec![vec![0.1, 0.1, 0.1], vec![0.1, 0.1, 0.1]], rru: &vec![vec![1.0, 1.0, 1.0], vec![1.0, 1.0, 1.0]], rrd: &vec![vec![1.0, 1.0, 1.0], vec![1.0, 1.0, 1.0]], fr0: &vec![1.0e15, 2.0e15], temp: &vec![10000.0, 9000.0, 8000.0], line: &vec![true, false], icrsw: 0, // 禁用 swpfac: 1.0, swplim: 1.0, }; let result = switch_init(¶ms); // 禁用时应该返回全 1 for &val in &result.crsw { assert!((val - 1.0).abs() < 1e-10); } } #[test] fn test_switch_global_minimum() { let params = SwitchInitParams { nd: 3, ntrans: 2, colrat: &vec![vec![0.1, 0.2, 0.3], vec![0.05, 0.1, 0.15]], rru: &vec![vec![1.0, 1.0, 1.0], vec![1.0, 1.0, 1.0]], rrd: &vec![vec![1.0, 1.0, 1.0], vec![1.0, 1.0, 1.0]], fr0: &vec![1.0e15, 2.0e15], temp: &vec![10000.0, 9000.0, 8000.0], line: &vec![false, false], icrsw: 1, // 全局最小 swpfac: 0.5, swplim: 1.0, }; let result = switch_init(¶ms); // 所有深度应该使用相同的全局最小值 assert!(result.swmin.is_some()); let swmin = result.swmin.unwrap(); assert!(swmin < 1.0); // CRSW 应该是 swmin * swpfac for &val in &result.crsw { assert!((val - swmin * 0.5).abs() < 1e-10); } } #[test] fn test_switch_depth_dependent() { let params = SwitchInitParams { nd: 3, ntrans: 1, colrat: &vec![vec![0.1, 0.2, 0.3]], rru: &vec![vec![1.0, 1.0, 1.0]], rrd: &vec![vec![1.0, 1.0, 1.0]], fr0: &vec![1.0e15], temp: &vec![10000.0, 9000.0, 8000.0], line: &vec![false], icrsw: 2, // 深度依赖 swpfac: 1.0, swplim: 1.0, }; let result = switch_init(¶ms); // 每个深度应该有不同的值 assert!((result.crsw[0] - 0.1).abs() < 1e-10); assert!((result.crsw[1] - 0.2).abs() < 1e-10); assert!((result.crsw[2] - 0.3).abs() < 1e-10); } #[test] fn test_switch_limit() { let params = SwitchInitParams { nd: 2, ntrans: 1, colrat: &vec![vec![10.0, 0.5]], // 第一个深度比例很大 rru: &vec![vec![1.0, 1.0]], rrd: &vec![vec![1.0, 1.0]], fr0: &vec![1.0e15], temp: &vec![10000.0, 9000.0], line: &vec![false], icrsw: 2, swpfac: 1.0, swplim: 0.5, // 限制为 0.5 }; let result = switch_init(¶ms); // 第一个深度应该被限制为 1.0 assert!((result.crsw[0] - 1.0).abs() < 1e-10); // 第二个深度应该正常 assert!((result.crsw[1] - 0.5).abs() < 1e-10); } #[test] fn test_switch_update() { let mut crsw = vec![0.1, 0.2, 0.3]; let mut params = SwitchUpdateParams { nd: 3, crsw: &mut crsw, swpinc: 2.0, swplim: 1.0, }; let result = switch_update(&mut params); // 应该乘以 swpinc assert!((result.crsw[0] - 0.2).abs() < 1e-10); assert!((result.crsw[1] - 0.4).abs() < 1e-10); assert!((result.crsw[2] - 0.6).abs() < 1e-10); } #[test] fn test_switch_update_limit() { let mut crsw = vec![0.8, 0.9]; let mut params = SwitchUpdateParams { nd: 2, crsw: &mut crsw, swpinc: 2.0, swplim: 1.0, }; let result = switch_update(&mut params); // 超过限制应该设为 1.0 assert!((result.crsw[0] - 1.0).abs() < 1e-10); assert!((result.crsw[1] - 1.0).abs() < 1e-10); } #[test] fn test_format_message() { let crsw = vec![0.1, 0.2, 0.3]; let msg = format_crsw_message(&crsw); // Rust 的 {:10.3e} 格式产生类似 " 1.000e-1" 的输出 // 检查包含科学计数法 assert!(msg.contains("e-1") || msg.contains("E-1")); // 检查第一个值 assert!(msg.contains("1.000e-1") || msg.contains("1.00e-1")); } }