use super::functions::*;
use std::f64::consts::PI;
pub struct GeometricStiffness {
pub(super) data: Vec<f64>,
pub(super) n: usize,
}
impl GeometricStiffness {
pub fn new(n: usize) -> Self {
Self {
data: vec![0.0; n * n],
n,
}
}
pub fn set(&mut self, i: usize, j: usize, v: f64) {
self.data[i * self.n + j] = v;
}
pub fn get(&self, i: usize, j: usize) -> f64 {
self.data[i * self.n + j]
}
}
pub struct EulerBuckling {
pub e: f64,
pub i: f64,
pub l: f64,
pub k: f64,
}
impl EulerBuckling {
pub fn critical_load(&self) -> f64 {
PI * PI * self.e * self.i / (self.k * self.l).powi(2)
}
pub fn slenderness_ratio(&self, _a: f64, r: f64) -> f64 {
(self.k * self.l) / r
}
}
pub struct BucklingProblem {
pub(super) k: StiffnessMatrix,
pub(super) kg: GeometricStiffness,
pub(super) n: usize,
}
impl BucklingProblem {
pub fn new(n: usize) -> Self {
Self {
k: StiffnessMatrix::new(n),
kg: GeometricStiffness::new(n),
n,
}
}
pub fn set_k(&mut self, i: usize, j: usize, v: f64) {
self.k.set(i, j, v);
}
pub fn set_kg(&mut self, i: usize, j: usize, v: f64) {
self.kg.set(i, j, v);
}
pub fn solve_buckling_load_factors(&self, num_modes: usize) -> Vec<f64> {
let n = self.n;
let num_modes = num_modes.min(n);
let mut factors = Vec::with_capacity(num_modes);
for mode_idx in 0..num_modes {
let shift = if mode_idx == 0 {
0.0
} else {
factors[mode_idx - 1] * 1.01 + 1e-6
};
let lf = self.inverse_power_iteration(shift, 500, 1e-10);
factors.push(lf);
}
factors.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
factors
}
fn inverse_power_iteration(&self, sigma: f64, max_iter: usize, tol: f64) -> f64 {
let n = self.n;
let mut a = vec![0.0f64; n * n];
for i in 0..n {
for j in 0..n {
a[i * n + j] = self.k.get(i, j) - sigma * self.kg.get(i, j);
}
}
let mut v = vec![1.0f64 / (n as f64).sqrt(); n];
let mut lambda = sigma + 1.0;
for _ in 0..max_iter {
let mut w = vec![0.0f64; n];
for (i, w_i) in w.iter_mut().enumerate().take(n) {
for (j, v_j) in v.iter().enumerate().take(n) {
*w_i += self.kg.get(i, j) * v_j;
}
}
let z = match gaussian_solve(&a, &w, n) {
Some(x) => x,
None => return sigma + 1.0,
};
let mut num = 0.0f64;
let mut den = 0.0f64;
for i in 0..n {
let mut kv_i = 0.0f64;
let mut kgv_i = 0.0f64;
for (j, z_j) in z.iter().enumerate().take(n) {
kv_i += self.k.get(i, j) * z_j;
kgv_i += self.kg.get(i, j) * z_j;
}
num += z[i] * kv_i;
den += z[i] * kgv_i;
}
let new_lambda = if den.abs() > 1e-300 {
num / den
} else {
sigma + 1.0
};
let norm: f64 = z.iter().map(|x| x * x).sum::<f64>().sqrt();
if norm > 1e-300 {
v = z.iter().map(|x| x / norm).collect();
}
if (new_lambda - lambda).abs() < tol * (1.0 + lambda.abs()) {
return new_lambda;
}
lambda = new_lambda;
}
lambda
}
}
pub struct KoiterExpansion {
pub lambda_c: f64,
pub a: f64,
pub b: f64,
}
impl KoiterExpansion {
pub fn new(lambda_c: f64, a: f64, b: f64) -> Self {
Self { lambda_c, a, b }
}
pub fn load_factor(&self, xi: f64) -> f64 {
self.lambda_c + self.a * xi + self.b * xi * xi
}
pub fn is_stable(&self) -> bool {
if self.a.abs() > 1e-14 {
false
} else {
self.b > 0.0
}
}
pub fn max_load_with_imperfection(&self, delta: f64, c: f64) -> f64 {
if self.a.abs() < 1e-14 {
if self.b < 0.0 {
(self.lambda_c * (1.0 - c * delta.abs().sqrt())).max(0.0)
} else {
self.lambda_c
}
} else {
let sign = if self.a * delta > 0.0 { 1.0 } else { -1.0 };
let b_abs = self.b.abs().max(1e-30);
let denom = (6.0 * b_abs).powf(1.0 / 3.0);
(self.lambda_c - sign * (self.a.abs() * delta.abs()).powf(2.0 / 3.0) / denom).max(0.0)
}
}
pub fn equilibrium_amplitude(&self, lambda: f64) -> Option<f64> {
if self.a.abs() < 1e-14 {
if self.b <= 0.0 || lambda < self.lambda_c {
return None;
}
Some(((lambda - self.lambda_c) / self.b).sqrt())
} else {
let delta_lambda = lambda - self.lambda_c;
let discriminant = self.a * self.a + 4.0 * self.b * delta_lambda;
if discriminant < 0.0 {
return None;
}
Some((-self.a + discriminant.sqrt()) / (2.0 * self.b))
}
}
}
pub(super) struct StiffnessMatrix {
pub(super) data: Vec<f64>,
pub(super) n: usize,
}
impl StiffnessMatrix {
fn new(n: usize) -> Self {
Self {
data: vec![0.0; n * n],
n,
}
}
fn set(&mut self, i: usize, j: usize, v: f64) {
self.data[i * self.n + j] = v;
}
fn get(&self, i: usize, j: usize) -> f64 {
self.data[i * self.n + j]
}
}
pub struct BucklingAnalysis {
pub n_dof: usize,
pub k_global: Vec<(usize, usize, f64)>,
pub kg_global: Vec<(usize, usize, f64)>,
}
impl BucklingAnalysis {
pub fn new(n_dof: usize) -> Self {
Self {
n_dof,
k_global: Vec::new(),
kg_global: Vec::new(),
}
}
pub fn add_geometric_stiffness(&mut self, element_nodes: [usize; 2], n: f64, l: f64) {
let kge = geometric_stiffness_beam(n, l);
let dofs = [
2 * element_nodes[0],
2 * element_nodes[0] + 1,
2 * element_nodes[1],
2 * element_nodes[1] + 1,
];
for a in 0..4 {
for b in 0..4 {
self.kg_global.push((dofs[a], dofs[b], kge[a][b]));
}
}
}
pub fn critical_load_factor_estimate(&self) -> f64 {
let n = self.n_dof;
let mut k_dense = vec![0.0f64; n * n];
let mut kg_dense = vec![0.0f64; n * n];
for &(r, c, v) in &self.k_global {
if r < n && c < n {
k_dense[r * n + c] += v;
}
}
for &(r, c, v) in &self.kg_global {
if r < n && c < n {
kg_dense[r * n + c] += v;
}
}
let v_vec = vec![1.0f64; n];
let mut num = 0.0f64;
let mut den = 0.0f64;
for i in 0..n {
let mut kv_i = 0.0f64;
let mut kgv_i = 0.0f64;
for j in 0..n {
kv_i += k_dense[i * n + j] * v_vec[j];
kgv_i += kg_dense[i * n + j] * v_vec[j];
}
num += v_vec[i] * kv_i;
den += v_vec[i] * kgv_i;
}
if den.abs() > 1e-300 {
num / den
} else {
f64::INFINITY
}
}
}
pub struct BucklingMode {
pub load_factor: f64,
pub mode_shape: Vec<f64>,
}
pub enum SlendernessClass {
Short,
Medium,
Long,
}
pub struct BucklingModeResult {
pub mode_num: usize,
pub eigenvalue: f64,
pub shape: Vec<f64>,
}