geolysis 0.12.0__py3-none-any.whl → 0.14.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.
geolysis/foundation.py CHANGED
@@ -1,18 +1,17 @@
1
1
  import enum
2
- import math
3
2
  from abc import ABC, abstractmethod
4
- from math import isinf
5
- from typing import Optional, TypeVar, Annotated
3
+ from typing import Optional, TypeAlias, TypeVar, Annotated
6
4
 
7
5
  from func_validator import (
8
- validate_func_args,
6
+ validate_params,
9
7
  MustBePositive,
10
8
  MustBeNonNegative,
11
9
  MustBeBetween,
12
10
  MustBeMemberOf,
11
+ DependsOn,
13
12
  )
14
13
 
15
- from .utils import AbstractStrEnum, inf, isclose, pi, round_, add_repr
14
+ from .utils import AbstractStrEnum, inf, isclose, pi, round_, add_repr, isinf
16
15
 
17
16
  __all__ = [
18
17
  "create_foundation",
@@ -82,7 +81,7 @@ class FootingSize(ABC):
82
81
  raise NotImplementedError
83
82
 
84
83
  @width.setter
85
- def width(self, value: float):
84
+ def width(self, width: float):
86
85
  raise NotImplementedError
87
86
 
88
87
  @property
@@ -91,7 +90,7 @@ class FootingSize(ABC):
91
90
  raise NotImplementedError
92
91
 
93
92
  @length.setter
94
- def length(self, value: float):
93
+ def length(self, length: float):
95
94
  raise NotImplementedError
96
95
 
97
96
  def area(self) -> float:
@@ -123,9 +122,8 @@ class StripFooting(FootingSize):
123
122
  return self._width
124
123
 
125
124
  @width.setter
126
- @validate_func_args
127
- def width(self, val: Annotated[float, MustBePositive]) -> None:
128
- self._width = val
125
+ def width(self, width: float) -> None:
126
+ self._width = width
129
127
 
130
128
  @property
131
129
  def length(self) -> float:
@@ -133,13 +131,12 @@ class StripFooting(FootingSize):
133
131
  return self._length
134
132
 
135
133
  @length.setter
136
- @validate_func_args
137
- def length(self, val: Annotated[float, MustBePositive]) -> None:
138
- self._length = val
134
+ def length(self, length: float) -> None:
135
+ self._length = length
139
136
 
140
137
  def area(self) -> float:
141
138
  """Area of strip footing ($m \text{or} m^2$)."""
142
- if math.isinf(self.length):
139
+ if isinf(self.length):
143
140
  return self.width
144
141
  return self.width * self.length
145
142
 
@@ -169,9 +166,8 @@ class CircularFooting(FootingSize):
169
166
  return self._diameter
170
167
 
171
168
  @diameter.setter
172
- @validate_func_args
173
- def diameter(self, val: Annotated[float, MustBePositive]) -> None:
174
- self._diameter = val
169
+ def diameter(self, diameter: float) -> None:
170
+ self._diameter = diameter
175
171
 
176
172
  @property
177
173
  def width(self):
@@ -180,8 +176,8 @@ class CircularFooting(FootingSize):
180
176
 
181
177
  # Not checking for positive as diameter setter already does that
182
178
  @width.setter
183
- def width(self, val: float):
184
- self.diameter = val
179
+ def width(self, width: float):
180
+ self.diameter = width
185
181
 
186
182
  # Not checking for positive as diameter setter already does that
187
183
  @property
@@ -190,8 +186,8 @@ class CircularFooting(FootingSize):
190
186
  return self.diameter
191
187
 
192
188
  @length.setter
193
- def length(self, val: float):
194
- self.diameter = val
189
+ def length(self, length: float):
190
+ self.diameter = length
195
191
 
196
192
  def area(self) -> float:
197
193
  """Area of circular footing ($m^2$)."""
@@ -216,9 +212,8 @@ class SquareFooting(FootingSize):
216
212
  return self._width
217
213
 
218
214
  @width.setter
219
- @validate_func_args
220
- def width(self, val: Annotated[float, MustBePositive]) -> None:
221
- self._width = val
215
+ def width(self, width: float) -> None:
216
+ self._width = width
222
217
 
223
218
  @property
224
219
  def length(self):
@@ -227,8 +222,8 @@ class SquareFooting(FootingSize):
227
222
 
228
223
  # Not checking for positive as width setter already does that
229
224
  @length.setter
230
- def length(self, val):
231
- self.width = val
225
+ def length(self, length: float):
226
+ self.width = length
232
227
 
233
228
  def area(self) -> float:
234
229
  """Area of square footing ($m^2$)."""
@@ -255,9 +250,8 @@ class RectangularFooting(FootingSize):
255
250
  return self._width
256
251
 
257
252
  @width.setter
258
- @validate_func_args
259
- def width(self, val: Annotated[float, MustBePositive]) -> None:
260
- self._width = val
253
+ def width(self, width: float) -> None:
254
+ self._width = width
261
255
 
262
256
  @property
263
257
  def length(self) -> float:
@@ -265,9 +259,8 @@ class RectangularFooting(FootingSize):
265
259
  return self._length
266
260
 
267
261
  @length.setter
268
- @validate_func_args
269
- def length(self, val: Annotated[float, MustBePositive]) -> None:
270
- self._length = val
262
+ def length(self, length: float) -> None:
263
+ self._length = length
271
264
 
272
265
  def area(self) -> float:
273
266
  """Area of rectangular footing ($m^2$)."""
@@ -284,7 +277,7 @@ class Foundation:
284
277
  footing_size: FootingSize,
285
278
  eccentricity: float = 0.0,
286
279
  load_angle: float = 0.0,
287
- ground_water_level: Optional[float] = None,
280
+ ground_water_level: Optional[float] = inf,
288
281
  foundation_type: FoundationType = FoundationType.PAD,
289
282
  ) -> None:
290
283
  r"""
@@ -304,7 +297,7 @@ class Foundation:
304
297
  self.eccentricity = eccentricity
305
298
  self.load_angle = load_angle
306
299
 
307
- self._ground_water_level = ground_water_level
300
+ self.ground_water_level = ground_water_level
308
301
  self.foundation_type = foundation_type
309
302
 
310
303
  @property
@@ -313,9 +306,9 @@ class Foundation:
313
306
  return self._depth
314
307
 
315
308
  @depth.setter
316
- @validate_func_args
317
- def depth(self, val: Annotated[float, MustBePositive]) -> None:
318
- self._depth = val
309
+ @validate_params
310
+ def depth(self, depth: Annotated[float, MustBePositive]) -> None:
311
+ self._depth = depth
319
312
 
320
313
  @property
321
314
  def width(self) -> float:
@@ -323,8 +316,8 @@ class Foundation:
323
316
  return self.footing_size.width
324
317
 
325
318
  @width.setter
326
- def width(self, val: Annotated[float, MustBePositive]):
327
- self.footing_size.width = val
319
+ def width(self, width: Annotated[float, MustBePositive]):
320
+ self.footing_size.width = width
328
321
 
329
322
  @property
330
323
  def length(self) -> float:
@@ -332,8 +325,8 @@ class Foundation:
332
325
  return self.footing_size.length
333
326
 
334
327
  @length.setter
335
- def length(self, val: Annotated[float, MustBePositive]):
336
- self.footing_size.length = val
328
+ def length(self, length: Annotated[float, MustBePositive]):
329
+ self.footing_size.length = length
337
330
 
338
331
  @property
339
332
  def footing_shape(self) -> Shape:
@@ -348,9 +341,10 @@ class Foundation:
348
341
  return self._eccentricity
349
342
 
350
343
  @eccentricity.setter
351
- @validate_func_args
352
- def eccentricity(self, val: Annotated[float, MustBeNonNegative]) -> None:
353
- self._eccentricity = val
344
+ @validate_params
345
+ def eccentricity(self, eccentricity: Annotated[
346
+ float, MustBeNonNegative]) -> None:
347
+ self._eccentricity = eccentricity
354
348
 
355
349
  @property
356
350
  def load_angle(self) -> float:
@@ -358,12 +352,13 @@ class Foundation:
358
352
  return self._load_angle
359
353
 
360
354
  @load_angle.setter
361
- @validate_func_args
355
+ @validate_params
362
356
  def load_angle(
363
357
  self,
364
- val: Annotated[float, MustBeBetween(min_value=0.0, max_value=90.0)]
358
+ load_angle: Annotated[
359
+ float, MustBeBetween(min_value=0.0, max_value=90.0)],
365
360
  ) -> None:
366
- self._load_angle = val
361
+ self._load_angle = load_angle
367
362
 
368
363
  @property
369
364
  def ground_water_level(self) -> Optional[float]:
@@ -371,9 +366,12 @@ class Foundation:
371
366
  return self._ground_water_level
372
367
 
373
368
  @ground_water_level.setter
374
- @validate_func_args
375
- def ground_water_level(self, val: Annotated[float, MustBePositive]):
376
- self._ground_water_level = val
369
+ @validate_params
370
+ def ground_water_level(
371
+ self,
372
+ ground_water_level: Annotated[float, MustBePositive],
373
+ ):
374
+ self._ground_water_level = ground_water_level
377
375
 
378
376
  @property
379
377
  def foundation_type(self) -> FoundationType:
@@ -381,12 +379,13 @@ class Foundation:
381
379
  return self._foundation_type
382
380
 
383
381
  @foundation_type.setter
384
- @validate_func_args
382
+ @validate_params
385
383
  def foundation_type(
386
384
  self,
387
- val: Annotated[FoundationType, MustBeMemberOf(FoundationType)]
385
+ foundation_type: Annotated[
386
+ FoundationType, MustBeMemberOf(FoundationType)],
388
387
  ):
389
- self._foundation_type = val
388
+ self._foundation_type = foundation_type
390
389
 
391
390
  @round_(2)
392
391
  def foundation_area(self) -> float:
@@ -403,7 +402,10 @@ class Foundation:
403
402
  of the foundation footing.
404
403
  """
405
404
  width, length, shape = (
406
- self.effective_width, self.length, self.footing_shape)
405
+ self.effective_width,
406
+ self.length,
407
+ self.footing_shape,
408
+ )
407
409
 
408
410
  if not isclose(width, length) and shape != Shape.STRIP:
409
411
  shape = Shape.RECTANGLE
@@ -411,11 +413,11 @@ class Foundation:
411
413
  return width, length, shape
412
414
 
413
415
 
414
- @validate_func_args
416
+ @validate_params
415
417
  def create_foundation(
416
418
  depth: float,
417
419
  width: float,
418
- length: float = inf,
420
+ length: Annotated[float, DependsOn(shape=Shape.RECTANGLE)] = None,
419
421
  eccentricity: float = 0.0,
420
422
  load_angle: float = 0.0,
421
423
  ground_water_level: Optional[float] = inf,
@@ -434,31 +436,25 @@ def create_foundation(
434
436
  load aligns with the center of gravity of the
435
437
  foundation footing .
436
438
  :param load_angle: Inclination of the applied load with the vertical
437
- (:math:`\alpha^{\circ}`), defaults to 0.0.
438
- :param ground_water_level: Depth of the water below ground level (m),
439
- defaults to None.
440
- :param foundation_type: Type of foundation footing, defaults to
441
- :py:enum:mem:`~FoundationType.PAD`.
442
- :param shape: Shape of foundation footing, defaults to
443
- :py:enum:mem:`~Shape.SQUARE`
444
- :raises ValueError: Raised when length is not provided for a
445
- rectangular footing.
446
- :raises ValidationError: Raised if an invalid footing shape is
439
+ (:math:`\alpha^{\circ}`).
440
+ :param ground_water_level: Depth of the water below ground level (m)
441
+ :param foundation_type: Type of foundation footing.
442
+ :param shape: Shape of foundation footing
443
+
444
+ :raises ValidationError: Raised when length is not provided for a
445
+ rectangular footing or an invalid shape is
447
446
  provided.
448
447
  """
449
-
448
+ footing_size: FootingSize
450
449
  shape = Shape(str(shape).casefold())
451
450
 
452
- if shape is Shape.STRIP:
451
+ if shape == Shape.STRIP:
453
452
  footing_size = StripFooting(width=width)
454
- elif shape is Shape.SQUARE:
453
+ elif shape == Shape.SQUARE:
455
454
  footing_size = SquareFooting(width=width)
456
- elif shape is Shape.CIRCLE:
455
+ elif shape == Shape.CIRCLE:
457
456
  footing_size = CircularFooting(diameter=width)
458
457
  else: # RECTANGLE
459
- if isinf(length):
460
- msg = "Length of rectangular footing must be provided."
461
- raise ValueError(msg)
462
458
  footing_size = RectangularFooting(width=width, length=length)
463
459
 
464
460
  return Foundation(
@@ -2,9 +2,9 @@ import enum
2
2
  from dataclasses import dataclass
3
3
  from typing import Annotated, Sequence
4
4
 
5
- from func_validator import validate_func_args, MustBeNonNegative
5
+ from func_validator import validate_params, MustBeNonNegative
6
6
 
7
- from .utils import isclose, round_
7
+ from .utils import isclose, round_, nan, isnan
8
8
 
9
9
  __all__ = [
10
10
  "AtterbergLimits",
@@ -256,9 +256,9 @@ class AtterbergLimits:
256
256
  return self._liquid_limit
257
257
 
258
258
  @liquid_limit.setter
259
- @validate_func_args
260
- def liquid_limit(self, val: Annotated[float, MustBeNonNegative]):
261
- self._liquid_limit = val
259
+ @validate_params
260
+ def liquid_limit(self, liquid_limit: Annotated[float, MustBeNonNegative]):
261
+ self._liquid_limit = liquid_limit
262
262
 
263
263
  @property
264
264
  def plastic_limit(self) -> float:
@@ -268,16 +268,16 @@ class AtterbergLimits:
268
268
  return self._plastic_limit
269
269
 
270
270
  @plastic_limit.setter
271
- @validate_func_args
272
- def plastic_limit(self, val: Annotated[float, MustBeNonNegative]):
273
- if self.liquid_limit < val:
271
+ @validate_params
272
+ def plastic_limit(self, plastic_limit: Annotated[float, MustBeNonNegative]):
273
+ if self.liquid_limit < plastic_limit:
274
274
  msg = (
275
- f"plastic_limit: {val} cannot be greater than "
275
+ f"plastic_limit: {plastic_limit} cannot be greater than "
276
276
  f"liquid_limit: {self.liquid_limit}"
277
277
  )
278
278
  raise ValueError(msg)
279
279
 
280
- self._plastic_limit = val
280
+ self._plastic_limit = plastic_limit
281
281
 
282
282
  @property
283
283
  @round_(2)
@@ -353,7 +353,7 @@ class _SizeDistribution:
353
353
  Features obtained from the Particle Size Distribution graph.
354
354
  """
355
355
 
356
- def __init__(self, d_10: float = 0.0, d_30: float = 0.0, d_60: float = 0.0):
356
+ def __init__(self, d_10: float, d_30: float, d_60: float):
357
357
  self.d_10 = d_10
358
358
  self.d_30 = d_30
359
359
  self.d_60 = d_60
@@ -389,6 +389,12 @@ class _SizeDistribution:
389
389
  grade = USCSSymbol.POORLY_GRADED
390
390
  return grade
391
391
 
392
+ def has_particle_sizes(self) -> bool:
393
+ """Checks if particle sizes are provided."""
394
+ if isnan(self.d_10) or isnan(self.d_30) or isnan(self.d_60):
395
+ return False
396
+ return True
397
+
392
398
 
393
399
  class PSD:
394
400
  """Quantitative proportions by mass of various sizes of particles
@@ -399,9 +405,9 @@ class PSD:
399
405
  self,
400
406
  fines: float,
401
407
  sand: float,
402
- d_10: float = 0,
403
- d_30: float = 0,
404
- d_60: float = 0,
408
+ d_10: float = nan,
409
+ d_30: float = nan,
410
+ d_60: float = nan,
405
411
  ):
406
412
  """
407
413
  :param fines: Percentage of fines in soil sample (%) i.e. The
@@ -459,7 +465,7 @@ class PSD:
459
465
 
460
466
  def has_particle_sizes(self) -> bool:
461
467
  """Checks if soil sample has particle sizes."""
462
- return any(self.size_dist)
468
+ return self.size_dist.has_particle_sizes()
463
469
 
464
470
  def grade(self) -> USCSSymbol:
465
471
  r"""Return the grade of the soil sample, either well graded or
@@ -473,7 +479,7 @@ class PSD:
473
479
  return self.size_dist.grade(coarse_soil=self.coarse_material_type)
474
480
 
475
481
 
476
- @dataclass
482
+ @dataclass(frozen=True, slots=True)
477
483
  class AASHTOResult:
478
484
  symbol: str
479
485
  symbol_no_group_idx: str
@@ -527,9 +533,9 @@ class AASHTO:
527
533
  return self._fines
528
534
 
529
535
  @fines.setter
530
- @validate_func_args
531
- def fines(self, val: Annotated[float, MustBeNonNegative]):
532
- self._fines = val
536
+ @validate_params
537
+ def fines(self, fines: Annotated[float, MustBeNonNegative]):
538
+ self._fines = fines
533
539
 
534
540
  @round_(ndigits=0)
535
541
  def group_index(self) -> float:
@@ -619,7 +625,7 @@ class AASHTO:
619
625
  return soil_clf
620
626
 
621
627
 
622
- @dataclass
628
+ @dataclass(frozen=True, slots=True)
623
629
  class USCSResult:
624
630
  symbol: str
625
631
  description: str
@@ -672,7 +678,8 @@ class USCS:
672
678
 
673
679
  if isinstance(soil_clf, USCSSymbol):
674
680
  soil_clf = USCSResult(
675
- symbol=soil_clf.symbol, description=soil_clf.description
681
+ symbol=soil_clf.symbol,
682
+ description=soil_clf.description,
676
683
  )
677
684
  else:
678
685
  # Handling tuple or list case for dual classification
@@ -790,12 +797,10 @@ def create_aashto_classifier(
790
797
  the minimum moisture content at which a soil
791
798
  flows upon application of a very small shear
792
799
  force.
793
-
794
800
  :param plastic_limit: Water content at which plastic deformation can
795
801
  be initiated (%). It is also the minimum water
796
802
  content at which soil can be rolled into a
797
803
  thread 3mm thick (molded without breaking).
798
-
799
804
  :param fines: Percentage of fines in soil sample (%) i.e. The
800
805
  percentage of soil sample passing through No. 200
801
806
  sieve (0.075mm).
@@ -809,9 +814,9 @@ def create_uscs_classifier(
809
814
  plastic_limit: float,
810
815
  fines: float,
811
816
  sand: float,
812
- d_10: float = 0,
813
- d_30: float = 0,
814
- d_60: float = 0,
817
+ d_10: float = nan,
818
+ d_30: float = nan,
819
+ d_60: float = nan,
815
820
  organic: bool = False,
816
821
  ):
817
822
  """A helper function that encapsulates the creation of a USCS
@@ -822,24 +827,17 @@ def create_uscs_classifier(
822
827
  the minimum moisture content at which a soil
823
828
  flows upon application of a very small shear
824
829
  force.
825
-
826
830
  :param plastic_limit: Water content at which plastic deformation can
827
831
  be initiated (%). It is also the minimum water
828
832
  content at which soil can be rolled into a
829
833
  thread 3mm thick. (molded without breaking)
830
-
831
834
  :param fines: Percentage of fines in soil sample (%) i.e. The
832
835
  percentage of soil sample passing through No. 200
833
836
  sieve (0.075mm).
834
-
835
837
  :param sand: Percentage of sand in soil sample (%).
836
-
837
838
  :param d_10: Diameter at which 10% of the soil by weight is finer.
838
-
839
839
  :param d_30: Diameter at which 30% of the soil by weight is finer.
840
-
841
840
  :param d_60: Diameter at which 60% of the soil by weight is finer.
842
-
843
841
  :param organic: Indicates whether soil is organic or not.
844
842
  """
845
843
  atterberg_lmts = AtterbergLimits(liquid_limit, plastic_limit)