From 2e70179b15367ce42bda243cfccad7ab88ea210c Mon Sep 17 00:00:00 2001 From: Morten Engen Date: Tue, 13 Dec 2022 21:01:29 +0100 Subject: [PATCH 01/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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 20ff0104c4e6685824e8277e6bc3dea1e02b9b56 Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Thu, 22 Aug 2024 12:21:41 +0200 Subject: [PATCH 22/27] basic mc2020 config added to framework --- structuralcodes/codes/__init__.py | 4 +++- structuralcodes/codes/mc2020/__init__.py | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 structuralcodes/codes/mc2020/__init__.py diff --git a/structuralcodes/codes/__init__.py b/structuralcodes/codes/__init__.py index 40f6c6bd..cddba143 100644 --- a/structuralcodes/codes/__init__.py +++ b/structuralcodes/codes/__init__.py @@ -3,10 +3,11 @@ import types import typing as t -from . import ec2_2004, ec2_2023, mc2010 +from . import ec2_2004, ec2_2023, mc2010, mc2020 __all__ = [ 'mc2010', + 'mc2020', 'ec2_2023', 'ec2_2004', 'set_design_code', @@ -23,6 +24,7 @@ # Design code registry _DESIGN_CODES = { 'mc2010': mc2010, + 'mc2020': mc2020, 'ec2_2004': ec2_2004, 'ec2_2023': ec2_2023, } diff --git a/structuralcodes/codes/mc2020/__init__.py b/structuralcodes/codes/mc2020/__init__.py new file mode 100644 index 00000000..f4782d6f --- /dev/null +++ b/structuralcodes/codes/mc2020/__init__.py @@ -0,0 +1,7 @@ +"""The fib Model Code 2020.""" + +import typing as t + +__title__: str = 'fib Model Code 2020' +__year__: str = '2020' +__materials__: t.Tuple[str] = ('concrete', 'reinforcement') From ba1a41769dd1d42990bab9e32f9fe5ff54637c5e Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Thu, 22 Aug 2024 13:21:37 +0200 Subject: [PATCH 23/27] mc2020 test module --- tests/test_mc2020/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 tests/test_mc2020/__init__.py diff --git a/tests/test_mc2020/__init__.py b/tests/test_mc2020/__init__.py new file mode 100644 index 00000000..d738a442 --- /dev/null +++ b/tests/test_mc2020/__init__.py @@ -0,0 +1 @@ +"""Collection of tests.""" From f06f0242e73f75a9054f6bb7eebe15daa7c91b10 Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Tue, 27 Aug 2024 11:41:22 +0200 Subject: [PATCH 24/27] chapter finished --- .../mc2020/_concrete_material_properties.py | 2833 +++++++++++++++++ ...est_mc2020_concrete_material_properties.py | 1363 ++++++++ 2 files changed, 4196 insertions(+) create mode 100644 structuralcodes/codes/mc2020/_concrete_material_properties.py create mode 100644 tests/test_mc2020/test_mc2020_concrete_material_properties.py diff --git a/structuralcodes/codes/mc2020/_concrete_material_properties.py b/structuralcodes/codes/mc2020/_concrete_material_properties.py new file mode 100644 index 00000000..de35e40a --- /dev/null +++ b/structuralcodes/codes/mc2020/_concrete_material_properties.py @@ -0,0 +1,2833 @@ +"""fib MC2020 Chapter 14.6.1.""" + +import math +from typing import List, Literal, Optional, Tuple + + +def fcm(fck: float, delta_f: float = 8) -> float: + """Calculate the mean compressive strength fcm based on the + characteristic compressive strength fck. + + fib Model Code 2020, eq. (14.6-1) + + Args: + fck (float): Characteristic compressive strength fck in MPa. + delta_f (float): increment in MPa. Defaults to 8MPa. + + Returns: + float: Mean compressive strength fcm in MPa. + + Raises: + ValueError: If fck is negative or zero. + """ + if fck <= 0: + raise ValueError( + 'Characteristic compressive strength fck must be positive.' + ) + + return fck + delta_f + + +def flcm(flck: float, delta_f: float = 8) -> float: + """Calculate the mean compressive strength flcm for lightweight + aggregate concrete based on the characteristic + compressive strength flck. + + fib Model Code 2020, eq. (14.6-2) + + Args: + flck (float): Characteristic compressive strength flck in MPa. + + Returns: + float: Mean compressive strength flcm in MPa. + delta_f (float): increment in MPa. Defaults to 8MPa. + + Raises: + ValueError: If flck is negative or zero. + """ + if flck <= 0: + raise ValueError( + 'Characteristic compressive strength flck must be positive.' + ) + + return flck + delta_f + + +# Dictionary for normal concrete grades and +# their respective fck and fck,cube values +_concrete_strengths = { + 'C12': {'fck': 12, 'fck_cube': 15}, + 'C16': {'fck': 16, 'fck_cube': 20}, + 'C20': {'fck': 20, 'fck_cube': 25}, + 'C25': {'fck': 25, 'fck_cube': 30}, + 'C30': {'fck': 30, 'fck_cube': 37}, + 'C35': {'fck': 35, 'fck_cube': 45}, + 'C40': {'fck': 40, 'fck_cube': 50}, + 'C45': {'fck': 45, 'fck_cube': 55}, + 'C50': {'fck': 50, 'fck_cube': 60}, + 'C55': {'fck': 55, 'fck_cube': 67}, + 'C60': {'fck': 60, 'fck_cube': 75}, + 'C70': {'fck': 70, 'fck_cube': 85}, + 'C80': {'fck': 80, 'fck_cube': 95}, + 'C90': {'fck': 90, 'fck_cube': 105}, + 'C100': {'fck': 100, 'fck_cube': 115}, + 'C110': {'fck': 110, 'fck_cube': 130}, + 'C120': {'fck': 120, 'fck_cube': 140}, + 'LC8': {'fck': 8, 'fck_cube': 9}, + 'LC12': {'fck': 12, 'fck_cube': 13}, + 'LC16': {'fck': 16, 'fck_cube': 18}, + 'LC20': {'fck': 20, 'fck_cube': 22}, + 'LC25': {'fck': 25, 'fck_cube': 28}, + 'LC30': {'fck': 30, 'fck_cube': 33}, + 'LC35': {'fck': 35, 'fck_cube': 38}, + 'LC40': {'fck': 40, 'fck_cube': 44}, + 'LC45': {'fck': 45, 'fck_cube': 50}, + 'LC50': {'fck': 50, 'fck_cube': 55}, + 'LC55': {'fck': 55, 'fck_cube': 60}, + 'LC60': {'fck': 60, 'fck_cube': 66}, + 'LC70': {'fck': 70, 'fck_cube': 77}, + 'LC80': {'fck': 80, 'fck_cube': 88}, +} + + +def fck( + grade: Literal[ + 'C12', + 'C16', + 'C20', + 'C25', + 'C30', + 'C35', + 'C40', + 'C45', + 'C50', + 'C55', + 'C60', + 'C70', + 'C80', + 'C90', + 'C100', + 'C110', + 'C120', + 'LC8', + 'LC12', + 'LC16', + 'LC20', + 'LC25', + 'LC30', + 'LC35', + 'LC40', + 'LC45', + 'LC50', + 'LC55', + 'LC60', + 'LC70', + 'LC80', + ], +) -> float: + """Retrieve the characteristic strength values (fck) + for a given con. + + fib Model Code 2020, Tables 14.6-1, 14.6-2 + + Args: + grade (str): Concrete grade (e.g., 'C20', 'C30', 'LC12'). + + Returns: + dict: fck in MPa. + + Raises: + ValueError: If the grade is not found. + + Reference: + + """ + if grade not in _concrete_strengths: + raise ValueError( + f'Invalid concrete grade: {grade}. ' + + f'Available grades are {list(_concrete_strengths.keys())}.' + ) + + return _concrete_strengths[grade]['fck'] + + +def fck_cube( + grade: Literal[ + 'C12', + 'C16', + 'C20', + 'C25', + 'C30', + 'C35', + 'C40', + 'C45', + 'C50', + 'C55', + 'C60', + 'C70', + 'C80', + 'C90', + 'C100', + 'C110', + 'C120', + 'LC8', + 'LC12', + 'LC16', + 'LC20', + 'LC25', + 'LC30', + 'LC35', + 'LC40', + 'LC45', + 'LC50', + 'LC55', + 'LC60', + 'LC70', + 'LC80', + ], +) -> float: + """Retrieve the characteristic cube strength values (fck_cube) + for a given con. + + fib Model Code 2020, Tables 14.6-1, 14.6-2 + + Args: + grade (str): Concrete grade (e.g., 'C20', 'C30', 'LC12'). + + Returns: + dict: fck_cube in MPa. + + Raises: + ValueError: If the grade is not found. + + Reference: + + """ + if grade not in _concrete_strengths: + raise ValueError( + f'Invalid concrete grade: {grade}. ' + + f'Available grades are {list(_concrete_strengths.keys())}.' + ) + + return _concrete_strengths[grade]['fck_cube'] + + +def fctm(fck: float) -> float: + """Calculate the mean tensile strength fctm for normal weight concrete. + + fib Model Code 2020, eq. (14.6-3) + + Args: + fck (float): Characteristic compressive strength fck in MPa. + + Returns: + float: Mean tensile strength fctm in MPa. + + Raises: + ValueError: If fck is negative or zero. + """ + if fck <= 0: + raise ValueError( + 'Characteristic compressive strength fck must be positive.' + ) + + return 1.8 * math.log(fck) - 3.1 + + +def fctk_min(fctm: float) -> float: + """Calculate the lower bound characteristic tensile strength fctk,min. + + fib Model Code 2020, eq. (14.6-4) + + Args: + fctm (float): Mean tensile strength fctm in MPa. + + Returns: + float: Lower bound characteristic tensile strength fctk,min in MPa. + """ + return 0.7 * fctm + + +def fctk_max(fctm: float) -> float: + """Calculate the upper bound characteristic tensile strength fctk,max. + + fib Model Code 2020, eq. (14.6-5) + + Args: + fctm (float): Mean tensile strength fctm in MPa. + + Returns: + float: Upper bound characteristic tensile strength fctk,max in MPa. + """ + return 1.3 * fctm + + +def flctm(fctm: float, density: float) -> float: + """Calculate the mean tensile strength flctm for + lightweight aggregate concrete. + + fib Model Code 2020, eqs. (14.6-6a) and (14.6-6b) + + Args: + fctm (float): Mean tensile strength for normal weight concrete in MPa. + density (float): Oven-dry density of the lightweight + aggregate concrete in kg/m3. + + Returns: + float: Mean tensile strength flctm in MPa. + + Raises: + ValueError: If density is outside the + reasonable range for lightweight aggregate concrete. + """ + if density < 1000 or density > 2200: + raise ValueError( + 'Density must be between 1000 and 2200 kg/m³ ' + + 'for lightweight aggregate concrete.' + ) + + eta_l = 0.4 + 0.6 * (density / 2200) + return eta_l * fctm + + +def flctk_min(flctm: float) -> float: + """Calculate the lower bound characteristic + tensile strength flctk,min for lightweight aggregate concrete. + + fib Model Code 2020, similar to eq. (14.6-4) + + Args: + flctm (float): Mean tensile strength flctm in MPa. + + Returns: + float: Lower bound characteristic tensile strength flctk,min in MPa. + """ + return 0.7 * flctm + + +def flctk_max(flctm: float) -> float: + """Calculate the upper bound characteristic tensile strength + flctk,max for lightweight aggregate concrete. + + fib Model Code 2020, similar to eq. (14.6-5) + + Args: + flctm (float): Mean tensile strength flctm in MPa. + + Returns: + float: Upper bound characteristic tensile strength flctk,max in MPa. + """ + return 1.3 * flctm + + +def fctm_from_splitting(fctm_sp: float, alpha_sp: float = 1.0) -> float: + """Calculate the mean uniaxial tensile strength + fctm from the mean splitting tensile strength fctm,sp. + + fib Model Code 2020, eq. (14.6-7) + + Args: + fctm_sp (float): Mean splitting tensile strength fctm,sp in MPa. + alpha_sp (float): Conversion factor (default is 1.0). + + Returns: + float: Mean uniaxial tensile strength fctm iun MPa. + """ + return alpha_sp * fctm_sp + + +def fctm_from_flexural(fctm_fl: float, hb: float) -> float: + """Calculate the mean uniaxial tensile strength + fctm from the mean flexural tensile strength fctm,fl. + + fib Model Code 2020, eqs. (14.6-8a) and (14.6-8b) + + Args: + fctm_fl (float): Mean flexural tensile strength fctm,fl in MPa. + hb (float): Beam depth in mm. + + Returns: + float: Mean uniaxial tensile strength fctm in MPa. + + Raises: + ValueError: If hb is non-positive. + """ + if hb <= 0: + raise ValueError(f'Beam depth hb must be positive. Got {hb}') + + alpha_fl = 0.06 * hb**0.7 / (1 + 0.06 * hb**0.7) + return alpha_fl * fctm_fl + + +def GF(fck: float) -> float: + """Calculate the fracture energy GF for normal weight concrete. + + fib Model Code 2020, eq. (14.6-9) + + Args: + fck (float): Characteristic compressive strength fck in MPa. + + Returns: + float: Fracture energy GF in N/m. + + Raises: + ValueError: If fck is negative or zero. + """ + if fck <= 0: + raise ValueError( + 'Characteristic compressive strength fck must be positive.' + ) + + return 85 * fck**0.15 + + +def GF_l(flctm: float, use_normal_weight_sand: bool = True) -> float: + """Calculate the fracture energy GF,l for lightweight aggregate concrete. + + fib Model Code 2020, eq. (14.6-10) + + Args: + flctm (float): Mean tensile strength flctm in MPa. + use_normal_weight_sand (bool): True if using normal + weight sand, False if using lightweight sand. + + Returns: + float: Fracture energy GF,l in N/m. + + Raises: + ValueError: If flctm is negative or zero. + """ + if flctm <= 0: + raise ValueError('Mean tensile strength flctm must be positive.') + + G_FoA = 24 if use_normal_weight_sand else 0 + return G_FoA + 16 * flctm + + +def multiaxial_stress_equation( + sigma_1: float, + sigma_2: float, + sigma_3: float, + fcm: float, + fctm: float, + fc2cm: float, + sigma_com: Optional[float] = None, + tau_com: Optional[float] = None, + is_lightweight: bool = False, +) -> float: + """Returns the symbolic equation for the mean + strength under multiaxial states of stress. The result + of this evaluation must be equal to zero to + fulfill the failure criterion. So a custom + solver defined by the user can be used. + + fib Model Code 2020, eq. (14.6-11 to 14.6-19.b) + + Args: + sigma_1 (float): first principal stress in MPa. + sigma_2 (float): second principal stress in MPa. + sigma_3 (float): third principal stress in MPa. + fcm (float): mean compressive strength of concrete in MPa. Use + flcm for lightweight concrete. + fctm (float): mean tensile strength of concrete in MPa. Use flctm + for lightweight concrete. + fc2cm (float): biaxial compressive strength in MPa. + sigma_com (float): triaxial compressive strength at on point + on the compressive meridian in MPa. (sigma_1 = sigma_2 > sigma_3). + If None, it computes the recommended value. + tau_com (float): triaxial comrpessive shear strength at one point + on the compressive meridian in MPa. (sigma_1 = sigma_2 > sigma_3). + If None, it computes the recommended value. + is_lightweight (bool): True if lightweight concrete. False otherwise. + + Returns: + float: the result of the evaluation of the multiaxial stress. + When this result is equal to zero, the equilibrium is met. + A custom solver/standard solver can be used passing the 3 + principal stresses (sigma_1, sigma_2, sigma_3). + """ + # Compute the invariants + I1 = sigma_1 + sigma_2 + sigma_3 + sigma_m = I1 / 3 + J2 = (1 / 6) * ( + (sigma_1 - sigma_2) ** 2 + + (sigma_2 - sigma_3) ** 2 + + (sigma_3 - sigma_1) ** 2 + ) + J3 = (sigma_1 - sigma_m) * (sigma_2 - sigma_m) * (sigma_3 - sigma_m) + + # Compute sigma_com and tau_com if not specified + if sigma_com is None: + sigma_com = -240 if not is_lightweight else -60 + if tau_com is None: + if is_lightweight: + tau_com = ( + 185 + - 180 * (fcm / 100) + + 260 * (fcm / 100) ** 2 + - 84 * (fcm / 100) ** 3 + ) + else: + tau_com = ( + 250 * (fcm / 100) + - 460 * (fcm / 100) ** 2 + + 310 * (fcm / 100) ** 3 + ) + + # Compute k, f2c, x, y, h, alpha and beta + k = fctm / fcm + f2c = fc2cm / fcm + x = sigma_com / fcm + y = tau_com / fcm + h = -(math.sqrt(2) * x + y) / ((y / math.sqrt(2)) - (1 / 3)) + beta = (math.sqrt(2) - (3 * y) / (k * f2c)) / (h - (9 * y) / (f2c - k)) + alpha = (h * beta - math.sqrt(2)) / y + + # Compute lambda_c and lambda_t + lambda_c = ( + (1 - h / (3 * y)) * math.sqrt(3) * beta + + math.sqrt(3) + + math.sqrt(2) / (math.sqrt(3) * y) + ) + lambda_t = ( + 2 * math.sqrt(3) + - (f2c * h) / (math.sqrt(3) * y) * beta + + math.sqrt(3) / f2c + + (math.sqrt(2) * f2c) / (math.sqrt(3) * y) + ) + lambda_r = lambda_c / lambda_t + + # Compute theta + cos_3_theta = (3 * math.sqrt(3) / 2) * J3 / (J2 ** (3 / 2)) + theta = math.acos(cos_3_theta) / 3 + cos_theta = math.cos(theta) + + # Compute c1 and c2 + if lambda_r <= 1 / 2: + c2 = 1 + else: + c2 = math.cos(3 * math.atan((2 * lambda_r - 1) / (math.sqrt(3)))) + + if lambda_r <= 1 / 2: + c1 = (2 * cos_theta - 1) * lambda_t + 4 * (1 - cos_theta) * lambda_c + else: + c1 = lambda_c / (math.cos(math.pi / 3 - 1 / 3 * math.acos(c2))) + + # Compute lambda + _lambda = c1 * math.cos(1 / 3 * math.acos(c2 * cos_3_theta)) + + # Return the total result + return ( + (alpha * J2 / fcm**2) + + (_lambda * math.sqrt(J2) / fcm) + + (beta * I1 / fcm) + - 1 + ) + + +def E_ci(fcm: float, alpha_E: float = 1.0, Ec0: float = 21500) -> float: + """Calculate the modulus of elasticity Eci for normal + weight concrete at 28 days. + + fib Model Code 2020, eq. (14.6-20) + + Args: + fcm (float): Mean compressive strength fck in MPa. + alpha_E (float): Coefficient depending on + the aggregate type (default is 1.0 for quartzite). + Ec0 (float): base elastic modulus value in MPa. + + Returns: + float: Modulus of elasticity E_ci in MPa. + + Raises: + ValueError: If fck or alpha_E is invalid. + """ + if fcm <= 0: + raise ValueError( + 'Characteristic compressive strength fck must be positive.' + ) + if not (0.7 <= alpha_E <= 1.2): + raise ValueError('Coefficient alpha_E must be between 0.7 and 1.2.') + + return alpha_E * Ec0 * (fcm / 10) ** (1 / 3) + + +def El_ci( + fcm: float, density: float, alpha_E: float = 1.0, Ec0: float = 21500 +) -> float: + """Calculate the modulus of elasticity Elci for + lightweight aggregate concrete. + + fib Model Code 2020, eq. (14.6-22) + + Args: + fcm (float): Mean compressive strength fcm in MPa. + density (float): Oven-dry density of the lightweight + aggregate concrete in kg/m3. + alpha_E (float): Coefficient depending on the aggregate + type (default is 1.0 for quartzite). + Ec0 (float): base elastic modulus value in MPa. + + Returns: + float: Modulus of elasticity El_ci in MPa. + + Raises: + ValueError: If fcm, density, or alpha_E is invalid. + """ + if fcm <= 0: + raise ValueError('Mean compressive strength fcm must be positive.') + if density <= 0: + raise ValueError('Density must be positive.') + if not (0.7 <= alpha_E <= 1.2): + raise ValueError('Coefficient alpha_E must be between 0.7 and 1.2.') + + eta_E = (density / 2200) ** 2 + + Eci = alpha_E * Ec0 * (fcm / 10) ** (1 / 3) + return eta_E * Eci + + +def E_ci_red_el(Eci: float, fcm: float) -> float: + """Calculate the reduced modulus of elasticity Ec for concrete + for only static analysis. + + fib Model Code 2020, eq. (14.6-23) (14.6-24) + + Args: + Eci (float): Modulus of elasticity Eci in MPa. + fcm (float): Mean compressive strength fcm in MPa. + + Returns: + float: Reduced modulus of elasticity Ec in MPa. + + Raises: + ValueError: If Eci or fcm is negative or zero. + """ + if Eci < 0: + raise ValueError('Modulus of elasticity Eci must be positive.') + + alpha_i = min(0.8 + 0.2 * fcm / 88, 1) + return alpha_i * Eci + + +def nu_c() -> float: + """Get the Poisson's ratio νc for concrete for design purposes. + + fib Model Code 2020 (14.6.1.4.3) + + Returns: + float: Poisson's ratio νc for concrete, + typically 0.20 for design purposes. + """ + return 0.20 + + +def sigma_c( + eps_c: float, + eps_c1: float, + eps_lim: float, + Eci: float, + Ec1: float, + fcm: float, + k: float, +) -> float: + """Calculate the compressive stress σc for short-term + uniaxial compression of concrete. + + fib Model Code 2020, eq. (14.6-26) + + Args: + eps_c (float): Compressive strain εc. + eps_c1 (float): Strain at maximum compressive stress εc1. + eps_lim (float): Limit strain. + Eci (float): Modulus of elasticity of concrete in MPa. + Ec1 (float): Secant modulus of elasticity from origin + to peak stress Ec1 in MPa. + fcm (float): Mean compressive strength fcm in MPa. + k (float): Plasticity number k. + + Returns: + float: Compressive stress σc in MPa. + + Raises: + ValueError: If input values are not positive or within valid ranges. + """ + if eps_c < 0 or eps_c1 < 0 or Ec1 < 0 or fcm < 0 or k < 0: + raise ValueError('All input values must be positive.') + if eps_c > eps_lim: + raise ValueError('eps_c must be equal or less than eps_lim') + + # Calculate η (eta) + eta = eps_c / eps_c1 + k = Eci / Ec1 + + # Calculate compressive stress σc using the given formula + return -(k * eta - eta**2) / (1 + (k - 2) * eta) + + +# Dictionary for concrete properties based on grade +concrete_properties = { + 'C12': { + 'Eci': 27.1, + 'Ec1': 11.1, + 'epsilon_c1': 1.9, + 'epsilon_c_lim': 3.5, + 'k': 2.44, + }, + 'C16': { + 'Eci': 28.8, + 'Ec1': 12.2, + 'epsilon_c1': 2.0, + 'epsilon_c_lim': 3.5, + 'k': 2.36, + }, + 'C20': { + 'Eci': 30.3, + 'Ec1': 13.3, + 'epsilon_c1': 2.1, + 'epsilon_c_lim': 3.5, + 'k': 2.28, + }, + 'C25': { + 'Eci': 32.0, + 'Ec1': 14.9, + 'epsilon_c1': 2.2, + 'epsilon_c_lim': 3.5, + 'k': 2.15, + }, + 'C30': { + 'Eci': 33.6, + 'Ec1': 16.5, + 'epsilon_c1': 2.3, + 'epsilon_c_lim': 3.5, + 'k': 2.04, + }, + 'C35': { + 'Eci': 35.0, + 'Ec1': 18.2, + 'epsilon_c1': 2.3, + 'epsilon_c_lim': 3.5, + 'k': 1.92, + }, + 'C40': { + 'Eci': 36.3, + 'Ec1': 20.0, + 'epsilon_c1': 2.4, + 'epsilon_c_lim': 3.5, + 'k': 1.82, + }, + 'C45': { + 'Eci': 37.5, + 'Ec1': 21.6, + 'epsilon_c1': 2.5, + 'epsilon_c_lim': 3.5, + 'k': 1.74, + }, + 'C50': { + 'Eci': 38.6, + 'Ec1': 23.2, + 'epsilon_c1': 2.6, + 'epsilon_c_lim': 3.4, + 'k': 1.66, + }, + 'C55': { + 'Eci': 39.7, + 'Ec1': 24.7, + 'epsilon_c1': 2.6, + 'epsilon_c_lim': 3.4, + 'k': 1.61, + }, + 'C60': { + 'Eci': 40.7, + 'Ec1': 26.2, + 'epsilon_c1': 2.7, + 'epsilon_c_lim': 3.3, + 'k': 1.55, + }, + 'C70': { + 'Eci': 42.6, + 'Ec1': 28.9, + 'epsilon_c1': 2.7, + 'epsilon_c_lim': 3.2, + 'k': 1.47, + }, + 'C80': { + 'Eci': 44.4, + 'Ec1': 31.4, + 'epsilon_c1': 2.8, + 'epsilon_c_lim': 3.1, + 'k': 1.41, + }, + 'C90': { + 'Eci': 46.0, + 'Ec1': 33.8, + 'epsilon_c1': 2.9, + 'epsilon_c_lim': 3.0, + 'k': 1.36, + }, + 'C100': { + 'Eci': 47.5, + 'Ec1': 36.0, + 'epsilon_c1': 3.0, + 'epsilon_c_lim': 3.0, + 'k': 1.32, + }, + 'C110': { + 'Eci': 48.9, + 'Ec1': 39.3, + 'epsilon_c1': 3.0, + 'epsilon_c_lim': 3.0, + 'k': 1.24, + }, + 'C120': { + 'Eci': 50.3, + 'Ec1': 42.7, + 'epsilon_c1': 3.0, + 'epsilon_c_lim': 3.0, + 'k': 1.18, + }, +} + + +def Ec1(concrete_grade: str) -> float: + """Get the secant modulus of elasticity Ec1 for a given concrete grade. + + Args: + concrete_grade (str): Concrete grade (e.g., 'C20', 'C30'). + + Returns: + float: Secant modulus of elasticity Ec1 in MPa. + + Raises: + ValueError: If the concrete grade is not recognized. + """ + if concrete_grade not in concrete_properties: + raise ValueError( + f'Invalid concrete grade: {concrete_grade}. ' + + f'Available grades are {list(concrete_properties.keys())}.' + ) + + return concrete_properties[concrete_grade]['Ec1'] * 1000 + + +def eps_c1(concrete_grade: str) -> float: + """Get the strain at maximum compressive stress + εc1 for a given concrete grade. + + Args: + concrete_grade (str): Concrete grade (e.g., 'C20', 'C30'). + + Returns: + float: Strain at maximum compressive stress εc1. + + Raises: + ValueError: If the concrete grade is not recognized. + """ + if concrete_grade not in concrete_properties: + raise ValueError( + f'Invalid concrete grade: {concrete_grade}. ' + + f'Available grades are {list(concrete_properties.keys())}.' + ) + + return concrete_properties[concrete_grade]['epsilon_c1'] / 1000 + + +def eps_c_lim(concrete_grade: str) -> float: + """Get the strain limit εc,lim for a given concrete grade. + + Args: + concrete_grade (str): Concrete grade (e.g., 'C20', 'C30'). + + Returns: + float: Strain limit εc,lim. + + Raises: + ValueError: If the concrete grade is not recognized. + """ + if concrete_grade not in concrete_properties: + raise ValueError( + f'Invalid concrete grade: {concrete_grade}. ' + + f'Available grades are {list(concrete_properties.keys())}.' + ) + + return concrete_properties[concrete_grade]['epsilon_c_lim'] / 1000 + + +def eps_lc1( + flck: float, Elc: float, sand: Literal['light', 'natural'] +) -> float: + """Calculate the strain εlc1 for lightweight aggregate concrete. + + fib Model Code 2020, eq. (14.6-27) + + Args: + flck (float): Characteristic compressive strength flck in MPa. + E_lc (float): Modulus of elasticity E_lc in MPa. + sand (float): Specifies the type of sand used. + + Returns: + float: Strain εlc1. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if flck < 0 or Elc <= 0: + raise ValueError( + 'Characteristic compressive strength f_lck ' + + 'and modulus of elasticity E_lc must be positive.' + ) + + kappa_lc = 1.1 if sand == 'light' else 1.3 + return kappa_lc * (flck + 8) / Elc + + +def sigma_ct_uncracked(eps_ct: float, E_ci: float, fctm: float) -> float: + """Calculate the tensile stress σct for uncracked + normal weight concrete subjected to tension. + + fib Model Code 2020, eqs. (14.6-29) and (14.6-30) + + Args: + eps_ct (float): Tensile strain εct. + E_ci (float): Tangent modulus of elasticity Eci in MPa. + fctm (float): Mean tensile strength fctm in MPa. + + Returns: + float: Tensile stress σct in MPa. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if E_ci <= 0 or fctm < 0: + raise ValueError( + 'Modulus of elasticity Eci and tensile ' + + 'strength fctm must be positive.' + ) + + sigma_ct_linear = E_ci * eps_ct + + if sigma_ct_linear <= 0.9 * fctm: + return sigma_ct_linear + if sigma_ct_linear <= fctm: + return fctm * ( + 1 - 0.1 * (0.00015 - eps_ct) / (0.00015 - 0.9 * fctm / E_ci) + ) + raise ValueError('Stress is larger than mean concrete tensile stress.') + + +def sigma_ct_cracked(w: float, GF: float, fctm: float) -> float: + """Calculate the tensile stress σct for cracked normal + weight concrete subjected to tension. + + fib Model Code 2020, eqs. (14.6-31) and (14.6-32) + + Args: + w (float): Crack opening w in mm. + GF (float): Fracture energy GF in N/mm. + fctm (float): Mean tensile strength fctm in MPa. + + Returns: + float: Tensile stress σct in MPa. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if GF <= 0 or fctm <= 0: + raise ValueError( + 'Fracture energy GF and tensile strength fctm must be positive.' + ) + + w1 = GF / fctm + wc = 5 * GF / fctm + + if w <= w1: + return fctm * (1 - 0.8 * w / w1) + if w <= wc: + return fctm * (0.25 - 0.05 * w / w1) + + raise ValueError('w cannot be larger than wc') + + +# Chapter 14.6.1.5.3 Multiaxial states of stress skipped + + +def tau_crack_friction( + delta: float, w: float, fcm: float, Cf: float = 1.0 +) -> float: + """Calculate the mean shear stress τ in an open + crack subjected to shear displacements. + + fib Model Code 2020, eq. (14.6-43) + + Args: + delta (float): Shear displacement δ in mm. + w (float): Crack width w in mm. + fcm (float): Mean compressive strength fcm in MPa. + Cf (float): Constant C (default is 1.0). + + Returns: + float: Mean shear stress τ in MPa. + + Raises: + ValueError: If input values are not within valid ranges. + + """ + if delta <= 0 or w <= 0 or fcm <= 0: + raise ValueError( + 'Shear displacement δ, crack width w, ' + + 'and mean compressive strength fcm must be positive.' + ) + + return Cf * ( + -0.04 * fcm + (1.8 * w**-0.8 + (0.292 * w**-0.7 - 0.25) * fcm) * delta + ) + + +def sigma_crack_friction( + delta: float, w: float, fcm: float, Cf: float = 1.0 +) -> float: + """Calculate the mean normal stress σ in an open + crack subjected to shear displacements. + + fib Model Code 2020, eq. (14.6-44) + + Args: + delta (float): Shear displacement δ in mm. + w (float): Crack width w in mm. + fcm (float): Mean compressive strength fcm in MPa. + Cf (float): Constant C (default is 1.0). + + Returns: + float: Mean normal stress σ in MPa. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if delta <= 0 or w <= 0 or fcm <= 0: + raise ValueError( + 'Shear displacement δ, crack width w, and ' + + 'mean compressive strength fcm must be positive.' + ) + + return Cf * ( + -0.06 * fcm + + (1.35 * w**-0.63 + (0.242 * w**-0.55 - 0.19) * fcm) * delta + ) + + +SC_dict = {'CR': [0.3, 0.2, 0.1], 'CN': [0.5, 0.4, 0.3], 'CS': [0.6, 0.5, 0.4]} + + +def sC(fck: float, strength_class: Literal['CR', 'CN', 'CS']) -> float: + """Get the coefficient sC based on concrete strength class and fck. + + fib Model Code 2020, Table (14.6-7) + + Args: + f_ck (float): Characteristic compressive strength fck in MPa. + strength_class (str): Strength development class ('CR', 'CN', or 'CS'). + + Returns: + float: Coefficient sC. + + Raises: + ValueError: If the input values are not valid. + """ + if fck <= 0: + raise ValueError( + 'Characteristic compressive strength fck must be positive.' + ) + + if strength_class not in ['CR', 'CN', 'CS']: + raise ValueError("Strength class must be 'CR', 'CN', or 'CS'.") + + t = SC_dict[strength_class] + if fck <= 35: + return t[0] + if fck < 60: + return t[1] + return t[2] + + +def beta_cc(t: float, t_ref: float, sC: float) -> float: + """Calculate the strength development function βcc(t) for concrete. + + fib Model Code 2020, eq. (14.6-46) + + + Args: + t (float): Concrete age t in days. + t_ref (float): Reference age tref in days. + sC (float): Coefficient sC based on strength development class. + + Returns: + float: Strength development function βcc(t). + + Raises: + ValueError: If the input values are not valid. + """ + if t <= 0 or t_ref <= 0: + raise ValueError( + 'Concrete age t and reference age tref must be positive.' + ) + + return math.exp(sC * (1 - (t_ref / t) ** 0.5) * ((28 / t_ref) ** 0.5)) + + +def fcm_t(fcm: float, beta_cc: float) -> float: + """Calculate the mean compressive strength fcm(t) at a given age t. + + fib Model Code 2020, eq. (14.6-45) + + Args: + fcm (float): Mean compressive strength at reference age fcm in MPa. + beta_cc (float): Strength development function βcc(t). + + Returns: + float: Mean compressive strength fcm(t) in MPa. + + Raises: + ValueError: If the input values are not valid. + """ + if fcm <= 0 or beta_cc <= 0: + raise ValueError('Mean compressive strength fcm must be positive.') + + return beta_cc * fcm + + +def slC(aggregate_strength: Literal['high', 'low']) -> float: + """Get the coefficient slC based on the strength of lightweight aggregate. + + fib Model Code 2020, eq. (14.6-47) + + Args: + aggregate_strength (str): Strength of + lightweight aggregate ('high' or 'low'). + + Returns: + float: Coefficient slC. + + Raises: + ValueError: If the input values are not valid. + """ + if aggregate_strength == 'high': + return 0.05 + # if aggregate_strength == 'low': + return 0.25 + + +def beta_lcc(t: float, t_ref: float, slC: float) -> float: + """Calculate the strength development function βlcc(t) + for lightweight aggregate concrete. + + fib Model Code 2020, eq. (14.6-47) + + Args: + t (float): Concrete age t in days. + t_ref (float): Reference age tref ins days (typically 28 days). + slC (float): Coefficient slc based on aggregate strength. + + Returns: + float: Strength development function βlcc(t). + + Raises: + ValueError: If the input values are not valid. + """ + if t <= 0 or t_ref <= 0: + raise ValueError( + 'Concrete age t and reference age tref must be positive.' + ) + + return math.exp(slC * (1 - (t_ref / t) ** 0.5) * ((28 / t_ref) ** 0.5)) + + +def flcm_t(f_lcm: float, beta_lcc: float) -> float: + """Calculate the mean compressive strength flcm(t) at + a given age t for lightweight aggregate concrete. + + fib Model Code 2020, eq. (14.6-47) + + Args: + f_lcm (float): Mean compressive strength at reference age flcm in MPa. + beta_lcc (float): Strength development function βlcc(t). + + Returns: + float: Mean compressive strength flcm(t) in MPa. + + Raises: + ValueError: If the input values are not valid. + """ + if f_lcm <= 0 or beta_lcc <= 0: + raise ValueError('Mean compressive strength flcm must be positive.') + + return beta_lcc * f_lcm + + +def beta_E(beta_cc: float) -> float: + """Calculate the modulus of elasticity development + coefficient βE(t) for concrete. + + fib Model Code 2020, eq. (14.6-49) + + Args: + beta_cc (float) + + Returns: + float: Modulus of elasticity development coefficient βE(t). + + Raises: + ValueError: If the input values are not valid. + """ + return beta_cc**0.33 + + +def E_ci_t(E_ci: float, beta_E: float) -> float: + """Calculate the modulus of elasticity Eci(t) at a given age t. + + fib Model Code 2020, eq. (14.6-48) + + Args: + E_ci (float): Modulus of elasticity at reference age Eci in MPa. + beta_E (float): Modulus of elasticity development coefficient βE(t). + + Returns: + float: Modulus of elasticity Eci(t) in MPa. + + Raises: + ValueError: If the input values are not valid. + """ + if E_ci <= 0: + raise ValueError('Modulus of elasticity Eci must be positive.') + + return beta_E * E_ci + + +def beta_t0(t0: float) -> float: + """Calculate the parameter βt0(t0) which + considers the maturity of the concrete at loading. + + fib Model Code 2020, eq. (14.6-51) + + Args: + t0 (float): Age of concrete at loading t0 in days. + + Returns: + float: Parameter βt0(t0). + + Raises: + ValueError: If the input values are not valid. + """ + if t0 < 7: + raise ValueError( + 'Age of concrete at loading t0 must be at least 7 days.' + ) + + return 0.64 + 0.01 * math.log(t0) + + +def beta_c_sus(t: float, t0: float, beta_t0: float) -> float: + """Calculate the function βc,sus(t, t0) describing the + decrease of strength with time under high sustained load. + + fib Model Code 2020, eq. (14.6-51) + + Args: + t (float): Concrete age t in days. + t0 (float): Age of concrete at loading t0 in days. + beta_t0 (float): Value of evaluated function beta_T0. + + Returns: + float: Function βc,sus(t, t0). + + Raises: + ValueError: If the input values are not valid. + """ + if t <= t0: + raise ValueError( + 'Concrete age t must be greater than age at loading t0.' + ) + + return beta_t0 + (1 - beta_t0) * (1 + 10000 * (t - t0) / t0) ** -0.1 + + +def fcm_sus(fcm: float, beta_cc_t: float, beta_c_sus: float) -> float: + """Calculate the mean compressive strength + fcm,sus(t, t0) under sustained load. + + fib Model Code 2020, eq. (14.6-50) + + Args: + f_cm (float): Mean compressive strength at reference age fcm in MPa. + beta_cc_t (float): Strength development function βcc(t). + beta_c_sus_t_t0 (float): Function βc,sus(t, t0) describing + the decrease of strength under high sustained load. + + Returns: + float: Mean compressive strength fcm,sus(t, t0) in MPa. + + Raises: + ValueError: If the input values are not valid. + """ + if fcm <= 0: + raise ValueError('Mean compressive strength fcm must be positive.') + + return beta_cc_t * beta_c_sus * fcm + + +def fctk_sus( + fctk: float, concrete: Literal['normal', 'high-strength'] +) -> float: + """Calculate the sustained tensile strength fctk,sus + for concrete under sustained loading. + + fib Model Code 2020, eq. (14.6-52) + + Args: + fctk (float): Short-term tensile strength fctk in MPa. + alpha (float): Reduction factor for sustained loading + (default is 0.60). + + Returns: + float: Sustained tensile strength fctk,sus in MPa. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if fctk <= 0: + raise ValueError('Short-term tensile strength f_ctk must be positive.') + + alpha = 0.60 if concrete == 'normal' else 0.75 + return alpha * fctk + + +def eps_cc(sigma_c_t0: float, phi_t_t0: float, E_ci: float) -> float: + """Calculate the creep strain εcc(t, t0) at time + t for a concrete member under sustained loading. + + fib Model Code 2020, eq. (14.6-55) + + Args: + sigma_c_t0 (float): Constant stress applied at time t0, σc(t0) in MPa. + phi_t_t0 (float): Creep coefficient φ(t, t0). + E_ci (float): Modulus of elasticity at the age of 28 days, Eci in MPa. + + Returns: + float: Creep strain εcc(t, t0). + + Raises: + ValueError: If input values are not within valid ranges + """ + if E_ci <= 0: + raise ValueError('Modulus of elasticity Eci must be positive.') + + return (sigma_c_t0 / E_ci) * phi_t_t0 + + +def eps_c_sigma( + sigma_c_t0: float, E_ci_t0: float, E_ci: float, phi_t_t0: float +) -> float: + """Calculate the stress-dependent strain εcσ(t, t0) at time t + for a concrete member under sustained loading. + + fib Model Code 2020, eq. (14.6-56) + + Args: + sigma_c_t0 (float): value of the stress at time t0 in MPa. + E_ci_t0 (float): modulus of elasticity at time t0 in MPa. + E_ci (float): modulus of elasticity of concrete in MPa. + phi_t_t0 (float): Creep coefficient φ(t, t0). + + Returns: + float: Stress-dependent strain εcσ(t, t0). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if E_ci_t0 <= 0 or sigma_c_t0 < 0 or E_ci <= 0 or phi_t_t0 < 0: + raise ValueError('Modulus of elasticity Eci(t0) must be positive.') + + return sigma_c_t0 * (1 / E_ci_t0 + phi_t_t0 / E_ci) + + +def phi_t_t0(phi_bc_t_t0: float, phi_dc_t_t0: float) -> float: + """Calculate the total creep coefficient φ(t, t0) + for concrete under sustained loading. + + fib Model Code 2020, eq. (14.6-58) + + Args: + phi_bc_t_t0 (float): basic creep coefficient. + phi_dc_t_t0 (float): drying creep coefficient. + + Returns: + float: Total creep coefficient φ(t, t0). + + Raises: + ValueError: If input values are not within valid ranges. + """ + return phi_bc_t_t0 + phi_dc_t_t0 + + +def beta_bc_fcm(fcm: float) -> float: + """Calculate the mean compressive strength factor + (fcm) for drying creep coefficient. + + fib Model Code 2020, eq. (14.6-60) + + Args: + fcm (float): Mean compressive strength at an age of 28 days fcm in MPa. + + Returns: + float: Mean compressive strength factor β_bc_fcm(f_cm). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if fcm <= 0: + raise ValueError('Mean compressive strength fcm must be positive.') + + return 1.8 / fcm**0.7 + + +def beta_bc_t_t0(t: float, t0: float, t0_adj: float) -> float: + """Calculte the beta_bc coefficient evaluated for + t,t0. + + fib Model Code 2020, eq. (14.6-61) + + Args: + t (float): Age of concrete at the moment considered in days. + t0 (float): Age of concrete at loading in days. + t0_adj (float): Adjusted age at loading t0,adj in dats. + + Returns: + float: the value of beta_bc. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if t <= t0_adj: + raise ValueError( + 'Age of concrete t must be greater than' + + ' adjusted age at loading t0_adj.' + ) + if t0_adj <= 0: + raise ValueError( + 'Adjusted age at loading t0_adj and mean' + + ' compressive strength f_cm must be positive.' + ) + return math.log((30 / t0_adj + 0.0035) ** 2 * (t - t0) + 1) + + +def phi_bc_t_t0(beta_bc_t_t0: float, beta_bc_fcm: float) -> float: + """Calculate the basic creep coefficient φbc(t, t0) + for concrete under sustained loading. + + fib Model Code 2020, eqs. (14.6-59) + + Args: + beta_bc_t_t0 (float): Value of beta_bc_t_t0. + beta_bc_fcm (float): value of beta_bc_fcm. + + Returns: + float: Basic creep coefficient φbc(t, t0). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if beta_bc_t_t0 <= 0 or beta_bc_fcm <= 0: + raise ValueError('beta_bc_t_t0 and beta_bc_fcm should be positive.') + + return beta_bc_fcm * beta_bc_t_t0 + + +def beta_dc_fcm(fcm: float) -> float: + """Calculate the mean compressive strength factor + βf_cm(fcm) for drying creep coefficient. + + fib Model Code 2020, eq. (14.6-63) + + Args: + fcm (float): Mean compressive strength at an age of 28 days fcm in MPa. + + Returns: + float: Mean compressive strength factor βf_cm(f_cm). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if fcm <= 0: + raise ValueError('Mean compressive strength f_cm must be positive.') + + return 412 / fcm**1.4 + + +def beta_RH_c(RH: float, h: float) -> float: + """Calculate the relative humidity factor βRH(RH) + for drying creep coefficient. + + fib Model Code 2020, eq. (14.6-64) + + Args: + RH (float): Relative humidity of the ambient environment in %. + h (float): Notional size of the member h in mm. + + Returns: + float: Relative humidity factor βRH(RH). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if RH < 0 or RH > 100: + raise ValueError('Relative humidity RH must be between 0 and 100.') + if h <= 0: + raise ValueError('Notional size h must be positive.') + + return (1 - RH / 100) / (0.1 * h / 100) ** (1 / 3) + + +def beta_dc_t0(t0_adj: float) -> float: + """Calculate the time factor βt0(t0) for drying creep coefficient. + + fib Model Code 2020, eq. (14.6-65) + + Args: + t0_adj (float): Adjusted age at loading t0,adj in days. + + Returns: + float: Time factor βt0(t0). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if t0_adj <= 0: + raise ValueError('Adjusted age at loading t0_adj must be positive.') + + return 1 / (0.1 + t0_adj**0.2) + + +def gamma_t0(t0_adj: float) -> float: + """Calculate the factor γ for drying creep development with time. + + fib Model Code 2020, eq. (14.6-66b) + + Args: + t0_adj (float): Adjusted age at loading t0,adj in days. + + Returns: + float: Factor γ. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if t0_adj <= 0: + raise ValueError('t0_adj must be positive.') + + return 1 / (2.3 + 3.5 / math.sqrt(t0_adj)) + + +def beta_h(h: float, fcm: float) -> float: + """Calculate the factor beta_h for drying creep development with time. + + fib Model Code 2020, eqs. (14.6-66c), (14.6-66d) + + Args: + h (float): Notional size of the member h in mm. + fcm (float): Mean compressive strength at an age of 28 days fcm in MPa. + + Returns: + float: Factor beta_h. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if h <= 0 or fcm <= 0: + raise ValueError( + 'Notional size h and mean compressive' + + ' strength fcm must be positive.' + ) + + alpha_fcm = (35 / fcm) ** 0.5 + return min(1.5 * h + 250 * alpha_fcm, 1500 * alpha_fcm) + + +def beta_dc_t_t0(t: float, t0: float, gamma_t0: float, beta_h: float) -> float: + """Calculate the development of drying creep with time βdc(t, t0). + + fib Model Code 2020, eq. (14.6-66a) + + Args: + t (float): Age of concrete at the moment considered in days. + t0 (float): Age of concrete at loading t0 in days. + h (float): Notional size of the member h in mm. + gamma_t0 (float): gamma_t0 coefficient. + beta_h (float): beta_h coefficient. + + + Returns: + float: Development of drying creep with time βdc(t, t0). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if t <= t0: + raise ValueError( + 'Age of concrete t must be greater than age at loading t0.' + ) + if gamma_t0 <= 0: + raise ValueError('Notional size h must be positive.') + + return ((t - t0) / (beta_h + (t - t0))) ** gamma_t0 + + +def phi_dc_t_t0( + beta_dc_fm: float, + beta_RH: float, + beta_dc_t0: float, + beta_dc_t_t0: float, +) -> float: + """Calculate the drying creep coefficient φdc(t, t0) + for concrete under sustained loading. + + fib Model Code 2020, eq. (14.6-62) + + Args: + beta_dc_fm (float): beta_dc_fm coefficient. + beta_RH (float): beta_RH coefficient. + beta_dc_t0 (float): beta_dc_t0 coefficient. + beta_dc_t_t0 (float): beta_dc_t_t0 coefficient. + + Returns: + float: Drying creep coefficient φdc(t, t0). + + Raises: + ValueError: If input values are not within valid ranges. + """ + return beta_dc_fm * beta_RH * beta_dc_t0 * beta_dc_t_t0 + + +def phi_l_t_t0(phi_t_t0: float, rho: float, concrete_grade: str) -> float: + """Calculate the creep coefficient φl for lightweight aggregate concrete. + + fib Model Code 2020, eq. (14.6-67) + + Args: + phi_t_t0 (float): Creep coefficient φ(t, t0) + according to Eq. (14.6-58). + rho (float): Oven-dry density of + lightweight aggregate concrete in kg/m3. + concrete_grade (str): Grade of concrete (e.g., 'LC12', 'LC16', 'LC20'). + + Returns: + float: Creep coefficient φl for lightweight aggregate concrete. + + Raises: + ValueError: If input values are not within + valid ranges or invalid grade. + """ + if rho <= 0: + raise ValueError('Oven-dry density ρ must be positive.') + if phi_t_t0 < 0: + raise ValueError('Creep coefficient φ(t, t0) must not be negative.') + + # Calculate ηE + eta_E = (rho / 2200) ** 2 + + # Calculate the initial creep coefficient for lightweight concrete + phi_l = eta_E * phi_t_t0 + + # Adjust for concrete grades LC12 and LC16 + if concrete_grade in ['LC12', 'LC16']: + phi_l *= 1.3 + + return phi_l + + +def t0_adj(t0_T: float, strength_class: Literal['CS', 'CN', 'CR']) -> float: + """Calculate the adjusted age at loading t0,adj for + concrete under sustained loading. + + fib Model Code 2020, eq. (14.6-68) + + Args: + t0_T (float): Age of concrete at loading in + days adjusted according to Eq. (14.6-80). + strength_class (str): Strength development + class of concrete ('CS', 'CN', 'CR'). + + Returns: + float: Adjusted age at loading t0,adj in days. + + Raises: + ValueError: If input values are not + within valid ranges or invalid class. + """ + if t0_T <= 0: + raise ValueError('Age of concrete at loading t0_T must be positive.') + + if strength_class == 'CS': + alpha = -1 + if strength_class == 'CN': + alpha = 0 + if strength_class == 'CR': + alpha = 1 + + return max(0.5, t0_T * (9 / (2 + t0_T**1.2) + 1) ** alpha) + + +def phi_sigma_t_t0(phi_t_t0: float, sigma_c: float, fcm_t0: float) -> float: + """Calculate the non-linear creep coefficient φσ(t, t0) + for concrete under high stress. + + fib Model Code 2020, eq. (14.6-69) + + Args: + phi_t_t0 (float): Linear creep coefficient φ(t, t0) + according to Eq. (14.6-58). + sigma_c (float): Applied stress in MPa. + fcm_t0 (float): Mean compressive strength of concrete + at the age of loading t0 in MPa. + + Returns: + float: Non-linear creep coefficient φσ(t, t0). + + Raises: + ValueError: If input values are not within valid ranges or + if stress-strength ratio is not within 0.4 < kσ ≤ 0.6. + """ + if phi_t_t0 < 0: + raise ValueError( + 'Linear creep coefficient φ(t, t0) must not be negative.' + ) + if fcm_t0 <= 0: + raise ValueError( + 'Mean compressive strength f_cm(t0) must be positive.' + ) + + # Calculate the stress-strength ratio + k_sigma = abs(sigma_c) / fcm_t0 + + # Calculate the non-linear creep coefficient + return phi_t_t0 * math.exp(1.50 * (k_sigma - 0.4)) + + +def eps_cs_t_ts(eps_cbs_t: float, eps_cds_t_ts: float) -> float: + """Calculate the total shrinkage strain εcs(t, ts). + + fib Model Code 2020, eq. (14.6-70) + + Args: + eps_cbs_t (float): Basic shrinkage strain εcbs(t). + eps_cds_t_ts (float): Drying shrinkage strain εcds(t, ts). + + Returns: + float: Total shrinkage strain εcs(t, ts). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if eps_cbs_t < 0: + raise ValueError( + 'Basic shrinkage strain εcbs(t) must not be negative.' + ) + if eps_cds_t_ts < 0: + raise ValueError( + 'Drying shrinkage strain εcds(t, ts) must not be negative.' + ) + + return eps_cbs_t + eps_cds_t_ts + + +def eps_cbs_t(eps_cbs0: float, beta_bs_t: float) -> float: + """Calculate the basic shrinkage strain εcbs(t). + + fib Model Code 2020, eq. (14.6-71) + + Args: + eps_cbs0 (float): Basic shrinkage strain coefficient εcbs0. + beta_bs_t (float): Development function for basic shrinkage βbs(t). + + Returns: + float: Basic shrinkage strain εcbs(t). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if eps_cbs0 < 0: + raise ValueError( + 'Basic shrinkage strain coefficient εcbs0 must not be negative.' + ) + if beta_bs_t < 0: + raise ValueError('Development function βbs(t) must not be negative.') + + return eps_cbs0 * beta_bs_t + + +def eps_cds_t_ts( + eps_cds0: float, beta_RH: float, beta_ds_t_ts: float +) -> float: + """Calculate the drying shrinkage strain εcds(t, ts). + + fib Model Code 2020, eq. (14.6-72) + + Args: + eps_cds0 (float): Drying shrinkage strain coefficient εcds0. + beta_RH (float): Relative humidity factor βRH(RH). + beta_ds_t_ts (float): Development function for + drying shrinkage βds(t - ts). + + Returns: + float: Drying shrinkage strain εcds(t, ts). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if eps_cds0 < 0: + raise ValueError( + 'Drying shrinkage strain coefficient εcds0 must not be negative.' + ) + if beta_RH < 0 or beta_RH > 1: + raise ValueError( + 'Relative humidity factor βRH must be between 0 and 1.' + ) + if beta_ds_t_ts < 0: + raise ValueError( + 'Development function βds(t - ts) must not be negative.' + ) + + return eps_cds0 * beta_RH * beta_ds_t_ts + + +def eps_cbs_0(fcm: float, alpha_bs: float) -> float: + """Calculate the basic notional shrinkage coefficient εcbs0(fcm). + + fib Model Code 2020, eq. (14.6-73) + + Args: + fcm (float): Mean compressive strength at the age of + 28 days fcm in MPa. + alpha_bs (float): Coefficient dependent on + the strength development class of concrete. + + Returns: + float: Basic notional shrinkage coefficient εcbs0(fcm). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if fcm <= 0: + raise ValueError('Mean compressive strength f_cm must be positive.') + if alpha_bs < 0: + raise ValueError('Coefficient alpha_bs must not be negative.') + + return -alpha_bs * (((0.1 * fcm) / (6 + 0.1 * fcm)) ** 2.5) * 10e-6 + + +def beta_bs_t(t: float) -> float: + """Calculate the time function for basic shrinkage βbs(t). + + fib Model Code 2020, eq. (14.6-74) + + Args: + t (float): Age of concrete in days. + + Returns: + float: Time function for basic shrinkage βbs(t). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if t < 0: + raise ValueError('Age of concrete t must not be negative.') + + return 1 - math.exp(-0.2 * t**0.5) + + +# Alpha bs, alpha_ds_1 and alpha_ds_2 coefficients +alpha_coefficients = { + 'CS': (800, 3, 0.013), + 'CN': (700, 4, 0.012), + 'CR': (600, 6, 0.012), +} + + +def alpha_bs(strength_class: Literal['CS', 'CN', 'CR']) -> tuple: + """Retrieve the coefficient αbs strength development class of concrete. + + Args: + strength_class (str): The strength development + class of concrete ('CS', 'CN', 'CR'). + + Returns: + float: the value of αbs. + + Raises: + ValueError: If the strength development class is not recognized. + """ + # Convert input to uppercase to avoid case sensitivity issues + strength_class = strength_class.upper() + + # Retrieve the coefficients based on the provided + # strength development class + if strength_class in alpha_coefficients: + return alpha_coefficients[strength_class][0] + raise ValueError( + "Invalid strength development class. Choose 'CS', 'CN', or 'CR'." + ) + + +def alpha_ds_1(strength_class: Literal['CS', 'CN', 'CR']) -> tuple: + """Retrieve the coefficient αds1 strength development class of concrete. + + Args: + strength_class (str): The strength development + class of concrete ('CS', 'CN', 'CR'). + + Returns: + float: the value of αds1. + + Raises: + ValueError: If the strength development class is not recognized. + """ + # Convert input to uppercase to avoid case sensitivity issues + strength_class = strength_class.upper() + + # Retrieve the coefficients based on the provided + # strength development class + if strength_class in alpha_coefficients: + return alpha_coefficients[strength_class][1] + raise ValueError( + "Invalid strength development class. Choose 'CS', 'CN', or 'CR'." + ) + + +def alpha_ds_2(strength_class: Literal['CS', 'CN', 'CR']) -> tuple: + """Retrieve the coefficient αds2 strength development class of concrete. + + Args: + strength_class (str): The strength development + class of concrete ('CS', 'CN', 'CR'). + + Returns: + float: the value of αds2. + + Raises: + ValueError: If the strength development class is not recognized. + """ + # Convert input to uppercase to avoid case sensitivity issues + strength_class = strength_class.upper() + + # Retrieve the coefficients based on the provided + # strength development class + if strength_class in alpha_coefficients: + return alpha_coefficients[strength_class][2] + raise ValueError( + "Invalid strength development class. Choose 'CS', 'CN', or 'CR'." + ) + + +def eps_cds_0_fcm(fcm: float, alpha_ds1: float, alpha_ds2: float) -> float: + """Calculate the notional drying shrinkage coefficient εcds0(fcm). + + fib Model Code 2020, eq. (14.6-75) + + Args: + fcm (float): Mean compressive strength at the age of 28 days in MPa. + alpha_ds1 (float): Coefficient dependent on the strength + development class of concrete. + alpha_ds2 (float): Coefficient dependent on the strength + development class of concrete. + + Returns: + float: Notional drying shrinkage coefficient εcds0(fcm). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if fcm <= 0: + raise ValueError('Mean compressive strength f_cm must be positive.') + + return ((220 + 110 * alpha_ds1) * math.exp(-alpha_ds2 * fcm)) * 10e-6 + + +def beta_RH_s(RH: float, RH_eq: float) -> float: + """Calculate the relative humidity factor βRH(RH) for shrinkage. + + fib Model Code 2020, eq. (14.6-76) + + Args: + RH (float): Relative humidity of the ambient atmosphere in %. + RH_eq (float): value of equivalent humidity. + + Returns: + float: Relative humidity factor βRH(RH). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if RH < 20 or RH > 100: + raise ValueError('Relative humidity RH must be between 0 and 100.') + + ratio = RH / RH_eq + if 20 <= RH <= RH_eq: + return -1.55 * (1 - ratio**3) + if RH_eq < RH < 100: + return -1.55 * (1 - ratio**2) + if RH == 100: + return -1.55 * (1 - ratio**2) + 0.25 + + raise ValueError('Relative humidity RH is out of defined range.') + + +def beta_ds_t_ts(t: float, ts: float, h: float) -> float: + """Calculate the time-development function + for drying shrinkage βds(t - ts). + + fib Model Code 2020, eq. (14.6-77) + + Args: + t (float): Age of concrete in days. + ts (float): Age of concrete at the beginning of drying in days. + h (float): Notional size of the member in mm. + + Returns: + float: Time-development function for drying shrinkage βds(t - ts). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if t < ts: + raise ValueError( + 'Concrete age t must be greater than or ' + + 'equal to the beginning of drying ts.' + ) + if h <= 0: + raise ValueError('Notional size h must be positive.') + + t_diff = t - ts + return (t_diff / (0.0035 * h**2 + t_diff)) ** 0.5 + + +def RH_eq(fcm: float) -> float: + """Calculate the equivalent relative humidity RH_eq for a given + fcm. + + Args: + fcm (float): Mean compressive strength at the age of 28 days in MPa. + """ + if fcm <= 0: + raise ValueError('Mean compressive strength f_cm must be positive.') + + return min(99, 99 * (35 / fcm) ** 0.1) + + +def eps_lcs_t_ts( + eps_cs_t_ts: float, + concrete_grade: Literal[ + 'LC8', + 'LC12', + 'LC16', + 'LC20', + 'LC25', + 'LC30', + 'LC35', + 'LC40', + 'LC45', + 'LC50', + 'LC55', + 'LC60', + 'LC70', + 'LC80', + ], +) -> float: + """Calculate the shrinkage of lightweight aggregate concrete εlcs(t, ts). + + fib Model Code 2020, eq. (14.6-79) + + Args: + eps_cs_t_ts (float): Shrinkage strain for normal + weight concrete εcs(t, ts). + concrete_grade (str): Grade of lightweight aggregate concrete + (e.g., 'LC8', 'LC12', 'LC16', 'LC20'). + + Returns: + float: Shrinkage strain for lightweight aggregate concrete εlcs(t, ts). + + Raises: + ValueError: if invalid concrete grade. + """ + # Define eta values for different concrete grades + eta_values = { + 'LC8': 1.5, + 'LC12': 1.5, + 'LC16': 1.5, + 'LC20': 1.2, + 'LC25': 1.2, + 'LC30': 1.2, + 'LC35': 1.2, + 'LC40': 1.2, + 'LC45': 1.2, + 'LC50': 1.2, + 'LC55': 1.2, + 'LC60': 1.2, + 'LC70': 1.2, + 'LC80': 1.2, + } + + # Convert input to uppercase to avoid case sensitivity issues + concrete_grade = concrete_grade.upper() + + # Retrieve the eta value based on the provided concrete grade + if concrete_grade in eta_values: + eta = eta_values[concrete_grade] + else: + raise ValueError( + 'Invalid concrete grade. Choose from ' + + "'LC8', 'LC12', 'LC16', 'LC20', and higher." + ) + + # Calculate shrinkage for lightweight aggregate concrete + return eta * eps_cs_t_ts + + +def t_T_maturity( + temperature_intervals: List[Tuple[float, float]], +) -> float: + """Calculate the temperature-adjusted concrete age tT. + + fib Model Code 2020, eq. (14.6-80) + + Args: + temperature_intervals (List[Tuple[float, float]]): A list of tuples + where each tuple contains + (Δti, T(Δti)), where Δti is the number of days + where a temperature T prevails, and T(Δti) is the + mean temperature in °C during the time period Δti. + + Returns: + float: Temperature-adjusted concrete age tT in days. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if not temperature_intervals: + raise ValueError('Temperature intervals list must not be empty.') + + t_T = 0.0 + + for delta_t, T in temperature_intervals: + if delta_t < 0: + raise ValueError('Number of days Δti must not be negative.') + if T < -273.15: + raise ValueError( + 'Temperature T(Δti) must be above absolute zero (-273°C).' + ) + + t_T += delta_t * math.exp(13.65 - 4000 / (273 + T)) + + return t_T + + +def eps_c_T(delta_T: float, alpha_T: str) -> float: + """Calculate the thermal strain εcT due to thermal expansion of concrete. + + fib Model Code 2020, eq. (14.6-81) + + Args: + delta_T (float): Change in temperature in Kelvin (K). + alpha_T (str): thermal expecansion coefficient. + + Returns: + float: Thermal strain εcT. + + Raises: + ValueError: If input values are not within + valid ranges or invalid concrete type. + """ + if alpha_T < 0: + raise ValueError('alpha_T must not be negative.') + + # Calculate thermal strain + return alpha_T * delta_T + + +def alpha_T(concrete_type: Literal['normal', 'lightweight']) -> float: + """Return the thermal expansion coefficient as a function + of the concrete type. + + Args: + concrete_type (str): concrete type. + + Returns: + float: the thermal expansion coefficient in K-1 + """ + return 8e-6 if concrete_type == 'lightweight' else 10e-6 + + +def fcm_T(fcm: float, T: float) -> float: + """Calculate the compressive strength fcm(T) for + normal weight concrete at a given temperature. + + fib Model Code 2020, eq. (14.6-82) + + Args: + fcm (float): Compressive strength in MPa at T = 20°C. + T (float): Temperature in °C. + + Returns: + float: Compressive strength fcm(T) at temperature T in MPa. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if fcm <= 0: + raise ValueError('Compressive strength fcm must be positive.') + + return fcm * (1.06 - 0.003 * T) + + +def flcm_T(flcm: float, T: float) -> float: + """Calculate the compressive strength flcm(T) + for lightweight aggregate concrete at a given temperature. + + fib Model Code 2020, eq. (14.6-83) + + Args: + flcm (float): Compressive strength in MPa at T = 20°C. + T (float): Temperature in °C. + + Returns: + float: Compressive strength flcm(T) at temperature T in MPa. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if flcm <= 0: + raise ValueError('Compressive strength flcm must be positive.') + + return flcm * (1.04 - 0.002 * T) + + +def fcm_hT_T(fcm_href_Tref: float, D_fc: float, S_fc: float) -> float: + """Calculate the compressive strength fcm(h_T, T) under drying conditions. + + fib Model Code 2020, eq. (14.6-84) + + Args: + fcm_href_Tref (float): Compressive strength in MPa + at Tref = 20°C at reference storage conditions. + D_fc (float): Drying effect parameter. + S_fc (float): Thermal dilation incompatibility factor. + + Returns: + float: Compressive strength fcm(h_T, T) in MPa. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if fcm_href_Tref <= 0: + raise ValueError('Compressive strength f_cm_ref must be positive.') + + return fcm_href_Tref * (1 + D_fc + S_fc) + + +def D_fc(h_Tref: float) -> float: + """Calculate the drying effect parameter D_fc. + + fib Model Code 2020, eq. (14.6-85) + + Args: + T (float): Temperature in °C. + h_Tref (float): Relative humidity of the + concrete pores at temperature T. + + Returns: + float: Drying effect parameter D_fc. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if h_Tref < 0 or h_Tref > 1: + raise ValueError('Relative humidity h_T must be between 0 and 1.') + + return -0.32 * h_Tref**26 + 0.32 + + +def hT_Tref(hT: float, K_hT: float, T: float, T_ref: float = 20.0) -> float: + """Calculate the relative humidity in the concrete pores h_Tref. + + fib Model Code 2020, eq. (14.6-86) + + Args: + hT (float): Relative humidity at reference temperature T_ref. + K_hT (float): Coefficient for computing the relative humidity. + T (float): Temperature in °C. + T_ref (float): Reference temperature in °C, default is 20°C. + + Returns: + float: Relative humidity h_Tref in the concrete pores at temperature T. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if hT < 0 or hT > 1: + raise ValueError('Relative humidity h_ref must be between 0 and 1.') + + return hT - K_hT * (T - T_ref) + + +def K_hT(hT: float) -> float: + """Calculate the humidity effect coefficient K_hT. + + fib Model Code 2020, eq. (14.6-87) + + Args: + h_ref (float): Relative humidity at reference temperature T_ref. + + Returns: + float: Humidity effect coefficient K_hT. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if hT < 0 or hT > 1: + raise ValueError('Relative humidity h_ref must be between 0 and 1.') + + return 0.0135 * hT * (1 - hT) / (1.25 - hT) + + +def S_fc(T: float, alpha_fc: float) -> float: + """Calculate the thermal dilation incompatibility factor S_fc. + + fib Model Code 2020, eq. (14.6-88) + + Args: + T (float): Temperature in °C. + alpha_fc (float): Alpha coefficient. + + Returns: + float: Thermal dilation incompatibility factor S_fc. + + Raises: + ValueError: If input values are not within valid ranges. + """ + ratio = (T - 20) / 100 + return 0.35 * ratio**2 + alpha_fc * ratio + + +def alpha_fc(h_Tref: float) -> float: + """Calculate the alpha coefficient alpha_fc. + + fib Model Code 2020, eq. (14.6-89) + + Args: + h_Tref (float): Relative humidity at reference temperature T_ref. + + Returns: + float: Alpha coefficient alpha_fc. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if h_Tref < 0 or h_Tref > 1: + raise ValueError('Relative humidity h_ref must be between 0 and 1.') + + return 2.3e-9 * math.exp(19.1 * h_Tref) - 0.55 * math.exp(0.64 * h_Tref) + + +def fctm_T(fctm: float, T: float) -> float: + """Calculate the uniaxial tensile strength fctm(T) + for concrete at a given temperature. + + fib Model Code 2020, eq. (14.6-90) + + Args: + fctm (float): Uniaxial tensile strength in MPa at T = 20°C. + T (float): Temperature in °C. + + Returns: + float: Uniaxial tensile strength fctm(T) at temperature T in MPa. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if fctm <= 0: + raise ValueError('Uniaxial tensile strength fctm must be positive.') + return fctm * (1.16 - 0.008 * T) + + +def GF_T(GF: float, T: float, concrete_type: Literal['dry', 'mass']) -> float: + """Calculate the fracture energy GF(T) for dry + concrete at a given temperature. + + fib Model Code 2020, eq. (14.6-93a) + + Args: + GF (float): Fracture energy in N/m at T = 20°C. + T (float): Temperature in °C. + concrete_type (str): Concrete type. + + Returns: + float: Fracture energy G_F(T) at temperature T in N/m. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if GF <= 0: + raise ValueError('Fracture energy GF must be positive.') + + return ( + GF * (1.06 - 0.003 * T) + if concrete_type == 'dry' + else GF * (1.12 - 0.006 * T) + ) + + +def fctm_hT_T(fctm_ref: float, D_fct: float, S_fct: float) -> float: + """Calculate the uniaxial tensile strength f_ctm(h_T, T) under + combined effects of temperature and drying. + + fib Model Code 2020, eq. (14.6-94) + + Args: + fctm_ref (float): Uniaxial tensile strength in MPa at Tref = 20°C. + D_fct (float): Drying effect parameter. + S_fct (float): Thermal dilation incompatibility factor. + + Returns: + float: Uniaxial tensile strength f_ctm(h_T, T) in MPa. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if fctm_ref <= 0: + raise ValueError( + 'Uniaxial tensile strength f_ctm_ref must be positive.' + ) + + return fctm_ref * (1 + D_fct + S_fct) + + +def D_fct(T: float, hT_Tref: float) -> float: + """Calculate the drying effect parameter D_fct. + + fib Model Code 2020, eq. (14.6-95) + + Args: + T (float): Temperature in °C. + hT_Tref (float): Relative humidity of the + concrete pores at temperature T. + + Returns: + float: Drying effect parameter D_fct. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if T < 20 or T > 80: + raise ValueError('Temperature T must be in the range 20°C ≤ T ≤ 80°C.') + if hT_Tref < 0 or hT_Tref > 1: + raise ValueError('Relative humidity h_T must be between 0 and 1.') + + return -0.44 * hT_Tref**4 + 0.44 + + +def S_fct(T: float, alpha_fct: float) -> float: + """Calculate the thermal dilation incompatibility factor S_fct. + + fib Model Code 2020, eq. (14.6-96) + + Args: + T (float): Temperature in °C. + alpha_fct (float): Alpha coefficient. + + Returns: + float: Thermal dilation incompatibility factor S_fct. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if T < 20 or T > 80: + raise ValueError('Temperature T must be in the range 20°C ≤ T ≤ 80°C.') + + ratio = (T - 20) / 100 + return 0.45 * ratio**2 + alpha_fct * ratio + + +def alpha_fct(hT_Tref: float) -> float: + """Calculate the alpha coefficientalpha_fct. + + fib Model Code 2020, eq. (14.6-97) + + Args: + hT_Tref (float): Relative humidity at reference temperature T_ref. + + Returns: + float: Alpha coefficient alpha_fct. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if hT_Tref < 0 or hT_Tref > 1: + raise ValueError('Relative humidity h_ref must be between 0 and 1.') + + return 7.6e-5 * math.exp(8.5 * hT_Tref) - 0.72 * math.exp(0.48 * hT_Tref) + + +def E_ci_T(E_ci: float, T: float) -> float: + """Calculate the modulus of elasticity E_ci(T) + for normal weight concrete at a given temperature. + + fib Model Code 2020, eq. (14.6-98) + + Args: + E_ci (float): Modulus of elasticity in MPa at T = 20°C. + T (float): Temperature in °C. + + Returns: + float: Modulus of elasticity E_ci(T) at temperature T in MPa. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if E_ci <= 0: + raise ValueError('Modulus of elasticity E_ci must be positive.') + if T < 0 or T > 80: + raise ValueError('Temperature T must be in the range 0°C ≤ T ≤ 80°C.') + + return E_ci * (1.06 - 0.003 * T) + + +def El_ci_T(E_l_ci: float, T: float) -> float: + """Calculate the modulus of elasticity E_l_ci(T) + for lightweight aggregate concrete at a given temperature. + + fib Model Code 2020, eq. (14.6-99) + + Args: + E_lci (float): Modulus of elasticity in MPa at T = 20°C. + T (float): Temperature in °C. + + Returns: + float: Modulus of elasticity E_l_ci(T) at temperature T in MPa. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if E_l_ci <= 0: + raise ValueError('Modulus of elasticity E_lci must be positive.') + if T < 0 or T > 80: + raise ValueError('Temperature T must be in the range 0°C ≤ T ≤ 80°C.') + + return E_l_ci * (1.04 - 0.002 * T) + + +def E_cm_hT_T(E_cm_ref: float, D_Ec: float, S_Ec: float) -> float: + """Calculate the modulus of elasticity E_cm(hT, T) + under combined effects of temperature and drying. + + fib Model Code 2020, eq. (14.6-100) + + Args: + E_cm_ref (float): Modulus of elasticity in MPa at + Tref = 20°C at reference storage conditions. + D_Ec (float): Drying effect parameter. + S_Ec (float): Thermal dilation incompatibility factor. + + Returns: + float: Modulus of elasticity E_cm(h_T, T) in MPa. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if E_cm_ref <= 0: + raise ValueError('Modulus of elasticity E_cm_ref must be positive.') + + return E_cm_ref * (1 + D_Ec + S_Ec) + + +def D_Ec(T: float, hT_Tref: float) -> float: + """Calculate the drying effect parameter D_Ec. + + fib Model Code 2020, eq. (14.6-101) + + Args: + T (float): Temperature in °C. + hT_Tref (float): Relative humidity of the + concrete pores at temperature T. + + Returns: + float: Drying effect parameter D_Ec. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if T < 20 or T > 80: + raise ValueError('Temperature T must be in the range 20°C ≤ T ≤ 80°C.') + if hT_Tref < 0 or hT_Tref > 1: + raise ValueError('Relative humidity h_T must be between 0 and 1.') + + if hT_Tref == 1.0: + return 0 + + return -0.12 * hT_Tref**19 - 0.14 + + +def S_Ec(T: float, alpha_Ec: float) -> float: + """Calculate the thermal dilation incompatibility factor S_Ec. + + fib Model Code 2020, eq. (14.6-102) + + Args: + T (float): Temperature in °C. + alpha_Ec (float): Alpha coefficient for elastic modulus. + + Returns: + float: Thermal dilation incompatibility factor S_Ec. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if T < 20 or T > 80: + raise ValueError('Temperature T must be in the range 20°C ≤ T ≤ 80°C.') + + ratio = (T - 20) / 100 + return alpha_Ec * ratio + + +def alpha_Ec(hT_Tref: float) -> float: + """Calculate the alpha coefficient alpha_Ec for the modulus of elasticity. + + fib Model Code 2020, eq. (14.6-103) + + Args: + h_ref (float): Relative humidity at reference temperature T_ref. + + Returns: + float: Alpha coefficient alpha_Ec. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if hT_Tref < 0 or hT_Tref > 1: + raise ValueError('Relative humidity h_ref must be between 0 and 1.') + + return 2.3e-5 * math.exp(9.0 * hT_Tref) - 0.2 * math.exp(0.7 * hT_Tref) + + +def beta_h_T(beta_h: float, T: float) -> float: + """Calculate the temperature-dependent coefficient + for creep time-development βh,T. + + fib Model Code 2020, eqs. (14.6-104) and (14.6-105) + + Args: + beta_h (float): Coefficient βh according to Eq. (14.6-66c). + T (float): Temperature in °C. + + Returns: + float: Temperature-dependent coefficient βh,T. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if beta_h <= 0: + raise ValueError('Coefficient βh must be positive.') + if T < 0 or T > 80: + raise ValueError('Temperature T must be in the range 0°C ≤ T ≤ 80°C.') + + beta_T = math.exp(1500 / ((273 + T) - 5.12)) + return beta_h * beta_T + + +def phi_bc_T(phi_bc: float, T: float) -> float: + """Calculate the temperature-dependent basic creep coefficient φbc,T. + + fib Model Code 2020, eq. (14.6-106) and (14.6-108) + + Args: + phi_bc (float): Basic creep coefficient φbc according to Eq. (14.6-59). + T (float): Temperature in °C. + + Returns: + float: Temperature-dependent basic creep coefficient φbc,T. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if phi_bc <= 0: + raise ValueError('Basic creep coefficient φbc must be positive.') + if T < 0 or T > 80: + raise ValueError('Temperature T must be in the range 0°C ≤ T ≤ 80°C.') + + phi_T = math.exp(0.015 * (T - 20)) + return phi_bc * phi_T + + +def phi_dc_T(phi_dc: float, T: float) -> float: + """Calculate the temperature-dependent drying creep coefficient φdc,T. + + fib Model Code 2020, eq. (14.6-107) and (14.6-108) + + Args: + phi_dc (float): Drying creep coefficient φdc + according to Eq. (14.6-62). + T (float): Temperature in °C. + + Returns: + float: Temperature-dependent drying creep coefficient φdc,T. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if phi_dc <= 0: + raise ValueError('Drying creep coefficient φdc must be positive.') + if T < 0 or T > 80: + raise ValueError('Temperature T must be in the range 0°C ≤ T ≤ 80°C.') + + phi_T = math.exp(0.015 * (T - 20)) + return phi_dc * phi_T**1.2 + + +def phi_t_t0_T(phi_t_t0: float, T: float) -> float: + """Creep coefficient ΔφT,trans for taking into consideration + increase in temperature while member is under load. + + fib Model Code 2020, eq. (14.6-109) and (14.6-110) + + Args: + phi_t_t0 (float): base creep coefficient. + T (float): Temperature in °C. + + Returns: + float: Transient thermal creep coefficient ΔφT,trans. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if T < 0 or T > 80: + raise ValueError('Temperature T must be in the range 0°C ≤ T ≤ 80°C.') + + delta_phi_T_trans = 0.0004 * (T - 20) ** 2 + return phi_t_t0 + delta_phi_T_trans + + +def alpha_sT(T: float, h: float) -> float: + """Calculate the temperature-dependent coefficient + for drying shrinkage time development αsT(T). + + fib Model Code 2020, eq. (14.6-111) + + Args: + T (float): Temperature in °C. + h (float): Notional size parameter. + + Returns: + float: Temperature-dependent coefficient αsT(T). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if T < 0 or T > 80: + raise ValueError('Temperature T must be in the range 0°C ≤ T ≤ 80°C.') + if h <= 0: + raise ValueError('Notional size parameter h must be positive.') + + return 0.035 * h**2 * math.exp(-0.06 * (T - 20)) + + +def beta_RH_T(beta_RH: float, beta_sT: float) -> float: + """Calculate the temperature-dependent coefficient + for drying shrinkage magnitude βRH,T. + + fib Model Code 2020, eq. (14.6-112) + + Args: + beta_RH (float): Coefficient βRH. + beta_sT (float): Coefficient βsT. + + Returns: + float: Temperature-dependent coefficient βRH,T. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if beta_RH <= 0 or beta_sT <= 0: + raise ValueError('Coefficients βRH and βsT must be positive.') + + return beta_RH * beta_sT + + +def beta_sT(RH: float, T: float) -> float: + """Calculate the temperature-dependent coefficient replacing βRH. + + fib Model Code 2020, eq. (14.6-113) + + Args: + RH (float): Relative humidity of the ambient environment in %. + T (float): Temperature in °C. + + Returns: + float: Temperature-dependent coeSfficient βRH,T. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if RH < 0 or RH > 100: + raise ValueError('Relative humidity RH must be between 0 and 100.') + if T < 0 or T > 80: + raise ValueError('Temperature T must be in the range 0°C ≤ T ≤ 80°C.') + + return 1 + (4 / (103 - RH)) * ((T - 20) / 40) + + +def beta_RH(RH: float, RH_T: float) -> float: + """Calculate the coefficient βRH,T for relative humidity. + + fib Model Code 2020, eq. (14.6-114) + + Args: + RH (float): Relative humidity of the ambient environment in %. + RH_T (float): Relative humidity in % obtained from eq. (14.6-115) + + Returns: + float: Coefficient βRH. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if RH < 0 or RH > 100: + raise ValueError('Relative humidity RH must be between 0 and 100.') + + if 40 <= RH < RH_T: + return -1.55 * (1 - (RH / 100) ** 3) + if RH >= RH_T: + return 0.25 + return ValueError('RH has not a valid value.') + + +def RH_T(fcm: float, T: float) -> float: + """Computes the relative humidity in % adjusted for the temperature T. + + fib Model Code 2020, eq. (14.6-115), (14.6-116) and (14.6-117) + + Args: + fcm (float): mean compressive strength of concrete in MPa. + T (float): Temperature in °C. + + Returns: + float: Value of RH_T. + + Raise: + ValueError: if values are not within a valid range. + """ + if fcm < 0: + raise ValueError('fcm cannot be negative.') + if T < 0 or T > 80: + raise ValueError('Temperature T must be in the range 0°C ≤ T ≤ 80°C.') + + beta_s1 = min((35 / fcm) ** 0.1, 1) + beta_s1_T = ((T - 20) / 25) ** 3 + + return min(99 * beta_s1 - beta_s1_T, 100) diff --git a/tests/test_mc2020/test_mc2020_concrete_material_properties.py b/tests/test_mc2020/test_mc2020_concrete_material_properties.py new file mode 100644 index 00000000..f048c8a4 --- /dev/null +++ b/tests/test_mc2020/test_mc2020_concrete_material_properties.py @@ -0,0 +1,1363 @@ +"""Tests for fib MC2020 Chapter 14.6.1.""" + +import pytest + +from structuralcodes.codes.mc2020 import _concrete_material_properties + + +@pytest.mark.parametrize( + 'fck, delta_f, expected', + [ + (30, 8, 38), + (40, 10, 50), + (50, 5, 55), + ], +) +def test_fcm(fck, delta_f, expected): + """Test fcm.""" + result = _concrete_material_properties.fcm(fck, delta_f) + assert result == expected + + +@pytest.mark.parametrize( + 'flck, delta_f, expected', + [ + (25, 8, 33), + (35, 10, 45), + (45, 5, 50), + ], +) +def test_flcm(flck, delta_f, expected): + """Test flcm.""" + result = _concrete_material_properties.flcm(flck, delta_f) + assert result == expected + + +@pytest.mark.parametrize( + 'grade, expected', + [ + ('C20', 20), + ('C30', 30), + ('C50', 50), + ('C100', 100), + ], +) +def test_fck(grade, expected): + """Test fck.""" + result = _concrete_material_properties.fck(grade) + assert result == expected + + +@pytest.mark.parametrize( + 'grade, expected', + [ + ('LC12', 13), + ('LC25', 28), + ('LC50', 55), + ('LC80', 88), + ], +) +def test_fck_cube(grade, expected): + """Test fck_cube.""" + result = _concrete_material_properties.fck_cube(grade) + assert result == expected + + +@pytest.mark.parametrize( + 'fck, expected', + [ + (20, 2.292), + (30, 3.022), + (50, 3.942), + ], +) +def test_calculate_fctm(fck, expected): + """Test calculation of mean tensile strength fctm.""" + result = _concrete_material_properties.fctm(fck) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'fctm, expected', + [ + (3.5, 2.45), + (4.0, 2.8), + (5.0, 3.5), + ], +) +def test_fctk_min(fctm, expected): + """Test fctk_min.""" + result = _concrete_material_properties.fctk_min(fctm) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'fctm, expected', + [ + (3.5, 4.55), + (4.0, 5.2), + (5.0, 6.5), + ], +) +def test_fctk_max(fctm, expected): + """Test fctk_max.""" + result = _concrete_material_properties.fctk_max(fctm) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'fctm, density, expected', + [ + (4.0, 1800, 3.563), + (5.0, 1500, 4.045), + (3.0, 2000, 2.836), + ], +) +def test_flctm(fctm, density, expected): + """Test flctm.""" + result = _concrete_material_properties.flctm(fctm, density) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'fctm, expected', + [ + (3.5, 2.45), + (4.0, 2.8), + (5.0, 3.5), + ], +) +def test_flctk_min(fctm, expected): + """Test flctk_min.""" + result = _concrete_material_properties.flctk_min(fctm) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'fctm, expected', + [ + (3.5, 4.55), + (4.0, 5.2), + (5.0, 6.5), + ], +) +def test_flctk_max(fctm, expected): + """Test flctk_max.""" + result = _concrete_material_properties.flctk_max(fctm) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'fctm_sp, alpha_sp, expected_fctm', + [ + (2.5, 1.0, 2.5), + (3.0, 1.0, 3.0), + (4.0, 1.0, 4.0), + ], +) +def test_fctm_from_splitting(fctm_sp, alpha_sp, expected_fctm): + """Test fctm_from_splitting.""" + result = _concrete_material_properties.fctm_from_splitting( + fctm_sp, alpha_sp + ) + assert result == expected_fctm + + +@pytest.mark.parametrize( + 'fctm_fl, hb, expected_fctm', + [ + (4.0, 500, 3.292), + (3.5, 300, 2.676), + (5.0, 800, 4.329), + ], +) +def test_fctm_from_flexural(fctm_fl, hb, expected_fctm): + """Test fctm_from_flexural.""" + result = _concrete_material_properties.fctm_from_flexural(fctm_fl, hb) + assert result == pytest.approx(expected_fctm, rel=1e-2) + + +@pytest.mark.parametrize( + 'fck, expected_GF', + [ + (20, 133.220), + (30, 141.575), + (50, 152.849), + ], +) +def test_GF(fck, expected_GF): + """Test GF.""" + result = _concrete_material_properties.GF(fck) + assert result == pytest.approx(expected_GF, rel=1e-2) + + +@pytest.mark.parametrize( + 'flctm, use_normal_weight_sand, expected_GF_l', + [ + (3.5, True, 80), + (4.0, False, 64), + (5.0, True, 104), + ], +) +def test_GF_l(flctm, use_normal_weight_sand, expected_GF_l): + """Test GF_l.""" + result = _concrete_material_properties.GF_l(flctm, use_normal_weight_sand) + assert result == expected_GF_l + + +@pytest.mark.parametrize( + 'sigma_1, sigma_2, sigma_3, fcm, fctm, fc2cm, sigma_com, ' + + 'tau_com, is_lightweight, expected', + [ + # Test case 1: Normal weight concrete with given sigma_com and tau_com + (30, 20, 10, 40, 4, 35, None, None, False, -16.71), + (25, 15, 5, 30, 3.5, 28, -240, None, False, 115.88), + (20, 15, 10, 25, 2.5, 22, None, None, True, 5.51), + (15, 10, 5, 20, 2, 18, -60, 75, True, 4.99), + ], +) +def test_multiaxial_stress_equation( + sigma_1, + sigma_2, + sigma_3, + fcm, + fctm, + fc2cm, + sigma_com, + tau_com, + is_lightweight, + expected, +): + """Test multiaxial_stress_equation.""" + result = _concrete_material_properties.multiaxial_stress_equation( + sigma_1=sigma_1, + sigma_2=sigma_2, + sigma_3=sigma_3, + fcm=fcm, + fctm=fctm, + fc2cm=fc2cm, + sigma_com=sigma_com, + tau_com=tau_com, + is_lightweight=is_lightweight, + ) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'fck, alpha_E, expected', + [ + (30, 1.0, 31008.365), + (40, 1.2, 40954.947), + (50, 0.8, 29411.586), + ], +) +def test_E_ci(fck, alpha_E, expected): + """Test Eci.""" + result = _concrete_material_properties.E_ci(fck, alpha_E) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'fcm, density, alpha_E, expected', + [ + (30, 1800, 1.0, 20757.67), + (40, 1600, 1.2, 21662.12), + (50, 2000, 0.8, 24307.10), + ], +) +def test_El_ci(fcm, density, alpha_E, expected): + """Test El_ci.""" + result = _concrete_material_properties.El_ci(fcm, density, alpha_E) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'Eci, fcm, expected', + [ + (30000, 30, 26045.45), + (35000, 40, 31181.82), + (40000, 50, 36545.45), + ], +) +def test_E_ci_red_el(Eci, fcm, expected): + """Test E_ci_red_el.""" + result = _concrete_material_properties.E_ci_red_el(Eci, fcm) + assert result == pytest.approx(expected, rel=1e-2) + + +def test_nu_c(): + """Test test_nu_c.""" + result = _concrete_material_properties.nu_c() + assert result == 0.20 + + +@pytest.mark.parametrize( + 'concrete_grade, expected', + [ + ('C20', 13300), + ('C30', 16500), + ('C50', 23200), + ('C100', 36000), + ], +) +def test_Ec1(concrete_grade, expected): + """Test Ec1.""" + result = _concrete_material_properties.Ec1(concrete_grade) + assert result == expected + + +@pytest.mark.parametrize( + 'concrete_grade, expected', + [ + ('C20', 0.0021), + ('C30', 0.0023), + ('C50', 0.0026), + ('C100', 0.003), + ], +) +def test_eps_c1(concrete_grade, expected): + """Test eps_c1.""" + result = _concrete_material_properties.eps_c1(concrete_grade) + assert result == pytest.approx(expected, rel=1e-1) + + +@pytest.mark.parametrize( + 'concrete_grade, expected', + [ + ('C20', 0.0035), + ('C30', 0.0035), + ('C50', 0.0034), + ('C100', 0.003), + ], +) +def test_eps_c_lim(concrete_grade, expected): + """Test eps_c_lim.""" + result = _concrete_material_properties.eps_c_lim(concrete_grade) + assert result == pytest.approx(expected, rel=1e-1) + + +@pytest.mark.parametrize( + 'f_lck, E_lc, kappa_lc, expected', + [ + (25, 18000, 1.1, 0.0024), + (30, 20000, 1.3, 0.00247), + ], +) +def test_eps_lc1(f_lck, E_lc, kappa_lc, expected): + """Test calculation of strain εlc1 for lightweight aggregate concrete.""" + result = _concrete_material_properties.eps_lc1(f_lck, E_lc, kappa_lc) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'epsilon_ct, E_ci, f_ctm, expected_sigma_ct', + [ + (0.0001, 30000, 3.0, 2.75), + (0.00005, 30000, 3.0, 1.5), + (0.0001, 30000, 3.5, 3.0), + ], +) +def test_sigma_ct_uncracked(epsilon_ct, E_ci, f_ctm, expected_sigma_ct): + """Test csigma_ct_uncracked.""" + result = _concrete_material_properties.sigma_ct_uncracked( + epsilon_ct, E_ci, f_ctm + ) + assert result == pytest.approx(expected_sigma_ct, rel=1e-2) + + +@pytest.mark.parametrize( + 'w, GF, f_ctm, expected_sigma_ct', + [ + (0.001, 0.15, 3.0, 2.95), + (0.01, 0.15, 3.0, 2.5), + (0.05, 0.15, 3.0, 0.6), + ], +) +def test_sigma_ct_cracked(w, GF, f_ctm, expected_sigma_ct): + """Test sigma_ct_cracked.""" + result = _concrete_material_properties.sigma_ct_cracked(w, GF, f_ctm) + assert result == pytest.approx(expected_sigma_ct, rel=1e-2) + + +@pytest.mark.parametrize( + 'delta, w, f_cm, C, expected', + [ + (0.5, 0.2, 30, 1.0, 11.825), + (0.3, 0.1, 40, 1.0, 16.368), + ], +) +def test_calculate_mean_shear_stress(delta, w, f_cm, C, expected): + """Test calculation of mean shear stress τ in an open crack.""" + result = _concrete_material_properties.tau_crack_friction( + delta, w, f_cm, C + ) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'delta, w, f_cm, C, expected', + [ + (0.5, 0.2, 30, 1.0, 6.007), + (0.3, 0.1, 40, 1.0, 7.351), + ], +) +def test_calculate_mean_normal_stress(delta, w, f_cm, C, expected): + """Test calculation of mean normal stress σ in an open crack.""" + result = _concrete_material_properties.sigma_crack_friction( + delta, w, f_cm, C + ) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'f_ck, strength_class, expected', + [ + (30, 'CR', 0.3), + (50, 'CN', 0.4), + (70, 'CS', 0.4), + ], +) +def test_get_sc_coefficient(f_ck, strength_class, expected): + """Test sC.""" + result = _concrete_material_properties.sC(f_ck, strength_class) + assert result == expected + + +@pytest.mark.parametrize( + 't, t_ref, s_C, expected_beta_cc', + [ + (14, 28, 0.5, 0.8129), + (7, 28, 0.3, 0.7408), + ], +) +def test_beta_cc(t, t_ref, s_C, expected_beta_cc): + """Test beta_cc.""" + result = _concrete_material_properties.beta_cc(t, t_ref, s_C) + assert result == pytest.approx(expected_beta_cc, rel=1e-2) + + +@pytest.mark.parametrize( + 'f_cm, beta_cc, expected', + [ + (38, 0.7071, 26.67), + (48, 0.5443, 26.13), + ], +) +def test_fcm_t(f_cm, beta_cc, expected): + """Test fcm_t.""" + result = _concrete_material_properties.fcm_t(f_cm, beta_cc) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'aggregate_strength, expected_slc', + [ + ('high', 0.05), + ('low', 0.25), + ], +) +def test_slC(aggregate_strength, expected_slc): + """Test slC.""" + result = _concrete_material_properties.slC(aggregate_strength) + assert result == expected_slc + + +@pytest.mark.parametrize( + 't, t_ref, s_lc, expected', + [ + (14, 28, 0.05, 0.979), + (7, 28, 0.25, 0.779), + ], +) +def test_beta_lcc(t, t_ref, s_lc, expected): + """Test beta_lcc.""" + result = _concrete_material_properties.beta_lcc(t, t_ref, s_lc) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'f_lcm, beta_lcc, expected', + [ + (30, 0.747, 22.41), + (35, 0.657, 22.995), + ], +) +def test_flcm_t(f_lcm, beta_lcc, expected): + """Test flcm_t.""" + result = _concrete_material_properties.flcm_t(f_lcm, beta_lcc) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'beta_cc, expected', + [ + (0.75, 0.909), + (0.8, 0.929), + ], +) +def test_beta_E(beta_cc, expected): + """Test beta_E.""" + result = _concrete_material_properties.beta_E(beta_cc) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'E_ci, beta_E_t, expected', + [ + (33000, 0.9005, 29716.5), + (35000, 0.8178, 28623), + ], +) +def test_E_ci_t(E_ci, beta_E_t, expected): + """Test E_ci_t.""" + result = _concrete_material_properties.E_ci_t(E_ci, beta_E_t) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 't0, expected', + [ + (7, 0.6621), + (28, 0.6743), + ], +) +def test_beta_t0(t0, expected): + """Test beta_t0.""" + result = _concrete_material_properties.beta_t0(t0) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 't, t0, beta_T0, expected', + [ + (180, 28, 0.9, 0.933), + (365, 28, 0.85, 0.896), + ], +) +def test_beta_c_sus(t, t0, beta_T0, expected): + """Test beta_c_sus.""" + result = _concrete_material_properties.beta_c_sus(t, t0, beta_T0) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'f_cm, beta_cc_t, beta_c_sus_t_t0, expected', + [ + (30, 0.8, 0.936, 22.464), + (40, 0.9, 0.922, 33.192), + ], +) +def test_fcm_sus(f_cm, beta_cc_t, beta_c_sus_t_t0, expected): + """Test fcm_sus.""" + result = _concrete_material_properties.fcm_sus( + f_cm, beta_cc_t, beta_c_sus_t_t0 + ) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'f_ctk, alpha, expected', + [ + (3, 'normal', 1.8), + (4, 'high-strength', 3.0), + ], +) +def test_fctk_sus(f_ctk, alpha, expected): + """Test fctk_sus.""" + result = _concrete_material_properties.fctk_sus(f_ctk, alpha) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'sigma_c_t0, phi_t_t0, E_ci, expected', + [ + (10, 2.0, 30000, 0.0006667), + (15, 1.5, 28000, 0.0008036), + ], +) +def test_eps_cc(sigma_c_t0, phi_t_t0, E_ci, expected): + """Test eps_cc.""" + result = _concrete_material_properties.eps_cc(sigma_c_t0, phi_t_t0, E_ci) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'sigma_c_t0, E_ci_t0, E_ci, phi_t_t0, expected', + [ + (10, 25000, 28000, 2.0, 0.00111), + (15, 24500, 29000, 1.5, 0.00139), + ], +) +def test_eps_c_sigma(sigma_c_t0, E_ci_t0, E_ci, phi_t_t0, expected): + """Test eps_c_sigma.""" + result = _concrete_material_properties.eps_c_sigma( + sigma_c_t0, E_ci_t0, E_ci, phi_t_t0 + ) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'phi_bc_t_t0, phi_dc_t_t0, expected_phi', + [ + (0.5, 0.3, 0.8), + (0.0, 0.0, 0.0), + (1.2, 0.8, 2.0), + ], +) +def test_phi_t_t0(phi_bc_t_t0, phi_dc_t_t0, expected_phi): + """Test calculation of total creep coefficient φ(t, t0).""" + result = _concrete_material_properties.phi_t_t0(phi_bc_t_t0, phi_dc_t_t0) + assert result == pytest.approx(expected_phi, rel=1e-2) + + +@pytest.mark.parametrize( + 'fcm, expected', + [ + (30, 0.166), + (40, 0.136), + (50, 0.116), + ], +) +def test_beta_bc_fcm(fcm, expected): + """Test beta_bc_fcm.""" + result = _concrete_material_properties.beta_bc_fcm(fcm) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 't, t0, t0_adj, expected', + [ + (180, 28, 14, 6.55), + (365, 28, 28, 5.97), + ], +) +def test_beta_bc_t_t0(t, t0, t0_adj, expected): + """Test beta_bc_t_t0.""" + result = _concrete_material_properties.beta_bc_t_t0(t, t0, t0_adj) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'beta_bc_t_t0, beta_bc_fcm, expected', + [ + (2.0, 0.22, 0.44), + (1.5, 0.30, 0.45), + ], +) +def test_phi_bc_t_t0(beta_bc_t_t0, beta_bc_fcm, expected): + """Test phi_bc_t_t0.""" + result = _concrete_material_properties.phi_bc_t_t0( + beta_bc_t_t0, beta_bc_fcm + ) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'fcm, expected', + [ + (30, 3.523), + (40, 2.355), + ], +) +def test_beta_dc_fcm(fcm, expected): + """Test beta_dc_fcm.""" + result = _concrete_material_properties.beta_dc_fcm(fcm) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'RH, h, expected', + [ + (50, 200, 0.854), + (75, 100, 0.538), + ], +) +def test_beta_RH(RH, h, expected): + """Test beta_RH.""" + result = _concrete_material_properties.beta_RH_c(RH, h) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 't0_adj, expected', + [ + (28, 0.488), + (14, 0.557), + ], +) +def test_beta_dc_t0(t0_adj, expected): + """Test beta_dc_t0.""" + result = _concrete_material_properties.beta_dc_t0(t0_adj) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 't0_adj, expected', + [ + (28, 0.338), + (14, 0.309), + ], +) +def test_gamma_t0(t0_adj, expected): + """Test gamma_t0.""" + result = _concrete_material_properties.gamma_t0(t0_adj) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'h, fcm, expected', + [ + (200, 30, 570.030), + (100, 40, 383.85), + ], +) +def test_beta_h(h, fcm, expected): + """Test beta_h.""" + result = _concrete_material_properties.beta_h(h, fcm) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 't, t0, gamma_t0, beta_h, expected', + [ + (180, 28, 0.191, 875, 0.694), + (365, 28, 0.228, 488, 0.815), + ], +) +def test_beta_dc_t_t0(t, t0, gamma_t0, beta_h, expected): + """Test beta_dc_t_t0.""" + result = _concrete_material_properties.beta_dc_t_t0( + t, t0, gamma_t0, beta_h + ) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'beta_dc_fm, beta_RH, beta_dc_t0, beta_dc_t_t0, expected', + [ + (3.67, 0.0886, 0.754, 0.143, 0.035), + (2.58, 0.114, 0.852, 0.051, 0.0127), + ], +) +def test_phi_dc_t_t0(beta_dc_fm, beta_RH, beta_dc_t0, beta_dc_t_t0, expected): + """Test phi_dc_t_t0.""" + result = _concrete_material_properties.phi_dc_t_t0( + beta_dc_fm, beta_RH, beta_dc_t0, beta_dc_t_t0 + ) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'phi_t_t0, rho, concrete_grade, expected', + [ + (2.0, 1800, 'LC12', 1.740), + (2.0, 1800, 'LC16', 1.740), + (2.0, 1800, 'LC20', 1.339), + (1.5, 2000, 'LC16', 1.612), + ], +) +def test_phi_l_t_t0(phi_t_t0, rho, concrete_grade, expected): + """Test phi_l_t_t0.""" + result = _concrete_material_properties.phi_l_t_t0( + phi_t_t0, rho, concrete_grade + ) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 't0_T, strength_class, expected', + [ + (28, 'CS', 24.15), + (28, 'CN', 28.0), + (28, 'CR', 32.46), + (14, 'CN', 14.0), + ], +) +def test_t0_adj(t0_T, strength_class, expected): + """Test t0_adj.""" + result = _concrete_material_properties.t0_adj(t0_T, strength_class) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'phi_t_t0, sigma_c, f_cm_t0, expected_phi_sigma', + [ + (1.2, 18, 30, 1.620), + (1.5, 15, 25, 2.025), + ], +) +def test_phi_sigma_t_t0(phi_t_t0, sigma_c, f_cm_t0, expected_phi_sigma): + """Test phi_sigma_t_t0.""" + result = _concrete_material_properties.phi_sigma_t_t0( + phi_t_t0, sigma_c, f_cm_t0 + ) + assert result == pytest.approx(expected_phi_sigma, rel=1e-2) + + +@pytest.mark.parametrize( + 'eps_cbs_t, eps_cds_t_ts, expected', + [ + (0.00024, 0.000168, 0.000408), + (0.0002, 0.0002, 0.0004), + ], +) +def test_eps_cs_t_ts(eps_cbs_t, eps_cds_t_ts, expected): + """Test eps_cs_t_ts.""" + result = _concrete_material_properties.eps_cs_t_ts(eps_cbs_t, eps_cds_t_ts) + assert result == expected + + +@pytest.mark.parametrize( + 'eps_cbs0, beta_bs_t, expected', + [ + (0.0003, 0.8, 0.00024), + (0.0002, 1.0, 0.0002), + ], +) +def test_eps_cbs_t(eps_cbs0, beta_bs_t, expected): + """Test eps_cbs_t.""" + result = _concrete_material_properties.eps_cbs_t(eps_cbs0, beta_bs_t) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'eps_cds0, beta_RH, beta_ds_t_ts, expected', + [ + (0.0004, 0.7, 0.6, 0.000168), + (0.0005, 0.8, 0.5, 0.0002), + ], +) +def test_calculate_drying_shrinkage_strain( + eps_cds0, beta_RH, beta_ds_t_ts, expected +): + """Test eps_cds_t_ts.""" + result = _concrete_material_properties.eps_cds_t_ts( + eps_cds0, beta_RH, beta_ds_t_ts + ) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'f_cm, alpha_bs, expected', + [ + (30, 700, -0.000449), + (25, 800, -0.000375), + ], +) +def test_eps_cbs_0(f_cm, alpha_bs, expected): + """Test eps_cbs_0.""" + result = _concrete_material_properties.eps_cbs_0(f_cm, alpha_bs) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 't, expected', + [ + (28, 0.653), + (180, 0.932), + ], +) +def test_beta_bs_t(t, expected): + """Test beta_bs_t.""" + result = _concrete_material_properties.beta_bs_t(t) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'strength_class, expected', + [ + ('CS', 800), + ('CN', 700), + ('CR', 600), + ('cs', 800), + ('Cr', 600), + ], +) +def test_alpha_bs(strength_class, expected): + """Test alpha_bs.""" + result = _concrete_material_properties.alpha_bs(strength_class) + assert result == expected + + +@pytest.mark.parametrize( + 'strength_class, expected', + [ + ('CS', 3), + ('CN', 4), + ('CR', 6), + ('cs', 3), + ('Cr', 6), + ], +) +def test_alpha_ds_1(strength_class, expected): + """Test alpha_ds_1.""" + result = _concrete_material_properties.alpha_ds_1(strength_class) + assert result == expected + + +@pytest.mark.parametrize( + 'strength_class, expected', + [ + ('CS', 0.013), + ('CN', 0.012), + ('CR', 0.012), + ('cs', 0.013), + ('Cr', 0.012), + ], +) +def test_alpha_ds_2(strength_class, expected): + """Test alpha_ds_2.""" + result = _concrete_material_properties.alpha_ds_2(strength_class) + assert result == expected + + +@pytest.mark.parametrize( + 'f_cm, alpha_ds1, alpha_ds2, expected', + [ + (30, 4, 0.012, 0.00460), + (25, 3, 0.013, 0.00397), + ], +) +def test_eps_cds_0_fcm(f_cm, alpha_ds1, alpha_ds2, expected): + """Test eps_cds_0_fcm.""" + result = _concrete_material_properties.eps_cds_0_fcm( + f_cm, alpha_ds1, alpha_ds2 + ) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'RH, RH_eq, expected', + [ + (80, 99, -0.7320), + (98, 99, -0.0464), + (100, 99, 0.2815), + ], +) +def test_beta_RH_s(RH, RH_eq, expected): + """Test beta_RH_s.""" + result = _concrete_material_properties.beta_RH_s(RH, RH_eq) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 't, ts, h, expected', + [ + (180, 28, 150, 0.8116), + (365, 0, 200, 0.8502), + ], +) +def test_beta_ds_t_ts(t, ts, h, expected): + """Test beta_ds_t_ts.""" + result = _concrete_material_properties.beta_ds_t_ts(t, ts, h) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'fcm, expected', + [ + (25, 99), + (30, 99), + (50, 95.5311), + ], +) +def test_RH_eq(fcm, expected): + """Test RH_eq.""" + result = _concrete_material_properties.RH_eq(fcm) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'eps_cs, concrete_grade, expected', + [ + (0.0005, 'LC8', 0.00075), + (0.0005, 'LC12', 0.00075), + (0.0005, 'LC16', 0.00075), + (0.0005, 'LC20', 0.0006), + (0.0005, 'LC25', 0.0006), + ], +) +def test_eps_lcs_t_ts(eps_cs, concrete_grade, expected): + """Test eps_lcs_t_ts.""" + result = _concrete_material_properties.eps_lcs_t_ts(eps_cs, concrete_grade) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'temperature_intervals, expected', + [ + ( + [(5, 20), (10, 15), (7, 25)], + 21.651, + ), + ( + [(1, 0), (2, 5), (1, -5)], + 1.602, + ), + ( + [(10, 30)], + 15.662, + ), + ], +) +def test_t_T_maturity(temperature_intervals, expected): + """Test t_T_maturity.""" + result = _concrete_material_properties.t_T_maturity(temperature_intervals) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'delta_T, concrete_type, expected', + [ + (20, 10e-6, 0.0002), + (15, 8e-6, 0.00012), + (10, 10e-6, 0.0001), + ], +) +def test_eps_c_T(delta_T, concrete_type, expected): + """Test eps_c_T.""" + result = _concrete_material_properties.eps_c_T(delta_T, concrete_type) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'concrete_type, expected', + [ + ('normal', 10e-6), + ('lightweight', 8e-6), + ], +) +def test_alpha_T(concrete_type, expected): + """Test alpha_T.""" + result = _concrete_material_properties.alpha_T(concrete_type) + assert result == expected + + +@pytest.mark.parametrize( + 'f_cm, T, expected', + [ + (30, 40, 28.2), + (35, 60, 30.8), + ], +) +def test_fcm_T(f_cm, T, expected): + """Test fcm_T.""" + result = _concrete_material_properties.fcm_T(f_cm, T) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'f_lcm, T, expected', + [ + (25, 40, 24.0), + (20, 60, 18.4), + ], +) +def test_flcm_T(f_lcm, T, expected): + """Test flcm_T.""" + result = _concrete_material_properties.flcm_T(f_lcm, T) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'f_cm_ref, D_fc, S_fc, expected', + [ + (30, 0.01, 0.04, 31.5), + ], +) +def test_fcm_hT_T(f_cm_ref, D_fc, S_fc, expected): + """Test fcm_hT_T.""" + result = _concrete_material_properties.fcm_hT_T(f_cm_ref, D_fc, S_fc) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'h_T, expected', + [ + (0.8, 0.319), + ], +) +def test_calculate_D_fc(h_T, expected): + """Test D_fc.""" + result = _concrete_material_properties.D_fc(h_T) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'h_ref, K_hT, T, T_ref, expected', + [ + (1.0, 0.01, 25, 20, 0.95), + ], +) +def test_hT_Tref(h_ref, K_hT, T, T_ref, expected): + """Test hT_Tref.""" + result = _concrete_material_properties.hT_Tref(h_ref, K_hT, T, T_ref) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'h_ref, expected', + [ + (0.05, 0.00053), + ], +) +def test_K_hT(h_ref, expected): + """Test K_hT.""" + result = _concrete_material_properties.K_hT(h_ref) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'T, alpha_f, expected', + [ + (50, 0.2, 0.0915), + ], +) +def test_S_fc(T, alpha_f, expected): + """Test S_fc.""" + result = _concrete_material_properties.S_fc(T, alpha_f) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'h_ref, expected', + [ + (1.0, -0.589), + ], +) +def test_calculate_alpha_f(h_ref, expected): + """Test alpha_fc.""" + result = _concrete_material_properties.alpha_fc(h_ref) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'f_ctm, T, expected', + [ + (3.0, 40, 2.52), + (4.0, 60, 2.72), + ], +) +def test_fctm_T(f_ctm, T, expected): + """Test fctm_T.""" + result = _concrete_material_properties.fctm_T(f_ctm, T) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'G_F, T, concrete_type, expected', + [ + (150, 50, 'dry', 136.5), + (180, 60, 'mass', 136.8), + ], +) +def test_GF_T(G_F, T, concrete_type, expected): + """Test GF_T.""" + result = _concrete_material_properties.GF_T(G_F, T, concrete_type) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'f_ctm_ref, D_fct, S_fct, expected', + [ + (3.0, 0.02, 0.05, 3.21), + ], +) +def test_fctm_hT_T(f_ctm_ref, D_fct, S_fct, expected): + """Test fctm_hT_T.""" + result = _concrete_material_properties.fctm_hT_T(f_ctm_ref, D_fct, S_fct) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'T, h_T, expected', + [ + (50, 0.8, 0.2598), + ], +) +def test_D_fct(T, h_T, expected): + """Test D_fct.""" + result = _concrete_material_properties.D_fct(T, h_T) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'T, alpha_f, expected', + [ + (50, 0.2, 0.1005), + ], +) +def test_S_fct(T, alpha_f, expected): + """Test S_fct.""" + result = _concrete_material_properties.S_fct(T, alpha_f) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'h_ref, expected', + [ + (1.0, -0.7900), + ], +) +def test_alpha_fct(h_ref, expected): + """Test alpha_fct.""" + result = _concrete_material_properties.alpha_fct(h_ref) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'E_cm_ref, D_Ec, S_Ec, expected', + [ + (35000, -0.01, -0.02, 33950.0), + ], +) +def test_E_cm_hT_T(E_cm_ref, D_Ec, S_Ec, expected): + """Test E_cm_hT_T.""" + result = _concrete_material_properties.E_cm_hT_T(E_cm_ref, D_Ec, S_Ec) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'T, h_T, expected', + [ + (50, 0.8, -0.1417), + ], +) +def test_calculate_D_Ec(T, h_T, expected): + """Test D_Ec.""" + result = _concrete_material_properties.D_Ec(T, h_T) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'h_ref, expected', + [ + (1.0, -0.2163), + ], +) +def test_alpha_Ec(h_ref, expected): + """Test alpha_Ec.""" + result = _concrete_material_properties.alpha_Ec(h_ref) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'T, alpha_Ec, expected', + [ + (50, 0.2, 0.06), + ], +) +def test_calculate_S_Ec(T, alpha_Ec, expected): + """Test S_Ec.""" + result = _concrete_material_properties.S_Ec(T, alpha_Ec) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'beta_h, T, expected', + [ + (1.0, 50, 112.029), + ], +) +def test_beta_h_T(beta_h, T, expected): + """Test beta_h_T.""" + result = _concrete_material_properties.beta_h_T(beta_h, T) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'phi_bc, T, expected', + [ + (1.5, 50, 2.3524), + ], +) +def test_phi_bc_T(phi_bc, T, expected): + """Test phi_bc_T.""" + result = _concrete_material_properties.phi_bc_T(phi_bc, T) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'phi_dc, T, expected', + [ + (0.5, 50, 0.8580), + ], +) +def test_phi_dc_T(phi_dc, T, expected): + """Test phi_dc_T.""" + result = _concrete_material_properties.phi_dc_T(phi_dc, T) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'phi_t_t0, T, expected', + [ + (0.004, 50, 0.3640), + ], +) +def test_phi_t_t0_T(phi_t_t0, T, expected): + """Test phi_t_t0_T.""" + result = _concrete_material_properties.phi_t_t0_T(phi_t_t0, T) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'T, h, expected', + [ + (50, 1.0, 0.00578), + ], +) +def test_alpha_sT(T, h, expected): + """Test alpha_sT.""" + result = _concrete_material_properties.alpha_sT(T, h) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'beta_RH, beta_sT, expected', + [ + (1.2, 1.1, 1.32), + ], +) +def test__(beta_RH, beta_sT, expected): + """Test beta_RH_T.""" + result = _concrete_material_properties.beta_RH_T(beta_RH, beta_sT) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'RH, T, expected', + [ + (50, 50, 1.0566), + ], +) +def test_beta_sT(RH, T, expected): + """Test beta_sT.""" + result = _concrete_material_properties.beta_sT(RH, T) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'RH, RH_T, expected', + [ + (70, 98, -1.018), + (98, 95, 0.25), + ], +) +def test_beta_RH_(RH, RH_T, expected): + """Test beta_RH.""" + result = _concrete_material_properties.beta_RH(RH, RH_T) + assert result == pytest.approx(expected, rel=1e-2) + + +@pytest.mark.parametrize( + 'fcm, T, expected', + [ + (25, 60, 94.904), + (40, 70, 89.687), + ], +) +def test_RH_T(fcm, T, expected): + """Test RH_T.""" + result = _concrete_material_properties.RH_T(fcm, T) + assert result == pytest.approx(expected, rel=1e-2) From 73a43061d8e78a3c30d39e32d80e8e03af29d94b Mon Sep 17 00:00:00 2001 From: DanielGMorenaFhecor Date: Tue, 27 Aug 2024 11:57:08 +0200 Subject: [PATCH 25/27] functions added to public API --- structuralcodes/codes/mc2020/__init__.py | 208 +++++++++++++++++++++++ 1 file changed, 208 insertions(+) diff --git a/structuralcodes/codes/mc2020/__init__.py b/structuralcodes/codes/mc2020/__init__.py index f4782d6f..91c5a3e2 100644 --- a/structuralcodes/codes/mc2020/__init__.py +++ b/structuralcodes/codes/mc2020/__init__.py @@ -2,6 +2,214 @@ import typing as t +from ._concrete_material_properties import ( + GF, + GF_T, + RH_T, + D_Ec, + D_fc, + D_fct, + E_ci, + E_ci_red_el, + E_ci_T, + E_ci_t, + E_cm_hT_T, + Ec1, + El_ci, + El_ci_T, + GF_l, + K_hT, + RH_eq, + S_Ec, + S_fc, + S_fct, + alpha_bs, + alpha_ds_1, + alpha_ds_2, + alpha_Ec, + alpha_fc, + alpha_fct, + alpha_sT, + alpha_T, + beta_bc_fcm, + beta_bc_t_t0, + beta_bs_t, + beta_c_sus, + beta_cc, + beta_dc_fcm, + beta_dc_t0, + beta_dc_t_t0, + beta_ds_t_ts, + beta_E, + beta_h, + beta_h_T, + beta_lcc, + beta_RH, + beta_RH_c, + beta_RH_s, + beta_RH_T, + beta_sT, + beta_t0, + eps_c1, + eps_c_lim, + eps_c_sigma, + eps_c_T, + eps_cbs_0, + eps_cbs_t, + eps_cc, + eps_cds_0_fcm, + eps_cds_t_ts, + eps_cs_t_ts, + eps_lc1, + eps_lcs_t_ts, + fck, + fck_cube, + fcm, + fcm_hT_T, + fcm_sus, + fcm_T, + fcm_t, + fctk_max, + fctk_min, + fctk_sus, + fctm, + fctm_from_flexural, + fctm_from_splitting, + fctm_hT_T, + fctm_T, + flcm, + flcm_T, + flcm_t, + flctk_max, + flctk_min, + flctm, + gamma_t0, + hT_Tref, + multiaxial_stress_equation, + nu_c, + phi_bc_T, + phi_bc_t_t0, + phi_dc_T, + phi_dc_t_t0, + phi_l_t_t0, + phi_sigma_t_t0, + phi_t_t0, + phi_t_t0_T, + sC, + sigma_c, + sigma_crack_friction, + sigma_ct_cracked, + sigma_ct_uncracked, + slC, + t0_adj, + t_T_maturity, + tau_crack_friction, +) + +__all__ = [ + 'GF', + 'GF_T', + 'RH_T', + 'D_Ec', + 'D_fc', + 'D_fct', + 'E_ci', + 'E_ci_red_el', + 'E_ci_T', + 'E_ci_t', + 'E_cm_hT_T', + 'Ec1', + 'El_ci', + 'El_ci_T', + 'GF_l', + 'K_hT', + 'RH_eq', + 'S_Ec', + 'S_fc', + 'S_fct', + 'alpha_bs', + 'alpha_ds_1', + 'alpha_ds_2', + 'alpha_Ec', + 'alpha_fc', + 'alpha_fct', + 'alpha_sT', + 'alpha_T', + 'beta_bc_fcm', + 'beta_bc_t_t0', + 'beta_bs_t', + 'beta_c_sus', + 'beta_cc', + 'beta_dc_fcm', + 'beta_dc_t0', + 'beta_dc_t_t0', + 'beta_ds_t_ts', + 'beta_E', + 'beta_h', + 'beta_h_T', + 'beta_lcc', + 'beta_RH', + 'beta_RH_c', + 'beta_RH_s', + 'beta_RH_T', + 'beta_sT', + 'beta_t0', + 'eps_c1', + 'eps_c_lim', + 'eps_c_sigma', + 'eps_c_T', + 'eps_cbs_0', + 'eps_cbs_t', + 'eps_cc', + 'eps_cds_0_fcm', + 'eps_cds_t_ts', + 'eps_cs_t_ts', + 'eps_lc1', + 'eps_lcs_t_ts', + 'fck', + 'fck_cube', + 'fcm', + 'fcm_hT_T', + 'fcm_sus', + 'fcm_T', + 'fcm_t', + 'fctk_max', + 'fctk_min', + 'fctk_sus', + 'fctm', + 'fctm_from_flexural', + 'fctm_from_splitting', + 'fctm_hT_T', + 'fctm_T', + 'flcm', + 'flcm_T', + 'flcm_t', + 'flctk_max', + 'flctk_min', + 'flctm', + 'gamma_t0', + 'hT_Tref', + 'multiaxial_stress_equation', + 'nu_c', + 'phi_bc_T', + 'phi_bc_t_t0', + 'phi_dc_T', + 'phi_dc_t_t0', + 'phi_l_t_t0', + 'phi_sigma_t_t0', + 'phi_t_t0', + 'phi_t_t0_T', + 'sC', + 'sigma_c', + 'sigma_crack_friction', + 'sigma_ct_cracked', + 'sigma_ct_uncracked', + 'slC', + 't0_adj', + 't_T_maturity', + 'tau_crack_friction', +] + __title__: str = 'fib Model Code 2020' __year__: str = '2020' __materials__: t.Tuple[str] = ('concrete', 'reinforcement') From bebc2d63b023955c99fbb08f95ccdcec0b348458 Mon Sep 17 00:00:00 2001 From: Morten Engen Date: Tue, 24 Sep 2024 21:53:58 +0200 Subject: [PATCH 26/27] Fix docstrings --- .../mc2020/_concrete_material_properties.py | 838 ++++++++++-------- 1 file changed, 456 insertions(+), 382 deletions(-) diff --git a/structuralcodes/codes/mc2020/_concrete_material_properties.py b/structuralcodes/codes/mc2020/_concrete_material_properties.py index de35e40a..d79d81f9 100644 --- a/structuralcodes/codes/mc2020/_concrete_material_properties.py +++ b/structuralcodes/codes/mc2020/_concrete_material_properties.py @@ -5,14 +5,14 @@ def fcm(fck: float, delta_f: float = 8) -> float: - """Calculate the mean compressive strength fcm based on the - characteristic compressive strength fck. + """Calculate the mean compressive strength fcm based on the characteristic + compressive strength fck. fib Model Code 2020, eq. (14.6-1) Args: fck (float): Characteristic compressive strength fck in MPa. - delta_f (float): increment in MPa. Defaults to 8MPa. + delta_f (float): Increment in MPa. Defaults to 8MPa. Returns: float: Mean compressive strength fcm in MPa. @@ -29,9 +29,8 @@ def fcm(fck: float, delta_f: float = 8) -> float: def flcm(flck: float, delta_f: float = 8) -> float: - """Calculate the mean compressive strength flcm for lightweight - aggregate concrete based on the characteristic - compressive strength flck. + """Calculate the mean compressive strength flcm for lightweight aggregate + concrete based on the characteristic compressive strength flck. fib Model Code 2020, eq. (14.6-2) @@ -40,7 +39,7 @@ def flcm(flck: float, delta_f: float = 8) -> float: Returns: float: Mean compressive strength flcm in MPa. - delta_f (float): increment in MPa. Defaults to 8MPa. + delta_f (float): Increment in MPa. Defaults to 8MPa. Raises: ValueError: If flck is negative or zero. @@ -125,8 +124,8 @@ def fck( 'LC80', ], ) -> float: - """Retrieve the characteristic strength values (fck) - for a given con. + """Retrieve the characteristic strength values (fck) for a given concrete + grade. fib Model Code 2020, Tables 14.6-1, 14.6-2 @@ -138,9 +137,6 @@ def fck( Raises: ValueError: If the grade is not found. - - Reference: - """ if grade not in _concrete_strengths: raise ValueError( @@ -186,8 +182,8 @@ def fck_cube( 'LC80', ], ) -> float: - """Retrieve the characteristic cube strength values (fck_cube) - for a given con. + """Retrieve the characteristic cube strength values (fck_cube) for a given + concrete grade. fib Model Code 2020, Tables 14.6-1, 14.6-2 @@ -199,9 +195,6 @@ def fck_cube( Raises: ValueError: If the grade is not found. - - Reference: - """ if grade not in _concrete_strengths: raise ValueError( @@ -263,22 +256,22 @@ def fctk_max(fctm: float) -> float: def flctm(fctm: float, density: float) -> float: - """Calculate the mean tensile strength flctm for - lightweight aggregate concrete. + """Calculate the mean tensile strength flctm for lightweight aggregate + concrete. fib Model Code 2020, eqs. (14.6-6a) and (14.6-6b) Args: fctm (float): Mean tensile strength for normal weight concrete in MPa. - density (float): Oven-dry density of the lightweight - aggregate concrete in kg/m3. + density (float): Oven-dry density of the lightweight aggregate concrete + in kg/m3. Returns: float: Mean tensile strength flctm in MPa. Raises: - ValueError: If density is outside the - reasonable range for lightweight aggregate concrete. + ValueError: If density is outside the reasonable range for lightweight + aggregate concrete. """ if density < 1000 or density > 2200: raise ValueError( @@ -291,8 +284,8 @@ def flctm(fctm: float, density: float) -> float: def flctk_min(flctm: float) -> float: - """Calculate the lower bound characteristic - tensile strength flctk,min for lightweight aggregate concrete. + """Calculate the lower bound characteristic tensile strength flctk,min for + lightweight aggregate concrete. fib Model Code 2020, similar to eq. (14.6-4) @@ -306,8 +299,8 @@ def flctk_min(flctm: float) -> float: def flctk_max(flctm: float) -> float: - """Calculate the upper bound characteristic tensile strength - flctk,max for lightweight aggregate concrete. + """Calculate the upper bound characteristic tensile strength flctk,max for + lightweight aggregate concrete. fib Model Code 2020, similar to eq. (14.6-5) @@ -321,8 +314,8 @@ def flctk_max(flctm: float) -> float: def fctm_from_splitting(fctm_sp: float, alpha_sp: float = 1.0) -> float: - """Calculate the mean uniaxial tensile strength - fctm from the mean splitting tensile strength fctm,sp. + """Calculate the mean uniaxial tensile strength fctm from the mean + splitting tensile strength fctm,sp. fib Model Code 2020, eq. (14.6-7) @@ -331,14 +324,14 @@ def fctm_from_splitting(fctm_sp: float, alpha_sp: float = 1.0) -> float: alpha_sp (float): Conversion factor (default is 1.0). Returns: - float: Mean uniaxial tensile strength fctm iun MPa. + float: Mean uniaxial tensile strength fctm in MPa. """ return alpha_sp * fctm_sp def fctm_from_flexural(fctm_fl: float, hb: float) -> float: - """Calculate the mean uniaxial tensile strength - fctm from the mean flexural tensile strength fctm,fl. + """Calculate the mean uniaxial tensile strength fctm from the mean flexural + tensile strength fctm,fl. fib Model Code 2020, eqs. (14.6-8a) and (14.6-8b) @@ -388,8 +381,8 @@ def GF_l(flctm: float, use_normal_weight_sand: bool = True) -> float: Args: flctm (float): Mean tensile strength flctm in MPa. - use_normal_weight_sand (bool): True if using normal - weight sand, False if using lightweight sand. + use_normal_weight_sand (bool): True if using normal weight sand, False + if using lightweight sand. Returns: float: Fracture energy GF,l in N/m. @@ -415,36 +408,35 @@ def multiaxial_stress_equation( tau_com: Optional[float] = None, is_lightweight: bool = False, ) -> float: - """Returns the symbolic equation for the mean - strength under multiaxial states of stress. The result - of this evaluation must be equal to zero to - fulfill the failure criterion. So a custom - solver defined by the user can be used. + """Returns the symbolic equation for the mean strength under multiaxial + states of stress. The result of this evaluation must be equal to zero to + fulfill the failure criterion. So a custom solver defined by the user can + be used. fib Model Code 2020, eq. (14.6-11 to 14.6-19.b) Args: - sigma_1 (float): first principal stress in MPa. - sigma_2 (float): second principal stress in MPa. - sigma_3 (float): third principal stress in MPa. - fcm (float): mean compressive strength of concrete in MPa. Use - flcm for lightweight concrete. - fctm (float): mean tensile strength of concrete in MPa. Use flctm - for lightweight concrete. - fc2cm (float): biaxial compressive strength in MPa. - sigma_com (float): triaxial compressive strength at on point - on the compressive meridian in MPa. (sigma_1 = sigma_2 > sigma_3). - If None, it computes the recommended value. - tau_com (float): triaxial comrpessive shear strength at one point - on the compressive meridian in MPa. (sigma_1 = sigma_2 > sigma_3). - If None, it computes the recommended value. + sigma_1 (float): First principal stress in MPa. + sigma_2 (float): Second principal stress in MPa. + sigma_3 (float): Third principal stress in MPa. + fcm (float): Mean compressive strength of concrete in MPa. Use flcm for + lightweight concrete. + fctm (float): Mean tensile strength of concrete in MPa. Use flctm for + lightweight concrete. + fc2cm (float): Biaxial compressive strength in MPa. + sigma_com (float): Triaxial compressive strength at on point on the + compressive meridian in MPa. (sigma_1 = sigma_2 > sigma_3). If + None, it computes the recommended value. + tau_com (float): Triaxial comrpessive shear strength at one point on + the compressive meridian in MPa. (sigma_1 = sigma_2 > sigma_3). If + None, it computes the recommended value. is_lightweight (bool): True if lightweight concrete. False otherwise. Returns: - float: the result of the evaluation of the multiaxial stress. - When this result is equal to zero, the equilibrium is met. - A custom solver/standard solver can be used passing the 3 - principal stresses (sigma_1, sigma_2, sigma_3). + float: The result of the evaluation of the multiaxial stress. When this + result is equal to zero, the strength criterion is met. A + custom solver/standard solver can be used passing the 3 principal + stresses (sigma_1, sigma_2, sigma_3). """ # Compute the invariants I1 = sigma_1 + sigma_2 + sigma_3 @@ -526,16 +518,16 @@ def multiaxial_stress_equation( def E_ci(fcm: float, alpha_E: float = 1.0, Ec0: float = 21500) -> float: - """Calculate the modulus of elasticity Eci for normal - weight concrete at 28 days. + """Calculate the modulus of elasticity Eci for normal weight concrete at 28 + days. fib Model Code 2020, eq. (14.6-20) Args: fcm (float): Mean compressive strength fck in MPa. - alpha_E (float): Coefficient depending on - the aggregate type (default is 1.0 for quartzite). - Ec0 (float): base elastic modulus value in MPa. + alpha_E (float): Coefficient depending on the aggregate type (default + is 1.0 for quartzite). + Ec0 (float): Base elastic modulus value in MPa. Returns: float: Modulus of elasticity E_ci in MPa. @@ -556,18 +548,18 @@ def E_ci(fcm: float, alpha_E: float = 1.0, Ec0: float = 21500) -> float: def El_ci( fcm: float, density: float, alpha_E: float = 1.0, Ec0: float = 21500 ) -> float: - """Calculate the modulus of elasticity Elci for - lightweight aggregate concrete. + """Calculate the modulus of elasticity Elci for lightweight aggregate + concrete. fib Model Code 2020, eq. (14.6-22) Args: fcm (float): Mean compressive strength fcm in MPa. - density (float): Oven-dry density of the lightweight - aggregate concrete in kg/m3. - alpha_E (float): Coefficient depending on the aggregate - type (default is 1.0 for quartzite). - Ec0 (float): base elastic modulus value in MPa. + density (float): Oven-dry density of the lightweight aggregate concrete + in kg/m3. + alpha_E (float): Coefficient depending on the aggregate type (default + is 1.0 for quartzite). + Ec0 (float): Base elastic modulus value in MPa. Returns: float: Modulus of elasticity El_ci in MPa. @@ -589,8 +581,8 @@ def El_ci( def E_ci_red_el(Eci: float, fcm: float) -> float: - """Calculate the reduced modulus of elasticity Ec for concrete - for only static analysis. + """Calculate the reduced modulus of elasticity Ec for concrete for only + static analysis. fib Model Code 2020, eq. (14.6-23) (14.6-24) @@ -612,13 +604,13 @@ def E_ci_red_el(Eci: float, fcm: float) -> float: def nu_c() -> float: - """Get the Poisson's ratio νc for concrete for design purposes. + """Get the Poisson's ratio nuc for concrete for design purposes. fib Model Code 2020 (14.6.1.4.3) Returns: - float: Poisson's ratio νc for concrete, - typically 0.20 for design purposes. + float: Poisson's ratio nuc for concrete, typically 0.20 for design + purposes. """ return 0.20 @@ -632,23 +624,23 @@ def sigma_c( fcm: float, k: float, ) -> float: - """Calculate the compressive stress σc for short-term - uniaxial compression of concrete. + """Calculate the compressive stress sigma_c for short-term uniaxial + compression of concrete. fib Model Code 2020, eq. (14.6-26) Args: - eps_c (float): Compressive strain εc. - eps_c1 (float): Strain at maximum compressive stress εc1. + eps_c (float): Compressive strain eps_c. + eps_c1 (float): Strain at maximum compressive stress eps_c1. eps_lim (float): Limit strain. Eci (float): Modulus of elasticity of concrete in MPa. - Ec1 (float): Secant modulus of elasticity from origin - to peak stress Ec1 in MPa. + Ec1 (float): Secant modulus of elasticity from origin to peak stress + Ec1 in MPa. fcm (float): Mean compressive strength fcm in MPa. k (float): Plasticity number k. Returns: - float: Compressive stress σc in MPa. + float: Compressive stress sigma_c in MPa. Raises: ValueError: If input values are not positive or within valid ranges. @@ -812,14 +804,14 @@ def Ec1(concrete_grade: str) -> float: def eps_c1(concrete_grade: str) -> float: - """Get the strain at maximum compressive stress - εc1 for a given concrete grade. + """Get the strain at maximum compressive stress eps_c1 for a given concrete + grade. Args: concrete_grade (str): Concrete grade (e.g., 'C20', 'C30'). Returns: - float: Strain at maximum compressive stress εc1. + float: Strain at maximum compressive stress eps_c1. Raises: ValueError: If the concrete grade is not recognized. @@ -834,13 +826,13 @@ def eps_c1(concrete_grade: str) -> float: def eps_c_lim(concrete_grade: str) -> float: - """Get the strain limit εc,lim for a given concrete grade. + """Get the strain limit eps_c,lim for a given concrete grade. Args: concrete_grade (str): Concrete grade (e.g., 'C20', 'C30'). Returns: - float: Strain limit εc,lim. + float: Strain limit eps_c,lim. Raises: ValueError: If the concrete grade is not recognized. @@ -857,7 +849,7 @@ def eps_c_lim(concrete_grade: str) -> float: def eps_lc1( flck: float, Elc: float, sand: Literal['light', 'natural'] ) -> float: - """Calculate the strain εlc1 for lightweight aggregate concrete. + """Calculate the strain eps_lc1 for lightweight aggregate concrete. fib Model Code 2020, eq. (14.6-27) @@ -867,7 +859,7 @@ def eps_lc1( sand (float): Specifies the type of sand used. Returns: - float: Strain εlc1. + float: Strain eps_lc1. Raises: ValueError: If input values are not within valid ranges. @@ -883,18 +875,18 @@ def eps_lc1( def sigma_ct_uncracked(eps_ct: float, E_ci: float, fctm: float) -> float: - """Calculate the tensile stress σct for uncracked - normal weight concrete subjected to tension. + """Calculate the tensile stress sigma_ct for uncracked normal weight + concrete subjected to tension. fib Model Code 2020, eqs. (14.6-29) and (14.6-30) Args: - eps_ct (float): Tensile strain εct. + eps_ct (float): Tensile strain eps_ct. E_ci (float): Tangent modulus of elasticity Eci in MPa. fctm (float): Mean tensile strength fctm in MPa. Returns: - float: Tensile stress σct in MPa. + float: Tensile stress sigma_ct in MPa. Raises: ValueError: If input values are not within valid ranges. @@ -917,8 +909,8 @@ def sigma_ct_uncracked(eps_ct: float, E_ci: float, fctm: float) -> float: def sigma_ct_cracked(w: float, GF: float, fctm: float) -> float: - """Calculate the tensile stress σct for cracked normal - weight concrete subjected to tension. + """Calculate the tensile stress sigma_ct for cracked normal weight concrete + subjected to tension. fib Model Code 2020, eqs. (14.6-31) and (14.6-32) @@ -928,7 +920,7 @@ def sigma_ct_cracked(w: float, GF: float, fctm: float) -> float: fctm (float): Mean tensile strength fctm in MPa. Returns: - float: Tensile stress σct in MPa. + float: Tensile stress sigma_ct in MPa. Raises: ValueError: If input values are not within valid ranges. @@ -955,8 +947,8 @@ def sigma_ct_cracked(w: float, GF: float, fctm: float) -> float: def tau_crack_friction( delta: float, w: float, fcm: float, Cf: float = 1.0 ) -> float: - """Calculate the mean shear stress τ in an open - crack subjected to shear displacements. + """Calculate the mean shear stress tau in an open crack subjected to shear + displacements. fib Model Code 2020, eq. (14.6-43) @@ -967,7 +959,7 @@ def tau_crack_friction( Cf (float): Constant C (default is 1.0). Returns: - float: Mean shear stress τ in MPa. + float: Mean shear stress tau in MPa. Raises: ValueError: If input values are not within valid ranges. @@ -987,26 +979,26 @@ def tau_crack_friction( def sigma_crack_friction( delta: float, w: float, fcm: float, Cf: float = 1.0 ) -> float: - """Calculate the mean normal stress σ in an open - crack subjected to shear displacements. + """Calculate the mean normal stress sigma in an open crack subjected to + shear displacements. fib Model Code 2020, eq. (14.6-44) Args: - delta (float): Shear displacement δ in mm. + delta (float): Shear displacement delta in mm. w (float): Crack width w in mm. fcm (float): Mean compressive strength fcm in MPa. Cf (float): Constant C (default is 1.0). Returns: - float: Mean normal stress σ in MPa. + float: Mean normal stress sigma in MPa. Raises: ValueError: If input values are not within valid ranges. """ if delta <= 0 or w <= 0 or fcm <= 0: raise ValueError( - 'Shear displacement δ, crack width w, and ' + 'Shear displacement delta, crack width w, and ' + 'mean compressive strength fcm must be positive.' ) @@ -1051,18 +1043,17 @@ def sC(fck: float, strength_class: Literal['CR', 'CN', 'CS']) -> float: def beta_cc(t: float, t_ref: float, sC: float) -> float: - """Calculate the strength development function βcc(t) for concrete. + """Calculate the strength development function beta_cc for concrete. fib Model Code 2020, eq. (14.6-46) - Args: t (float): Concrete age t in days. t_ref (float): Reference age tref in days. sC (float): Coefficient sC based on strength development class. Returns: - float: Strength development function βcc(t). + float: Strength development function beta_cc. Raises: ValueError: If the input values are not valid. @@ -1076,13 +1067,13 @@ def beta_cc(t: float, t_ref: float, sC: float) -> float: def fcm_t(fcm: float, beta_cc: float) -> float: - """Calculate the mean compressive strength fcm(t) at a given age t. + """Calculate the mean compressive strength fcm at a given age t. fib Model Code 2020, eq. (14.6-45) Args: fcm (float): Mean compressive strength at reference age fcm in MPa. - beta_cc (float): Strength development function βcc(t). + beta_cc (float): Strength development function beta_cc. Returns: float: Mean compressive strength fcm(t) in MPa. @@ -1102,8 +1093,8 @@ def slC(aggregate_strength: Literal['high', 'low']) -> float: fib Model Code 2020, eq. (14.6-47) Args: - aggregate_strength (str): Strength of - lightweight aggregate ('high' or 'low'). + aggregate_strength (str): Strength of lightweight aggregate ('high' or + 'low'). Returns: float: Coefficient slC. @@ -1113,23 +1104,22 @@ def slC(aggregate_strength: Literal['high', 'low']) -> float: """ if aggregate_strength == 'high': return 0.05 - # if aggregate_strength == 'low': return 0.25 def beta_lcc(t: float, t_ref: float, slC: float) -> float: - """Calculate the strength development function βlcc(t) - for lightweight aggregate concrete. + """Calculate the strength development function beta_lcc for lightweight + aggregate concrete. fib Model Code 2020, eq. (14.6-47) Args: t (float): Concrete age t in days. - t_ref (float): Reference age tref ins days (typically 28 days). + t_ref (float): Reference age tref in days (typically 28 days). slC (float): Coefficient slc based on aggregate strength. Returns: - float: Strength development function βlcc(t). + float: Strength development function beta_lcc(t). Raises: ValueError: If the input values are not valid. @@ -1143,8 +1133,8 @@ def beta_lcc(t: float, t_ref: float, slC: float) -> float: def flcm_t(f_lcm: float, beta_lcc: float) -> float: - """Calculate the mean compressive strength flcm(t) at - a given age t for lightweight aggregate concrete. + """Calculate the mean compressive strength flcm(t) at a given age t for + lightweight aggregate concrete. fib Model Code 2020, eq. (14.6-47) @@ -1165,8 +1155,8 @@ def flcm_t(f_lcm: float, beta_lcc: float) -> float: def beta_E(beta_cc: float) -> float: - """Calculate the modulus of elasticity development - coefficient βE(t) for concrete. + """Calculate the modulus of elasticity development coefficient beta_E for + concrete. fib Model Code 2020, eq. (14.6-49) @@ -1174,7 +1164,7 @@ def beta_E(beta_cc: float) -> float: beta_cc (float) Returns: - float: Modulus of elasticity development coefficient βE(t). + float: Modulus of elasticity development coefficient beta_E(t). Raises: ValueError: If the input values are not valid. @@ -1189,7 +1179,7 @@ def E_ci_t(E_ci: float, beta_E: float) -> float: Args: E_ci (float): Modulus of elasticity at reference age Eci in MPa. - beta_E (float): Modulus of elasticity development coefficient βE(t). + beta_E (float): Modulus of elasticity development coefficient beta_E. Returns: float: Modulus of elasticity Eci(t) in MPa. @@ -1204,8 +1194,8 @@ def E_ci_t(E_ci: float, beta_E: float) -> float: def beta_t0(t0: float) -> float: - """Calculate the parameter βt0(t0) which - considers the maturity of the concrete at loading. + """Calculate the parameter beta_t0 which considers the maturity of the + concrete at loading. fib Model Code 2020, eq. (14.6-51) @@ -1213,7 +1203,7 @@ def beta_t0(t0: float) -> float: t0 (float): Age of concrete at loading t0 in days. Returns: - float: Parameter βt0(t0). + float: Parameter beta_t0(t0). Raises: ValueError: If the input values are not valid. @@ -1227,8 +1217,8 @@ def beta_t0(t0: float) -> float: def beta_c_sus(t: float, t0: float, beta_t0: float) -> float: - """Calculate the function βc,sus(t, t0) describing the - decrease of strength with time under high sustained load. + """Calculate the function beta_c,sus(t, t0) describing the decrease of + strength with time under high sustained load. fib Model Code 2020, eq. (14.6-51) @@ -1238,7 +1228,7 @@ def beta_c_sus(t: float, t0: float, beta_t0: float) -> float: beta_t0 (float): Value of evaluated function beta_T0. Returns: - float: Function βc,sus(t, t0). + float: Function beta_c,sus(t, t0). Raises: ValueError: If the input values are not valid. @@ -1252,16 +1242,16 @@ def beta_c_sus(t: float, t0: float, beta_t0: float) -> float: def fcm_sus(fcm: float, beta_cc_t: float, beta_c_sus: float) -> float: - """Calculate the mean compressive strength - fcm,sus(t, t0) under sustained load. + """Calculate the mean compressive strength fcm,sus(t, t0) under sustained + load. fib Model Code 2020, eq. (14.6-50) Args: f_cm (float): Mean compressive strength at reference age fcm in MPa. - beta_cc_t (float): Strength development function βcc(t). - beta_c_sus_t_t0 (float): Function βc,sus(t, t0) describing - the decrease of strength under high sustained load. + beta_cc_t (float): Strength development function beta_cc(t). + beta_c_sus_t_t0 (float): Function beta_c,sus(t, t0) describing the + decrease of strength under high sustained load. Returns: float: Mean compressive strength fcm,sus(t, t0) in MPa. @@ -1278,15 +1268,15 @@ def fcm_sus(fcm: float, beta_cc_t: float, beta_c_sus: float) -> float: def fctk_sus( fctk: float, concrete: Literal['normal', 'high-strength'] ) -> float: - """Calculate the sustained tensile strength fctk,sus - for concrete under sustained loading. + """Calculate the sustained tensile strength fctk,sus for concrete under + sustained loading. fib Model Code 2020, eq. (14.6-52) Args: fctk (float): Short-term tensile strength fctk in MPa. - alpha (float): Reduction factor for sustained loading - (default is 0.60). + alpha (float): Reduction factor for sustained loading (default is + 0.60). Returns: float: Sustained tensile strength fctk,sus in MPa. @@ -1302,18 +1292,19 @@ def fctk_sus( def eps_cc(sigma_c_t0: float, phi_t_t0: float, E_ci: float) -> float: - """Calculate the creep strain εcc(t, t0) at time - t for a concrete member under sustained loading. + """Calculate the creep strain eps_cc at time t for a concrete member under + sustained loading. fib Model Code 2020, eq. (14.6-55) Args: - sigma_c_t0 (float): Constant stress applied at time t0, σc(t0) in MPa. - phi_t_t0 (float): Creep coefficient φ(t, t0). + sigma_c_t0 (float): Constant stress applied at time t0, sigma_c(t0) in + MPa. + phi_t_t0 (float): Creep coefficient phi(t, t0). E_ci (float): Modulus of elasticity at the age of 28 days, Eci in MPa. Returns: - float: Creep strain εcc(t, t0). + float: Creep strain eps_cc(t, t0). Raises: ValueError: If input values are not within valid ranges @@ -1327,19 +1318,19 @@ def eps_cc(sigma_c_t0: float, phi_t_t0: float, E_ci: float) -> float: def eps_c_sigma( sigma_c_t0: float, E_ci_t0: float, E_ci: float, phi_t_t0: float ) -> float: - """Calculate the stress-dependent strain εcσ(t, t0) at time t - for a concrete member under sustained loading. + """Calculate the stress-dependent strain eps_c_sigma at time t for a + concrete member under sustained loading. fib Model Code 2020, eq. (14.6-56) Args: - sigma_c_t0 (float): value of the stress at time t0 in MPa. - E_ci_t0 (float): modulus of elasticity at time t0 in MPa. - E_ci (float): modulus of elasticity of concrete in MPa. - phi_t_t0 (float): Creep coefficient φ(t, t0). + sigma_c_t0 (float): Value of the stress at time t0 in MPa. + E_ci_t0 (float): Modulus of elasticity at time t0 in MPa. + E_ci (float): Modulus of elasticity of concrete in MPa. + phi_t_t0 (float): Creep coefficient phi(t, t0). Returns: - float: Stress-dependent strain εcσ(t, t0). + float: Stress-dependent strain eps_c_sigma(t, t0). Raises: ValueError: If input values are not within valid ranges. @@ -1351,17 +1342,17 @@ def eps_c_sigma( def phi_t_t0(phi_bc_t_t0: float, phi_dc_t_t0: float) -> float: - """Calculate the total creep coefficient φ(t, t0) - for concrete under sustained loading. + """Calculate the total creep coefficient phi(t, t0) for concrete under + sustained loading. fib Model Code 2020, eq. (14.6-58) Args: - phi_bc_t_t0 (float): basic creep coefficient. - phi_dc_t_t0 (float): drying creep coefficient. + phi_bc_t_t0 (float): Basic creep coefficient. + phi_dc_t_t0 (float): Drying creep coefficient. Returns: - float: Total creep coefficient φ(t, t0). + float: Total creep coefficient phi(t, t0). Raises: ValueError: If input values are not within valid ranges. @@ -1370,8 +1361,8 @@ def phi_t_t0(phi_bc_t_t0: float, phi_dc_t_t0: float) -> float: def beta_bc_fcm(fcm: float) -> float: - """Calculate the mean compressive strength factor - (fcm) for drying creep coefficient. + """Calculate the mean compressive strength factor (fcm) for drying creep + coefficient. fib Model Code 2020, eq. (14.6-60) @@ -1379,7 +1370,7 @@ def beta_bc_fcm(fcm: float) -> float: fcm (float): Mean compressive strength at an age of 28 days fcm in MPa. Returns: - float: Mean compressive strength factor β_bc_fcm(f_cm). + float: Mean compressive strength factor beta_bc_fcm(f_cm). Raises: ValueError: If input values are not within valid ranges. @@ -1391,8 +1382,7 @@ def beta_bc_fcm(fcm: float) -> float: def beta_bc_t_t0(t: float, t0: float, t0_adj: float) -> float: - """Calculte the beta_bc coefficient evaluated for - t,t0. + """Calculte the beta_bc coefficient evaluated for t,t0. fib Model Code 2020, eq. (14.6-61) @@ -1421,8 +1411,8 @@ def beta_bc_t_t0(t: float, t0: float, t0_adj: float) -> float: def phi_bc_t_t0(beta_bc_t_t0: float, beta_bc_fcm: float) -> float: - """Calculate the basic creep coefficient φbc(t, t0) - for concrete under sustained loading. + """Calculate the basic creep coefficient phi_bc(t, t0) for concrete under + sustained loading. fib Model Code 2020, eqs. (14.6-59) @@ -1431,7 +1421,7 @@ def phi_bc_t_t0(beta_bc_t_t0: float, beta_bc_fcm: float) -> float: beta_bc_fcm (float): value of beta_bc_fcm. Returns: - float: Basic creep coefficient φbc(t, t0). + float: Basic creep coefficient phi_bc(t, t0). Raises: ValueError: If input values are not within valid ranges. @@ -1443,8 +1433,8 @@ def phi_bc_t_t0(beta_bc_t_t0: float, beta_bc_fcm: float) -> float: def beta_dc_fcm(fcm: float) -> float: - """Calculate the mean compressive strength factor - βf_cm(fcm) for drying creep coefficient. + """Calculate the mean compressive strength factor beta_f_cm(fcm) for drying + creep coefficient. fib Model Code 2020, eq. (14.6-63) @@ -1452,7 +1442,7 @@ def beta_dc_fcm(fcm: float) -> float: fcm (float): Mean compressive strength at an age of 28 days fcm in MPa. Returns: - float: Mean compressive strength factor βf_cm(f_cm). + float: Mean compressive strength factor beta_f_cm(f_cm). Raises: ValueError: If input values are not within valid ranges. @@ -1464,8 +1454,8 @@ def beta_dc_fcm(fcm: float) -> float: def beta_RH_c(RH: float, h: float) -> float: - """Calculate the relative humidity factor βRH(RH) - for drying creep coefficient. + """Calculate the relative humidity factor beta_RH(RH) for drying creep + coefficient. fib Model Code 2020, eq. (14.6-64) @@ -1474,7 +1464,7 @@ def beta_RH_c(RH: float, h: float) -> float: h (float): Notional size of the member h in mm. Returns: - float: Relative humidity factor βRH(RH). + float: Relative humidity factor beta_RH(RH). Raises: ValueError: If input values are not within valid ranges. @@ -1488,7 +1478,7 @@ def beta_RH_c(RH: float, h: float) -> float: def beta_dc_t0(t0_adj: float) -> float: - """Calculate the time factor βt0(t0) for drying creep coefficient. + """Calculate the time factor beta_t0(t0) for drying creep coefficient. fib Model Code 2020, eq. (14.6-65) @@ -1496,7 +1486,7 @@ def beta_dc_t0(t0_adj: float) -> float: t0_adj (float): Adjusted age at loading t0,adj in days. Returns: - float: Time factor βt0(t0). + float: Time factor beta_t0(t0). Raises: ValueError: If input values are not within valid ranges. @@ -1508,7 +1498,7 @@ def beta_dc_t0(t0_adj: float) -> float: def gamma_t0(t0_adj: float) -> float: - """Calculate the factor γ for drying creep development with time. + """Calculate the factor gamma for drying creep development with time. fib Model Code 2020, eq. (14.6-66b) @@ -1516,7 +1506,7 @@ def gamma_t0(t0_adj: float) -> float: t0_adj (float): Adjusted age at loading t0,adj in days. Returns: - float: Factor γ. + float: Factor gamma. Raises: ValueError: If input values are not within valid ranges. @@ -1553,7 +1543,7 @@ def beta_h(h: float, fcm: float) -> float: def beta_dc_t_t0(t: float, t0: float, gamma_t0: float, beta_h: float) -> float: - """Calculate the development of drying creep with time βdc(t, t0). + """Calculate the development of drying creep with time beta_dc(t, t0). fib Model Code 2020, eq. (14.6-66a) @@ -1566,7 +1556,7 @@ def beta_dc_t_t0(t: float, t0: float, gamma_t0: float, beta_h: float) -> float: Returns: - float: Development of drying creep with time βdc(t, t0). + float: Development of drying creep with time beta_dc(t, t0). Raises: ValueError: If input values are not within valid ranges. @@ -1587,8 +1577,8 @@ def phi_dc_t_t0( beta_dc_t0: float, beta_dc_t_t0: float, ) -> float: - """Calculate the drying creep coefficient φdc(t, t0) - for concrete under sustained loading. + """Calculate the drying creep coefficient phi_dc(t, t0) for concrete under + sustained loading. fib Model Code 2020, eq. (14.6-62) @@ -1599,7 +1589,7 @@ def phi_dc_t_t0( beta_dc_t_t0 (float): beta_dc_t_t0 coefficient. Returns: - float: Drying creep coefficient φdc(t, t0). + float: Drying creep coefficient phi_dc(t, t0). Raises: ValueError: If input values are not within valid ranges. @@ -1608,28 +1598,29 @@ def phi_dc_t_t0( def phi_l_t_t0(phi_t_t0: float, rho: float, concrete_grade: str) -> float: - """Calculate the creep coefficient φl for lightweight aggregate concrete. + """Calculate the creep coefficient phi_l for lightweight aggregate + concrete. fib Model Code 2020, eq. (14.6-67) Args: - phi_t_t0 (float): Creep coefficient φ(t, t0) - according to Eq. (14.6-58). - rho (float): Oven-dry density of - lightweight aggregate concrete in kg/m3. + phi_t_t0 (float): Creep coefficient phi(t, t0) according to Eq. + (14.6-58). + rho (float): Oven-dry density of lightweight aggregate concrete in + kg/m3. concrete_grade (str): Grade of concrete (e.g., 'LC12', 'LC16', 'LC20'). Returns: - float: Creep coefficient φl for lightweight aggregate concrete. + float: Creep coefficient phi_l for lightweight aggregate concrete. Raises: - ValueError: If input values are not within - valid ranges or invalid grade. + ValueError: If input values are not within valid ranges or invalid + grade. """ if rho <= 0: - raise ValueError('Oven-dry density ρ must be positive.') + raise ValueError('Oven-dry density rho must be positive.') if phi_t_t0 < 0: - raise ValueError('Creep coefficient φ(t, t0) must not be negative.') + raise ValueError('Creep coefficient phi(t, t0) must not be negative.') # Calculate ηE eta_E = (rho / 2200) ** 2 @@ -1645,23 +1636,23 @@ def phi_l_t_t0(phi_t_t0: float, rho: float, concrete_grade: str) -> float: def t0_adj(t0_T: float, strength_class: Literal['CS', 'CN', 'CR']) -> float: - """Calculate the adjusted age at loading t0,adj for - concrete under sustained loading. + """Calculate the adjusted age at loading t0,adj for concrete under + sustained loading. fib Model Code 2020, eq. (14.6-68) Args: - t0_T (float): Age of concrete at loading in - days adjusted according to Eq. (14.6-80). - strength_class (str): Strength development - class of concrete ('CS', 'CN', 'CR'). + t0_T (float): Age of concrete at loading in days adjusted according to + Eq. (14.6-80). + strength_class (str): Strength development class of concrete ('CS', + 'CN', 'CR'). Returns: float: Adjusted age at loading t0,adj in days. Raises: - ValueError: If input values are not - within valid ranges or invalid class. + ValueError: If input values are not within valid ranges or invalid + class. """ if t0_T <= 0: raise ValueError('Age of concrete at loading t0_T must be positive.') @@ -1677,24 +1668,24 @@ class of concrete ('CS', 'CN', 'CR'). def phi_sigma_t_t0(phi_t_t0: float, sigma_c: float, fcm_t0: float) -> float: - """Calculate the non-linear creep coefficient φσ(t, t0) - for concrete under high stress. + """Calculate the non-linear creep coefficient phi_sigma(t, t0) for concrete + under high stress. fib Model Code 2020, eq. (14.6-69) Args: - phi_t_t0 (float): Linear creep coefficient φ(t, t0) - according to Eq. (14.6-58). + phi_t_t0 (float): Linear creep coefficient phi(t, t0) according to Eq. + (14.6-58). sigma_c (float): Applied stress in MPa. - fcm_t0 (float): Mean compressive strength of concrete - at the age of loading t0 in MPa. + fcm_t0 (float): Mean compressive strength of concrete at the age of + loading t0 in MPa. Returns: - float: Non-linear creep coefficient φσ(t, t0). + float: Non-linear creep coefficient phi_sigma(t, t0). Raises: - ValueError: If input values are not within valid ranges or - if stress-strength ratio is not within 0.4 < kσ ≤ 0.6. + ValueError: If input values are not within valid ranges or if + stress-strength ratio is not within 0.4 < k_sigma ≤ 0.6. """ if phi_t_t0 < 0: raise ValueError( @@ -1713,53 +1704,55 @@ def phi_sigma_t_t0(phi_t_t0: float, sigma_c: float, fcm_t0: float) -> float: def eps_cs_t_ts(eps_cbs_t: float, eps_cds_t_ts: float) -> float: - """Calculate the total shrinkage strain εcs(t, ts). + """Calculate the total shrinkage strain eps_cs(t, ts). fib Model Code 2020, eq. (14.6-70) Args: - eps_cbs_t (float): Basic shrinkage strain εcbs(t). - eps_cds_t_ts (float): Drying shrinkage strain εcds(t, ts). + eps_cbs_t (float): Basic shrinkage strain eps_cbs(t). + eps_cds_t_ts (float): Drying shrinkage strain eps_cds(t, ts). Returns: - float: Total shrinkage strain εcs(t, ts). + float: Total shrinkage strain eps_cs(t, ts). Raises: ValueError: If input values are not within valid ranges. """ if eps_cbs_t < 0: raise ValueError( - 'Basic shrinkage strain εcbs(t) must not be negative.' + 'Basic shrinkage strain eps_cbs(t) must not be negative.' ) if eps_cds_t_ts < 0: raise ValueError( - 'Drying shrinkage strain εcds(t, ts) must not be negative.' + 'Drying shrinkage strain eps_cds(t, ts) must not be negative.' ) return eps_cbs_t + eps_cds_t_ts def eps_cbs_t(eps_cbs0: float, beta_bs_t: float) -> float: - """Calculate the basic shrinkage strain εcbs(t). + """Calculate the basic shrinkage strain eps_cbs(t). fib Model Code 2020, eq. (14.6-71) Args: - eps_cbs0 (float): Basic shrinkage strain coefficient εcbs0. - beta_bs_t (float): Development function for basic shrinkage βbs(t). + eps_cbs0 (float): Basic shrinkage strain coefficient eps_cbs0. + beta_bs_t (float): Development function for basic shrinkage beta_bs(t). Returns: - float: Basic shrinkage strain εcbs(t). + float: Basic shrinkage strain eps_cbs(t). Raises: ValueError: If input values are not within valid ranges. """ if eps_cbs0 < 0: raise ValueError( - 'Basic shrinkage strain coefficient εcbs0 must not be negative.' + 'Basic shrinkage strain coefficient eps_cbs0 must not be negative.' ) if beta_bs_t < 0: - raise ValueError('Development function βbs(t) must not be negative.') + raise ValueError( + 'Development function beta_bs(t) must not be negative.' + ) return eps_cbs0 * beta_bs_t @@ -1767,51 +1760,54 @@ def eps_cbs_t(eps_cbs0: float, beta_bs_t: float) -> float: def eps_cds_t_ts( eps_cds0: float, beta_RH: float, beta_ds_t_ts: float ) -> float: - """Calculate the drying shrinkage strain εcds(t, ts). + """Calculate the drying shrinkage strain eps_cds(t, ts). fib Model Code 2020, eq. (14.6-72) Args: - eps_cds0 (float): Drying shrinkage strain coefficient εcds0. - beta_RH (float): Relative humidity factor βRH(RH). - beta_ds_t_ts (float): Development function for - drying shrinkage βds(t - ts). + eps_cds0 (float): Drying shrinkage strain coefficient eps_cds0. + beta_RH (float): Relative humidity factor beta_RH(RH). + beta_ds_t_ts (float): Development function for drying shrinkage + beta_ds(t - ts). Returns: - float: Drying shrinkage strain εcds(t, ts). + float: Drying shrinkage strain eps_cds(t, ts). Raises: ValueError: If input values are not within valid ranges. """ if eps_cds0 < 0: raise ValueError( - 'Drying shrinkage strain coefficient εcds0 must not be negative.' + ( + 'Drying shrinkage strain coefficient eps_cds0 must not be ' + 'negative.' + ) ) if beta_RH < 0 or beta_RH > 1: raise ValueError( - 'Relative humidity factor βRH must be between 0 and 1.' + 'Relative humidity factor beta_RH must be between 0 and 1.' ) if beta_ds_t_ts < 0: raise ValueError( - 'Development function βds(t - ts) must not be negative.' + 'Development function beta_ds(t - ts) must not be negative.' ) return eps_cds0 * beta_RH * beta_ds_t_ts def eps_cbs_0(fcm: float, alpha_bs: float) -> float: - """Calculate the basic notional shrinkage coefficient εcbs0(fcm). + """Calculate the basic notional shrinkage coefficient eps_cbs0(fcm). fib Model Code 2020, eq. (14.6-73) Args: - fcm (float): Mean compressive strength at the age of - 28 days fcm in MPa. - alpha_bs (float): Coefficient dependent on - the strength development class of concrete. + fcm (float): Mean compressive strength at the age of 28 days fcm in + MPa. + alpha_bs (float): Coefficient dependent on the strength development + class of concrete. Returns: - float: Basic notional shrinkage coefficient εcbs0(fcm). + float: Basic notional shrinkage coefficient eps_cbs0(fcm). Raises: ValueError: If input values are not within valid ranges. @@ -1825,7 +1821,7 @@ def eps_cbs_0(fcm: float, alpha_bs: float) -> float: def beta_bs_t(t: float) -> float: - """Calculate the time function for basic shrinkage βbs(t). + """Calculate the time function for basic shrinkage beta_bs(t). fib Model Code 2020, eq. (14.6-74) @@ -1833,7 +1829,7 @@ def beta_bs_t(t: float) -> float: t (float): Age of concrete in days. Returns: - float: Time function for basic shrinkage βbs(t). + float: Time function for basic shrinkage beta_bs(t). Raises: ValueError: If input values are not within valid ranges. @@ -1853,14 +1849,15 @@ def beta_bs_t(t: float) -> float: def alpha_bs(strength_class: Literal['CS', 'CN', 'CR']) -> tuple: - """Retrieve the coefficient αbs strength development class of concrete. + """Retrieve the coefficient alpha_bs strength development class of + concrete. Args: - strength_class (str): The strength development - class of concrete ('CS', 'CN', 'CR'). + strength_class (str): The strength development class of concrete ('CS', + 'CN', 'CR'). Returns: - float: the value of αbs. + float: the value of alpha_bs. Raises: ValueError: If the strength development class is not recognized. @@ -1878,14 +1875,15 @@ class of concrete ('CS', 'CN', 'CR'). def alpha_ds_1(strength_class: Literal['CS', 'CN', 'CR']) -> tuple: - """Retrieve the coefficient αds1 strength development class of concrete. + """Retrieve the coefficient alpha_ds1 strength development class of + concrete. Args: - strength_class (str): The strength development - class of concrete ('CS', 'CN', 'CR'). + strength_class (str): The strength development class of concrete ('CS', + 'CN', 'CR'). Returns: - float: the value of αds1. + float: the value of alpha_ds1. Raises: ValueError: If the strength development class is not recognized. @@ -1903,14 +1901,15 @@ class of concrete ('CS', 'CN', 'CR'). def alpha_ds_2(strength_class: Literal['CS', 'CN', 'CR']) -> tuple: - """Retrieve the coefficient αds2 strength development class of concrete. + """Retrieve the coefficient alpha_ds2 strength development class of + concrete. Args: - strength_class (str): The strength development - class of concrete ('CS', 'CN', 'CR'). + strength_class (str): The strength development class of concrete ('CS', + 'CN', 'CR'). Returns: - float: the value of αds2. + float: the value of alpha_ds2. Raises: ValueError: If the strength development class is not recognized. @@ -1928,19 +1927,19 @@ class of concrete ('CS', 'CN', 'CR'). def eps_cds_0_fcm(fcm: float, alpha_ds1: float, alpha_ds2: float) -> float: - """Calculate the notional drying shrinkage coefficient εcds0(fcm). + """Calculate the notional drying shrinkage coefficient eps_cds0(fcm). fib Model Code 2020, eq. (14.6-75) Args: fcm (float): Mean compressive strength at the age of 28 days in MPa. - alpha_ds1 (float): Coefficient dependent on the strength - development class of concrete. - alpha_ds2 (float): Coefficient dependent on the strength - development class of concrete. + alpha_ds1 (float): Coefficient dependent on the strength development + class of concrete. + alpha_ds2 (float): Coefficient dependent on the strength development + class of concrete. Returns: - float: Notional drying shrinkage coefficient εcds0(fcm). + float: Notional drying shrinkage coefficient eps_cds0(fcm). Raises: ValueError: If input values are not within valid ranges. @@ -1952,7 +1951,7 @@ def eps_cds_0_fcm(fcm: float, alpha_ds1: float, alpha_ds2: float) -> float: def beta_RH_s(RH: float, RH_eq: float) -> float: - """Calculate the relative humidity factor βRH(RH) for shrinkage. + """Calculate the relative humidity factor beta_RH(RH) for shrinkage. fib Model Code 2020, eq. (14.6-76) @@ -1961,7 +1960,7 @@ def beta_RH_s(RH: float, RH_eq: float) -> float: RH_eq (float): value of equivalent humidity. Returns: - float: Relative humidity factor βRH(RH). + float: Relative humidity factor beta_RH(RH). Raises: ValueError: If input values are not within valid ranges. @@ -1981,8 +1980,8 @@ def beta_RH_s(RH: float, RH_eq: float) -> float: def beta_ds_t_ts(t: float, ts: float, h: float) -> float: - """Calculate the time-development function - for drying shrinkage βds(t - ts). + """Calculate the time-development function for drying shrinkage + beta_ds(t - ts). fib Model Code 2020, eq. (14.6-77) @@ -1992,15 +1991,17 @@ def beta_ds_t_ts(t: float, ts: float, h: float) -> float: h (float): Notional size of the member in mm. Returns: - float: Time-development function for drying shrinkage βds(t - ts). + float: Time-development function for drying shrinkage beta_ds(t - ts). Raises: ValueError: If input values are not within valid ranges. """ if t < ts: raise ValueError( - 'Concrete age t must be greater than or ' - + 'equal to the beginning of drying ts.' + ( + 'Concrete age t must be greater than or ' + 'equal to the beginning of drying ts.' + ) ) if h <= 0: raise ValueError('Notional size h must be positive.') @@ -2041,21 +2042,23 @@ def eps_lcs_t_ts( 'LC80', ], ) -> float: - """Calculate the shrinkage of lightweight aggregate concrete εlcs(t, ts). + """Calculate the shrinkage of lightweight aggregate concrete + eps_lcs(t, ts). fib Model Code 2020, eq. (14.6-79) Args: - eps_cs_t_ts (float): Shrinkage strain for normal - weight concrete εcs(t, ts). - concrete_grade (str): Grade of lightweight aggregate concrete - (e.g., 'LC8', 'LC12', 'LC16', 'LC20'). + eps_cs_t_ts (float): Shrinkage strain for normal weight concrete + eps_cs(t, ts). + concrete_grade (str): Grade of lightweight aggregate concrete (e.g., + 'LC8', 'LC12', 'LC16', 'LC20'). Returns: - float: Shrinkage strain for lightweight aggregate concrete εlcs(t, ts). + float: Shrinkage strain for lightweight aggregate concrete + eps_lcs(t, ts). Raises: - ValueError: if invalid concrete grade. + ValueError: If invalid concrete grade. """ # Define eta values for different concrete grades eta_values = { @@ -2083,8 +2086,10 @@ def eps_lcs_t_ts( eta = eta_values[concrete_grade] else: raise ValueError( - 'Invalid concrete grade. Choose from ' - + "'LC8', 'LC12', 'LC16', 'LC20', and higher." + ( + 'Invalid concrete grade. Choose from ' + "'LC8', 'LC12', 'LC16', 'LC20', and higher." + ) ) # Calculate shrinkage for lightweight aggregate concrete @@ -2100,10 +2105,10 @@ def t_T_maturity( Args: temperature_intervals (List[Tuple[float, float]]): A list of tuples - where each tuple contains - (Δti, T(Δti)), where Δti is the number of days - where a temperature T prevails, and T(Δti) is the - mean temperature in °C during the time period Δti. + where each tuple contains (delta_ti, T(delta_ti)), where delta_ti + is the number of days where a temperature T prevails, and + T(delta_ti) is the mean temperature in degrees C during the time + period delta_ti. Returns: float: Temperature-adjusted concrete age tT in days. @@ -2118,10 +2123,10 @@ def t_T_maturity( for delta_t, T in temperature_intervals: if delta_t < 0: - raise ValueError('Number of days Δti must not be negative.') + raise ValueError('Number of days delta_ti must not be negative.') if T < -273.15: raise ValueError( - 'Temperature T(Δti) must be above absolute zero (-273°C).' + 'Temperature T(delta_ti) must be above absolute zero (-273C).' ) t_T += delta_t * math.exp(13.65 - 4000 / (273 + T)) @@ -2130,20 +2135,21 @@ def t_T_maturity( def eps_c_T(delta_T: float, alpha_T: str) -> float: - """Calculate the thermal strain εcT due to thermal expansion of concrete. + """Calculate the thermal strain eps_cT due to thermal expansion of + concrete. fib Model Code 2020, eq. (14.6-81) Args: delta_T (float): Change in temperature in Kelvin (K). - alpha_T (str): thermal expecansion coefficient. + alpha_T (str): Thermal expecansion coefficient. Returns: - float: Thermal strain εcT. + float: Thermal strain eps_cT. Raises: - ValueError: If input values are not within - valid ranges or invalid concrete type. + ValueError: If input values are not within valid ranges or invalid + concrete type. """ if alpha_T < 0: raise ValueError('alpha_T must not be negative.') @@ -2153,27 +2159,27 @@ def eps_c_T(delta_T: float, alpha_T: str) -> float: def alpha_T(concrete_type: Literal['normal', 'lightweight']) -> float: - """Return the thermal expansion coefficient as a function - of the concrete type. + """Return the thermal expansion coefficient as a function of the concrete + type. Args: - concrete_type (str): concrete type. + concrete_type (str): Concrete type. Returns: - float: the thermal expansion coefficient in K-1 + float: The thermal expansion coefficient in 1/K. """ return 8e-6 if concrete_type == 'lightweight' else 10e-6 def fcm_T(fcm: float, T: float) -> float: - """Calculate the compressive strength fcm(T) for - normal weight concrete at a given temperature. + """Calculate the compressive strength fcm(T) for normal weight concrete at + a given temperature. fib Model Code 2020, eq. (14.6-82) Args: - fcm (float): Compressive strength in MPa at T = 20°C. - T (float): Temperature in °C. + fcm (float): Compressive strength in MPa at T = 20 degrees C. + T (float): Temperature in degrees C. Returns: float: Compressive strength fcm(T) at temperature T in MPa. @@ -2188,14 +2194,14 @@ def fcm_T(fcm: float, T: float) -> float: def flcm_T(flcm: float, T: float) -> float: - """Calculate the compressive strength flcm(T) - for lightweight aggregate concrete at a given temperature. + """Calculate the compressive strength flcm(T) for lightweight aggregate + concrete at a given temperature. fib Model Code 2020, eq. (14.6-83) Args: - flcm (float): Compressive strength in MPa at T = 20°C. - T (float): Temperature in °C. + flcm (float): Compressive strength in MPa at T = 20 degrees C. + T (float): Temperature in degrees C. Returns: float: Compressive strength flcm(T) at temperature T in MPa. @@ -2215,8 +2221,8 @@ def fcm_hT_T(fcm_href_Tref: float, D_fc: float, S_fc: float) -> float: fib Model Code 2020, eq. (14.6-84) Args: - fcm_href_Tref (float): Compressive strength in MPa - at Tref = 20°C at reference storage conditions. + fcm_href_Tref (float): Compressive strength in MPa at Tref = 20 degrees + C at reference storage conditions. D_fc (float): Drying effect parameter. S_fc (float): Thermal dilation incompatibility factor. @@ -2238,9 +2244,9 @@ def D_fc(h_Tref: float) -> float: fib Model Code 2020, eq. (14.6-85) Args: - T (float): Temperature in °C. - h_Tref (float): Relative humidity of the - concrete pores at temperature T. + T (float): Temperature in degrees C. + h_Tref (float): Relative humidity of the concrete pores at temperature + T. Returns: float: Drying effect parameter D_fc. @@ -2262,8 +2268,9 @@ def hT_Tref(hT: float, K_hT: float, T: float, T_ref: float = 20.0) -> float: Args: hT (float): Relative humidity at reference temperature T_ref. K_hT (float): Coefficient for computing the relative humidity. - T (float): Temperature in °C. - T_ref (float): Reference temperature in °C, default is 20°C. + T (float): Temperature in degrees C. + T_ref (float): Reference temperature in degrees C, default is 20 + degrees C. Returns: float: Relative humidity h_Tref in the concrete pores at temperature T. @@ -2303,7 +2310,7 @@ def S_fc(T: float, alpha_fc: float) -> float: fib Model Code 2020, eq. (14.6-88) Args: - T (float): Temperature in °C. + T (float): Temperature in degrees C. alpha_fc (float): Alpha coefficient. Returns: @@ -2337,14 +2344,14 @@ def alpha_fc(h_Tref: float) -> float: def fctm_T(fctm: float, T: float) -> float: - """Calculate the uniaxial tensile strength fctm(T) - for concrete at a given temperature. + """Calculate the uniaxial tensile strength fctm(T) for concrete at a given + temperature. fib Model Code 2020, eq. (14.6-90) Args: - fctm (float): Uniaxial tensile strength in MPa at T = 20°C. - T (float): Temperature in °C. + fctm (float): Uniaxial tensile strength in MPa at T = 20 degrees C. + T (float): Temperature in degrees C. Returns: float: Uniaxial tensile strength fctm(T) at temperature T in MPa. @@ -2358,14 +2365,14 @@ def fctm_T(fctm: float, T: float) -> float: def GF_T(GF: float, T: float, concrete_type: Literal['dry', 'mass']) -> float: - """Calculate the fracture energy GF(T) for dry - concrete at a given temperature. + """Calculate the fracture energy GF(T) for dry concrete at a given + temperature. fib Model Code 2020, eq. (14.6-93a) Args: - GF (float): Fracture energy in N/m at T = 20°C. - T (float): Temperature in °C. + GF (float): Fracture energy in N/m at T = 20 degrees C. + T (float): Temperature in degrees C. concrete_type (str): Concrete type. Returns: @@ -2385,13 +2392,14 @@ def GF_T(GF: float, T: float, concrete_type: Literal['dry', 'mass']) -> float: def fctm_hT_T(fctm_ref: float, D_fct: float, S_fct: float) -> float: - """Calculate the uniaxial tensile strength f_ctm(h_T, T) under - combined effects of temperature and drying. + """Calculate the uniaxial tensile strength f_ctm(h_T, T) under combined + effects of temperature and drying. fib Model Code 2020, eq. (14.6-94) Args: - fctm_ref (float): Uniaxial tensile strength in MPa at Tref = 20°C. + fctm_ref (float): Uniaxial tensile strength in MPa at Tref = 20 degrees + C. D_fct (float): Drying effect parameter. S_fct (float): Thermal dilation incompatibility factor. @@ -2415,9 +2423,9 @@ def D_fct(T: float, hT_Tref: float) -> float: fib Model Code 2020, eq. (14.6-95) Args: - T (float): Temperature in °C. - hT_Tref (float): Relative humidity of the - concrete pores at temperature T. + T (float): Temperature in degrees C. + hT_Tref (float): Relative humidity of the concrete pores at temperature + T. Returns: float: Drying effect parameter D_fct. @@ -2426,7 +2434,12 @@ def D_fct(T: float, hT_Tref: float) -> float: ValueError: If input values are not within valid ranges. """ if T < 20 or T > 80: - raise ValueError('Temperature T must be in the range 20°C ≤ T ≤ 80°C.') + raise ValueError( + ( + 'Temperature T must be in the range 20 degrees C ≤ T ≤ 80 ' + 'degrees C.' + ) + ) if hT_Tref < 0 or hT_Tref > 1: raise ValueError('Relative humidity h_T must be between 0 and 1.') @@ -2439,7 +2452,7 @@ def S_fct(T: float, alpha_fct: float) -> float: fib Model Code 2020, eq. (14.6-96) Args: - T (float): Temperature in °C. + T (float): Temperature in degrees C. alpha_fct (float): Alpha coefficient. Returns: @@ -2449,14 +2462,19 @@ def S_fct(T: float, alpha_fct: float) -> float: ValueError: If input values are not within valid ranges. """ if T < 20 or T > 80: - raise ValueError('Temperature T must be in the range 20°C ≤ T ≤ 80°C.') + raise ValueError( + ( + 'Temperature T must be in the range 20 degrees C ≤ T ≤ 80 ' + 'degrees C.' + ) + ) ratio = (T - 20) / 100 return 0.45 * ratio**2 + alpha_fct * ratio def alpha_fct(hT_Tref: float) -> float: - """Calculate the alpha coefficientalpha_fct. + """Calculate the alpha coefficient alpha_fct. fib Model Code 2020, eq. (14.6-97) @@ -2476,14 +2494,14 @@ def alpha_fct(hT_Tref: float) -> float: def E_ci_T(E_ci: float, T: float) -> float: - """Calculate the modulus of elasticity E_ci(T) - for normal weight concrete at a given temperature. + """Calculate the modulus of elasticity E_ci(T) for normal weight concrete + at a given temperature. fib Model Code 2020, eq. (14.6-98) Args: - E_ci (float): Modulus of elasticity in MPa at T = 20°C. - T (float): Temperature in °C. + E_ci (float): Modulus of elasticity in MPa at T = 20 degrees C. + T (float): Temperature in degrees C. Returns: float: Modulus of elasticity E_ci(T) at temperature T in MPa. @@ -2494,20 +2512,25 @@ def E_ci_T(E_ci: float, T: float) -> float: if E_ci <= 0: raise ValueError('Modulus of elasticity E_ci must be positive.') if T < 0 or T > 80: - raise ValueError('Temperature T must be in the range 0°C ≤ T ≤ 80°C.') + raise ValueError( + ( + 'Temperature T must be in the range 0 degrees C ≤ T ≤ 80 ' + 'degrees C.' + ) + ) return E_ci * (1.06 - 0.003 * T) def El_ci_T(E_l_ci: float, T: float) -> float: - """Calculate the modulus of elasticity E_l_ci(T) - for lightweight aggregate concrete at a given temperature. + """Calculate the modulus of elasticity E_l_ci(T) for lightweight aggregate + concrete at a given temperature. fib Model Code 2020, eq. (14.6-99) Args: - E_lci (float): Modulus of elasticity in MPa at T = 20°C. - T (float): Temperature in °C. + E_lci (float): Modulus of elasticity in MPa at T = 20 degrees C. + T (float): Temperature in degrees C. Returns: float: Modulus of elasticity E_l_ci(T) at temperature T in MPa. @@ -2518,20 +2541,25 @@ def El_ci_T(E_l_ci: float, T: float) -> float: if E_l_ci <= 0: raise ValueError('Modulus of elasticity E_lci must be positive.') if T < 0 or T > 80: - raise ValueError('Temperature T must be in the range 0°C ≤ T ≤ 80°C.') + raise ValueError( + ( + 'Temperature T must be in the range 0 degrees C ≤ T ≤ 80 ' + 'degrees C.' + ) + ) return E_l_ci * (1.04 - 0.002 * T) def E_cm_hT_T(E_cm_ref: float, D_Ec: float, S_Ec: float) -> float: - """Calculate the modulus of elasticity E_cm(hT, T) - under combined effects of temperature and drying. + """Calculate the modulus of elasticity E_cm(hT, T) under combined effects + of temperature and drying. fib Model Code 2020, eq. (14.6-100) Args: - E_cm_ref (float): Modulus of elasticity in MPa at - Tref = 20°C at reference storage conditions. + E_cm_ref (float): Modulus of elasticity in MPa at Tref = 20 degrees C + at reference storage conditions. D_Ec (float): Drying effect parameter. S_Ec (float): Thermal dilation incompatibility factor. @@ -2553,9 +2581,9 @@ def D_Ec(T: float, hT_Tref: float) -> float: fib Model Code 2020, eq. (14.6-101) Args: - T (float): Temperature in °C. - hT_Tref (float): Relative humidity of the - concrete pores at temperature T. + T (float): Temperature in degrees C. + hT_Tref (float): Relative humidity of the concrete pores at temperature + T. Returns: float: Drying effect parameter D_Ec. @@ -2564,7 +2592,12 @@ def D_Ec(T: float, hT_Tref: float) -> float: ValueError: If input values are not within valid ranges. """ if T < 20 or T > 80: - raise ValueError('Temperature T must be in the range 20°C ≤ T ≤ 80°C.') + raise ValueError( + ( + 'Temperature T must be in the range 20 degrees C ≤ T ≤ 80 ' + 'degrees C.' + ) + ) if hT_Tref < 0 or hT_Tref > 1: raise ValueError('Relative humidity h_T must be between 0 and 1.') @@ -2580,7 +2613,7 @@ def S_Ec(T: float, alpha_Ec: float) -> float: fib Model Code 2020, eq. (14.6-102) Args: - T (float): Temperature in °C. + T (float): Temperature in degrees C. alpha_Ec (float): Alpha coefficient for elastic modulus. Returns: @@ -2590,7 +2623,12 @@ def S_Ec(T: float, alpha_Ec: float) -> float: ValueError: If input values are not within valid ranges. """ if T < 20 or T > 80: - raise ValueError('Temperature T must be in the range 20°C ≤ T ≤ 80°C.') + raise ValueError( + ( + 'Temperature T must be in the range 20 degrees C ≤ T ≤ 80 ' + 'degrees C.' + ) + ) ratio = (T - 20) / 100 return alpha_Ec * ratio @@ -2617,120 +2655,146 @@ def alpha_Ec(hT_Tref: float) -> float: def beta_h_T(beta_h: float, T: float) -> float: - """Calculate the temperature-dependent coefficient - for creep time-development βh,T. + """Calculate the temperature-dependent coefficient for creep + time-development beta_h,T. fib Model Code 2020, eqs. (14.6-104) and (14.6-105) Args: - beta_h (float): Coefficient βh according to Eq. (14.6-66c). - T (float): Temperature in °C. + beta_h (float): Coefficient beta_h according to Eq. (14.6-66c). + T (float): Temperature in degrees C. Returns: - float: Temperature-dependent coefficient βh,T. + float: Temperature-dependent coefficient beta_h,T. Raises: ValueError: If input values are not within valid ranges. """ if beta_h <= 0: - raise ValueError('Coefficient βh must be positive.') + raise ValueError('Coefficient beta_h must be positive.') if T < 0 or T > 80: - raise ValueError('Temperature T must be in the range 0°C ≤ T ≤ 80°C.') + raise ValueError( + ( + 'Temperature T must be in the range 0 degrees C ≤ T ≤ 80 ' + 'degrees C.' + ) + ) beta_T = math.exp(1500 / ((273 + T) - 5.12)) return beta_h * beta_T def phi_bc_T(phi_bc: float, T: float) -> float: - """Calculate the temperature-dependent basic creep coefficient φbc,T. + """Calculate the temperature-dependent basic creep coefficient phi_bc,T. fib Model Code 2020, eq. (14.6-106) and (14.6-108) Args: - phi_bc (float): Basic creep coefficient φbc according to Eq. (14.6-59). - T (float): Temperature in °C. + phi_bc (float): Basic creep coefficient phi_bc according to Eq. + (14.6-59). + T (float): Temperature in degrees C. Returns: - float: Temperature-dependent basic creep coefficient φbc,T. + float: Temperature-dependent basic creep coefficient phi_bc,T. Raises: ValueError: If input values are not within valid ranges. """ if phi_bc <= 0: - raise ValueError('Basic creep coefficient φbc must be positive.') + raise ValueError('Basic creep coefficient phi_bc must be positive.') if T < 0 or T > 80: - raise ValueError('Temperature T must be in the range 0°C ≤ T ≤ 80°C.') + raise ValueError( + ( + 'Temperature T must be in the range 0 degrees C ≤ T ≤ 80 ' + 'degrees C.' + ) + ) phi_T = math.exp(0.015 * (T - 20)) return phi_bc * phi_T def phi_dc_T(phi_dc: float, T: float) -> float: - """Calculate the temperature-dependent drying creep coefficient φdc,T. + """Calculate the temperature-dependent drying creep coefficient phi_dc,T. fib Model Code 2020, eq. (14.6-107) and (14.6-108) Args: - phi_dc (float): Drying creep coefficient φdc - according to Eq. (14.6-62). - T (float): Temperature in °C. + phi_dc (float): Drying creep coefficient phi_dc according to Eq. + (14.6-62). + T (float): Temperature in degrees C. Returns: - float: Temperature-dependent drying creep coefficient φdc,T. + float: Temperature-dependent drying creep coefficient phi_dc,T. Raises: ValueError: If input values are not within valid ranges. """ if phi_dc <= 0: - raise ValueError('Drying creep coefficient φdc must be positive.') + raise ValueError('Drying creep coefficient phi_dc must be positive.') if T < 0 or T > 80: - raise ValueError('Temperature T must be in the range 0°C ≤ T ≤ 80°C.') + raise ValueError( + ( + 'Temperature T must be in the range 0 degrees C ≤ T ≤ 80 ' + 'degrees C.' + ) + ) phi_T = math.exp(0.015 * (T - 20)) return phi_dc * phi_T**1.2 def phi_t_t0_T(phi_t_t0: float, T: float) -> float: - """Creep coefficient ΔφT,trans for taking into consideration - increase in temperature while member is under load. + """Creep coefficient delta_phi_T,trans for taking into consideration + increase in temperature while member is under load. fib Model Code 2020, eq. (14.6-109) and (14.6-110) Args: - phi_t_t0 (float): base creep coefficient. - T (float): Temperature in °C. + phi_t_t0 (float): Base creep coefficient. + T (float): Temperature in degrees C. Returns: - float: Transient thermal creep coefficient ΔφT,trans. + float: Transient thermal creep coefficient delta_phi_T,trans. Raises: ValueError: If input values are not within valid ranges. """ if T < 0 or T > 80: - raise ValueError('Temperature T must be in the range 0°C ≤ T ≤ 80°C.') + raise ValueError( + ( + 'Temperature T must be in the range 0 degrees C ≤ T ≤ 80 ' + 'degrees C.' + ) + ) delta_phi_T_trans = 0.0004 * (T - 20) ** 2 return phi_t_t0 + delta_phi_T_trans def alpha_sT(T: float, h: float) -> float: - """Calculate the temperature-dependent coefficient - for drying shrinkage time development αsT(T). + """Calculate the temperature-dependent coefficient for drying shrinkage + time development alpha_sT. fib Model Code 2020, eq. (14.6-111) Args: - T (float): Temperature in °C. + T (float): Temperature in degrees C. h (float): Notional size parameter. Returns: - float: Temperature-dependent coefficient αsT(T). + float: Temperature-dependent coefficient alpha_sT(T). Raises: ValueError: If input values are not within valid ranges. """ if T < 0 or T > 80: - raise ValueError('Temperature T must be in the range 0°C ≤ T ≤ 80°C.') + raise ValueError( + ( + 'Temperature T must be in the range 0 degrees C ≤ T ≤ 80 ' + 'degrees C.' + ) + ) if h <= 0: raise ValueError('Notional size parameter h must be positive.') @@ -2738,38 +2802,38 @@ def alpha_sT(T: float, h: float) -> float: def beta_RH_T(beta_RH: float, beta_sT: float) -> float: - """Calculate the temperature-dependent coefficient - for drying shrinkage magnitude βRH,T. + """Calculate the temperature-dependent coefficient for drying shrinkage + magnitude beta_RH,T. fib Model Code 2020, eq. (14.6-112) Args: - beta_RH (float): Coefficient βRH. - beta_sT (float): Coefficient βsT. + beta_RH (float): Coefficient beta_RH. + beta_sT (float): Coefficient beta_sT. Returns: - float: Temperature-dependent coefficient βRH,T. + float: Temperature-dependent coefficient beta_RH,T. Raises: ValueError: If input values are not within valid ranges. """ if beta_RH <= 0 or beta_sT <= 0: - raise ValueError('Coefficients βRH and βsT must be positive.') + raise ValueError('Coefficients beta_RH and beta_sT must be positive.') return beta_RH * beta_sT def beta_sT(RH: float, T: float) -> float: - """Calculate the temperature-dependent coefficient replacing βRH. + """Calculate the temperature-dependent coefficient replacing beta_RH. fib Model Code 2020, eq. (14.6-113) Args: RH (float): Relative humidity of the ambient environment in %. - T (float): Temperature in °C. + T (float): Temperature in degrees C. Returns: - float: Temperature-dependent coeSfficient βRH,T. + float: Temperature-dependent coeSfficient beta_RH,T. Raises: ValueError: If input values are not within valid ranges. @@ -2777,13 +2841,18 @@ def beta_sT(RH: float, T: float) -> float: if RH < 0 or RH > 100: raise ValueError('Relative humidity RH must be between 0 and 100.') if T < 0 or T > 80: - raise ValueError('Temperature T must be in the range 0°C ≤ T ≤ 80°C.') + raise ValueError( + ( + 'Temperature T must be in the range 0 degrees C ≤ T ≤ 80 ' + 'degrees C.' + ) + ) return 1 + (4 / (103 - RH)) * ((T - 20) / 40) def beta_RH(RH: float, RH_T: float) -> float: - """Calculate the coefficient βRH,T for relative humidity. + """Calculate the coefficient beta_RH,T for relative humidity. fib Model Code 2020, eq. (14.6-114) @@ -2792,7 +2861,7 @@ def beta_RH(RH: float, RH_T: float) -> float: RH_T (float): Relative humidity in % obtained from eq. (14.6-115) Returns: - float: Coefficient βRH. + float: Coefficient beta_RH. Raises: ValueError: If input values are not within valid ranges. @@ -2813,19 +2882,24 @@ def RH_T(fcm: float, T: float) -> float: fib Model Code 2020, eq. (14.6-115), (14.6-116) and (14.6-117) Args: - fcm (float): mean compressive strength of concrete in MPa. - T (float): Temperature in °C. + fcm (float): Mean compressive strength of concrete in MPa. + T (float): Temperature in degrees C. Returns: float: Value of RH_T. Raise: - ValueError: if values are not within a valid range. + ValueError: If values are not within a valid range. """ if fcm < 0: raise ValueError('fcm cannot be negative.') if T < 0 or T > 80: - raise ValueError('Temperature T must be in the range 0°C ≤ T ≤ 80°C.') + raise ValueError( + ( + 'Temperature T must be in the range 0 degrees C ≤ T ≤ 80 ' + 'degrees C.' + ) + ) beta_s1 = min((35 / fcm) ** 0.1, 1) beta_s1_T = ((T - 20) / 25) ** 3 From 6f802b8dff1d9f205a1956abec9e2297d6f597e9 Mon Sep 17 00:00:00 2001 From: Morten Engen Date: Tue, 24 Sep 2024 21:54:37 +0200 Subject: [PATCH 27/27] Add functions to api docs --- docs/api/codes/index.md | 1 + docs/api/codes/mc2020/creep_shrinkage.md | 219 +++++++++++++++++++++ docs/api/codes/mc2020/index.md | 8 + docs/api/codes/mc2020/material_concrete.md | 197 ++++++++++++++++++ 4 files changed, 425 insertions(+) create mode 100644 docs/api/codes/mc2020/creep_shrinkage.md create mode 100644 docs/api/codes/mc2020/index.md create mode 100644 docs/api/codes/mc2020/material_concrete.md diff --git a/docs/api/codes/index.md b/docs/api/codes/index.md index 5256aa19..f6f04f14 100644 --- a/docs/api/codes/index.md +++ b/docs/api/codes/index.md @@ -8,4 +8,5 @@ General functions Eurocode 2 (2004) Eurocode 2 (2023) fib Model Code 2010 +fib Model Code 2020 ::: \ No newline at end of file diff --git a/docs/api/codes/mc2020/creep_shrinkage.md b/docs/api/codes/mc2020/creep_shrinkage.md new file mode 100644 index 00000000..479c80f7 --- /dev/null +++ b/docs/api/codes/mc2020/creep_shrinkage.md @@ -0,0 +1,219 @@ +# Creep and shrinkage + +## Creep + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_bc_fcm +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_bc_t_t0 +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_dc_fcm +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_dc_t0 +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_dc_t_t0 +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_RH_c +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_t0 +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.eps_c_sigma +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.eps_cc +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.gamma_t0 +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.phi_bc_T +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.phi_bc_t_t0 +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.phi_dc_T +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.phi_dc_t_t0 +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.phi_l_t_t0 +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.phi_sigma_t_t0 +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.phi_t_t0 +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.phi_t_t0_T +``` + +## Shrinkage + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.alpha_bs +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.alpha_ds_1 +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.alpha_ds_2 +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.alpha_Ec +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.alpha_fc +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.alpha_fct +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.alpha_sT +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.alpha_T +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_bs_t +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_ds_t_ts +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_h +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_h_T +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_RH_s +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_RH_T +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.eps_cbs_0 +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.eps_cbs_t +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.eps_cds_0_fcm +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.eps_cds_t_ts +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.eps_cs_t_ts +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.eps_lcs_t_ts +``` + +## Miscellaneous equations + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.RH_T +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.RH_eq +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.D_Ec +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.D_fc +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.D_fct +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.K_hT +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.S_Ec +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.S_fc +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.S_fct +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_RH +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_sT +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.eps_c_T +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.hT_Tref +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.t0_adj +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.t_T_maturity +``` \ No newline at end of file diff --git a/docs/api/codes/mc2020/index.md b/docs/api/codes/mc2020/index.md new file mode 100644 index 00000000..b7fae180 --- /dev/null +++ b/docs/api/codes/mc2020/index.md @@ -0,0 +1,8 @@ +(api-mc2020)= +# _fib_ Model Code 2020 + +:::{toctree} + +Material properties for concrete +Creep and shrinkage +::: \ No newline at end of file diff --git a/docs/api/codes/mc2020/material_concrete.md b/docs/api/codes/mc2020/material_concrete.md new file mode 100644 index 00000000..b5546e96 --- /dev/null +++ b/docs/api/codes/mc2020/material_concrete.md @@ -0,0 +1,197 @@ +# Material properties for concrete + +## Strength + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.fck +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.fck_cube +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.fcm +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.fcm_sus +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.fcm_T +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.fcm_t +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.fcm_hT_T +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.fctk_max +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.fctk_min +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.fctk_sus +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.fctm +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.fctm_from_flexural +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.fctm_from_splitting +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.fctm_hT_T +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.fctm_T +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.flcm +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.flcm_T +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.flcm_t +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.flctk_max +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.flctk_min +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.flctm +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_c_sus +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_cc +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_lcc +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.sC +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.slC +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.multiaxial_stress_equation +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.sigma_crack_friction +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.sigma_ct_cracked +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.sigma_ct_uncracked +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.tau_crack_friction +``` + +## Stiffness + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.E_ci +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.E_ci_red_el +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.E_ci_T +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.E_ci_t +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.E_cm_hT_T +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.Ec1 +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.El_ci +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.El_ci_T +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.beta_E +``` + +## Parameters of constitutive relations + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.eps_c1 +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.eps_c_lim +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.eps_lc1 +``` + +## Miscellaneous properties + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.nu_c +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.GF +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.GF_T +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.mc2020.GF_l +``` \ No newline at end of file