// Mesh refinement algorithms use crate::error::{CfdError, CfdResult}; /// Refinement strategy #[derive(Debug, Clone, Copy)] pub enum RefinementStrategy { /// Uniform refinement - refine all cells Uniform, /// Adaptive refinement based on error indicators Adaptive, /// Refine specific regions Regional, } /// Refinement criteria #[derive(Debug, Clone)] pub struct RefinementCriteria { /// Maximum allowed error per cell pub max_error: f64, /// Minimum cell size pub min_cell_size: f64, /// Maximum cell size pub max_cell_size: f64, /// Maximum refinement levels pub max_levels: usize, } impl Default for RefinementCriteria { fn default() -> Self { Self { max_error: 1e-3, min_cell_size: 1e-6, max_cell_size: 1e6, max_levels: 10, } } } /// Adaptive mesh refinement implementation pub struct AdaptiveRefinement { criteria: RefinementCriteria, current_level: usize, } impl AdaptiveRefinement { /// Create new adaptive refinement #[must_use] pub fn new(criteria: RefinementCriteria) -> Self { Self { criteria, current_level: 0, } } /// Check if refinement is needed #[must_use] pub fn needs_refinement(&self, error: f64, cell_size: f64, current_level: usize) -> bool { error > self.criteria.max_error && cell_size > self.criteria.min_cell_size && current_level < self.criteria.max_levels } /// Mark cells for refinement based on error indicators pub fn mark_cells_for_refinement( &self, errors: &[f64], cell_sizes: &[f64], cell_levels: &[usize], ) -> CfdResult> { if errors.len() != cell_sizes.len() || errors.len() != cell_levels.len() { return Err(CfdError::mesh( "Error, cell size, and level arrays must have same length", )); } let mut marked_cells = Vec::new(); for (i, (&error, (&size, &level))) in errors .iter() .zip(cell_sizes.iter().zip(cell_levels.iter())) .enumerate() { if self.needs_refinement(error, size, level) { marked_cells.push(i); } } Ok(marked_cells) } /// Compute gradient-based error indicator pub fn compute_gradient_error_indicator( &self, velocity_field: &[nalgebra::Vector3], ) -> CfdResult> { let mut errors = Vec::with_capacity(velocity_field.len()); for velocity in velocity_field { // Use velocity magnitude as a simple gradient indicator // In practice, this would be computed from actual velocity gradients let error = velocity.magnitude(); errors.push(error); } Ok(errors) } /// Compute residual-based error indicator pub fn compute_residual_error_indicator( &self, momentum_residuals: &[f64], continuity_residuals: &[f64], ) -> CfdResult> { if momentum_residuals.len() != continuity_residuals.len() { return Err(CfdError::mesh( "Momentum and continuity residual arrays must have same length", )); } let mut errors = Vec::with_capacity(momentum_residuals.len()); for (&mom_res, &cont_res) in momentum_residuals.iter().zip(continuity_residuals.iter()) { // Combine momentum and continuity residuals using L2 norm let combined_error = (mom_res * mom_res + cont_res * cont_res).sqrt(); errors.push(combined_error); } Ok(errors) } }