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

162 lines
4.3 KiB
Rust

//! 网格点评估
//!
//! 原始 Fortran: gridp.f
//! 将曲线 y=f(x) 分成 n-1 等长段,确定新网格点
use crate::state::MDEPTH;
/// 网格点评估
///
/// 将曲线 Y=f(X) 分成 N-1 个等长段,
/// 每段端点的 x 坐标定义新网格点
///
/// # 参数
/// - `x`: 原始 x 坐标数组
/// - `y`: 原始 y 坐标数组
/// - `xnew`: 新 x 坐标数组 (输出)
/// - `ynew`: 新 y 坐标数组 (输出)
/// - `n`: 点数
///
/// # 算法
/// 1. 计算原始曲线各段长度
/// 2. 计算总长度和新段长度
/// 3. 沿曲线均匀采样新点
pub fn gridp(x: &[f64], y: &[f64], xnew: &mut [f64], ynew: &mut [f64], n: usize) {
// 工作数组:存储各段长度
let mut z = vec![0.0; MDEPTH.min(n)];
// 计算原始曲线各段长度和总长度
let mut ztot = 0.0;
for i in 1..n {
let dx = x[i] - x[i - 1];
let dy = y[i] - y[i - 1];
z[i - 1] = (dx * dx + dy * dy).sqrt();
ztot += z[i - 1];
}
// 新段长度
let z0 = ztot / (n - 1) as f64;
// 初始化
let mut iseg: usize = 0;
let mut xlast = x[iseg];
let mut ylast = y[iseg];
let mut zrest = z[iseg];
let mut zrem = z0;
let mut ip: usize = 0;
xnew[ip] = x[0];
ynew[ip] = y[0];
// 沿曲线采样新点
loop {
if zrem < zrest {
// 在当前段内
zrest -= zrem;
xlast += zrem * (x[iseg + 1] - x[iseg]) / z[iseg];
ylast += zrem * (y[iseg + 1] - y[iseg]) / z[iseg];
ip += 1;
xnew[ip] = xlast;
ynew[ip] = ylast;
zrem = z0;
if ip >= n - 1 {
break;
}
} else {
// 跨到下一段
zrem -= zrest;
iseg += 1;
if iseg >= n - 1 {
// 已到达最后一段
break;
}
xlast = x[iseg];
ylast = y[iseg];
zrest = z[iseg];
}
}
// 最后一个点
xnew[n - 1] = x[n - 1];
ynew[n - 1] = y[n - 1];
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gridp_linear() {
// 线性函数 y = x
let n = 5;
let x = vec![0.0, 1.0, 2.0, 3.0, 4.0];
let y = vec![0.0, 1.0, 2.0, 3.0, 4.0];
let mut xnew = vec![0.0; n];
let mut ynew = vec![0.0; n];
gridp(&x, &y, &mut xnew, &mut ynew, n);
// 端点应保持不变
assert!((xnew[0] - x[0]).abs() < 1e-10);
assert!((xnew[n - 1] - x[n - 1]).abs() < 1e-10);
assert!((ynew[0] - y[0]).abs() < 1e-10);
assert!((ynew[n - 1] - y[n - 1]).abs() < 1e-10);
// 对于线性函数,新网格应与原网格相同
for i in 0..n {
assert!((xnew[i] - x[i]).abs() < 1e-10);
assert!((ynew[i] - y[i]).abs() < 1e-10);
}
}
#[test]
fn test_gridp_curve() {
// 曲线 y = x^2
let n = 5;
let x = vec![0.0, 1.0, 2.0, 3.0, 4.0];
let y = vec![0.0, 1.0, 4.0, 9.0, 16.0];
let mut xnew = vec![0.0; n];
let mut ynew = vec![0.0; n];
gridp(&x, &y, &mut xnew, &mut ynew, n);
// 端点应保持不变
assert!((xnew[0] - 0.0).abs() < 1e-10);
assert!((xnew[n - 1] - 4.0).abs() < 1e-10);
assert!((ynew[0] - 0.0).abs() < 1e-10);
assert!((ynew[n - 1] - 16.0).abs() < 1e-10);
// 新网格应单调递增
for i in 1..n {
assert!(xnew[i] > xnew[i - 1]);
assert!(ynew[i] > ynew[i - 1]);
}
}
#[test]
fn test_gridp_uniform_segment_length() {
// 验证新网格各段长度大致相等
let n = 10;
let x: Vec<f64> = (0..n).map(|i| i as f64).collect();
let y: Vec<f64> = x.iter().map(|&x| x * x).collect();
let mut xnew = vec![0.0; n];
let mut ynew = vec![0.0; n];
gridp(&x, &y, &mut xnew, &mut ynew, n);
// 计算各段长度
let mut lengths = Vec::new();
for i in 1..n {
let dx = xnew[i] - xnew[i - 1];
let dy = ynew[i] - ynew[i - 1];
lengths.push((dx * dx + dy * dy).sqrt());
}
// 验证各段长度相近
let avg_len: f64 = lengths.iter().sum::<f64>() / lengths.len() as f64;
for &len in &lengths {
assert!((len - avg_len).abs() / avg_len < 0.1); // 误差 < 10%
}
}
}