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

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: py-pilecore
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Public python SDK for the CEMS PileCore web-API.
5
5
  License: MIT License
6
6
 
@@ -37,6 +37,7 @@ Requires-Dist: matplotlib <4,>=3.8
37
37
  Requires-Dist: tqdm[notebook] <5,>4
38
38
  Requires-Dist: natsort <9,>8
39
39
  Requires-Dist: shapely <3,>=2
40
+ Requires-Dist: scipy <2,>=1
40
41
  Provides-Extra: docs
41
42
  Requires-Dist: Sphinx ==6.1.3 ; extra == 'docs'
42
43
  Requires-Dist: sphinx-autodoc-typehints ==1.22 ; extra == 'docs'
@@ -1,24 +1,24 @@
1
1
  pypilecore/__init__.py,sha256=T2Uuao6fboVhrzKI_Sa7jXbcNAUl_PKk9BFdsozOG98,78
2
- pypilecore/_version.py,sha256=c8cd26QQ6zmNw4FZZlzQGhINwVjZ_hWaE2xv6ELt5Nw,175
2
+ pypilecore/_version.py,sha256=0_HYvCjqXJ8bLPFLe6E2tkYlW15w7EOTtTD9FQ5jQGM,175
3
3
  pypilecore/api.py,sha256=dXgjfttEUcQHUCMkTX9eYip0dd3bf_ppE6cdx87ouHY,6822
4
4
  pypilecore/exceptions.py,sha256=-MZOfsxyHLCI0k1-wZFfVsMxc1lya5buuhLks5rxlCo,89
5
5
  pypilecore/plot_utils.py,sha256=rK5_067-4-x7LzZgt_t6ahcGrZInxNrqHqsy0RzCnq8,954
6
6
  pypilecore/utils.py,sha256=ib9LgJBIgWukL7zd_Zk1LP27UTMIZTRJ4RBB6ubn97o,1186
7
7
  pypilecore/input/__init__.py,sha256=tlmThdPtO8e6L6pqxuRQ7EOHRxYwuIcaNNGlZyAnzig,606
8
- pypilecore/input/grouper_properties.py,sha256=AqZHvg-CXcoTW7u9wgyqSm1oZLBc7sSoTawxGK69144,10895
9
- pypilecore/input/multi_cpt.py,sha256=1gEK-Imdhr0xOfcn50lq1OwYvjFs2NMf51LDaVddGas,19539
10
- pypilecore/input/pile_properties.py,sha256=mK8_VRy-tHZCKFHcv8ftMlCEs8KEsZpG9eGLq13hYPA,8400
11
- pypilecore/input/soil_properties.py,sha256=PlAutpueCzd5WHkEau16gVai7CT2j3kEAKVHxllMRz8,7688
8
+ pypilecore/input/grouper_properties.py,sha256=qOc-SNYSPdOcZZ9DsRE-sBNVOKHbUD044EWRsWDu_kc,10893
9
+ pypilecore/input/multi_cpt.py,sha256=Ua5IN9-m1jgCwa5xFNAYrMcxkVe2L1LIW907CWLJBW0,19577
10
+ pypilecore/input/pile_properties.py,sha256=6O7iH-f7_G9PZbBsbzSWT_zUF2RtwYLfF5GRtUFvhHk,8404
11
+ pypilecore/input/soil_properties.py,sha256=oqZWeSvhGyIKl4j0ZlGr923qyxkBjYxbVKBckG2rLAw,7687
12
12
  pypilecore/results/__init__.py,sha256=ggTI2QzILhX_oNx1YMOih6IVCkBFg8I5-Jyn9Sw3_h0,389
13
- pypilecore/results/grouper_result.py,sha256=tJl6NEdaFQKgx4W62P3dO5dNlOtf5OqZISTfNY35kSs,30903
13
+ pypilecore/results/grouper_result.py,sha256=wQxWSJRrgh7aO5pglrCNTPvCNdERBMDXAlm6YZE3v-s,30927
14
14
  pypilecore/results/load_settlement.py,sha256=EbfTrSvH_g96KE-x8ZjmO8D0mt5KFaQ_-AR8u4blLsU,9752
15
15
  pypilecore/results/multi_cpt_results.py,sha256=dzNpP3hu0Sl00gNPHT37DcQqLbfp8IYci46QLSycyqA,25398
16
16
  pypilecore/results/pile_properties.py,sha256=z1R5UNoYjBem2rS8LMJ_ye2J5ejDoSilU5fCjUrNdUg,28592
17
- pypilecore/results/post_processing.py,sha256=ddIcbb3j5tva17F7hnkspDB6nd-D2_qlNmuJP_0vq10,15580
17
+ pypilecore/results/post_processing.py,sha256=UWXcdff5dhPFDwzKbVIayEIp3HX6pxW8oQR3Z7AHPn0,22262
18
18
  pypilecore/results/single_cpt_results.py,sha256=irNqsL_cBKUh-QEmWC216KZjkv9MAZkaiLTvDDlEt20,17448
19
19
  pypilecore/results/soil_properties.py,sha256=Mcwz23PcaIW1xKLabMUSfIeKCTBli7ouuZPfT5dfrb4,20722
20
- py_pilecore-0.4.0.dist-info/LICENSE,sha256=3OCAZXffN0Bettjeya8uF_ZYegyvvCfH1WUt6CrHb_0,1061
21
- py_pilecore-0.4.0.dist-info/METADATA,sha256=QJEV5dvj3JqCrudtj9XSefcu0wGqr4TXjIjsJJXgtz8,5806
22
- py_pilecore-0.4.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
23
- py_pilecore-0.4.0.dist-info/top_level.txt,sha256=7BKIWZuSkbQtJ0ho5P1JvcaEbHzqADCcBuOduZmIaiI,11
24
- py_pilecore-0.4.0.dist-info/RECORD,,
20
+ py_pilecore-0.4.1.dist-info/LICENSE,sha256=3OCAZXffN0Bettjeya8uF_ZYegyvvCfH1WUt6CrHb_0,1061
21
+ py_pilecore-0.4.1.dist-info/METADATA,sha256=oQzHxa-c_UofjtUdg5FCaFWsqvanEgxCxuAkn3M_A3g,5834
22
+ py_pilecore-0.4.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
23
+ py_pilecore-0.4.1.dist-info/top_level.txt,sha256=7BKIWZuSkbQtJ0ho5P1JvcaEbHzqADCcBuOduZmIaiI,11
24
+ py_pilecore-0.4.1.dist-info/RECORD,,
pypilecore/_version.py CHANGED
@@ -4,4 +4,4 @@ try:
4
4
  __version__ = version("py-pilecore")
5
5
  # during CI
6
6
  except PackageNotFoundError:
7
- __version__ = "0.4.0"
7
+ __version__ = "0.4.1"
@@ -43,7 +43,7 @@ def create_grouper_payload(
43
43
  "/grouper/group_cpts"
44
44
 
45
45
 
46
- Notes
46
+ Note
47
47
  ------
48
48
  The grouper uses pile bearing capacity results calculated by PileCore or other software to
49
49
  form subgroups of the total group of CPT’s belonging to this project.
@@ -56,7 +56,6 @@ def create_grouper_payload(
56
56
  - is spatially coherent, which means there are no other CPTs in between the members
57
57
  of the subgroup. (Spatial check)
58
58
 
59
-
60
59
  Additionally, centre to centre validation (include_centre_to_centre_check; NEN9997-1 3.2.3) can be
61
60
  added to the cluster method. This check adds restrictions to the maximum allowable R;c;cal outliers
62
61
  and makes sure that the suitable data density requirements for the subgroup are met, by checking the
@@ -344,7 +344,7 @@ def create_multi_cpt_payload(
344
344
  pile_head_level_nap=pile_head_level_nap,
345
345
  stiff_construction=stiff_construction,
346
346
  rel_pile_load=relative_pile_load,
347
- soil_load=soil_load_sls,
347
+ soil_load=soil_load_sls if soil_load_sls is not None else 0.0,
348
348
  excavation_param_t=excavation_param_t,
349
349
  use_almere_rules=use_almere_rules,
350
350
  gamma_f_nk=gamma_f_nk,
@@ -29,8 +29,9 @@ def create_pile_properties_payload(
29
29
  Creates a dictionary with the `pile_properties` payload content for the PileCore
30
30
  endpoints.
31
31
 
32
- Note that
33
- the dictionary should be converted to a jsonifyable message before it can be passed
32
+ Note
33
+ ----
34
+ The dictionary should be converted to a jsonifyable message before it can be passed
34
35
  to a `requests` call directly, for instance with
35
36
  `nuclei.client.utils.python_types_to_message()`.
36
37
 
@@ -35,7 +35,7 @@ def create_soil_properties_payload(
35
35
  Creates a dictionary with the `soil_properties` payload content for the PileCore
36
36
  endpoints.
37
37
 
38
- Notes
38
+ Note
39
39
  ------
40
40
  the dictionary should be converted to a jsonifyable message before it can be passed
41
41
  to a `requests` call directly, for instance with
@@ -211,7 +211,7 @@ class SingleClusterData:
211
211
  """
212
212
  Plot the bearing capacity and variation coefficient in a subplot
213
213
 
214
- Notes
214
+ Note
215
215
  ------
216
216
  For the `Net bearing capacity` subplot there are two colors plotted:
217
217
  - orange: conservative bearing capacity
@@ -251,7 +251,7 @@ class SingleClusterData:
251
251
  """
252
252
  Plot the spacing checks in a subplot
253
253
 
254
- Notes
254
+ Note
255
255
  ------
256
256
  For the `spacing` subplot there are two colors plotted:
257
257
  - red: invalid spacing
@@ -308,7 +308,7 @@ class SingleClusterData:
308
308
  """
309
309
  Plot the xi factor in a subplot
310
310
 
311
- Notes
311
+ Note
312
312
  ------
313
313
  For the `xi factor` subplot there are two colors plotted:
314
314
  - olive: xi3
@@ -499,7 +499,7 @@ class SingleClusterResult:
499
499
  - xi factor
500
500
  - centre to centre validation
501
501
 
502
- Notes
502
+ Note
503
503
  ------
504
504
  For the `Net bearing capacity` subplot there are two colors plotted:
505
505
  - orange: conservative bearing capacity
@@ -775,6 +775,9 @@ class GrouperResults:
775
775
  ) -> plt.Figure:
776
776
  """
777
777
  Plot a summary of the valid subgroups.
778
+
779
+ Note
780
+ -----
778
781
  Plot contains the:
779
782
 
780
783
  - cpts within a subgroup
@@ -9,8 +9,10 @@ import numpy as np
9
9
  import pandas as pd
10
10
  from matplotlib import pyplot as plt
11
11
  from matplotlib.axes import Axes
12
+ from matplotlib.collections import PatchCollection
12
13
  from matplotlib.figure import Figure
13
14
  from numpy.typing import NDArray
15
+ from scipy.spatial import Delaunay, Voronoi, voronoi_plot_2d
14
16
 
15
17
  from pypilecore.results.soil_properties import SoilProperties, get_soil_layer_handles
16
18
 
@@ -94,7 +96,7 @@ class MaxBearingResult:
94
96
  The object with soil properties
95
97
  pile_head_level_nap
96
98
  The elevation of the pile-head, in [m] w.r.t. NAP.
97
- results_table
99
+ table
98
100
  The object with CPT results.
99
101
  """
100
102
 
@@ -371,6 +373,59 @@ class MaxBearingResults:
371
373
 
372
374
  return cpt_results_df
373
375
 
376
+ @lru_cache()
377
+ def triangulation(self, pile_tip_level_nap: float) -> List[Dict[str, list]]:
378
+ """
379
+ Delaunay tessellation based on the CPT location
380
+
381
+ Returns
382
+ -------
383
+ collection: List
384
+ A list of dictionaries containing the tessellation
385
+ geometry and corresponding cpt names:
386
+
387
+ - geometry: List[Tuple[float, float]]
388
+ - test_id: List[str]
389
+
390
+ """
391
+ _lookup = {
392
+ (point.soil_properties.x, point.soil_properties.y): key
393
+ for key, point in self.cpt_results_dict.items()
394
+ }
395
+ # select point with valid bearing capacity at pile tip level
396
+ _points = (
397
+ self.to_pandas()
398
+ .loc[
399
+ (self.to_pandas()["pile_tip_level_nap"] == pile_tip_level_nap)
400
+ & (~pd.isna(self.to_pandas()["R_c_d_net"])),
401
+ ["x", "y"],
402
+ ]
403
+ .to_numpy()
404
+ .tolist()
405
+ )
406
+
407
+ # check if enough points Delaunay
408
+ if len(_points) < 4:
409
+ raise ValueError(
410
+ "Not enough points at this pile tip level to construct "
411
+ "the delaunay tessellation based on the CPT location."
412
+ )
413
+ tri = Delaunay(
414
+ _points,
415
+ incremental=False,
416
+ furthest_site=False,
417
+ qhull_options="Qbb",
418
+ )
419
+ geometries = np.array(_points)[tri.simplices]
420
+
421
+ return [
422
+ {
423
+ "geometry": geometry.tolist(),
424
+ "test_id": [_lookup[(xy[0], xy[1])] for xy in geometry],
425
+ }
426
+ for geometry in geometries
427
+ ]
428
+
374
429
  def plot(
375
430
  self,
376
431
  projection: Optional[Literal["3d"]] = "3d",
@@ -413,20 +468,20 @@ class MaxBearingResults:
413
468
  kwargs_subplot.update(kwargs)
414
469
  fig = plt.figure(**kwargs_subplot)
415
470
  axes = fig.add_subplot(projection=projection)
471
+ df = self.to_pandas().dropna()
416
472
  # create color list based on hue option
417
473
  if hue == "category":
418
474
  colors = [
419
- "red" if var < pile_load_uls else "green"
420
- for var in self.to_pandas()["R_c_d_net"]
475
+ "red" if var < pile_load_uls else "green" for var in df["R_c_d_net"]
421
476
  ]
422
477
  else:
423
- colors = self.to_pandas()["R_c_d_net"].tolist()
478
+ colors = df["R_c_d_net"].tolist()
424
479
  # create scatter plot
425
480
  if projection == "3d":
426
481
  cmap = axes.scatter(
427
- self.to_pandas()["x"],
428
- self.to_pandas()["y"],
429
- self.to_pandas()["pile_tip_level_nap"],
482
+ df["x"],
483
+ df["y"],
484
+ df["pile_tip_level_nap"],
430
485
  c=colors,
431
486
  )
432
487
  axes.set_xlabel("X")
@@ -444,12 +499,13 @@ class MaxBearingResults:
444
499
  )
445
500
  else:
446
501
  cmap = axes.scatter(
447
- self.to_pandas()["test_id"],
448
- self.to_pandas()["pile_tip_level_nap"],
502
+ df["test_id"],
503
+ df["pile_tip_level_nap"],
449
504
  c=colors,
450
505
  )
451
506
  axes.set_ylabel("Z [m w.r.t NAP]")
452
507
  axes.tick_params(axis="x", labelrotation=90)
508
+ axes.grid()
453
509
 
454
510
  if hue == "category":
455
511
  fig.legend(
@@ -475,3 +531,149 @@ class MaxBearingResults:
475
531
  fig.colorbar(cmap, orientation="vertical", label="$R_{c;d;net}$ [kN]")
476
532
 
477
533
  return fig
534
+
535
+ def map(
536
+ self,
537
+ pile_tip_level_nap: float,
538
+ pile_load_uls: float = 100,
539
+ show_delaunay_vertices: bool = True,
540
+ show_voronoi_vertices: bool = False,
541
+ figsize: Tuple[int, int] | None = None,
542
+ **kwargs: Any,
543
+ ) -> plt.Figure:
544
+ """
545
+ Plot a map of the valid ULS load for a given depth.
546
+
547
+ Note
548
+ ------
549
+ Based on the Delaunay methode a tessellation is created with
550
+ the location of the CPT's. Each triangle is then colored according to
551
+ the bearing capacity of the CPT its based on. If any of the CPT does
552
+ not meet the required capacity the triangle becomes also invalid.
553
+
554
+ Warnings
555
+ --------
556
+ Please note that this map indication of valid ULS zones is intended as a visual aid to help
557
+ the geotechnical engineer. It does not necessarily comply with the NEN 9997-1+C2:2017 since the NEN is open
558
+ to interpretation. It is therefore that the interpretation provided by this methode must be carefully
559
+ validated by a geotechnical engineer.
560
+
561
+ Parameters
562
+ ----------
563
+ pile_tip_level_nap:
564
+ Pile tip level to generate map.
565
+ pile_load_uls
566
+ default is 100 kN
567
+ ULS load in kN. Used to determine if a pile tip level configuration is valid.
568
+ show_delaunay_vertices
569
+ default is True
570
+ Add delaunay vertices to the figure
571
+ show_voronoi_vertices
572
+ default is False
573
+ Add voronoi vertices to the figure
574
+ figsize:
575
+ Size of the activate figure, as the `plt.figure()` argument.
576
+ **kwargs:
577
+ All additional keyword arguments are passed to the `pyplot.subplots()` call.
578
+
579
+ Returns
580
+ -------
581
+ figure:
582
+ The `Figure` object where the data was plotted on.
583
+ """
584
+ kwargs_subplot = {
585
+ "figsize": figsize,
586
+ "tight_layout": True,
587
+ }
588
+
589
+ kwargs_subplot.update(kwargs)
590
+ fig, axes = plt.subplots(**kwargs_subplot)
591
+
592
+ # filter data
593
+ df = (
594
+ self.to_pandas()
595
+ .loc[self.to_pandas()["pile_tip_level_nap"] == pile_tip_level_nap]
596
+ .dropna()
597
+ )
598
+
599
+ if df.empty:
600
+ raise ValueError(
601
+ "Pile tip level is not valid pile tip level. "
602
+ "Please select one of the following pile tip level: "
603
+ f"[{(self.to_pandas()['pile_tip_level_nap']).unique()}]"
604
+ )
605
+
606
+ df["valid"] = [
607
+ False if var < pile_load_uls else True for var in df["R_c_d_net"]
608
+ ]
609
+
610
+ # iterate over geometry
611
+ if show_delaunay_vertices:
612
+ _patches = []
613
+ for tri in self.triangulation(pile_tip_level_nap):
614
+ color = (
615
+ "green"
616
+ if all(
617
+ df.where(df["test_id"].isin(tri["test_id"])).dropna()["valid"]
618
+ )
619
+ else "red"
620
+ )
621
+ _patches.append(
622
+ patches.Polygon(
623
+ np.array(tri["geometry"]), facecolor=color, edgecolor="grey"
624
+ )
625
+ )
626
+
627
+ collection = PatchCollection(_patches, match_original=True)
628
+ axes.add_collection(collection)
629
+
630
+ if show_voronoi_vertices:
631
+ points = [
632
+ (point.soil_properties.x, point.soil_properties.y)
633
+ for point in self.cpt_results_dict.values()
634
+ ]
635
+ vor = Voronoi(points)
636
+ voronoi_plot_2d(
637
+ vor,
638
+ show_vertices=False,
639
+ show_points=False,
640
+ ax=axes,
641
+ line_colors="black",
642
+ line_alpha=0.7,
643
+ line_width=0.1,
644
+ point_size=0.0,
645
+ )
646
+
647
+ # add the cpt names
648
+ axes.scatter(
649
+ df["x"],
650
+ df["y"],
651
+ c=["green" if val else "red" for val in df["valid"]],
652
+ )
653
+ for label, x, y in zip(df["test_id"], df["x"], df["y"]):
654
+ axes.annotate(label, xy=(x, y), xytext=(3, 3), textcoords="offset points")
655
+ axes.set_xlabel("X")
656
+ axes.set_ylabel("Y")
657
+ axes.ticklabel_format(useOffset=False)
658
+ fig.legend(
659
+ title="$R_{c;d;net}$ [kN]",
660
+ title_fontsize=18,
661
+ fontsize=15,
662
+ loc="lower right",
663
+ handles=[
664
+ patches.Patch(
665
+ facecolor=color,
666
+ label=label,
667
+ alpha=0.9,
668
+ linewidth=2,
669
+ edgecolor="black",
670
+ )
671
+ for label, color in zip(
672
+ [f">= {pile_load_uls}", f"< {pile_load_uls}"],
673
+ ["green", "red"],
674
+ )
675
+ ],
676
+ )
677
+ axes.set_title(f"Pile tip level at: {pile_tip_level_nap} [m w.r.t NAP]")
678
+
679
+ return fig