Skip to main content

squid_n_core/
flexural_strength.rs

1//! 部材の曲げ降伏モーメント My の共通算定。
2//!
3//! プッシュオーバー曲げヒンジ判定(`squid_n_solver::nonlinear::pushover::hinge`)と
4//! 材端曲げバネ(`squid_n_element::factory::springs`)が同じ My を使うため、
5//! 算定の情報源を 1 つに保つ。
6
7use crate::material_grade::rebar_yield_strength;
8use crate::model::{ElementData, Material, Model, Section};
9use crate::rc_capacity::{rc_mu_simple, RcCapacityInput};
10use crate::rc_rebar_geom::rebar_effective_depth;
11use crate::section_shape::{bar_set_area, SectionShape};
12
13/// 曲げ降伏 My 算定に用いる材料強度係数。
14///
15/// 公称値解析では `(1.0, 1.0)`、保有水平耐力計算では鋼材・主筋それぞれの
16/// 材料強度割増係数を与える。
17#[derive(Clone, Copy, Debug, PartialEq)]
18pub struct FlexuralStrengthFactors {
19    /// 鋼材(部材材料)の σy 倍率。
20    pub steel: f64,
21    /// RC 主筋の σy 倍率。
22    pub rebar: f64,
23}
24
25impl FlexuralStrengthFactors {
26    /// 公称値(割増なし)。
27    pub const NOMINAL: Self = Self {
28        steel: 1.0,
29        rebar: 1.0,
30    };
31}
32
33/// 断面の弾性断面係数 Ze [mm³](強軸 I とせい D から `Ze = I / (D/2)`)。
34pub fn section_elastic_modulus(sec: &Section) -> f64 {
35    let depth = sec.depth.max(sec.width);
36    let i_gross = sec.iz.max(sec.iy);
37    if depth > 0.0 {
38        i_gross / (depth / 2.0)
39    } else {
40        0.0
41    }
42}
43
44/// 部材の曲げ降伏(終局)モーメント My [N·mm]。
45///
46/// - RC 配筋形状(`RcRect` / `RcCircle`): `0.9·at·σy·j`([`rc_mu_simple`])
47/// - 塑性断面係数を持つ形状: `Zp·σy`(全塑性 Mp)
48/// - それ以外: `σy·Ze`(弾性断面係数フォールバック)
49///
50/// 分岐順序は材端曲げバネ(`squid_n_element::factory::springs`)と同一。
51/// 曲げヒンジ判定も本関数を My の情報源とする。
52pub fn member_flexural_yield_moment(
53    elem: &ElementData,
54    model: &Model,
55    factors: FlexuralStrengthFactors,
56) -> f64 {
57    let sec = elem.section.and_then(|sid| model.sections.get(sid.index()));
58    let mat = model.element_material(elem);
59    let ze = sec.map(section_elastic_modulus).unwrap_or(0.0);
60    let fy = mat.and_then(|m| m.fy);
61    match sec.and_then(|s| s.shape.as_ref()) {
62        Some(SectionShape::RcRect { rebar, d, .. }) | Some(SectionShape::RcCircle { rebar, d }) => {
63            rc_flexural_yield_moment(elem, model, mat, rebar, *d, ze, factors.rebar)
64        }
65        Some(shape) => {
66            let sy = fy.unwrap_or(235.0) * factors.steel;
67            match shape.plastic_modulus_strong() {
68                Some(zp) => sy * zp,
69                None => sy * ze,
70            }
71        }
72        None => fy.unwrap_or(235.0) * factors.steel * ze,
73    }
74}
75
76fn rc_flexural_yield_moment(
77    elem: &ElementData,
78    model: &Model,
79    mat: Option<&Material>,
80    rebar: &crate::section_shape::RcRebar,
81    d: f64,
82    ze: f64,
83    rebar_factor: f64,
84) -> f64 {
85    let rebar_mat = model.element_rebar_material(elem);
86    let sy = rebar_yield_strength(rebar_mat)
87        .or_else(|| mat.and_then(|m| m.fy))
88        .unwrap_or(345.0)
89        * rebar_factor;
90    let fc = mat.and_then(|m| m.fc).unwrap_or(0.0);
91    let at = bar_set_area(&rebar.main_x) / 2.0;
92    let d_eff = rebar_effective_depth(d, rebar);
93    let my = rc_mu_simple(&RcCapacityInput {
94        b: 1.0,
95        d,
96        at,
97        d_eff,
98        sigma_y: sy,
99        fc: fc.max(1e-9),
100        pw: 0.0,
101        sigma_wy: 0.0,
102        clear_span: 1.0,
103        sigma_0: 0.0,
104    });
105    if my > 0.0 {
106        my
107    } else {
108        sy * ze
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use super::member_flexural_yield_moment;
115    use super::FlexuralStrengthFactors;
116    use crate::ids::{ElemId, MaterialId, SectionId};
117    use crate::model::{
118        ElementData, ElementKind, EndCondition, ForceRegime, LocalAxis, Material, MaterialCategory,
119        Model, Node, RigidZone,
120    };
121    use crate::section_shape::SectionShape;
122
123    #[test]
124    fn steel_yield_uses_plastic_modulus_and_strength_factor() {
125        let mut model = Model::default();
126        model.materials.push(Material {
127            id: MaterialId(0),
128            name: "SN400".into(),
129            category: MaterialCategory::Steel,
130            young: 205_000.0,
131            poisson: 0.3,
132            density: 7.85e-9,
133            shear: None,
134            fc: None,
135            fy: Some(235.0),
136            concrete_class: Default::default(),
137            strength_factor: Some(1.1),
138        });
139        let mut sec = SectionShape::SteelH {
140            height: 400.0,
141            width: 200.0,
142            web_thick: 9.0,
143            flange_thick: 16.0,
144        }
145        .to_section(SectionId(0), "H-400".into());
146        sec.material = Some(MaterialId(0));
147        model.sections.push(sec);
148        model.nodes.extend([
149            Node {
150                id: crate::ids::NodeId(0),
151                coord: [0.0, 0.0, 0.0],
152                restraint: Default::default(),
153                mass: None,
154                story: None,
155                support_spring: None,
156            },
157            Node {
158                id: crate::ids::NodeId(1),
159                coord: [3000.0, 0.0, 0.0],
160                restraint: Default::default(),
161                mass: None,
162                story: None,
163                support_spring: None,
164            },
165        ]);
166        let elem = ElementData {
167            id: ElemId(0),
168            kind: ElementKind::Beam,
169            nodes: smallvec::smallvec![crate::ids::NodeId(0), crate::ids::NodeId(1)],
170            section: Some(SectionId(0)),
171            local_axis: LocalAxis {
172                ref_vector: [0.0, 0.0, 1.0],
173            },
174            end_cond: [EndCondition::Fixed, EndCondition::Fixed],
175            force_regime: ForceRegime::Auto,
176            rigid_zone: RigidZone::default(),
177            plastic_zone: None,
178            spring: None,
179        };
180        let my = member_flexural_yield_moment(
181            &elem,
182            &model,
183            FlexuralStrengthFactors {
184                steel: 1.1,
185                rebar: 1.0,
186            },
187        );
188        let sec = &model.sections[0];
189        let zp = sec
190            .shape
191            .as_ref()
192            .unwrap()
193            .plastic_modulus_strong()
194            .unwrap();
195        assert!((my - 235.0 * 1.1 * zp).abs() < 1e-3 * my.max(1.0));
196    }
197}