diff --git a/Pipfile b/Pipfile
new file mode 100644
index 000000000..6323cbf56
--- /dev/null
+++ b/Pipfile
@@ -0,0 +1,26 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+matplotlib = ">=3.7.2"
+plotly = ">=5.15.0"
+pandas = ">=2.1.0"
+shapely = ">=2.0.1"
+numpy = ">=1.25.2"
+mypy = "==1.13.0"
+ruff = "==0.8.0"
+pre-commit = "==4.0.1"
+pytest = "==8.3.3"
+pytest-cov = "==6.0.0"
+pytest-describe = "==2.2.0"
+pytest-pspec = "==0.0.4"
+pytest-raises = "==0.11"
+types-shapely = "==2.0.0.20241112"
+pandas-stubs = "==2.2.3.241009"
+
+[dev-packages]
+
+[requires]
+python_version = "3.10"
diff --git a/README.md b/README.md
index 0d932c947..ed5800702 100644
--- a/README.md
+++ b/README.md
@@ -161,6 +161,16 @@ figures for streamlined access and reference.
NEN 9997-1+C2:2017 |
diff --git a/blueprints/codes/eurocode/pren_1995_1_1_2023/__init__.py b/blueprints/codes/eurocode/pren_1995_1_1_2023/__init__.py
new file mode 100644
index 000000000..0b0f33959
--- /dev/null
+++ b/blueprints/codes/eurocode/pren_1995_1_1_2023/__init__.py
@@ -0,0 +1,3 @@
+"""prEN 1995-1-1:2023."""
+
+PREN_1995_1_1_2023 = "prEN 1995-1-1:2023"
diff --git a/blueprints/codes/eurocode/pren_1995_1_1_2023/chapter_11_connections/__init__.py b/blueprints/codes/eurocode/pren_1995_1_1_2023/chapter_11_connections/__init__.py
new file mode 100644
index 000000000..694a86135
--- /dev/null
+++ b/blueprints/codes/eurocode/pren_1995_1_1_2023/chapter_11_connections/__init__.py
@@ -0,0 +1 @@
+"""Module containing all formulas from prEN 1995-1-1-2023 Chapter 11 - Connections."""
diff --git a/blueprints/codes/eurocode/pren_1995_1_1_2023/chapter_11_connections/formula_11_1.py b/blueprints/codes/eurocode/pren_1995_1_1_2023/chapter_11_connections/formula_11_1.py
new file mode 100644
index 000000000..b79440ca2
--- /dev/null
+++ b/blueprints/codes/eurocode/pren_1995_1_1_2023/chapter_11_connections/formula_11_1.py
@@ -0,0 +1,74 @@
+"""Formula 11.1 from prEN 1995-1-1: Chapter 11 - Connections."""
+
+from blueprints.codes.eurocode.pren_1995_1_1_2023 import PREN_1995_1_1_2023
+from blueprints.codes.formula import Formula
+from blueprints.codes.latex_formula import LatexFormula, latex_replace_symbols
+from blueprints.type_alias import DIMENSIONLESS, KN
+from blueprints.validations import raise_if_less_or_equal_to_zero
+
+
+class Form11Dot1AxialTensileResistance(Formula):
+ """Class representing formula 11.1 for axial tensile resistance of a fastener."""
+
+ label = "11.1"
+ source_document = PREN_1995_1_1_2023
+
+ def __init__(
+ self,
+ k_mod: DIMENSIONLESS,
+ gamma_r: DIMENSIONLESS,
+ f_pull_k: KN,
+ f_w_k: KN,
+ ) -> None:
+ r"""[:math:`F_{ax,t,d}`] Calculate the design axial tensile resistance of an axially-loaded fastener.
+
+ Parameters
+ ----------
+ k_mod : DIMENSIONLESS.
+ [:math:`k_{mod}`] Modification factor accounting for the effect of the duration of load and moisture
+ gamma_r : DIMENSIONLESS
+ [:math:`\\gamma_R`] Partial factor for resistance.
+ f_pull_k : KN
+ [:math:`F_{pull,k}`] Characteristic head pull-through resistance in [:math:`kN`].
+ f_w_k : KN
+ [:math:`F_{w,k}`] Characteristic withdrawal resistance in [:math:`kN`].
+ """
+ super().__init__()
+ self.k_mod: float = k_mod
+ self.gamma_r: float = gamma_r
+ self.f_pull_k: float = f_pull_k
+ self.f_w_k: float = f_w_k
+
+ @staticmethod
+ def _evaluate(
+ k_mod: DIMENSIONLESS,
+ gamma_r: DIMENSIONLESS,
+ f_pull_k: KN,
+ f_w_k: KN,
+ ) -> KN:
+ """Evaluates the formula for the design axial tensile resistance."""
+ raise_if_less_or_equal_to_zero(k_mod=k_mod, gamma_r=gamma_r, f_pull_k=f_pull_k, f_w_k=f_w_k)
+ return k_mod / gamma_r * max(f_pull_k, f_w_k)
+
+ def latex(self) -> LatexFormula:
+ """Returns LatexFormula object for formula 11.1."""
+ _equation: str = r"\frac{k_{mod}}{\gamma_R} \cdot \max \left \{ \begin{array}{c}F_{pull,k} \\ F_{w,k} \end{array}}"
+ _numeric_equation: str = latex_replace_symbols(
+ _equation,
+ {
+ r"k_{mod}": f"{self.k_mod:.2f}",
+ r"\gamma_R": f"{self.gamma_r:.2f}",
+ r"F_{pull,k}": f"{self.f_pull_k:.2f}",
+ r"F_{w,k}": f"{self.f_w_k:.2f}",
+ },
+ True,
+ )
+
+ return LatexFormula(
+ return_symbol=r"F_{ax,t,d}",
+ result=str(self),
+ equation=_equation,
+ numeric_equation=_numeric_equation,
+ comparison_operator_label="=",
+ unit="kN",
+ )
diff --git a/docs/source/codes/eurocode/pren_1995_1_1_2023/figures.md b/docs/source/codes/eurocode/pren_1995_1_1_2023/figures.md
new file mode 100644
index 000000000..257ab7ce2
--- /dev/null
+++ b/docs/source/codes/eurocode/pren_1995_1_1_2023/figures.md
@@ -0,0 +1,10 @@
+**prEN 1995-1-1 - September 2023
+Eurocode 5: Design of Timber structures
+Part 1-1: General rules and rules for building**
+
+The table presents a list of figures from the Eurocode 5 standards for timber structures, tracking their implementation status (:x: or :heavy_check_mark:) and any pertinent remarks. The 'Object Name' column references the corresponding Python entities inside of Blueprints.
+
+Total of 0 figures present.
+
+| Figure number | Done | Remarks | Object name |
+|:--------------|:----:|:--------|:------------|
diff --git a/docs/source/codes/eurocode/pren_1995_1_1_2023/formulas.md b/docs/source/codes/eurocode/pren_1995_1_1_2023/formulas.md
new file mode 100644
index 000000000..798c25ab6
--- /dev/null
+++ b/docs/source/codes/eurocode/pren_1995_1_1_2023/formulas.md
@@ -0,0 +1,11 @@
+**prEN 1995-1-1 - September 2023
+Eurocode 5: Design of Timber structures
+Part 1-1: General rules and rules for building**
+
+The table presents a list of formulas from the Eurocode 5 standards for timber structures, tracking their implementation status (:x: or :heavy_check_mark:) and any pertinent remarks. The 'Object Name' column references the corresponding Python entities inside of Blueprints.
+
+Total of 1 formulas present.
+
+| Formula number | Done | Remarks | Object name |
+|:---------------|:----:|:--------|:------------|
+| 11.1 | :heavy_check_mark: | | Form11Dot1AxialTensileResistance |
diff --git a/docs/source/codes/eurocode/pren_1995_1_1_2023/tables.md b/docs/source/codes/eurocode/pren_1995_1_1_2023/tables.md
new file mode 100644
index 000000000..6c2e794d8
--- /dev/null
+++ b/docs/source/codes/eurocode/pren_1995_1_1_2023/tables.md
@@ -0,0 +1,10 @@
+**prEN 1995-1-1 - September 2023
+Eurocode 5: Design of Timber structures
+Part 1-1: General rules and rules for building**
+
+The table presents a list of tables from the Eurocode 5 standards for timber structures, tracking their implementation status (:x: or :heavy_check_mark:) and any pertinent remarks. The 'Object Name' column references the corresponding Python entities inside of Blueprints.
+
+Total of 0 tables present.
+
+| Table number | Done | Remarks | Object name |
+|:-------------|:----:|:--------|:------------|
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 33d933335..45ebc3368 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -46,6 +46,7 @@ Our mission is to reduce the cost and time associated with civil engineering cal
* NEN-EN 1993-1-1+C2+A1:2016 🚧
* NEN-EN 1993-1-9+C2:2012 🚧
* NEN-EN 1993-5:2008 🚧
+ * prEN-EN 1995-1-1:2023 🚧
* NEN 9997-1+C2:2017 🚧
* Reinforced Concrete Section 🚧
diff --git a/tests/codes/eurocode/pren_1995_1_1_2023/__init__.py b/tests/codes/eurocode/pren_1995_1_1_2023/__init__.py
new file mode 100644
index 000000000..7a3e0b3e5
--- /dev/null
+++ b/tests/codes/eurocode/pren_1995_1_1_2023/__init__.py
@@ -0,0 +1 @@
+"""Module containing all tests for prEN 1995-1-1-2023."""
diff --git a/tests/codes/eurocode/pren_1995_1_1_2023/chapter_11_connections/__init__.py b/tests/codes/eurocode/pren_1995_1_1_2023/chapter_11_connections/__init__.py
new file mode 100644
index 000000000..083488646
--- /dev/null
+++ b/tests/codes/eurocode/pren_1995_1_1_2023/chapter_11_connections/__init__.py
@@ -0,0 +1 @@
+"""Module containing all tests from prEN 1995-1-1-2023 Chapter 11 - Connections."""
diff --git a/tests/codes/eurocode/pren_1995_1_1_2023/chapter_11_connections/test_formula_11_1.py b/tests/codes/eurocode/pren_1995_1_1_2023/chapter_11_connections/test_formula_11_1.py
new file mode 100644
index 000000000..ae7241c08
--- /dev/null
+++ b/tests/codes/eurocode/pren_1995_1_1_2023/chapter_11_connections/test_formula_11_1.py
@@ -0,0 +1,64 @@
+"""Tests for formula 11.1 from prEN 1995-1-1: Chapter 11 - Connections."""
+
+import pytest
+
+from blueprints.codes.eurocode.pren_1995_1_1_2023.chapter_11_connections.formula_11_1 import Form11Dot1AxialTensileResistance
+from blueprints.validations import LessOrEqualToZeroError
+
+
+class TestForm11Dot1AxialTensileResistance:
+ """Validation for formula 11.1 from prEN 1995-1-1."""
+
+ @pytest.mark.parametrize(
+ ("k_mod", "gamma_r", "f_pull_k", "f_w_k", "expected"),
+ [
+ (0.55, 1.3, 15, 1, 6.35),
+ (1.10, 1.3, 15, 1, 12.69),
+ (0.55, 1.3, 1, 15, 6.35),
+ (1.10, 1.3, 1, 15, 12.69),
+ ],
+ )
+ def test_evaluation(self, k_mod: float, gamma_r: float, f_pull_k: float, f_w_k: float, expected: float) -> None:
+ """Test the evaluation of the result."""
+ form = Form11Dot1AxialTensileResistance(k_mod=k_mod, gamma_r=gamma_r, f_pull_k=f_pull_k, f_w_k=f_w_k)
+ assert form == pytest.approx(expected, rel=1e-3)
+
+ @pytest.mark.parametrize(
+ ("k_mod", "gamma_r", "f_pull_k", "f_w_k"),
+ [
+ (-1.0, 1.2, 10.0, 10.0),
+ (1.0, -1.2, 10.0, 10.0),
+ (1.0, 1.2, -10.0, 10.0),
+ (1.0, 1.2, 10.0, -10.0),
+ ],
+ )
+ def test_raise_error_when_less_or_equal_to_zero(self, k_mod: float, gamma_r: float, f_pull_k: float, f_w_k: float) -> None:
+ """Test values that are less than or equal to zero raise error."""
+ with pytest.raises(LessOrEqualToZeroError):
+ Form11Dot1AxialTensileResistance(k_mod=k_mod, gamma_r=gamma_r, f_pull_k=f_pull_k, f_w_k=f_w_k)
+
+ @pytest.mark.parametrize(
+ ("k_mod", "gamma_r", "f_pull_k", "f_w_k", "representation", "expected"),
+ [
+ (
+ 0.9,
+ 1.2,
+ 15,
+ 12,
+ "complete",
+ r"F_{ax,t,d} = \frac{k_{mod}}{\gamma_R} \cdot \max \left \{ \begin{array}{c}F_{pull,k} \\ F_{w,k} \end{array}}"
+ r" = \frac{0.90}{1.20} \cdot \max \left \{ \begin{array}{c}15.00 \\ 12.00 \end{array}} = 11.25 kN",
+ ),
+ (0.9, 1.2, 15, 12, "short", r"F_{ax,t,d} = 11.25 kN"),
+ ],
+ )
+ def test_latex(self, k_mod: float, gamma_r: float, f_pull_k: float, f_w_k: float, representation: str, expected: str) -> None:
+ """Test the LaTeX representation of the formula."""
+ form_latex = Form11Dot1AxialTensileResistance(k_mod=k_mod, gamma_r=gamma_r, f_pull_k=f_pull_k, f_w_k=f_w_k).latex()
+
+ actual = {
+ "complete": form_latex.complete,
+ "short": form_latex.short,
+ }
+
+ assert actual[representation] == expected, f"{representation} representation failed."
|