Skip to main content

scirs2_integrate/
amr_advanced.rs

1//! Advanced Adaptive Mesh Refinement (AMR) with sophisticated error indicators
2//!
3//! This module provides state-of-the-art adaptive mesh refinement techniques
4//! including gradient-based refinement, feature detection, load balancing,
5//! and hierarchical mesh management for complex PDE solutions.
6
7use crate::common::IntegrateFloat;
8use crate::error::{IntegrateError, IntegrateResult};
9use scirs2_core::ndarray::{Array1, Array2};
10use std::collections::{HashMap, HashSet};
11
12/// Advanced AMR manager with multiple refinement strategies
13pub struct AdvancedAMRManager<F: IntegrateFloat> {
14    /// Current mesh hierarchy
15    pub mesh_hierarchy: MeshHierarchy<F>,
16    /// Refinement criteria
17    pub refinement_criteria: Vec<Box<dyn RefinementCriterion<F>>>,
18    /// Load balancing strategy
19    pub load_balancer: Option<Box<dyn LoadBalancer<F>>>,
20    /// Maximum refinement levels
21    pub max_levels: usize,
22    /// Minimum cell size
23    pub min_cell_size: F,
24    /// Coarsening tolerance
25    pub coarsening_tolerance: F,
26    /// Error tolerance for refinement
27    pub refinement_tolerance: F,
28    /// Adaptation frequency
29    pub adaptation_frequency: usize,
30    /// Current adaptation step
31    current_step: usize,
32}
33
34/// Hierarchical mesh structure supporting multiple levels
35#[derive(Debug, Clone)]
36pub struct MeshHierarchy<F: IntegrateFloat> {
37    /// Mesh levels (0 = coarsest)
38    pub levels: Vec<AdaptiveMeshLevel<F>>,
39    /// Parent-child relationships
40    pub hierarchy_map: HashMap<CellId, Vec<CellId>>,
41    /// Ghost cell information for parallel processing
42    pub ghost_cells: HashMap<usize, Vec<CellId>>, // level -> ghost cells
43}
44
45/// Single level in adaptive mesh
46#[derive(Debug, Clone)]
47pub struct AdaptiveMeshLevel<F: IntegrateFloat> {
48    /// Level number (0 = coarsest)
49    pub level: usize,
50    /// Active cells at this level
51    pub cells: HashMap<CellId, AdaptiveCell<F>>,
52    /// Grid spacing at this level
53    pub grid_spacing: F,
54    /// Boundary information
55    pub boundary_cells: HashSet<CellId>,
56}
57
58/// Individual adaptive cell
59#[derive(Debug, Clone)]
60pub struct AdaptiveCell<F: IntegrateFloat> {
61    /// Unique cell identifier
62    pub id: CellId,
63    /// Cell center coordinates
64    pub center: Array1<F>,
65    /// Cell size
66    pub size: F,
67    /// Solution value(s) in cell
68    pub solution: Array1<F>,
69    /// Error estimate for cell
70    pub error_estimate: F,
71    /// Refinement flag
72    pub refinement_flag: RefinementFlag,
73    /// Activity status
74    pub is_active: bool,
75    /// Neighboring cell IDs
76    pub neighbors: Vec<CellId>,
77    /// Parent cell ID (if refined)
78    pub parent: Option<CellId>,
79    /// Children cell IDs (if coarsened)
80    pub children: Vec<CellId>,
81}
82
83/// Cell identifier
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
85pub struct CellId {
86    pub level: usize,
87    pub index: usize,
88}
89
90/// Refinement action flags
91#[derive(Debug, Clone, Copy, PartialEq)]
92pub enum RefinementFlag {
93    /// No action needed
94    None,
95    /// Cell should be refined
96    Refine,
97    /// Cell should be coarsened
98    Coarsen,
99    /// Cell marked for potential refinement
100    Tagged,
101}
102
103/// Trait for refinement criteria
104pub trait RefinementCriterion<F: IntegrateFloat>: Send + Sync {
105    /// Evaluate refinement criterion for a cell
106    fn evaluate(&self, cell: &AdaptiveCell<F>, neighbors: &[&AdaptiveCell<F>]) -> F;
107
108    /// Get criterion name
109    fn name(&self) -> &'static str;
110
111    /// Get criterion weight in combined evaluation
112    fn weight(&self) -> F {
113        F::one()
114    }
115}
116
117/// Gradient-based refinement criterion
118pub struct GradientRefinementCriterion<F: IntegrateFloat> {
119    /// Component to analyze (None = all components)
120    pub component: Option<usize>,
121    /// Gradient threshold
122    pub threshold: F,
123    /// Relative tolerance
124    pub relative_tolerance: F,
125}
126
127/// Feature detection refinement criterion
128pub struct FeatureDetectionCriterion<F: IntegrateFloat> {
129    /// Feature detection threshold
130    pub threshold: F,
131    /// Feature types to detect
132    pub feature_types: Vec<FeatureType>,
133    /// Window size for feature detection
134    pub window_size: usize,
135}
136
137/// Curvature-based refinement criterion
138pub struct CurvatureRefinementCriterion<F: IntegrateFloat> {
139    /// Curvature threshold
140    pub threshold: F,
141    /// Approximation order for curvature calculation
142    pub approximation_order: usize,
143}
144
145/// Load balancing strategy trait
146pub trait LoadBalancer<F: IntegrateFloat>: Send + Sync {
147    /// Balance computational load across processors/threads
148    fn balance(&self, hierarchy: &mut MeshHierarchy<F>) -> IntegrateResult<()>;
149}
150
151/// Zoltan-style geometric load balancer
152pub struct GeometricLoadBalancer<F: IntegrateFloat> {
153    /// Number of partitions
154    pub num_partitions: usize,
155    /// Load imbalance tolerance
156    pub imbalance_tolerance: F,
157    /// Partitioning method
158    pub method: PartitioningMethod,
159}
160
161/// Types of features to detect
162#[derive(Debug, Clone, Copy, PartialEq)]
163pub enum FeatureType {
164    /// Sharp gradients
165    SharpGradient,
166    /// Discontinuities
167    Discontinuity,
168    /// Local extrema
169    LocalExtremum,
170    /// Oscillatory behavior
171    Oscillation,
172    /// Boundary layers
173    BoundaryLayer,
174}
175
176/// Partitioning methods
177#[derive(Debug, Clone, Copy)]
178pub enum PartitioningMethod {
179    /// Recursive coordinate bisection
180    RCB,
181    /// Space-filling curves
182    SFC,
183    /// Graph partitioning
184    Graph,
185}
186
187/// AMR adaptation result
188pub struct AMRAdaptationResult<F: IntegrateFloat> {
189    /// Number of cells refined
190    pub cells_refined: usize,
191    /// Number of cells coarsened
192    pub cells_coarsened: usize,
193    /// Total active cells after adaptation
194    pub total_active_cells: usize,
195    /// Load balance quality metric
196    pub load_balance_quality: F,
197    /// Memory usage change
198    pub memory_change: i64,
199    /// Adaptation time
200    pub adaptation_time: std::time::Duration,
201}
202
203impl<F: IntegrateFloat> AdvancedAMRManager<F> {
204    /// Create new advanced AMR manager
205    pub fn new(_initial_mesh: AdaptiveMeshLevel<F>, max_levels: usize, min_cellsize: F) -> Self {
206        let mesh_hierarchy = MeshHierarchy {
207            levels: vec![_initial_mesh],
208            hierarchy_map: HashMap::new(),
209            ghost_cells: HashMap::new(),
210        };
211
212        Self {
213            mesh_hierarchy,
214            refinement_criteria: Vec::new(),
215            load_balancer: None,
216            max_levels,
217            min_cell_size: min_cellsize,
218            coarsening_tolerance: F::from(0.1).expect("Failed to convert constant to float"),
219            refinement_tolerance: F::from(1.0).expect("Failed to convert constant to float"),
220            adaptation_frequency: 1,
221            current_step: 0,
222        }
223    }
224
225    /// Add refinement criterion
226    pub fn add_criterion(&mut self, criterion: Box<dyn RefinementCriterion<F>>) {
227        self.refinement_criteria.push(criterion);
228    }
229
230    /// Set load balancer
231    pub fn set_load_balancer(&mut self, balancer: Box<dyn LoadBalancer<F>>) {
232        self.load_balancer = Some(balancer);
233    }
234
235    /// Perform adaptive mesh refinement
236    pub fn adapt_mesh(&mut self, solution: &Array2<F>) -> IntegrateResult<AMRAdaptationResult<F>> {
237        let start_time = std::time::Instant::now();
238        let initial_cells = self.count_active_cells();
239
240        self.current_step += 1;
241
242        // Skip adaptation if not at adaptation frequency
243        if !self.current_step.is_multiple_of(self.adaptation_frequency) {
244            return Ok(AMRAdaptationResult {
245                cells_refined: 0,
246                cells_coarsened: 0,
247                total_active_cells: initial_cells,
248                load_balance_quality: F::one(),
249                memory_change: 0,
250                adaptation_time: start_time.elapsed(),
251            });
252        }
253
254        // Step 1: Update solution values in cells
255        self.update_cell_solutions(solution)?;
256
257        // Step 2: Evaluate refinement criteria
258        self.evaluate_refinement_criteria()?;
259
260        // Step 3: Flag cells for refinement/coarsening
261        let _refine_count_coarsen_count = self.flag_cells_for_adaptation()?;
262
263        // Step 4: Perform refinement
264        let cells_refined = self.refine_flagged_cells()?;
265
266        // Step 5: Perform coarsening
267        let cells_coarsened = self.coarsen_flagged_cells()?;
268
269        // Step 6: Load balancing
270        let load_balance_quality = if let Some(ref balancer) = self.load_balancer {
271            balancer.balance(&mut self.mesh_hierarchy)?;
272            self.assess_load_balance()
273        } else {
274            F::one()
275        };
276
277        // Step 7: Update ghost cells
278        self.update_ghost_cells()?;
279
280        let final_cells = self.count_active_cells();
281        let memory_change = (final_cells as i64 - initial_cells as i64) * 8; // Rough estimate
282
283        Ok(AMRAdaptationResult {
284            cells_refined,
285            cells_coarsened,
286            total_active_cells: final_cells,
287            load_balance_quality,
288            memory_change,
289            adaptation_time: start_time.elapsed(),
290        })
291    }
292
293    /// Update solution values in mesh cells
294    fn update_cell_solutions(&mut self, solution: &Array2<F>) -> IntegrateResult<()> {
295        // Map solution array to mesh cells
296        // This is a simplified mapping - in practice would need sophisticated interpolation
297        for level in &mut self.mesh_hierarchy.levels {
298            for cell in level.cells.values_mut() {
299                if cell.is_active {
300                    // Simple mapping - in practice would use proper interpolation
301                    let i = (cell.center[0] * F::from(solution.nrows()).expect("Operation failed"))
302                        .to_usize()
303                        .unwrap_or(0)
304                        .min(solution.nrows() - 1);
305                    let j = if solution.ncols() > 1 && cell.center.len() > 1 {
306                        (cell.center[1] * F::from(solution.ncols()).expect("Operation failed"))
307                            .to_usize()
308                            .unwrap_or(0)
309                            .min(solution.ncols() - 1)
310                    } else {
311                        0
312                    };
313
314                    // Update cell solution (simplified)
315                    if cell.solution.len() == 1 {
316                        cell.solution[0] = solution[[i, j]];
317                    }
318                }
319            }
320        }
321        Ok(())
322    }
323
324    /// Evaluate all refinement criteria for all cells
325    fn evaluate_refinement_criteria(&mut self) -> IntegrateResult<()> {
326        for level in &mut self.mesh_hierarchy.levels {
327            let cellids: Vec<CellId> = level.cells.keys().cloned().collect();
328
329            for cellid in cellids {
330                if let Some(cell) = level.cells.get(&cellid) {
331                    if !cell.is_active {
332                        continue;
333                    }
334
335                    // Get neighboring cells
336                    let neighbor_cells: Vec<&AdaptiveCell<F>> = cell
337                        .neighbors
338                        .iter()
339                        .filter_map(|&neighbor_id| level.cells.get(&neighbor_id))
340                        .collect();
341
342                    // Evaluate all criteria
343                    let mut total_error = F::zero();
344                    let mut total_weight = F::zero();
345
346                    for criterion in &self.refinement_criteria {
347                        let error = criterion.evaluate(cell, &neighbor_cells);
348                        let weight = criterion.weight();
349                        total_error += error * weight;
350                        total_weight += weight;
351                    }
352
353                    // Normalize error estimate
354                    let error_estimate = if total_weight > F::zero() {
355                        total_error / total_weight
356                    } else {
357                        F::zero()
358                    };
359
360                    // Update cell error estimate
361                    if let Some(cell) = level.cells.get_mut(&cellid) {
362                        cell.error_estimate = error_estimate;
363                    }
364                }
365            }
366        }
367        Ok(())
368    }
369
370    /// Flag cells for refinement or coarsening
371    fn flag_cells_for_adaptation(&mut self) -> IntegrateResult<(usize, usize)> {
372        let mut refine_count = 0;
373        let mut coarsen_count = 0;
374
375        // Collect cells that can be coarsened first to avoid borrowing issues
376        let mut cells_to_check: Vec<(usize, CellId, F, usize, F)> = Vec::new();
377
378        for level in &self.mesh_hierarchy.levels {
379            for cell in level.cells.values() {
380                if cell.is_active {
381                    cells_to_check.push((
382                        level.level,
383                        cell.id,
384                        cell.error_estimate,
385                        level.level,
386                        cell.size,
387                    ));
388                }
389            }
390        }
391
392        // Now flag cells based on collected information
393        for (level_idx, cellid, error_estimate, level_num, cell_size) in cells_to_check {
394            if let Some(level) = self.mesh_hierarchy.levels.get_mut(level_idx) {
395                if let Some(cell) = level.cells.get_mut(&cellid) {
396                    // Refinement criterion
397                    if error_estimate > self.refinement_tolerance
398                        && level_num < self.max_levels
399                        && cell_size > self.min_cell_size
400                    {
401                        cell.refinement_flag = RefinementFlag::Refine;
402                        refine_count += 1;
403                    }
404                    // Coarsening criterion (simplified check)
405                    else if error_estimate < self.coarsening_tolerance && level_num > 0 {
406                        cell.refinement_flag = RefinementFlag::Coarsen;
407                        coarsen_count += 1;
408                    } else {
409                        cell.refinement_flag = RefinementFlag::None;
410                    }
411                }
412            }
413        }
414
415        Ok((refine_count, coarsen_count))
416    }
417
418    /// Check if cell can be coarsened (all siblings must be flagged)
419    fn can_coarsen_cell(&self, cell: &AdaptiveCell<F>) -> bool {
420        if let Some(parent_id) = cell.parent {
421            // Check if all sibling cells are also flagged for coarsening
422            if let Some(parent_children) = self.mesh_hierarchy.hierarchy_map.get(&parent_id) {
423                for &child_id in parent_children {
424                    if let Some(level) = self.mesh_hierarchy.levels.get(child_id.level) {
425                        if let Some(sibling) = level.cells.get(&child_id) {
426                            if sibling.refinement_flag != RefinementFlag::Coarsen {
427                                return false;
428                            }
429                        }
430                    }
431                }
432                return true;
433            }
434        }
435        false
436    }
437
438    /// Refine flagged cells
439    fn refine_flagged_cells(&mut self) -> IntegrateResult<usize> {
440        let mut cells_refined = 0;
441
442        // Process each level separately to avoid borrowing issues
443        for level_idx in 0..self.mesh_hierarchy.levels.len() {
444            let cells_to_refine: Vec<CellId> = self.mesh_hierarchy.levels[level_idx]
445                .cells
446                .values()
447                .filter(|cell| cell.refinement_flag == RefinementFlag::Refine)
448                .map(|cell| cell.id)
449                .collect();
450
451            for cellid in cells_to_refine {
452                self.refine_cell(cellid)?;
453                cells_refined += 1;
454            }
455        }
456
457        Ok(cells_refined)
458    }
459
460    /// Refine a single cell
461    fn refine_cell(&mut self, cellid: CellId) -> IntegrateResult<()> {
462        let parent_cell = if let Some(level) = self.mesh_hierarchy.levels.get(cellid.level) {
463            level.cells.get(&cellid).cloned()
464        } else {
465            return Err(IntegrateError::ValueError("Invalid cell level".to_string()));
466        };
467
468        let parent_cell =
469            parent_cell.ok_or_else(|| IntegrateError::ValueError("Cell not found".to_string()))?;
470
471        // Create child level if it doesn't exist
472        let child_level = cellid.level + 1;
473        while self.mesh_hierarchy.levels.len() <= child_level {
474            let new_level = AdaptiveMeshLevel {
475                level: self.mesh_hierarchy.levels.len(),
476                cells: HashMap::new(),
477                grid_spacing: if let Some(last_level) = self.mesh_hierarchy.levels.last() {
478                    last_level.grid_spacing
479                        / F::from(2.0).expect("Failed to convert constant to float")
480                } else {
481                    F::one()
482                },
483                boundary_cells: HashSet::new(),
484            };
485            self.mesh_hierarchy.levels.push(new_level);
486        }
487
488        // Create child cells (2D refinement = 4 children, 3D = 8 children)
489        let num_children = 2_usize.pow(parent_cell.center.len() as u32);
490        let mut child_ids = Vec::new();
491        let child_size =
492            parent_cell.size / F::from(2.0).expect("Failed to convert constant to float");
493
494        for child_idx in 0..num_children {
495            let child_id = CellId {
496                level: child_level,
497                index: self.mesh_hierarchy.levels[child_level].cells.len(),
498            };
499
500            // Compute child center
501            let mut child_center = parent_cell.center.clone();
502            let offset = child_size / F::from(2.0).expect("Failed to convert constant to float");
503
504            // Binary representation determines position
505            for dim in 0..parent_cell.center.len() {
506                if (child_idx >> dim) & 1 == 1 {
507                    child_center[dim] += offset;
508                } else {
509                    child_center[dim] -= offset;
510                }
511            }
512
513            let child_cell = AdaptiveCell {
514                id: child_id,
515                center: child_center,
516                size: child_size,
517                solution: parent_cell.solution.clone(), // Inherit parent solution
518                error_estimate: F::zero(),
519                refinement_flag: RefinementFlag::None,
520                is_active: true,
521                neighbors: Vec::new(),
522                parent: Some(cellid),
523                children: Vec::new(),
524            };
525
526            self.mesh_hierarchy.levels[child_level]
527                .cells
528                .insert(child_id, child_cell);
529            child_ids.push(child_id);
530        }
531
532        // Update hierarchy map
533        self.mesh_hierarchy
534            .hierarchy_map
535            .insert(cellid, child_ids.clone());
536
537        // Deactivate parent cell
538        if let Some(parent) = self.mesh_hierarchy.levels[cellid.level]
539            .cells
540            .get_mut(&cellid)
541        {
542            parent.is_active = false;
543            parent.children = child_ids;
544        }
545
546        // Update neighbor relationships
547        self.update_neighbor_relationships(child_level)?;
548
549        Ok(())
550    }
551
552    /// Coarsen flagged cells
553    fn coarsen_flagged_cells(&mut self) -> IntegrateResult<usize> {
554        let mut cells_coarsened = 0;
555
556        // Process from finest to coarsest level
557        for level_idx in (1..self.mesh_hierarchy.levels.len()).rev() {
558            let parent_cells_to_activate: Vec<CellId> = self.mesh_hierarchy.levels[level_idx]
559                .cells
560                .values()
561                .filter(|cell| cell.refinement_flag == RefinementFlag::Coarsen)
562                .filter_map(|cell| cell.parent)
563                .collect::<HashSet<_>>()
564                .into_iter()
565                .collect();
566
567            for parent_id in parent_cells_to_activate {
568                if self.coarsen_to_parent(parent_id)? {
569                    cells_coarsened += 1;
570                }
571            }
572        }
573
574        Ok(cells_coarsened)
575    }
576
577    /// Coarsen children back to parent cell
578    fn coarsen_to_parent(&mut self, parentid: CellId) -> IntegrateResult<bool> {
579        let child_ids = if let Some(children) = self.mesh_hierarchy.hierarchy_map.get(&parentid) {
580            children.clone()
581        } else {
582            return Ok(false);
583        };
584
585        // Verify all children are flagged for coarsening
586        for &child_id in &child_ids {
587            if let Some(level) = self.mesh_hierarchy.levels.get(child_id.level) {
588                if let Some(child) = level.cells.get(&child_id) {
589                    if child.refinement_flag != RefinementFlag::Coarsen {
590                        return Ok(false);
591                    }
592                }
593            }
594        }
595
596        // Average child solutions for parent
597        let mut avg_solution = Array1::zeros(child_ids.len());
598        if !child_ids.is_empty() {
599            if let Some(first_child_level) = self.mesh_hierarchy.levels.get(child_ids[0].level) {
600                if let Some(first_child) = first_child_level.cells.get(&child_ids[0]) {
601                    avg_solution = Array1::zeros(first_child.solution.len());
602
603                    for &child_id in &child_ids {
604                        if let Some(child_level) = self.mesh_hierarchy.levels.get(child_id.level) {
605                            if let Some(child) = child_level.cells.get(&child_id) {
606                                avg_solution = &avg_solution + &child.solution;
607                            }
608                        }
609                    }
610                    avg_solution /= F::from(child_ids.len()).expect("Operation failed");
611                }
612            }
613        }
614
615        // Reactivate parent cell
616        if let Some(parent_level) = self.mesh_hierarchy.levels.get_mut(parentid.level) {
617            if let Some(parent) = parent_level.cells.get_mut(&parentid) {
618                parent.is_active = true;
619                parent.solution = avg_solution;
620                parent.children.clear();
621                parent.refinement_flag = RefinementFlag::None;
622            }
623        }
624
625        // Remove children from hierarchy
626        for &child_id in &child_ids {
627            if let Some(child_level) = self.mesh_hierarchy.levels.get_mut(child_id.level) {
628                child_level.cells.remove(&child_id);
629            }
630        }
631
632        // Remove from hierarchy map
633        self.mesh_hierarchy.hierarchy_map.remove(&parentid);
634
635        Ok(true)
636    }
637
638    /// Update neighbor relationships after refinement
639    fn update_neighbor_relationships(&mut self, level: usize) -> IntegrateResult<()> {
640        // Collect neighbor relationships first to avoid borrowing conflicts
641        let mut all_neighbor_relationships: Vec<(CellId, Vec<CellId>)> = Vec::new();
642
643        if let Some(mesh_level) = self.mesh_hierarchy.levels.get(level) {
644            let cellids: Vec<CellId> = mesh_level.cells.keys().cloned().collect();
645
646            // Build spatial hash map for efficient neighbor searching
647            let mut spatial_hash: HashMap<(i32, i32, i32), Vec<CellId>> = HashMap::new();
648            let grid_spacing = mesh_level.grid_spacing;
649
650            // Hash all cells based on their spatial location
651            for cellid in &cellids {
652                if let Some(cell) = mesh_level.cells.get(cellid) {
653                    if cell.center.len() >= 3 {
654                        let hash_x = (cell.center[0] / grid_spacing)
655                            .floor()
656                            .to_i32()
657                            .unwrap_or(0);
658                        let hash_y = (cell.center[1] / grid_spacing)
659                            .floor()
660                            .to_i32()
661                            .unwrap_or(0);
662                        let hash_z = (cell.center[2] / grid_spacing)
663                            .floor()
664                            .to_i32()
665                            .unwrap_or(0);
666
667                        spatial_hash
668                            .entry((hash_x, hash_y, hash_z))
669                            .or_default()
670                            .push(*cellid);
671                    }
672                }
673            }
674
675            for cellid in &cellids {
676                if let Some(cell) = mesh_level.cells.get(cellid) {
677                    let mut neighbors = Vec::new();
678
679                    if cell.center.len() >= 3 {
680                        let hash_x = (cell.center[0] / grid_spacing)
681                            .floor()
682                            .to_i32()
683                            .unwrap_or(0);
684                        let hash_y = (cell.center[1] / grid_spacing)
685                            .floor()
686                            .to_i32()
687                            .unwrap_or(0);
688                        let hash_z = (cell.center[2] / grid_spacing)
689                            .floor()
690                            .to_i32()
691                            .unwrap_or(0);
692
693                        // Search in 27 neighboring hash buckets (3x3x3)
694                        for dx in -1..=1 {
695                            for dy in -1..=1 {
696                                for dz in -1..=1 {
697                                    let hash_key = (hash_x + dx, hash_y + dy, hash_z + dz);
698
699                                    if let Some(potential_neighbors) = spatial_hash.get(&hash_key) {
700                                        for &neighbor_id in potential_neighbors {
701                                            if neighbor_id != *cellid {
702                                                if let Some(neighbor_cell) =
703                                                    mesh_level.cells.get(&neighbor_id)
704                                                {
705                                                    // Check if cells are actually neighbors (face/edge/vertex sharing)
706                                                    if self.are_cells_neighbors(cell, neighbor_cell)
707                                                    {
708                                                        neighbors.push(neighbor_id);
709                                                    }
710                                                }
711                                            }
712                                        }
713                                    }
714                                }
715                            }
716                        }
717                    }
718                    all_neighbor_relationships.push((*cellid, neighbors));
719                }
720            }
721        }
722
723        // Now apply all neighbor relationships with mutable access
724        if let Some(mesh_level) = self.mesh_hierarchy.levels.get_mut(level) {
725            for (cellid, neighbors) in all_neighbor_relationships {
726                if let Some(cell) = mesh_level.cells.get_mut(&cellid) {
727                    cell.neighbors = neighbors;
728                }
729            }
730        }
731
732        // Now update inter-level neighbors separately to avoid borrowing conflicts
733        let cellids: Vec<CellId> = if let Some(mesh_level) = self.mesh_hierarchy.levels.get(level) {
734            mesh_level.cells.keys().cloned().collect()
735        } else {
736            Vec::new()
737        };
738
739        for cellid in cellids {
740            self.update_interlevel_neighbors(cellid, level)?;
741        }
742
743        Ok(())
744    }
745
746    /// Check if two cells are geometric neighbors
747    fn are_cells_neighbors(&self, cell1: &AdaptiveCell<F>, cell2: &AdaptiveCell<F>) -> bool {
748        if cell1.center.len() != cell2.center.len() || cell1.center.len() < 3 {
749            return false;
750        }
751
752        let max_size = cell1.size.max(cell2.size);
753        let tolerance = max_size * F::from(1.1).expect("Failed to convert constant to float"); // 10% tolerance
754
755        // Calculate distance between cell centers
756        let mut distance_squared = F::zero();
757        for i in 0..cell1.center.len() {
758            let diff = cell1.center[i] - cell2.center[i];
759            distance_squared += diff * diff;
760        }
761
762        let distance = distance_squared.sqrt();
763
764        // Cells are neighbors if distance is approximately equal to sum of half-sizes
765        let expected_distance =
766            (cell1.size + cell2.size) / F::from(2.0).expect("Failed to convert constant to float");
767
768        distance <= tolerance
769            && distance
770                >= expected_distance * F::from(0.7).expect("Failed to convert constant to float")
771    }
772
773    /// Update neighbor relationships across different mesh levels
774    fn update_interlevel_neighbors(&mut self, cellid: CellId, level: usize) -> IntegrateResult<()> {
775        // Collect neighbor relationships first to avoid borrowing conflicts
776        let mut coarser_neighbors = Vec::new();
777        let mut finer_neighbors = Vec::new();
778
779        // Check neighbors at level-1 (coarser level)
780        if level > 0 {
781            if let (Some(current_level), Some(coarser_level)) = (
782                self.mesh_hierarchy.levels.get(level),
783                self.mesh_hierarchy.levels.get(level - 1),
784            ) {
785                if let Some(current_cell) = current_level.cells.get(&cellid) {
786                    for (coarser_cellid, coarser_cell) in &coarser_level.cells {
787                        if self.are_cells_neighbors(current_cell, coarser_cell) {
788                            coarser_neighbors.push(*coarser_cellid);
789                        }
790                    }
791                }
792            }
793        }
794
795        // Check neighbors at level+1 (finer level)
796        if level + 1 < self.mesh_hierarchy.levels.len() {
797            if let (Some(current_level), Some(finer_level)) = (
798                self.mesh_hierarchy.levels.get(level),
799                self.mesh_hierarchy.levels.get(level + 1),
800            ) {
801                if let Some(current_cell) = current_level.cells.get(&cellid) {
802                    for (finer_cellid, finer_cell) in &finer_level.cells {
803                        if self.are_cells_neighbors(current_cell, finer_cell) {
804                            finer_neighbors.push(*finer_cellid);
805                        }
806                    }
807                }
808            }
809        }
810
811        // Now apply the neighbor relationships with mutable access
812        if let Some(current_level) = self.mesh_hierarchy.levels.get_mut(level) {
813            if let Some(current_cell) = current_level.cells.get_mut(&cellid) {
814                for coarser_id in coarser_neighbors {
815                    if !current_cell.neighbors.contains(&coarser_id) {
816                        current_cell.neighbors.push(coarser_id);
817                    }
818                }
819
820                for finer_id in finer_neighbors {
821                    if !current_cell.neighbors.contains(&finer_id) {
822                        current_cell.neighbors.push(finer_id);
823                    }
824                }
825            }
826        }
827
828        Ok(())
829    }
830
831    /// Update ghost cells for parallel processing
832    fn update_ghost_cells(&mut self) -> IntegrateResult<()> {
833        // Clear existing ghost cells
834        self.mesh_hierarchy.ghost_cells.clear();
835
836        // Identify boundary cells and their external neighbors for each level
837        for level_idx in 0..self.mesh_hierarchy.levels.len() {
838            let mut ghost_cells_for_level = Vec::new();
839            let mut boundary_cells = HashSet::new();
840
841            // First pass: identify boundary cells (cells with fewer neighbors than expected)
842            let expected_neighbors = self.calculate_expected_neighbors();
843
844            if let Some(mesh_level) = self.mesh_hierarchy.levels.get(level_idx) {
845                for (cellid, cell) in &mesh_level.cells {
846                    // A cell is on the boundary if it has fewer neighbors than expected
847                    // or if it's marked as a boundary cell
848                    if cell.neighbors.len() < expected_neighbors
849                        || mesh_level.boundary_cells.contains(cellid)
850                    {
851                        boundary_cells.insert(*cellid);
852                    }
853                }
854
855                // Second pass: create ghost cells for parallel processing
856                for boundary_cellid in &boundary_cells {
857                    if let Some(boundary_cell) = mesh_level.cells.get(boundary_cellid) {
858                        // Create ghost cells in the expected neighbor positions
859                        let ghost_cells =
860                            self.create_ghost_cells_for_boundary(boundary_cell, level_idx)?;
861                        ghost_cells_for_level.extend(ghost_cells);
862                    }
863                }
864
865                // Third pass: handle inter-level ghost cells
866                self.create_interlevel_ghost_cells(level_idx, &mut ghost_cells_for_level)?;
867            }
868
869            self.mesh_hierarchy
870                .ghost_cells
871                .insert(level_idx, ghost_cells_for_level);
872        }
873
874        Ok(())
875    }
876
877    /// Calculate expected number of neighbors for a regular cell
878    fn calculate_expected_neighbors(&self) -> usize {
879        // For a 3D structured grid, a regular internal cell should have 6 face neighbors
880        // For 2D: 4 neighbors, for 1D: 2 neighbors
881        // This is a simplification - actual count depends on mesh structure
882        6
883    }
884
885    /// Create ghost cells for a boundary cell
886    fn create_ghost_cells_for_boundary(
887        &self,
888        boundary_cell: &AdaptiveCell<F>,
889        level: usize,
890    ) -> IntegrateResult<Vec<CellId>> {
891        let mut ghost_cells = Vec::new();
892
893        if boundary_cell.center.len() >= 3 {
894            let cell_size = boundary_cell.size;
895
896            // Create ghost cells in the 6 cardinal directions (±x, ±y, ±z)
897            let directions = [
898                [F::one(), F::zero(), F::zero()],  // +x
899                [-F::one(), F::zero(), F::zero()], // -x
900                [F::zero(), F::one(), F::zero()],  // +y
901                [F::zero(), -F::one(), F::zero()], // -y
902                [F::zero(), F::zero(), F::one()],  // +z
903                [F::zero(), F::zero(), -F::one()], // -z
904            ];
905
906            for (dir_idx, direction) in directions.iter().enumerate() {
907                // Calculate ghost _cell position
908                let mut ghost_center = boundary_cell.center.clone();
909                for i in 0..3 {
910                    ghost_center[i] += direction[i] * cell_size;
911                }
912
913                // Check if a real _cell exists at this position
914                if !self.cell_exists_at_position(&ghost_center, level) {
915                    // Create ghost _cell ID (using high indices to avoid conflicts)
916                    let ghost_id = CellId {
917                        level,
918                        index: 1_000_000 + boundary_cell.id.index * 10 + dir_idx,
919                    };
920
921                    ghost_cells.push(ghost_id);
922                }
923            }
924        }
925
926        Ok(ghost_cells)
927    }
928
929    /// Create ghost cells for inter-level communication
930    fn create_interlevel_ghost_cells(
931        &self,
932        level: usize,
933        ghost_cells: &mut Vec<CellId>,
934    ) -> IntegrateResult<()> {
935        // Handle ghost _cells needed for communication between mesh levels
936
937        // Check if we need ghost _cells from coarser level
938        if level > 0 {
939            if let Some(current_level) = self.mesh_hierarchy.levels.get(level) {
940                for (cellid, cell) in &current_level.cells {
941                    // If this fine cell doesn't have a parent at the coarser level,
942                    // it might need ghost cell communication
943                    if cell.parent.is_none() {
944                        let ghost_id = CellId {
945                            level: level - 1,
946                            index: 2_000_000 + cellid.index,
947                        };
948                        ghost_cells.push(ghost_id);
949                    }
950                }
951            }
952        }
953
954        // Check if we need ghost _cells from finer level
955        if level + 1 < self.mesh_hierarchy.levels.len() {
956            if let Some(current_level) = self.mesh_hierarchy.levels.get(level) {
957                for (cellid, cell) in &current_level.cells {
958                    // If this coarse cell has children at the finer level,
959                    // it might need ghost cell communication
960                    if !cell.children.is_empty() {
961                        let ghost_id = CellId {
962                            level: level + 1,
963                            index: 3_000_000 + cellid.index,
964                        };
965                        ghost_cells.push(ghost_id);
966                    }
967                }
968            }
969        }
970
971        Ok(())
972    }
973
974    /// Check if a cell exists at the given position and level
975    fn cell_exists_at_position(&self, position: &Array1<F>, level: usize) -> bool {
976        if let Some(mesh_level) = self.mesh_hierarchy.levels.get(level) {
977            let tolerance = mesh_level.grid_spacing
978                * F::from(0.1).expect("Failed to convert constant to float");
979
980            for cell in mesh_level.cells.values() {
981                if position.len() == cell.center.len() {
982                    let mut distance_squared = F::zero();
983                    for i in 0..position.len() {
984                        let diff = position[i] - cell.center[i];
985                        distance_squared += diff * diff;
986                    }
987
988                    if distance_squared.sqrt() < tolerance {
989                        return true;
990                    }
991                }
992            }
993        }
994        false
995    }
996
997    /// Count total active cells across all levels
998    fn count_active_cells(&self) -> usize {
999        self.mesh_hierarchy
1000            .levels
1001            .iter()
1002            .map(|level| level.cells.values().filter(|cell| cell.is_active).count())
1003            .sum()
1004    }
1005
1006    /// Assess load balance quality
1007    fn assess_load_balance(&self) -> F {
1008        let total_cells = self.count_active_cells();
1009        if total_cells == 0 {
1010            return F::one(); // Empty mesh is perfectly balanced
1011        }
1012
1013        // Calculate multiple load balance metrics
1014        let cell_distribution_balance = self.calculate_cell_distribution_balance();
1015        let computational_load_balance = self.calculate_computational_load_balance();
1016        let communication_overhead_balance = self.calculate_communication_balance();
1017        let memory_distribution_balance = self.calculate_memory_balance();
1018
1019        // Weighted combination of different balance metrics
1020        let weight_cell = F::from(0.3).expect("Failed to convert constant to float");
1021        let weight_compute = F::from(0.4).expect("Failed to convert constant to float");
1022        let weight_comm = F::from(0.2).expect("Failed to convert constant to float");
1023        let weight_memory = F::from(0.1).expect("Failed to convert constant to float");
1024
1025        let overall_balance = weight_cell * cell_distribution_balance
1026            + weight_compute * computational_load_balance
1027            + weight_comm * communication_overhead_balance
1028            + weight_memory * memory_distribution_balance;
1029
1030        // Clamp to [0, 1] range where 1.0 = perfect balance
1031        overall_balance.min(F::one()).max(F::zero())
1032    }
1033
1034    /// Calculate cell count distribution balance across levels
1035    fn calculate_cell_distribution_balance(&self) -> F {
1036        if self.mesh_hierarchy.levels.is_empty() {
1037            return F::one();
1038        }
1039
1040        // Calculate cells per level
1041        let mut cells_per_level: Vec<usize> = Vec::new();
1042        let mut total_cells = 0;
1043
1044        for level in &self.mesh_hierarchy.levels {
1045            let active_cells = level.cells.values().filter(|c| c.is_active).count();
1046            cells_per_level.push(active_cells);
1047            total_cells += active_cells;
1048        }
1049
1050        if total_cells == 0 {
1051            return F::one();
1052        }
1053
1054        // Calculate variance in cell distribution
1055        let mean_cells = total_cells as f64 / cells_per_level.len() as f64;
1056        let variance: f64 = cells_per_level
1057            .iter()
1058            .map(|&count| {
1059                let diff = count as f64 - mean_cells;
1060                diff * diff
1061            })
1062            .sum::<f64>()
1063            / cells_per_level.len() as f64;
1064
1065        let std_dev = variance.sqrt();
1066        let coefficient_of_variation = if mean_cells > 0.0 {
1067            std_dev / mean_cells
1068        } else {
1069            0.0
1070        };
1071
1072        // Convert to balance score (lower variation = better balance)
1073        let balance = (1.0 - coefficient_of_variation.min(1.0)).max(0.0);
1074        F::from(balance).unwrap_or(F::zero())
1075    }
1076
1077    /// Calculate computational load balance based on cell error estimates
1078    fn calculate_computational_load_balance(&self) -> F {
1079        let mut level_computational_loads: Vec<F> = Vec::new();
1080        let mut total_load = F::zero();
1081
1082        for level in &self.mesh_hierarchy.levels {
1083            let mut level_load = F::zero();
1084
1085            for cell in level.cells.values() {
1086                if cell.is_active {
1087                    // Computational cost is proportional to error estimate and refinement complexity
1088                    let cell_cost = cell.error_estimate * cell.size * cell.size; // O(h^2) scaling
1089                    level_load += cell_cost;
1090                }
1091            }
1092
1093            level_computational_loads.push(level_load);
1094            total_load += level_load;
1095        }
1096
1097        if total_load <= F::zero() {
1098            return F::one();
1099        }
1100
1101        // Calculate coefficient of variation for computational loads
1102        let mean_load =
1103            total_load / F::from(level_computational_loads.len()).expect("Operation failed");
1104        let mut variance = F::zero();
1105
1106        for &load in &level_computational_loads {
1107            let diff = load - mean_load;
1108            variance += diff * diff;
1109        }
1110
1111        variance /= F::from(level_computational_loads.len()).expect("Operation failed");
1112        let std_dev = variance.sqrt();
1113
1114        let coeff_var = if mean_load > F::zero() {
1115            std_dev / mean_load
1116        } else {
1117            F::zero()
1118        };
1119
1120        // Convert to balance score
1121        let balance = F::one() - coeff_var.min(F::one());
1122        balance.max(F::zero())
1123    }
1124
1125    /// Calculate communication balance based on ghost cell overhead
1126    fn calculate_communication_balance(&self) -> F {
1127        let mut level_comm_costs: Vec<F> = Vec::new();
1128        let mut total_comm_cost = F::zero();
1129
1130        for (level_idx, level) in self.mesh_hierarchy.levels.iter().enumerate() {
1131            let active_cells = level.cells.values().filter(|c| c.is_active).count();
1132            let ghost_cells = self
1133                .mesh_hierarchy
1134                .ghost_cells
1135                .get(&level_idx)
1136                .map(|ghosts| ghosts.len())
1137                .unwrap_or(0);
1138
1139            // Communication cost is proportional to ghost cells per active cell
1140            let comm_cost = if active_cells > 0 {
1141                F::from(ghost_cells as f64 / active_cells as f64).unwrap_or(F::zero())
1142            } else {
1143                F::zero()
1144            };
1145
1146            level_comm_costs.push(comm_cost);
1147            total_comm_cost += comm_cost;
1148        }
1149
1150        if level_comm_costs.is_empty() || total_comm_cost <= F::zero() {
1151            return F::one();
1152        }
1153
1154        // Calculate variance in communication costs
1155        let mean_comm =
1156            total_comm_cost / F::from(level_comm_costs.len()).expect("Operation failed");
1157        let mut variance = F::zero();
1158
1159        for &cost in &level_comm_costs {
1160            let diff = cost - mean_comm;
1161            variance += diff * diff;
1162        }
1163
1164        variance /= F::from(level_comm_costs.len()).expect("Operation failed");
1165        let std_dev = variance.sqrt();
1166
1167        let coeff_var = if mean_comm > F::zero() {
1168            std_dev / mean_comm
1169        } else {
1170            F::zero()
1171        };
1172
1173        // Convert to balance score
1174        let balance = F::one() - coeff_var.min(F::one());
1175        balance.max(F::zero())
1176    }
1177
1178    /// Calculate memory distribution balance
1179    fn calculate_memory_balance(&self) -> F {
1180        let mut level_memory_usage: Vec<F> = Vec::new();
1181        let mut total_memory = F::zero();
1182
1183        for level in &self.mesh_hierarchy.levels {
1184            // Estimate memory usage: cells + solution data + neighbor lists
1185            let cell_count = level.cells.len();
1186            let total_neighbors: usize = level.cells.values().map(|c| c.neighbors.len()).sum();
1187
1188            let solution_size: usize = level.cells.values().map(|c| c.solution.len()).sum();
1189
1190            // Memory estimate (simplified)
1191            let memory_estimate = F::from(cell_count + total_neighbors + solution_size)
1192                .expect("Failed to convert to float");
1193            level_memory_usage.push(memory_estimate);
1194            total_memory += memory_estimate;
1195        }
1196
1197        if level_memory_usage.is_empty() || total_memory <= F::zero() {
1198            return F::one();
1199        }
1200
1201        // Calculate memory distribution balance
1202        let mean_memory =
1203            total_memory / F::from(level_memory_usage.len()).expect("Operation failed");
1204        let mut variance = F::zero();
1205
1206        for &memory in &level_memory_usage {
1207            let diff = memory - mean_memory;
1208            variance += diff * diff;
1209        }
1210
1211        variance /= F::from(level_memory_usage.len()).expect("Operation failed");
1212        let std_dev = variance.sqrt();
1213
1214        let coeff_var = if mean_memory > F::zero() {
1215            std_dev / mean_memory
1216        } else {
1217            F::zero()
1218        };
1219
1220        // Convert to balance score
1221        let balance = F::one() - coeff_var.min(F::one());
1222        balance.max(F::zero())
1223    }
1224}
1225
1226// Refinement criterion implementations
1227impl<F: IntegrateFloat + Send + Sync> RefinementCriterion<F> for GradientRefinementCriterion<F> {
1228    fn evaluate(&self, cell: &AdaptiveCell<F>, neighbors: &[&AdaptiveCell<F>]) -> F {
1229        if neighbors.is_empty() {
1230            return F::zero();
1231        }
1232
1233        let mut max_gradient = F::zero();
1234
1235        for neighbor in neighbors {
1236            let gradient = if let Some(comp) = self.component {
1237                if comp < cell.solution.len() && comp < neighbor.solution.len() {
1238                    (cell.solution[comp] - neighbor.solution[comp]).abs() / cell.size
1239                } else {
1240                    F::zero()
1241                }
1242            } else {
1243                // Use L2 norm of solution difference
1244                let diff = &cell.solution - &neighbor.solution;
1245                diff.mapv(|x| x.powi(2)).sum().sqrt() / cell.size
1246            };
1247
1248            max_gradient = max_gradient.max(gradient);
1249        }
1250
1251        // Relative criterion
1252        let solution_magnitude = if let Some(comp) = self.component {
1253            cell.solution
1254                .get(comp)
1255                .map(|&x| x.abs())
1256                .unwrap_or(F::zero())
1257        } else {
1258            cell.solution.mapv(|x| x.abs()).sum()
1259        };
1260
1261        if solution_magnitude > F::zero() {
1262            max_gradient / solution_magnitude
1263        } else {
1264            max_gradient
1265        }
1266    }
1267
1268    fn name(&self) -> &'static str {
1269        "Gradient"
1270    }
1271}
1272
1273impl<F: IntegrateFloat + Send + Sync> RefinementCriterion<F> for FeatureDetectionCriterion<F> {
1274    fn evaluate(&self, cell: &AdaptiveCell<F>, neighbors: &[&AdaptiveCell<F>]) -> F {
1275        let mut feature_score = F::zero();
1276
1277        for &feature_type in &self.feature_types {
1278            match feature_type {
1279                FeatureType::SharpGradient => {
1280                    // Detect sharp gradients
1281                    if neighbors.len() >= 2 {
1282                        let gradients: Vec<F> = neighbors
1283                            .iter()
1284                            .map(|n| (&cell.solution - &n.solution).mapv(|x| x.abs()).sum())
1285                            .collect();
1286
1287                        let max_grad = gradients.iter().fold(F::zero(), |acc, &x| acc.max(x));
1288                        let avg_grad = gradients.iter().fold(F::zero(), |acc, &x| acc + x)
1289                            / F::from(gradients.len()).expect("Operation failed");
1290
1291                        if avg_grad > F::zero() {
1292                            feature_score += max_grad / avg_grad;
1293                        }
1294                    }
1295                }
1296                FeatureType::LocalExtremum => {
1297                    // Detect local extrema
1298                    let cell_value = cell.solution.mapv(|x| x.abs()).sum();
1299                    let mut is_extremum = true;
1300
1301                    for neighbor in neighbors {
1302                        let neighbor_value = neighbor.solution.mapv(|x| x.abs()).sum();
1303                        if (neighbor_value - cell_value).abs() < self.threshold {
1304                            is_extremum = false;
1305                            break;
1306                        }
1307                    }
1308
1309                    if is_extremum {
1310                        feature_score += F::one();
1311                    }
1312                }
1313                _ => {
1314                    // Other feature types would be implemented here
1315                }
1316            }
1317        }
1318
1319        feature_score
1320    }
1321
1322    fn name(&self) -> &'static str {
1323        "FeatureDetection"
1324    }
1325}
1326
1327impl<F: IntegrateFloat + Send + Sync> RefinementCriterion<F> for CurvatureRefinementCriterion<F> {
1328    fn evaluate(&self, cell: &AdaptiveCell<F>, neighbors: &[&AdaptiveCell<F>]) -> F {
1329        if neighbors.len() < 2 {
1330            return F::zero();
1331        }
1332
1333        // Estimate curvature using second differences
1334        let mut curvature = F::zero();
1335
1336        for component in 0..cell.solution.len() {
1337            let center_value = cell.solution[component];
1338            let neighbor_values: Vec<F> = neighbors
1339                .iter()
1340                .filter_map(|n| n.solution.get(component).copied())
1341                .collect();
1342
1343            if neighbor_values.len() >= 2 {
1344                // Simple second difference approximation
1345                let avg_neighbor = neighbor_values.iter().fold(F::zero(), |acc, &x| acc + x)
1346                    / F::from(neighbor_values.len()).expect("Operation failed");
1347
1348                let second_diff = (avg_neighbor - center_value).abs() / (cell.size * cell.size);
1349                curvature += second_diff;
1350            }
1351        }
1352
1353        curvature
1354    }
1355
1356    fn name(&self) -> &'static str {
1357        "Curvature"
1358    }
1359}
1360
1361#[cfg(test)]
1362mod tests {
1363    use super::*;
1364
1365    #[test]
1366    fn test_amr_manager_creation() {
1367        let initial_level = AdaptiveMeshLevel {
1368            level: 0,
1369            cells: HashMap::new(),
1370            grid_spacing: 1.0,
1371            boundary_cells: HashSet::new(),
1372        };
1373
1374        let amr = AdvancedAMRManager::new(initial_level, 5, 0.01);
1375        assert_eq!(amr.max_levels, 5);
1376        assert_eq!(amr.mesh_hierarchy.levels.len(), 1);
1377    }
1378
1379    #[test]
1380    fn test_gradient_criterion() {
1381        let cell = AdaptiveCell {
1382            id: CellId { level: 0, index: 0 },
1383            center: Array1::from_vec(vec![0.5, 0.5]),
1384            size: 0.1,
1385            solution: Array1::from_vec(vec![1.0]),
1386            error_estimate: 0.0,
1387            refinement_flag: RefinementFlag::None,
1388            is_active: true,
1389            neighbors: vec![],
1390            parent: None,
1391            children: vec![],
1392        };
1393
1394        let neighbor = AdaptiveCell {
1395            id: CellId { level: 0, index: 1 },
1396            center: Array1::from_vec(vec![0.6, 0.5]),
1397            size: 0.1,
1398            solution: Array1::from_vec(vec![2.0]),
1399            error_estimate: 0.0,
1400            refinement_flag: RefinementFlag::None,
1401            is_active: true,
1402            neighbors: vec![],
1403            parent: None,
1404            children: vec![],
1405        };
1406
1407        let criterion = GradientRefinementCriterion {
1408            component: Some(0),
1409            threshold: 1.0,
1410            relative_tolerance: 0.1,
1411        };
1412
1413        let neighbors = vec![&neighbor];
1414        let result = criterion.evaluate(&cell, &neighbors);
1415        assert!(result > 0.0);
1416    }
1417}