Skip to content

Bearing capacity

Four classical methods in one function. The general bearing-capacity equation (Meyerhof / Hansen / Vesic):

\[ \boxed{\;q_u = c\,N_c\,s_c\,d_c\,i_c + q\,N_q\,s_q\,d_q\,i_q + \tfrac{1}{2}\gamma B\,N_\gamma\,s_\gamma\,d_\gamma\,i_\gamma\;} \]

Terzaghi (1943) uses the same first/second/third-term form but without shape, depth, or inclination corrections.

Quick example

Python
import geoeq as ge

# Square footing on dense sand
bc = ge.bearing_capacity(
    c=0, gamma=18, Df=1, B=2, L=2,
    phi=35, method="meyerhof",
)
print(bc["q_u"])                    # ultimate bearing capacity, kPa

# Allowable with FS = 3 (standard for static loading)
ge.bearing_allowable(bc["q_u"], FS=3)

Method comparison

The four methods diverge most for \(N_\gamma\) in granular soils:

Python
for m in ("terzaghi", "meyerhof", "hansen", "vesic"):
    f = ge.bearing_factors(phi=30, method=m)
    print(f"{m:9s} -> Nc={f['Nc']:6.2f} Nq={f['Nq']:6.2f} Ng={f['Ngamma']:6.2f}")
φ° Nc Nq \(N_\gamma\) Meyerhof \(N_\gamma\) Hansen \(N_\gamma\) Vesic
0 5.14 1.00 0.00 0.00 0.00
20 14.83 6.40 2.87 2.95 5.39
30 30.14 18.40 15.67 14.39 22.40
40 75.31 64.20 93.69 79.54 109.41

A design chart showing \(q_u\) vs \(B\) for all four methods:

Bearing capacity chart

Ultimate bearing capacity vs footing width for c = 0, φ = 30°, Df = 1 m. Generated by ge.bearing_capacity_plot().

Shape, depth, inclination factors

The corrections are independently callable if you want to inspect them:

Python
ge.bearing_shape_factors(B=2, L=3, phi=30, method="meyerhof")
# {'sc': 1.40, 'sq': 1.20, 's_gamma': 1.20}

ge.bearing_depth_factors(Df=1, B=2, phi=30, method="meyerhof")
# {'dc': 1.17, 'dq': 1.09, 'd_gamma': 1.09}

ge.bearing_inclination_factors(beta=10, phi=30)
# {'ic': 0.79, 'iq': 0.79, 'i_gamma': 0.45}

The unified bearing_capacity() function applies them automatically unless you pass shape=False, depth=False, or inclination=0.

Water-table effects

Pass water_table="at_base" to use the effective (buoyant) unit weight under the footing. This is the most common case for shallow footings on saturated sand.

API reference

bearing_factors

Python
bearing_factors(phi: float, method: str = 'meyerhof') -> dict

Bearing-capacity factors Nc, Nq, N_gamma for a given friction angle.

PARAMETER DESCRIPTION
phi

Effective friction angle (degrees).

TYPE: float

method

'terzaghi' | 'meyerhof' | 'hansen' | 'vesic'.

TYPE: str DEFAULT: 'meyerhof'

RETURNS DESCRIPTION
dict

{'Nc': ..., 'Nq': ..., 'Ngamma': ...}.

Reference

Das (2014) Tables 4.1-4.3.

Source code in geoeq/design/bearing.py
Python
def bearing_factors(phi: float, method: str = "meyerhof") -> dict:
    """Bearing-capacity factors Nc, Nq, N_gamma for a given friction angle.

    Parameters
    ----------
    phi : float
        Effective friction angle (degrees).
    method : str
        ``'terzaghi'`` | ``'meyerhof'`` | ``'hansen'`` | ``'vesic'``.

    Returns
    -------
    dict
        ``{'Nc': ..., 'Nq': ..., 'Ngamma': ...}``.

    Reference
    ---------
    Das (2014) Tables 4.1-4.3.
    """
    check_non_negative(phi, "phi")
    check_range(phi, "phi", 0, 50)
    method = method.lower()
    phi_rad = np.radians(phi)

    # Nq is identical across Meyerhof/Hansen/Vesic (Prandtl-Reissner).
    if phi == 0:
        Nq = 1.0
    else:
        Nq = np.exp(np.pi * np.tan(phi_rad)) * \
            np.tan(np.pi / 4 + phi_rad / 2) ** 2

    # Nc via classical relation Nc = (Nq - 1) / tan(phi); Nc = 5.14 at phi=0.
    if phi == 0:
        Nc = 5.14  # = pi + 2
    else:
        Nc = (Nq - 1) / np.tan(phi_rad)

    if method == "terzaghi":
        # Terzaghi's original Nq, Nc differ slightly (he used local shear);
        # use the Bowles-tabulated forms for general shear.
        Nq = np.exp((1.5 * np.pi - phi_rad) * np.tan(phi_rad)) / \
            (2 * np.cos(np.pi / 4 + phi_rad / 2) ** 2)
        if phi == 0:
            Nc = 5.7  # Terzaghi original
        else:
            Nc = (Nq - 1) / np.tan(phi_rad)
        # Terzaghi N_gamma (empirical fit to chart, Bowles 1996):
        Ngamma = (Nq - 1) * np.tan(1.4 * phi_rad) if phi > 0 else 0.0
    elif method == "meyerhof":
        Ngamma = (Nq - 1) * np.tan(1.4 * phi_rad)
    elif method == "hansen":
        Ngamma = 1.5 * (Nq - 1) * np.tan(phi_rad)
    elif method == "vesic":
        Ngamma = 2 * (Nq + 1) * np.tan(phi_rad)
    else:
        raise ValueError(
            "method must be 'terzaghi', 'meyerhof', 'hansen', or 'vesic'.")
    return {"Nc": float(Nc), "Nq": float(Nq), "Ngamma": float(Ngamma)}

bearing_capacity

Python
bearing_capacity(c: float, gamma: float, Df: float, B: float, phi: float, L: float = None, method: str = 'meyerhof', shape: bool = True, depth: bool = True, inclination: float = 0.0, gamma_above: float = None, water_table: str = 'deep') -> dict

Ultimate bearing capacity q_u of a shallow footing.

PARAMETER DESCRIPTION
c

Effective cohesion (kPa). Use Su for undrained (phi = 0) analysis.

TYPE: float

gamma

Effective unit weight of soil below the footing (kN/m^3). Use gamma' = gamma_sat - gamma_w if the water table is at the base of the footing.

TYPE: float

Df

Depth of embedment (m).

TYPE: float

B

Footing width (m).

TYPE: float

phi

Effective friction angle (degrees).

TYPE: float

L

Footing length (m). None -> strip footing (B/L -> 0).

TYPE: float DEFAULT: None

method

'terzaghi' | 'meyerhof' | 'hansen' | 'vesic'.

TYPE: str DEFAULT: 'meyerhof'

shape

Whether to apply shape/depth corrections. Terzaghi ignores both.

TYPE: bool DEFAULT: True

depth

Whether to apply shape/depth corrections. Terzaghi ignores both.

TYPE: bool DEFAULT: True

inclination

Load inclination from vertical (deg). Default 0.

TYPE: float DEFAULT: 0.0

gamma_above

Effective unit weight above the footing (kN/m^3). Default = gamma.

TYPE: float DEFAULT: None

water_table

'deep' (no correction) | 'at_base' (gamma below = gamma') | 'above_base' (full submergence -- conservative).

TYPE: str DEFAULT: 'deep'

RETURNS DESCRIPTION
dict with 'q_u' (kPa), 'Nc', 'Nq', 'Ngamma', and the applied factors.
Reference

Das (2014), Ch. 4.

Source code in geoeq/design/bearing.py
Python
def bearing_capacity(
    c: float, gamma: float, Df: float, B: float, phi: float,
    L: float = None, method: str = "meyerhof",
    shape: bool = True, depth: bool = True,
    inclination: float = 0.0, gamma_above: float = None,
    water_table: str = "deep",
) -> dict:
    """Ultimate bearing capacity q_u of a shallow footing.

    Parameters
    ----------
    c : float
        Effective cohesion (kPa). Use Su for undrained (phi = 0) analysis.
    gamma : float
        Effective unit weight of soil *below* the footing (kN/m^3).
        Use gamma' = gamma_sat - gamma_w if the water table is at the
        base of the footing.
    Df : float
        Depth of embedment (m).
    B : float
        Footing width (m).
    phi : float
        Effective friction angle (degrees).
    L : float, optional
        Footing length (m). ``None`` -> strip footing (B/L -> 0).
    method : str
        'terzaghi' | 'meyerhof' | 'hansen' | 'vesic'.
    shape, depth : bool
        Whether to apply shape/depth corrections. Terzaghi ignores both.
    inclination : float
        Load inclination from vertical (deg). Default 0.
    gamma_above : float
        Effective unit weight above the footing (kN/m^3). Default = gamma.
    water_table : str
        'deep' (no correction) | 'at_base' (gamma below = gamma') |
        'above_base' (full submergence -- conservative).

    Returns
    -------
    dict with 'q_u' (kPa), 'Nc', 'Nq', 'Ngamma', and the applied factors.

    Reference
    ---------
    Das (2014), Ch. 4.
    """
    check_positive(B, "B")
    check_non_negative(Df, "Df")
    check_non_negative(c, "c")
    check_positive(gamma, "gamma")
    if gamma_above is None:
        gamma_above = gamma

    factors = bearing_factors(phi, method=method)
    Nc, Nq, Ng = factors["Nc"], factors["Nq"], factors["Ngamma"]

    if method == "terzaghi":
        # Apply Terzaghi's shape multipliers for square/circle (Das Eq. 4.6-4.7).
        if L is not None and np.isclose(L, B):
            # Square
            sc_, sg_ = 1.3, 0.8
        elif L is None or L > 10 * B:
            sc_, sg_ = 1.0, 1.0
        else:
            sc_, sg_ = 1.3, 0.8  # treat near-square as square; conservative
        q = gamma_above * Df
        q_u = c * Nc * sc_ + q * Nq + 0.5 * gamma * B * Ng * sg_
        return {
            "q_u": float(q_u), "Nc": Nc, "Nq": Nq, "Ngamma": Ng,
            "method": method,
        }

    # Strip default
    L_eff = L if L is not None else 1e6 * B

    sc = sq = sg = 1.0
    dc = dq = dg = 1.0
    ic = iq = ig = 1.0
    if shape:
        sf = bearing_shape_factors(B, L_eff, phi, method=method)
        sc, sq, sg = sf["sc"], sf["sq"], sf["s_gamma"]
    if depth:
        df_ = bearing_depth_factors(Df, B, phi, method=method)
        dc, dq, dg = df_["dc"], df_["dq"], df_["d_gamma"]
    if inclination > 0:
        i_ = bearing_inclination_factors(inclination, phi, c, method=method)
        ic, iq, ig = i_["ic"], i_["iq"], i_["i_gamma"]

    q = gamma_above * Df
    q_u = (c * Nc * sc * dc * ic
           + q * Nq * sq * dq * iq
           + 0.5 * gamma * B * Ng * sg * dg * ig)
    return {
        "q_u": float(q_u),
        "Nc": Nc, "Nq": Nq, "Ngamma": Ng,
        "sc": sc, "sq": sq, "s_gamma": sg,
        "dc": dc, "dq": dq, "d_gamma": dg,
        "method": method,
    }

bearing_allowable

Python
bearing_allowable(q_u: float, FS: float = 3.0) -> float

Allowable bearing pressure q_all = q_u / FS.

Standard FS=3 for shallow foundations under static loads (Das 2014, p. 224).

Source code in geoeq/design/bearing.py
Python
def bearing_allowable(q_u: float, FS: float = 3.0) -> float:
    """Allowable bearing pressure q_all = q_u / FS.

    Standard FS=3 for shallow foundations under static loads
    (Das 2014, p. 224).
    """
    check_positive(FS, "FS")
    return q_u / FS

bearing_shape_factors

Python
bearing_shape_factors(B: float, L: float, phi: float, method: str = 'meyerhof') -> dict

Shape factors sc, sq, s_gamma for B x L footing.

Reference

Das (2014) Table 4.3.

Source code in geoeq/design/bearing.py
Python
def bearing_shape_factors(B: float, L: float, phi: float,
                          method: str = "meyerhof") -> dict:
    """Shape factors sc, sq, s_gamma for B x L footing.

    Reference
    ---------
    Das (2014) Table 4.3.
    """
    check_positive(B, "B")
    check_positive(L, "L")
    if B > L:
        B, L = L, B  # ensure B is the shorter side
    phi_rad = np.radians(phi)
    method = method.lower()
    Kp = np.tan(np.pi / 4 + phi_rad / 2) ** 2
    if method == "meyerhof":
        sc = 1 + 0.2 * Kp * B / L
        if phi >= 10:
            sq = s_gamma = 1 + 0.1 * Kp * B / L
        else:
            sq = s_gamma = 1.0
    elif method in ("hansen", "vesic"):
        Nq = np.exp(np.pi * np.tan(phi_rad)) * \
            np.tan(np.pi / 4 + phi_rad / 2) ** 2 if phi > 0 else 1.0
        Nc = (Nq - 1) / np.tan(phi_rad) if phi > 0 else 5.14
        sc = 1 + (Nq / Nc) * (B / L)
        sq = 1 + (B / L) * np.tan(phi_rad)
        s_gamma = max(1 - 0.4 * (B / L), 0.6)
    else:
        raise ValueError("method must be 'meyerhof', 'hansen', or 'vesic'.")
    return {"sc": float(sc), "sq": float(sq), "s_gamma": float(s_gamma)}

bearing_depth_factors

Python
bearing_depth_factors(Df: float, B: float, phi: float, method: str = 'meyerhof') -> dict

Depth factors dc, dq, d_gamma.

Reference

Das (2014) Table 4.3.

Source code in geoeq/design/bearing.py
Python
def bearing_depth_factors(Df: float, B: float, phi: float,
                          method: str = "meyerhof") -> dict:
    """Depth factors dc, dq, d_gamma.

    Reference
    ---------
    Das (2014) Table 4.3.
    """
    check_positive(B, "B")
    check_non_negative(Df, "Df")
    phi_rad = np.radians(phi)
    method = method.lower()
    Kp = np.tan(np.pi / 4 + phi_rad / 2) ** 2
    if method == "meyerhof":
        dc = 1 + 0.2 * np.sqrt(Kp) * Df / B
        if phi >= 10:
            dq = d_gamma = 1 + 0.1 * np.sqrt(Kp) * Df / B
        else:
            dq = d_gamma = 1.0
    elif method in ("hansen", "vesic"):
        # k factor (Hansen): Df/B if <= 1; atan(Df/B) [rad] if > 1.
        k = Df / B if Df / B <= 1 else np.arctan(Df / B)
        if phi == 0:
            dc = 1 + 0.4 * k
        else:
            dc = 1 + 0.4 * k
        dq = 1 + 2 * np.tan(phi_rad) * (1 - np.sin(phi_rad)) ** 2 * k
        d_gamma = 1.0
    else:
        raise ValueError("method must be 'meyerhof', 'hansen', or 'vesic'.")
    return {"dc": float(dc), "dq": float(dq), "d_gamma": float(d_gamma)}

bearing_inclination_factors

Python
bearing_inclination_factors(beta: float, phi: float, c: float = 0.0, method: str = 'meyerhof') -> dict

Inclination factors ic, iq, i_gamma for load inclined beta degrees from vertical.

Reference

Das (2014) Table 4.3; Hansen (1970).

Source code in geoeq/design/bearing.py
Python
def bearing_inclination_factors(beta: float, phi: float, c: float = 0.0,
                                method: str = "meyerhof") -> dict:
    """Inclination factors ic, iq, i_gamma for load inclined beta degrees
    from vertical.

    Reference
    ---------
    Das (2014) Table 4.3; Hansen (1970).
    """
    check_non_negative(beta, "beta")
    check_range(beta, "beta", 0, 90)
    method = method.lower()
    if method == "meyerhof":
        ic = iq = (1 - beta / 90) ** 2
        i_gamma = (1 - beta / phi) ** 2 if phi > 0 else 1.0
    else:
        ic = iq = (1 - beta / 90) ** 2
        i_gamma = (1 - beta / phi) ** 2 if phi > 0 else 1.0
    return {"ic": float(ic), "iq": float(iq), "i_gamma": float(i_gamma)}

bearing_capacity_plot

Python
bearing_capacity_plot(c: float, gamma: float, Df: float, phi: float, B_range: Sequence[float] = None, methods: Sequence[str] = None, L_over_B: float = 1.0, ax=None, save_as: str = None)

Plot ultimate bearing capacity vs. footing width B.

Useful design-chart helper: shows how q_u scales with B and how the four classical methods diverge for granular soils.

PARAMETER DESCRIPTION
c

Soil parameters (kPa, kN/m^3, m, degrees).

TYPE: float

gamma

Soil parameters (kPa, kN/m^3, m, degrees).

TYPE: float

Df

Soil parameters (kPa, kN/m^3, m, degrees).

TYPE: float

phi

Soil parameters (kPa, kN/m^3, m, degrees).

TYPE: float

B_range

Footing widths to evaluate (m). Default 0.5 .. 5 m.

TYPE: sequence DEFAULT: None

methods

Subset of {"terzaghi", "meyerhof", "hansen", "vesic"}. Default: all four.

TYPE: sequence of str DEFAULT: None

L_over_B

Footing aspect ratio (1.0 = square). Use 1e6 for strip.

TYPE: float DEFAULT: 1.0

Source code in geoeq/design/plots.py
Python
def bearing_capacity_plot(
    c: float, gamma: float, Df: float, phi: float,
    B_range: Sequence[float] = None, methods: Sequence[str] = None,
    L_over_B: float = 1.0, ax=None, save_as: str = None,
):
    """Plot ultimate bearing capacity vs. footing width B.

    Useful design-chart helper: shows how q_u scales with B and how
    the four classical methods diverge for granular soils.

    Parameters
    ----------
    c, gamma, Df, phi : float
        Soil parameters (kPa, kN/m^3, m, degrees).
    B_range : sequence
        Footing widths to evaluate (m). Default 0.5 .. 5 m.
    methods : sequence of str
        Subset of ``{"terzaghi", "meyerhof", "hansen", "vesic"}``.
        Default: all four.
    L_over_B : float
        Footing aspect ratio (1.0 = square). Use 1e6 for strip.
    """
    import matplotlib.pyplot as plt
    if B_range is None:
        B_range = np.linspace(0.5, 5.0, 30)
    if methods is None:
        methods = ["terzaghi", "meyerhof", "hansen", "vesic"]
    if ax is None:
        fig, ax = plt.subplots(figsize=(7, 5))
    else:
        fig = ax.figure
    colours = {"terzaghi": "#888888", "meyerhof": "#1f3a93",
               "hansen": "#27ae60", "vesic": "#c0392b"}
    for m in methods:
        q = []
        for B in B_range:
            L = B * L_over_B if np.isfinite(L_over_B) else None
            res = bearing_capacity(c, gamma, Df, B, phi, L=L, method=m)
            q.append(res["q_u"])
        ax.plot(B_range, q, label=m.title(),
                color=colours.get(m, "k"), linewidth=1.8)
    ax.set_xlabel("Footing width $B$ (m)")
    ax.set_ylabel("Ultimate bearing capacity $q_u$ (kPa)")
    ax.set_title(
        f"$q_u$ vs $B$ — $c={c}$, $\\phi={phi}°$, $D_f={Df}$ m, $\\gamma={gamma}$ kN/m³")
    ax.grid(alpha=0.3)
    ax.legend()
    if save_as:
        fig.savefig(save_as, dpi=300, bbox_inches="tight")
    return fig