Skip to main content

squid_n_core/model/
section.rs

1//! 断面の型。
2//!
3//! - [`rect_shear_area`] — 矩形断面の有効せん断断面積。
4//! - [`Section`] — 断面(断面性能・形状定義)。
5
6use super::*;
7
8pub fn rect_shear_area(area: f64) -> f64 {
9    area * 5.0 / 6.0
10}
11
12#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
13pub struct Section {
14    pub id: SectionId,
15    /// 断面符号(例 `C1`・`GY2`)。単独では断面を一意に定めない。
16    pub name: String,
17    /// 階(例 `1`・`PH1`)。ST-Bridge の `floor` 属性をそのまま保持する自由文字列で、
18    /// [`Story`](crate::model::Story) への参照ではない(断面の階名と階の名称は
19    /// 一致しないことがあり、対応する階が存在しない階名もあるため)。
20    ///
21    /// 断面の同一性は **符号+階** で決まる。同じ符号でも階が違えば別断面として扱い、
22    /// 逆に符号+階が同じ断面がモデル内に複数存在してはならない。階を持たない断面
23    /// (アプリ内で作成した断面・階の指定がない ST-Bridge 断面)は `None` とし、
24    /// このときは符号だけが同一性キーになる。
25    #[serde(default)]
26    pub floor: Option<String>,
27    pub area: f64,
28    pub iy: f64,
29    pub iz: f64,
30    pub j: f64,
31    #[serde(default)]
32    pub depth: f64,
33    #[serde(default)]
34    pub width: f64,
35    #[serde(default)]
36    pub as_y: f64,
37    #[serde(default)]
38    pub as_z: f64,
39    #[serde(default)]
40    pub panel_thickness: Option<f64>,
41    #[serde(default)]
42    pub thickness: Option<f64>,
43    /// パラメトリック形状定義(UI設計 §4.2: Section は SectionShape の派生)。
44    /// 形状から生成されなかった断面(カタログ数値直入力・ST-Bridge 読込等)は None。
45    #[serde(default)]
46    pub shape: Option<crate::section_shape::SectionShape>,
47    /// 主材料(この断面の弾性剛性 E・ν と自重の密度を決める材料)。
48    ///
49    /// S 断面は鋼材、RC・SRC・CFT 断面はコンクリートを指す。**材料は断面の属性**で
50    /// あり、部材は持たない(材料が違えばそれは別の断面である)。要素から引くときは
51    /// [`Model::element_material`] を用いる。
52    ///
53    /// `None` は未割当。もっともらしい既定値で埋めず、解析へ進む時点で
54    /// 解析前チェックが止める。
55    #[serde(default)]
56    pub material: Option<MaterialId>,
57    /// 主筋の材料(RC・SRC 断面のみ意味を持つ)。降伏点は `Material::fy`。
58    #[serde(default)]
59    pub rebar_material: Option<MaterialId>,
60    /// せん断補強筋の材料(RC・SRC 断面のみ意味を持つ)。
61    ///
62    /// `None` は未設定。呼び出し側は普通強度せん断補強筋の SD295 相当
63    /// ([`crate::material_grade::SHEAR_REBAR_DEFAULT_FY`])を既定とする
64    /// (規格上の最小グレードであり、実際がより高強度でも耐力を過小評価する
65    /// 側=安全側に外れる)。
66    #[serde(default)]
67    pub shear_rebar_material: Option<MaterialId>,
68    /// SRC 断面の内蔵鉄骨の材料(SRC 断面のみ意味を持つ)。
69    #[serde(default)]
70    pub steel_material: Option<MaterialId>,
71}
72
73/// 断面の同一性キー(符号+階)。モデル内で重複してはならない。
74pub type SectionKey<'a> = (&'a str, Option<&'a str>);
75
76impl Section {
77    /// 物性がすべてゼロ・形状も材料も持たない断面。
78    ///
79    /// 用途は 2 つある。1 つは断面を解決できなかったときのフォールバックで、断面 ID が
80    /// 範囲外・世代違い・未割当だった要素の構築(`squid-n-element` の `section_lookup`)と、
81    /// 形鋼名を解決できなかった ST-Bridge 断面の取り込み(`squid-n-io`)が使う。
82    /// もう 1 つは、線材の断面性能を本当に持たない版の断面(壁・スラブ)の土台で、
83    /// `..Section::zero(id, name)` に板厚だけを与える形で使う。
84    ///
85    /// **もっともらしい既定値で埋めてはならない。** 架空の断面性能を与えると、
86    /// 解析前チェック(`precheck_model`・`ensure_nonlinear_input`)を通らない経路から
87    /// 来たモデルが無音のまま解析され、根拠のない結果が返る。物性ゼロにしておけば
88    /// チェックが検出でき、検出漏れがあっても結果が明らかに異常になる。
89    pub fn zero(id: SectionId, name: String) -> Self {
90        Self {
91            id,
92            name,
93            floor: None,
94            area: 0.0,
95            iy: 0.0,
96            iz: 0.0,
97            j: 0.0,
98            depth: 0.0,
99            width: 0.0,
100            as_y: 0.0,
101            as_z: 0.0,
102            panel_thickness: None,
103            thickness: None,
104            shape: None,
105            material: None,
106            rebar_material: None,
107            shear_rebar_material: None,
108            steel_material: None,
109        }
110    }
111
112    /// 同一性キー(符号+階)を借用で返す。
113    pub fn key(&self) -> SectionKey<'_> {
114        (self.name.as_str(), self.floor.as_deref())
115    }
116
117    /// 表示用のラベル。階を持つ断面は `C1 (2)`、持たない断面は符号のみ。
118    pub fn display_name(&self) -> String {
119        match &self.floor {
120            Some(f) => format!("{} ({})", self.name, f),
121            None => self.name.clone(),
122        }
123    }
124
125    /// 断面性能・形状・材料が一致するか(同一性キーは見ない)。
126    /// 取り込み時に符号+階が衝突した断面を統合してよいかの判定に使う。
127    ///
128    /// **材料も比較の対象に含める。** 材料は断面が持ち、違う材料を割り当てるなら
129    /// それは別の断面であるため、材料だけが違う定義を統合すると片方の材料が
130    /// 無言で捨てられる。
131    pub fn properties_eq(&self, other: &Section) -> bool {
132        self.area == other.area
133            && self.iy == other.iy
134            && self.iz == other.iz
135            && self.j == other.j
136            && self.depth == other.depth
137            && self.width == other.width
138            && self.as_y == other.as_y
139            && self.as_z == other.as_z
140            && self.panel_thickness == other.panel_thickness
141            && self.thickness == other.thickness
142            && self.shape == other.shape
143            && self.material == other.material
144            && self.rebar_material == other.rebar_material
145            && self.shear_rebar_material == other.shear_rebar_material
146            && self.steel_material == other.steel_material
147    }
148}
149
150/// `sections` に符号+階が `key` と一致する断面があるか(`skip` の添字は除く)。
151///
152/// 断面の追加・改名の前段でモデル側の不変条件(符号+階は一意)を守るために使う。
153/// `skip` は改名時に自分自身を衝突判定から外すためのもので、追加時は `None`。
154pub fn section_key_taken(sections: &[Section], key: SectionKey<'_>, skip: Option<usize>) -> bool {
155    sections
156        .iter()
157        .enumerate()
158        .any(|(i, s)| Some(i) != skip && s.key() == key)
159}
160
161impl Model {
162    /// 要素の断面。
163    pub fn element_section(&self, elem: &ElementData) -> Option<&Section> {
164        self.sections.get(elem.section?.index())
165    }
166
167    /// 要素の主材料(弾性剛性 E・ν と自重の密度を決める材料)。
168    ///
169    /// **材料は断面が持つ**([`Section::material`])ため、要素からは断面を経由して
170    /// 引く。材料が要る箇所は常にこのヘルパーを情報源とし、断面と材料の対応を
171    /// 呼び出し側へ散らさない。断面が未割当、または断面が材料を持たない場合は `None`。
172    pub fn element_material(&self, elem: &ElementData) -> Option<&Material> {
173        self.materials
174            .get(self.element_section(elem)?.material?.index())
175    }
176
177    /// 二次部材(小梁・間柱)の主材料(自重算定に用いる)。
178    /// 規約は [`Model::element_material`] と同じ。
179    pub fn secondary_material(&self, sm: &SecondaryMember) -> Option<&Material> {
180        let sec = self.sections.get(sm.section?.index())?;
181        self.materials.get(sec.material?.index())
182    }
183
184    /// 要素の主筋材料(RC・SRC 断面のみ)。
185    pub fn element_rebar_material(&self, elem: &ElementData) -> Option<&Material> {
186        self.materials
187            .get(self.element_section(elem)?.rebar_material?.index())
188    }
189
190    /// 要素のせん断補強筋材料(RC・SRC 断面のみ)。
191    pub fn element_shear_rebar_material(&self, elem: &ElementData) -> Option<&Material> {
192        self.materials
193            .get(self.element_section(elem)?.shear_rebar_material?.index())
194    }
195
196    /// 要素の内蔵鉄骨材料(SRC 断面のみ)。
197    pub fn element_steel_material(&self, elem: &ElementData) -> Option<&Material> {
198        self.materials
199            .get(self.element_section(elem)?.steel_material?.index())
200    }
201}