Skip to main content

use_strain/
lib.rs

1#![forbid(unsafe_code)]
2//! Primitive strain helpers.
3//!
4//! Initial calculations assume SI units unless otherwise documented.
5//!
6//! # Examples
7//!
8//! ```rust
9//! use use_strain::{Strain, engineering_strain, percent_strain, strain_from_change};
10//!
11//! let strain = Strain::new(0.05).unwrap();
12//! let engineering = engineering_strain(2.0, 2.1).unwrap();
13//! let from_change = strain_from_change(2.0, 0.1).unwrap();
14//!
15//! assert_eq!(strain.percent(), 5.0);
16//! assert!((engineering - 0.05).abs() < 1.0e-12);
17//! assert!((from_change - 0.05).abs() < 1.0e-12);
18//! assert_eq!(percent_strain(0.05).unwrap(), 5.0);
19//! ```
20
21#[derive(Debug, Clone, Copy, PartialEq)]
22pub struct Strain {
23    ratio: f64,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum StrainError {
28    InvalidStrain,
29    InvalidOriginalLength,
30    InvalidFinalLength,
31    InvalidChangeInLength,
32}
33
34fn validate_finite(value: f64, error: StrainError) -> Result<f64, StrainError> {
35    if !value.is_finite() {
36        Err(error)
37    } else {
38        Ok(value)
39    }
40}
41
42fn validate_original_length(value: f64) -> Result<f64, StrainError> {
43    if !value.is_finite() || value <= 0.0 {
44        Err(StrainError::InvalidOriginalLength)
45    } else {
46        Ok(value)
47    }
48}
49
50impl Strain {
51    pub fn new(ratio: f64) -> Result<Self, StrainError> {
52        Ok(Self {
53            ratio: validate_finite(ratio, StrainError::InvalidStrain)?,
54        })
55    }
56
57    #[must_use]
58    pub fn ratio(&self) -> f64 {
59        self.ratio
60    }
61
62    #[must_use]
63    pub fn percent(&self) -> f64 {
64        self.ratio * 100.0
65    }
66}
67
68pub fn engineering_strain(original_length_m: f64, final_length_m: f64) -> Result<f64, StrainError> {
69    let original_length_m = validate_original_length(original_length_m)?;
70    let final_length_m = validate_finite(final_length_m, StrainError::InvalidFinalLength)?;
71
72    Ok((final_length_m - original_length_m) / original_length_m)
73}
74
75pub fn strain_from_change(
76    original_length_m: f64,
77    change_in_length_m: f64,
78) -> Result<f64, StrainError> {
79    Ok(
80        validate_finite(change_in_length_m, StrainError::InvalidChangeInLength)?
81            / validate_original_length(original_length_m)?,
82    )
83}
84
85pub fn percent_strain(strain: f64) -> Result<f64, StrainError> {
86    Ok(validate_finite(strain, StrainError::InvalidStrain)? * 100.0)
87}
88
89#[cfg(test)]
90mod tests {
91    use super::{Strain, StrainError, engineering_strain, percent_strain, strain_from_change};
92
93    #[test]
94    fn computes_strain_values() {
95        let strain = Strain::new(0.05).unwrap();
96        let engineering = engineering_strain(2.0, 2.1).unwrap();
97        let from_change = strain_from_change(2.0, 0.1).unwrap();
98
99        assert_eq!(strain.ratio(), 0.05);
100        assert_eq!(strain.percent(), 5.0);
101        assert!((engineering - 0.05).abs() < 1.0e-12);
102        assert!((from_change - 0.05).abs() < 1.0e-12);
103        assert_eq!(percent_strain(0.05).unwrap(), 5.0);
104    }
105
106    #[test]
107    fn allows_negative_strain_for_contraction() {
108        assert!((engineering_strain(2.0, 1.9).unwrap() + 0.05).abs() < 1.0e-12);
109        assert_eq!(Strain::new(-0.02).unwrap().percent(), -2.0);
110    }
111
112    #[test]
113    fn rejects_invalid_length_and_strain_inputs() {
114        assert_eq!(Strain::new(f64::NAN), Err(StrainError::InvalidStrain));
115        assert_eq!(
116            engineering_strain(0.0, 2.0),
117            Err(StrainError::InvalidOriginalLength)
118        );
119        assert_eq!(
120            engineering_strain(2.0, f64::INFINITY),
121            Err(StrainError::InvalidFinalLength)
122        );
123        assert_eq!(
124            strain_from_change(-1.0, 0.1),
125            Err(StrainError::InvalidOriginalLength)
126        );
127        assert_eq!(
128            percent_strain(f64::INFINITY),
129            Err(StrainError::InvalidStrain)
130        );
131    }
132}