import math
from typing import Any
from scipy.optimize import minimize_scalar
[docs]
class DuncanMokwaLogSpiral:
"""Compute passive pressure on retaining wall.
ref: Mokwa, R. L. (1999). Investigation of Resistance of Pile Caps to Lateral Loading.
Duncan, M. J., and Mokwa, R. L. (2001). Passive Earth Pressures: Theories and Tests
"""
def __init__(self, soil_layer, foundation):
"""Initialize the log spiral method for passive pressure with soil_layer and foundation.
:param :class: SoilLayer soil_layer:
:param :class: Foundation foundation:
"""
self._c: float = soil_layer.c
self._phi: float = math.radians(soil_layer.phi)
self._gamma: float = soil_layer.gamma
self._delta: float = math.radians(soil_layer.delta)
self._alphac: float = soil_layer.alphac
self._q: float = soil_layer.q
self._h: float = foundation.h
self._alpha: float = math.pi / 4 - self._phi / 2
self._w: float = 0.0
self._xo: float = 0.0
self._yo: float = 0.0
self._hd: float = 0.0
self._r: float = 0.0
self._dist_r: float = 0.0
self._ro: float = 0.0
self._theta: float = 0.0
self._l1: float = 0.0
self._l2: float = 0.0
self._l3: float = 0.0
self._l4: float = 0.0
self._l5: float = 0.0
self._mc: float = 0.0
self._log_spiral_weight: float = 0.0
self._Eprphi: float = 0.0
self._Eprc: float = 0.0
self._Eprq: float = 0.0
self._Ppphi: float = 0.0
self._Ppc: float = 0.0
self._Ppq: float = 0.0
self._Ep: float = 0.0
@property
def w(self) -> float:
"""The width of the log spiral along the surface."""
return self._w
@property
def xo(self) -> float:
"""The horizontal distance of the log spiral center from the wall."""
return self._xo
@property
def yo(self) -> float:
"""The vertical distance of the log spiral center from the top of the wall."""
return self._yo
@property
def alpha(self) -> float:
"""Passive failure angle with the horizontal"""
return self._alpha
@property
def ro(self) -> float:
"""Log spiral initial radius"""
return self._ro
@property
def r(self) -> float:
"""Log spiral radius"""
return self._r
@property
def log_spiral_weight(self) -> float:
"""The weight of the soil in the log spiral region"""
return self._log_spiral_weight
@property
def theta(self) -> float:
"""Angle of the log spiral"""
return self._theta
@property
def hd(self) -> float:
"""The vertical height of the Rankine passive earth pressure region behind the log spiral region."""
return self._hd
@property
def l1(self) -> float:
"""Vertical moment arm of the passive force, Ppphi, from the log spiral center."""
return self._l1
@property
def l2(self) -> float:
"""Moment arm of the log spiral weight from the log spiral center."""
return self._l2
@property
def l3(self) -> float:
"""Moment arm of the Rankine passive force from friction, from passive region behind the log spiral region."""
return self._l3
@property
def l4(self) -> float:
"""Moment arm of the surcharge force from the log spiral center."""
return self._l4
@property
def l5(self) -> float:
"""Moment arm of Rankine passive force from cohesion, from passive region behind the log spiral region."""
return self._l5
@property
def Eprphi(self) -> None:
"""Rankine passive earth pressure due to soil weight and friction angle."""
return self._Eprphi
@property
def Eprc(self) -> None:
"""Rankine passive earth pressure due to cohesion."""
return self._Eprc
@property
def Eprq(self) -> None:
"""Rankine passive earth pressure due to surface surcharge."""
return self._Eprq
@property
def Ep(self) -> float:
"""The ultimate passive pressure."""
return self._Ep
@property
def Ppphi(self) -> float:
"""Passive force component due to soil weight and friction."""
return self._Ppphi
@property
def Ppc(self) -> float:
"""Passive force component due to soil cohesion."""
return self._Ppc
@property
def Ppq(self) -> float:
"""Passive force component due to surface surcharge."""
return self._Ppq
[docs]
def calc_hd1(self, w: float) -> float:
"""Calculate the height of Rankine earth presssure region behind the log spiral region.
during log spiral radius minimization, Eq. F.3.c
:param w: length of the failure region on the surface
:type: float
:return: hd, the height of the Rankine earth pressure region
:rtype: float
"""
return w * math.tan(self._alpha)
[docs]
def calc_hd2(self) -> float:
"""Calculate the height of Rankine earth presssure region behind the log spiral region,
after log spiral radius minimization.
Eq. F.4.a
:return: hd, the height of the Rankine earth pressure region
:rtype: float
"""
return self._r * math.sin(self._alpha) - self._yo
[docs]
def calc_yo(self, xo: float) -> float:
"""Compute ordinate of the log spiral center.
Eq. F.3.d
:param xo: abscissa of the log spiral ordinate.float = 5 * self._h
:type: float
:return: yo, ordinate of the log spiral center.
:rtype: float
"""
return xo * math.tan(self._alpha)
[docs]
def calc_log_spiral_ro(self, xo: float) -> float:
"""Compute, ro, the starting radius of the log spiral.
Eq F.3.e
:param xo: abscissa of the log spiral center
:type: float
:return: ro, initial radius of the log spiral
:rtype: float
"""
return math.sqrt((self._h + self._yo) ** 2 + xo**2)
[docs]
def calc_log_spiral_theta(self, xo: float) -> float:
"""Compute, log spiral angle theta.
Eq F.3.f
:param xo: abscissa of the log spiral center
:type: float
:return: theta, log spiral angle
:rtype: float
"""
return math.pi / 2 - math.atan(xo / (self._h + self._yo)) - self._alpha
[docs]
def calc_log_spiral_r(self) -> float:
"""Compute, log spiral radius at any angle theta from ro.
Eq F.2
:return: r, log spiral radius
:rtype: float
"""
return self._ro * math.exp(self._theta * math.tan((self._phi)))
[docs]
def calc_diagonal_dist(self, w: float, xo: float) -> float:
"""Compute, the diagonal distance from the log spiral center to the end of the log spiral
Eq F.3.g
:param w: length of the failure surface on the ground surface
:type: float
:param xo: abscissa of the log spiral center
:type: float
:return: r, the diagonal distance from the log spiral center to the end of the log spiral
:rtype: float
"""
return math.sqrt(w**2 + self._hd**2) + math.copysign(1, xo) * math.sqrt(
xo**2 + self._yo**2
)
[docs]
def calc_r(self, xo: float, w: float) -> float:
"""Adjust the center of the log spiral such that the diagonal distance and the log spiral
radius at the end of the log spiral are equal, i.e. minimize abs(log_spiral_r - r)
:param xo: abscissa of the origin of the log spiral
:type: float
:param w: length of the failure surface on the ground surface
:type: float
:return: r, the distance from the log spiral center to the end of the log spiral
:rtype: float
"""
self._xo = xo
# step 1: Compute Hd using Eq F.3.c
self._hd = self.calc_hd1(w)
# step 3: compute yo using Eqn F.3.d
self._yo = self.calc_yo(xo)
# step 4: compute ro using Eqn
self._ro = self.calc_log_spiral_ro(xo)
# step 5: compute theta, Eqn. F.3.f
self._theta = self.calc_log_spiral_theta(xo)
# step 6: compute using log spiral Eq F.2
self._r = self.calc_log_spiral_r()
# step 7: compute r using Eq F.3.g
self._dist_r = self.calc_diagonal_dist(w, xo)
return abs(self._r - self._dist_r)
[docs]
def calc_l1(self) -> float:
"""Vertical moment arm of the passive force, Ppphi, from the log spiral center, l1.
F.4.b
:return: l1, moment arm l1
:rtype: float
"""
return 2 / 3 * self._h + self._yo
[docs]
def calc_l2(self, w: float) -> float:
"""Moment arm of the log spiral weight from the log spiral center, l2.
Eqn F.4.d
:param w: the length of the failure surface on the ground
:type: float
:return: moment arm l2
:rtype: float
"""
return self._xo + w * (self._h + 2 * self._hd) / (3 * (self._h + self._hd))
[docs]
def calc_l3(self) -> float:
"""Moment arm of the Rankine passive force from friction, from passive region behind the log spiral region, l3.
Eqn F.4.e
:return: moment arm l3
:rtype: float
"""
return 2 / 3 * self._hd + self._yo
[docs]
def calc_log_sprial_soil_weight(self, w: float) -> float:
"""Compute the weight of the log spiral region
Eqn F.5
:param w: lenght of the failure surface on the ground
:type: float
:return: the weight of the log spiral
:rtype: float
"""
return self._gamma * (
(self._r**2 - self._ro**2) / (4 * math.tan(self._phi))
- 1 / 2 * self._xo * self._h
+ 1 / 2 * w * self._hd
)
[docs]
def calc_Eprphi(self) -> float:
"""Rankine earth pressure due to soil weight,
Eqn F.6
:return: Rankine earth pressure due to friction and soil weight
:rtype: float
"""
return (1 / 2 * self._gamma * self._hd**2) * (
math.tan(math.pi / 4 + self._phi / 2) ** 2
)
[docs]
def calc_l4(self, w: float) -> float:
"""Moment arm of the surcharge force from the log spiral center, l4.
Eq F.8
:param w: the length of the failure surface on the ground
:type: float
:return: moment arm l4
:rtype: float
"""
return self._xo + w / 2
[docs]
def calc_l5(self) -> float:
"""Moment arm of Rankine passive force from cohesion, from passive region behind the log spiral region.
Eq F.9
:return: moment arm l5
:rtype: float
"""
return self._yo + self._hd / 2
[docs]
def calc_Eprc(self) -> float:
"""Rankine earth pressure due to cohesion acting on the vertical face in the Rankine region
Eqn F.10
:return: Rankine earth pressure due to cohesion acting on the vertical face in the Rankine region
:rtype: float
"""
return 2 * self._c * math.tan(math.pi / 4 + self._phi / 2) * self._hd
[docs]
def calc_Mc(self) -> float:
"""Moment due to cohesion about point O
Eqn. F.11
:return: moment due to cohesion
:rtype: float
"""
return self._c / (2 * math.tan(self._phi)) * (self._r**2 - self._ro**2)
[docs]
def calc_Eprq(self) -> float:
"""Rankine earth pressure due to surcharge in the Rankine region
F.13
:return: Rankine earth pressure due to surcharge
:rtype: float
"""
return self._q * math.tan(math.pi / 4 + self._phi / 2) ** 2 * self._hd
[docs]
def calc_moment_arms(self, w: float) -> None:
"""Compute remaining quantities and moment arms after determing log spiral radius from minimization"""
self._hd = self.calc_hd2()
self._l1 = self.calc_l1()
self._l2 = self.calc_l2(w)
self._l3 = self.calc_l3()
self._l4 = self.calc_l4(w)
self._l5 = self.calc_l5()
self._log_spiral_weight = self.calc_log_sprial_soil_weight(w)
[docs]
def calc_Rankine_passive_earth_pressures(self) -> None:
"""Compute Rankine passive earth pressures"""
self._Eprphi = self.calc_Eprphi()
self._Eprc = self.calc_Eprc()
self._Eprq = self.calc_Eprq()
[docs]
def calc_Ppphi(self) -> float:
"""Earth pressure due to self weight of the soil and friction angle
Eqn F.7
:return: Passive force from self weight and friction angle
:rtype: float
"""
return (self._l2 * self._log_spiral_weight + self._l3 * self._Eprphi) / (
self._l1 * math.cos(self._delta) - self._xo * math.sin(self._delta)
)
[docs]
def calc_Ppc(self) -> float:
"""Earth pressure due to cohesion,
Eq F.12
:return: Earth pressure due to cohesion
:rtype: float
"""
self._mc = self.calc_Mc()
return (
self._mc
+ self._l5 * self._Eprc
+ self._alphac * self._c * self._h * self._xo
) / (self._l1 * math.cos(self._delta) - self._xo * math.sin(self._delta))
[docs]
def calc_Ppq(self, w: float) -> float:
"""Passvive force at the wall face due to surface surcharge
Eqn F.14
:return: Passive force due to surface surcharge
:rtype: float
"""
return (self._l4 * w * self._q + self._l5 * self._Eprq) / (
self._l1 * math.cos(self._delta) - self._xo * math.sin(self._delta)
)
[docs]
def calc_Ep(self, w: float) -> float:
"""Ultimate passive earth pressure
Eqn F.1
:param w: the length of the failure surface on the ground
:type: float
"""
# minimization to compute the extent of the log spiral curve
res = minimize_scalar(
self.calc_r,
args=(w),
bounds=(-5.0 * self._h, 5 * self._h),
method="bounded",
)
self._xo = res.x
self.calc_moment_arms(w)
self.calc_Rankine_passive_earth_pressures()
self._Ppphi = self.calc_Ppphi()
self._Ppc = self.calc_Ppc()
self._Ppq = self.calc_Ppq(w)
return self._Ppphi + self._Ppc + self._Ppq
[docs]
def passive_force(self) -> Any:
# minimize ultimate passive earth pressure by changing the horizontal extent of the log spiral
bnds = (0.25 * self._h, 5 * self._h)
res = minimize_scalar(
self.calc_Ep,
bounds=bnds,
method="bounded",
)
self._w = res.x
self._Ep = res.fun
return res