From 2e70179b15367ce42bda243cfccad7ab88ea210c Mon Sep 17 00:00:00 2001 From: Morten Engen Date: Tue, 13 Dec 2022 21:01:29 +0100 Subject: [PATCH 01/24] Draft initial structure for the concrete class Co-authored-by: talledodiego <38036285+talledodiego@users.noreply.github.com> --- structuralcodes/__init__.py | 10 +- structuralcodes/{code => codes}/__init__.py | 7 + .../{code => codes}/mc2010/__init__.py | 0 .../mc2010/_concrete_material_properties.py | 18 +- structuralcodes/core/__init__.py | 0 structuralcodes/core/base.py | 26 +++ structuralcodes/material/__init__.py | 0 structuralcodes/material/concrete/__init__.py | 58 ++++++ .../material/concrete/_concrete.py | 45 +++++ .../material/concrete/_concreteMC2010.py | 189 ++++++++++++++++++ tests/test_get_set_design_code.py | 12 +- tests/test_mc2010_material_properties.py | 12 +- 12 files changed, 355 insertions(+), 22 deletions(-) rename structuralcodes/{code => codes}/__init__.py (94%) rename structuralcodes/{code => codes}/mc2010/__init__.py (100%) rename structuralcodes/{code => codes}/mc2010/_concrete_material_properties.py (82%) create mode 100644 structuralcodes/core/__init__.py create mode 100644 structuralcodes/core/base.py create mode 100644 structuralcodes/material/__init__.py create mode 100644 structuralcodes/material/concrete/__init__.py create mode 100644 structuralcodes/material/concrete/_concrete.py create mode 100644 structuralcodes/material/concrete/_concreteMC2010.py diff --git a/structuralcodes/__init__.py b/structuralcodes/__init__.py index 440bb17c..8f0abf85 100644 --- a/structuralcodes/__init__.py +++ b/structuralcodes/__init__.py @@ -1,7 +1,9 @@ """A Python package that contains models from structural design codes""" -from .code import set_design_code, get_design_codes, set_national_annex +from .codes import set_design_code, get_design_codes, set_national_annex -from .code import mc2010 +from . import material +from . import core +from . import codes __version__ = '' @@ -9,5 +11,7 @@ 'set_design_code', 'get_design_codes', 'set_national_annex', - 'mc2010', + 'codes', + 'core', + 'material', ] diff --git a/structuralcodes/code/__init__.py b/structuralcodes/codes/__init__.py similarity index 94% rename from structuralcodes/code/__init__.py rename to structuralcodes/codes/__init__.py index 47fcddd1..497e5086 100644 --- a/structuralcodes/code/__init__.py +++ b/structuralcodes/codes/__init__.py @@ -4,6 +4,13 @@ from . import mc2010 +__all__ = [ + 'mc2010', + 'set_design_code', + 'get_design_codes', + 'set_national_annex', +] + # Global code object used by material classes _CODE: t.Optional[types.ModuleType] = None diff --git a/structuralcodes/code/mc2010/__init__.py b/structuralcodes/codes/mc2010/__init__.py similarity index 100% rename from structuralcodes/code/mc2010/__init__.py rename to structuralcodes/codes/mc2010/__init__.py diff --git a/structuralcodes/code/mc2010/_concrete_material_properties.py b/structuralcodes/codes/mc2010/_concrete_material_properties.py similarity index 82% rename from structuralcodes/code/mc2010/_concrete_material_properties.py rename to structuralcodes/codes/mc2010/_concrete_material_properties.py index 65c022c2..6be8b025 100644 --- a/structuralcodes/code/mc2010/_concrete_material_properties.py +++ b/structuralcodes/codes/mc2010/_concrete_material_properties.py @@ -11,7 +11,7 @@ def fcm(fck: float, delta_f: float = 8.0) -> float: Args: fck (float): The characteristic compressive strength in MPa. - Kwargs: + Keyword Args: delta_f (float): The difference between the mean and the characteristic strength. @@ -38,34 +38,34 @@ def fctm(fck: float) -> float: return 2.12 * math.log(1 + 0.1 * fcm(fck)) -def fctkmin(fck: float) -> float: +def fctkmin(_fctm: float) -> float: """Compute the lower bound value of the characteristic tensile strength - from the characteristic compressive strength. + from the mean tensile strength. fib Model Code 2010, Eq. (5.1-4) Args: - fck (float): The characteristic compressive strength in MPa. + _fctm (float): The mean tensile strength in MPa. Returns: float: Lower bound of the characteristic tensile strength in MPa. """ - return 0.7 * fctm(fck) + return 0.7 * _fctm -def fctkmax(fck: float) -> float: +def fctkmax(_fctm: float) -> float: """Compute the upper bound value of the characteristic tensile strength - from the characteristic compressive strength. + from the mean tensile strength. fib Model Code 2010, Eq. (5.1-5) Args: - fck (float): The characteristic compressive strength in MPa. + _fctm (float): The mean tensile strength in MPa. Returns: float: Upper bound of the characteristic tensile strength in MPa. """ - return 1.3 * fctm(fck) + return 1.3 * _fctm def Gf(fck: float) -> float: diff --git a/structuralcodes/core/__init__.py b/structuralcodes/core/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/structuralcodes/core/base.py b/structuralcodes/core/base.py new file mode 100644 index 00000000..2a9fc080 --- /dev/null +++ b/structuralcodes/core/base.py @@ -0,0 +1,26 @@ +"""Abstract base classes""" +import abc +import typing as t + + +class Material(abc.ABC): + """Abstract base class for materials.""" + + def __init__(self, density: float, name: t.Optional[str] = None) -> None: + """ + Initializes an instance of a new material + :param float density: density of the material in kg/m3 + :param Optional[str] name: descriptive name of the material + """ + self._density = abs(density) + self._name = name if name is not None else "Material" + + @property + def name(self): + """Returns the name of the material""" + return self._name + + @property + def density(self): + """Returns the density of the material in kg/m3""" + return self._density diff --git a/structuralcodes/material/__init__.py b/structuralcodes/material/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/structuralcodes/material/concrete/__init__.py b/structuralcodes/material/concrete/__init__.py new file mode 100644 index 00000000..ca314baf --- /dev/null +++ b/structuralcodes/material/concrete/__init__.py @@ -0,0 +1,58 @@ +"""Concrete material""" +import typing as t +from structuralcodes.codes import _use_design_code +from ._concrete import Concrete +from ._concreteMC2010 import ConcreteMC2010 + +__all__ = [ + 'create_concrete', + 'Concrete', + 'ConcreteMC2010', +] + + +def create_concrete( + fck: float, + name: t.Optional[str] = None, + density: float = 2400.0, + existing: bool = False, + design_code: t.Optional[str] = None, +) -> t.Optional[Concrete]: + """ + A factory function to create the correct type of concrete based on the + desired design code. + + Args: + fck (float): Characteristic strength of concrete in MPa. + (if existing it is intended as the mean strength) + + Keyword Args: + density (float): Density of Concrete in kg/m3 (default: 2400) + existing (bool): Boolean indicating if the concrete is of an + existing structure (default: False) + deisgn_code (str): Optional string (default: None) indicating the + desired standard. If None (default) the globally used design + standard will be adopted. Otherwise the design standard specified + will be used for the instance of the material. + Currently available codes: 'mc2010' + + Raises: + ValueError: if the design code is not valid or does not cover + concrete as a material. + """ + # Get the code from the global variable + _code = _use_design_code(design_code) + + # Check if the code is a proper concrete code + code = _code if 'concrete' in _code.__materials__ else None + if code is None: + raise ValueError( + 'The design code is not set, either use ' + 'structuralcodes.code.set_designcode, or provide a valid ' + 'string in the function.' + ) + + # Create the proper concrete object + if code.__title__ == 'fib Model Code 2010': + return ConcreteMC2010(fck, name, density, existing) + return None diff --git a/structuralcodes/material/concrete/_concrete.py b/structuralcodes/material/concrete/_concrete.py new file mode 100644 index 00000000..19ac2048 --- /dev/null +++ b/structuralcodes/material/concrete/_concrete.py @@ -0,0 +1,45 @@ +"""Core implementation of the concrete material""" +import abc +import typing as t +from structuralcodes.core.base import Material + + +class Concrete(Material): + """The abstract concrete material.""" + + _fck: float + _existing: bool + + def __init__( + self, + fck: float, + name: t.Optional[str] = None, + density: float = 2400, + existing: t.Optional[bool] = False, + ) -> None: + """Initializes an abstract concrete material""" + name = name if name is not None else "Concrete" + super().__init__(density=density, name=name) + + self._fck = abs(fck) + if existing: + raise NotImplementedError( + 'Existing concrete feature not implemented yet' + ) + self._existing = existing + + @property + def fck(self) -> float: + """Returns fck in MPa""" + return self._fck + + @fck.setter + def fck(self, fck: float) -> None: + """Setter for fck (in MPa)""" + self._fck = abs(fck) + self._reset_attributes() + + @abc.abstractmethod + def _reset_attributes(self): + """Each concrete should define its own _reset_attributes method + This is because fck setting, reset the object arguments""" diff --git a/structuralcodes/material/concrete/_concreteMC2010.py b/structuralcodes/material/concrete/_concreteMC2010.py new file mode 100644 index 00000000..faf0ad97 --- /dev/null +++ b/structuralcodes/material/concrete/_concreteMC2010.py @@ -0,0 +1,189 @@ +"""The concrete class for Model Code 2020 Concrete Material""" +import typing as t +import warnings + +from structuralcodes.codes import mc2010 +from ._concrete import Concrete + + +class ConcreteMC2010(Concrete): + """Concrete implementation for MC 2010""" + + _fcm: t.Optional[float] = None + _fctm: t.Optional[float] = None + _fctkmin: t.Optional[float] = None + _fctkmax: t.Optional[float] = None + _Gf: t.Optional[float] = None + + def __init__( + self, + fck: float, + name: t.Optional[str] = None, + density: float = 2400.0, + existing: bool = False, + ): + """Initializes a new instance of Concrete for MC 2010 + + Args: + fck (float): Characteristic strength in MPa if concrete is not + existing. + + Keyword Args: + name (str): A descriptive name for concrete + density (float): Density of material in kg/m3 (default: 2400) + existing (bool): The material is of an existing structure + (default: False) + """ + + if name is None: + name = f'C{round(fck):d}' + super().__init__( + fck=fck, name=name, density=density, existing=existing + ) + + def _reset_attributes(self): + self._fcm = None + self._fctm = None + self._fctkmin = None + self._fctkmax = None + self._Gf = None + + def update_attributes(self, updated_attributes: dict) -> None: + """Function for updating the attributes specified in the input + dictionary + + Args: + updated_attributes (dict): the dictionary of parameters to be + updated (not found parameters are skipped with a warning) + """ + for key, value in updated_attributes.items(): + if not hasattr(self, '_' + key): + str_list_keys = '' + for k in updated_attributes.keys(): + str_list_keys += k + ', ' + str_warn = ( + f'WARNING: attribute {key} not found. Ignoring the entry.' + ) + str_warn += '\nAvailable keys: ' + str_list_keys + warnings.warn(str_warn) + continue + setattr(self, '_' + key, value) + + @property + def fcm(self) -> float: + """Returns fcm in MPa. + + Returns: + float: The mean compressive strength in MPa. + """ + if self._fcm is not None: + return self._fcm + return mc2010.fcm(self._fck) + + @fcm.setter + def fcm(self, value: float): + """Sets a user defined value for fcm + + Args: + value (float): the value of fcm in MPa + + Raises: + ValueError: if value is lower than fck + """ + if abs(value) <= self._fck: + raise ValueError( + ( + 'Mean compressive strength cannot be lower than', + 'characteristic strength.\n', + 'Current characteristing strength: ', + f'fck = {self._fck}.', + f'Current value: value = {value}', + ) + ) + self._fcm = abs(value) + + @property + def fctm(self) -> float: + """Returns fctm in MPa + + Returns: + float: The mean tensile strength in MPa + """ + if self._fctm is not None: + return self._fctm + return mc2010.fctm(self._fck) + + @fctm.setter + def fctm(self, value: float): + """Sets a user defined value for fctm + + Args: + value (float): the value of fctm in MPa + """ + if value > 0.5 * self._fck: + warnings.warn( + 'A suspect value of fctm has been input. Please check.' + ) + self._fctm = abs(value) + + @property + def fctkmin(self) -> float: + """Returns fctkmin in MPa + + Returns: + float: The lower bound tensile strength in MPa + """ + if self._fctkmin is not None: + return self._fctkmin + + return mc2010.fctkmin(self.fctm) + + @fctkmin.setter + def fctkmin(self, value: float): + """Sets a user defined value for fctkmin + + Args: + value (float): the value of fctkmin in MPa + """ + self._fctkmin = abs(value) + + @property + def fctkmax(self) -> float: + """Returns fctkmax in MPa + + Returns: + float: The upper bound tensile strength in MPa + """ + if self._fctkmax is not None: + return self._fctkmax + + return mc2010.fctkmax(self.fctm) + + @fctkmax.setter + def fctkmax(self, value: float): + """Sets a user defined value for fctkmax + + Args: + value (float): the value of fctkmax in MPa + """ + self._fctkmax = abs(value) + + @property + def Gf(self) -> float: + """Fracture energy of concrete + + Returns: + float: The fracture energy in N/m + """ + if self._Gf is not None: + return self._Gf + return mc2010.Gf(self._fck) + + @Gf.setter + def Gf(self, value: float): + """Sets a user defined value for fracture energy Gf + + Args: + value (float): the value of Gf in N/m + """ + self._Gf = abs(value) diff --git a/tests/test_get_set_design_code.py b/tests/test_get_set_design_code.py index 65686dcf..1711be83 100644 --- a/tests/test_get_set_design_code.py +++ b/tests/test_get_set_design_code.py @@ -19,14 +19,14 @@ def test_set_design_code(design_code_to_set): structuralcodes.set_design_code(design_code_to_set) # Assert - assert isinstance(structuralcodes.code._CODE, types.ModuleType) - assert structuralcodes.code._CODE.__title__ == expected_design_code_title + assert isinstance(structuralcodes.codes._CODE, types.ModuleType) + assert structuralcodes.codes._CODE.__title__ == expected_design_code_title def test_get_design_codes(): """Test get a list of implemented design codes.""" # Arrange - expected_list_of_codes = list(structuralcodes.code._DESIGN_CODES.keys()) + expected_list_of_codes = list(structuralcodes.codes._DESIGN_CODES.keys()) # Act available_codes = structuralcodes.get_design_codes() @@ -48,7 +48,7 @@ def test_set_national_annex(na_to_set): structuralcodes.set_national_annex(na_to_set) # Assert - assert structuralcodes.code._NATIONAL_ANNEX == expected_na + assert structuralcodes.codes._NATIONAL_ANNEX == expected_na @pytest.mark.parametrize( @@ -61,7 +61,7 @@ def test_use_design_code(design_code_to_user): expected_design_code_title = 'fib Model Code 2010' # Act - code_to_use = structuralcodes.code._use_design_code(design_code_to_user) + code_to_use = structuralcodes.codes._use_design_code(design_code_to_user) # Assert assert isinstance(code_to_use, types.ModuleType) @@ -76,7 +76,7 @@ def test_use_design_code_none(): structuralcodes.set_design_code(design_code_to_set) # Act - code_to_use = structuralcodes.code._use_design_code() + code_to_use = structuralcodes.codes._use_design_code() # Assert assert isinstance(code_to_use, types.ModuleType) diff --git a/tests/test_mc2010_material_properties.py b/tests/test_mc2010_material_properties.py index 13e339f7..e43bd26e 100644 --- a/tests/test_mc2010_material_properties.py +++ b/tests/test_mc2010_material_properties.py @@ -3,7 +3,7 @@ import pytest -from structuralcodes.code.mc2010 import _concrete_material_properties +from structuralcodes.codes.mc2010 import _concrete_material_properties @pytest.mark.parametrize( @@ -71,7 +71,9 @@ def test_fctm(test_input, expected): def test_fctkmin(test_input, expected): """Test the fctkmin function.""" assert math.isclose( - _concrete_material_properties.fctkmin(test_input), + _concrete_material_properties.fctkmin( + _concrete_material_properties.fctm(test_input) + ), expected, rel_tol=0.031, ) @@ -102,7 +104,9 @@ def test_fctkmin(test_input, expected): def test_fctkmax(test_input, expected): """Test the fctkmax function.""" assert math.isclose( - _concrete_material_properties.fctkmax(test_input), + _concrete_material_properties.fctkmax( + _concrete_material_properties.fctm(test_input) + ), expected, rel_tol=0.028, ) @@ -115,7 +119,7 @@ def test_fctkmax(test_input, expected): (35, 143.664), (55, 153.888), (90, 166.626), - (120, 174.832) + (120, 174.832), ], ) def test_Gf(test_input, expected): From b8f67bbfa78de0b09896870de8210be8c76e8e6f Mon Sep 17 00:00:00 2001 From: Morten Engen Date: Tue, 13 Dec 2022 21:05:57 +0100 Subject: [PATCH 02/24] Update docstring of base material --- structuralcodes/core/base.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/structuralcodes/core/base.py b/structuralcodes/core/base.py index 2a9fc080..e0fa6d91 100644 --- a/structuralcodes/core/base.py +++ b/structuralcodes/core/base.py @@ -7,10 +7,13 @@ class Material(abc.ABC): """Abstract base class for materials.""" def __init__(self, density: float, name: t.Optional[str] = None) -> None: - """ - Initializes an instance of a new material - :param float density: density of the material in kg/m3 - :param Optional[str] name: descriptive name of the material + """Initializes an instance of a new material + + Args: + density (float): density of the material in kg/m3 + + Keyword Args: + name (Optional[str]): descriptive name of the material """ self._density = abs(density) self._name = name if name is not None else "Material" From c299dcc894f019ade6a4da8fe306b484e942a804 Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Thu, 15 Dec 2022 13:17:10 +0100 Subject: [PATCH 03/24] minimum reinforcement areas functions --- .vscode/settings.json | 11 +- structuralcodes/codes/ec2_2004/__init__.py | 10 + .../codes/ec2_2004/_crack_control.py | 302 ++++++++++++++++++ tests/test_ec2_2004_crack_control.py | 150 +++++++++ 4 files changed, 471 insertions(+), 2 deletions(-) create mode 100644 structuralcodes/codes/ec2_2004/__init__.py create mode 100644 structuralcodes/codes/ec2_2004/_crack_control.py create mode 100644 tests/test_ec2_2004_crack_control.py diff --git a/.vscode/settings.json b/.vscode/settings.json index 72069360..2676da93 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,17 @@ { - "python.formatting.provider": "black", "python.testing.pytestArgs": [ "tests" ], + "python.formatting.provider": "black", "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, "python.linting.pylintEnabled": true, - "python.linting.flake8Enabled": true + "python.linting.flake8Enabled": true, + "[python]": { + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": true + }, + "editor.defaultFormatter": "ms-python.python", + }, } \ No newline at end of file diff --git a/structuralcodes/codes/ec2_2004/__init__.py b/structuralcodes/codes/ec2_2004/__init__.py new file mode 100644 index 00000000..96694004 --- /dev/null +++ b/structuralcodes/codes/ec2_2004/__init__.py @@ -0,0 +1,10 @@ +"""EUROCODE 2 1992-1-1:2004""" +import typing as t + +from ._crack_control import w_max + +__all__ = ['w_max'] + +__title__: str = 'EUROCODE 2 1992-1-1' +__year__: str = '2004' +__materials__: t.Tuple[str] = ('concrete',) diff --git a/structuralcodes/codes/ec2_2004/_crack_control.py b/structuralcodes/codes/ec2_2004/_crack_control.py new file mode 100644 index 00000000..76b1e988 --- /dev/null +++ b/structuralcodes/codes/ec2_2004/_crack_control.py @@ -0,0 +1,302 @@ +"""Collection of functions from EUROCODE 1992-1-1:2004 +Chapter 7.3 - Crack control""" +import scipy.interpolate + + +def w_max(exposure_class: str, load_combination: str) -> float: + """Computes the recomended value of the maximum crack width. + + EUROCODE 2 1992-1-1:2004, Table (7.1N) + + Args: + exposure_class (str): The exposure class. + Possible values: X0, XC1, XC2, XC3, XC4, XD1, XD2, XS1, XS2, XS3 + load_combination (str): + - f: for frequent load combination + - qp: for quasi-permanent load combination + + Returns: + float: The maximum recommended value for the crack width wmax in mm. + + Raises: + ValueError: if not valid exposure_class or load_combination values. + """ + _load_combination = load_combination.lower() + _exposure_class = exposure_class.upper() + if _load_combination == 'f': + if _exposure_class in ('X0', 'XC1'): + return 0.2 + if _exposure_class in ('XC2', 'XC3', 'XC4'): + return 0.2 + if _load_combination == 'qp': + if _exposure_class in ('X0', 'XC1'): + return 0.4 + if _exposure_class in ( + 'XC2', + 'XC3', + 'XC4', + 'XD1', + 'XD2', + 'XS1', + 'XS2', + 'XS3', + ): + return 0.3 + raise ValueError( + f'{exposure_class} is not a valid value for exposure_class.' + + ' Please enter one of the following: X0, XC1, XC2, XC3, XC4, XD1' + + ',XD2, XS1, XS2, XS3' + ) + raise ValueError( + f'{load_combination} is not a valid value for load_combination.' + + 'Please enter "f" for frequent load combination or "qp" for' + + 'quasi-permanent load combination.' + ) + + +def crack_min_steel_area( + a_ct: float, s_steel: float, fct_eff: float, k: float, kc: float +) -> float: + """Computes the minimum area of reinforcing steel within the tensile zone + for control of cracking areas + + EUROCODE 2 1992-1-1:2004, Eq. (7.1) + + Args: + a_ct (float): is the area of concrete within the tensile zone in mm2. + The tensile zone is that parg of the section which is calculated + to be in tension just before the formation of the first crack. + s_steel (float): is the absolute value of the maximum stress in MPa + permitted in the reinforcement immediately after the formation + of the crack. This may be taken as theyield strength of the + reinforcement, fyk. A lower value may, however, be needed to + satisfy the crack width limits according to the maximum + bar size of spacing (see 7.3.3 (2)). + fct_eff (float): is the mean value of the tensile strength in MPa of + the concrete effective at the time when the cracks may first be + expected to occur: fct,eff=fct or lower (fct(t)), is cracking + is expected earlier than 28 days. + k (float): is the coefficient which allow for the effect of + non-uniform self-equilibrating stresses, which lead to a + reduction of restraint forces. Use 'k_crack_min_steel_area' + to compute it + k=1 for webs w<=300mm or flanges widths less than 300mm + k=0.65 for webs w>=800mm or flanges with widths greater than 800mm + Intermediate values may be interpolated. + kc (float): is a coefficient which takes account of the stress + distribution within the section immediately prior to cracking and + the change of the lever arm. + + Returns: + float: the minimm area of reinforcing steel within the tensile + zone in mm2. + + Raises: + ValueError: if k value is not between 0.65 and 1 or kc is not + larger than 0 and lower than 1. + """ + s_steel = abs(s_steel) + fct_eff = abs(fct_eff) + + if k < 0.65 or k > 1.0: + raise ValueError(f'k={k} must be between 0.65 and 1') + if kc > 1 or kc < 0: + raise ValueError(f'kc={kc} must be lower than 1 and larger than 0') + + return kc * k * fct_eff * a_ct / s_steel + + +def k_crack_min_steel_area(h: float) -> float: + """Is the coefficient which allow for the effect of + non-uniform self-equilibrating stresses, which lead to a + reduction of restraint forces. Use 'k_crack_min_steel_area' + to compute it + k=1 for webs w<=300mm or flanges widths less than 300mm + k=0.65 for webs w>=800mm or flanges with widths greater than 800mm + + EUROCODE 2 1992-1-1:2004, Eq. (7.1) + + Args: + h (float): flange length or flange width in mm + + Returns: + float: k coefficient value + + Raises: + ValueError: if h is less than 0 + """ + if h < 0: + raise ValueError(f'h={h} cannot be less than 0mm') + if h <= 300: + return 1 + if h < 800: + interpol = scipy.interpolate.interp1d((300, 800), (1, 0.65)) + return (float)(interpol(h)) + return 0.65 + + +def kc_crack_min_steel_area_pure_tension() -> float: + """Computes the coefficient which takes account of the stress + distribution within the section immediately prior to cracking and + the change of the lever arm in pure dtension. + + EUROCODE 2 1992-1-1:2004, Eq. (7.1) + + Returns: + float: value of the kc coefficient in pure tension + """ + return 1 + + +def kc_crack_min_steel_area_rectangular( + h: float, b: float, fct_eff: float, n_ed: float +) -> float: + """Computes the coefficient which takes account of the stress + distribution within the section immediately prior to cracking and + the change of the lever arm for bending+axial combination + in rectangular sections and webs of box sections and T-sections. + + EUROCODE 2 1992-1-1:2004, Eq. (7.2) + + Args: + h (float): heigth of the element in mm + b (float): width of the element in mm + fct_eff (float): is the mean value of the tensile strength in MPa of + the concrete effective at the time when the cracks may first be + expected to occur: fct,eff=fct or lower (fct(t)), is cracking + is expected earlier than 28 days. + n_ed (str): axial force at the serviceability limit state acting on + the part of the cross-section under consideration (compressive + force positive). n_ed should be determined considering the + characteristic values of prestress and axial forces under the + relevant combination of actions + + Returns: + float: value of the kc coefficient + + Raises: + ValueError: is h or b are less than 0 + """ + if h < 0: + raise ValueError(f'h={h} should be larger than 0mm') + if b < 0: + raise ValueError(f'b={b} should be larger than 0mm') + + h_s = min(h, 1000) + k1 = 1.5 if n_ed >= 0 else 2 * h_s / 3 / h + s_concrete = n_ed * 1000 / b / h + h_ratio = h / h_s + return min(max(0.4 * (1 - s_concrete / k1 / h_ratio / fct_eff), 0), 1) + + +def kc_crack_min_steel_area_flanges( + f_cr: float, a_ct: float, fct_eff: float +) -> float: + """Computes the coefficient which takes account of the stress + distribution within the section immediately prior to cracking and + the change of the lever arm for bending+axial combination + in rectangular sections for flanges of box sections and T-sections. + + EUROCODE 2 1992-1-1:2004, Eq. (7.3) + + Args: + f_cr: is the absolute value in kN of the tensile force within the + flange immediately prior to cracking due to cracking moment + calculated with fct,eff + a_ct (float): is the area of concrete within the tensile zone in mm2. + The tensile zone is that part of the section which is calculated + to be in tension just before the formation of the first crack. + fct_eff (float): is the mean value of the tensile strength in MPa of + the concrete effective at the time when the cracks may first be + expected to occur: fct,eff=fct or lower (fct(t)), is cracking + is expected earlier than 28 days. + + Returns: + float: value of the kc coefficient + + Raises: + ValueError: is a_ct is less than 0mm2 + """ + f_cr = abs(f_cr) + return max(0.9 * f_cr * 1000 / a_ct / fct_eff, 0.5) + + +def crack_min_steel_area_with_prestresed_tendons( + a_ct: float, + s_steel: float, + fct_eff: float, + k: float, + kc: float, + ap: float, + d_steel: float, + d_press: float, + e: float, + incr_stress: float, +) -> float: + """Computes the minimum area of reinforcing steel within the tensile zone + for control of cracking areas in addition with bonded tendons + + EUROCODE 2 1992-1-1:2004, Eq. (7.1) + + Args: + a_ct (float): is the area of concrete within the tensile zone in mm2. + The tensile zone is that part of the section which is calculated + to be in tension just before the formation of the first crack. + s_steel (float): is the absolute value of the maximum stress in MPa + permitted in the reinforcement immediately after the formation + of the crack. This may be taken as theyield strength of the + reinforcement, fyk. A lower value may, however, be needed to + satisfy the crack width limits according to the maximum + bar size of spacing (see 7.3.3 (2)). + fct_eff (float): is the mean value of the tensile strength in MPa of + the concrete effective at the time when the cracks may first be + expected to occur: fct,eff=fct or lower (fct(t)), is cracking + is expected earlier than 28 days. + k (float): is the coefficient which allow for the effect of + non-uniform self-equilibrating stresses, which lead to a + reduction of restraint forces. Use 'k_crack_min_steel_area' + to compute it + k=1 for webs w<=300mm or flanges widths less than 300mm + k=0.65 for webs w>=800mm or flanges with widths greater than 800mm + Intermediate values may be interpolated. + kc (float): is a coefficient which takes account of the stress + distribution within the section immediately prior to cracking and + the change of the lever arm. + ac_eff (float): is the effective area in mm2 of concrete in tension + surrounding or prestressing tendons if depth hc,ef + ap (float): is the area in mm2 of pre or post-tensioned tendons + within ac_eff + d_steel (float): largest bar diameter in mm of reinforcing steel. + Equal to zero if only prestressing is used in control cracking + d_press (float): equivalent diameter in mm of tendon acoording + to 6.8.2 + e (float): ratio of bond strength of prestressing and reinforcing + steel, according to Table 6.2 in 6.8.2 + incr_stress (float): stress variation in MPa in prestressing tendons + from the state of zero strain of the concrete at the same level + + Returns: + float: the minimm area of reinforcing steel within the tensile + zone in mm2. + + Raises: + ValueError: if k value is not between 0.65 and 1 or kc is not + larger than 0 and lower than 1. If diameters d_steel or + d_press are lower than 0. If ratio of bond strength e + is less than 0 or larger than 1. If area of tendons ac_eff + is less than 0. Is stress variation incr_stress is less than 0 + """ + as_min = crack_min_steel_area(a_ct, s_steel, fct_eff, k, kc) + + if d_press < 0: + raise ValueError(f'd_press={d_press} cannot be less than 0') + if d_steel < 0: + raise ValueError(f'd_steel={d_steel} cannot be less than 0') + if ap < 0: + raise ValueError(f'ap={ap} cannot be less than 0') + if incr_stress < 0: + raise ValueError(f'incr_stress={incr_stress} cannot be less than 0') + + e1 = d_steel > 0 if (e * d_steel / d_press) ** 0.5 else e**0.5 + f = e1 * ap * incr_stress + return as_min * f diff --git a/tests/test_ec2_2004_crack_control.py b/tests/test_ec2_2004_crack_control.py new file mode 100644 index 00000000..2b49e3d9 --- /dev/null +++ b/tests/test_ec2_2004_crack_control.py @@ -0,0 +1,150 @@ +"""Tests for EUROCODE 2-1-1:2004 Chapter 7.3 Crack Control""" +import math + +import pytest +from structuralcodes.codes.ec2_2004 import _crack_control + + +@pytest.mark.parametrize( + 'test_exposure_class, test_load_combination, expected', + [ + ('X0', 'f', 0.2), + ('x0', 'F', 0.2), + ('X0', 'qp', 0.4), + ('x0', 'QP', 0.4), + ('XC2', 'f', 0.2), + ('xc2', 'F', 0.2), + ('XC3', 'f', 0.2), + ('xc3', 'F', 0.2), + ('XC4', 'f', 0.2), + ('xc4', 'F', 0.2), + ('XC2', 'qp', 0.3), + ('xc2', 'QP', 0.3), + ('XC3', 'qp', 0.3), + ('xc3', 'QP', 0.3), + ('XC4', 'qp', 0.3), + ('xc4', 'QP', 0.3), + ('XD1', 'qp', 0.3), + ('xd1', 'QP', 0.3), + ('XD2', 'qp', 0.3), + ('xd2', 'QP', 0.3), + ('XS1', 'qp', 0.3), + ('xs1', 'QP', 0.3), + ('XS2', 'qp', 0.3), + ('xs2', 'QP', 0.3), + ('XS3', 'qp', 0.3), + ('xs3', 'QP', 0.3), + ], +) +def test_w_max_returns_expected_values( + test_exposure_class, test_load_combination, expected +): + """Test that the w_max function returns expected values""" + w_max = _crack_control.w_max(test_exposure_class, test_load_combination) + assert w_max == expected + + +@pytest.mark.parametrize( + 'test_exposure_class, test_load_combination', + [('dummy1', 'f'), ('dummy2', 'qp'), ('XD1', 'dummy3'), ('XS1', 'dummy4')], +) +def test_w_max_not_valid_input_raises_valueerror( + test_exposure_class, test_load_combination +): + """Test that not valid input returns ValueError""" + with pytest.raises(ValueError): + _crack_control.w_max(test_exposure_class, test_load_combination) + + +@pytest.mark.parametrize( + 'h, expected', + [ + (200, 1), + (300, 1), + (800, 0.65), + (1000, 0.65), + (400, 0.93), + (500, 0.86), + (600, 0.79), + (700, 0.72), + ], +) +def test_k_crack_min_steel_area_returns_expected_values(h, expected): + """Test the k_crack_min_steel_area function""" + k = _crack_control.k_crack_min_steel_area(h) + assert math.isclose(k, expected) + + +def test_k_crack_min_steel_area_raises_valueerror(): + """Test that not valid input returns ValueError exeption""" + with pytest.raises(ValueError): + h = -100 + _crack_control.k_crack_min_steel_area(h) + + +def test_kc_crack_min_steel_area_pure_tension_returns_expected_values(): + """Test the kc_crack_min_steel_area_pure_tension function""" + assert 1 == _crack_control.kc_crack_min_steel_area_pure_tension() + + +@pytest.mark.parametrize( + 'h, b, fct_eff, n_ed, expected', + [ + (600, 400, 3, 20, 0.3925926), + (600, 400, 3, -20, 0.4166667), + (400, 200, 4, 3, 0.397500), + (200, 50, 5, -80, 1), + (200, 50, 5, 80, 0), + ], +) +def test_kc_crack_min_steel_area_rectangular_returns_expected_values( + h, b, fct_eff, n_ed, expected +): + """Test the kc_crack_min_steel_area_rectangular""" + kc = _crack_control.kc_crack_min_steel_area_rectangular( + h, b, fct_eff, n_ed + ) + assert math.isclose(kc, expected, rel_tol=0.000001) + + +def test_kc_crack_min_steel_area_rectangular_rasies_valueerror(): + """Test the kc_crack_min_steel_area_rectangular raises Value + Error for not correct input values for b and h""" + with pytest.raises(ValueError): + _crack_control.kc_crack_min_steel_area_rectangular( + h=-100, b=100, fct_eff=100, n_ed=10 + ) + _crack_control.kc_crack_min_steel_area_rectangular( + h=100, b=-100, fct_eff=100, n_ed=10 + ) + + +@pytest.mark.parametrize( + 'f_cr, a_ct, fct_eff, expected', + [ + (30, 10000, 5, 0.54), + (20, 5000, 3, 1.2), + (55, 7500, 4, 1.65), + (55, 50000, 4, 0.5), + ], +) +def test_kc_crack_min_steel_area_flanges(f_cr, a_ct, fct_eff, expected): + """Test the kc_crack_min_steel_area_flanges function""" + kc = _crack_control.kc_crack_min_steel_area_flanges(f_cr, a_ct, fct_eff) + assert math.isclose(kc, expected, rel_tol=0.000001) + + +@pytest.mark.parametrize( + 'a_ct, s_steel, fct_eff, k, kc, expected', + [ + (10000, 500, 3, 1, 1, 60), + (80000, 500, 5, 0.65, 0.5, 260), + (80000, 400, 4, 0.9, 0.75, 540), + ], +) +def test_crack_min_steel_area_returns_expected_values( + a_ct, s_steel, fct_eff, k, kc, expected +): + """Test the crack_min_steel_area returns expected values""" + as_min = _crack_control.crack_min_steel_area(a_ct, s_steel, fct_eff, k, kc) + assert math.isclose(as_min, expected, rel_tol=0.000001) From 59a04f5e92b7684fdfb6d1c58a186f3c6b2c55c0 Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Tue, 27 Dec 2022 11:11:12 +0100 Subject: [PATCH 04/24] raise ValueError test functions for min area --- .../codes/ec2_2004/_crack_control.py | 5 +++- tests/test_ec2_2004_crack_control.py | 24 ++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/structuralcodes/codes/ec2_2004/_crack_control.py b/structuralcodes/codes/ec2_2004/_crack_control.py index 76b1e988..251c78c2 100644 --- a/structuralcodes/codes/ec2_2004/_crack_control.py +++ b/structuralcodes/codes/ec2_2004/_crack_control.py @@ -95,9 +95,12 @@ def crack_min_steel_area( ValueError: if k value is not between 0.65 and 1 or kc is not larger than 0 and lower than 1. """ - s_steel = abs(s_steel) fct_eff = abs(fct_eff) + if a_ct <= 0: + raise ValueError(f'a_ct={a_ct} must be larger than 0') + if s_steel < 0: + raise ValueError(f's_steel={s_steel} must be equal or larger than 0') if k < 0.65 or k > 1.0: raise ValueError(f'k={k} must be between 0.65 and 1') if kc > 1 or kc < 0: diff --git a/tests/test_ec2_2004_crack_control.py b/tests/test_ec2_2004_crack_control.py index 2b49e3d9..802f7bfd 100644 --- a/tests/test_ec2_2004_crack_control.py +++ b/tests/test_ec2_2004_crack_control.py @@ -107,7 +107,7 @@ def test_kc_crack_min_steel_area_rectangular_returns_expected_values( assert math.isclose(kc, expected, rel_tol=0.000001) -def test_kc_crack_min_steel_area_rectangular_rasies_valueerror(): +def test_kc_crack_min_steel_area_rectangular_raises_valueerror(): """Test the kc_crack_min_steel_area_rectangular raises Value Error for not correct input values for b and h""" with pytest.raises(ValueError): @@ -148,3 +148,25 @@ def test_crack_min_steel_area_returns_expected_values( """Test the crack_min_steel_area returns expected values""" as_min = _crack_control.crack_min_steel_area(a_ct, s_steel, fct_eff, k, kc) assert math.isclose(as_min, expected, rel_tol=0.000001) + + +@pytest.mark.parametrize( + 'a_ct, s_steel, fct_eff, k, kc', + [ + (-10000, 100, 3, 0.7, 0.67), + (10000, -100, 3, 0.7, 0.65), + (10000, 100, 3, 0.5, 0.65), + (10000, 100, 3, 1.1, 0.65), + (10000, 100, 3, 0.7, -0.1), + (10000, 100, 3, 0.7, 1.1), + ], +) +def test_crack_min_steel_area_raises_valueerror(a_ct, s_steel, fct_eff, k, kc): + """Test the crack_min_steel_area raises value error""" + with pytest.raises(ValueError): + _crack_control.crack_min_steel_area(a_ct, s_steel, fct_eff, k, kc) + + +def test_crack_min_steel_area_with_prestressed_tendons_returns_expected_values(): + """Test the crack_min_steel_area returns expected values""" + pass From b7167aa4b462253002c3581c579180d1488ce4ef Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Thu, 12 Jan 2023 09:57:48 +0100 Subject: [PATCH 05/24] crack_min_steel_without_direct_calculation --- requirements.txt | 2 + .../codes/ec2_2004/_crack_control.py | 156 +++++++++++++++++- tests/test_ec2_2004_crack_control.py | 84 +++++++++- 3 files changed, 232 insertions(+), 10 deletions(-) diff --git a/requirements.txt b/requirements.txt index e69de29b..80ba09bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -0,0 +1,2 @@ +numpy==1.23.5 +scipy==1.9.3 diff --git a/structuralcodes/codes/ec2_2004/_crack_control.py b/structuralcodes/codes/ec2_2004/_crack_control.py index 251c78c2..b3a2ba53 100644 --- a/structuralcodes/codes/ec2_2004/_crack_control.py +++ b/structuralcodes/codes/ec2_2004/_crack_control.py @@ -1,5 +1,6 @@ """Collection of functions from EUROCODE 1992-1-1:2004 Chapter 7.3 - Crack control""" +import numpy as np import scipy.interpolate @@ -270,7 +271,7 @@ def crack_min_steel_area_with_prestresed_tendons( ap (float): is the area in mm2 of pre or post-tensioned tendons within ac_eff d_steel (float): largest bar diameter in mm of reinforcing steel. - Equal to zero if only prestressing is used in control cracking + Equal to 0 if only prestressing is used in control cracking d_press (float): equivalent diameter in mm of tendon acoording to 6.8.2 e (float): ratio of bond strength of prestressing and reinforcing @@ -289,9 +290,9 @@ def crack_min_steel_area_with_prestresed_tendons( is less than 0 or larger than 1. If area of tendons ac_eff is less than 0. Is stress variation incr_stress is less than 0 """ - as_min = crack_min_steel_area(a_ct, s_steel, fct_eff, k, kc) + fct_eff = abs(fct_eff) - if d_press < 0: + if d_press <= 0: raise ValueError(f'd_press={d_press} cannot be less than 0') if d_steel < 0: raise ValueError(f'd_steel={d_steel} cannot be less than 0') @@ -299,7 +300,150 @@ def crack_min_steel_area_with_prestresed_tendons( raise ValueError(f'ap={ap} cannot be less than 0') if incr_stress < 0: raise ValueError(f'incr_stress={incr_stress} cannot be less than 0') + if e < 0.15: + raise ValueError(f'The minimum value for e={e} is 0.15') + if e > 0.8: + raise ValueError(f'The maximum value for e={e} is 0.8') + if a_ct <= 0: + raise ValueError(f'a_ct={a_ct} must be larger than 0') + if s_steel < 0: + raise ValueError(f's_steel={s_steel} must be equal or larger than 0') + if k < 0.65 or k > 1.0: + raise ValueError(f'k={k} must be between 0.65 and 1') + if kc > 1 or kc < 0: + raise ValueError(f'kc={kc} must be lower than 1 and larger than 0') + + a1 = kc * k * fct_eff * a_ct + e1 = ((e * d_steel / d_press) ** 0.5) if d_steel > 0 else e**0.5 + a2 = e1 * ap * incr_stress + a = a1 - a2 + + return a / s_steel + + +def crack_min_steel_without_direct_calculation( + wk: float, + s_steel: float, + fct_eff: float, + kc: float, + h_cr: float, + h: float, + d: float, + incr_stress: float = 0, +) -> tuple(float, float): + """Computes the minimum area of reinforcing steel within the tensile zone + for control of cracking areas + + EUROCODE 2 1992-1-1:2004, Table (7.2N), Table (7.3N) + + Args: + wk (float): the characteristic crack width value in mm. + s_steel (float): the steel stress value in MPa under the relevant + combination of actions. + fct_eff (float): is the mean value of the tensile strength in MPa of + the concrete effective at the time when the cracks may first be + expected to occur: fct,eff=fct or lower (fct(t)), is cracking + is expected earlier than 28 days. + kc (float): is a coefficient which takes account of the stress + distribution within the section immediately prior to cracking and + the change of the lever arm. + h_cr (float): is the depth of the tensile zone immediately prior to + cracking, considering the characteristic values of prestress and + axial forces under the quasi-permanent combination of actions. + h (float): the overall depth of the section in mm. + d (float): is the effective depth to the centroid of the outer layer + of the reinforcement. + incr_stress (float, optional): value of prestressed stress in MPa if + applicable + + Returns: + tuple(float, float): with the value of the maximum bar diameters in mm + in the first position and the maximum bar spacing in mm in the + second position + Raises: + ValueError: if wk, fct_eff, h_cr, h or d are less than 0 + ValueError: if kc is not between 0 and 1 + """ + if wk < 0: + raise ValueError(f'wd={wk} cannot be less than 0') + if fct_eff < 0: + raise ValueError(f'fct_eff={fct_eff} is less than 0') + if h_cr < 0: + raise ValueError(f'h_cr={h_cr} is less than 0') + if h < 0: + raise ValueError(f'h={h} is less than 0') + if d < 0: + raise ValueError(f'd={d} is less than 0') + if kc < 0 or kc > 1: + raise ValueError(f'kc={kc} is not between 0 and 1') + + s = s_steel - incr_stress + if s <= 0: + return (0, 0) + + x = (0.4, 0.3, 0.2) + y_phi = (160, 200, 240, 280, 320, 360, 400, 450) + y_spa = (160, 200, 240, 280, 320, 360) + phi_s_v = ( + 40, + 32, + 25, + 32, + 25, + 16, + 20, + 16, + 12, + 16, + 12, + 8, + 12, + 10, + 6, + 10, + 8, + 5, + 8, + 6, + 4, + 6, + 5, + None, + ) + spa_v = ( + 300, + 300, + 200, + 300, + 250, + 150, + 250, + 200, + 100, + 200, + 150, + 50, + 150, + 100, + None, + 100, + 50, + None, + ) + + points_phi = np.meshgrid(x, y_phi) + points_spa = np.meshgrid(x, y_spa) + xi = (wk, s) + + phi_grid = scipy.interpolate.griddata( + points_phi, phi_s_v, xi, method='linear' + ) + phi_star = phi_grid[0] + phi = phi_star * (fct_eff / 2.9) * kc * h_cr / (2 * (h - d)) + + spa_grid = scipy.interpolate.griddata( + points_spa, spa_v, xi, method='linear' + ) + spa = spa_grid[0] - e1 = d_steel > 0 if (e * d_steel / d_press) ** 0.5 else e**0.5 - f = e1 * ap * incr_stress - return as_min * f + return (phi, spa) diff --git a/tests/test_ec2_2004_crack_control.py b/tests/test_ec2_2004_crack_control.py index 802f7bfd..39fa0f98 100644 --- a/tests/test_ec2_2004_crack_control.py +++ b/tests/test_ec2_2004_crack_control.py @@ -102,7 +102,10 @@ def test_kc_crack_min_steel_area_rectangular_returns_expected_values( ): """Test the kc_crack_min_steel_area_rectangular""" kc = _crack_control.kc_crack_min_steel_area_rectangular( - h, b, fct_eff, n_ed + h, + b, + fct_eff, + n_ed, ) assert math.isclose(kc, expected, rel_tol=0.000001) @@ -147,7 +150,7 @@ def test_crack_min_steel_area_returns_expected_values( ): """Test the crack_min_steel_area returns expected values""" as_min = _crack_control.crack_min_steel_area(a_ct, s_steel, fct_eff, k, kc) - assert math.isclose(as_min, expected, rel_tol=0.000001) + assert math.isclose(as_min, expected, rel_tol=10e-6) @pytest.mark.parametrize( @@ -167,6 +170,79 @@ def test_crack_min_steel_area_raises_valueerror(a_ct, s_steel, fct_eff, k, kc): _crack_control.crack_min_steel_area(a_ct, s_steel, fct_eff, k, kc) -def test_crack_min_steel_area_with_prestressed_tendons_returns_expected_values(): +@pytest.mark.parametrize( + ( + 'a_ct, s_steel, fct_eff, k, kc, ap, d_steel, d_press, e, ' + ' incr_stress, expected' + ), + [ + (80000, 400, 4, 0.9, 0.75, 500, 10, 10, 0.5, 10, 531.161), + (50000, 500, 3, 0.7, 0.4, 700, 10, 30, 0.8, 20, 69.541), + (50000, 500, 4, 1, 1, 1000, 0, 20, 0.8, 20, 364.223), + ], +) +def test_crack_min_steel_area_with_press_tendons_returns_expected_values( + a_ct, + s_steel, + fct_eff, + k, + kc, + ap, + d_steel, + d_press, + e, + incr_stress, + expected, +): """Test the crack_min_steel_area returns expected values""" - pass + as_min = _crack_control.crack_min_steel_area_with_prestresed_tendons( + a_ct, s_steel, fct_eff, k, kc, ap, d_steel, d_press, e, incr_stress + ) + assert math.isclose(as_min, expected, rel_tol=10e-6) + + +@pytest.mark.parametrize( + 'a_ct, s_steel, fct_eff, k, kc, ap, d_steel, d_press, e, incr_stress', + [ + (-80000, 400, 4, 0.9, 0.75, 500, 10, 10, 0.5, 10), + (80000, -400, 4, 0.9, 0.75, 500, 10, 10, 0.5, 10), + (80000, 400, 4, 0.5, 0.75, 500, 10, 10, 0.5, 10), + (80000, 400, 4, 1.1, 0.75, 500, 10, 10, 0.5, 10), + (80000, 400, 4, 0.9, -0.1, 500, 10, 10, 0.5, 10), + (80000, 400, 4, 0.9, 1.1, 500, 10, 10, 0.5, 10), + (80000, 400, 4, 0.9, 0.75, -500, 10, 10, 0.5, 10), + (80000, 400, 4, 0.9, 0.75, 500, -10, 10, 0.5, 10), + (80000, 400, 4, 0.9, 0.75, 500, 10, 0, 0.5, 10), + (80000, 400, 4, 0.9, 0.75, 500, 10, 10, 0.1, 10), + (80000, 400, 4, 0.9, 0.75, 500, 10, 10, 0.9, 10), + ], +) +def test_crack_min_steel_area_with_press_tendons_raise_valueerror( + a_ct, s_steel, fct_eff, k, kc, ap, d_steel, d_press, e, incr_stress +): + """Test the crack_min_steel_area raise ValueError for non valid values""" + with pytest.raises(ValueError): + _crack_control.crack_min_steel_area_with_prestresed_tendons( + a_ct, s_steel, fct_eff, k, kc, ap, d_steel, d_press, e, incr_stress + ) + + +@pytest.mark.parametrize( + 'wk, s_steel, fct_eff, kc, h_cr, h, d, incr_stress', + [ + (-0.1, 200, 3, 0.7, 250, 300, 280, 0), + (0.2, 200, -3, 0.7, 250, 300, 280, 0), + (0.2, 200, 3, 1.1, 250, 300, 280, 0), + (0.2, 200, 3, 0.7, -250, 300, 280, 0), + (0.2, 200, 3, 0.7, -250, -300, 280, 0), + (0.2, 200, 3, 0.7, -250, -300, -280, 0), + ], +) +def test_crack_min_steel_without_direct_calculation_raise_valueerror( + wk, s_steel, fct_eff, kc, h_cr, h, d, incr_stress +): + """Test the crack_min_steel_area raise ValueError for non valid values""" + with pytest.raises(ValueError): + _crack_control.crack_min_steel_without_direct_calculation( + wk, s_steel, fct_eff, kc, h_cr, h, d, incr_stress + ) From 7189d31c244aafbb3f599def76d85d94fb1e4744 Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Thu, 12 Jan 2023 11:08:41 +0100 Subject: [PATCH 06/24] Commit --- .../codes/ec2_2004/_crack_control.py | 42 ++++++++++----- tests/test_ec2_2004_crack_control.py | 54 +++++++++++++++---- 2 files changed, 75 insertions(+), 21 deletions(-) diff --git a/structuralcodes/codes/ec2_2004/_crack_control.py b/structuralcodes/codes/ec2_2004/_crack_control.py index b3a2ba53..d96da5d3 100644 --- a/structuralcodes/codes/ec2_2004/_crack_control.py +++ b/structuralcodes/codes/ec2_2004/_crack_control.py @@ -1,5 +1,8 @@ """Collection of functions from EUROCODE 1992-1-1:2004 Chapter 7.3 - Crack control""" +import math +import typing as t + import numpy as np import scipy.interpolate @@ -329,8 +332,9 @@ def crack_min_steel_without_direct_calculation( h_cr: float, h: float, d: float, + load_type: str, incr_stress: float = 0, -) -> tuple(float, float): +) -> t.Tuple[float, float]: """Computes the minimum area of reinforcing steel within the tensile zone for control of cracking areas @@ -353,6 +357,9 @@ def crack_min_steel_without_direct_calculation( h (float): the overall depth of the section in mm. d (float): is the effective depth to the centroid of the outer layer of the reinforcement. + load_type (str): load combination type: + - bending: for at least part of section in compression + - tension: uniform axial tension incr_stress (float, optional): value of prestressed stress in MPa if applicable @@ -363,6 +370,7 @@ def crack_min_steel_without_direct_calculation( Raises: ValueError: if wk, fct_eff, h_cr, h or d are less than 0 ValueError: if kc is not between 0 and 1 + ValueError: if combination of wk and stress values are out of scope """ if wk < 0: raise ValueError(f'wd={wk} cannot be less than 0') @@ -376,6 +384,12 @@ def crack_min_steel_without_direct_calculation( raise ValueError(f'd={d} is less than 0') if kc < 0 or kc > 1: raise ValueError(f'kc={kc} is not between 0 and 1') + load_type = load_type.lower() + if load_type != 'bending' and load_type != 'tension': + raise ValueError( + f'load_type={load_type} can only have as values "bending" or' + ' "tension"' + ) s = s_steel - incr_stress if s <= 0: @@ -431,19 +445,23 @@ def crack_min_steel_without_direct_calculation( None, ) - points_phi = np.meshgrid(x, y_phi) - points_spa = np.meshgrid(x, y_spa) - xi = (wk, s) + points_phi = np.array(np.meshgrid(y_phi, x)).T.reshape(-1, 2) + points_spa = np.array(np.meshgrid(y_spa, x)).T.reshape(-1, 2) + xi = (s, wk) - phi_grid = scipy.interpolate.griddata( - points_phi, phi_s_v, xi, method='linear' + phi_star = float( + scipy.interpolate.griddata(points_phi, phi_s_v, xi, method='linear') ) - phi_star = phi_grid[0] - phi = phi_star * (fct_eff / 2.9) * kc * h_cr / (2 * (h - d)) + if load_type == 'bending': + phi = phi_star * (fct_eff / 2.9) * kc * h_cr / (2 * (h - d)) + else: + phi = phi_star * (fct_eff / 2.9) * h_cr / (8 * (h - d)) - spa_grid = scipy.interpolate.griddata( - points_spa, spa_v, xi, method='linear' + spa = float( + scipy.interpolate.griddata(points_spa, spa_v, xi, method='linear') ) - spa = spa_grid[0] - return (phi, spa) + if math.isnan(phi) or math.isnan(spa): + raise ValueError('Combination of wk or stress values out of scope') + + return phi, spa diff --git a/tests/test_ec2_2004_crack_control.py b/tests/test_ec2_2004_crack_control.py index 39fa0f98..894b0a32 100644 --- a/tests/test_ec2_2004_crack_control.py +++ b/tests/test_ec2_2004_crack_control.py @@ -228,21 +228,57 @@ def test_crack_min_steel_area_with_press_tendons_raise_valueerror( @pytest.mark.parametrize( - 'wk, s_steel, fct_eff, kc, h_cr, h, d, incr_stress', + ( + 'wk, s_steel, fct_eff, kc, h_cr, h, d, load_type, incr_stress, exp_phi,' + ' exp_sep' + ), + [ + (0.3, 240, 2.9, 0.4, 200, 400, 360, 'bending', 40, 25, 250), + (0.2, 260, 2.9, 0.4, 200, 400, 360, 'axial', 40, 14, 125), + (0.35, 360, 2.9, 0.4, 200, 400, 360, 'bending', 40, 11, 125), + (0.35, 360, 2.9, 0.4, 200, 400, 360, 'axial', 40, 11, 125), + ], +) +def test_crack_min_steel_without_direct_calculation_returns_expected_values( + wk, + s_steel, + fct_eff, + kc, + h_cr, + h, + d, + load_type, + incr_stress, + exp_phi, + exp_sep, +): + """Test the crack_min_steel_area raise ValueError for non valid values""" + phi, sep = _crack_control.crack_min_steel_without_direct_calculation( + wk, s_steel, fct_eff, kc, h_cr, h, d, load_type, incr_stress + ) + assert math.isclose(phi, exp_phi, rel_tol=10e-6) + assert math.isclose(sep, exp_sep, rel_tol=10e-6) + + +@pytest.mark.parametrize( + 'wk, s_steel, fct_eff, kc, h_cr, h, d, load_type, incr_stress', [ - (-0.1, 200, 3, 0.7, 250, 300, 280, 0), - (0.2, 200, -3, 0.7, 250, 300, 280, 0), - (0.2, 200, 3, 1.1, 250, 300, 280, 0), - (0.2, 200, 3, 0.7, -250, 300, 280, 0), - (0.2, 200, 3, 0.7, -250, -300, 280, 0), - (0.2, 200, 3, 0.7, -250, -300, -280, 0), + (-0.1, 200, 3, 0.7, 250, 300, 280, 'bending', 0), + (0.2, 200, -3, 0.7, 250, 300, 280, 'bending', 0), + (0.2, 200, 3, 0.7, 250, 300, 280, 'bending', 0), + (0.2, 200, 3, 1.1, 250, 300, 280, 'bending', 0), + (0.2, 200, 3, 0.7, -250, 300, 280, 'bending', 0), + (0.2, 200, 3, 0.7, -250, -300, 280, 'bending', 0), + (0.2, 200, 3, 0.7, -250, -300, -280, 'bending', 0), + (0.2, 360, 2.9, 0.4, 200, 400, 360, 'bending', 0), + (0.5, 200, 2.9, 0.4, 200, 400, 360, 'bending', 0), ], ) def test_crack_min_steel_without_direct_calculation_raise_valueerror( - wk, s_steel, fct_eff, kc, h_cr, h, d, incr_stress + wk, s_steel, fct_eff, kc, h_cr, h, d, load_type, incr_stress ): """Test the crack_min_steel_area raise ValueError for non valid values""" with pytest.raises(ValueError): _crack_control.crack_min_steel_without_direct_calculation( - wk, s_steel, fct_eff, kc, h_cr, h, d, incr_stress + wk, s_steel, fct_eff, kc, h_cr, h, d, load_type, incr_stress ) From 4a0fcfbb446369218e3ef578c214fd4ad70b064c Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Thu, 12 Jan 2023 11:28:57 +0100 Subject: [PATCH 07/24] crack without direct calculation tests --- requirements.txt | 2 - .../codes/ec2_2004/_crack_control.py | 23 ++++------- tests/test_ec2_2004_crack_control.py | 41 ++++++++----------- 3 files changed, 25 insertions(+), 41 deletions(-) diff --git a/requirements.txt b/requirements.txt index 80ba09bb..e69de29b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +0,0 @@ -numpy==1.23.5 -scipy==1.9.3 diff --git a/structuralcodes/codes/ec2_2004/_crack_control.py b/structuralcodes/codes/ec2_2004/_crack_control.py index d96da5d3..a78f572e 100644 --- a/structuralcodes/codes/ec2_2004/_crack_control.py +++ b/structuralcodes/codes/ec2_2004/_crack_control.py @@ -328,12 +328,11 @@ def crack_min_steel_without_direct_calculation( wk: float, s_steel: float, fct_eff: float, - kc: float, h_cr: float, h: float, d: float, - load_type: str, incr_stress: float = 0, + kc: t.Optional[float] = None, ) -> t.Tuple[float, float]: """Computes the minimum area of reinforcing steel within the tensile zone for control of cracking areas @@ -348,20 +347,18 @@ def crack_min_steel_without_direct_calculation( the concrete effective at the time when the cracks may first be expected to occur: fct,eff=fct or lower (fct(t)), is cracking is expected earlier than 28 days. - kc (float): is a coefficient which takes account of the stress - distribution within the section immediately prior to cracking and - the change of the lever arm. h_cr (float): is the depth of the tensile zone immediately prior to cracking, considering the characteristic values of prestress and axial forces under the quasi-permanent combination of actions. h (float): the overall depth of the section in mm. d (float): is the effective depth to the centroid of the outer layer of the reinforcement. - load_type (str): load combination type: - - bending: for at least part of section in compression - - tension: uniform axial tension incr_stress (float, optional): value of prestressed stress in MPa if applicable + kc (float, optional): is a coefficient which takes account of the + stress distribution within the section immediately prior to + cracking and the change of the lever arm in a bending section. + 'None' for pure tensile uniform axial section. Returns: tuple(float, float): with the value of the maximum bar diameters in mm @@ -382,14 +379,8 @@ def crack_min_steel_without_direct_calculation( raise ValueError(f'h={h} is less than 0') if d < 0: raise ValueError(f'd={d} is less than 0') - if kc < 0 or kc > 1: + if kc is not None and (kc < 0 or kc > 1): raise ValueError(f'kc={kc} is not between 0 and 1') - load_type = load_type.lower() - if load_type != 'bending' and load_type != 'tension': - raise ValueError( - f'load_type={load_type} can only have as values "bending" or' - ' "tension"' - ) s = s_steel - incr_stress if s <= 0: @@ -452,7 +443,7 @@ def crack_min_steel_without_direct_calculation( phi_star = float( scipy.interpolate.griddata(points_phi, phi_s_v, xi, method='linear') ) - if load_type == 'bending': + if kc is not None: phi = phi_star * (fct_eff / 2.9) * kc * h_cr / (2 * (h - d)) else: phi = phi_star * (fct_eff / 2.9) * h_cr / (8 * (h - d)) diff --git a/tests/test_ec2_2004_crack_control.py b/tests/test_ec2_2004_crack_control.py index 894b0a32..06c23820 100644 --- a/tests/test_ec2_2004_crack_control.py +++ b/tests/test_ec2_2004_crack_control.py @@ -228,57 +228,52 @@ def test_crack_min_steel_area_with_press_tendons_raise_valueerror( @pytest.mark.parametrize( - ( - 'wk, s_steel, fct_eff, kc, h_cr, h, d, load_type, incr_stress, exp_phi,' - ' exp_sep' - ), + 'wk, s_steel, fct_eff, h_cr, h, d, incr_stress, kc, exp_phi, exp_sep', [ - (0.3, 240, 2.9, 0.4, 200, 400, 360, 'bending', 40, 25, 250), - (0.2, 260, 2.9, 0.4, 200, 400, 360, 'axial', 40, 14, 125), - (0.35, 360, 2.9, 0.4, 200, 400, 360, 'bending', 40, 11, 125), - (0.35, 360, 2.9, 0.4, 200, 400, 360, 'axial', 40, 11, 125), + (0.3, 240, 2.9, 200, 400, 360, 40, 0.4, 25, 250), + (0.2, 260, 2.9, 200, 400, 360, 40, None, 8.75, 125), + (0.35, 360, 2.9, 200, 400, 360, 40, 0.4, 11, 125), + (0.35, 360, 2.9, 200, 400, 360, 40, None, 6.875, 125), ], ) def test_crack_min_steel_without_direct_calculation_returns_expected_values( wk, s_steel, fct_eff, - kc, h_cr, h, d, - load_type, incr_stress, + kc, exp_phi, exp_sep, ): """Test the crack_min_steel_area raise ValueError for non valid values""" phi, sep = _crack_control.crack_min_steel_without_direct_calculation( - wk, s_steel, fct_eff, kc, h_cr, h, d, load_type, incr_stress + wk, s_steel, fct_eff, h_cr, h, d, incr_stress, kc ) assert math.isclose(phi, exp_phi, rel_tol=10e-6) assert math.isclose(sep, exp_sep, rel_tol=10e-6) @pytest.mark.parametrize( - 'wk, s_steel, fct_eff, kc, h_cr, h, d, load_type, incr_stress', + 'wk, s_steel, fct_eff, h_cr, h, d, incr_stress, kc', [ - (-0.1, 200, 3, 0.7, 250, 300, 280, 'bending', 0), - (0.2, 200, -3, 0.7, 250, 300, 280, 'bending', 0), - (0.2, 200, 3, 0.7, 250, 300, 280, 'bending', 0), - (0.2, 200, 3, 1.1, 250, 300, 280, 'bending', 0), - (0.2, 200, 3, 0.7, -250, 300, 280, 'bending', 0), - (0.2, 200, 3, 0.7, -250, -300, 280, 'bending', 0), - (0.2, 200, 3, 0.7, -250, -300, -280, 'bending', 0), - (0.2, 360, 2.9, 0.4, 200, 400, 360, 'bending', 0), - (0.5, 200, 2.9, 0.4, 200, 400, 360, 'bending', 0), + (-0.1, 200, 3, 250, 300, 280, 0, 0.7), + (0.2, 200, -3, 250, 300, 280, 0, 0.7), + (0.2, 200, 3, 250, 300, 280, 0, 1.1), + (0.2, 200, 3, -250, 300, 280, 0, 0.7), + (0.2, 200, 3, -250, -300, 280, 0, 0.7), + (0.2, 200, 3, -250, -300, -280, 0, 0.7), + (0.2, 360, 2.9, 200, 400, 360, 0, 0.4), + (0.5, 200, 2.9, 200, 400, 360, 0, 0.4), ], ) def test_crack_min_steel_without_direct_calculation_raise_valueerror( - wk, s_steel, fct_eff, kc, h_cr, h, d, load_type, incr_stress + wk, s_steel, fct_eff, h_cr, h, d, incr_stress, kc ): """Test the crack_min_steel_area raise ValueError for non valid values""" with pytest.raises(ValueError): _crack_control.crack_min_steel_without_direct_calculation( - wk, s_steel, fct_eff, kc, h_cr, h, d, load_type, incr_stress + wk, s_steel, fct_eff, h_cr, h, d, incr_stress, kc ) From 84c0140cec46e29754ada8f328c1bf427e7b1c97 Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Thu, 12 Jan 2023 12:39:12 +0100 Subject: [PATCH 08/24] adjusted bond strength --- .../codes/ec2_2004/_crack_control.py | 46 +++++++++++++++---- tests/test_ec2_2004_crack_control.py | 35 ++++++++++++++ 2 files changed, 71 insertions(+), 10 deletions(-) diff --git a/structuralcodes/codes/ec2_2004/_crack_control.py b/structuralcodes/codes/ec2_2004/_crack_control.py index a78f572e..903ced7d 100644 --- a/structuralcodes/codes/ec2_2004/_crack_control.py +++ b/structuralcodes/codes/ec2_2004/_crack_control.py @@ -228,6 +228,40 @@ def kc_crack_min_steel_area_flanges( return max(0.9 * f_cr * 1000 / a_ct / fct_eff, 0.5) +def adjusted_bond_strength(e: float, d_press: float, d_steel: float) -> float: + """Computes the adjusted ratio of bond strength taking into account + the different diameters of prestressing and reinforcing steel. + + Args: + e (float): ratio of bond strength of prestressing and reinforcing + steel, according to Table 6.2 in 6.8.2 + d_steel (float): largest bar diameter in mm of reinforcing steel. + Equal to 0 if only prestressing is used in control cracking + d_press (float): equivalent diameter in mm of tendon acoording + to 6.8.2 + + Returns: + float: with the value of the ratio + + Raises: + ValueError: if diameters d_steel or d_press are lower than 0. + If ratio of bond strength e is less than 0.15 or larger than 0.8. + If area of tendons ac_eff is less than 0. Is stress variation + incr_stress is less than 0. + """ + + if d_press <= 0: + raise ValueError(f'd_press={d_press} cannot be less than 0') + if d_steel < 0: + raise ValueError(f'd_steel={d_steel} cannot be less than 0') + if e < 0.15: + raise ValueError(f'The minimum value for e={e} is 0.15') + if e > 0.8: + raise ValueError(f'The maximum value for e={e} is 0.8') + + return ((e * d_steel / d_press) ** 0.5) if d_steel > 0 else e**0.5 + + def crack_min_steel_area_with_prestresed_tendons( a_ct: float, s_steel: float, @@ -290,23 +324,15 @@ def crack_min_steel_area_with_prestresed_tendons( ValueError: if k value is not between 0.65 and 1 or kc is not larger than 0 and lower than 1. If diameters d_steel or d_press are lower than 0. If ratio of bond strength e - is less than 0 or larger than 1. If area of tendons ac_eff + is less than 0.15 or larger than 0.8. If area of tendons ac_eff is less than 0. Is stress variation incr_stress is less than 0 """ fct_eff = abs(fct_eff) - if d_press <= 0: - raise ValueError(f'd_press={d_press} cannot be less than 0') - if d_steel < 0: - raise ValueError(f'd_steel={d_steel} cannot be less than 0') if ap < 0: raise ValueError(f'ap={ap} cannot be less than 0') if incr_stress < 0: raise ValueError(f'incr_stress={incr_stress} cannot be less than 0') - if e < 0.15: - raise ValueError(f'The minimum value for e={e} is 0.15') - if e > 0.8: - raise ValueError(f'The maximum value for e={e} is 0.8') if a_ct <= 0: raise ValueError(f'a_ct={a_ct} must be larger than 0') if s_steel < 0: @@ -317,7 +343,7 @@ def crack_min_steel_area_with_prestresed_tendons( raise ValueError(f'kc={kc} must be lower than 1 and larger than 0') a1 = kc * k * fct_eff * a_ct - e1 = ((e * d_steel / d_press) ** 0.5) if d_steel > 0 else e**0.5 + e1 = adjusted_bond_strength(e, d_press, d_steel) a2 = e1 * ap * incr_stress a = a1 - a2 diff --git a/tests/test_ec2_2004_crack_control.py b/tests/test_ec2_2004_crack_control.py index 06c23820..12c07ce3 100644 --- a/tests/test_ec2_2004_crack_control.py +++ b/tests/test_ec2_2004_crack_control.py @@ -277,3 +277,38 @@ def test_crack_min_steel_without_direct_calculation_raise_valueerror( _crack_control.crack_min_steel_without_direct_calculation( wk, s_steel, fct_eff, h_cr, h, d, incr_stress, kc ) + + +@pytest.mark.parametrize( + 'e, d_press, d_steel, expected', + [ + (0.8, 20, 0, 0.894427), + (0.6, 25, 10, 0.489898), + (0.5, 10, 10, 0.707107), + ], +) +def test_adjusted_bond_length_return_expected_values( + e, d_press, d_steel, expected +): + """Test the adjusted_bond_length_function returns expected values""" + assert math.isclose( + _crack_control.adjusted_bond_strength(e, d_press, d_steel), + expected, + rel_tol=10e-5, + ) + + +@pytest.mark.parametrize( + 'e, d_press, d_steel', + [ + (0.1, 20, 0), + (-2, 25, 10), + (1.15, 10, 10), + (0.6, -10, 10), + (0.6, 10, -10), + ], +) +def test_adjusted_bond_length_raise_valuerror(e, d_press, d_steel): + """Test the adjusted_bond_length_function raises exceptions""" + with pytest.raises(ValueError): + _crack_control.adjusted_bond_strength(e, d_press, d_steel) From e0f1baac8887cfe4a6386924c648a25453bcfa57 Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Thu, 12 Jan 2023 13:01:54 +0100 Subject: [PATCH 09/24] hc_eff_concrete_tension formulation and testing --- .../codes/ec2_2004/_crack_control.py | 31 +++++++++++++++++ tests/test_ec2_2004_crack_control.py | 33 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/structuralcodes/codes/ec2_2004/_crack_control.py b/structuralcodes/codes/ec2_2004/_crack_control.py index 903ced7d..56d9f79d 100644 --- a/structuralcodes/codes/ec2_2004/_crack_control.py +++ b/structuralcodes/codes/ec2_2004/_crack_control.py @@ -262,6 +262,37 @@ def adjusted_bond_strength(e: float, d_press: float, d_steel: float) -> float: return ((e * d_steel / d_press) ** 0.5) if d_steel > 0 else e**0.5 +def hc_eff_concrete_tension(h: float, d: float, x: float): + """Returns the effective height of concrete in tension surrounding + the reinforcement or prestressing tendons. + + Args: + h (float): total depth of the element in mm + d (float): distance in mm to the level of the steel centroid + x (float): distance in mm to the zero tensile stress line + + Returns: + float: the effective height in mm + + Raises: + ValueError: if any of h, d or x is lower than zero. + ValueError: if d is greater than h + ValueError: if x is greater than h + """ + if h < 0: + raise ValueError(f'h={h} cannot be less than 0') + if d < 0: + raise ValueError(f'd={d} cannot be less than 0') + if x < 0: + raise ValueError(f'x={x} cannot be less than zero') + if d > h: + raise ValueError(f'd={d} cannot be larger than h={h}') + if x > h: + raise ValueError(f'x={x} cannot be larger than h={h}') + + return min(2.5 * (h - d), (h - x) / 3, h / 2) + + def crack_min_steel_area_with_prestresed_tendons( a_ct: float, s_steel: float, diff --git a/tests/test_ec2_2004_crack_control.py b/tests/test_ec2_2004_crack_control.py index 12c07ce3..289d8dfd 100644 --- a/tests/test_ec2_2004_crack_control.py +++ b/tests/test_ec2_2004_crack_control.py @@ -312,3 +312,36 @@ def test_adjusted_bond_length_raise_valuerror(e, d_press, d_steel): """Test the adjusted_bond_length_function raises exceptions""" with pytest.raises(ValueError): _crack_control.adjusted_bond_strength(e, d_press, d_steel) + + +@pytest.mark.parametrize( + 'h, d, x, expected', + [ + (400, 200, 100, 100), + (400, 200, 150, 83.333333), + (550, 150, 150, 133.33333), + ], +) +def test_hc_eff_concrete_tension_returns_expected_values(h, d, x, expected): + """Test the hc_eff_concrete_tension returns expected results""" + assert math.isclose( + _crack_control.hc_eff_concrete_tension(h, d, x), + expected, + rel_tol=10e-5, + ) + + +@pytest.mark.parametrize( + 'h, d, x', + [ + (-50, 200, 100), + (50, -200, 100), + (50, 200, -100), + (400, 450, 100), + (400, 200, 450) + ], +) +def test_hc_eff_concrete_tension_raise_exceptions(h, d, x): + """Test hc_eff_concrete tension raises expected exceptions""" + with pytest.raises(ValueError): + _crack_control.hc_eff_concrete_tension(h, d, x) From f2cbb4989a7233e685b8dc55e343ac692866b61d Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Thu, 12 Jan 2023 13:03:10 +0100 Subject: [PATCH 10/24] requiremets.txt updated --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements.txt b/requirements.txt index e69de29b..1de8c504 100644 --- a/requirements.txt +++ b/requirements.txt @@ -0,0 +1,2 @@ +numpy==1.23.5 +scipy==1.9.3 \ No newline at end of file From 333dcbe11c4b284fee41d8761817fea107885c82 Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Thu, 12 Jan 2023 14:04:38 +0100 Subject: [PATCH 11/24] rho_p_eff --- .../codes/ec2_2004/_crack_control.py | 61 ++++++++++++++++++- tests/test_ec2_2004_crack_control.py | 59 +++++++++++++++++- 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/structuralcodes/codes/ec2_2004/_crack_control.py b/structuralcodes/codes/ec2_2004/_crack_control.py index 56d9f79d..e80c6c66 100644 --- a/structuralcodes/codes/ec2_2004/_crack_control.py +++ b/structuralcodes/codes/ec2_2004/_crack_control.py @@ -232,6 +232,8 @@ def adjusted_bond_strength(e: float, d_press: float, d_steel: float) -> float: """Computes the adjusted ratio of bond strength taking into account the different diameters of prestressing and reinforcing steel. + EUROCODE 2 1992-1-1:2004, Eq. (7.5) + Args: e (float): ratio of bond strength of prestressing and reinforcing steel, according to Table 6.2 in 6.8.2 @@ -262,10 +264,12 @@ def adjusted_bond_strength(e: float, d_press: float, d_steel: float) -> float: return ((e * d_steel / d_press) ** 0.5) if d_steel > 0 else e**0.5 -def hc_eff_concrete_tension(h: float, d: float, x: float): +def hc_eff_concrete_tension(h: float, d: float, x: float) -> float: """Returns the effective height of concrete in tension surrounding the reinforcement or prestressing tendons. + EUROCODE 2 1992-1-1:2004, Section (7.3.2-3) + Args: h (float): total depth of the element in mm d (float): distance in mm to the level of the steel centroid @@ -513,3 +517,58 @@ def crack_min_steel_without_direct_calculation( raise ValueError('Combination of wk or stress values out of scope') return phi, spa + + +def alpha_e(es: float, ecm: float) -> float: + """Compute the ratio between the steel and mean concrete + modules. + + EUROCODE 2 1992-1-1:2004, Section 7.3.4-2 + + Args: + es (float): steel elastic modulus in MPa + ecm (float): ecm concrete mean elastic modulus in MPa + + Returns: + float: ratio between modules + Raise: + ValueError: if any of es or ecm is lower than 0. + """ + if es < 0: + raise ValueError(f'es={es} cannot be less than 0') + if ecm < 0: + raise ValueError(f'ecm={ecm} cannot be less than 0') + + return es / ecm + + +def rho_p_eff(a_s: float, e1: float, a_p, ac_eff: float) -> float: + """Effective bond ratio between areas + + EUROCODE 2 1992-1-1:2004, Eq. (7.10) + + Args: + a_s (float): steel area in mm2 + e1 (float): the adjusted ratio of bond according + to expression (7.5) + a_p (float): the area in mm2 of post-tensioned tendons in ac_eff + ac_eff (float): effective area of concrete in tension surrounding + the reinforcement or prestressing tendons of depth hc_eff. + + Returns: + float: with the retio between areas + + + Raise: + ValueError: if any of a_s, e1, a_p or ac_eff is less than 0 + """ + if a_s < 0: + raise ValueError(f'a_s={a_s} cannot be less than 0') + if e1 < 0: + raise ValueError(f'e1={e1} cannot be less than 0') + if a_p < 0: + raise ValueError(f'a_p={a_p} cannot be less than 0') + if ac_eff < 0: + raise ValueError(f'ac_eff={ac_eff} cannot be less than 0') + + return (a_s + e1**2 * a_p) / ac_eff diff --git a/tests/test_ec2_2004_crack_control.py b/tests/test_ec2_2004_crack_control.py index 289d8dfd..a935db15 100644 --- a/tests/test_ec2_2004_crack_control.py +++ b/tests/test_ec2_2004_crack_control.py @@ -338,10 +338,67 @@ def test_hc_eff_concrete_tension_returns_expected_values(h, d, x, expected): (50, -200, 100), (50, 200, -100), (400, 450, 100), - (400, 200, 450) + (400, 200, 450), ], ) def test_hc_eff_concrete_tension_raise_exceptions(h, d, x): """Test hc_eff_concrete tension raises expected exceptions""" with pytest.raises(ValueError): _crack_control.hc_eff_concrete_tension(h, d, x) + + +@pytest.mark.parametrize( + 'es, ecm, expected', + [ + (10e9, 10e5, 1e4), + ], +) +def test_alpha_e_returns_expected_values(es, ecm, expected): + """Test alpha_e returns expected values""" + assert math.isclose( + _crack_control.alpha_e(es, ecm), + expected, + rel_tol=10e-5, + ) + + +@pytest.mark.parametrize( + 'es, ecm', + [ + (-10e9, 10e5), + (100e9, -10e-5), + ], +) +def test_alpha_e_raise_exceptions(es, ecm): + """Test alpha_e raises exceptions""" + with pytest.raises(ValueError): + _crack_control.alpha_e(es, ecm) + + +@pytest.mark.parametrize( + 'a_s, e1, a_p, ac_eff, expected', + [ + (200, 0.8, 125, 600, 0.46666667), + (125, 1.5, 125, 1200, 0.33854), + ], +) +def test_rho_p_eff_returns_expected_values(a_s, e1, a_p, ac_eff, expected): + """Test rho_p_eff returns expeceted values""" + assert math.isclose( + _crack_control.rho_p_eff(a_s, e1, a_p, ac_eff), expected, rel_tol=10e-5 + ) + + +@pytest.mark.parametrize( + 'a_s, e1, a_p, ac_eff', + [ + (-200, 0.8, 125, 600), + (200, -0.8, 125, 600), + (200, 0.8, -125, 600), + (200, 0.8, 125, -600), + ], +) +def test_rho_p_eff_raise_value_error(a_s, e1, a_p, ac_eff): + """Test rho_p_eff raise exceptions""" + with pytest.raises(ValueError): + _crack_control.rho_p_eff(a_s, e1, a_p, ac_eff) From 59f1198a1aabf6d331d0b499a19e0a5dbaf83c72 Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Thu, 12 Jan 2023 16:06:32 +0100 Subject: [PATCH 12/24] kt load duration --- .../codes/ec2_2004/_crack_control.py | 27 +++++++++++++++++++ tests/test_ec2_2004_crack_control.py | 21 +++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/structuralcodes/codes/ec2_2004/_crack_control.py b/structuralcodes/codes/ec2_2004/_crack_control.py index e80c6c66..76e30cb4 100644 --- a/structuralcodes/codes/ec2_2004/_crack_control.py +++ b/structuralcodes/codes/ec2_2004/_crack_control.py @@ -572,3 +572,30 @@ def rho_p_eff(a_s: float, e1: float, a_p, ac_eff: float) -> float: raise ValueError(f'ac_eff={ac_eff} cannot be less than 0') return (a_s + e1**2 * a_p) / ac_eff + + +def kt_load_duration(load_type: str): + """Returns the kt factor dependent on the load duration for + the crack width calculation + + Args: + load_type (str): the load type: + - 'short' for term loading + - 'long' for long term loading + + Returns: + float: with the kt factor + + Raises: + ValueError: if load_type is not 'short' and not 'long' + """ + if not isinstance(load_type, str): + raise TypeError + + load_type = load_type.lower() + if load_type != 'short' and load_type != 'long': + raise ValueError( + f'load_type={load_type} can only have "short" or "long" as a value' + ) + + return 0.6 if load_type == 'short' else 0.4 diff --git a/tests/test_ec2_2004_crack_control.py b/tests/test_ec2_2004_crack_control.py index a935db15..c7bf42d8 100644 --- a/tests/test_ec2_2004_crack_control.py +++ b/tests/test_ec2_2004_crack_control.py @@ -402,3 +402,24 @@ def test_rho_p_eff_raise_value_error(a_s, e1, a_p, ac_eff): """Test rho_p_eff raise exceptions""" with pytest.raises(ValueError): _crack_control.rho_p_eff(a_s, e1, a_p, ac_eff) + + +@pytest.mark.parametrize( + 'load_type, expected', + [ + ('short', 0.6), + ('long', 0.4), + ], +) +def test_kt_load_duration_returns_expected_values(load_type, expected): + """Test kt_load_duration returns expected values""" + assert _crack_control.kt_load_duration(load_type) == expected + + +def test_kt_load_duration_raise_value_errors(): + """Test kt_load_duration raise value errors""" + with pytest.raises(TypeError): + _crack_control.kt_load_duration(load_type=123) + + with pytest.raises(ValueError): + _crack_control.kt_load_duration(load_type='asdf') From 34d85d24609279f074f61cf8620a2dab05d4fba1 Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Thu, 12 Jan 2023 18:10:44 +0100 Subject: [PATCH 13/24] strain diff formula --- .../codes/ec2_2004/_crack_control.py | 65 ++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/structuralcodes/codes/ec2_2004/_crack_control.py b/structuralcodes/codes/ec2_2004/_crack_control.py index 76e30cb4..91ab738a 100644 --- a/structuralcodes/codes/ec2_2004/_crack_control.py +++ b/structuralcodes/codes/ec2_2004/_crack_control.py @@ -574,7 +574,7 @@ def rho_p_eff(a_s: float, e1: float, a_p, ac_eff: float) -> float: return (a_s + e1**2 * a_p) / ac_eff -def kt_load_duration(load_type: str): +def kt_load_duration(load_type: str) -> float: """Returns the kt factor dependent on the load duration for the crack width calculation @@ -599,3 +599,66 @@ def kt_load_duration(load_type: str): ) return 0.6 if load_type == 'short' else 0.4 + + +def steel_stress_strain( + s_steel: float, + alpha_e: float, + rho_p_eff: float, + kt: float, + fct_eff: float, + es: float, +) -> float: + """Returns the strain difference (esm - ecm) needed to compute the crack + width. esm is the mean strain in the reinforcement under the relevant + combination of loads of imposed deformations and taking into account the + effects of tension stiffening. Only the additional tensile strain beyond + the state of zero strain of the concrete is considered. ecm is the mean + strain in the concrete between the cracks. + + EUROCODE 2 1992-1-1:2004, Eq. (7.9) + + Args: + s_steel (float): is the stress in MPa in the tension reinforcement + assuming a cracked section. FOr pretensioned members, s_steel may + be replaced by increment of s_steel stress variation in + prestressing tendons from the state of zero strain of the + concrete at the same level. + alpha_e (float): is the ratio Es/Ecm + rho_p_eff (float): effective bond ratio between areas given by the + Eq. (7.10) + kt (float): is a factor dependent on the load duration + fct_eff (float): is the mean value of the tensile strength in MPa + of the concrete effectvie at the time when the cracks may + first be expected to occur: fct_eff=fctm or fctm(t) if + crack is expected earlier than 28 days. + es: steel elastic mudulus in MPa + + Returns: + float: the strain difference between concrete and steel + + Raises: + ValueError: if any s_steel, alpha_e, rho_p_eff, fct_Eff is less + than 0. + ValueError: if kt is not 0.6 and not 0.4 + """ + if s_steel < 0: + raise ValueError(f's_steel={s_steel} cannot be less than 0') + if alpha_e < 0: + raise ValueError(f'alpha_e={alpha_e} cannot be less than 0') + if rho_p_eff < 0: + raise ValueError(f'rho_p_eff={rho_p_eff} cannot be less than 0') + if fct_eff < 0: + raise ValueError(f'fct_eff={fct_eff} cannot be less than 0') + if es < 0: + raise ValueError(f'es={es} cannot be less than 0') + if kt != 0.6 and kt != 0.4: + raise ValueError(f'kt={kt} can only take as values 0.4 and 0.6') + + min_val = 0.6 * s_steel / es + + a = 1 + alpha_e * rho_p_eff + b = kt * fct_eff / rho_p_eff * a + c = (s_steel - b) / es + + return max(c, min_val) From a8ab1298037c223f97ded415ce6d1662f9fcc029 Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Fri, 13 Jan 2023 13:32:14 +0100 Subject: [PATCH 14/24] chapter completed --- .../codes/ec2_2004/_crack_control.py | 288 +++++++++++++++++- tests/test_ec2_2004_crack_control.py | 278 ++++++++++++++++- 2 files changed, 558 insertions(+), 8 deletions(-) diff --git a/structuralcodes/codes/ec2_2004/_crack_control.py b/structuralcodes/codes/ec2_2004/_crack_control.py index 91ab738a..e0561fe3 100644 --- a/structuralcodes/codes/ec2_2004/_crack_control.py +++ b/structuralcodes/codes/ec2_2004/_crack_control.py @@ -25,8 +25,8 @@ def w_max(exposure_class: str, load_combination: str) -> float: Raises: ValueError: if not valid exposure_class or load_combination values. """ - _load_combination = load_combination.lower() - _exposure_class = exposure_class.upper() + _load_combination = load_combination.lower().strip() + _exposure_class = exposure_class.upper().strip() if _load_combination == 'f': if _exposure_class in ('X0', 'XC1'): return 0.2 @@ -519,7 +519,7 @@ def crack_min_steel_without_direct_calculation( return phi, spa -def alpha_e(es: float, ecm: float) -> float: +def get_alpha_e(es: float, ecm: float) -> float: """Compute the ratio between the steel and mean concrete modules. @@ -592,7 +592,7 @@ def kt_load_duration(load_type: str) -> float: if not isinstance(load_type, str): raise TypeError - load_type = load_type.lower() + load_type = load_type.lower().strip() if load_type != 'short' and load_type != 'long': raise ValueError( f'load_type={load_type} can only have "short" or "long" as a value' @@ -601,7 +601,7 @@ def kt_load_duration(load_type: str) -> float: return 0.6 if load_type == 'short' else 0.4 -def steel_stress_strain( +def esm_ecm( s_steel: float, alpha_e: float, rho_p_eff: float, @@ -662,3 +662,281 @@ def steel_stress_strain( c = (s_steel - b) / es return max(c, min_val) + + +def s_threshold(c: float, phi: float) -> float: + """Computes the distance threshold from which the + maximum crack spacing is constant. + + EUROCODE 2 1992-1-1:2004, Sect. (7.3.4-3) + + Args: + c (float): cover of the longitudinal reinforcement in mm + phi (float): is the bar diameter in mm. Where mixed bar diameters + used, then it should be replaced for an equivalente bar diameter. + + Returns: + float: threshold distance in mm + + Raises: + ValueError: if any of c or phi is less than 0. + """ + if c < 0: + raise ValueError(f'c={c} cannot be less than 0') + if phi < 0: + raise ValueError(f'phi={phi} cannot be less than 0') + + return 5 * (c + phi / 2) + + +def phi_eq(n1: int, n2: int, phi1: float, phi2: float) -> float: + """Computes the equivalent diameter. For a section with n1 bars of + diameter phi1 and n2 bars of diameter phi2 + + EUROCODE 2 1992-1-1:2004, Sect. (7.12) + + Args: + n1 (int): number of bars with diameter phi1 + n2 (int): number of bars with diameter phi2 + phi1 (float): diameter of n1 bars in mm + phi2 (float): diamater of n2 bars in mm + + Returns: + float: the equivalent diameter in mm + + Raises: + ValueError: if any of n1 or n2 is less than 0 + ValueError: if any of phi1 or phi2 is less than 0 + TypeError: if any of n1 or n2 is not an integer + """ + if n1 < 0: + raise ValueError(f'n1={n1} cannot be less than 0') + if not isinstance(n1, int): + raise TypeError(f'n1={n1} needs to be an integer value') + if n2 < 0: + raise ValueError(f'n2={n2} cannot be less than 0') + if not isinstance(n2, int): + raise TypeError(f'n2={n2} needs to be an integer value') + if phi1 < 0: + raise ValueError(f'phi1={phi1} cannot be less than 0') + if phi2 < 0: + raise ValueError(f'phi2={phi2} cannot be less than 0') + + a = n1 * phi1**2 + n2 * phi2**2 + b = n1 * phi1 + n2 * phi2 + return a / b + + +def k1(bond_type: str) -> float: + """Get the k1 coefficient which takes account of the bond properties + of the bounded reinforcement + + EUROCODE 2 1992-1-1:2004, Eq. (7.11-k1) + + Args: + bond_type (str): the bond property of the reinforcement. + Possible values: + - 'bond': for high bond bars + - 'plane': for bars with an effectively plain surface (e.g. + prestressing tendons) + + Returns: + (float): value of the k1 coefficient + + Raises: + ValueError: if bond_type is neither 'bond' nor 'plane' + TypeError: if bond_type is not an str + """ + if not isinstance(bond_type, str): + raise TypeError(f'bond_type={bond_type} is not an str') + + bond_type = bond_type.lower().strip() + if bond_type != 'bond' and bond_type != 'plane': + raise ValueError( + f'bond_type={bond_type} can only have "bond" or "plane" as values' + ) + + return 0.8 if bond_type == 'bond' else 1.6 + + +def k2(epsilon_r: float) -> float: + """Computes a coefficient which takes into account of the + distribution of strain: + + EUROCODE 2 1992-1-1:2004, Eq. (7.13) + + Args: + epsilon_r (float): ratio epsilon_2/epsilon_1 where epsilon_1 is + thre greater and epsilon_2 is the lesser strain at the boundaries + of the section considererd, assessed on the basis of a cracked + section. epsilon_r=0 for bending and epsilon_r=1 for pure tension. + + Returns: + float: the k2 coefficient value. + + Raises: + ValueError: if epsilon_r is not between 0 and 1. + """ + if epsilon_r < 0 or epsilon_r > 1: + raise ValueError(f'epsilon_r={epsilon_r} must be between 0 and 1') + + return (1 + epsilon_r) / 2 + + +def k3(): + """Returns the k3 coefficient for computing sr_max + + Returns: + float: value for the coefficient + """ + return 3.4 + + +def k4(): + """Returns the k4 coefficient for computing sr_max + + Returns: + float: value for the coefficient + """ + return 0.425 + + +def sr_max_close( + c: float, + phi: float, + rho_p_eff: float, + k1: float, + k2: float, + k3: float, + k4: float, +) -> float: + """Computes the maximum crack spacing in cases where bonded reinforcement + is fixed at reasonably close centres within the tension zone + (spacing<=5(c+phi/2)). + + EUROCODE 2 1992-1-1:2004, Eq. (7.11) + + Args: + c (float): is the cover in mm of the longitudinal reinforcement + phi (float): is the bar diameter in mm. Where mixed bar diameters + used, then it should be replaced for an equivalente bar diameter. + rho_p_eff (float): effective bond ratio between areas given by the + Eq. (7.10) + k1 (float): coefficient that takes into account the bound properties + of the bonded reinforcement + k2 (float): coefficient that takes into account the distribution of + of the strain + k3 (float): coefficient from the National Annex + k4 (float): coefficient from the National Annex + + Returns: + float: the maximum crack spaing in mm. + + Raises: + ValueError: if one or more of c, phi, rho_p_eff, k3 or k4 + is lower than zero. + ValueError: if k1 is not 0.8 or 1.6 + ValueError: if k2 is not between 0.5 and 1.0 + """ + if c < 0: + raise ValueError(f'c={c} cannot be less than zero') + if phi < 0: + raise ValueError(f'phi={phi} cannot be less than zero') + if rho_p_eff < 0: + raise ValueError(f'rho_p_eff={rho_p_eff} cannot be less than zero') + if k3 < 0: + raise ValueError(f'k3={k3} cannot be less than zero') + if k4 < 0: + raise ValueError(f'k4={k4} cannot be less than zero') + if k1 != 0.8 and k1 != 1.6: + raise ValueError(f'k1={k1} can only take as values 0.8 and 1.6') + if k2 < 0.5 or k2 > 1: + raise ValueError(f'k2={k2} is not between 0.5 and 1.0') + + return k3 * c + k1 * k2 * k4 * phi / rho_p_eff + + +def sr_max_far(h: float, x: float) -> float: + """Computes the maximum crack spacing in cases where bonded reinforcement + is fixed at reasonably close centres within the tension zone + (spacing>5(c+phi/2)). + + EUROCODE 2 1992-1-1:2004, Eq. (7.14) + + Args: + h (float): total depth of the beam in mm + x (float): distance to non tension area of the element mm + + Returns: + float: maximum crack spacing in mm + + Raises: + ValueError: if one of h or x is less than zero. + ValueError: x is greater than h. + """ + if x < 0: + raise ValueError(f'x={x} cannot be less than zero') + if h < 0: + raise ValueError(f'h={h} cannot be less than zero') + if x > h: + raise ValueError(f'x={x} cannot be larger than h={h}') + + return 1.3 * (h - x) + + +def sr_max_theta(sr_max_y: float, sr_max_z: float, theta: float) -> float: + """Computes the crack spacing sr_max when there is an angle + between the angle of principal stress and the direction + of the reinforcement, for members in two orthogonal directions, + that is significant (> 15º). + + EUROCODE 2 1992-1-1:2004, Eq. (7.15) + + Args: + sr_max_y (float): crack spacing in mm in the y-direction. + sr_max_z (float): crack spacing in mm in the z-direction. + theta (float): angle in radians between the reinforcement in the + y-direction and the direction of the principal tensile stress. + + Returns: + float: the crack spacing in mm. + + Raises: + ValueError: if sr_max_y or sr_max_z is negative. + ValueError: if theta is not between 0 and pi/2 + """ + if sr_max_y < 0: + raise ValueError(f'sr_max_y={sr_max_y} cannot be less than zero') + if sr_max_z < 0: + raise ValueError(f'sr_max_z={sr_max_z} cannot be less than zero') + + a = math.cos(theta) / sr_max_y + b = math.sin(theta) / sr_max_z + return 1 / (a + b) + + +def wk(sr_max: float, esm_ecm: float) -> float: + """Computes the crack width + + EUROCODE 2 1992-1-1:2004, Eq. (7.8) + + Args: + sr_max (float): the maximum crack length spacing in mm. + esm_ecm (float): the difference between the mean strain in the + reinforcement under relevant combination of loads, including + the effect of imposed deformations and taking into account + tension stiffening and the mean strain in the concrete + between cracks. + + Returns: + float: crack width in mm. + + Raises: + ValueError: if any of sr_max or esm_ecm is less than zero. + """ + if sr_max < 0: + raise ValueError(f'sr_max={sr_max} cannot be less than zero') + if esm_ecm < 0: + raise ValueError(f'esm_scm={esm_ecm} cannot be less than zero') + + return sr_max * esm_ecm diff --git a/tests/test_ec2_2004_crack_control.py b/tests/test_ec2_2004_crack_control.py index c7bf42d8..66e8adeb 100644 --- a/tests/test_ec2_2004_crack_control.py +++ b/tests/test_ec2_2004_crack_control.py @@ -356,7 +356,7 @@ def test_hc_eff_concrete_tension_raise_exceptions(h, d, x): def test_alpha_e_returns_expected_values(es, ecm, expected): """Test alpha_e returns expected values""" assert math.isclose( - _crack_control.alpha_e(es, ecm), + _crack_control.get_alpha_e(es, ecm), expected, rel_tol=10e-5, ) @@ -372,7 +372,7 @@ def test_alpha_e_returns_expected_values(es, ecm, expected): def test_alpha_e_raise_exceptions(es, ecm): """Test alpha_e raises exceptions""" with pytest.raises(ValueError): - _crack_control.alpha_e(es, ecm) + _crack_control.get_alpha_e(es, ecm) @pytest.mark.parametrize( @@ -385,7 +385,9 @@ def test_alpha_e_raise_exceptions(es, ecm): def test_rho_p_eff_returns_expected_values(a_s, e1, a_p, ac_eff, expected): """Test rho_p_eff returns expeceted values""" assert math.isclose( - _crack_control.rho_p_eff(a_s, e1, a_p, ac_eff), expected, rel_tol=10e-5 + _crack_control.rho_p_eff(a_s, e1, a_p, ac_eff), + expected, + rel_tol=10e-5, ) @@ -423,3 +425,273 @@ def test_kt_load_duration_raise_value_errors(): with pytest.raises(ValueError): _crack_control.kt_load_duration(load_type='asdf') + + +@pytest.mark.parametrize( + 's_steel, alpha_e, rho_p_eff, kt, fct_eff, es, expected', + [ + (250, 5.25, 0.34, 0.4, 2.9, 210000, 0.00114523), + (200, 5.25, 0.4, 0.6, 3.1, 210000, 0.00088374), + (250, 5.25, 0.2, 0.6, 2.5, 210000, 0.00111726), + ], +) +def test_esm_ecm_returns_expected_values( + s_steel, alpha_e, rho_p_eff, kt, fct_eff, es, expected +): + """Test esm_ecm returns the expected values""" + assert math.isclose( + _crack_control.esm_ecm(s_steel, alpha_e, rho_p_eff, kt, fct_eff, es), + expected, + abs_tol=10e-5, + ) + + +@pytest.mark.parametrize( + 's_steel, alpha_e, rho_p_eff, kt, fct_eff, es', + [ + (-250, 5.25, 0.34, 0.4, 2.9, 210000), + (250, -5.25, 0.34, 0.4, 2.9, 210000), + (250, 5.25, -0.34, 0.4, 2.9, 210000), + (250, 5.25, 0.34, 0.4, -2.9, 210000), + (250, 5.25, 0.34, 0.4, 2.9, -210000), + (250, 5.25, 0.34, 0.2, 2.9, 210000), + ], +) +def test_esm_ecm_raises_exception(s_steel, alpha_e, rho_p_eff, kt, fct_eff, es): + """Test esm_ecm raise expected exceptions""" + with pytest.raises(ValueError): + _crack_control.esm_ecm(s_steel, alpha_e, rho_p_eff, kt, fct_eff, es) + + +@pytest.mark.parametrize( + 'c, phi, expected', + [ + (30, 16, 190), + (25, 8, 145), + ], +) +def test_s_returns_expected_returns(c, phi, expected): + """Test s returns expected results""" + assert math.isclose( + _crack_control.s_threshold(c, phi), + expected, + rel_tol=10e-5, + ) + + +@pytest.mark.parametrize( + 'c, phi', + [ + (-20, 18), + (20, -18), + ], +) +def test_s_raise_expected_exceptions(c, phi): + """Test s raise expected exceptions""" + with pytest.raises(ValueError): + _crack_control.s_threshold(c, phi) + + +@pytest.mark.parametrize( + 'n1, n2, phi1, phi2, expected', + [(3, 5, 20, 12, 16), (5, 5, 20, 12, 17), (6, 2, 24, 10, 22.29268)], +) +def test_phi_eq_returns_expected_results(n1, n2, phi1, phi2, expected): + """Test phi_eq returns expected results""" + assert math.isclose( + _crack_control.phi_eq(n1, n2, phi1, phi2), + expected, + rel_tol=10e-5, + ) + + +@pytest.mark.parametrize( + 'n1, n2, phi1, phi2, exception_type', + [ + (-2, 2, 20, 18, ValueError), + (2, -2, 20, 18, ValueError), + (2, 2, -20, 18, ValueError), + (2, 2, 20, -18, ValueError), + (4.5, 2, 20, 18, TypeError), + (3, 4.5, 20, 20, TypeError), + ], +) +def test_phi_eq_raises_expected_values(n1, n2, phi1, phi2, exception_type): + """Test phi_eq raises expected exception""" + with pytest.raises(exception_type): + _crack_control.phi_eq(n1, n2, phi1, phi2) + + +@pytest.mark.parametrize( + 'bond_type, expected', + [('bond', 0.8), ('plane', 1.6), ('BOND ', 0.8), (' PLANE ', 1.6)], +) +def test_k1_returns_expected_values(bond_type, expected): + """Test k1 returns expected values""" + assert _crack_control.k1(bond_type) == expected + + +@pytest.mark.parametrize( + 'bond_type, exception_type', + [('asdf ad', ValueError), (123, TypeError), (14.2, TypeError)], +) +def test_k1_raise_expected_exceptions(bond_type, exception_type): + """Test k1 raises expected exceptions""" + with pytest.raises(exception_type): + _crack_control.k1(bond_type) + + +@pytest.mark.parametrize( + 'epsilon_r, expected', + [(0, 0.5), (1, 1), (0.75, 0.875), (0.67, 0.835)], +) +def test_k2_returns_expected_values(epsilon_r, expected): + """Test k2 returns expected values""" + assert math.isclose( + _crack_control.k2(epsilon_r), + expected, + rel_tol=10e-5, + ) + + +@pytest.mark.parametrize('epsilon_r', [(-0.1), (-2), (1.1), (2)]) +def test_k2_raises_value_exceptions(epsilon_r): + """Test k2 raises expected exceptions""" + with pytest.raises(ValueError): + _crack_control.k2(epsilon_r) + + +def test_k3_returns_expected_values(): + """Test k3 returns the expected values""" + assert _crack_control.k3() == 3.4 + + +def test_k4_returns_expected_values(): + """Test k4 returns the expected values""" + assert _crack_control.k4() == 0.425 + + +@pytest.mark.parametrize( + 'c, phi, rho_p_eff, k1, k2, k3, k4, expected', + [ + (20, 8, 5, 0.8, 0.5, 3.4, 0.425, 68.272), + (30, 15, 0.2, 1.6, 0.5, 3.4, 0.425, 127.5), + (45, 20, 0.4, 0.8, 1, 3.4, 0.425, 170), + ], +) +def test_sr_max_close(c, phi, rho_p_eff, k1, k2, k3, k4, expected): + """Test sr_max_close returns the expected values""" + assert math.isclose( + _crack_control.sr_max_close(c, phi, rho_p_eff, k1, k2, k3, k4), + expected, + rel_tol=10e-5, + ) + + +@pytest.mark.parametrize( + 'c, phi, rho_p_eff, k1, k2, k3, k4', + [ + (-20, 8, 5, 0.8, 0.5, 3.4, 0.425), + (20, -8, 5, 0.8, 0.5, 3.4, 0.425), + (20, 8, -5, 0.8, 0.5, 3.4, 0.425), + (20, 8, 5, -0.8, 0.5, 3.4, 0.425), + (20, 8, 5, 0.8, -0.5, 3.4, 0.425), + (20, 8, 5, 0.8, 0.5, -3.4, 0.425), + (20, 8, 5, 0.8, 0.5, 3.4, -0.425), + (20, 8, 5, 0.9, 0.5, 3.4, 0.425), + (20, 8, 5, 0.8, 0.2, 3.4, 0.425), + (20, 8, 5, 0.8, 1.1, 3.4, 0.425), + ], +) +def test_sr_max_close_raises_exceptions(c, phi, rho_p_eff, k1, k2, k3, k4): + """Test sr_max_close raises the expected value errors""" + with pytest.raises(ValueError): + _crack_control.sr_max_close(c, phi, rho_p_eff, k1, k2, k3, k4) + + +@pytest.mark.parametrize( + 'h, x, expected', + [ + (300, 100, 260), + (200, 75, 162.5), + (400, 100, 390), + ], +) +def test_sr_max_far_returns_expected_values(h, x, expected): + """Test sr_max_far returns the expected values""" + assert math.isclose( + _crack_control.sr_max_far(h, x), expected, rel_tol=10e-5 + ) + + +@pytest.mark.parametrize( + 'h, x', + [ + (-100, 100), + (200, -100), + (100, 200), + ], +) +def test_sr_max_far_raises_exceptions(h, x): + """Test sr_max_far raises exceptions""" + with pytest.raises(ValueError): + _crack_control.sr_max_far(h, x) + + +@pytest.mark.parametrize( + 'sr_max_y, sr_max_z, theta, expected', + [ + (200, 160, 0.2618, 155.10493), + (265, 50, 0.7854, 59.4868), + (140, 10, 1.39626, 10.028), + ], +) +def test_sr_max_theta_returns_expected_values( + sr_max_y, sr_max_z, theta, expected +): + """Test sr_max_theta returns expeceted values""" + assert math.isclose( + _crack_control.sr_max_theta(sr_max_y, sr_max_z, theta), + expected, + rel_tol=10e-5, + ) + + +@pytest.mark.parametrize( + 'sr_max_y, sr_max_z, theta', + [ + (-100, 200, 0), + (100, -200, -0.5), + (100, -200, 150), + ], +) +def test_sr_max_theta_raises_exceptions(sr_max_y, sr_max_z, theta): + """Test sr_max_theta raises value errors""" + with pytest.raises(ValueError): + _crack_control.sr_max_theta(sr_max_y, sr_max_z, theta) + + +@pytest.mark.parametrize( + 'sr_max, esm_ecm, expected', + [ + (200, 0.00112, 0.224), + (260, 0.0007, 0.182), + ], +) +def test_wk_returns_expected_values(sr_max, esm_ecm, expected): + """Test wk returns expected values""" + assert math.isclose( + _crack_control.wk(sr_max, esm_ecm), + expected, + rel_tol=10e-5, + ) + + +@pytest.mark.parametrize( + 'sr_max, esm_ecm', + [(-200, 0.0001), (200, -0.0001)], +) +def test_wk_raises_exceptions(sr_max, esm_ecm: float): + """Test wk raises value errors""" + with pytest.raises(ValueError): + _crack_control.wk(sr_max, esm_ecm) From ce4e432ba2a0059149e58582d2cb45080757cbcf Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Fri, 13 Jan 2023 14:31:24 +0100 Subject: [PATCH 15/24] imports and renamed functions --- prueba.py | 0 structuralcodes/codes/ec2_2004/__init__.py | 54 +- .../{_crack_control.py => _section_7.3.py} | 347 ++++--- .../ec2_2004/_section_7_3_crack_control.py | 933 ++++++++++++++++++ ...est_ec2_2004_section_7_3_crack_control.py} | 127 ++- 5 files changed, 1228 insertions(+), 233 deletions(-) create mode 100644 prueba.py rename structuralcodes/codes/ec2_2004/{_crack_control.py => _section_7.3.py} (74%) create mode 100644 structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py rename tests/{test_ec2_2004_crack_control.py => test_ec2_2004_section_7_3_crack_control.py} (83%) diff --git a/prueba.py b/prueba.py new file mode 100644 index 00000000..e69de29b diff --git a/structuralcodes/codes/ec2_2004/__init__.py b/structuralcodes/codes/ec2_2004/__init__.py index 96694004..d322788b 100644 --- a/structuralcodes/codes/ec2_2004/__init__.py +++ b/structuralcodes/codes/ec2_2004/__init__.py @@ -1,9 +1,59 @@ """EUROCODE 2 1992-1-1:2004""" import typing as t -from ._crack_control import w_max +from ._section_7_3_crack_control import ( + As_min, + As_min_2, + As_min_p, + alpha_e, + esm_ecm, + hc_eff, + k, + k1, + k2, + k3, + k4, + kc_flanges_area, + kc_rect_area, + kc_tension, + kt, + phi_eq, + rho_p_eff, + sr_max_close, + sr_max_far, + sr_max_theta, + w_max, + w_spacing, + wk, + xi1, +) -__all__ = ['w_max'] +__all__ = [ + 'As_min', + 'As_min_2', + 'As_min_p', + 'alpha_e', + 'esm_ecm', + 'hc_eff', + 'k', + 'k1', + 'k2', + 'k3', + 'k4', + 'kc_flanges_area', + 'kc_rect_area', + 'kc_tension', + 'kt', + 'phi_eq', + 'rho_p_eff', + 'sr_max_close', + 'sr_max_far', + 'sr_max_theta', + 'w_max', + 'w_spacing', + 'wk', + 'xi1', +] __title__: str = 'EUROCODE 2 1992-1-1' __year__: str = '2004' diff --git a/structuralcodes/codes/ec2_2004/_crack_control.py b/structuralcodes/codes/ec2_2004/_section_7.3.py similarity index 74% rename from structuralcodes/codes/ec2_2004/_crack_control.py rename to structuralcodes/codes/ec2_2004/_section_7.3.py index e0561fe3..3ad05170 100644 --- a/structuralcodes/codes/ec2_2004/_crack_control.py +++ b/structuralcodes/codes/ec2_2004/_section_7.3.py @@ -58,8 +58,8 @@ def w_max(exposure_class: str, load_combination: str) -> float: ) -def crack_min_steel_area( - a_ct: float, s_steel: float, fct_eff: float, k: float, kc: float +def As_min( + A_ct: float, sigma_s: float, fct_eff: float, _k: float, kc: float ) -> float: """Computes the minimum area of reinforcing steel within the tensile zone for control of cracking areas @@ -67,10 +67,10 @@ def crack_min_steel_area( EUROCODE 2 1992-1-1:2004, Eq. (7.1) Args: - a_ct (float): is the area of concrete within the tensile zone in mm2. + A_ct (float): is the area of concrete within the tensile zone in mm2. The tensile zone is that parg of the section which is calculated to be in tension just before the formation of the first crack. - s_steel (float): is the absolute value of the maximum stress in MPa + sigma_s (float): is the absolute value of the maximum stress in MPa permitted in the reinforcement immediately after the formation of the crack. This may be taken as theyield strength of the reinforcement, fyk. A lower value may, however, be needed to @@ -80,7 +80,7 @@ def crack_min_steel_area( the concrete effective at the time when the cracks may first be expected to occur: fct,eff=fct or lower (fct(t)), is cracking is expected earlier than 28 days. - k (float): is the coefficient which allow for the effect of + _k (float): is the coefficient which allow for the effect of non-uniform self-equilibrating stresses, which lead to a reduction of restraint forces. Use 'k_crack_min_steel_area' to compute it @@ -96,28 +96,27 @@ def crack_min_steel_area( zone in mm2. Raises: - ValueError: if k value is not between 0.65 and 1 or kc is not + ValueError: if _k value is not between 0.65 and 1 or kc is not larger than 0 and lower than 1. """ fct_eff = abs(fct_eff) - if a_ct <= 0: - raise ValueError(f'a_ct={a_ct} must be larger than 0') - if s_steel < 0: - raise ValueError(f's_steel={s_steel} must be equal or larger than 0') - if k < 0.65 or k > 1.0: - raise ValueError(f'k={k} must be between 0.65 and 1') + if A_ct <= 0: + raise ValueError(f'A_ct={A_ct} must be larger than 0') + if sigma_s < 0: + raise ValueError(f'sigma_s={sigma_s} must be equal or larger than 0') + if _k < 0.65 or _k > 1.0: + raise ValueError(f'_k={_k} must be between 0.65 and 1') if kc > 1 or kc < 0: raise ValueError(f'kc={kc} must be lower than 1 and larger than 0') - return kc * k * fct_eff * a_ct / s_steel + return kc * _k * fct_eff * A_ct / sigma_s -def k_crack_min_steel_area(h: float) -> float: +def k(h: float) -> float: """Is the coefficient which allow for the effect of non-uniform self-equilibrating stresses, which lead to a - reduction of restraint forces. Use 'k_crack_min_steel_area' - to compute it + reduction of restraint forces. k=1 for webs w<=300mm or flanges widths less than 300mm k=0.65 for webs w>=800mm or flanges with widths greater than 800mm @@ -142,7 +141,7 @@ def k_crack_min_steel_area(h: float) -> float: return 0.65 -def kc_crack_min_steel_area_pure_tension() -> float: +def kc_pure_tension() -> float: """Computes the coefficient which takes account of the stress distribution within the section immediately prior to cracking and the change of the lever arm in pure dtension. @@ -155,8 +154,8 @@ def kc_crack_min_steel_area_pure_tension() -> float: return 1 -def kc_crack_min_steel_area_rectangular( - h: float, b: float, fct_eff: float, n_ed: float +def kc_rectangular_area( + h: float, b: float, fct_eff: float, N_ed: float ) -> float: """Computes the coefficient which takes account of the stress distribution within the section immediately prior to cracking and @@ -172,7 +171,7 @@ def kc_crack_min_steel_area_rectangular( the concrete effective at the time when the cracks may first be expected to occur: fct,eff=fct or lower (fct(t)), is cracking is expected earlier than 28 days. - n_ed (str): axial force at the serviceability limit state acting on + N_ed (str): axial force at the serviceability limit state acting on the part of the cross-section under consideration (compressive force positive). n_ed should be determined considering the characteristic values of prestress and axial forces under the @@ -190,15 +189,13 @@ def kc_crack_min_steel_area_rectangular( raise ValueError(f'b={b} should be larger than 0mm') h_s = min(h, 1000) - k1 = 1.5 if n_ed >= 0 else 2 * h_s / 3 / h - s_concrete = n_ed * 1000 / b / h + _k1 = 1.5 if N_ed >= 0 else 2 * h_s / 3 / h + s_concrete = N_ed * 1000 / b / h h_ratio = h / h_s - return min(max(0.4 * (1 - s_concrete / k1 / h_ratio / fct_eff), 0), 1) + return min(max(0.4 * (1 - s_concrete / _k1 / h_ratio / fct_eff), 0), 1) -def kc_crack_min_steel_area_flanges( - f_cr: float, a_ct: float, fct_eff: float -) -> float: +def kc_flanges_area(f_cr: float, A_ct: float, fct_eff: float) -> float: """Computes the coefficient which takes account of the stress distribution within the section immediately prior to cracking and the change of the lever arm for bending+axial combination @@ -210,7 +207,7 @@ def kc_crack_min_steel_area_flanges( f_cr: is the absolute value in kN of the tensile force within the flange immediately prior to cracking due to cracking moment calculated with fct,eff - a_ct (float): is the area of concrete within the tensile zone in mm2. + A_ct (float): is the area of concrete within the tensile zone in mm2. The tensile zone is that part of the section which is calculated to be in tension just before the formation of the first crack. fct_eff (float): is the mean value of the tensile strength in MPa of @@ -222,49 +219,47 @@ def kc_crack_min_steel_area_flanges( float: value of the kc coefficient Raises: - ValueError: is a_ct is less than 0mm2 + ValueError: is A_ct is less than 0mm2 """ f_cr = abs(f_cr) - return max(0.9 * f_cr * 1000 / a_ct / fct_eff, 0.5) + return max(0.9 * f_cr * 1000 / A_ct / fct_eff, 0.5) -def adjusted_bond_strength(e: float, d_press: float, d_steel: float) -> float: +def xi_1(xi: float, phi_p: float, phi_s: float) -> float: """Computes the adjusted ratio of bond strength taking into account the different diameters of prestressing and reinforcing steel. EUROCODE 2 1992-1-1:2004, Eq. (7.5) Args: - e (float): ratio of bond strength of prestressing and reinforcing + xi (float): ratio of bond strength of prestressing and reinforcing steel, according to Table 6.2 in 6.8.2 - d_steel (float): largest bar diameter in mm of reinforcing steel. + phi_p (float): largest bar diameter in mm of reinforcing steel. Equal to 0 if only prestressing is used in control cracking - d_press (float): equivalent diameter in mm of tendon acoording + phi_s (float): equivalent diameter in mm of tendon acoording to 6.8.2 Returns: float: with the value of the ratio Raises: - ValueError: if diameters d_steel or d_press are lower than 0. - If ratio of bond strength e is less than 0.15 or larger than 0.8. - If area of tendons ac_eff is less than 0. Is stress variation - incr_stress is less than 0. + ValueError: if diameters phi_s or phi_p are lower than 0. + If ratio of bond strength xi is less than 0.15 or larger than 0.8. """ - if d_press <= 0: - raise ValueError(f'd_press={d_press} cannot be less than 0') - if d_steel < 0: - raise ValueError(f'd_steel={d_steel} cannot be less than 0') - if e < 0.15: - raise ValueError(f'The minimum value for e={e} is 0.15') - if e > 0.8: - raise ValueError(f'The maximum value for e={e} is 0.8') + if phi_p <= 0: + raise ValueError(f'phi_p={phi_p} cannot be less than 0') + if phi_s < 0: + raise ValueError(f'phi_s={phi_s} cannot be less than 0') + if xi < 0.15: + raise ValueError(f'The minimum value for xi={xi} is 0.15') + if xi > 0.8: + raise ValueError(f'The maximum value for xi={xi} is 0.8') - return ((e * d_steel / d_press) ** 0.5) if d_steel > 0 else e**0.5 + return ((xi * phi_s / phi_p) ** 0.5) if phi_s > 0 else xi**0.5 -def hc_eff_concrete_tension(h: float, d: float, x: float) -> float: +def hc_eff(h: float, d: float, x: float) -> float: """Returns the effective height of concrete in tension surrounding the reinforcement or prestressing tendons. @@ -297,17 +292,17 @@ def hc_eff_concrete_tension(h: float, d: float, x: float) -> float: return min(2.5 * (h - d), (h - x) / 3, h / 2) -def crack_min_steel_area_with_prestresed_tendons( - a_ct: float, - s_steel: float, +def As_min_p( + A_ct: float, + sigma_s: float, fct_eff: float, - k: float, + _k: float, kc: float, - ap: float, - d_steel: float, - d_press: float, - e: float, - incr_stress: float, + Ap: float, + phi_s: float, + phi_p: float, + xi: float, + delta_s: float, ) -> float: """Computes the minimum area of reinforcing steel within the tensile zone for control of cracking areas in addition with bonded tendons @@ -315,10 +310,10 @@ def crack_min_steel_area_with_prestresed_tendons( EUROCODE 2 1992-1-1:2004, Eq. (7.1) Args: - a_ct (float): is the area of concrete within the tensile zone in mm2. + A_ct (float): is the area of concrete within the tensile zone in mm2. The tensile zone is that part of the section which is calculated to be in tension just before the formation of the first crack. - s_steel (float): is the absolute value of the maximum stress in MPa + sigma_s (float): is the absolute value of the maximum stress in MPa permitted in the reinforcement immediately after the formation of the crack. This may be taken as theyield strength of the reinforcement, fyk. A lower value may, however, be needed to @@ -328,7 +323,7 @@ def crack_min_steel_area_with_prestresed_tendons( the concrete effective at the time when the cracks may first be expected to occur: fct,eff=fct or lower (fct(t)), is cracking is expected earlier than 28 days. - k (float): is the coefficient which allow for the effect of + _k (float): is the coefficient which allow for the effect of non-uniform self-equilibrating stresses, which lead to a reduction of restraint forces. Use 'k_crack_min_steel_area' to compute it @@ -338,17 +333,15 @@ def crack_min_steel_area_with_prestresed_tendons( kc (float): is a coefficient which takes account of the stress distribution within the section immediately prior to cracking and the change of the lever arm. - ac_eff (float): is the effective area in mm2 of concrete in tension - surrounding or prestressing tendons if depth hc,ef - ap (float): is the area in mm2 of pre or post-tensioned tendons + Ap (float): is the area in mm2 of pre or post-tensioned tendons within ac_eff - d_steel (float): largest bar diameter in mm of reinforcing steel. + phi_s (float): largest bar diameter in mm of reinforcing steel. Equal to 0 if only prestressing is used in control cracking - d_press (float): equivalent diameter in mm of tendon acoording + phi_p (float): equivalent diameter in mm of tendon acoording to 6.8.2 - e (float): ratio of bond strength of prestressing and reinforcing + chi (float): ratio of bond strength of prestressing and reinforcing steel, according to Table 6.2 in 6.8.2 - incr_stress (float): stress variation in MPa in prestressing tendons + delta_s (float): stress variation in MPa in prestressing tendons from the state of zero strain of the concrete at the same level Returns: @@ -356,43 +349,43 @@ def crack_min_steel_area_with_prestresed_tendons( zone in mm2. Raises: - ValueError: if k value is not between 0.65 and 1 or kc is not - larger than 0 and lower than 1. If diameters d_steel or - d_press are lower than 0. If ratio of bond strength e - is less than 0.15 or larger than 0.8. If area of tendons ac_eff - is less than 0. Is stress variation incr_stress is less than 0 + ValueError: if _k value is not between 0.65 and 1 or kc is not + larger than 0 and lower than 1. If diameters phi_s or + phi_p are lower than 0. If ratio of bond xi strength e + is less than 0.15 or larger than 0.8. + Is stress variation incr_stress is less than 0. """ fct_eff = abs(fct_eff) - if ap < 0: - raise ValueError(f'ap={ap} cannot be less than 0') - if incr_stress < 0: - raise ValueError(f'incr_stress={incr_stress} cannot be less than 0') - if a_ct <= 0: - raise ValueError(f'a_ct={a_ct} must be larger than 0') - if s_steel < 0: - raise ValueError(f's_steel={s_steel} must be equal or larger than 0') - if k < 0.65 or k > 1.0: - raise ValueError(f'k={k} must be between 0.65 and 1') + if Ap < 0: + raise ValueError(f'Ap={Ap} cannot be less than 0') + if delta_s < 0: + raise ValueError(f'delta_s={delta_s} cannot be less than 0') + if A_ct <= 0: + raise ValueError(f'A_ct={A_ct} must be larger than 0') + if sigma_s < 0: + raise ValueError(f'sigma_s={sigma_s} must be equal or larger than 0') + if _k < 0.65 or _k > 1.0: + raise ValueError(f'_k={_k} must be between 0.65 and 1') if kc > 1 or kc < 0: raise ValueError(f'kc={kc} must be lower than 1 and larger than 0') - a1 = kc * k * fct_eff * a_ct - e1 = adjusted_bond_strength(e, d_press, d_steel) - a2 = e1 * ap * incr_stress + a1 = kc * _k * fct_eff * A_ct + e1 = xi_1(xi, phi_p, phi_s) + a2 = e1 * Ap * delta_s a = a1 - a2 - return a / s_steel + return a / sigma_s -def crack_min_steel_without_direct_calculation( - wk: float, - s_steel: float, +def As_min_2( + _wk: float, + sigma_s: float, fct_eff: float, h_cr: float, h: float, d: float, - incr_stress: float = 0, + delta_s: float = 0, kc: t.Optional[float] = None, ) -> t.Tuple[float, float]: """Computes the minimum area of reinforcing steel within the tensile zone @@ -401,8 +394,8 @@ def crack_min_steel_without_direct_calculation( EUROCODE 2 1992-1-1:2004, Table (7.2N), Table (7.3N) Args: - wk (float): the characteristic crack width value in mm. - s_steel (float): the steel stress value in MPa under the relevant + _wk (float): the characteristic crack width value in mm. + sigma_s (float): the steel stress value in MPa under the relevant combination of actions. fct_eff (float): is the mean value of the tensile strength in MPa of the concrete effective at the time when the cracks may first be @@ -414,7 +407,7 @@ def crack_min_steel_without_direct_calculation( h (float): the overall depth of the section in mm. d (float): is the effective depth to the centroid of the outer layer of the reinforcement. - incr_stress (float, optional): value of prestressed stress in MPa if + delta_s (float, optional): value of prestressed stress in MPa if applicable kc (float, optional): is a coefficient which takes account of the stress distribution within the section immediately prior to @@ -426,12 +419,12 @@ def crack_min_steel_without_direct_calculation( in the first position and the maximum bar spacing in mm in the second position Raises: - ValueError: if wk, fct_eff, h_cr, h or d are less than 0 + ValueError: if _wk, fct_eff, h_cr, h or d are less than 0 ValueError: if kc is not between 0 and 1 ValueError: if combination of wk and stress values are out of scope """ - if wk < 0: - raise ValueError(f'wd={wk} cannot be less than 0') + if _wk < 0: + raise ValueError(f'_wk={_wk} cannot be less than 0') if fct_eff < 0: raise ValueError(f'fct_eff={fct_eff} is less than 0') if h_cr < 0: @@ -443,7 +436,7 @@ def crack_min_steel_without_direct_calculation( if kc is not None and (kc < 0 or kc > 1): raise ValueError(f'kc={kc} is not between 0 and 1') - s = s_steel - incr_stress + s = sigma_s - delta_s if s <= 0: return (0, 0) @@ -499,7 +492,7 @@ def crack_min_steel_without_direct_calculation( points_phi = np.array(np.meshgrid(y_phi, x)).T.reshape(-1, 2) points_spa = np.array(np.meshgrid(y_spa, x)).T.reshape(-1, 2) - xi = (s, wk) + xi = (s, _wk) phi_star = float( scipy.interpolate.griddata(points_phi, phi_s_v, xi, method='linear') @@ -519,40 +512,40 @@ def crack_min_steel_without_direct_calculation( return phi, spa -def get_alpha_e(es: float, ecm: float) -> float: +def alpha_e(Es: float, Ecm: float) -> float: """Compute the ratio between the steel and mean concrete - modules. + elastic modules. EUROCODE 2 1992-1-1:2004, Section 7.3.4-2 Args: - es (float): steel elastic modulus in MPa - ecm (float): ecm concrete mean elastic modulus in MPa + Es (float): steel elastic modulus in MPa + Ecm (float): concrete mean elastic modulus in MPa Returns: float: ratio between modules Raise: ValueError: if any of es or ecm is lower than 0. """ - if es < 0: - raise ValueError(f'es={es} cannot be less than 0') - if ecm < 0: - raise ValueError(f'ecm={ecm} cannot be less than 0') + if Es < 0: + raise ValueError(f'Es={Es} cannot be less than 0') + if Ecm < 0: + raise ValueError(f'Ecm={Ecm} cannot be less than 0') - return es / ecm + return Es / Ecm -def rho_p_eff(a_s: float, e1: float, a_p, ac_eff: float) -> float: +def rho_p_eff(As: float, xi1: float, Ap: float, Ac_eff: float) -> float: """Effective bond ratio between areas EUROCODE 2 1992-1-1:2004, Eq. (7.10) Args: - a_s (float): steel area in mm2 - e1 (float): the adjusted ratio of bond according + As (float): steel area in mm2 + xi1 (float): the adjusted ratio of bond according to expression (7.5) - a_p (float): the area in mm2 of post-tensioned tendons in ac_eff - ac_eff (float): effective area of concrete in tension surrounding + Ap (float): the area in mm2 of post-tensioned tendons in ac_eff + Ac_eff (float): effective area of concrete in tension surrounding the reinforcement or prestressing tendons of depth hc_eff. Returns: @@ -560,21 +553,21 @@ def rho_p_eff(a_s: float, e1: float, a_p, ac_eff: float) -> float: Raise: - ValueError: if any of a_s, e1, a_p or ac_eff is less than 0 + ValueError: if any of As, xi1, Ap or Ac_eff is less than 0 """ - if a_s < 0: - raise ValueError(f'a_s={a_s} cannot be less than 0') - if e1 < 0: - raise ValueError(f'e1={e1} cannot be less than 0') - if a_p < 0: - raise ValueError(f'a_p={a_p} cannot be less than 0') - if ac_eff < 0: - raise ValueError(f'ac_eff={ac_eff} cannot be less than 0') + if As < 0: + raise ValueError(f'As={As} cannot be less than 0') + if xi1 < 0: + raise ValueError(f'xi1={xi1} cannot be less than 0') + if Ap < 0: + raise ValueError(f'Ap={Ap} cannot be less than 0') + if Ac_eff < 0: + raise ValueError(f'Ac_eff={Ac_eff} cannot be less than 0') - return (a_s + e1**2 * a_p) / ac_eff + return (As + xi1**2 * Ap) / Ac_eff -def kt_load_duration(load_type: str) -> float: +def kt(load_type: str) -> float: """Returns the kt factor dependent on the load duration for the crack width calculation @@ -602,12 +595,12 @@ def kt_load_duration(load_type: str) -> float: def esm_ecm( - s_steel: float, - alpha_e: float, - rho_p_eff: float, - kt: float, + sigma_s: float, + _alpha_e: float, + _rho_p_eff: float, + _kt: float, fct_eff: float, - es: float, + Es: float, ) -> float: """Returns the strain difference (esm - ecm) needed to compute the crack width. esm is the mean strain in the reinforcement under the relevant @@ -619,52 +612,52 @@ def esm_ecm( EUROCODE 2 1992-1-1:2004, Eq. (7.9) Args: - s_steel (float): is the stress in MPa in the tension reinforcement + sigma_s (float): is the stress in MPa in the tension reinforcement assuming a cracked section. FOr pretensioned members, s_steel may be replaced by increment of s_steel stress variation in prestressing tendons from the state of zero strain of the concrete at the same level. - alpha_e (float): is the ratio Es/Ecm - rho_p_eff (float): effective bond ratio between areas given by the + _alpha_e (float): is the ratio Es/Ecm + _rho_p_eff (float): effective bond ratio between areas given by the Eq. (7.10) - kt (float): is a factor dependent on the load duration + _kt (float): is a factor dependent on the load duration fct_eff (float): is the mean value of the tensile strength in MPa of the concrete effectvie at the time when the cracks may first be expected to occur: fct_eff=fctm or fctm(t) if crack is expected earlier than 28 days. - es: steel elastic mudulus in MPa + Es: steel elastic mudulus in MPa Returns: float: the strain difference between concrete and steel Raises: - ValueError: if any s_steel, alpha_e, rho_p_eff, fct_Eff is less + ValueError: if any sigma_s, alpha_e, rho_p_eff, fct_eff or Es is less than 0. ValueError: if kt is not 0.6 and not 0.4 """ - if s_steel < 0: - raise ValueError(f's_steel={s_steel} cannot be less than 0') - if alpha_e < 0: - raise ValueError(f'alpha_e={alpha_e} cannot be less than 0') - if rho_p_eff < 0: - raise ValueError(f'rho_p_eff={rho_p_eff} cannot be less than 0') + if sigma_s < 0: + raise ValueError(f'sigma_s={sigma_s} cannot be less than 0') + if _alpha_e < 0: + raise ValueError(f'_alpha_e={_alpha_e} cannot be less than 0') + if _rho_p_eff < 0: + raise ValueError(f'_rho_p_eff={_rho_p_eff} cannot be less than 0') if fct_eff < 0: raise ValueError(f'fct_eff={fct_eff} cannot be less than 0') - if es < 0: - raise ValueError(f'es={es} cannot be less than 0') - if kt != 0.6 and kt != 0.4: - raise ValueError(f'kt={kt} can only take as values 0.4 and 0.6') + if Es < 0: + raise ValueError(f'Es={Es} cannot be less than 0') + if _kt != 0.6 and _kt != 0.4: + raise ValueError(f'_kt={_kt} can only take as values 0.4 and 0.6') - min_val = 0.6 * s_steel / es + min_val = 0.6 * sigma_s / Es - a = 1 + alpha_e * rho_p_eff - b = kt * fct_eff / rho_p_eff * a - c = (s_steel - b) / es + a = 1 + _alpha_e * _rho_p_eff + b = _kt * fct_eff / _rho_p_eff * a + c = (sigma_s - b) / Es return max(c, min_val) -def s_threshold(c: float, phi: float) -> float: +def w_spacing(c: float, phi: float) -> float: """Computes the distance threshold from which the maximum crack spacing is constant. @@ -804,15 +797,15 @@ def k4(): def sr_max_close( c: float, phi: float, - rho_p_eff: float, - k1: float, - k2: float, - k3: float, - k4: float, + _rho_p_eff: float, + _k1: float, + _k2: float, + _k3: float, + _k4: float, ) -> float: """Computes the maximum crack spacing in cases where bonded reinforcement is fixed at reasonably close centres within the tension zone - (spacing<=5(c+phi/2)). + (w_spacing<=5(c+phi/2)). EUROCODE 2 1992-1-1:2004, Eq. (7.11) @@ -820,14 +813,14 @@ def sr_max_close( c (float): is the cover in mm of the longitudinal reinforcement phi (float): is the bar diameter in mm. Where mixed bar diameters used, then it should be replaced for an equivalente bar diameter. - rho_p_eff (float): effective bond ratio between areas given by the + _rho_p_eff (float): effective bond ratio between areas given by the Eq. (7.10) - k1 (float): coefficient that takes into account the bound properties + _k1 (float): coefficient that takes into account the bound properties of the bonded reinforcement - k2 (float): coefficient that takes into account the distribution of + _k2 (float): coefficient that takes into account the distribution of of the strain - k3 (float): coefficient from the National Annex - k4 (float): coefficient from the National Annex + _k3 (float): coefficient from the National Annex + _k4 (float): coefficient from the National Annex Returns: float: the maximum crack spaing in mm. @@ -842,24 +835,24 @@ def sr_max_close( raise ValueError(f'c={c} cannot be less than zero') if phi < 0: raise ValueError(f'phi={phi} cannot be less than zero') - if rho_p_eff < 0: - raise ValueError(f'rho_p_eff={rho_p_eff} cannot be less than zero') - if k3 < 0: - raise ValueError(f'k3={k3} cannot be less than zero') - if k4 < 0: - raise ValueError(f'k4={k4} cannot be less than zero') - if k1 != 0.8 and k1 != 1.6: - raise ValueError(f'k1={k1} can only take as values 0.8 and 1.6') - if k2 < 0.5 or k2 > 1: - raise ValueError(f'k2={k2} is not between 0.5 and 1.0') + if _rho_p_eff < 0: + raise ValueError(f'_rho_p_eff={_rho_p_eff} cannot be less than zero') + if _k3 < 0: + raise ValueError(f'_k3={_k3} cannot be less than zero') + if _k4 < 0: + raise ValueError(f'_k4={_k4} cannot be less than zero') + if _k1 != 0.8 and _k1 != 1.6: + raise ValueError(f'_k1={_k1} can only take as values 0.8 and 1.6') + if _k2 < 0.5 or _k2 > 1: + raise ValueError(f'_k2={_k2} is not between 0.5 and 1.0') - return k3 * c + k1 * k2 * k4 * phi / rho_p_eff + return _k3 * c + _k1 * _k2 * _k4 * phi / _rho_p_eff def sr_max_far(h: float, x: float) -> float: """Computes the maximum crack spacing in cases where bonded reinforcement is fixed at reasonably close centres within the tension zone - (spacing>5(c+phi/2)). + (w_spacing>5(c+phi/2)). EUROCODE 2 1992-1-1:2004, Eq. (7.14) @@ -915,14 +908,14 @@ def sr_max_theta(sr_max_y: float, sr_max_z: float, theta: float) -> float: return 1 / (a + b) -def wk(sr_max: float, esm_ecm: float) -> float: +def wk(sr_max: float, _esm_ecm: float) -> float: """Computes the crack width EUROCODE 2 1992-1-1:2004, Eq. (7.8) Args: sr_max (float): the maximum crack length spacing in mm. - esm_ecm (float): the difference between the mean strain in the + _esm_ecm (float): the difference between the mean strain in the reinforcement under relevant combination of loads, including the effect of imposed deformations and taking into account tension stiffening and the mean strain in the concrete @@ -932,11 +925,11 @@ def wk(sr_max: float, esm_ecm: float) -> float: float: crack width in mm. Raises: - ValueError: if any of sr_max or esm_ecm is less than zero. + ValueError: if any of sr_max or _esm_ecm is less than zero. """ if sr_max < 0: raise ValueError(f'sr_max={sr_max} cannot be less than zero') - if esm_ecm < 0: - raise ValueError(f'esm_scm={esm_ecm} cannot be less than zero') + if _esm_ecm < 0: + raise ValueError(f'_esm_scm={_esm_ecm} cannot be less than zero') - return sr_max * esm_ecm + return sr_max * _esm_ecm diff --git a/structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py b/structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py new file mode 100644 index 00000000..1de528de --- /dev/null +++ b/structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py @@ -0,0 +1,933 @@ +"""Collection of functions from EUROCODE 1992-1-1:2004 +Chapter 7.3 - Crack control""" +import math +import typing as t + +import numpy as np +import scipy.interpolate + + +def w_max(exposure_class: str, load_combination: str) -> float: + """Computes the recomended value of the maximum crack width. + + EUROCODE 2 1992-1-1:2004, Table (7.1N) + + Args: + exposure_class (str): The exposure class. + Possible values: X0, XC1, XC2, XC3, XC4, XD1, XD2, XS1, XS2, XS3 + load_combination (str): + - f: for frequent load combination + - qp: for quasi-permanent load combination + + Returns: + float: The maximum recommended value for the crack width wmax in mm. + + Raises: + ValueError: if not valid exposure_class or load_combination values. + """ + _load_combination = load_combination.lower().strip() + _exposure_class = exposure_class.upper().strip() + if _load_combination == 'f': + if _exposure_class in ('X0', 'XC1'): + return 0.2 + if _exposure_class in ('XC2', 'XC3', 'XC4'): + return 0.2 + if _load_combination == 'qp': + if _exposure_class in ('X0', 'XC1'): + return 0.4 + if _exposure_class in ( + 'XC2', + 'XC3', + 'XC4', + 'XD1', + 'XD2', + 'XS1', + 'XS2', + 'XS3', + ): + return 0.3 + raise ValueError( + f'{exposure_class} is not a valid value for exposure_class.' + + ' Please enter one of the following: X0, XC1, XC2, XC3, XC4, XD1' + + ',XD2, XS1, XS2, XS3' + ) + raise ValueError( + f'{load_combination} is not a valid value for load_combination.' + + 'Please enter "f" for frequent load combination or "qp" for' + + 'quasi-permanent load combination.' + ) + + +def As_min( + A_ct: float, sigma_s: float, fct_eff: float, _k: float, kc: float +) -> float: + """Computes the minimum area of reinforcing steel within the tensile zone + for control of cracking areas + + EUROCODE 2 1992-1-1:2004, Eq. (7.1) + + Args: + A_ct (float): is the area of concrete within the tensile zone in mm2. + The tensile zone is that parg of the section which is calculated + to be in tension just before the formation of the first crack. + sigma_s (float): is the absolute value of the maximum stress in MPa + permitted in the reinforcement immediately after the formation + of the crack. This may be taken as theyield strength of the + reinforcement, fyk. A lower value may, however, be needed to + satisfy the crack width limits according to the maximum + bar size of spacing (see 7.3.3 (2)). + fct_eff (float): is the mean value of the tensile strength in MPa of + the concrete effective at the time when the cracks may first be + expected to occur: fct,eff=fct or lower (fct(t)), is cracking + is expected earlier than 28 days. + _k (float): is the coefficient which allow for the effect of + non-uniform self-equilibrating stresses, which lead to a + reduction of restraint forces. Use 'k_crack_min_steel_area' + to compute it + k=1 for webs w<=300mm or flanges widths less than 300mm + k=0.65 for webs w>=800mm or flanges with widths greater than 800mm + Intermediate values may be interpolated. + kc (float): is a coefficient which takes account of the stress + distribution within the section immediately prior to cracking and + the change of the lever arm. + + Returns: + float: the minimm area of reinforcing steel within the tensile + zone in mm2. + + Raises: + ValueError: if _k value is not between 0.65 and 1 or kc is not + larger than 0 and lower than 1. + """ + fct_eff = abs(fct_eff) + + if A_ct <= 0: + raise ValueError(f'A_ct={A_ct} must be larger than 0') + if sigma_s < 0: + raise ValueError(f'sigma_s={sigma_s} must be equal or larger than 0') + if _k < 0.65 or _k > 1.0: + raise ValueError(f'_k={_k} must be between 0.65 and 1') + if kc > 1 or kc < 0: + raise ValueError(f'kc={kc} must be lower than 1 and larger than 0') + + return kc * _k * fct_eff * A_ct / sigma_s + + +def k(h: float) -> float: + """Is the coefficient which allow for the effect of + non-uniform self-equilibrating stresses, which lead to a + reduction of restraint forces. + k=1 for webs w<=300mm or flanges widths less than 300mm + k=0.65 for webs w>=800mm or flanges with widths greater than 800mm + + EUROCODE 2 1992-1-1:2004, Eq. (7.1) + + Args: + h (float): flange length or flange width in mm + + Returns: + float: k coefficient value + + Raises: + ValueError: if h is less than 0 + """ + if h < 0: + raise ValueError(f'h={h} cannot be less than 0mm') + if h <= 300: + return 1 + if h < 800: + interpol = scipy.interpolate.interp1d((300, 800), (1, 0.65)) + return (float)(interpol(h)) + return 0.65 + + +def kc_tension() -> float: + """Computes the coefficient which takes account of the stress + distribution within the section immediately prior to cracking and + the change of the lever arm in pure dtension. + + EUROCODE 2 1992-1-1:2004, Eq. (7.1) + + Returns: + float: value of the kc coefficient in pure tension + """ + return 1 + + +def kc_rect_area(h: float, b: float, fct_eff: float, N_ed: float) -> float: + """Computes the coefficient which takes account of the stress + distribution within the section immediately prior to cracking and + the change of the lever arm for bending+axial combination + in rectangular sections and webs of box sections and T-sections. + + EUROCODE 2 1992-1-1:2004, Eq. (7.2) + + Args: + h (float): heigth of the element in mm + b (float): width of the element in mm + fct_eff (float): is the mean value of the tensile strength in MPa of + the concrete effective at the time when the cracks may first be + expected to occur: fct,eff=fct or lower (fct(t)), is cracking + is expected earlier than 28 days. + N_ed (str): axial force at the serviceability limit state acting on + the part of the cross-section under consideration (compressive + force positive). n_ed should be determined considering the + characteristic values of prestress and axial forces under the + relevant combination of actions + + Returns: + float: value of the kc coefficient + + Raises: + ValueError: is h or b are less than 0 + """ + if h < 0: + raise ValueError(f'h={h} should be larger than 0mm') + if b < 0: + raise ValueError(f'b={b} should be larger than 0mm') + + h_s = min(h, 1000) + _k1 = 1.5 if N_ed >= 0 else 2 * h_s / 3 / h + s_concrete = N_ed * 1000 / b / h + h_ratio = h / h_s + return min(max(0.4 * (1 - s_concrete / _k1 / h_ratio / fct_eff), 0), 1) + + +def kc_flanges_area(f_cr: float, A_ct: float, fct_eff: float) -> float: + """Computes the coefficient which takes account of the stress + distribution within the section immediately prior to cracking and + the change of the lever arm for bending+axial combination + in rectangular sections for flanges of box sections and T-sections. + + EUROCODE 2 1992-1-1:2004, Eq. (7.3) + + Args: + f_cr: is the absolute value in kN of the tensile force within the + flange immediately prior to cracking due to cracking moment + calculated with fct,eff + A_ct (float): is the area of concrete within the tensile zone in mm2. + The tensile zone is that part of the section which is calculated + to be in tension just before the formation of the first crack. + fct_eff (float): is the mean value of the tensile strength in MPa of + the concrete effective at the time when the cracks may first be + expected to occur: fct,eff=fct or lower (fct(t)), is cracking + is expected earlier than 28 days. + + Returns: + float: value of the kc coefficient + + Raises: + ValueError: is A_ct is less than 0mm2 + """ + f_cr = abs(f_cr) + return max(0.9 * f_cr * 1000 / A_ct / fct_eff, 0.5) + + +def xi1(xi: float, phi_p: float, phi_s: float) -> float: + """Computes the adjusted ratio of bond strength taking into account + the different diameters of prestressing and reinforcing steel. + + EUROCODE 2 1992-1-1:2004, Eq. (7.5) + + Args: + xi (float): ratio of bond strength of prestressing and reinforcing + steel, according to Table 6.2 in 6.8.2 + phi_p (float): largest bar diameter in mm of reinforcing steel. + Equal to 0 if only prestressing is used in control cracking + phi_s (float): equivalent diameter in mm of tendon acoording + to 6.8.2 + + Returns: + float: with the value of the ratio + + Raises: + ValueError: if diameters phi_s or phi_p are lower than 0. + If ratio of bond strength xi is less than 0.15 or larger than 0.8. + """ + + if phi_p <= 0: + raise ValueError(f'phi_p={phi_p} cannot be less than 0') + if phi_s < 0: + raise ValueError(f'phi_s={phi_s} cannot be less than 0') + if xi < 0.15: + raise ValueError(f'The minimum value for xi={xi} is 0.15') + if xi > 0.8: + raise ValueError(f'The maximum value for xi={xi} is 0.8') + + return ((xi * phi_s / phi_p) ** 0.5) if phi_s > 0 else xi**0.5 + + +def hc_eff(h: float, d: float, x: float) -> float: + """Returns the effective height of concrete in tension surrounding + the reinforcement or prestressing tendons. + + EUROCODE 2 1992-1-1:2004, Section (7.3.2-3) + + Args: + h (float): total depth of the element in mm + d (float): distance in mm to the level of the steel centroid + x (float): distance in mm to the zero tensile stress line + + Returns: + float: the effective height in mm + + Raises: + ValueError: if any of h, d or x is lower than zero. + ValueError: if d is greater than h + ValueError: if x is greater than h + """ + if h < 0: + raise ValueError(f'h={h} cannot be less than 0') + if d < 0: + raise ValueError(f'd={d} cannot be less than 0') + if x < 0: + raise ValueError(f'x={x} cannot be less than zero') + if d > h: + raise ValueError(f'd={d} cannot be larger than h={h}') + if x > h: + raise ValueError(f'x={x} cannot be larger than h={h}') + + return min(2.5 * (h - d), (h - x) / 3, h / 2) + + +def As_min_p( + A_ct: float, + sigma_s: float, + fct_eff: float, + _k: float, + kc: float, + Ap: float, + phi_s: float, + phi_p: float, + xi: float, + delta_s: float, +) -> float: + """Computes the minimum area of reinforcing steel within the tensile zone + for control of cracking areas in addition with bonded tendons + + EUROCODE 2 1992-1-1:2004, Eq. (7.1) + + Args: + A_ct (float): is the area of concrete within the tensile zone in mm2. + The tensile zone is that part of the section which is calculated + to be in tension just before the formation of the first crack. + sigma_s (float): is the absolute value of the maximum stress in MPa + permitted in the reinforcement immediately after the formation + of the crack. This may be taken as theyield strength of the + reinforcement, fyk. A lower value may, however, be needed to + satisfy the crack width limits according to the maximum + bar size of spacing (see 7.3.3 (2)). + fct_eff (float): is the mean value of the tensile strength in MPa of + the concrete effective at the time when the cracks may first be + expected to occur: fct,eff=fct or lower (fct(t)), is cracking + is expected earlier than 28 days. + _k (float): is the coefficient which allow for the effect of + non-uniform self-equilibrating stresses, which lead to a + reduction of restraint forces. Use 'k_crack_min_steel_area' + to compute it + k=1 for webs w<=300mm or flanges widths less than 300mm + k=0.65 for webs w>=800mm or flanges with widths greater than 800mm + Intermediate values may be interpolated. + kc (float): is a coefficient which takes account of the stress + distribution within the section immediately prior to cracking and + the change of the lever arm. + Ap (float): is the area in mm2 of pre or post-tensioned tendons + within ac_eff + phi_s (float): largest bar diameter in mm of reinforcing steel. + Equal to 0 if only prestressing is used in control cracking + phi_p (float): equivalent diameter in mm of tendon acoording + to 6.8.2 + chi (float): ratio of bond strength of prestressing and reinforcing + steel, according to Table 6.2 in 6.8.2 + delta_s (float): stress variation in MPa in prestressing tendons + from the state of zero strain of the concrete at the same level + + Returns: + float: the minimm area of reinforcing steel within the tensile + zone in mm2. + + Raises: + ValueError: if _k value is not between 0.65 and 1 or kc is not + larger than 0 and lower than 1. If diameters phi_s or + phi_p are lower than 0. If ratio of bond xi strength e + is less than 0.15 or larger than 0.8. + Is stress variation incr_stress is less than 0. + """ + fct_eff = abs(fct_eff) + + if Ap < 0: + raise ValueError(f'Ap={Ap} cannot be less than 0') + if delta_s < 0: + raise ValueError(f'delta_s={delta_s} cannot be less than 0') + if A_ct <= 0: + raise ValueError(f'A_ct={A_ct} must be larger than 0') + if sigma_s < 0: + raise ValueError(f'sigma_s={sigma_s} must be equal or larger than 0') + if _k < 0.65 or _k > 1.0: + raise ValueError(f'_k={_k} must be between 0.65 and 1') + if kc > 1 or kc < 0: + raise ValueError(f'kc={kc} must be lower than 1 and larger than 0') + + a1 = kc * _k * fct_eff * A_ct + e1 = xi1(xi, phi_p, phi_s) + a2 = e1 * Ap * delta_s + a = a1 - a2 + + return a / sigma_s + + +def As_min_2( + _wk: float, + sigma_s: float, + fct_eff: float, + h_cr: float, + h: float, + d: float, + delta_s: float = 0, + kc: t.Optional[float] = None, +) -> t.Tuple[float, float]: + """Computes the minimum area of reinforcing steel within the tensile zone + for control of cracking areas + + EUROCODE 2 1992-1-1:2004, Table (7.2N), Table (7.3N) + + Args: + wk (float): the characteristic crack width value in mm. + sigma_s (float): the steel stress value in MPa under the relevant + combination of actions. + fct_eff (float): is the mean value of the tensile strength in MPa of + the concrete effective at the time when the cracks may first be + expected to occur: fct,eff=fct or lower (fct(t)), is cracking + is expected earlier than 28 days. + h_cr (float): is the depth of the tensile zone immediately prior to + cracking, considering the characteristic values of prestress and + axial forces under the quasi-permanent combination of actions. + h (float): the overall depth of the section in mm. + d (float): is the effective depth to the centroid of the outer layer + of the reinforcement. + delta_s (float, optional): value of prestressed stress in MPa if + applicable + kc (float, optional): is a coefficient which takes account of the + stress distribution within the section immediately prior to + cracking and the change of the lever arm in a bending section. + 'None' for pure tensile uniform axial section. + + Returns: + tuple(float, float): with the value of the maximum bar diameters in mm + in the first position and the maximum bar spacing in mm in the + second position + Raises: + ValueError: if wk, fct_eff, h_cr, h or d are less than 0 + ValueError: if kc is not between 0 and 1 + ValueError: if combination of wk and stress values are out of scope + """ + if _wk < 0: + raise ValueError(f'_wk={_wk} cannot be less than 0') + if fct_eff < 0: + raise ValueError(f'fct_eff={fct_eff} is less than 0') + if h_cr < 0: + raise ValueError(f'h_cr={h_cr} is less than 0') + if h < 0: + raise ValueError(f'h={h} is less than 0') + if d < 0: + raise ValueError(f'd={d} is less than 0') + if kc is not None and (kc < 0 or kc > 1): + raise ValueError(f'kc={kc} is not between 0 and 1') + + s = sigma_s - delta_s + if s <= 0: + return (0, 0) + + x = (0.4, 0.3, 0.2) + y_phi = (160, 200, 240, 280, 320, 360, 400, 450) + y_spa = (160, 200, 240, 280, 320, 360) + phi_s_v = ( + 40, + 32, + 25, + 32, + 25, + 16, + 20, + 16, + 12, + 16, + 12, + 8, + 12, + 10, + 6, + 10, + 8, + 5, + 8, + 6, + 4, + 6, + 5, + None, + ) + spa_v = ( + 300, + 300, + 200, + 300, + 250, + 150, + 250, + 200, + 100, + 200, + 150, + 50, + 150, + 100, + None, + 100, + 50, + None, + ) + + points_phi = np.array(np.meshgrid(y_phi, x)).T.reshape(-1, 2) + points_spa = np.array(np.meshgrid(y_spa, x)).T.reshape(-1, 2) + xi = (s, _wk) + + phi_star = float( + scipy.interpolate.griddata(points_phi, phi_s_v, xi, method='linear') + ) + if kc is not None: + phi = phi_star * (fct_eff / 2.9) * kc * h_cr / (2 * (h - d)) + else: + phi = phi_star * (fct_eff / 2.9) * h_cr / (8 * (h - d)) + + spa = float( + scipy.interpolate.griddata(points_spa, spa_v, xi, method='linear') + ) + + if math.isnan(phi) or math.isnan(spa): + raise ValueError('Combination of _wk or stress values out of scope') + + return phi, spa + + +def alpha_e(Es: float, Ecm: float) -> float: + """Compute the ratio between the steel and mean concrete + elastic modules. + + EUROCODE 2 1992-1-1:2004, Section 7.3.4-2 + + Args: + Es (float): steel elastic modulus in MPa + Ecm (float): concrete mean elastic modulus in MPa + + Returns: + float: ratio between modules + Raise: + ValueError: if any of es or ecm is lower than 0. + """ + if Es < 0: + raise ValueError(f'Es={Es} cannot be less than 0') + if Ecm < 0: + raise ValueError(f'Ecm={Ecm} cannot be less than 0') + + return Es / Ecm + + +def rho_p_eff(As: float, _xi1: float, Ap: float, Ac_eff: float) -> float: + """Effective bond ratio between areas + + EUROCODE 2 1992-1-1:2004, Eq. (7.10) + + Args: + As (float): steel area in mm2 + _xi1 (float): the adjusted ratio of bond according + to expression (7.5) + Ap (float): the area in mm2 of post-tensioned tendons in ac_eff + Ac_eff (float): effective area of concrete in tension surrounding + the reinforcement or prestressing tendons of depth hc_eff. + + Returns: + float: with the retio between areas + + + Raise: + ValueError: if any of As, xi1, Ap or Ac_eff is less than 0 + """ + if As < 0: + raise ValueError(f'As={As} cannot be less than 0') + if _xi1 < 0: + raise ValueError(f'_xi1={_xi1} cannot be less than 0') + if Ap < 0: + raise ValueError(f'Ap={Ap} cannot be less than 0') + if Ac_eff < 0: + raise ValueError(f'Ac_eff={Ac_eff} cannot be less than 0') + + return (As + _xi1**2 * Ap) / Ac_eff + + +def kt(load_type: str) -> float: + """Returns the kt factor dependent on the load duration for + the crack width calculation + + Args: + load_type (str): the load type: + - 'short' for term loading + - 'long' for long term loading + + Returns: + float: with the kt factor + + Raises: + ValueError: if load_type is not 'short' and not 'long' + """ + if not isinstance(load_type, str): + raise TypeError + + load_type = load_type.lower().strip() + if load_type != 'short' and load_type != 'long': + raise ValueError( + f'load_type={load_type} can only have "short" or "long" as a value' + ) + + return 0.6 if load_type == 'short' else 0.4 + + +def esm_ecm( + sigma_s: float, + _alpha_e: float, + _rho_p_eff: float, + _kt: float, + fct_eff: float, + Es: float, +) -> float: + """Returns the strain difference (esm - ecm) needed to compute the crack + width. esm is the mean strain in the reinforcement under the relevant + combination of loads of imposed deformations and taking into account the + effects of tension stiffening. Only the additional tensile strain beyond + the state of zero strain of the concrete is considered. ecm is the mean + strain in the concrete between the cracks. + + EUROCODE 2 1992-1-1:2004, Eq. (7.9) + + Args: + sigma_s (float): is the stress in MPa in the tension reinforcement + assuming a cracked section. FOr pretensioned members, s_steel may + be replaced by increment of s_steel stress variation in + prestressing tendons from the state of zero strain of the + concrete at the same level. + _alpha_e (float): is the ratio Es/Ecm + _rho_p_eff (float): effective bond ratio between areas given by the + Eq. (7.10) + _kt (float): is a factor dependent on the load duration + fct_eff (float): is the mean value of the tensile strength in MPa + of the concrete effectvie at the time when the cracks may + first be expected to occur: fct_eff=fctm or fctm(t) if + crack is expected earlier than 28 days. + Es: steel elastic mudulus in MPa + + Returns: + float: the strain difference between concrete and steel + + Raises: + ValueError: if any sigma_s, _alpha_e, _rho_p_eff, fct_eff or Es is less + than 0. + ValueError: if _kt is not 0.6 and not 0.4 + """ + if sigma_s < 0: + raise ValueError(f'sigma_s={sigma_s} cannot be less than 0') + if _alpha_e < 0: + raise ValueError(f'_alpha_e={_alpha_e} cannot be less than 0') + if _rho_p_eff < 0: + raise ValueError(f'_rho_p_eff={_rho_p_eff} cannot be less than 0') + if fct_eff < 0: + raise ValueError(f'fct_eff={fct_eff} cannot be less than 0') + if Es < 0: + raise ValueError(f'Es={Es} cannot be less than 0') + if _kt != 0.6 and _kt != 0.4: + raise ValueError(f'_kt={_kt} can only take as values 0.4 and 0.6') + + min_val = 0.6 * sigma_s / Es + + a = 1 + _alpha_e * _rho_p_eff + b = _kt * fct_eff / _rho_p_eff * a + c = (sigma_s - b) / Es + + return max(c, min_val) + + +def w_spacing(c: float, phi: float) -> float: + """Computes the distance threshold from which the + maximum crack spacing is constant. + + EUROCODE 2 1992-1-1:2004, Sect. (7.3.4-3) + + Args: + c (float): cover of the longitudinal reinforcement in mm + phi (float): is the bar diameter in mm. Where mixed bar diameters + used, then it should be replaced for an equivalente bar diameter. + + Returns: + float: threshold distance in mm + + Raises: + ValueError: if any of c or phi is less than 0. + """ + if c < 0: + raise ValueError(f'c={c} cannot be less than 0') + if phi < 0: + raise ValueError(f'phi={phi} cannot be less than 0') + + return 5 * (c + phi / 2) + + +def phi_eq(n1: int, n2: int, phi1: float, phi2: float) -> float: + """Computes the equivalent diameter. For a section with n1 bars of + diameter phi1 and n2 bars of diameter phi2 + + EUROCODE 2 1992-1-1:2004, Sect. (7.12) + + Args: + n1 (int): number of bars with diameter phi1 + n2 (int): number of bars with diameter phi2 + phi1 (float): diameter of n1 bars in mm + phi2 (float): diamater of n2 bars in mm + + Returns: + float: the equivalent diameter in mm + + Raises: + ValueError: if any of n1 or n2 is less than 0 + ValueError: if any of phi1 or phi2 is less than 0 + TypeError: if any of n1 or n2 is not an integer + """ + if n1 < 0: + raise ValueError(f'n1={n1} cannot be less than 0') + if not isinstance(n1, int): + raise TypeError(f'n1={n1} needs to be an integer value') + if n2 < 0: + raise ValueError(f'n2={n2} cannot be less than 0') + if not isinstance(n2, int): + raise TypeError(f'n2={n2} needs to be an integer value') + if phi1 < 0: + raise ValueError(f'phi1={phi1} cannot be less than 0') + if phi2 < 0: + raise ValueError(f'phi2={phi2} cannot be less than 0') + + a = n1 * phi1**2 + n2 * phi2**2 + b = n1 * phi1 + n2 * phi2 + return a / b + + +def k1(bond_type: str) -> float: + """Get the k1 coefficient which takes account of the bond properties + of the bounded reinforcement + + EUROCODE 2 1992-1-1:2004, Eq. (7.11-k1) + + Args: + bond_type (str): the bond property of the reinforcement. + Possible values: + - 'bond': for high bond bars + - 'plane': for bars with an effectively plain surface (e.g. + prestressing tendons) + + Returns: + (float): value of the k1 coefficient + + Raises: + ValueError: if bond_type is neither 'bond' nor 'plane' + TypeError: if bond_type is not an str + """ + if not isinstance(bond_type, str): + raise TypeError(f'bond_type={bond_type} is not an str') + + bond_type = bond_type.lower().strip() + if bond_type != 'bond' and bond_type != 'plane': + raise ValueError( + f'bond_type={bond_type} can only have "bond" or "plane" as values' + ) + + return 0.8 if bond_type == 'bond' else 1.6 + + +def k2(epsilon_r: float) -> float: + """Computes a coefficient which takes into account of the + distribution of strain: + + EUROCODE 2 1992-1-1:2004, Eq. (7.13) + + Args: + epsilon_r (float): ratio epsilon_2/epsilon_1 where epsilon_1 is + thre greater and epsilon_2 is the lesser strain at the boundaries + of the section considererd, assessed on the basis of a cracked + section. epsilon_r=0 for bending and epsilon_r=1 for pure tension. + + Returns: + float: the k2 coefficient value. + + Raises: + ValueError: if epsilon_r is not between 0 and 1. + """ + if epsilon_r < 0 or epsilon_r > 1: + raise ValueError(f'epsilon_r={epsilon_r} must be between 0 and 1') + + return (1 + epsilon_r) / 2 + + +def k3(): + """Returns the k3 coefficient for computing sr_max + + Returns: + float: value for the coefficient + """ + return 3.4 + + +def k4(): + """Returns the k4 coefficient for computing sr_max + + Returns: + float: value for the coefficient + """ + return 0.425 + + +def sr_max_close( + c: float, + phi: float, + _rho_p_eff: float, + _k1: float, + _k2: float, + _k3: float, + _k4: float, +) -> float: + """Computes the maximum crack spacing in cases where bonded reinforcement + is fixed at reasonably close centres within the tension zone + (w_spacing<=5(c+phi/2)). + + EUROCODE 2 1992-1-1:2004, Eq. (7.11) + + Args: + c (float): is the cover in mm of the longitudinal reinforcement + phi (float): is the bar diameter in mm. Where mixed bar diameters + used, then it should be replaced for an equivalente bar diameter. + _rho_p_eff (float): effective bond ratio between areas given by the + Eq. (7.10) + _k1 (float): coefficient that takes into account the bound properties + of the bonded reinforcement + _k2 (float): coefficient that takes into account the distribution of + of the strain + _k3 (float): coefficient from the National Annex + _k4 (float): coefficient from the National Annex + + Returns: + float: the maximum crack spaing in mm. + + Raises: + ValueError: if one or more of c, phi, _rho_p_eff, _k3 or _k4 + is lower than zero. + ValueError: if _k1 is not 0.8 or 1.6 + ValueError: if _k2 is not between 0.5 and 1.0 + """ + if c < 0: + raise ValueError(f'c={c} cannot be less than zero') + if phi < 0: + raise ValueError(f'phi={phi} cannot be less than zero') + if _rho_p_eff < 0: + raise ValueError(f'_rho_p_eff={_rho_p_eff} cannot be less than zero') + if _k3 < 0: + raise ValueError(f'_k3={_k3} cannot be less than zero') + if _k4 < 0: + raise ValueError(f'_k4={_k4} cannot be less than zero') + if _k1 != 0.8 and _k1 != 1.6: + raise ValueError(f'_k1={_k1} can only take as values 0.8 and 1.6') + if _k2 < 0.5 or _k2 > 1: + raise ValueError(f'_k2={_k2} is not between 0.5 and 1.0') + + return _k3 * c + _k1 * _k2 * _k4 * phi / _rho_p_eff + + +def sr_max_far(h: float, x: float) -> float: + """Computes the maximum crack spacing in cases where bonded reinforcement + is fixed at reasonably close centres within the tension zone + (w_spacing>5(c+phi/2)). + + EUROCODE 2 1992-1-1:2004, Eq. (7.14) + + Args: + h (float): total depth of the beam in mm + x (float): distance to non tension area of the element mm + + Returns: + float: maximum crack spacing in mm + + Raises: + ValueError: if one of h or x is less than zero. + ValueError: x is greater than h. + """ + if x < 0: + raise ValueError(f'x={x} cannot be less than zero') + if h < 0: + raise ValueError(f'h={h} cannot be less than zero') + if x > h: + raise ValueError(f'x={x} cannot be larger than h={h}') + + return 1.3 * (h - x) + + +def sr_max_theta(sr_max_y: float, sr_max_z: float, theta: float) -> float: + """Computes the crack spacing sr_max when there is an angle + between the angle of principal stress and the direction + of the reinforcement, for members in two orthogonal directions, + that is significant (> 15º). + + EUROCODE 2 1992-1-1:2004, Eq. (7.15) + + Args: + sr_max_y (float): crack spacing in mm in the y-direction. + sr_max_z (float): crack spacing in mm in the z-direction. + theta (float): angle in radians between the reinforcement in the + y-direction and the direction of the principal tensile stress. + + Returns: + float: the crack spacing in mm. + + Raises: + ValueError: if sr_max_y or sr_max_z is negative. + ValueError: if theta is not between 0 and pi/2 + """ + if sr_max_y < 0: + raise ValueError(f'sr_max_y={sr_max_y} cannot be less than zero') + if sr_max_z < 0: + raise ValueError(f'sr_max_z={sr_max_z} cannot be less than zero') + + a = math.cos(theta) / sr_max_y + b = math.sin(theta) / sr_max_z + return 1 / (a + b) + + +def wk(sr_max: float, _esm_ecm: float) -> float: + """Computes the crack width + + EUROCODE 2 1992-1-1:2004, Eq. (7.8) + + Args: + sr_max (float): the maximum crack length spacing in mm. + _esm_ecm (float): the difference between the mean strain in the + reinforcement under relevant combination of loads, including + the effect of imposed deformations and taking into account + tension stiffening and the mean strain in the concrete + between cracks. + + Returns: + float: crack width in mm. + + Raises: + ValueError: if any of sr_max or esm_ecm is less than zero. + """ + if sr_max < 0: + raise ValueError(f'sr_max={sr_max} cannot be less than zero') + if _esm_ecm < 0: + raise ValueError(f'_esm_scm={_esm_ecm} cannot be less than zero') + + return sr_max * _esm_ecm diff --git a/tests/test_ec2_2004_crack_control.py b/tests/test_ec2_2004_section_7_3_crack_control.py similarity index 83% rename from tests/test_ec2_2004_crack_control.py rename to tests/test_ec2_2004_section_7_3_crack_control.py index 66e8adeb..54b14fc7 100644 --- a/tests/test_ec2_2004_crack_control.py +++ b/tests/test_ec2_2004_section_7_3_crack_control.py @@ -2,7 +2,7 @@ import math import pytest -from structuralcodes.codes.ec2_2004 import _crack_control +from structuralcodes.codes.ec2_2004 import _section_7_3_crack_control @pytest.mark.parametrize( @@ -40,7 +40,9 @@ def test_w_max_returns_expected_values( test_exposure_class, test_load_combination, expected ): """Test that the w_max function returns expected values""" - w_max = _crack_control.w_max(test_exposure_class, test_load_combination) + w_max = _section_7_3_crack_control.w_max( + test_exposure_class, test_load_combination + ) assert w_max == expected @@ -53,7 +55,9 @@ def test_w_max_not_valid_input_raises_valueerror( ): """Test that not valid input returns ValueError""" with pytest.raises(ValueError): - _crack_control.w_max(test_exposure_class, test_load_combination) + _section_7_3_crack_control.w_max( + test_exposure_class, test_load_combination + ) @pytest.mark.parametrize( @@ -71,7 +75,7 @@ def test_w_max_not_valid_input_raises_valueerror( ) def test_k_crack_min_steel_area_returns_expected_values(h, expected): """Test the k_crack_min_steel_area function""" - k = _crack_control.k_crack_min_steel_area(h) + k = _section_7_3_crack_control.k(h) assert math.isclose(k, expected) @@ -79,12 +83,12 @@ def test_k_crack_min_steel_area_raises_valueerror(): """Test that not valid input returns ValueError exeption""" with pytest.raises(ValueError): h = -100 - _crack_control.k_crack_min_steel_area(h) + _section_7_3_crack_control.k(h) def test_kc_crack_min_steel_area_pure_tension_returns_expected_values(): """Test the kc_crack_min_steel_area_pure_tension function""" - assert 1 == _crack_control.kc_crack_min_steel_area_pure_tension() + assert 1 == _section_7_3_crack_control.kc_tension() @pytest.mark.parametrize( @@ -101,7 +105,7 @@ def test_kc_crack_min_steel_area_rectangular_returns_expected_values( h, b, fct_eff, n_ed, expected ): """Test the kc_crack_min_steel_area_rectangular""" - kc = _crack_control.kc_crack_min_steel_area_rectangular( + kc = _section_7_3_crack_control.kc_rect_area( h, b, fct_eff, @@ -114,11 +118,11 @@ def test_kc_crack_min_steel_area_rectangular_raises_valueerror(): """Test the kc_crack_min_steel_area_rectangular raises Value Error for not correct input values for b and h""" with pytest.raises(ValueError): - _crack_control.kc_crack_min_steel_area_rectangular( - h=-100, b=100, fct_eff=100, n_ed=10 + _section_7_3_crack_control.kc_rect_area( + h=-100, b=100, fct_eff=100, N_ed=10 ) - _crack_control.kc_crack_min_steel_area_rectangular( - h=100, b=-100, fct_eff=100, n_ed=10 + _section_7_3_crack_control.kc_rect_area( + h=100, b=-100, fct_eff=100, N_ed=10 ) @@ -133,7 +137,7 @@ def test_kc_crack_min_steel_area_rectangular_raises_valueerror(): ) def test_kc_crack_min_steel_area_flanges(f_cr, a_ct, fct_eff, expected): """Test the kc_crack_min_steel_area_flanges function""" - kc = _crack_control.kc_crack_min_steel_area_flanges(f_cr, a_ct, fct_eff) + kc = _section_7_3_crack_control.kc_flanges_area(f_cr, a_ct, fct_eff) assert math.isclose(kc, expected, rel_tol=0.000001) @@ -145,11 +149,11 @@ def test_kc_crack_min_steel_area_flanges(f_cr, a_ct, fct_eff, expected): (80000, 400, 4, 0.9, 0.75, 540), ], ) -def test_crack_min_steel_area_returns_expected_values( +def test_As_min_returns_expected_values( a_ct, s_steel, fct_eff, k, kc, expected ): - """Test the crack_min_steel_area returns expected values""" - as_min = _crack_control.crack_min_steel_area(a_ct, s_steel, fct_eff, k, kc) + """Test the As_min returns expected values""" + as_min = _section_7_3_crack_control.As_min(a_ct, s_steel, fct_eff, k, kc) assert math.isclose(as_min, expected, rel_tol=10e-6) @@ -164,10 +168,10 @@ def test_crack_min_steel_area_returns_expected_values( (10000, 100, 3, 0.7, 1.1), ], ) -def test_crack_min_steel_area_raises_valueerror(a_ct, s_steel, fct_eff, k, kc): - """Test the crack_min_steel_area raises value error""" +def test_crack_As_min_raises_valueerror(a_ct, s_steel, fct_eff, k, kc): + """Test the As_min raises value error""" with pytest.raises(ValueError): - _crack_control.crack_min_steel_area(a_ct, s_steel, fct_eff, k, kc) + _section_7_3_crack_control.As_min(a_ct, s_steel, fct_eff, k, kc) @pytest.mark.parametrize( @@ -195,7 +199,7 @@ def test_crack_min_steel_area_with_press_tendons_returns_expected_values( expected, ): """Test the crack_min_steel_area returns expected values""" - as_min = _crack_control.crack_min_steel_area_with_prestresed_tendons( + as_min = _section_7_3_crack_control.As_min_p( a_ct, s_steel, fct_eff, k, kc, ap, d_steel, d_press, e, incr_stress ) assert math.isclose(as_min, expected, rel_tol=10e-6) @@ -222,7 +226,7 @@ def test_crack_min_steel_area_with_press_tendons_raise_valueerror( ): """Test the crack_min_steel_area raise ValueError for non valid values""" with pytest.raises(ValueError): - _crack_control.crack_min_steel_area_with_prestresed_tendons( + _section_7_3_crack_control.As_min_p( a_ct, s_steel, fct_eff, k, kc, ap, d_steel, d_press, e, incr_stress ) @@ -249,7 +253,7 @@ def test_crack_min_steel_without_direct_calculation_returns_expected_values( exp_sep, ): """Test the crack_min_steel_area raise ValueError for non valid values""" - phi, sep = _crack_control.crack_min_steel_without_direct_calculation( + phi, sep = _section_7_3_crack_control.As_min_2( wk, s_steel, fct_eff, h_cr, h, d, incr_stress, kc ) assert math.isclose(phi, exp_phi, rel_tol=10e-6) @@ -274,7 +278,7 @@ def test_crack_min_steel_without_direct_calculation_raise_valueerror( ): """Test the crack_min_steel_area raise ValueError for non valid values""" with pytest.raises(ValueError): - _crack_control.crack_min_steel_without_direct_calculation( + _section_7_3_crack_control.As_min_2( wk, s_steel, fct_eff, h_cr, h, d, incr_stress, kc ) @@ -292,7 +296,7 @@ def test_adjusted_bond_length_return_expected_values( ): """Test the adjusted_bond_length_function returns expected values""" assert math.isclose( - _crack_control.adjusted_bond_strength(e, d_press, d_steel), + _section_7_3_crack_control.xi1(e, d_press, d_steel), expected, rel_tol=10e-5, ) @@ -311,7 +315,7 @@ def test_adjusted_bond_length_return_expected_values( def test_adjusted_bond_length_raise_valuerror(e, d_press, d_steel): """Test the adjusted_bond_length_function raises exceptions""" with pytest.raises(ValueError): - _crack_control.adjusted_bond_strength(e, d_press, d_steel) + _section_7_3_crack_control.xi1(e, d_press, d_steel) @pytest.mark.parametrize( @@ -325,7 +329,7 @@ def test_adjusted_bond_length_raise_valuerror(e, d_press, d_steel): def test_hc_eff_concrete_tension_returns_expected_values(h, d, x, expected): """Test the hc_eff_concrete_tension returns expected results""" assert math.isclose( - _crack_control.hc_eff_concrete_tension(h, d, x), + _section_7_3_crack_control.hc_eff(h, d, x), expected, rel_tol=10e-5, ) @@ -344,7 +348,7 @@ def test_hc_eff_concrete_tension_returns_expected_values(h, d, x, expected): def test_hc_eff_concrete_tension_raise_exceptions(h, d, x): """Test hc_eff_concrete tension raises expected exceptions""" with pytest.raises(ValueError): - _crack_control.hc_eff_concrete_tension(h, d, x) + _section_7_3_crack_control.hc_eff(h, d, x) @pytest.mark.parametrize( @@ -356,7 +360,7 @@ def test_hc_eff_concrete_tension_raise_exceptions(h, d, x): def test_alpha_e_returns_expected_values(es, ecm, expected): """Test alpha_e returns expected values""" assert math.isclose( - _crack_control.get_alpha_e(es, ecm), + _section_7_3_crack_control.alpha_e(es, ecm), expected, rel_tol=10e-5, ) @@ -372,7 +376,7 @@ def test_alpha_e_returns_expected_values(es, ecm, expected): def test_alpha_e_raise_exceptions(es, ecm): """Test alpha_e raises exceptions""" with pytest.raises(ValueError): - _crack_control.get_alpha_e(es, ecm) + _section_7_3_crack_control.alpha_e(es, ecm) @pytest.mark.parametrize( @@ -385,7 +389,7 @@ def test_alpha_e_raise_exceptions(es, ecm): def test_rho_p_eff_returns_expected_values(a_s, e1, a_p, ac_eff, expected): """Test rho_p_eff returns expeceted values""" assert math.isclose( - _crack_control.rho_p_eff(a_s, e1, a_p, ac_eff), + _section_7_3_crack_control.rho_p_eff(a_s, e1, a_p, ac_eff), expected, rel_tol=10e-5, ) @@ -403,7 +407,7 @@ def test_rho_p_eff_returns_expected_values(a_s, e1, a_p, ac_eff, expected): def test_rho_p_eff_raise_value_error(a_s, e1, a_p, ac_eff): """Test rho_p_eff raise exceptions""" with pytest.raises(ValueError): - _crack_control.rho_p_eff(a_s, e1, a_p, ac_eff) + _section_7_3_crack_control.rho_p_eff(a_s, e1, a_p, ac_eff) @pytest.mark.parametrize( @@ -415,16 +419,16 @@ def test_rho_p_eff_raise_value_error(a_s, e1, a_p, ac_eff): ) def test_kt_load_duration_returns_expected_values(load_type, expected): """Test kt_load_duration returns expected values""" - assert _crack_control.kt_load_duration(load_type) == expected + assert _section_7_3_crack_control.kt(load_type) == expected def test_kt_load_duration_raise_value_errors(): """Test kt_load_duration raise value errors""" with pytest.raises(TypeError): - _crack_control.kt_load_duration(load_type=123) + _section_7_3_crack_control.kt(load_type=123) with pytest.raises(ValueError): - _crack_control.kt_load_duration(load_type='asdf') + _section_7_3_crack_control.kt(load_type='asdf') @pytest.mark.parametrize( @@ -440,7 +444,9 @@ def test_esm_ecm_returns_expected_values( ): """Test esm_ecm returns the expected values""" assert math.isclose( - _crack_control.esm_ecm(s_steel, alpha_e, rho_p_eff, kt, fct_eff, es), + _section_7_3_crack_control.esm_ecm( + s_steel, alpha_e, rho_p_eff, kt, fct_eff, es + ), expected, abs_tol=10e-5, ) @@ -457,10 +463,19 @@ def test_esm_ecm_returns_expected_values( (250, 5.25, 0.34, 0.2, 2.9, 210000), ], ) -def test_esm_ecm_raises_exception(s_steel, alpha_e, rho_p_eff, kt, fct_eff, es): +def test_esm_ecm_raises_exception( + s_steel, + alpha_e, + rho_p_eff, + kt, + fct_eff, + es, +): """Test esm_ecm raise expected exceptions""" with pytest.raises(ValueError): - _crack_control.esm_ecm(s_steel, alpha_e, rho_p_eff, kt, fct_eff, es) + _section_7_3_crack_control.esm_ecm( + s_steel, alpha_e, rho_p_eff, kt, fct_eff, es + ) @pytest.mark.parametrize( @@ -473,7 +488,7 @@ def test_esm_ecm_raises_exception(s_steel, alpha_e, rho_p_eff, kt, fct_eff, es): def test_s_returns_expected_returns(c, phi, expected): """Test s returns expected results""" assert math.isclose( - _crack_control.s_threshold(c, phi), + _section_7_3_crack_control.w_spacing(c, phi), expected, rel_tol=10e-5, ) @@ -489,7 +504,7 @@ def test_s_returns_expected_returns(c, phi, expected): def test_s_raise_expected_exceptions(c, phi): """Test s raise expected exceptions""" with pytest.raises(ValueError): - _crack_control.s_threshold(c, phi) + _section_7_3_crack_control.w_spacing(c, phi) @pytest.mark.parametrize( @@ -499,7 +514,7 @@ def test_s_raise_expected_exceptions(c, phi): def test_phi_eq_returns_expected_results(n1, n2, phi1, phi2, expected): """Test phi_eq returns expected results""" assert math.isclose( - _crack_control.phi_eq(n1, n2, phi1, phi2), + _section_7_3_crack_control.phi_eq(n1, n2, phi1, phi2), expected, rel_tol=10e-5, ) @@ -519,7 +534,7 @@ def test_phi_eq_returns_expected_results(n1, n2, phi1, phi2, expected): def test_phi_eq_raises_expected_values(n1, n2, phi1, phi2, exception_type): """Test phi_eq raises expected exception""" with pytest.raises(exception_type): - _crack_control.phi_eq(n1, n2, phi1, phi2) + _section_7_3_crack_control.phi_eq(n1, n2, phi1, phi2) @pytest.mark.parametrize( @@ -528,7 +543,7 @@ def test_phi_eq_raises_expected_values(n1, n2, phi1, phi2, exception_type): ) def test_k1_returns_expected_values(bond_type, expected): """Test k1 returns expected values""" - assert _crack_control.k1(bond_type) == expected + assert _section_7_3_crack_control.k1(bond_type) == expected @pytest.mark.parametrize( @@ -538,7 +553,7 @@ def test_k1_returns_expected_values(bond_type, expected): def test_k1_raise_expected_exceptions(bond_type, exception_type): """Test k1 raises expected exceptions""" with pytest.raises(exception_type): - _crack_control.k1(bond_type) + _section_7_3_crack_control.k1(bond_type) @pytest.mark.parametrize( @@ -548,7 +563,7 @@ def test_k1_raise_expected_exceptions(bond_type, exception_type): def test_k2_returns_expected_values(epsilon_r, expected): """Test k2 returns expected values""" assert math.isclose( - _crack_control.k2(epsilon_r), + _section_7_3_crack_control.k2(epsilon_r), expected, rel_tol=10e-5, ) @@ -558,17 +573,17 @@ def test_k2_returns_expected_values(epsilon_r, expected): def test_k2_raises_value_exceptions(epsilon_r): """Test k2 raises expected exceptions""" with pytest.raises(ValueError): - _crack_control.k2(epsilon_r) + _section_7_3_crack_control.k2(epsilon_r) def test_k3_returns_expected_values(): """Test k3 returns the expected values""" - assert _crack_control.k3() == 3.4 + assert _section_7_3_crack_control.k3() == 3.4 def test_k4_returns_expected_values(): """Test k4 returns the expected values""" - assert _crack_control.k4() == 0.425 + assert _section_7_3_crack_control.k4() == 0.425 @pytest.mark.parametrize( @@ -582,7 +597,9 @@ def test_k4_returns_expected_values(): def test_sr_max_close(c, phi, rho_p_eff, k1, k2, k3, k4, expected): """Test sr_max_close returns the expected values""" assert math.isclose( - _crack_control.sr_max_close(c, phi, rho_p_eff, k1, k2, k3, k4), + _section_7_3_crack_control.sr_max_close( + c, phi, rho_p_eff, k1, k2, k3, k4 + ), expected, rel_tol=10e-5, ) @@ -606,7 +623,9 @@ def test_sr_max_close(c, phi, rho_p_eff, k1, k2, k3, k4, expected): def test_sr_max_close_raises_exceptions(c, phi, rho_p_eff, k1, k2, k3, k4): """Test sr_max_close raises the expected value errors""" with pytest.raises(ValueError): - _crack_control.sr_max_close(c, phi, rho_p_eff, k1, k2, k3, k4) + _section_7_3_crack_control.sr_max_close( + c, phi, rho_p_eff, k1, k2, k3, k4 + ) @pytest.mark.parametrize( @@ -620,7 +639,7 @@ def test_sr_max_close_raises_exceptions(c, phi, rho_p_eff, k1, k2, k3, k4): def test_sr_max_far_returns_expected_values(h, x, expected): """Test sr_max_far returns the expected values""" assert math.isclose( - _crack_control.sr_max_far(h, x), expected, rel_tol=10e-5 + _section_7_3_crack_control.sr_max_far(h, x), expected, rel_tol=10e-5 ) @@ -635,7 +654,7 @@ def test_sr_max_far_returns_expected_values(h, x, expected): def test_sr_max_far_raises_exceptions(h, x): """Test sr_max_far raises exceptions""" with pytest.raises(ValueError): - _crack_control.sr_max_far(h, x) + _section_7_3_crack_control.sr_max_far(h, x) @pytest.mark.parametrize( @@ -651,7 +670,7 @@ def test_sr_max_theta_returns_expected_values( ): """Test sr_max_theta returns expeceted values""" assert math.isclose( - _crack_control.sr_max_theta(sr_max_y, sr_max_z, theta), + _section_7_3_crack_control.sr_max_theta(sr_max_y, sr_max_z, theta), expected, rel_tol=10e-5, ) @@ -668,7 +687,7 @@ def test_sr_max_theta_returns_expected_values( def test_sr_max_theta_raises_exceptions(sr_max_y, sr_max_z, theta): """Test sr_max_theta raises value errors""" with pytest.raises(ValueError): - _crack_control.sr_max_theta(sr_max_y, sr_max_z, theta) + _section_7_3_crack_control.sr_max_theta(sr_max_y, sr_max_z, theta) @pytest.mark.parametrize( @@ -681,7 +700,7 @@ def test_sr_max_theta_raises_exceptions(sr_max_y, sr_max_z, theta): def test_wk_returns_expected_values(sr_max, esm_ecm, expected): """Test wk returns expected values""" assert math.isclose( - _crack_control.wk(sr_max, esm_ecm), + _section_7_3_crack_control.wk(sr_max, esm_ecm), expected, rel_tol=10e-5, ) @@ -694,4 +713,4 @@ def test_wk_returns_expected_values(sr_max, esm_ecm, expected): def test_wk_raises_exceptions(sr_max, esm_ecm: float): """Test wk raises value errors""" with pytest.raises(ValueError): - _crack_control.wk(sr_max, esm_ecm) + _section_7_3_crack_control.wk(sr_max, esm_ecm) From 938c0f5807cf6a12a11a0ddc8f004422172a8d4c Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Fri, 13 Jan 2023 14:33:29 +0100 Subject: [PATCH 16/24] removed duplicate file --- .../codes/ec2_2004/_section_7.3.py | 935 ------------------ 1 file changed, 935 deletions(-) delete mode 100644 structuralcodes/codes/ec2_2004/_section_7.3.py diff --git a/structuralcodes/codes/ec2_2004/_section_7.3.py b/structuralcodes/codes/ec2_2004/_section_7.3.py deleted file mode 100644 index 3ad05170..00000000 --- a/structuralcodes/codes/ec2_2004/_section_7.3.py +++ /dev/null @@ -1,935 +0,0 @@ -"""Collection of functions from EUROCODE 1992-1-1:2004 -Chapter 7.3 - Crack control""" -import math -import typing as t - -import numpy as np -import scipy.interpolate - - -def w_max(exposure_class: str, load_combination: str) -> float: - """Computes the recomended value of the maximum crack width. - - EUROCODE 2 1992-1-1:2004, Table (7.1N) - - Args: - exposure_class (str): The exposure class. - Possible values: X0, XC1, XC2, XC3, XC4, XD1, XD2, XS1, XS2, XS3 - load_combination (str): - - f: for frequent load combination - - qp: for quasi-permanent load combination - - Returns: - float: The maximum recommended value for the crack width wmax in mm. - - Raises: - ValueError: if not valid exposure_class or load_combination values. - """ - _load_combination = load_combination.lower().strip() - _exposure_class = exposure_class.upper().strip() - if _load_combination == 'f': - if _exposure_class in ('X0', 'XC1'): - return 0.2 - if _exposure_class in ('XC2', 'XC3', 'XC4'): - return 0.2 - if _load_combination == 'qp': - if _exposure_class in ('X0', 'XC1'): - return 0.4 - if _exposure_class in ( - 'XC2', - 'XC3', - 'XC4', - 'XD1', - 'XD2', - 'XS1', - 'XS2', - 'XS3', - ): - return 0.3 - raise ValueError( - f'{exposure_class} is not a valid value for exposure_class.' - + ' Please enter one of the following: X0, XC1, XC2, XC3, XC4, XD1' - + ',XD2, XS1, XS2, XS3' - ) - raise ValueError( - f'{load_combination} is not a valid value for load_combination.' - + 'Please enter "f" for frequent load combination or "qp" for' - + 'quasi-permanent load combination.' - ) - - -def As_min( - A_ct: float, sigma_s: float, fct_eff: float, _k: float, kc: float -) -> float: - """Computes the minimum area of reinforcing steel within the tensile zone - for control of cracking areas - - EUROCODE 2 1992-1-1:2004, Eq. (7.1) - - Args: - A_ct (float): is the area of concrete within the tensile zone in mm2. - The tensile zone is that parg of the section which is calculated - to be in tension just before the formation of the first crack. - sigma_s (float): is the absolute value of the maximum stress in MPa - permitted in the reinforcement immediately after the formation - of the crack. This may be taken as theyield strength of the - reinforcement, fyk. A lower value may, however, be needed to - satisfy the crack width limits according to the maximum - bar size of spacing (see 7.3.3 (2)). - fct_eff (float): is the mean value of the tensile strength in MPa of - the concrete effective at the time when the cracks may first be - expected to occur: fct,eff=fct or lower (fct(t)), is cracking - is expected earlier than 28 days. - _k (float): is the coefficient which allow for the effect of - non-uniform self-equilibrating stresses, which lead to a - reduction of restraint forces. Use 'k_crack_min_steel_area' - to compute it - k=1 for webs w<=300mm or flanges widths less than 300mm - k=0.65 for webs w>=800mm or flanges with widths greater than 800mm - Intermediate values may be interpolated. - kc (float): is a coefficient which takes account of the stress - distribution within the section immediately prior to cracking and - the change of the lever arm. - - Returns: - float: the minimm area of reinforcing steel within the tensile - zone in mm2. - - Raises: - ValueError: if _k value is not between 0.65 and 1 or kc is not - larger than 0 and lower than 1. - """ - fct_eff = abs(fct_eff) - - if A_ct <= 0: - raise ValueError(f'A_ct={A_ct} must be larger than 0') - if sigma_s < 0: - raise ValueError(f'sigma_s={sigma_s} must be equal or larger than 0') - if _k < 0.65 or _k > 1.0: - raise ValueError(f'_k={_k} must be between 0.65 and 1') - if kc > 1 or kc < 0: - raise ValueError(f'kc={kc} must be lower than 1 and larger than 0') - - return kc * _k * fct_eff * A_ct / sigma_s - - -def k(h: float) -> float: - """Is the coefficient which allow for the effect of - non-uniform self-equilibrating stresses, which lead to a - reduction of restraint forces. - k=1 for webs w<=300mm or flanges widths less than 300mm - k=0.65 for webs w>=800mm or flanges with widths greater than 800mm - - EUROCODE 2 1992-1-1:2004, Eq. (7.1) - - Args: - h (float): flange length or flange width in mm - - Returns: - float: k coefficient value - - Raises: - ValueError: if h is less than 0 - """ - if h < 0: - raise ValueError(f'h={h} cannot be less than 0mm') - if h <= 300: - return 1 - if h < 800: - interpol = scipy.interpolate.interp1d((300, 800), (1, 0.65)) - return (float)(interpol(h)) - return 0.65 - - -def kc_pure_tension() -> float: - """Computes the coefficient which takes account of the stress - distribution within the section immediately prior to cracking and - the change of the lever arm in pure dtension. - - EUROCODE 2 1992-1-1:2004, Eq. (7.1) - - Returns: - float: value of the kc coefficient in pure tension - """ - return 1 - - -def kc_rectangular_area( - h: float, b: float, fct_eff: float, N_ed: float -) -> float: - """Computes the coefficient which takes account of the stress - distribution within the section immediately prior to cracking and - the change of the lever arm for bending+axial combination - in rectangular sections and webs of box sections and T-sections. - - EUROCODE 2 1992-1-1:2004, Eq. (7.2) - - Args: - h (float): heigth of the element in mm - b (float): width of the element in mm - fct_eff (float): is the mean value of the tensile strength in MPa of - the concrete effective at the time when the cracks may first be - expected to occur: fct,eff=fct or lower (fct(t)), is cracking - is expected earlier than 28 days. - N_ed (str): axial force at the serviceability limit state acting on - the part of the cross-section under consideration (compressive - force positive). n_ed should be determined considering the - characteristic values of prestress and axial forces under the - relevant combination of actions - - Returns: - float: value of the kc coefficient - - Raises: - ValueError: is h or b are less than 0 - """ - if h < 0: - raise ValueError(f'h={h} should be larger than 0mm') - if b < 0: - raise ValueError(f'b={b} should be larger than 0mm') - - h_s = min(h, 1000) - _k1 = 1.5 if N_ed >= 0 else 2 * h_s / 3 / h - s_concrete = N_ed * 1000 / b / h - h_ratio = h / h_s - return min(max(0.4 * (1 - s_concrete / _k1 / h_ratio / fct_eff), 0), 1) - - -def kc_flanges_area(f_cr: float, A_ct: float, fct_eff: float) -> float: - """Computes the coefficient which takes account of the stress - distribution within the section immediately prior to cracking and - the change of the lever arm for bending+axial combination - in rectangular sections for flanges of box sections and T-sections. - - EUROCODE 2 1992-1-1:2004, Eq. (7.3) - - Args: - f_cr: is the absolute value in kN of the tensile force within the - flange immediately prior to cracking due to cracking moment - calculated with fct,eff - A_ct (float): is the area of concrete within the tensile zone in mm2. - The tensile zone is that part of the section which is calculated - to be in tension just before the formation of the first crack. - fct_eff (float): is the mean value of the tensile strength in MPa of - the concrete effective at the time when the cracks may first be - expected to occur: fct,eff=fct or lower (fct(t)), is cracking - is expected earlier than 28 days. - - Returns: - float: value of the kc coefficient - - Raises: - ValueError: is A_ct is less than 0mm2 - """ - f_cr = abs(f_cr) - return max(0.9 * f_cr * 1000 / A_ct / fct_eff, 0.5) - - -def xi_1(xi: float, phi_p: float, phi_s: float) -> float: - """Computes the adjusted ratio of bond strength taking into account - the different diameters of prestressing and reinforcing steel. - - EUROCODE 2 1992-1-1:2004, Eq. (7.5) - - Args: - xi (float): ratio of bond strength of prestressing and reinforcing - steel, according to Table 6.2 in 6.8.2 - phi_p (float): largest bar diameter in mm of reinforcing steel. - Equal to 0 if only prestressing is used in control cracking - phi_s (float): equivalent diameter in mm of tendon acoording - to 6.8.2 - - Returns: - float: with the value of the ratio - - Raises: - ValueError: if diameters phi_s or phi_p are lower than 0. - If ratio of bond strength xi is less than 0.15 or larger than 0.8. - """ - - if phi_p <= 0: - raise ValueError(f'phi_p={phi_p} cannot be less than 0') - if phi_s < 0: - raise ValueError(f'phi_s={phi_s} cannot be less than 0') - if xi < 0.15: - raise ValueError(f'The minimum value for xi={xi} is 0.15') - if xi > 0.8: - raise ValueError(f'The maximum value for xi={xi} is 0.8') - - return ((xi * phi_s / phi_p) ** 0.5) if phi_s > 0 else xi**0.5 - - -def hc_eff(h: float, d: float, x: float) -> float: - """Returns the effective height of concrete in tension surrounding - the reinforcement or prestressing tendons. - - EUROCODE 2 1992-1-1:2004, Section (7.3.2-3) - - Args: - h (float): total depth of the element in mm - d (float): distance in mm to the level of the steel centroid - x (float): distance in mm to the zero tensile stress line - - Returns: - float: the effective height in mm - - Raises: - ValueError: if any of h, d or x is lower than zero. - ValueError: if d is greater than h - ValueError: if x is greater than h - """ - if h < 0: - raise ValueError(f'h={h} cannot be less than 0') - if d < 0: - raise ValueError(f'd={d} cannot be less than 0') - if x < 0: - raise ValueError(f'x={x} cannot be less than zero') - if d > h: - raise ValueError(f'd={d} cannot be larger than h={h}') - if x > h: - raise ValueError(f'x={x} cannot be larger than h={h}') - - return min(2.5 * (h - d), (h - x) / 3, h / 2) - - -def As_min_p( - A_ct: float, - sigma_s: float, - fct_eff: float, - _k: float, - kc: float, - Ap: float, - phi_s: float, - phi_p: float, - xi: float, - delta_s: float, -) -> float: - """Computes the minimum area of reinforcing steel within the tensile zone - for control of cracking areas in addition with bonded tendons - - EUROCODE 2 1992-1-1:2004, Eq. (7.1) - - Args: - A_ct (float): is the area of concrete within the tensile zone in mm2. - The tensile zone is that part of the section which is calculated - to be in tension just before the formation of the first crack. - sigma_s (float): is the absolute value of the maximum stress in MPa - permitted in the reinforcement immediately after the formation - of the crack. This may be taken as theyield strength of the - reinforcement, fyk. A lower value may, however, be needed to - satisfy the crack width limits according to the maximum - bar size of spacing (see 7.3.3 (2)). - fct_eff (float): is the mean value of the tensile strength in MPa of - the concrete effective at the time when the cracks may first be - expected to occur: fct,eff=fct or lower (fct(t)), is cracking - is expected earlier than 28 days. - _k (float): is the coefficient which allow for the effect of - non-uniform self-equilibrating stresses, which lead to a - reduction of restraint forces. Use 'k_crack_min_steel_area' - to compute it - k=1 for webs w<=300mm or flanges widths less than 300mm - k=0.65 for webs w>=800mm or flanges with widths greater than 800mm - Intermediate values may be interpolated. - kc (float): is a coefficient which takes account of the stress - distribution within the section immediately prior to cracking and - the change of the lever arm. - Ap (float): is the area in mm2 of pre or post-tensioned tendons - within ac_eff - phi_s (float): largest bar diameter in mm of reinforcing steel. - Equal to 0 if only prestressing is used in control cracking - phi_p (float): equivalent diameter in mm of tendon acoording - to 6.8.2 - chi (float): ratio of bond strength of prestressing and reinforcing - steel, according to Table 6.2 in 6.8.2 - delta_s (float): stress variation in MPa in prestressing tendons - from the state of zero strain of the concrete at the same level - - Returns: - float: the minimm area of reinforcing steel within the tensile - zone in mm2. - - Raises: - ValueError: if _k value is not between 0.65 and 1 or kc is not - larger than 0 and lower than 1. If diameters phi_s or - phi_p are lower than 0. If ratio of bond xi strength e - is less than 0.15 or larger than 0.8. - Is stress variation incr_stress is less than 0. - """ - fct_eff = abs(fct_eff) - - if Ap < 0: - raise ValueError(f'Ap={Ap} cannot be less than 0') - if delta_s < 0: - raise ValueError(f'delta_s={delta_s} cannot be less than 0') - if A_ct <= 0: - raise ValueError(f'A_ct={A_ct} must be larger than 0') - if sigma_s < 0: - raise ValueError(f'sigma_s={sigma_s} must be equal or larger than 0') - if _k < 0.65 or _k > 1.0: - raise ValueError(f'_k={_k} must be between 0.65 and 1') - if kc > 1 or kc < 0: - raise ValueError(f'kc={kc} must be lower than 1 and larger than 0') - - a1 = kc * _k * fct_eff * A_ct - e1 = xi_1(xi, phi_p, phi_s) - a2 = e1 * Ap * delta_s - a = a1 - a2 - - return a / sigma_s - - -def As_min_2( - _wk: float, - sigma_s: float, - fct_eff: float, - h_cr: float, - h: float, - d: float, - delta_s: float = 0, - kc: t.Optional[float] = None, -) -> t.Tuple[float, float]: - """Computes the minimum area of reinforcing steel within the tensile zone - for control of cracking areas - - EUROCODE 2 1992-1-1:2004, Table (7.2N), Table (7.3N) - - Args: - _wk (float): the characteristic crack width value in mm. - sigma_s (float): the steel stress value in MPa under the relevant - combination of actions. - fct_eff (float): is the mean value of the tensile strength in MPa of - the concrete effective at the time when the cracks may first be - expected to occur: fct,eff=fct or lower (fct(t)), is cracking - is expected earlier than 28 days. - h_cr (float): is the depth of the tensile zone immediately prior to - cracking, considering the characteristic values of prestress and - axial forces under the quasi-permanent combination of actions. - h (float): the overall depth of the section in mm. - d (float): is the effective depth to the centroid of the outer layer - of the reinforcement. - delta_s (float, optional): value of prestressed stress in MPa if - applicable - kc (float, optional): is a coefficient which takes account of the - stress distribution within the section immediately prior to - cracking and the change of the lever arm in a bending section. - 'None' for pure tensile uniform axial section. - - Returns: - tuple(float, float): with the value of the maximum bar diameters in mm - in the first position and the maximum bar spacing in mm in the - second position - Raises: - ValueError: if _wk, fct_eff, h_cr, h or d are less than 0 - ValueError: if kc is not between 0 and 1 - ValueError: if combination of wk and stress values are out of scope - """ - if _wk < 0: - raise ValueError(f'_wk={_wk} cannot be less than 0') - if fct_eff < 0: - raise ValueError(f'fct_eff={fct_eff} is less than 0') - if h_cr < 0: - raise ValueError(f'h_cr={h_cr} is less than 0') - if h < 0: - raise ValueError(f'h={h} is less than 0') - if d < 0: - raise ValueError(f'd={d} is less than 0') - if kc is not None and (kc < 0 or kc > 1): - raise ValueError(f'kc={kc} is not between 0 and 1') - - s = sigma_s - delta_s - if s <= 0: - return (0, 0) - - x = (0.4, 0.3, 0.2) - y_phi = (160, 200, 240, 280, 320, 360, 400, 450) - y_spa = (160, 200, 240, 280, 320, 360) - phi_s_v = ( - 40, - 32, - 25, - 32, - 25, - 16, - 20, - 16, - 12, - 16, - 12, - 8, - 12, - 10, - 6, - 10, - 8, - 5, - 8, - 6, - 4, - 6, - 5, - None, - ) - spa_v = ( - 300, - 300, - 200, - 300, - 250, - 150, - 250, - 200, - 100, - 200, - 150, - 50, - 150, - 100, - None, - 100, - 50, - None, - ) - - points_phi = np.array(np.meshgrid(y_phi, x)).T.reshape(-1, 2) - points_spa = np.array(np.meshgrid(y_spa, x)).T.reshape(-1, 2) - xi = (s, _wk) - - phi_star = float( - scipy.interpolate.griddata(points_phi, phi_s_v, xi, method='linear') - ) - if kc is not None: - phi = phi_star * (fct_eff / 2.9) * kc * h_cr / (2 * (h - d)) - else: - phi = phi_star * (fct_eff / 2.9) * h_cr / (8 * (h - d)) - - spa = float( - scipy.interpolate.griddata(points_spa, spa_v, xi, method='linear') - ) - - if math.isnan(phi) or math.isnan(spa): - raise ValueError('Combination of wk or stress values out of scope') - - return phi, spa - - -def alpha_e(Es: float, Ecm: float) -> float: - """Compute the ratio between the steel and mean concrete - elastic modules. - - EUROCODE 2 1992-1-1:2004, Section 7.3.4-2 - - Args: - Es (float): steel elastic modulus in MPa - Ecm (float): concrete mean elastic modulus in MPa - - Returns: - float: ratio between modules - Raise: - ValueError: if any of es or ecm is lower than 0. - """ - if Es < 0: - raise ValueError(f'Es={Es} cannot be less than 0') - if Ecm < 0: - raise ValueError(f'Ecm={Ecm} cannot be less than 0') - - return Es / Ecm - - -def rho_p_eff(As: float, xi1: float, Ap: float, Ac_eff: float) -> float: - """Effective bond ratio between areas - - EUROCODE 2 1992-1-1:2004, Eq. (7.10) - - Args: - As (float): steel area in mm2 - xi1 (float): the adjusted ratio of bond according - to expression (7.5) - Ap (float): the area in mm2 of post-tensioned tendons in ac_eff - Ac_eff (float): effective area of concrete in tension surrounding - the reinforcement or prestressing tendons of depth hc_eff. - - Returns: - float: with the retio between areas - - - Raise: - ValueError: if any of As, xi1, Ap or Ac_eff is less than 0 - """ - if As < 0: - raise ValueError(f'As={As} cannot be less than 0') - if xi1 < 0: - raise ValueError(f'xi1={xi1} cannot be less than 0') - if Ap < 0: - raise ValueError(f'Ap={Ap} cannot be less than 0') - if Ac_eff < 0: - raise ValueError(f'Ac_eff={Ac_eff} cannot be less than 0') - - return (As + xi1**2 * Ap) / Ac_eff - - -def kt(load_type: str) -> float: - """Returns the kt factor dependent on the load duration for - the crack width calculation - - Args: - load_type (str): the load type: - - 'short' for term loading - - 'long' for long term loading - - Returns: - float: with the kt factor - - Raises: - ValueError: if load_type is not 'short' and not 'long' - """ - if not isinstance(load_type, str): - raise TypeError - - load_type = load_type.lower().strip() - if load_type != 'short' and load_type != 'long': - raise ValueError( - f'load_type={load_type} can only have "short" or "long" as a value' - ) - - return 0.6 if load_type == 'short' else 0.4 - - -def esm_ecm( - sigma_s: float, - _alpha_e: float, - _rho_p_eff: float, - _kt: float, - fct_eff: float, - Es: float, -) -> float: - """Returns the strain difference (esm - ecm) needed to compute the crack - width. esm is the mean strain in the reinforcement under the relevant - combination of loads of imposed deformations and taking into account the - effects of tension stiffening. Only the additional tensile strain beyond - the state of zero strain of the concrete is considered. ecm is the mean - strain in the concrete between the cracks. - - EUROCODE 2 1992-1-1:2004, Eq. (7.9) - - Args: - sigma_s (float): is the stress in MPa in the tension reinforcement - assuming a cracked section. FOr pretensioned members, s_steel may - be replaced by increment of s_steel stress variation in - prestressing tendons from the state of zero strain of the - concrete at the same level. - _alpha_e (float): is the ratio Es/Ecm - _rho_p_eff (float): effective bond ratio between areas given by the - Eq. (7.10) - _kt (float): is a factor dependent on the load duration - fct_eff (float): is the mean value of the tensile strength in MPa - of the concrete effectvie at the time when the cracks may - first be expected to occur: fct_eff=fctm or fctm(t) if - crack is expected earlier than 28 days. - Es: steel elastic mudulus in MPa - - Returns: - float: the strain difference between concrete and steel - - Raises: - ValueError: if any sigma_s, alpha_e, rho_p_eff, fct_eff or Es is less - than 0. - ValueError: if kt is not 0.6 and not 0.4 - """ - if sigma_s < 0: - raise ValueError(f'sigma_s={sigma_s} cannot be less than 0') - if _alpha_e < 0: - raise ValueError(f'_alpha_e={_alpha_e} cannot be less than 0') - if _rho_p_eff < 0: - raise ValueError(f'_rho_p_eff={_rho_p_eff} cannot be less than 0') - if fct_eff < 0: - raise ValueError(f'fct_eff={fct_eff} cannot be less than 0') - if Es < 0: - raise ValueError(f'Es={Es} cannot be less than 0') - if _kt != 0.6 and _kt != 0.4: - raise ValueError(f'_kt={_kt} can only take as values 0.4 and 0.6') - - min_val = 0.6 * sigma_s / Es - - a = 1 + _alpha_e * _rho_p_eff - b = _kt * fct_eff / _rho_p_eff * a - c = (sigma_s - b) / Es - - return max(c, min_val) - - -def w_spacing(c: float, phi: float) -> float: - """Computes the distance threshold from which the - maximum crack spacing is constant. - - EUROCODE 2 1992-1-1:2004, Sect. (7.3.4-3) - - Args: - c (float): cover of the longitudinal reinforcement in mm - phi (float): is the bar diameter in mm. Where mixed bar diameters - used, then it should be replaced for an equivalente bar diameter. - - Returns: - float: threshold distance in mm - - Raises: - ValueError: if any of c or phi is less than 0. - """ - if c < 0: - raise ValueError(f'c={c} cannot be less than 0') - if phi < 0: - raise ValueError(f'phi={phi} cannot be less than 0') - - return 5 * (c + phi / 2) - - -def phi_eq(n1: int, n2: int, phi1: float, phi2: float) -> float: - """Computes the equivalent diameter. For a section with n1 bars of - diameter phi1 and n2 bars of diameter phi2 - - EUROCODE 2 1992-1-1:2004, Sect. (7.12) - - Args: - n1 (int): number of bars with diameter phi1 - n2 (int): number of bars with diameter phi2 - phi1 (float): diameter of n1 bars in mm - phi2 (float): diamater of n2 bars in mm - - Returns: - float: the equivalent diameter in mm - - Raises: - ValueError: if any of n1 or n2 is less than 0 - ValueError: if any of phi1 or phi2 is less than 0 - TypeError: if any of n1 or n2 is not an integer - """ - if n1 < 0: - raise ValueError(f'n1={n1} cannot be less than 0') - if not isinstance(n1, int): - raise TypeError(f'n1={n1} needs to be an integer value') - if n2 < 0: - raise ValueError(f'n2={n2} cannot be less than 0') - if not isinstance(n2, int): - raise TypeError(f'n2={n2} needs to be an integer value') - if phi1 < 0: - raise ValueError(f'phi1={phi1} cannot be less than 0') - if phi2 < 0: - raise ValueError(f'phi2={phi2} cannot be less than 0') - - a = n1 * phi1**2 + n2 * phi2**2 - b = n1 * phi1 + n2 * phi2 - return a / b - - -def k1(bond_type: str) -> float: - """Get the k1 coefficient which takes account of the bond properties - of the bounded reinforcement - - EUROCODE 2 1992-1-1:2004, Eq. (7.11-k1) - - Args: - bond_type (str): the bond property of the reinforcement. - Possible values: - - 'bond': for high bond bars - - 'plane': for bars with an effectively plain surface (e.g. - prestressing tendons) - - Returns: - (float): value of the k1 coefficient - - Raises: - ValueError: if bond_type is neither 'bond' nor 'plane' - TypeError: if bond_type is not an str - """ - if not isinstance(bond_type, str): - raise TypeError(f'bond_type={bond_type} is not an str') - - bond_type = bond_type.lower().strip() - if bond_type != 'bond' and bond_type != 'plane': - raise ValueError( - f'bond_type={bond_type} can only have "bond" or "plane" as values' - ) - - return 0.8 if bond_type == 'bond' else 1.6 - - -def k2(epsilon_r: float) -> float: - """Computes a coefficient which takes into account of the - distribution of strain: - - EUROCODE 2 1992-1-1:2004, Eq. (7.13) - - Args: - epsilon_r (float): ratio epsilon_2/epsilon_1 where epsilon_1 is - thre greater and epsilon_2 is the lesser strain at the boundaries - of the section considererd, assessed on the basis of a cracked - section. epsilon_r=0 for bending and epsilon_r=1 for pure tension. - - Returns: - float: the k2 coefficient value. - - Raises: - ValueError: if epsilon_r is not between 0 and 1. - """ - if epsilon_r < 0 or epsilon_r > 1: - raise ValueError(f'epsilon_r={epsilon_r} must be between 0 and 1') - - return (1 + epsilon_r) / 2 - - -def k3(): - """Returns the k3 coefficient for computing sr_max - - Returns: - float: value for the coefficient - """ - return 3.4 - - -def k4(): - """Returns the k4 coefficient for computing sr_max - - Returns: - float: value for the coefficient - """ - return 0.425 - - -def sr_max_close( - c: float, - phi: float, - _rho_p_eff: float, - _k1: float, - _k2: float, - _k3: float, - _k4: float, -) -> float: - """Computes the maximum crack spacing in cases where bonded reinforcement - is fixed at reasonably close centres within the tension zone - (w_spacing<=5(c+phi/2)). - - EUROCODE 2 1992-1-1:2004, Eq. (7.11) - - Args: - c (float): is the cover in mm of the longitudinal reinforcement - phi (float): is the bar diameter in mm. Where mixed bar diameters - used, then it should be replaced for an equivalente bar diameter. - _rho_p_eff (float): effective bond ratio between areas given by the - Eq. (7.10) - _k1 (float): coefficient that takes into account the bound properties - of the bonded reinforcement - _k2 (float): coefficient that takes into account the distribution of - of the strain - _k3 (float): coefficient from the National Annex - _k4 (float): coefficient from the National Annex - - Returns: - float: the maximum crack spaing in mm. - - Raises: - ValueError: if one or more of c, phi, rho_p_eff, k3 or k4 - is lower than zero. - ValueError: if k1 is not 0.8 or 1.6 - ValueError: if k2 is not between 0.5 and 1.0 - """ - if c < 0: - raise ValueError(f'c={c} cannot be less than zero') - if phi < 0: - raise ValueError(f'phi={phi} cannot be less than zero') - if _rho_p_eff < 0: - raise ValueError(f'_rho_p_eff={_rho_p_eff} cannot be less than zero') - if _k3 < 0: - raise ValueError(f'_k3={_k3} cannot be less than zero') - if _k4 < 0: - raise ValueError(f'_k4={_k4} cannot be less than zero') - if _k1 != 0.8 and _k1 != 1.6: - raise ValueError(f'_k1={_k1} can only take as values 0.8 and 1.6') - if _k2 < 0.5 or _k2 > 1: - raise ValueError(f'_k2={_k2} is not between 0.5 and 1.0') - - return _k3 * c + _k1 * _k2 * _k4 * phi / _rho_p_eff - - -def sr_max_far(h: float, x: float) -> float: - """Computes the maximum crack spacing in cases where bonded reinforcement - is fixed at reasonably close centres within the tension zone - (w_spacing>5(c+phi/2)). - - EUROCODE 2 1992-1-1:2004, Eq. (7.14) - - Args: - h (float): total depth of the beam in mm - x (float): distance to non tension area of the element mm - - Returns: - float: maximum crack spacing in mm - - Raises: - ValueError: if one of h or x is less than zero. - ValueError: x is greater than h. - """ - if x < 0: - raise ValueError(f'x={x} cannot be less than zero') - if h < 0: - raise ValueError(f'h={h} cannot be less than zero') - if x > h: - raise ValueError(f'x={x} cannot be larger than h={h}') - - return 1.3 * (h - x) - - -def sr_max_theta(sr_max_y: float, sr_max_z: float, theta: float) -> float: - """Computes the crack spacing sr_max when there is an angle - between the angle of principal stress and the direction - of the reinforcement, for members in two orthogonal directions, - that is significant (> 15º). - - EUROCODE 2 1992-1-1:2004, Eq. (7.15) - - Args: - sr_max_y (float): crack spacing in mm in the y-direction. - sr_max_z (float): crack spacing in mm in the z-direction. - theta (float): angle in radians between the reinforcement in the - y-direction and the direction of the principal tensile stress. - - Returns: - float: the crack spacing in mm. - - Raises: - ValueError: if sr_max_y or sr_max_z is negative. - ValueError: if theta is not between 0 and pi/2 - """ - if sr_max_y < 0: - raise ValueError(f'sr_max_y={sr_max_y} cannot be less than zero') - if sr_max_z < 0: - raise ValueError(f'sr_max_z={sr_max_z} cannot be less than zero') - - a = math.cos(theta) / sr_max_y - b = math.sin(theta) / sr_max_z - return 1 / (a + b) - - -def wk(sr_max: float, _esm_ecm: float) -> float: - """Computes the crack width - - EUROCODE 2 1992-1-1:2004, Eq. (7.8) - - Args: - sr_max (float): the maximum crack length spacing in mm. - _esm_ecm (float): the difference between the mean strain in the - reinforcement under relevant combination of loads, including - the effect of imposed deformations and taking into account - tension stiffening and the mean strain in the concrete - between cracks. - - Returns: - float: crack width in mm. - - Raises: - ValueError: if any of sr_max or _esm_ecm is less than zero. - """ - if sr_max < 0: - raise ValueError(f'sr_max={sr_max} cannot be less than zero') - if _esm_ecm < 0: - raise ValueError(f'_esm_scm={_esm_ecm} cannot be less than zero') - - return sr_max * _esm_ecm From 6ba6dc905ffbe76be7c2d9b0e948e3c4c742c285 Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Fri, 13 Jan 2023 14:36:25 +0100 Subject: [PATCH 17/24] removed testing file --- prueba.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 prueba.py diff --git a/prueba.py b/prueba.py deleted file mode 100644 index e69de29b..00000000 From a9c926338152606c8e0a57e88127da84d7915e2a Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Mon, 16 Jan 2023 08:29:41 +0100 Subject: [PATCH 18/24] test renaming and docstring corrections --- .../ec2_2004/_section_7_3_crack_control.py | 4 +- ...test_ec2_2004_section_7_3_crack_control.py | 60 +++++++++---------- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py b/structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py index 1de528de..529ef679 100644 --- a/structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py +++ b/structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py @@ -392,7 +392,7 @@ def As_min_2( EUROCODE 2 1992-1-1:2004, Table (7.2N), Table (7.3N) Args: - wk (float): the characteristic crack width value in mm. + _wk (float): the characteristic crack width value in mm. sigma_s (float): the steel stress value in MPa under the relevant combination of actions. fct_eff (float): is the mean value of the tensile strength in MPa of @@ -417,7 +417,7 @@ def As_min_2( in the first position and the maximum bar spacing in mm in the second position Raises: - ValueError: if wk, fct_eff, h_cr, h or d are less than 0 + ValueError: if _wk, fct_eff, h_cr, h or d are less than 0 ValueError: if kc is not between 0 and 1 ValueError: if combination of wk and stress values are out of scope """ diff --git a/tests/test_ec2_2004_section_7_3_crack_control.py b/tests/test_ec2_2004_section_7_3_crack_control.py index 54b14fc7..8dc120f8 100644 --- a/tests/test_ec2_2004_section_7_3_crack_control.py +++ b/tests/test_ec2_2004_section_7_3_crack_control.py @@ -73,21 +73,21 @@ def test_w_max_not_valid_input_raises_valueerror( (700, 0.72), ], ) -def test_k_crack_min_steel_area_returns_expected_values(h, expected): - """Test the k_crack_min_steel_area function""" +def test_k_returns_expected_values(h, expected): + """Test the k function""" k = _section_7_3_crack_control.k(h) assert math.isclose(k, expected) -def test_k_crack_min_steel_area_raises_valueerror(): +def test_k_raises_valueerror(): """Test that not valid input returns ValueError exeption""" with pytest.raises(ValueError): h = -100 _section_7_3_crack_control.k(h) -def test_kc_crack_min_steel_area_pure_tension_returns_expected_values(): - """Test the kc_crack_min_steel_area_pure_tension function""" +def test_kc_tension_returns_expected_values(): + """Test the kc_tension function""" assert 1 == _section_7_3_crack_control.kc_tension() @@ -101,10 +101,8 @@ def test_kc_crack_min_steel_area_pure_tension_returns_expected_values(): (200, 50, 5, 80, 0), ], ) -def test_kc_crack_min_steel_area_rectangular_returns_expected_values( - h, b, fct_eff, n_ed, expected -): - """Test the kc_crack_min_steel_area_rectangular""" +def test_kc_rect_area_returns_expected_values(h, b, fct_eff, n_ed, expected): + """Test the kc_rect_area""" kc = _section_7_3_crack_control.kc_rect_area( h, b, @@ -114,8 +112,8 @@ def test_kc_crack_min_steel_area_rectangular_returns_expected_values( assert math.isclose(kc, expected, rel_tol=0.000001) -def test_kc_crack_min_steel_area_rectangular_raises_valueerror(): - """Test the kc_crack_min_steel_area_rectangular raises Value +def test_kc_rect_area_raises_valueerror(): + """Test the kc_rect_area raises Value Error for not correct input values for b and h""" with pytest.raises(ValueError): _section_7_3_crack_control.kc_rect_area( @@ -135,8 +133,8 @@ def test_kc_crack_min_steel_area_rectangular_raises_valueerror(): (55, 50000, 4, 0.5), ], ) -def test_kc_crack_min_steel_area_flanges(f_cr, a_ct, fct_eff, expected): - """Test the kc_crack_min_steel_area_flanges function""" +def test_kc_flanges_area(f_cr, a_ct, fct_eff, expected): + """Test the kc_flanges function""" kc = _section_7_3_crack_control.kc_flanges_area(f_cr, a_ct, fct_eff) assert math.isclose(kc, expected, rel_tol=0.000001) @@ -185,7 +183,7 @@ def test_crack_As_min_raises_valueerror(a_ct, s_steel, fct_eff, k, kc): (50000, 500, 4, 1, 1, 1000, 0, 20, 0.8, 20, 364.223), ], ) -def test_crack_min_steel_area_with_press_tendons_returns_expected_values( +def test_As_min_p_returns_expected_values( a_ct, s_steel, fct_eff, @@ -198,7 +196,7 @@ def test_crack_min_steel_area_with_press_tendons_returns_expected_values( incr_stress, expected, ): - """Test the crack_min_steel_area returns expected values""" + """Test the As_min_p returns expected values""" as_min = _section_7_3_crack_control.As_min_p( a_ct, s_steel, fct_eff, k, kc, ap, d_steel, d_press, e, incr_stress ) @@ -221,10 +219,10 @@ def test_crack_min_steel_area_with_press_tendons_returns_expected_values( (80000, 400, 4, 0.9, 0.75, 500, 10, 10, 0.9, 10), ], ) -def test_crack_min_steel_area_with_press_tendons_raise_valueerror( +def test_As_min_p_raise_valueerror( a_ct, s_steel, fct_eff, k, kc, ap, d_steel, d_press, e, incr_stress ): - """Test the crack_min_steel_area raise ValueError for non valid values""" + """Test the As_min_p raise ValueError for non valid values""" with pytest.raises(ValueError): _section_7_3_crack_control.As_min_p( a_ct, s_steel, fct_eff, k, kc, ap, d_steel, d_press, e, incr_stress @@ -240,7 +238,7 @@ def test_crack_min_steel_area_with_press_tendons_raise_valueerror( (0.35, 360, 2.9, 200, 400, 360, 40, None, 6.875, 125), ], ) -def test_crack_min_steel_without_direct_calculation_returns_expected_values( +def test_As_min_2_returns_expected_values( wk, s_steel, fct_eff, @@ -252,7 +250,7 @@ def test_crack_min_steel_without_direct_calculation_returns_expected_values( exp_phi, exp_sep, ): - """Test the crack_min_steel_area raise ValueError for non valid values""" + """Test the As_min_2 raise ValueError for non valid values""" phi, sep = _section_7_3_crack_control.As_min_2( wk, s_steel, fct_eff, h_cr, h, d, incr_stress, kc ) @@ -273,10 +271,10 @@ def test_crack_min_steel_without_direct_calculation_returns_expected_values( (0.5, 200, 2.9, 200, 400, 360, 0, 0.4), ], ) -def test_crack_min_steel_without_direct_calculation_raise_valueerror( +def test_As_min_2_raise_valueerror( wk, s_steel, fct_eff, h_cr, h, d, incr_stress, kc ): - """Test the crack_min_steel_area raise ValueError for non valid values""" + """Test the As_min_2 raise ValueError for non valid values""" with pytest.raises(ValueError): _section_7_3_crack_control.As_min_2( wk, s_steel, fct_eff, h_cr, h, d, incr_stress, kc @@ -291,10 +289,8 @@ def test_crack_min_steel_without_direct_calculation_raise_valueerror( (0.5, 10, 10, 0.707107), ], ) -def test_adjusted_bond_length_return_expected_values( - e, d_press, d_steel, expected -): - """Test the adjusted_bond_length_function returns expected values""" +def test_xi1_values(e, d_press, d_steel, expected): + """Test xi1 returns expected values""" assert math.isclose( _section_7_3_crack_control.xi1(e, d_press, d_steel), expected, @@ -312,7 +308,7 @@ def test_adjusted_bond_length_return_expected_values( (0.6, 10, -10), ], ) -def test_adjusted_bond_length_raise_valuerror(e, d_press, d_steel): +def test_xi1_raise_valuerror(e, d_press, d_steel): """Test the adjusted_bond_length_function raises exceptions""" with pytest.raises(ValueError): _section_7_3_crack_control.xi1(e, d_press, d_steel) @@ -326,7 +322,7 @@ def test_adjusted_bond_length_raise_valuerror(e, d_press, d_steel): (550, 150, 150, 133.33333), ], ) -def test_hc_eff_concrete_tension_returns_expected_values(h, d, x, expected): +def test_hc_eff_returns_expected_values(h, d, x, expected): """Test the hc_eff_concrete_tension returns expected results""" assert math.isclose( _section_7_3_crack_control.hc_eff(h, d, x), @@ -345,7 +341,7 @@ def test_hc_eff_concrete_tension_returns_expected_values(h, d, x, expected): (400, 200, 450), ], ) -def test_hc_eff_concrete_tension_raise_exceptions(h, d, x): +def test_hc_eff_raise_exceptions(h, d, x): """Test hc_eff_concrete tension raises expected exceptions""" with pytest.raises(ValueError): _section_7_3_crack_control.hc_eff(h, d, x) @@ -417,13 +413,13 @@ def test_rho_p_eff_raise_value_error(a_s, e1, a_p, ac_eff): ('long', 0.4), ], ) -def test_kt_load_duration_returns_expected_values(load_type, expected): - """Test kt_load_duration returns expected values""" +def test_kt_returns_expected_values(load_type, expected): + """Test kt returns expected values""" assert _section_7_3_crack_control.kt(load_type) == expected -def test_kt_load_duration_raise_value_errors(): - """Test kt_load_duration raise value errors""" +def test_kt_raise_value_errors(): + """Test kt raise value errors""" with pytest.raises(TypeError): _section_7_3_crack_control.kt(load_type=123) From 4fd8b7e9ac5dc274cb86f8fcb658679ac3df4312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gonz=C3=A1lez=20de=20la=20Morena?= Date: Thu, 9 Mar 2023 09:53:18 +0100 Subject: [PATCH 19/24] 230309 requested changes applied --- requirements.txt | 4 +- .../ec2_2004/_section_7_3_crack_control.py | 61 +++--- structuralcodes/material/__init__.py | 0 structuralcodes/material/concrete/__init__.py | 58 ------ .../material/concrete/_concrete.py | 45 ----- .../material/concrete/_concreteMC2010.py | 189 ------------------ ...test_ec2_2004_section_7_3_crack_control.py | 5 +- 7 files changed, 39 insertions(+), 323 deletions(-) delete mode 100644 structuralcodes/material/__init__.py delete mode 100644 structuralcodes/material/concrete/__init__.py delete mode 100644 structuralcodes/material/concrete/_concrete.py delete mode 100644 structuralcodes/material/concrete/_concreteMC2010.py diff --git a/requirements.txt b/requirements.txt index 1de8c504..d68dafa2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -numpy==1.23.5 -scipy==1.9.3 \ No newline at end of file +numpy>=1.20.0 +scipy>=1.6.0 \ No newline at end of file diff --git a/structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py b/structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py index 529ef679..e530a885 100644 --- a/structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py +++ b/structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py @@ -8,7 +8,7 @@ def w_max(exposure_class: str, load_combination: str) -> float: - """Computes the recomended value of the maximum crack width. + """Computes the recommended value of the maximum crack width. EUROCODE 2 1992-1-1:2004, Table (7.1N) @@ -68,7 +68,7 @@ def As_min( Args: A_ct (float): is the area of concrete within the tensile zone in mm2. - The tensile zone is that parg of the section which is calculated + The tensile zone is that part of the section which is calculated to be in tension just before the formation of the first crack. sigma_s (float): is the absolute value of the maximum stress in MPa permitted in the reinforcement immediately after the formation @@ -82,8 +82,7 @@ def As_min( is expected earlier than 28 days. _k (float): is the coefficient which allow for the effect of non-uniform self-equilibrating stresses, which lead to a - reduction of restraint forces. Use 'k_crack_min_steel_area' - to compute it + reduction of restraint forces. k=1 for webs w<=300mm or flanges widths less than 300mm k=0.65 for webs w>=800mm or flanges with widths greater than 800mm Intermediate values may be interpolated. @@ -137,14 +136,14 @@ def k(h: float) -> float: return 1 if h < 800: interpol = scipy.interpolate.interp1d((300, 800), (1, 0.65)) - return (float)(interpol(h)) + return interpol(h) return 0.65 def kc_tension() -> float: """Computes the coefficient which takes account of the stress distribution within the section immediately prior to cracking and - the change of the lever arm in pure dtension. + the change of the lever arm in pure tension. EUROCODE 2 1992-1-1:2004, Eq. (7.1) @@ -313,7 +312,7 @@ def As_min_p( to be in tension just before the formation of the first crack. sigma_s (float): is the absolute value of the maximum stress in MPa permitted in the reinforcement immediately after the formation - of the crack. This may be taken as theyield strength of the + of the crack. This may be taken as the yield strength of the reinforcement, fyk. A lower value may, however, be needed to satisfy the crack width limits according to the maximum bar size of spacing (see 7.3.3 (2)). @@ -323,8 +322,7 @@ def As_min_p( is expected earlier than 28 days. _k (float): is the coefficient which allow for the effect of non-uniform self-equilibrating stresses, which lead to a - reduction of restraint forces. Use 'k_crack_min_steel_area' - to compute it + reduction of restraint forces. k=1 for webs w<=300mm or flanges widths less than 300mm k=0.65 for webs w>=800mm or flanges with widths greater than 800mm Intermediate values may be interpolated. @@ -337,7 +335,7 @@ def As_min_p( Equal to 0 if only prestressing is used in control cracking phi_p (float): equivalent diameter in mm of tendon acoording to 6.8.2 - chi (float): ratio of bond strength of prestressing and reinforcing + xi (float): ratio of bond strength of prestressing and reinforcing steel, according to Table 6.2 in 6.8.2 delta_s (float): stress variation in MPa in prestressing tendons from the state of zero strain of the concrete at the same level @@ -611,7 +609,7 @@ def esm_ecm( Args: sigma_s (float): is the stress in MPa in the tension reinforcement - assuming a cracked section. FOr pretensioned members, s_steel may + assuming a cracked section. For pretensioned members, s_steel may be replaced by increment of s_steel stress variation in prestressing tendons from the state of zero strain of the concrete at the same level. @@ -620,10 +618,10 @@ def esm_ecm( Eq. (7.10) _kt (float): is a factor dependent on the load duration fct_eff (float): is the mean value of the tensile strength in MPa - of the concrete effectvie at the time when the cracks may + of the concrete effective at the time when the cracks may first be expected to occur: fct_eff=fctm or fctm(t) if crack is expected earlier than 28 days. - Es: steel elastic mudulus in MPa + Es: steel elastic modulus in MPa Returns: float: the strain difference between concrete and steel @@ -664,7 +662,7 @@ def w_spacing(c: float, phi: float) -> float: Args: c (float): cover of the longitudinal reinforcement in mm phi (float): is the bar diameter in mm. Where mixed bar diameters - used, then it should be replaced for an equivalente bar diameter. + used, then it should be replaced for an equivalent bar diameter. Returns: float: threshold distance in mm @@ -728,23 +726,23 @@ def k1(bond_type: str) -> float: bond_type (str): the bond property of the reinforcement. Possible values: - 'bond': for high bond bars - - 'plane': for bars with an effectively plain surface (e.g. + - 'plain': for bars with an effectively plain surface (e.g. prestressing tendons) Returns: (float): value of the k1 coefficient Raises: - ValueError: if bond_type is neither 'bond' nor 'plane' + ValueError: if bond_type is neither 'bond' nor 'plain' TypeError: if bond_type is not an str """ if not isinstance(bond_type, str): raise TypeError(f'bond_type={bond_type} is not an str') bond_type = bond_type.lower().strip() - if bond_type != 'bond' and bond_type != 'plane': + if bond_type not in ('bond', 'plain'): raise ValueError( - f'bond_type={bond_type} can only have "bond" or "plane" as values' + f'bond_type={bond_type} can only have "bond" or "plain" as values' ) return 0.8 if bond_type == 'bond' else 1.6 @@ -758,8 +756,8 @@ def k2(epsilon_r: float) -> float: Args: epsilon_r (float): ratio epsilon_2/epsilon_1 where epsilon_1 is - thre greater and epsilon_2 is the lesser strain at the boundaries - of the section considererd, assessed on the basis of a cracked + the greater and epsilon_2 is the lesser strain at the boundaries + of the section considered, assessed on the basis of a cracked section. epsilon_r=0 for bending and epsilon_r=1 for pure tension. Returns: @@ -798,8 +796,8 @@ def sr_max_close( _rho_p_eff: float, _k1: float, _k2: float, - _k3: float, - _k4: float, + _k3: t.Optional[float] = None, + _k4: t.Optional[float] = None, ) -> float: """Computes the maximum crack spacing in cases where bonded reinforcement is fixed at reasonably close centres within the tension zone @@ -810,15 +808,17 @@ def sr_max_close( Args: c (float): is the cover in mm of the longitudinal reinforcement phi (float): is the bar diameter in mm. Where mixed bar diameters - used, then it should be replaced for an equivalente bar diameter. + used, then it should be replaced for an equivalent bar diameter. _rho_p_eff (float): effective bond ratio between areas given by the Eq. (7.10) _k1 (float): coefficient that takes into account the bound properties of the bonded reinforcement _k2 (float): coefficient that takes into account the distribution of of the strain - _k3 (float): coefficient from the National Annex - _k4 (float): coefficient from the National Annex + _k3 (float, optional): coefficient from the National Annex. + If not specified then _k3=3.4 + _k4 (float): coefficient from the National Annex. + If not specified then _k4=0.425 Returns: float: the maximum crack spaing in mm. @@ -829,6 +829,11 @@ def sr_max_close( ValueError: if _k1 is not 0.8 or 1.6 ValueError: if _k2 is not between 0.5 and 1.0 """ + if _k3 is None: + _k3 = k3() + if _k4 is None: + _k4 = k4() + if c < 0: raise ValueError(f'c={c} cannot be less than zero') if phi < 0: @@ -839,7 +844,7 @@ def sr_max_close( raise ValueError(f'_k3={_k3} cannot be less than zero') if _k4 < 0: raise ValueError(f'_k4={_k4} cannot be less than zero') - if _k1 != 0.8 and _k1 != 1.6: + if _k1 not in (0.8, 1.6): raise ValueError(f'_k1={_k1} can only take as values 0.8 and 1.6') if _k2 < 0.5 or _k2 > 1: raise ValueError(f'_k2={_k2} is not between 0.5 and 1.0') @@ -849,8 +854,8 @@ def sr_max_close( def sr_max_far(h: float, x: float) -> float: """Computes the maximum crack spacing in cases where bonded reinforcement - is fixed at reasonably close centres within the tension zone - (w_spacing>5(c+phi/2)). + exceeds (w_spacing>5(c+phi/2)) or where there is no bonded reinforcement + at all. EUROCODE 2 1992-1-1:2004, Eq. (7.14) diff --git a/structuralcodes/material/__init__.py b/structuralcodes/material/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/structuralcodes/material/concrete/__init__.py b/structuralcodes/material/concrete/__init__.py deleted file mode 100644 index ca314baf..00000000 --- a/structuralcodes/material/concrete/__init__.py +++ /dev/null @@ -1,58 +0,0 @@ -"""Concrete material""" -import typing as t -from structuralcodes.codes import _use_design_code -from ._concrete import Concrete -from ._concreteMC2010 import ConcreteMC2010 - -__all__ = [ - 'create_concrete', - 'Concrete', - 'ConcreteMC2010', -] - - -def create_concrete( - fck: float, - name: t.Optional[str] = None, - density: float = 2400.0, - existing: bool = False, - design_code: t.Optional[str] = None, -) -> t.Optional[Concrete]: - """ - A factory function to create the correct type of concrete based on the - desired design code. - - Args: - fck (float): Characteristic strength of concrete in MPa. - (if existing it is intended as the mean strength) - - Keyword Args: - density (float): Density of Concrete in kg/m3 (default: 2400) - existing (bool): Boolean indicating if the concrete is of an - existing structure (default: False) - deisgn_code (str): Optional string (default: None) indicating the - desired standard. If None (default) the globally used design - standard will be adopted. Otherwise the design standard specified - will be used for the instance of the material. - Currently available codes: 'mc2010' - - Raises: - ValueError: if the design code is not valid or does not cover - concrete as a material. - """ - # Get the code from the global variable - _code = _use_design_code(design_code) - - # Check if the code is a proper concrete code - code = _code if 'concrete' in _code.__materials__ else None - if code is None: - raise ValueError( - 'The design code is not set, either use ' - 'structuralcodes.code.set_designcode, or provide a valid ' - 'string in the function.' - ) - - # Create the proper concrete object - if code.__title__ == 'fib Model Code 2010': - return ConcreteMC2010(fck, name, density, existing) - return None diff --git a/structuralcodes/material/concrete/_concrete.py b/structuralcodes/material/concrete/_concrete.py deleted file mode 100644 index 19ac2048..00000000 --- a/structuralcodes/material/concrete/_concrete.py +++ /dev/null @@ -1,45 +0,0 @@ -"""Core implementation of the concrete material""" -import abc -import typing as t -from structuralcodes.core.base import Material - - -class Concrete(Material): - """The abstract concrete material.""" - - _fck: float - _existing: bool - - def __init__( - self, - fck: float, - name: t.Optional[str] = None, - density: float = 2400, - existing: t.Optional[bool] = False, - ) -> None: - """Initializes an abstract concrete material""" - name = name if name is not None else "Concrete" - super().__init__(density=density, name=name) - - self._fck = abs(fck) - if existing: - raise NotImplementedError( - 'Existing concrete feature not implemented yet' - ) - self._existing = existing - - @property - def fck(self) -> float: - """Returns fck in MPa""" - return self._fck - - @fck.setter - def fck(self, fck: float) -> None: - """Setter for fck (in MPa)""" - self._fck = abs(fck) - self._reset_attributes() - - @abc.abstractmethod - def _reset_attributes(self): - """Each concrete should define its own _reset_attributes method - This is because fck setting, reset the object arguments""" diff --git a/structuralcodes/material/concrete/_concreteMC2010.py b/structuralcodes/material/concrete/_concreteMC2010.py deleted file mode 100644 index faf0ad97..00000000 --- a/structuralcodes/material/concrete/_concreteMC2010.py +++ /dev/null @@ -1,189 +0,0 @@ -"""The concrete class for Model Code 2020 Concrete Material""" -import typing as t -import warnings - -from structuralcodes.codes import mc2010 -from ._concrete import Concrete - - -class ConcreteMC2010(Concrete): - """Concrete implementation for MC 2010""" - - _fcm: t.Optional[float] = None - _fctm: t.Optional[float] = None - _fctkmin: t.Optional[float] = None - _fctkmax: t.Optional[float] = None - _Gf: t.Optional[float] = None - - def __init__( - self, - fck: float, - name: t.Optional[str] = None, - density: float = 2400.0, - existing: bool = False, - ): - """Initializes a new instance of Concrete for MC 2010 - - Args: - fck (float): Characteristic strength in MPa if concrete is not - existing. - - Keyword Args: - name (str): A descriptive name for concrete - density (float): Density of material in kg/m3 (default: 2400) - existing (bool): The material is of an existing structure - (default: False) - """ - - if name is None: - name = f'C{round(fck):d}' - super().__init__( - fck=fck, name=name, density=density, existing=existing - ) - - def _reset_attributes(self): - self._fcm = None - self._fctm = None - self._fctkmin = None - self._fctkmax = None - self._Gf = None - - def update_attributes(self, updated_attributes: dict) -> None: - """Function for updating the attributes specified in the input - dictionary - - Args: - updated_attributes (dict): the dictionary of parameters to be - updated (not found parameters are skipped with a warning) - """ - for key, value in updated_attributes.items(): - if not hasattr(self, '_' + key): - str_list_keys = '' - for k in updated_attributes.keys(): - str_list_keys += k + ', ' - str_warn = ( - f'WARNING: attribute {key} not found. Ignoring the entry.' - ) - str_warn += '\nAvailable keys: ' + str_list_keys - warnings.warn(str_warn) - continue - setattr(self, '_' + key, value) - - @property - def fcm(self) -> float: - """Returns fcm in MPa. - - Returns: - float: The mean compressive strength in MPa. - """ - if self._fcm is not None: - return self._fcm - return mc2010.fcm(self._fck) - - @fcm.setter - def fcm(self, value: float): - """Sets a user defined value for fcm - - Args: - value (float): the value of fcm in MPa - - Raises: - ValueError: if value is lower than fck - """ - if abs(value) <= self._fck: - raise ValueError( - ( - 'Mean compressive strength cannot be lower than', - 'characteristic strength.\n', - 'Current characteristing strength: ', - f'fck = {self._fck}.', - f'Current value: value = {value}', - ) - ) - self._fcm = abs(value) - - @property - def fctm(self) -> float: - """Returns fctm in MPa - - Returns: - float: The mean tensile strength in MPa - """ - if self._fctm is not None: - return self._fctm - return mc2010.fctm(self._fck) - - @fctm.setter - def fctm(self, value: float): - """Sets a user defined value for fctm - - Args: - value (float): the value of fctm in MPa - """ - if value > 0.5 * self._fck: - warnings.warn( - 'A suspect value of fctm has been input. Please check.' - ) - self._fctm = abs(value) - - @property - def fctkmin(self) -> float: - """Returns fctkmin in MPa - - Returns: - float: The lower bound tensile strength in MPa - """ - if self._fctkmin is not None: - return self._fctkmin - - return mc2010.fctkmin(self.fctm) - - @fctkmin.setter - def fctkmin(self, value: float): - """Sets a user defined value for fctkmin - - Args: - value (float): the value of fctkmin in MPa - """ - self._fctkmin = abs(value) - - @property - def fctkmax(self) -> float: - """Returns fctkmax in MPa - - Returns: - float: The upper bound tensile strength in MPa - """ - if self._fctkmax is not None: - return self._fctkmax - - return mc2010.fctkmax(self.fctm) - - @fctkmax.setter - def fctkmax(self, value: float): - """Sets a user defined value for fctkmax - - Args: - value (float): the value of fctkmax in MPa - """ - self._fctkmax = abs(value) - - @property - def Gf(self) -> float: - """Fracture energy of concrete - - Returns: - float: The fracture energy in N/m - """ - if self._Gf is not None: - return self._Gf - return mc2010.Gf(self._fck) - - @Gf.setter - def Gf(self, value: float): - """Sets a user defined value for fracture energy Gf - - Args: - value (float): the value of Gf in N/m - """ - self._Gf = abs(value) diff --git a/tests/test_ec2_2004_section_7_3_crack_control.py b/tests/test_ec2_2004_section_7_3_crack_control.py index 8dc120f8..d4e8543b 100644 --- a/tests/test_ec2_2004_section_7_3_crack_control.py +++ b/tests/test_ec2_2004_section_7_3_crack_control.py @@ -535,7 +535,7 @@ def test_phi_eq_raises_expected_values(n1, n2, phi1, phi2, exception_type): @pytest.mark.parametrize( 'bond_type, expected', - [('bond', 0.8), ('plane', 1.6), ('BOND ', 0.8), (' PLANE ', 1.6)], + [('bond', 0.8), ('PLAIN', 1.6), ('BOND ', 0.8), (' PLAIN ', 1.6)], ) def test_k1_returns_expected_values(bond_type, expected): """Test k1 returns expected values""" @@ -588,6 +588,9 @@ def test_k4_returns_expected_values(): (20, 8, 5, 0.8, 0.5, 3.4, 0.425, 68.272), (30, 15, 0.2, 1.6, 0.5, 3.4, 0.425, 127.5), (45, 20, 0.4, 0.8, 1, 3.4, 0.425, 170), + (45, 20, 0.4, 0.8, 1, 3.4, None, 170), + (45, 20, 0.4, 0.8, 1, None, 0.425, 170), + (45, 20, 0.4, 0.8, 1, None, None, 170), ], ) def test_sr_max_close(c, phi, rho_p_eff, k1, k2, k3, k4, expected): From 1cffa61f8583e680ad31950de8c4e7288c416ed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gonz=C3=A1lez=20de=20la=20Morena?= Date: Thu, 9 Mar 2023 10:01:50 +0100 Subject: [PATCH 20/24] small lint fixes --- structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py b/structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py index e530a885..205bb410 100644 --- a/structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py +++ b/structuralcodes/codes/ec2_2004/_section_7_3_crack_control.py @@ -582,7 +582,7 @@ def kt(load_type: str) -> float: raise TypeError load_type = load_type.lower().strip() - if load_type != 'short' and load_type != 'long': + if load_type not in ('short', 'long'): raise ValueError( f'load_type={load_type} can only have "short" or "long" as a value' ) @@ -641,7 +641,7 @@ def esm_ecm( raise ValueError(f'fct_eff={fct_eff} cannot be less than 0') if Es < 0: raise ValueError(f'Es={Es} cannot be less than 0') - if _kt != 0.6 and _kt != 0.4: + if _kt not in (0.6, 0.4): raise ValueError(f'_kt={_kt} can only take as values 0.4 and 0.6') min_val = 0.6 * sigma_s / Es From b483d4047f4cdecd897cab958fba87afd0a4bc3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gonz=C3=A1lez=20de=20la=20Morena?= Date: Thu, 9 Mar 2023 10:05:28 +0100 Subject: [PATCH 21/24] vscode config updated --- .vscode/settings.json | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 2676da93..72069360 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,17 +1,10 @@ { + "python.formatting.provider": "black", "python.testing.pytestArgs": [ "tests" ], - "python.formatting.provider": "black", "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, "python.linting.pylintEnabled": true, - "python.linting.flake8Enabled": true, - "[python]": { - "editor.formatOnSave": true, - "editor.codeActionsOnSave": { - "source.organizeImports": true - }, - "editor.defaultFormatter": "ms-python.python", - }, + "python.linting.flake8Enabled": true } \ No newline at end of file From 125693f6f8906eb00dbd0b9fbd42b9b729b9a588 Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Mon, 19 Aug 2024 13:17:39 +0200 Subject: [PATCH 22/24] files created --- structuralcodes/codes/ec2_2023/_section12_detailing_members.py | 1 + tests/test_ec2_2023/test_ec2_2023_section12_detailing_members.py | 1 + 2 files changed, 2 insertions(+) create mode 100644 structuralcodes/codes/ec2_2023/_section12_detailing_members.py create mode 100644 tests/test_ec2_2023/test_ec2_2023_section12_detailing_members.py diff --git a/structuralcodes/codes/ec2_2023/_section12_detailing_members.py b/structuralcodes/codes/ec2_2023/_section12_detailing_members.py new file mode 100644 index 00000000..8188fe7f --- /dev/null +++ b/structuralcodes/codes/ec2_2023/_section12_detailing_members.py @@ -0,0 +1 @@ +"""Functions from Section 12 of EN 1992-1-1:2023.""" diff --git a/tests/test_ec2_2023/test_ec2_2023_section12_detailing_members.py b/tests/test_ec2_2023/test_ec2_2023_section12_detailing_members.py new file mode 100644 index 00000000..7d95907d --- /dev/null +++ b/tests/test_ec2_2023/test_ec2_2023_section12_detailing_members.py @@ -0,0 +1 @@ +"""Test for functions from Section 12 of EN 1992-1-1:2023.""" From 11ba7f811e0c40693dddadc56661de21411b25f6 Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Wed, 21 Aug 2024 08:53:11 +0200 Subject: [PATCH 23/24] partially completed --- .../ec2_2023/_section12_detailing_members.py | 988 ++++++++++++++++++ ...st_ec2_2023_section12_detailing_members.py | 474 +++++++++ 2 files changed, 1462 insertions(+) diff --git a/structuralcodes/codes/ec2_2023/_section12_detailing_members.py b/structuralcodes/codes/ec2_2023/_section12_detailing_members.py index 8188fe7f..88d3fc6a 100644 --- a/structuralcodes/codes/ec2_2023/_section12_detailing_members.py +++ b/structuralcodes/codes/ec2_2023/_section12_detailing_members.py @@ -1 +1,989 @@ """Functions from Section 12 of EN 1992-1-1:2023.""" + +import math +from typing import Literal + + +def As_min(Ac: float, fctm: float, fyk: float) -> float: + """Calculate the minimum reinforcement area + for members subjected to pure tension. + + EN1992-1-1:2023 Eq. (12.2) + + Args: + Ac (float): The concrete cross-sectional area in mm2. + fctm (float): The mean value of axial tensile + strength of the concrete in MPa. + fyk (float): The characteristic yield + strength of the reinforcement in MPa. + + Returns: + float: The minimum reinforcement area As,min in mm2. + + Raises: + ValueError: If any input value is negative. + """ + if Ac < 0: + raise ValueError( + 'Ac (concrete cross-sectional area) must not be negative. ' + + f'Got {Ac}' + ) + if fctm < 0: + raise ValueError( + 'fctm (tensile strength of concrete) must not be negative.' + + f' Got {fctm}' + ) + if fyk < 0: + raise ValueError( + 'fyk (yield strength of reinforcement) must not be negative. ' + + f'Got {fyk}' + ) + + return Ac * fctm / fyk + + +def As_w_min( + fck: float, + fyk: float, + alpha: float, + bw: float, + s: float, + ductility_class: Literal['A', 'B', 'C'], +) -> float: + """Calculate the minimum shear reinforcement. + + EN1992-1-1:2023 Eq. (12.4) + + Args: + fck (float): The characteristic compressive + strength of concrete in MPa. + fyk (float): The characteristic yield strength + of reinforcement in MPa. + alpha (float): The angle between shear + reinforcement and the longitudinal axis in degrees. + bw (float): The width of the + web of the member in mm. + s (float): The spacing of the shear reinforcement + along the longitudinal axis in mm. + ductility_class (str): The ductility class + of the reinforcement ('A', 'B', or 'C'). + + Returns: + float: The minimum shear reinforcement in mm2. + + Raises: + ValueError: If any input value is negative. + """ + if fck < 0: + raise ValueError( + 'fck (compressive strength of concrete) ' + + f'must not be negative. Got {fck}' + ) + if fyk < 0: + raise ValueError( + 'f_yk (yield strength of reinforcement) ' + + f'must not be negative. Got {fyk}' + ) + if alpha < 0 or alpha > 90: + raise ValueError( + f'alpha (angle) must be between 0 and 90 degrees. Got {alpha}' + ) + if bw < 0: + raise ValueError(f'b_w (width of web) must not be negative. Got {bw}') + if s < 0: + raise ValueError( + f's (spacing of shear reinforcement) must not be negative. Got {s}' + ) + + alpha_rad = math.radians(alpha) + rho_w_min = 0.08 * math.sqrt(fck) / fyk + As_w_min = rho_w_min * s * bw * math.sin(alpha_rad) + + if ductility_class == 'B': + As_w_min *= 0.9 + elif ductility_class == 'C': + As_w_min *= 0.8 + + return As_w_min + + +def MRd_min( + Med: float, + ductility_class: Literal['A', 'B', 'C'], +) -> float: + """Calculate the minimum moment resistance. + + EN1992-1-1:2023 Eq. (12.3) + + Args: + Med (float): The design moment in kNm. + ductility_class (str): The ductility class of the reinforcement + (A, B, or C). + + Returns: + float: The minimum moment resistance M_rd,min in kN·m. + """ + kdc_values = {'A': 1.3, 'B': 1.1, 'C': 1.0} + kdc = kdc_values[ductility_class] + + return kdc * Med + + +def As_min_bot(As_min_req_span: float) -> float: + """Calculate the minimum bottom reinforcement at inner and end supports + taking account unforeseen postive moments. + + EN1992-1-1:2023 Table (12.1) + + Args: + As_min_req_span (float): the minimum reinforcement area requirement in + the span of the beam in mm2. + + Returns: + float: The minimum reinforcement area in mm2 + """ + if As_min_req_span < 0: + raise ValueError( + f'As_min_req_span must not be negative. Got {As_min_req_span}' + ) + + return 0.25 * As_min_req_span + + +def sl_max(d: float, alpha: float) -> float: + """Calculate maximum longitudinal spacing of shear assemblies/stirrups. + + EN1992-1-1:2023 Table (12.1) + + Args: + d (float): Effective depth in mm. + alpha (float): Angle of shear reinforcement in degrees. + + Returns: + float: Maximum spacing sl,max in mm. + + Raises: + ValueError: If d is negative or alpha is out of range. + """ + if d < 0: + raise ValueError(f'd must not be negative. Got {d}') + if not (0 <= alpha <= 90): + raise ValueError( + f'alpha must be between 0 and 90 degrees. Got {alpha}' + ) + + rad_alpha = math.radians(alpha) + cot_alpha = 1.0 / math.tan(rad_alpha) + + return 0.75 * d * (1 + cot_alpha) + + +def sbu_max(d: float, alpha: float) -> float: + """Calculate maximum longitudinal spacing of bent-up bars. + + EN1992-1-1:2023 Table (12.1) + + Args: + d (float): Effective depth in mm. + alpha (float): Angle of shear reinforcement in degrees. + + Returns: + float: Maximum spacing sbu,max in mm. + + Raises: + ValueError: If d is negative or alpha is out of range. + """ + if d < 0: + raise ValueError(f'd must not be negative. Got {d}') + if not (0 <= alpha <= 90): + raise ValueError( + f'alpha must be between 0 and 90 degrees. Got {alpha}' + ) + + rad_alpha = math.radians(alpha) + cot_alpha = 1.0 / math.tan(rad_alpha) + + return 0.6 * d * (1 + cot_alpha) + + +def str_max(d: float) -> float: + """Calculate maximum transverse spacing of shear legs. + + EN1992-1-1:2023 Table (12.1) + + Args: + d (float): Effective depth in mm. + + Returns: + float: Maximum transverse spacing str,max ion mm. + + Raises: + ValueError: If d is negative. + """ + if d < 0: + raise ValueError(f'd must not be negative. Got {d}') + + return min(0.75 * d, 600) + + +def rho_w_stir_min(rho_w_req: float) -> float: + """Calculate the minimum ratio of shear reinforcement in the + form of stirrups with respect to the required reinforcement ratio. + + EN1992-1-1:2023 Table (12.1) + + Args: + rho_w_req (float): Required reinforcement ratio. + + Returns: + float: Minimum reinforcement ratio ρw,stir. + + Raises: + ValueError: If rho_w_req is negative. + """ + if rho_w_req < 0: + raise ValueError(f'rho_w_req must not be negative. Got {rho_w_req}') + + return 0.5 * rho_w_req + + +def rho_w_tors_min(rho_w_req: float) -> float: + """Calculate the minimum ratio of torsion reinforcement in the + form of closed stirrups with respect to the + required reinforcement ratio. + + EN1992-1-1:2023 Table (12.1) + + Args: + rho_w_req (float): Required reinforcement ratio. + + Returns: + float: Minimum torsion reinforcement ratio ρw,stir. + + Raises: + ValueError: If rho_w_req is negative. + """ + if rho_w_req < 0: + raise ValueError(f'rho_w_req must not be negative. Got {rho_w_req}') + + return 0.2 * rho_w_req + + +def s_stir_max(u: float, b: float, h: float) -> float: + """Calculate the maximum spacing for torsion assemblies/stirrups. + + EN1992-1-1:2023 Table (12.1) + + Args: + u (float): Perimeter of the section in mm. + b (float): Width of the section in mm. + h (float): Height of the section in mm. + + Returns: + float: Maximum spacing for torsion assemblies/stirrups sstir,max in mm. + + Raises: + ValueError: If any of the inputs are negative. + """ + if u < 0: + raise ValueError(f'u must not be negative. Got {u}') + if b < 0: + raise ValueError(f'b must not be negative. Got {b}') + if h < 0: + raise ValueError(f'h must not be negative. Got {h}') + + return min(u / 8, b, h) + + +def sl_surf_max() -> float: + """Calculate the minimum spacing of longitudinal surface reinforcement. + + EN1992-1-1:2023 Table (12.1) + + Returns: + float: Minimum spacing in mm. + """ + return 300.0 + + +def al_with_shear_reinforcement(z: float, theta: float, alpha: float) -> float: + """Calculate the shift distance al for members + with shear reinforcement using the given parameters. + + EN1992-1-1:2023 Eq. (12.5) + + Args: + z (float): Internal lever arm in mm. + theta (float): Angle of compression strut in degrees. + alpha (float): Angle of shear reinforcement in degrees. + + Returns: + float: Shift distance al in mm. + + Raises: + ValueError: If z, theta, or alpha are out of their valid ranges. + """ + if z < 0: + raise ValueError(f'z must not be negative. Got {z}') + if not (0 <= theta <= alpha): + raise ValueError( + f'theta must be between 0 and 90 degrees. Got {theta}' + ) + + rad_theta = math.radians(theta) + rad_alpha = math.radians(alpha) + cot_theta = 1 / math.tan(rad_theta) + cot_alpha = 1 / math.tan(rad_alpha) + + return z * (cot_theta - cot_alpha) / 2 + + +def al_without_shear_reinforcement(d: float) -> float: + """Calculate the shift distance al for members without shear reinforcement. + + EN1992-1-1:2023 Eq. (12.6) + + Args: + d (float): Effective depth in mm. + + Returns: + float: Shift distance al in mm. + + Raises: + ValueError: If d is negative. + + """ + if d < 0: + raise ValueError(f'd must not be negative. Got {d}') + return d + + +def min_anchorage_length_bent_up_bar( + lbd: float, zone: Literal['tension', 'compression'] +) -> float: + """Calculate the anchorage length of a bent-up bar + contributing to shear resistance. + + EN1992-1-1:2023 12.3.2(4) + + Args: + lbd (float): Basic anchorage length in mm. + zone (str): The zone in which the bar is anchored, + either "tension" or "compression". + + Returns: + float: Required anchorage length ion mm. + + Raises: + ValueError: If `lbd` is negative. + """ + if lbd < 0: + raise ValueError(f'lbd must not be negative. Got {lbd}') + + if zone == 'tension': + return 1.3 * lbd + return 0.7 * lbd + + +def bottom_reinforcement_extension(phi: float) -> float: + """Calculate the minimum extension of bottom reinforcement + at intermediate supports. + + EN1992-1-1:2023 12.3.2(5) + + Args: + phi (float): Diameter of the reinforcement bar in mm. + + Returns: + float: Minimum extension length in mm. + + Raises: + ValueError: If phi is negative. + """ + if phi < 0: + raise ValueError(f'phi must not be negative. Got {phi}') + return 10 * phi + + +def As_slab_min_secondary_reinforcement(As_req_span: float) -> float: + """Calculate the minimum secondary reinforcement in slabs. + + EN1992-1-1:2023 Table (12.2) + + Args: + As_req_span (float): Required reinforcement for positive + bending moments at the span in mm2. + + Returns: + float: Minimum secondary reinforcement area in mm2. + + Raises: + ValueError: If As_req_span is negative. + + """ + if As_req_span < 0: + raise ValueError( + f'As_req_span must not be negative. Got {As_req_span}' + ) + + return 0.2 * As_req_span + + +def As_slab_min_bottom_reinforcement_inner_supports( + As_req_span: float, +) -> float: + """Calculate the minimum longitudinal bottom + reinforcement at inner supports of slabs. + + EN1992-1-1:2023 Table (12.2) + + Args: + As_req_span (float): Required reinforcement for positive + bending moments at the span in mm2. + + Returns: + float: Minimum bottom reinforcement area in mm2. + + Raises: + ValueError: If As_req_span is negative. + """ + if As_req_span < 0: + raise ValueError( + f'As_req_span must not be negative. Got {As_req_span}' + ) + + return 0.25 * As_req_span + + +def As_slab_min_bottom_reinforcement_end_supports(As_req_span: float) -> float: + """Calculate the minimum longitudinal + bottom reinforcement at end supports of slabs. + + EN1992-1-1:2023 Table (12.2) + + Args: + As_req_span (float): Required reinforcement + for positive bending moments at the span in mm2. + + Returns: + float: Minimum bottom reinforcement area in mm2. + + Raises: + ValueError: If As_req_span is negative. + """ + if As_req_span < 0: + raise ValueError( + f'As_req_span must not be negative. Got {As_req_span}' + ) + + return 0.25 * As_req_span + + +def As_slab_min_top_reinforcement_end_supports( + As_req_span: float, As_min: float +) -> float: + """Calculate the minimum top reinforcement at + end supports in buildings where unintentional restraint may occur. + + EN1992-1-1:2023 Table (12.2) + + Args: + As_req_span (float): Required reinforcement for + positive bending moments at the span in mm2. + As_min (float): Minimum area of reinforcement + according to 12.2(2) in mm2. + + Returns: + float: Minimum top reinforcement area in mm2. + + Raises: + ValueError: If As_req_span or As_min are negative + ) + """ + if As_req_span < 0: + raise ValueError( + f'As_req_span must not be negative. Got {As_req_span}' + ) + if As_min < 0: + raise ValueError(f'As_min must not be negative. Got {As_min}') + + return max(0.25 * As_req_span, As_min) + + +def s_slab_max(h: float) -> float: + """Calculate the maximum spacing of bars for concrete in tension in slabs. + + EN1992-1-1:2023 Table (12.2) + + Args: + h (float): Thickness of the slab in mm. + + Returns: + float: Maximum spacing of bars in mm. + + Raises: + ValueError: If h is negative. + """ + if h < 0: + raise ValueError(f'h must not be negative. Got {h}') + + return min(3 * h, 400) + + +def sl_max_slab(d: float, alpha: float) -> float: + """Calculate the maximum longitudinal spacing of + shear assemblies/stirrups in slabs. + + EN1992-1-1:2023 Table (12.2) + + Args: + d (float): Effective depth of the slab in mm. + alpha (float): Angle of shear reinforcement in degrees. + + Returns: + float: Maximum longitudinal spacing sl,max in mm. + + Raises: + ValueError: If d is negative or alpha is out of range. + + """ + if d < 0: + raise ValueError(f'd must not be negative. Got {d}') + if not (0 <= alpha <= 90): + raise ValueError( + f'alpha must be between 0 and 90 degrees. Got {alpha}' + ) + + rad_alpha = math.radians(alpha) + cot_alpha = 1.0 / math.tan(rad_alpha) + + return 0.75 * d * (1 + cot_alpha) + + +def sbu_max_slab(d: float) -> float: + """Calculate the maximum longitudinal spacing of bent-up bars in slabs. + + EN1992-1-1:2023 Table (12.2) + + Args: + d (float): Effective depth of the slab in mm. + + Returns: + float: Maximum longitudinal spacing of bent-up bars sbu,max in mm. + + Raises: + ValueError: If d is negative. + """ + if d < 0: + raise ValueError(f'd must not be negative. Got {d}') + + return d + + +def str_max_slab(d: float) -> float: + """Calculate the maximum transverse spacing of shear legs in slabs. + + EN1992-1-1:2023 Table (12.2) + + Args: + d (float): Effective depth of the slab in mm. + + Returns: + float: Maximum transverse spacing of shear legs str,max in mm. + + Raises: + ValueError: If d is negative. + """ + if d < 0: + raise ValueError(f'd must not be negative. Got {d}') + + return 1.5 * d + + +def min_slab_reinforcement_along_free_edge(h: float, lbd: float) -> bool: + """Get the slab reinforcement minimum length along free edge. + + EN1992-1-1:2023 Fig. (12.4) + + Args: + h (float): Height of the free edge in mm. + lbd (float): Base anchor length in mm. + + Returns: + bool: The mimimun reinforcement length in mm. + + Raises: + ValueError: If as_top or as_bottom is negative. + """ + if h < 0: + raise ValueError(f'd must not be negative. Got {h}') + if lbd < 0: + raise ValueError(f'd must not be negative. Got {lbd}') + + return max(2 * h, lbd) + + +def check_slab_depth_for_shear_reinforcement( + h: float, + reinforcement_type: Literal[ + 'stirrups', 'links', 'headed_bars', 'bent_up_bars' + ], +) -> bool: + """Check if the slab depth meets the minimum + requirement for the specified type of shear reinforcement. + + EN1992-1-1:2023 Section 12.4.2(2) + + Args: + h (float): Depth of the slab in mm. + reinforcement_type (str): Type of reinforcement + ('stirrups', 'links', 'headed_bars', 'bent_up_bars'). + + Returns: + bool: True if the slab depth meets the requirement, False otherwise. + + Raises: + ValueError: If h is negative. + """ + if h < 0: + raise ValueError(f'd must not be negative. Got {h}') + + if reinforcement_type in ('stirrups', 'links', 'headed_bars'): + return h >= 200 + # If bent_up_bars + return h >= 160 + + +def check_slab_shear_stress_conditions( + tau_ed: float, fcd: float, alignment: float +) -> bool: + """Check if the shear reinforcement + condition is met for a slab based on the stress and alignment. + Interpolates the allowable shear stress based on the alignment angle. + + EN1992-1-1:2023 12.4.2(5) + + Args: + tau_ed (float): Design shear stress in MPa. + fcd (float): Design compressive strength of concrete in MPa. + alignment (float): Alignment of shear reinforcement + in degrees (0 for vertical, 45 for 45°). + + Returns: + bool: True if the shear stress condition is satisfied, False otherwise. + + Raises: + ValueError: If tau_ed or fcd is negative or + if alignment is out of range. + + """ + if tau_ed < 0: + raise ValueError(f'tau_ed must not be negative. Got {tau_ed}') + if fcd < 0: + raise ValueError(f'tau_ed must not be negative. Got {fcd}') + + if not (0 <= alignment <= 45): + raise ValueError( + f'alignment must be between 0 and 45 degrees. Got {alignment}' + ) + + # Linear interpolation between the vertical and 45-degree values + tau_ed_limit = (0.08 + (alignment / 45) * 0.08) * fcd + + return tau_ed <= tau_ed_limit + + +def slab_column_max_leg_shear_reinforcement_diameter( + d: float, + reinforcement_type: Literal[ + 'single_leg', + 'open_stirrups', + 'closed_stirrups', + 'bent_up_bars', + 'headed_bars', + ], +) -> float: + """Calculate the maximum effective diameter of shear reinforcement + based on the type and effective depth. + + EN1992-1-1:2023 Eq. (12.7), (12.8), (12.9) + + Args: + d (float): Effective depth of the slab in mm. + reinforcement_type (str): Type of reinforcement + ('single_leg', 'open_stirrups', 'closed_stirrups', + 'bent_up_bars', 'headed_bars'). + + Returns: + float: Maximum effective diameter of shear reinforcement in mm. + + Raises: + ValueError: If the reinforcement_type is not + recognized or if d is negative. + """ + if d < 0: + raise ValueError(f'd must not be negative. Got {d}') + + if reinforcement_type in ('single_leg', 'open_stirrups'): + return 10 * math.sqrt(d / 200) + if reinforcement_type in ( + 'closed_stirrups', + 'bars_with_similar_anchorage', + ): + return 11 * math.sqrt(d / 200) + # if ('bent_up_bars', 'headed_bars') + return 16 * math.sqrt(d / 200) + + +def slab_column_tangential_spacing_limit( + dv: float, + distance_to_column_edge: float, + slab_type: Literal['column_edge', 'flat_slab', 'column_base'], +) -> float: + """Calculate the tangential spacing limit for shear + reinforcement based on the distance to the column edge. + + EN1992-1-1:2023 Section 12.5.1(2) + + Args: + dv (float): Effective depth of the slab in mm. + distance_to_column_edge (float): Distance from + the shear reinforcement to the column edge in mm. + slab_type (str): Type of slab + ('column_edge', 'flat_slab', 'column_base'). + + Returns: + float: Maximum tangential spacing in mm. + + Raises: + ValueError: If dv or distance_to_column_edge is negative, + or if slab_type is not recognized. + """ + if dv < 0: + raise ValueError(f'dv must not be negative. Got {dv}') + if distance_to_column_edge < 0: + raise ValueError( + 'distance_to_column_edge must not be negative. ' + + f'Got {distance_to_column_edge}' + ) + if slab_type == 'column_edge': + return 1.5 * dv + if slab_type == 'flat_slab': + return 0.75 * dv + + # If column_base + return 0.5 * dv + + +def Vrd_int_flat_slab( + As_int: float, fyd: float, ductility_class: Literal['B', 'C'], VEd: float +) -> float: + """Calculate the integrity reinforcement + resistance for progressive collapse. + + EN1992-1-1:2023 Eq. (12.10) + + Args: + As_int (float): Sum of the cross-sections of all + integrity reinforcement bars crossing a column edge in mm2. + fyd (float): Yield strength of the integrity reinforcement in MPa. + kint (float): Ductility class (B or C). + VEd (float): Design shear in kN + + Returns: + float: Integrity reinforcement resistance VRd,int in kN. + + Raises: + ValueError: If any input value is negative. + """ + if As_int < 0: + raise ValueError(f'As_int must not be negative. Got {As_int}') + if fyd < 0: + raise ValueError(f'fyd must not be negative. Got {fyd}') + kint = 0.37 if ductility_class == 'B' else 0.49 + + return max(As_int * fyd * kint / 1000, abs(VEd)) + + +def Vrd_w_int_flat_slab( + rho_w: float, fywd: float, b0_5: float, dv: float, VEd: float +) -> float: + """Calculate the integrity reinforcement resistance + for slabs with shear reinforcement. + + EN1992-1-1:2023 Eq. (12.11) + + Args: + rho_w (float): Ratio of shear reinforcement. + fywd (float): Yield strength of the shear reinforcement in MPa. + b0_5 (float): Length of the control perimeter in mm. + dv (float): Effective depth of the slab in mm. + VEd (float): Design shear value in kN. + + Returns: + float: Integrity reinforcement resistance VRd,w,int in kN. + + Raises: + ValueError: If any input value is negative. + """ + if rho_w < 0: + raise ValueError(f'rho_w must not be negative. Got {rho_w}') + if fywd < 0: + raise ValueError(f'fywd must not be negative. Got {fywd}') + if b0_5 < 0: + raise ValueError(f'b0_5 must not be negative. Got {b0_5}') + if dv < 0: + raise ValueError(f'dv must not be negative. Got {dv}') + + return min(rho_w * fywd * b0_5 * dv / 1000, abs(VEd)) # Convert to kN + + +def Vrd_hog_flat_slab( + nhog: int, fck: float, gamma_c: float, phi: float, s: float, c: float +) -> float: + """Calculate the contribution of hogging reinforcement + to the resistance against progressive collapse. + + EN1992-1-1:2023 Eq. (12.12) + + Args: + nhog (int): Number of hogging bars crossing the + control perimeter and fully anchored. + fck (float): Characteristic compressive + strength of concrete in MPa. + gamma_c (float): Partial factor for concrete + for accidental design situation. + phi (float): Diameter of the + hogging reinforcement in mm. + s (float): Spacing of the hogging reinforcement in mm. + c (float): Cover of the hogging + reinforcement in mm. + + Returns: + float: Hogging reinforcement resistance VRd,hog (kN). + + Raises: + ValueError: If any input value is negative. + """ + if nhog < 0: + raise ValueError(f'nhog must not be negative. Got {nhog}') + if fck < 0: + raise ValueError(f'fck must not be negative. Got {fck}') + if gamma_c < 0: + raise ValueError(f'gamma_c must not be negative. Got {gamma_c}') + if phi < 0: + raise ValueError(f'phi must not be negative. Got {phi}') + if s < 0: + raise ValueError(f's must not be negative. Got {s}') + if c < 0: + raise ValueError(f'c must not be negative. Got {c}') + + bef_hog = min(s - phi, 6 * phi, 4 * c) + return ( + nhog * (math.sqrt(fck) / gamma_c) * bef_hog * phi / 1000 + ) # Convert to kN + + +def As_column_min(NEd: float, fyd: float, Ac: float) -> float: + """Calculate the minimum amount of longitudinal + reinforcement for robustness and to avoid + compressive yielding due to creep and shrinkage. + + EN1992-1-1:2023 Table 12.3 + + Args: + NEd (float): Design axial force in kN. + fyd (float): Design yield strength of the reinforcement in MPa. + Ac (float): Cross-sectional area of the column in mm2. + + Returns: + float: Minimum area of longitudinal reinforcement in mm2. + + Raises: + ValueError: If any input value is negative. + """ + NEd = abs(NEd) + if fyd < 0: + raise ValueError(f'fyd must not be negative. Got {fyd}') + if Ac < 0: + raise ValueError(f'Ac must not be negative. Got {Ac}') + + return max(0.1 * NEd * 1000 / fyd, 0.002 * Ac) + + +def s_max_poly_col(h: float, b: float) -> float: + """Calculate the maximum longitudinal spacing of + transverse reinforcement for polygonal cross-sections. + + EN1992-1-1:2023 Table 12.3 + + Args: + h (float): Height of the column in mm. + b (float): Width of the column in mm. + + Returns: + float: Maximum longitudinal spacing in mm. + + Raises: + ValueError: If any input value is negative. + """ + if h < 0: + raise ValueError(f'h must not be negative. Got {h}') + if b < 0: + raise ValueError(f'b must not be negative. Got {b}') + + return min(h, b, 400) + + +def s_max_circular_col(n_bars: int, diameter: float) -> float: + """Calculate the maximum longitudinal spacing of + transverse reinforcement for circular cross-sections. + + EN1992-1-1:2023 Table 12.3 + + Args: + n_bars (int): Number of longitudinal bars. + diameter (float): Diameter of the circular column in mm. + + Returns: + float: Maximum longitudinal spacing in mm. + + Raises: + ValueError: If any input value is negative. + """ + if n_bars < 0: + raise ValueError(f'n_bars must not be negative. Got {n_bars}') + if diameter < 0: + raise ValueError(f'diameter must not be negative. Got {diameter}') + + return min(diameter * math.pi / n_bars, 400) + + +def s_max_col(s_max_col: float, fck: float, fcd: float) -> float: + """Calculate the maximum spacing of transverse + reinforcement for columns in the end region. + + EN1992-1-1:2023 Table 12.3 + + Args: + s_max_col (float): Maximum spacing of transverse reinforcement in mm. + fck (float): Characteristic compressive strength of concrete in MPa. + fcd (float): Design compressive strength of concrete in MPa. + + Returns: + float: Maximum spacing of transverse reinforcement in mm. + + Raises: + ValueError: If any input value is negative. + """ + if s_max_col < 0: + raise ValueError(f's_max_col must not be negative. Got {s_max_col}') + if fck < 0: + raise ValueError(f'fck must not be negative. Got {fck}') + if fcd < 0: + raise ValueError(f'fcd must not be negative. Got {fcd}') + + if fck > 50: + confinement_min = 0.6 * s_max_col * fcd / fck + return min(s_max_col * 0.6, confinement_min) + return s_max_col * 0.6 diff --git a/tests/test_ec2_2023/test_ec2_2023_section12_detailing_members.py b/tests/test_ec2_2023/test_ec2_2023_section12_detailing_members.py index 7d95907d..8389f6a7 100644 --- a/tests/test_ec2_2023/test_ec2_2023_section12_detailing_members.py +++ b/tests/test_ec2_2023/test_ec2_2023_section12_detailing_members.py @@ -1 +1,475 @@ """Test for functions from Section 12 of EN 1992-1-1:2023.""" + +import pytest + +from structuralcodes.codes.ec2_2023 import _section12_detailing_members + + +@pytest.mark.parametrize( + 'A_c, f_ctm, f_yk, expected', + [ + (1000, 2.5, 500, 5.0), + (1500, 3.0, 600, 7.5), + (1200, 2.0, 400, 6.0), + (-1000, 2.5, 500, None), + ], +) +def test_As_min(A_c, f_ctm, f_yk, expected): + """Test As_min.""" + if A_c < 0 or f_ctm < 0 or f_yk < 0: + with pytest.raises(ValueError): + _section12_detailing_members.As_min(A_c, f_ctm, f_yk) + else: + assert ( + _section12_detailing_members.As_min(A_c, f_ctm, f_yk) == expected + ) + + +@pytest.mark.parametrize( + 'f_ck, f_yk, alpha, b_w, s, ductility_class, expected', + [ + (30, 500, 45, 200, 100, 'C', 9.91), + (25, 400, 30, 150, 80, 'B', 5.4), + (40, 600, 60, 250, 120, 'A', 21.91), + ], +) +def test_As_w_min(f_ck, f_yk, alpha, b_w, s, ductility_class, expected): + """Test As_w_min.""" + assert ( + pytest.approx( + _section12_detailing_members.As_w_min( + f_ck, f_yk, alpha, b_w, s, ductility_class + ), + rel=1e-2, + ) + == expected + ) + + +@pytest.mark.parametrize( + 'M_ed, ductility_class, expected', + [ + (100, 'C', 100), + (80, 'B', 88), + (60, 'A', 78), + ], +) +def test_MRd_min(M_ed, ductility_class, expected): + """Test the minimum moment resistance.""" + assert ( + _section12_detailing_members.MRd_min(M_ed, ductility_class) == expected + ) + + +@pytest.mark.parametrize( + 'd, alpha, expected', + [ + (500, 45, 750), + (400, 30, 819.62), + ], +) +def test_sl_max(d, alpha, expected): + """Test maximum longitudinal spacing of shear assemblies.""" + assert _section12_detailing_members.sl_max(d, alpha) == pytest.approx( + expected, rel=1e-2 + ) + + +@pytest.mark.parametrize( + 'd, alpha, expected', + [ + (500, 45, 600.0), + (400, 30, 655.69), + ], +) +def test_sbu_max(d, alpha, expected): + """Test maximum longitudinal spacing of bent-up bars.""" + assert _section12_detailing_members.sbu_max(d, alpha) == pytest.approx( + expected, rel=1e-2 + ) + + +@pytest.mark.parametrize( + 'd, expected', + [ + (500, 375.0), + (400, 300.0), + ], +) +def test_str_max(d, expected): + """Test maximum transverse spacing of shear legs.""" + assert _section12_detailing_members.str_max(d) == pytest.approx( + expected, rel=1e-2 + ) + + +@pytest.mark.parametrize( + 'rho_w_req, expected', + [ + (0.02, 0.01), + (0.04, 0.02), + ], +) +def test_rho_w_stir_min(rho_w_req, expected): + """Test minimum ratio of shear reinforcement in the form of stirrups.""" + assert _section12_detailing_members.rho_w_stir_min( + rho_w_req + ) == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'rho_w_req, expected', + [ + (0.02, 0.004), + (0.04, 0.008), + ], +) +def test_rho_w_tors_min(rho_w_req, expected): + """Test minimum ratio of torsion reinforcement in + the form of closed stirrups. + """ + assert _section12_detailing_members.rho_w_tors_min( + rho_w_req + ) == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'u, b, h, expected', + [ + (1600, 300, 500, 200.0), + (2400, 250, 400, 250.0), + ], +) +def test_sstir_max(u, b, h, expected): + """Test maximum spacing for torsion assemblies/stirrups.""" + assert _section12_detailing_members.s_stir_max(u, b, h) == pytest.approx( + expected, rel=1e-2 + ) + + +def test_as_web_min(): + """Test minimum area of longitudinal surface + reinforcement in beams with a downstand ≥ 600 mm. + """ + assert _section12_detailing_members.sl_surf_max() == 300.0 + + +@pytest.mark.parametrize( + 'z, theta, alpha, expected', + [ + (500, 30, 45, 183.01), + (400, 45, 60, 84.53), + ], +) +def test_al_with_shear_reinforcement(z, theta, alpha, expected): + """Test shift distance calculation for members with shear reinforcement.""" + assert _section12_detailing_members.al_with_shear_reinforcement( + z, theta, alpha + ) == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'd, expected', + [ + (500, 500), + (400, 400), + ], +) +def test_al_without_shear_reinforcement(d, expected): + """Test shift distance calculation for + members without shear reinforcement. + """ + assert _section12_detailing_members.al_without_shear_reinforcement( + d + ) == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'lbd, zone, expected', + [ + (200, 'tension', 260.0), + (150, 'compression', 105.0), + ], +) +def test_anchorage_length_bent_up_bar(lbd, zone, expected): + """Test anchorage length calculation for bent-up bars.""" + assert _section12_detailing_members.min_anchorage_length_bent_up_bar( + lbd, zone + ) == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'phi, expected', + [ + (16, 160), + (12, 120), + ], +) +def test_bottom_reinforcement_extension(phi, expected): + """Test minimum extension of bottom + reinforcement at intermediate supports. + """ + assert _section12_detailing_members.bottom_reinforcement_extension( + phi + ) == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'as_req_span, expected', + [ + (1000, 200), + (1200, 240), + ], +) +def test_minimum_secondary_reinforcement(as_req_span, expected): + """Test minimum secondary reinforcement calculation in slabs.""" + assert ( + _section12_detailing_members.As_slab_min_secondary_reinforcement( + as_req_span + ) + == expected + ) + + +@pytest.mark.parametrize( + 'as_req_span, expected', + [ + (1000, 250), + (1200, 300), + ], +) +def test_minimum_bottom_reinforcement_inner_supports(as_req_span, expected): + """Test minimum bottom reinforcement at inner supports in slabs.""" + assert ( + _section12_detailing_members.As_slab_min_bottom_reinforcement_inner_supports( + as_req_span + ) + == expected + ) + + +@pytest.mark.parametrize( + 'as_req_span, expected', + [ + (1000, 250), + (1200, 300), + ], +) +def test_minimum_bottom_reinforcement_end_supports(as_req_span, expected): + """Test minimum bottom reinforcement at end supports in slabs.""" + assert ( + _section12_detailing_members.As_slab_min_bottom_reinforcement_end_supports( + as_req_span + ) + == expected + ) + + +@pytest.mark.parametrize( + 'as_req_span, as_min, expected', + [ + (1000, 150, 250), + (800, 250, 250), + ], +) +def test_minimum_top_reinforcement_end_supports(as_req_span, as_min, expected): + """Test minimum top reinforcement at end supports in slabs.""" + assert ( + _section12_detailing_members.As_slab_min_top_reinforcement_end_supports( + as_req_span, as_min + ) + == expected + ) + + +@pytest.mark.parametrize( + 'h, expected', + [ + (150, 400), + (100, 300), + ], +) +def test_sslab_max(h, expected): + """Test maximum spacing of bars in slabs.""" + assert _section12_detailing_members.s_slab_max(h) == expected + + +@pytest.mark.parametrize( + 'd, alpha, expected', + [ + (200, 45, 300), + (180, 30, 368.82), + ], +) +def test_sl_max_slab(d, alpha, expected): + """Test maximum longitudinal spacing of shear assemblies in slabs.""" + assert _section12_detailing_members.sl_max_slab(d, alpha) == pytest.approx( + expected, rel=1e-2 + ) + + +@pytest.mark.parametrize( + 'd, expected', + [ + (200, 200), + (180, 180), + ], +) +def test_sbu_max_slab(d, expected): + """Test maximum longitudinal spacing of bent-up bars in slabs.""" + assert _section12_detailing_members.sbu_max_slab(d) == expected + + +@pytest.mark.parametrize( + 'd, expected', + [ + (200, 300), + (180, 270), + ], +) +def test_str_max_slab(d, expected): + """Test maximum transverse spacing of shear legs in slabs.""" + assert _section12_detailing_members.str_max_slab(d) == expected + + +@pytest.mark.parametrize( + 'tau_ed, fcd, alignment, expected', + [ + (2.4, 30, 0, True), + (4.8, 30, 45, True), + (2.5, 30, 0, False), + (5.0, 30, 30, False), + (5.5, 30, 30, False), + ], +) +def test_check_shear_stress_conditions(tau_ed, fcd, alignment, expected): + """Test shear stress condition check with + interpolation based on alignment angle. + """ + assert ( + _section12_detailing_members.check_slab_shear_stress_conditions( + tau_ed, fcd, alignment + ) + == expected + ) + + +@pytest.mark.parametrize( + 'h, lbd, expected', + [ + (40, 30, 80), + (50, 30, 100), + (60, 30, 120), + (10, 30, 30), + (25, 30, 50), + ], +) +def test_min_slab_reinforcement_along_free_edge(h, lbd, expected): + """Test min_slab_reinforcement_along_free_edge.""" + assert _section12_detailing_members.min_slab_reinforcement_along_free_edge( + h, lbd + ) == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'h, reinforcement_type, expected', + [ + (220, 'stirrups', True), + (180, 'bent_up_bars', True), + (190, 'stirrups', False), + (150, 'bent_up_bars', False), + ], +) +def test_check_slab_depth_for_shear_reinforcement( + h, reinforcement_type, expected +): + """Test slab depth check for various shear reinforcement types.""" + assert ( + _section12_detailing_members.check_slab_depth_for_shear_reinforcement( + h, reinforcement_type + ) + == expected + ) + + +@pytest.mark.parametrize( + 'd, reinforcement_type, expected', + [ + (200, 'single_leg', 10), + (300, 'closed_stirrups', 13.47), + (250, 'bent_up_bars', 17.88), + ], +) +def test_max_shear_reinforcement_diameter(d, reinforcement_type, expected): + """Test maximum effective diameter of shear reinforcement.""" + assert ( + _section12_detailing_members.slab_column_max_leg_shear_reinforcement_diameter( + d, reinforcement_type + ) + == pytest.approx(expected, rel=1e-2) + ) + + +@pytest.mark.parametrize( + 'dv, distance_to_column_edge, slab_type, expected', + [ + (200, 300, 'column_edge', 300.0), + (180, 360, 'flat_slab', 135.0), + (200, 100, 'column_base', 100.0), + ], +) +def test_tangential_spacing_limit( + dv, distance_to_column_edge, slab_type, expected +): + """Test tangential spacing limit based on + the distance to the column edge. + """ + assert _section12_detailing_members.slab_column_tangential_spacing_limit( + dv, distance_to_column_edge, slab_type + ) == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'As_int, fyd, kint, VEd, expected', + [ + (4000, 500, 'B', 730, 740.0), + (3000, 600, 'C', 900, 900), + ], +) +def test_integrity_reinforcement_vrd_int(As_int, fyd, kint, VEd, expected): + """Test integrity reinforcement resistance VRd,int.""" + assert _section12_detailing_members.Vrd_int_flat_slab( + As_int, fyd, kint, VEd + ) == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'rho_w, fywd, b0_5, dv, VEd, expected', + [ + (0.002, 500, 3000, 200, 800, 600.0), + (0.0015, 600, 3500, 180, 400, 400.0), + ], +) +def test_integrity_reinforcement_vrd_w_int( + rho_w, fywd, b0_5, dv, VEd, expected +): + """Test integrity reinforcement resistance VRd,w,int.""" + assert _section12_detailing_members.Vrd_w_int_flat_slab( + rho_w, fywd, b0_5, dv, VEd + ) == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'nhog, fck, gamma_c, phi, s, c, expected', + [ + (4, 30, 1.5, 16, 150, 30, 22.43), + (6, 40, 1.5, 20, 180, 35, 60.72), + ], +) +def test_hogging_reinforcement_vrd_hog( + nhog, fck, gamma_c, phi, s, c, expected +): + """Test hogging reinforcement resistance VRd,hog.""" + assert _section12_detailing_members.Vrd_hog_flat_slab( + nhog, fck, gamma_c, phi, s, c + ) == pytest.approx(expected, rel=1e-2) From 0149347c7d0f9118ffefd6031003a83623baae80 Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Wed, 21 Aug 2024 12:13:46 +0200 Subject: [PATCH 24/24] chapter to review --- structuralcodes/codes/ec2_2023/__init__.py | 84 ++++++++ .../ec2_2023/_section12_detailing_members.py | 198 ++++++++++++++++-- ...st_ec2_2023_section12_detailing_members.py | 137 ++++++++++++ 3 files changed, 407 insertions(+), 12 deletions(-) diff --git a/structuralcodes/codes/ec2_2023/__init__.py b/structuralcodes/codes/ec2_2023/__init__.py index 632d7ec5..daf73122 100644 --- a/structuralcodes/codes/ec2_2023/__init__.py +++ b/structuralcodes/codes/ec2_2023/__init__.py @@ -48,8 +48,92 @@ wk_cal, wk_cal2, ) +from ._section12_detailing_members import ( + As_column_min, + As_min, + As_min_bot, + As_slab_min_bottom_reinforcement_end_supports, + As_slab_min_bottom_reinforcement_inner_supports, + As_slab_min_secondary_reinforcement, + As_slab_min_top_reinforcement_end_supports, + As_w_min, + As_wall_min_h, + As_wall_min_v, + MRd_min, + Vrd_hog_flat_slab, + Vrd_int_flat_slab, + Vrd_w_int_flat_slab, + al_with_shear_reinforcement, + al_without_shear_reinforcement, + bottom_reinforcement_extension, + check_slab_depth_for_shear_reinforcement, + check_slab_shear_stress_conditions, + min_anchorage_length_bent_up_bar, + min_di_support_and_joint, + min_slab_reinforcement_along_free_edge, + rho_w_stir_min, + rho_w_tors_min, + s_max_circular_col, + s_max_col_end, + s_max_col_int, + s_max_poly_col, + s_max_wall_h, + s_max_wall_v, + s_slab_max, + s_stir_max, + sbu_max, + sbu_max_slab, + sl_max, + sl_max_slab, + sl_surf_max, + slab_column_max_leg_shear_reinforcement_diameter, + slab_column_tangential_spacing_limit, + str_max, + str_max_slab, +) __all__ = [ + 'As_column_min', + 'As_min', + 'As_min_bot', + 'As_slab_min_bottom_reinforcement_end_supports', + 'As_slab_min_bottom_reinforcement_inner_supports', + 'As_slab_min_secondary_reinforcement', + 'As_slab_min_top_reinforcement_end_supports', + 'As_w_min', + 'As_wall_min_h', + 'As_wall_min_v', + 'MRd_min', + 'Vrd_hog_flat_slab', + 'Vrd_int_flat_slab', + 'Vrd_w_int_flat_slab', + 'al_with_shear_reinforcement', + 'al_without_shear_reinforcement', + 'bottom_reinforcement_extension', + 'check_slab_depth_for_shear_reinforcement', + 'check_slab_shear_stress_conditions', + 'min_anchorage_length_bent_up_bar', + 'min_di_support_and_joint', + 'min_slab_reinforcement_along_free_edge', + 'rho_w_stir_min', + 'rho_w_tors_min', + 's_max_circular_col', + 's_max_col_end', + 's_max_col_int', + 's_max_poly_col', + 's_max_wall_h', + 's_max_wall_v', + 's_slab_max', + 's_stir_max', + 'sbu_max', + 'sbu_max_slab', + 'sl_max', + 'sl_max_slab', + 'sl_surf_max', + 'slab_column_max_leg_shear_reinforcement_diameter', + 'slab_column_tangential_spacing_limit', + 'str_max', + 'str_max_slab', 'A_phi_correction_exp', 'alpha_c_th', 'alpha_s_th', diff --git a/structuralcodes/codes/ec2_2023/_section12_detailing_members.py b/structuralcodes/codes/ec2_2023/_section12_detailing_members.py index 88d3fc6a..f145ff3a 100644 --- a/structuralcodes/codes/ec2_2023/_section12_detailing_members.py +++ b/structuralcodes/codes/ec2_2023/_section12_detailing_members.py @@ -959,7 +959,40 @@ def s_max_circular_col(n_bars: int, diameter: float) -> float: return min(diameter * math.pi / n_bars, 400) -def s_max_col(s_max_col: float, fck: float, fcd: float) -> float: +def s_max_col_int( + phi_l_max: float, h: float, b: float, long_bars_res: bool +) -> float: + """Calculate the maximum spacing of transverse + reinforcement for columns in the intermediate region. + + EN1992-1-1:2023 Table 12.3 + + Args: + phi_l_max (float): Maximum diameter of longitudinal bars in mm. + h (float): height of the column in mm. + b (float): width of the column in mm. + long_bars_res (bool): True if the longitudinal bars account + for column resistance. Otherwise False. + + Returns: + float: Maximum spacing of transverse reinforcement in mm. + + Raises: + ValueError: If any input value is negative. + """ + if phi_l_max < 0: + raise ValueError(f'phi_l_max must not be negative. Got {phi_l_max}') + if h < 0: + raise ValueError(f'h must not be negative. Got {h}') + if b < 0: + raise ValueError(f'b must not be negative. Got {b}') + if long_bars_res: + return min(h, b, 400) + + return min(20 * phi_l_max, h, b, 300) + + +def s_max_col_end(s_max_col: float) -> float: """Calculate the maximum spacing of transverse reinforcement for columns in the end region. @@ -967,23 +1000,164 @@ def s_max_col(s_max_col: float, fck: float, fcd: float) -> float: Args: s_max_col (float): Maximum spacing of transverse reinforcement in mm. - fck (float): Characteristic compressive strength of concrete in MPa. - fcd (float): Design compressive strength of concrete in MPa. Returns: float: Maximum spacing of transverse reinforcement in mm. Raises: - ValueError: If any input value is negative. + ValueError: If s_max_col is negative. """ if s_max_col < 0: raise ValueError(f's_max_col must not be negative. Got {s_max_col}') - if fck < 0: - raise ValueError(f'fck must not be negative. Got {fck}') - if fcd < 0: - raise ValueError(f'fcd must not be negative. Got {fcd}') + return 0.6 * s_max_col + + +def As_wall_min_v( + Ac: float, + fctm: float, + fyk: float, + design_case: Literal['in_plane_stress', 'compression_bending'], +) -> float: + """Calculate the minimum amount of vertical + reinforcement for walls and deep beams. + + EN1992-1-1:2023 Table 12.4 + + Args: + Ac (float): Cross-sectional area of the wall or deep beam . + fctm (float): Mean tensile strength of concrete in MPa. + fyk (float): Characteristic yield strength of reinforcement in MPa. + design_case (str): Design case + ('in_plane_stress' or 'compression_bending'). + + Returns: + float: Minimum area of vertical reinforcement in mm2. + + Raises: + ValueError: If any input value is negative or + if design_case is not recognized. + """ + if Ac < 0: + raise ValueError(f'Ac must not be negative. Got {Ac}') + if fctm < 0: + raise ValueError(f'fctm must not be negative. Got {fctm}') + if fyk < 0: + raise ValueError(f'fyk must not be negative. Got {fyk}') + + if design_case == 'in_plane_stress': + return 0.25 * Ac * fctm / fyk + + # If compression_bending + return 0.001 * Ac + + +def As_wall_min_h( + Ac: float, + As_v: float, + fctm: float, + fyk: float, + design_case: Literal['in_plane_stress', 'compression_bending'], +) -> float: + """Calculate the minimum amount of horizontal reinforcement + for walls and deep beams. + + EN1992-1-1:2023 Table 12.4 + + Args: + Ac (float): Cross-sectional area of the wall or deep beam in mm2. + As_v (float): Area of vertical reinforcement in mm2. + fctm (float): Mean tensile strength of concrete in MPa. + fyk (float): Characteristic yield strength of reinforcement in MPa. + design_case (str): Design case + ('in_plane_stress' or 'compression_bending'). + + Returns: + float: Minimum area of horizontal reinforcement in mm2. + + Raises: + ValueError: If any input value is negative or if + design_case is not recognized. + """ + if Ac < 0: + raise ValueError(f'Ac must not be negative. Got {Ac}') + if As_v < 0: + raise ValueError(f'As_v must not be negative. Got {As_v}') + if fctm < 0: + raise ValueError(f'fctm must not be negative. Got {fctm}') + if fyk < 0: + raise ValueError(f'fyk must not be negative. Got {fyk}') + + if design_case == 'in_plane_stress': + return 0.25 * Ac * fctm / fyk + # If compression_bending + return 0.25 * As_v + + +def s_max_wall_v(h: float) -> float: + """Calculate the maximum spacing of vertical reinforcement in walls. + + EN1992-1-1:2023 Table 12.4 + + Args: + h (float): Thickness of the wall in mm. + + Returns: + float: Maximum spacing of vertical reinforcement in mm. + + Raises: + ValueError: If h is negative. + """ + if h < 0: + raise ValueError(f'h must not be negative. Got {h}') + + return min(3 * h, 400) + + +def s_max_wall_h() -> float: + """Calculate the maximum spacing of horizontal reinforcement + in walls. + + EN1992-1-1:2023 Table 12.4 + + Returns: + float: Maximum spacing of horizontal reinforcement in mm. + """ + return 400 - if fck > 50: - confinement_min = 0.6 * s_max_col * fcd / fck - return min(s_max_col * 0.6, confinement_min) - return s_max_col * 0.6 + +def min_di_support_and_joint( + ch_i: float, + delta_a: float, + loop_type: Literal['horizontal_loops', 'vertical_bent'], + r_i: float = 0, +) -> float: + """Calculate the nominal length di of a simple support or bearing + based on the type of loop and reinforcement. + + EN1992-1-1:2023 12.10(5) + + Args: + ch_i (float): Nominal cover (horizontal or vertical) in mm. + delta_a (float): Allowance for construction deviations in mm. + r_i (float, optional): Mandrel radius of bend of longitudinal + reinforcement in mm. Required if loop_type is "vertical_bent". + loop_type (str): Type of loop ('horizontal_loops', 'vertical_bent'). + + Returns: + float: Nominal length di in mm. + + Raises: + ValueError: If any input value is negative. + """ + if ch_i < 0: + raise ValueError(f'ch_i must not be negative. Got {ch_i}') + if delta_a < 0: + raise ValueError(f'delta_a must not be negative. Got {delta_a}') + if r_i < 0: + raise ValueError(f'r_i must not be negative. Got {r_i}') + + if loop_type == 'horizontal_loops': + return ch_i + delta_a + + # If vertical_bent + return ch_i + delta_a + r_i diff --git a/tests/test_ec2_2023/test_ec2_2023_section12_detailing_members.py b/tests/test_ec2_2023/test_ec2_2023_section12_detailing_members.py index 8389f6a7..a48f3aa1 100644 --- a/tests/test_ec2_2023/test_ec2_2023_section12_detailing_members.py +++ b/tests/test_ec2_2023/test_ec2_2023_section12_detailing_members.py @@ -473,3 +473,140 @@ def test_hogging_reinforcement_vrd_hog( assert _section12_detailing_members.Vrd_hog_flat_slab( nhog, fck, gamma_c, phi, s, c ) == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'NEd, fyd, Ac, expected', + [ + (1000, 500, 400000, 800.0), + (2000, 600, 500000, 1000.0), + (1500, 400, 300000, 600.0), + ], +) +def test_As_column_min(NEd, fyd, Ac, expected): + """Test minimum longitudinal reinforcement calculation.""" + assert _section12_detailing_members.As_column_min( + NEd, fyd, Ac + ) == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'h, b, expected', + [ + (600, 300, 300.0), + (500, 500, 400.0), + (800, 450, 400.0), + ], +) +def test_s_max_poly_col(h, b, expected): + """Test maximum longitudinal spacing for polygonal cross-sections.""" + assert _section12_detailing_members.s_max_poly_col(h, b) == pytest.approx( + expected, rel=1e-2 + ) + + +@pytest.mark.parametrize( + 'n_bars, diameter, expected', + [ + (8, 300, 117.8), + (6, 400, 209.4), + (10, 500, 157.1), + ], +) +def test_s_max_circular_col(n_bars, diameter, expected): + """Test s_max_circular_col.""" + assert _section12_detailing_members.s_max_circular_col( + n_bars, diameter + ) == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'phi_l_max, h, b, long_bars_res, expected', + [ + (16, 600, 300, True, 300.0), + (16, 600, 300, False, 300.0), + (20, 500, 500, True, 400.0), + ], +) +def test_s_max_col_int(phi_l_max, h, b, long_bars_res, expected): + """Test s_max_col_int.""" + assert _section12_detailing_members.s_max_col_int( + phi_l_max, h, b, long_bars_res + ) == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 's_max_col, expected', + [ + (300, 180.0), + (250, 150.0), + ], +) +def test_s_max_col_end(s_max_col, expected): + """Test s_max_col_end.""" + assert _section12_detailing_members.s_max_col_end( + s_max_col + ) == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'Ac, fctm, fyk, design_case, expected', + [ + (200000, 2.9, 500, 'in_plane_stress', 290.0), + (200000, 2.9, 500, 'compression_bending', 200.0), + ], +) +def test_As_wall_min_v(Ac, fctm, fyk, design_case, expected): + """Test As_wall_min_v.""" + assert _section12_detailing_members.As_wall_min_v( + Ac, fctm, fyk, design_case + ) == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'Ac, As_v, fctm, fyk, design_case, expected', + [ + (200000, 300, 2.9, 500, 'in_plane_stress', 290.0), + (200000, 300, 2.9, 500, 'compression_bending', 75.0), + ], +) +def test_As_wall_min_h(Ac, As_v, fctm, fyk, design_case, expected): + """Test As_wall_min_h.""" + assert _section12_detailing_members.As_wall_min_h( + Ac, As_v, fctm, fyk, design_case + ) == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'h, expected', + [ + (100, 300.0), + (150, 400.0), + ], +) +def test_s_max_wall_v(h, expected): + """Test s_max_wall_v.""" + assert _section12_detailing_members.s_max_wall_v(h) == pytest.approx( + expected, rel=1e-2 + ) + + +def test_s_max_wall_h(): + """Test s_max_wall_h.""" + assert _section12_detailing_members.s_max_wall_h() == pytest.approx( + 400.0, rel=1e-2 + ) + + +@pytest.mark.parametrize( + 'ch_i, delta_a, r_i, loop_type, expected', + [ + (40, 10, 'horizontal_loops', 0, 50.0), + (40, 10, 'vertical_bent', 20, 70.0), + ], +) +def test_min_di_support_and_joint(ch_i, delta_a, r_i, loop_type, expected): + """Test min_di_support_and_joint.""" + assert _section12_detailing_members.min_di_support_and_joint( + ch_i, delta_a, r_i, loop_type + ) == pytest.approx(expected, rel=1e-2)