qualia_core_db/specialized_libs/engineering_analysis/
dynamics.rs1use super::*;
2
3pub struct StructuralDynamics {
5 modal_analysis: ModalAnalysis,
6 transient_analysis: TransientAnalysis,
7 harmonic_analysis: HarmonicAnalysis,
8}
9
10pub struct ModalAnalysis {
12 eigenvalue_solver: EigenvalueSolver,
13 mode_shapes: Vec<ModeShape>,
14 modal_parameters: ModalParameters,
15}
16
17#[derive(Debug, Clone)]
19pub struct EigenvalueSolver {
20 pub solver_type: EigenvalueSolverType,
21 pub num_modes: u32,
22 pub frequency_range: (f64, f64),
23}
24
25#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
27pub enum EigenvalueSolverType {
28 Lanczos,
29 Subspace,
30 Power,
31 QR,
32}
33
34#[derive(Debug, Clone)]
36pub struct ModeShape {
37 pub mode_number: u32,
38 pub natural_frequency: f64,
39 pub damping_ratio: f64,
40 pub mode_shape_vector: Vec<f64>,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct ModalParameters {
46 pub mass_normalization: bool,
47 pub participation_factors: Vec<f64>,
48 pub effective_mass: Vec<f64>,
49}
50
51pub struct TransientAnalysis {
53 time_integration: TimeIntegration,
54 loading_history: LoadingHistory,
55 response_calculation: ResponseCalculation,
56}
57
58#[derive(Debug, Clone)]
60pub struct TimeIntegration {
61 pub integration_method: IntegrationMethod,
62 pub time_step: f64,
63 pub total_time: f64,
64}
65
66#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
68pub enum IntegrationMethod {
69 CentralDifference,
70 Newmark,
71 WilsonTheta,
72 HilberHughesTaylor,
73}
74
75#[derive(Debug, Clone)]
77pub struct LoadingHistory {
78 pub time_points: Vec<f64>,
79 pub load_values: Vec<f64>,
80 pub load_type: LoadType,
81}
82
83#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
85pub enum LoadType {
86 Force,
87 Displacement,
88 Acceleration,
89 Pressure,
90 Point,
91}
92
93#[derive(Debug, Clone)]
95pub struct ResponseCalculation {
96 pub response_types: Vec<ResponseType>,
97 pub calculation_method: CalculationMethod,
98}
99
100#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
102pub enum ResponseType {
103 Displacement,
104 Velocity,
105 Acceleration,
106 Stress,
107 Strain,
108}
109
110#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
112pub enum CalculationMethod {
113 Direct,
114 Modal,
115 FrequencyDomain,
116}
117
118pub struct HarmonicAnalysis {
120 frequency_response: FrequencyResponse,
121 resonance_detection: ResonanceDetection,
122}
123
124#[derive(Debug, Clone)]
126pub struct FrequencyResponse {
127 pub frequencies: Vec<f64>,
128 pub response_amplitudes: Vec<f64>,
129 pub response_phases: Vec<f64>,
130}
131
132#[derive(Debug, Clone)]
134pub struct ResonanceDetection {
135 pub resonance_frequencies: Vec<f64>,
136 pub resonance_amplitudes: Vec<f64>,
137 pub quality_factors: Vec<f64>,
138}
139impl StructuralDynamics {
140 pub fn new() -> Self {
141 Self {
142 modal_analysis: ModalAnalysis::new(),
143 transient_analysis: TransientAnalysis::new(),
144 harmonic_analysis: HarmonicAnalysis::new(),
145 }
146 }
147
148 pub fn initialize(&mut self) -> Result<(), EngineeringError> {
149 Ok(())
150 }
151
152 pub fn modal_analysis(&self) -> &ModalAnalysis {
154 &self.modal_analysis
155 }
156
157 pub fn modal_analysis_mut(&mut self) -> &mut ModalAnalysis {
159 &mut self.modal_analysis
160 }
161
162 pub fn transient_analysis(&self) -> &TransientAnalysis {
164 &self.transient_analysis
165 }
166
167 pub fn transient_analysis_mut(&mut self) -> &mut TransientAnalysis {
169 &mut self.transient_analysis
170 }
171
172 pub fn harmonic_analysis(&self) -> &HarmonicAnalysis {
174 &self.harmonic_analysis
175 }
176
177 pub fn harmonic_analysis_mut(&mut self) -> &mut HarmonicAnalysis {
179 &mut self.harmonic_analysis
180 }
181
182 pub fn analyze_transient(
186 &self,
187 mass: f64,
188 stiffness: f64,
189 damping: f64,
190 ) -> Result<DynamicsResults, EngineeringError> {
191 if mass <= 0.0 {
192 return Err(EngineeringError::ValidationError(
193 "mass must be positive".to_string(),
194 ));
195 }
196 let ti = &self.transient_analysis.time_integration;
197 let lh = &self.transient_analysis.loading_history;
198 if ti.time_step <= 0.0 || ti.total_time <= 0.0 {
199 return Err(EngineeringError::ValidationError(
200 "time_step and total_time must be positive".to_string(),
201 ));
202 }
203
204 let num_steps = (ti.total_time / ti.time_step).ceil() as usize;
205 let mut positions = Vec::with_capacity(num_steps + 1);
206 let mut velocities = Vec::with_capacity(num_steps + 1);
207 let mut accelerations = Vec::with_capacity(num_steps + 1);
208
209 let mut pos = 0.0;
210 let mut vel = 0.0;
211 let dt = ti.time_step;
212
213 for i in 0..=num_steps {
214 let t = i as f64 * dt;
215
216 let mut force = 0.0;
218 if !lh.time_points.is_empty() && lh.time_points.len() == lh.load_values.len() {
219 if t <= lh.time_points[0] {
220 force = lh.load_values[0];
221 } else if t >= *lh.time_points.last().unwrap() {
222 force = *lh.load_values.last().unwrap();
223 } else {
224 for j in 0..lh.time_points.len() - 1 {
225 if t >= lh.time_points[j] && t <= lh.time_points[j + 1] {
226 let dt_int = lh.time_points[j + 1] - lh.time_points[j];
227 let df = lh.load_values[j + 1] - lh.load_values[j];
228 let frac = (t - lh.time_points[j]) / dt_int;
229 force = lh.load_values[j] + df * frac;
230 break;
231 }
232 }
233 }
234 }
235
236 let acc = (force - damping * vel - stiffness * pos) / mass;
237
238 positions.push(pos);
239 velocities.push(vel);
240 accelerations.push(acc);
241
242 vel += acc * dt;
244 pos += vel * dt;
245 }
246
247 let final_pos = positions.last().copied().unwrap_or(0.0);
248 let final_vel = velocities.last().copied().unwrap_or(0.0);
249 let ke = 0.5 * mass * final_vel * final_vel;
250 let pe = 0.5 * stiffness * final_pos * final_pos;
251 Ok(DynamicsResults {
252 positions,
253 velocities,
254 accelerations,
255 kinetic_energy: ke,
256 potential_energy: pe,
257 total_energy: ke + pe,
258 time_steps: (0..=num_steps).map(|i| i as f64 * dt).collect(),
259 })
260 }
261}
262
263impl ModalAnalysis {
264 pub fn new() -> Self {
265 Self {
266 eigenvalue_solver: EigenvalueSolver::new(),
267 mode_shapes: Vec::new(),
268 modal_parameters: ModalParameters::new(),
269 }
270 }
271
272 pub fn eigenvalue_solver(&self) -> &EigenvalueSolver {
274 &self.eigenvalue_solver
275 }
276
277 pub fn eigenvalue_solver_mut(&mut self) -> &mut EigenvalueSolver {
279 &mut self.eigenvalue_solver
280 }
281
282 pub fn add_mode_shape(&mut self, mode: ModeShape) {
284 self.mode_shapes.push(mode);
285 }
286
287 pub fn mode_shapes(&self) -> &[ModeShape] {
289 &self.mode_shapes
290 }
291
292 pub fn modal_parameters(&self) -> &ModalParameters {
294 &self.modal_parameters
295 }
296
297 pub fn modal_parameters_mut(&mut self) -> &mut ModalParameters {
299 &mut self.modal_parameters
300 }
301
302 pub fn analyze_modal(
312 &mut self,
313 stiffness: &[f64],
314 mass_diag: &[f64],
315 num_dofs: usize,
316 ) -> Result<Vec<ModeShape>, EngineeringError> {
317 let modes = solve_modal_eigen(stiffness, mass_diag, num_dofs)?;
318 let shapes: Vec<ModeShape> = modes
319 .into_iter()
320 .enumerate()
321 .map(|(i, (omega, phi))| ModeShape {
322 mode_number: (i + 1) as u32,
323 natural_frequency: omega,
324 damping_ratio: 0.0,
325 mode_shape_vector: phi,
326 })
327 .collect();
328 self.mode_shapes = shapes.clone();
329 Ok(shapes)
330 }
331}
332
333impl EigenvalueSolver {
334 pub fn new() -> Self {
335 Self {
336 solver_type: EigenvalueSolverType::Lanczos,
337 num_modes: 10,
338 frequency_range: (0.0, 1000.0),
339 }
340 }
341}
342
343impl ModalParameters {
344 pub fn new() -> Self {
345 Self {
346 mass_normalization: true,
347 participation_factors: Vec::new(),
348 effective_mass: Vec::new(),
349 }
350 }
351}
352
353impl TransientAnalysis {
354 pub fn new() -> Self {
355 Self {
356 time_integration: TimeIntegration::new(),
357 loading_history: LoadingHistory::new(),
358 response_calculation: ResponseCalculation::new(),
359 }
360 }
361
362 pub fn time_integration(&self) -> &TimeIntegration {
364 &self.time_integration
365 }
366
367 pub fn time_integration_mut(&mut self) -> &mut TimeIntegration {
369 &mut self.time_integration
370 }
371
372 pub fn loading_history(&self) -> &LoadingHistory {
374 &self.loading_history
375 }
376
377 pub fn loading_history_mut(&mut self) -> &mut LoadingHistory {
379 &mut self.loading_history
380 }
381
382 pub fn response_calculation(&self) -> &ResponseCalculation {
384 &self.response_calculation
385 }
386
387 pub fn response_calculation_mut(&mut self) -> &mut ResponseCalculation {
389 &mut self.response_calculation
390 }
391}
392
393impl TimeIntegration {
394 pub fn new() -> Self {
395 Self {
396 integration_method: IntegrationMethod::Newmark,
397 time_step: 0.01,
398 total_time: 10.0,
399 }
400 }
401}
402
403impl LoadingHistory {
404 pub fn new() -> Self {
405 Self {
406 time_points: Vec::new(),
407 load_values: Vec::new(),
408 load_type: LoadType::Force,
409 }
410 }
411}
412
413impl ResponseCalculation {
414 pub fn new() -> Self {
415 Self {
416 response_types: vec![ResponseType::Displacement, ResponseType::Stress],
417 calculation_method: CalculationMethod::Modal,
418 }
419 }
420}
421
422impl HarmonicAnalysis {
423 pub fn new() -> Self {
424 Self {
425 frequency_response: FrequencyResponse::new(),
426 resonance_detection: ResonanceDetection::new(),
427 }
428 }
429
430 pub fn frequency_response(&self) -> &FrequencyResponse {
432 &self.frequency_response
433 }
434
435 pub fn frequency_response_mut(&mut self) -> &mut FrequencyResponse {
437 &mut self.frequency_response
438 }
439
440 pub fn resonance_detection(&self) -> &ResonanceDetection {
442 &self.resonance_detection
443 }
444
445 pub fn resonance_detection_mut(&mut self) -> &mut ResonanceDetection {
447 &mut self.resonance_detection
448 }
449}
450
451impl FrequencyResponse {
452 pub fn new() -> Self {
453 Self {
454 frequencies: Vec::new(),
455 response_amplitudes: Vec::new(),
456 response_phases: Vec::new(),
457 }
458 }
459}
460
461impl ResonanceDetection {
462 pub fn new() -> Self {
463 Self {
464 resonance_frequencies: Vec::new(),
465 resonance_amplitudes: Vec::new(),
466 quality_factors: Vec::new(),
467 }
468 }
469}