#[derive(Debug, Clone, PartialEq)]
pub enum RefineStrategy {
Uniform,
FixedFraction(f64),
Dorflfer(f64),
}
#[derive(Debug, Clone)]
pub struct ZzErrorEstimator {
pub recovered_stress: Vec<Vec<f64>>,
pub element_indicators: Vec<f64>,
pub global_error: f64,
pub n_stress_components: usize,
}
impl ZzErrorEstimator {
pub fn new(n_nodes: usize, n_elements: usize, n_stress_components: usize) -> Self {
Self {
recovered_stress: vec![vec![0.0; n_stress_components]; n_nodes],
element_indicators: vec![0.0; n_elements],
global_error: 0.0,
n_stress_components,
}
}
pub fn n_nodes(&self) -> usize {
self.recovered_stress.len()
}
pub fn n_elements(&self) -> usize {
self.element_indicators.len()
}
}
pub fn stress_patch_recovery(
node_patches: &[Vec<usize>],
raw_stress: &[Vec<f64>],
n_nodes: usize,
n_stress_components: usize,
) -> Vec<Vec<f64>> {
assert_eq!(node_patches.len(), n_nodes);
let mut recovered = vec![vec![0.0f64; n_stress_components]; n_nodes];
for (node, patch) in node_patches.iter().enumerate() {
if patch.is_empty() {
continue;
}
let n_patch = patch.len() as f64;
for &elem in patch {
for c in 0..n_stress_components {
recovered[node][c] += raw_stress[elem][c] / n_patch;
}
}
}
recovered
}
pub fn element_error_indicator(
_elem_idx: usize,
raw_stress: &[f64],
recovered_stress_at_elem: &[f64],
elem_volume: f64,
) -> f64 {
assert_eq!(raw_stress.len(), recovered_stress_at_elem.len());
let sq_diff: f64 = raw_stress
.iter()
.zip(recovered_stress_at_elem.iter())
.map(|(&r, &rec)| (rec - r).powi(2))
.sum();
(elem_volume * sq_diff).sqrt()
}
pub fn global_error_norm(indicators: &[f64]) -> f64 {
let sum_sq: f64 = indicators.iter().map(|&e| e * e).sum();
sum_sq.sqrt()
}
pub fn dorflfer_marking(indicators: &[f64], theta: f64) -> Vec<bool> {
let n = indicators.len();
let total_sq: f64 = indicators.iter().map(|&e| e * e).sum();
let target = theta * total_sq;
let mut sorted_idx: Vec<usize> = (0..n).collect();
sorted_idx.sort_by(|&a, &b| {
indicators[b]
.partial_cmp(&indicators[a])
.unwrap_or(std::cmp::Ordering::Equal)
});
let mut marked = vec![false; n];
let mut accumulated = 0.0;
for idx in sorted_idx {
if accumulated >= target {
break;
}
marked[idx] = true;
accumulated += indicators[idx] * indicators[idx];
}
marked
}
pub fn apply_refine_strategy(indicators: &[f64], strategy: &RefineStrategy) -> Vec<bool> {
match strategy {
RefineStrategy::Uniform => vec![true; indicators.len()],
RefineStrategy::FixedFraction(frac) => {
let n = indicators.len();
let n_mark = ((frac * n as f64).ceil() as usize).min(n);
let mut sorted_idx: Vec<usize> = (0..n).collect();
sorted_idx.sort_by(|&a, &b| {
indicators[b]
.partial_cmp(&indicators[a])
.unwrap_or(std::cmp::Ordering::Equal)
});
let mut marked = vec![false; n];
for &idx in &sorted_idx[..n_mark] {
marked[idx] = true;
}
marked
}
RefineStrategy::Dorflfer(theta) => dorflfer_marking(indicators, *theta),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_zz_new_sizes() {
let est = ZzErrorEstimator::new(10, 8, 3);
assert_eq!(est.n_nodes(), 10);
assert_eq!(est.n_elements(), 8);
}
#[test]
fn test_zz_recovered_stress_initialized_zero() {
let est = ZzErrorEstimator::new(5, 4, 3);
for row in &est.recovered_stress {
for &v in row {
assert!(v.abs() < 1e-15);
}
}
}
#[test]
fn test_zz_element_indicators_initialized_zero() {
let est = ZzErrorEstimator::new(5, 4, 3);
for &ind in &est.element_indicators {
assert!(ind.abs() < 1e-15);
}
}
#[test]
fn test_zz_global_error_initialized_zero() {
let est = ZzErrorEstimator::new(5, 4, 3);
assert!(est.global_error.abs() < 1e-15);
}
#[test]
fn test_zz_3d_components() {
let est = ZzErrorEstimator::new(4, 2, 6);
assert_eq!(est.n_stress_components, 6);
assert_eq!(est.recovered_stress[0].len(), 6);
}
#[test]
fn test_patch_recovery_single_patch_elem() {
let patches = vec![vec![0usize]];
let raw = vec![vec![1.0, 2.0, 3.0]];
let rec = stress_patch_recovery(&patches, &raw, 1, 3);
assert_eq!(rec.len(), 1);
assert!((rec[0][0] - 1.0).abs() < 1e-14);
assert!((rec[0][1] - 2.0).abs() < 1e-14);
assert!((rec[0][2] - 3.0).abs() < 1e-14);
}
#[test]
fn test_patch_recovery_average_two_elems() {
let patches = vec![vec![0, 1]];
let raw = vec![vec![2.0, 4.0], vec![4.0, 0.0]];
let rec = stress_patch_recovery(&patches, &raw, 1, 2);
assert!((rec[0][0] - 3.0).abs() < 1e-14);
assert!((rec[0][1] - 2.0).abs() < 1e-14);
}
#[test]
fn test_patch_recovery_empty_patch() {
let patches = vec![vec![]];
let raw: Vec<Vec<f64>> = vec![];
let rec = stress_patch_recovery(&patches, &raw, 1, 2);
assert!((rec[0][0]).abs() < 1e-15);
assert!((rec[0][1]).abs() < 1e-15);
}
#[test]
fn test_patch_recovery_multiple_nodes() {
let patches = vec![vec![0], vec![1], vec![0, 1]];
let raw = vec![vec![10.0], vec![20.0]];
let rec = stress_patch_recovery(&patches, &raw, 3, 1);
assert!((rec[0][0] - 10.0).abs() < 1e-14);
assert!((rec[1][0] - 20.0).abs() < 1e-14);
assert!((rec[2][0] - 15.0).abs() < 1e-14);
}
#[test]
fn test_patch_recovery_zero_raw() {
let patches = vec![vec![0, 1, 2]];
let raw = vec![vec![0.0], vec![0.0], vec![0.0]];
let rec = stress_patch_recovery(&patches, &raw, 1, 1);
assert!(rec[0][0].abs() < 1e-14);
}
#[test]
fn test_element_error_zero_difference() {
let raw = vec![1.0, 2.0, 3.0];
let rec = vec![1.0, 2.0, 3.0];
let eta = element_error_indicator(0, &raw, &rec, 1.0);
assert!(eta.abs() < 1e-14);
}
#[test]
fn test_element_error_nonzero() {
let raw = vec![0.0];
let rec = vec![1.0];
let eta = element_error_indicator(0, &raw, &rec, 1.0);
assert!((eta - 1.0).abs() < 1e-14);
}
#[test]
fn test_element_error_scales_with_volume() {
let raw = vec![0.0];
let rec = vec![1.0];
let eta1 = element_error_indicator(0, &raw, &rec, 1.0);
let eta2 = element_error_indicator(0, &raw, &rec, 4.0);
assert!((eta2 / eta1 - 2.0).abs() < 1e-12);
}
#[test]
fn test_element_error_non_negative() {
let raw = vec![5.0, -3.0, 1.0];
let rec = vec![2.0, 1.0, -4.0];
let eta = element_error_indicator(0, &raw, &rec, 0.5);
assert!(eta >= 0.0);
}
#[test]
fn test_element_error_3_components() {
let raw = vec![1.0, 2.0, 3.0];
let rec = vec![3.0, 4.0, 5.0];
let eta = element_error_indicator(0, &raw, &rec, 1.0);
assert!((eta - 12.0f64.sqrt()).abs() < 1e-12);
}
#[test]
fn test_global_error_norm_zero() {
let ind = vec![0.0, 0.0, 0.0];
assert!(global_error_norm(&ind).abs() < 1e-14);
}
#[test]
fn test_global_error_norm_single() {
let ind = vec![3.0];
assert!((global_error_norm(&ind) - 3.0).abs() < 1e-14);
}
#[test]
fn test_global_error_norm_pythagorean() {
let ind = vec![3.0, 4.0];
assert!((global_error_norm(&ind) - 5.0).abs() < 1e-12);
}
#[test]
fn test_global_error_norm_all_ones() {
let ind = vec![1.0; 9];
assert!((global_error_norm(&ind) - 3.0).abs() < 1e-12);
}
#[test]
fn test_dorfler_theta_one_marks_all() {
let ind = vec![1.0, 2.0, 3.0, 0.5];
let marked = dorflfer_marking(&ind, 1.0);
assert!(marked.iter().all(|&m| m));
}
#[test]
fn test_dorfler_marks_largest_first() {
let ind = vec![1.0, 10.0, 2.0];
let marked = dorflfer_marking(&ind, 0.5);
assert!(marked[1]); }
#[test]
fn test_dorfler_count_reasonable() {
let ind = vec![1.0, 1.0, 1.0, 1.0];
let marked = dorflfer_marking(&ind, 0.5);
let n_marked: usize = marked.iter().filter(|&&m| m).count();
assert!((1..=4).contains(&n_marked));
}
#[test]
fn test_dorfler_returns_correct_length() {
let ind = vec![3.0, 1.0, 4.0, 1.0, 5.0];
let marked = dorflfer_marking(&ind, 0.7);
assert_eq!(marked.len(), 5);
}
#[test]
fn test_dorfler_all_zero_indicators() {
let ind = vec![0.0, 0.0, 0.0];
let marked = dorflfer_marking(&ind, 0.5);
assert_eq!(marked.len(), 3);
}
#[test]
fn test_uniform_marks_all() {
let ind = vec![1.0, 0.5, 2.0];
let marked = apply_refine_strategy(&ind, &RefineStrategy::Uniform);
assert!(marked.iter().all(|&m| m));
}
#[test]
fn test_fixed_fraction_marks_correct_count() {
let ind = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let marked = apply_refine_strategy(&ind, &RefineStrategy::FixedFraction(0.4));
let n_marked: usize = marked.iter().filter(|&&m| m).count();
assert_eq!(n_marked, 2); }
#[test]
fn test_fixed_fraction_marks_largest() {
let ind = vec![1.0, 5.0, 2.0, 4.0];
let marked = apply_refine_strategy(&ind, &RefineStrategy::FixedFraction(0.25));
assert!(marked[1]);
}
#[test]
fn test_dorfler_strategy_wrapper() {
let ind = vec![1.0, 10.0, 2.0];
let marked = apply_refine_strategy(&ind, &RefineStrategy::Dorflfer(0.5));
assert!(marked[1]);
}
#[test]
fn test_refine_strategy_clone() {
let s = RefineStrategy::FixedFraction(0.3);
let _s2 = s.clone();
}
#[test]
fn test_refine_strategy_eq() {
assert_eq!(RefineStrategy::Uniform, RefineStrategy::Uniform);
assert_ne!(
RefineStrategy::FixedFraction(0.3),
RefineStrategy::FixedFraction(0.7)
);
}
#[test]
fn test_fixed_fraction_full() {
let ind = vec![1.0, 2.0];
let marked = apply_refine_strategy(&ind, &RefineStrategy::FixedFraction(1.0));
assert!(marked.iter().all(|&m| m));
}
}