1use crate::model::{ElementData, MaterialCategory, Model, Section};
44use crate::section_shape::SectionShape;
45
46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
48pub enum StructureKind {
49 Rc,
51 S,
53 Src,
55 Cft,
57}
58
59impl StructureKind {
60 pub fn label(self) -> &'static str {
62 match self {
63 StructureKind::Rc => "RC",
64 StructureKind::S => "S",
65 StructureKind::Src => "SRC",
66 StructureKind::Cft => "CFT",
67 }
68 }
69
70 pub fn is_steel_like(self) -> bool {
76 matches!(self, StructureKind::S | StructureKind::Cft)
77 }
78}
79
80pub fn shape_composite_kind(shape: &SectionShape) -> Option<StructureKind> {
86 match shape {
87 SectionShape::SrcRect { .. } => Some(StructureKind::Src),
88 SectionShape::CftBox { .. } | SectionShape::CftPipe { .. } => Some(StructureKind::Cft),
89 SectionShape::RcRect { .. }
90 | SectionShape::RcCircle { .. }
91 | SectionShape::RcWall { .. }
92 | SectionShape::RcSlab { .. }
93 | SectionShape::SteelH { .. }
94 | SectionShape::SteelBox { .. }
95 | SectionShape::SteelAngle { .. }
96 | SectionShape::SteelChannel { .. }
97 | SectionShape::SteelTee { .. }
98 | SectionShape::SteelPipe { .. }
99 | SectionShape::SteelFlatBar { .. }
100 | SectionShape::SteelRoundBar { .. }
101 | SectionShape::SteelLipChannel { .. }
102 | SectionShape::SteelBuiltH { .. } => None,
103 }
104}
105
106pub fn shape_default_kind(shape: &SectionShape) -> StructureKind {
112 match shape {
113 SectionShape::SrcRect { .. } => StructureKind::Src,
114 SectionShape::CftBox { .. } | SectionShape::CftPipe { .. } => StructureKind::Cft,
115 SectionShape::RcRect { .. }
116 | SectionShape::RcCircle { .. }
117 | SectionShape::RcWall { .. }
118 | SectionShape::RcSlab { .. } => StructureKind::Rc,
119 SectionShape::SteelH { .. }
120 | SectionShape::SteelBox { .. }
121 | SectionShape::SteelAngle { .. }
122 | SectionShape::SteelChannel { .. }
123 | SectionShape::SteelTee { .. }
124 | SectionShape::SteelPipe { .. }
125 | SectionShape::SteelFlatBar { .. }
126 | SectionShape::SteelRoundBar { .. }
127 | SectionShape::SteelLipChannel { .. }
128 | SectionShape::SteelBuiltH { .. } => StructureKind::S,
129 }
130}
131
132pub fn material_structure_kind(category: MaterialCategory) -> StructureKind {
138 match category {
139 MaterialCategory::Steel => StructureKind::S,
140 MaterialCategory::Concrete | MaterialCategory::Rebar => StructureKind::Rc,
141 }
142}
143
144pub fn structure_kind_of(
146 sec: Option<&Section>,
147 category: Option<MaterialCategory>,
148) -> StructureKind {
149 let shape = sec.and_then(|s| s.shape.as_ref());
150 if let Some(kind) = shape.and_then(shape_composite_kind) {
151 return kind;
152 }
153 match category {
154 Some(category) => material_structure_kind(category),
155 None => shape.map_or(StructureKind::Rc, shape_default_kind),
157 }
158}
159
160pub fn member_structure_kind(model: &Model, elem: &ElementData) -> StructureKind {
162 let sec = model.element_section(elem);
163 let category = model.element_material(elem).map(|m| m.category);
164 structure_kind_of(sec, category)
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170 use crate::ids::{ElemId, MaterialId, SectionId};
171 use crate::model::{ElementKind, EndCondition, ForceRegime, LocalAxis, Material};
172
173 fn material(category: MaterialCategory) -> Material {
174 Material {
175 strength_factor: None,
176 concrete_class: Default::default(),
177 id: MaterialId(0),
178 name: String::new(),
179 category,
180 young: 205_000.0,
181 poisson: 0.3,
182 density: 0.0,
183 shear: None,
184 fc: None,
185 fy: None,
186 }
187 }
188
189 fn section(shape: Option<SectionShape>) -> Section {
190 Section {
191 id: SectionId(0),
192 name: String::new(),
193 area: 1.0e4,
194 iy: 1.0e8,
195 iz: 1.0e8,
196 j: 1.0e7,
197 depth: 400.0,
198 width: 400.0,
199 as_y: 4.0e3,
200 as_z: 4.0e3,
201 floor: None,
202 panel_thickness: None,
203 thickness: None,
204 shape,
205 material: None,
206 rebar_material: None,
207 shear_rebar_material: None,
208 steel_material: None,
209 }
210 }
211
212 fn model_with(shape: Option<SectionShape>, category: MaterialCategory) -> Model {
213 let sec = Section {
215 material: Some(crate::ids::MaterialId(0)),
216 ..section(shape)
217 };
218 Model {
219 sections: vec![sec],
220 materials: vec![material(category)],
221 elements: vec![ElementData {
222 id: ElemId(0),
223 kind: ElementKind::Beam,
224 nodes: smallvec::smallvec![crate::ids::NodeId(0), crate::ids::NodeId(1)],
225 section: Some(SectionId(0)),
226 local_axis: LocalAxis {
227 ref_vector: [0.0, 1.0, 0.0],
228 },
229 end_cond: [EndCondition::Fixed, EndCondition::Fixed],
230 force_regime: ForceRegime::Auto,
231 rigid_zone: Default::default(),
232 plastic_zone: None,
233 spring: None,
234 }],
235 ..Default::default()
236 }
237 }
238
239 fn h_shape() -> SectionShape {
240 SectionShape::SteelH {
241 height: 400.0,
242 width: 200.0,
243 web_thick: 8.0,
244 flange_thick: 13.0,
245 }
246 }
247
248 fn rect_shape() -> SectionShape {
249 SectionShape::SteelBox {
250 height: 400.0,
251 width: 400.0,
252 thick: 16.0,
253 corner_r: 0.0,
254 }
255 }
256
257 fn rc_rebar() -> crate::section_shape::RcRebar {
258 use crate::section_shape::{BarSet, RcRebar, ShearBar};
259 let bars = BarSet {
260 dia: 25.0,
261 count: 4,
262 layers: 1,
263 };
264 RcRebar {
265 main_x: bars.clone(),
266 main_y: bars,
267 cover: 40.0,
268 shear: ShearBar {
269 dia: 10.0,
270 pitch: 100.0,
271 legs: 2,
272 },
273 }
274 }
275
276 #[test]
279 fn test_material_decides_kind_not_shape() {
280 let m = model_with(Some(h_shape()), MaterialCategory::Concrete);
281 assert_eq!(
282 member_structure_kind(&m, &m.elements[0]),
283 StructureKind::Rc,
284 "H 形でも材料がコンクリートなら RC"
285 );
286
287 let m = model_with(Some(rect_shape()), MaterialCategory::Steel);
288 assert_eq!(
289 member_structure_kind(&m, &m.elements[0]),
290 StructureKind::S,
291 "矩形でも材料が鋼材なら S"
292 );
293 }
294
295 #[test]
297 fn test_shapeless_section_uses_material() {
298 let m = model_with(None, MaterialCategory::Steel);
299 assert_eq!(member_structure_kind(&m, &m.elements[0]), StructureKind::S);
300
301 let m = model_with(None, MaterialCategory::Concrete);
302 assert_eq!(member_structure_kind(&m, &m.elements[0]), StructureKind::Rc);
303 }
304
305 #[test]
307 fn test_composite_shape_wins_over_material() {
308 let src = SectionShape::SrcRect {
309 b: 700.0,
310 d: 700.0,
311 rebar: rc_rebar(),
312 steel_height: 400.0,
313 steel_width: 200.0,
314 steel_web_thick: 8.0,
315 steel_flange_thick: 13.0,
316 };
317 let m = model_with(Some(src), MaterialCategory::Steel);
318 assert_eq!(
319 member_structure_kind(&m, &m.elements[0]),
320 StructureKind::Src
321 );
322
323 let cft = SectionShape::CftBox {
324 height: 400.0,
325 width: 400.0,
326 thick: 16.0,
327 };
328 let m = model_with(Some(cft), MaterialCategory::Concrete);
329 assert_eq!(
330 member_structure_kind(&m, &m.elements[0]),
331 StructureKind::Cft
332 );
333 }
334
335 #[test]
337 fn test_rebar_is_not_steel_structure() {
338 let m = model_with(Some(h_shape()), MaterialCategory::Rebar);
339 assert_eq!(member_structure_kind(&m, &m.elements[0]), StructureKind::Rc);
340 }
341
342 #[test]
345 fn test_missing_material_falls_back_to_shape() {
346 let mut m = model_with(Some(h_shape()), MaterialCategory::Steel);
347 m.sections[0].material = None;
348 assert_eq!(member_structure_kind(&m, &m.elements[0]), StructureKind::S);
349
350 let rc = SectionShape::RcRect {
351 b: 700.0,
352 d: 700.0,
353 rebar: rc_rebar(),
354 };
355 let mut m = model_with(Some(rc), MaterialCategory::Steel);
356 m.sections[0].material = None;
357 assert_eq!(member_structure_kind(&m, &m.elements[0]), StructureKind::Rc);
358 }
359
360 #[test]
362 fn test_no_section_no_material_is_rc() {
363 let mut m = model_with(None, MaterialCategory::Steel);
364 m.sections[0].material = None;
365 m.elements[0].section = None;
366 assert_eq!(member_structure_kind(&m, &m.elements[0]), StructureKind::Rc);
367 }
368
369 #[test]
371 fn test_steel_like_folds_s_and_cft() {
372 assert!(StructureKind::S.is_steel_like());
373 assert!(StructureKind::Cft.is_steel_like());
374 assert!(!StructureKind::Rc.is_steel_like());
375 assert!(!StructureKind::Src.is_steel_like());
376 }
377}