From d76de804d960c5a519fef73c6322f488ad5b8188 Mon Sep 17 00:00:00 2001 From: "johan.tuls" Date: Wed, 5 Mar 2025 14:12:02 +0100 Subject: [PATCH 1/9] 233 Formula 5.8 from NEN-EN 1993-1-1 --- .github/prompts/comparison_formula.prompt.md | 10 +-- .gitignore | 3 +- .../chapter_5_structural_analysis/__init__.py | 1 + .../formula_5_8.py | 79 +++++++++++++++++++ .../chapter_5_structural_analysis/__init__.py | 1 + .../test_formula_5_8.py | 64 +++++++++++++++ 6 files changed, 152 insertions(+), 6 deletions(-) create mode 100644 blueprints/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/__init__.py create mode 100644 blueprints/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/formula_5_8.py create mode 100644 tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/__init__.py create mode 100644 tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/test_formula_5_8.py diff --git a/.github/prompts/comparison_formula.prompt.md b/.github/prompts/comparison_formula.prompt.md index 46d57b481..0843c763c 100644 --- a/.github/prompts/comparison_formula.prompt.md +++ b/.github/prompts/comparison_formula.prompt.md @@ -4,10 +4,10 @@ - Make sure the script returns a bool. - Keep all formatting and naming conventions such as they are presented in the template. - If variable descriptions are given or found, copy precisely and fully from input or Eurocode. -- Variablenames are always lowercase. +- Variable names are always lowercase in line with PEP8. - In the LaTeX formula, edit the return symbol such that it is the left hand side of the equation - Edit the _equation variable such that it represents the right hand side of the equation -- LaTeX variables should be rounded to 3 decimals. +- LaTeX variables should be rounded to 3 decimals as default, but could be overwritten at usage level. - Import the necessary typehinting with type alias units found in type_alias.py and remove the unused imported type aliases. Forces in N, (Bending) moments in Nmm, distances in mm, areas in mm^2, Stress in MPa, angles in DEG, no unit is DIMENSIONLESS. When dealing with angles, use np.deg2rad. ## Template for service @@ -58,14 +58,14 @@ class Form5Dot38aCheckRelativeSlendernessRatio(Formula): return (lambda_y / lambda_z <= 2) and (lambda_z / lambda_y <= 2) - def latex(self) -> LatexFormula: + def latex(self, n: int = 3) -> LatexFormula: """Returns LatexFormula object for formula 5.38a.""" _equation: str = r"\left( \frac{\lambda_{y}}{\lambda_{z}} \leq 2 \text{ and } \frac{\lambda_{z}}{\lambda_{y}} \leq 2 \right)" _numeric_equation: str = latex_replace_symbols( _equation, { - "lambda_y": f"{self.lambda_y:.3f}", - "lambda_z": f"{self.lambda_z:.3f}", + "lambda_y": f"{self.lambda_y:.{n}f}", + "lambda_z": f"{self.lambda_z:.{n}f}", }, False, ) diff --git a/.gitignore b/.gitignore index e85e0527b..d34d68216 100644 --- a/.gitignore +++ b/.gitignore @@ -165,4 +165,5 @@ cython_debug/ .vscode/ # ENV file -.ENV \ No newline at end of file +.ENV +/local/ diff --git a/blueprints/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/__init__.py b/blueprints/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/__init__.py new file mode 100644 index 000000000..1436a5910 --- /dev/null +++ b/blueprints/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/__init__.py @@ -0,0 +1 @@ +"""Module containing all formulas from 1993-1-1+C2+A1:2016: Chapter 5 - Structural analysis.""" diff --git a/blueprints/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/formula_5_8.py b/blueprints/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/formula_5_8.py new file mode 100644 index 000000000..b41492bff --- /dev/null +++ b/blueprints/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/formula_5_8.py @@ -0,0 +1,79 @@ +"""Formula 5.8 from NEN-EN 1993-1-1+C2+A1:2016: Chapter 5 - Structural Analysis.""" + +import numpy as np + +from blueprints.codes.eurocode.nen_en_1993_1_1_c2_a1_2016 import NEN_EN_1993_1_1_C2_A1_2016 +from blueprints.codes.formula import Formula +from blueprints.codes.latex_formula import LatexFormula, latex_replace_symbols +from blueprints.type_alias import DIMENSIONLESS, MM2, MPA, N +from blueprints.validations import raise_if_less_or_equal_to_zero + + +class Form5Dot8CheckSlenderness(Formula): + r"""Class representing formula 5.8 for check of slenderness.""" + + label = "5.8" + source_document = NEN_EN_1993_1_1_C2_A1_2016 + + def __init__( + self, + lambda_bar: DIMENSIONLESS, + a: MM2, + f_y: MPA, + n_ed: N, + ) -> None: + r"""Check the slenderness ratio. + + NEN-EN 1993-1-1+C2+A1:2016 art.5.3.2(6) - Formula (5.8) + + Parameters + ---------- + lambda_bar : DIMENSIONLESS + [$\lambda_{bar}$] Non-dimensional slenderness [-]. + a : MM2 + [$A$] Cross-sectional area [$mm^2$]. + f_y : MPA + [$f_y$] Yield strength [$MPa$]. + n_ed : N + [$N_{Ed}$] Design value of the compression force [$N$]. + """ + super().__init__() + self.lambda_bar = lambda_bar + self.a = a + self.f_y = f_y + self.n_ed = n_ed + + @staticmethod + def _evaluate( + lambda_bar: DIMENSIONLESS, + a: MM2, + f_y: MPA, + n_ed: N, + ) -> bool: + """Evaluates the formula, for more information see the __init__ method.""" + raise_if_less_or_equal_to_zero(lambda_bar=lambda_bar, A=a, f_y=f_y, N_Ed=n_ed) + + return lambda_bar > 0.5 * np.sqrt(a * f_y / n_ed) + + def latex(self) -> LatexFormula: + """Returns LatexFormula object for formula 5.8.""" + n = 2 + _equation: str = r"\left( \lambda_{bar} > 0.5 \sqrt{\frac{A \cdot f_{y}}{N_{Ed}}} \right)" + _numeric_equation: str = latex_replace_symbols( + _equation, + { + r"\lambda_{bar}": f"{self.lambda_bar:.{n}f}", + "A": f"{self.a:.{n}f}", + "f_{y}": f"{self.f_y:.{n}f}", + "N_{Ed}": f"{self.n_ed:.{n}f}", + }, + False, + ) + return LatexFormula( + return_symbol=r"CHECK", + result="OK" if self.__bool__() else "\\text{Not OK}", + equation=_equation, + numeric_equation=_numeric_equation, + comparison_operator_label="\\to", + unit="", + ) diff --git a/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/__init__.py b/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/__init__.py new file mode 100644 index 000000000..7625734e2 --- /dev/null +++ b/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/__init__.py @@ -0,0 +1 @@ +"""Tests for the module `nen_en_1993_1_1_c2_a1_2016.chapter_5_structural_analysis`.""" diff --git a/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/test_formula_5_8.py b/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/test_formula_5_8.py new file mode 100644 index 000000000..5e8bcd571 --- /dev/null +++ b/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/test_formula_5_8.py @@ -0,0 +1,64 @@ +"""Testing formula 5.8 of NEN-EN 1993-1-1+C2+A1:2016.""" + +import pytest + +from blueprints.codes.eurocode.nen_en_1993_1_1_c2_a1_2016.chapter_5_structural_analysis.formula_5_8 import Form5Dot8CheckSlenderness +from blueprints.validations import LessOrEqualToZeroError + + +class TestForm5Dot8CheckSlenderness: + """Validation for formula 5.8 from NEN-EN 1993-1-1+C2+A1:2016.""" + + def test_evaluation(self) -> None: + """Tests the evaluation of the result.""" + # Example values + lambda_bar = 1.0 + a = 1000.0 + f_y = 355.0 + n_ed = 100000.0 + + # Object to test + formula = Form5Dot8CheckSlenderness(lambda_bar=lambda_bar, a=a, f_y=f_y, n_ed=n_ed) + + # Expected result, manually calculated + expected_result = True + + assert formula(lambda_bar, a, f_y, n_ed) == expected_result + + @pytest.mark.parametrize( + ("lambda_bar", "a", "f_y", "n_ed"), + [ + (-1.0, 1000.0, 355.0, 100000.0), # lambda_bar is negative + (1.0, -1000.0, 355.0, 100000.0), # a is negative + (1.0, 1000.0, -355.0, 100000.0), # f_y is negative + (1.0, 1000.0, 355.0, -100000.0), # n_ed is negative + (0.0, 1000.0, 355.0, 100000.0), # lambda_bar is zero + (1.0, 0.0, 355.0, 100000.0), # a is zero + (1.0, 1000.0, 0.0, 100000.0), # f_y is zero + (1.0, 1000.0, 355.0, 0.0), # n_ed is zero + ], + ) + def test_raise_error_when_invalid_values_are_given(self, lambda_bar: float, a: float, f_y: float, n_ed: float) -> None: + """Test invalid values.""" + with pytest.raises(LessOrEqualToZeroError): + Form5Dot8CheckSlenderness(lambda_bar, a, f_y, n_ed) + + def test_latex(self) -> None: + """Test the latex representation of the formula.""" + # Example values + lambda_bar = 1.0 + a = 1000.0 + f_y = 355.0 + n_ed = 100000.0 + + # Object to test + formula = Form5Dot8CheckSlenderness(lambda_bar=lambda_bar, a=a, f_y=f_y, n_ed=n_ed) + latex = formula.latex() + + expected_equation = r"\left( \lambda_{bar} > 0.5 \sqrt{\frac{A \cdot f_{y}}{N_{Ed}}} \right)" + expected_numeric_equation = r"\left( 1.00 > 0.5 \sqrt{\frac{1000.00 \cdot 355.00}{100000.00}} \right)" + expected_result = "OK" + + assert latex.equation == expected_equation + assert latex.numeric_equation == expected_numeric_equation + assert latex.result == expected_result From e088af9189b473440d659e627f1b85a1635611e6 Mon Sep 17 00:00:00 2001 From: "johan.tuls" Date: Wed, 5 Mar 2025 14:15:02 +0100 Subject: [PATCH 2/9] 233 Fix tests --- .../chapter_5_structural_analysis/test_formula_5_8.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/test_formula_5_8.py b/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/test_formula_5_8.py index 5e8bcd571..cf5964cfb 100644 --- a/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/test_formula_5_8.py +++ b/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/test_formula_5_8.py @@ -23,7 +23,7 @@ def test_evaluation(self) -> None: # Expected result, manually calculated expected_result = True - assert formula(lambda_bar, a, f_y, n_ed) == expected_result + assert formula == expected_result @pytest.mark.parametrize( ("lambda_bar", "a", "f_y", "n_ed"), From b33a432126769d4d50f3ce5305e0095efc9a497c Mon Sep 17 00:00:00 2001 From: "johan.tuls" Date: Wed, 5 Mar 2025 14:16:15 +0100 Subject: [PATCH 3/9] 233 Update pre-commit --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5f33cefa4..743ca201a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.4 + rev: v0.9.9 hooks: # Run the linter. - id: ruff @@ -26,7 +26,7 @@ repos: - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.14.1 + rev: v1.15.0 hooks: - id: mypy language_version: python3.12 From bb3f411f441443806e1210792d1e4db7074ef5cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Wed, 11 Jun 2025 14:25:03 +0200 Subject: [PATCH 4/9] Fix .gitignore to retain .ENV entry and remove local directory reference --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index d34d68216..e85e0527b 100644 --- a/.gitignore +++ b/.gitignore @@ -165,5 +165,4 @@ cython_debug/ .vscode/ # ENV file -.ENV -/local/ +.ENV \ No newline at end of file From 123126adc01ef8eba1bfe820c81967aca63ab0d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Wed, 11 Jun 2025 14:28:40 +0200 Subject: [PATCH 5/9] Rename references from NEN-EN 1993-1-1+C2+A1:2016 to EN 1993-1-1:2005 in formulas and tests --- .../chapter_5_structural_analysis/__init__.py | 1 + .../chapter_5_structural_analysis/formula_5_8.py | 8 ++++---- .../chapter_6_ultimate_limit_state/formula_6_3.py | 4 ++-- .../chapter_6_ultimate_limit_state/formula_6_4.py | 4 ++-- .../chapter_6_ultimate_limit_state/formula_6_6.py | 4 ++-- .../chapter_6_ultimate_limit_state/formula_6_7.py | 4 ++-- .../chapter_6_ultimate_limit_state/formula_6_8.py | 4 ++-- .../chapter_5_structural_analysis/__init__.py | 1 - .../chapter_5_structural_analysis/test_formula_5_8.py | 4 ++-- 9 files changed, 17 insertions(+), 17 deletions(-) create mode 100644 blueprints/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/__init__.py rename blueprints/codes/eurocode/{nen_en_1993_1_1_c2_a1_2016 => en_1993_1_1_2005}/chapter_5_structural_analysis/formula_5_8.py (88%) delete mode 100644 blueprints/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/__init__.py diff --git a/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/__init__.py b/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/__init__.py new file mode 100644 index 000000000..16b6f2b1b --- /dev/null +++ b/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/__init__.py @@ -0,0 +1 @@ +"""Module containing all formulas from EN 1993-1-1:2005: Chapter 5 - Structural analysis.""" diff --git a/blueprints/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/formula_5_8.py b/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/formula_5_8.py similarity index 88% rename from blueprints/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/formula_5_8.py rename to blueprints/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/formula_5_8.py index b41492bff..50fd074a3 100644 --- a/blueprints/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/formula_5_8.py +++ b/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/formula_5_8.py @@ -1,8 +1,8 @@ -"""Formula 5.8 from NEN-EN 1993-1-1+C2+A1:2016: Chapter 5 - Structural Analysis.""" +"""Formula 5.8 from EN 1993-1-1:2005: Chapter 5 - Structural Analysis.""" import numpy as np -from blueprints.codes.eurocode.nen_en_1993_1_1_c2_a1_2016 import NEN_EN_1993_1_1_C2_A1_2016 +from blueprints.codes.eurocode.en_1993_1_1_2005 import EN_1993_1_1_2005 from blueprints.codes.formula import Formula from blueprints.codes.latex_formula import LatexFormula, latex_replace_symbols from blueprints.type_alias import DIMENSIONLESS, MM2, MPA, N @@ -13,7 +13,7 @@ class Form5Dot8CheckSlenderness(Formula): r"""Class representing formula 5.8 for check of slenderness.""" label = "5.8" - source_document = NEN_EN_1993_1_1_C2_A1_2016 + source_document = EN_1993_1_1_2005 def __init__( self, @@ -24,7 +24,7 @@ def __init__( ) -> None: r"""Check the slenderness ratio. - NEN-EN 1993-1-1+C2+A1:2016 art.5.3.2(6) - Formula (5.8) + EN 1993-1-1:2005 art.5.3.2(6) - Formula (5.8) Parameters ---------- diff --git a/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_3.py b/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_3.py index 91e7c5ee0..e30e4a17e 100644 --- a/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_3.py +++ b/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_3.py @@ -1,4 +1,4 @@ -"""Formula 6.3 from NEN-EN 1993-1-1+C2+A1:2016: Chapter 6 - Ultimate limit state.""" +"""Formula 6.3 from EN 1993-1-1:2005: Chapter 6 - Ultimate limit state.""" from collections.abc import Sequence @@ -25,7 +25,7 @@ def __init__( ) -> None: """[$A_{deduction}$] Calculation of the area deduction for staggered fastener holes [$mm^2$]. - NEN-EN 1993-1-1+C2+A1:2016 art.6.2.2.2 (4) b) - Formula (6.3) + EN 1993-1-1:2005 art.6.2.2.2 (4) b) - Formula (6.3) section (4) a) should be handled separately. Parameters diff --git a/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_4.py b/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_4.py index bcbeccc54..f052cb5aa 100644 --- a/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_4.py +++ b/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_4.py @@ -1,4 +1,4 @@ -"""Formula 6.4 from NEN-EN 1993-1-1+C2+A1:2016: Chapter 6 - Ultimate Limit State.""" +"""Formula 6.4 from EN 1993-1-1:2005: Chapter 6 - Ultimate Limit State.""" from blueprints.codes.eurocode.en_1993_1_1_2005 import EN_1993_1_1_2005 from blueprints.codes.formula import Formula @@ -20,7 +20,7 @@ def __init__( ) -> None: r"""[$\Delta M_{Ed}$] Calculation of the additional moment [$Nmm$]. - NEN-EN 1993-1-1+C2+A1:2016 art.6.2.2.5(4) - Formula (6.4) + EN 1993-1-1:2005 art.6.2.2.5(4) - Formula (6.4) Where a class 4 cross section is subjected to an axial compression force, the method given in EN 1993-1-5 should be used to determine the possible shift [$e_{N}$] of the centroid of the effective area [$A_{eff}$] relative to the centre of gravity of the gross cross section and the resulting additional moment according to this formula. diff --git a/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_6.py b/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_6.py index db7468ae9..810b7b4e6 100644 --- a/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_6.py +++ b/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_6.py @@ -1,4 +1,4 @@ -"""Formula 6.6 from NEN-EN 1993-1-1+C2+A1:2016: Chapter 6 - Ultimate Limit State.""" +"""Formula 6.6 from EN 1993-1-1:2005: Chapter 6 - Ultimate Limit State.""" from blueprints.codes.eurocode.en_1993_1_1_2005 import EN_1993_1_1_2005 from blueprints.codes.formula import Formula @@ -21,7 +21,7 @@ def __init__( ) -> None: r"""[$N_{pl,Rd}$] Calculation of the design plastic resistance of the gross cross-section [$N$]. - NEN-EN 1993-1-1+C2+A1:2016 art.6.2.3(2) - Formula (6.6) + EN 1993-1-1:2005 art.6.2.3(2) - Formula (6.6) Parameters ---------- diff --git a/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_7.py b/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_7.py index f17d0beae..77f47740a 100644 --- a/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_7.py +++ b/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_7.py @@ -1,4 +1,4 @@ -"""Formula 6.7 from NEN-EN 1993-1-1+C2+A1:2016: Chapter 6 - Ultimate Limit State.""" +"""Formula 6.7 from EN 1993-1-1:2005: Chapter 6 - Ultimate Limit State.""" from blueprints.codes.eurocode.en_1993_1_1_2005 import EN_1993_1_1_2005 from blueprints.codes.formula import Formula @@ -21,7 +21,7 @@ def __init__( ) -> None: r"""[$N_{u,Rd}$] Calculation of the design tension resistance [$N$]. - NEN-EN 1993-1-1+C2+A1:2016 art.6.2.3(2) - Formula (6.7) + EN 1993-1-1:2005 art.6.2.3(2) - Formula (6.7) Parameters ---------- diff --git a/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_8.py b/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_8.py index 4ac07e6f6..d6f1ba7c2 100644 --- a/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_8.py +++ b/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_8.py @@ -1,4 +1,4 @@ -"""Formula 6.8 from NEN-EN 1993-1-1+C2+A1:2016: Chapter 6 - Ultimate Limit State.""" +"""Formula 6.8 from EN 1993-1-1:2005: Chapter 6 - Ultimate Limit State.""" from blueprints.codes.eurocode.en_1993_1_1_2005 import EN_1993_1_1_2005 from blueprints.codes.formula import Formula @@ -21,7 +21,7 @@ def __init__( ) -> None: r"""[$N_{net,Rd}$] Calculation of the design tension resistance [$N$]. - NEN-EN 1993-1-1+C2+A1:2016 art.6.2.3(4) - Formula (6.8) + EN 1993-1-1:2005 art.6.2.3(4) - Formula (6.8) Parameters ---------- diff --git a/blueprints/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/__init__.py b/blueprints/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/__init__.py deleted file mode 100644 index 1436a5910..000000000 --- a/blueprints/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Module containing all formulas from 1993-1-1+C2+A1:2016: Chapter 5 - Structural analysis.""" diff --git a/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/test_formula_5_8.py b/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/test_formula_5_8.py index cf5964cfb..438105307 100644 --- a/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/test_formula_5_8.py +++ b/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/test_formula_5_8.py @@ -1,4 +1,4 @@ -"""Testing formula 5.8 of NEN-EN 1993-1-1+C2+A1:2016.""" +"""Testing formula 5.8 of EN 1993-1-1:2005.""" import pytest @@ -7,7 +7,7 @@ class TestForm5Dot8CheckSlenderness: - """Validation for formula 5.8 from NEN-EN 1993-1-1+C2+A1:2016.""" + """Validation for formula 5.8 from EN 1993-1-1:2005.""" def test_evaluation(self) -> None: """Tests the evaluation of the result.""" From 401f0722fc185aec26daf4ec2aa6535d0e083e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Wed, 11 Jun 2025 14:30:28 +0200 Subject: [PATCH 6/9] Add tests for EN 1993-1-1:2005 and update import paths in test_formula_5_8 --- .../en_1993_1_1_2005/chapter_5_structural_analysis/__init__.py | 1 + .../chapter_5_structural_analysis/test_formula_5_8.py | 2 +- .../chapter_5_structural_analysis/__init__.py | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 tests/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/__init__.py rename tests/codes/eurocode/{nen_en_1993_1_1_c2_a1_2016 => en_1993_1_1_2005}/chapter_5_structural_analysis/test_formula_5_8.py (94%) delete mode 100644 tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/__init__.py diff --git a/tests/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/__init__.py b/tests/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/__init__.py new file mode 100644 index 000000000..c062c2a77 --- /dev/null +++ b/tests/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/__init__.py @@ -0,0 +1 @@ +"""Tests for the module EN 1993-1-1:2005 .chapter_5_structural_analysis`.""" diff --git a/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/test_formula_5_8.py b/tests/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/test_formula_5_8.py similarity index 94% rename from tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/test_formula_5_8.py rename to tests/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/test_formula_5_8.py index 438105307..b6d171611 100644 --- a/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/test_formula_5_8.py +++ b/tests/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/test_formula_5_8.py @@ -2,7 +2,7 @@ import pytest -from blueprints.codes.eurocode.nen_en_1993_1_1_c2_a1_2016.chapter_5_structural_analysis.formula_5_8 import Form5Dot8CheckSlenderness +from blueprints.codes.eurocode.en_1993_1_1_2005.chapter_5_structural_analysis.formula_5_8 import Form5Dot8CheckSlenderness from blueprints.validations import LessOrEqualToZeroError diff --git a/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/__init__.py b/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/__init__.py deleted file mode 100644 index 7625734e2..000000000 --- a/tests/codes/eurocode/nen_en_1993_1_1_c2_a1_2016/chapter_5_structural_analysis/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for the module `nen_en_1993_1_1_c2_a1_2016.chapter_5_structural_analysis`.""" From d023ef9de5629a5ad2bb9a91a6fc122c5abdf553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Wed, 11 Jun 2025 18:00:06 +0200 Subject: [PATCH 7/9] Refactor Form5Dot8CheckSlenderness to use ComparisonFormula and add error handling for negative values --- .../formula_5_8.py | 46 ++++++++++--- .../test_formula_5_8.py | 67 ++++++++++++++----- 2 files changed, 86 insertions(+), 27 deletions(-) diff --git a/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/formula_5_8.py b/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/formula_5_8.py index 50fd074a3..3442e3363 100644 --- a/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/formula_5_8.py +++ b/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/formula_5_8.py @@ -3,13 +3,13 @@ import numpy as np from blueprints.codes.eurocode.en_1993_1_1_2005 import EN_1993_1_1_2005 -from blueprints.codes.formula import Formula +from blueprints.codes.formula import ComparisonFormula from blueprints.codes.latex_formula import LatexFormula, latex_replace_symbols from blueprints.type_alias import DIMENSIONLESS, MM2, MPA, N -from blueprints.validations import raise_if_less_or_equal_to_zero +from blueprints.validations import raise_if_less_or_equal_to_zero, raise_if_negative -class Form5Dot8CheckSlenderness(Formula): +class Form5Dot8CheckSlenderness(ComparisonFormula): r"""Class representing formula 5.8 for check of slenderness.""" label = "5.8" @@ -29,7 +29,8 @@ def __init__( Parameters ---------- lambda_bar : DIMENSIONLESS - [$\lambda_{bar}$] Non-dimensional slenderness [-]. + [$\lambda_{bar}$] In-plane non-dimensional slenderness calculated for the member + considered as hinged at its ends [-]. a : MM2 [$A$] Cross-sectional area [$mm^2$]. f_y : MPA @@ -43,6 +44,24 @@ def __init__( self.f_y = f_y self.n_ed = n_ed + @staticmethod + def _evaluate_lhs(lambda_bar: DIMENSIONLESS, *_args, **_kwargs) -> float: + """Evaluates the left-hand side of the comparison. See __init__ for details.""" + raise_if_negative(lambda_bar=lambda_bar) + return lambda_bar + + @staticmethod + def _evaluate_rhs(a: MM2, f_y: MPA, n_ed: N, *_args, **_kwargs) -> float: + """Evaluates the right-hand side of the comparison. See __init__ for details.""" + raise_if_less_or_equal_to_zero(n_ed=n_ed) + raise_if_negative(a=a, f_y=f_y) + return 0.5 * np.sqrt(a * f_y / n_ed) + + @property + def unity_check(self) -> float: + """Returns the unity check value.""" + return self.lhs / self.rhs + @staticmethod def _evaluate( lambda_bar: DIMENSIONLESS, @@ -51,23 +70,32 @@ def _evaluate( n_ed: N, ) -> bool: """Evaluates the formula, for more information see the __init__ method.""" - raise_if_less_or_equal_to_zero(lambda_bar=lambda_bar, A=a, f_y=f_y, N_Ed=n_ed) + lhs = Form5Dot8CheckSlenderness._evaluate_lhs(lambda_bar=lambda_bar) + rhs = Form5Dot8CheckSlenderness._evaluate_rhs(a=a, f_y=f_y, n_ed=n_ed) + return lhs > rhs - return lambda_bar > 0.5 * np.sqrt(a * f_y / n_ed) + def __bool__(self) -> bool: + """Allow truth-checking of the check object itself.""" + return self._evaluate( + lambda_bar=self.lambda_bar, + a=self.a, + f_y=self.f_y, + n_ed=self.n_ed, + ) def latex(self) -> LatexFormula: """Returns LatexFormula object for formula 5.8.""" n = 2 - _equation: str = r"\left( \lambda_{bar} > 0.5 \sqrt{\frac{A \cdot f_{y}}{N_{Ed}}} \right)" + _equation: str = r"\left( \overline{\lambda} > 0.5 \sqrt{\frac{A \cdot f_{y}}{N_{Ed}}} \right)" _numeric_equation: str = latex_replace_symbols( _equation, { - r"\lambda_{bar}": f"{self.lambda_bar:.{n}f}", + r"\lambda": f"{self.lambda_bar:.{n}f}", "A": f"{self.a:.{n}f}", "f_{y}": f"{self.f_y:.{n}f}", "N_{Ed}": f"{self.n_ed:.{n}f}", }, - False, + unique_symbol_check=False, ) return LatexFormula( return_symbol=r"CHECK", diff --git a/tests/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/test_formula_5_8.py b/tests/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/test_formula_5_8.py index b6d171611..3b9152d92 100644 --- a/tests/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/test_formula_5_8.py +++ b/tests/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/test_formula_5_8.py @@ -3,7 +3,7 @@ import pytest from blueprints.codes.eurocode.en_1993_1_1_2005.chapter_5_structural_analysis.formula_5_8 import Form5Dot8CheckSlenderness -from blueprints.validations import LessOrEqualToZeroError +from blueprints.validations import LessOrEqualToZeroError, NegativeValueError class TestForm5Dot8CheckSlenderness: @@ -28,22 +28,50 @@ def test_evaluation(self) -> None: @pytest.mark.parametrize( ("lambda_bar", "a", "f_y", "n_ed"), [ - (-1.0, 1000.0, 355.0, 100000.0), # lambda_bar is negative - (1.0, -1000.0, 355.0, 100000.0), # a is negative - (1.0, 1000.0, -355.0, 100000.0), # f_y is negative (1.0, 1000.0, 355.0, -100000.0), # n_ed is negative - (0.0, 1000.0, 355.0, 100000.0), # lambda_bar is zero - (1.0, 0.0, 355.0, 100000.0), # a is zero - (1.0, 1000.0, 0.0, 100000.0), # f_y is zero (1.0, 1000.0, 355.0, 0.0), # n_ed is zero ], ) - def test_raise_error_when_invalid_values_are_given(self, lambda_bar: float, a: float, f_y: float, n_ed: float) -> None: + def test_raise_error_when_invalid_values_less_or_equal_to_zero(self, lambda_bar: float, a: float, f_y: float, n_ed: float) -> None: """Test invalid values.""" with pytest.raises(LessOrEqualToZeroError): - Form5Dot8CheckSlenderness(lambda_bar, a, f_y, n_ed) + Form5Dot8CheckSlenderness( + lambda_bar=lambda_bar, + a=a, + f_y=f_y, + n_ed=n_ed, + ) + + @pytest.mark.parametrize( + ("lambda_bar", "a", "f_y", "n_ed"), + [ + (-1.0, 1000.0, 355.0, 100000.0), # lambda_bar is negative + (1.0, -1000.0, 355.0, 100000.0), # a is negative + (1.0, 1000.0, -355.0, 100000.0), # f_y is negative + ], + ) + def test_raise_error_when_negative(self, lambda_bar: float, a: float, f_y: float, n_ed: float) -> None: + """Test invalid values.""" + with pytest.raises(NegativeValueError): + Form5Dot8CheckSlenderness( + lambda_bar=lambda_bar, + a=a, + f_y=f_y, + n_ed=n_ed, + ) - def test_latex(self) -> None: + @pytest.mark.parametrize( + ("representation", "expected"), + [ + ( + "complete", + r"CHECK \to \left( \overline{\lambda} > 0.5 \sqrt{\frac{A \cdot f_{y}}{N_{Ed}}} " + r"\right) \to \left( \overline{1.00} > 0.5 \sqrt{\frac{1000.00 \cdot 355.00}{100000.00}} \right) \to OK", + ), + ("short", r"CHECK \to OK"), + ], + ) + def test_latex(self, representation: str, expected: str) -> None: """Test the latex representation of the formula.""" # Example values lambda_bar = 1.0 @@ -52,13 +80,16 @@ def test_latex(self) -> None: n_ed = 100000.0 # Object to test - formula = Form5Dot8CheckSlenderness(lambda_bar=lambda_bar, a=a, f_y=f_y, n_ed=n_ed) - latex = formula.latex() + latex = Form5Dot8CheckSlenderness( + lambda_bar=lambda_bar, + a=a, + f_y=f_y, + n_ed=n_ed, + ).latex() - expected_equation = r"\left( \lambda_{bar} > 0.5 \sqrt{\frac{A \cdot f_{y}}{N_{Ed}}} \right)" - expected_numeric_equation = r"\left( 1.00 > 0.5 \sqrt{\frac{1000.00 \cdot 355.00}{100000.00}} \right)" - expected_result = "OK" + actual = { + "complete": latex.complete, + "short": latex.short, + } - assert latex.equation == expected_equation - assert latex.numeric_equation == expected_numeric_equation - assert latex.result == expected_result + assert expected == actual[representation], f"{representation} representation failed." From c71e95719a7691eb93d8577cc7cfa6fc2a5d75d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Wed, 11 Jun 2025 18:01:22 +0200 Subject: [PATCH 8/9] Update parameter description for lambda_bar in formula_5_8.py to use correct notation --- .../chapter_5_structural_analysis/formula_5_8.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/formula_5_8.py b/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/formula_5_8.py index 3442e3363..1f12b247c 100644 --- a/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/formula_5_8.py +++ b/blueprints/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/formula_5_8.py @@ -29,7 +29,7 @@ def __init__( Parameters ---------- lambda_bar : DIMENSIONLESS - [$\lambda_{bar}$] In-plane non-dimensional slenderness calculated for the member + [$\overline{\lambda}$] In-plane non-dimensional slenderness calculated for the member considered as hinged at its ends [-]. a : MM2 [$A$] Cross-sectional area [$mm^2$]. From ef7083354d4804f1f512b92ffda2b3fea0c4a85f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Wed, 11 Jun 2025 18:11:06 +0200 Subject: [PATCH 9/9] Add unity check assertion to formula 5.8 tests --- .../chapter_5_structural_analysis/test_formula_5_8.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/test_formula_5_8.py b/tests/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/test_formula_5_8.py index 3b9152d92..c7fa47162 100644 --- a/tests/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/test_formula_5_8.py +++ b/tests/codes/eurocode/en_1993_1_1_2005/chapter_5_structural_analysis/test_formula_5_8.py @@ -1,5 +1,6 @@ """Testing formula 5.8 of EN 1993-1-1:2005.""" +import numpy as np import pytest from blueprints.codes.eurocode.en_1993_1_1_2005.chapter_5_structural_analysis.formula_5_8 import Form5Dot8CheckSlenderness @@ -22,8 +23,10 @@ def test_evaluation(self) -> None: # Expected result, manually calculated expected_result = True + expected_unity_check = lambda_bar / (0.5 * np.sqrt(a * f_y / n_ed)) assert formula == expected_result + assert formula.unity_check == expected_unity_check @pytest.mark.parametrize( ("lambda_bar", "a", "f_y", "n_ed"),