Skip to main content

squid_n_core/model/
load.rs

1//! 荷重関連の型(節点荷重・部材荷重・荷重ケース・荷重条件など)。
2
3use super::*;
4
5/// 節点に作用する荷重。同一の荷重ケース内で 1 つの節点に何件でも定義できる
6/// (解析では全件が加算される)。
7#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
8pub struct NodalLoad {
9    pub node: NodeId,
10    pub values: [f64; 6],
11    /// 利用者が付けた荷重の名称。空文字は無名(一覧では成分値から作った
12    /// 自動ラベルで表示する)。
13    pub name: String,
14    /// この荷重を作ったのが準備計算か利用者か。準備計算が作った荷重は同期のたびに
15    /// 再生成されるため、利用者は編集・削除できない([`LoadSource`] を参照)。
16    pub source: LoadSource,
17}
18
19/// 荷重をどこが作ったか。準備計算による自動生成分と利用者の手入力を区別し、
20/// 自動同期が手入力を消さないようにする。
21#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
22pub enum LoadSource {
23    /// 利用者が入力した荷重。自動同期の対象外で、消えることはない。
24    #[default]
25    Manual,
26    /// 準備計算(床荷重の分配・自重の集計・Ai 分布の水平力)が生成した荷重。
27    /// 同期のたびに全件が作り直される。
28    Auto,
29}
30
31impl LoadSource {
32    /// 準備計算が生成した荷重か。
33    pub fn is_auto(self) -> bool {
34        matches!(self, LoadSource::Auto)
35    }
36}
37
38impl NodalLoad {
39    /// 利用者入力の節点荷重(名称なし)を作る。
40    pub fn manual(node: NodeId, values: [f64; 6]) -> Self {
41        Self {
42            node,
43            values,
44            name: String::new(),
45            source: LoadSource::Manual,
46        }
47    }
48
49    /// 準備計算が生成する節点荷重を作る。
50    pub fn auto(node: NodeId, values: [f64; 6]) -> Self {
51        Self {
52            node,
53            values,
54            name: String::new(),
55            source: LoadSource::Auto,
56        }
57    }
58}
59
60/// 部材(梁)荷重の種別。位置・強度はすべて部材ローカル x 軸(i→j)に沿った
61/// 距離 [mm] と強度で与える。作用方向は `MemberLoad::dir`(全体座標)で指定する。
62#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
63pub enum MemberLoadKind {
64    /// 中間集中荷重: i 端から距離 `a` [mm] の位置に大きさ `p` [N]。
65    Point { a: f64, p: f64 },
66    /// 区間分布荷重: [`a`, `b`] 区間に強度 `w1`→`w2` [N/mm] の線形分布。
67    /// 等分布は `w1 == w2`、全長は `a = 0, b = L`、三角形は端の強度を 0 にする。
68    Distributed { a: f64, b: f64, w1: f64, w2: f64 },
69}
70
71/// 部材に作用する荷重。`dir` は全体座標系での作用方向(内部で正規化)。
72/// 既定の重力方向は `[0.0, 0.0, -1.0]`。
73#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
74pub struct MemberLoad {
75    pub elem: ElemId,
76    pub dir: [f64; 3],
77    pub kind: MemberLoadKind,
78    /// 利用者が付けた荷重の名称。空文字は無名([`NodalLoad::name`] と同じ規約)。
79    pub name: String,
80    /// 準備計算が生成した荷重か([`NodalLoad::source`] と同じ規約)。
81    pub source: LoadSource,
82}
83
84impl MemberLoad {
85    /// 利用者入力の部材荷重(名称なし)を作る。
86    pub fn manual(elem: ElemId, dir: [f64; 3], kind: MemberLoadKind) -> Self {
87        Self {
88            elem,
89            dir,
90            kind,
91            name: String::new(),
92            source: LoadSource::Manual,
93        }
94    }
95
96    /// 準備計算が生成する部材荷重を作る。
97    pub fn auto(elem: ElemId, dir: [f64; 3], kind: MemberLoadKind) -> Self {
98        Self {
99            elem,
100            dir,
101            kind,
102            name: String::new(),
103            source: LoadSource::Auto,
104        }
105    }
106}
107
108/// 荷重ケースの種別。地震用重量の集計(固定+地震用積載)や
109/// 荷重組合せの自動生成(長期・短期・多雪区域の係数)に用いる。
110/// 旧スキーマ・種別未指定は `Other`(従来の「先頭ケースを重力とみなす」
111/// フォールバック規則の対象)。
112#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
113pub enum LoadCaseKind {
114    /// 固定荷重(自重・仕上げ)
115    Dead,
116    /// 積載荷重(架構用・長期)
117    Live,
118    /// 積載荷重(地震用)。地震用重量の集計にはこちらを用いる(令85条)。
119    LiveSeismic,
120    /// 積雪荷重
121    Snow,
122    /// 風荷重
123    Wind,
124    /// 地震荷重(自動生成された水平力など)
125    Seismic,
126    #[default]
127    Other,
128}
129
130impl LoadCaseKind {
131    /// 長期応力解析の対象となる荷重ケース種別か(令82条の応力解析)。
132    ///
133    /// 固定・積載・積雪(多雪区域の 0.7S 相当を含む常時荷重として登録される想定)と、
134    /// 種別未指定 `Other`(従来の「先頭ケースを重力とみなす」フォールバック)を長期として扱う。
135    /// 地震用積載(`LiveSeismic`。重量集計専用)・風・地震は短期側なので対象外。
136    pub fn is_long_term(&self) -> bool {
137        matches!(
138            self,
139            LoadCaseKind::Dead | LoadCaseKind::Live | LoadCaseKind::Snow | LoadCaseKind::Other
140        )
141    }
142}
143
144#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
145pub struct LoadCase {
146    pub id: LoadCaseId,
147    pub name: String,
148    pub nodal: Vec<NodalLoad>,
149    /// 部材(梁)荷重。既存データとの後方互換のため `#[serde(default)]`。
150    #[serde(default)]
151    pub member: Vec<MemberLoad>,
152    /// 荷重種別。旧スキーマは `Other`。
153    #[serde(default)]
154    pub kind: LoadCaseKind,
155}
156
157impl LoadCase {
158    /// 手入力の節点荷重を「`nodal` 上の添字」付きで列挙する。
159    /// 編集・削除コマンドは添字で対象を指すため、表示側も添字を持ち回る。
160    pub fn manual_nodal(&self) -> impl Iterator<Item = (usize, &NodalLoad)> {
161        self.nodal
162            .iter()
163            .enumerate()
164            .filter(|(_, nl)| !nl.source.is_auto())
165    }
166
167    /// 手入力の部材荷重を「`member` 上の添字」付きで列挙する。
168    pub fn manual_member(&self) -> impl Iterator<Item = (usize, &MemberLoad)> {
169        self.member
170            .iter()
171            .enumerate()
172            .filter(|(_, ml)| !ml.source.is_auto())
173    }
174
175    /// 準備計算が生成した荷重だけを `auto_nodal` / `auto_member` の内容へ入れ替える。
176    /// 手入力の荷重は順序を保ったまま残す。
177    ///
178    /// 渡された荷重の `source` は [`LoadSource::Auto`] に揃える。手入力扱いのまま
179    /// 積むと次回の入れ替えで残ってしまい、同期のたびに荷重が増え続けるため。
180    pub fn replace_auto_loads(&mut self, auto_nodal: Vec<NodalLoad>, auto_member: Vec<MemberLoad>) {
181        self.nodal.retain(|nl| !nl.source.is_auto());
182        self.nodal.extend(auto_nodal.into_iter().map(|mut nl| {
183            nl.source = LoadSource::Auto;
184            nl
185        }));
186        self.member.retain(|ml| !ml.source.is_auto());
187        self.member.extend(auto_member.into_iter().map(|mut ml| {
188            ml.source = LoadSource::Auto;
189            ml
190        }));
191    }
192
193    /// 自動生成分が `auto_nodal` / `auto_member` と一致するか(同期の要否判定)。
194    /// 手入力分は比較に含めない。
195    pub fn auto_loads_match(&self, auto_nodal: &[NodalLoad], auto_member: &[MemberLoad]) -> bool {
196        self.nodal
197            .iter()
198            .filter(|nl| nl.source.is_auto())
199            .eq(auto_nodal.iter())
200            && self
201                .member
202                .iter()
203                .filter(|ml| ml.source.is_auto())
204                .eq(auto_member.iter())
205    }
206}
207
208/// 固定荷重(DL)の標準荷重ケース名。躯体自重(柱・梁・ブレース・壁・ダンパー・二次部材)と
209/// スラブの固定荷重(仕上げ等)が解析実行前の同期アクションで自動集計される。
210/// 自動集計が入れ替えるのは [`LoadSource::Auto`] の荷重だけなので、このケースへ
211/// 手入力で荷重を足しても消えない。
212pub const DL_CASE_NAME: &str = "DL";
213
214/// 積載荷重(LL・架構用)の標準荷重ケース名。スラブ用途(令別表第1)の
215/// 骨組用積載が自動分配される(長期骨組解析用。令85条1項)。
216pub const LL_FRAME_CASE_NAME: &str = "LL(架構用)";
217
218/// 積載荷重(LL・地震用)の標準荷重ケース名。スラブ用途(令別表第1)の
219/// 地震用積載が自動分配され、地震用重量の集計に用いる(令85条1項・令88条)。
220pub const LL_SEISMIC_CASE_NAME: &str = "LL(地震用)";
221
222/// 地震荷重(X 方向・Ai 分布)の標準荷重ケース名。階の定義があるとき、
223/// 解析実行前の同期アクションで水平力(Ai 分布)が自動生成される。
224pub const EX_CASE_NAME: &str = "EX";
225
226/// 地震荷重(Y 方向・Ai 分布)の標準荷重ケース名。[`EX_CASE_NAME`] の Y 方向版。
227pub const EY_CASE_NAME: &str = "EY";
228
229/// 新規モデルにデフォルトで用意する標準荷重ケース一式
230/// (DL・LL(架構用)・LL(地震用)・EX・EY。内容は空で、解析実行前の
231/// 同期アクションが自動計算値を書き込む)。ID は 0 起点の連番
232/// (`Model::validate` の「id == 添字」規約に従う)。
233pub fn default_load_cases() -> Vec<LoadCase> {
234    let make = |i: u32, name: &str, kind: LoadCaseKind| LoadCase {
235        id: LoadCaseId(i),
236        name: name.to_string(),
237        nodal: Vec::new(),
238        member: Vec::new(),
239        kind,
240    };
241    vec![
242        make(0, DL_CASE_NAME, LoadCaseKind::Dead),
243        make(1, LL_FRAME_CASE_NAME, LoadCaseKind::Live),
244        make(2, LL_SEISMIC_CASE_NAME, LoadCaseKind::LiveSeismic),
245        make(3, EX_CASE_NAME, LoadCaseKind::Seismic),
246        make(4, EY_CASE_NAME, LoadCaseKind::Seismic),
247    ]
248}
249
250#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
251pub struct LoadCombination {
252    pub name: String,
253    pub terms: Vec<(LoadCaseId, f64)>,
254}
255
256/// 新規モデルにデフォルトで用意する標準荷重組合せ一式
257/// (長期 1 + 短期地震 4 の計 5 組合せ)。
258///
259/// [`default_load_cases`] が生成する標準ケースの並び
260/// (0:DL、1:LL(架構用)、2:LL(地震用)、3:EX、4:EY)を前提に、
261/// [`crate::load_combo::standard_combinations`] を積雪なし・非多雪区域で呼ぶ。
262///
263/// - 長期: `DL + LL`
264/// - 短期地震: `DL + LL + EX`/`DL + LL - EX`/`DL + LL + EY`/`DL + LL - EY`
265///
266/// 長期には架構用の積載(令85条1項の長期骨組解析用)を用いる。
267/// 生成規則そのものは令82条の一般実装と**同一の関数**であり、両者が食い違う
268/// 余地はない(かつては本関数が組合せを手書きしており、一致はテストによる
269/// 手動同期でのみ担保していた)。
270pub fn default_combinations() -> Vec<LoadCombination> {
271    // ID は default_load_cases() の並びに対応する。
272    crate::load_combo::standard_combinations(&crate::load_combo::ComboInput {
273        dl: LoadCaseId(0),
274        ll: LoadCaseId(1),
275        seismic_x: Some(LoadCaseId(3)),
276        seismic_y: Some(LoadCaseId(4)),
277        snow: None,
278        heavy_snow_zone: false,
279        snow_factors: None,
280    })
281}
282
283/// ダンパー装置の自重諸元(固定荷重)。
284/// 自重 = 装置重量 + 支持部断面積 ×(節点間距離 − 装置長さ)× 鋼材単位体積重量。
285/// 両端節点へ 1/2 ずつ伝達(鉛直配置は上下階へ、水平配置は同一階の両節点へ、
286/// が節点標高から自然に成立する)。
287#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
288pub struct DamperSpec {
289    pub elem: ElemId,
290    /// 装置重量 [N](直接入力)。自重を考慮しない装置は 0 を入力する
291    /// (自重を考慮しない部材の扱い)。
292    pub device_weight: f64,
293    /// 装置長さ [mm]。支持部長さ =(節点間距離 − 装置長さ)の算定に用いる。
294    pub device_length: f64,
295    /// 支持部断面積 [mm²]。0 なら支持部重量なし。
296    pub support_area: f64,
297}
298
299/// K 型ブレースの重量配分規則(固定荷重の重量配分規則)。
300#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
301pub enum KBraceWeightRule {
302    /// 内部節点(ブレース同士のみが接続する節点)にも重量を配分する(両端 1/2)。
303    #[default]
304    InternalNodes,
305    /// 基準節点(柱梁が接続する節点)にのみ重量を配分する。
306    BaseNodesOnly,
307}
308
309/// 自重算定の付加設定(固定荷重の鉄骨重量割増率・
310/// 仕上げ荷重・耐火被覆・ダンパー自重・K型ブレース配分に対応する簡易版)。
311#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
312pub struct LoadCfg {
313    /// 鉄骨重量割増率 α(デフォルト 1.0)。コンクリート材(`fc` あり)には適用しない。
314    /// 0 以下が入力された場合は 1.0 として扱う(本実装の規則)。
315    pub steel_weight_factor: f64,
316    /// 部材ごとの付加線重量 [N/mm](耐火被覆 γc·Ac 等の直接入力)。
317    pub extra_line_weight: Vec<(ElemId, f64)>,
318    /// 部材ごとの仕上げ面重量 w_f [N/mm²]。断面寸法から仕上げ周長
319    /// (梁: b+2D の三面、柱: 2(b+D) の四周)を求めて線重量 w_f·φ に換算し
320    /// 自重へ加算する(固定荷重の仕上げ荷重)。
321    #[serde(default)]
322    pub finish_area_weight: Vec<(ElemId, f64)>,
323    /// ダンパー装置の自重諸元。対象部材の断面自重(ρ·A·L·g)は使わず、
324    /// この諸元による装置+支持部重量で置き換える。
325    #[serde(default)]
326    pub dampers: Vec<DamperSpec>,
327    /// K 型ブレース(`ElementKind::Brace`)の重量配分規則。
328    #[serde(default)]
329    pub k_brace_rule: KBraceWeightRule,
330    /// 支える床の数に応じた柱軸力算定時の積載荷重低減(令85条2項)を考慮するか。
331    /// デフォルトは「低減を考慮しない」。
332    #[serde(default)]
333    pub live_load_reduction: bool,
334}
335
336impl Default for LoadCfg {
337    fn default() -> Self {
338        Self {
339            steel_weight_factor: 1.0,
340            extra_line_weight: Vec::new(),
341            finish_area_weight: Vec::new(),
342            dampers: Vec::new(),
343            k_brace_rule: KBraceWeightRule::default(),
344            live_load_reduction: false,
345        }
346    }
347}
348
349impl LoadCfg {
350    /// 有効な鉄骨重量割増率(0 以下の入力は 1.0 とみなす)。
351    pub fn effective_steel_factor(&self) -> f64 {
352        if self.steel_weight_factor > 0.0 {
353            self.steel_weight_factor
354        } else {
355            1.0
356        }
357    }
358}