1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct EulerBernoulliBeam {
8 pub e: f64,
10 pub i: f64,
12 pub length: f64,
14 pub loads: Vec<BeamLoad>,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
20pub enum BeamLoad {
21 Point { p: f64, a: f64 },
23 Distributed { w: f64, a_start: f64, a_end: f64 },
25 Moment { m: f64, a: f64 },
27}
28
29impl EulerBernoulliBeam {
30 pub fn new(e: f64, i: f64, length: f64) -> Self {
32 Self { e, i, length, loads: Vec::new() }
33 }
34
35 pub fn flexural_rigidity(&self) -> f64 {
37 self.e * self.i
38 }
39
40 pub fn with_point_load(mut self, p: f64, a: f64) -> Self {
42 self.loads.push(BeamLoad::Point { p, a });
43 self
44 }
45
46 pub fn with_distributed_load(mut self, w: f64, a_start: f64, a_end: f64) -> Self {
48 self.loads.push(BeamLoad::Distributed { w, a_start, a_end });
49 self
50 }
51
52 pub fn with_moment(mut self, m: f64, a: f64) -> Self {
54 self.loads.push(BeamLoad::Moment { m, a });
55 self
56 }
57
58 pub fn deflection_simply_supported_point(&self, x: f64, p: f64, a: f64) -> f64 {
65 let l = self.length;
66 let ei = self.flexural_rigidity();
67 let b = l - a;
68 if x <= a {
69 p * b / (6.0 * l * ei) * ((l * l - b * b) * x - x.powi(3))
70 } else {
71 let x2 = l - x; p * a / (6.0 * l * ei) * ((l * l - a * a) * x2 - x2.powi(3))
73 }
74 }
75
76 pub fn max_deflection_centered_point(&self, p: f64) -> f64 {
79 let l = self.length;
80 let ei = self.flexural_rigidity();
81 p * l.powi(3) / (48.0 * ei)
82 }
83
84 pub fn bending_moment_simply_supported_point(&self, x: f64, p: f64, a: f64) -> f64 {
87 let l = self.length;
88 let b = l - a;
89 if x <= a {
90 p * b * x / l
91 } else {
92 p * a * (l - x) / l
93 }
94 }
95
96 pub fn shear_force_simply_supported_point(&self, x: f64, p: f64, a: f64) -> f64 {
98 let l = self.length;
99 let b = l - a;
100 if x < a {
101 p * b / l
102 } else {
103 -p * a / l
104 }
105 }
106
107 pub fn max_bending_moment_point(&self, p: f64, a: f64) -> f64 {
109 let l = self.length;
110 let b = l - a;
111 p * a * b / l
112 }
113
114 pub fn deflection_cantilever_point_end(&self, x: f64, p: f64) -> f64 {
117 let l = self.length;
118 let ei = self.flexural_rigidity();
119 p / (6.0 * ei) * (3.0 * l * x * x - x.powi(3))
120 }
121
122 pub fn max_deflection_cantilever_point_end(&self, p: f64) -> f64 {
124 let l = self.length;
125 let ei = self.flexural_rigidity();
126 p * l.powi(3) / (3.0 * ei)
127 }
128
129 pub fn bending_moment_cantilever_point_end(&self, x: f64, p: f64) -> f64 {
132 let l = self.length;
133 p * (l - x)
134 }
135
136 pub fn deflection_cantilever_udl(&self, x: f64, w: f64) -> f64 {
139 let l = self.length;
140 let ei = self.flexural_rigidity();
141 w / (24.0 * ei) * (x.powi(4) - 4.0 * l * x.powi(3) + 6.0 * l * l * x * x)
142 }
143
144 pub fn max_deflection_cantilever_udl(&self, w: f64) -> f64 {
146 let l = self.length;
147 let ei = self.flexural_rigidity();
148 w * l.powi(4) / (8.0 * ei)
149 }
150
151 pub fn bending_stress(&self, moment: f64, y: f64) -> f64 {
153 moment * y / self.i
154 }
155
156 pub fn max_bending_stress(&self, moment: f64, section_height: f64) -> f64 {
158 self.bending_stress(moment, section_height / 2.0)
159 }
160
161 pub fn reactions_simply_supported_point(&self, p: f64, a: f64) -> (f64, f64) {
164 let l = self.length;
165 let b = l - a;
166 (p * b / l, p * a / l)
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173 use approx::assert_relative_eq;
174
175 #[test]
176 fn test_simply_supported_centered_deflection() {
177 let beam = EulerBernoulliBeam::new(200e9, 1e-4, 5.0);
178 let p = 10000.0;
180 let expected = p * 125.0 / (48.0 * 200e9 * 1e-4);
181 let actual = beam.max_deflection_centered_point(p);
182 assert_relative_eq!(actual, expected, epsilon = 1e-10);
183 }
184
185 #[test]
186 fn test_cantilever_end_deflection() {
187 let beam = EulerBernoulliBeam::new(200e9, 1e-4, 3.0);
188 let p = 5000.0;
189 let expected = p * 27.0 / (3.0 * 200e9 * 1e-4);
190 let actual = beam.max_deflection_cantilever_point_end(p);
191 assert_relative_eq!(actual, expected, epsilon = 1e-10);
192 }
193
194 #[test]
195 fn test_bending_moment_simply_supported() {
196 let beam = EulerBernoulliBeam::new(200e9, 1e-4, 4.0);
197 let p = 10000.0;
198 let a = 2.0;
199 let m = beam.max_bending_moment_point(p, a);
200 assert_relative_eq!(m, 10000.0, epsilon = 1e-6);
201 }
202
203 #[test]
204 fn test_reactions_sum_to_load() {
205 let beam = EulerBernoulliBeam::new(200e9, 1e-4, 4.0);
206 let p = 10000.0;
207 let (rl, rr) = beam.reactions_simply_supported_point(p, 1.0);
208 assert_relative_eq!(rl + rr, p);
209 assert_relative_eq!(rl, 7500.0);
210 assert_relative_eq!(rr, 2500.0);
211 }
212
213 #[test]
214 fn test_bending_stress() {
215 let beam = EulerBernoulliBeam::new(200e9, 1e-4, 4.0);
216 let stress = beam.bending_stress(5000.0, 0.05);
218 assert_relative_eq!(stress, 2_500_000.0);
219 }
220
221 #[test]
222 fn test_cantilever_udl_deflection() {
223 let beam = EulerBernoulliBeam::new(200e9, 1e-4, 2.0);
224 let w = 5000.0;
225 let expected = w * 16.0 / (8.0 * 200e9 * 1e-4);
226 let actual = beam.max_deflection_cantilever_udl(w);
227 assert_relative_eq!(actual, expected, epsilon = 1e-10);
228 }
229
230 #[test]
231 fn test_deflection_zero_at_supports() {
232 let beam = EulerBernoulliBeam::new(200e9, 1e-4, 5.0);
233 let p = 10000.0;
234 let a = 2.5;
235 let d0 = beam.deflection_simply_supported_point(0.0, p, a);
236 let d_l = beam.deflection_simply_supported_point(5.0, p, a);
237 assert_relative_eq!(d0, 0.0, epsilon = 1e-10);
238 assert_relative_eq!(d_l, 0.0, epsilon = 1e-10);
239 }
240}