Files
rustytorch/crates/specialized/rtx-fea/src/error.rs
T
2026-03-04 00:08:42 +00:00

596 lines
19 KiB
Rust

// Copyright (c) 2024 RustyTorch++ Team
// Licensed under the Apache License, Version 2.0
//! Comprehensive error handling for FEA operations.
//!
//! This module provides robust error types covering all aspects of finite element analysis,
//! including mesh operations, assembly, solving, and GPU operations.
use thiserror::Error;
/// Result type alias for FEA operations.
pub type FeaResult<T> = Result<T, FeaError>;
/// Comprehensive error types for finite element analysis operations.
#[derive(Error, Debug)]
pub enum FeaError {
/// Mesh-related errors
#[error("Mesh error: {0}")]
Mesh(#[from] MeshError),
/// Element-related errors
#[error("Element error: {0}")]
Element(#[from] ElementError),
/// Material model errors
#[error("Material error: {0}")]
Material(#[from] MaterialError),
/// Assembly process errors
#[error("Assembly error: {0}")]
Assembly(#[from] AssemblyError),
/// Boundary condition errors
#[error("Boundary condition error: {0}")]
BoundaryCondition(#[from] BoundaryConditionError),
/// Analysis errors
#[error("Analysis error: {0}")]
Analysis(#[from] AnalysisError),
/// Solver errors
#[error("Solver error: {0}")]
Solver(#[from] SolverError),
/// GPU/CUDA operation errors
#[error("GPU error: {0}")]
Gpu(#[from] GpuError),
/// GPU kernel specific errors
#[error("Kernel error: {0}")]
Kernel(#[from] KernelError),
/// Memory management errors
#[error("Memory error: {0}")]
Memory(#[from] MemoryError),
/// General computational errors
#[error("Computation error: {0}")]
Computation(#[from] ComputationError),
/// I/O and serialization errors
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
/// Generic error for wrapping external errors
#[error("External error: {0}")]
External(#[from] anyhow::Error),
/// Invalid operation error
#[error("Invalid operation: {message}")]
InvalidOperation { message: String },
/// Invalid input error
#[error("Invalid input: {0}")]
InvalidInput(String),
/// Computation failed error
#[error("Computation failed: {0}")]
ComputationFailed(String),
}
/// Mesh-specific error types.
#[derive(Error, Debug)]
pub enum MeshError {
#[error("Invalid mesh topology: {message}")]
InvalidTopology { message: String },
#[error("Node index {index} out of bounds (max: {max_index})")]
NodeIndexOutOfBounds { index: usize, max_index: usize },
#[error("Element index {index} out of bounds (max: {max_index})")]
ElementIndexOutOfBounds { index: usize, max_index: usize },
#[error("Invalid element type: expected {expected}, found {found}")]
InvalidElementType { expected: String, found: String },
#[error("Mesh connectivity is invalid: {message}")]
InvalidConnectivity { message: String },
#[error("Mesh refinement failed: {reason}")]
RefinementFailed { reason: String },
#[error("Mesh partitioning failed: {reason}")]
PartitioningFailed { reason: String },
#[error("Empty mesh: no nodes or elements")]
EmptyMesh,
#[error(
"Inconsistent mesh dimensions: nodes have {node_dim}D coordinates but elements require {element_dim}D"
)]
InconsistentDimensions { node_dim: usize, element_dim: usize },
#[error("Invalid element: {0}")]
InvalidElement(String),
#[error("Node {node_id} not found")]
NodeNotFound { node_id: usize },
#[error("Element {element_id} not found")]
ElementNotFound { element_id: usize },
#[error("Set {set_name} not found")]
SetNotFound { set_name: String },
#[error("Invalid dimension: {0}")]
InvalidDimension(String),
}
/// Element computation error types.
#[derive(Error, Debug)]
pub enum ElementError {
#[error("Shape function evaluation failed at coordinates ({xi}, {eta}, {zeta})")]
ShapeFunctionEvaluation { xi: f64, eta: f64, zeta: f64 },
#[error("Jacobian computation failed: determinant is {det} (near zero or negative)")]
JacobianSingular { det: f64 },
#[error("Invalid quadrature rule: {rule} is not supported for element type {element_type}")]
InvalidQuadratureRule { rule: String, element_type: String },
#[error("Element matrix computation failed: {reason}")]
MatrixComputationFailed { reason: String },
#[error("Invalid element geometry: {message}")]
InvalidGeometry { message: String },
#[error("Unsupported element type: {element_type}")]
UnsupportedElementType { element_type: String },
#[error("Integration point {point} out of range [0, {max_points}]")]
IntegrationPointOutOfRange { point: usize, max_points: usize },
#[error("Unsupported quadrature order {requested_order} for {element_type} (max: {max_order})")]
UnsupportedQuadratureOrder {
element_type: String,
requested_order: usize,
max_order: usize,
},
#[error("Invalid quadrature: {reason}")]
InvalidQuadrature { reason: String },
#[error("GPU required for operation: {operation}")]
GpuRequired { operation: String },
}
/// Material model error types.
#[derive(Error, Debug)]
pub enum MaterialError {
#[error("Invalid material property: {property} = {value} is out of valid range [{min}, {max}]")]
InvalidProperty {
property: String,
value: f64,
min: f64,
max: f64,
},
#[error("Material model convergence failed after {iterations} iterations")]
ConvergenceFailed { iterations: usize },
#[error("Unsupported material model: {model}")]
UnsupportedModel { model: String },
#[error("Material state update failed: {reason}")]
StateUpdateFailed { reason: String },
#[error("Invalid stress/strain state: {message}")]
InvalidState { message: String },
#[error("Material parameter {parameter} not found")]
ParameterNotFound { parameter: String },
#[error("Plastic consistency condition violated: f = {yield_function_value}")]
PlasticConsistencyViolated { yield_function_value: f64 },
}
/// Assembly process error types.
#[derive(Error, Debug)]
pub enum AssemblyError {
#[error(
"Matrix dimension mismatch: expected {expected_rows}x{expected_cols}, got {actual_rows}x{actual_cols}"
)]
MatrixDimensionMismatch {
expected_rows: usize,
expected_cols: usize,
actual_rows: usize,
actual_cols: usize,
},
#[error("Sparse matrix assembly failed: {reason}")]
SparseAssemblyFailed { reason: String },
#[error("Global DOF index {index} out of bounds (max: {max_index})")]
GlobalDofOutOfBounds { index: usize, max_index: usize },
#[error("Element contribution assembly failed for element {element_id}: {reason}")]
ElementContributionFailed { element_id: usize, reason: String },
#[error("Constraint assembly failed: {reason}")]
ConstraintAssemblyFailed { reason: String },
#[error("Matrix pattern inconsistency: {message}")]
MatrixPatternInconsistent { message: String },
#[error("Node {node_id} not found")]
NodeNotFound { node_id: usize },
#[error("Material {material_id} not found")]
MaterialNotFound { material_id: usize },
#[error("Dimension mismatch: expected {expected}, got {actual}")]
DimensionMismatch { expected: usize, actual: usize },
#[error("Singular matrix detected")]
SingularMatrix,
}
/// Boundary condition error types.
pub type BoundaryError = BoundaryConditionError;
/// Analysis error types.
#[derive(Error, Debug)]
pub enum AnalysisError {
#[error("Analysis setup failed: {reason}")]
SetupFailed { reason: String },
#[error("Analysis convergence failed after {iterations} iterations")]
ConvergenceFailed { iterations: usize },
#[error("Time stepping failed at time {time}: {reason}")]
TimeSteppingFailed { time: f64, reason: String },
#[error("Invalid analysis parameters: {message}")]
InvalidParameters { message: String },
#[error("Analysis not supported: {analysis_type}")]
UnsupportedAnalysis { analysis_type: String },
#[error("Invalid configuration: {0}")]
InvalidConfiguration(String),
}
/// Boundary condition error types.
#[derive(Error, Debug)]
pub enum BoundaryConditionError {
#[error("Boundary condition applied to invalid node {node_id}")]
InvalidNode { node_id: usize },
#[error("Boundary condition applied to invalid DOF {dof} (valid range: 0-{max_dof})")]
InvalidDof { dof: usize, max_dof: usize },
#[error("Conflicting boundary conditions on node {node_id}, DOF {dof}")]
ConflictingConditions { node_id: usize, dof: usize },
#[error("Invalid boundary surface specification: {message}")]
InvalidBoundarySurface { message: String },
#[error("Traction boundary condition requires surface normal vector")]
MissingSurfaceNormal,
#[error("Invalid load pattern: {pattern}")]
InvalidLoadPattern { pattern: String },
#[error("DOF {dof} out of bounds (max: {max_dof})")]
DofOutOfBounds { dof: usize, max_dof: usize },
#[error("History data mismatch: {times_len} times vs {values_len} values")]
HistoryDataMismatch { times_len: usize, values_len: usize },
#[error("Node {node_id} not found")]
NodeNotFound { node_id: usize },
#[error("Element {element_id} not found")]
ElementNotFound { element_id: usize },
}
/// Solver error types.
#[derive(Error, Debug)]
pub enum SolverError {
#[error("Matrix is singular: condition number = {condition_number}")]
SingularMatrix { condition_number: f64 },
#[error(
"Iterative solver failed to converge after {iterations} iterations (residual: {residual})"
)]
ConvergenceFailed { iterations: usize, residual: f64 },
#[error("Direct solver failed: {reason}")]
DirectSolverFailed { reason: String },
#[error("Preconditioner setup failed: {reason}")]
PreconditionerFailed { reason: String },
#[error("Invalid solver parameters: {message}")]
InvalidParameters { message: String },
#[error("Solver not supported for matrix type: {matrix_type}")]
UnsupportedMatrixType { matrix_type: String },
#[error("Numerical instability detected: {message}")]
NumericalInstability { message: String },
#[error("Device is not available or unsupported")]
DeviceUnavailable,
#[error("Device error: {message}")]
DeviceError { message: String },
#[error("Matrix dimension mismatch: {matrix_rows}x{matrix_cols} vs RHS {rhs_rows}x{rhs_cols}")]
DimensionMismatch {
matrix_rows: usize,
matrix_cols: usize,
rhs_rows: usize,
rhs_cols: usize,
},
#[error("Matrix is not symmetric")]
MatrixNotSymmetric,
#[error("Solve operation failed: {reason}")]
SolveError { reason: String },
#[error("Factorization failed: {reason}")]
FactorizationFailed { reason: String },
#[error("Factorization required but not performed")]
FactorizationRequired,
#[error("GPU not available: {0}")]
GpuNotAvailable(String),
#[error("Preconditioner not setup")]
PreconditionerNotSetup,
}
/// GPU operation error types.
#[derive(Error, Debug)]
pub enum GpuError {
#[error("CUDA runtime error: {message}")]
CudaRuntime { message: String },
#[error("cuBLAS error: {message}")]
Cublas { message: String },
#[error("cuSPARSE error: {message}")]
Cusparse { message: String },
#[error("cuSOLVER error: {message}")]
Cusolver { message: String },
#[error("Kernel launch failed: {kernel_name}")]
KernelLaunchFailed { kernel_name: String },
#[error("GPU memory allocation failed: requested {requested} bytes")]
MemoryAllocationFailed { requested: usize },
#[error("GPU-CPU memory transfer failed: {direction}")]
MemoryTransferFailed { direction: String },
#[error("GPU device not available or unsupported")]
DeviceUnavailable,
#[error("GPU context error: {message}")]
ContextError { message: String },
}
/// GPU kernel specific error types.
#[derive(Error, Debug)]
pub enum KernelError {
#[error("Kernel compilation failed for {kernel_name}: {reason}")]
CompilationFailed { kernel_name: String, reason: String },
#[error("Kernel not found: {kernel_name} - {reason}")]
KernelNotFound { kernel_name: String, reason: String },
#[error("Kernel launch failed for {kernel_name}: {reason}")]
LaunchFailed { kernel_name: String, reason: String },
#[error("Device initialization failed: {reason}")]
DeviceInitializationFailed { reason: String },
#[error("Stream creation failed: {reason}")]
StreamCreationFailed { reason: String },
#[error("Synchronization failed: {reason}")]
SynchronizationFailed { reason: String },
#[error("Memory allocation failed: size {size} bytes - {reason}")]
MemoryAllocationFailed { size: usize, reason: String },
#[error("Memory copy failed: {direction} - {reason}")]
MemoryCopyFailed { direction: String, reason: String },
#[error("Device {device_id} not found")]
DeviceNotFound { device_id: usize },
#[error("Invalid kernel parameters: {message}")]
InvalidParameters { message: String },
#[error("Kernel execution timeout: {kernel_name}")]
ExecutionTimeout { kernel_name: String },
}
/// Memory management error types.
#[derive(Error, Debug)]
pub enum MemoryError {
#[error("Memory allocation failed: requested {size} bytes")]
AllocationFailed { size: usize },
#[error("Memory alignment error: required {required}, got {actual}")]
AlignmentError { required: usize, actual: usize },
#[error("Buffer overflow: accessing index {index} in buffer of size {size}")]
BufferOverflow { index: usize, size: usize },
#[error("Memory pool exhausted: {pool_name}")]
PoolExhausted { pool_name: String },
#[error("Invalid memory layout: {message}")]
InvalidLayout { message: String },
}
/// General computation error types.
#[derive(Error, Debug)]
pub enum ComputationError {
#[error("Numerical overflow in computation: {operation}")]
NumericalOverflow { operation: String },
#[error("Numerical underflow in computation: {operation}")]
NumericalUnderflow { operation: String },
#[error("Division by zero in {context}")]
DivisionByZero { context: String },
#[error("Invalid floating point result: {value} in {context}")]
InvalidFloatingPoint { value: f64, context: String },
#[error("Algorithm failed to converge: {algorithm}")]
AlgorithmConvergence { algorithm: String },
#[error("Dimension mismatch: expected {expected}, got {actual}")]
DimensionMismatch { expected: String, actual: String },
}
impl FeaError {
/// Check if the error is recoverable.
pub fn is_recoverable(&self) -> bool {
match self {
Self::Solver(SolverError::ConvergenceFailed { .. }) => true,
Self::Material(MaterialError::ConvergenceFailed { .. }) => true,
Self::Gpu(GpuError::MemoryAllocationFailed { .. }) => true,
_ => false,
}
}
/// Get the error severity level.
pub fn severity(&self) -> ErrorSeverity {
match self {
Self::Mesh(MeshError::EmptyMesh) => ErrorSeverity::Critical,
Self::Solver(SolverError::SingularMatrix { .. }) => ErrorSeverity::Critical,
Self::Gpu(GpuError::DeviceUnavailable) => ErrorSeverity::Critical,
Self::Memory(MemoryError::AllocationFailed { .. }) => ErrorSeverity::High,
Self::Element(ElementError::JacobianSingular { .. }) => ErrorSeverity::High,
Self::Solver(SolverError::ConvergenceFailed { .. }) => ErrorSeverity::Medium,
Self::Material(MaterialError::ConvergenceFailed { .. }) => ErrorSeverity::Medium,
_ => ErrorSeverity::Low,
}
}
}
/// Error severity levels for proper error handling and logging.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorSeverity {
Critical, // System cannot continue
High, // Major functionality affected
Medium, // Recoverable with potential data loss
Low, // Minor issues, operation can continue
}
/// Helper trait for error context enhancement.
pub trait FeaErrorExt<T> {
/// Add context to a mesh error.
fn with_mesh_context(self, context: &str) -> FeaResult<T>;
/// Add element context.
fn with_element_context(self, element_id: usize) -> FeaResult<T>;
/// Add material context.
fn with_material_context(self, material_id: usize) -> FeaResult<T>;
}
impl<T> FeaErrorExt<T> for FeaResult<T> {
fn with_mesh_context(self, context: &str) -> Self {
self.map_err(|e| match e {
FeaError::Mesh(mesh_err) => FeaError::Mesh(match mesh_err {
MeshError::InvalidTopology { message } => MeshError::InvalidTopology {
message: format!("{context}: {message}"),
},
other => other,
}),
other => other,
})
}
fn with_element_context(self, element_id: usize) -> Self {
self.map_err(|e| match e {
FeaError::Element(elem_err) => FeaError::Element(match elem_err {
ElementError::MatrixComputationFailed { reason } => {
ElementError::MatrixComputationFailed {
reason: format!("Element {element_id}: {reason}"),
}
}
other => other,
}),
other => other,
})
}
fn with_material_context(self, material_id: usize) -> Self {
self.map_err(|e| match e {
FeaError::Material(mat_err) => FeaError::Material(match mat_err {
MaterialError::StateUpdateFailed { reason } => MaterialError::StateUpdateFailed {
reason: format!("Material {material_id}: {reason}"),
},
other => other,
}),
other => other,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_severity() {
let error = FeaError::Mesh(MeshError::EmptyMesh);
assert_eq!(error.severity(), ErrorSeverity::Critical);
let error = FeaError::Solver(SolverError::ConvergenceFailed {
iterations: 100,
residual: 1e-6,
});
assert_eq!(error.severity(), ErrorSeverity::Medium);
}
#[test]
fn test_error_recoverability() {
let error = FeaError::Solver(SolverError::ConvergenceFailed {
iterations: 100,
residual: 1e-6,
});
assert!(error.is_recoverable());
let error = FeaError::Mesh(MeshError::EmptyMesh);
assert!(!error.is_recoverable());
}
#[test]
fn test_error_context() {
let result: FeaResult<()> = Err(FeaError::Mesh(MeshError::InvalidTopology {
message: "original message".to_string(),
}));
let with_context = result.with_mesh_context("preprocessing");
if let Err(FeaError::Mesh(MeshError::InvalidTopology { message })) = with_context {
assert!(message.contains("preprocessing"));
assert!(message.contains("original message"));
} else {
panic!("Expected mesh error with context");
}
}
}