Skip to main content

squid_n_core/
structure_kind.rs

1//! 部材の構造種別(RC・S・SRC・CFT)の判定。
2//!
3//! 剛域長の算定式・仕口パネルのモデル化対象・断面検定で用いる式・略算周期の
4//! 構造種別・数量集計の分類は、いずれも「その部材が何造か」で分岐する。判定が
5//! 箇所ごとにずれると、剛域長 0 の接合部にパネルが設けられない、鋼部材が RC の
6//! 検定式で検定される、といった食い違いが生じるため、判定を本モジュールへ
7//! 一元化する。
8//!
9//! # 判定の順序
10//!
11//! 1. 断面形状が**複合断面**なら、その種別で決まる
12//!    - `SrcRect` → [`StructureKind::Src`]
13//!    - `CftBox` / `CftPipe` → [`StructureKind::Cft`]
14//! 2. それ以外は**材料の区分**([`MaterialCategory`])で決まる
15//!    - `Steel` → [`StructureKind::S`]
16//!    - `Concrete` / `Rebar` → [`StructureKind::Rc`]
17//! 3. 材料が解決できない場合だけ、断面形状の系統で補う([`shape_default_kind`])
18//! 4. 断面形状もなければ [`StructureKind::Rc`] とする
19//!
20//! # 断面形状ではなく材料で判定する理由
21//!
22//! 断面形状は見た目であって力学的な性質ではない。H 形のコンクリート部材も、
23//! 矩形断面の鋼部材もありうる。材料の区分で判定すれば、任意の材料と任意の断面の
24//! 組み合わせに対して、どの検定式を適用すべきかが定まる。
25//!
26//! SRC・CFT だけを断面形状で判定するのは、これらが 1 つの材料では表せない
27//! **複合断面**だからである。`SrcRect` は内蔵鉄骨のグレードを断面側に持ち、
28//! CFT は `Material::fc` を充填コンクリートの強度として使う。
29//!
30//! # 用途ごとの畳み込み
31//!
32//! 4 種別をそのまま使うのは断面検定と数量集計で、他の用途はより粗い区分へ
33//! 畳み込む。畳み込みは本モジュールのメソッドとして定義し、各所が `matches!` で
34//! 書き下すのを避ける。`SectionShape` にバリアントが増えたとき、追随が必要なのは
35//! [`shape_composite_kind`] の網羅 `match` 1 箇所だけになる。
36//!
37//! | 用途 | 畳み込み |
38//! |---|---|
39//! | 剛域長の算定式・仕口パネルの対象 | [`StructureKind::is_steel_like`](S・CFT) |
40//! | 略算周期の構造種別 | `StoryStructure`(CFT は SRC へ寄せる) |
41//! | 断面検定の式の選択・数量集計 | 4 種別をそのまま使う |
42
43use crate::model::{ElementData, MaterialCategory, Model, Section};
44use crate::section_shape::SectionShape;
45
46/// 部材の構造種別。
47#[derive(Clone, Copy, Debug, PartialEq, Eq)]
48pub enum StructureKind {
49    /// 鉄筋コンクリート造。
50    Rc,
51    /// 鉄骨造。
52    S,
53    /// 鉄骨鉄筋コンクリート造。
54    Src,
55    /// コンクリート充填鋼管造。
56    Cft,
57}
58
59impl StructureKind {
60    /// 表示名。
61    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    /// 鋼系(S・CFT)か。
71    ///
72    /// 剛域長の算定式(S・CFT 造は `D_self/4` を控除しない)と、仕口パネルの
73    /// 対象判定に用いる区分。CFT を S と同じ側へ置くのは、いずれも接合部の
74    /// 剛域長が 0 になり、接合部の有限寸法を剛域で評価しないためである。
75    pub fn is_steel_like(self) -> bool {
76        matches!(self, StructureKind::S | StructureKind::Cft)
77    }
78}
79
80/// 断面形状が複合断面(SRC・CFT)なら、その構造種別を返す。
81///
82/// 単一材料で表せる形状は `None` を返し、呼び出し側が材料の区分で判定する。
83/// `SectionShape` にバリアントが増えたときに追随が要るのはこの網羅 `match` と
84/// [`shape_default_kind`] だけで、追随を忘れるとコンパイルエラーになる。
85pub 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
106/// 材料が解決できないときに断面形状から補う構造種別。
107///
108/// 判定の主は材料の区分だが、材料が未割当の部材まで一律 RC とすると、材料を
109/// 付け忘れた鋼部材に RC の剛域が入って架構が硬くなる。形状名の系統は入力の
110/// 意図をよく表すため、材料がないときに限ってこれを既定として採る。
111pub 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
132/// 材料の区分から構造種別を求める。
133///
134/// 鉄筋は材料としては鋼だが、これを割り当てた線材は S 造ではないため RC とする
135/// (RC 断面の配筋は断面側にグレード名として持ち、線材の材料として鉄筋を
136/// 割り当てるのは入力の誤り)。
137pub 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
144/// 断面と材料から構造種別を判定する(モジュール冒頭「判定の順序」)。
145pub 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        // 材料が解決できない場合は断面形状の系統で補い、形状もなければ RC とする。
156        None => shape.map_or(StructureKind::Rc, shape_default_kind),
157    }
158}
159
160/// 要素の構造種別を判定する。
161pub 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        // 材料は断面が持つ。
214        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    /// 断面形状ではなく材料の区分で判定する。
277    /// H 形のコンクリート部材・矩形断面の鋼部材のいずれも正しく分類できる。
278    #[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    /// 断面形状を持たない断面(カタログ数値の直入力)でも材料で判定できる。
296    #[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    /// 複合断面は材料に依らず断面形状で決まる。
306    #[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    /// 鉄筋は材料としては鋼だが、割り当てた線材は S 造ではない。
336    #[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    /// 材料が未割当の部材は断面形状の系統で補う。
343    /// 材料を付け忘れた鋼部材に RC の剛域が入らないようにするための既定。
344    #[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    /// 断面形状も材料もない部材は RC とする。
361    #[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    /// 剛域式・仕口パネルの判定に使う畳み込みは S と CFT を同じ側へ置く。
370    #[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}