use_material_elasticity/
lib.rs1#![forbid(unsafe_code)]
2#[derive(Debug, Clone, Copy, PartialEq)]
24pub struct ElasticModulus {
25 pascals: f64,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum ElasticityError {
30 InvalidStress,
31 InvalidStrain,
32 InvalidModulus,
33 InvalidForce,
34 InvalidLength,
35 InvalidArea,
36}
37
38const fn validate_positive(value: f64, error: ElasticityError) -> Result<f64, ElasticityError> {
39 if value.is_finite() && value > 0.0 {
40 Ok(value)
41 } else {
42 Err(error)
43 }
44}
45
46const fn validate_finite(value: f64, error: ElasticityError) -> Result<f64, ElasticityError> {
47 if value.is_finite() {
48 Ok(value)
49 } else {
50 Err(error)
51 }
52}
53
54impl ElasticModulus {
55 pub fn new(pascals: f64) -> Result<Self, ElasticityError> {
62 Ok(Self {
63 pascals: validate_positive(pascals, ElasticityError::InvalidModulus)?,
64 })
65 }
66
67 #[must_use]
68 pub const fn pascals(&self) -> f64 {
69 self.pascals
70 }
71
72 #[must_use]
73 pub const fn gigapascals(&self) -> f64 {
74 self.pascals / 1_000_000_000.0
75 }
76}
77
78pub fn youngs_modulus(stress_pa: f64, strain: f64) -> Result<f64, ElasticityError> {
86 let stress_pa = validate_finite(stress_pa, ElasticityError::InvalidStress)?;
87 let strain = validate_finite(strain, ElasticityError::InvalidStrain)?;
88
89 if strain == 0.0 {
90 return Err(ElasticityError::InvalidStrain);
91 }
92
93 let modulus = stress_pa / strain;
94 validate_positive(modulus, ElasticityError::InvalidModulus)
95}
96
97pub fn stress_from_modulus(modulus_pa: f64, strain: f64) -> Result<f64, ElasticityError> {
104 Ok(
105 validate_positive(modulus_pa, ElasticityError::InvalidModulus)?
106 * validate_finite(strain, ElasticityError::InvalidStrain)?,
107 )
108}
109
110pub fn strain_from_modulus(stress_pa: f64, modulus_pa: f64) -> Result<f64, ElasticityError> {
118 Ok(validate_finite(stress_pa, ElasticityError::InvalidStress)?
119 / validate_positive(modulus_pa, ElasticityError::InvalidModulus)?)
120}
121
122pub fn elastic_deformation(
129 force_newtons: f64,
130 length_m: f64,
131 area_m2: f64,
132 modulus_pa: f64,
133) -> Result<f64, ElasticityError> {
134 Ok(
135 validate_finite(force_newtons, ElasticityError::InvalidForce)?
136 * validate_positive(length_m, ElasticityError::InvalidLength)?
137 / (validate_positive(area_m2, ElasticityError::InvalidArea)?
138 * validate_positive(modulus_pa, ElasticityError::InvalidModulus)?),
139 )
140}
141
142#[cfg(test)]
143mod tests {
144 use super::{
145 ElasticModulus, ElasticityError, elastic_deformation, strain_from_modulus,
146 stress_from_modulus, youngs_modulus,
147 };
148
149 fn assert_close(actual: f64, expected: f64) {
150 assert!((actual - expected).abs() < 1.0e-12);
151 }
152
153 #[test]
154 fn computes_modulus_and_hookes_law_values() {
155 let modulus = ElasticModulus::new(200_000_000_000.0).unwrap();
156
157 assert_close(modulus.pascals(), 200_000_000_000.0);
158 assert_close(modulus.gigapascals(), 200.0);
159 assert_close(
160 youngs_modulus(400_000_000.0, 0.002).unwrap(),
161 200_000_000_000.0,
162 );
163 assert_close(
164 stress_from_modulus(200_000_000_000.0, 0.002).unwrap(),
165 400_000_000.0,
166 );
167 assert_close(
168 strain_from_modulus(400_000_000.0, 200_000_000_000.0).unwrap(),
169 0.002,
170 );
171 }
172
173 #[test]
174 fn computes_elastic_deformation() {
175 assert_close(
176 elastic_deformation(1_000.0, 2.0, 0.01, 200_000_000_000.0).unwrap(),
177 0.000_001,
178 );
179 }
180
181 #[test]
182 fn rejects_invalid_elasticity_inputs() {
183 assert_eq!(
184 ElasticModulus::new(0.0),
185 Err(ElasticityError::InvalidModulus)
186 );
187 assert_eq!(
188 youngs_modulus(400_000_000.0, 0.0),
189 Err(ElasticityError::InvalidStrain)
190 );
191 assert_eq!(
192 youngs_modulus(-1.0, 0.001),
193 Err(ElasticityError::InvalidModulus)
194 );
195 assert_eq!(
196 stress_from_modulus(f64::NAN, 0.001),
197 Err(ElasticityError::InvalidModulus)
198 );
199 assert_eq!(
200 strain_from_modulus(1.0, -1.0),
201 Err(ElasticityError::InvalidModulus)
202 );
203 assert_eq!(
204 elastic_deformation(1_000.0, 2.0, 0.0, 200_000_000_000.0),
205 Err(ElasticityError::InvalidArea)
206 );
207 }
208}