geolysis 0.2.0__py3-none-any.whl → 0.4.2__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/spt.py CHANGED
@@ -1,424 +1,447 @@
1
- import functools
2
- from statistics import StatisticsError
3
- from typing import Callable, Iterable, Sequence
4
-
5
- from geolysis.constants import ERROR_TOL, UNITS
6
- from geolysis.utils import FloatOrInt, isclose, log10, mean, round_, sqrt
7
-
8
- __all__ = [
9
- "weighted_avg_spt_n_val",
10
- "avg_uncorrected_spt_n_val",
11
- "SPTCorrections",
12
- ]
13
-
14
-
15
- class OverburdenPressureError(ValueError):
16
- """
17
- Exception raised for overburden pressure related errors.
18
- """
19
-
20
-
21
- @round_(ndigits=0)
22
- def weighted_avg_spt_n_val(corrected_spt_vals: Sequence[float]) -> float:
23
- r"""
24
- Calculates the weighted average of the corrected SPT N-values in the foundation
25
- influence zone. (:math:`N_{design}`)
26
-
27
- Due to uncertainty in field procedure in standard penetration test and also
28
- to consider all the N-value in the influence zone of a foundation, a method
29
- was suggested to calculate the design N-value which should be used in
30
- calculating the allowable bearing capacity of shallow foundation rather than
31
- using a particular N-value. All the N-value from the influence zone is taken
32
- under consideration by giving the highest weightage to the closest N-value
33
- from the base.
34
-
35
- The determination of :math:`N_{design}` is given by:
36
-
37
- .. math::
38
-
39
- N_{design} = \dfrac{\sum_{i=1}^{n} \frac{N_i}{i^2}}{\sum_{i=1}^{n} \frac{1}{i^2}}
40
-
41
- - influence zone = :math:`D_f + 2B` or to a depth up to which soil
42
- types are approximately the same.
43
- - B = width of footing
44
- - :math:`n` = number of layers in the influence zone.
45
- - :math:`N_i` = corrected N-value at ith layer from the footing base.
46
-
47
- .. note::
48
-
49
- Alternatively, for ease in calculation, the lowest N-value from the influence zone
50
- can be taken as the :math:`N_{design}` as suggested by ``Terzaghi & Peck (1948)``.
51
-
52
- :param Sequence[float] corrected_spt_vals: Corrected SPT N-values within the
53
- foundation influence zone i.e. :math:`D_f` to :math:`D_f + 2B`
54
-
55
- :return: Weighted average of corrected SPT N-values
56
- :rtype: float
57
-
58
- :raises StatisticError: If ``corrected_spt_vals`` is empty, StatisticError is raised.
59
- """
60
- if not corrected_spt_vals:
61
- err_msg = "spt_n_design requires at least one corrected spt n-value"
62
- raise StatisticsError(err_msg)
63
-
64
- total_num = 0.0
65
- total_den = 0.0
66
-
67
- for i, corrected_spt in enumerate(corrected_spt_vals, start=1):
68
- idx_weight = 1 / i**2
69
- total_num += idx_weight * corrected_spt
70
- total_den += idx_weight
71
-
72
- return total_num / total_den
73
-
74
-
75
- @round_(ndigits=0)
76
- def avg_uncorrected_spt_n_val(uncorrected_spt_vals: Sequence[float]) -> float:
77
- """
78
- Calculates the average of the uncorrected SPT N-values in the foundation
79
- influence zone.
80
-
81
- :param Sequence[float] uncorrected_spt_vals: Uncorrected SPT N-values within the
82
- foundation influence zone i.e. :math:`D_f` |rarr| :math:`D_f + 2B`. Only water
83
- table correction suggested.
84
-
85
- :return: Average of corrected SPT N-values
86
- :rtype: float
87
-
88
- :raises StatisticError: If ``uncorrected_spt_vals`` is empty, StatisticError is raised.
89
- """
90
- if not uncorrected_spt_vals:
91
- msg = "spt_n_val requires at least one corrected spt n-value"
92
- raise StatisticsError(msg)
93
-
94
- return mean(uncorrected_spt_vals)
95
-
96
-
97
- class SPTCorrections:
98
- r"""
99
- SPT N-value correction for **Overburden Pressure** and **Dilatancy**.
100
-
101
- There are three (3) different SPT corrections namely:
102
-
103
- - Energy Correction / Correction for Field Procedures
104
- - Overburden Pressure Corrections
105
- - Dilatancy Correction
106
-
107
- Energy correction is used to standardized the SPT N-values for field procedures.
108
-
109
- In cohesionless soils, penetration resistance is affected by overburden pressure.
110
- Soils with the same density but different confining pressures have varying penetration
111
- numbers, with higher confining pressures leading to higher penetration numbers. As depth
112
- increases, confining pressure rises, causing underestimation of penetration numbers
113
- at shallow depths and overestimation at deeper depths. The need for corrections in
114
- Standard Penetration Test (SPT) values was acknowledged only in 1957 by Gibbs & Holtz,
115
- meaning data published before this, like Terzaghi's, are based on uncorrected values.
116
-
117
- The general formula for overburden pressure correction is:
118
-
119
- .. math::
120
-
121
- (N_1)_{60} = C_N \cdot N_{60} \le 2 \cdot N_{60}
122
-
123
- Where:
124
-
125
- - :math:`C_N` = Overburden Pressure Correction Factor
126
-
127
- Available overburden pressure corrections are given by the following authors:
128
-
129
- - Gibbs & Holtz (1957)
130
- - Peck et al (1974)
131
- - Liao & Whitman (1986)
132
- - Skempton (1986)
133
- - Bazaraa & Peck (1969)
134
-
135
- **Dilatancy Correction** is a correction for silty fine sands and fine sands below the
136
- water table that develop pore pressure which is not easily dissipated. The pore pressure
137
- increases the resistance of the soil hence the standard penetration number (N).
138
- Correction of silty fine sands recommended by ``Terzaghi and Peck (1948)`` if :math:`N_{60}`
139
- exceeds 15.
140
- """
141
-
142
- unit = UNITS.unitless
143
-
144
- @staticmethod
145
- def map(
146
- opc_func: Callable[..., FloatOrInt],
147
- standardized_spt_vals: Iterable[float],
148
- dc_func: Callable[..., FloatOrInt] | None = None,
149
- **kwargs,
150
- ):
151
- """
152
- Returns an iterator from computing functions (``opc_func``, "dc_func") using
153
- arguments from the iterable (``standard_spt_vals``).
154
-
155
- :param Callable opc_func: Overburden pressure correction function to use.
156
- :param Callable | None dc_func: Dilatancy correction function to use.
157
- :param dict kwargs: Keyword arguments to pass to ```opc_func`.
158
- """
159
- opc_func = functools.partial(opc_func, **kwargs)
160
- corrected_spt_vals = map(opc_func, standardized_spt_vals)
161
-
162
- if dc_func:
163
- corrected_spt_vals = map(dc_func, corrected_spt_vals)
164
-
165
- return corrected_spt_vals
166
-
167
- @staticmethod
168
- @round_(ndigits=2)
169
- def energy_correction(
170
- recorded_spt_val: float,
171
- percentage_energy=0.6,
172
- *,
173
- hammer_efficiency=0.6,
174
- borehole_diameter_correction=1,
175
- sampler_correction=1,
176
- rod_length_correction=0.75,
177
- ) -> FloatOrInt:
178
- r"""
179
- Return SPT N-value standardized for field procedures.
180
-
181
- On the basis of field observations, it appears reasonable to standardize the field
182
- SPT N-value as a function of the input driving energy and its dissipation around
183
- the sampler around the surrounding soil. The variations in testing procedures may
184
- be at least partially compensated by converting the measured N-value to :math:`N_{60}`.
185
-
186
- .. math::
187
-
188
- N_{60} = \dfrac{E_H \cdot C_B \cdot C_S \cdot C_R \cdot N}{0.6}
189
-
190
- Where:
191
-
192
- - :math:`N_{60}` = Corrected SPT N-value for field procedures
193
- - :math:`E_{H}` = Hammer efficiency
194
- - :math:`C_{B}` = Borehole diameter correction
195
- - :math:`C_{S}` = Sampler correction
196
- - :math:`C_{R}` = Rod length correction
197
- - N = Recorded SPT N-value in field
198
-
199
- The values of :math:`E_H`, :math:`C_B`, :math:`C_S`, and :math:`C_R` can be found in
200
- the table below.
201
-
202
- .. table:: Correction table for field procedure of SPT N-value
203
-
204
- +--------------------+------------------------------+--------------------------------+
205
- | SPT Hammer Efficiencies |
206
- +====================+==============================+================================+
207
- | **Hammer Type** | **Hammer Release Mechanism** | **Efficiency**, :math:`E_H` |
208
- +--------------------+------------------------------+--------------------------------+
209
- | Automatic | Trip | 0.70 |
210
- +--------------------+------------------------------+--------------------------------+
211
- | Donut | Hand dropped | 0.60 |
212
- +--------------------+------------------------------+--------------------------------+
213
- | Donut | Cathead+2 turns | 0.50 |
214
- +--------------------+------------------------------+--------------------------------+
215
- | Safety | Cathead+2 turns | 0.55 - 0.60 |
216
- +--------------------+------------------------------+--------------------------------+
217
- | Drop/Pin | Hand dropped | 0.45 |
218
- +--------------------+------------------------------+--------------------------------+
219
- | Borehole, Sampler and Rod Correction |
220
- +--------------------+------------------------------+--------------------------------+
221
- | **Factor** | **Equipment Variables** | **Correction Factor** |
222
- +--------------------+------------------------------+--------------------------------+
223
- | Borehole Dia | 65 - 115 mm (2.5-4.5 in) | 1.00 |
224
- | Factor, | | |
225
- | :math:`C_B` | | |
226
- +--------------------+------------------------------+--------------------------------+
227
- | | 150 mm (6 in) | 1.05 |
228
- +--------------------+------------------------------+--------------------------------+
229
- | | 200 mm (8 in) | 1.15 |
230
- +--------------------+------------------------------+--------------------------------+
231
- | Sampler | Standard sampler | 1.00 |
232
- | Correction, | | |
233
- | :math:`C_S` | | |
234
- +--------------------+------------------------------+--------------------------------+
235
- | | Sampler without liner | 1.20 |
236
- | | (not recommended) | |
237
- +--------------------+------------------------------+--------------------------------+
238
- | Rod Length | 3 - 4 m (10-13 ft) | 0.75 |
239
- | Correction, | | |
240
- | :math:`C_R` | | |
241
- +--------------------+------------------------------+--------------------------------+
242
- | | 4 - 6 m (13-20 ft) | 0.85 |
243
- +--------------------+------------------------------+--------------------------------+
244
- | | 6 - 10 m (20-30 ft) | 0.95 |
245
- +--------------------+------------------------------+--------------------------------+
246
- | | >10 m (>30 ft) | 1.00 |
247
- +--------------------+------------------------------+--------------------------------+
248
-
249
- :param float percentage_energy: Percentage energy reaching the tip of the sampler.
250
- :param int recorded_spt_val: Recorded SPT N-value from field.
251
- :kwparam float hammer_efficiency: hammer efficiency, defaults to 0.6
252
- :kwparam float borehole_diameter_correction: borehole diameter correction,
253
- defaults to 1.0
254
- :kwparam float sampler_correction: sampler correction, defaults to 1.0
255
- :kwparam float rod_length_correction: rod Length correction, defaults to 0.75
256
-
257
- .. note::
258
-
259
- The ``energy correction`` is to be applied irrespective of the type of soil.
260
- """
261
- correction = (
262
- hammer_efficiency
263
- * borehole_diameter_correction
264
- * sampler_correction
265
- * rod_length_correction
266
- )
267
-
268
- return (correction * recorded_spt_val) / percentage_energy
269
-
270
- @staticmethod
271
- @round_(ndigits=2)
272
- def terzaghi_peck_dc_1948(corrected_spt_val: float) -> float:
273
- r"""
274
- Return the dilatancy spt correction.
275
-
276
- :param float corrected_spt_val: Corrected SPT N-value. This should be corrected
277
- using any of the overburden pressure corrections.
278
-
279
- .. math::
280
-
281
- (N_1)_{60} &= 15 + \dfrac{1}{2}((N_1)_{60} - 15) \, , \, (N_1)_{60} \gt 15
282
-
283
- (N_1)_{60} &= (N_1)_{60} \, , \, (N_1)_{60} \le 15
284
-
285
- .. note::
286
-
287
- For coarse sand, this correction is not required. In applying this correction,
288
- overburden pressure correction is applied first and then dilatancy correction
289
- is applied.
290
- """
291
-
292
- if corrected_spt_val <= 15:
293
- return corrected_spt_val
294
-
295
- return 15 + 0.5 * (corrected_spt_val - 15)
296
-
297
- @staticmethod
298
- @round_(ndigits=2)
299
- def gibbs_holtz_opc_1957(spt_n_60: float, eop: float) -> float:
300
- r"""
301
- Return the overburden pressure correction given by ``Gibbs and Holtz
302
- (1957)``.
303
-
304
- :param float spt_n_60: SPT N-value standardized for field procedures.
305
- :param float eop: Effective overburden pressure (:math:`kN/m^2`).
306
-
307
- .. math::
308
-
309
- C_N = \dfrac{350}{\sigma_o + 70} \, \sigma_o \le 280kN/m^2
310
-
311
- .. note::
312
-
313
- :math:`\frac{N_c}{N_{60}}` should lie between 0.45 and 2.0, if :math:`\frac{N_c}{N_{60}}`
314
- is greater than 2.0, :math:`N_c` should be divided by 2.0 to obtain the design value
315
- used in finding the bearing capacity of the soil.
316
- """
317
-
318
- std_pressure = 280
319
-
320
- if eop <= 0 or eop > std_pressure:
321
- err_msg = (
322
- f"eop: {eop} should be less than or equal to {std_pressure}"
323
- "but not less than or equal to 0"
324
- )
325
- raise OverburdenPressureError(err_msg)
326
-
327
- corrected_spt = spt_n_60 * (350 / (eop + 70))
328
- spt_ratio = corrected_spt / spt_n_60
329
-
330
- if 0.45 < spt_ratio < 2.0:
331
- return corrected_spt
332
-
333
- corrected_spt = corrected_spt / 2 if spt_ratio > 2.0 else corrected_spt
334
- return min(corrected_spt, 2 * spt_n_60)
335
-
336
- @staticmethod
337
- @round_(ndigits=2)
338
- def peck_et_al_opc_1974(spt_n_60: float, eop: float) -> float:
339
- r"""
340
- Return the overburden pressure given by ``Peck et al (1974)``.
341
-
342
- :param float spt_n_60: SPT N-value standardized for field procedures.
343
- :param float eop: Effective overburden pressure (:math:`kN/m^2`).
344
-
345
- .. math::
346
-
347
- C_N = 0.77 \log \left( \dfrac{2000}{\sigma_o} \right)
348
- """
349
- std_pressure = 24
350
-
351
- if eop <= 0 or eop < std_pressure:
352
- err_msg = f"eop: {eop} >= {std_pressure}"
353
- raise OverburdenPressureError(err_msg)
354
-
355
- corrected_spt = 0.77 * log10(2000 / eop) * spt_n_60
356
- return min(corrected_spt, 2 * spt_n_60)
357
-
358
- @staticmethod
359
- @round_(ndigits=2)
360
- def liao_whitman_opc_1986(spt_n_60: float, eop: float) -> float:
361
- r"""
362
- Return the overburden pressure given by ``Liao Whitman (1986)``.
363
-
364
- :param float spt_n_60: SPT N-value standardized for field procedures.
365
- :param float eop: Effective overburden pressure (:math:`kN/m^2`).
366
-
367
- .. math::
368
-
369
- C_N = \sqrt{\dfrac{100}{\sigma_o}}
370
- """
371
- if eop <= 0:
372
- err_msg = f"eop: {eop} > 0"
373
- raise OverburdenPressureError(err_msg)
374
-
375
- corrected_spt = sqrt(100 / eop) * spt_n_60
376
- return min(corrected_spt, 2 * spt_n_60)
377
-
378
- @staticmethod
379
- @round_(ndigits=2)
380
- def skempton_opc_1986(spt_n_60: float, eop: float) -> float:
381
- r"""
382
- Return the overburden pressure correction given by ``Skempton (1986).``
383
-
384
- :param float spt_n_60: SPT N-value standardized for field procedures.
385
- :param float eop: Effective overburden pressure (:math:`kN/m^2`).
386
-
387
- .. math::
388
-
389
- C_N = \dfrac{2}{1 + 0.01044 \cdot \sigma_o}
390
- """
391
- corrected_spt = (2 / (1 + 0.01044 * eop)) * spt_n_60
392
- return min(corrected_spt, 2 * spt_n_60)
393
-
394
- @staticmethod
395
- @round_(ndigits=2)
396
- def bazaraa_peck_opc_1969(spt_n_60: float, eop: float) -> float:
397
- r"""
398
- Return the overburden pressure correction given by ``Bazaraa (1967)``
399
- and also by ``Peck and Bazaraa (1969)``.
400
-
401
- :param float spt_n_60: SPT N-value standardized for field procedures.
402
- :param float eop: Effective overburden pressure (:math:`kN/m^2`).
403
-
404
- .. math::
405
-
406
- C_N &= \dfrac{4}{1 + 0.0418 \cdot \sigma_o}, \, \sigma_o \lt 71.8kN/m^2
407
-
408
- C_N &= \dfrac{4}{3.25 + 0.0104 \cdot \sigma_o}, \, \sigma_o \gt 71.8kN/m^2
409
-
410
- C_N &= 1 \, , \, \sigma_o = 71.8kN/m^2
411
- """
412
-
413
- std_pressure = 71.8
414
-
415
- if isclose(eop, std_pressure, rel_tol=ERROR_TOL):
416
- return spt_n_60
417
-
418
- if eop < std_pressure:
419
- corrected_spt = 4 * spt_n_60 / (1 + 0.0418 * eop)
420
-
421
- else:
422
- corrected_spt = 4 * spt_n_60 / (3.25 + 0.0104 * eop)
423
-
424
- return min(corrected_spt, 2 * spt_n_60)
1
+ import enum
2
+ from abc import abstractmethod
3
+ from typing import Final, Sequence
4
+
5
+ from geolysis import validators
6
+ from geolysis.utils import isclose, log10, mean, round_, sqrt
7
+
8
+ __all__ = ["EnergyCorrection", "GibbsHoltzOPC", "BazaraaPeckOPC", "PeckOPC",
9
+ "LiaoWhitmanOPC", "SkemptonOPC", "DilatancyCorrection"]
10
+
11
+
12
+ class SPTDesign:
13
+ """ SPT Design Calculations.
14
+
15
+ Due to uncertainty in field procedure in standard penetration test and also
16
+ to consider all the N-value in the influence zone of a foundation, a method
17
+ was suggested to calculate the design N-value which should be used in
18
+ calculating the allowable bearing capacity of shallow foundation rather
19
+ than using a particular N-value. All the N-value from the influence zone is
20
+ taken under consideration by giving the highest weightage to the closest
21
+ N-value from the base.
22
+ """
23
+
24
+ def __init__(self, spt_n_values: Sequence[float]) -> None:
25
+ """
26
+ :param spt_n_values: SPT N-values within the foundation influence zone.
27
+ ``spt_n_values`` can either be **corrected** or
28
+ **uncorrected** SPT N-values.
29
+ :type spt_n_values: Sequence[float]
30
+ """
31
+ self.spt_n_values = spt_n_values
32
+
33
+ @round_(ndigits=1)
34
+ def average_spt_n_design(self) -> float:
35
+ """Calculates the average of the corrected SPT N-values within the
36
+ foundation influence zone.
37
+ """
38
+ return mean(self.spt_n_values)
39
+
40
+ @round_(ndigits=1)
41
+ def minimum_spt_n_design(self):
42
+ """The lowest SPT N-value within the influence zone can be taken as the
43
+ :math:`N_{design}` as suggested by ``Terzaghi & Peck (1948)``.
44
+ """
45
+ return min(self.spt_n_values)
46
+
47
+ @round_(ndigits=1)
48
+ def weighted_spt_n_design(self):
49
+ r"""Calculates the weighted average of the corrected SPT N-values
50
+ within the foundation influence zone.
51
+
52
+ Weighted average is given by the formula:
53
+
54
+ .. math::
55
+
56
+ N_{design} = \dfrac{\sum_{i=1}^{n} \frac{N_i}{i^2}}
57
+ {\sum_{i=1}^{n}\frac{1}{i^2}}
58
+ """
59
+
60
+ sum_total = 0.0
61
+ sum_wgts = 0.0
62
+
63
+ for i, corrected_spt in enumerate(self.spt_n_values, start=1):
64
+ wgt = 1 / i ** 2
65
+ sum_total += wgt * corrected_spt
66
+ sum_wgts += wgt
67
+
68
+ return sum_total / sum_wgts
69
+
70
+
71
+ class HammerType(enum.StrEnum):
72
+ """Enumeration of hammer types."""
73
+ AUTOMATIC = enum.auto()
74
+ DONUT_1 = enum.auto()
75
+ DONUT_2 = enum.auto()
76
+ SAFETY = enum.auto()
77
+ DROP = PIN = enum.auto()
78
+
79
+
80
+ class SamplerType(enum.StrEnum):
81
+ """Enumeration of sampler types."""
82
+ STANDARD = enum.auto()
83
+ NON_STANDARD = enum.auto()
84
+
85
+
86
+ class EnergyCorrection:
87
+ r"""SPT N-value standardized for field procedures.
88
+
89
+ On the basis of field observations, it appears reasonable to standardize
90
+ the field SPT N-value as a function of the input driving energy and its
91
+ dissipation around the sampler around the surrounding soil. The variations
92
+ in testing procedures may be at least partially compensated by converting
93
+ the measured N-value to :math:`N_{60}` assuming 60% hammer energy being
94
+ transferred to the tip of the standard split spoon.
95
+
96
+ Energy correction is given by the formula:
97
+
98
+ .. math::
99
+
100
+ N_{ENERGY} = \dfrac{E_H \cdot C_B \cdot C_S \cdot C_R \cdot N}{ENERGY}
101
+
102
+ ``ENERGY``: 0.6, 0.55, etc
103
+ """
104
+
105
+ HAMMER_EFFICIENCY_FACTORS = {HammerType.AUTOMATIC: 0.70,
106
+ HammerType.DONUT_1: 0.60,
107
+ HammerType.DONUT_2: 0.50,
108
+ HammerType.SAFETY: 0.55,
109
+ HammerType.DROP: 0.45,
110
+ HammerType.PIN: 0.45}
111
+
112
+ SAMPLER_CORRECTION_FACTORS = {SamplerType.STANDARD: 1.00,
113
+ SamplerType.NON_STANDARD: 1.20}
114
+
115
+ def __init__(self, recorded_spt_n_value: int, *, energy_percentage=0.6,
116
+ borehole_diameter=65.0, rod_length=3.0,
117
+ hammer_type=HammerType.DONUT_1,
118
+ sampler_type=SamplerType.STANDARD):
119
+ """
120
+ :param recorded_spt_n_value: Recorded SPT N-value from field.
121
+ :type recorded_spt_n_value: int
122
+
123
+ :param energy_percentage: Energy percentage reaching the tip of the
124
+ sampler, defaults to 0.6
125
+ :type energy_percentage: float, optional
126
+
127
+ :param borehole_diameter: Borehole diameter, defaults to 65.0. (mm)
128
+ :type borehole_diameter: float, optional
129
+
130
+ :param rod_length: Rod length, defaults to 3.0. (m)
131
+ :type rod_length: float, optional
132
+
133
+ :param hammer_type: Hammer type, defaults to :attr:`HammerType.DONUT_1`
134
+ :type hammer_type: HammerType, optional
135
+
136
+ :param sampler_type: Sampler type, defaults to :attr:`SamplerType.STANDARD`
137
+ :type sampler_type: SamplerType, optional
138
+ """
139
+
140
+ self.recorded_spt_n_value = recorded_spt_n_value
141
+ self.energy_percentage = energy_percentage
142
+ self.borehole_diameter = borehole_diameter
143
+ self.rod_length = rod_length
144
+ self.hammer_type = hammer_type
145
+ self.sampler_type = sampler_type
146
+
147
+ @property
148
+ def recorded_spt_n_value(self) -> int:
149
+ return self._recorded_spt_value
150
+
151
+ @recorded_spt_n_value.setter
152
+ @validators.le(100)
153
+ @validators.gt(0)
154
+ def recorded_spt_n_value(self, val: int) -> None:
155
+ self._recorded_spt_value = val
156
+
157
+ @property
158
+ def energy_percentage(self) -> float:
159
+ return self._energy_percentage
160
+
161
+ @energy_percentage.setter
162
+ @validators.le(1.0)
163
+ @validators.gt(0.0)
164
+ def energy_percentage(self, val: float) -> None:
165
+ self._energy_percentage = val
166
+
167
+ @property
168
+ def borehole_diameter(self) -> float:
169
+ return self._borehole_diameter
170
+
171
+ @borehole_diameter.setter
172
+ @validators.le(200.0)
173
+ @validators.ge(65.0)
174
+ def borehole_diameter(self, val: float) -> None:
175
+ self._borehole_diameter = val
176
+
177
+ @property
178
+ def rod_length(self) -> float:
179
+ return self._rod_length
180
+
181
+ @rod_length.setter
182
+ @validators.gt(0.0)
183
+ def rod_length(self, val: float) -> None:
184
+ self._rod_length = val
185
+
186
+ @property
187
+ def hammer_efficiency(self) -> float:
188
+ """Hammer efficiency correction factor."""
189
+ return self.HAMMER_EFFICIENCY_FACTORS[self.hammer_type]
190
+
191
+ @property
192
+ def borehole_diameter_correction(self) -> float:
193
+ """Borehole diameter correction factor."""
194
+ if 65 <= self.borehole_diameter <= 115:
195
+ corr = 1.00
196
+ elif 115 < self.borehole_diameter <= 150:
197
+ corr = 1.05
198
+ else:
199
+ corr = 1.15
200
+
201
+ return corr
202
+
203
+ @property
204
+ def sampler_correction(self) -> float:
205
+ """Sampler correction factor."""
206
+ return self.SAMPLER_CORRECTION_FACTORS[self.sampler_type]
207
+
208
+ @property
209
+ def rod_length_correction(self) -> float:
210
+ """Rod length correction factor."""
211
+ if 3.0 <= self.rod_length <= 4.0:
212
+ corr = 0.75
213
+ elif 4.0 < self.rod_length <= 6.0:
214
+ corr = 0.85
215
+ elif 6.0 < self.rod_length <= 10.0:
216
+ corr = 0.95
217
+ else:
218
+ corr = 1.00
219
+
220
+ return corr
221
+
222
+ def correction(self) -> float:
223
+ """Energy correction factor."""
224
+ numerator = (self.hammer_efficiency
225
+ * self.borehole_diameter_correction
226
+ * self.sampler_correction
227
+ * self.rod_length_correction)
228
+ return numerator / self.energy_percentage
229
+
230
+ @round_(ndigits=1)
231
+ def corrected_spt_n_value(self) -> float:
232
+ """Corrected SPT N-value."""
233
+ return self.correction() * self.recorded_spt_n_value
234
+
235
+
236
+ class OPC:
237
+ """Base class for Overburden Pressure Correction (OPC)."""
238
+
239
+ def __init__(self, std_spt_n_value: float, eop: float) -> None:
240
+ """
241
+ :param std_spt_n_value: SPT N-value standardized for field procedures.
242
+ :type std_spt_n_value: float
243
+
244
+ :param eop: Effective overburden pressure (:math:`kN/m^2`)
245
+ :type eop: float
246
+ """
247
+ self.std_spt_n_value = std_spt_n_value
248
+ self.eop = eop
249
+
250
+ @property
251
+ def std_spt_n_value(self) -> float:
252
+ return self._std_spt_n_value
253
+
254
+ @std_spt_n_value.setter
255
+ @validators.gt(0.0)
256
+ def std_spt_n_value(self, val: float) -> None:
257
+ self._std_spt_n_value = val
258
+
259
+ @round_(ndigits=1)
260
+ def corrected_spt_n_value(self) -> float:
261
+ """Corrected SPT N-value."""
262
+ corrected_spt = self.correction() * self.std_spt_n_value
263
+ # Corrected SPT should not be more
264
+ # than 2 times the Standardized SPT
265
+ return min(corrected_spt, 2 * self.std_spt_n_value)
266
+
267
+ @abstractmethod
268
+ def correction(self) -> float:
269
+ raise NotImplementedError
270
+
271
+
272
+ class GibbsHoltzOPC(OPC):
273
+ r"""Overburden Pressure Correction according to ``Gibbs & Holtz (1957)``.
274
+
275
+ Overburden Pressure Correction is given by the formula:
276
+
277
+ .. math:: C_N = \dfrac{350}{\sigma_o + 70} \, \sigma_o \le 280kN/m^2
278
+
279
+ :math:`\frac{N_c}{N_{60}}` should lie between 0.45 and 2.0, if
280
+ :math:`\frac{N_c}{N_{60}}` is greater than 2.0, :math:`N_c` should be
281
+ divided by 2.0 to obtain the design value used in finding the bearing
282
+ capacity of the soil.
283
+ """
284
+
285
+ @property
286
+ def eop(self) -> float:
287
+ return self._eop
288
+
289
+ @eop.setter
290
+ @validators.le(280.0)
291
+ @validators.gt(0.0)
292
+ def eop(self, val: float) -> None:
293
+ self._eop = val
294
+
295
+ def correction(self) -> float:
296
+ """SPT Correction."""
297
+ corr = 350.0 / (self.eop + 70.0)
298
+ if corr > 2.0:
299
+ corr /= 2.0
300
+ return corr
301
+
302
+
303
+ class BazaraaPeckOPC(OPC):
304
+ r"""Overburden Pressure Correction according to ``Bazaraa (1967)``, and
305
+ also by ``Peck and Bazaraa (1969)``.
306
+
307
+ Overburden Pressure Correction is given by the formula:
308
+
309
+ .. math::
310
+
311
+ C_N &= \dfrac{4}{1 + 0.0418 \cdot \sigma_o}, \, \sigma_o \lt 71.8kN/m^2
312
+
313
+ C_N &= \dfrac{4}{3.25 + 0.0104 \cdot \sigma_o},
314
+ \, \sigma_o \gt 71.8kN/m^2
315
+
316
+ C_N &= 1 \, , \, \sigma_o = 71.8kN/m^2
317
+ """
318
+
319
+ #: Maximum effective overburden pressure. (:math:`kN/m^2`)
320
+ STD_PRESSURE: Final = 71.8
321
+
322
+ @property
323
+ def eop(self) -> float:
324
+ return self._eop
325
+
326
+ @eop.setter
327
+ @validators.ge(0.0)
328
+ def eop(self, val: float) -> None:
329
+ self._eop = val
330
+
331
+ def correction(self) -> float:
332
+ """SPT Correction."""
333
+ if isclose(self.eop, self.STD_PRESSURE, rel_tol=0.01):
334
+ corr = 1.0
335
+ elif self.eop < self.STD_PRESSURE:
336
+ corr = 4.0 / (1.0 + 0.0418 * self.eop)
337
+ else:
338
+ corr = 4.0 / (3.25 + 0.0104 * self.eop)
339
+ return corr
340
+
341
+
342
+ class PeckOPC(OPC):
343
+ r"""Overburden Pressure Correction according to ``Peck et al. (1974)``.
344
+
345
+ Overburden Pressure Correction is given by the formula:
346
+
347
+ .. math:: C_N = 0.77 \log \left(\dfrac{2000}{\sigma_o} \right)
348
+ """
349
+
350
+ @property
351
+ def eop(self) -> float:
352
+ return self._eop
353
+
354
+ @eop.setter
355
+ @validators.ge(24.0)
356
+ def eop(self, val: float) -> None:
357
+ self._eop = val
358
+
359
+ def correction(self) -> float:
360
+ """SPT Correction."""
361
+ return 0.77 * log10(2000.0 / self.eop)
362
+
363
+
364
+ class LiaoWhitmanOPC(OPC):
365
+ r"""Overburden Pressure Correction according to ``Liao & Whitman (1986)``.
366
+
367
+ Overburden Pressure Correction is given by the formula:
368
+
369
+ .. math:: C_N = \sqrt{\dfrac{100}{\sigma_o}}
370
+ """
371
+
372
+ @property
373
+ def eop(self) -> float:
374
+ return self._eop
375
+
376
+ @eop.setter
377
+ @validators.gt(0.0)
378
+ def eop(self, val: float) -> None:
379
+ self._eop = val
380
+
381
+ def correction(self) -> float:
382
+ """SPT Correction."""
383
+ return sqrt(100.0 / self.eop)
384
+
385
+
386
+ class SkemptonOPC(OPC):
387
+ r"""Overburden Pressure Correction according to ``Skempton (1986)``.
388
+
389
+ Overburden Pressure Correction is given by the formula:
390
+
391
+ .. math:: C_N = \dfrac{2}{1 + 0.01044 \cdot \sigma_o}
392
+ """
393
+
394
+ @property
395
+ def eop(self) -> float:
396
+ return self._eop
397
+
398
+ @eop.setter
399
+ @validators.ge(0.0)
400
+ def eop(self, val: float) -> None:
401
+ self._eop = val
402
+
403
+ def correction(self) -> float:
404
+ """SPT Correction."""
405
+ return 2.0 / (1.0 + 0.01044 * self.eop)
406
+
407
+
408
+ class DilatancyCorrection:
409
+ r"""Dilatancy SPT Correction according to ``Terzaghi & Peck (1948)``.
410
+
411
+ For coarse sand, this correction is not required. In applying this
412
+ correction, overburden pressure correction is applied first and then
413
+ dilatancy correction is applied.
414
+
415
+ Dilatancy correction is given by the formula:
416
+
417
+ .. math::
418
+
419
+ (N_1)_{60} &= 15 + \dfrac{1}{2}((N_1)_{60} - 15) \, , \,
420
+ (N_1)_{60} \gt 15
421
+
422
+ (N_1)_{60} &= (N_1)_{60} \, , \, (N_1)_{60} \le 15
423
+ """
424
+
425
+ def __init__(self, std_spt_n_value: float) -> None:
426
+ """
427
+ :param std_spt_n_value: SPT N-value standardized for field procedures
428
+ and/or corrected for overburden pressure.
429
+ :type std_spt_n_value: float
430
+ """
431
+ self.std_spt_n_value = std_spt_n_value
432
+
433
+ @property
434
+ def std_spt_n_value(self) -> float:
435
+ return self._std_spt_n_value
436
+
437
+ @std_spt_n_value.setter
438
+ @validators.gt(0.0)
439
+ def std_spt_n_value(self, val: float) -> None:
440
+ self._std_spt_n_value = val
441
+
442
+ @round_(ndigits=1)
443
+ def corrected_spt_n_value(self) -> float:
444
+ """Corrected SPT N-value."""
445
+ if self.std_spt_n_value <= 15.0:
446
+ return self.std_spt_n_value
447
+ return 15.0 + 0.5 * (self.std_spt_n_value - 15.0)