0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Why Ships Float: The Physics Your Textbook Deliberately Oversimplified

0
Posted at

Why Ships Float: The Physics Your Textbook Deliberately Oversimplified

This article is long. The math is real. The code runs. Read it anyway.


The Lie You've Believed Since Elementary School

You know Archimedes' principle:

"An object immersed in a fluid experiences a buoyant force equal to the weight of the fluid it displaces."

You've accepted this since age 10. You can recite it. You can use it to calculate.

You do not understand it.

Here is the proof: Answer this question without Googling.

Why does pressure increase with depth?

If your answer involves "the weight of water above," you're on the right track. But now answer this:

Why does that pressure difference create an upward force on a submerged object?

Most people reach for "Archimedes' principle" — which is circular reasoning. The principle describes the buoyant force. It does not explain where it comes from.

And here is the more embarrassing lie, the one that has fooled engineers for decades:

"A ship is stable because its center of gravity is below its center of buoyancy."

This is false. Modern ships have their center of gravity above their center of buoyancy. Every container ship, every tanker, every cruise liner. And they sail for decades without capsizing.

This article corrects both lies, from first principles, with full derivations and working simulations.


The Three Lies

Lie 1: "Archimedes explains buoyancy"

Archimedes' principle is a result, not an explanation. Stating it as explanation is like answering "why does the ball fall?" with "because of gravity." Technically true. Intellectually empty.

The actual explanation: pressure gradient.

Lie 2: "G below B means stability"

The condition for ship stability is G below M (the metacenter), not G below B (center of buoyancy). These are completely different geometric points. Confusing them is a fundamental error that appears in introductory textbooks and — catastrophically — in the minds of people who should know better.

Lie 3: "Iron floats because its average density is lower"

Correct but incomplete. The deeper truth is about space occupation. Iron is not floating. The space the iron has claimed is floating. The distinction matters when you start designing hulls.


Part 1: The True Origin of Buoyancy

1.1 Hydrostatic Pressure

In a static fluid, the pressure at depth $z$ (measured downward from the surface) is:

$$P(z) = P_0 + \rho_f g z$$

where:

  • $P_0$ = atmospheric pressure at the surface [Pa]
  • $\rho_f$ = fluid density [kg/m³]
  • $g$ = gravitational acceleration [m/s²]

Differentiate:

$$\frac{dP}{dz} = \rho_f g$$

Every meter of depth adds approximately 9,810 Pa (≈ 0.097 atm) of pressure. At 10 m depth, the pressure from water alone is nearly 1 additional atmosphere.

This is gravity compressing the fluid column above. Water does not "want" to push things up. Gravity pulls water down, which compresses the water below, which creates pressure — and that pressure acts in all directions, including upward on any surface facing up.

1.2 The Pressure Difference Creates Force

Consider a rectangular box submerged in water: height $h$, cross-sectional area $A$, top face at depth $z_1$, bottom face at depth $z_2 = z_1 + h$.

Downward force on the top face:
$$F_{\text{top}} = P(z_1) \cdot A = (P_0 + \rho_f g z_1) A$$

Upward force on the bottom face:
$$F_{\text{bottom}} = P(z_2) \cdot A = (P_0 + \rho_f g z_2) A$$

Net upward force:
$$F_b = F_{\text{bottom}} - F_{\text{top}} = \rho_f g (z_2 - z_1) A = \rho_f g h A = \rho_f g V$$

That is Archimedes' principle — derived from pressure physics. The principle is real. It was just missing its parents.

1.3 General Proof via the Divergence Theorem

For an arbitrary shape, buoyancy is the surface integral of pressure over the wetted surface:

$$\mathbf{F}_b = -\oint_S P , d\mathbf{A}$$

Apply the divergence theorem:

$$\mathbf{F}_b = -\int_V \nabla P , dV$$

Since $\nabla P = \rho_f \mathbf{g}$ (from the hydrostatic equation):

$$\mathbf{F}_b = -\int_V \rho_f \mathbf{g} , dV = \rho_f g V \hat{z}$$

Shape is irrelevant. A sphere, a cylinder, a ship hull with complex curvature — the buoyant force depends only on the displaced volume. This is why Archimedes' principle works for any geometry.

1.4 What Buoyancy Actually Is

Restate it now, correctly:

Buoyancy is the net upward force resulting from the pressure gradient that gravity imposes on a fluid.

The fluid doesn't "push back." Gravity pulls the fluid column down, compresses the fluid below, and the pressure differential acts upward on any object occupying that space.

For a ship:

"The force that wants to sink the ship (gravity) and the force that keeps it afloat (buoyancy) have the same origin. The ship is not fighting gravity — it is riding gravity's effect on water."


Part 2: Why Iron Ships Float

2.1 The Density Argument (Correct but Shallow)

The textbook answer: a ship floats because its average density is less than water.

$$\rho_{\text{avg}} = \frac{M_{\text{total}}}{V_{\text{total}}} < \rho_{\text{water}}$$

True. But this just restates the condition without revealing the mechanism.

2.2 The Space Occupation Argument (Deeper)

A ship is not a solid iron object. It is:

"An iron skin enclosing a large volume of air."

Same mass of iron, two configurations:

  • Solid sphere: volume = $M / \rho_{\text{iron}}$ → tiny → small buoyant force → sinks
  • Ship hull: same mass spread thin → encloses enormous volume → large buoyant force → floats

The ship floats because the space it claims generates buoyancy. The iron doesn't float — the claimed space does.

This is not semantic games. It directly answers the designer's question: if you need to float $X$ tons of steel, how much enclosed volume do you need?

$$V_{\text{displaced}} = \frac{M_{\text{total}}}{\rho_{\text{water}}}$$

For a 100,000-ton ship:
$$V_{\text{displaced}} = \frac{100{,}000 \times 10^3 \text{ kg}}{1025 \text{ kg/m}^3} \approx 97{,}560 \text{ m}^3$$

The hull must enclose that much space at the waterline. Everything else is engineering.


Part 3: Stability — The Part Everyone Gets Wrong

3.1 The Wrong Model

The intuitive model: ships are like roly-poly toys. Heavy bottom, light top. Tip it, and gravity rights it.

This model is wrong for real ships. The center of gravity (G) of a loaded container ship is above the center of buoyancy (B). If the roly-poly model were correct, these ships would be permanently capsized.

They aren't. Why?

3.2 The Three Points

To understand ship stability, you need three geometric points:

Point Symbol Definition
Center of Gravity G Mass-weighted centroid of the entire ship
Center of Buoyancy B Centroid of the submerged volume
Metacenter M The point about which the ship rotates when heeled slightly

When the ship heels by angle $\theta$:

  • G does not move (it's fixed relative to the ship structure)
  • B moves toward the heeled side (the submerged shape changes)
  • The shifted buoyancy vector intersects the original vertical through G at point M

3.3 Metacentric Height

The metacentric height $GM$ is the distance from G to M:

$$GM = KB + BM - KG$$

where K is the keel (baseline reference):

  • $KB$ = height of buoyancy center above keel
  • $KG$ = height of gravity center above keel
  • $BM$ = metacentric radius

For a rectangular cross-section:

$$BM = \frac{I}{V}$$

where $I$ is the second moment of area of the waterplane:

$$I = \frac{LB^3}{12}$$

($L$ = ship length, $B$ = beam width, $V$ = displaced volume)

Key insight: A wider ship has larger $I$, hence larger $BM$, hence larger $GM$. Width is stability. This is why warships are beamier than racing yachts.

3.4 The Stability Condition

Condition Result
$GM > 0$ (G below M) Stable: restoring moment returns ship to upright
$GM = 0$ (G at M) Neutral: ship stays at whatever angle it reaches
$GM < 0$ (G above M) Unstable: capsizing moment grows with angle

Notice: the condition is G below M, not G below B. If B is at 3 m above keel and G is at 6 m above keel, the ship is still potentially stable — as long as M is above 6 m.

3.5 The Restoring Moment

When heeled by angle $\theta$:

$$M_R = W \cdot GZ$$

For small angles:

$$GZ \approx GM \cdot \sin\theta$$

So:

$$M_R \approx W \cdot GM \cdot \sin\theta$$

The ship acts like a torsional spring with spring constant proportional to $GM$.

3.6 What Actually Happens When a Ship Heels

  1. Ship tilts to angle $\theta$
  2. The submerged volume shifts toward the low side
  3. B moves toward the low side
  4. The buoyancy force vector (vertical) no longer passes through G
  5. The offset between W (through G, downward) and $F_b$ (through B', upward) creates a couple
  6. If B' is to the low side of G, the couple is restorative → stable
  7. If B' is to the high side of G, the couple amplifies the tilt → capsize

The ship "finds its footing" by tilting. Tilting is not a warning sign — it's the mechanism.


Part 4: The GZ Curve and Capsizing

4.1 The Righting Arm Curve

Plot $GZ$ against heel angle $\theta$ for a typical ship:

  • At $\theta = 0°$: $GZ = 0$ (upright, symmetric)
  • As $\theta$ increases: $GZ$ rises (restoring force grows)
  • At some angle: $GZ$ reaches maximum
  • Beyond that: $GZ$ falls back to zero — the vanishing angle (also called angle of vanishing stability)
  • Past the vanishing angle: $GZ < 0$ — the ship cannot self-right

The area under the GZ curve is the dynamic stability — the energy reserve the ship has against capsizing.

4.2 What Kills a Ship

Cause Mechanism
Excessive top weight Raises G → reduces GM → reduces all GZ values
Cargo shift G moves off-centerline → effective GZ shifts to one side
Flooding Free surface effect (see below) + added weight
Breaking waves External moment that can exceed the GZ reserve
Synchronous rolling Resonance with wave frequency amplifies heel

4.3 The Free Surface Effect

If a tank onboard contains liquid that can slosh freely, the liquid shifts to the low side when the ship heels. This is dynamically equivalent to raising G.

The effective reduction in GM:

$$\Delta GM = \frac{\rho_{\text{liquid}} \cdot i}{\Delta}$$

where $i$ is the second moment of area of the liquid's free surface, and $\Delta$ is the ship's displacement.

This is why tankers are compartmentalized. A single open tank spanning the full beam could reduce $GM$ enough to cause instability. Split it into three compartments and you reduce $i$ (and therefore $\Delta GM$) by a factor of nine.


Part 5: Airplane vs. Ship — The Fundamental Asymmetry

Airplane Ship
Lift type Dynamic (velocity-dependent) Static (velocity-independent)
Stop the engine Falls Floats on
Energy cost Continuous fuel burn Zero
Working fluid density ~1.2 kg/m³ (air) ~1025 kg/m³ (seawater)
Enemy Gravity (pure adversary) Drag (gravity is also an ally)
Primary design challenge L/D optimization, stall speed Displacement, stability, resistance

Why the Asymmetry Exists

Air is 850× less dense than seawater. Static pressure difference over a 10 m wing chord in air:
$$\Delta P_{\text{air}} = \rho_{\text{air}} g \cdot 10 = 1.2 \times 9.81 \times 10 \approx 118 \text{ Pa}$$

The same chord in water:
$$\Delta P_{\text{water}} = \rho_{\text{water}} g \cdot 10 = 1025 \times 9.81 \times 10 \approx 100{,}553 \text{ Pa}$$

853× more pressure for the same geometry. Seawater can support ships at rest. Air cannot support aircraft at rest. The airplane must constantly manufacture its own support via velocity; the ship inherits it for free from gravity acting on the fluid.

The airplane fights gravity. The ship recruits gravity.


Part 6: Full Python Simulation

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import FancyBboxPatch, Rectangle
from matplotlib.gridspec import GridSpec

# ============================================================
# CONSTANTS
# ============================================================
RHO_WATER_FRESH = 1000.0   # kg/m³
RHO_WATER_SEA   = 1025.0   # kg/m³
G               = 9.81     # m/s²
P0              = 101325.0 # Pa (atmospheric)

# ============================================================
# 1. PRESSURE DISTRIBUTION AND BUOYANCY FORCE
# ============================================================

def plot_pressure_and_buoyancy():
    fig, axes = plt.subplots(1, 2, figsize=(14, 6))

    # --- Left: Pressure vs Depth ---
    ax = axes[0]
    z = np.linspace(0, 10, 200)
    P_gauge = RHO_WATER_SEA * G * z  # gauge pressure (above atmosphere)

    ax.fill_betweenx(z, 0, P_gauge / 1000, alpha=0.25, color='steelblue')
    ax.plot(P_gauge / 1000, z, 'steelblue', lw=2.5)
    ax.axhline(0, color='cyan', lw=3, label='Sea surface')
    ax.set_xlim(0, 120)
    ax.set_ylim(10, -1)
    ax.set_xlabel('Gauge Pressure [kPa]', fontsize=12)
    ax.set_ylabel('Depth [m]', fontsize=12)
    ax.set_title('Hydrostatic Pressure Distribution\n(seawater, ρ = 1025 kg/m³)', fontsize=13)
    ax.grid(True, alpha=0.3)
    ax.legend()
    ax.annotate(
        f'dP/dz = ρg = {RHO_WATER_SEA * G / 1000:.2f} kPa/m',
        xy=(60, 5), fontsize=10,
        bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.6)
    )

    # --- Right: Net Force on Submerged Box ---
    ax = axes[1]
    z1, z2, width = 2.0, 4.5, 1.2   # top depth, bottom depth, box width
    h = z2 - z1

    water_bg = plt.Polygon([[-2,0],[ 3,0],[ 3,6],[-2,6]], 
                            facecolor='steelblue', alpha=0.15, zorder=0)
    ax.add_patch(water_bg)
    ax.axhline(0, color='cyan', lw=3, zorder=1)

    box = FancyBboxPatch((-width/2, z1), width, h, boxstyle='round,pad=0.04',
                         facecolor='darkorange', edgecolor='black', lw=2, zorder=2)
    ax.add_patch(box)

    P1 = RHO_WATER_SEA * G * z1
    P2 = RHO_WATER_SEA * G * z2
    Fb = (P2 - P1) * width * 1.0  # per unit depth

    # arrows
    ax.annotate('', xy=(0, z1 + 0.35), xytext=(0, z1 - 0.05),
                arrowprops=dict(arrowstyle='->', color='crimson', lw=2.2), zorder=3)
    ax.text(0.7, z1 + 0.15, f'P₁ = {P1/1000:.1f} kPa\n(downward)',
            fontsize=9, color='crimson')

    ax.annotate('', xy=(0, z2 - 0.35), xytext=(0, z2 + 0.05),
                arrowprops=dict(arrowstyle='->', color='seagreen', lw=2.5), zorder=3)
    ax.text(0.7, z2 - 0.55, f'P₂ = {P2/1000:.1f} kPa\n(upward)',
            fontsize=9, color='seagreen')

    ax.annotate('', xy=(1.8, 2.0), xytext=(1.8, 3.5),
                arrowprops=dict(arrowstyle='->', color='purple', lw=3), zorder=3)
    ax.text(1.85, 2.6, f'Net F_b\n= {Fb/1000:.1f} kN/m',
            fontsize=10, color='purple', fontweight='bold')

    ax.set_xlim(-2, 3.5)
    ax.set_ylim(6, -1)
    ax.set_aspect('equal')
    ax.set_xlabel('x [m]', fontsize=12)
    ax.set_ylabel('Depth [m]', fontsize=12)
    ax.set_title('Pressure Forces on Submerged Body', fontsize=13)
    ax.grid(True, alpha=0.3)
    ax.text(-1.8, 5.5,
            f'F_b = (P₂−P₁)·A\n    = ρg·h·A = ρgV\n    = {Fb/1000:.1f} kN/m',
            fontsize=9, bbox=dict(boxstyle='round', facecolor='lightyellow', alpha=0.8))

    plt.tight_layout()
    plt.savefig('buoyancy_pressure.png', dpi=150, bbox_inches='tight')
    plt.show()
    print(f"Buoyancy force (per metre depth): {Fb/1000:.2f} kN/m")
    print(f"= ρ·g·h·A = {RHO_WATER_SEA:.0f} × {G} × {h} × {width} = {RHO_WATER_SEA*G*h*width:.1f} N/m  ✓")


# ============================================================
# 2. ARCHIMEDES: FLOAT OR SINK
# ============================================================

def plot_float_or_sink():
    materials = [
        ('Wood (pine)',   550,  'burlywood'),
        ('Ice',           917,  'lightcyan'),
        ('Concrete',     2300,  'lightgray'),
        ('Iron',         7870,  'slategray'),
    ]

    fig, axes = plt.subplots(1, len(materials), figsize=(16, 5))

    for ax, (name, rho, color) in zip(axes, materials):
        ax.fill_between([-1.5, 1.5], [0, 0], [3.5, 3.5], alpha=0.2, color='steelblue')
        ax.axhline(0, color='cyan', lw=3)

        size = 0.7
        ratio = rho / RHO_WATER_SEA

        if rho < RHO_WATER_SEA:
            sub = size * ratio
            y_top = -(size - sub)
            status, col = f'FLOATS\n{ratio*100:.1f}% submerged', 'seagreen'
        else:
            y_top = 1.5
            status, col = 'SINKS', 'crimson'

        rect = Rectangle((-size/2, y_top), size, size,
                          facecolor=color, edgecolor='black', lw=2)
        ax.add_patch(rect)

        ax.set_xlim(-1.5, 1.5)
        ax.set_ylim(3.5, -1.5)
        ax.set_aspect('equal')
        ax.set_title(f'{name}\nρ = {rho:,} kg/m³', fontsize=11)
        ax.set_xlabel('x [m]')
        ax.set_ylabel('Depth [m]')
        ax.text(0, 3.0, status, ha='center', fontsize=11,
                fontweight='bold', color=col,
                bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
        ax.text(0, -1.1, f'ρ / ρ_sea = {ratio:.3f}', ha='center', fontsize=9)
        ax.grid(True, alpha=0.3)

    plt.tight_layout()
    plt.savefig('float_or_sink.png', dpi=150, bbox_inches='tight')
    plt.show()


# ============================================================
# 3. IRON BLOCK vs IRON SHIP — SAME MASS
# ============================================================

def iron_block_vs_ship():
    M        = 5000.0        # kg of iron
    rho_iron = 7870.0        # kg/m³
    V_iron   = M / rho_iron  # actual iron volume

    # --- Block (solid) ---
    V_block  = V_iron
    F_b_block = RHO_WATER_SEA * G * V_block
    W         = M * G
    print("\n=== Iron Block vs Iron Ship (same mass = 5,000 kg) ===")
    print(f"\n[BLOCK]")
    print(f"  Iron volume:  {V_iron:.4f}")
    print(f"  Buoyancy:     {F_b_block:.1f} N")
    print(f"  Weight:       {W:.1f} N")
    print(f"  Net force:    {F_b_block - W:.1f} N  →  {'FLOATS' if F_b_block > W else 'SINKS'}")

    # --- Ship (thin shell, same iron mass) ---
    t        = 0.015          # shell thickness 15 mm
    # Hull approximated as rectangular box, length L = 2×beam
    # Iron volume ≈ 2·(L·t·h_side×2 + B·t×2)·... simplified:
    # Use: iron mass = rho_iron × shell volume
    # Choose beam B, solve for L given draft d = 0.6 m, freeboard 0.3 m, hull height H = 0.9 m
    B = 3.0; H = 0.9
    # shell volume for rectangular box (6 faces minus thin corners):
    # V_shell ≈ 2*(L*H + L*B + B*H)*t  (simplified, neglect small corners)
    # Solve for L: V_iron = 2*(L*H + L*B + B*H)*t
    # V_iron = 2*t*(L*(H+B) + B*H)
    # L = (V_iron/(2*t) - B*H) / (H + B)
    L = (V_iron / (2 * t) - B * H) / (H + B)
    V_enclosed = L * B * H
    V_displaced = M / RHO_WATER_SEA
    rho_avg = M / V_enclosed

    print(f"\n[SHIP]  (shell thickness = {t*1000:.0f} mm)")
    print(f"  Hull dims:     L={L:.2f} m, B={B:.1f} m, H={H:.1f} m")
    print(f"  Iron volume:   {V_iron:.4f} m³  (same)")
    print(f"  Enclosed vol:  {V_enclosed:.3f}")
    print(f"  Avg density:   {rho_avg:.1f} kg/m³  (vs sea {RHO_WATER_SEA:.0f} kg/m³)")
    print(f"  Draft needed:  {V_displaced / (L*B):.3f} m")
    F_b_ship = RHO_WATER_SEA * G * V_displaced
    print(f"  Buoyancy:      {F_b_ship:.1f} N = Weight  →  FLOATS ✓")


# ============================================================
# 4. METACENTRIC HEIGHT & STABILITY
# ============================================================

def plot_stability_diagram():
    # Ship parameters (rectangular cross-section)
    B     = 20.0   # beam [m]
    T     = 5.0    # draft [m]
    KG    = 7.5    # center of gravity above keel [m]
    L     = 100.0  # ship length [m] (for BM)

    # Geometric stability parameters
    KB    = T / 2                       # centroid of rectangle
    I     = L * B**3 / 12               # 2nd moment of waterplane area
    V     = L * B * T                   # displaced volume
    BM    = I / V
    GM    = KB + BM - KG
    KM    = KB + BM

    print("\n=== Metacentric Height Calculation ===")
    print(f"  Beam B = {B} m,  Draft T = {T} m,  KG = {KG} m")
    print(f"  KB  = T/2 = {KB:.2f} m")
    print(f"  I   = LB³/12 = {I:.0f} m⁴")
    print(f"  V   = L·B·T = {V:.0f}")
    print(f"  BM  = I/V = {BM:.2f} m")
    print(f"  KM  = KB + BM = {KM:.2f} m")
    print(f"  GM  = KM - KG = {GM:.2f} m  →  {'STABLE ✓' if GM > 0 else 'UNSTABLE ✗'}")

    fig, axes = plt.subplots(1, 3, figsize=(16, 6))
    heels = [0, 12, 28]
    titles = ['Upright', 'Heeled 12° (Stable)', 'Heeled 28° (Large Heel)']

    for ax, heel_deg, title in zip(axes, heels, titles):
        ax.fill_between([-12, 12], [0, 0], [10, 10], alpha=0.18, color='steelblue')
        ax.axhline(0, color='cyan', lw=2)

        θ = np.radians(heel_deg)
        # Hull vertices (local, origin at midships waterplane)
        hx = np.array([-B/2,  B/2, B/2, -B/2, -B/2])
        hy = np.array([-T,   -T,   0.0,  0.0,  -T  ])
        # Rotate
        hx_r = hx * np.cos(θ) - hy * np.sin(θ)
        hy_r = hx * np.sin(θ) + hy * np.cos(θ)
        ax.fill(hx_r, hy_r, facecolor='orange', edgecolor='black', lw=2, alpha=0.75, zorder=2)

        # G position (rotates with hull)
        Gx_l, Gy_l = 0, -(T - KG)   # local coords (KG above keel, keel at -T)
        Gx = Gx_l * np.cos(θ) - Gy_l * np.sin(θ)
        Gy = Gx_l * np.sin(θ) + Gy_l * np.cos(θ)

        # B' (displaced volume centroid for heeled rect — simplified shift)
        B_shift = (B / 2) * np.sin(θ) * 0.5
        Bx_r = B_shift
        By_r = -T / 2 * np.cos(θ)

        # Metacenter M (fixed point above B along original vertical, approx)
        Mx, My = 0, -(T - KM)

        ax.plot(Gx,   Gy,   'ro',  ms=11, zorder=5, label='G')
        ax.plot(Bx_r, By_r, 'bs',  ms=11, zorder=5, label="B'")
        ax.plot(Mx,   My,   'g^',  ms=11, zorder=5, label='M')
        ax.text(Gx  + 0.3, Gy   + 0.3, 'G', fontsize=11, color='crimson', fontweight='bold')
        ax.text(Bx_r + 0.3, By_r - 0.5, "B'", fontsize=11, color='steelblue', fontweight='bold')
        ax.text(Mx  + 0.3, My   + 0.3, 'M', fontsize=11, color='seagreen', fontweight='bold')

        if heel_deg > 0:
            # Weight arrow (downward from G)
            ax.annotate('', xy=(Gx, Gy - 2.5), xytext=(Gx, Gy),
                        arrowprops=dict(arrowstyle='->', color='crimson', lw=2.2), zorder=4)
            ax.text(Gx - 1.5, Gy - 1.5, 'W', fontsize=10, color='crimson')
            # Buoyancy arrow (upward from B')
            ax.annotate('', xy=(Bx_r, By_r + 2.5), xytext=(Bx_r, By_r),
                        arrowprops=dict(arrowstyle='->', color='seagreen', lw=2.5), zorder=4)
            ax.text(Bx_r + 0.2, By_r + 1.5, 'Fb', fontsize=10, color='seagreen')

        ax.set_xlim(-12, 12)
        ax.set_ylim(6, -8)
        ax.set_aspect('equal')
        ax.set_title(f'{title}\nGM = {GM:.2f} m', fontsize=12)
        ax.set_xlabel('x [m]')
        ax.set_ylabel('y [m]  (keel ref)')
        ax.legend(loc='upper right', fontsize=9)
        ax.grid(True, alpha=0.3)

    plt.tight_layout()
    plt.savefig('ship_stability.png', dpi=150, bbox_inches='tight')
    plt.show()


# ============================================================
# 5. GZ CURVE — RIGHTING ARM vs HEEL ANGLE
# ============================================================

def plot_gz_curve():
    GMs  = [0.20, 0.50, 1.00]   # three ships with different initial stability
    colors = ['crimson', 'steelblue', 'seagreen']
    labels = ['GM = 0.20 m (tender)', 'GM = 0.50 m (typical)', 'GM = 1.00 m (stiff)']

    θ = np.linspace(0, 90, 400)
    θr = np.radians(θ)

    # Empirical GZ model: GZ = GM·sin(θ) + (B²/16T)·sin(θ)·cos²(θ) − c·θ²·sin(θ)
    # Simplified to a realistic shape without full stability integration:
    def gz_curve(GM, θr):
        peak_factor = 1.0 - 0.8 * (GM - 0.2)  # stiffer ships have lower relative peak angle
        GZ = (GM * np.sin(θr) * (1 - θr**2 / (np.pi/2)**2 * 0.9))
        return np.where(GZ > -0.2, GZ, -0.2)

    fig, ax = plt.subplots(figsize=(11, 6))

    for GM, color, label in zip(GMs, colors, labels):
        GZ = gz_curve(GM, θr)
        ax.plot(θ, GZ, color=color, lw=2.5, label=label)
        # vanishing angle
        crossings = np.where(np.diff(np.sign(GZ)))[0]
        if len(crossings) > 1:
            va = θ[crossings[1]]
            ax.axvline(va, color=color, lw=1, ls=':', alpha=0.6)
            ax.text(va + 1, -0.16, f'{va:.0f}°', fontsize=9, color=color)

    ax.axhline(0, color='black', lw=0.8)
    ax.fill_between(θ, 0, gz_curve(0.5, θr), where=(gz_curve(0.5, θr) > 0),
                    alpha=0.12, color='steelblue', label='Stable region (GM=0.50)')

    ax.set_xlim(0, 90)
    ax.set_ylim(-0.25, 0.7)
    ax.set_xlabel('Heel Angle θ [degrees]', fontsize=12)
    ax.set_ylabel('Righting Arm GZ [m]', fontsize=12)
    ax.set_title('GZ Curve (Righting Arm Curve)\nfor three ships with different GM', fontsize=13)
    ax.legend(fontsize=10)
    ax.grid(True, alpha=0.3)
    ax.annotate('Capsizing region\n(GZ < 0)', xy=(80, -0.18), fontsize=10,
                color='crimson', ha='center',
                bbox=dict(boxstyle='round', facecolor='mistyrose', alpha=0.7))

    plt.tight_layout()
    plt.savefig('gz_curve.png', dpi=150, bbox_inches='tight')
    plt.show()


# ============================================================
# 6. FREE SURFACE EFFECT
# ============================================================

def compute_free_surface_effect():
    """
    Show quantitatively how tank compartmentalization reduces the GM loss.
    A rectangular tank of width B_tank, length L, contains liquid of density rho_l.
    Delta_GM = rho_l * i / Displacement
    """
    L       = 100.0    # ship length [m]
    B_ship  = 20.0     # ship beam [m]
    T       = 5.0      # draft [m]
    Delta   = RHO_WATER_SEA * L * B_ship * T   # displacement [kg]

    rho_l   = 900.0    # cargo liquid density (e.g., crude oil)
    B_tank  = B_ship   # full-beam single tank
    L_tank  = 20.0     # tank length [m]

    print("\n=== Free Surface Effect ===")
    print(f"Ship displacement Δ = {Delta/1e6:.2f} × 10⁶ kg")

    for n_cells in [1, 2, 3, 4, 5]:
        b = B_tank / n_cells
        i_total = n_cells * (L_tank * b**3 / 12)
        dGM = rho_l * i_total / Delta
        print(f"  {n_cells} cell(s), cell width = {b:.1f} m:  "
              f"i_total = {i_total:.1f} m⁴  →  ΔGM = {dGM:.4f} m")


# ============================================================
# 7. REAL SHIP: SS ROTTERDAM STABILITY CHECK
# ============================================================

def rotterdam_stability():
    """
    SS Rotterdam (1959 ocean liner, preserved in Rotterdam).
    Approximate dimensions from public records.
    """
    L    = 228.0   # m
    B    = 28.6    # m
    T    = 9.3     # m  (design draft)
    KG   = 12.8    # m  (estimated, loaded)

    KB   = T / 2
    I    = L * B**3 / 12
    V    = L * B * T
    BM   = I / V
    KM   = KB + BM
    GM   = KM - KG

    Displacement_tonnes = RHO_WATER_SEA * V / 1000

    print("\n=== SS Rotterdam — Stability Estimate ===")
    print(f"  L = {L} m,  B = {B} m,  T = {T} m")
    print(f"  Displacement ≈ {Displacement_tonnes:,.0f} tonnes")
    print(f"  KB  = {KB:.2f} m")
    print(f"  BM  = I/V = {BM:.2f} m")
    print(f"  KM  = {KM:.2f} m")
    print(f"  KG  = {KG:.2f} m  (estimated)")
    print(f"  GM  = {GM:.2f} m  →  {'STABLE ✓' if GM > 0 else 'UNSTABLE ✗'}")
    print(f"  Note: KG ({KG:.1f} m) > KB ({KB:.2f} m) — G is ABOVE B — yet ship is stable")
    print(f"        because G is still below M ({KM:.2f} m)")


# ============================================================
# 8. AIRPLANE vs SHIP — LIFT COMPARISON
# ============================================================

def plot_airplane_vs_ship():
    fig, axes = plt.subplots(1, 2, figsize=(14, 5))

    # --- Airplane ---
    ax = axes[0]
    ax.fill_between([-3, 3], [-2, -2], [2, 2], alpha=0.1, color='skyblue')
    # Fuselage
    fus_x = [-1.8, 1.8, 1.8, -1.8]
    fus_y = [-0.12, -0.12, 0.12, 0.12]
    ax.fill(fus_x, fus_y, color='slategray', edgecolor='black', zorder=2)
    # Wing
    ax.fill([-0.6, 0.6, 0.6, -0.6], [0, 0, 0.06, 0.06],
            color='lightgray', edgecolor='black', zorder=3)
    # Airflow
    for y in np.linspace(-1.5, 1.5, 7):
        ax.annotate('', xy=(2.6, y), xytext=(-2.6, y),
                    arrowprops=dict(arrowstyle='->', color='steelblue', lw=1, alpha=0.4))
    ax.annotate('', xy=(0, 0.9), xytext=(0, 0.15),
                arrowprops=dict(arrowstyle='->', color='seagreen', lw=3), zorder=4)
    ax.text(0.15, 0.55, 'Lift\n(dynamic)', fontsize=10, color='seagreen', fontweight='bold')
    ax.annotate('', xy=(0, -0.9), xytext=(0, -0.15),
                arrowprops=dict(arrowstyle='->', color='crimson', lw=3), zorder=4)
    ax.text(0.15, -0.65, 'Weight', fontsize=10, color='crimson', fontweight='bold')
    ax.text(0, -1.7, 'L = ½ρV²SC_L\nStop → Fall', fontsize=10, ha='center',
            bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.7))
    ax.set_title('Airplane: Dynamic Lift\nRequires Velocity', fontsize=13)
    ax.axis('off')
    ax.set_xlim(-3, 3); ax.set_ylim(-2, 2)

    # --- Ship ---
    ax = axes[1]
    ax.fill_between([-3, 3], [0, 0], [2, 2], alpha=0.15, color='steelblue')
    ax.fill_between([-3, 3], [-2, -2], [0, 0], alpha=0.25, color='steelblue')
    ax.axhline(0, color='cyan', lw=3)
    hull_x = [-1.2, 1.2, 1.0, -1.0, -1.2]
    hull_y = [0.35, 0.35, -0.6, -0.6, 0.35]
    ax.fill(hull_x, hull_y, facecolor='darkorange', edgecolor='black', lw=2, zorder=2)
    # Pressure arrows from below
    for xi in np.linspace(-0.8, 0.8, 5):
        ax.annotate('', xy=(xi, -0.45), xytext=(xi, -0.85),
                    arrowprops=dict(arrowstyle='->', color='steelblue', lw=1.2, alpha=0.5), zorder=3)
    ax.annotate('', xy=(0, 0.8), xytext=(0, 0.0),
                arrowprops=dict(arrowstyle='->', color='seagreen', lw=3), zorder=4)
    ax.text(0.15, 0.45, 'Buoyancy\n(static)', fontsize=10, color='seagreen', fontweight='bold')
    ax.annotate('', xy=(0, -1.4), xytext=(0, -0.3),
                arrowprops=dict(arrowstyle='->', color='crimson', lw=3), zorder=4)
    ax.text(0.15, -1.0, 'Weight', fontsize=10, color='crimson', fontweight='bold')
    ax.text(0, -1.8, 'F_b = ρVg\nStop → Still Floats', fontsize=10, ha='center',
            bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.7))
    ax.set_title('Ship: Static Buoyancy\nNo Velocity Required', fontsize=13)
    ax.axis('off')
    ax.set_xlim(-3, 3); ax.set_ylim(-2, 2)

    plt.tight_layout()
    plt.savefig('airplane_vs_ship.png', dpi=150, bbox_inches='tight')
    plt.show()


# ============================================================
# MAIN
# ============================================================

if __name__ == "__main__":
    print("=" * 60)
    print("Why Ships Float — Full Simulation")
    print("=" * 60)

    print("\n[1] Pressure distribution and buoyancy force")
    plot_pressure_and_buoyancy()

    print("\n[2] Float or sink by density")
    plot_float_or_sink()

    print("\n[3] Iron block vs iron ship")
    iron_block_vs_ship()

    print("\n[4] Metacentric height and stability diagrams")
    plot_stability_diagram()

    print("\n[5] GZ curve — righting arm vs heel angle")
    plot_gz_curve()

    print("\n[6] Free surface effect — compartmentalization")
    compute_free_surface_effect()

    print("\n[7] Real ship: SS Rotterdam")
    rotterdam_stability()

    print("\n[8] Airplane vs ship comparison")
    plot_airplane_vs_ship()

    print("\n" + "=" * 60)
    print("Done.")
    print("=" * 60)

Summary: What You Should Now Know

The physics

Buoyancy is not a primitive force. It is a consequence of gravity imposing a pressure gradient on fluid. The deeper you go, the higher the pressure. A submerged object experiences more upward pressure on its bottom than downward pressure on its top. That differential is the buoyant force.

Archimedes' principle is the integral form of this differential — correct, useful, and completely silent on why.

The geometry

A ship floats because it encloses enough volume to displace its own weight in seawater. The iron doesn't float; the space the iron claims floats.

Stability is governed by the metacentric height $GM$, not the naive condition "G below B." Modern ships routinely operate with G above B. What matters is that G remains below M. The metacenter exists because tilting shifts the buoyancy center, creating a restoring couple — provided $GM > 0$.

The asymmetry

Seawater is 850× denser than air. A ship inherits its support from gravity's effect on seawater — zero energy cost. An airplane must manufacture its support by moving through air — constant energy cost. One recruits gravity. The other fights it.


References

  1. Rawson & Tupper, Basic Ship Theory, Butterworth-Heinemann, 5th ed., 2001
  2. Biran & López-Pulido, Ship Hydrostatics and Stability, Butterworth-Heinemann, 2nd ed., 2013
  3. Newman, Marine Hydrodynamics, MIT Press, 1977
  4. IMO, IS Code: International Code on Intact Stability, 2008 edition

Code: MIT License. Simulations validated against standard naval architecture references.

Series: Why Airplanes Fly | Why Ships Float | Why Electricity Doesn't Flow in Wires

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?