use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EulerBernoulliBeam {
pub e: f64,
pub i: f64,
pub length: f64,
pub loads: Vec<BeamLoad>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BeamLoad {
Point { p: f64, a: f64 },
Distributed { w: f64, a_start: f64, a_end: f64 },
Moment { m: f64, a: f64 },
}
impl EulerBernoulliBeam {
pub fn new(e: f64, i: f64, length: f64) -> Self {
Self { e, i, length, loads: Vec::new() }
}
pub fn flexural_rigidity(&self) -> f64 {
self.e * self.i
}
pub fn with_point_load(mut self, p: f64, a: f64) -> Self {
self.loads.push(BeamLoad::Point { p, a });
self
}
pub fn with_distributed_load(mut self, w: f64, a_start: f64, a_end: f64) -> Self {
self.loads.push(BeamLoad::Distributed { w, a_start, a_end });
self
}
pub fn with_moment(mut self, m: f64, a: f64) -> Self {
self.loads.push(BeamLoad::Moment { m, a });
self
}
pub fn deflection_simply_supported_point(&self, x: f64, p: f64, a: f64) -> f64 {
let l = self.length;
let ei = self.flexural_rigidity();
let b = l - a;
if x <= a {
p * b / (6.0 * l * ei) * ((l * l - b * b) * x - x.powi(3))
} else {
let x2 = l - x; p * a / (6.0 * l * ei) * ((l * l - a * a) * x2 - x2.powi(3))
}
}
pub fn max_deflection_centered_point(&self, p: f64) -> f64 {
let l = self.length;
let ei = self.flexural_rigidity();
p * l.powi(3) / (48.0 * ei)
}
pub fn bending_moment_simply_supported_point(&self, x: f64, p: f64, a: f64) -> f64 {
let l = self.length;
let b = l - a;
if x <= a {
p * b * x / l
} else {
p * a * (l - x) / l
}
}
pub fn shear_force_simply_supported_point(&self, x: f64, p: f64, a: f64) -> f64 {
let l = self.length;
let b = l - a;
if x < a {
p * b / l
} else {
-p * a / l
}
}
pub fn max_bending_moment_point(&self, p: f64, a: f64) -> f64 {
let l = self.length;
let b = l - a;
p * a * b / l
}
pub fn deflection_cantilever_point_end(&self, x: f64, p: f64) -> f64 {
let l = self.length;
let ei = self.flexural_rigidity();
p / (6.0 * ei) * (3.0 * l * x * x - x.powi(3))
}
pub fn max_deflection_cantilever_point_end(&self, p: f64) -> f64 {
let l = self.length;
let ei = self.flexural_rigidity();
p * l.powi(3) / (3.0 * ei)
}
pub fn bending_moment_cantilever_point_end(&self, x: f64, p: f64) -> f64 {
let l = self.length;
p * (l - x)
}
pub fn deflection_cantilever_udl(&self, x: f64, w: f64) -> f64 {
let l = self.length;
let ei = self.flexural_rigidity();
w / (24.0 * ei) * (x.powi(4) - 4.0 * l * x.powi(3) + 6.0 * l * l * x * x)
}
pub fn max_deflection_cantilever_udl(&self, w: f64) -> f64 {
let l = self.length;
let ei = self.flexural_rigidity();
w * l.powi(4) / (8.0 * ei)
}
pub fn bending_stress(&self, moment: f64, y: f64) -> f64 {
moment * y / self.i
}
pub fn max_bending_stress(&self, moment: f64, section_height: f64) -> f64 {
self.bending_stress(moment, section_height / 2.0)
}
pub fn reactions_simply_supported_point(&self, p: f64, a: f64) -> (f64, f64) {
let l = self.length;
let b = l - a;
(p * b / l, p * a / l)
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_simply_supported_centered_deflection() {
let beam = EulerBernoulliBeam::new(200e9, 1e-4, 5.0);
let p = 10000.0;
let expected = p * 125.0 / (48.0 * 200e9 * 1e-4);
let actual = beam.max_deflection_centered_point(p);
assert_relative_eq!(actual, expected, epsilon = 1e-10);
}
#[test]
fn test_cantilever_end_deflection() {
let beam = EulerBernoulliBeam::new(200e9, 1e-4, 3.0);
let p = 5000.0;
let expected = p * 27.0 / (3.0 * 200e9 * 1e-4);
let actual = beam.max_deflection_cantilever_point_end(p);
assert_relative_eq!(actual, expected, epsilon = 1e-10);
}
#[test]
fn test_bending_moment_simply_supported() {
let beam = EulerBernoulliBeam::new(200e9, 1e-4, 4.0);
let p = 10000.0;
let a = 2.0;
let m = beam.max_bending_moment_point(p, a);
assert_relative_eq!(m, 10000.0, epsilon = 1e-6);
}
#[test]
fn test_reactions_sum_to_load() {
let beam = EulerBernoulliBeam::new(200e9, 1e-4, 4.0);
let p = 10000.0;
let (rl, rr) = beam.reactions_simply_supported_point(p, 1.0);
assert_relative_eq!(rl + rr, p);
assert_relative_eq!(rl, 7500.0);
assert_relative_eq!(rr, 2500.0);
}
#[test]
fn test_bending_stress() {
let beam = EulerBernoulliBeam::new(200e9, 1e-4, 4.0);
let stress = beam.bending_stress(5000.0, 0.05);
assert_relative_eq!(stress, 2_500_000.0);
}
#[test]
fn test_cantilever_udl_deflection() {
let beam = EulerBernoulliBeam::new(200e9, 1e-4, 2.0);
let w = 5000.0;
let expected = w * 16.0 / (8.0 * 200e9 * 1e-4);
let actual = beam.max_deflection_cantilever_udl(w);
assert_relative_eq!(actual, expected, epsilon = 1e-10);
}
#[test]
fn test_deflection_zero_at_supports() {
let beam = EulerBernoulliBeam::new(200e9, 1e-4, 5.0);
let p = 10000.0;
let a = 2.5;
let d0 = beam.deflection_simply_supported_point(0.0, p, a);
let d_l = beam.deflection_simply_supported_point(5.0, p, a);
assert_relative_eq!(d0, 0.0, epsilon = 1e-10);
assert_relative_eq!(d_l, 0.0, epsilon = 1e-10);
}
}