squid_n_core/model/wall.rs
1//! 壁関連の型(開口・壁属性・雑壁・鉄骨/BRB/PCa 属性など)。
2
3use super::*;
4
5/// 複数開口の取り扱い(耐震壁の開口。RC 規準)。
6/// 建物全体で一律に選択する(`Model::multi_opening_mode`)。
7#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
8pub enum MultiOpeningMode {
9 /// 等価開口とする(既定): l0′·h0′=Σli·hi、l0′:h0′=lw:hw で1開口に置換。
10 #[default]
11 Equivalent,
12 /// 包絡する: 全開口の包絡矩形1つに置換(位置 `offset` が必要。
13 /// 位置不明の開口は包絡対象にできず個別のまま残る)。
14 Envelope,
15 /// 包絡開口・等価開口自動判定: 包絡可能な開口対がなくなるまで繰り返し
16 /// 包絡開口を作成し、残った開口で「等価開口とする」と同様の評価を行う。
17 Auto,
18}
19
20/// 壁の個別開口(耐震壁の複数開口の取り扱い。RC 規準)。
21///
22/// 寸法は壁面内で定義する: `width`=壁長さ方向の開口長さ l0 [mm]、
23/// `height`=壁高さ方向の開口高さ h0 [mm]。
24#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
25pub struct WallOpening {
26 /// 開口長さ l0 [mm](壁長さ方向)。
27 pub width: f64,
28 /// 開口高さ h0 [mm](壁高さ方向)。
29 pub height: f64,
30 /// 開口左下の位置 [mm](壁面内: [壁始端からの水平距離, 壁下端からの高さ])。
31 /// 包絡開口の作成・開口の位置効果評価(将来対応)用。None は位置不定
32 /// (等価開口による面積評価のみに用いられる)。
33 #[serde(default)]
34 pub offset: Option<[f64; 2]>,
35}
36
37impl WallOpening {
38 /// 開口面積 [mm²]。
39 pub fn area(&self) -> f64 {
40 (self.width * self.height).max(0.0)
41 }
42
43 /// 壁面内の矩形 (x0, z0, x1, z1)。位置不明(offset=None)は None。
44 fn rect(&self) -> Option<[f64; 4]> {
45 let [x, z] = self.offset?;
46 Some([x, z, x + self.width.max(0.0), z + self.height.max(0.0)])
47 }
48
49 /// 2開口の包絡開口(外接矩形)。どちらかの位置が不明なら None。
50 pub fn envelope(&self, other: &WallOpening) -> Option<WallOpening> {
51 let a = self.rect()?;
52 let b = other.rect()?;
53 let x0 = a[0].min(b[0]);
54 let z0 = a[1].min(b[1]);
55 let x1 = a[2].max(b[2]);
56 let z1 = a[3].max(b[3]);
57 Some(WallOpening {
58 width: x1 - x0,
59 height: z1 - z0,
60 offset: Some([x0, z0]),
61 })
62 }
63
64 /// 自動判定モードで 2 開口を包絡してよいかの判定
65 /// (複数開口の取り扱いの判定図。RC 規準)。
66 ///
67 /// **l < 1.5·h または l < 1m(1000mm)のとき包絡開口とみなす。**
68 /// - l: 開口間距離(矩形間の純距離。重なっていれば 0)
69 /// - h: 包絡開口とした場合の高さ
70 ///
71 /// 位置(offset)不明の開口は距離を定義できないため包絡不可。
72 pub fn can_envelope(&self, other: &WallOpening) -> bool {
73 let (Some(a), Some(b)) = (self.rect(), other.rect()) else {
74 return false;
75 };
76 // 開口間距離 l: 各方向の純間隔(重なっていれば 0)の合成
77 let gap_x = (a[0].max(b[0]) - a[2].min(b[2])).max(0.0);
78 let gap_z = (a[1].max(b[1]) - a[3].min(b[3])).max(0.0);
79 let l = (gap_x * gap_x + gap_z * gap_z).sqrt();
80 // 包絡開口とした場合の高さ h
81 let h = a[3].max(b[3]) - a[1].min(b[1]);
82 l < 1.5 * h || l < 1000.0
83 }
84}
85
86/// 壁要素(`ElementKind::Wall`/`Shell`)の壁属性
87/// (壁自重(固定荷重)の開口、柱際スリット、および
88/// 剛性計算の開口低減・耐震壁判定に用いる個別開口寸法。RC 規準)。
89#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
90pub struct WallAttr {
91 pub elem: ElemId,
92 /// 開口面積の合計 [mm²]。壁自重から ρ·t·開口面積·g を控除する。
93 /// `openings`(個別開口)が非空の場合はそちらの面積和を優先し、
94 /// 本フィールドは無視される(`total_opening_area` 参照)。
95 #[serde(default)]
96 pub opening_area: f64,
97 /// 開口部(サッシ等)の重量 [N]。控除後に加算する。
98 #[serde(default)]
99 pub opening_weight: f64,
100 /// 耐震スリット。由来する壁版の [`super::WallPlate::slit`] を写したもので、
101 /// 意味も添字の規則も同じである。いずれかの辺が切れていれば耐震壁として
102 /// 成立しない。
103 #[serde(default)]
104 pub slit: super::WallSlit,
105 /// 個別開口の寸法リスト。非空の場合、開口の面積評価(自重控除・
106 /// 開口周比 r0・開口低減率 r)と耐震壁検定の開口供給はこのリストを
107 /// 優先する。空の場合は従来どおり `opening_area`(合計面積のみ)で評価する。
108 #[serde(default)]
109 pub openings: Vec<WallOpening>,
110 /// 仕上げ・増打ちの面荷重強度 [N/mm²]。由来する壁版の
111 /// [`super::WallPlate::finish_intensity`] を写したもので、意味も同じである。
112 /// **躯体の自重は含まない。**
113 ///
114 /// 壁エレメントになる壁版もこの重さを持つため、要素経由の自重算定
115 /// (`squid_n_load::story_gen::self_weight_calc`)が読む。写し忘れると、
116 /// 入力された仕上げ・増打ちが壁エレメントの壁だけ黙って落ちる。
117 #[serde(default)]
118 pub finish_intensity: f64,
119}
120
121impl WallAttr {
122 /// 開口の合計面積 [mm²]。個別開口 `openings` が非空ならその面積和、
123 /// 空なら `opening_area` を返す(全消費側はこのメソッドを経由すること)。
124 pub fn total_opening_area(&self) -> f64 {
125 if self.openings.is_empty() {
126 self.opening_area.max(0.0)
127 } else {
128 self.openings.iter().map(WallOpening::area).sum()
129 }
130 }
131
132 /// 個別開口の (l0, h0) ペア列。個別開口が未入力(面積のみ)なら None。
133 /// 面積ゼロの開口は除外する。
134 pub fn opening_dims(&self) -> Option<Vec<(f64, f64)>> {
135 Self::dims_of(&self.openings)
136 }
137
138 /// 複数開口の取り扱い(`mode`)適用後の (l0, h0) ペア列。
139 /// 個別開口が未入力(面積のみ)なら None(消費側は `opening_area` で評価)。
140 pub fn opening_dims_for(&self, mode: MultiOpeningMode) -> Option<Vec<(f64, f64)>> {
141 Self::dims_of(&self.openings_for_mode(mode))
142 }
143
144 /// 複数開口の取り扱い(`mode`)適用後の開口合計面積 [mm²]。
145 /// 包絡モードでは包絡矩形の面積となるため、生の面積和
146 /// (`total_opening_area`、自重控除用)とは異なり得る。
147 pub fn total_opening_area_for(&self, mode: MultiOpeningMode) -> f64 {
148 if self.openings.is_empty() {
149 self.opening_area.max(0.0)
150 } else {
151 self.openings_for_mode(mode)
152 .iter()
153 .map(WallOpening::area)
154 .sum()
155 }
156 }
157
158 /// 複数開口の取り扱い(RC 規準)を適用した開口リスト。
159 /// - `Equivalent`: 個別開口をそのまま返す(等価開口への統合は消費側の式)。
160 /// - `Envelope`: 位置(offset)を持つ開口全体の包絡矩形 1 つに置換。
161 /// 位置不明の開口は包絡できないため個別のまま残る。
162 /// - `Auto`: 包絡可能(`WallOpening::can_envelope`、l<1.5h または l<1m)な開口対が
163 /// なくなるまで繰り返し包絡開口を作成し、残った開口を返す
164 /// (包絡できなくなった時点の開口状況で『等価開口とする』と
165 /// 同様の判定を行う扱いに対応。等価開口への統合は消費側)。
166 pub fn openings_for_mode(&self, mode: MultiOpeningMode) -> Vec<WallOpening> {
167 match mode {
168 MultiOpeningMode::Equivalent => self.openings.clone(),
169 MultiOpeningMode::Envelope => {
170 let mut out: Vec<WallOpening> = Vec::new();
171 let mut merged: Option<WallOpening> = None;
172 for o in &self.openings {
173 if o.rect().is_some() {
174 merged = Some(match merged {
175 Some(m) => m.envelope(o).expect("両者とも位置あり"),
176 None => o.clone(),
177 });
178 } else {
179 out.push(o.clone());
180 }
181 }
182 if let Some(m) = merged {
183 out.insert(0, m);
184 }
185 out
186 }
187 MultiOpeningMode::Auto => {
188 let mut list: Vec<WallOpening> = self.openings.clone();
189 loop {
190 let mut merged_pair: Option<(usize, usize)> = None;
191 'outer: for i in 0..list.len() {
192 for j in (i + 1)..list.len() {
193 if list[i].can_envelope(&list[j]) {
194 merged_pair = Some((i, j));
195 break 'outer;
196 }
197 }
198 }
199 let Some((i, j)) = merged_pair else {
200 break;
201 };
202 let env = list[i].envelope(&list[j]).expect("can_envelope=位置あり");
203 list.remove(j);
204 list[i] = env;
205 }
206 list
207 }
208 }
209 }
210
211 fn dims_of(openings: &[WallOpening]) -> Option<Vec<(f64, f64)>> {
212 if openings.is_empty() {
213 return None;
214 }
215 let dims: Vec<(f64, f64)> = openings
216 .iter()
217 .filter(|o| o.area() > 0.0)
218 .map(|o| (o.width, o.height))
219 .collect();
220 if dims.is_empty() {
221 None
222 } else {
223 Some(dims)
224 }
225 }
226}
227
228/// S 造部材の断面検定用属性(鋼構造設計規準。鉄骨の断面検定における
229/// 断面性能)。継手部・スカラップによる断面欠損と横座屈長さの指定に用いる。
230#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
231pub struct SteelDesignAttr {
232 pub elem: ElemId,
233 /// 継手部のフランジ欠損率 βf [%](0=欠損なし)
234 #[serde(default)]
235 pub joint_flange_loss: f64,
236 /// 継手部のウェブ欠損率 βw [%]
237 #[serde(default)]
238 pub joint_web_loss: f64,
239 /// スカラップによるウェブ欠損率 αw [%](端部断面に適用)
240 #[serde(default)]
241 pub scallop_web_loss: f64,
242 /// 横座屈長さの直接入力 (始端, 中央, 終端) [mm](None=自動)
243 #[serde(default)]
244 pub lb_direct: Option<(f64, f64, f64)>,
245 /// 等間隔横補剛の本数(lb 自動計算: lb = L/(n+1))
246 #[serde(default)]
247 pub lateral_brace_count: Option<u32>,
248 /// 強軸まわり座屈長さの直接入力 lk_y [mm](None=自動算定)
249 #[serde(default)]
250 pub lk_y_direct: Option<f64>,
251 /// 弱軸まわり座屈長さの直接入力 lk_z [mm](None=自動算定)
252 #[serde(default)]
253 pub lk_z_direct: Option<f64>,
254 /// 横座屈修正係数 C の直接入力(None=自動算定)。
255 /// 入力がある場合は自動算定(M2/M1 比・上限 2.3)を行わず入力値を採用する。
256 #[serde(default)]
257 pub c_direct: Option<f64>,
258}
259
260impl SteelDesignAttr {
261 /// 欠損率が全て 0・横座屈長さ関連の指定が全て `None` か(=側テーブルから
262 /// 削除してよいか)。[`MemberDetailAttr::is_empty`](super::MemberDetailAttr::is_empty)
263 /// の流儀に合わせる。
264 pub fn is_empty(&self) -> bool {
265 self.joint_flange_loss == 0.0
266 && self.joint_web_loss == 0.0
267 && self.scallop_web_loss == 0.0
268 && self.lb_direct.is_none()
269 && self.lateral_brace_count.is_none()
270 && self.lk_y_direct.is_none()
271 && self.lk_z_direct.is_none()
272 && self.c_direct.is_none()
273 }
274}
275
276/// 座屈補剛ブレース(BRB)の断面検定用属性。許容値はメーカー資料による入力値
277/// (各メーカーの製品技術資料による)。
278#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
279pub struct BrbAttr {
280 pub elem: ElemId,
281 /// 短期許容軸力 [N](メーカー値)
282 pub allowable_axial_short: f64,
283 /// 限界座屈長さ [mm](メーカー値)
284 pub critical_length: f64,
285 /// 座屈長さ低減距離 L1 [mm](= (L1上+L1下)/2)
286 #[serde(default)]
287 pub length_reduction: f64,
288}
289
290/// 免震支承材の種別(各免震部材指針)。
291#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
292pub enum IsolatorKind {
293 /// 天然ゴム系積層ゴム(マルチシアスプリング、水平バイリニア)。
294 LaminatedRubber,
295 /// 鉛プラグ挿入型積層ゴム(LRB。鉛 Qd + ゴム二次剛性のバイリニア)。
296 LeadRubber,
297 /// 高減衰ゴム系積層ゴム(HDR。等価せん断剛性が歪 γ 依存)。
298 HighDampingRubber,
299 /// 弾性すべり支承(摩擦ばね、Qmax=μ·N)。
300 ElasticSliding,
301}
302
303fn default_ckd_gamma() -> [f64; 3] {
304 [1.0, 0.0, 0.0]
305}
306fn default_cqd_gamma() -> [f64; 3] {
307 [1.0, 0.0, 0.0]
308}
309
310/// 免震支承材の特性(`ElementKind::Isolator` 要素の非線形特性、
311/// 各免震部材指針)。`Model::isolator_attrs` に要素 ID と対で保持する。
312#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
313pub struct IsolatorProps {
314 /// 支承種別。
315 pub kind: IsolatorKind,
316 /// 水平初期(弾性)剛性 K1 [N/mm]。
317 pub k1: f64,
318 /// 水平二次剛性 K2 [N/mm](積層ゴム系バイリニア。すべり支承は 0)。
319 pub k2: f64,
320 /// 降伏(特性)耐力 Qd [N](積層ゴム系バイリニア)。すべり支承は 0。
321 pub qd: f64,
322 /// 鉛直剛性 Kv [N/mm]。
323 pub kv: f64,
324 /// 摩擦係数 μ(すべり支承。Qmax=μ·N)。
325 pub mu: f64,
326 /// 長期軸力 N [N](摩擦力算定用、圧縮正)。
327 pub n_long: f64,
328 /// マルチシアスプリング本数 n(既定 8、天然ゴム系 2。表示・低減率照合用)。
329 pub n_springs: u32,
330 /// ゴム総厚 H [mm](歪 γ=δ/H の算定用。0 以下で歪依存を無効化)。
331 #[serde(default)]
332 pub total_rubber_thickness: f64,
333 /// 二次剛性の歪依存係数 CKd(γ)=c0+c1·γ+c2·γ²(K2(γ)=K2·CKd(γ))。
334 /// 既定 [1,0,0](歪依存なし)。LRB・高減衰ゴムの歪依存(各免震部材の製品技術資料)。
335 #[serde(default = "default_ckd_gamma")]
336 pub ckd_gamma: [f64; 3],
337 /// 特性耐力の歪依存係数 CQd(γ)=c0+c1·γ+c2·γ²(Qd(γ)=Qd·CQd(γ))。既定 [1,0,0]。
338 #[serde(default = "default_cqd_gamma")]
339 pub cqd_gamma: [f64; 3],
340}
341
342impl Default for IsolatorProps {
343 fn default() -> Self {
344 IsolatorProps {
345 kind: IsolatorKind::LaminatedRubber,
346 k1: 1000.0,
347 k2: 100.0,
348 qd: 50_000.0,
349 kv: 2_000_000.0,
350 mu: 0.1,
351 n_long: 0.0,
352 n_springs: 8,
353 total_rubber_thickness: 0.0,
354 ckd_gamma: default_ckd_gamma(),
355 cqd_gamma: default_cqd_gamma(),
356 }
357 }
358}
359
360/// 免震支承材の属性(要素 ID と特性の対)。
361#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
362pub struct IsolatorAttr {
363 pub elem: ElemId,
364 pub props: IsolatorProps,
365}
366
367/// 制振ダンパーの種別(各制振部材の力学モデル)。
368#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
369pub enum DamperKind {
370 /// マクスウェル要素(バネ Kd と粘性ダッシュポットの直列。速度依存型)。
371 #[default]
372 Maxwell,
373 /// 履歴型(弾塑性バイリニア)ダンパー。鋼材系ダンパー(SUB/アンボンドブレース/
374 /// 二重鋼管座屈補剛ブレース/鉛ダンパー/U 型ダンパー等の標準型バイリニア)。
375 HystereticBilinear,
376}
377
378fn default_damper_qy() -> f64 {
379 50_000.0
380}
381fn default_damper_k2_ratio() -> f64 {
382 0.02
383}
384
385/// 制振ダンパー要素(`ElementKind::Damper`)の特性(各制振部材の力学モデル)。
386///
387/// - **マクスウェル(速度依存型):** バネ剛性 `Kd` と粘性ダッシュポット
388/// (力 `Fc=C0·sign(V)·|V|^α`)の直列。α=1 で線形粘性。`relief_velocity`
389/// (リリーフ速度 Vr)を指定すると、Vr 超過域で減衰係数比 `c2_ratio` による
390/// リリーフ特性(オイルダンパーのバイパス弁による頭打ち特性)を折れ線で
391/// 近似する(`element/src/springs/damper/maxwell.rs` 参照)。
392/// - **履歴型バイリニア:** 初期軸剛性 `Kd`(=k1)、降伏軸力 `qy`、第2剛性比 `k2_ratio`
393/// (k2=k2_ratio·k1)の弾塑性軸ばね(変位依存。静的・動的いずれでも作用)。
394#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
395pub struct DamperProps {
396 /// ダンパー種別。
397 pub kind: DamperKind,
398 /// バネ剛性 Kd [N/mm](履歴型では初期軸剛性 k1)。
399 pub kd: f64,
400 /// 粘性係数 C0 [N·(s/mm)^α](マクスウェルのみ)。
401 pub c0: f64,
402 /// 速度指数 α(1.0 で線形粘性。マクスウェルのみ)。
403 pub alpha: f64,
404 /// 降伏軸力 qy [N](履歴型のみ)。
405 #[serde(default = "default_damper_qy")]
406 pub qy: f64,
407 /// 第2剛性比 k2/k1(履歴型のみ)。
408 #[serde(default = "default_damper_k2_ratio")]
409 pub k2_ratio: f64,
410 /// リリーフ速度 Vr [mm/s](マクスウェルのみ。オイルダンパーのリリーフ機構)。
411 /// `None`(既定)はリリーフ特性なし(従来どおり `Fc=C0·sign(V)·|V|^α` を
412 /// 全域に適用)。`Some(vr)`(vr>0)の場合、|V|≤vr は従来どおり、|V|>vr は
413 /// `c2_ratio` による減衰係数比で頭打ちにした折れ線特性となる
414 /// (式は `c2_ratio` の docs、および `maxwell.rs` 参照)。
415 #[serde(default)]
416 pub relief_velocity: Option<f64>,
417 /// リリーフ後の減衰係数比 C2/C1(C1 はリリーフ速度 Vr における接線減衰係数
418 /// `C1=C0·α·Vr^(α−1)`)。`relief_velocity` が `Some` の場合のみ意味を持つ
419 /// (`None`=リリーフなし)。既定 `None`(リリーフ無効時は未使用のため
420 /// 0 でも同義だが、意図を明確にするため未指定を表す)。
421 #[serde(default)]
422 pub c2_ratio: Option<f64>,
423}
424
425impl Default for DamperProps {
426 fn default() -> Self {
427 DamperProps {
428 kind: DamperKind::Maxwell,
429 kd: 100_000.0,
430 c0: 1_000.0,
431 alpha: 1.0,
432 qy: default_damper_qy(),
433 k2_ratio: default_damper_k2_ratio(),
434 relief_velocity: None,
435 c2_ratio: None,
436 }
437 }
438}
439
440/// 制振ダンパーの属性(要素 ID と特性の対、`Model::damper_attrs`)。
441#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
442pub struct DamperAttr {
443 pub elem: ElemId,
444 pub props: DamperProps,
445}
446
447/// 名前付き制振ダンパー定義(`Model::damper_defs`)。
448///
449/// 「断面を選ぶように制振要素を選んで部材に割当てる」UX の土台となる、
450/// 再利用可能なダンパー諸元のプリセット。`ElemId` への参照は持たず、
451/// 部材への割当時は `props` の値をコピーして `Model::damper_attrs` へ入れる
452/// 設計とする(`DamperAttr` 同様、割当は値のコピーであり参照ではない)。
453/// そのため本定義を更新・削除しても、既に割り当て済みの部材(`damper_attrs`)は
454/// 影響を受けない。
455#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
456pub struct DamperDef {
457 /// 定義名(製品名・型式等、利用者が識別するための表示名)。
458 pub name: String,
459 /// ダンパー特性。
460 pub props: DamperProps,
461}
462
463/// PCa(プレキャスト)梁の水平接合面検定用属性(水平接合面のせん断摩擦検定)。
464#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
465pub struct PcaBeamAttr {
466 pub elem: ElemId,
467 /// 水平接合面の摩擦係数 μ
468 pub mu: f64,
469 /// 接合面を横切る補強筋の体積比合計 p′w(あばら筋+接合面補強筋)
470 pub pw_joint: f64,
471 /// 補強筋の降伏強度 σy [N/mm²]
472 pub sigma_y_joint: f64,
473 /// 接合面の位置: 断面上端からの距離 [mm](例: 後打ちスラブ厚)
474 pub joint_depth_from_top: f64,
475}