use super::{
super::{Coordinates, FiniteElementMethods, FiniteElementSpecifics},
TetrahedralFiniteElements,
};
use conspire::math::Tensor;
use ndarray::Array2;
use ndarray_npy::ReadNpyExt;
use std::fs::File; use std::fs::remove_file; use std::io::Read;
const EPSILON: f64 = 1.0e-14;
const EPSILON_6: f64 = 1.0e-6;
#[test]
fn simple_tetrahedral() {
let nodal_coordinates = Coordinates::from([
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[0.5, 1.0, 0.0],
[0.5, 0.5, 1.0],
]);
let element_node_connectivity = vec![[0, 1, 2, 3]];
let element_blocks = vec![1];
let fem = TetrahedralFiniteElements::from((
element_blocks,
element_node_connectivity,
nodal_coordinates,
));
let volumes = fem.volumes();
assert!((volumes[0] - 1.0 / 6.0).abs() < EPSILON);
let connectivity = &fem.get_element_node_connectivity()[0];
let found_edge_lengths: Vec<f64> = fem
.edge_vectors(connectivity)
.into_iter()
.map(|v| v.norm())
.collect();
let known_edge_lengths = [
1.0, (1.25_f64).sqrt(), (1.25_f64).sqrt(), (1.50_f64).sqrt(), (1.50_f64).sqrt(), (1.25_f64).sqrt(), ];
found_edge_lengths
.into_iter()
.zip(known_edge_lengths)
.enumerate()
.for_each(|(i, (found, known))| {
let diff = (found - known).abs();
assert!(
diff < EPSILON,
"Edge length mismatch at index {}. Known: {}, Found: {}. Difference: {}",
i,
known,
found,
diff
);
});
let known_edge_ratio_max = 1.224744871391589;
let found_edge_ratio_max = fem.maximum_edge_ratios()[0];
assert!((known_edge_ratio_max - found_edge_ratio_max).abs() < EPSILON);
let known_scaled_jacobian_min = 0.8432740427115679;
let found_scaled_jacobian_min = fem.minimum_scaled_jacobians()[0];
assert!((known_scaled_jacobian_min - found_scaled_jacobian_min).abs() < EPSILON);
let known_skew_max = 0.19683858159631012;
let found_skew_max = fem.maximum_skews()[0];
assert!((known_skew_max - found_skew_max).abs() < EPSILON);
let known_volume = 0.16666666666666666;
let found_volume = fem.volumes()[0];
assert!((known_volume - found_volume).abs() < EPSILON);
}
#[test]
fn signed_element_volume_positive() {
let nodal_coordinates = Coordinates::from([
[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], ]);
let element_node_connectivity = vec![[0, 1, 2, 3]];
let element_blocks = vec![1];
let fem = TetrahedralFiniteElements::from((
element_blocks,
element_node_connectivity,
nodal_coordinates,
));
let known = 1.0 / 6.0;
let found = fem.signed_element_volume(&fem.get_element_node_connectivity()[0]);
assert!(
(known - found).abs() < EPSILON,
"Expected positive volume {} but found {}",
known,
found
);
}
#[test]
fn signed_element_volume_negative() {
let nodal_coordinates = Coordinates::from([
[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], ]);
let element_node_connectivity = vec![[0, 2, 1, 3]];
let element_blocks = vec![1];
let fem = TetrahedralFiniteElements::from((
element_blocks,
element_node_connectivity,
nodal_coordinates,
));
let known = -1.0 / 6.0;
let found = fem.signed_element_volume(&fem.get_element_node_connectivity()[0]);
assert!(
(known - found).abs() < EPSILON,
"Expected negative volume {} but found {}",
known,
found
);
}
#[test]
fn signed_element_volume_zero() {
let nodal_coordinates = Coordinates::from([
[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 1.0, 0.0], ]);
let element_node_connectivity = vec![[0, 1, 2, 3]];
let element_blocks = vec![1];
let fem = TetrahedralFiniteElements::from((
element_blocks,
element_node_connectivity,
nodal_coordinates,
));
let known = 0.0;
let found = fem.signed_element_volume(&fem.get_element_node_connectivity()[0]);
assert!(
(known - found).abs() < EPSILON,
"Expected zero volume but found {}",
found
);
}
#[test]
fn random_tetrahedron() {
let nodal_coordinates = Coordinates::from([
[0.5, 0.5, 0.5], [1.8, 0.2, 1.1], [0.1, 1.5, 0.3], [1.3, 1.9, 2.0], ]);
let element_node_connectivity = vec![[0, 1, 2, 3]];
let element_blocks = vec![1];
let fem = TetrahedralFiniteElements::from((
element_blocks,
element_node_connectivity,
nodal_coordinates,
));
let known = 0.22766666666666668;
let found = fem.signed_element_volume(&fem.get_element_node_connectivity()[0]);
assert!(
(known - found).abs() < EPSILON,
"Expected positive volume {} but found {}",
known,
found
);
}
#[test]
fn minimum_scaled_jacobians_unit_tetrahedron() {
let nodal_coordinates = Coordinates::from([
[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], ]);
let element_node_connectivity = vec![[0, 1, 2, 3]];
let element_blocks = vec![1];
let fem = TetrahedralFiniteElements::from((
element_blocks,
element_node_connectivity,
nodal_coordinates,
));
let expected = 2.0_f64.sqrt() / 2.0;
let found_metrics = fem.minimum_scaled_jacobians();
assert_eq!(found_metrics.len(), 1);
let found = found_metrics[0];
assert!(
(expected - found).abs() < EPSILON,
"Expected minimum scaled Jacobian {} but found {}",
expected,
found
);
}
#[test]
fn minimum_scaled_jacobians_degenerate_tetrahedron() {
let nodal_coordinates = Coordinates::from([
[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.5, 0.5, 0.0], ]);
let element_node_connectivity = vec![[0, 1, 2, 3]];
let element_blocks = vec![1];
let fem = TetrahedralFiniteElements::from((
element_blocks,
element_node_connectivity,
nodal_coordinates,
));
let expected = 0.0;
let found_metrics = fem.minimum_scaled_jacobians();
assert_eq!(found_metrics.len(), 1);
let found = found_metrics[0];
assert!(
(expected - found).abs() < EPSILON,
"Expected minimum scaled Jacobian {} but found {}",
expected,
found
);
}
#[test]
fn maximum_skews_regular_tetrahedron() {
let nodal_coordinates = Coordinates::from([
[1.0, 1.0, 1.0],
[1.0, -1.0, -1.0],
[-1.0, 1.0, -1.0],
[-1.0, -1.0, 1.0],
]);
let element_node_connectivity = vec![[0, 1, 2, 3]];
let element_blocks = vec![1];
let fem = TetrahedralFiniteElements::from((
element_blocks,
element_node_connectivity,
nodal_coordinates,
));
let expected = 0.0;
let found_metrics = fem.maximum_skews();
assert_eq!(found_metrics.len(), 1);
let found = found_metrics[0];
assert!(
(expected - found).abs() < EPSILON,
"Expected maximum skew {} but found {}",
expected,
found
);
}
#[test]
fn write_metrics() {
let nodal_coordinates = Coordinates::from([
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[0.5, 1.0, 0.0],
[0.5, 0.5, 1.0],
]);
let element_node_connectivity = vec![[0, 1, 2, 3]];
let element_blocks = vec![1];
let fem = TetrahedralFiniteElements::from((
element_blocks,
element_node_connectivity,
nodal_coordinates,
));
let known_edge_ratio_max = 1.224744871391589;
let known_scaled_jacobian_min = 0.8432740427115679;
let known_skew_max = 0.19683858159631012;
let known_volume = 1.0 / 6.0;
let csv_path = "test_write_tet_metrics.csv";
fem.write_metrics(csv_path).unwrap();
let mut csv_file = File::open(csv_path).unwrap();
let mut csv_contents = String::new();
csv_file.read_to_string(&mut csv_contents).unwrap();
let mut lines = csv_contents.lines();
assert_eq!(
lines.next().unwrap(),
"maximum edge ratio,minimum scaled jacobian,maximum skew,element volume"
);
let data_line = lines.next().unwrap();
let values: Vec<f64> = data_line
.split(',')
.map(|s| s.trim().parse::<f64>().unwrap())
.collect();
assert_eq!(values.len(), 4);
assert!((values[0] - known_edge_ratio_max).abs() < EPSILON_6);
assert!((values[1] - known_scaled_jacobian_min).abs() < EPSILON_6);
assert!((values[2] - known_skew_max).abs() < EPSILON_6);
assert!((values[3] - known_volume).abs() < EPSILON_6);
let npy_path = "test_write_tet_metrics.npy";
fem.write_metrics(npy_path).unwrap();
let npy_file = File::open(npy_path).unwrap();
let arr: Array2<f64> = Array2::read_npy(npy_file).unwrap();
assert_eq!(arr.shape(), &[1, 4]);
assert!((arr[[0, 0]] - known_edge_ratio_max).abs() < EPSILON);
assert!((arr[[0, 1]] - known_scaled_jacobian_min).abs() < EPSILON);
assert!((arr[[0, 2]] - known_skew_max).abs() < EPSILON);
assert!((arr[[0, 3]] - known_volume).abs() < EPSILON);
remove_file(csv_path).unwrap();
remove_file(npy_path).unwrap();
}