Skip to content

Pile design

Pile design from soil properties. The interpretation of measured load-test curves (Davisson, Chin, Hansen 80%, etc.) lives in ge.site. This page is the prediction side: predicting ultimate capacity from \(\phi\), \(S_u\), \(\sigma'_v\), etc.

\[ Q_{\text{ult}} = Q_p + Q_s = q_p \, A_p + f_s \, p \, L \]

End bearing — \(q_p\)

Two regimes:

  • Sand: \(q_p = \sigma'_v\,N_q^*\) — Meyerhof or Vesic for \(N_q^*\)
  • Clay: \(q_p = 9\,S_u\) — Skempton
Python
# Sand
ge.pile_end_bearing(phi=35, sigma_v_eff=100, method="meyerhof")
# {'q_p': 6311, 'Nq_star': 63.1, 'method': 'meyerhof'}

# Clay
ge.pile_end_bearing(Su=100)
# {'q_p': 900, 'Nc_star': 9.0, 'method': 'skempton_clay'}

Skin friction — \(f_s\) (three methods)

Method Form When
α (Tomlinson 1957) \(f_s = \alpha\,S_u\), \(\alpha\) from API RP 2A table Clay, total-stress
β (Burland 1973) \(f_s = \beta\,\sigma'_v\), \(\beta = K\tan\delta\) Effective-stress, any soil
λ (Vijayvergiya-Focht 1972) \(f_s = \lambda(\bar\sigma'_v + 2\bar S_u)\) Long offshore piles
Python
# Alpha method
ge.pile_skin_friction(Su=20,  method="alpha")          # soft clay -> f_s = 20
ge.pile_skin_friction(Su=200, method="alpha")          # stiff clay -> f_s = 70

# Beta method
ge.pile_skin_friction(sigma_v_eff=100, method="beta", phi=30)
# {'f_s': 28.87, 'beta': 0.289, 'method': 'beta'}

# Lambda method (offshore)
ge.pile_skin_friction(Su=50, sigma_v_eff=100, method="lambda")

Total capacity

Python
ge.pile_capacity(D=0.3, L=10, q_p=2000, f_s=30, FS=3)
# {'Q_p': 141.4, 'Q_s': 282.7, 'Q_ult': 424.1, 'Q_all': 141.4}

Group efficiency

Two methods — Converse-Labarre (most common) and Feld:

\[ \eta_{\text{CL}} = 1 - \frac{\theta[(n-1)m + (m-1)n]}{90\,n m}, \quad \theta = \tan^{-1}(D/s) \;[°] \]
Python
ge.pile_group_efficiency(n=3, m=3, D=0.3, s=0.9)
# {'eta': 0.727, 'method': 'converse_labarre', ...}

Single-pile settlement (Vesic semi-empirical)

Three components: elastic compression of the shaft, tip settlement, shaft-friction transfer settlement.

Python
ge.pile_settlement(
    Q_w=400, Q_p=200, Q_s=200,
    D=0.3, L=10, Es=20000, Ep=25e6,
)

API reference

pile_end_bearing

Python
pile_end_bearing(phi: float = None, sigma_v_eff: float = None, c: float = None, method: str = 'meyerhof', Su: float = None) -> dict

Unit tip resistance q_p (kPa) at the pile base.

Two regimes:

  • Sand (drained): q_p = sigma'_v * N_q* Meyerhof: N_q* from chart -- approximation N_q* = exp(pi tan phi) * tan^2(45+phi/2) but capped per Meyerhof (1976) Fig 9.11. Vesic: N_q* = (1 + 2 K_0) / 3 * (tan phi)^? -- simplified here.
  • Clay (undrained): q_p = 9 * Su (Skempton)
PARAMETER DESCRIPTION
phi

Friction angle (degrees). Required for sand.

TYPE: float DEFAULT: None

sigma_v_eff

Effective vertical stress at pile tip (kPa). Required for sand.

TYPE: float DEFAULT: None

Su

Undrained shear strength at tip (kPa). Required for clay.

TYPE: float DEFAULT: None

method

'meyerhof' | 'vesic' | 'skempton' (clay -- alias).

TYPE: str DEFAULT: 'meyerhof'

c

Optional drained cohesion (kPa). Adds c * Nc* contribution.

TYPE: float DEFAULT: None

RETURNS DESCRIPTION
dict

{'q_p': kPa, 'Nq*': ..., 'method': ...}.

Reference

Meyerhof (1976); Vesic (1977); Das (2014) Ch. 9.

Source code in geoeq/design/piles.py
Python
def pile_end_bearing(
    phi: float = None, sigma_v_eff: float = None, c: float = None,
    method: str = "meyerhof", Su: float = None,
) -> dict:
    """Unit tip resistance q_p (kPa) at the pile base.

    Two regimes:

    * **Sand (drained):** q_p = sigma'_v * N_q*
        Meyerhof:  N_q* from chart -- approximation N_q* = exp(pi tan phi) * tan^2(45+phi/2) but
                   capped per Meyerhof (1976) Fig 9.11.
        Vesic:     N_q* = (1 + 2 K_0) / 3 * (tan phi)^? -- simplified here.
    * **Clay (undrained):** q_p = 9 * Su    (Skempton)

    Parameters
    ----------
    phi : float
        Friction angle (degrees). Required for sand.
    sigma_v_eff : float
        Effective vertical stress at pile tip (kPa). Required for sand.
    Su : float
        Undrained shear strength at tip (kPa). Required for clay.
    method : str
        'meyerhof' | 'vesic' | 'skempton' (clay -- alias).
    c : float
        Optional drained cohesion (kPa). Adds c * Nc* contribution.

    Returns
    -------
    dict
        ``{'q_p': kPa, 'Nq*': ..., 'method': ...}``.

    Reference
    ---------
    Meyerhof (1976); Vesic (1977); Das (2014) Ch. 9.
    """
    method = method.lower()
    if Su is not None:
        # Clay -- Skempton's classical.
        check_positive(Su, "Su")
        return {"q_p": float(9 * Su), "Nc_star": 9.0, "method": "skempton_clay"}

    check_range(phi, "phi", 0, 50)
    check_positive(sigma_v_eff, "sigma_v_eff")
    phi_r = np.radians(phi)
    if method == "meyerhof":
        Nq_star = np.exp(np.pi * np.tan(phi_r)) * \
            np.tan(np.pi / 4 + phi_r / 2) ** 2
        # Meyerhof's empirical cap.
        Nq_star = min(Nq_star, 500.0)
    elif method == "vesic":
        # Reduced/refined per Vesic (1977).
        Nq_star = np.exp((np.pi / 2 + phi_r) * np.tan(phi_r)) * \
            np.tan(np.pi / 4 + phi_r / 2)
    else:
        raise ValueError("method must be 'meyerhof', 'vesic', or pass Su for clay.")
    q_p = sigma_v_eff * Nq_star
    if c:
        # Add cohesion contribution (Das Eq. 9.21).
        Nc_star = (Nq_star - 1) / np.tan(phi_r) if phi > 0 else 9.0
        q_p += c * Nc_star
    return {"q_p": float(q_p), "Nq_star": float(Nq_star), "method": method}

pile_skin_friction

Python
pile_skin_friction(Su: float = None, sigma_v_eff: float = None, method: str = 'alpha', phi: float = None, alpha: float = None, beta: float = None, K: float = None, delta: float = None, lambda_: float = None, layer_thicknesses: Sequence[float] = None) -> dict

Unit shaft friction f_s along a pile.

Methods (drained vs total stress):

  • alpha (Tomlinson 1957) -- total stress, for clays: f_s = alpha * Su
  • beta (Burland 1973) -- effective stress, for clays or sands: f_s = beta * sigma'_v where beta = K * tan(delta). Default K = K0 = 1 - sin(phi); delta = phi.
  • lambda (Vijayvergiya & Focht 1972) -- offshore long piles: f_s,avg = lambda * (sigma'_v_avg + 2 * Su_avg)
PARAMETER DESCRIPTION
Su

Undrained shear strength (kPa). Required for alpha.

TYPE: float DEFAULT: None

sigma_v_eff

Effective vertical stress at depth (kPa). Required for beta/lambda.

TYPE: float DEFAULT: None

method

'alpha' | 'beta' | 'lambda'.

TYPE: str DEFAULT: 'alpha'

alpha

Method-specific parameters. Sensible defaults are computed.

TYPE: float DEFAULT: None

beta

Method-specific parameters. Sensible defaults are computed.

TYPE: float DEFAULT: None

K

Method-specific parameters. Sensible defaults are computed.

TYPE: float DEFAULT: None

delta

Method-specific parameters. Sensible defaults are computed.

TYPE: float DEFAULT: None

lambda_

Method-specific parameters. Sensible defaults are computed.

TYPE: float DEFAULT: None

layer_thicknesses

For lambda, weighting of multi-layer averages.

TYPE: sequence DEFAULT: None

Reference

Tomlinson (1957); Burland (1973); Vijayvergiya & Focht (1972).

Source code in geoeq/design/piles.py
Python
def pile_skin_friction(
    Su: float = None, sigma_v_eff: float = None,
    method: str = "alpha", phi: float = None,
    alpha: float = None, beta: float = None,
    K: float = None, delta: float = None, lambda_: float = None,
    layer_thicknesses: Sequence[float] = None,
) -> dict:
    """Unit shaft friction f_s along a pile.

    Methods (drained vs total stress):

    * **alpha (Tomlinson 1957)** -- total stress, for clays:
        f_s = alpha * Su
    * **beta (Burland 1973)** -- effective stress, for clays or sands:
        f_s = beta * sigma'_v   where beta = K * tan(delta).
        Default K = K0 = 1 - sin(phi); delta = phi.
    * **lambda (Vijayvergiya & Focht 1972)** -- offshore long piles:
        f_s,avg = lambda * (sigma'_v_avg + 2 * Su_avg)

    Parameters
    ----------
    Su : float
        Undrained shear strength (kPa). Required for alpha.
    sigma_v_eff : float
        Effective vertical stress at depth (kPa). Required for beta/lambda.
    method : str
        'alpha' | 'beta' | 'lambda'.
    alpha, beta, K, delta, lambda_ : float
        Method-specific parameters. Sensible defaults are computed.
    layer_thicknesses : sequence, optional
        For ``lambda``, weighting of multi-layer averages.

    Reference
    ---------
    Tomlinson (1957); Burland (1973); Vijayvergiya & Focht (1972).
    """
    method = method.lower()
    if method == "alpha":
        check_positive(Su, "Su")
        if alpha is None:
            # API RP 2A (1993) tabulation; simplified.
            if Su <= 25:
                alpha = 1.0
            elif Su <= 75:
                alpha = 1.0 - 0.5 * (Su - 25) / 50  # linear 1.0 -> 0.5
            elif Su <= 175:
                alpha = 0.5 - 0.0014 * (Su - 75)    # linear 0.5 -> ~0.35
            else:
                alpha = 0.35
        return {"f_s": float(alpha * Su), "alpha": float(alpha),
                "method": "alpha"}
    if method == "beta":
        check_positive(sigma_v_eff, "sigma_v_eff")
        if beta is None:
            if K is None:
                if phi is None:
                    raise ValueError("beta method needs phi (or beta/K).")
                K = 1 - np.sin(np.radians(phi))
            if delta is None:
                if phi is None:
                    raise ValueError("beta method needs phi (or delta).")
                delta = phi
            beta = K * np.tan(np.radians(delta))
        return {"f_s": float(beta * sigma_v_eff), "beta": float(beta),
                "method": "beta"}
    if method == "lambda":
        check_positive(sigma_v_eff, "sigma_v_eff")
        check_positive(Su, "Su")
        if lambda_ is None:
            # Vijayvergiya & Focht (1972) chart, approximated.
            # lambda decreases with embedment; rough default 0.3.
            lambda_ = 0.3
        f_s = lambda_ * (sigma_v_eff + 2 * Su)
        return {"f_s": float(f_s), "lambda": float(lambda_),
                "method": "lambda"}
    raise ValueError("method must be 'alpha', 'beta', or 'lambda'.")

pile_capacity

Python
pile_capacity(D: float, L: float, q_p: float, f_s: float, area_base: float = None, perimeter: float = None, FS: float = 3.0) -> dict

Axial capacity Q_ult = Q_p + Q_s = q_p * A_p + f_s * As.

PARAMETER DESCRIPTION
D

Pile diameter (m).

TYPE: float

L

Pile length (embedded depth, m).

TYPE: float

q_p

Unit tip resistance (kPa) -- from pile_end_bearing.

TYPE: float

f_s

Unit shaft friction (kPa) -- from pile_skin_friction. Pass the depth-averaged value, or pre-multiply by length.

TYPE: float

area_base

Base area (m^2). Default pi*D^2/4 (closed-ended round pile).

TYPE: float DEFAULT: None

perimeter

Shaft perimeter (m). Default pi*D.

TYPE: float DEFAULT: None

FS

Factor of safety on Q_ult.

TYPE: float DEFAULT: 3.0

Reference

Das (2014) Eq. 9.20.

Source code in geoeq/design/piles.py
Python
def pile_capacity(
    D: float, L: float,
    q_p: float, f_s: float, area_base: float = None,
    perimeter: float = None, FS: float = 3.0,
) -> dict:
    """Axial capacity Q_ult = Q_p + Q_s = q_p * A_p + f_s * As.

    Parameters
    ----------
    D : float
        Pile diameter (m).
    L : float
        Pile length (embedded depth, m).
    q_p : float
        Unit tip resistance (kPa) -- from ``pile_end_bearing``.
    f_s : float
        Unit shaft friction (kPa) -- from ``pile_skin_friction``.
        Pass the depth-averaged value, or pre-multiply by length.
    area_base : float
        Base area (m^2). Default pi*D^2/4 (closed-ended round pile).
    perimeter : float
        Shaft perimeter (m). Default pi*D.
    FS : float
        Factor of safety on Q_ult.

    Reference
    ---------
    Das (2014) Eq. 9.20.
    """
    check_positive(D, "D")
    check_positive(L, "L")
    if area_base is None:
        area_base = np.pi * D ** 2 / 4.0
    if perimeter is None:
        perimeter = np.pi * D
    Q_p = q_p * area_base
    Q_s = f_s * perimeter * L
    Q_ult = Q_p + Q_s
    Q_all = Q_ult / FS
    return {
        "Q_p": float(Q_p), "Q_s": float(Q_s),
        "Q_ult": float(Q_ult), "Q_all": float(Q_all),
        "FS": float(FS),
    }

pile_group_efficiency

Python
pile_group_efficiency(n: int, m: int, D: float, s: float, method: str = 'converse_labarre') -> dict

Group efficiency factor eta for an n x m pile group.

Methods: * Converse-Labarre: eta = 1 - [theta * ((n-1)m + (m-1)n)] / (90 * n * m) with theta = atan(D/s) in degrees. * Feld: each pile loses 1/16 capacity for each adjacent pile.

Reference

Converse & Labarre (1947); Feld (1943); Das (2014) Eq. 9.74.

Source code in geoeq/design/piles.py
Python
def pile_group_efficiency(
    n: int, m: int, D: float, s: float, method: str = "converse_labarre",
) -> dict:
    """Group efficiency factor eta for an n x m pile group.

    Methods:
    * **Converse-Labarre**:
        eta = 1 - [theta * ((n-1)*m + (m-1)*n)] / (90 * n * m)
        with theta = atan(D/s) in degrees.
    * **Feld**: each pile loses 1/16 capacity for each adjacent pile.

    Reference
    ---------
    Converse & Labarre (1947); Feld (1943); Das (2014) Eq. 9.74.
    """
    if n < 1 or m < 1:
        raise ValueError("n, m must be >= 1.")
    check_positive(D, "D")
    check_positive(s, "s")
    method = method.lower()
    if method in ("converse_labarre", "cl"):
        theta = np.degrees(np.arctan(D / s))
        eta = 1 - theta * ((n - 1) * m + (m - 1) * n) / (90 * n * m)
    elif method == "feld":
        # Per pile: 1 - n_adj * 1/16. Average over the group.
        # Corner: 3 adj; edge: 5; interior: 8.
        total = 0.0
        for i in range(n):
            for j in range(m):
                adj = 0
                for di in (-1, 0, 1):
                    for dj in (-1, 0, 1):
                        if (di, dj) == (0, 0):
                            continue
                        if 0 <= i + di < n and 0 <= j + dj < m:
                            adj += 1
                total += max(0, 1 - adj / 16)
        eta = total / (n * m)
    else:
        raise ValueError("method must be 'converse_labarre' or 'feld'.")
    return {"eta": float(eta), "method": method,
            "n_piles": n * m, "spacing_ratio": s / D}

pile_settlement

Python
pile_settlement(Q_w: float, Q_p: float, Q_s: float, D: float, L: float, Es: float, Ep: float = 25000000.0, mu_s: float = 0.3, Cp: float = 0.03, Cs: float = None, qp_ult: float = None) -> dict

Vesic's three-component settlement of a single pile.

Text Only
s = s1 + s2 + s3
PARAMETER DESCRIPTION
Q_w

Working axial load (kN).

TYPE: float

Q_p

Tip load at working condition (kN).

TYPE: float

Q_s

Shaft load at working condition (kN).

TYPE: float

D

Pile diameter and length (m).

TYPE: float

L

Pile diameter and length (m).

TYPE: float

Es

Soil elastic modulus (kPa).

TYPE: float

Ep

Pile material modulus (kPa). Default 25 GPa (concrete).

TYPE: float DEFAULT: 25000000.0

mu_s

Soil Poisson's ratio.

TYPE: float DEFAULT: 0.3

Cp

Empirical tip-settlement coefficient (Das Table 9.5). Default 0.03.

TYPE: float DEFAULT: 0.03

Cs

Shaft-settlement coefficient. Default 0.93 + 0.16 sqrt(L/D) * Cp.

TYPE: float DEFAULT: None

qp_ult

Ultimate tip resistance (kPa). If given, used in s2 calc.

TYPE: float DEFAULT: None

Reference

Vesic (1977); Das (2014) Eq. 9.83-9.86.

Source code in geoeq/design/piles.py
Python
def pile_settlement(
    Q_w: float, Q_p: float, Q_s: float, D: float, L: float, Es: float,
    Ep: float = 25e6, mu_s: float = 0.3,
    Cp: float = 0.03, Cs: float = None, qp_ult: float = None,
) -> dict:
    """Vesic's three-component settlement of a single pile.

        s = s1 + s2 + s3

    Parameters
    ----------
    Q_w : float
        Working axial load (kN).
    Q_p : float
        Tip load at working condition (kN).
    Q_s : float
        Shaft load at working condition (kN).
    D, L : float
        Pile diameter and length (m).
    Es : float
        Soil elastic modulus (kPa).
    Ep : float
        Pile material modulus (kPa). Default 25 GPa (concrete).
    mu_s : float
        Soil Poisson's ratio.
    Cp : float
        Empirical tip-settlement coefficient (Das Table 9.5). Default 0.03.
    Cs : float, optional
        Shaft-settlement coefficient. Default 0.93 + 0.16 sqrt(L/D) * Cp.
    qp_ult : float
        Ultimate tip resistance (kPa). If given, used in s2 calc.

    Reference
    ---------
    Vesic (1977); Das (2014) Eq. 9.83-9.86.
    """
    check_positive(D, "D")
    check_positive(L, "L")
    check_positive(Es, "Es")
    check_positive(Ep, "Ep")
    # s1: elastic compression of pile shaft.
    # s1 = (Q_p + xi * Q_s) * L / (A_p * E_p)   with xi ~ 0.5 to 0.67 (avg).
    xi = 0.5
    A_p = np.pi * D ** 2 / 4
    s1 = (Q_p + xi * Q_s) * L / (A_p * Ep)
    # s2: tip settlement caused by Q_p.
    if qp_ult is None:
        qp_ult = (Q_p / A_p) * 3  # rough -- mobilization at FS=3
    s2 = (Q_p / A_p) * D * (1 - mu_s ** 2) / Es * Cp  # Vesic semi-empirical
    if Cs is None:
        Cs = (0.93 + 0.16 * np.sqrt(L / D)) * Cp
    # s3: shaft skin friction transferred load.
    s3 = (Q_s / (np.pi * D * L)) * D * (1 - mu_s ** 2) / Es * Cs
    s_total = s1 + s2 + s3
    return {
        "s1_elastic": float(s1), "s2_tip": float(s2), "s3_shaft": float(s3),
        "s_total": float(s_total),
    }