diff --git a/structuralcodes/codes/ec2_2023/__init__.py b/structuralcodes/codes/ec2_2023/__init__.py index c8662148..a80655e6 100644 --- a/structuralcodes/codes/ec2_2023/__init__.py +++ b/structuralcodes/codes/ec2_2023/__init__.py @@ -41,11 +41,13 @@ from ._section9_sls import ( As_min_y, Ec_eff, + delta_gen, delta_simpl, epssm_epscm, k_1_r, kfl, kh, + simpl_span_depth_ratio, srm_cal, wk_cal, wk_cal2, @@ -89,6 +91,7 @@ 'alpha_c', 'As_min_y', 'delta_simpl', + 'delta_gen', 'Ec_eff', 'epssm_epscm', 'k_1_r', @@ -97,6 +100,7 @@ 'srm_cal', 'wk_cal', 'wk_cal2', + 'simpl_span_depth_ratio', ] __title__: str = 'EUROCODE 2 1992-1-1:2023' diff --git a/structuralcodes/codes/ec2_2023/_section9_sls.py b/structuralcodes/codes/ec2_2023/_section9_sls.py index b3630284..e42b39ac 100644 --- a/structuralcodes/codes/ec2_2023/_section9_sls.py +++ b/structuralcodes/codes/ec2_2023/_section9_sls.py @@ -1,7 +1,10 @@ """Functions from Section 9 of EN 1992-1-1:2023.""" import math -from typing import Tuple +from typing import Literal, Tuple + +import numpy as np +from scipy.interpolate import griddata from ._annexB_time_dependent import alpha_c from ._section5_materials import fcm, fctm @@ -276,6 +279,123 @@ def wk_cal( return wk_cal_, k_1_r_, srm_cal_, epssm_epscm_ +def simpl_span_depth_ratio( + ss: Literal['ss', 'es', 'is', 'c'], wr: float, ll_tl: float +) -> float: + """Interpolates or extrapolates the limiting span/effective depth ratios + (l/d) for reinforced concrete beams or slabs based on the structural + system, mechanical reinforcement ratio, and load ratio. + + EN1992-1-1:2023 Table (9.3). + + This function adheres to the guidelines specified in Table 9.3 of the + standards, which outline the limits on l/d ratios for various conditions. + If the provided values of `wr` (mechanical reinforcement ratio) or `ll_tl` + (load ratio) fall outside the tabulated ranges, the function extrapolates + the necessary values. + + Args: + ss (str): An integer corresponding to the structural system type: 'ss' + for simply supported beams/slabs, 'es' for end spans or one-way + spanning slab, 'is' for interior spans or one-way spanning slab, + 'c' for cantilevers. + wr (float): The mechanical reinforcement ratio, expressed as a decimal + (e.g., 0.1, 0.2, 0.3). + ll_tl (int): The percentage ratio of live load to total load (e.g., 60, + 45, 30). + + Returns: + float: The interpolated or extrapolated l/d ratio. + + Note: + The function assumes the quasi-permanent value of the live load with + psi_2 = 0.3. + + It uses linear interpolation; however, cubic or nearest interpolation + methods can also be applied. + + Deflection limits are set to l/250, in line with the standards. + + The `wr` and `ll_tl` values should ideally be within the bounds given + in the table. Extrapolation is possible but may lead to less accurate + results. + + `l/d` values from the table are conservative for flanged sections and + should be interpreted accordingly. + """ + reinforcement_ratios = [0.3, 0.2, 0.1] + load_ratios = [60, 45, 30] + + # Generate the points array using list comprehension + points = np.array( + [[wr, ll_tl] for wr in reinforcement_ratios for ll_tl in load_ratios] + ) + + # Define the corresponding l/d values + values = { + 'ss': np.array( + [ + # Simple supported beams + 15, + 14, + 12, + 17, + 15, + 13, + 22, + 19, + 17, + ] + ), + 'es': np.array( + [ + 20, + 18, + 16, + 22, + 20, + 17, + 29, + 25, + 22, + ] + ), + 'is': np.array( + [ + 23, + 21, + 18, + 26, + 23, + 20, + 33, + 29, + 26, + ] + ), + 'c': np.array( + [ + 7, + 7, + 6, + 8, + 7, + 6, + 10, + 9, + 8, + ] + ), + } + # Perform the interpolation or extrapolation + query_point = np.array([[wr, ll_tl]]) + interpolated_value = griddata( + points, values[ss], query_point, method='linear' + ) + + return interpolated_value[0] + + def delta_simpl( delta_loads: float, delta_shr: float, @@ -323,3 +443,37 @@ def delta_simpl( kS = 455 * rho_l**2 - 35 * rho_l + 1.6 kI = zeta * Ig_Icr + (1 - zeta) return kI * (delta_loads + kS * delta_shr) + + +def delta_gen( + alpha_I: float, + alpha_II: float, + load_type: Literal['short', 'cycle'], + sigma_sr_sigma_s: float, +) -> float: + """General method for deflection calculations. + + EN1992-1-1:2023 Eq. (9.28). + + Args: + alpha_I (float): Deformation parameter calculated for the uncracked + condition. Could be a strain, curvature or rotation. + alpha_II (float): Deformation parameter calculated for the cracked + condition. Could be a strain, curvature or rotation. + load_type (str): Used for getting the beta_parameter that takes into + consideration the type of the load. Short for 'short' loads and + 'cycle' for repeated loading. + sigma_sr_sigma_s (float): The ratio between the highest stress having + occurred up to the moment being analysed in the tension + reinforcement calculated on the basis of a cracked section and the + stress in the tension reinforcement calculated on the basis of a + cracked section under loading conditions causing first cracking. + Can be replaced by Mcr/M or Ncr/N where Mcr is the cracking moment + and Ncr is the cracking force. + + Returns: + float: The resulting deformation parameter. + """ + beta = 1.0 if load_type == 'short' else 0.5 + zeta = max(0, 1 - beta * sigma_sr_sigma_s**2) + return (1 - zeta) * alpha_I + zeta * alpha_II diff --git a/tests/test_ec2_2023/test_ec2_2023_section9_sls.py b/tests/test_ec2_2023/test_ec2_2023_section9_sls.py index e3045455..0c76d30d 100644 --- a/tests/test_ec2_2023/test_ec2_2023_section9_sls.py +++ b/tests/test_ec2_2023/test_ec2_2023_section9_sls.py @@ -421,6 +421,33 @@ def test_wk_cal( assert math.isclose(epssm_epscm_, expected4, rel_tol=0.01) +# Define test cases with expected results +test_cases = [ + ('ss', 0.3, 60, 15), + ('ss', 0.2, 45, 15), + ('ss', 0.1, 30, 17), + ('es', 0.3, 60, 20), + ('es', 0.2, 45, 20), + ('es', 0.1, 30, 22), + ('is', 0.3, 60, 23), + ('is', 0.2, 45, 23), + ('is', 0.1, 30, 26), + ('c', 0.3, 60, 7), + ('c', 0.2, 45, 7), + ('c', 0.1, 30, 8), + ('ss', 0.25, 50, 15.1667), # This is an interpolated value + ('es', 0.25, 50, 19.6667), # Another interpolated value +] + + +@pytest.mark.parametrize('ss, wr, ll_tl, expected', test_cases) +def test_simpl_span_depth_ratio(ss, wr, ll_tl, expected): + """Test the limiting span/effective depth ratios formula.""" + assert _section9_sls.simpl_span_depth_ratio( + ss, wr, ll_tl + ) == pytest.approx(expected, rel=1e-2) + + @pytest.mark.parametrize( ( 'test_input1, test_input2,test_input3,test_input4, test_input5, ' @@ -469,3 +496,27 @@ def test_delta_simpl( expected, rel_tol=0.01, ) + + +@pytest.mark.parametrize( + 'alpha_I, alpha_II, load_type, sigma_sr_sigma_s, expected', + [ + # Basic functionality with short load type + (1.0, 0.5, 'short', 0.0, 0.5), + (1.0, 0.5, 'short', 1.0, 1.0), + # Basic functionality with cycle load type + (1.0, 0.5, 'cycle', 0.0, 0.5), + (1.0, 0.5, 'cycle', 1.0, 0.75), + # Testing edge cases where sigma_sr_sigma_s is at boundary conditions + (1.0, 0.5, 'short', -1.0, 1.0), + (1.0, 0.5, 'cycle', -1.0, 0.75), + # Testing extremes of sigma_sr_sigma_s + (1.0, 0.5, 'short', 10.0, 1.0), + (1.0, 0.5, 'cycle', 10.0, 1.0), + ], +) +def test_delta_gen(alpha_I, alpha_II, load_type, sigma_sr_sigma_s, expected): + """Test the general method for deflection calculations.""" + assert _section9_sls.delta_gen( + alpha_I, alpha_II, load_type, sigma_sr_sigma_s + ) == pytest.approx(expected)