geolysis 0.9.0__py3-none-any.whl → 0.10.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.
Files changed (32) hide show
  1. geolysis/__init__.py +3 -3
  2. geolysis/bearing_capacity/abc/__init__.py +21 -0
  3. geolysis/bearing_capacity/abc/_cohl/__init__.py +109 -0
  4. geolysis/bearing_capacity/abc/{cohl → _cohl}/_core.py +19 -8
  5. geolysis/bearing_capacity/abc/_cohl/bowles_abc.py +103 -0
  6. geolysis/bearing_capacity/abc/_cohl/meyerhof_abc.py +100 -0
  7. geolysis/bearing_capacity/abc/_cohl/terzaghi_abc.py +143 -0
  8. geolysis/bearing_capacity/ubc/__init__.py +107 -128
  9. geolysis/bearing_capacity/ubc/_core.py +65 -52
  10. geolysis/bearing_capacity/ubc/_hansen_ubc.py +271 -0
  11. geolysis/bearing_capacity/ubc/_terzaghi_ubc.py +178 -0
  12. geolysis/bearing_capacity/ubc/_vesic_ubc.py +253 -0
  13. geolysis/foundation.py +146 -136
  14. geolysis/soil_classifier.py +379 -283
  15. geolysis/spt.py +323 -257
  16. geolysis/{utils/__init__.py → utils.py} +44 -33
  17. geolysis-0.10.0.dist-info/METADATA +181 -0
  18. geolysis-0.10.0.dist-info/RECORD +22 -0
  19. {geolysis-0.9.0.dist-info → geolysis-0.10.0.dist-info}/WHEEL +1 -1
  20. geolysis/bearing_capacity/abc/cohl/__init__.py +0 -137
  21. geolysis/bearing_capacity/abc/cohl/bowles_abc.py +0 -96
  22. geolysis/bearing_capacity/abc/cohl/meyerhof_abc.py +0 -96
  23. geolysis/bearing_capacity/abc/cohl/terzaghi_abc.py +0 -131
  24. geolysis/bearing_capacity/ubc/hansen_ubc.py +0 -287
  25. geolysis/bearing_capacity/ubc/terzaghi_ubc.py +0 -246
  26. geolysis/bearing_capacity/ubc/vesic_ubc.py +0 -293
  27. geolysis/utils/exceptions.py +0 -65
  28. geolysis/utils/validators.py +0 -119
  29. geolysis-0.9.0.dist-info/METADATA +0 -206
  30. geolysis-0.9.0.dist-info/RECORD +0 -24
  31. {geolysis-0.9.0.dist-info → geolysis-0.10.0.dist-info}/licenses/LICENSE.txt +0 -0
  32. {geolysis-0.9.0.dist-info → geolysis-0.10.0.dist-info}/top_level.txt +0 -0
@@ -1,25 +1,25 @@
1
- """This module provides classes for soil classification using systems like
2
- USCS and AASHTO, based on particle size distribution and Atterberg limits.
3
- """
4
1
  import enum
5
- from typing import NamedTuple, Optional, Sequence
2
+ from dataclasses import dataclass
3
+ from typing import Annotated, Sequence
6
4
 
7
- from .utils import enum_repr, isclose, round_, validators
8
- from .utils.exceptions import ErrorMsg, ValidationError
5
+ from func_validator import validate_func_args, MustBeNonNegative
9
6
 
10
- __all__ = ["ClfType",
11
- "AtterbergLimits",
12
- "PSD",
13
- "AASHTO",
14
- "USCS",
15
- "create_soil_classifier"]
7
+ from .utils import isclose, round_
8
+
9
+ __all__ = [
10
+ "AtterbergLimits",
11
+ "PSD",
12
+ "AASHTO",
13
+ "USCS",
14
+ "create_aashto_classifier",
15
+ "create_uscs_classifier",
16
+ ]
16
17
 
17
18
 
18
19
  class SizeDistError(ZeroDivisionError):
19
20
  """Exception raised when size distribution is not provided."""
20
21
 
21
22
 
22
- @enum_repr
23
23
  class _Clf(tuple, enum.Enum):
24
24
 
25
25
  def __str__(self) -> str:
@@ -35,63 +35,191 @@ class _Clf(tuple, enum.Enum):
35
35
 
36
36
 
37
37
  class USCSSymbol(_Clf):
38
- """Unified Soil Classification System (USCS) symbols and descriptions."""
39
- G = GRAVEL = ("G", "Gravel")
40
- S = SAND = ("S", "Sand")
41
- M = SILT = ("M", "Silt")
42
- C = CLAY = ("C", "Clay")
43
- O = ORGANIC = ("O", "Organic")
44
- W = WELL_GRADED = ("W", "Well graded")
45
- P = POORLY_GRADED = ("P", "Poorly graded")
46
- L = LOW_PLASTICITY = ("L", "Low plasticity")
47
- H = HIGH_PLASTICITY = ("H", "High plasticity")
38
+ """
39
+ Unified Soil Classification System (USCS) symbols and descriptions.
40
+
41
+ Each member represents a USCS soil type, grading, or plasticity symbol.
42
+ Aliases are provided where applicable.
43
+ """
44
+
45
+ # General soil types
46
+ G = ("G", "Gravel")
47
+ """Gravel"""
48
+
49
+ GRAVEL = G
50
+
51
+ S = ("S", "Sand")
52
+ """Sand"""
53
+
54
+ SAND = S
55
+
56
+ M = ("M", "Silt")
57
+ """Silt"""
58
+
59
+ SILT = M
60
+
61
+ C = ("C", "Clay")
62
+ """Clay"""
63
+
64
+ CLAY = C
65
+
66
+ O = ("O", "Organic")
67
+ """Organic soil"""
68
+
69
+ ORGANIC = O
70
+
71
+ # Grading descriptors
72
+ W = ("W", "Well graded")
73
+ """Well graded"""
74
+
75
+ WELL_GRADED = W
76
+
77
+ P = ("P", "Poorly graded")
78
+ """Poorly graded"""
79
+
80
+ POORLY_GRADED = P
81
+
82
+ # Plasticity descriptors
83
+ L = ("L", "Low plasticity")
84
+ """Low plasticity"""
85
+
86
+ LOW_PLASTICITY = L
87
+
88
+ H = ("H", "High plasticity")
89
+ """High plasticity"""
90
+
91
+ HIGH_PLASTICITY = H
92
+
93
+ # Gravels
48
94
  GW = ("GW", "Well graded gravels")
95
+ """Well graded gravels"""
96
+
49
97
  GP = ("GP", "Poorly graded gravels")
98
+ """Poorly graded gravels"""
99
+
50
100
  GM = ("GM", "Silty gravels")
101
+ """Silty gravels"""
102
+
51
103
  GC = ("GC", "Clayey gravels")
104
+ """Clayey gravels"""
105
+
52
106
  GM_GC = ("GM-GC", "Gravelly clayey silt")
107
+ """Gravelly clayey silt"""
108
+
53
109
  GW_GM = ("GW-GM", "Well graded gravel with silt")
110
+ """Well graded gravel with silt"""
111
+
54
112
  GP_GM = ("GP-GM", "Poorly graded gravel with silt")
113
+ """Poorly graded gravel with silt"""
114
+
55
115
  GW_GC = ("GW-GC", "Well graded gravel with clay")
116
+ """Well graded gravel with clay"""
117
+
56
118
  GP_GC = ("GP-GC", "Poorly graded gravel with clay")
119
+ """Poorly graded gravel with clay"""
120
+
121
+ # Sands
57
122
  SW = ("SW", "Well graded sands")
123
+ """Well graded sands"""
124
+
58
125
  SP = ("SP", "Poorly graded sands")
126
+ """Poorly graded sands"""
127
+
59
128
  SM = ("SM", "Silty sands")
129
+ """Silty sands"""
130
+
60
131
  SC = ("SC", "Clayey sands")
132
+ """Clayey sands"""
133
+
61
134
  SM_SC = ("SM-SC", "Sandy clayey silt")
135
+ """Sandy clayey silt"""
136
+
62
137
  SW_SM = ("SW-SM", "Well graded sand with silt")
138
+ """Well graded sand with silt"""
139
+
63
140
  SP_SM = ("SP-SM", "Poorly graded sand with silt")
141
+ """Poorly graded sand with silt"""
142
+
64
143
  SW_SC = ("SW-SC", "Well graded sand with clay")
144
+ """Well graded sand with clay"""
145
+
65
146
  SP_SC = ("SP-SC", "Poorly graded sand with clay")
147
+ """Poorly graded sand with clay"""
148
+
149
+ # Silts and clays
66
150
  ML = ("ML", "Inorganic silts with low plasticity")
151
+ """Inorganic silts with low plasticity"""
152
+
67
153
  CL = ("CL", "Inorganic clays with low plasticity")
154
+ """Inorganic clays with low plasticity"""
155
+
68
156
  ML_CL = ("ML-CL", "Clayey silt with low plasticity")
157
+ """Clayey silt with low plasticity"""
158
+
69
159
  OL = ("OL", "Organic clays with low plasticity")
160
+ """Organic clays with low plasticity"""
161
+
70
162
  MH = ("MH", "Inorganic silts with high plasticity")
163
+ """Inorganic silts with high plasticity"""
164
+
71
165
  CH = ("CH", "Inorganic clays with high plasticity")
166
+ """Inorganic clays with high plasticity"""
167
+
72
168
  OH = ("OH", "Organic silts with high plasticity")
169
+ """Organic silts with high plasticity"""
170
+
73
171
  Pt = ("Pt", "Highly organic soils")
172
+ """Highly organic soils"""
74
173
 
75
174
 
76
175
  class AASHTOSymbol(_Clf):
77
- """AASHTO soil classification symbols and descriptions."""
176
+ """
177
+ AASHTO soil classification symbols and descriptions.
178
+
179
+ Each member represents a standard AASHTO soil class used in
180
+ pavement and highway engineering.
181
+ """
182
+
78
183
  A_1_a = ("A-1-a", "Stone fragments, gravel, and sand")
184
+ """Stone fragments, gravel, and sand"""
185
+
79
186
  A_1_b = ("A-1-b", "Stone fragments, gravel, and sand")
187
+ """Stone fragments, gravel, and sand"""
188
+
80
189
  A_3 = ("A-3", "Fine sand")
190
+ """Fine sand"""
191
+
81
192
  A_2_4 = ("A-2-4", "Silty or clayey gravel and sand")
193
+ """Silty or clayey gravel and sand"""
194
+
82
195
  A_2_5 = ("A-2-5", "Silty or clayey gravel and sand")
196
+ """Silty or clayey gravel and sand"""
197
+
83
198
  A_2_6 = ("A-2-6", "Silty or clayey gravel and sand")
199
+ """Silty or clayey gravel and sand"""
200
+
84
201
  A_2_7 = ("A-2-7", "Silty or clayey gravel and sand")
202
+ """Silty or clayey gravel and sand"""
203
+
85
204
  A_4 = ("A-4", "Silty soils")
205
+ """Silty soils"""
206
+
86
207
  A_5 = ("A-5", "Silty soils")
208
+ """Silty soils"""
209
+
87
210
  A_6 = ("A-6", "Clayey soils")
211
+ """Clayey soils"""
212
+
88
213
  A_7_5 = ("A-7-5", "Clayey soils")
214
+ """Clayey soils"""
215
+
89
216
  A_7_6 = ("A-7-6", "Clayey soils")
217
+ """Clayey soils"""
90
218
 
91
219
 
92
220
  class AtterbergLimits:
93
- """Represents the water contents at which soil changes from one state to
94
- the other.
221
+ """Represents the water contents at which soil changes from one state
222
+ to the other.
95
223
  """
96
224
 
97
225
  class __A_LINE:
@@ -99,67 +227,69 @@ class AtterbergLimits:
99
227
  def __get__(self, obj, objtype=None) -> float:
100
228
  return 0.73 * (obj.liquid_limit - 20.0)
101
229
 
102
- #: The ``A-line`` determines if a soil is clayey or silty.
103
- #: :math:`A = 0.73(LL - 20.0)`
104
230
  A_LINE = __A_LINE()
231
+ """The ``A-line`` determines if a soil is clayey or silty.
232
+
233
+ $A = 0.73(LL - 20.0)$
234
+ """
105
235
 
106
236
  def __init__(self, liquid_limit: float, plastic_limit: float):
107
237
  """
108
- :param liquid_limit: Water content beyond which soils flows under their
109
- own weight (%). It can also be defined as the
110
- minimum moisture content at which a soil flows upon
111
- application of a very small shear force.
112
- :type liquid_limit: float
113
-
114
- :param plastic_limit: Water content at which plastic deformation can be
115
- initiated (%). It is also the minimum water
116
- content at which soil can be rolled into a thread
117
- 3mm thick. (molded without breaking)
118
- :type plastic_limit: float
238
+ :param liquid_limit: Water content beyond which soils flows
239
+ under their own weight (%). It can also be
240
+ defined as the minimum moisture content at
241
+ which a soil flows upon application of a
242
+ very small shear force.
243
+ :param plastic_limit: Water content at which plastic deformation
244
+ can be initiated (%). It is also the
245
+ minimum water content at which soil can be
246
+ rolled into a thread 3mm thick (molded
247
+ without breaking).
119
248
  """
120
249
  self.liquid_limit = liquid_limit
121
250
  self.plastic_limit = plastic_limit
122
251
 
123
252
  @property
124
253
  def liquid_limit(self) -> float:
125
- """Water content beyond which soils flows under their own weight (%)."""
254
+ """
255
+ Water content beyond which soils flows under their own weight (%).
256
+ """
126
257
  return self._liquid_limit
127
258
 
128
259
  @liquid_limit.setter
129
- @validators.ge(0.0)
130
- def liquid_limit(self, val: float) -> None:
260
+ @validate_func_args
261
+ def liquid_limit(self, val: Annotated[float, MustBeNonNegative]):
131
262
  self._liquid_limit = val
132
263
 
133
264
  @property
134
265
  def plastic_limit(self) -> float:
135
- """Water content at which plastic deformation can be initiated (%)."""
266
+ """
267
+ Water content at which plastic deformation can be initiated (%).
268
+ """
136
269
  return self._plastic_limit
137
270
 
138
271
  @plastic_limit.setter
139
- @validators.ge(0.0)
140
- def plastic_limit(self, val: float) -> None:
272
+ @validate_func_args
273
+ def plastic_limit(self, val: Annotated[float, MustBeNonNegative]):
141
274
  if self.liquid_limit < val:
142
- msg = ErrorMsg(param_name="plastic_limit",
143
- param_value=val,
144
- param_value_bound=f"<{self.liquid_limit}",
145
- msg=f"plastic_limit: {val} cannot be greater than "
146
- f"liquid_limit: {self.liquid_limit}")
147
- raise ValidationError(msg)
275
+ msg = (
276
+ f"plastic_limit: {val} cannot be greater than "
277
+ f"liquid_limit: {self.liquid_limit}"
278
+ )
279
+ raise ValueError(msg)
148
280
 
149
281
  self._plastic_limit = val
150
282
 
151
283
  @property
152
284
  @round_(2)
153
285
  def plasticity_index(self) -> float:
154
- """Plasticity index (PI) is the range of water content over which the
155
- soil remains in the plastic state.
286
+ """Plasticity index (PI) is the range of water content over which
287
+ the soil remains in the plastic state.
156
288
 
157
289
  It is also the numerical difference between the liquid limit and
158
290
  plastic limit of the soil.
159
291
 
160
- :Equation:
161
-
162
- .. math:: PI = LL - PL
292
+ $$PI = LL - PL$$
163
293
  """
164
294
  return self.liquid_limit - self.plastic_limit
165
295
 
@@ -173,8 +303,8 @@ class AtterbergLimits:
173
303
  return self.plasticity_index > self.A_LINE
174
304
 
175
305
  def limit_plot_in_hatched_zone(self) -> bool:
176
- """Checks if soil sample plot in the hatched zone on the atterberg
177
- chart.
306
+ """Checks if soil sample plot in the hatched zone on the
307
+ atterberg chart.
178
308
  """
179
309
  return 4 <= self.plasticity_index <= 7 and 10 < self.liquid_limit < 30
180
310
 
@@ -182,17 +312,16 @@ class AtterbergLimits:
182
312
  def liquidity_index(self, nmc: float) -> float:
183
313
  r"""Return the liquidity index of the soil.
184
314
 
185
- Liquidity index of a soil indicates the nearness of its ``natural water
186
- content`` to its ``liquid limit``. When the soil is at the plastic
187
- limit its liquidity index is zero. Negative values of the liquidity
188
- index indicate that the soil is in a hard (desiccated) state. It is
189
- also known as Water-Plasticity ratio.
190
-
191
- :param float nmc: Moisture contents of the soil in natural condition.
315
+ $$I_l = \dfrac{w - PL}{PI} \cdot 100$$
192
316
 
193
- :Equation:
317
+ Liquidity index of a soil indicates the nearness of its
318
+ `natural water content` to its `liquid limit`. When the soil
319
+ is at the plastic limit its liquidity index is zero. Negative
320
+ values of the liquidity index indicate that the soil is in a
321
+ hard (desiccated) state. It is also known as Water-Plasticity
322
+ ratio.
194
323
 
195
- .. math:: I_l = \dfrac{w - PL}{PI} \cdot 100
324
+ :param nmc: Moisture contents of the soil in natural condition.
196
325
  """
197
326
  return ((nmc - self.plastic_limit) / self.plasticity_index) * 100.0
198
327
 
@@ -200,22 +329,21 @@ class AtterbergLimits:
200
329
  def consistency_index(self, nmc: float) -> float:
201
330
  r"""Return the consistency index of the soil.
202
331
 
203
- Consistency index indicates the consistency (firmness) of soil. It
204
- shows the nearness of the ``natural water content`` of the soil to its
205
- ``plastic limit``. When the soil is at the liquid limit, the
206
- consistency index is zero. The soil at consistency index of zero will
207
- be extremely soft and has negligible shear strength. A soil at a water
208
- content equal to the plastic limit has consistency index of 100%
209
- indicating that the soil is relatively firm. A consistency index of
210
- greater than 100% shows the soil is relatively strong
211
- (semi-solid state). A negative value indicate the soil is in the liquid
212
- state. It is also known as Relative Consistency.
213
-
214
- :param float nmc: Moisture contents of the soil in natural condition.
215
-
216
- :Equation:
217
-
218
- .. math:: I_c = \dfrac{LL - w}{PI} \cdot 100
332
+ $$I_c = \dfrac{LL - w}{PI} \cdot 100$$
333
+
334
+ Consistency index indicates the consistency (firmness) of soil.
335
+ It shows the nearness of the ``natural water content`` of the
336
+ soil to its `plastic limit`. When the soil is at the liquid
337
+ limit, the consistency index is zero. The soil at consistency
338
+ index of zero will be extremely soft and has negligible shear
339
+ strength. A soil at a water content equal to the plastic limit
340
+ has consistency index of 100% indicating that the soil is
341
+ relatively firm. A consistency index of greater than 100% shows
342
+ the soil is relatively strong (semi-solid state). A negative
343
+ value indicate the soil is in the liquid state. It is also known
344
+ as Relative Consistency.
345
+
346
+ :param nmc: Moisture contents of the soil in natural condition.
219
347
  """
220
348
  return ((self.liquid_limit - nmc) / self.plasticity_index) * 100.0
221
349
 
@@ -226,8 +354,7 @@ class _SizeDistribution:
226
354
  Features obtained from the Particle Size Distribution graph.
227
355
  """
228
356
 
229
- def __init__(self, d_10: float = 0.0,
230
- d_30: float = 0.0,
357
+ def __init__(self, d_10: float = 0.0, d_30: float = 0.0,
231
358
  d_60: float = 0.0):
232
359
  self.d_10 = d_10
233
360
  self.d_30 = d_30
@@ -245,12 +372,10 @@ class _SizeDistribution:
245
372
  return self.d_60 / self.d_10
246
373
 
247
374
  def grade(self, coarse_soil: USCSSymbol) -> USCSSymbol:
248
- """Grade of soil sample. Soil grade can either be well graded or poorly
249
- graded.
375
+ """Grade of soil sample. Soil grade can either be well graded or
376
+ poorly graded.
250
377
 
251
- :param coarse_soil: Coarse fraction of the soil sample. Valid arguments
252
- are :py:enum:mem:`~USCSSymbol.GRAVEL` and
253
- :py:enum:mem:`~USCSSymbol.SAND`.
378
+ :param coarse_soil: Coarse fraction of the soil sample.
254
379
  """
255
380
  if coarse_soil is USCSSymbol.GRAVEL:
256
381
  if 1 < self.coeff_of_curvature < 3 and self.coeff_of_uniformity >= 4:
@@ -268,27 +393,26 @@ class _SizeDistribution:
268
393
 
269
394
 
270
395
  class PSD:
271
- """Quantitative proportions by mass of various sizes of particles present
272
- in a soil.
396
+ """Quantitative proportions by mass of various sizes of particles
397
+ present in a soil.
273
398
  """
274
399
 
275
- def __init__(self, fines: float, sand: float,
276
- d_10: float = 0, d_30: float = 0, d_60: float = 0):
400
+ def __init__(
401
+ self,
402
+ fines: float,
403
+ sand: float,
404
+ d_10: float = 0,
405
+ d_30: float = 0,
406
+ d_60: float = 0,
407
+ ):
277
408
  """
278
409
  :param fines: Percentage of fines in soil sample (%) i.e. The
279
- percentage of soil sample passing through No. 200 sieve
280
- (0.075mm).
281
- :type fines: float
282
-
410
+ percentage of soil sample passing through No. 200
411
+ sieve (0.075mm).
283
412
  :param sand: Percentage of sand in soil sample (%).
284
- :type sand: float
285
-
286
413
  :param d_10: Diameter at which 10% of the soil by weight is finer.
287
- :type d_10: float
288
414
  :param d_30: Diameter at which 30% of the soil by weight is finer.
289
- :type d_30: float
290
415
  :param d_60: Diameter at which 60% of the soil by weight is finer.
291
- :type d_60: float
292
416
  """
293
417
  self.fines = fines
294
418
  self.sand = sand
@@ -311,11 +435,9 @@ class PSD:
311
435
  def coeff_of_curvature(self) -> float:
312
436
  r"""Coefficient of curvature of soil sample.
313
437
 
314
- :Equation:
315
-
316
- .. math:: C_c = \dfrac{D^2_{30}}{D_{60} \cdot D_{10}}
438
+ $$C_c = \dfrac{D^2_{30}}{D_{60} \cdot D_{10}}$$
317
439
 
318
- For the soil to be well graded, the value of :math:`C_c` must be
440
+ For the soil to be well graded, the value of $C_c$ must be
319
441
  between 1 and 3.
320
442
  """
321
443
  return self.size_dist.coeff_of_curvature
@@ -325,15 +447,14 @@ class PSD:
325
447
  def coeff_of_uniformity(self) -> float:
326
448
  r"""Coefficient of uniformity of soil sample.
327
449
 
328
- :Equation:
450
+ $$C_u = \dfrac{D_{60}}{D_{10}}$$
329
451
 
330
- .. math:: C_u = \dfrac{D_{60}}{D_{10}}
452
+ $C_u$ value greater than 4 to 6 classifies the soil as well
453
+ graded for gravels and sands respectively. When $C_u$ is less
454
+ than 4, it is classified as poorly graded or uniformly graded
455
+ soil.
331
456
 
332
- :math:`C_u` value greater than 4 to 6 classifies the soil as well
333
- graded for gravels and sands respectively. When :math:`C_u` is less
334
- than 4, it is classified as poorly graded or uniformly graded soil.
335
-
336
- Higher values of :math:`C_u` indicates that the soil mass consists of
457
+ Higher values of $C_u$ indicates that the soil mass consists of
337
458
  soil particles with different size ranges.
338
459
  """
339
460
  return self.size_dist.coeff_of_uniformity
@@ -343,70 +464,64 @@ class PSD:
343
464
  return any(self.size_dist)
344
465
 
345
466
  def grade(self) -> USCSSymbol:
346
- r"""Return the grade of the soil sample, either well graded or poorly
347
- graded.
467
+ r"""Return the grade of the soil sample, either well graded or
468
+ poorly graded.
348
469
 
349
470
  Conditions for a well-graded soil:
350
471
 
351
- :Equation:
352
-
353
- - :math:`1 \lt C_c \lt 3` and :math:`C_u \ge 4` (for gravels)
354
- - :math:`1 \lt C_c \lt 3` and :math:`C_u \ge 6` (for sands)
472
+ - $1 \lt C_c \lt 3$ and $C_u \ge 4$ (for gravels)
473
+ - $1 \lt C_c \lt 3$ and $C_u \ge 6$ (for sands)
355
474
  """
356
475
  return self.size_dist.grade(coarse_soil=self.coarse_material_type)
357
476
 
358
477
 
359
- class SoilClf(NamedTuple):
478
+ @dataclass
479
+ class AASHTOResult:
360
480
  symbol: str
481
+ symbol_no_group_idx: str
361
482
  description: str
483
+ group_index: str
362
484
 
363
485
 
364
486
  class AASHTO:
365
- r"""American Association of State Highway and Transportation Officials
366
- (AASHTO) classification system.
487
+ r"""American Association of State Highway and Transportation
488
+ Officials (AASHTO) classification system.
367
489
 
368
490
  The AASHTO classification system is useful for classifying soils for
369
- highways. It categorizes soils for highways based on particle size analysis
370
- and plasticity characteristics. It classifies both coarse-grained and
371
- fine-grained soils into eight main groups (A1-A7) with subgroups, along with
372
- a separate category (A8) for organic soils.
373
-
374
- - ``A1 ~ A3`` (Granular Materials) :math:`\le` 35% pass No. 200 sieve
375
- - ``A4 ~ A7`` (Silt-clay Materials) :math:`\ge` 36% pass No. 200 sieve
376
- - ``A8`` (Organic Materials)
491
+ highways. It categorizes soils for highways based on particle size
492
+ analysis and plasticity characteristics. It classifies both
493
+ coarse-grained and fine-grained soils into eight main groups (A1-A7)
494
+ with subgroups, along with a separate category (A8) for organic
495
+ soils.
377
496
 
378
- The Group Index ``(GI)`` is used to further evaluate soils within a group.
497
+ - `A1 ~ A3` (Granular Materials) $\le$ 35% pass No. 200 sieve
498
+ - `A4 ~ A7` (Silt-clay Materials) $\ge$ 36% pass No. 200 sieve
499
+ - `A8` (Organic Materials)
379
500
 
380
- .. note::
501
+ The Group Index `(GI)` is used to further evaluate soils within a
502
+ group.
381
503
 
382
- The ``GI`` must be mentioned even when it is zero, to indicate that the
383
- soil has been classified as per AASHTO system.
504
+ !!! note
384
505
 
385
- :Equation:
506
+ The `GI` must be mentioned even when it is zero, to indicate
507
+ that the soil has been classified as per AASHTO system.
386
508
 
387
- .. math::
388
509
 
389
- GI = (F_{200} - 35)[0.2 + 0.005(LL - 40)] + 0.01(F_{200} - 15)(PI - 10)
510
+ $$
511
+ GI = (F_{200} - 35)[0.2 + 0.005(LL - 40)] + 0.01(F_{200} - 15)(PI - 10)
512
+ $$
390
513
  """
391
514
 
392
- def __init__(self, atterberg_limits: AtterbergLimits,
393
- fines: float, add_group_idx: bool = True):
515
+ def __init__(self, atterberg_limits: AtterbergLimits, fines: float):
394
516
  """
395
517
  :param atterberg_limits: Atterberg limits of soil sample.
396
- :type atterberg_limits: AtterbergLimits
397
518
 
398
- :param fines: Percentage of fines in soil sample (%) i.e. The percentage
399
- of soil sample passing through No. 200 sieve (0.075mm).
400
- :type fines: float
401
-
402
- :param add_group_idx: Used to indicate whether the group index should
403
- be added to the classification or not, defaults
404
- to True.
405
- :type add_group_idx: bool, optional
519
+ :param fines: Percentage of fines in soil sample (%) i.e. The
520
+ percentage of soil sample passing through No. 200
521
+ sieve (0.075mm).
406
522
  """
407
523
  self.atterberg_limits = atterberg_limits
408
524
  self.fines = fines
409
- self.add_group_idx = add_group_idx
410
525
 
411
526
  @property
412
527
  def fines(self) -> float:
@@ -414,14 +529,13 @@ class AASHTO:
414
529
  return self._fines
415
530
 
416
531
  @fines.setter
417
- @validators.ge(0.0)
418
- def fines(self, val: float) -> None:
532
+ @validate_func_args
533
+ def fines(self, val: Annotated[float, MustBeNonNegative]):
419
534
  self._fines = val
420
535
 
421
536
  @round_(ndigits=0)
422
537
  def group_index(self) -> float:
423
538
  """Return the Group Index (GI) of the soil sample."""
424
-
425
539
  liquid_lmt = self.atterberg_limits.liquid_limit
426
540
  plasticity_idx = self.atterberg_limits.plasticity_index
427
541
  fines = self.fines
@@ -433,16 +547,20 @@ class AASHTO:
433
547
 
434
548
  return x_1 * (0.2 + 0.005 * x_2) + 0.01 * x_3 * x_4
435
549
 
436
- def classify(self) -> SoilClf:
550
+ def classify(self) -> AASHTOResult:
437
551
  """Return the AASHTO classification of the soil."""
438
552
  soil_clf = self._classify()
439
553
 
440
- symbol, description = soil_clf.symbol, soil_clf.description
441
-
442
- if self.add_group_idx:
443
- symbol = f"{symbol}({self.group_index():.0f})"
554
+ symbol_no_grp_idx, description = soil_clf.symbol, soil_clf.description
555
+ group_idx = f"{self.group_index():.0f}"
556
+ symbol = f"{symbol_no_grp_idx}({group_idx})"
444
557
 
445
- return SoilClf(symbol, description)
558
+ return AASHTOResult(
559
+ symbol=symbol,
560
+ symbol_no_group_idx=symbol_no_grp_idx,
561
+ description=description,
562
+ group_index=group_idx,
563
+ )
446
564
 
447
565
  def _classify(self) -> AASHTOSymbol:
448
566
  # Silts A4-A7
@@ -503,46 +621,52 @@ class AASHTO:
503
621
  return soil_clf
504
622
 
505
623
 
624
+ @dataclass
625
+ class USCSResult:
626
+ symbol: str
627
+ description: str
628
+
629
+
506
630
  class USCS:
507
631
  """Unified Soil Classification System (USCS).
508
632
 
509
- The Unified Soil Classification System, initially developed by Casagrande
510
- in 1948 and later modified in 1952, is widely utilized in engineering
511
- projects involving soils. It is the most popular system for soil
512
- classification and is similar to Casagrande's Classification System. The
513
- system relies on particle size analysis and atterberg limits for
514
- classification.
633
+ The Unified Soil Classification System, initially developed by
634
+ Casagrande in 1948 and later modified in 1952, is widely utilized in
635
+ engineering projects involving soils. It is the most popular system
636
+ for soil classification and is similar to Casagrande's
637
+ Classification System. The system relies on particle size analysis
638
+ and atterberg limits for classification.
515
639
 
516
640
  In this system, soils are first classified into two categories:
517
641
 
518
642
  - Coarse grained soils: If more than 50% of the soils is retained on
519
643
  No. 200 (0.075 mm) sieve, it is designated as coarse-grained soil.
520
644
 
521
- - Fine grained soils: If more than 50% of the soil passes through No. 200
522
- sieve, it is designated as fine-grained soil.
645
+ - Fine grained soils: If more than 50% of the soil passes through
646
+ No. 200 sieve, it is designated as fine-grained soil.
523
647
 
524
- Highly Organic soils are identified by visual inspection. These soils are
525
- termed as Peat. (:math:`P_t`)
648
+ Highly Organic soils are identified by visual inspection. These
649
+ soils are termed as Peat ($P_t$).
526
650
  """
527
651
 
528
- def __init__(self, atterberg_limits: AtterbergLimits,
529
- psd: PSD, organic=False):
652
+ def __init__(
653
+ self,
654
+ atterberg_limits: AtterbergLimits,
655
+ psd: PSD,
656
+ organic=False,
657
+ ):
530
658
  """
531
659
  :param atterberg_limits: Atterberg limits of the soil.
532
- :type atterberg_limits: AtterbergLimits
533
660
 
534
661
  :param psd: Particle size distribution of the soil.
535
- :type psd: PSD
536
662
 
537
- :param organic: Indicates whether soil is organic or not, defaults to
538
- False.
539
- :type organic: bool, optional
663
+ :param organic: Indicates whether soil is organic or not.
540
664
  """
541
665
  self.atterberg_limits = atterberg_limits
542
666
  self.psd = psd
543
667
  self.organic = organic
544
668
 
545
- def classify(self) -> SoilClf:
669
+ def classify(self):
546
670
  """Return the USCS classification of the soil."""
547
671
  soil_clf = self._classify()
548
672
 
@@ -551,15 +675,17 @@ class USCS:
551
675
  soil_clf = USCSSymbol[soil_clf]
552
676
 
553
677
  if isinstance(soil_clf, USCSSymbol):
554
- return SoilClf(soil_clf.symbol, soil_clf.description)
555
-
556
- # Handling tuple or list case for dual classification
557
- first_clf, second_clf = map(lambda clf: USCSSymbol[clf], soil_clf)
558
-
559
- comb_symbol = f"{first_clf.symbol},{second_clf.symbol}"
560
- comb_desc = f"{first_clf.description},{second_clf.description}"
678
+ soil_clf = USCSResult(
679
+ symbol=soil_clf.symbol, description=soil_clf.description
680
+ )
681
+ else:
682
+ # Handling tuple or list case for dual classification
683
+ first_clf, second_clf = map(lambda clf: USCSSymbol[clf], soil_clf)
684
+ comb_symbol = f"{first_clf.symbol},{second_clf.symbol}"
685
+ comb_desc = f"{first_clf.description},{second_clf.description}"
686
+ soil_clf = USCSResult(symbol=comb_symbol, description=comb_desc)
561
687
 
562
- return SoilClf(comb_symbol, comb_desc)
688
+ return soil_clf
563
689
 
564
690
  def _classify(self) -> USCSSymbol | str | Sequence[str]:
565
691
  # Fine-grained, Run Atterberg
@@ -578,21 +704,17 @@ class USCS:
578
704
  # Above A-line and PI > 7
579
705
  if self.atterberg_limits.above_A_LINE() and plasticity_idx > 7.0:
580
706
  soil_clf = USCSSymbol.CL
581
-
582
707
  # Limit plot in hatched area on plasticity chart
583
708
  elif self.atterberg_limits.limit_plot_in_hatched_zone():
584
709
  soil_clf = USCSSymbol.ML_CL
585
-
586
710
  # Below A-line or PI < 4
587
711
  else:
588
712
  soil_clf = USCSSymbol.OL if self.organic else USCSSymbol.ML
589
-
590
713
  # High LL
591
714
  else:
592
715
  # Above A-Line
593
716
  if self.atterberg_limits.above_A_LINE():
594
717
  soil_clf = USCSSymbol.CH
595
-
596
718
  # Below A-Line
597
719
  else:
598
720
  soil_clf = USCSSymbol.OH if self.organic else USCSSymbol.MH
@@ -628,11 +750,12 @@ class USCS:
628
750
  soil_clf = self._dual_soil_classifier()
629
751
  else:
630
752
  fine_material_type = self.atterberg_limits.fine_material_type
631
-
632
- soil_clf = (f"{coarse_material_type}{USCSSymbol.WELL_GRADED}_"
633
- f"{coarse_material_type}{fine_material_type}",
634
- f"{coarse_material_type}{USCSSymbol.POORLY_GRADED}_"
635
- f"{coarse_material_type}{fine_material_type}")
753
+ soil_clf = (
754
+ f"{coarse_material_type}{USCSSymbol.WELL_GRADED}_"
755
+ f"{coarse_material_type}{fine_material_type}",
756
+ f"{coarse_material_type}{USCSSymbol.POORLY_GRADED}_"
757
+ f"{coarse_material_type}{fine_material_type}",
758
+ )
636
759
 
637
760
  # Less than 5% pass No. 200 sieve
638
761
  # Obtain Cc and Cu from grain size graph
@@ -641,8 +764,10 @@ class USCS:
641
764
  soil_clf = f"{coarse_material_type}{self.psd.grade()}"
642
765
 
643
766
  else:
644
- soil_clf = (f"{coarse_material_type}{USCSSymbol.WELL_GRADED}",
645
- f"{coarse_material_type}{USCSSymbol.POORLY_GRADED}")
767
+ soil_clf = (
768
+ f"{coarse_material_type}{USCSSymbol.WELL_GRADED}",
769
+ f"{coarse_material_type}{USCSSymbol.POORLY_GRADED}",
770
+ )
646
771
 
647
772
  return soil_clf
648
773
 
@@ -650,104 +775,75 @@ class USCS:
650
775
  fine_material_type = self.atterberg_limits.fine_material_type
651
776
  coarse_material_type = self.psd.coarse_material_type
652
777
 
653
- return (f"{coarse_material_type}{self.psd.grade()}_"
654
- f"{coarse_material_type}{fine_material_type}")
655
-
656
-
657
- @enum_repr
658
- class ClfType(enum.StrEnum):
659
- """Enumeration of soil classification types."""
660
- AASHTO = enum.auto()
661
- USCS = enum.auto()
662
-
778
+ return (
779
+ f"{coarse_material_type}{self.psd.grade()}_"
780
+ f"{coarse_material_type}{fine_material_type}"
781
+ )
663
782
 
664
- def create_soil_classifier(liquid_limit: float,
665
- plastic_limit: float,
666
- fines: float,
667
- sand: Optional[float] = None,
668
- d_10: float = 0, d_30: float = 0, d_60: float = 0,
669
- add_group_idx: bool = True,
670
- organic: bool = False,
671
- clf_type: Optional[ClfType | str] = None
672
- ) -> AASHTO | USCS:
673
- """ A factory function that encapsulates the creation of a soil classifier.
674
783
 
675
- :param liquid_limit: Water content beyond which soils flows under their own
676
- weight (%). It can also be defined as the minimum
677
- moisture content at which a soil flows upon application
678
- of a very small shear force.
679
- :type liquid_limit: float
784
+ def create_aashto_classifier(
785
+ liquid_limit: float, plastic_limit: float, fines: float
786
+ ) -> AASHTO:
787
+ """A helper function that encapsulates the creation of a AASHTO
788
+ classifier.
680
789
 
681
- :param plastic_limit: Water content at which plastic deformation can be
682
- initiated (%). It is also the minimum water content at
683
- which soil can be rolled into a thread 3mm thick.
684
- (molded without breaking)
685
- :type plastic_limit: float
790
+ :param liquid_limit: Water content beyond which soils flows under
791
+ their own weight (%). It can also be defined as
792
+ the minimum moisture content at which a soil
793
+ flows upon application of a very small shear
794
+ force.
686
795
 
687
- :param fines: Percentage of fines in soil sample (%) i.e. The percentage of
688
- soil sample passing through No. 200 sieve (0.075mm).
689
- :type fines: float
796
+ :param plastic_limit: Water content at which plastic deformation can
797
+ be initiated (%). It is also the minimum water
798
+ content at which soil can be rolled into a
799
+ thread 3mm thick (molded without breaking).
690
800
 
691
- :param sand: Percentage of sand in soil sample (%). This is optional for
692
- :class:`AASHTO` classification.
693
- :type sand: float, optional
801
+ :param fines: Percentage of fines in soil sample (%) i.e. The
802
+ percentage of soil sample passing through No. 200
803
+ sieve (0.075mm).
804
+ """
805
+ atterberg_limits = AtterbergLimits(liquid_limit, plastic_limit)
806
+ return AASHTO(atterberg_limits, fines)
807
+
808
+
809
+ def create_uscs_classifier(
810
+ liquid_limit: float,
811
+ plastic_limit: float,
812
+ fines: float,
813
+ sand: float,
814
+ d_10: float = 0,
815
+ d_30: float = 0,
816
+ d_60: float = 0,
817
+ organic: bool = False,
818
+ ):
819
+ """A helper function that encapsulates the creation of a USCS
820
+ classifier.
821
+
822
+ :param liquid_limit: Water content beyond which soils flows under
823
+ their own weight (%). It can also be defined as
824
+ the minimum moisture content at which a soil
825
+ flows upon application of a very small shear
826
+ force.
827
+
828
+ :param plastic_limit: Water content at which plastic deformation can
829
+ be initiated (%). It is also the minimum water
830
+ content at which soil can be rolled into a
831
+ thread 3mm thick. (molded without breaking)
832
+
833
+ :param fines: Percentage of fines in soil sample (%) i.e. The
834
+ percentage of soil sample passing through No. 200
835
+ sieve (0.075mm).
836
+
837
+ :param sand: Percentage of sand in soil sample (%).
694
838
 
695
839
  :param d_10: Diameter at which 10% of the soil by weight is finer.
696
- :type d_10: float, optional
697
840
 
698
841
  :param d_30: Diameter at which 30% of the soil by weight is finer.
699
- :type d_30: float, optional
700
842
 
701
843
  :param d_60: Diameter at which 60% of the soil by weight is finer.
702
- :type d_60: float, optional
703
-
704
- :param add_group_idx: Used to indicate whether the group index should
705
- be added to the classification or not, defaults to
706
- True.
707
- :type add_group_idx: bool, optional
708
-
709
- :param organic: Indicates whether soil is organic or not, defaults to False.
710
- :type organic: bool, optional
711
844
 
712
- :param clf_type: Used to indicate which type of soil classifier should be
713
- used, defaults to None.
714
- :type clf_type: ClfType | str
715
-
716
- :raises ValueError: Raises ValueError if ``clf_type`` is not supported or
717
- None
718
- :raises ValueError: Raises ValueError if ``sand`` is not provided for
719
- :class:`USCS` classification.
845
+ :param organic: Indicates whether soil is organic or not.
720
846
  """
721
- msg = ErrorMsg(param_name="clf_type",
722
- param_value=clf_type,
723
- symbol="in",
724
- param_value_bound=list(ClfType))
725
-
726
- if clf_type is None:
727
- raise ValidationError(msg)
728
-
729
- try:
730
- clf_type = ClfType(str(clf_type).casefold())
731
- except ValueError as e:
732
- raise ValidationError(msg) from e
733
-
734
- atterberg_lmts = AtterbergLimits(liquid_limit=liquid_limit,
735
- plastic_limit=plastic_limit)
736
-
737
- if clf_type == ClfType.AASHTO:
738
- clf = AASHTO(atterberg_limits=atterberg_lmts,
739
- fines=fines,
740
- add_group_idx=add_group_idx)
741
- return clf
742
-
743
- # USCS classification
744
- if not sand:
745
- msg = ErrorMsg(param_name="sand",
746
- param_value=sand,
747
- msg="sand must be specified for USCS classification")
748
- raise ValidationError(msg)
749
-
847
+ atterberg_lmts = AtterbergLimits(liquid_limit, plastic_limit)
750
848
  psd = PSD(fines=fines, sand=sand, d_10=d_10, d_30=d_30, d_60=d_60)
751
- clf = USCS(atterberg_limits=atterberg_lmts, psd=psd, organic=organic)
752
-
753
- return clf
849
+ return USCS(atterberg_limits=atterberg_lmts, psd=psd, organic=organic)