geolysis 0.11.0__py3-none-any.whl → 0.13.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.
@@ -1,7 +1,16 @@
1
1
  from geolysis.foundation import Shape
2
- from geolysis.utils import isclose, round_, sin, tan
2
+ from geolysis.utils import (
3
+ isclose,
4
+ round_,
5
+ sindeg,
6
+ tandeg,
7
+ atan,
8
+ add_repr,
9
+ cotdeg,
10
+ exp,
11
+ pi,
12
+ )
3
13
  from ._core import UltimateBearingCapacity
4
- from ._hansen_ubc import HansenBearingCapacityFactors
5
14
 
6
15
  __all__ = ["VesicUltimateBearingCapacity"]
7
16
 
@@ -11,85 +20,120 @@ class VesicBearingCapacityFactors:
11
20
  @staticmethod
12
21
  @round_(ndigits=2)
13
22
  def n_c(friction_angle: float) -> float:
14
- return HansenBearingCapacityFactors.n_c(friction_angle)
23
+ if isclose(friction_angle, 0.0):
24
+ return 5.14
25
+ return cotdeg(friction_angle) * (
26
+ VesicBearingCapacityFactors.n_q(friction_angle) - 1.0
27
+ )
15
28
 
16
29
  @staticmethod
17
30
  @round_(ndigits=2)
18
31
  def n_q(friction_angle: float) -> float:
19
- return HansenBearingCapacityFactors.n_q(friction_angle)
32
+ return tandeg(45.0 + friction_angle / 2.0) ** 2.0 * exp(
33
+ pi * tandeg(friction_angle)
34
+ )
20
35
 
21
36
  @staticmethod
22
37
  @round_(ndigits=2)
23
38
  def n_gamma(friction_angle: float) -> float:
24
39
  return (
25
- 2.0
26
- * (VesicBearingCapacityFactors.n_q(friction_angle) + 1.0)
27
- * tan(friction_angle)
40
+ 2.0
41
+ * (VesicBearingCapacityFactors.n_q(friction_angle) + 1.0)
42
+ * tandeg(friction_angle)
28
43
  )
29
44
 
30
45
 
31
46
  class VesicShapeFactors:
32
47
  @staticmethod
33
- @round_(ndigits=2)
48
+ @round_(ndigits=3)
34
49
  def s_c(
35
- friction_angle: float,
36
- f_width: float,
37
- f_length: float,
38
- f_shape: Shape,
50
+ friction_angle: float,
51
+ f_width: float,
52
+ f_length: float,
53
+ f_shape: Shape,
39
54
  ) -> float:
40
- _n_q = VesicBearingCapacityFactors.n_q(friction_angle)
41
- _n_c = VesicBearingCapacityFactors.n_c(friction_angle)
55
+ n_q = VesicBearingCapacityFactors.n_q(friction_angle)
56
+ n_c = VesicBearingCapacityFactors.n_c(friction_angle)
42
57
 
43
58
  if f_shape == Shape.STRIP:
44
59
  return 1.0
45
- elif f_shape == Shape.RECTANGLE:
46
- return 1.0 + (f_width / f_length) * (_n_q / _n_c)
47
- else: # SQUARE, CIRCLE
48
- return 1.0 + (_n_q / _n_c)
60
+ else:
61
+ return 1.0 + (f_width / f_length) * (n_q / n_c)
49
62
 
50
63
  @staticmethod
51
- @round_(ndigits=2)
64
+ @round_(ndigits=3)
52
65
  def s_q(
53
- friction_angle: float,
54
- f_width: float,
55
- f_length: float,
56
- f_shape: Shape,
66
+ friction_angle: float,
67
+ f_width: float,
68
+ f_length: float,
69
+ f_shape: Shape,
57
70
  ) -> float:
58
71
  if f_shape == Shape.STRIP:
59
72
  return 1.0
60
- elif f_shape == Shape.RECTANGLE:
61
- return 1.0 + (f_width / f_length) * tan(friction_angle)
62
- else: # SQUARE, CIRCLE
63
- return 1.0 + tan(friction_angle)
73
+ else:
74
+ return 1.0 + (f_width / f_length) * tandeg(friction_angle)
64
75
 
65
76
  @staticmethod
66
- @round_(ndigits=2)
77
+ @round_(ndigits=3)
67
78
  def s_gamma(f_width: float, f_length: float, f_shape: Shape) -> float:
68
79
  if f_shape == Shape.STRIP:
69
80
  return 1.0
70
- elif f_shape == Shape.RECTANGLE:
81
+ else:
71
82
  return 1.0 - 0.4 * (f_width / f_length)
72
- else: # SQUARE, CIRCLE
73
- return 0.6
74
83
 
75
84
 
76
85
  class VesicDepthFactors:
77
86
 
78
87
  @staticmethod
79
- @round_(ndigits=2)
80
- def d_c(f_depth: float, f_width: float) -> float:
81
- return 1.0 + 0.4 * f_depth / f_width
88
+ def _d_c(friction_angle: float, f_depth: float, f_width: float) -> float:
89
+ d_q = VesicDepthFactors.d_q(friction_angle, f_depth, f_width)
90
+ n_c = VesicBearingCapacityFactors.n_c(friction_angle)
91
+ return d_q - ((1.0 - d_q) / (n_c * tandeg(friction_angle)))
82
92
 
83
93
  @staticmethod
84
- @round_(ndigits=2)
94
+ @round_(ndigits=3)
95
+ def d_c(friction_angle: float, f_depth: float, f_width: float) -> float:
96
+ d2w = round(f_depth / f_width, 1)
97
+
98
+ if d2w <= 1.0:
99
+ if isclose(friction_angle, 0.0):
100
+ _d_c = 1.0 + 0.4 * d2w
101
+ else:
102
+ _d_c = VesicDepthFactors._d_c(friction_angle, f_depth, f_width)
103
+ else:
104
+ if isclose(friction_angle, 0.0):
105
+ _d_c = 1.0 + 0.4 * atan(d2w)
106
+ else:
107
+ _d_c = VesicDepthFactors._d_c(friction_angle, f_depth, f_width)
108
+
109
+ return _d_c
110
+
111
+ @staticmethod
112
+ @round_(ndigits=3)
85
113
  def d_q(friction_angle: float, f_depth: float, f_width: float) -> float:
86
- return 1.0 + 2.0 * tan(friction_angle) * (
87
- 1.0 - sin(friction_angle)) ** 2.0 * (
88
- f_depth / f_width
89
- )
114
+ d2w = f_depth / f_width
115
+
116
+ if d2w <= 1.0:
117
+ if isclose(friction_angle, 0.0):
118
+ _d_q = 1.0
119
+ else:
120
+ _d_q = (
121
+ 1.0
122
+ + 2
123
+ * tandeg(friction_angle)
124
+ * (1 - sindeg(friction_angle)) ** 2
125
+ * d2w
126
+ )
127
+ else:
128
+ if isclose(friction_angle, 0.0):
129
+ _d_q = 1.0
130
+ else:
131
+ _d_q = 1.0 + (
132
+ 2.0 * tandeg(friction_angle) * (1 - sindeg(friction_angle)) ** 2
133
+ ) * atan(d2w)
134
+ return _d_q
90
135
 
91
136
  @staticmethod
92
- @round_(ndigits=2)
93
137
  def d_gamma() -> float:
94
138
  return 1.0
95
139
 
@@ -97,23 +141,24 @@ class VesicDepthFactors:
97
141
  class VesicInclinationFactors:
98
142
 
99
143
  @staticmethod
100
- @round_(ndigits=2)
144
+ @round_(ndigits=3)
101
145
  def i_c(load_angle: float) -> float:
102
146
  return (1.0 - load_angle / 90.0) ** 2.0
103
147
 
104
148
  @staticmethod
105
- @round_(ndigits=2)
149
+ @round_(ndigits=3)
106
150
  def i_q(load_angle: float) -> float:
107
151
  return VesicInclinationFactors.i_c(load_angle)
108
152
 
109
153
  @staticmethod
110
- @round_(ndigits=2)
154
+ @round_(ndigits=3)
111
155
  def i_gamma(friction_angle: float, load_angle: float) -> float:
112
156
  if isclose(friction_angle, 0.0):
113
157
  return 1.0
114
158
  return (1.0 - load_angle / friction_angle) ** 2.0
115
159
 
116
160
 
161
+ @add_repr
117
162
  class VesicUltimateBearingCapacity(UltimateBearingCapacity):
118
163
  """Ultimate bearing capacity for soils according to `Vesic (1973)`.
119
164
 
@@ -158,7 +203,7 @@ class VesicUltimateBearingCapacity(UltimateBearingCapacity):
158
203
  def d_c(self) -> float:
159
204
  r"""Depth factor $D_c$."""
160
205
  depth, width = self.foundation_size.depth, self.foundation_size.width
161
- return VesicDepthFactors.d_c(depth, width)
206
+ return VesicDepthFactors.d_c(self.friction_angle, depth, width)
162
207
 
163
208
  @property
164
209
  def d_q(self) -> float:
@@ -184,5 +229,4 @@ class VesicUltimateBearingCapacity(UltimateBearingCapacity):
184
229
  @property
185
230
  def i_gamma(self) -> float:
186
231
  r"""Inclination factor $I_{\gamma}$."""
187
- return VesicInclinationFactors.i_gamma(self.friction_angle,
188
- self.load_angle)
232
+ return VesicInclinationFactors.i_gamma(self.friction_angle, self.load_angle)
geolysis/foundation.py CHANGED
@@ -1,16 +1,17 @@
1
1
  import enum
2
2
  from abc import ABC, abstractmethod
3
- from typing import Optional, TypeVar, Annotated
3
+ from typing import Optional, TypeAlias, TypeVar, Annotated
4
4
 
5
5
  from func_validator import (
6
- validate_func_args,
6
+ validate_params,
7
7
  MustBePositive,
8
8
  MustBeNonNegative,
9
9
  MustBeBetween,
10
10
  MustBeMemberOf,
11
+ DependsOn,
11
12
  )
12
13
 
13
- from .utils import AbstractStrEnum, inf, isclose
14
+ from .utils import AbstractStrEnum, inf, isclose, pi, round_, add_repr, isinf
14
15
 
15
16
  __all__ = [
16
17
  "create_foundation",
@@ -29,10 +30,10 @@ T = TypeVar("T")
29
30
  class Shape(AbstractStrEnum):
30
31
  """Enumeration of foundation shapes.
31
32
 
32
- Each member represents a standard geometric shape commonly used
33
- in foundation design, which can affect bearing capacity and
34
- settlement calculations.
35
- """
33
+ Each member represents a standard geometric shape commonly used
34
+ in foundation design, which can affect bearing capacity and
35
+ settlement calculations.
36
+ """
36
37
 
37
38
  STRIP = enum.auto()
38
39
  """Strip (or continuous) foundation, typically long and narrow."""
@@ -80,7 +81,7 @@ class FootingSize(ABC):
80
81
  raise NotImplementedError
81
82
 
82
83
  @width.setter
83
- def width(self, value: float):
84
+ def width(self, width: float):
84
85
  raise NotImplementedError
85
86
 
86
87
  @property
@@ -89,7 +90,10 @@ class FootingSize(ABC):
89
90
  raise NotImplementedError
90
91
 
91
92
  @length.setter
92
- def length(self, value: float):
93
+ def length(self, length: float):
94
+ raise NotImplementedError
95
+
96
+ def area(self) -> float:
93
97
  raise NotImplementedError
94
98
 
95
99
  @property
@@ -98,6 +102,7 @@ class FootingSize(ABC):
98
102
  return self._SHAPE
99
103
 
100
104
 
105
+ @add_repr
101
106
  class StripFooting(FootingSize):
102
107
  """A class representation of strip footing."""
103
108
 
@@ -117,9 +122,8 @@ class StripFooting(FootingSize):
117
122
  return self._width
118
123
 
119
124
  @width.setter
120
- @validate_func_args
121
- def width(self, val: Annotated[float, MustBePositive]) -> None:
122
- self._width = val
125
+ def width(self, width: float) -> None:
126
+ self._width = width
123
127
 
124
128
  @property
125
129
  def length(self) -> float:
@@ -127,11 +131,17 @@ class StripFooting(FootingSize):
127
131
  return self._length
128
132
 
129
133
  @length.setter
130
- @validate_func_args
131
- def length(self, val: Annotated[float, MustBePositive]) -> None:
132
- self._length = val
134
+ def length(self, length: float) -> None:
135
+ self._length = length
136
+
137
+ def area(self) -> float:
138
+ """Area of strip footing ($m \text{or} m^2$)."""
139
+ if isinf(self.length):
140
+ return self.width
141
+ return self.width * self.length
133
142
 
134
143
 
144
+ @add_repr
135
145
  class CircularFooting(FootingSize):
136
146
  """A class representation of circular footing.
137
147
 
@@ -156,9 +166,8 @@ class CircularFooting(FootingSize):
156
166
  return self._diameter
157
167
 
158
168
  @diameter.setter
159
- @validate_func_args
160
- def diameter(self, val: Annotated[float, MustBePositive]) -> None:
161
- self._diameter = val
169
+ def diameter(self, diameter: float) -> None:
170
+ self._diameter = diameter
162
171
 
163
172
  @property
164
173
  def width(self):
@@ -167,8 +176,8 @@ class CircularFooting(FootingSize):
167
176
 
168
177
  # Not checking for positive as diameter setter already does that
169
178
  @width.setter
170
- def width(self, val: float):
171
- self.diameter = val
179
+ def width(self, width: float):
180
+ self.diameter = width
172
181
 
173
182
  # Not checking for positive as diameter setter already does that
174
183
  @property
@@ -177,10 +186,15 @@ class CircularFooting(FootingSize):
177
186
  return self.diameter
178
187
 
179
188
  @length.setter
180
- def length(self, val: float):
181
- self.diameter = val
189
+ def length(self, length: float):
190
+ self.diameter = length
182
191
 
192
+ def area(self) -> float:
193
+ """Area of circular footing ($m^2$)."""
194
+ return pi * self.diameter ** 2 / 4
183
195
 
196
+
197
+ @add_repr
184
198
  class SquareFooting(FootingSize):
185
199
  """A class representation of square footing."""
186
200
 
@@ -198,9 +212,8 @@ class SquareFooting(FootingSize):
198
212
  return self._width
199
213
 
200
214
  @width.setter
201
- @validate_func_args
202
- def width(self, val: Annotated[float, MustBePositive]) -> None:
203
- self._width = val
215
+ def width(self, width: float) -> None:
216
+ self._width = width
204
217
 
205
218
  @property
206
219
  def length(self):
@@ -209,10 +222,15 @@ class SquareFooting(FootingSize):
209
222
 
210
223
  # Not checking for positive as width setter already does that
211
224
  @length.setter
212
- def length(self, val):
213
- self.width = val
225
+ def length(self, length: float):
226
+ self.width = length
227
+
228
+ def area(self) -> float:
229
+ """Area of square footing ($m^2$)."""
230
+ return self.width ** 2
214
231
 
215
232
 
233
+ @add_repr
216
234
  class RectangularFooting(FootingSize):
217
235
  """A class representation of rectangular footing."""
218
236
 
@@ -232,9 +250,8 @@ class RectangularFooting(FootingSize):
232
250
  return self._width
233
251
 
234
252
  @width.setter
235
- @validate_func_args
236
- def width(self, val: Annotated[float, MustBePositive]) -> None:
237
- self._width = val
253
+ def width(self, width: float) -> None:
254
+ self._width = width
238
255
 
239
256
  @property
240
257
  def length(self) -> float:
@@ -242,11 +259,15 @@ class RectangularFooting(FootingSize):
242
259
  return self._length
243
260
 
244
261
  @length.setter
245
- @validate_func_args
246
- def length(self, val: Annotated[float, MustBePositive]) -> None:
247
- self._length = val
262
+ def length(self, length: float) -> None:
263
+ self._length = length
264
+
265
+ def area(self) -> float:
266
+ """Area of rectangular footing ($m^2$)."""
267
+ return self.width * self.length
248
268
 
249
269
 
270
+ @add_repr
250
271
  class Foundation:
251
272
  """A simple class representing a foundation structure."""
252
273
 
@@ -256,7 +277,7 @@ class Foundation:
256
277
  footing_size: FootingSize,
257
278
  eccentricity: float = 0.0,
258
279
  load_angle: float = 0.0,
259
- ground_water_level: Optional[float] = None,
280
+ ground_water_level: Optional[float] = inf,
260
281
  foundation_type: FoundationType = FoundationType.PAD,
261
282
  ) -> None:
262
283
  r"""
@@ -276,7 +297,7 @@ class Foundation:
276
297
  self.eccentricity = eccentricity
277
298
  self.load_angle = load_angle
278
299
 
279
- self._ground_water_level = ground_water_level
300
+ self.ground_water_level = ground_water_level
280
301
  self.foundation_type = foundation_type
281
302
 
282
303
  @property
@@ -285,9 +306,9 @@ class Foundation:
285
306
  return self._depth
286
307
 
287
308
  @depth.setter
288
- @validate_func_args
289
- def depth(self, val: Annotated[float, MustBePositive]) -> None:
290
- self._depth = val
309
+ @validate_params
310
+ def depth(self, depth: Annotated[float, MustBePositive]) -> None:
311
+ self._depth = depth
291
312
 
292
313
  @property
293
314
  def width(self) -> float:
@@ -295,8 +316,8 @@ class Foundation:
295
316
  return self.footing_size.width
296
317
 
297
318
  @width.setter
298
- def width(self, val: Annotated[float, MustBePositive]):
299
- self.footing_size.width = val
319
+ def width(self, width: Annotated[float, MustBePositive]):
320
+ self.footing_size.width = width
300
321
 
301
322
  @property
302
323
  def length(self) -> float:
@@ -304,8 +325,8 @@ class Foundation:
304
325
  return self.footing_size.length
305
326
 
306
327
  @length.setter
307
- def length(self, val: Annotated[float, MustBePositive]):
308
- self.footing_size.length = val
328
+ def length(self, length: Annotated[float, MustBePositive]):
329
+ self.footing_size.length = length
309
330
 
310
331
  @property
311
332
  def footing_shape(self) -> Shape:
@@ -320,9 +341,10 @@ class Foundation:
320
341
  return self._eccentricity
321
342
 
322
343
  @eccentricity.setter
323
- @validate_func_args
324
- def eccentricity(self, val: Annotated[float, MustBeNonNegative]) -> None:
325
- self._eccentricity = val
344
+ @validate_params
345
+ def eccentricity(self, eccentricity: Annotated[
346
+ float, MustBeNonNegative]) -> None:
347
+ self._eccentricity = eccentricity
326
348
 
327
349
  @property
328
350
  def load_angle(self) -> float:
@@ -330,12 +352,13 @@ class Foundation:
330
352
  return self._load_angle
331
353
 
332
354
  @load_angle.setter
333
- @validate_func_args
355
+ @validate_params
334
356
  def load_angle(
335
357
  self,
336
- val: Annotated[float, MustBeBetween(min_value=0.0, max_value=90.0)]
358
+ load_angle: Annotated[
359
+ float, MustBeBetween(min_value=0.0, max_value=90.0)],
337
360
  ) -> None:
338
- self._load_angle = val
361
+ self._load_angle = load_angle
339
362
 
340
363
  @property
341
364
  def ground_water_level(self) -> Optional[float]:
@@ -343,9 +366,12 @@ class Foundation:
343
366
  return self._ground_water_level
344
367
 
345
368
  @ground_water_level.setter
346
- @validate_func_args
347
- def ground_water_level(self, val: Annotated[float, MustBePositive]):
348
- self._ground_water_level = val
369
+ @validate_params
370
+ def ground_water_level(
371
+ self,
372
+ ground_water_level: Annotated[float, MustBePositive],
373
+ ):
374
+ self._ground_water_level = ground_water_level
349
375
 
350
376
  @property
351
377
  def foundation_type(self) -> FoundationType:
@@ -353,12 +379,18 @@ class Foundation:
353
379
  return self._foundation_type
354
380
 
355
381
  @foundation_type.setter
356
- @validate_func_args
382
+ @validate_params
357
383
  def foundation_type(
358
384
  self,
359
- val: Annotated[FoundationType, MustBeMemberOf(FoundationType)]
385
+ foundation_type: Annotated[
386
+ FoundationType, MustBeMemberOf(FoundationType)],
360
387
  ):
361
- self._foundation_type = val
388
+ self._foundation_type = foundation_type
389
+
390
+ @round_(2)
391
+ def foundation_area(self) -> float:
392
+ """Returns the area of the foundation footing ($m^2$)."""
393
+ return self.footing_size.area()
362
394
 
363
395
  @property
364
396
  def effective_width(self) -> float:
@@ -370,7 +402,10 @@ class Foundation:
370
402
  of the foundation footing.
371
403
  """
372
404
  width, length, shape = (
373
- self.effective_width, self.length, self.footing_shape)
405
+ self.effective_width,
406
+ self.length,
407
+ self.footing_shape,
408
+ )
374
409
 
375
410
  if not isclose(width, length) and shape != Shape.STRIP:
376
411
  shape = Shape.RECTANGLE
@@ -378,14 +413,14 @@ class Foundation:
378
413
  return width, length, shape
379
414
 
380
415
 
381
- @validate_func_args
416
+ @validate_params
382
417
  def create_foundation(
383
418
  depth: float,
384
419
  width: float,
385
- length: Optional[float] = None,
420
+ length: Annotated[float, DependsOn(shape=Shape.RECTANGLE)] = None,
386
421
  eccentricity: float = 0.0,
387
422
  load_angle: float = 0.0,
388
- ground_water_level: Optional[float] = None,
423
+ ground_water_level: Optional[float] = inf,
389
424
  foundation_type: FoundationType = "pad",
390
425
  shape: Annotated[Shape | str, MustBeMemberOf(Shape)] = "square",
391
426
  ) -> Foundation:
@@ -401,31 +436,25 @@ def create_foundation(
401
436
  load aligns with the center of gravity of the
402
437
  foundation footing .
403
438
  :param load_angle: Inclination of the applied load with the vertical
404
- (:math:`\alpha^{\circ}`), defaults to 0.0.
405
- :param ground_water_level: Depth of the water below ground level (m),
406
- defaults to None.
407
- :param foundation_type: Type of foundation footing, defaults to
408
- :py:enum:mem:`~FoundationType.PAD`.
409
- :param shape: Shape of foundation footing, defaults to
410
- :py:enum:mem:`~Shape.SQUARE`
411
- :raises ValueError: Raised when length is not provided for a
412
- rectangular footing.
413
- :raises ValidationError: Raised if an invalid footing shape is
439
+ (:math:`\alpha^{\circ}`).
440
+ :param ground_water_level: Depth of the water below ground level (m)
441
+ :param foundation_type: Type of foundation footing.
442
+ :param shape: Shape of foundation footing
443
+
444
+ :raises ValidationError: Raised when length is not provided for a
445
+ rectangular footing or an invalid shape is
414
446
  provided.
415
447
  """
416
-
448
+ footing_size: FootingSize
417
449
  shape = Shape(str(shape).casefold())
418
450
 
419
- if shape is Shape.STRIP:
451
+ if shape == Shape.STRIP:
420
452
  footing_size = StripFooting(width=width)
421
- elif shape is Shape.SQUARE:
453
+ elif shape == Shape.SQUARE:
422
454
  footing_size = SquareFooting(width=width)
423
- elif shape is Shape.CIRCLE:
455
+ elif shape == Shape.CIRCLE:
424
456
  footing_size = CircularFooting(diameter=width)
425
457
  else: # RECTANGLE
426
- if not length:
427
- msg = "Length of rectangular footing must be provided."
428
- raise ValueError(msg)
429
458
  footing_size = RectangularFooting(width=width, length=length)
430
459
 
431
460
  return Foundation(