Skip to main content

runmat_meshing_core/validation/
mod.rs

1use crate::{
2    contracts::{artifact::ANALYSIS_MESH_SCHEMA_VERSION, AnalysisMeshArtifact},
3    quality::QualityThresholds,
4};
5
6mod components;
7use components::validate_volume_component_count;
8
9mod connectivity;
10pub use connectivity::{volume_component_count, volume_component_element_counts};
11
12mod coverage;
13use coverage::{
14    validate_boundary_area_coverage, validate_bounds_coverage, validate_coverage_samples,
15    validate_volume_coverage,
16};
17
18mod elements;
19use elements::validate_volume_elements;
20
21mod edges;
22use edges::validate_boundary_edges;
23
24mod faces;
25use faces::validate_boundary_faces;
26
27mod geometry;
28pub use geometry::mesh_contains_point;
29
30mod nodes;
31use nodes::validate_nodes;
32
33mod plc_input;
34use plc_input::validate_plc_input_evidence;
35
36mod quality;
37use quality::validate_quality;
38
39mod recovery;
40use recovery::{
41    validate_boundary_edge_recovery, validate_boundary_face_recovery,
42    validate_no_rolled_back_material_interface_partitions,
43    validate_no_unrecovered_tetrahedron_components, validate_no_unrepaired_exact_quality,
44    validate_recovery_evidence_consistency, validate_tetrahedron_recovery_complete,
45};
46
47mod regions;
48use regions::{validate_required_boundary_regions, validate_required_material_regions};
49
50mod source_provenance;
51use source_provenance::validate_boundary_source_provenance;
52
53mod types;
54pub use types::{
55    analysis_mesh_validation_error_code, AnalysisMeshValidationError, AnalysisMeshValidationOptions,
56};
57
58pub fn validate_analysis_mesh(
59    mesh: &AnalysisMeshArtifact,
60    thresholds: QualityThresholds,
61) -> Result<(), AnalysisMeshValidationError> {
62    validate_analysis_mesh_with_options(
63        mesh,
64        AnalysisMeshValidationOptions {
65            quality: thresholds,
66            ..AnalysisMeshValidationOptions::default()
67        },
68    )
69}
70
71pub fn validate_analysis_mesh_with_options(
72    mesh: &AnalysisMeshArtifact,
73    options: AnalysisMeshValidationOptions,
74) -> Result<(), AnalysisMeshValidationError> {
75    if mesh.schema_version != ANALYSIS_MESH_SCHEMA_VERSION {
76        return Err(AnalysisMeshValidationError::UnsupportedSchema {
77            schema_version: mesh.schema_version.clone(),
78        });
79    }
80    if mesh.nodes.is_empty() {
81        return Err(AnalysisMeshValidationError::EmptyNodes);
82    }
83    if mesh.volume_elements.is_empty() {
84        return Err(AnalysisMeshValidationError::EmptyVolumeElements);
85    }
86    if let Some(max_element_count) = options.max_volume_element_count {
87        if mesh.volume_elements.len() > max_element_count {
88            return Err(AnalysisMeshValidationError::ElementBudgetExceeded {
89                element_count: mesh.volume_elements.len(),
90                max_element_count,
91            });
92        }
93    }
94
95    let node_ids = validate_nodes(mesh)?;
96
97    let element_ids = validate_volume_elements(mesh, &node_ids)?;
98
99    let face_ids = validate_boundary_faces(mesh, &node_ids, &element_ids)?;
100
101    let recovered_boundary_edges = validate_boundary_edges(mesh, &node_ids, &face_ids)?;
102
103    validate_required_boundary_regions(mesh, &options.required_boundary_region_ids)?;
104    validate_required_material_regions(mesh, &options.required_material_region_ids)?;
105    validate_plc_input_evidence(mesh)?;
106    validate_boundary_source_provenance(mesh, options.require_boundary_source_edge_provenance)?;
107    validate_no_unrecovered_tetrahedron_components(
108        mesh,
109        options.require_no_unrecovered_tetrahedron_components,
110    )?;
111    validate_no_rolled_back_material_interface_partitions(mesh)?;
112    validate_recovery_evidence_consistency(mesh)?;
113    validate_tetrahedron_recovery_complete(mesh)?;
114    validate_no_unrepaired_exact_quality(mesh, options.require_no_unrepaired_exact_quality)?;
115    validate_volume_component_count(mesh, options.max_volume_component_count)?;
116    validate_coverage_samples(
117        mesh,
118        &options.coverage_sample_points_m,
119        options.min_coverage_sample_ratio,
120    )?;
121    validate_bounds_coverage(
122        mesh,
123        options.expected_bounds_m,
124        options.min_bounds_coverage_ratio,
125    )?;
126    validate_volume_coverage(
127        mesh,
128        options.expected_volume_m3,
129        options.min_volume_coverage_ratio,
130    )?;
131    validate_boundary_area_coverage(
132        mesh,
133        options.expected_boundary_area_m2,
134        options.min_boundary_area_ratio,
135    )?;
136    validate_boundary_face_recovery(mesh, options.min_boundary_face_recovery_ratio)?;
137    validate_boundary_edge_recovery(
138        mesh,
139        &recovered_boundary_edges,
140        options.min_boundary_edge_recovery_ratio,
141    )?;
142    validate_quality(mesh, options.quality)
143}
144
145#[cfg(test)]
146mod tests;