Skip to content

Commit 3e7a288

Browse files
author
Moreno
committed
Update suction capacity model and add profile module for anchors in homogeneous soil profiles
1 parent 74851c7 commit 3e7a288

File tree

11 files changed

+2474
-8
lines changed

11 files changed

+2474
-8
lines changed

famodel/anchors/anchors_famodel/capacity_suction.py

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def getCapacitySuction(D, L, zlug, H, V, soil_type, gamma, Su0=None, k=None, phi
4040
Maximum vertical capacity [kN]
4141
'''
4242

43-
lambdap = L/D; m = -2/3; # Suction pile slenderness ratio
43+
lambdap = L/D; m = 2/3; # Suction pile slenderness ratio
4444
t = (6.35 + D*20)/1e3 # Suction pile wall thickness (m), API RP2A-WSD
4545
rlug = D/2 # Radial position of the lug
4646
thetalug = 5 # Angle of tilt misaligment, default is 5. (deg)
@@ -83,7 +83,7 @@ def calc_delta(Dr_val):
8383
if soil_type == 'clay':
8484
# Definitions for cohesive soils
8585
Nc = min (6.2*(1 + 0.34*np.arctan(lambdap)),9) # End-bearing capacity factor
86-
ez = (Su0*L**2/2 + k*L**3/3)/(Su0*L + k*L**2/2)
86+
ez = (Su0*L**2/2 + k*L**3/3)/(Su0*L + k*L**2/2); print(ez)
8787
Np_fixed = 10.25; Np_free = 4 # From Np vs L/D chart from CAISSON_VHM
8888
Su_av_L = Su0 + k*zlug # Undrained shear strength values (average)
8989
Su_tip = Su0 + k*L # Undrained shear strength values (tip)
@@ -104,7 +104,7 @@ def calc_delta(Dr_val):
104104
M = - V*rlugTilt(rlug,zlug,thetalug) - H*(zlugTilt(rlug,zlug,thetalug) - ez)
105105
def f(Hmax):
106106
return m*(Hmax/(L*D*(Su0 + k*zlug)) - Np_fixed) + M*(Hmax/(L*D*(Su0 + k*zlug))/(Hmax*L))
107-
Hmax = fsolve(f,5);
107+
Hmax = fsolve(f,5)
108108

109109
# Torsion capacity
110110
Fo = PileSurface(L, D)*alpha*Su_av_L
@@ -126,6 +126,15 @@ def f(Hmax):
126126
# "Leaking"
127127
Vmax3 = (PileWeight(L, D, t, rhows) + PileSurface(L, D)*alphastar*Su_av_L + SoilWeight(L, D, t, gamma))
128128
Vmax = min(Vmax1, Vmax2, Vmax3)
129+
130+
print("\n--- Parameter-Based Version ---")
131+
print(f"Su_av_L = {Su_av_L:.3f} kPa")
132+
print(f"sigma'_v(zlug) = {sigma_v_eff:.3f} kPa")
133+
print(f"psi_val = {psi_val:.3f}")
134+
print(f"alpha (API) = {alpha:.3f}")
135+
print(f"Hmax = {Hmax[0]:.2f} kN")
136+
print(f"Vmax = {Vmax:.2f} kN")
137+
129138

130139
elif soil_type == 'sand':
131140
# Definition for non-cohesive soils
@@ -159,15 +168,22 @@ def f(Hmax):
159168
# return np.e**(-depth) - 1 + depth
160169
# Ze = D/(4*7); Zi = D/(4*5)
161170
# Vmax = 7*gamma*Ze**2*y(L/Ze)*PileSurface(L, D)/L + 5*gamma*Zi**2*y(L/Zi)*PileSurface(L,(D - 2*t))/L
162-
171+
172+
print("\n--- Parameter-Based (Sand) ---")
173+
print(f"phi = {phi:.2f} deg")
174+
print(f"gamma = {gamma:.2f} kN/m3")
175+
print(f"deltastar = {deltastar:.2f} -")
176+
print(f"sigma_av_L = {sigma_av_L:.2f} kN")
177+
print(f"sigma_tip = {sigma_tip:.2f} kN")
178+
163179
# Pile weight (inc. stiffening plus vent) assessed as a factor
164180
Wp = 1.10*PileWeight(L, D, t, (rhows + rhow))
165181
# Submerged weight of the soil plug
166182
Ws = SoilWeight(L, D, t, gamma)
167183

168184
# Capacity envelope
169185
aVH = 0.5 + lambdap; bVH = 4.5 + lambdap/3
170-
# print('Env. exp = ' +str(aVH)+' '+str(bVH))
186+
print('Env. exp = ' +str(aVH)+' '+str(bVH))
171187
UC = (H/Hmax)**aVH + (V/Vmax)**bVH
172188
x = np.cos(np.linspace (0, np.pi/2, 100))
173189
y = (1 - x**bVH)**(1/aVH)
@@ -194,7 +210,7 @@ def f(Hmax):
194210
resultsSuction['UC'] = UC[0] # Unity check in clay
195211
elif soil_type == 'sand':
196212
resultsSuction['UC'] = UC # Unity check in sand
197-
resultsSuction['Weight'] = Wp # Dry weight of the suction pile (kN)
213+
resultsSuction['Weight'] = Wp # Dry weight of the suction pile (kN)
198214
resultsSuction['Weight Soil'] = Ws # Submerged weight of the soil plug (kN)
199215
resultsSuction['t'] = t # Pile thikness in [m]
200216

@@ -291,8 +307,27 @@ def SoilWeight(Len, Dia, tw, gamma_soil):
291307
resultsSuctionSimp['Horizontal max.'] = Hmax # Capacity at specified loading angle
292308
resultsSuctionSimp['Vertical max.'] = Vmax # Capacity at specified loading angle
293309
resultsSuctionSimp['UC'] = UC # Unity check
294-
resultsSuctionSimp['Weight'] = Wp # Pile weight in kN
310+
resultsSuctionSimp['Weight'] = Wp # Pile weight in kN
295311
resultsSuctionSimp['Weight Soil'] = Ws # in kN
296312
resultsSuctionSimp['t'] = t
297313

298-
return resultsSuctionSimp
314+
return resultsSuctionSimp
315+
316+
if __name__ == '__main__':
317+
318+
D = 2.0 # Diameter in meters
319+
L = 10.0 # Length in meters
320+
zlug = (2/3)*L # Padeye depth
321+
H = 1500.0 # Horizontal load in kN
322+
V = 1000.0 # Vertical load in kN
323+
324+
gamma = 8
325+
Su0 = 0
326+
k = 10
327+
328+
phi = 50
329+
Dr = 75
330+
331+
results_clay = getCapacitySuction(D, L, zlug, H, V, 'clay', gamma, Su0=Su0, k=k, phi=phi, Dr=Dr, plot=True)
332+
333+
results_sand = getCapacitySuction(D, L, zlug, H, V, 'sand', gamma, Su0=Su0, k=k, phi=phi, Dr=Dr, plot=True)
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
2+
import numpy as np
3+
import matplotlib.pyplot as plt
4+
from scipy.interpolate import interp1d
5+
from scipy import linalg
6+
from capacity_soils import rock_profile
7+
from capacity_pycurves import py_Lovera
8+
from capacity_plots import plot_pile
9+
10+
def getCapacityDandG(profile, soil_type, L, D, zlug, H, V, plot=True):
11+
'''Models a laterally loaded pile using the p-y method. The solution for
12+
lateral displacements is obtained by solving the 4th order ODE,
13+
EI*d4y/dz4 - V*d2y/dz2 + ky = 0 using the finite difference method.
14+
EI*d4y/dz4 - V*d2y/dz2 + K*z*dy/dz + ky = 0 using the finite difference method.
15+
16+
Assumes that EI remains constant with respect to curvature i.e. pile
17+
material remains in the elastic region.
18+
19+
Parameters
20+
----------
21+
profile : array
22+
Rock profile as a 2D array: (z (m), UCS (MPa), Em (MPa))
23+
soil_type : string
24+
Select soil condition, 'rock'
25+
L : float
26+
Pile length (m)
27+
D : float
28+
Pile diameter (m)
29+
zlug : float
30+
Load eccentricity above the mudline or depth to mudline relative to the pile head (m)
31+
H : float
32+
Horizontal load at pile lug elevation (N)
33+
V : float
34+
Vertical load at pile lug elevation (N)
35+
plot : bool
36+
Plot the p-y curve and the deflection pile condition if True
37+
38+
Returns
39+
-------
40+
y : array
41+
Lateral displacement at each node (n+1 real + 4 imaginary)
42+
z : array
43+
Node location along pile (m)
44+
resultsDandG : dict
45+
Dictionary with lateral, rotational, vertical and pile weight results
46+
'''
47+
48+
n = 50; iterations = 10; loc = 2
49+
nhuc = 1; nhu = 0.3 # Resistance factor (-)
50+
delta_r = 0.08 # Mean roughness height (m)
51+
52+
t = (6.35 + D*20)/1e3 # Pile wall thickness (m), API RP2A-WSD
53+
E = 200e9 # Elastic modulus of pile material (Pa)
54+
rhows = 66.90e3 # Submerged steel specific weight (N/m3)
55+
rhow = 10e3 # Water specific weight (N/m3)
56+
57+
# Pile geometry
58+
I = (np.pi/64.0)*(D**4 - (D - 2*t)**4)
59+
EI = E*I
60+
h = L/n # Element size
61+
N = (n + 1) + 4 # (n+1) Real + 4 Imaginary nodes
62+
63+
# Dry and wet mass of the pile
64+
def PileWeight(Len, Dia, tw, rho):
65+
Wp = ((np.pi/4)*(Dia**2 - (Dia - 2*tw)**2)*Len)*rho
66+
return Wp
67+
68+
# Array for displacements at nodes, including imaginary nodes.
69+
y = np.ones(N)*(0.01*D) # An initial value of 0.01D was arbitrarily chosen
70+
71+
# Initialize and assemble array/list of p-y curves at each real node
72+
z = np.zeros(N)
73+
k_secant = np.zeros(N)
74+
py_funs = []
75+
DQ = []
76+
z0, f_UCS, f_Em = rock_profile(profile)
77+
78+
for i in [0, 1]: # Top two imaginary nodes
79+
z[i] = (i - 2)*h
80+
py_funs.append(0)
81+
k_secant[i] = 0.0
82+
83+
for i in range(2, n+3): # Real nodes
84+
z[i] = (i - 2)*h
85+
if z[i] < z0:
86+
# No p-y curve above mudline
87+
py_funs.append(lambda y_val: np.zeros_like(y_val))
88+
k_secant[i] = 0.0
89+
DQ.append(0.0)
90+
else:
91+
py_funs.append(py_Lovera(z[i], D, f_UCS, f_Em, zlug, z0, plot=True))
92+
UCS = f_UCS(z[i])
93+
Em = f_Em(z[i])
94+
SCR = nhuc*Em/(UCS*(1 + nhu))*delta_r/D
95+
alpha = 0.36*SCR - 0.0005
96+
fs = alpha*UCS
97+
Dq = np.pi*D*fs*z[i]
98+
DQ.append(Dq)
99+
Vmax = PileWeight(L, D, t, rhows) + DQ[-1]
100+
101+
k_secant[i] = py_funs[i](y[i])/y[i]
102+
103+
for i in [n+3, n+4]: # Bottom two imaginary nodes
104+
z[i] = (i - 2)*h
105+
py_funs.append(0)
106+
k_secant[i] = 0.0
107+
108+
for j in range(iterations):
109+
# if j == 0: print 'FD Solver started!'
110+
y = fd_solver(n, N, h, EI, V, H, zlug, z0, k_secant)
111+
if plot:
112+
plt.plot(y[loc], k_secant[loc]*y[loc])
113+
114+
for i in range(2, n+3):
115+
k_secant[i] = py_funs[i](y[i])/y[i]
116+
117+
if plot:
118+
y1 = np.linspace(-2.*D, 2.*D, 500)
119+
plt.plot(y1, py_funs[loc](y1))
120+
plt.xlabel('y (m)'), plt.ylabel('p (N/m)')
121+
plt.grid(True)
122+
123+
fig, ax = plt.subplots(figsize=(3, 5))
124+
y0 = np.zeros_like(z[2:-2])
125+
# Plot original vertical pile
126+
ax.plot(y0, z[2:-2], 'k', label='Original pile axis')
127+
# Plot deflected shape
128+
ax.plot(y[2:-2], z[2:-2], 'r', label='Deflected shape')
129+
# Padeye marker
130+
ax.plot(0, zlug, 'ko', label=f'Padeye (zlug = {zlug:.2f} m)')
131+
# Mudline marker
132+
ax.axhline(z0, color='blue', linestyle='--', label=f'Mudline (z0 = {z0:.2f} m)')
133+
134+
ax.set_xlabel('Lateral displacement (m)')
135+
ax.set_ylabel('Depth (m)')
136+
ax.set_xlim([-0.1*D, 0.1*D])
137+
ax.set_ylim([L + 5, -2]) # Downward is positive z
138+
ax.grid(ls='--')
139+
ax.legend()
140+
141+
resultsDandG = {
142+
'Lateral displacement' : y[2],
143+
'Rotational displacement' : np.rad2deg((y[2] - y[3])/h),
144+
'Vertical max.' : Vmax,
145+
'Weight pile' : PileWeight(L, D, t, (rhows + rhow))
146+
}
147+
148+
return y[2:-2], z[2:-2], resultsDandG
149+
150+
def fd_solver(n, N, h, EI, H, V, zlug, z0, k_secant):
151+
'''Solves the finite difference equations from 'py_analysis_1'. This function should be run iteratively for
152+
non-linear p-y curves by updating 'k_secant' using 'y'. A single iteration is sufficient if the p-y curves
153+
are linear.
154+
155+
Parameters
156+
----------
157+
n : int
158+
Number of elements (-)
159+
N : int
160+
Total number of nodes (-)
161+
h : float
162+
Element size (m)
163+
EI : float
164+
Flexural rigidity of the pile (Nm²)
165+
H : float
166+
Horizontal load at padeye (N)
167+
V : float
168+
Vertical load at padeye (N)
169+
zlug : float
170+
Padeye depth from pile head (m)
171+
z0 : float
172+
Mudline elevation from pile head (m)
173+
k_secant : array
174+
Secant stiffness at each node
175+
176+
Returns
177+
-------
178+
y : array
179+
Lateral displacement at each node (n+1 real + 4 imaginary)
180+
'''
181+
182+
# Initialize and assemble matrix
183+
X = np.zeros((N, N))
184+
185+
# (n+1) finite difference equations for (n+1) real nodes
186+
for i in range(0,n+1):
187+
X[i, i] = 1.0
188+
X[i, i+1] = -4.0 + V*h**2/EI
189+
X[i, i+2] = 6.0 - 2*V*h**2/EI + k_secant[i+2]*h**4/EI
190+
X[i, i+3] = -4.0 + V*h**2/EI
191+
X[i, i+4] = 1.0
192+
193+
# Curvature at pile head
194+
X[n+1, 1] = 1.0
195+
X[n+1, 2] = -2.0
196+
X[n+1, 3] = 1.0
197+
198+
# Shear at pile head
199+
X[n+2, 0] = -1.0
200+
X[n+2, 1] = 2.0 - V*h**2/EI
201+
X[n+2, 2] = 0.0
202+
X[n+2, 3] = -2.0 + V*h**2/EI
203+
X[n+2, 4] = 1.0
204+
205+
# Curvature at pile tip
206+
X[n+3, -2] = 1.0
207+
X[n+3, -3] = -2.0
208+
X[n+3, -4] = 1.0
209+
210+
# Shear at pile tip
211+
X[n+4, -1] = 1.0
212+
X[n+4, -2] = -2.0 + V*h**2/EI
213+
X[n+4, -3] = 0.0
214+
X[n+4, -4] = 2.0 - V*h**2/EI
215+
X[n+4, -5] = -1.0
216+
217+
# Initialize vector q
218+
q = np.zeros(N)
219+
220+
# Always apply shear
221+
# Index of the node where the horizontal load is applied (padeye)
222+
zlug_index = int(zlug/h)
223+
q[zlug_index] = 2*H*h**3
224+
225+
# Solve for displacement
226+
y = linalg.solve(EI*X, q)
227+
228+
return y
229+
230+
if __name__ == '__main__':
231+
232+
profile_rock = np.array([
233+
[ 2.0, 1, 1e3],
234+
[ 5.0, 2, 2e4],
235+
[ 9.0, 4, 2e4],
236+
[30.0, 6, 5e4]
237+
])
238+
239+
D = 3.0 # Diameter (m)
240+
L = 10.0 # Length (m)
241+
zlug = 1 # Padeye elevation (m)
242+
243+
y, z, results = getCapacityDandG(profile_rock, 'rock', L, D, zlug, H=9.5e12, V=3.0e6, plot=True)
244+
245+
plot_pile(profile_rock, 'rock', y, z, D, L, profile_rock[0, 0], zlug)

0 commit comments

Comments
 (0)