py-pilecore 0.8.2__py3-none-any.whl → 0.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of py-pilecore might be problematic. Click here for more details.

Files changed (30) hide show
  1. {py_pilecore-0.8.2.dist-info → py_pilecore-0.9.0.dist-info}/METADATA +3 -2
  2. py_pilecore-0.9.0.dist-info/RECORD +49 -0
  3. {py_pilecore-0.8.2.dist-info → py_pilecore-0.9.0.dist-info}/WHEEL +1 -1
  4. pypilecore/_version.py +1 -1
  5. pypilecore/api.py +89 -0
  6. pypilecore/common/piles/__init__.py +2 -0
  7. pypilecore/common/piles/geometry/materials.py +15 -0
  8. pypilecore/common/piles/grid.py +222 -0
  9. pypilecore/common/piles/main.py +15 -1
  10. pypilecore/common/piles/type.py +25 -0
  11. pypilecore/input/grouper_properties.py +2 -2
  12. pypilecore/input/soil_properties.py +19 -0
  13. pypilecore/input/tension.py +280 -0
  14. pypilecore/results/__init__.py +12 -4
  15. pypilecore/results/cases_multi_cpt_results.py +31 -26
  16. pypilecore/results/{multi_cpt_results.py → compression/multi_cpt_results.py} +25 -19
  17. pypilecore/results/{single_cpt_results.py → compression/single_cpt_results.py} +7 -7
  18. pypilecore/results/grouper_result.py +5 -3
  19. pypilecore/results/result_definitions.py +20 -13
  20. pypilecore/results/soil_properties.py +50 -0
  21. pypilecore/results/tension/multi_cpt_results.py +393 -0
  22. pypilecore/results/tension/single_cpt_results.py +393 -0
  23. pypilecore/results/typing.py +44 -0
  24. pypilecore/viewers/interactive_figures/figure_cpt_results_plan_view.py +1 -1
  25. pypilecore/viewers/viewer_cpt_group_results.py +7 -3
  26. pypilecore/viewers/viewer_cpt_results.py +8 -3
  27. pypilecore/viewers/viewer_cpt_results_plan_view.py +7 -3
  28. py_pilecore-0.8.2.dist-info/RECORD +0 -44
  29. {py_pilecore-0.8.2.dist-info → py_pilecore-0.9.0.dist-info}/LICENSE +0 -0
  30. {py_pilecore-0.8.2.dist-info → py_pilecore-0.9.0.dist-info}/top_level.txt +0 -0
@@ -2,9 +2,6 @@ from __future__ import annotations # noqa: F404
2
2
 
3
3
  from dataclasses import dataclass
4
4
  from enum import Enum
5
- from typing import List
6
-
7
- from natsort import natsorted
8
5
 
9
6
 
10
7
  @dataclass
@@ -105,6 +102,16 @@ class CPTResultDefinitions(Enum):
105
102
  s_el = ResultDefinition(name="s_el", unit="mm", html="s<sub>el</sub>")
106
103
  k_v_b = ResultDefinition(name="k_v_b", unit="MN/m", html="k<sub>v;b</sub>")
107
104
  k_v_1 = ResultDefinition(name="k_v_1", unit="MN/m", html="k<sub>v;1</sub>")
105
+ R_t_d = ResultDefinition(name="R_t_d", unit="kN", html="R<sub>t;d</sub>")
106
+ R_t_k = ResultDefinition(name="R_t_k", unit="kN", html="R<sub>t;k</sub>")
107
+ R_t_d_plug = ResultDefinition(
108
+ name="R_t_d_plug", unit="kN", html="R<sub>t;d;kluit</sub>"
109
+ )
110
+ R_s_mob = ResultDefinition(name="R_s_mob", unit="kN", html="R<sub>s;mob</sub>")
111
+ R_s_mob_ratio = ResultDefinition(
112
+ name="R_s_mob_ratio", unit="-", html="R<sub>s;mob;ratio</sub>"
113
+ )
114
+ s_e = ResultDefinition(name="s_e", unit="mm", html="s<sub>e</sub>")
108
115
 
109
116
  @classmethod
110
117
  def get(cls, name: str) -> CPTResultDefinitions:
@@ -116,11 +123,6 @@ class CPTResultDefinitions(Enum):
116
123
  f"Result with name '{name}' not found in 'CPTResultDefinitions'."
117
124
  )
118
125
 
119
- @classmethod
120
- def natsorted_names(cls) -> List[str]:
121
- """Returns the names of the enum in natsorted order."""
122
- return natsorted([r.name for r in cls])
123
-
124
126
 
125
127
  class CPTGroupResultDefinitions(Enum):
126
128
  R_s_k = ResultDefinition(name="R_s_k", unit="kN", html="R<sub>s;k</sub>")
@@ -178,6 +180,16 @@ class CPTGroupResultDefinitions(Enum):
178
180
  cpt_normative = ResultDefinition(
179
181
  name="cpt_normative", unit="-", html="Normative CPT"
180
182
  )
183
+ R_t_d = ResultDefinition(name="R_t_d", unit="kN", html="R<sub>t;d</sub>")
184
+ R_t_d_mean = ResultDefinition(
185
+ name="R_t_d_mean", unit="kN", html="R<sub>t;d;mean</sub>"
186
+ )
187
+ R_t_d_min = ResultDefinition(
188
+ name="R_t_d_min", unit="kN", html="R<sub>t;d;min</sub>"
189
+ )
190
+ R_t_d_plug = ResultDefinition(
191
+ name="R_t_d_plug", unit="kN", html="R<sub>t;d;kluit</sub>"
192
+ )
181
193
 
182
194
  @classmethod
183
195
  def get(cls, name: str) -> CPTGroupResultDefinitions:
@@ -188,8 +200,3 @@ class CPTGroupResultDefinitions(Enum):
188
200
  raise ValueError(
189
201
  f"Result with name '{name}' not found in 'CPTGroupResultDefinitions'."
190
202
  )
191
-
192
- @classmethod
193
- def natsorted_names(cls) -> List[str]:
194
- """Returns the names of the enum in natsorted order."""
195
- return natsorted([r.name for r in cls])
@@ -140,6 +140,16 @@ class CPTTable:
140
140
  qc1: Sequence[float] | None,
141
141
  qc2: Sequence[float] | None,
142
142
  fs: Sequence[float] | None,
143
+ qs_d: Sequence[float] | None,
144
+ f1: Sequence[float] | None,
145
+ f2_d_mean: Sequence[float] | None,
146
+ f3: Sequence[float] | None,
147
+ R_t_d: Sequence[float] | None,
148
+ phi_plug: Sequence[float] | None,
149
+ R_t_d_plug: Sequence[float] | None,
150
+ alpha_t: Sequence[float] | None,
151
+ alpha_t_1: Sequence[float] | None,
152
+ alpha_t_2: Sequence[float] | None,
143
153
  ):
144
154
  if depth_nap is None:
145
155
  self.depth_nap = np.array([depth_nap]).astype(np.float64)
@@ -160,6 +170,36 @@ class CPTTable:
160
170
  """The Koppejan-qc2 trajectory [MPa]."""
161
171
  self.fs = np.array(fs).astype(np.float64)
162
172
  """The original fs signal from the CPT [MPa]."""
173
+ self.qs_d = np.array(qs_d).astype(np.float64)
174
+ """The computational value of shaft friction [kPa]."""
175
+ self.f1 = np.array(f1).astype(np.float64)
176
+ """factor for the effect of compaction in the pile group (7.6.3.3 (e)
177
+ NEN 9997-1+C2:2017) [-]."""
178
+ self.f2_d_mean = np.array(f2_d_mean).astype(np.float64)
179
+ """factor for the decrease in grain stress in sand layers from which
180
+ the pile derives its tensile resistance (7.6.3.3 (f) NEN
181
+ 9997-1+C2:2017) [-]."""
182
+ self.f3 = np.array(f3).astype(np.float64)
183
+ """factor for the decrease in bearing capacity from the length—effect,
184
+ according to figure 6.1a en 6.1b (CROW-CUR Rapport 236 Richtlijn
185
+ Ankerpalen [2023]) [-]."""
186
+ self.R_t_d = np.array(R_t_d).astype(np.float64)
187
+ """calculation value of the tensile resistance of a pile or pile group
188
+ (7.6.3.3 (a) NEN 9997-1+C2:2017 or CROW-CUR Rapport 236 Richtlijn Ankerpalen [2023]) [kN]."""
189
+ self.phi_plug = np.array(phi_plug).astype(np.float64)
190
+ """Half top angle of the ground cone (7.6.3.3 Table 7.e NEN 9997-1+C2:2017) [degrees]."""
191
+ self.R_t_d_plug = np.array(R_t_d_plug).astype(np.float64)
192
+ """root ball weight, excluding the weight of the pile (7.6.3.3 (h) NEN
193
+ 9997-1+C2:2017) [kN]."""
194
+ self.alpha_t = np.array(alpha_t).astype(np.float64)
195
+ """Alpha t factor used in tension resistance calculation [-]. based on
196
+ table 7.d NEN 9997-1+C2:2017."""
197
+ self.alpha_t_1 = np.array(alpha_t_1).astype(np.float64)
198
+ """Alpha t factor used in tension resistance calculation [-]. tabel 6.2
199
+ CROW-CUR Rapport 236 Richtlijn Ankerpalen [2023]."""
200
+ self.alpha_t_2 = np.array(alpha_t_2).astype(np.float64)
201
+ """Alpha t factor used in tension resistance calculation [-]. tabel 6.2
202
+ CROW-CUR Rapport 236 Richtlijn Ankerpalen [2023]."""
163
203
 
164
204
  dict_lengths = {}
165
205
  for key, value in self.__dict__.items():
@@ -186,6 +226,16 @@ class CPTTable:
186
226
  qc1=cpt_chart_dict.get("qc1"),
187
227
  qc2=cpt_chart_dict.get("qc2"),
188
228
  fs=cpt_chart_dict.get("fs"),
229
+ qs_d=cpt_chart_dict.get("qs_d"),
230
+ f1=cpt_chart_dict.get("f1"),
231
+ f2_d_mean=cpt_chart_dict.get("f2_d_mean"),
232
+ f3=cpt_chart_dict.get("f3"),
233
+ R_t_d=cpt_chart_dict.get("R_t_d"),
234
+ phi_plug=cpt_chart_dict.get("phi_plug"),
235
+ R_t_d_plug=cpt_chart_dict.get("R_t_d_plug"),
236
+ alpha_t=cpt_chart_dict.get("alpha_t"),
237
+ alpha_t_1=cpt_chart_dict.get("alpha_t_1"),
238
+ alpha_t_2=cpt_chart_dict.get("alpha_t_2"),
189
239
  )
190
240
 
191
241
  @property
@@ -0,0 +1,393 @@
1
+ from __future__ import annotations
2
+
3
+ from functools import lru_cache
4
+ from typing import Any, Dict, List, Sequence, Tuple, Union
5
+
6
+ import matplotlib.pyplot as plt
7
+ import numpy as np
8
+ import pandas as pd
9
+ from matplotlib.axes import Axes
10
+
11
+ from pypilecore.common.piles import PileProperties
12
+ from pypilecore.results.tension.single_cpt_results import SingleCPTTensionBearingResults
13
+
14
+ Number = Union[float, int]
15
+
16
+
17
+ class CPTTensionGroupResultsTable:
18
+ """
19
+ Dataclass that contains the bearing results of a group of CPTs.
20
+ """
21
+
22
+ def __init__(
23
+ self,
24
+ pile_tip_level_nap: Sequence[float],
25
+ var_coef: Sequence[float],
26
+ n_cpts: Sequence[float],
27
+ xi_value: Sequence[float],
28
+ xi_normative: Sequence[float],
29
+ cpt_normative: Sequence[float],
30
+ R_t_d_min: Sequence[float],
31
+ R_t_d_mean: Sequence[float],
32
+ R_t_d: Sequence[float],
33
+ R_t_d_plug: Sequence[float],
34
+ ):
35
+ """
36
+ Parameters
37
+ ----------
38
+ pile_tip_level_nap:
39
+ The pile-tip level [m] w.r.t. NAP.
40
+ var_coef:
41
+ The variation coefficient [%] of the calculated bearing capacities in the group.
42
+ n_cpts:
43
+ The number of CPTs [-] that have been taken into account to establish the Xi value.
44
+ xi_normative:
45
+ The normative Xi (either Xi_3 or Xi_4)
46
+ xi_value:
47
+ The Xi value [-] that was applied to calculate the characteristic value of the
48
+ total bearing capacity.
49
+ cpt_normative:
50
+ The normative CPT. Can be "group average" if that was found to be the normative scenario.
51
+ R_t_d_min:
52
+ The minimum of the single-CPT values for the calculated bearingcapacity [kN].
53
+ R_t_d_mean:
54
+ The mean of the single-CPT values for the calculated
55
+ bearingcapacity [kN].
56
+ R_t_d:
57
+ calculation value of the tensile resistance of a pile or pile group
58
+ (7.6.3.3 (a) NEN 9997-1+C2:2017) [kN]
59
+ R_t_d_plug:
60
+ The design value of the total plug weight bearingcapacity [kN].
61
+ """
62
+ self.pile_tip_level_nap = (
63
+ np.array(pile_tip_level_nap).astype(np.float64).round(decimals=2)
64
+ )
65
+ """The pile-tip level [m] w.r.t. NAP."""
66
+ self.var_coef = np.array(var_coef).astype(np.float64)
67
+ """The variation coefficient [%] of the calculated bearing capacities in the group."""
68
+ self.n_cpts = np.array(n_cpts).astype(np.int32)
69
+ """The number of CPTs [-] that have been taken into account to establish the Xi value."""
70
+ self.xi_normative = np.array(xi_normative).astype(np.str_)
71
+ """The normative Xi (either Xi_3 or Xi_4)"""
72
+ self.xi_value = np.array(xi_value).astype(np.float64)
73
+ """The Xi value [-] that was applied to calculate the characteristic value of the
74
+ total bearing capacity."""
75
+ self.cpt_normative = np.array(cpt_normative).astype(np.str_)
76
+ """The normative CPT. Can be "group average" if that was found to be the normative scenario."""
77
+ self.R_t_d_min = np.array(R_t_d_min).astype(np.float64)
78
+ """The minimum of the single-CPT values for the calculated bearingcapacity [kN]."""
79
+ self.R_t_d_mean = np.array(R_t_d_mean).astype(np.float64)
80
+ """The mean of the single-CPT values for the calculated
81
+ bearingcapacity [kN]."""
82
+ self.R_t_d = np.array(R_t_d).astype(np.float64)
83
+ """calculation value of the tensile resistance of a pile or pile group
84
+ (7.6.3.3 (a) NEN 9997-1+C2:2017) [kN]"""
85
+ self.R_t_d_plug = np.array(R_t_d_plug).astype(np.float64)
86
+ """The design value of the total plug weight bearingcapacity [kN]."""
87
+
88
+ for value in self.__dict__.values():
89
+ if not len(value) == len(self.pile_tip_level_nap):
90
+ raise ValueError(
91
+ "Inputs for CPTGroupResults dataclass must have same length."
92
+ )
93
+
94
+ @lru_cache
95
+ def to_pandas(self) -> pd.DataFrame:
96
+ """The pandas.DataFrame representation"""
97
+ return pd.DataFrame(self.__dict__).dropna(axis=0, how="all")
98
+
99
+ def plot_bearing_capacities(
100
+ self,
101
+ axes: Axes | None = None,
102
+ figsize: Tuple[int, int] = (8, 10),
103
+ add_legend: bool = True,
104
+ **kwargs: Any,
105
+ ) -> Axes:
106
+ """
107
+ Plots the design value of the bearing capacity Rcd, based on a group of CPTs.
108
+
109
+ Parameters
110
+ ----------
111
+ axes:
112
+ Optional `Axes` object where the bearing capacity data can be plotted on.
113
+ If not provided, a new `plt.Figure` will be activated and the `Axes`
114
+ object will be created and returned.
115
+ figsize:
116
+ Size of the activate figure, as the `plt.figure()` argument.
117
+ add_legend:
118
+ Add a legend to the `Axes` object
119
+ **kwargs:
120
+ All additional keyword arguments are passed to the `pyplot.subplots()` call.
121
+
122
+ Returns
123
+ -------
124
+ axes:
125
+ The `Axes` object where the bearing capacities were plotted on.
126
+ """
127
+
128
+ # Create axes objects if not provided
129
+ if axes is not None:
130
+ if not isinstance(axes, Axes):
131
+ raise TypeError(
132
+ "'axes' argument to plot_bearing_capacities() must be a `pyplot.axes.Axes` object or None."
133
+ )
134
+
135
+ else:
136
+ kwargs_subplot = {
137
+ "figsize": figsize,
138
+ "tight_layout": True,
139
+ }
140
+
141
+ kwargs_subplot.update(kwargs)
142
+
143
+ _, axes = plt.subplots(
144
+ 1,
145
+ 1,
146
+ **kwargs_subplot,
147
+ )
148
+
149
+ if not isinstance(axes, Axes):
150
+ raise ValueError(
151
+ "Could not create Axes objects. This is probably due to invalid matplotlib keyword arguments. "
152
+ )
153
+
154
+ axes.plot(
155
+ self.R_t_d,
156
+ self.pile_tip_level_nap,
157
+ "-",
158
+ label=r"$R_{t;d}$",
159
+ )
160
+ axes.plot(
161
+ self.R_t_d_plug,
162
+ self.pile_tip_level_nap,
163
+ ":",
164
+ label=r"$R_{t;d;kluit}$",
165
+ )
166
+ axes.set_xlabel("[kN]")
167
+ axes.set_ylabel("[m] w.r.t. NAP")
168
+
169
+ # set grid
170
+ axes.grid()
171
+
172
+ # Add legend
173
+ if add_legend:
174
+ axes.legend(
175
+ loc="upper left",
176
+ bbox_to_anchor=(1, 1),
177
+ )
178
+ return axes
179
+
180
+
181
+ class SingleCPTTensionBearingResultsContainer:
182
+ """A container that holds multiple SingleCPTBearingResults objects"""
183
+
184
+ def __init__(self, cpt_results_dict: Dict[str, SingleCPTTensionBearingResults]):
185
+ """
186
+ Parameters
187
+ ----------
188
+ cpt_results_dict:
189
+ A dictionary that maps the cpt-names to SingleCPTBearingResults objects.
190
+ """
191
+ self._cpt_results_dict = cpt_results_dict
192
+
193
+ @classmethod
194
+ def from_api_response(
195
+ cls, cpt_results_list: list, cpt_input: dict
196
+ ) -> "SingleCPTTensionBearingResultsContainer":
197
+ """
198
+ Instantiates the SingleCPTBearingResultsContainer object from the "cpts" array,
199
+ which is returned in the response of a "compression/multiple-cpts/results" endpoint call.
200
+ """
201
+ return cls(
202
+ cpt_results_dict={
203
+ cpt_results[
204
+ "test_id"
205
+ ]: SingleCPTTensionBearingResults.from_api_response(
206
+ cpt_results_dict=cpt_results,
207
+ ref_height=cpt_input[cpt_results["test_id"]]["ref_height"],
208
+ surface_level_ref=cpt_input[cpt_results["test_id"]][
209
+ "surface_level_nap"
210
+ ],
211
+ x=cpt_input[cpt_results["test_id"]].get("location", {}).get("x"),
212
+ y=cpt_input[cpt_results["test_id"]].get("location", {}).get("y"),
213
+ )
214
+ for cpt_results in cpt_results_list
215
+ }
216
+ )
217
+
218
+ def __getitem__(self, test_id: str) -> SingleCPTTensionBearingResults:
219
+ if not isinstance(test_id, str):
220
+ raise TypeError(f"Expected a test-id as a string, but got: {type(test_id)}")
221
+
222
+ return self.get_cpt_results(test_id)
223
+
224
+ @property
225
+ def cpt_results_dict(self) -> Dict[str, SingleCPTTensionBearingResults]:
226
+ """The dictionary that maps the cpt-names to SingleCPTBearingResults objects."""
227
+ return self._cpt_results_dict
228
+
229
+ @property
230
+ def test_ids(self) -> List[str]:
231
+ """The test-ids of the CPTs."""
232
+ return list(self.cpt_results_dict.keys())
233
+
234
+ @property
235
+ def results(self) -> List[SingleCPTTensionBearingResults]:
236
+ """The computed results, as a list of SingleCPTBearingResults objects."""
237
+ return list(self.cpt_results_dict.values())
238
+
239
+ def get_cpt_results(self, test_id: str) -> SingleCPTTensionBearingResults:
240
+ """
241
+ Returns the `SingleCPTBearingResults` object for the provided test_id.
242
+ """
243
+
244
+ if test_id not in self.cpt_results_dict.keys():
245
+ raise ValueError(
246
+ f"No Cpt-results were calculated for this test-id: {test_id}. "
247
+ "Please check the spelling or run a new calculation for this CPT."
248
+ )
249
+
250
+ return self.cpt_results_dict[test_id]
251
+
252
+ def get_results_per_cpt(self, column_name: str) -> pd.DataFrame:
253
+ """
254
+ Returns a pandas dataframe with a single result-item, organized per CPT
255
+ (test-id) and pile-tip-level-nap.
256
+
257
+ Parameters
258
+ ----------
259
+ column_name:
260
+ The name of the result-item / column name of the single-cpt-results table.
261
+ """
262
+ if column_name not in self.to_pandas().columns or column_name in [
263
+ "pile_tip_level_nap",
264
+ "test_id",
265
+ ]:
266
+ raise ValueError(f"Invalid column_name: {column_name} provided.")
267
+
268
+ results = pd.pivot_table(
269
+ self.to_pandas(),
270
+ values=column_name,
271
+ index="pile_tip_level_nap",
272
+ columns="test_id",
273
+ dropna=False,
274
+ )
275
+ return results.sort_values("pile_tip_level_nap", ascending=False)
276
+
277
+ @lru_cache
278
+ def to_pandas(self) -> pd.DataFrame:
279
+ """Returns a total overview of all single-cpt results in a pandas.DataFrame representation."""
280
+ df_list: List[pd.DataFrame] = []
281
+
282
+ for test_id in self.cpt_results_dict:
283
+ df = self.cpt_results_dict[test_id].table.to_pandas()
284
+ df = df.assign(test_id=test_id)
285
+ df_list.append(df)
286
+
287
+ cpt_results_df = pd.concat(df_list)
288
+ cpt_results_df = cpt_results_df.assign(
289
+ pile_tip_level_nap=cpt_results_df.pile_tip_level_nap.round(1)
290
+ )
291
+
292
+ return cpt_results_df
293
+
294
+
295
+ class MultiCPTTensionBearingResults:
296
+ """
297
+ Object that contains the results of a PileCore multi-cpt calculation.
298
+
299
+ *Not meant to be instantiated by the user.*
300
+ """
301
+
302
+ def __init__(
303
+ self,
304
+ cpt_results: SingleCPTTensionBearingResultsContainer,
305
+ group_results_table: CPTTensionGroupResultsTable,
306
+ pile_properties: PileProperties,
307
+ gamma_s_t: float,
308
+ gamma_m_var_qc: float,
309
+ soil_load: float,
310
+ ) -> None:
311
+ """
312
+ Parameters
313
+ ----------
314
+ cpt_results:
315
+ The container object with single-CPT results
316
+ group_results_table:
317
+ The table object with CPT-group-results
318
+ pile_properties:
319
+ The PileProperties object
320
+ gamma_s_t:
321
+ Safety factor γ_s_t. Used to determine the tensile resistance of a pile. [-]
322
+ gamma_m_var_qc:
323
+ partial factor for the influence of load switching; NEN 9997-1+C2:2017 7.6.3.3 (d). [-]
324
+ phi_plug:
325
+ The effective angle of internal friction of the soil layer based on pile type [degrees].
326
+ soil_load:
327
+ (Fictive) load on soil used to calculate soil settlement [kPa]. This is
328
+ required and used to determine settlement of pile w.r.t. soil.
329
+ """
330
+ self._pp = pile_properties
331
+ self._gamma_s_t = gamma_s_t
332
+ self._gamma_m_var_qc = gamma_m_var_qc
333
+ self._soil_load = soil_load
334
+
335
+ self._cpt_results = cpt_results
336
+
337
+ self._group_results_table = group_results_table
338
+
339
+ @classmethod
340
+ def from_api_response(
341
+ cls, response_dict: dict, cpt_input: dict
342
+ ) -> "MultiCPTTensionBearingResults":
343
+ """
344
+ Build the object from the response payload of the PileCore endpoint
345
+ "/compression/multi-cpt/results".
346
+ """
347
+ cpt_results_dict = SingleCPTTensionBearingResultsContainer.from_api_response(
348
+ cpt_results_list=response_dict["cpts"], cpt_input=cpt_input
349
+ )
350
+ group_results = response_dict["group_results"]
351
+ return cls(
352
+ cpt_results=cpt_results_dict,
353
+ pile_properties=PileProperties.from_api_response(
354
+ response_dict["pile_properties"]
355
+ ),
356
+ group_results_table=CPTTensionGroupResultsTable(
357
+ pile_tip_level_nap=group_results["pile_tip_level_nap"],
358
+ var_coef=group_results["var_coef"],
359
+ n_cpts=group_results["n_cpts"],
360
+ xi_value=group_results["xi_value"],
361
+ xi_normative=group_results["xi_normative"],
362
+ cpt_normative=group_results["cpt_normative"],
363
+ R_t_d_min=group_results["R_t_d_min"],
364
+ R_t_d_mean=group_results["R_t_d_mean"],
365
+ R_t_d=group_results["R_t_d"],
366
+ R_t_d_plug=group_results["R_t_d_plug"],
367
+ ),
368
+ gamma_s_t=response_dict["calculation_params"]["gamma_s_t"],
369
+ soil_load=response_dict["calculation_params"]["soil_load"],
370
+ gamma_m_var_qc=response_dict["calculation_params"]["gamma_m_var_qc"],
371
+ )
372
+
373
+ @property
374
+ def pile_properties(self) -> PileProperties:
375
+ """
376
+ The PileProperties object.
377
+ """
378
+ return self._pp
379
+
380
+ @property
381
+ def cpt_results(self) -> SingleCPTTensionBearingResultsContainer:
382
+ """The SingleCPTBearingResultsContainer object."""
383
+ return self._cpt_results
384
+
385
+ @property
386
+ def cpt_names(self) -> List[str]:
387
+ """The test-ids of the CPTs."""
388
+ return self.cpt_results.test_ids
389
+
390
+ @property
391
+ def group_results_table(self) -> CPTTensionGroupResultsTable:
392
+ """The CPTGroupResultsTable dataclass, containing the group results."""
393
+ return self._group_results_table