py-pilecore 0.4.1__py3-none-any.whl → 0.5.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.

@@ -0,0 +1,493 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import Any, Dict, Literal, Tuple
5
+
6
+ import numpy as np
7
+ from matplotlib import pyplot as plt
8
+ from matplotlib.axes import Axes
9
+ from numpy.typing import NDArray
10
+
11
+
12
+ def get_component_bounds_nap(
13
+ pile_tip_level_nap: float | int,
14
+ pile_head_level_nap: float | int,
15
+ component_primary_length: float | int | None,
16
+ ) -> Tuple[float, float]:
17
+ """
18
+ Returns component head and tip level in NAP.
19
+
20
+ Parameters
21
+ ----------
22
+ pile_tip_level_nap : float
23
+ pile tip level in [m] w.r.t. NAP.
24
+ pile_head_level_nap : float
25
+ pile head level in [m] w.r.t. NAP.
26
+ component_primary_length : float, optional
27
+ The length of the pile-geometry component.
28
+
29
+ Returns
30
+ -------
31
+ tuple
32
+ Tuple with component head and tip level in [m] w.r.t. NAP.
33
+ """
34
+ # Component tip level is always the pile tip level
35
+ component_tip_level_nap = pile_tip_level_nap
36
+
37
+ # Component head level is the pile head level if the length is not set
38
+ if component_primary_length is not None:
39
+ component_head_level_nap = pile_tip_level_nap + component_primary_length
40
+ else:
41
+ component_head_level_nap = pile_head_level_nap
42
+
43
+ return float(component_head_level_nap), float(component_tip_level_nap)
44
+
45
+
46
+ def get_circum_vs_depth(
47
+ depth_nap: NDArray[np.floating],
48
+ pile_tip_level_nap: float | int,
49
+ pile_head_level_nap: float | int,
50
+ length: float | None,
51
+ circumference: float,
52
+ ) -> NDArray[np.floating]:
53
+ """
54
+ Returns component circumferences at requested depths.
55
+
56
+ Parameters
57
+ ----------
58
+ depth_nap : np.array
59
+ Array with depths in [m] w.r.t. NAP.
60
+ pile_tip_level_nap : float
61
+ pile tip level in [m] w.r.t. NAP.
62
+ pile_head_level_nap : float
63
+ pile head level in [m] w.r.t. NAP.
64
+ length : float, optional
65
+ The length of the pile-geometry component, by default None.
66
+ circumference : float
67
+ The circumference of the pile-geometry component.
68
+
69
+ Returns
70
+ -------
71
+ np.array
72
+ Array with component circumferences at the depths in the depth parameter.
73
+ """
74
+ circum_vs_depth = np.zeros_like(depth_nap)
75
+
76
+ # Component tip level is always the pile tip level
77
+ component_tip_level_nap = pile_tip_level_nap
78
+
79
+ # Component head level is the pile head level if the length is not set
80
+ if length is not None:
81
+ component_head_level_nap = pile_tip_level_nap + length
82
+ else:
83
+ component_head_level_nap = pile_head_level_nap
84
+
85
+ # Fill the circumference between component tip and head level
86
+ circum_vs_depth[
87
+ (depth_nap <= component_head_level_nap) & (depth_nap >= component_tip_level_nap)
88
+ ] = circumference
89
+
90
+ return circum_vs_depth
91
+
92
+
93
+ def get_area_vs_depth(
94
+ depth_nap: NDArray[np.floating],
95
+ area_full: float | int,
96
+ component_head_level_nap: float | int,
97
+ component_tip_level_nap: float | int,
98
+ inner_area: NDArray[np.floating],
99
+ ) -> NDArray[np.floating]:
100
+ """
101
+ Returns component areas at requested depths.
102
+
103
+ Parameters
104
+ ----------
105
+ depth_nap : np.array
106
+ Array with depths in [m] w.r.t. NAP.
107
+ area_full : float
108
+ The full outer-area [m²] of the pile-geometry component, including any potential inner-components.
109
+ pile_tip_level_nap : float
110
+ pile tip level in [m] w.r.t. NAP.
111
+ pile_head_level_nap : float
112
+ pile head level in [m] w.r.t. NAP.
113
+ component_head_level_nap : float
114
+ Component head level in [m] w.r.t. NAP.
115
+ component_tip_level_nap : float
116
+ Component tip level in [m] w.r.t. NAP.
117
+ inner_area : np.array
118
+ Array with inner areas at the depths in the depth parameter.
119
+
120
+ Returns
121
+ -------
122
+ np.array
123
+ Array with component areas at the depths in the depth parameter.
124
+ """
125
+ area_vs_depth = np.zeros_like(depth_nap, dtype=np.floating)
126
+
127
+ # Mask the depths between the component tip and head level
128
+ mask_depths = (depth_nap <= component_head_level_nap) & (
129
+ depth_nap >= component_tip_level_nap
130
+ )
131
+
132
+ # Fill the area between component tip and head level, subtracting the inner area
133
+ area_vs_depth[mask_depths] = float(area_full) - inner_area[mask_depths]
134
+
135
+ return area_vs_depth
136
+
137
+
138
+ def instantiate_axes(
139
+ figsize: Tuple[float, float] = (6.0, 6.0),
140
+ axes: Axes | None = None,
141
+ **kwargs: Any,
142
+ ) -> Axes:
143
+ """
144
+ Validate axes objects if provided, otherwise create a new axes object.
145
+
146
+ Parameters
147
+ ----------
148
+ figsize : tuple, optional
149
+ The figure size (width, height) in inches, by default (6.0, 6.0).
150
+ axes : Axes, optional
151
+ The axes object to plot the cross-section on.
152
+ **kwargs
153
+ Additional keyword arguments to pass to the `plt.subplots()` function.
154
+
155
+ Returns
156
+ -------
157
+ Axes
158
+ The axes object to plot the cross-section on.
159
+ """
160
+ # Create axes objects if not provided
161
+ if axes is not None:
162
+ if not isinstance(axes, Axes):
163
+ raise ValueError(
164
+ "'axes' argument must be a `pyplot.axes.Axes` object or None."
165
+ )
166
+
167
+ else:
168
+ kwargs_subplot = {
169
+ "figsize": figsize,
170
+ "tight_layout": True,
171
+ }
172
+
173
+ kwargs_subplot.update(kwargs)
174
+
175
+ _, axes = plt.subplots(
176
+ 1,
177
+ 1,
178
+ **kwargs_subplot,
179
+ )
180
+
181
+ if not isinstance(axes, Axes):
182
+ raise ValueError(
183
+ "Could not create Axes objects. This is probably due to invalid matplotlib keyword arguments. "
184
+ )
185
+
186
+ return axes
187
+
188
+
189
+ class _BasePileGeometryComponent(ABC):
190
+ """
191
+ The _BasePileGeometryComponent class is an abstract base class for pile-geometry components.
192
+ """
193
+
194
+ @classmethod
195
+ @abstractmethod
196
+ def from_api_response(
197
+ cls,
198
+ component: dict,
199
+ inner_component: _BasePileGeometryComponent | None = None,
200
+ ) -> _BasePileGeometryComponent:
201
+ """
202
+ Instantiates a pile-geometry component from a component object in the API response payload.
203
+
204
+ Parameters:
205
+ -----------
206
+ component: dict
207
+ A dictionary that is retrieved from the API response payload at "/pile_properties/geometry/components/[i]".
208
+ inner_component: _BasePileGeometryComponent | None, optional
209
+ The component on the inside of the pile-geometry component, by default None.
210
+
211
+ Returns:
212
+ --------
213
+ _BasePileGeometryComponent
214
+ A pile-geometry component.
215
+ """
216
+ ...
217
+
218
+ @property
219
+ @abstractmethod
220
+ def inner_component(
221
+ self,
222
+ ) -> _BasePileGeometryComponent | None:
223
+ """The component on the inside of the pile-geometry component"""
224
+ ...
225
+
226
+ @property
227
+ @abstractmethod
228
+ def outer_shape(self) -> str:
229
+ """The outer shape of the pile-geometry component"""
230
+ ...
231
+
232
+ @property
233
+ @abstractmethod
234
+ def material(self) -> str | None:
235
+ """The material name of the pile-geometry component"""
236
+ ...
237
+
238
+ @property
239
+ @abstractmethod
240
+ def primary_dimension(self) -> PrimaryPileComponentDimension:
241
+ """
242
+ The primary dimension [m] of the pile-geometry component, which is measured along the primary axis of the pile.
243
+ """
244
+ ...
245
+
246
+ @property
247
+ @abstractmethod
248
+ def circumference(self) -> float:
249
+ """The outer-circumference [m] of the pile-geometry component"""
250
+ ...
251
+
252
+ @property
253
+ @abstractmethod
254
+ def equiv_tip_diameter(self) -> float:
255
+ """
256
+ Equivalent outer-diameter [m] of the component at the tip-level.
257
+ """
258
+ ...
259
+
260
+ @property
261
+ @abstractmethod
262
+ def area_full(self) -> float:
263
+ """The full outer-area [m²] of the pile-geometry component, including any potential inner-components"""
264
+ ...
265
+
266
+ @abstractmethod
267
+ def serialize_payload(self) -> dict:
268
+ """
269
+ Serialize the pile-geometry component to a dictionary payload for the API.
270
+
271
+ Returns:
272
+ A dictionary payload containing the geometry properties.
273
+ """
274
+
275
+ @abstractmethod
276
+ def get_component_bounds_nap(
277
+ self,
278
+ pile_tip_level_nap: float | int,
279
+ pile_head_level_nap: float | int,
280
+ ) -> Tuple[float, float]:
281
+ """
282
+ Returns component head and tip level in NAP.
283
+
284
+ Parameters
285
+ ----------
286
+ pile_tip_level_nap : float
287
+ pile tip level in [m] w.r.t. NAP.
288
+ pile_head_level_nap : float
289
+ pile head level in [m] w.r.t. NAP.
290
+
291
+ Returns
292
+ -------
293
+ tuple
294
+ Tuple with component head and tip level in [m] w.r.t. NAP.
295
+ """
296
+ ...
297
+
298
+ @abstractmethod
299
+ def get_circum_vs_depth(
300
+ self,
301
+ depth_nap: NDArray[np.floating],
302
+ pile_tip_level_nap: float | int,
303
+ pile_head_level_nap: float | int,
304
+ ) -> NDArray[np.floating]:
305
+ """
306
+ Returns component circumferences at requested depths.
307
+
308
+ Parameters
309
+ ----------
310
+ depth_nap : np.array
311
+ Array with depths in [m] w.r.t. NAP.
312
+ pile_tip_level_nap : float
313
+ pile tip level in [m] w.r.t. NAP.
314
+ pile_head_level_nap : float
315
+ pile head level in [m] w.r.t. NAP.
316
+
317
+ Returns
318
+ -------
319
+ np.array
320
+ Array with component circumferences at the depths in the depth parameter.
321
+ """
322
+ ...
323
+
324
+ @abstractmethod
325
+ def get_inner_area_vs_depth(
326
+ self,
327
+ depth_nap: NDArray[np.floating],
328
+ pile_tip_level_nap: float | int,
329
+ pile_head_level_nap: float | int,
330
+ ) -> NDArray[np.floating]:
331
+ """
332
+ Returns inner component areas at requested depths.
333
+
334
+ Parameters
335
+ ----------
336
+ depth_nap : np.array
337
+ Array with depths in [m] w.r.t. NAP.
338
+ pile_tip_level_nap : float
339
+ pile tip level in [m] w.r.t. NAP.
340
+ pile_head_level_nap : float
341
+ pile head level in [m] w.r.t. NAP.
342
+
343
+ Returns
344
+ -------
345
+ np.array
346
+ Array with inner component areas at the depths in the depth parameter.
347
+ """
348
+ ...
349
+
350
+ @abstractmethod
351
+ def get_area_vs_depth(
352
+ self,
353
+ depth_nap: NDArray[np.floating],
354
+ pile_tip_level_nap: float | int,
355
+ pile_head_level_nap: float | int,
356
+ ) -> NDArray[np.floating]:
357
+ """
358
+ Returns component areas at requested depths.
359
+
360
+ Parameters
361
+ ----------
362
+ depth_nap : np.array
363
+ Array with depths in [m] w.r.t. NAP.
364
+ pile_tip_level_nap : float
365
+ pile tip level in [m] w.r.t. NAP.
366
+ pile_head_level_nap : float
367
+ pile head level in [m] w.r.t. NAP.
368
+
369
+ Returns
370
+ -------
371
+ np.array
372
+ Array with component areas at the depths in the depth parameter.
373
+ """
374
+ ...
375
+
376
+ @abstractmethod
377
+ def plot_cross_section_exterior(
378
+ self,
379
+ figsize: Tuple[float, float] = (6.0, 6.0),
380
+ facecolor: Tuple[float, float, float] | str | None = None,
381
+ axes: Axes | None = None,
382
+ axis_arg: bool | str | Tuple[float, float, float, float] | None = "auto",
383
+ show: bool = True,
384
+ **kwargs: Any,
385
+ ) -> Axes:
386
+ """
387
+ Plot the cross-section of the component at a specified depth.
388
+
389
+ Parameters
390
+ ----------
391
+ figsize : tuple, optional
392
+ The figure size (width, height) in inches, by default (6.0, 6.0).
393
+ facecolor : tuple or str, optional
394
+ The face color of the cross-section, by default None.
395
+ axes : Axes
396
+ The axes object to plot the cross-section on.
397
+ axis_arg : bool or str or tuple, optional
398
+ The axis argument to pass to the `axes.axis()` function, by default "auto".
399
+ show : bool, optional
400
+ Whether to display the plot, by default True.
401
+
402
+ Returns
403
+ -------
404
+ Axes
405
+ The axes object to plot the cross-section on.
406
+ """
407
+ ...
408
+
409
+ @abstractmethod
410
+ def plot_side_view(
411
+ self,
412
+ bottom_boundary_nap: float | Literal["pile_tip"] = "pile_tip",
413
+ top_boundary_nap: float | Literal["pile_head"] = "pile_head",
414
+ pile_tip_level_nap: float | int = -10,
415
+ pile_head_level_nap: float | int = 0,
416
+ figsize: Tuple[float, float] = (6.0, 6.0),
417
+ facecolor: Tuple[float, float, float] | str | None = None,
418
+ axes: Axes | None = None,
419
+ axis_arg: bool | str | Tuple[float, float, float, float] | None = "scaled",
420
+ show: bool = True,
421
+ **kwargs: Any,
422
+ ) -> Axes:
423
+ """
424
+ Plot the side view of the cross-section at a specified depth.
425
+
426
+ Parameters
427
+ ----------
428
+ figsize : tuple, optional
429
+ The figure size (width, height) in inches, by default (6.0, 6.0).
430
+ facecolor : tuple or str, optional
431
+ The face color of the pile cross-section, by default None.
432
+ axes : Axes
433
+ The axes object to plot the cross-section on.
434
+ axis_arg : bool or str or tuple, optional
435
+ The axis argument to pass to the `axes.axis()` function, by default "auto".
436
+ show : bool, optional
437
+ Whether to display the plot, by default True.
438
+ **kwargs
439
+ Additional keyword arguments to pass to the `plt
440
+ """
441
+ ...
442
+
443
+
444
+ class PrimaryPileComponentDimension:
445
+ """
446
+ The PrimaryPileComponentDimension class represents the primary dimension of a pile-geometry component,
447
+ which is measured along the primary axis of the pile. It contains the length, top level, and bottom level
448
+ of the pile-geometry component.
449
+ """
450
+
451
+ def __init__(
452
+ self,
453
+ length: float | None = None,
454
+ ):
455
+ """
456
+ Represents the primary dimension of a pile-geometry component, which is measured
457
+ along the primary axis of the pile.
458
+
459
+ Args:
460
+ length: The length [m] of the pile-geometry component (along the primary axis).
461
+ """
462
+ self._length = length
463
+
464
+ @classmethod
465
+ def from_api_response(cls, primary_dim: dict) -> PrimaryPileComponentDimension:
466
+ """
467
+ Instantiates a PrimaryPileComponentDimension from a primary dimension object in the API response payload.
468
+
469
+ Args:
470
+ primary_dim: A dictionary that is retrieved from the API response payload at "/pile_properties/geometry/components/[i]/primary_dimension".
471
+ """
472
+ return cls(
473
+ length=primary_dim.get("length"),
474
+ )
475
+
476
+ @property
477
+ def length(self) -> float | None:
478
+ """The length [m] of the pile-geometry component"""
479
+ return self._length
480
+
481
+ def serialize_payload(self) -> Dict[str, float | None] | None:
482
+ """
483
+ Serialize the primary dimension to a dictionary payload for the API.
484
+
485
+ Only contains the length, since this is the only accepted value by the API.
486
+ Returns None if the length is not set.
487
+
488
+ Returns:
489
+ A dictionary payload containing the length of the primary dimension.
490
+ """
491
+ if self.length is not None:
492
+ return {"length": self.length}
493
+ return None