229 lines
6.3 KiB
Rust
229 lines
6.3 KiB
Rust
//! 溶解分数计算 (所有频率)。
|
||
//!
|
||
//! 重构自 TLUSTY `dwnfr.f`
|
||
|
||
use crate::state::constants::UN;
|
||
use crate::state::config::InpPar;
|
||
|
||
// ============================================================================
|
||
// DWNFR - 溶解分数 (所有频率)
|
||
// ============================================================================
|
||
|
||
/// 计算所有频率的溶解分数。
|
||
///
|
||
/// # 参数
|
||
///
|
||
/// - `mode` - 模式: 0 表示 DW=1 (无下降), >0 表示计算
|
||
/// - `n` - 频率数量
|
||
/// - `fre` - 阈值频率
|
||
/// - `a` - acor 参数 (从 dwnfr0 计算得到)
|
||
/// - `ane` - 电子密度
|
||
/// - `z` - 离子电荷
|
||
/// - `fr` - 频率数组
|
||
/// - `inppar` - 输入参数 (bergfc)
|
||
/// - `dw` - 输出溶解分数数组
|
||
///
|
||
/// # Fortran 原始代码
|
||
///
|
||
/// ```fortran
|
||
/// SUBROUTINE DWNFR(MODE,N,FRE,A,ANE,Z,FR,DW)
|
||
/// INCLUDE 'IMPLIC.FOR'
|
||
/// INCLUDE 'BASICS.FOR'
|
||
/// INCLUDE 'MODELQ.FOR'
|
||
/// parameter (p1=0.1402,p2=0.1285,p3=un,p4=3.15,p5=4.)
|
||
/// parameter (tkn=3.01,ckn=5.33333333,cb0=8.59d14,f23=-2./3.)
|
||
/// PARAMETER (FRH=3.28805D15,SQFRH=5.734152D7)
|
||
/// DIMENSION FR(N),DW(N)
|
||
///
|
||
/// cb=cb0*berfc ! 注意: 原文是 berfc,应为 bergfc
|
||
/// IF(MODE.EQ.0) THEN
|
||
/// DO IJ=1,N
|
||
/// DW(IJ)=UN
|
||
/// END DO
|
||
/// ELSE
|
||
/// DO IJ=1,N
|
||
/// IF(FR(IJ).LT.FRE) THEN
|
||
/// XN=SQFRH*Z/SQRT(FRE-FR(IJ))
|
||
/// if(xn.le.tkn) then
|
||
/// xkn=un
|
||
/// else
|
||
/// xn1=un/(xn+un)
|
||
/// xkn=ckn*xn*xn1*xn1
|
||
/// end if
|
||
/// beta=cb*z*z*z*xkn/(xn*xn*xn*xn)*exp(f23*log(ane))
|
||
/// x=exp(p4*log(un+p3*a))
|
||
/// c1=p1*(x+p5*(z-un)*a*a*a)
|
||
/// c2=p2*x
|
||
/// f=(c1*beta*beta*beta)/(un+c2*beta*sqrt(beta))
|
||
/// DW(IJ)=UN-f/(un+f)
|
||
/// ELSE
|
||
/// DW(IJ)=UN
|
||
/// END IF
|
||
/// END DO
|
||
/// END IF
|
||
/// END
|
||
/// ```
|
||
pub fn dwnfr(
|
||
mode: i32,
|
||
n: usize,
|
||
fre: f64,
|
||
a: f64,
|
||
ane: f64,
|
||
z: f64,
|
||
fr: &[f64],
|
||
inppar: &InpPar,
|
||
dw: &mut [f64],
|
||
) {
|
||
const P1: f64 = 0.1402;
|
||
const P2: f64 = 0.1285;
|
||
const P3: f64 = UN;
|
||
const P4: f64 = 3.15;
|
||
const P5: f64 = 4.0;
|
||
const TKN: f64 = 3.01;
|
||
const CKN: f64 = 5.33333333;
|
||
const CB0: f64 = 8.59e14;
|
||
const F23: f64 = -2.0 / 3.0;
|
||
const SQFRH: f64 = 5.734152e7;
|
||
|
||
// 注意: Fortran 原文是 berfc,但应该是 bergfc
|
||
let cb = CB0 * inppar.bergfc;
|
||
|
||
if mode == 0 {
|
||
// MODE=0: DW=1 (无下降)
|
||
for ij in 0..n {
|
||
dw[ij] = UN;
|
||
}
|
||
} else {
|
||
// MODE>0: 计算溶解分数
|
||
let z3 = z * z * z;
|
||
let a3 = a * a * a;
|
||
let elec23 = ane.powf(F23);
|
||
let x = (UN + P3 * a).powf(P4);
|
||
let c1 = P1 * (x + P5 * (z - UN) * a3);
|
||
let c2 = P2 * x;
|
||
|
||
for ij in 0..n {
|
||
if fr[ij] < fre {
|
||
let xn = SQFRH * z / (fre - fr[ij]).sqrt();
|
||
|
||
let xkn = if xn <= TKN {
|
||
UN
|
||
} else {
|
||
let xn1 = UN / (xn + UN);
|
||
CKN * xn * xn1 * xn1
|
||
};
|
||
|
||
let beta = cb * z3 * xkn / xn.powi(4) * elec23;
|
||
let beta3 = beta * beta * beta;
|
||
let beta32 = beta3.sqrt();
|
||
let f = (c1 * beta3) / (UN + c2 * beta * beta32);
|
||
|
||
dw[ij] = UN - f / (UN + f);
|
||
} else {
|
||
dw[ij] = UN;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_dwnfr_mode_zero() {
|
||
let inppar = InpPar::default();
|
||
let fr = [0.5, 0.6, 0.7, 0.8, 0.9];
|
||
let mut dw = [0.0; 5];
|
||
|
||
dwnfr(0, 5, 1.0, 0.1, 1e12, 2.0, &fr, &inppar, &mut dw);
|
||
|
||
// MODE=0 时,所有 DW 应该是 1.0
|
||
for ij in 0..5 {
|
||
assert!((dw[ij] - 1.0).abs() < 1e-10, "dw[{}] = {}, expected 1.0", ij, dw[ij]);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_dwnfr_mode_nonzero_above_threshold() {
|
||
let inppar = InpPar::default();
|
||
let fr = [1.1, 1.2, 1.3]; // 都大于 fre=1.0
|
||
let mut dw = [0.0; 3];
|
||
|
||
dwnfr(1, 3, 1.0, 0.1, 1e12, 2.0, &fr, &inppar, &mut dw);
|
||
|
||
// fr >= fre 时,DW 应该是 1.0
|
||
for ij in 0..3 {
|
||
assert!((dw[ij] - 1.0).abs() < 1e-10);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_dwnfr_mode_nonzero_below_threshold() {
|
||
let inppar = InpPar::default();
|
||
let fr = [0.5, 0.6, 0.7, 0.8, 0.9]; // 都小于 fre=1.0
|
||
let mut dw = [0.0; 5];
|
||
|
||
dwnfr(1, 5, 1.0, 0.1, 1e12, 2.0, &fr, &inppar, &mut dw);
|
||
|
||
// DW 应该在 (0, 1] 范围内
|
||
for ij in 0..5 {
|
||
assert!(dw[ij] <= 1.0 && dw[ij] > 0.0, "dw[{}] = {}", ij, dw[ij]);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_dwnfr_mixed_frequencies() {
|
||
let inppar = InpPar::default();
|
||
let fr = [0.5, 1.0, 1.5]; // 混合: <, =, >
|
||
let mut dw = [0.0; 3];
|
||
|
||
dwnfr(1, 3, 1.0, 0.1, 1e12, 2.0, &fr, &inppar, &mut dw);
|
||
|
||
// fr[0] < fre: dw < 1 (可能)
|
||
// fr[1] == fre: dw == 1
|
||
// fr[2] > fre: dw == 1
|
||
assert!(dw[0] <= 1.0);
|
||
assert!((dw[1] - 1.0).abs() < 1e-10);
|
||
assert!((dw[2] - 1.0).abs() < 1e-10);
|
||
}
|
||
|
||
#[test]
|
||
fn test_dwnfr_consistency_with_dwnfr1() {
|
||
use super::super::dwnfr1;
|
||
use crate::state::model::DwnPar;
|
||
|
||
let inppar = InpPar::default();
|
||
|
||
// 设置 dwnpar 以匹配 dwnfr 的参数
|
||
let mut dwnpar = DwnPar::default();
|
||
let id = 5;
|
||
let izz = 1; // z = 2
|
||
let z = (izz + 1) as f64;
|
||
let a: f64 = 0.1;
|
||
let ane: f64 = 1e12;
|
||
|
||
// 计算 dwnfr0 会设置的值
|
||
dwnpar.elec23[id] = ane.powf(-2.0 / 3.0);
|
||
dwnpar.z3[izz] = z * z * z;
|
||
let x = (1.0 + a).powf(3.15);
|
||
dwnpar.dwc2[id] = 0.1285 * x;
|
||
let a3 = a * a * a;
|
||
dwnpar.dwc1[izz][id] = 0.1402 * (x + 4.0 * (z - 1.0) * a3);
|
||
|
||
let fr = [0.5, 0.8];
|
||
let mut dw = [0.0; 2];
|
||
let fre = 1.0;
|
||
|
||
dwnfr(1, 2, fre, a, ane, z, &fr, &inppar, &mut dw);
|
||
|
||
// 与 dwnfr1 比较
|
||
let dw1_0 = dwnfr1(fr[0], fre, id, izz, &inppar, &dwnpar);
|
||
let dw1_1 = dwnfr1(fr[1], fre, id, izz, &inppar, &dwnpar);
|
||
|
||
// 由于参数设置可能不完全一致,只检查大致趋势
|
||
assert!((dw[0] - dw1_0).abs() < 1e-6, "dw[0]={}, dwnfr1={}", dw[0], dw1_0);
|
||
assert!((dw[1] - dw1_1).abs() < 1e-6, "dw[1]={}, dwnfr1={}", dw[1], dw1_1);
|
||
}
|
||
}
|