structural_design_env / tests /test_eurocode.py
Ayush-Singh's picture
Rebuild as structural_design_env: replace pgsa_env with full OpenEnv implementation
63dd587
"""
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)