runmat-meshing-core 0.6.0

Deterministic meshing preparation contracts for RunMat
Documentation
use std::collections::{BTreeMap, BTreeSet};

use crate::contracts::{AnalysisMeshArtifact, BoundaryElementKind, VolumeElementKind};

pub(super) fn boundary_face_edges(mesh: &AnalysisMeshArtifact) -> BTreeSet<[u32; 2]> {
    let mut edges = BTreeSet::<[u32; 2]>::new();
    for face in &mesh.boundary_faces {
        if face.kind != BoundaryElementKind::Tri3 || face.node_ids.len() != 3 {
            continue;
        }
        edges.insert(sorted_edge(face.node_ids[0], face.node_ids[1]));
        edges.insert(sorted_edge(face.node_ids[1], face.node_ids[2]));
        edges.insert(sorted_edge(face.node_ids[2], face.node_ids[0]));
    }
    edges
}

pub fn volume_component_count(mesh: &AnalysisMeshArtifact) -> usize {
    volume_component_element_counts(mesh).len()
}

pub fn volume_component_element_counts(mesh: &AnalysisMeshArtifact) -> Vec<usize> {
    if mesh.volume_elements.is_empty() {
        return Vec::new();
    }
    let mut face_to_elements = BTreeMap::<[u32; 3], Vec<usize>>::new();
    for (element_index, element) in mesh.volume_elements.iter().enumerate() {
        if element.kind != VolumeElementKind::Tetrahedron4 || element.node_ids.len() != 4 {
            continue;
        }
        for face in tetrahedron_element_faces(element.node_ids.as_slice()) {
            face_to_elements
                .entry(face)
                .or_default()
                .push(element_index);
        }
    }
    let mut adjacency = vec![Vec::<usize>::new(); mesh.volume_elements.len()];
    for element_indices in face_to_elements.values() {
        for left_position in 0..element_indices.len() {
            for right_position in (left_position + 1)..element_indices.len() {
                let left = element_indices[left_position];
                let right = element_indices[right_position];
                adjacency[left].push(right);
                adjacency[right].push(left);
            }
        }
    }

    let mut visited = vec![false; mesh.volume_elements.len()];
    let mut component_element_counts = Vec::<usize>::new();
    for start in 0..mesh.volume_elements.len() {
        if visited[start] {
            continue;
        }
        visited[start] = true;
        let mut component_element_count = 0_usize;
        let mut stack = vec![start];
        while let Some(current) = stack.pop() {
            component_element_count += 1;
            for neighbor in &adjacency[current] {
                if visited[*neighbor] {
                    continue;
                }
                visited[*neighbor] = true;
                stack.push(*neighbor);
            }
        }
        component_element_counts.push(component_element_count);
    }
    component_element_counts
}

fn tetrahedron_element_faces(node_ids: &[u32]) -> [[u32; 3]; 4] {
    [
        sorted_node_face([node_ids[0], node_ids[1], node_ids[2]]),
        sorted_node_face([node_ids[0], node_ids[1], node_ids[3]]),
        sorted_node_face([node_ids[0], node_ids[2], node_ids[3]]),
        sorted_node_face([node_ids[1], node_ids[2], node_ids[3]]),
    ]
}

pub(super) fn sorted_edge(left: u32, right: u32) -> [u32; 2] {
    if left <= right {
        [left, right]
    } else {
        [right, left]
    }
}

fn sorted_node_face(mut node_ids: [u32; 3]) -> [u32; 3] {
    node_ids.sort_unstable();
    node_ids
}