Files
rustytorch/crates/specialized/rtx-cfd/src/mesh/mod.rs
T
Omar SobhandClaude Fable 5.1 52da75a3a9
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
rtx-cfd: curvilinear collocated PISO on a structured patch (overset A-P0, WIP) — PatchMesh (right-handed s,n; periodic seam with shift; face metrics), patch generators (TFI, skewed annulus, sheared/varying-skew channels), CSR + Jacobi-BiCGSTAB, the Zang–Street–Koseff incremental step with the node-based 9-point L_f, LSQ gradients, explicit and line-implicit-n predictors, adjustPhi; tests: mesh metrics (5 green), operators exact on linear fields incl. the seam (green), sparse (2 green), MMS ladder (Cartesian 16/32: 1.37–1.39x the staggered error, order 0.83; n=64 stalls at a |du/dt| floor 2e-4 — open, tolerance-scaling hypothesis), annulus/Poiseuille not yet run
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01X2GmJXeQ2njUecEKiJZ1G2
2026-09-04 05:00:08 -07:00

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
}
}