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}