Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
2e70179
Draft initial structure for the concrete class
mortenengen Dec 13, 2022
b8f67bb
Update docstring of base material
mortenengen Dec 13, 2022
c299dcc
minimum reinforcement areas functions
DanielGMorenaFhecor Dec 15, 2022
59a04f5
raise ValueError test functions for min area
DanielGMorenaFhecor Dec 27, 2022
b7167aa
crack_min_steel_without_direct_calculation
DanielGMorenaFhecor Jan 12, 2023
7189d31
Commit
DanielGMorenaFhecor Jan 12, 2023
4a0fcfb
crack without direct calculation tests
DanielGMorenaFhecor Jan 12, 2023
84c0140
adjusted bond strength
DanielGMorenaFhecor Jan 12, 2023
e0f1baa
hc_eff_concrete_tension formulation and testing
DanielGMorenaFhecor Jan 12, 2023
f2cbb49
requiremets.txt updated
DanielGMorenaFhecor Jan 12, 2023
333dcbe
rho_p_eff
DanielGMorenaFhecor Jan 12, 2023
59f1198
kt load duration
DanielGMorenaFhecor Jan 12, 2023
34d85d2
strain diff formula
DanielGMorenaFhecor Jan 12, 2023
a8ab129
chapter completed
DanielGMorenaFhecor Jan 13, 2023
ce4e432
imports and renamed functions
DanielGMorenaFhecor Jan 13, 2023
938c0f5
removed duplicate file
DanielGMorenaFhecor Jan 13, 2023
6ba6dc9
removed testing file
DanielGMorenaFhecor Jan 13, 2023
a9c9263
test renaming and docstring corrections
DanielGMorenaFhecor Jan 16, 2023
50c65b7
pull from upstream
DanielGMorenaFhecor Feb 8, 2023
ea3552b
Merge branch 'dev' of https://github.com/fib-international/structural…
DanielGMorenaFhecor Mar 9, 2023
4fd8b7e
230309 requested changes applied
DanielGMorenaFhecor Mar 9, 2023
1cffa61
small lint fixes
DanielGMorenaFhecor Mar 9, 2023
b483d40
vscode config updated
DanielGMorenaFhecor Mar 9, 2023
e9d953d
Merge branch 'dev' of https://github.com/fib-international/structural…
DanielGMorenaFhecor May 26, 2023
182e538
Merge branch 'dev' of https://github.com/fib-international/structural…
DanielGMorenaFhecor Dec 18, 2023
e509fb9
Merge branch 'dev' of https://github.com/fib-international/structural…
DanielGMorenaFhecor Mar 4, 2024
d57f945
Merge branch 'dev' of https://github.com/fib-international/structural…
DanielGMorenaFhecor Apr 4, 2024
bc049e4
Merge branch 'dev' of https://github.com/DanielGMorenaFhecor/structur…
DanielGMorenaFhecor Jul 30, 2024
39fe5bb
chapter 8.5 finished
DanielGMorenaFhecor Aug 5, 2024
79b0d83
code reviewed
DanielGMorenaFhecor Aug 6, 2024
a5fa0ab
eq 8.119
DanielGMorenaFhecor Aug 7, 2024
fd634a7
Merge branch 'dev' into ec2_2023-8.5
mortenengen Sep 24, 2024
2fba158
Fix docstrings
mortenengen Sep 24, 2024
0dc4d0c
Add new functions to API docs
mortenengen Sep 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/api/codes/ec2_2023/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ Material properties for reinforcement steel <material_reinforcement>
Crack width calculation <cracks>
Deflection calculation <deflections>
Creep and shrinkage <creep_shrinkage>
Strut-and-tie models and stress fields <strut_and_tie>
:::
32 changes: 32 additions & 0 deletions docs/api/codes/ec2_2023/strut_and_tie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Design with strut-and-tie models and stress fields

The following functions are related to design with strut-and-tie models and stress fields.

## Resistance calculation

```{eval-rst}
.. autofunction:: structuralcodes.codes.ec2_2023.FRd_tie
```

```{eval-rst}
.. autofunction:: structuralcodes.codes.ec2_2023.nu_refined
```

```{eval-rst}
.. autofunction:: structuralcodes.codes.ec2_2023.nu_strut
```

```{eval-rst}
.. autofunction:: structuralcodes.codes.ec2_2023.nu_strut_no_crack
```

## Response calculation

```{eval-rst}
.. autofunction:: structuralcodes.codes.ec2_2023.sigma_cd_strut
```

## Reinforcement calculation
```{eval-rst}
.. autofunction:: structuralcodes.codes.ec2_2023.Ftd_conc
```
14 changes: 14 additions & 0 deletions structuralcodes/codes/ec2_2023/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,22 @@
wk_cal,
wk_cal2,
)
from ._section_8_5_strut_and_ties import (
FRd_tie,
Ftd_conc,
nu_refined,
nu_strut,
nu_strut_no_crack,
sigma_cd_strut,
)

__all__ = [
'nu_strut',
'Ftd_conc',
'FRd_tie',
'nu_refined',
'nu_strut_no_crack',
'sigma_cd_strut',
'A_phi_correction_exp',
'alpha_c_th',
'alpha_s_th',
Expand Down
184 changes: 184 additions & 0 deletions structuralcodes/codes/ec2_2023/_section_8_5_strut_and_ties.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
"""Functions from Section 8.5 of EN 1992-1-1:2023."""

import math


def sigma_cd_strut(F_cd: float, b_c: float, t: float) -> float:
"""Calculate the compressive stress in a concrete strut or compression
field.

EN1992-1-1:2023 Eq. (8.113).

Args:
F_cd (float): Compressive force of the strut in kN.
b_c (float): Width of the strut at the considered location in mm.
t (float): Thickness of the strut in mm.

Returns:
float: Compressive stress sigma_cd in MPa.

Raises:
ValueError: If b_c or t are not within valid ranges.
"""
if b_c <= 0:
raise ValueError(f'b_c must be positive. Got {b_c}')
if t <= 0:
raise ValueError(f't must be positive. Got {t}')

# Convert F_cd from kN to N and calculate σ_cd in MPa
return abs(F_cd) * 1000 / (b_c * t)


def nu_strut(theta_cs: float) -> float:
"""Determine the strength reduction factor nu based on the smallest angle
theta_cs.

EN1992-1-1:2023 Eqs. (8.119)

Args:
theta_cs (float): Angle between the strut and the tie in degrees. Must
be between 0 and 90.
transverse_cracking (bool): Indicates if the region has transverse
cracking. Defaults to True.

Returns:
float: Strength reduction factor nu.

Raises:
ValueError: If theta_cs is not within the valid range.
"""
if not 0 <= theta_cs <= 90:
raise ValueError(
f'theta_cs must be between 0° and 90°. Got {theta_cs}'
)

theta_rad = math.radians(theta_cs)
epsilon = 1e-10 # To avoid num error in edge case when theta=0
cot_theta = 1 / math.tan(theta_rad + epsilon)
return 1 / (1.11 + 0.22 * (cot_theta**2))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure that using eq. 8.119 we have the same limits for theta as using eqs. 8.115-8.118?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a good question! Mmm... If i perform the calculations in a simple Excel Worksheet I am getting the following values:

Angle Rads Cotg Nu
20 0.349 2.747 0.36
30 0.524 1.732 0.56
40 0.698 1.192 0.70
60 1.047 0.577 0.85
90 1.571 0.000 0.90

Values from 8.199 are more or less similar to values from Eq. 8.115-8.118, but limits are not identical.

Would you suggets implementing both approaches in the codebase?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the comment. I agree with your observation, I see equations 8.115-8.118 as alternative formulation to 8.119.
We can either implement the two approaches or leave only the 8.119 approach. Either way, I am not sure that equation 8.119 is not valid for an angle lower than 20 degrees.



def nu_strut_no_crack() -> float:
"""Determine the strength reduction factor nu in areas without transverse
cracking.

EN1992-1-1:2023 Eqs. (8.120)

Returns:
float: Strength reduction factor nu.
"""
return 1.0


def nu_refined(eps_1: float) -> float:
"""Calculate a more refined value for the strength reduction factor nu for
compression fields in cracked zones based on principal tensile strain.

EN1992-1-1:2023 Eq. (8.121)

Args:
epsilon_1 (float): Maximum principal tensile strain (dimensionless).

Returns:
float: Refined strength reduction factor nu, capped at a maximum value
of 1.0.

Raises:
ValueError: If epsilon_1 is negative.
"""
if eps_1 < 0:
raise ValueError(f'epsilon_1 must be non-negative. Got {eps_1}')

nu = 1 / (1.0 + 110 * eps_1)
return min(nu, 1.0)


def FRd_tie(
As: float,
fyd: float,
Ap: float = 0.0,
fpd: float = 0.0,
sigma_pd: float = 0.0,
) -> float:
"""Calculate the resistance of a tie.

EN1992-1-1:2023 Eq. (8.122)

Args:
As (float): Cross-sectional area of non-prestressed reinforcement in
mm2.
fyd (float): Design yield strength of non-prestressed reinforcement in
MPa.
Ap (float, optional): Cross-sectional area of prestressed reinforcement
in mm2. Default is 0.0.
fpd (float, optional): Design strength of prestressed reinforcement in
MPa. Default is 0.0.
sigma_pd (float, optional): Stress in the prestressed reinforcement
considered as an external action in MPa. Default is 0.0.

Returns:
float: Resistance of the tie in kN.

Raises:
ValueError: If any of the input values are negative.
"""
# Input validation
if As < 0:
raise ValueError(f'As must not be negative. Got {As}')
if fyd < 0:
raise ValueError(f'fyd must not be negative. Got {fyd}')
if Ap < 0:
raise ValueError(f'Ap must not be negative. Got {Ap}')
if fpd < 0:
raise ValueError(f'fpd must not be negative. Got {fpd}')
if sigma_pd < 0:
raise ValueError(f'sigma_pd must not be negative. Got {sigma_pd}')

# Calculate tie resistance
FRd = As * fyd + Ap * (fpd - sigma_pd)
return FRd / 1000


def Ftd_conc(
Fd: float, a: float, b: float, H: float, near_edge: bool = False
) -> float:
"""Calculate the transverse reinforcement for concentrated forces
spreading into a member using the strut-and-ties model.

EN1992-1-1:2023 Eq. (8.123), Eq. (8.124), Eq. (8.125)

Args:
Fd (float): Design value of concentrated force in kN.
a (float): Width of the concentrated force application area in mm.
b (float): Width of the member in which force spreads in mm.
H (float): Height of the member section in mm.
near_edge (bool, optional): Indicates if the force is acting near an
edge. Default is False.

Returns:
float: The transverse reinforcement force F_td in kN.

Raises:
ValueError: If any of the input values are negative.
"""
# Input validation
if Fd < 0:
raise ValueError(f'Fd must not be negative. Got {Fd}')
if a < 0:
raise ValueError(f'a must not be negative. Got {a}')
if b < 0:
raise ValueError(f'b must not be negative. Got {b}')
if H < 0:
raise ValueError(f'H must not be negative. Got {H}')

# Calculate tan(theta_cf) based on position and geometry
tan_theta_cf = (1 - a / b) / 2 if b <= a + H / 2 else 0.5

# For forces near an edge, tan_theta_cf is assumed to be at least 1/4
if near_edge:
tan_theta_cf = max(tan_theta_cf, 1 / 4)
F_td = Fd * tan_theta_cf
else:
F_td = Fd / 2 * tan_theta_cf

return F_td
128 changes: 128 additions & 0 deletions tests/test_ec2_2023/test_ec2_2023_section_8_5_struct_and_ties.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"""Test for functions from Section 8.5 of EN 1992-1-1:2023."""

import pytest

from structuralcodes.codes.ec2_2023 import _section_8_5_strut_and_ties


@pytest.mark.parametrize(
'F_cd, b_c, t, expected',
[
(100, 200, 20, 25.0),
(150, 350, 40, 10.714),
(-150, 350, 40, 10.714),
],
)
def test_calculate_sigma_cd(F_cd, b_c, t, expected):
"""Test the calculation of compressive stress σ_cd."""
assert _section_8_5_strut_and_ties.sigma_cd_strut(
F_cd, b_c, t
) == pytest.approx(expected, rel=1e-2)


@pytest.mark.parametrize(
'F_cd, b_c, t',
[
(100, -200, 20),
(200, 350, -40),
],
)
def test_calculate_sigma_cd_exceptions(F_cd, b_c, t):
"""Test exceptions in compressive stress σ_cd calculation."""
with pytest.raises(ValueError):
_section_8_5_strut_and_ties.sigma_cd_strut(F_cd, b_c, t)


@pytest.mark.parametrize(
'input_value, expected',
[
(0, 0),
(25, 0.471),
(35, 0.642),
(50, 0.791),
(75, 0.888),
],
)
def test_calculate_nu(input_value, expected):
"""Test the calculation of strength reduction factor ν."""
assert _section_8_5_strut_and_ties.nu_strut(input_value) == pytest.approx(
expected, rel=1e-2
)


@pytest.mark.parametrize(
'input_value',
[(-10), (180)],
)
def test_calculate_nu_exceptions(input_value):
"""Test exceptions in strength reduction factor ν calculation."""
with pytest.raises(ValueError):
_section_8_5_strut_and_ties.nu_strut(input_value)


@pytest.mark.parametrize(
'input_value, expected',
[
(0.0, 1.0),
(0.001, 0.9009),
(0.01, 0.4762),
(0.1, 0.0833),
],
)
def test_calculate_nu_refined(input_value, expected):
"""Test the calculation of refined strength reduction factor ν."""
assert _section_8_5_strut_and_ties.nu_refined(
input_value
) == pytest.approx(expected, rel=1e-2)


@pytest.mark.parametrize(
'input_value',
[
(-0.01),
],
)
def test_calculate_nu_refined_exceptions(input_value):
"""Test exceptions in refined strength reduction factor ν calculation."""
with pytest.raises(ValueError):
_section_8_5_strut_and_ties.nu_refined(input_value)


@pytest.mark.parametrize(
'As, fyd, Ap, fpd, sigma_pd, expected',
[
(1000, 500, 500, 1600, 0, 1300),
(1000, 500, 500, 1600, 100, 1250),
(1500, 450, 300, 1450, 0, 1110),
(1200, 550, 400, 1500, 50, 1240),
(0, 500, 500, 1600, 0, 800),
(1000, 0, 500, 1600, 0, 800),
(1000, 500, 0, 1600, 0, 500),
(1000, 500, 500, 0, 0, 500),
],
)
def test_calculate_tie_resistance(As, fyd, Ap, fpd, sigma_pd, expected):
"""Test the calculate_tie_resistance function with various inputs."""
result = _section_8_5_strut_and_ties.FRd_tie(As, fyd, Ap, fpd, sigma_pd)
assert pytest.approx(result, 0.01) == expected


@pytest.mark.parametrize(
'Fd, a, b, H, near_edge, expected_F_td',
[
(1000, 200, 400, 600, False, 125),
(1000, 200, 450, 600, False, 138.888),
(1000, 200, 300, 600, True, 250),
(800, 150, 300, 500, False, 100),
(800, 150, 500, 300, True, 400),
(800, 100, 200, 400, False, 100),
],
)
def test_calculate_transverse_reinforcement(
Fd, a, b, H, near_edge, expected_F_td
):
"""Test the calculate_transverse_reinforcement
function with various inputs.
"""
F_td = _section_8_5_strut_and_ties.Ftd_conc(Fd, a, b, H, near_edge)
assert pytest.approx(F_td, 0.01) == expected_F_td