runmat-meshing-core 0.6.0

Deterministic meshing preparation contracts for RunMat
Documentation
use super::*;
use fixtures::*;

#[test]
fn accepts_face_connected_volume_components_within_budget() {
    let mut mesh = valid_tetrahedron_mesh();
    mesh.nodes.push(AnalysisMeshNode {
        node_id: 5,
        coordinates_m: [0.0, 0.0, -1.0],
        provenance: Vec::new(),
    });
    mesh.volume_elements.push(AnalysisVolumeElement {
        element_id: "e2".to_string(),
        kind: VolumeElementKind::Tetrahedron4,
        node_ids: vec![1, 3, 2, 5],
        material_region_id: "mat_region".to_string(),
        provenance: Vec::new(),
    });

    assert_eq!(volume_component_count(&mesh), 1);
    validate_analysis_mesh_with_options(
        &mesh,
        AnalysisMeshValidationOptions {
            max_volume_component_count: Some(1),
            ..AnalysisMeshValidationOptions::default()
        },
    )
    .expect("face-connected tetrahedra should remain one volume component");
}

#[test]
fn rejects_unintended_isolated_volume_components() {
    let mut mesh = valid_tetrahedron_mesh();
    mesh.nodes.extend([
        AnalysisMeshNode {
            node_id: 5,
            coordinates_m: [10.0, 0.0, 0.0],
            provenance: Vec::new(),
        },
        AnalysisMeshNode {
            node_id: 6,
            coordinates_m: [11.0, 0.0, 0.0],
            provenance: Vec::new(),
        },
        AnalysisMeshNode {
            node_id: 7,
            coordinates_m: [10.0, 1.0, 0.0],
            provenance: Vec::new(),
        },
        AnalysisMeshNode {
            node_id: 8,
            coordinates_m: [10.0, 0.0, 1.0],
            provenance: Vec::new(),
        },
    ]);
    mesh.volume_elements.push(AnalysisVolumeElement {
        element_id: "e2".to_string(),
        kind: VolumeElementKind::Tetrahedron4,
        node_ids: vec![5, 6, 7, 8],
        material_region_id: "mat_region".to_string(),
        provenance: Vec::new(),
    });

    let err = validate_analysis_mesh_with_options(
        &mesh,
        AnalysisMeshValidationOptions {
            max_volume_component_count: Some(1),
            ..AnalysisMeshValidationOptions::default()
        },
    )
    .expect_err("isolated volume component should fail");

    assert_eq!(
        err,
        AnalysisMeshValidationError::VolumeComponentCountExceeded {
            component_count: 2,
            max_component_count: 1,
        }
    );
}

#[test]
fn rejects_unsupported_element_kind_until_assembly_exists() {
    let mut mesh = valid_tetrahedron_mesh();
    mesh.volume_elements[0].kind = VolumeElementKind::Hex8;
    mesh.volume_elements[0].node_ids = vec![1, 2, 3, 4, 1, 2, 3, 4];
    let err = validate_analysis_mesh(&mesh, QualityThresholds::default())
        .expect_err("unsupported element kind should fail");
    assert_eq!(
        err,
        AnalysisMeshValidationError::UnsupportedVolumeElementKind {
            element_id: "e1".to_string()
        }
    );
}

#[test]
fn rejects_missing_material_coverage() {
    let mut mesh = valid_tetrahedron_mesh();
    mesh.volume_elements[0].material_region_id.clear();
    let err = validate_analysis_mesh(&mesh, QualityThresholds::default())
        .expect_err("missing material region should fail");
    assert_eq!(
        err,
        AnalysisMeshValidationError::MissingMaterialRegion {
            element_id: "e1".to_string()
        }
    );
}

#[test]
fn rejects_unclassified_material_ownership() {
    let mut mesh = valid_tetrahedron_mesh();
    mesh.volume_elements[0].material_region_id = "unclassified".to_string();
    let err = validate_analysis_mesh(&mesh, QualityThresholds::default())
        .expect_err("unresolved material ownership should fail");
    assert_eq!(
        err,
        AnalysisMeshValidationError::UnclassifiedMaterialRegion {
            element_id: "e1".to_string()
        }
    );
    assert_eq!(
        analysis_mesh_validation_error_code(&err),
        "unclassified_material_region"
    );
}

#[test]
fn rejects_unmapped_boundary_nodes() {
    let mut mesh = valid_tetrahedron_mesh();
    mesh.boundary_faces[0].node_ids = vec![1, 2, 99];
    let err = validate_analysis_mesh(&mesh, QualityThresholds::default())
        .expect_err("unknown boundary node should fail");
    assert_eq!(
        err,
        AnalysisMeshValidationError::UnknownBoundaryFaceNode {
            face_id: "f1".to_string(),
            node_id: 99
        }
    );
}

#[test]
fn rejects_unmapped_boundary_edge_nodes() {
    let mut mesh = valid_tetrahedron_mesh();
    mesh.boundary_edges = vec![AnalysisBoundaryEdge {
        edge_id: "edge1".to_string(),
        node_ids: [1, 99],
        adjacent_boundary_face_ids: vec!["f1".to_string()],
        region_ids: Vec::new(),
        provenance: Vec::new(),
    }];
    let err = validate_analysis_mesh(&mesh, QualityThresholds::default())
        .expect_err("unknown boundary edge node should fail");
    assert_eq!(
        err,
        AnalysisMeshValidationError::UnknownBoundaryEdgeNode {
            edge_id: "edge1".to_string(),
            node_id: 99
        }
    );
}

#[test]
fn rejects_boundary_edge_adjacent_to_unknown_face() {
    let mut mesh = valid_tetrahedron_mesh();
    mesh.boundary_edges = vec![AnalysisBoundaryEdge {
        edge_id: "edge1".to_string(),
        node_ids: [1, 2],
        adjacent_boundary_face_ids: vec!["missing_face".to_string()],
        region_ids: Vec::new(),
        provenance: Vec::new(),
    }];
    let err = validate_analysis_mesh(&mesh, QualityThresholds::default())
        .expect_err("unknown boundary edge adjacent face should fail");
    assert_eq!(
        err,
        AnalysisMeshValidationError::UnknownBoundaryEdgeAdjacentFace {
            edge_id: "edge1".to_string(),
            face_id: "missing_face".to_string()
        }
    );
}