geolysis 0.3.0__py3-none-any.whl → 0.4.3__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/__init__.py +1 -3
- geolysis/foundation.py +393 -0
- geolysis/soil_classifier.py +781 -0
- geolysis/spt.py +492 -0
- {geolysis-0.3.0.dist-info → geolysis-0.4.3.dist-info}/LICENSE.txt +21 -21
- geolysis-0.4.3.dist-info/METADATA +193 -0
- geolysis-0.4.3.dist-info/RECORD +9 -0
- {geolysis-0.3.0.dist-info → geolysis-0.4.3.dist-info}/WHEEL +1 -1
- geolysis/core/__init__.py +0 -9
- geolysis/core/abc_4_cohl_soils.py +0 -495
- geolysis/core/constants.py +0 -47
- geolysis/core/estimators.py +0 -549
- geolysis/core/foundation.py +0 -543
- geolysis/core/soil_classifier.py +0 -859
- geolysis/core/spt.py +0 -633
- geolysis/core/utils.py +0 -113
- geolysis-0.3.0.dist-info/METADATA +0 -223
- geolysis-0.3.0.dist-info/RECORD +0 -14
- {geolysis-0.3.0.dist-info → geolysis-0.4.3.dist-info}/top_level.txt +0 -0
geolysis/core/soil_classifier.py
DELETED
@@ -1,859 +0,0 @@
|
|
1
|
-
from abc import abstractmethod
|
2
|
-
from typing import NamedTuple, Protocol
|
3
|
-
|
4
|
-
from .constants import ERROR_TOL
|
5
|
-
from .utils import isclose, round_
|
6
|
-
|
7
|
-
__all__ = ["AtterbergLimits", "PSD", "AASHTO", "USCS"]
|
8
|
-
|
9
|
-
|
10
|
-
class PSDAggSumError(ValueError):
|
11
|
-
pass
|
12
|
-
|
13
|
-
|
14
|
-
class SoilGradationError(ZeroDivisionError):
|
15
|
-
pass
|
16
|
-
|
17
|
-
|
18
|
-
class _SoilClassifier(Protocol):
|
19
|
-
|
20
|
-
@property
|
21
|
-
@abstractmethod
|
22
|
-
def soil_class(self): ...
|
23
|
-
|
24
|
-
@property
|
25
|
-
@abstractmethod
|
26
|
-
def soil_desc(self): ...
|
27
|
-
|
28
|
-
|
29
|
-
#: USCS symbol for gravel.
|
30
|
-
GRAVEL: str = "G"
|
31
|
-
|
32
|
-
#: USCS symbol for sand.
|
33
|
-
SAND: str = "S"
|
34
|
-
|
35
|
-
#: USCS symbol for silt.
|
36
|
-
SILT: str = "M"
|
37
|
-
|
38
|
-
#: USCS symbol for clay.
|
39
|
-
CLAY: str = "C"
|
40
|
-
|
41
|
-
#: USCS symbol for organic material.
|
42
|
-
ORGANIC: str = "O"
|
43
|
-
|
44
|
-
#: USCS symbol for well-graded material.
|
45
|
-
WELL_GRADED: str = "W"
|
46
|
-
|
47
|
-
#: USCS symbol for poorly-graded material.
|
48
|
-
POORLY_GRADED: str = "P"
|
49
|
-
|
50
|
-
#: USCS symbol for low soil plasticity.
|
51
|
-
LOW_PLASTICITY: str = "L"
|
52
|
-
|
53
|
-
#: USCS symbol for high soil plasticity.
|
54
|
-
HIGH_PLASTICITY: str = "H"
|
55
|
-
|
56
|
-
|
57
|
-
class _SoilGradation(NamedTuple):
|
58
|
-
"""Features obtained from the Particle Size Distribution graph."""
|
59
|
-
|
60
|
-
d_10: float
|
61
|
-
d_30: float
|
62
|
-
d_60: float
|
63
|
-
|
64
|
-
ERR_MSG: str = "d_10, d_30, and d_60 cannot be 0"
|
65
|
-
|
66
|
-
@property
|
67
|
-
@round_
|
68
|
-
def coeff_of_curvature(self) -> float:
|
69
|
-
try:
|
70
|
-
return (self.d_30**2) / (self.d_60 * self.d_10)
|
71
|
-
except ZeroDivisionError as e:
|
72
|
-
raise SoilGradationError(self.ERR_MSG) from e
|
73
|
-
|
74
|
-
@property
|
75
|
-
@round_
|
76
|
-
def coeff_of_uniformity(self) -> float:
|
77
|
-
try:
|
78
|
-
return self.d_60 / self.d_10
|
79
|
-
except ZeroDivisionError as e:
|
80
|
-
raise SoilGradationError(self.ERR_MSG) from e
|
81
|
-
|
82
|
-
def grade(self, coarse_soil: str) -> str:
|
83
|
-
"""Grade of soil sample. Soil grade can either be ``WELL_GRADED`` or
|
84
|
-
``POORLY_GRADED``.
|
85
|
-
|
86
|
-
Parameters
|
87
|
-
----------
|
88
|
-
coarse_soil : str
|
89
|
-
Coarse fraction of the soil sample. Valid arguments are :data:`GRAVEL`
|
90
|
-
or :data:`SAND`.
|
91
|
-
"""
|
92
|
-
|
93
|
-
if coarse_soil == GRAVEL and (
|
94
|
-
1 < self.coeff_of_curvature < 3 and self.coeff_of_uniformity >= 4
|
95
|
-
):
|
96
|
-
grade = WELL_GRADED
|
97
|
-
|
98
|
-
elif coarse_soil == SAND and (
|
99
|
-
1 < self.coeff_of_curvature < 3 and self.coeff_of_uniformity >= 6
|
100
|
-
):
|
101
|
-
grade = WELL_GRADED
|
102
|
-
|
103
|
-
else:
|
104
|
-
grade = POORLY_GRADED
|
105
|
-
|
106
|
-
return grade
|
107
|
-
|
108
|
-
|
109
|
-
class AtterbergLimits:
|
110
|
-
"""Water contents at which soil changes from one state to the other.
|
111
|
-
|
112
|
-
In 1911, a Swedish agriculture engineer ``Atterberg`` mentioned that a
|
113
|
-
fined-grained soil can exist in four states, namely, liquid, plastic,
|
114
|
-
semi-solid or solid state.
|
115
|
-
|
116
|
-
The main use of Atterberg Limits is in the classification of soils.
|
117
|
-
|
118
|
-
Parameters
|
119
|
-
----------
|
120
|
-
liquid_limit : float
|
121
|
-
Water content beyond which soils flows under their own weight.
|
122
|
-
It can also be defined as the minimum moisture content at which
|
123
|
-
a soil flows upon application of a very small shear force.
|
124
|
-
|
125
|
-
plastic_limit : float
|
126
|
-
Water content at which plastic deformation can be initiated. It
|
127
|
-
is also the minimum water content at which soil can be rolled into
|
128
|
-
a thread 3mm thick. (molded without breaking)
|
129
|
-
|
130
|
-
Attributes
|
131
|
-
----------
|
132
|
-
plasticity_index : float
|
133
|
-
A_line : float
|
134
|
-
type_of_fines : str
|
135
|
-
|
136
|
-
Methods
|
137
|
-
-------
|
138
|
-
above_A_LINE
|
139
|
-
limit_plot_in_hatched_zone
|
140
|
-
liquidity_index
|
141
|
-
consistency_index
|
142
|
-
|
143
|
-
Examples
|
144
|
-
--------
|
145
|
-
>>> from geolysis.core.soil_classifier import AtterbergLimits as AL
|
146
|
-
|
147
|
-
>>> atterberg_limits = AL(liquid_limit=55.44, plastic_limit=33.31)
|
148
|
-
>>> atterberg_limits.plasticity_index
|
149
|
-
22.13
|
150
|
-
>>> atterberg_limits.A_line
|
151
|
-
25.87
|
152
|
-
|
153
|
-
>>> soil_type = atterberg_limits.type_of_fines
|
154
|
-
>>> soil_type
|
155
|
-
'M'
|
156
|
-
>>> USCS.SOIL_DESCRIPTIONS[soil_type]
|
157
|
-
'Silt'
|
158
|
-
>>> atterberg_limits.above_A_LINE()
|
159
|
-
False
|
160
|
-
>>> atterberg_limits.limit_plot_in_hatched_zone()
|
161
|
-
False
|
162
|
-
|
163
|
-
Negative values of liquidity index indicates that the soil is in a hard state.
|
164
|
-
|
165
|
-
>>> atterberg_limits.liquidity_index(nmc=15.26)
|
166
|
-
-81.56
|
167
|
-
|
168
|
-
A consistency index greater than 100% shows the soil is relatively strong.
|
169
|
-
|
170
|
-
>>> atterberg_limits.consistency_index(nmc=15.26)
|
171
|
-
181.56
|
172
|
-
"""
|
173
|
-
|
174
|
-
def __init__(self, liquid_limit: float, plastic_limit: float):
|
175
|
-
self.liquid_limit = liquid_limit
|
176
|
-
self.plastic_limit = plastic_limit
|
177
|
-
|
178
|
-
@property
|
179
|
-
@round_
|
180
|
-
def plasticity_index(self) -> float:
|
181
|
-
"""Plasticity index (PI) is the range of water content over which the
|
182
|
-
soil remains in the plastic state.
|
183
|
-
|
184
|
-
It is also the numerical difference between the liquid limit and plastic
|
185
|
-
limit of the soil.
|
186
|
-
|
187
|
-
.. math:: PI = LL - PL
|
188
|
-
"""
|
189
|
-
return self.liquid_limit - self.plastic_limit
|
190
|
-
|
191
|
-
@property
|
192
|
-
@round_
|
193
|
-
def A_line(self) -> float:
|
194
|
-
"""The ``A-line`` is used to determine if a soil is clayey or silty.
|
195
|
-
|
196
|
-
.. math:: A = 0.73(LL - 20)
|
197
|
-
"""
|
198
|
-
return 0.73 * (self.liquid_limit - 20)
|
199
|
-
|
200
|
-
@property
|
201
|
-
def type_of_fines(self) -> str:
|
202
|
-
"""Determines whether the soil is either :data:`CLAY` or :data:`SILT`."""
|
203
|
-
return CLAY if self.above_A_LINE() else SILT
|
204
|
-
|
205
|
-
def above_A_LINE(self) -> bool:
|
206
|
-
"""Checks if the soil sample is above A-Line."""
|
207
|
-
return self.plasticity_index > self.A_line
|
208
|
-
|
209
|
-
def limit_plot_in_hatched_zone(self) -> bool:
|
210
|
-
"""Checks if soil sample plot in the hatched zone on the atterberg
|
211
|
-
chart.
|
212
|
-
"""
|
213
|
-
return 4 <= self.plasticity_index <= 7 and 10 < self.liquid_limit < 30
|
214
|
-
|
215
|
-
@round_
|
216
|
-
def liquidity_index(self, nmc: float) -> float:
|
217
|
-
r"""Return the liquidity index of the soil.
|
218
|
-
|
219
|
-
Liquidity index of a soil indicates the nearness of its ``natural water
|
220
|
-
content`` to its ``liquid limit``. When the soil is at the plastic limit
|
221
|
-
its liquidity index is zero. Negative values of the liquidity index
|
222
|
-
indicate that the soil is in a hard (desiccated) state. It is also known
|
223
|
-
as Water-Plasticity ratio.
|
224
|
-
|
225
|
-
Parameters
|
226
|
-
----------
|
227
|
-
nmc : float
|
228
|
-
Moisture contents of the soil in natural condition. (Natural Moisture
|
229
|
-
Content)
|
230
|
-
|
231
|
-
Notes
|
232
|
-
-----
|
233
|
-
The ``liquidity index`` is given by the formula:
|
234
|
-
|
235
|
-
.. math:: I_l = \dfrac{w - PL}{PI} \cdot 100
|
236
|
-
"""
|
237
|
-
return ((nmc - self.plastic_limit) / self.plasticity_index) * 100
|
238
|
-
|
239
|
-
@round_
|
240
|
-
def consistency_index(self, nmc: float) -> float:
|
241
|
-
r"""Return the consistency index of the soil.
|
242
|
-
|
243
|
-
Consistency index indicates the consistency (firmness) of soil. It shows
|
244
|
-
the nearness of the ``natural water content`` of the soil to its
|
245
|
-
``plastic limit``. When the soil is at the liquid limit, the consistency
|
246
|
-
index is zero. The soil at consistency index of zero will be extremely
|
247
|
-
soft and has negligible shear strength. A soil at a water content equal
|
248
|
-
to the plastic limit has consistency index of 100% indicating that the
|
249
|
-
soil is relatively firm. A consistency index of greater than 100% shows
|
250
|
-
the soil is relatively strong (semi-solid state). A negative value indicate
|
251
|
-
the soil is in the liquid state. It is also known as Relative Consistency.
|
252
|
-
|
253
|
-
Parameters
|
254
|
-
----------
|
255
|
-
nmc : float
|
256
|
-
Moisture contents of the soil in natural condition. (Natural Moisture
|
257
|
-
Content)
|
258
|
-
|
259
|
-
Notes
|
260
|
-
-----
|
261
|
-
The ``consistency index`` is given by the formula:
|
262
|
-
|
263
|
-
.. math:: I_c = \dfrac{LL - w}{PI} \cdot 100
|
264
|
-
"""
|
265
|
-
return ((self.liquid_limit - nmc) / self.plasticity_index) * 100.0
|
266
|
-
|
267
|
-
|
268
|
-
class PSD:
|
269
|
-
r"""Quantitative proportions by mass of various sizes of particles present
|
270
|
-
in a soil.
|
271
|
-
|
272
|
-
Particle Size Distribution is a method of separation of soils into
|
273
|
-
different fractions using a stack of sieves to measure the size of the
|
274
|
-
particles in a sample and graphing the results to illustrate the
|
275
|
-
distribution of the particle sizes.
|
276
|
-
|
277
|
-
Parameters
|
278
|
-
----------
|
279
|
-
fines : float
|
280
|
-
Percentage of fines in soil sample i.e. the percentage of soil sample
|
281
|
-
passing through No. 200 sieve (0.075mm)
|
282
|
-
sand : float
|
283
|
-
Percentage of sand in soil sample.
|
284
|
-
gravel : float
|
285
|
-
Percentage of gravel in soil sample.
|
286
|
-
d_10 : float, unit=millimetre
|
287
|
-
Diameter at which 10% of the soil by weight is finer.
|
288
|
-
d_30 : float, unit=millimetre
|
289
|
-
Diameter at which 30% of the soil by weight is finer.
|
290
|
-
d_60 : float, unit=millimetre
|
291
|
-
Diameter at which 60% of the soil by weight is finer.
|
292
|
-
|
293
|
-
Attributes
|
294
|
-
----------
|
295
|
-
coeff_of_curvature : float
|
296
|
-
coeff_of_uniformity : float
|
297
|
-
type_of_coarse : str
|
298
|
-
|
299
|
-
|
300
|
-
Raises
|
301
|
-
------
|
302
|
-
PSDAggSumError
|
303
|
-
Raised when soil aggregates does not approximately sum up to 100%.
|
304
|
-
SoilGradationError
|
305
|
-
Raised when d_10, d_30, and d_60 are not provided.
|
306
|
-
|
307
|
-
Examples
|
308
|
-
--------
|
309
|
-
>>> from geolysis.core.soil_classifier import PSD
|
310
|
-
|
311
|
-
>>> psd = PSD(fines=30.25, sand=53.55, gravel=16.20)
|
312
|
-
>>> soil_type = psd.type_of_coarse
|
313
|
-
>>> soil_type
|
314
|
-
'S'
|
315
|
-
>>> USCS.SOIL_DESCRIPTIONS[soil_type]
|
316
|
-
'Sand'
|
317
|
-
|
318
|
-
Raises error because parameters d_10, d_30, and d_60 are not provided.
|
319
|
-
|
320
|
-
>>> psd.coeff_of_curvature
|
321
|
-
Traceback (most recent call last):
|
322
|
-
...
|
323
|
-
SoilGradationError: d_10, d_30, and d_60 cannot be 0
|
324
|
-
|
325
|
-
>>> psd.coeff_of_uniformity
|
326
|
-
Traceback (most recent call last):
|
327
|
-
...
|
328
|
-
SoilGradationError: d_10, d_30, and d_60 cannot be 0
|
329
|
-
|
330
|
-
>>> psd = PSD(fines=10.29, sand=81.89, gravel=7.83,
|
331
|
-
... d_10=0.07, d_30=0.30, d_60=0.8)
|
332
|
-
>>> psd.d_10, psd.d_30, psd.d_60
|
333
|
-
(0.07, 0.3, 0.8)
|
334
|
-
>>> psd.coeff_of_curvature
|
335
|
-
1.61
|
336
|
-
>>> psd.coeff_of_uniformity
|
337
|
-
11.43
|
338
|
-
|
339
|
-
>>> soil_grade = psd.grade()
|
340
|
-
>>> soil_grade
|
341
|
-
'W'
|
342
|
-
>>> USCS.SOIL_DESCRIPTIONS[soil_grade]
|
343
|
-
'Well graded'
|
344
|
-
"""
|
345
|
-
|
346
|
-
def __init__(
|
347
|
-
self,
|
348
|
-
fines: float,
|
349
|
-
sand: float,
|
350
|
-
gravel: float,
|
351
|
-
d_10: float = 0,
|
352
|
-
d_30: float = 0,
|
353
|
-
d_60: float = 0,
|
354
|
-
):
|
355
|
-
self.fines = fines
|
356
|
-
self.sand = sand
|
357
|
-
self.gravel = gravel
|
358
|
-
self.size_dist = _SoilGradation(d_10, d_30, d_60)
|
359
|
-
|
360
|
-
total_agg = self.fines + self.sand + self.gravel
|
361
|
-
|
362
|
-
if not isclose(total_agg, 100.0, rel_tol=ERROR_TOL):
|
363
|
-
err_msg = f"fines + sand + gravels = 100% not {total_agg}"
|
364
|
-
raise PSDAggSumError(err_msg)
|
365
|
-
|
366
|
-
@property
|
367
|
-
def d_10(self) -> float:
|
368
|
-
"""Diameter at which 10% of the soil by weight is finer."""
|
369
|
-
return self.size_dist.d_10
|
370
|
-
|
371
|
-
@property
|
372
|
-
def d_30(self) -> float:
|
373
|
-
"""Diameter at which 30% of the soil by weight is finer."""
|
374
|
-
return self.size_dist.d_30
|
375
|
-
|
376
|
-
@property
|
377
|
-
def d_60(self) -> float:
|
378
|
-
"""Diameter at which 60% of the soil by weight is finer."""
|
379
|
-
return self.size_dist.d_60
|
380
|
-
|
381
|
-
@property
|
382
|
-
def type_of_coarse(self) -> str:
|
383
|
-
"""Determines whether the soil is either :data:`GRAVEL` or
|
384
|
-
:data:`SAND`.
|
385
|
-
"""
|
386
|
-
return GRAVEL if self.gravel > self.sand else SAND
|
387
|
-
|
388
|
-
@property
|
389
|
-
def coeff_of_curvature(self) -> float:
|
390
|
-
r"""Coefficient of curvature of soil sample.
|
391
|
-
|
392
|
-
Coefficient of curvature :math:`(C_c)` is given by the formula:
|
393
|
-
|
394
|
-
.. math:: C_c = \dfrac{D^2_{30}}{D_{60} \times D_{10}}
|
395
|
-
|
396
|
-
For the soil to be well graded, the value of :math:`C_c` must be
|
397
|
-
between 1 and 3.
|
398
|
-
"""
|
399
|
-
return self.size_dist.coeff_of_curvature
|
400
|
-
|
401
|
-
@property
|
402
|
-
def coeff_of_uniformity(self) -> float:
|
403
|
-
r"""Coefficient of uniformity of soil sample.
|
404
|
-
|
405
|
-
Coefficient of uniformity :math:`(C_u)` is given by the formula:
|
406
|
-
|
407
|
-
.. math:: C_u = \dfrac{D_{60}}{D_{10}}
|
408
|
-
|
409
|
-
:math:`C_u` value greater than 4 to 6 classifies the soil as well
|
410
|
-
graded for gravels and sands respectively. When :math:`C_u` is less
|
411
|
-
than 4, it is classified as poorly graded or uniformly graded soil.
|
412
|
-
Higher values of :math:`C_u` indicates that the soil mass consists
|
413
|
-
of soil particles with different size ranges.
|
414
|
-
"""
|
415
|
-
return self.size_dist.coeff_of_uniformity
|
416
|
-
|
417
|
-
def has_particle_sizes(self) -> bool:
|
418
|
-
"""Checks if soil sample has particle sizes."""
|
419
|
-
return all(self.size_dist)
|
420
|
-
|
421
|
-
def grade(self) -> str:
|
422
|
-
r"""Return the grade of the soil sample, either :data:`WELL_GRADED`
|
423
|
-
or :data:`POORLY_GRADED`.
|
424
|
-
|
425
|
-
Conditions for a well-graded soil:
|
426
|
-
|
427
|
-
- :math:`1 \lt C_c \lt 3` and :math:`C_u \ge 4` (for gravels)
|
428
|
-
- :math:`1 \lt C_c \lt 3` and :math:`C_u \ge 6` (for sands)
|
429
|
-
"""
|
430
|
-
return self.size_dist.grade(coarse_soil=self.type_of_coarse)
|
431
|
-
|
432
|
-
|
433
|
-
class AASHTO:
|
434
|
-
r"""American Association of State Highway and Transportation Officials
|
435
|
-
(AASHTO) classification system.
|
436
|
-
|
437
|
-
The AASHTO classification system is useful for classifying soils for highways.
|
438
|
-
It categorizes soils for highways based on particle size analysis and
|
439
|
-
plasticity characteristics. It classifies both coarse-grained and fine-grained
|
440
|
-
soils into eight main groups (A1-A7) with subgroups, along with a separate
|
441
|
-
category (A8) for organic soils.
|
442
|
-
|
443
|
-
- ``A1 ~ A3`` (Granular Materials) :math:`\le` 35% pass No. 200 sieve
|
444
|
-
- ``A4 ~ A7`` (Silt-clay Materials) :math:`\ge` 36% pass No. 200 sieve
|
445
|
-
- ``A8`` (Organic Materials)
|
446
|
-
|
447
|
-
The Group Index ``(GI)`` is used to further evaluate soils within a group.
|
448
|
-
|
449
|
-
Parameters
|
450
|
-
----------
|
451
|
-
liquid_limit : float
|
452
|
-
Water content beyond which soils flows under their own weight.
|
453
|
-
plasticity_index : float
|
454
|
-
Range of water content over which soil remains in plastic condition.
|
455
|
-
fines : float
|
456
|
-
Percentage of fines in soil sample i.e. the percentage of soil sample
|
457
|
-
passing through No. 200 sieve (0.075mm).
|
458
|
-
add_group_idx : bool, default=True
|
459
|
-
Used to indicate whether the group index should be added to the classification
|
460
|
-
or not. Defaults to True.
|
461
|
-
|
462
|
-
Notes
|
463
|
-
-----
|
464
|
-
The ``GI`` must be mentioned even when it is zero, to indicate that the soil has
|
465
|
-
been classified as per AASHTO system.
|
466
|
-
|
467
|
-
.. math:: GI = (F_{200} - 35)[0.2 + 0.005(LL - 40)] + 0.01(F_{200} - 15)(PI - 10)
|
468
|
-
|
469
|
-
Examples
|
470
|
-
--------
|
471
|
-
>>> from geolysis.core.soil_classifier import AASHTO
|
472
|
-
|
473
|
-
>>> aashto_clf = AASHTO(liquid_limit=30.2, plasticity_index=6.3, fines=11.18)
|
474
|
-
>>> aashto_clf.group_index()
|
475
|
-
0.0
|
476
|
-
>>> aashto_clf.soil_class
|
477
|
-
'A-2-4(0)'
|
478
|
-
>>> aashto_clf.soil_desc
|
479
|
-
'Silty or clayey gravel and sand'
|
480
|
-
|
481
|
-
If you would like to exclude the group index from the classification, you can do
|
482
|
-
the following:
|
483
|
-
|
484
|
-
>>> aashto_clf.add_group_idx = False
|
485
|
-
>>> aashto_clf.soil_class
|
486
|
-
'A-2-4'
|
487
|
-
"""
|
488
|
-
|
489
|
-
SOIL_DESCRIPTIONS = {
|
490
|
-
"A-1-a": "Stone fragments, gravel, and sand",
|
491
|
-
"A-1-b": "Stone fragments, gravel, and sand",
|
492
|
-
"A-3": "Fine sand",
|
493
|
-
"A-2-4": "Silty or clayey gravel and sand",
|
494
|
-
"A-2-5": "Silty or clayey gravel and sand",
|
495
|
-
"A-2-6": "Silty or clayey gravel and sand",
|
496
|
-
"A-2-7": "Silty or clayey gravel and sand",
|
497
|
-
"A-4": "Silty soils",
|
498
|
-
"A-5": "Silty soils",
|
499
|
-
"A-6": "Clayey soils",
|
500
|
-
"A-7-5": "Clayey soils",
|
501
|
-
"A-7-6": "Clayey soils",
|
502
|
-
}
|
503
|
-
|
504
|
-
def __init__(
|
505
|
-
self,
|
506
|
-
liquid_limit: float,
|
507
|
-
plasticity_index: float,
|
508
|
-
fines: float,
|
509
|
-
add_group_idx=True,
|
510
|
-
):
|
511
|
-
self.liquid_limit = liquid_limit
|
512
|
-
self.plasticity_index = plasticity_index
|
513
|
-
self.fines = fines
|
514
|
-
self.add_group_idx = add_group_idx
|
515
|
-
|
516
|
-
def _classify(self) -> str:
|
517
|
-
# Silts A4-A7
|
518
|
-
if self.fines > 35:
|
519
|
-
soil_class = self._fine_soil_classifier()
|
520
|
-
# Coarse A1-A3
|
521
|
-
else:
|
522
|
-
soil_class = self._coarse_soil_classifier()
|
523
|
-
|
524
|
-
return (
|
525
|
-
f"{soil_class}({self.group_index():.0f})"
|
526
|
-
if self.add_group_idx
|
527
|
-
else soil_class
|
528
|
-
)
|
529
|
-
|
530
|
-
def _coarse_soil_classifier(self) -> str:
|
531
|
-
# A-3, Fine sand
|
532
|
-
if self.fines <= 10 and isclose(
|
533
|
-
self.plasticity_index, 0, rel_tol=ERROR_TOL
|
534
|
-
):
|
535
|
-
soil_class = "A-3"
|
536
|
-
|
537
|
-
# A-1-a -> A-1-b, Stone fragments, gravel, and sand
|
538
|
-
elif self.fines <= 15 and self.plasticity_index <= 6:
|
539
|
-
soil_class = "A-1-a"
|
540
|
-
|
541
|
-
elif self.fines <= 25 and self.plasticity_index <= 6:
|
542
|
-
soil_class = "A-1-b"
|
543
|
-
|
544
|
-
# A-2-4 -> A-2-7, Silty or clayey gravel and sand
|
545
|
-
elif self.liquid_limit <= 40:
|
546
|
-
soil_class = "A-2-4" if self.plasticity_index <= 10 else "A-2-6"
|
547
|
-
|
548
|
-
else:
|
549
|
-
soil_class = "A-2-5" if self.plasticity_index <= 10 else "A-2-7"
|
550
|
-
|
551
|
-
return soil_class
|
552
|
-
|
553
|
-
def _fine_soil_classifier(self) -> str:
|
554
|
-
# A-4 -> A-5, Silty Soils
|
555
|
-
# A-6 -> A-7, Clayey Soils
|
556
|
-
if self.liquid_limit <= 40:
|
557
|
-
soil_class = "A-4" if self.plasticity_index <= 10 else "A-6"
|
558
|
-
else:
|
559
|
-
if self.plasticity_index <= 10:
|
560
|
-
soil_class = "A-5"
|
561
|
-
else:
|
562
|
-
_x = self.liquid_limit - 30
|
563
|
-
soil_class = (
|
564
|
-
"A-7-5" if self.plasticity_index <= _x else "A-7-6"
|
565
|
-
)
|
566
|
-
|
567
|
-
return soil_class
|
568
|
-
|
569
|
-
@property
|
570
|
-
def soil_class(self) -> str:
|
571
|
-
"""Return the AASHTO classification of the soil."""
|
572
|
-
return self._classify()
|
573
|
-
|
574
|
-
@property
|
575
|
-
def soil_desc(self) -> str:
|
576
|
-
"""Return the AASHTO description of the soil."""
|
577
|
-
tmp_state = self.add_group_idx
|
578
|
-
try:
|
579
|
-
self.add_group_idx = False
|
580
|
-
soil_cls = self.soil_class
|
581
|
-
return AASHTO.SOIL_DESCRIPTIONS[soil_cls]
|
582
|
-
finally:
|
583
|
-
self.add_group_idx = tmp_state
|
584
|
-
|
585
|
-
def group_index(self) -> float:
|
586
|
-
"""Return the Group Index (GI) of the soil sample."""
|
587
|
-
a = 1 if (x_0 := self.fines - 35) < 0 else min(x_0, 40)
|
588
|
-
b = 1 if (x_0 := self.liquid_limit - 40) < 0 else min(x_0, 20)
|
589
|
-
c = 1 if (x_0 := self.fines - 15) < 0 else min(x_0, 40)
|
590
|
-
d = 1 if (x_0 := self.plasticity_index - 10) < 0 else min(x_0, 20)
|
591
|
-
|
592
|
-
return round(a * (0.2 + 0.005 * b) + 0.01 * c * d, 0)
|
593
|
-
|
594
|
-
|
595
|
-
class USCS:
|
596
|
-
"""Unified Soil Classification System (USCS).
|
597
|
-
|
598
|
-
The Unified Soil Classification System, initially developed by Casagrande in
|
599
|
-
1948 and later modified in 1952, is widely utilized in engineering projects
|
600
|
-
involving soils. It is the most popular system for soil classification and is
|
601
|
-
similar to Casagrande's Classification System. The system relies on particle
|
602
|
-
size analysis and atterberg limits for classification.
|
603
|
-
|
604
|
-
In this system, soils are first classified into two categories:
|
605
|
-
|
606
|
-
- Coarse grained soils: If more than 50% of the soils is retained on No. 200
|
607
|
-
(0.075 mm) sieve, it is designated as coarse-grained soil.
|
608
|
-
|
609
|
-
- Fine grained soils: If more than 50% of the soil passes through No. 200 sieve,
|
610
|
-
it is designated as fine grained soil.
|
611
|
-
|
612
|
-
Highly Organic soils are identified by visual inspection. These soils are termed
|
613
|
-
as Peat. (:math:`P_t`)
|
614
|
-
|
615
|
-
Parameters
|
616
|
-
----------
|
617
|
-
liquid_limit : float
|
618
|
-
Water content beyond which soils flows under their own weight. It can also
|
619
|
-
be defined as the minimum moisture content at which a soil flows upon
|
620
|
-
application of a very small shear force.
|
621
|
-
plastic_limit : float
|
622
|
-
Water content at which plastic deformation can be initiated. It is also the
|
623
|
-
minimum water content at which soil can be rolled into a thread 3mm thick
|
624
|
-
(molded without breaking)
|
625
|
-
fines : float
|
626
|
-
Percentage of fines in soil sample i.e. The percentage of soil sample passing
|
627
|
-
through No. 200 sieve (0.075mm)
|
628
|
-
sand : float
|
629
|
-
Percentage of sand in soil sample (%)
|
630
|
-
gravel : float
|
631
|
-
Percentage of gravel in soil sample (%)
|
632
|
-
d_10 : float, mm
|
633
|
-
Diameter at which 10% of the soil by weight is finer.
|
634
|
-
d_30 : float, mm
|
635
|
-
Diameter at which 30% of the soil by weight is finer.
|
636
|
-
d_60 : float, mm
|
637
|
-
Diameter at which 60% of the soil by weight is finer.
|
638
|
-
organic : bool, default=False
|
639
|
-
Indicates whether soil is organic or not.
|
640
|
-
|
641
|
-
Attributes
|
642
|
-
----------
|
643
|
-
atterberg_limits : AtterbergLimits
|
644
|
-
psd : PSD
|
645
|
-
soil_class : str
|
646
|
-
soil_desc : str
|
647
|
-
|
648
|
-
Examples
|
649
|
-
--------
|
650
|
-
>>> from geolysis.core.soil_classifier import USCS
|
651
|
-
|
652
|
-
>>> uscs_clf = USCS(liquid_limit=34.1, plastic_limit=21.1,
|
653
|
-
... fines=47.88, sand=37.84, gravel=14.28)
|
654
|
-
>>> uscs_clf.soil_class
|
655
|
-
'SC'
|
656
|
-
>>> uscs_clf.soil_desc
|
657
|
-
'Clayey sands'
|
658
|
-
|
659
|
-
>>> uscs_clf = USCS(liquid_limit=27.7, plastic_limit=22.7,
|
660
|
-
... fines=18.95, sand=77.21, gravel=3.84)
|
661
|
-
>>> uscs_clf.soil_class
|
662
|
-
'SM-SC'
|
663
|
-
>>> uscs_clf.soil_desc
|
664
|
-
'Sandy clayey silt'
|
665
|
-
|
666
|
-
>>> uscs_clf = USCS(liquid_limit=30.8, plastic_limit=20.7, fines=10.29,
|
667
|
-
... sand=81.89, gravel=7.83, d_10=0.07, d_30=0.3, d_60=0.8)
|
668
|
-
>>> uscs_clf.soil_class
|
669
|
-
'SW-SC'
|
670
|
-
>>> uscs_clf.soil_desc
|
671
|
-
'Well graded sand with clay'
|
672
|
-
|
673
|
-
Soil gradation (d_10, d_30, d_60) is needed to obtain soil description for
|
674
|
-
certain type of soils.
|
675
|
-
|
676
|
-
>>> uscs_clf = USCS(liquid_limit=30.8, plastic_limit=20.7,
|
677
|
-
... fines=10.29, sand=81.89, gravel=7.83)
|
678
|
-
>>> uscs_clf.soil_class
|
679
|
-
'SW-SC,SP-SC'
|
680
|
-
>>> uscs_clf.soil_desc
|
681
|
-
'Well graded sand with clay or Poorly graded sand with clay'
|
682
|
-
"""
|
683
|
-
|
684
|
-
SOIL_DESCRIPTIONS = {
|
685
|
-
"G": "Gravel",
|
686
|
-
"S": "Sand",
|
687
|
-
"M": "Silt",
|
688
|
-
"C": "Clay",
|
689
|
-
"O": "Organic",
|
690
|
-
"W": "Well graded",
|
691
|
-
"P": "Poorly graded",
|
692
|
-
"L": "Low plasticity",
|
693
|
-
"H": "High plasticity",
|
694
|
-
"GW": "Well graded gravels",
|
695
|
-
"GP": "Poorly graded gravels",
|
696
|
-
"GM": "Silty gravels",
|
697
|
-
"GC": "Clayey gravels",
|
698
|
-
"GM-GC": "Gravelly clayey silt",
|
699
|
-
"GW-GM": "Well graded gravel with silt",
|
700
|
-
"GP-GM": "Poorly graded gravel with silt",
|
701
|
-
"GW-GC": "Well graded gravel with clay",
|
702
|
-
"GP-GC": "Poorly graded gravel with clay",
|
703
|
-
"SW": "Well graded sands",
|
704
|
-
"SP": "Poorly graded sands",
|
705
|
-
"SM": "Silty sands",
|
706
|
-
"SC": "Clayey sands",
|
707
|
-
"SM-SC": "Sandy clayey silt",
|
708
|
-
"SW-SM": "Well graded sand with silt",
|
709
|
-
"SP-SM": "Poorly graded sand with silt",
|
710
|
-
"SW-SC": "Well graded sand with clay",
|
711
|
-
"SP-SC": "Poorly graded sand with clay",
|
712
|
-
"ML": "Inorganic silts with low plasticity",
|
713
|
-
"CL": "Inorganic clays with low plasticity",
|
714
|
-
"ML-CL": "Clayey silt with low plasticity",
|
715
|
-
"OL": "Organic clays with low plasticity",
|
716
|
-
"MH": "Inorganic silts with high plasticity",
|
717
|
-
"CH": "Inorganic clays with high plasticity",
|
718
|
-
"OH": "Organic silts with high plasticity",
|
719
|
-
"Pt": "Highly organic soils",
|
720
|
-
}
|
721
|
-
|
722
|
-
def __init__(
|
723
|
-
self,
|
724
|
-
liquid_limit: float,
|
725
|
-
plastic_limit: float,
|
726
|
-
fines: float,
|
727
|
-
sand: float,
|
728
|
-
gravel: float,
|
729
|
-
*,
|
730
|
-
d_10=0,
|
731
|
-
d_30=0,
|
732
|
-
d_60=0,
|
733
|
-
organic=False,
|
734
|
-
):
|
735
|
-
self._atterberg_limits = AtterbergLimits(liquid_limit, plastic_limit)
|
736
|
-
self._psd = PSD(fines, sand, gravel, d_10, d_30, d_60)
|
737
|
-
self.organic = organic
|
738
|
-
|
739
|
-
def _classify(self) -> str:
|
740
|
-
# Fine grained, Run Atterberg
|
741
|
-
if self.psd.fines > 50:
|
742
|
-
return self._fine_soil_classifier()
|
743
|
-
|
744
|
-
# Coarse grained, Run Sieve Analysis
|
745
|
-
# Gravel or Sand
|
746
|
-
return self._coarse_soil_classifier()
|
747
|
-
|
748
|
-
def _dual_soil_classifier(self) -> str:
|
749
|
-
fine_soil = self.atterberg_limits.type_of_fines
|
750
|
-
coarse_soil = self.psd.type_of_coarse
|
751
|
-
|
752
|
-
return f"{coarse_soil}{self.psd.grade()}-{coarse_soil}{fine_soil}"
|
753
|
-
|
754
|
-
def _coarse_soil_classifier(self) -> str:
|
755
|
-
coarse_soil = self.psd.type_of_coarse
|
756
|
-
|
757
|
-
# More than 12% pass No. 200 sieve
|
758
|
-
if self.psd.fines > 12:
|
759
|
-
# Above A-line
|
760
|
-
if self.atterberg_limits.above_A_LINE():
|
761
|
-
soil_class = f"{coarse_soil}{CLAY}"
|
762
|
-
|
763
|
-
# Limit plot in hatched zone on plasticity chart
|
764
|
-
elif self.atterberg_limits.limit_plot_in_hatched_zone():
|
765
|
-
soil_class = f"{coarse_soil}{SILT}-{coarse_soil}{CLAY}"
|
766
|
-
|
767
|
-
# Below A-line
|
768
|
-
else:
|
769
|
-
soil_class = f"{coarse_soil}{SILT}"
|
770
|
-
|
771
|
-
elif 5 <= self.psd.fines <= 12:
|
772
|
-
# Requires dual symbol based on graduation and plasticity chart
|
773
|
-
if self.psd.has_particle_sizes():
|
774
|
-
soil_class = self._dual_soil_classifier()
|
775
|
-
|
776
|
-
else:
|
777
|
-
fine_soil = self.atterberg_limits.type_of_fines
|
778
|
-
soil_class = (
|
779
|
-
f"{coarse_soil}{WELL_GRADED}-{coarse_soil}{fine_soil},"
|
780
|
-
f"{coarse_soil}{POORLY_GRADED}-{coarse_soil}{fine_soil}"
|
781
|
-
)
|
782
|
-
|
783
|
-
# Less than 5% pass No. 200 sieve
|
784
|
-
# Obtain Cc and Cu from grain size graph
|
785
|
-
else:
|
786
|
-
if self.psd.has_particle_sizes():
|
787
|
-
soil_class = f"{coarse_soil}{self.psd.grade()}"
|
788
|
-
|
789
|
-
else:
|
790
|
-
soil_class = (
|
791
|
-
f"{coarse_soil}{WELL_GRADED},{coarse_soil}{POORLY_GRADED}"
|
792
|
-
)
|
793
|
-
|
794
|
-
return soil_class
|
795
|
-
|
796
|
-
def _fine_soil_classifier(self) -> str:
|
797
|
-
if self.atterberg_limits.liquid_limit < 50:
|
798
|
-
# Low LL
|
799
|
-
# Above A-line and PI > 7
|
800
|
-
if (self.atterberg_limits.above_A_LINE()) and (
|
801
|
-
self.atterberg_limits.plasticity_index > 7
|
802
|
-
):
|
803
|
-
soil_class = f"{CLAY}{LOW_PLASTICITY}"
|
804
|
-
|
805
|
-
# Limit plot in hatched area on plasticity chart
|
806
|
-
elif self.atterberg_limits.limit_plot_in_hatched_zone():
|
807
|
-
soil_class = f"{SILT}{LOW_PLASTICITY}-{CLAY}{LOW_PLASTICITY}"
|
808
|
-
|
809
|
-
# Below A-line or PI < 4
|
810
|
-
else:
|
811
|
-
soil_class = (
|
812
|
-
f"{ORGANIC}{LOW_PLASTICITY}"
|
813
|
-
if self.organic
|
814
|
-
else f"{SILT}{LOW_PLASTICITY}"
|
815
|
-
)
|
816
|
-
|
817
|
-
# High LL
|
818
|
-
else:
|
819
|
-
# Above A-Line
|
820
|
-
if self.atterberg_limits.above_A_LINE():
|
821
|
-
soil_class = f"{CLAY}{HIGH_PLASTICITY}"
|
822
|
-
|
823
|
-
# Below A-Line
|
824
|
-
else:
|
825
|
-
soil_class = (
|
826
|
-
f"{ORGANIC}{HIGH_PLASTICITY}"
|
827
|
-
if self.organic
|
828
|
-
else f"{SILT}{HIGH_PLASTICITY}"
|
829
|
-
)
|
830
|
-
|
831
|
-
return soil_class
|
832
|
-
|
833
|
-
@property
|
834
|
-
def atterberg_limits(self) -> AtterbergLimits:
|
835
|
-
"""Return the atterberg limits of soil."""
|
836
|
-
return self._atterberg_limits
|
837
|
-
|
838
|
-
@property
|
839
|
-
def psd(self) -> PSD:
|
840
|
-
"""Return the particle size distribution of soil."""
|
841
|
-
return self._psd
|
842
|
-
|
843
|
-
@property
|
844
|
-
def soil_class(self) -> str:
|
845
|
-
"""Return the USCS classification of the soil."""
|
846
|
-
return self._classify()
|
847
|
-
|
848
|
-
@property
|
849
|
-
def soil_desc(self) -> str:
|
850
|
-
"""Return the USCS description of the soil."""
|
851
|
-
soil_cls = self.soil_class
|
852
|
-
try:
|
853
|
-
soil_descr = USCS.SOIL_DESCRIPTIONS[soil_cls]
|
854
|
-
except KeyError:
|
855
|
-
soil_descr = " or ".join(
|
856
|
-
map(USCS.SOIL_DESCRIPTIONS.get, soil_cls.split(","))
|
857
|
-
) # type: ignore
|
858
|
-
|
859
|
-
return soil_descr
|