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/cur/__init__.py b/blueprints/codes/cur/__init__.py new file mode 100644 index 000000000..449163d2e --- /dev/null +++ b/blueprints/codes/cur/__init__.py @@ -0,0 +1 @@ +"""Cur guidelines.""" diff --git a/blueprints/codes/cur/cur_228/__init__.py b/blueprints/codes/cur/cur_228/__init__.py new file mode 100644 index 000000000..b930b991d --- /dev/null +++ b/blueprints/codes/cur/cur_228/__init__.py @@ -0,0 +1,7 @@ +"""CUR-228 package.""" + +from blueprints.type_alias import M + +CUR_228 = "CUR 228" + +R_0: M = 0.3 diff --git a/blueprints/codes/cur/cur_228/formula_2_21.py b/blueprints/codes/cur/cur_228/formula_2_21.py new file mode 100644 index 000000000..47ffe5b99 --- /dev/null +++ b/blueprints/codes/cur/cur_228/formula_2_21.py @@ -0,0 +1,79 @@ +"""Contains formula 2.21 from CUR 228.""" + +from blueprints.codes.cur.cur_228 import CUR_228, R_0 +from blueprints.codes.formula import Formula +from blueprints.codes.latex_formula import LatexFormula +from blueprints.type_alias import DIMENSIONLESS, KN_M3, KPA, M +from blueprints.validations import raise_if_less_or_equal_to_zero + + +class Form2Dot21ModulusHorizontalSubgrade(Formula): + """Representation of equation 2.21 CUR 228.""" + + source_document = CUR_228 + label = "2.21" + n_decimals: int = 2 + + def __init__(self, r: M, e_p: KPA, alpha: DIMENSIONLESS) -> None: + """Calculates the modulus of horizontal subgrade reaction (k_h) using Menard stiffness for r >= 0.3 m. + + Parameters + ---------- + r: M + The radius of a foundation pile [m]: + r >= 0.3 m + e_p: KPA + Elastic modulus of Ménard [kPa]: + e_p ≈ beta * q_c + beta: DIMENSIONLESS + Dependent on soil type [-]: + q_c: KPA + Cone resistance [kPa] + alpha: DIMENSIONLESS + Factor dependent on soil type [-]: + """ + super().__init__() + self.r = float(r) + self.e_p = float(e_p) + self.alpha = float(alpha) + + @staticmethod + def _evaluate(r: M, e_p: KPA, alpha: DIMENSIONLESS) -> KN_M3: + """Evaluates the formula, for more information see the __init__ method.""" + r_0 = R_0 + raise_if_less_or_equal_to_zero(r=r, e_p=e_p, alpha=alpha) + + if r >= r_0: + return 3 * e_p / (1.3 * r_0 * (2.65 * r / r_0) ** alpha + alpha * r) + msg = "Radius is smaller than 0.3m, use: Eq2Dot21ModulusHorizontalSubgrade" + raise ValueError(msg) + + def latex(self) -> LatexFormula: + """Latex representation of the full equation including result. + + Parameters + ---------- + n_decimals: int + Number of decimals to round the result to + + Returns + ------- + LatexFormula + Latex representation of the equation + + """ + n = self.n_decimals + + return LatexFormula( + return_symbol="k_{h}", + equation=r"\frac{1}{3 \cdot E_{p}} \cdot " + r"\left[1.3 \cdot R_{0} " + r"\left( 2.65 \frac{R}{R_0}\right)^\alpha" + r" + \alpha \cdot R \right]", + numeric_equation=rf"\frac{{1}}{{3 \cdot {self.e_p:.{n}}}} \cdot" + rf"\left[1.3 \cdot {R_0:.{n}} " + rf"\left( 2.65 \cdot \frac{{{self.r:.{n}}}}{{{R_0:.{n}}}}\right)^{{{self.alpha:.{n}f}}}" + rf"+ {self.alpha:.{n}} \cdot {self.r:.{n}}\right]", + result=f"{self:.{n}f}", + unit="kN/m^3", + ) diff --git a/blueprints/codes/cur/cur_228/formula_2_22.py b/blueprints/codes/cur/cur_228/formula_2_22.py new file mode 100644 index 000000000..3c8e064cd --- /dev/null +++ b/blueprints/codes/cur/cur_228/formula_2_22.py @@ -0,0 +1,66 @@ +"""Contains formula 2.22 from CUR 228.""" + +from blueprints.codes.cur.cur_228 import CUR_228, R_0 +from blueprints.codes.formula import Formula +from blueprints.codes.latex_formula import LatexFormula +from blueprints.type_alias import DIMENSIONLESS, KN_M3, KPA, M +from blueprints.validations import raise_if_less_or_equal_to_zero + + +class Form2Dot22ModulusHorizontalSubgrade(Formula): + """Representation of equation 2.22 CUR 228.""" + + source_document = CUR_228 + label = "2.22" + n_decimals: int = 2 + + def __init__(self, r: M, e_p: KPA, alpha: DIMENSIONLESS) -> None: + """Calculates the modulus of horizontal subgrade reaction (k_h) using Menard stiffness for r < 0.3 m. + + Parameters + ---------- + r: M + The radius of a foundation pile [m]: + r >= 0.3 m + e_p: KPA + Elastic modulus of Ménard [kPa]: + e_p ≈ beta * q_c + beta: DIMENSIONLESS + Dependent on soil type [-]: + q_c: KPA + Cone resistance [kPa] + alpha: DIMENSIONLESS + Factor dependent on soil type [-]: + """ + super().__init__() + self.r = r + self.e_p = e_p + self.alpha = alpha + + @staticmethod + def _evaluate(r: M, e_p: KPA, alpha: DIMENSIONLESS) -> KN_M3: + """Return the Menard stiffness k_h when r < 0.3 m [kN/m3].""" + raise_if_less_or_equal_to_zero(e_p=e_p, r=r, alpha=alpha) + if r < R_0: + return e_p / 2 / r / ((4 * 2.65**alpha + 3 * alpha) / 18) + msg = "Radius is equal to- or larger than 0.3m, use: Eq2Dot21ModulusHorizontalSubgrade" + raise ValueError(msg) + + def latex(self) -> LatexFormula: + """Latex representation of the full equation including result. + + Returns + ------- + LatexFormula + Latex representation of the equation + + """ + n = self.n_decimals + return LatexFormula( + return_symbol=r"k_{h}", + result=f"{self:.{n}f}", + equation=r"\frac{2 \cdot R}{E_{p}} \cdot \frac{4 \cdot 2.65^{\alpha} + 3 \alpha}{18}", + numeric_equation=rf"\frac{{2 \cdot {self.r:.{n}f}}}{{{self.e_p:.{n}f}}} \cdot \frac{{4 \cdot 2.65^{{{self.alpha:.{n}f}}} + 3 \cdot " + rf"{self.alpha:.{n}f}}}{{18}}", + unit="kN/m^3", + ) diff --git a/blueprints/codes/formula.py b/blueprints/codes/formula.py index 907a05f3c..ca15552c4 100644 --- a/blueprints/codes/formula.py +++ b/blueprints/codes/formula.py @@ -2,6 +2,8 @@ from abc import ABC, abstractmethod +from blueprints.codes.latex_formula import LatexFormula + class Formula(float, ABC): """Abstract base class for formulas used in the codes.""" @@ -85,3 +87,14 @@ def _evaluate(*args, **kwargs) -> float: The result of the formula. This is an abstract method and must be implemented in all subclasses. """ + + @abstractmethod + def latex(self) -> LatexFormula: + """Abstract method for the latex representation of the formula. + + Returns + ------- + LatexFormula + The latex representation of the formula. + This is an abstract method and must be implemented in all subclasses. + """ diff --git a/blueprints/type_alias.py b/blueprints/type_alias.py index 7ede7b02f..ce571ecb9 100644 --- a/blueprints/type_alias.py +++ b/blueprints/type_alias.py @@ -95,6 +95,8 @@ """Newton per square meter (N/m²), represented as a float.""" KN_M2 = float """Kilonewton per square meter (kN/m²), represented as a float.""" +KN_M3 = float +"""Kilonewton per cubic meter (kN/m²), represented as a float.""" # # diff --git a/docs/source/codes/cur/cur_228/formulas.md b/docs/source/codes/cur/cur_228/formulas.md new file mode 100644 index 000000000..9ddd30718 --- /dev/null +++ b/docs/source/codes/cur/cur_228/formulas.md @@ -0,0 +1,12 @@ +**CUR 228 - 2010 +Design guideline for soil laterally loaded piles** + +The table presents a list of formulas from the CUR 228 guideline, 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 2 formulas present. + +| Formula number | Done | Remarks | Object name | +|:---------------|:------------------:|:--------|:--------------------------| +| 2.21 | :heavy_check_mark: | | Form2Dot21MenardStiffness | +| 2.22 | :heavy_check_mark: | | Form2Dot22MenardStiffness | diff --git a/pyproject.toml b/pyproject.toml index c2e612d46..cd16a2fcf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,3 +54,5 @@ python_version = "3.12" [[tool.mypy.overrides]] module = ["matplotlib.*"] ignore_missing_imports = true + + diff --git a/tests/codes/cur/__init__.py b/tests/codes/cur/__init__.py new file mode 100644 index 000000000..6871e3fb1 --- /dev/null +++ b/tests/codes/cur/__init__.py @@ -0,0 +1 @@ +"""Tests for the CUR guidelines.""" diff --git a/tests/codes/cur/cur_228/__init__.py b/tests/codes/cur/cur_228/__init__.py new file mode 100644 index 000000000..10a2474cc --- /dev/null +++ b/tests/codes/cur/cur_228/__init__.py @@ -0,0 +1 @@ +"""Tests for the CUR 228.""" diff --git a/tests/codes/cur/cur_228/test_formula_2_21.py b/tests/codes/cur/cur_228/test_formula_2_21.py new file mode 100644 index 000000000..225d5a2b9 --- /dev/null +++ b/tests/codes/cur/cur_228/test_formula_2_21.py @@ -0,0 +1,57 @@ +"""Test for formula 2.21 from CUR 228.""" + +import pytest + +from blueprints.codes.cur.cur_228.formula_2_21 import Form2Dot21ModulusHorizontalSubgrade +from blueprints.validations import LessOrEqualToZeroError + + +class TestForm2Dot21ModulusHorizontalSubgrade: + """Validation for formula 2.21 from CUR 228.""" + + def test_evaluation(self) -> None: + """Test the evaluation of the result.""" + # Example values + r = 0.5 # m + e_p = 2.47 # kN/m² + alpha = 1 / 3 # - + form_2_21 = Form2Dot21ModulusHorizontalSubgrade(r=r, e_p=e_p, alpha=alpha) + + # Expected result, manually calculated + manually_calculated_result = 9.187357198 + + assert form_2_21 == pytest.approx(expected=manually_calculated_result, rel=1e-4) + + @pytest.mark.parametrize( + ("e_p", "r", "alpha"), + [ + (-500.0, -0.6, -0.33), + (0.0, 0.0, 0.0), + ], + ) + def test_raise_error_when_invalid_values_are_given(self, e_p: float, r: float, alpha: float) -> None: + """Test invalid values.""" + with pytest.raises(LessOrEqualToZeroError): + Form2Dot21ModulusHorizontalSubgrade(r=r, e_p=e_p, alpha=alpha) + + @pytest.mark.parametrize( + ("r"), + [ + (0.2), + (0.29), + ], + ) + def test_raise_error_when_invalid_diameter_values_are_given(self, r: float) -> None: + """Test invalid values.""" + with pytest.raises(ValueError): + Form2Dot21ModulusHorizontalSubgrade(r=r, e_p=500, alpha=0.33) + + def test_latex_method(self) -> None: + """Test the latex method.""" + r = 0.5 # m + e_p = 2.47 # kN/m² + alpha = 1 / 3 # - + form_2_21 = Form2Dot21ModulusHorizontalSubgrade(r=r, e_p=e_p, alpha=alpha) + + # Test the full LaTeX representation + assert isinstance(form_2_21.latex().complete, str) diff --git a/tests/codes/cur/cur_228/test_formula_2_22.py b/tests/codes/cur/cur_228/test_formula_2_22.py new file mode 100644 index 000000000..5efc8cfa6 --- /dev/null +++ b/tests/codes/cur/cur_228/test_formula_2_22.py @@ -0,0 +1,55 @@ +"""Test for formula 2.22 from CUR 228.""" + +import pytest + +from blueprints.codes.cur.cur_228.formula_2_22 import Form2Dot22ModulusHorizontalSubgrade +from blueprints.validations import LessOrEqualToZeroError + + +class TestForm2Dot22ModulusHorizontalSubgrade: + """Validation for formula 2.22 from CUR 228.""" + + def test_evaluation(self) -> None: + """Test the evaluation of the result.""" + # Example values + r = 0.2 # m + e_p = 2.47 # kN/m² + alpha = 1 / 3 # - + form_2_22 = Form2Dot22ModulusHorizontalSubgrade(r=r, e_p=e_p, alpha=alpha) + + # Expected result, manually calculated + manually_calculated_result = 17.00760939 + + assert form_2_22 == pytest.approx(expected=manually_calculated_result, rel=1e-4) + + def test_raise_error_when_r_is_higher_then_0_3(self) -> None: + """Tests if an ValueError is raised when r > 0.3.""" + # Example values + r = 0.5 # m + e_p = 2.47 # kN/m² + alpha = 1 / 3 # - + + with pytest.raises(ValueError): + Form2Dot22ModulusHorizontalSubgrade(r=r, e_p=e_p, alpha=alpha) + + @pytest.mark.parametrize( + ("e_p", "r", "alpha"), + [ + (-500.0, -0.2, -0.33), + (0.0, 0.0, 0.0), + ], + ) + def test_raise_error_when_invalid_values_are_given(self, e_p: float, r: float, alpha: float) -> None: + """Test invalid values.""" + with pytest.raises(LessOrEqualToZeroError): + Form2Dot22ModulusHorizontalSubgrade(r=r, e_p=e_p, alpha=alpha) + + def test_latex_method(self) -> None: + """Test the latex method.""" + r = 0.2 # m + e_p = 2.47 # kN/m² + alpha = 1 / 3 # - + form_2_22 = Form2Dot22ModulusHorizontalSubgrade(r=r, e_p=e_p, alpha=alpha) + + # Test the full LaTeX representation + assert isinstance(form_2_22.latex().complete, str)