// TDD: GREEN phase - Implement mesh module to pass tests //! Mesh generation and management for CFD simulations //! //! This module provides structured and unstructured mesh implementations //! with support for adaptive refinement and GPU-accelerated operations. /// Mesh entities (nodes, faces, cells) pub mod entities; /// Generators for structured curvilinear patches pub mod patch_gen; /// Structured curvilinear 2-D patch with face metrics (the overset patch) pub mod patch_mesh; /// Adaptive mesh refinement algorithms pub mod refinement; /// Mesh quality and statistics calculations pub mod statistics; /// Structured mesh implementation for rectangular domains pub mod structured; /// Unstructured mesh implementation for complex geometries pub mod unstructured; use crate::error::{CfdError, CfdResult}; use nalgebra::Vector3; // Re-export main types pub use entities::{Cell, Face, Node}; pub use patch_mesh::{Face as PatchFace, PatchMesh, Side as PatchSide}; pub use structured::StructuredMesh; pub use unstructured::UnstructuredMesh; /// Mesh bounds information #[derive(Debug, Clone)] pub struct MeshBounds { /// Minimum coordinates pub min: Vector3, /// Maximum coordinates pub max: Vector3, } impl MeshBounds { /// Create new mesh bounds #[must_use] pub fn new(min: Vector3, max: Vector3) -> Self { Self { min, max } } /// Get mesh dimensions #[must_use] pub fn dimensions(&self) -> Vector3 { self.max - self.min } /// Get mesh center #[must_use] pub fn center(&self) -> Vector3 { (self.min + self.max) * 0.5 } /// Check if point is inside bounds #[must_use] pub fn contains(&self, point: Vector3) -> bool { point.x >= self.min.x && point.x <= self.max.x && point.y >= self.min.y && point.y <= self.max.y && point.z >= self.min.z && point.z <= self.max.z } } /// Mesh quality metrics #[derive(Debug, Clone)] pub struct MeshStatistics { /// Total number of cells pub total_cells: usize, /// Total number of nodes pub total_nodes: usize, /// Total number of faces pub total_faces: usize, /// Number of boundary faces pub boundary_faces: usize, /// Minimum cell volume pub min_cell_volume: f64, /// Maximum cell volume pub max_cell_volume: f64, /// Average cell volume pub average_cell_volume: f64, /// Mesh aspect ratio (max/min dimensions) pub aspect_ratio: f64, /// Skewness measure (0 = perfect, 1 = degenerate) pub max_skewness: f64, /// Orthogonality measure (1 = perfect, 0 = non-orthogonal) pub orthogonality: f64, /// Non-orthogonality measure (0 = perfect, 1 = non-orthogonal) pub non_orthogonality: f64, } impl MeshStatistics { /// Create default statistics #[must_use] pub fn new() -> Self { Self { total_cells: 0, total_nodes: 0, total_faces: 0, boundary_faces: 0, min_cell_volume: 0.0, max_cell_volume: 0.0, average_cell_volume: 0.0, aspect_ratio: 1.0, max_skewness: 0.0, orthogonality: 1.0, non_orthogonality: 0.0, } } } impl Default for MeshStatistics { fn default() -> Self { Self::new() } } /// Common trait for all mesh types pub trait Mesh: Send + Sync { /// Get total number of cells fn cell_count(&self) -> usize; /// Get total number of nodes fn node_count(&self) -> usize; /// Get mesh bounds fn bounds(&self) -> MeshBounds; /// Validate mesh topology and quality fn validate(&self) -> CfdResult<()>; /// Get mesh statistics fn statistics(&self) -> MeshStatistics; /// Check if a cell is on the boundary fn is_boundary_cell(&self, cell_id: usize) -> bool; /// Get neighboring cells for a given cell fn get_cell_neighbors(&self, cell_id: usize) -> CfdResult>; /// Refine the mesh (adaptive refinement) fn refine(&mut self) -> CfdResult<()>; /// Coarsen the mesh fn coarsen(&mut self) -> CfdResult<()> { // Default implementation - not supported Err(CfdError::mesh( "Mesh coarsening not implemented for this mesh type", )) } } /// Mesh generation utilities pub struct MeshGenerator; impl MeshGenerator { /// Generate a structured rectangular mesh pub fn rectangle(width: f64, height: f64, nx: usize, ny: usize) -> CfdResult { StructuredMesh::new(nx, ny, width, height) } /// Generate a structured cuboid mesh pub fn cuboid( width: f64, height: f64, depth: f64, nx: usize, ny: usize, nz: usize, ) -> CfdResult { StructuredMesh::new_3d(nx, ny, nz, width, height, depth) } /// Generate an unstructured triangular mesh for a circle pub fn circle(radius: f64, elements: usize) -> CfdResult { let mut mesh = UnstructuredMesh::new(); // Add center node let center = mesh.add_node(Vector3::new(0.0, 0.0, 0.0))?; // Add perimeter nodes let mut perimeter_nodes = Vec::new(); for i in 0..elements { let angle = 2.0 * std::f64::consts::PI * i as f64 / elements as f64; let x = radius * angle.cos(); let y = radius * angle.sin(); let node = mesh.add_node(Vector3::new(x, y, 0.0))?; perimeter_nodes.push(node); } // Create triangular cells connecting center to perimeter for i in 0..elements { let next_i = (i + 1) % elements; mesh.add_triangle_cell(center, perimeter_nodes[i], perimeter_nodes[next_i])?; } Ok(mesh) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_mesh_bounds() { let min = Vector3::new(0.0, 0.0, 0.0); let max = Vector3::new(1.0, 2.0, 3.0); let bounds = MeshBounds::new(min, max); assert_eq!(bounds.dimensions(), Vector3::new(1.0, 2.0, 3.0)); assert_eq!(bounds.center(), Vector3::new(0.5, 1.0, 1.5)); assert!(bounds.contains(Vector3::new(0.5, 1.0, 1.5))); assert!(!bounds.contains(Vector3::new(-1.0, 0.0, 0.0))); } #[test] fn test_mesh_statistics_default() { let stats = MeshStatistics::default(); assert_eq!(stats.total_cells, 0); assert_eq!(stats.total_nodes, 0); assert_eq!(stats.aspect_ratio, 1.0); } #[test] fn test_mesh_generator_rectangle() { let mesh = MeshGenerator::rectangle(2.0, 3.0, 5, 6).unwrap(); assert_eq!(mesh.nx(), 5); assert_eq!(mesh.ny(), 6); assert_eq!(mesh.width(), 2.0); assert_eq!(mesh.height(), 3.0); } #[test] fn test_mesh_generator_circle() { let mesh = MeshGenerator::circle(1.0, 8).unwrap(); assert_eq!(mesh.node_count(), 9); // 1 center + 8 perimeter assert_eq!(mesh.cell_count(), 8); // 8 triangular cells } }