qualia_core_db/specialized_libs/engineering_analysis/
mod.rs1use super::linear_algebra::LinearAlgebraLibrary;
10use super::physics_simulation::PhysicsSimulationLibrary;
11use super::statistical_computing::StatisticalComputingLibrary;
12use crate::zns_storage::ZnsZoneManager;
13use serde::{Deserialize, Serialize};
14use std::collections::HashMap;
15use std::sync::{Arc, Mutex};
16
17fn standard_normal_sample() -> f64 {
22 let mut u1: f64 = rand::random();
24 while u1 <= 0.0 {
25 u1 = rand::random();
26 }
27 let u2: f64 = rand::random();
28 let r = (-2.0 * u1.ln()).sqrt();
29 let theta = 2.0 * std::f64::consts::PI * u2;
30 r * theta.cos()
31}
32
33fn inverse_normal_cdf(p: f64) -> f64 {
38 let p = p.clamp(1e-12, 1.0 - 1e-12);
39 let a = [
41 -3.969_683_028_665_376e+01,
42 2.209_460_984_245_205e+02,
43 -2.759_285_104_469_687e+02,
44 1.383_577_518_672_69e+02,
45 -3.066_479_806_617_929e+01,
46 2.506_628_277_459_239e+00,
47 ];
48 let b = [
49 -5.447_609_879_822_406e+01,
50 1.615_858_368_580_409e+02,
51 -1.556_989_798_598_866e+02,
52 6.680_131_188_771_972e+01,
53 -1.328_068_155_288_362e+01,
54 ];
55 let c = [
56 -7.784_894_002_430_993e-03,
57 -3.223_964_580_411_365e-01,
58 -2.400_758_277_161_838e+00,
59 -2.549_732_539_349_742e+00,
60 4.374_664_141_464_968e+00,
61 2.938_163_982_698_783e+00,
62 ];
63 let d = [
64 7.784_695_709_041_462e-03,
65 3.224_671_290_700_398e-01,
66 2.445_134_137_232_851e+00,
67 3.754_408_661_907_416e+00,
68 ];
69
70 let plow = 0.02425;
71 let phigh = 1.0 - plow;
72 if p < plow {
73 let q = (-2.0 * p.ln()).sqrt();
74 (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5])
75 / ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1.0)
76 } else if p <= phigh {
77 let q = p - 0.5;
78 let r = q * q;
79 (((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q
80 / (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1.0)
81 } else {
82 let q = (-2.0 * (1.0 - p).ln()).sqrt();
83 -(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5])
84 / ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1.0)
85 }
86}
87
88fn normal_cdf(x: f64) -> f64 {
92 let z = x / std::f64::consts::SQRT_2;
94 let erf = if z >= 0.0 {
95 let t = 1.0 / (1.0 + 0.3275911 * z);
97 let poly = t
98 * (0.254829592
99 + t * (-0.284496736 + t * (1.421413741 + t * (-1.453152027 + t * 1.061405429))));
100 1.0 - poly * (-z * z).exp()
101 } else {
102 let az = -z;
104 let t = 1.0 / (1.0 + 0.3275911 * az);
105 let poly = t
106 * (0.254829592
107 + t * (-0.284496736 + t * (1.421413741 + t * (-1.453152027 + t * 1.061405429))));
108 -(1.0 - poly * (-az * az).exp())
109 };
110 0.5 * (1.0 + erf)
111}
112
113fn solve_modal_eigen(
127 stiffness: &[f64],
128 mass_diag: &[f64],
129 n: usize,
130) -> Result<Vec<(f64, Vec<f64>)>, EngineeringError> {
131 if n == 0 {
132 return Err(EngineeringError::InsufficientData(
133 "system has zero degrees of freedom".to_string(),
134 ));
135 }
136 if stiffness.len() != n * n {
137 return Err(EngineeringError::ValidationError(format!(
138 "stiffness must have n*n = {} entries, got {}",
139 n * n,
140 stiffness.len()
141 )));
142 }
143 if mass_diag.len() != n {
144 return Err(EngineeringError::ValidationError(format!(
145 "mass diagonal must have n = {} entries, got {}",
146 n,
147 mass_diag.len()
148 )));
149 }
150 if mass_diag.iter().any(|&m| !(m > 0.0)) {
151 return Err(EngineeringError::ValidationError(
152 "all lumped masses must be positive".to_string(),
153 ));
154 }
155
156 let inv_sqrt_m: Vec<f64> = mass_diag.iter().map(|&m| 1.0 / m.sqrt()).collect();
158 let mut a = vec![0.0_f64; n * n];
159 for i in 0..n {
160 for j in 0..n {
161 a[i * n + j] = stiffness[i * n + j] * inv_sqrt_m[i] * inv_sqrt_m[j];
162 }
163 }
164
165 let mut eigvecs = vec![0.0_f64; n * n];
166 crate::solvers::linear_algebra::eigen::symmetric_eigen(n, &mut a, &mut eigvecs).map_err(
167 |e| EngineeringError::SolverError(format!("symmetric eigensolver failed: {:?}", e)),
168 )?;
169
170 let mut modes: Vec<(f64, Vec<f64>)> = Vec::with_capacity(n);
173 for j in 0..n {
174 let lambda = a[j * n + j];
175 let omega = lambda.max(0.0).sqrt();
176 let mut phi: Vec<f64> = (0..n).map(|i| eigvecs[i * n + j] * inv_sqrt_m[i]).collect();
178 let max_abs = phi.iter().fold(0.0_f64, |m, &v| m.max(v.abs()));
179 if max_abs > 0.0 {
180 for v in phi.iter_mut() {
181 *v /= max_abs;
182 }
183 }
184 modes.push((omega, phi));
185 }
186 modes.sort_by(|x, y| x.0.partial_cmp(&y.0).unwrap_or(core::cmp::Ordering::Equal));
187 Ok(modes)
188}
189
190pub mod thermal_conduction;
195
196pub mod cfd;
201
202pub mod fem;
208
209mod buckling;
215mod dynamics;
216mod errors;
217mod fluid;
218mod library;
219mod mechanical;
220mod model;
221mod reliability;
222mod structural;
223mod survival;
224mod thermal;
225mod vibration;
226
227pub use buckling::*;
228pub use dynamics::*;
229pub use errors::*;
230pub use fluid::*;
231pub use library::*;
232pub use mechanical::*;
233pub use model::*;
234pub use reliability::*;
235pub use structural::*;
236pub use survival::*;
237pub use thermal::*;
238pub use vibration::*;
239
240#[cfg(test)]
241mod tests;