#![forbid(unsafe_code)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ElasticModulus {
pascals: f64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ElasticityError {
InvalidStress,
InvalidStrain,
InvalidModulus,
InvalidForce,
InvalidLength,
InvalidArea,
}
fn validate_positive(value: f64, error: ElasticityError) -> Result<f64, ElasticityError> {
if !value.is_finite() || value <= 0.0 {
Err(error)
} else {
Ok(value)
}
}
fn validate_finite(value: f64, error: ElasticityError) -> Result<f64, ElasticityError> {
if !value.is_finite() {
Err(error)
} else {
Ok(value)
}
}
impl ElasticModulus {
pub fn new(pascals: f64) -> Result<Self, ElasticityError> {
Ok(Self {
pascals: validate_positive(pascals, ElasticityError::InvalidModulus)?,
})
}
#[must_use]
pub fn pascals(&self) -> f64 {
self.pascals
}
#[must_use]
pub fn gigapascals(&self) -> f64 {
self.pascals / 1_000_000_000.0
}
}
pub fn youngs_modulus(stress_pa: f64, strain: f64) -> Result<f64, ElasticityError> {
let stress_pa = validate_finite(stress_pa, ElasticityError::InvalidStress)?;
let strain = validate_finite(strain, ElasticityError::InvalidStrain)?;
if strain == 0.0 {
return Err(ElasticityError::InvalidStrain);
}
let modulus = stress_pa / strain;
validate_positive(modulus, ElasticityError::InvalidModulus)
}
pub fn stress_from_modulus(modulus_pa: f64, strain: f64) -> Result<f64, ElasticityError> {
Ok(
validate_positive(modulus_pa, ElasticityError::InvalidModulus)?
* validate_finite(strain, ElasticityError::InvalidStrain)?,
)
}
pub fn strain_from_modulus(stress_pa: f64, modulus_pa: f64) -> Result<f64, ElasticityError> {
Ok(validate_finite(stress_pa, ElasticityError::InvalidStress)?
/ validate_positive(modulus_pa, ElasticityError::InvalidModulus)?)
}
pub fn elastic_deformation(
force_newtons: f64,
length_m: f64,
area_m2: f64,
modulus_pa: f64,
) -> Result<f64, ElasticityError> {
Ok(
validate_finite(force_newtons, ElasticityError::InvalidForce)?
* validate_positive(length_m, ElasticityError::InvalidLength)?
/ (validate_positive(area_m2, ElasticityError::InvalidArea)?
* validate_positive(modulus_pa, ElasticityError::InvalidModulus)?),
)
}
#[cfg(test)]
mod tests {
use super::{
ElasticModulus, ElasticityError, elastic_deformation, strain_from_modulus,
stress_from_modulus, youngs_modulus,
};
#[test]
fn computes_modulus_and_hookes_law_values() {
let modulus = ElasticModulus::new(200_000_000_000.0).unwrap();
assert_eq!(modulus.pascals(), 200_000_000_000.0);
assert_eq!(modulus.gigapascals(), 200.0);
assert_eq!(
youngs_modulus(400_000_000.0, 0.002).unwrap(),
200_000_000_000.0
);
assert_eq!(
stress_from_modulus(200_000_000_000.0, 0.002).unwrap(),
400_000_000.0
);
assert_eq!(
strain_from_modulus(400_000_000.0, 200_000_000_000.0).unwrap(),
0.002
);
}
#[test]
fn computes_elastic_deformation() {
assert_eq!(
elastic_deformation(1_000.0, 2.0, 0.01, 200_000_000_000.0).unwrap(),
0.000001
);
}
#[test]
fn rejects_invalid_elasticity_inputs() {
assert_eq!(
ElasticModulus::new(0.0),
Err(ElasticityError::InvalidModulus)
);
assert_eq!(
youngs_modulus(400_000_000.0, 0.0),
Err(ElasticityError::InvalidStrain)
);
assert_eq!(
youngs_modulus(-1.0, 0.001),
Err(ElasticityError::InvalidModulus)
);
assert_eq!(
stress_from_modulus(f64::NAN, 0.001),
Err(ElasticityError::InvalidModulus)
);
assert_eq!(
strain_from_modulus(1.0, -1.0),
Err(ElasticityError::InvalidModulus)
);
assert_eq!(
elastic_deformation(1_000.0, 2.0, 0.0, 200_000_000_000.0),
Err(ElasticityError::InvalidArea)
);
}
}