soilrust/models/
foundation.rs

1use serde::{Deserialize, Serialize};
2
3use crate::validation::{validate_field, ValidationError};
4
5/// Represents a foundation with geometry and load effects.
6///
7/// # Fields
8///
9/// * `foundation_depth` - Depth of the foundation (m).
10/// * `foundation_length` - Length of the foundation (m).
11/// * `foundation_width` - Width of the foundation (m).
12/// * `foundation_area` - Area of the foundation (m²).
13/// * `effective_length` - Effective length of the foundation after load effects (m).
14/// * `effective_width` - Effective width of the foundation after load effects (m).
15/// * `base_tilt_angle` - Foundation inclination angle (degrees).
16/// * `slope_angle` - Slope angle of the ground (degrees).
17#[derive(Debug, Clone, Serialize, Deserialize, Default)]
18pub struct Foundation {
19    /// Depth of the foundation (m).
20    pub foundation_depth: Option<f64>,
21    /// Length of the foundation (m).
22    pub foundation_length: Option<f64>,
23    /// Width of the foundation (m).
24    pub foundation_width: Option<f64>,
25    /// Area of the foundation (m²).
26    pub foundation_area: Option<f64>,
27    /// Foundation inclination angle (degrees).
28    pub base_tilt_angle: Option<f64>,
29    /// Slope angle of the ground (degrees).
30    pub slope_angle: Option<f64>,
31    /// Effective length of the foundation after load effects (m).
32    pub effective_length: Option<f64>,
33    /// Effective width of the foundation after load effects (m).
34    pub effective_width: Option<f64>,
35    /// Friction coefficient for horizontal sliding (unitless).
36    pub surface_friction_coefficient: Option<f64>,
37}
38
39impl Foundation {
40    /// Creates a new `Foundation` instance.
41    ///
42    /// # Arguments
43    /// * `depth` - Depth of the foundation (m).
44    /// * `length` - Length of the foundation (m).
45    /// * `width` - Width of the foundation (m).
46    /// * `angle` - Foundation inclination angle (degrees).
47    /// * `slope` - Slope angle of the ground (degrees).
48    /// * `area` - Area of the foundation (m²).
49    ///
50    /// # Returns
51    /// A new `Foundation` instance.
52    pub fn new(
53        depth: Option<f64>,
54        length: Option<f64>,
55        width: Option<f64>,
56        angle: Option<f64>,
57        slope: Option<f64>,
58        area: Option<f64>,
59        surface_friction_coefficient: Option<f64>,
60    ) -> Self {
61        Self {
62            foundation_depth: depth,
63            foundation_length: length,
64            foundation_width: width,
65            foundation_area: area,
66            base_tilt_angle: angle,
67            slope_angle: slope,
68            effective_length: None,
69            effective_width: None,
70            surface_friction_coefficient,
71        }
72    }
73    /// Calculates effective lengths based on applied loads.
74    ///
75    /// # Arguments
76    ///
77    /// * `ex` - Eccentricity in x-direction (m).
78    /// * `ey` - Eccentricity in y-direction (m).
79    pub fn calc_effective_lengths(&mut self, ex: f64, ey: f64) {
80        let b_ = self.foundation_width.unwrap() - 2.0 * ex;
81        let l_ = self.foundation_length.unwrap() - 2.0 * ey;
82
83        self.effective_width = Some(f64::min(b_, l_).max(0.0));
84        self.effective_length = Some(f64::max(b_, l_).max(0.0));
85    }
86
87    /// Validates specific fields of the Foundation using field names.
88    /// This enables context-specific validation like `["foundation_depth", "effective_width"]`
89    ///
90    /// # Arguments
91    /// * `fields` - A slice of field names to validate.
92    ///
93    /// # Returns
94    /// Ok(()) if all fields are valid, or an error if any field is invalid.
95    pub fn validate(&self, fields: &[&str]) -> Result<(), ValidationError> {
96        for &field in fields {
97            let result = match field {
98                "foundation_depth" => validate_field(
99                    "foundation_depth",
100                    self.foundation_depth,
101                    Some(0.0),
102                    None,
103                    "foundation",
104                ),
105
106                "foundation_length" => validate_field(
107                    "foundation_length",
108                    self.foundation_length,
109                    Some(0.0001),
110                    None,
111                    "foundation",
112                ),
113
114                "foundation_width" => validate_field(
115                    "foundation_width",
116                    self.foundation_width,
117                    Some(0.001),
118                    self.foundation_length,
119                    "foundation",
120                ),
121
122                "foundation_area" => validate_field(
123                    "foundation_area",
124                    self.foundation_area,
125                    Some(0.001),
126                    None,
127                    "foundation",
128                ),
129
130                "base_tilt_angle" => validate_field(
131                    "base_tilt_angle",
132                    self.base_tilt_angle,
133                    Some(0.0),
134                    Some(45.0),
135                    "foundation",
136                ),
137
138                "slope_angle" => validate_field(
139                    "slope_angle",
140                    self.slope_angle,
141                    Some(0.0),
142                    Some(90.0),
143                    "foundation",
144                ),
145
146                "effective_width" => validate_field(
147                    "effective_width",
148                    self.effective_width,
149                    Some(0.0),
150                    None,
151                    "foundation",
152                ),
153
154                "effective_length" => validate_field(
155                    "effective_length",
156                    self.effective_length,
157                    Some(0.0),
158                    None,
159                    "foundation",
160                ),
161
162                "surface_friction_coefficient" => validate_field(
163                    "surface_friction_coefficient",
164                    self.surface_friction_coefficient,
165                    Some(0.0),
166                    Some(1.0),
167                    "foundation",
168                ),
169
170                unknown => Err(ValidationError {
171                    code: "foundation.invalid_field".into(),
172                    message: format!("Field '{}' is not valid for Foundation.", unknown),
173                }),
174            };
175
176            result?; // propagate error if any field fails
177        }
178
179        Ok(())
180    }
181}