Skip to main content

squid_n_core/model/
vibration.rs

1//! 振動荷重ケース(立体時刻歴・質点系時刻歴)。
2//!
3//! 静的荷重ケース([`LoadCase`])とは別系統で、解析実行時にのみ生成する。
4
5use crate::ids::{LumpedVibrationCaseId, VibrationCaseId};
6
7/// 立体時刻歴の入力方向([`squid_n_job::settings::ThDir`] と同値だが core 単体で完結させる)。
8#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
9pub enum VibrationThDir {
10    X,
11    Y,
12    Xy,
13}
14
15/// 質点系振動ケースの入力方向(X/Y のみ)。
16#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17pub enum LumpedVibrationDir {
18    X,
19    Y,
20}
21
22/// 質点系振動ケースのモデル次元。
23#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
24pub enum LumpedVibrationDim {
25    /// 2 次元せん断串。
26    Planar,
27    /// 3 次元。
28    Spatial,
29}
30
31/// 立体時刻歴応答解析の振動ケース(実行時にモデルへ upsert する)。
32#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
33pub struct VibrationCase {
34    pub id: VibrationCaseId,
35    pub name: String,
36    pub wave_name: String,
37    pub dir: VibrationThDir,
38    pub nonlinear: bool,
39}
40
41/// 質点系時刻歴応答解析の振動ケース。
42#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
43pub struct LumpedVibrationCase {
44    pub id: LumpedVibrationCaseId,
45    pub name: String,
46    pub wave_name: String,
47    pub dir: LumpedVibrationDir,
48    pub nonlinear: bool,
49    pub dim: LumpedVibrationDim,
50}
51
52fn dir_label_th(dir: VibrationThDir) -> &'static str {
53    match dir {
54        VibrationThDir::X => "X",
55        VibrationThDir::Y => "Y",
56        VibrationThDir::Xy => "X+Y",
57    }
58}
59
60fn dir_label_lumped(dir: LumpedVibrationDir) -> &'static str {
61    match dir {
62        LumpedVibrationDir::X => "X",
63        LumpedVibrationDir::Y => "Y",
64    }
65}
66
67fn linearity_label(nonlinear: bool) -> &'static str {
68    if nonlinear {
69        "非線形"
70    } else {
71        "線形"
72    }
73}
74
75fn dim_label(dim: LumpedVibrationDim) -> &'static str {
76    match dim {
77        LumpedVibrationDim::Planar => "2次元",
78        LumpedVibrationDim::Spatial => "3次元",
79    }
80}
81
82/// 立体時刻歴振動ケースの表示名(規約 B)。
83pub fn spatial_vibration_case_name(
84    wave_name: &str,
85    dir: VibrationThDir,
86    nonlinear: bool,
87) -> String {
88    format!(
89        "{} {} ({})",
90        wave_name,
91        dir_label_th(dir),
92        linearity_label(nonlinear)
93    )
94}
95
96/// 質点系振動ケースの表示名。
97pub fn lumped_vibration_case_name(
98    wave_name: &str,
99    dir: LumpedVibrationDir,
100    nonlinear: bool,
101    dim: LumpedVibrationDim,
102) -> String {
103    format!(
104        "{} {} ({}・{})",
105        wave_name,
106        dir_label_lumped(dir),
107        linearity_label(nonlinear),
108        dim_label(dim)
109    )
110}
111
112fn next_vibration_case_id(cases: &[VibrationCase]) -> VibrationCaseId {
113    let next = cases
114        .iter()
115        .map(|c| c.id.0)
116        .max()
117        .map(|m| m + 1)
118        .unwrap_or(0);
119    VibrationCaseId(next)
120}
121
122fn next_lumped_vibration_case_id(cases: &[LumpedVibrationCase]) -> LumpedVibrationCaseId {
123    let next = cases
124        .iter()
125        .map(|c| c.id.0)
126        .max()
127        .map(|m| m + 1)
128        .unwrap_or(0);
129    LumpedVibrationCaseId(next)
130}
131
132impl super::Model {
133    /// 同名の立体振動ケースがあれば ID を維持して属性を更新し、なければ追加する。
134    pub fn upsert_vibration_case(
135        &mut self,
136        wave_name: String,
137        dir: VibrationThDir,
138        nonlinear: bool,
139    ) -> VibrationCaseId {
140        let name = spatial_vibration_case_name(&wave_name, dir, nonlinear);
141        if let Some(pos) = self.vibration_cases.iter().position(|c| c.name == name) {
142            let id = self.vibration_cases[pos].id;
143            self.vibration_cases[pos].wave_name = wave_name;
144            self.vibration_cases[pos].dir = dir;
145            self.vibration_cases[pos].nonlinear = nonlinear;
146            return id;
147        }
148        let id = next_vibration_case_id(&self.vibration_cases);
149        self.vibration_cases.push(VibrationCase {
150            id,
151            name,
152            wave_name,
153            dir,
154            nonlinear,
155        });
156        id
157    }
158
159    /// 同名の質点系振動ケースがあれば ID を維持して属性を更新し、なければ追加する。
160    pub fn upsert_lumped_vibration_case(
161        &mut self,
162        wave_name: String,
163        dir: LumpedVibrationDir,
164        nonlinear: bool,
165        dim: LumpedVibrationDim,
166    ) -> LumpedVibrationCaseId {
167        let name = lumped_vibration_case_name(&wave_name, dir, nonlinear, dim);
168        if let Some(pos) = self
169            .lumped_vibration_cases
170            .iter()
171            .position(|c| c.name == name)
172        {
173            let id = self.lumped_vibration_cases[pos].id;
174            self.lumped_vibration_cases[pos].wave_name = wave_name;
175            self.lumped_vibration_cases[pos].dir = dir;
176            self.lumped_vibration_cases[pos].nonlinear = nonlinear;
177            self.lumped_vibration_cases[pos].dim = dim;
178            return id;
179        }
180        let id = next_lumped_vibration_case_id(&self.lumped_vibration_cases);
181        self.lumped_vibration_cases.push(LumpedVibrationCase {
182            id,
183            name,
184            wave_name,
185            dir,
186            nonlinear,
187            dim,
188        });
189        id
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196
197    #[test]
198    fn spatial_vibration_case_name_format() {
199        assert_eq!(
200            spatial_vibration_case_name("サンプル", VibrationThDir::X, false),
201            "サンプル X (線形)"
202        );
203        assert_eq!(
204            spatial_vibration_case_name("elcentro", VibrationThDir::Y, true),
205            "elcentro Y (非線形)"
206        );
207        assert_eq!(
208            spatial_vibration_case_name("wave", VibrationThDir::Xy, false),
209            "wave X+Y (線形)"
210        );
211    }
212
213    #[test]
214    fn lumped_vibration_case_name_format() {
215        assert_eq!(
216            lumped_vibration_case_name(
217                "サンプル",
218                LumpedVibrationDir::X,
219                false,
220                LumpedVibrationDim::Planar
221            ),
222            "サンプル X (線形・2次元)"
223        );
224        assert_eq!(
225            lumped_vibration_case_name(
226                "wave",
227                LumpedVibrationDir::Y,
228                true,
229                LumpedVibrationDim::Spatial
230            ),
231            "wave Y (非線形・3次元)"
232        );
233    }
234
235    #[test]
236    fn upsert_vibration_case_preserves_id_on_same_name() {
237        let mut model = super::super::Model::default();
238        let id1 = model.upsert_vibration_case("サンプル".into(), VibrationThDir::X, false);
239        let id2 = model.upsert_vibration_case("サンプル".into(), VibrationThDir::X, false);
240        assert_eq!(id1, id2);
241        assert_eq!(model.vibration_cases.len(), 1);
242    }
243
244    #[test]
245    fn upsert_vibration_case_distinct_names_get_distinct_ids() {
246        let mut model = super::super::Model::default();
247        let id1 = model.upsert_vibration_case("サンプル".into(), VibrationThDir::X, false);
248        let id2 = model.upsert_vibration_case("サンプル".into(), VibrationThDir::Y, false);
249        assert_ne!(id1, id2);
250        assert_eq!(model.vibration_cases.len(), 2);
251    }
252}