qualia_core_db/specialized_libs/engineering_analysis/
buckling.rs1use super::*;
2
3pub struct BucklingAnalysis {
5 eigenvalue_buckling: EigenvalueBuckling,
6 nonlinear_buckling: NonlinearBuckling,
7}
8
9#[derive(Debug, Clone)]
11pub struct EigenvalueBuckling {
12 pub critical_loads: Vec<f64>,
13 pub buckling_modes: Vec<BucklingMode>,
14}
15
16#[derive(Debug, Clone)]
18pub struct BucklingMode {
19 pub mode_number: u32,
20 pub critical_load: f64,
21 pub mode_shape: Vec<f64>,
22}
23
24#[derive(Debug, Clone)]
26pub struct NonlinearBuckling {
27 pub load_displacement_curve: Vec<(f64, f64)>,
28 pub post_buckling_behavior: PostBucklingBehavior,
29}
30
31#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
33pub enum PostBucklingBehavior {
34 Stable,
35 Unstable,
36 SnapThrough,
37}
38impl BucklingAnalysis {
39 pub fn new() -> Self {
40 Self {
41 eigenvalue_buckling: EigenvalueBuckling::new(),
42 nonlinear_buckling: NonlinearBuckling::new(),
43 }
44 }
45
46 pub fn initialize(&mut self) -> Result<(), EngineeringError> {
47 Ok(())
48 }
49
50 pub fn eigenvalue_buckling(&self) -> &EigenvalueBuckling {
52 &self.eigenvalue_buckling
53 }
54
55 pub fn eigenvalue_buckling_mut(&mut self) -> &mut EigenvalueBuckling {
57 &mut self.eigenvalue_buckling
58 }
59
60 pub fn nonlinear_buckling(&self) -> &NonlinearBuckling {
62 &self.nonlinear_buckling
63 }
64
65 pub fn nonlinear_buckling_mut(&mut self) -> &mut NonlinearBuckling {
67 &mut self.nonlinear_buckling
68 }
69
70 pub fn analyze_euler(
78 &mut self,
79 youngs_modulus: f64,
80 moment_of_inertia: f64,
81 length: f64,
82 effective_length_factor: f64,
83 num_modes: usize,
84 ) -> Result<EigenvalueBuckling, EngineeringError> {
85 if youngs_modulus <= 0.0 || moment_of_inertia <= 0.0 {
86 return Err(EngineeringError::InsufficientData(
87 "Young's modulus and moment of inertia must be positive".to_string(),
88 ));
89 }
90 if length <= 0.0 || effective_length_factor <= 0.0 {
91 return Err(EngineeringError::ValidationError(
92 "length and effective-length factor must be positive".to_string(),
93 ));
94 }
95 if num_modes == 0 {
96 return Err(EngineeringError::InsufficientData(
97 "num_modes must be at least 1".to_string(),
98 ));
99 }
100 let le = effective_length_factor * length;
101 let base = std::f64::consts::PI.powi(2) * youngs_modulus * moment_of_inertia / (le * le);
102 const STATIONS: usize = 11;
103 let mut critical_loads = Vec::with_capacity(num_modes);
104 let mut buckling_modes = Vec::with_capacity(num_modes);
105 for n in 1..=num_modes {
106 let p_cr = (n as f64).powi(2) * base;
107 let shape: Vec<f64> = (0..STATIONS)
108 .map(|s| {
109 let x = length * s as f64 / (STATIONS as f64 - 1.0);
110 (n as f64 * std::f64::consts::PI * x / length).sin()
111 })
112 .collect();
113 critical_loads.push(p_cr);
114 buckling_modes.push(BucklingMode {
115 mode_number: n as u32,
116 critical_load: p_cr,
117 mode_shape: shape,
118 });
119 }
120 let eb = EigenvalueBuckling {
121 critical_loads,
122 buckling_modes,
123 };
124 self.eigenvalue_buckling = eb.clone();
125 Ok(eb)
126 }
127
128 pub fn analyze_from_model(
135 &mut self,
136 model: &EngineeringModel,
137 num_modes: usize,
138 ) -> Result<EigenvalueBuckling, EngineeringError> {
139 let material = model.materials.values().next().ok_or_else(|| {
140 EngineeringError::InsufficientData(
141 "model has no material; cannot compute buckling load".to_string(),
142 )
143 })?;
144 let e = material.material_properties.youngs_modulus;
145 let dims = &model.geometry.dimensions;
146 if dims.len() < 3 || dims.iter().take(3).any(|&d| !(d > 0.0)) {
147 return Err(EngineeringError::InsufficientData(
148 "geometry needs three positive dimensions [b, h, L] for column buckling"
149 .to_string(),
150 ));
151 }
152 let (b, h, l) = (dims[0], dims[1], dims[2]);
153 let i_weak = (b * h * h * h).min(h * b * b * b) / 12.0;
154 self.analyze_euler(e, i_weak, l, 1.0, num_modes)
155 }
156}
157
158impl EigenvalueBuckling {
159 pub fn new() -> Self {
160 Self {
161 critical_loads: Vec::new(),
162 buckling_modes: Vec::new(),
163 }
164 }
165}
166
167impl NonlinearBuckling {
168 pub fn new() -> Self {
169 Self {
170 load_displacement_curve: Vec::new(),
171 post_buckling_behavior: PostBucklingBehavior::Stable,
172 }
173 }
174}