Skip to main content

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}