geolysis 0.4.2__py3-none-any.whl → 0.4.4__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.
@@ -1,53 +1,73 @@
1
- import enum
2
- from abc import abstractmethod
3
- from typing import Protocol
4
- from typing import NamedTuple, Optional, Sequence
1
+ """ Soil classification module.
5
2
 
6
- from geolysis import validators
7
- from geolysis.utils import isclose, round_
3
+ Exceptions
4
+ ==========
8
5
 
9
- __all__ = ["CLF_TYPE", "AtterbergLimits", "PSD", "AASHTO", "USCS",
10
- "SizeDistribution", "create_soil_classifier"]
6
+ .. autosummary::
7
+ :toctree: _autosummary
11
8
 
9
+ SizeDistError
12
10
 
13
- class SizeDistError(ZeroDivisionError):
14
- """Exception raised when size distribution is not provided."""
15
- pass
11
+ Enums
12
+ =====
16
13
 
14
+ .. autosummary::
15
+ :toctree: _autosummary
16
+ :nosignatures:
17
17
 
18
- class SoilClf(NamedTuple):
19
- symbol: str
20
- description: str
18
+ CLF_TYPE
19
+ USCSSymbol
20
+ AASHTOSymbol
21
21
 
22
+ Classes
23
+ =======
22
24
 
23
- class SoilClassifier(Protocol):
25
+ .. autosummary::
26
+ :toctree: _autosummary
24
27
 
25
- @abstractmethod
26
- def classify(self) -> SoilClf: ...
28
+ AtterbergLimits
29
+ PSD
30
+ AASHTO
31
+ USCS
27
32
 
33
+ Functions
34
+ =========
28
35
 
29
- class CLF_TYPE(enum.StrEnum):
30
- """Enumeration of soil classification types."""
31
- AASHTO = enum.auto()
32
- USCS = enum.auto()
36
+ .. autosummary::
37
+ :toctree: _autosummary
33
38
 
39
+ create_soil_classifier
40
+ """
41
+ import enum
42
+ from abc import abstractmethod
43
+ from typing import NamedTuple, Optional, Protocol, Sequence
44
+
45
+ from .utils import enum_repr, isclose, round_, validators
46
+
47
+ __all__ = ["CLF_TYPE",
48
+ "AtterbergLimits",
49
+ "PSD",
50
+ "AASHTO",
51
+ "USCS",
52
+ "create_soil_classifier"]
53
+
54
+
55
+ class SizeDistError(ZeroDivisionError):
56
+ """Exception raised when size distribution is not provided."""
34
57
 
35
- class _Clf(enum.Enum):
58
+
59
+ @enum_repr
60
+ class _Clf(tuple, enum.Enum):
36
61
 
37
62
  def __str__(self) -> str:
38
63
  return self.name
39
64
 
40
- def __eq__(self, value: object) -> bool:
41
- if isinstance(value, str):
42
- return self.clf_symbol == value
43
- return super().__eq__(value)
44
-
45
65
  @property
46
- def clf_symbol(self) -> str:
66
+ def symbol(self) -> str:
47
67
  return self.value[0]
48
68
 
49
69
  @property
50
- def clf_description(self) -> str:
70
+ def description(self) -> str:
51
71
  return self.value[1]
52
72
 
53
73
 
@@ -112,30 +132,31 @@ class AtterbergLimits:
112
132
  """
113
133
 
114
134
  class __A_LINE:
115
- """The ``A-line`` is used to determine if a soil is clayey or silty.
116
-
117
- .. math:: A = 0.73(LL - 20.0)
118
- """
119
135
 
120
136
  def __get__(self, obj, objtype=None) -> float:
121
137
  return 0.73 * (obj.liquid_limit - 20.0)
122
138
 
139
+ #: The ``A-line`` determines if a soil is clayey or silty.
140
+ #: :math:`A = 0.73(LL - 20.0)`
123
141
  A_LINE = __A_LINE()
124
142
 
125
143
  def __init__(self, liquid_limit: float, plastic_limit: float):
126
144
  """
127
145
  :param liquid_limit: Water content beyond which soils flows under their
128
- own weight. It can also be defined as the minimum
129
- moisture content at which a soil flows upon
130
- application of a very small shear force. (%)
146
+ own weight (%). It can also be defined as the
147
+ minimum moisture content at which a soil flows upon
148
+ application of a very small shear force.
131
149
  :type liquid_limit: float
132
150
 
133
151
  :param plastic_limit: Water content at which plastic deformation can be
134
- initiated. It is also the minimum water content at
135
- which soil can be rolled into a thread 3mm thick.
136
- (molded without breaking) (%)
152
+ initiated (%). It is also the minimum water
153
+ content at which soil can be rolled into a thread
154
+ 3mm thick. (molded without breaking)
137
155
  :type plastic_limit: float
138
156
  """
157
+ if liquid_limit < plastic_limit:
158
+ raise ValueError("liquid_limit cannot be less than plastic_limit")
159
+
139
160
  self.liquid_limit = liquid_limit
140
161
  self.plastic_limit = plastic_limit
141
162
 
@@ -166,6 +187,8 @@ class AtterbergLimits:
166
187
  It is also the numerical difference between the liquid limit and
167
188
  plastic limit of the soil.
168
189
 
190
+ :Equation:
191
+
169
192
  .. math:: PI = LL - PL
170
193
  """
171
194
  return self.liquid_limit - self.plastic_limit
@@ -199,6 +222,8 @@ class AtterbergLimits:
199
222
 
200
223
  :param float nmc: Moisture contents of the soil in natural condition.
201
224
 
225
+ :Equation:
226
+
202
227
  .. math:: I_l = \dfrac{w - PL}{PI} \cdot 100
203
228
  """
204
229
  return ((nmc - self.plastic_limit) / self.plasticity_index) * 100.0
@@ -220,20 +245,20 @@ class AtterbergLimits:
220
245
 
221
246
  :param float nmc: Moisture contents of the soil in natural condition.
222
247
 
248
+ :Equation:
249
+
223
250
  .. math:: I_c = \dfrac{LL - w}{PI} \cdot 100
224
251
  """
225
252
  return ((self.liquid_limit - nmc) / self.plasticity_index) * 100.0
226
253
 
227
254
 
228
- class SizeDistribution:
229
- """Features obtained from the Particle Size Distribution graph."""
255
+ class _SizeDistribution:
256
+ """Particle size distribution of soil sample.
257
+
258
+ Features obtained from the Particle Size Distribution graph.
259
+ """
230
260
 
231
261
  def __init__(self, d_10: float = 0, d_30: float = 0, d_60: float = 0):
232
- """
233
- :param float d_10: Diameter at which 10% of the soil by weight is finer.
234
- :param float d_30: Diameter at which 30% of the soil by weight is finer.
235
- :param float d_60: Diameter at which 60% of the soil by weight is finer.
236
- """
237
262
  self.d_10 = d_10
238
263
  self.d_30 = d_30
239
264
  self.d_60 = d_60
@@ -256,9 +281,6 @@ class SizeDistribution:
256
281
  :param coarse_soil: Coarse fraction of the soil sample. Valid arguments
257
282
  are ``USCSSymbol.GRAVEL`` and ``USCSSymbol.SAND``.
258
283
  """
259
- if not (coarse_soil in (USCSSymbol.GRAVEL, USCSSymbol.SAND)):
260
- raise NotImplementedError
261
-
262
284
  if coarse_soil is USCSSymbol.GRAVEL:
263
285
  if 1 < self.coeff_of_curvature < 3 and self.coeff_of_uniformity >= 4:
264
286
  grade = USCSSymbol.WELL_GRADED
@@ -280,22 +302,31 @@ class PSD:
280
302
  """
281
303
 
282
304
  def __init__(self, fines: float, sand: float,
283
- size_dist: Optional[SizeDistribution] = None):
305
+ d_10: float = 0, d_30: float = 0, d_60: float = 0):
284
306
  """
285
- :param fines: Percentage of fines in soil sample i.e. The percentage of
286
- soil sample passing through No. 200 sieve (0.075mm).
307
+ :param fines: Percentage of fines in soil sample (%) i.e. The
308
+ percentage of soil sample passing through No. 200 sieve
309
+ (0.075mm).
287
310
  :type fines: float
288
311
 
289
- :param sand: Percentage of sand in soil sample.
312
+ :param sand: Percentage of sand in soil sample (%).
290
313
  :type sand: float
291
314
 
292
- :param size_dist: Particle size distribution of soil sample.
293
- :type size_dist: SizeDistribution
315
+ :param d_10: Diameter at which 10% of the soil by weight is finer.
316
+ :type d_10: float
317
+ :param d_30: Diameter at which 30% of the soil by weight is finer.
318
+ :type d_30: float
319
+ :param d_60: Diameter at which 60% of the soil by weight is finer.
320
+ :type d_60: float
294
321
  """
295
322
  self.fines = fines
296
323
  self.sand = sand
297
- self.gravel = 100.0 - (fines + sand)
298
- self.size_dist = size_dist if size_dist else SizeDistribution()
324
+ self.size_dist = _SizeDistribution(d_10=d_10, d_30=d_30, d_60=d_60)
325
+
326
+ @property
327
+ def gravel(self):
328
+ """Percentage of gravel in soil sample (%)."""
329
+ return 100.0 - (self.fines + self.sand)
299
330
 
300
331
  @property
301
332
  def coarse_material_type(self) -> USCSSymbol:
@@ -309,7 +340,7 @@ class PSD:
309
340
  def coeff_of_curvature(self) -> float:
310
341
  r"""Coefficient of curvature of soil sample.
311
342
 
312
- Coefficient of curvature :math:`(C_c)` is given by the formula:
343
+ :Equation:
313
344
 
314
345
  .. math:: C_c = \dfrac{D^2_{30}}{D_{60} \cdot D_{10}}
315
346
 
@@ -323,13 +354,13 @@ class PSD:
323
354
  def coeff_of_uniformity(self) -> float:
324
355
  r"""Coefficient of uniformity of soil sample.
325
356
 
326
- Coefficient of uniformity :math:`(C_u)` is given by the formula:
357
+ :Equation:
327
358
 
328
359
  .. math:: C_u = \dfrac{D_{60}}{D_{10}}
329
360
 
330
- :math:`C_u` value greater than 4 to 6 classifies the soil as well graded
331
- for gravels and sands respectively. When :math:`C_u` is less than 4, it
332
- is classified as poorly graded or uniformly graded soil.
361
+ :math:`C_u` value greater than 4 to 6 classifies the soil as well
362
+ graded for gravels and sands respectively. When :math:`C_u` is less
363
+ than 4, it is classified as poorly graded or uniformly graded soil.
333
364
 
334
365
  Higher values of :math:`C_u` indicates that the soil mass consists of
335
366
  soil particles with different size ranges.
@@ -346,12 +377,19 @@ class PSD:
346
377
 
347
378
  Conditions for a well-graded soil:
348
379
 
380
+ :Equation:
381
+
349
382
  - :math:`1 \lt C_c \lt 3` and :math:`C_u \ge 4` (for gravels)
350
383
  - :math:`1 \lt C_c \lt 3` and :math:`C_u \ge 6` (for sands)
351
384
  """
352
385
  return self.size_dist.grade(coarse_soil=self.coarse_material_type)
353
386
 
354
387
 
388
+ class SoilClf(NamedTuple):
389
+ symbol: str
390
+ description: str
391
+
392
+
355
393
  class AASHTO:
356
394
  r"""American Association of State Highway and Transportation Officials
357
395
  (AASHTO) classification system.
@@ -373,6 +411,8 @@ class AASHTO:
373
411
  The ``GI`` must be mentioned even when it is zero, to indicate that the
374
412
  soil has been classified as per AASHTO system.
375
413
 
414
+ :Equation:
415
+
376
416
  .. math::
377
417
 
378
418
  GI = (F_{200} - 35)[0.2 + 0.005(LL - 40)] + 0.01(F_{200} - 15)(PI - 10)
@@ -381,11 +421,11 @@ class AASHTO:
381
421
  def __init__(self, atterberg_limits: AtterbergLimits,
382
422
  fines: float, add_group_idx=True):
383
423
  """
384
- :param atterberg_limits: Atterberg limits of the soil.
424
+ :param atterberg_limits: Atterberg limits of soil sample.
385
425
  :type atterberg_limits: AtterbergLimits
386
426
 
387
- :param fines: Percentage of fines in soil sample i.e. The percentage of
388
- soil sample passing through No. 200 sieve (0.075mm).
427
+ :param fines: Percentage of fines in soil sample (%) i.e. The percentage
428
+ of soil sample passing through No. 200 sieve (0.075mm).
389
429
  :type fines: float
390
430
 
391
431
  :param add_group_idx: Used to indicate whether the group index should
@@ -425,7 +465,7 @@ class AASHTO:
425
465
  """Return the AASHTO classification of the soil."""
426
466
  soil_clf = self._classify()
427
467
 
428
- symbol, description = soil_clf.clf_symbol, soil_clf.clf_description
468
+ symbol, description = soil_clf.symbol, soil_clf.description
429
469
 
430
470
  if self.add_group_idx:
431
471
  symbol = f"{symbol}({self.group_index():.0f})"
@@ -513,7 +553,7 @@ class USCS:
513
553
  termed as Peat. (:math:`P_t`)
514
554
  """
515
555
 
516
- def __init__(self, atterberg_limits: AtterbergLimits,
556
+ def __init__(self, atterberg_limits: AtterbergLimits,
517
557
  psd: PSD, organic=False):
518
558
  """
519
559
  :param atterberg_limits: Atterberg limits of the soil.
@@ -539,13 +579,13 @@ class USCS:
539
579
  soil_clf = USCSSymbol[soil_clf]
540
580
 
541
581
  if isinstance(soil_clf, USCSSymbol):
542
- return SoilClf(soil_clf.clf_symbol, soil_clf.clf_description)
582
+ return SoilClf(soil_clf.symbol, soil_clf.description)
543
583
 
544
584
  # Handling tuple or list case for dual classification
545
585
  first_clf, second_clf = map(lambda clf: USCSSymbol[clf], soil_clf)
546
586
 
547
- comb_symbol = f"{first_clf.clf_symbol},{second_clf.clf_symbol}"
548
- comb_desc = f"{first_clf.clf_description},{second_clf.clf_description}"
587
+ comb_symbol = f"{first_clf.symbol},{second_clf.symbol}"
588
+ comb_desc = f"{first_clf.description},{second_clf.description}"
549
589
 
550
590
  return SoilClf(comb_symbol, comb_desc)
551
591
 
@@ -642,40 +682,55 @@ class USCS:
642
682
  f"{coarse_material_type}{fine_material_type}")
643
683
 
644
684
 
645
- def create_soil_classifier(liquid_limit: float, plastic_limit: float,
646
- fines: float, sand: Optional[float] = None,
685
+ @enum_repr
686
+ class CLF_TYPE(enum.StrEnum):
687
+ """Enumeration of soil classification types."""
688
+ AASHTO = enum.auto()
689
+ USCS = enum.auto()
690
+
691
+
692
+ class SoilClassifier(Protocol):
693
+ @abstractmethod
694
+ def classify(self) -> SoilClf: ...
695
+
696
+
697
+ def create_soil_classifier(liquid_limit: float,
698
+ plastic_limit: float,
699
+ fines: float,
700
+ sand: Optional[float] = None,
647
701
  d_10: float = 0, d_30: float = 0, d_60: float = 0,
648
- add_group_idx: bool = True, organic: bool = False,
649
- clf_type: CLF_TYPE | str | None = None
702
+ add_group_idx: bool = True,
703
+ organic: bool = False,
704
+ clf_type: Optional[CLF_TYPE | str] = None
650
705
  ) -> SoilClassifier:
651
706
  """ A factory function that encapsulates the creation of a soil classifier.
652
707
 
653
708
  :param liquid_limit: Water content beyond which soils flows under their own
654
- weight. It can also be defined as the minimum moisture
655
- content at which a soil flows upon application of a
656
- very small shear force. (%)
709
+ weight (%). It can also be defined as the minimum
710
+ moisture content at which a soil flows upon application
711
+ of a very small shear force.
657
712
  :type liquid_limit: float
658
713
 
659
714
  :param plastic_limit: Water content at which plastic deformation can be
660
- initiated. It is also the minimum water content at
715
+ initiated (%). It is also the minimum water content at
661
716
  which soil can be rolled into a thread 3mm thick.
662
- (molded without breaking) (%)
717
+ (molded without breaking)
663
718
  :type plastic_limit: float
664
719
 
665
- :param fines: Percentage of fines in soil sample i.e. The percentage of
666
- soil sample passing through No. 200 sieve (0.075mm). (%)
720
+ :param fines: Percentage of fines in soil sample (%) i.e. The percentage of
721
+ soil sample passing through No. 200 sieve (0.075mm).
667
722
  :type fines: float
668
723
 
669
- :param sand: Percentage of sand in soil sample. (%)
724
+ :param sand: Percentage of sand in soil sample (%).
670
725
  :type sand: float
671
726
 
672
- :param d_10: Diameter at which 10% of the soil by weight is finer. (mm)
727
+ :param d_10: Diameter at which 10% of the soil by weight is finer.
673
728
  :type d_10: float
674
729
 
675
- :param d_30: Diameter at which 30% of the soil by weight is finer. (mm)
730
+ :param d_30: Diameter at which 30% of the soil by weight is finer.
676
731
  :type d_30: float
677
732
 
678
- :param d_60: Diameter at which 60% of the soil by weight is finer. (mm)
733
+ :param d_60: Diameter at which 60% of the soil by weight is finer.
679
734
  :type d_60: float
680
735
 
681
736
  :param add_group_idx: Used to indicate whether the group index should
@@ -683,28 +738,35 @@ def create_soil_classifier(liquid_limit: float, plastic_limit: float,
683
738
  True.
684
739
  :type add_group_idx: bool, optional
685
740
 
686
- :param organic: Indicates whether soil is organic or not, defaults to
687
- False.
741
+ :param organic: Indicates whether soil is organic or not, defaults to False.
688
742
  :type organic: bool, optional
689
743
 
690
744
  :param clf_type: Used to indicate which type of soil classifier should be
691
745
  used, defaults to None.
692
- :type clf_type: ClfType, optional
746
+ :type clf_type: CLF_TYPE | str
693
747
 
694
- :raises ValueError: Raises ValueError if ``clf_type`` is ``None`` or
695
- invalid
748
+ :raises ValueError: Raises ValueError if ``clf_type`` is not supported or
749
+ None
750
+ :raises ValueError: Raises ValueError if ``sand`` is not provided for
751
+ :class:`USCS` classification.
696
752
  """
753
+ msg = (f"{clf_type = } is not supported, Supported "
754
+ f"types are: {list(CLF_TYPE)}")
755
+
697
756
  if clf_type is None:
698
- raise ValueError("clf_type must be specified")
757
+ raise ValueError(msg)
699
758
 
700
- # raises ValueError if clf_type is not supported
701
- if isinstance(clf_type, str):
702
- clf_type = CLF_TYPE(clf_type.casefold())
759
+ try:
760
+ clf_type = CLF_TYPE(str(clf_type).casefold())
761
+ except ValueError as e:
762
+ raise ValueError(msg) from e
703
763
 
704
- al = AtterbergLimits(liquid_limit=liquid_limit, plastic_limit=plastic_limit)
764
+ atterberg_lmts = AtterbergLimits(liquid_limit=liquid_limit,
765
+ plastic_limit=plastic_limit)
705
766
 
706
767
  if clf_type == CLF_TYPE.AASHTO:
707
- clf = AASHTO(atterberg_limits=al, fines=fines,
768
+ clf = AASHTO(atterberg_limits=atterberg_lmts,
769
+ fines=fines,
708
770
  add_group_idx=add_group_idx)
709
771
  return clf
710
772
 
@@ -712,8 +774,7 @@ def create_soil_classifier(liquid_limit: float, plastic_limit: float,
712
774
  if sand is None:
713
775
  raise ValueError("sand must be specified for USCS classification")
714
776
 
715
- size_dist = SizeDistribution(d_10=d_10, d_30=d_30, d_60=d_60)
716
- psd = PSD(fines=fines, sand=sand, size_dist=size_dist)
717
- clf = USCS(atterberg_limits=al, psd=psd, organic=organic)
777
+ psd = PSD(fines=fines, sand=sand, d_10=d_10, d_30=d_30, d_60=d_60)
778
+ clf = USCS(atterberg_limits=atterberg_lmts, psd=psd, organic=organic)
718
779
 
719
780
  return clf