694 lines
21 KiB
Rust
694 lines
21 KiB
Rust
//! 超级能级能量和统计权重计算。
|
|
//!
|
|
//! 重构自 TLUSTY `levcd.f`。
|
|
//!
|
|
//! 从 Kurucz CD-ROM 文件 (gf*.gam) 读取原子数据,
|
|
//! 计算超级能级的平均能量和统计权重。
|
|
//!
|
|
//! 使用 Eissner-Seaton 公式设置超级能级之间的碰撞强度,
|
|
//! 假设 Gamma(T)=0.05, T=Teff。
|
|
|
|
use super::{FortranReader, IoError, Result};
|
|
use crate::tlusty::math::indexx as indexx_func;
|
|
use crate::tlusty::math::quit as quit_func;
|
|
use crate::tlusty::math::wn as wn_func;
|
|
use crate::tlusty::state::atomic::{AtomicData, IonPar, LevPar};
|
|
use crate::tlusty::state::constants::*;
|
|
use crate::tlusty::state::model::ModPar;
|
|
use crate::tlusty::state::odfpar::LevCom;
|
|
use std::fs::File;
|
|
use std::io::{BufRead, BufReader, BufWriter, Write};
|
|
|
|
// ============================================================================
|
|
// 常量参数
|
|
// ============================================================================
|
|
|
|
/// 玻尔兹曼常数转换因子 (用于能量单位转换)
|
|
const BOLCM: f64 = 1.0e8 / HK / CAS;
|
|
/// 相关参数
|
|
const CCOR: f64 = 0.09;
|
|
/// 1/6
|
|
const SIXTH: f64 = UN / 6.0;
|
|
/// Gamma(T) 值
|
|
const GES: f64 = 0.05;
|
|
|
|
/// Fe 离子的基准能量 (cm⁻¹)
|
|
const E0FE: [f64; 10] = [
|
|
63480.0, 130563.0, 247220.0, 442000.0, 605000.0, 799000.0, 1008000.0, 1218380.0, 1884000.0,
|
|
2114000.0,
|
|
];
|
|
|
|
/// Ni 离子的基准能量 (cm⁻¹)
|
|
const E0NI: [f64; 10] = [
|
|
61590.0, 146560.0, 283700.0, 443000.0, 613500.0, 871000.0, 1070000.0, 1310000.0, 1560000.0,
|
|
1812000.0,
|
|
];
|
|
|
|
/// Cr 离子的基准能量 (cm⁻¹)
|
|
const E0CR: [f64; 10] = [
|
|
54576.0, 132966.0, 249700.0, 396500.0, 560200.0, 731020.0, 1291900.0, 1490000.0, 1688000.0,
|
|
1971000.0,
|
|
];
|
|
|
|
// ============================================================================
|
|
// 碰撞强度 COMMON /COLKUR/
|
|
// ============================================================================
|
|
|
|
/// 碰撞强度和 Kurucz 数据。
|
|
/// 对应 COMMON /COLKUR/
|
|
#[derive(Debug, Clone)]
|
|
pub struct ColKur {
|
|
/// 碰撞强度矩阵 (100 x 100)
|
|
pub omes: Vec<Vec<f64>>,
|
|
/// Kurucz 能级能量
|
|
pub eku: Vec<f64>,
|
|
/// Kurucz 能级统计权重
|
|
pub gku: Vec<f64>,
|
|
/// GST 常数
|
|
pub gst: f64,
|
|
/// Kurucz 能级到超级能级的映射
|
|
pub kku: Vec<i32>,
|
|
}
|
|
|
|
impl Default for ColKur {
|
|
fn default() -> Self {
|
|
Self {
|
|
omes: vec![vec![0.0; 100]; 100],
|
|
eku: vec![0.0; 15000],
|
|
gku: vec![0.0; 15000],
|
|
gst: 0.0,
|
|
kku: vec![0; 15000],
|
|
}
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// 输入参数
|
|
// ============================================================================
|
|
|
|
/// LEVCD 输入参数。
|
|
pub struct LevcdParams<'a> {
|
|
/// 离子索引 (1-based)
|
|
pub ion: usize,
|
|
/// 观测标志 (0=标准, 1=使用观测能级, 2=使用所有能级)
|
|
pub iobs: i32,
|
|
/// 有效温度 (K)
|
|
pub teff: f64,
|
|
/// β 引力因子
|
|
pub bergfc: f64,
|
|
/// 模型参数
|
|
pub modpar: &'a mut ModPar,
|
|
/// 离子参数
|
|
pub ionpar: &'a IonPar,
|
|
/// 能级参数
|
|
pub levpar: &'a LevPar,
|
|
/// 深度点数
|
|
pub nd: usize,
|
|
/// 占据概率写入选项 (IFWOP)
|
|
pub ifwop: &'a [i32],
|
|
/// Kurucz 文件路径
|
|
pub fiodf1: &'a str,
|
|
}
|
|
|
|
// ============================================================================
|
|
// Kurucz 文件格式读取
|
|
// ============================================================================
|
|
|
|
/// Kurucz 文件头部信息
|
|
struct KuruczHeader {
|
|
/// 链接数
|
|
nlinku: i32,
|
|
/// 偶宇称能级数
|
|
keve: i32,
|
|
/// 奇宇称能级数
|
|
kodd: i32,
|
|
}
|
|
|
|
/// Kurucz 能级数据
|
|
struct KuruczLevel {
|
|
/// 角动量量子数 J
|
|
yj: f64,
|
|
/// 能量 (cm⁻¹)
|
|
e: f64,
|
|
/// 自动电离宽度
|
|
ar: f64,
|
|
/// Stark 宽度参数
|
|
sr: f64,
|
|
/// 辐射宽度
|
|
wr: f64,
|
|
}
|
|
|
|
/// 读取 Kurucz 文件头部
|
|
///
|
|
/// FORMAT 170: I7, 13X, I6, 12X, I6
|
|
fn read_kurucz_header<R: BufRead>(reader: &mut R) -> Result<KuruczHeader> {
|
|
let mut line = String::new();
|
|
reader.read_line(&mut line)?;
|
|
|
|
if line.len() < 44 {
|
|
return Err(IoError::ParseError(format!(
|
|
"Kurucz header line too short: {}",
|
|
line.len()
|
|
)));
|
|
}
|
|
|
|
// I7: 列 1-7
|
|
let nlinku: i32 = line[0..7].trim().parse().map_err(|e| {
|
|
IoError::ParseError(format!("Failed to parse NLINKU: {} in '{}'", e, line))
|
|
})?;
|
|
|
|
// I6: 列 21-26 (跳过 13 个字符)
|
|
let keve: i32 = line[20..26].trim().parse().map_err(|e| {
|
|
IoError::ParseError(format!("Failed to parse KEVE: {} in '{}'", e, line))
|
|
})?;
|
|
|
|
// I6: 列 39-44 (跳过 12 个字符)
|
|
let kodd: i32 = line[38..44].trim().parse().map_err(|e| {
|
|
IoError::ParseError(format!("Failed to parse KODD: {} in '{}'", e, line))
|
|
})?;
|
|
|
|
Ok(KuruczHeader {
|
|
nlinku,
|
|
keve,
|
|
kodd,
|
|
})
|
|
}
|
|
|
|
/// 读取 Kurucz 能级数据
|
|
///
|
|
/// FORMAT 171: 8X, F4.1, 4X, F13.3, 18X, 3E9.2
|
|
fn read_kurucz_level<R: BufRead>(reader: &mut R) -> Result<KuruczLevel> {
|
|
let mut line = String::new();
|
|
reader.read_line(&mut line)?;
|
|
|
|
if line.len() < 70 {
|
|
// 最小长度检查
|
|
// 如果行较短但包含有效数据,尝试解析
|
|
if line.trim().is_empty() {
|
|
return Err(IoError::UnexpectedEof);
|
|
}
|
|
}
|
|
|
|
// F4.1: 列 9-12 (跳过 8 个字符) - YJ
|
|
let yj: f64 = if line.len() >= 12 {
|
|
line[8..12].trim().parse().unwrap_or(0.0)
|
|
} else {
|
|
0.0
|
|
};
|
|
|
|
// F13.3: 列 17-29 (跳过 4 个字符) - E
|
|
let e: f64 = if line.len() >= 29 {
|
|
line[16..29].trim().parse().unwrap_or(0.0)
|
|
} else {
|
|
0.0
|
|
};
|
|
|
|
// 3E9.2: 列 48-74 (跳过 18 个字符) - AR, SR, WR
|
|
let ar: f64 = if line.len() >= 56 {
|
|
line[47..56].trim().parse().unwrap_or(0.0)
|
|
} else {
|
|
0.0
|
|
};
|
|
|
|
let sr: f64 = if line.len() >= 65 {
|
|
line[56..65].trim().parse().unwrap_or(0.0)
|
|
} else {
|
|
0.0
|
|
};
|
|
|
|
let wr: f64 = if line.len() >= 74 {
|
|
line[65..74].trim().parse().unwrap_or(0.0)
|
|
} else {
|
|
0.0
|
|
};
|
|
|
|
Ok(KuruczLevel { yj, e, ar, sr, wr })
|
|
}
|
|
|
|
// ============================================================================
|
|
// 主函数
|
|
// ============================================================================
|
|
|
|
/// 执行 LEVCD 计算。
|
|
///
|
|
/// # 参数
|
|
/// - `params` - 输入参数
|
|
/// - `levcom` - 能级 ODF 数据 (可变)
|
|
/// - `colkur` - 碰撞强度数据 (可变)
|
|
/// - `wop` - 占据概率数组 (可变)
|
|
///
|
|
/// # Fortran 原始代码
|
|
/// ```fortran
|
|
/// SUBROUTINE LEVCD(ION,IOBS)
|
|
/// ```
|
|
pub fn levcd(
|
|
params: &mut LevcdParams,
|
|
levcom: &mut LevCom,
|
|
colkur: &mut ColKur,
|
|
wop: &mut [Vec<f64>],
|
|
) -> Result<()> {
|
|
let ion = params.ion;
|
|
let iobs = if params.iobs != 1 && params.iobs != 2 {
|
|
0
|
|
} else {
|
|
params.iobs
|
|
};
|
|
|
|
let nd = params.nd;
|
|
let nevku = levcom.nevku[ion] as usize;
|
|
let nodku = levcom.nodku[ion] as usize;
|
|
|
|
// 临时数组
|
|
let mut gwe = vec![vec![vec![0.0f64; 2]; MLEVEL]; MDEPTH];
|
|
let mut gwb = vec![vec![vec![0.0f64; 2]; MLEVEL]; MDEPTH];
|
|
let mut aa = vec![0.0f64; MDEPTH];
|
|
|
|
// 初始化
|
|
for i in 0..nevku {
|
|
levcom.ymku[i][0] = 0.0;
|
|
levcom.emku[i][0] = 0.0;
|
|
for id in 0..nd {
|
|
gwe[id][i][0] = 0.0;
|
|
gwb[id][i][0] = 0.0;
|
|
}
|
|
}
|
|
|
|
for i in 0..nodku {
|
|
levcom.ymku[i][1] = 0.0;
|
|
levcom.emku[i][1] = 0.0;
|
|
for id in 0..nd {
|
|
gwe[id][i][1] = 0.0;
|
|
gwb[id][i][1] = 0.0;
|
|
}
|
|
}
|
|
|
|
let nevod = nevku + nodku;
|
|
if nevod > 100 {
|
|
quit_func(
|
|
"Too many superlevels in a single Fe ion",
|
|
nevku as i32,
|
|
nodku as i32,
|
|
);
|
|
}
|
|
|
|
// 初始化碰撞矩阵
|
|
for i in 0..nevod {
|
|
for j in 0..nevod {
|
|
colkur.omes[i][j] = 0.0;
|
|
}
|
|
}
|
|
|
|
// 检查占据概率计算选项
|
|
let iw_sup = params.ifwop[params.ionpar.nfirst[ion] as usize];
|
|
if iw_sup >= 2 {
|
|
// 预计算温度相关量
|
|
for id in 0..nd {
|
|
params.modpar.temp1[id] = UN / params.modpar.temp[id];
|
|
aa[id] = CCOR * (params.modpar.elec[id].ln() * SIXTH).exp() / params.modpar.temp[id].sqrt();
|
|
}
|
|
|
|
let zz = params.ionpar.iz[ion] as f64;
|
|
if params.ionpar.iz[ion] > 10 {
|
|
quit_func(
|
|
"Too high Fe, Ni or Cr ion: ion,iz",
|
|
ion as i32,
|
|
params.ionpar.iz[ion],
|
|
);
|
|
}
|
|
|
|
let iat = params.levpar.iatm[params.ionpar.nfirst[ion] as usize] as usize;
|
|
let mut e0 = E0FE[params.ionpar.iz[ion] as usize - 1];
|
|
if params.levpar.iel[params.ionpar.nfirst[ion] as usize] >= 0 {
|
|
// 检查原子类型
|
|
let numat = params.levpar.iel[params.ionpar.nfirst[ion] as usize]; // 简化处理
|
|
if numat == 28 {
|
|
e0 = E0NI[params.ionpar.iz[ion] as usize - 1];
|
|
} else if numat == 24 {
|
|
e0 = E0CR[params.ionpar.iz[ion] as usize - 1];
|
|
}
|
|
}
|
|
|
|
// 打开 Kurucz 文件
|
|
let file = File::open(params.fiodf1)?;
|
|
let mut reader = BufReader::new(file);
|
|
|
|
// 读取头部
|
|
let header = read_kurucz_header(&mut reader)?;
|
|
let nlinku = header.nlinku;
|
|
let keve = header.keve as usize;
|
|
let kodd = header.kodd as usize;
|
|
|
|
levcom.nlinku = nlinku;
|
|
levcom.keve = keve as i32;
|
|
levcom.kodd = kodd as i32;
|
|
|
|
if keve + kodd > 15000 {
|
|
quit_func(
|
|
"Too many levels in Kurucz file",
|
|
keve as i32,
|
|
kodd as i32,
|
|
);
|
|
}
|
|
|
|
// 读取偶宇称能级
|
|
for k in 0..keve {
|
|
let level = read_kurucz_level(&mut reader)?;
|
|
let yj = level.yj;
|
|
let mut e = level.e;
|
|
let ar = level.ar;
|
|
let sr = level.sr;
|
|
let wr = level.wr;
|
|
|
|
let gev = TWO * yj + UN;
|
|
|
|
if e < 0.0 {
|
|
e = -e;
|
|
if iobs == 0 {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// 确定超级能级索引
|
|
let mut ksl = 0;
|
|
if e <= levcom.xev[0][ion] {
|
|
ksl = 1;
|
|
}
|
|
for i in 1..nevku {
|
|
if e <= levcom.xev[i][ion] && e > levcom.xev[i - 1][ion] {
|
|
ksl = (i + 1) as i32;
|
|
}
|
|
}
|
|
|
|
if ksl == 0 {
|
|
// WRITE(10,*) 'Error with even levels', E, YJ
|
|
eprintln!("Error with even levels: E={}, YJ={}", e, yj);
|
|
}
|
|
|
|
colkur.kku[k] = ksl;
|
|
colkur.gku[k] = gev;
|
|
colkur.eku[k] = e;
|
|
|
|
levcom.ymku[(ksl - 1) as usize][0] += gev;
|
|
levcom.emku[(ksl - 1) as usize][0] += gev * e;
|
|
|
|
if iw_sup == 2 {
|
|
let ebcm = e / BOLCM;
|
|
for id in 0..nd {
|
|
let gwx = gev * (-ebcm * params.modpar.temp1[id]).exp();
|
|
gwb[id][(ksl - 1) as usize][0] += gwx;
|
|
gwe[id][(ksl - 1) as usize][0] += gwx * e;
|
|
}
|
|
} else if iw_sup == 3 {
|
|
let ebcm = e / BOLCM;
|
|
if e < e0 {
|
|
let xn = (e0 / (e0 - e)).sqrt();
|
|
for id in 0..nd {
|
|
let wid = wn_func(xn, aa[id], params.modpar.elec[id], zz, params.bergfc);
|
|
let gwx = gev * wid * (-ebcm * params.modpar.temp1[id]).exp();
|
|
gwb[id][(ksl - 1) as usize][0] += gwx;
|
|
gwe[id][(ksl - 1) as usize][0] += gwx * e;
|
|
}
|
|
} else {
|
|
for id in 0..nd {
|
|
let wid = UN;
|
|
let gwx = gev * wid * (-ebcm * params.modpar.temp1[id]).exp();
|
|
gwb[id][(ksl - 1) as usize][0] += gwx;
|
|
gwe[id][(ksl - 1) as usize][0] += gwx * e;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 存储能级数据
|
|
if k < levcom.eev.len() {
|
|
levcom.eev[k] = e;
|
|
levcom.aev[k] = ar;
|
|
levcom.sev[k] = sr;
|
|
levcom.wev[k] = wr;
|
|
levcom.ksev[k] = ksl;
|
|
}
|
|
}
|
|
|
|
// 检查偶宇称超级能级
|
|
for i in 0..nevku {
|
|
if levcom.ymku[i][0] == 0.0 {
|
|
quit_func("No levels in even superlevel", (i + 1) as i32, (i + 1) as i32);
|
|
}
|
|
levcom.emku[i][0] /= levcom.ymku[i][0];
|
|
}
|
|
|
|
// 读取奇宇称能级
|
|
for k in 0..kodd {
|
|
let level = read_kurucz_level(&mut reader)?;
|
|
let yj = level.yj;
|
|
let mut e = level.e;
|
|
let ar = level.ar;
|
|
let sr = level.sr;
|
|
let wr = level.wr;
|
|
|
|
let god = TWO * yj + UN;
|
|
|
|
if e < 0.0 {
|
|
e = -e;
|
|
if iobs == 0 {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// 确定超级能级索引
|
|
let mut ksl = 0;
|
|
if e <= levcom.xod[0][ion] {
|
|
ksl = 1;
|
|
}
|
|
for i in 1..nodku {
|
|
if e <= levcom.xod[i][ion] && e > levcom.xod[i - 1][ion] {
|
|
ksl = (i + 1) as i32;
|
|
}
|
|
}
|
|
|
|
if ksl == 0 {
|
|
eprintln!("Error with odd levels: E={}, YJ={}", e, yj);
|
|
}
|
|
|
|
let kku_idx = k + keve;
|
|
colkur.kku[kku_idx] = ksl + nevku as i32;
|
|
colkur.gku[kku_idx] = god;
|
|
colkur.eku[kku_idx] = e;
|
|
|
|
levcom.ymku[(ksl - 1) as usize][1] += god;
|
|
levcom.emku[(ksl - 1) as usize][1] += god * e;
|
|
|
|
if iw_sup == 2 {
|
|
let ebcm = e / BOLCM;
|
|
for id in 0..nd {
|
|
let gwx = god * (-ebcm * params.modpar.temp1[id]).exp();
|
|
gwb[id][(ksl - 1) as usize][1] += gwx;
|
|
gwe[id][(ksl - 1) as usize][1] += gwx * e;
|
|
}
|
|
} else if iw_sup == 3 {
|
|
let ebcm = e / BOLCM;
|
|
if e < e0 {
|
|
let xn = (e0 / (e0 - e)).sqrt();
|
|
for id in 0..nd {
|
|
let wid = wn_func(xn, aa[id], params.modpar.elec[id], zz, params.bergfc);
|
|
let gwx = god * wid * (-ebcm * params.modpar.temp1[id]).exp();
|
|
gwb[id][(ksl - 1) as usize][1] += gwx;
|
|
gwe[id][(ksl - 1) as usize][1] += gwx * e;
|
|
}
|
|
} else {
|
|
for id in 0..nd {
|
|
let wid = UN;
|
|
let gwx = god * wid * (-ebcm * params.modpar.temp1[id]).exp();
|
|
gwb[id][(ksl - 1) as usize][1] += gwx;
|
|
gwe[id][(ksl - 1) as usize][1] += gwx * e;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 存储能级数据
|
|
if k < levcom.eod.len() {
|
|
levcom.eod[k] = e;
|
|
levcom.aod[k] = ar;
|
|
levcom.sod[k] = sr;
|
|
levcom.wod[k] = wr;
|
|
levcom.ksod[k] = ksl;
|
|
}
|
|
}
|
|
|
|
// 检查奇宇称超级能级
|
|
for i in 0..nodku {
|
|
if levcom.ymku[i][1] == 0.0 {
|
|
quit_func("No levels in odd superlevel", (i + 1) as i32, (i + 1) as i32);
|
|
}
|
|
levcom.emku[i][1] /= levcom.ymku[i][1];
|
|
}
|
|
|
|
// 计算碰撞强度
|
|
colkur.gst = 8.63e-6 * GES / params.teff.sqrt();
|
|
let tk0 = UN / BOLCM / params.teff;
|
|
|
|
for i in 0..(keve + kodd - 1) {
|
|
let ki = colkur.kku[i] as usize;
|
|
for j in (i + 1)..(keve + kodd) {
|
|
let kj = colkur.kku[j] as usize;
|
|
let u0 = (colkur.eku[i] - colkur.eku[j]).abs() * tk0;
|
|
colkur.omes[ki][kj] += colkur.gst * (-u0).exp();
|
|
colkur.omes[kj][ki] = colkur.omes[ki][kj];
|
|
}
|
|
}
|
|
|
|
// 排序超级能级能量
|
|
let nlevku = nevku + nodku;
|
|
levcom.nlevku = nlevku as i32;
|
|
|
|
// 复制能量到 EU 数组
|
|
for i in 0..nevku {
|
|
levcom.eu[i] = levcom.emku[i][0];
|
|
}
|
|
for i in 0..nodku {
|
|
levcom.eu[nevku + i] = levcom.emku[i][1];
|
|
}
|
|
|
|
// 排序
|
|
let jen = indexx_func(&levcom.eu[0..nlevku]);
|
|
for i in 0..nlevku {
|
|
levcom.jen[i] = jen[i] as i32;
|
|
}
|
|
|
|
// 计算超级能级的广义占据概率
|
|
for i in 0..nlevku {
|
|
let ii = params.ionpar.nfirst[ion] as usize + i;
|
|
let jj = levcom.jen[i] as usize;
|
|
let mut jk = 1;
|
|
let mut jj_adj = jj;
|
|
if jj >= nevku {
|
|
jj_adj = jj - nevku;
|
|
jk = 2;
|
|
}
|
|
|
|
for id in 0..nd {
|
|
if gwb[id][jj_adj][jk - 1] != 0.0 {
|
|
let esup = gwe[id][jj_adj][jk - 1] / gwb[id][jj_adj][jk - 1];
|
|
let wsup = (esup / BOLCM * params.modpar.temp1[id]).exp() / levcom.ymku[jj_adj][jk - 1];
|
|
wop[ii][id] = wsup * gwb[id][jj_adj][jk - 1];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
// ============================================================================
|
|
// 测试
|
|
// ============================================================================
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_constants() {
|
|
assert!(BOLCM > 0.0);
|
|
assert!(CCOR > 0.0);
|
|
assert!(GES > 0.0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_e0_arrays() {
|
|
// Fe 离子能量应该递增
|
|
for i in 1..E0FE.len() {
|
|
assert!(E0FE[i] > E0FE[i - 1], "E0FE should be increasing");
|
|
}
|
|
// Ni 离子能量应该递增
|
|
for i in 1..E0NI.len() {
|
|
assert!(E0NI[i] > E0NI[i - 1], "E0NI should be increasing");
|
|
}
|
|
// Cr 离子能量应该递增
|
|
for i in 1..E0CR.len() {
|
|
assert!(E0CR[i] > E0CR[i - 1], "E0CR should be increasing");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_colkur_default() {
|
|
let colkur = ColKur::default();
|
|
assert_eq!(colkur.omes.len(), 100);
|
|
assert_eq!(colkur.omes[0].len(), 100);
|
|
assert_eq!(colkur.eku.len(), 15000);
|
|
assert_eq!(colkur.gku.len(), 15000);
|
|
assert_eq!(colkur.kku.len(), 15000);
|
|
}
|
|
|
|
#[test]
|
|
fn test_read_kurucz_header() {
|
|
// 模拟 Kurucz 文件头部行
|
|
// FORMAT: I7, 13X, I6, 12X, I6
|
|
// 列 1-7: NLINKU (I7)
|
|
// 列 8-20: 跳过 (13X)
|
|
// 列 21-26: KEVE (I6)
|
|
// 列 27-38: 跳过 (12X)
|
|
// 列 39-44: KODD (I6)
|
|
let header_line = format!(
|
|
"{:7}{:13}{:6}{:12}{:6}\n",
|
|
"123", // NLINKU at 1-7
|
|
"", // 13X at 8-20
|
|
"456", // KEVE at 21-26
|
|
"", // 12X at 27-38
|
|
"789" // KODD at 39-44
|
|
);
|
|
println!("Header line length: {}", header_line.len());
|
|
println!("Header line: {:?}", header_line);
|
|
let cursor = std::io::Cursor::new(header_line.as_bytes());
|
|
let mut reader = BufReader::new(cursor);
|
|
|
|
let header = read_kurucz_header(&mut reader).unwrap();
|
|
assert_eq!(header.nlinku, 123);
|
|
assert_eq!(header.keve, 456);
|
|
assert_eq!(header.kodd, 789);
|
|
}
|
|
|
|
#[test]
|
|
fn test_read_kurucz_level() {
|
|
// 模拟 Kurucz 能级数据行
|
|
// FORMAT: 8X, F4.1, 4X, F13.3, 18X, 3E9.2
|
|
// 列 1-8: 跳过 (8X)
|
|
// 列 9-12: YJ (F4.1)
|
|
// 列 13-16: 跳过 (4X)
|
|
// 列 17-29: E (F13.3)
|
|
// 列 30-47: 跳过 (18X)
|
|
// 列 48-56: AR (E9.2)
|
|
// 列 57-65: SR (E9.2)
|
|
// 列 66-74: WR (E9.2)
|
|
let level_line = format!(
|
|
"{:8}{:4}{:4}{:13}{:18}{:9}{:9}{:9}\n",
|
|
"", // 8X at 1-8
|
|
"2.5", // YJ at 9-12
|
|
"", // 4X at 13-16
|
|
"12345.678", // E at 17-29
|
|
"", // 18X at 30-47
|
|
"1.23E-03", // AR at 48-56
|
|
"4.56E-04", // SR at 57-65
|
|
"7.89E-05" // WR at 66-74
|
|
);
|
|
println!("Level line length: {}", level_line.len());
|
|
println!("Level line: {:?}", level_line);
|
|
let cursor = std::io::Cursor::new(level_line.as_bytes());
|
|
let mut reader = BufReader::new(cursor);
|
|
|
|
let level = read_kurucz_level(&mut reader).unwrap();
|
|
assert!((level.yj - 2.5).abs() < 0.01, "YJ mismatch: got {}", level.yj);
|
|
assert!((level.e - 12345.678).abs() < 0.001, "E mismatch: got {}", level.e);
|
|
// 使用更宽松的容差
|
|
assert!((level.ar - 1.23e-3).abs() < 1e-4, "AR mismatch: got {}", level.ar);
|
|
assert!((level.sr - 4.56e-4).abs() < 1e-5, "SR mismatch: got {}", level.sr);
|
|
assert!((level.wr - 7.89e-5).abs() < 1e-6, "WR mismatch: got {}", level.wr);
|
|
}
|
|
|
|
#[test]
|
|
fn test_bolcm_calculation() {
|
|
// 验证 BOLCM 常数
|
|
let expected = 1.0e8 / HK / CAS;
|
|
assert!((BOLCM - expected).abs() < 1e-10);
|
|
}
|
|
}
|