feat: 添加更多重构模块 (第7批)

包含 IO 和 math 模块的实现:
- IO: initia, levcd, linset, ltegr, ltegrd, odfset, outpri, resolv, srtfrq, start, tabini, xenini
- Math: accel2, alisk1, alisk2, alist1, alist2, concor, conout, conref, contmd, contmp, coolrt, greyd, inilam, linsel, lucy, lymlin, matcon, matgen, moleq, newdm, newdmt, odf1, opacf0, opacf1, opacfa, opacfd, opacfl, opactr, opadd, opahst, pgset, princ, prnt, pzeval, quasim, radpre, radtot, rates1, ratsp1, rdata, rdatax, rechck, rhoeos, rhonen, rhsgen, rossop, rtecf1, rtecmc, rtecmu, rtecom, rtefr1, rteint, russel, rybchn, rybene, rybheq, rybsol, sgmer1, sigave, sigk, solve, solves, state, steqeq, temcor, temper, topbas, trmder, trmdrt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
fmq
2026-03-25 01:46:29 +08:00
co-authored by Claude Opus 4.6
parent 21cb6af16c
commit a086e313cb
85 changed files with 48464 additions and 6 deletions
+421
View File
@@ -0,0 +1,421 @@
//! Lyman 线系不透明度和发射率计算。
//!
//! 重构自 TLUSTY `lymlin.f`
//!
//! 计算前 30 条 Lyman 线的不透明度和发射率贡献。
use crate::state::constants::{MDEPTH, TWO};
// ============================================================================
// 常量参数
// ============================================================================
const SIXTH: f64 = 1.0 / 6.0;
const TTW: f64 = 2.0 / 3.0;
const OS0: f64 = 0.02654;
const CPP: f64 = 4.1412e-16;
const CPJ: f64 = 157803.0;
const C00: f64 = 1.25e-9;
const C18: f64 = 2.997925e18;
/// Lyman 线数量
const MLEVL: usize = 30;
// ============================================================================
// Lymlin 缓存结构
// ============================================================================
/// Lyman 线计算的缓存数据。
///
/// 第一次调用时计算并存储,后续调用直接使用。
#[derive(Debug, Clone)]
pub struct LymlinCache {
/// 是否已初始化
pub initialized: bool,
/// Stark K 系数 (j=2..30)
pub xkijl: Vec<f64>,
/// Stark f 值 (j=2..30)
pub fijl: Vec<f64>,
/// 谱线波长 (j=2..30)
pub wl0l: Vec<f64>,
/// 谱线频率 (j=2..30)
pub fr0l: Vec<f64>,
/// 能级占据数 (j, id)
pub pj: Vec<Vec<f64>>,
/// 吸收系数 (j, id)
pub abtr: Vec<Vec<f64>>,
/// 发射系数 (j, id)
pub emtr: Vec<Vec<f64>>,
/// FID 系数 (j, id)
pub fid: Vec<Vec<f64>>,
/// ADH 系数 (j, id)
pub ad0: Vec<Vec<f64>>,
/// DIVH 系数 (j, id)
pub div0: Vec<Vec<f64>>,
/// DBETA 系数 (j, id)
pub dbet0: Vec<Vec<f64>>,
/// BETAD 系数 (j, id)
pub betad0: Vec<Vec<f64>>,
}
impl Default for LymlinCache {
fn default() -> Self {
Self {
initialized: false,
xkijl: vec![0.0; MLEVL + 1], // 索引 2..30
fijl: vec![0.0; MLEVL + 1],
wl0l: vec![0.0; MLEVL + 1],
fr0l: vec![0.0; MLEVL + 1],
pj: vec![vec![0.0; MDEPTH]; MLEVL + 1],
abtr: vec![vec![0.0; MDEPTH]; MLEVL + 1],
emtr: vec![vec![0.0; MDEPTH]; MLEVL + 1],
fid: vec![vec![0.0; MDEPTH]; MLEVL + 1],
ad0: vec![vec![0.0; MDEPTH]; MLEVL + 1],
div0: vec![vec![0.0; MDEPTH]; MLEVL + 1],
dbet0: vec![vec![0.0; MDEPTH]; MLEVL + 1],
betad0: vec![vec![0.0; MDEPTH]; MLEVL + 1],
}
}
}
// ============================================================================
// 参数结构体
// ============================================================================
/// Lymlin 函数的输入参数。
pub struct LymlinParams<'a> {
/// 深度点数
pub nd: usize,
/// 氢元素索引 (0-based, Fortran 中是 1-based)
pub ielh: i32,
/// 氢原子能级起始索引 (0-based)
pub n0h: i32,
/// 氢原子能级终止索引 (0-based)
pub n1h: i32,
/// 氢离子能级索引 (0-based)
pub nkh: i32,
/// 氢原子能级数
pub nlh: i32,
/// 温度数组 (nd)
pub temp: &'a [f64],
/// 电子密度数组 (nd)
pub elec: &'a [f64],
/// 占据数数组 (nlevel, nd)
pub popul: &'a [Vec<f64>],
/// 湍流速度数组 (nd)
pub vturb: &'a [f64],
/// 氢能级占据概率 (nlmx, nd)
pub wnhint: &'a [Vec<f64>],
/// 频率数组
pub freq: &'a [f64],
/// 当前频率索引 (0-based)
pub ij: usize,
/// 不透明度数组 (nd) - 输入/输出
pub abso1: &'a mut [f64],
/// 发射率数组 (nd) - 输入/输出
pub emis1: &'a mut [f64],
}
// ============================================================================
// 主函数
// ============================================================================
/// 计算 Lyman 线系的不透明度和发射率。
///
/// # 参数
///
/// * `params` - 输入参数结构体
/// * `cache` - 缓存数据(第一次调用时初始化)
///
/// # 说明
///
/// 计算前 30 条 Lyman 线 (n=1 到 n=2..30) 对不透明度和发射率的贡献。
/// 使用 Stark 展宽理论处理谱线轮廓。
///
/// # Fortran 原始代码
///
/// ```fortran
/// SUBROUTINE LYMLIN(IJ)
/// ...
/// FR=FREQ(IJ)
/// IF(FR.GT.3.28805E15.OR.FR.LT.1.5E15) RETURN
/// ...
/// ```
pub fn lymlin(params: &mut LymlinParams, cache: &mut LymlinCache) {
// 如果没有氢,直接返回
if params.ielh == 0 {
return;
}
let ij = params.ij;
let fr = params.freq[ij];
// 频率范围检查:1.5e15 到 3.28805e15 Hz
if fr > 3.28805e15 || fr < 1.5e15 {
return;
}
let nd = params.nd;
let n0h = params.n0h;
let n1h = params.n1h;
let nkh = params.nkh;
let nlh = params.nlh;
// 第一次调用时初始化缓存
if !cache.initialized {
initialize_cache(params, cache);
cache.initialized = true;
}
// 主计算循环
let wl = C18 / fr;
let f15 = fr * 1.0e-15;
// 工作数组
let mut ablym = vec![0.0; MDEPTH];
let mut emlym = vec![0.0; MDEPTH];
for id in 0..nd {
ablym[id] = 0.0;
emlym[id] = 0.0;
for j in 2..=30 {
let beta = cache.dbet0[j][id] * (fr - cache.fr0l[j]).abs();
let betad = cache.betad0[j][id];
let adh = cache.ad0[j][id];
let divh = cache.div0[j][id];
let sg = super::starka::starka(beta, TWO, adh, betad, divh) * cache.fid[j][id];
ablym[id] += sg * cache.abtr[j][id];
emlym[id] += sg * cache.emtr[j][id];
}
let xkt = (-4.79928e-11 * fr / params.temp[id]).exp();
let xkb = xkt * 1.4743e-2 * f15 * f15 * f15;
ablym[id] -= xkt * emlym[id];
emlym[id] = xkb * emlym[id];
// 累加到输出数组
params.abso1[id] += ablym[id];
params.emis1[id] += emlym[id];
}
}
/// 初始化缓存数据。
fn initialize_cache(params: &LymlinParams, cache: &mut LymlinCache) {
let nd = params.nd;
let n0h = params.n0h;
let n1h = params.n1h;
let nkh = params.nkh;
let nlh = params.nlh;
// 计算 j=2..30 的 Stark 参数
for j in 2..=30 {
let (xkij0, wl00, fij0) = super::stark0::stark0(1, j, 1);
cache.xkijl[j] = xkij0;
cache.fijl[j] = fij0;
cache.wl0l[j] = wl00;
cache.fr0l[j] = C18 / wl00;
}
// 对每个深度点计算
for id in 0..nd {
let t = params.temp[id];
let t1 = 1.0 / t;
let sqt = t.sqrt();
let ane = params.elec[id];
let anp = params.popul[nkh as usize][id];
let f00 = C00 * ane.powf(TTW);
let dop0 = 1.0e8 * (1.65e8 * t + params.vturb[id]).sqrt();
let p0 = CPP * ane * anp * t1 / sqt;
let p1 = params.popul[n0h as usize][id];
for j in 2..=30 {
let x = (j * j) as f64;
let jj = n0h + (j as i32) - 1;
let xjj = 1.0 / (jj as f64);
// 计算 PJ
if j as i32 <= nlh {
cache.pj[j][id] = params.popul[jj as usize][id];
} else {
// 使用 LTE 近似
let wnhint_j = if (j as usize) < params.wnhint.len() {
params.wnhint[j][id]
} else {
1.0
};
cache.pj[j][id] = p0 * (CPJ / (x * t)).exp() * x * wnhint_j;
}
// 计算吸收和发射系数
cache.abtr[j][id] = p1 * params.wnhint[j][id];
cache.emtr[j][id] = cache.pj[j][id] * xjj * (CPJ * (1.0 - xjj) * t1).exp();
// 计算其他参数
let fxk = f00 * cache.xkijl[j];
let dbeta = cache.wl0l[j] * cache.wl0l[j] / (C18 * fxk);
cache.fid[j][id] = OS0 * cache.fijl[j] * dbeta;
let dop = dop0 / cache.wl0l[j];
let betad = dop * dbeta;
// 调用 divstr
let (adh, divh) = super::divstr::divstr(betad, 1);
cache.ad0[j][id] = adh;
cache.div0[j][id] = divh;
cache.dbet0[j][id] = dbeta;
cache.betad0[j][id] = betad;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_params<'a>(
nd: usize,
abso1: &'a mut [f64],
emis1: &'a mut [f64],
freq: &'a [f64],
temp: &'a [f64],
elec: &'a [f64],
popul: &'a [Vec<f64>],
vturb: &'a [f64],
wnhint: &'a [Vec<f64>],
) -> LymlinParams<'a> {
LymlinParams {
nd,
ielh: 1, // 假设有氢
n0h: 0, // 氢原子能级起始
n1h: 29, // 氢原子能级终止 (30 个能级)
nkh: 30, // 氢离子能级
nlh: 30, // 氢原子能级数
temp,
elec,
popul,
vturb,
wnhint,
freq,
ij: 0,
abso1,
emis1,
}
}
#[test]
fn test_lymlin_no_hydrogen() {
let mut cache = LymlinCache::default();
let mut abso1 = vec![0.0; 10];
let mut emis1 = vec![0.0; 10];
let freq = vec![2.0e15; 100];
let temp = vec![10000.0; 10];
let elec = vec![1.0e13; 10];
let popul = vec![vec![1.0e10; 10]; 50];
let vturb = vec![0.0; 10];
let wnhint = vec![vec![1.0; 10]; 80];
let mut params = LymlinParams {
nd: 10,
ielh: 0, // 无氢
n0h: 0,
n1h: 29,
nkh: 30,
nlh: 30,
temp: &temp,
elec: &elec,
popul: &popul,
vturb: &vturb,
wnhint: &wnhint,
freq: &freq,
ij: 0,
abso1: &mut abso1,
emis1: &mut emis1,
};
lymlin(&mut params, &mut cache);
// 无氢时应该不修改数组
for i in 0..10 {
assert!((params.abso1[i] - 0.0).abs() < 1e-15);
assert!((params.emis1[i] - 0.0).abs() < 1e-15);
}
}
#[test]
fn test_lymlin_frequency_out_of_range() {
let mut cache = LymlinCache::default();
let mut abso1 = vec![0.0; 10];
let mut emis1 = vec![0.0; 10];
// 频率太低
let freq_low = vec![1.0e15; 100];
let temp = vec![10000.0; 10];
let elec = vec![1.0e13; 10];
let popul = vec![vec![1.0e10; 10]; 50];
let vturb = vec![0.0; 10];
let wnhint = vec![vec![1.0; 10]; 80];
let mut params = create_test_params(
10, &mut abso1, &mut emis1, &freq_low,
&temp, &elec, &popul, &vturb, &wnhint,
);
lymlin(&mut params, &mut cache);
// 频率超出范围时应该不修改数组
for i in 0..10 {
assert!((params.abso1[i] - 0.0).abs() < 1e-15);
assert!((params.emis1[i] - 0.0).abs() < 1e-15);
}
}
#[test]
fn test_lymlin_valid_frequency() {
let mut cache = LymlinCache::default();
let mut abso1 = vec![0.0; 5];
let mut emis1 = vec![0.0; 5];
// 有效频率范围内的频率
let freq = vec![2.5e15; 100];
let temp = vec![10000.0; 5];
let elec = vec![1.0e13; 5];
let popul = vec![vec![1.0e10; 5]; 50];
let vturb = vec![0.0; 5];
let wnhint = vec![vec![1.0; 5]; 80];
let mut params = create_test_params(
5, &mut abso1, &mut emis1, &freq,
&temp, &elec, &popul, &vturb, &wnhint,
);
lymlin(&mut params, &mut cache);
// 检查缓存已初始化
assert!(cache.initialized);
// 检查 Stark 参数已计算
for j in 2..=30 {
assert!(cache.xkijl[j] > 0.0);
assert!(cache.fijl[j] > 0.0);
assert!(cache.wl0l[j] > 0.0);
assert!(cache.fr0l[j] > 0.0);
}
}
#[test]
fn test_cache_initialization() {
let cache = LymlinCache::default();
// 检查默认值
assert!(!cache.initialized);
assert_eq!(cache.xkijl.len(), MLEVL + 1);
assert_eq!(cache.pj.len(), MLEVL + 1);
assert_eq!(cache.pj[0].len(), MDEPTH);
}
}