Skip to main content

runmat_analysis_core/
validate.rs

1use runmat_geometry_core::UnitSystem;
2use thiserror::Error;
3
4use crate::problem::{
5    loads::LoadKind,
6    model::{AnalysisModel, ReferenceFrame},
7};
8
9#[derive(Debug, Clone, PartialEq, Eq, Error)]
10pub enum AnalysisValidationError {
11    #[error(
12        "ANALYSIS_VALIDATION_MISSING_MATERIALS: analysis model must include at least one material"
13    )]
14    MissingMaterials,
15    #[error("ANALYSIS_VALIDATION_MISSING_BCS: analysis model must include at least one boundary condition")]
16    MissingBoundaryConditions,
17    #[error("ANALYSIS_VALIDATION_MISSING_LOADS: analysis model must include at least one load")]
18    MissingLoads,
19    #[error(
20        "ANALYSIS_VALIDATION_INVALID_MOMENT: moment load {load_id} must have finite components"
21    )]
22    InvalidMomentVector { load_id: String },
23    #[error("ANALYSIS_VALIDATION_ZERO_MOMENT: moment load {load_id} must have nonzero magnitude")]
24    ZeroMomentVector { load_id: String },
25    #[error(
26        "ANALYSIS_VALIDATION_UNIT_MISMATCH: model units {model:?} do not match geometry units {geometry:?}"
27    )]
28    UnitMismatch {
29        model: UnitSystem,
30        geometry: UnitSystem,
31    },
32    #[error(
33        "ANALYSIS_VALIDATION_FRAME_MISMATCH: model frame {model:?} does not match geometry frame {geometry:?}"
34    )]
35    FrameMismatch {
36        model: ReferenceFrame,
37        geometry: ReferenceFrame,
38    },
39}
40
41pub fn validate_model(model: &AnalysisModel) -> Result<(), AnalysisValidationError> {
42    if model.materials.is_empty() {
43        return Err(AnalysisValidationError::MissingMaterials);
44    }
45    if model.boundary_conditions.is_empty() {
46        return Err(AnalysisValidationError::MissingBoundaryConditions);
47    }
48    if model.loads.is_empty() {
49        return Err(AnalysisValidationError::MissingLoads);
50    }
51    for load in &model.loads {
52        if let LoadKind::Moment { mx, my, mz } = load.kind {
53            if !mx.is_finite() || !my.is_finite() || !mz.is_finite() {
54                return Err(AnalysisValidationError::InvalidMomentVector {
55                    load_id: load.load_id.clone(),
56                });
57            }
58            if mx == 0.0 && my == 0.0 && mz == 0.0 {
59                return Err(AnalysisValidationError::ZeroMomentVector {
60                    load_id: load.load_id.clone(),
61                });
62            }
63        }
64    }
65    Ok(())
66}
67
68pub fn validate_model_against_geometry(
69    model: &AnalysisModel,
70    geometry_units: UnitSystem,
71    geometry_frame: &ReferenceFrame,
72) -> Result<(), AnalysisValidationError> {
73    validate_model(model)?;
74
75    if model.units != geometry_units {
76        return Err(AnalysisValidationError::UnitMismatch {
77            model: model.units,
78            geometry: geometry_units,
79        });
80    }
81    if &model.frame != geometry_frame {
82        return Err(AnalysisValidationError::FrameMismatch {
83            model: model.frame.clone(),
84            geometry: geometry_frame.clone(),
85        });
86    }
87    Ok(())
88}