#![allow(missing_docs)]
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct FemBarElement {
pub nodes: [usize; 2],
pub ea: f64,
pub length: f64,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct PyFemAssembly {
pub n_dofs: usize,
elements: Vec<FemBarElement>,
forces: Vec<f64>,
fixed_dofs: Vec<usize>,
k_global: Vec<f64>,
displacements: Vec<f64>,
assembled: bool,
}
impl PyFemAssembly {
pub fn new(n_nodes: usize) -> Self {
let n_dofs = n_nodes * 3;
Self {
n_dofs,
elements: Vec::new(),
forces: vec![0.0; n_dofs],
fixed_dofs: Vec::new(),
k_global: vec![0.0; n_dofs * n_dofs],
displacements: vec![0.0; n_dofs],
assembled: false,
}
}
pub fn add_bar_element(&mut self, node_a: usize, node_b: usize, ea: f64, length: f64) {
self.elements.push(FemBarElement {
nodes: [node_a, node_b],
ea,
length,
});
self.assembled = false;
}
pub fn apply_force(&mut self, dof: usize, force: f64) {
if dof < self.n_dofs {
self.forces[dof] += force;
}
}
pub fn fix_dof(&mut self, dof: usize) {
if dof < self.n_dofs && !self.fixed_dofs.contains(&dof) {
self.fixed_dofs.push(dof);
}
}
pub fn assemble(&mut self) {
for v in &mut self.k_global {
*v = 0.0;
}
let n = self.n_dofs;
for elem in &self.elements {
let ea_l = if elem.length > 1e-15 {
elem.ea / elem.length
} else {
0.0
};
let dof_a = elem.nodes[0] * 3;
let dof_b = elem.nodes[1] * 3;
if dof_a < n && dof_b < n {
self.k_global[dof_a * n + dof_a] += ea_l;
self.k_global[dof_a * n + dof_b] -= ea_l;
self.k_global[dof_b * n + dof_a] -= ea_l;
self.k_global[dof_b * n + dof_b] += ea_l;
}
}
let big = 1.0e20;
for i in 0..n {
if self.k_global[i * n + i].abs() < 1e-30 {
self.k_global[i * n + i] = big;
self.forces[i] = 0.0;
}
}
for &dof in &self.fixed_dofs {
if dof < n {
for col in 0..n {
self.k_global[dof * n + col] = 0.0;
}
self.k_global[dof * n + dof] = big;
self.forces[dof] = 0.0;
}
}
self.assembled = true;
}
pub fn solve(&mut self) -> bool {
if !self.assembled {
self.assemble();
}
let n = self.n_dofs;
if n == 0 {
return false;
}
let mut aug: Vec<f64> = vec![0.0; n * (n + 1)];
for i in 0..n {
for j in 0..n {
aug[i * (n + 1) + j] = self.k_global[i * n + j];
}
aug[i * (n + 1) + n] = self.forces[i];
}
for col in 0..n {
let mut max_row = col;
let mut max_val = aug[col * (n + 1) + col].abs();
for row in (col + 1)..n {
let v = aug[row * (n + 1) + col].abs();
if v > max_val {
max_val = v;
max_row = row;
}
}
if max_val < 1e-15 {
return false; }
if max_row != col {
for j in 0..=(n) {
aug.swap(col * (n + 1) + j, max_row * (n + 1) + j);
}
}
let pivot = aug[col * (n + 1) + col];
for row in (col + 1)..n {
let factor = aug[row * (n + 1) + col] / pivot;
for j in col..=(n) {
let sub = factor * aug[col * (n + 1) + j];
aug[row * (n + 1) + j] -= sub;
}
}
}
let mut u = vec![0.0f64; n];
for i in (0..n).rev() {
let mut sum = aug[i * (n + 1) + n];
for j in (i + 1)..n {
sum -= aug[i * (n + 1) + j] * u[j];
}
u[i] = sum / aug[i * (n + 1) + i];
}
self.displacements = u;
true
}
pub fn displacement(&self, dof: usize) -> f64 {
self.displacements.get(dof).copied().unwrap_or(0.0)
}
pub fn displacements(&self) -> &[f64] {
&self.displacements
}
pub fn element_force(&self, elem_idx: usize) -> Option<f64> {
let elem = self.elements.get(elem_idx)?;
if elem.length < 1e-15 {
return Some(0.0);
}
let dof_a = elem.nodes[0] * 3;
let dof_b = elem.nodes[1] * 3;
let ua = self.displacements.get(dof_a).copied().unwrap_or(0.0);
let ub = self.displacements.get(dof_b).copied().unwrap_or(0.0);
Some(elem.ea / elem.length * (ub - ua))
}
}