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.
- {py_pilecore-0.4.1.dist-info → py_pilecore-0.5.0.dist-info}/METADATA +2 -2
- py_pilecore-0.5.0.dist-info/RECORD +33 -0
- {py_pilecore-0.4.1.dist-info → py_pilecore-0.5.0.dist-info}/WHEEL +1 -1
- pypilecore/__init__.py +2 -0
- pypilecore/_version.py +1 -1
- pypilecore/api.py +26 -6
- pypilecore/common/__init__.py +0 -0
- pypilecore/common/piles/__init__.py +6 -0
- pypilecore/common/piles/geometry/__init__.py +18 -0
- pypilecore/common/piles/geometry/components/__init__.py +10 -0
- pypilecore/common/piles/geometry/components/common.py +493 -0
- pypilecore/common/piles/geometry/components/rectangle.py +521 -0
- pypilecore/common/piles/geometry/components/round.py +435 -0
- pypilecore/common/piles/geometry/main.py +335 -0
- pypilecore/common/piles/geometry/materials.py +173 -0
- pypilecore/common/piles/main.py +290 -0
- pypilecore/common/piles/type.py +196 -0
- pypilecore/input/__init__.py +0 -2
- pypilecore/input/multi_cpt.py +7 -119
- pypilecore/input/soil_properties.py +12 -1
- pypilecore/results/grouper_result.py +3 -2
- pypilecore/results/multi_cpt_results.py +171 -7
- py_pilecore-0.4.1.dist-info/RECORD +0 -24
- pypilecore/input/pile_properties.py +0 -213
- pypilecore/results/pile_properties.py +0 -891
- {py_pilecore-0.4.1.dist-info → py_pilecore-0.5.0.dist-info}/LICENSE +0 -0
- {py_pilecore-0.4.1.dist-info → py_pilecore-0.5.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Tuple
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from matplotlib import pyplot as plt
|
|
7
|
+
from matplotlib.axes import Axes
|
|
8
|
+
from numpy.typing import NDArray
|
|
9
|
+
|
|
10
|
+
from pypilecore.common.piles.geometry.components import (
|
|
11
|
+
RectPileGeometryComponent,
|
|
12
|
+
RoundPileGeometryComponent,
|
|
13
|
+
)
|
|
14
|
+
from pypilecore.common.piles.geometry.materials import Color, PileMaterial
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PileGeometry:
|
|
18
|
+
"""The PileGeometry class represents the geometry of a pile."""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
components: List[RoundPileGeometryComponent | RectPileGeometryComponent],
|
|
23
|
+
materials: List[PileMaterial] | None = None,
|
|
24
|
+
pile_tip_factor_s: float | None = None,
|
|
25
|
+
beta_p: float | None = None,
|
|
26
|
+
):
|
|
27
|
+
"""
|
|
28
|
+
Represents the geometry of a pile.
|
|
29
|
+
|
|
30
|
+
Parameters:
|
|
31
|
+
-----------
|
|
32
|
+
components : list
|
|
33
|
+
A list of pile geometry components.
|
|
34
|
+
materials : list, optional
|
|
35
|
+
A list of materials used in the pile geometry, by default None.
|
|
36
|
+
pile_tip_factor_s : float, optional
|
|
37
|
+
The pile tip factor S, by default None.
|
|
38
|
+
beta_p : float, optional
|
|
39
|
+
The beta_p value, by default None.
|
|
40
|
+
"""
|
|
41
|
+
self._components = components
|
|
42
|
+
self._materials = materials
|
|
43
|
+
self._pile_tip_factor_s = pile_tip_factor_s
|
|
44
|
+
self._beta_p = beta_p
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def from_api_response(cls, geometry: dict) -> PileGeometry:
|
|
48
|
+
"""
|
|
49
|
+
Instantiates a PileGeometry from a geometry object in the API response payload.
|
|
50
|
+
|
|
51
|
+
Parameters:
|
|
52
|
+
-----------
|
|
53
|
+
geometry: dict
|
|
54
|
+
A dictionary that is retrieved from the API response payload at "/pile_properties/geometry".
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
--------
|
|
58
|
+
PileGeometry
|
|
59
|
+
A pile geometry.
|
|
60
|
+
"""
|
|
61
|
+
components: List[RoundPileGeometryComponent | RectPileGeometryComponent] = []
|
|
62
|
+
for component in geometry["components"]:
|
|
63
|
+
if component["outer_shape"] == "round":
|
|
64
|
+
components.append(
|
|
65
|
+
RoundPileGeometryComponent.from_api_response(component)
|
|
66
|
+
)
|
|
67
|
+
else:
|
|
68
|
+
components.append(
|
|
69
|
+
RectPileGeometryComponent.from_api_response(component)
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
materials = []
|
|
73
|
+
if "materials" in geometry:
|
|
74
|
+
for material in geometry["materials"]:
|
|
75
|
+
materials.append(PileMaterial.from_api_response(material))
|
|
76
|
+
|
|
77
|
+
return cls(
|
|
78
|
+
components=components,
|
|
79
|
+
materials=materials,
|
|
80
|
+
pile_tip_factor_s=geometry["properties"].get("pile_tip_factor_s"),
|
|
81
|
+
beta_p=geometry["properties"]["beta_p"],
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def components(
|
|
86
|
+
self,
|
|
87
|
+
) -> List[RoundPileGeometryComponent | RectPileGeometryComponent]:
|
|
88
|
+
"""The components of the pile geometry"""
|
|
89
|
+
return self._components
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def materials(self) -> List[PileMaterial]:
|
|
93
|
+
"""The materials used in the pile geometry"""
|
|
94
|
+
return self._materials if self._materials is not None else []
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def materials_dict(self) -> Dict[str, PileMaterial]:
|
|
98
|
+
"""The materials used in the pile geometry as a dictionary with the material name as key"""
|
|
99
|
+
return {material.name: material for material in self.materials}
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def pile_tip_factor_s(self) -> float | None:
|
|
103
|
+
"""The pile tip factor S of the pile geometry"""
|
|
104
|
+
return self._pile_tip_factor_s
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def beta_p(self) -> float | None:
|
|
108
|
+
"""The beta_p value of the pile geometry"""
|
|
109
|
+
return self._beta_p
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def equiv_diameter_pile_tip(self) -> float:
|
|
113
|
+
"""The equivalent diameter of the pile at the pile tip."""
|
|
114
|
+
return self.components[-1].equiv_tip_diameter
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def circumference_pile_tip(self) -> float:
|
|
118
|
+
"""The outer-circumference of the pile at the pile tip."""
|
|
119
|
+
return self.components[-1].circumference
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def area_pile_tip(self) -> float:
|
|
123
|
+
"""The area of the pile at the pile tip."""
|
|
124
|
+
return self.components[-1].area_full
|
|
125
|
+
|
|
126
|
+
def serialize_payload(self) -> Dict[str, list | dict]:
|
|
127
|
+
"""
|
|
128
|
+
Serialize the pile geometry to a dictionary payload for the API.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
A dictionary payload containing the components, materials (if set), pile tip factor S (if set), and beta_p (if set).
|
|
132
|
+
"""
|
|
133
|
+
components = [component.serialize_payload() for component in self.components]
|
|
134
|
+
payload: Dict[str, Any] = {"components": components}
|
|
135
|
+
|
|
136
|
+
if self.materials is not None and len(self.materials) > 0:
|
|
137
|
+
materials = [material.serialize_payload() for material in self.materials]
|
|
138
|
+
payload["materials"] = materials
|
|
139
|
+
|
|
140
|
+
custom_geom_properties: Dict[str, float] = {}
|
|
141
|
+
if self.pile_tip_factor_s is not None:
|
|
142
|
+
custom_geom_properties["pile_tip_factor_s"] = self.pile_tip_factor_s
|
|
143
|
+
|
|
144
|
+
if self.beta_p is not None:
|
|
145
|
+
custom_geom_properties["beta_p"] = self.beta_p
|
|
146
|
+
|
|
147
|
+
if len(custom_geom_properties.keys()) > 0:
|
|
148
|
+
payload["custom_properties"] = custom_geom_properties
|
|
149
|
+
|
|
150
|
+
return payload
|
|
151
|
+
|
|
152
|
+
def get_circum_vs_depth(
|
|
153
|
+
self,
|
|
154
|
+
pile_tip_level_nap: float | int,
|
|
155
|
+
pile_head_level_nap: float | int,
|
|
156
|
+
depth_nap: NDArray[np.floating],
|
|
157
|
+
) -> NDArray[np.floating]:
|
|
158
|
+
"""
|
|
159
|
+
Returns pile circumferences at requested depths.
|
|
160
|
+
|
|
161
|
+
Parameters
|
|
162
|
+
---------_
|
|
163
|
+
pile_tip_level_nap : float
|
|
164
|
+
Pile tip level in [m] w.r.t. NAP.
|
|
165
|
+
pile_head_level_nap : float
|
|
166
|
+
Pile head level in [m] w.r.t. NAP.
|
|
167
|
+
depth_nap : np.array
|
|
168
|
+
Array with depths in [m] w.r.t. NAP.
|
|
169
|
+
|
|
170
|
+
Returns
|
|
171
|
+
-------
|
|
172
|
+
np.array
|
|
173
|
+
Array with pile circumferences at the requested `depth_nap` levels.
|
|
174
|
+
"""
|
|
175
|
+
circum_vs_depth = np.zeros_like(depth_nap)
|
|
176
|
+
|
|
177
|
+
# Use the maximum circumference of all components at each depth.
|
|
178
|
+
for component in self.components:
|
|
179
|
+
circum_vs_depth = np.maximum(
|
|
180
|
+
circum_vs_depth,
|
|
181
|
+
component.get_circum_vs_depth(
|
|
182
|
+
pile_tip_level_nap=pile_tip_level_nap,
|
|
183
|
+
pile_head_level_nap=pile_head_level_nap,
|
|
184
|
+
depth_nap=depth_nap,
|
|
185
|
+
),
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
return circum_vs_depth
|
|
189
|
+
|
|
190
|
+
def get_area_vs_depth(
|
|
191
|
+
self,
|
|
192
|
+
pile_tip_level_nap: float | int,
|
|
193
|
+
pile_head_level_nap: float | int,
|
|
194
|
+
depth_nap: NDArray[np.floating],
|
|
195
|
+
) -> NDArray[np.floating]:
|
|
196
|
+
"""
|
|
197
|
+
Returns cross-sectional area of the pile at requested depths.
|
|
198
|
+
|
|
199
|
+
Parameters
|
|
200
|
+
---------_
|
|
201
|
+
pile_tip_level_nap : float
|
|
202
|
+
Pile tip level in [m] w.r.t. NAP.
|
|
203
|
+
pile_head_level_nap : float
|
|
204
|
+
Pile head level in [m] w.r.t. NAP.
|
|
205
|
+
depth_nap : np.array
|
|
206
|
+
Array with depths in [m] w.r.t. NAP.
|
|
207
|
+
|
|
208
|
+
Returns
|
|
209
|
+
-------
|
|
210
|
+
np.array
|
|
211
|
+
Array with pile areas at the requested `depth_nap` levels.
|
|
212
|
+
"""
|
|
213
|
+
area_vs_depth = np.zeros_like(depth_nap)
|
|
214
|
+
|
|
215
|
+
# Use the maximum area of all components at each depth.
|
|
216
|
+
for component in self.components:
|
|
217
|
+
area_vs_depth = np.maximum(
|
|
218
|
+
area_vs_depth,
|
|
219
|
+
component.get_area_vs_depth(
|
|
220
|
+
pile_tip_level_nap=pile_tip_level_nap,
|
|
221
|
+
pile_head_level_nap=pile_head_level_nap,
|
|
222
|
+
depth_nap=depth_nap,
|
|
223
|
+
),
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
return area_vs_depth
|
|
227
|
+
|
|
228
|
+
def plot(
|
|
229
|
+
self,
|
|
230
|
+
pile_tip_level_nap: float | int = -10,
|
|
231
|
+
pile_head_level_nap: float | int = 0,
|
|
232
|
+
figsize: Tuple[float, float] | None = (3, 9),
|
|
233
|
+
show: bool = True,
|
|
234
|
+
**kwargs: Any,
|
|
235
|
+
) -> List[Axes]:
|
|
236
|
+
"""
|
|
237
|
+
Plot the top-view of the pile at a specified depth.
|
|
238
|
+
|
|
239
|
+
Parameters
|
|
240
|
+
----------
|
|
241
|
+
pile_tip_level_nap : float, optional
|
|
242
|
+
The pile tip level in m w.r.t. NAP, by default -10.
|
|
243
|
+
pile_head_level_nap : float, optional
|
|
244
|
+
The pile head level in m w.r.t. NAP, by default 0.
|
|
245
|
+
figsize : tuple, optional
|
|
246
|
+
The figure size (width, height) in inches, by default (6.0, 6.0).
|
|
247
|
+
show : bool, optional
|
|
248
|
+
Whether to display the plot, by default True.
|
|
249
|
+
**kwargs
|
|
250
|
+
Additional keyword arguments to pass to the `plt
|
|
251
|
+
"""
|
|
252
|
+
kwargs_subplot = {
|
|
253
|
+
"figsize": figsize,
|
|
254
|
+
"gridspec_kw": {
|
|
255
|
+
"hspace": 0.15,
|
|
256
|
+
"height_ratios": [1, 4],
|
|
257
|
+
},
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
kwargs_subplot.update(kwargs)
|
|
261
|
+
|
|
262
|
+
height_ratio = (
|
|
263
|
+
kwargs_subplot["gridspec_kw"]["height_ratios"][1] # type: ignore
|
|
264
|
+
/ kwargs_subplot["gridspec_kw"]["height_ratios"][0] # type: ignore
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
_, axes = plt.subplots(
|
|
268
|
+
2,
|
|
269
|
+
1,
|
|
270
|
+
**kwargs_subplot,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
x_ticks = set([0.0])
|
|
274
|
+
y_ticks = set([0.0])
|
|
275
|
+
|
|
276
|
+
for component in self.components[::-1]:
|
|
277
|
+
facecolor = "grey"
|
|
278
|
+
if component.material in self.materials_dict:
|
|
279
|
+
material_color = self.materials_dict[component.material].color
|
|
280
|
+
if isinstance(material_color, Color):
|
|
281
|
+
facecolor = material_color.hex
|
|
282
|
+
elif isinstance(material_color, Color):
|
|
283
|
+
facecolor = (
|
|
284
|
+
material_color.red,
|
|
285
|
+
material_color.green,
|
|
286
|
+
material_color.blue,
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
component.plot_cross_section_exterior(
|
|
290
|
+
axes=axes[0],
|
|
291
|
+
facecolor=facecolor,
|
|
292
|
+
edgecolor="black",
|
|
293
|
+
axis_arg=None,
|
|
294
|
+
show=False,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
component.plot_side_view(
|
|
298
|
+
pile_tip_level_nap=pile_tip_level_nap,
|
|
299
|
+
pile_head_level_nap=pile_head_level_nap,
|
|
300
|
+
axes=axes[1],
|
|
301
|
+
facecolor=facecolor,
|
|
302
|
+
axis_arg=None,
|
|
303
|
+
show=False,
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
x_ticks.add(component.cross_section_bounds[0])
|
|
307
|
+
x_ticks.add(component.cross_section_bounds[1])
|
|
308
|
+
y_ticks.add(component.cross_section_bounds[2])
|
|
309
|
+
y_ticks.add(component.cross_section_bounds[3])
|
|
310
|
+
axes[0].axis("scaled")
|
|
311
|
+
axes[0].set(aspect=1)
|
|
312
|
+
axes[1].axis("auto")
|
|
313
|
+
ax1_aspect = (
|
|
314
|
+
abs(axes[0].axis()[2] - axes[0].axis()[3])
|
|
315
|
+
/ abs(axes[1].axis()[2] - axes[1].axis()[3])
|
|
316
|
+
* height_ratio
|
|
317
|
+
)
|
|
318
|
+
axes[1].set(aspect=ax1_aspect)
|
|
319
|
+
|
|
320
|
+
axes[0].spines[:].set_visible(False)
|
|
321
|
+
axes[1].spines[:].set_visible(False)
|
|
322
|
+
|
|
323
|
+
axes[0].set_xticks(ticks=list(x_ticks))
|
|
324
|
+
axes[0].set_yticks(ticks=list(y_ticks))
|
|
325
|
+
axes[1].set_xticks(ticks=list(x_ticks))
|
|
326
|
+
axes[1].set_yticks(
|
|
327
|
+
ticks=[pile_head_level_nap, pile_tip_level_nap],
|
|
328
|
+
labels=["Pile Head", "Pile Tip"],
|
|
329
|
+
)
|
|
330
|
+
axes[0].tick_params(axis="x", labelrotation=45)
|
|
331
|
+
axes[1].tick_params(axis="x", labelrotation=45)
|
|
332
|
+
|
|
333
|
+
if show:
|
|
334
|
+
plt.show()
|
|
335
|
+
return axes
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Dict
|
|
4
|
+
|
|
5
|
+
MATERIALS = {
|
|
6
|
+
"concrete": {"name": "concrete", "elastic_modulus": 20000, "color": "#525252"},
|
|
7
|
+
"steel": {"name": "steel", "elastic_modulus": 195000, "color": "#E2E2E2"},
|
|
8
|
+
"wood": {"name": "wood", "elastic_modulus": 3600, "color": "#BD7205"},
|
|
9
|
+
"grout": {
|
|
10
|
+
"name": "grout",
|
|
11
|
+
"elastic_modulus": 15000,
|
|
12
|
+
"yield_stress": 1.5,
|
|
13
|
+
"color": "#8A8A8A",
|
|
14
|
+
},
|
|
15
|
+
"grout_extorted": {
|
|
16
|
+
"name": "grout_extorted",
|
|
17
|
+
"elastic_modulus": 20000,
|
|
18
|
+
"yield_stress": 2,
|
|
19
|
+
"color": "#8A8A8A",
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Color:
|
|
25
|
+
"""The Color class represents an RGB color."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, red: int, green: int, blue: int):
|
|
28
|
+
"""
|
|
29
|
+
Represents an RGB color.
|
|
30
|
+
|
|
31
|
+
Parameters:
|
|
32
|
+
-----------
|
|
33
|
+
red : int
|
|
34
|
+
The red component of the RGB color.
|
|
35
|
+
green : int
|
|
36
|
+
The green component of the RGB color.
|
|
37
|
+
blue : int
|
|
38
|
+
The blue component of the RGB color.
|
|
39
|
+
"""
|
|
40
|
+
self.red = red
|
|
41
|
+
self.green = green
|
|
42
|
+
self.blue = blue
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def from_hex(cls, hex: str) -> Color:
|
|
46
|
+
"""
|
|
47
|
+
Instantiate a Color object from a hexadecimal color string.
|
|
48
|
+
|
|
49
|
+
Parameters:
|
|
50
|
+
-----------
|
|
51
|
+
hex : str
|
|
52
|
+
The hexadecimal representation of the RGB color.
|
|
53
|
+
example: "#ff0000" for red.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
--------
|
|
57
|
+
Color
|
|
58
|
+
A Color object.
|
|
59
|
+
"""
|
|
60
|
+
hex = hex.lstrip("#")
|
|
61
|
+
return cls(
|
|
62
|
+
red=int(hex[0:2], 16),
|
|
63
|
+
green=int(hex[2:4], 16),
|
|
64
|
+
blue=int(hex[4:6], 16),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def hex(self) -> str:
|
|
69
|
+
"""The hexadecimal representation of the RGB color. e.g. "#ff0000" for red."""
|
|
70
|
+
return "#{:02x}{:02x}{:02x}".format(self.red, self.green, self.blue)
|
|
71
|
+
|
|
72
|
+
def serialize_payload(self) -> Dict[str, int]:
|
|
73
|
+
"""Serialize the RGB color to a dictionary payload for the API."""
|
|
74
|
+
return {"r": self.red, "g": self.green, "b": self.blue}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class PileMaterial:
|
|
78
|
+
"""The PileMaterial class represents a material that can be used in a pile geometry component."""
|
|
79
|
+
|
|
80
|
+
def __init__(
|
|
81
|
+
self,
|
|
82
|
+
name: str,
|
|
83
|
+
elastic_modulus: float,
|
|
84
|
+
yield_stress: float | None = None,
|
|
85
|
+
color: Color | str | Dict[str, int] | None = None,
|
|
86
|
+
):
|
|
87
|
+
"""
|
|
88
|
+
Represents a material that can be used in a pile geometry component.
|
|
89
|
+
|
|
90
|
+
Parameters:
|
|
91
|
+
-----------
|
|
92
|
+
name : str
|
|
93
|
+
The name of the material.
|
|
94
|
+
elastic_modulus : float
|
|
95
|
+
The elastic modulus [MPa] of the material.
|
|
96
|
+
yield_stress : float, optional
|
|
97
|
+
The yield stress [MPa] of the material, by default None.
|
|
98
|
+
color : Color or str or dict, optional
|
|
99
|
+
The color of the material, by default None.
|
|
100
|
+
"""
|
|
101
|
+
self._name = name
|
|
102
|
+
self._elastic_modulus = elastic_modulus
|
|
103
|
+
self._yield_stress = yield_stress
|
|
104
|
+
if isinstance(color, str):
|
|
105
|
+
color = Color.from_hex(hex=color)
|
|
106
|
+
elif isinstance(color, dict):
|
|
107
|
+
color = Color(
|
|
108
|
+
red=color["r"],
|
|
109
|
+
green=color["g"],
|
|
110
|
+
blue=color["b"],
|
|
111
|
+
)
|
|
112
|
+
self._color = color
|
|
113
|
+
|
|
114
|
+
@classmethod
|
|
115
|
+
def from_api_response(cls, material: dict) -> PileMaterial:
|
|
116
|
+
"""
|
|
117
|
+
Instantiates a PileMaterial from a material object in the API response payload.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
material: A dictionary that is retrieved from the API response payload at "/pile_properties/geometry/materials/[i]".
|
|
121
|
+
"""
|
|
122
|
+
if isinstance(material["color"], str):
|
|
123
|
+
color = Color.from_hex(hex=material["color"])
|
|
124
|
+
else:
|
|
125
|
+
color = Color(
|
|
126
|
+
red=material["color"]["r"],
|
|
127
|
+
green=material["color"]["g"],
|
|
128
|
+
blue=material["color"]["b"],
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
return cls(
|
|
132
|
+
name=material["name"],
|
|
133
|
+
elastic_modulus=material["elastic_modulus"],
|
|
134
|
+
yield_stress=material.get("yield_stress"),
|
|
135
|
+
color=color,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def name(self) -> str:
|
|
140
|
+
"""The name of the material"""
|
|
141
|
+
return self._name
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def elastic_modulus(self) -> float:
|
|
145
|
+
"""The elastic modulus [MPa] of the material"""
|
|
146
|
+
return self._elastic_modulus
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def yield_stress(self) -> float | None:
|
|
150
|
+
"""The yield stress [MPa] of the material"""
|
|
151
|
+
return self._yield_stress
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def color(self) -> Color | Color | None:
|
|
155
|
+
"""The color of the material"""
|
|
156
|
+
return self._color
|
|
157
|
+
|
|
158
|
+
def serialize_payload(self) -> Dict[str, str | float | Dict[str, int]]:
|
|
159
|
+
"""
|
|
160
|
+
Serialize the material to a dictionary payload for the API.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
A dictionary payload containing the material's name, elastic modulus, and color.
|
|
164
|
+
"""
|
|
165
|
+
payload: Dict[str, str | float | Dict[str, int]] = {
|
|
166
|
+
"name": self.name,
|
|
167
|
+
"elastic_modulus": self.elastic_modulus,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if self.color is not None:
|
|
171
|
+
payload["color"] = self.color.serialize_payload()
|
|
172
|
+
|
|
173
|
+
return payload
|