Spaces:
Sleeping
Sleeping
File size: 6,296 Bytes
63dd587 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | """
Tests for Eurocode 3 code checks (eurocode3.py).
Covers:
- Bending utilisation ratio
- Shear utilisation ratio
- Buckling reduction factor
- Interaction formula
- Deflection check
- Wall elements pass trivially
"""
import math
import pytest
from structural_design_env.solver.eurocode3 import check_member, MemberChecks
from structural_design_env.solver.sections import (
COLUMN_SECTIONS,
BEAM_SECTIONS,
F_Y_STEEL,
E_STEEL,
)
class TestBendingCheck:
def test_zero_moment_gives_zero_ur(self):
props = COLUMN_SECTIONS["HEB200"]
checks = check_member("column", props, {"N": 0, "V": 0, "M_max": 0, "delta_max_mm": 0}, L_m=3.5)
assert checks.UR_bending == 0.0
def test_at_yield_gives_ur_one(self):
props = BEAM_SECTIONS["IPE300"]
# M at yield = W_el_y * f_y
M_yield = props["W_el_y"] * F_Y_STEEL
checks = check_member("beam", props, {"N": 0, "V": 0, "M_max": M_yield, "delta_max_mm": 0}, L_m=5.0)
assert checks.UR_bending == pytest.approx(1.0, rel=0.01)
def test_overstressed_ur_above_one(self):
props = BEAM_SECTIONS["IPE200"]
M_yield = props["W_el_y"] * F_Y_STEEL
checks = check_member("beam", props, {"N": 0, "V": 0, "M_max": 2.0 * M_yield, "delta_max_mm": 0}, L_m=5.0)
assert checks.UR_bending > 1.0
assert not checks.passes_all
class TestShearCheck:
def test_zero_shear_gives_zero_ur(self):
props = BEAM_SECTIONS["IPE360"]
checks = check_member("beam", props, {"N": 0, "V": 0, "M_max": 0, "delta_max_mm": 0}, L_m=4.0)
assert checks.UR_shear == 0.0
def test_shear_at_capacity(self):
props = BEAM_SECTIONS["IPE300"]
V_Rd = F_Y_STEEL * props["A_v"] / math.sqrt(3)
checks = check_member("beam", props, {"N": 0, "V": V_Rd, "M_max": 0, "delta_max_mm": 0}, L_m=5.0)
assert checks.UR_shear == pytest.approx(1.0, rel=0.01)
class TestBucklingCheck:
def test_columns_have_buckling_check(self):
props = COLUMN_SECTIONS["HEB200"]
# Apply significant compressive load
N_Ed = 500e3 # 500 kN
checks = check_member(
"column", props,
{"N": -N_Ed, "V": 0, "M_max": 0, "delta_max_mm": 0},
L_m=3.5,
)
assert checks.UR_buckling > 0.0
def test_beams_have_zero_buckling(self):
props = BEAM_SECTIONS["IPE400"]
checks = check_member(
"beam", props,
{"N": -100e3, "V": 50e3, "M_max": 50e3, "delta_max_mm": 5.0},
L_m=6.0,
)
assert checks.UR_buckling == 0.0
def test_short_column_chi_near_one(self):
"""Very short column should have chi close to 1.0."""
props = COLUMN_SECTIONS["HEB300"]
A = props["A"]
# Squash load
N_b_Rd_max = A * F_Y_STEEL
# Apply small load → UR_buckling should be small
checks = check_member(
"column", props,
{"N": -N_b_Rd_max * 0.1, "V": 0, "M_max": 0, "delta_max_mm": 0},
L_m=1.0, # very short
)
assert checks.UR_buckling < 0.15
def test_slender_column_higher_buckling_ur(self):
props = COLUMN_SECTIONS["HEB140"]
A = props["A"]
N_Ed = A * F_Y_STEEL * 0.5 # 50% of squash load
checks_short = check_member(
"column", props,
{"N": -N_Ed, "V": 0, "M_max": 0, "delta_max_mm": 0},
L_m=2.0,
)
checks_tall = check_member(
"column", props,
{"N": -N_Ed, "V": 0, "M_max": 0, "delta_max_mm": 0},
L_m=8.0,
)
assert checks_tall.UR_buckling > checks_short.UR_buckling
class TestDeflectionCheck:
def test_zero_deflection_gives_zero_ur(self):
props = BEAM_SECTIONS["IPE300"]
checks = check_member("beam", props, {"N": 0, "V": 0, "M_max": 0, "delta_max_mm": 0}, L_m=5.0)
assert checks.UR_deflection == 0.0
def test_at_limit_gives_ur_one(self):
props = BEAM_SECTIONS["IPE400"]
L = 6.0 # m
defl_limit_mm = L * 1000 / 300.0 # = 20.0 mm
checks = check_member(
"beam", props,
{"N": 0, "V": 0, "M_max": 0, "delta_max_mm": defl_limit_mm},
L_m=L,
)
assert checks.UR_deflection == pytest.approx(1.0, rel=0.01)
def test_columns_have_zero_deflection_ur(self):
props = COLUMN_SECTIONS["HEB240"]
checks = check_member(
"column", props,
{"N": -300e3, "V": 0, "M_max": 0, "delta_max_mm": 50.0},
L_m=3.5,
)
assert checks.UR_deflection == 0.0
class TestWallCheck:
def test_wall_always_passes(self):
checks = check_member(
"wall", {},
{"N": 0, "V": 0, "M_max": 0, "delta_max_mm": 0},
L_m=5.0,
)
assert checks.passes_all
assert checks.max_UR == 0.0
class TestInteraction:
def test_combined_axial_bending_higher_ur(self):
props = COLUMN_SECTIONS["HEB200"]
A = props["A"]
W_pl = props["W_pl_y"]
# Pure bending at 50% capacity
M_50 = 0.5 * W_pl * F_Y_STEEL
checks_M_only = check_member(
"column", props,
{"N": 0, "V": 0, "M_max": M_50, "delta_max_mm": 0},
L_m=3.5,
)
# Add axial load on top
N_Ed = 0.3 * A * F_Y_STEEL
checks_combined = check_member(
"column", props,
{"N": -N_Ed, "V": 0, "M_max": M_50, "delta_max_mm": 0},
L_m=3.5,
)
assert checks_combined.UR_interaction >= checks_M_only.UR_interaction
class TestMaxUR:
def test_max_ur_is_maximum_of_all_checks(self):
props = BEAM_SECTIONS["IPE200"]
L = 3.0
M_80pct = 0.80 * props["W_el_y"] * F_Y_STEEL
defl_50pct = L * 1000 / 300.0 * 0.5
checks = check_member(
"beam", props,
{"N": 0, "V": 0, "M_max": M_80pct, "delta_max_mm": defl_50pct},
L_m=L,
)
expected_max = max(
checks.UR_bending,
checks.UR_shear,
checks.UR_buckling,
checks.UR_interaction,
checks.UR_deflection,
)
assert checks.max_UR == pytest.approx(expected_max, rel=0.001)
|