diff --git a/docs/api/codes/ec2_2004/material_concrete.md b/docs/api/codes/ec2_2004/material_concrete.md index 14c9a4ca..2f544ae1 100644 --- a/docs/api/codes/ec2_2004/material_concrete.md +++ b/docs/api/codes/ec2_2004/material_concrete.md @@ -59,3 +59,33 @@ The following functions are related to calculation of material properties of con ```{eval-rst} .. autofunction:: structuralcodes.codes.ec2_2004.n_parabolic_rectangular ``` + +## Time development of properties + +```{eval-rst} +.. autofunction:: structuralcodes.codes.ec2_2004.beta_cc +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.ec2_2004.beta_ct +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.ec2_2004.beta_E +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.ec2_2004.s_time_development +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.ec2_2004.fcm_time +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.ec2_2004.fctm_time +``` + +```{eval-rst} +.. autofunction:: structuralcodes.codes.ec2_2004.Ecm_time +``` diff --git a/structuralcodes/codes/ec2_2004/__init__.py b/structuralcodes/codes/ec2_2004/__init__.py index 9f767bd9..614a30b4 100644 --- a/structuralcodes/codes/ec2_2004/__init__.py +++ b/structuralcodes/codes/ec2_2004/__init__.py @@ -31,6 +31,10 @@ ) from ._concrete_material_properties import ( Ecm, + Ecm_time, + beta_cc, + beta_ct, + beta_E, eps_c1, eps_c2, eps_c3, @@ -39,11 +43,14 @@ eps_cu3, fcd, fcm, + fcm_time, fctk_5, fctk_95, fctm, + fctm_time, k_sargin, n_parabolic_rectangular, + s_time_development, ) from ._cracking_restraint_imposed_deformations import ( eps_sm_eps_cm_restraint_end, @@ -164,6 +171,13 @@ 'phi_RH', 't0_adj', 't_T', + 'beta_cc', + 'beta_ct', + 'beta_E', + 's_time_development', + 'fcm_time', + 'fctm_time', + 'Ecm_time', ] __title__: str = 'EUROCODE 2 1992-1-1:2004' diff --git a/structuralcodes/codes/ec2_2004/_concrete_material_properties.py b/structuralcodes/codes/ec2_2004/_concrete_material_properties.py index d2f5b70c..24664da1 100644 --- a/structuralcodes/codes/ec2_2004/_concrete_material_properties.py +++ b/structuralcodes/codes/ec2_2004/_concrete_material_properties.py @@ -1,9 +1,21 @@ """Concrete material properties according to Tab. 3.1.""" +from __future__ import annotations # To have clean hints of ArrayLike in docs + import math +import typing as t + +import numpy as np +from numpy.typing import ArrayLike from structuralcodes.codes import mc2010 +S_TIME_DEVELOPMENT_DICT = { + 'R': 0.20, + 'N': 0.25, + 'S': 0.38, +} # As defined in Eq. (3.2) + def fcm(fck: float, delta_f: float = 8) -> float: """The mean compressive strength of concrete. @@ -124,7 +136,7 @@ def k_sargin( ) -> float: """Computation of k parameter for Sargin constitutive Law. - EN 1992-1-1:2004, Eq. (3.14) + EN 1992-1-1:2004, Eq. (3.14). Args: Ecm (float): the mean elastic modulus of concrete in MPa. @@ -225,7 +237,7 @@ def eps_cu3(fck: float) -> float: def fcd(fck: float, alpha_cc: float, gamma_c: float) -> float: """The design compressive strength of concrete. - EN 1992-1-1:2004, Eq. (3.15) + EN 1992-1-1:2004, Eq. (3.15). Args: fck (float): The characteristic compressive strength in MPa. @@ -237,3 +249,158 @@ def fcd(fck: float, alpha_cc: float, gamma_c: float) -> float: float: The design compressive strength of concrete in MPa """ return abs(alpha_cc) * abs(fck) / abs(gamma_c) + + +def beta_cc(t: ArrayLike, s: float) -> ArrayLike: + """The time development function for compressive strength of concrete. + + EN 1992-1-1:2004, Eq. (3.2). + + Args: + t (ArrayLike): The time in days to evaluate the development function + for. + s (float): The scale factor in the exponent for the time development + function. s = 0.20 for class R, 0.25 for class N, and 0.38 for + class N. + + Returns: + ArrayLike: The value of the time development function. + """ + return np.exp(s * (1 - np.sqrt(28 / t))) + + +def beta_ct(t: ArrayLike, s: float) -> ArrayLike: + """The time development function for tensile strength of concrete. + + EN 1992-1-1:2004, part of Eq. (3.4). + + Args: + t (ArrayLike): The time in days to evaluate the development function + for. + s (float): The scale factor in the exponent for the time development + function. s = 0.20 for class R, 0.25 for class N, and 0.38 for + class N. + + Returns: + ArrayLike: The value of the time development function. + """ + if np.isscalar(t): + beta = beta_cc(t, s) + if t < 28: + return beta + return np.pow(beta, 2 / 3) + t = np.atleast_1d(t) + beta = beta_cc(t, s) + beta[t >= 28] = np.pow(beta[t >= 28], 2 / 3) + return beta + + +def beta_E(t: ArrayLike, s: float) -> ArrayLike: + """The time development function for Young's modulus of concrete. + + EN 1992-1-1:2004, part of Eq. (3.5). + + Args: + t (ArrayLike): The time in days to evaluate the development function + for. + s (float): The scale factor in the exponent for the time development + function. s = 0.20 for class R, 0.25 for class N, and 0.38 for + class N. + + Returns: + ArrayLike: The value of the time development function. + """ + return np.pow(beta_cc(t, s), 0.3) + + +def s_time_development(cement_class: t.Literal['S', 'N', 'R']) -> float: + """Return the scale factor for the exponent for the time development + function. + + EN 1992-1-1:2004, Eq. (3.2). + + Args: + cement_class (str): The cement class, either 'S', 'N' or 'R'. + + Returns: + float: The scale factor that depends on the cement type. + + Raises: + ValueError: If an invalid cement class is provided. + + """ + cement_class = ( + cement_class.upper().strip() if cement_class is not None else '' + ) + s = S_TIME_DEVELOPMENT_DICT.get(cement_class) + + if s is None: + raise ValueError( + ( + f'"{cement_class}" is not a valid cement class. ' + 'Use either S, N or R.' + ) + ) + return s + + +def fcm_time(fcm: float, beta_cc: ArrayLike) -> ArrayLike: + """Calculate the compressive strength as function of time. + + EN 1992-1-1:2004, Eq. (3.1). + + Args: + fcm (float): The reference value for the compressive strength. + beta_cc (ArrayLike): The value(s) of the time development function. + + Returns: + ArrayLike: The calculated value(s) of the compressive strength. + + Note: + The value of beta_cc should be calculated with the function beta_cc. + """ + return fcm * beta_cc + + +def fctm_time(fctm: float, beta_cc: ArrayLike, alpha: ArrayLike) -> ArrayLike: + """Calculate the tensile strength as function of time. + + EN 1992-1-1:2004, Eq. (3.4). + + Args: + fctm (float): The reference value for the tensile strength. + beta_cc (ArrayLike): The value(s) of the time development function. + alpha (ArrayLike): An exponent for the time development function. It + should be set to 1 for t < 28, and 2/3 else. + + Returns: + ArrayLike: The calculated value(s) of the tensile strength. + + Note: + The value of beta_cc should be calculated with the function beta_cc. + Alternatively, the time development function for the tensile strength + could be calculated directly with the function beta_ct. + """ + return np.pow(beta_cc, alpha) * fctm + + +def Ecm_time(fcm: float, fcm_time: ArrayLike, Ecm: float) -> ArrayLike: + """Calculate the Young's modulus as function of time. + + EN 1992-1-1:2004, Eq. (3.5). + + Args: + fcm (float): The reference value for the compressive strength. + fcm_time (float): The value(s) of the compressive strength at the + point(s) in time. + Ecm (float): The reference value for the Young's modulus. + + Returns: + ArrayLike: The calculated value(s) of the Young's modulus. + + Note: + The value of fcm_time should be calculated with the function fcm_time. + Alternatively, the time development function for the Young's modulus + could be calculated directly with the function beta_E. + """ + return np.pow(fcm_time / fcm, 0.3) * Ecm diff --git a/tests/test_ec2_2004/test_ec2_2004_property_development.py b/tests/test_ec2_2004/test_ec2_2004_property_development.py new file mode 100644 index 00000000..70bed0dd --- /dev/null +++ b/tests/test_ec2_2004/test_ec2_2004_property_development.py @@ -0,0 +1,146 @@ +"""Tests related to development of concrete property with time.""" + +import typing as t + +import numpy as np +import pytest + +from structuralcodes.codes.ec2_2004._concrete_material_properties import ( + Ecm, + Ecm_time, + beta_cc, + beta_ct, + beta_E, + fcm_time, + fctm, + fctm_time, + s_time_development, +) + + +@pytest.mark.parametrize('s', (0.2, 0.25, 0.38)) +def test_unit_scale_at_28_days(s: float): + """Test that all time development functions are unity at 28 days.""" + for fun in beta_cc, beta_ct, beta_E: + assert np.isclose(fun(28, s), 1.0) + + +@pytest.mark.parametrize('s', (0.2, 0.25, 0.38)) +def test_array_input(s: float): + """Test that all time development functions can be called with ArrayLike + input. + """ + time = np.linspace(1e-3, 1000, 50) + for fun in beta_cc, beta_ct, beta_E: + beta = fun(time, s) + assert not np.isscalar(beta) + assert len(beta) == len(time) + + +@pytest.mark.parametrize('s', (0.2, 0.25, 0.38)) +@pytest.mark.parametrize('t', (11, 28, 100)) +def test_relation_tension_compression(t: float, s: float): + """Test that the relation between development of compressive and tensile + strength is as intended. + """ + if t < 28: + assert np.isclose(beta_ct(t, s), beta_cc(t, s)) + else: + assert np.isclose(beta_ct(t, s), np.pow(beta_cc(t, s), 2 / 3)) + + +@pytest.mark.parametrize('s', (0.2, 0.25, 0.38)) +@pytest.mark.parametrize('t', (11, 28, 100)) +def test_relation_compression_stiffness(t: float, s: float): + """Test that the relation between development of compressive strength and + Young's modulus is as intended. + """ + assert np.isclose(beta_E(t, s), np.pow(beta_cc(t, s), 0.3)) + + +@pytest.mark.parametrize( + 't, expected', + ( + (9, 0.8261668305331398), + (27, 0.995422968073841), + (56, 1.075970779425791), + (365, 1.198124627036262), + ), +) +def test_compressive_strength_development(t, expected): + """Test time development function at specific stages.""" + assert np.isclose(beta_cc(t, 0.25), expected) + + +@pytest.mark.parametrize( + 'cement_class, expected', + ( + ('r', 0.20), + ('N', 0.25), + ('S', 0.38), + (None, None), + ), +) +def test_s_time_development( + cement_class: t.Optional[str], expected: t.Optional[float] +): + """Test the function for getting s based on the cement class.""" + if cement_class is not None: + assert np.isclose(s_time_development(cement_class), expected) + else: + with pytest.raises(ValueError): + s_time_development(cement_class) + + +@pytest.mark.parametrize('s', (0.2, 0.25, 0.38)) +@pytest.mark.parametrize('fcm', (20, 35, 65)) +def test_ecm_time(fcm: float, s: float): + """Test the function for development of Young's modulus with time.""" + # Arrange + t_max = 367 + E_reference = Ecm(fcm=fcm) + _beta_E = beta_E(t_max, s) + _beta_cc = beta_cc(t_max, s) + fcm_t = _beta_cc * fcm + Ecm_t_beta = E_reference * _beta_E + + # Act + Ecm_t = Ecm_time(fcm, fcm_t, E_reference) + + # Assert + assert np.isclose(Ecm_t, Ecm_t_beta) + + +@pytest.mark.parametrize('s', (0.2, 0.25, 0.38)) +@pytest.mark.parametrize('fcm', (20, 35, 65)) +def test_fcm_time(fcm: float, s: float): + """Test the function for development of compressive strength with time.""" + # Arrange + t_max = 367 + _beta_cc = beta_cc(t_max, s) + fcm_t_beta = _beta_cc * fcm + + # Act + fcm_t = fcm_time(fcm, _beta_cc) + + # Assert + assert np.isclose(fcm_t, fcm_t_beta) + + +@pytest.mark.parametrize('t', (26, 35, 367)) +@pytest.mark.parametrize('s', (0.2, 0.25, 0.38)) +@pytest.mark.parametrize('fcm', (20, 35, 65)) +def test_fctm_time(fcm: float, s: float, t: float): + """Test the function for development of tensile strength with time.""" + # Arrange + fctm_reference = fctm(fck=fcm - 8) + _beta_cc = beta_cc(t, s) + _beta_ct = beta_ct(t, s) + alpha = 1 if t < 28 else 2 / 3 + fctm_t_beta = _beta_ct * fctm_reference + + # Act + fctm_t = fctm_time(fctm_reference, _beta_cc, alpha) + + # Assert + assert np.isclose(fctm_t, fctm_t_beta)