Skip to main content

squid_n_core/section_shape/
material.rs

1//! 材料に関する換算関数。
2//!
3//! - [`concrete_young_modulus`] — コンクリート強度 Fc からヤング係数 Ec を算定
4//! - [`wall_shear_shape_factor`] — 耐震壁のせん断形状係数
5
6use super::constants::{GAMMA_CONCRETE, KAPPA_RC};
7
8/// コンクリート強度 Fc [N/mm²] からヤング係数 Ec [N/mm²] を算定する
9/// (RC 規準の Ec=3.35·10⁴·(γ/24)²·(Fc/60)^(1/3)、γ=23 固定)。
10pub fn concrete_young_modulus(fc: f64) -> f64 {
11    concrete_young_modulus_gamma(fc, GAMMA_CONCRETE)
12}
13
14/// コンクリート強度 Fc [N/mm²]・気乾単位体積重量 γ [kN/m³] から
15/// ヤング係数 Ec [N/mm²] を算定する(RC 規準の Ec=3.35·10⁴·(γ/24)²·(Fc/60)^(1/3))。
16pub fn concrete_young_modulus_gamma(fc: f64, gamma_kn_m3: f64) -> f64 {
17    if fc <= 0.0 {
18        return 0.0;
19    }
20    3.35e4 * (gamma_kn_m3 / 24.0).powi(2) * (fc / 60.0).powf(1.0 / 3.0)
21}
22
23/// 耐震壁(壁板+両側柱=平面 I 形断面)のせん断形状係数
24/// (側柱付き壁を I 形断面とみなしたせん断形状係数。材料力学)。
25///
26/// κ = 3(1+ξ)/(5·(1−ξ³(1−η))²)·[η + ξ(1−η)·((15/8)(1−ξ²)² − ξ⁴·η)]
27///
28/// ξ・η の定義は原典ページに明示がないため、
29/// ξ=壁板内法長さ/全長(側柱外面間)、η=壁厚/側柱幅 と仮定する
30/// (式の読み・記号定義とも dev_docs/specs/原典照合リスト.md に要照合として登録)。
31/// ξ=1(側柱なし=矩形断面)で κ=1.2(=`KAPPA_RC`)に一致する。
32/// 退化(非有限・非正)時は矩形の 1.2 にフォールバックする。
33/// 平面 I 形断面(壁板=ウェブ、両端の側柱=フランジ)の**厳密な**せん断形状係数
34/// κ = A/I²·∫(Q(y)²/b(y))dy(材料力学。Timoshenko 梁のせん断補正係数の定義)。
35///
36/// 面内曲げの断面は「壁長方向を断面のせい、壁厚方向を断面の幅」とみなす:
37/// - `d_total`: 側柱外面間の全長 D(せい方向)
38/// - `dc_each`: 側柱 1 本の沿壁方向せい dc(両端に 1 本ずつ。0 なら側柱なし)
39/// - `bc`: 側柱の壁直交方向の幅(フランジ幅)
40/// - `t`: 壁板厚(ウェブ幅)
41///
42/// 一様矩形(`dc_each = 0` または `bc == t`)では厳密に 1.2(=`KAPPA_RC`)を返す。
43/// 側柱が大きくなるほど κ は増大し、極限では A/A_web に漸近する(せん断をウェブが
44/// ほぼ全負担する I 形断面の性質)。
45///
46/// 従来の閉形式 [`wall_shear_shape_factor`] は記号定義が原典で確認できず、
47/// η=1(=側柱幅が壁厚に等しい=一様矩形)でも ξ に依存して 0.6(1+ξ) を返すなど
48/// 内部整合性を満たさなかった(側柱が大きいほど κ が 1.2 から**減少**し、
49/// せん断断面積が総断面積を超える非物理な値を与えていた)。本関数はその代替。
50///
51/// 退化入力(非正・非有限)では 1.2 を返す。
52pub fn wall_shear_shape_factor_isection(d_total: f64, dc_each: f64, bc: f64, t: f64) -> f64 {
53    if !(d_total.is_finite() && dc_each.is_finite() && bc.is_finite() && t.is_finite())
54        || d_total <= 0.0
55        || t <= 0.0
56        || bc <= 0.0
57    {
58        return KAPPA_RC;
59    }
60    let c = d_total / 2.0;
61    // 側柱せいは全長の半分を超えない(超える指定はウェブ無しとみなす)。
62    let dc = dc_each.clamp(0.0, c);
63    let a = c - dc; // ウェブの半せい
64                    // フランジ幅がウェブ幅以下なら実質一様矩形。
65    if dc <= 0.0 || bc <= t {
66        return KAPPA_RC;
67    }
68    let width = |y: f64| -> f64 {
69        if y.abs() > a {
70            bc
71        } else {
72            t
73        }
74    };
75    // 断面積・断面二次モーメント(矩形 bc×D から、ウェブ部の (bc−t) を控除)。
76    let area = 2.0 * dc * bc + 2.0 * a * t;
77    let i = bc * d_total.powi(3) / 12.0 - (bc - t) * (2.0 * a).powi(3) / 12.0;
78    if i <= 0.0 || area <= 0.0 {
79        return KAPPA_RC;
80    }
81    // Q(y): y より上の断面一次モーメント。
82    let q_of = |y: f64| -> f64 {
83        if y >= a {
84            // フランジ内
85            bc * (c * c - y * y) / 2.0
86        } else {
87            bc * (c * c - a * a) / 2.0 + t * (a * a - y * y) / 2.0
88        }
89    };
90    // ∫_{-c}^{c} Q²/b dy を Simpson 則で数値積分する(フランジ/ウェブ境界 ±a で
91    // 幅が不連続なため、区間を分割して各々で積分する)。対称なので 0..c を 2 倍。
92    let integrate = |lo: f64, hi: f64, n: usize| -> f64 {
93        if hi <= lo {
94            return 0.0;
95        }
96        let n = if n.is_multiple_of(2) { n } else { n + 1 };
97        let dx = (hi - lo) / n as f64;
98        let mut s = 0.0;
99        for k in 0..=n {
100            let y = lo + dx * k as f64;
101            // 境界の幅の曖昧さを避けるため、区間内部の幅で評価する。
102            let yy = y.clamp(lo + dx * 1e-9, hi - dx * 1e-9);
103            let v = q_of(y).powi(2) / width(yy);
104            let w = if k == 0 || k == n {
105                1.0
106            } else if k % 2 == 1 {
107                4.0
108            } else {
109                2.0
110            };
111            s += w * v;
112        }
113        s * dx / 3.0
114    };
115    let integral = 2.0 * (integrate(0.0, a, 200) + integrate(a, c, 200));
116    let kappa = area / (i * i) * integral;
117    if kappa.is_finite() && kappa >= 1.0 {
118        kappa
119    } else {
120        KAPPA_RC
121    }
122}
123
124pub fn wall_shear_shape_factor(xi: f64, eta: f64) -> f64 {
125    let xi = xi.clamp(0.0, 1.0);
126    let eta = eta.clamp(1e-6, 1.0);
127    let denom = 5.0 * (1.0 - xi.powi(3) * (1.0 - eta)).powi(2);
128    if denom <= 1e-12 {
129        return KAPPA_RC;
130    }
131    let bracket =
132        eta + xi * (1.0 - eta) * ((15.0 / 8.0) * (1.0 - xi * xi).powi(2) - xi.powi(4) * eta);
133    let k = 3.0 * (1.0 + xi) / denom * bracket;
134    if k.is_finite() && k > 0.0 {
135        k
136    } else {
137        KAPPA_RC
138    }
139}
140
141#[cfg(test)]
142mod isection_kappa_tests {
143    use super::*;
144
145    /// 一様矩形(側柱なし、または側柱幅=壁厚)では厳密に 1.2。
146    #[test]
147    fn test_kappa_uniform_rectangle_is_12() {
148        assert!((wall_shear_shape_factor_isection(4000.0, 0.0, 150.0, 150.0) - 1.2).abs() < 1e-9);
149        assert!((wall_shear_shape_factor_isection(4000.0, 600.0, 150.0, 150.0) - 1.2).abs() < 1e-9);
150    }
151
152    /// I 形(側柱がウェブより厚い)では κ > 1.2 で、側柱が大きいほど増大する。
153    /// 従来の閉形式は逆に 1.2 から減少していた(非物理)。
154    #[test]
155    fn test_kappa_increases_with_flange_size() {
156        let t = 150.0;
157        let k300 = wall_shear_shape_factor_isection(4000.0, 300.0, 300.0, t);
158        let k600 = wall_shear_shape_factor_isection(4000.0, 600.0, 600.0, t);
159        let k900 = wall_shear_shape_factor_isection(4000.0, 900.0, 900.0, t);
160        assert!(k300 > 1.2, "k300={}", k300);
161        assert!(k600 > k300, "k600={} k300={}", k600, k300);
162        assert!(k900 > k600, "k900={} k600={}", k900, k600);
163    }
164
165    /// せん断断面積 A/κ は総断面積を超えず、ウェブ断面積を下回らない
166    /// (I 形断面のせん断はウェブがほぼ全負担する)。従来式は A/κ > A となり破綻していた。
167    #[test]
168    fn test_shear_area_is_between_web_and_gross() {
169        let (d, dc, bc, t) = (4000.0, 600.0, 600.0, 150.0_f64);
170        let kappa = wall_shear_shape_factor_isection(d, dc, bc, t);
171        let area = 2.0 * dc * bc + 2.0 * (d / 2.0 - dc) * t;
172        let web = 2.0 * (d / 2.0 - dc) * t;
173        let as_shear = area / kappa;
174        assert!(as_shear <= area, "As={} > A={}", as_shear, area);
175        assert!(as_shear >= web * 0.8, "As={} << Aweb={}", as_shear, web);
176    }
177
178    /// 退化入力は 1.2 へフォールバック。
179    #[test]
180    fn test_kappa_degenerate_inputs() {
181        assert!((wall_shear_shape_factor_isection(0.0, 100.0, 300.0, 150.0) - 1.2).abs() < 1e-12);
182        assert!((wall_shear_shape_factor_isection(4000.0, 100.0, 300.0, 0.0) - 1.2).abs() < 1e-12);
183    }
184}