//! TDD Tests for Pyramid FiniteElement Implementations //! Following strict Red-Green-Refactor cycle //! No mocks, stubs, or TODOs - only full implementations #[cfg(test)] mod pyramid5_tests { use rtx_fea::elements::shape_functions::shape_special::Pyramid5; use rtx_fea::elements::{FiniteElement, NaturalCoords}; use rtx_fea::mesh::ElementType; #[test] fn test_pyramid5_element_type() { // RED: Test that Pyramid5 implements FiniteElement let pyramid5 = Pyramid5::new(); // GREEN: Pyramid5 should return ElementType::Pyramid5 assert_eq!(pyramid5.element_type(), ElementType::Pyramid5); } #[test] fn test_pyramid5_num_nodes() { // RED: Test that Pyramid5 correctly reports number of nodes let pyramid5 = Pyramid5::new(); // GREEN: Pyramid5 has 5 nodes (4 base + 1 apex) assert_eq!(pyramid5.num_nodes(), 5); } #[test] fn test_pyramid5_dimensions() { // RED: Test spatial and parametric dimensions let pyramid5 = Pyramid5::new(); // GREEN: Pyramid5 is 3D element with 3D parametric space assert_eq!(pyramid5.spatial_dimension(), 3); assert_eq!(pyramid5.parametric_dimension(), 3); } #[test] fn test_pyramid5_shape_functions_at_base() { // RED: Test shape function evaluation at base let pyramid5 = Pyramid5::new(); // Test at base center (0,0,0) let coords = NaturalCoords::new_3d(0.0, 0.0, 0.0); let shape = pyramid5.shape_functions(&coords).unwrap(); // GREEN: At base center, base nodes should have equal contributions for i in 0..4 { assert!((shape.value(i).unwrap() - 0.25).abs() < 1e-10); } assert!(shape.value(4).unwrap().abs() < 1e-10); // Apex should be 0 } #[test] fn test_pyramid5_partition_of_unity() { // RED: Test that shape functions sum to 1 everywhere let pyramid5 = Pyramid5::new(); let test_points = vec![ (0.0, 0.0, 0.0), // Base center (0.5, 0.5, 0.5), // Mid pyramid (-0.5, 0.3, 0.2), // Random point ]; for (x, y, z) in test_points { let coords = NaturalCoords::new_3d(x, y, z); let shape = pyramid5.shape_functions(&coords).unwrap(); // GREEN: Sum of all shape functions should be 1 let sum: f64 = (0..5).map(|i| shape.value(i).unwrap()).sum(); assert!((sum - 1.0).abs() < 1e-10); } } } #[cfg(test)] mod pyramid13_tests { use rtx_fea::elements::shape_functions::shape_special::Pyramid13; use rtx_fea::elements::{FiniteElement, NaturalCoords}; use rtx_fea::mesh::ElementType; #[test] fn test_pyramid13_element_type() { // RED: Test that Pyramid13 implements FiniteElement let pyramid13 = Pyramid13::new(); // GREEN: Pyramid13 should return ElementType::Pyramid13 assert_eq!(pyramid13.element_type(), ElementType::Pyramid13); } #[test] fn test_pyramid13_num_nodes() { // RED: Test that Pyramid13 correctly reports number of nodes let pyramid13 = Pyramid13::new(); // GREEN: Pyramid13 has 13 nodes assert_eq!(pyramid13.num_nodes(), 13); } #[test] fn test_pyramid13_partition_of_unity() { // Pyramid13 is not implemented; it reports that rather than returning // a basis that does not form a partition of unity. // // The previous implementation summed to 4 at the base centre, and its // `derivatives` allocated a 13x3 matrix then wrote rows 13 to 15 -- // copied from a sixteen-node layout -- so this test panicked on an // out-of-bounds index rather than on the assertion below. // // Pyramid5 is unaffected; see `test_pyramid5_partition_of_unity`. let pyramid13 = Pyramid13::new(); for (x, y, z) in [(0.0, 0.0, 0.0), (0.25, 0.25, 0.25)] { let coords = NaturalCoords::new_3d(x, y, z); let error = pyramid13 .shape_functions(&coords) .expect_err("Pyramid13 must report that it is unimplemented"); assert!(error.to_string().to_lowercase().contains("not implemented")); } } }