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 diff --git a/structuralcodes/codes/mc2020/__init__.py b/structuralcodes/codes/mc2020/__init__.py index b0f0a88a..3410d06d 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 = '2024' __materials__: t.Tuple[str] = ('concrete', 'reinforcement') diff --git a/structuralcodes/codes/mc2020/_concrete_material_properties.py b/structuralcodes/codes/mc2020/_concrete_material_properties.py new file mode 100644 index 00000000..d79d81f9 --- /dev/null +++ b/structuralcodes/codes/mc2020/_concrete_material_properties.py @@ -0,0 +1,2907 @@ +"""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 concrete + grade. + + 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. + """ + 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 + concrete grade. + + 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. + """ + 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 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. + + 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 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 + 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 nuc for concrete for design purposes. + + fib Model Code 2020 (14.6.1.4.3) + + Returns: + float: Poisson's ratio nuc 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 sigma_c for short-term uniaxial + compression of concrete. + + fib Model Code 2020, eq. (14.6-26) + + Args: + 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. + fcm (float): Mean compressive strength fcm in MPa. + k (float): Plasticity number k. + + Returns: + float: Compressive stress sigma_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 eps_c1 for a given concrete + grade. + + Args: + concrete_grade (str): Concrete grade (e.g., 'C20', 'C30'). + + Returns: + float: Strain at maximum compressive stress eps_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 eps_c,lim for a given concrete grade. + + Args: + concrete_grade (str): Concrete grade (e.g., 'C20', 'C30'). + + Returns: + float: Strain limit eps_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 eps_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 eps_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 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 eps_ct. + E_ci (float): Tangent modulus of elasticity Eci in MPa. + fctm (float): Mean tensile strength fctm in MPa. + + Returns: + float: Tensile stress sigma_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 sigma_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 sigma_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 tau 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 tau 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 sigma in an open crack subjected to + shear displacements. + + fib Model Code 2020, eq. (14.6-44) + + Args: + 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 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 delta, 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 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 beta_cc. + + 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 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 beta_cc. + + 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 + return 0.25 + + +def beta_lcc(t: float, t_ref: float, slC: float) -> float: + """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 in days (typically 28 days). + slC (float): Coefficient slc based on aggregate strength. + + Returns: + float: Strength development function beta_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 beta_E for + concrete. + + fib Model Code 2020, eq. (14.6-49) + + Args: + beta_cc (float) + + Returns: + float: Modulus of elasticity development coefficient beta_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 beta_E. + + 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 beta_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 beta_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 beta_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 beta_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 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. + + 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 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, 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 eps_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 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 phi(t, t0). + + Returns: + float: Stress-dependent strain eps_c_sigma(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 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. + + Returns: + float: Total creep coefficient phi(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 beta_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 phi_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 phi_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 beta_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 beta_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 beta_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 beta_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 beta_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 beta_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 gamma 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 gamma. + + 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 beta_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 beta_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 phi_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 phi_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 phi_l for lightweight aggregate + concrete. + + fib Model Code 2020, eq. (14.6-67) + + Args: + 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 phi_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 rho must be positive.') + if phi_t_t0 < 0: + raise ValueError('Creep coefficient phi(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 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 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. + + Returns: + 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_sigma ≤ 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 eps_cs(t, ts). + + fib Model Code 2020, eq. (14.6-70) + + Args: + 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 eps_cs(t, ts). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if eps_cbs_t < 0: + raise ValueError( + 'Basic shrinkage strain eps_cbs(t) must not be negative.' + ) + if eps_cds_t_ts < 0: + raise ValueError( + '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 eps_cbs(t). + + fib Model Code 2020, eq. (14.6-71) + + Args: + 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 eps_cbs(t). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if eps_cbs0 < 0: + raise ValueError( + 'Basic shrinkage strain coefficient eps_cbs0 must not be negative.' + ) + if beta_bs_t < 0: + raise ValueError( + 'Development function beta_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 eps_cds(t, ts). + + fib Model Code 2020, eq. (14.6-72) + + Args: + 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 eps_cds(t, ts). + + Raises: + ValueError: If input values are not within valid ranges. + """ + if eps_cds0 < 0: + raise ValueError( + ( + 'Drying shrinkage strain coefficient eps_cds0 must not be ' + 'negative.' + ) + ) + if beta_RH < 0 or beta_RH > 1: + raise ValueError( + 'Relative humidity factor beta_RH must be between 0 and 1.' + ) + if beta_ds_t_ts < 0: + raise ValueError( + '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 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. + + Returns: + float: Basic notional shrinkage coefficient eps_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 beta_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 beta_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 alpha_bs strength development class of + concrete. + + Args: + strength_class (str): The strength development class of concrete ('CS', + 'CN', 'CR'). + + Returns: + float: the value of alpha_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 alpha_ds1 strength development class of + concrete. + + Args: + strength_class (str): The strength development class of concrete ('CS', + 'CN', 'CR'). + + Returns: + float: the value of alpha_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 alpha_ds2 strength development class of + concrete. + + Args: + strength_class (str): The strength development class of concrete ('CS', + 'CN', 'CR'). + + Returns: + float: the value of alpha_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 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. + + Returns: + float: Notional drying shrinkage coefficient eps_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 beta_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 beta_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 + beta_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 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.' + ) + ) + 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 + eps_lcs(t, ts). + + fib Model Code 2020, eq. (14.6-79) + + Args: + 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 + eps_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 (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. + + 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 delta_ti must not be negative.') + if T < -273.15: + raise ValueError( + 'Temperature T(delta_ti) must be above absolute zero (-273C).' + ) + + 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 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. + + Returns: + float: Thermal strain eps_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 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. + + fib Model Code 2020, eq. (14.6-82) + + Args: + 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. + + 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 degrees C. + T (float): Temperature in degrees 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 degrees + 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 degrees 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 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. + + 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 degrees 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 degrees C. + T (float): Temperature in degrees 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 degrees C. + T (float): Temperature in degrees 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 degrees + 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 degrees 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 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.') + + 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 degrees 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 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 coefficient alpha_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 degrees C. + T (float): Temperature in degrees 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 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. + + fib Model Code 2020, eq. (14.6-99) + + Args: + 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. + + 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 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. + + fib Model Code 2020, eq. (14.6-100) + + Args: + 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. + + 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 degrees 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 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.') + + 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 degrees 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 degrees C ≤ T ≤ 80 ' + 'degrees 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 beta_h,T. + + fib Model Code 2020, eqs. (14.6-104) and (14.6-105) + + Args: + beta_h (float): Coefficient beta_h according to Eq. (14.6-66c). + T (float): Temperature in degrees C. + + Returns: + float: Temperature-dependent coefficient beta_h,T. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if beta_h <= 0: + raise ValueError('Coefficient beta_h must be positive.') + if T < 0 or T > 80: + 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 phi_bc,T. + + fib Model Code 2020, eq. (14.6-106) and (14.6-108) + + Args: + 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 phi_bc,T. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if phi_bc <= 0: + 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 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 phi_dc,T. + + fib Model Code 2020, eq. (14.6-107) and (14.6-108) + + Args: + 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 phi_dc,T. + + Raises: + ValueError: If input values are not within valid ranges. + """ + if phi_dc <= 0: + 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 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 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 degrees C. + + Returns: + 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 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 alpha_sT. + + fib Model Code 2020, eq. (14.6-111) + + Args: + T (float): Temperature in degrees C. + h (float): Notional size parameter. + + Returns: + 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 degrees C ≤ T ≤ 80 ' + 'degrees 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 beta_RH,T. + + fib Model Code 2020, eq. (14.6-112) + + Args: + beta_RH (float): Coefficient beta_RH. + beta_sT (float): Coefficient beta_sT. + + Returns: + 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 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 beta_RH. + + fib Model Code 2020, eq. (14.6-113) + + Args: + RH (float): Relative humidity of the ambient environment in %. + T (float): Temperature in degrees C. + + Returns: + float: Temperature-dependent coeSfficient beta_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 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 beta_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 beta_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 degrees 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 degrees C ≤ T ≤ 80 ' + 'degrees 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)