// TDD: RED phase - Tests for face connectivity use nalgebra::Vector3; use rtx_cfd::mesh::Mesh; use rtx_cfd::mesh::structured::StructuredMesh; use rtx_cfd::mesh::unstructured::UnstructuredMesh; use rtx_cfd::traits::MeshEntity; #[test] fn test_structured_2d_face_generation() { let mesh = StructuredMesh::new(3, 3, 2.0, 2.0).unwrap(); // Should generate faces for 2D mesh let stats = mesh.statistics(); // For 3x3 grid: // - Horizontal faces: 3 rows × 2 edges = 6 // - Vertical faces: 2 rows × 3 edges = 6 // Total: 12 faces assert_eq!(stats.total_faces, 12); // Boundary faces should be all perimeter faces // Perimeter: 2×(3-1) + 2×(3-1) = 8 boundary faces assert_eq!(stats.boundary_faces, 8); } #[test] fn test_structured_3d_face_generation() { let mesh = StructuredMesh::new_3d(3, 3, 3, 2.0, 2.0, 2.0).unwrap(); let stats = mesh.statistics(); // For 3x3x3 grid, there should be many more faces in 3D assert!(stats.total_faces > 0, "3D mesh should have faces"); // Should have boundary faces on all 6 sides of the cube assert!( stats.boundary_faces > 0, "3D mesh should have boundary faces" ); } #[test] fn test_face_normals_2d() { let mesh = StructuredMesh::new(3, 3, 2.0, 2.0).unwrap(); // Test that boundary faces have correct normals pointing outward let faces = mesh.get_faces(); let mut boundary_face_count = 0; for face in faces { if face.is_boundary() { boundary_face_count += 1; let normal = face.normal(); // Normal should be unit vector let magnitude = normal.magnitude(); assert!( (magnitude - 1.0).abs() < 1e-10, "Face normal should be unit vector" ); // For 2D mesh, normal should be in x or y direction assert!(normal.z.abs() < 1e-10 || (normal.x.abs() < 1e-10 && normal.y.abs() < 1e-10)); } } assert!(boundary_face_count > 0, "Should have boundary faces"); } #[test] fn test_face_areas_2d() { let mesh = StructuredMesh::new(4, 4, 3.0, 3.0).unwrap(); let faces = mesh.get_faces(); let dx = mesh.dx(); // 3.0 / (4-1) = 1.0 let dy = mesh.dy(); // 3.0 / (4-1) = 1.0 for face in faces { let area = face.area(); // For structured 2D mesh, face area should be dx or dy assert!( (area - dx).abs() < 1e-10 || (area - dy).abs() < 1e-10, "Face area should match grid spacing: got {}, expected {} or {}", area, dx, dy ); } } #[test] fn test_face_centroids_2d() { let mesh = StructuredMesh::new(3, 3, 2.0, 2.0).unwrap(); let faces = mesh.get_faces(); let bounds = mesh.bounds(); for face in faces { let centroid = face.centroid(); // All face centroids should be within mesh bounds assert!( bounds.contains(centroid), "Face centroid {:?} should be within bounds {:?}", centroid, bounds ); } } #[test] fn test_unstructured_face_generation() { let mut mesh = UnstructuredMesh::new(); // Create a simple triangle let n1 = mesh.add_node(Vector3::new(0.0, 0.0, 0.0)).unwrap(); let n2 = mesh.add_node(Vector3::new(1.0, 0.0, 0.0)).unwrap(); let n3 = mesh.add_node(Vector3::new(0.5, 1.0, 0.0)).unwrap(); let _cell = mesh.add_triangle_cell(n1, n2, n3).unwrap(); // Generate faces automatically mesh.generate_faces().unwrap(); let stats = mesh.statistics(); // Triangle should generate 3 edges (faces) assert_eq!(stats.total_faces, 3); // All faces should be boundary faces for single triangle assert_eq!(stats.boundary_faces, 3); } #[test] fn test_face_neighbors() { let mut mesh = UnstructuredMesh::new(); // Create two adjacent triangles sharing an edge let n1 = mesh.add_node(Vector3::new(0.0, 0.0, 0.0)).unwrap(); let n2 = mesh.add_node(Vector3::new(1.0, 0.0, 0.0)).unwrap(); let n3 = mesh.add_node(Vector3::new(0.5, 1.0, 0.0)).unwrap(); let n4 = mesh.add_node(Vector3::new(1.5, 1.0, 0.0)).unwrap(); let cell1 = mesh.add_triangle_cell(n1, n2, n3).unwrap(); let cell2 = mesh.add_triangle_cell(n2, n4, n3).unwrap(); mesh.generate_faces().unwrap(); // Check that cells are neighbors (share an edge n2-n3) let neighbors1 = mesh.get_cell_neighbors(cell1).unwrap(); let neighbors2 = mesh.get_cell_neighbors(cell2).unwrap(); assert!( neighbors1.contains(&cell2), "Cell1 should have Cell2 as neighbor" ); assert!( neighbors2.contains(&cell1), "Cell2 should have Cell1 as neighbor" ); } #[test] fn test_face_connectivity_validation() { let mesh = StructuredMesh::new(4, 4, 2.0, 2.0).unwrap(); // Mesh should validate successfully assert!(mesh.validate().is_ok()); // All faces should reference valid vertices let faces = mesh.get_faces(); for face in faces { for &vertex_id in face.vertex_indices() { // Each vertex should exist in the mesh assert!( mesh.has_node(vertex_id), "Face references non-existent vertex {}", vertex_id ); } } } #[test] fn test_3d_face_normal_computation() { let mesh = StructuredMesh::new_3d(2, 2, 2, 1.0, 1.0, 1.0).unwrap(); let faces = mesh.get_faces(); for face in faces.iter().filter(|f| f.is_boundary()) { let normal = face.normal(); // Normal should be unit vector let magnitude = normal.magnitude(); assert!( (magnitude - 1.0).abs() < 1e-6, "Face normal magnitude should be 1, got {}", magnitude ); // For axis-aligned cube, normals should be along coordinate axes let is_axis_aligned = (normal.x.abs() - 1.0).abs() < 1e-6 && normal.y.abs() < 1e-6 && normal.z.abs() < 1e-6 || (normal.y.abs() - 1.0).abs() < 1e-6 && normal.x.abs() < 1e-6 && normal.z.abs() < 1e-6 || (normal.z.abs() - 1.0).abs() < 1e-6 && normal.x.abs() < 1e-6 && normal.y.abs() < 1e-6; assert!( is_axis_aligned, "Boundary face normal should be axis-aligned, got {:?}", normal ); } } #[test] fn test_internal_vs_boundary_faces() { let mesh = StructuredMesh::new(4, 4, 2.0, 2.0).unwrap(); let faces = mesh.get_faces(); let mut internal_faces = 0; let mut boundary_faces = 0; for face in faces { if face.is_boundary() { boundary_faces += 1; } else { internal_faces += 1; } } // Should have both internal and boundary faces assert!(boundary_faces > 0, "Should have boundary faces"); assert!(internal_faces >= 0, "May have internal faces"); // Total should match statistics let stats = mesh.statistics(); assert_eq!(boundary_faces, stats.boundary_faces); assert_eq!(boundary_faces + internal_faces, stats.total_faces); } #[test] fn test_face_area_consistency() { let mesh = StructuredMesh::new(3, 3, 2.0, 2.0).unwrap(); let faces = mesh.get_faces(); // All faces in structured mesh should have the same area (grid spacing) if let Some(first_face) = faces.first() { let expected_area = first_face.area(); for face in faces { let area = face.area(); assert!( (area - expected_area).abs() < 1e-10, "All faces in structured mesh should have same area" ); } } }