SpectraRust/src/math/levsol.rs
2026-03-21 09:12:18 +08:00

230 lines
5.7 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 能级占据数求解器。
//!
//! 重构自 TLUSTY `levsol.f`。
//!
//! 通过求解速率方程得到新的能级占据数。
use crate::math::lineqs::lineqs_nr;
use crate::state::atomic::AtoPar;
use crate::state::config::{BasNum, InpPar};
use crate::state::constants::MLEVEL;
// ============================================================================
// LEVSOL - 能级求解器
// ============================================================================
/// 求解速率方程得到新的能级占据数。
///
/// 通过两种方式之一:
/// a) 反演全局速率矩阵 (如果 IRSPLT=0)
/// b) 反演各个化学元素的部分速率矩阵
///
/// # 参数
///
/// - `a` - 速率矩阵 A(MLEVEL,MLEVEL)
/// - `b` - 右端向量 B(MLEVEL)
/// - `popp` - 输出占据数 POPP(MLEVEL)
/// - `iical` - 能级索引映射 IICAL(MLEVEL)
/// - `nlvcal` - 实际计算的能级数
/// - `iall` - 标志 (0 表示跳过固定元素)
/// - `basnum` - 基本数值参数 (包含 natom, ioptab)
/// - `inppar` - 输入参数 (包含 irsplt)
/// - `atopar` - 原子参数 (包含 n0a, nka, iifix)
///
/// # Fortran 原始代码
///
/// ```fortran
/// SUBROUTINE LEVSOL(A,B,POPP,IICAL,NLVCAL,IALL)
/// ...
/// END
/// ```
pub fn levsol(
a: &mut [f64],
b: &mut [f64],
popp: &mut [f64],
iical: &[i32],
nlvcal: usize,
iall: i32,
basnum: &BasNum,
inppar: &InpPar,
atopar: &AtoPar,
) {
// 检查是否跳过
if basnum.ioptab < 0 {
return;
}
// 方式 a: 反演全局速率矩阵
if inppar.irsplt == 0 {
lineqs_nr(a, b, popp, nlvcal, MLEVEL);
return;
}
// 方式 b: 反演各个化学元素的部分速率矩阵
let natom = basnum.natom as usize;
// 工作数组
let mut ap = vec![0.0; MLEVEL * MLEVEL];
let mut bp = vec![0.0; MLEVEL];
let mut popp1 = vec![0.0; MLEVEL];
for iat in 0..natom {
// 跳过固定元素
if atopar.iifix[iat] == 1 && iall == 0 {
continue;
}
let mut n1 = atopar.n0a[iat] as usize;
let nk = atopar.nka[iat] as usize;
n1 = iical[n1] as usize;
let mut nk_idx = iical[nk] as usize;
// 查找有效的起始索引
if n1 == 0 {
for i in atopar.n0a[iat] as usize..=atopar.nka[iat] as usize {
let idx = iical[i] as usize;
if idx > 0 {
n1 = idx;
break;
}
}
}
if n1 == 0 {
continue;
}
// 修正 nk_idx (Fortran 中可能是 0这里需要调整)
if nk_idx == 0 {
nk_idx = n1;
}
let nlp = nk_idx - n1 + 1;
if nlp == 0 || n1 + nlp > MLEVEL {
continue;
}
// 提取部分矩阵
for i in 0..nlp {
for j in 0..nlp {
ap[j * MLEVEL + i] = a[(n1 + i) * MLEVEL + (n1 + j)];
}
bp[i] = b[n1 + i];
}
// 求解部分矩阵
lineqs_nr(&mut ap, &mut bp, &mut popp1, nlp, MLEVEL);
// 存储结果
for i in 0..nlp {
popp[n1 + i] = popp1[i];
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::state::atomic::AtoPar;
use crate::state::config::{BasNum, InpPar};
use crate::state::constants::MLEVEL;
fn create_test_atopar() -> AtoPar {
let mut atopar = AtoPar::default();
atopar.n0a[0] = 0;
atopar.n0a[1] = 3;
atopar.nka[0] = 2;
atopar.nka[1] = 5;
atopar.iifix[0] = 0;
atopar.iifix[1] = 0;
atopar
}
fn create_test_basnum() -> BasNum {
BasNum {
natom: 2,
ioptab: 0,
..Default::default()
}
}
fn create_test_inppar() -> InpPar {
InpPar {
irsplt: 0,
..Default::default()
}
}
#[test]
fn test_levsol_global_matrix() {
let mut a = vec![0.0; MLEVEL * MLEVEL];
let mut b = vec![0.0; MLEVEL];
let mut popp = vec![0.0; MLEVEL];
let iical = vec![0i32; MLEVEL];
// 设置简单的 2x2 系统
// [2 1] [x] [3]
// [1 2] [y] = [3]
// 解是 x=1, y=1
// Fortran 列优先: A(j,i) = a[j + i*MLEVEL]
a[0 + 0 * MLEVEL] = 2.0; // A(1,1)
a[1 + 0 * MLEVEL] = 1.0; // A(2,1)
a[0 + 1 * MLEVEL] = 1.0; // A(1,2)
a[1 + 1 * MLEVEL] = 2.0; // A(2,2)
b[0] = 3.0;
b[1] = 3.0;
let basnum = create_test_basnum();
let inppar = create_test_inppar();
let atopar = create_test_atopar();
levsol(
&mut a,
&mut b,
&mut popp,
&iical,
2,
1,
&basnum,
&inppar,
&atopar,
);
// 解应该是 x=1, y=1
assert!((popp[0] - 1.0).abs() < 1e-10);
assert!((popp[1] - 1.0).abs() < 1e-10);
}
#[test]
fn test_levsol_skip_negative_ioptab() {
let basnum = BasNum {
ioptab: -2,
..Default::default()
};
let inppar = create_test_inppar();
let atopar = create_test_atopar();
let mut a = vec![0.0; MLEVEL * MLEVEL];
let mut b = vec![0.0; MLEVEL];
let mut popp = vec![1.0; MLEVEL]; // 初始值
let iical = vec![0i32; MLEVEL];
let popp_before = popp[0];
levsol(
&mut a,
&mut b,
&mut popp,
&iical,
2,
1,
&basnum,
&inppar,
&atopar,
);
// 应该跳过popp 保持不变
assert!((popp[0] - popp_before).abs() < 1e-10);
}
}