CI / Distributed Training Tests (push) Canceled after 0s
Performance Benchmarks / Run Benchmarks (push) Canceled after 0s
CI / Format Check (push) Canceled after 0s
CI / Clippy Check (push) Canceled after 0s
CI / Build (macos-latest) (push) Canceled after 0s
CI / Build (ubuntu-latest) (push) Canceled after 0s
CI / Test (macos-latest) (push) Canceled after 0s
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Build CPU-Only (Explicit) (push) Canceled after 0s
CI / Python Bindings (maturin) (macos-latest) (push) Canceled after 0s
CI / Python Bindings (maturin) (ubuntu-latest) (push) Canceled after 0s
CI / WASM Build + Size Check (push) Canceled after 0s
CI / CI Success (push) Canceled after 0s
Documentation / Build API Documentation (push) Canceled after 0s
Documentation / Build User Guide (push) Canceled after 0s
Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01X2GmJXeQ2njUecEKiJZ1G2
248 lines
7.0 KiB
Rust
248 lines
7.0 KiB
Rust
// 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<f64>,
|
|
/// Maximum coordinates
|
|
pub max: Vector3<f64>,
|
|
}
|
|
|
|
impl MeshBounds {
|
|
/// Create new mesh bounds
|
|
#[must_use]
|
|
pub fn new(min: Vector3<f64>, max: Vector3<f64>) -> Self {
|
|
Self { min, max }
|
|
}
|
|
|
|
/// Get mesh dimensions
|
|
#[must_use]
|
|
pub fn dimensions(&self) -> Vector3<f64> {
|
|
self.max - self.min
|
|
}
|
|
|
|
/// Get mesh center
|
|
#[must_use]
|
|
pub fn center(&self) -> Vector3<f64> {
|
|
(self.min + self.max) * 0.5
|
|
}
|
|
|
|
/// Check if point is inside bounds
|
|
#[must_use]
|
|
pub fn contains(&self, point: Vector3<f64>) -> 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<Vec<usize>>;
|
|
|
|
/// 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> {
|
|
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> {
|
|
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<UnstructuredMesh> {
|
|
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
|
|
}
|
|
}
|