Skip to content
cfd-lab:~/en/posts/2026-08-13-metacentric-h…● online

Translated from the author's Korean original. Read the original (한국어)

NOTE #129DAY THU 유체역학DATE 2026.08.13UPDATED 2026.10.19READ 8 min read#Metacenter#Buoyancy#Free-Surface#Fluid-Mechanics#FSI

The Weight Sits on Top and She Still Rights Herself — Metacentric Height and the Free Surface Effect

GM is the margin the waterplane inertia buys you. A single free surface in a tank can take the whole margin away.

The centre of gravity of a container ship sits several metres above the centroid of her submerged volume. The heavy end is on top, and she still stands up by herself. Meanwhile a barge whose centre of gravity is below the buoyancy centre can roll over. Up versus down settles nothing. The quantity that decides the case is the metacentric height GMGM, and the note runs through to the tank liquid that can erase it.

The centre of gravity is above the centre of buoyancy#

Symbols first. KK is the keel, GG is the centre of gravity, BB is the centre of buoyancy — the centroid of the submerged volume. Archimedes only fixed the magnitude: buoyancy equals the weight of the displaced water, and its line of action passes through the centroid of that displaced volume.

Upright, GG and BB share the same vertical line. Weight and buoyancy are equal and opposite, so the moment is zero. In that state it makes no difference whether GG is high or low. Stability is not defined by the state at rest. It is defined by what appears when you tip the body slightly.

Heel her, and things change. BB is the centroid of the submerged shape, so when the shape changes, BB moves with it. GG rotates with the ship but stays put inside it. The two verticals separate and a lever arm appears. That arm is the righting arm GZGZ, and the righting moment is M=Δ⋅GZM = \Delta \cdot GZ for a displacement Δ\Delta.

Heel her and the submerged shape changes#

Tip her yourself in the simulation below. Change the beam, the draft and the height of the centre of gravity, then press release at 15 deg and watch whether she comes back.

phi = 15.0 deg
Raise KG past 7.33 m and the barge no longer comes back — it rolls out to an angle of loll and stays there. Hold the heel and drag it past 27 deg: the deck edge goes under, the waterplane stops widening, and the green GZ curve peels away from the dashed straight line GM sin phi. That gap is why a single number cannot certify a hull.

BB (green) is dragged toward the low side, and the gap that opens against the vertical through GG (yellow) is GZGZ. The purple MM is where the buoyancy line crosses the ship's centreline. Push the KG slider above 7.33 m and MM drops below GG: instead of returning, she lies over at a fixed angle and stays there.

BM=I/∇BM = I/\nabla — the waterplane inertia is the whole story#

At a small heel ϕ\phi the change in the submerged shape reduces to two wedges: one that emerges on the high side and one that immerses on the low side. Their volumes are equal, because the displacement cannot change, and they are separated horizontally. That transfer of volume is what moves BB.

Take a strip dAdA of the waterplane (the section the free surface cuts through the hull) at a distance xx from the centreline. It contributes a volume change xϕ dAx\phi\,dA and a moment x2ϕ dAx^2\phi\,dA. Summing over the waterplane gives a second moment of area.

BB′‾=ϕ∫Ax2 dA∇=I ϕ∇,BM=I∇\overline{BB'} = \frac{\phi \int_A x^{2}\,dA}{\nabla} = \frac{I\,\phi}{\nabla}, \qquad BM = \frac{I}{\nabla}

II is the second moment of the waterplane about the centreline and ∇\nabla is the displaced volume. For a rectangular section of beam bb, per unit length I=b3/12I = b^3/12 and ∇=b T\nabla = b\,T, so

BM=b212 TBM = \frac{b^{2}}{12\,T}

The beam enters cubed, then the ∇\nabla in the denominator cancels one power and leaves a square. Widen the hull by 20% and BMBM grows by 44%. That is why a canoe is twitchy and a raft is not. The verdict itself is one line.

GM=KB+BM−KGGM = KB + BM - KG

KBKB is the height of the buoyancy centre and KGKG the height of the centre of gravity. GM>0GM > 0 means she rights herself, GM<0GM < 0 means she goes over. For a barge with beam b=16b = 16 m and draft T=4T = 4 m, KB=2.0KB = 2.0 m and BM=5.333BM = 5.333 m, so KM=7.333KM = 7.333 m. If KGKG is 6.6 m, the whole margin is 0.733 m. Per unit length, dividing b3/12b^3/12 by ∇=bT=64\nabla = b T = 64 returns that same 5.333 m. Widen the beam 20% and b2b^2 becomes 1.44 times itself; the stated 44% is that square. Then 0.44×5.3330.44 \times 5.333 m is added straight onto GMGM. Hold KG=6.6KG = 6.6 m and the 0.733 m margin grows by exactly that addition. A thin side shell can eat the margin before the waterplane does. If shear locking kills the deflection, the stiffness matrix is the thing to doubt before reading GZGZ.

The angle where GMsin⁡ϕGM \sin\phi betrays you#

GMGM is a slope at ϕ→0\phi \to 0. At small angles GZ≈GMsin⁡ϕGZ \approx GM \sin\phi holds well. The interesting question is when it stops holding.

Geometry sets that point, because II stays constant only while the waterplane stays the same. The barge above has 4 m of freeboard, so the deck edge goes under at tan⁡ϕ=2×4/16\tan\phi = 2 \times 4/16, that is 26.6 degrees. From there the waterplane stops widening and starts to shrink. BMBM collapses and the GZGZ curve rolls over.

Press hold heel in the simulation and drag past 30 degrees: the solid green curve (the exact value) peels away from the dashed purple line (GMsin⁡ϕGM\sin\phi). The heel where the two disagree in sign is the angle of vanishing stability.

Liquid in a tank takes the whole of GM#

Now put a tank of liquid inside the hull. Ballast tanks, fuel tanks, cargo tanks, a car deck flooded with firefighting water — all the same problem.

The weight of the liquid does not change when she heels. It only moves. And that movement drags GG toward the low side. The bookkeeping is identical to BMBM: the free surface inside the tank is a waterplane too, and heeling swaps two wedges across it.

δGM=ρf iρ ∇\delta GM = \frac{\rho_f\, i}{\rho\, \nabla}

ii is the second moment of the tank's free surface, ρf\rho_f the density of the liquid inside and ρ\rho that of the water outside. The loss is not subtracted from the hull; it is treated as a virtual rise of GG by that amount. How much liquid the tank holds does not appear in the formula. Unless the tank is pressed full so there is no free surface at all, or completely empty, 20 cm of liquid costs exactly what 2 m of it costs.

The loss falls as 1/n21/n^2 because of the count in the next expression.

One bulkhead cuts the loss to a quarter#

Divide a tank of width btb_t into nn compartments and each is bt/nb_t/n wide, with nn of them.

i=n⋅(bt/n)312=bt312 n2i = n \cdot \frac{(b_t/n)^{3}}{12} = \frac{b_t^{3}}{12\,n^{2}}

The loss falls as 1/n21/n^2, not 1/n1/n: the cube on the width beats the single power on the count. One 12 m tank in the barge above gives i=144i = 144 m⁴ against ∇=64\nabla = 64 m³/m, so δGM=2.195\delta GM = 2.195 m. The 0.733 m of GMGM was gone long before that. A single centreline bulkhead brings the loss down to 0.549 m and she stands again.

That is what the longitudinal bulkhead down the middle of a tanker is for. The cargo does not change and neither does the height of the centre of gravity. Only the width of each free surface is halved.

The GZ curve and the vanishing angle, in Python#

GMGM is a single tangent slope. At large angles it is quicker to cut the submerged polygon directly and take its centroid. Represent the hull section as a polygon, bisect for the waterline height that makes the submerged area equal the displaced area, and compute the centroid of what is left.

import math
 
def poly_area_centroid(poly):
    a = cx = cy = 0.0
    n = len(poly)
    for i in range(n):
        x0, y0 = poly[i]
        x1, y1 = poly[(i + 1) % n]
        cr = x0 * y1 - x1 * y0
        a += cr
        cx += (x0 + x1) * cr
        cy += (y0 + y1) * cr
    a *= 0.5
    if abs(a) < 1e-14:
        return 0.0, 0.0, 0.0
    return a, cx / (6 * a), cy / (6 * a)
 
def clip_below(poly, phi, h):
    """keep the part of the polygon whose earth-frame height is below h"""
    def f(p):
        return p[1] * math.cos(phi) - p[0] * math.sin(phi) - h
    out = []
    n = len(poly)
    for i in range(n):
        p, q = poly[i], poly[(i + 1) % n]
        fp, fq = f(p), f(q)
        if fp <= 0.0:
            out.append(p)
        if (fp < 0.0) != (fq < 0.0):
            t = fp / (fp - fq)
            out.append((p[0] + t * (q[0] - p[0]), p[1] + t * (q[1] - p[1])))
    return out
 
def waterline_offset(poly, phi, area0):
    """bisect for the waterline height that submerges exactly area0"""
    lo, hi = -60.0, 60.0
    for _ in range(80):
        mid = 0.5 * (lo + hi)
        a, _, _ = poly_area_centroid(clip_below(poly, phi, mid))
        if a < area0:
            lo = mid
        else:
            hi = mid
    return 0.5 * (lo + hi)
 
def righting_arm(beam, depth, draft, kg, phi):
    hull = [(-beam / 2, 0.0), (beam / 2, 0.0), (beam / 2, depth), (-beam / 2, depth)]
    h = waterline_offset(hull, phi, beam * draft)
    _, xb, yb = poly_area_centroid(clip_below(hull, phi, h))
    return xb * math.cos(phi) + (yb - kg) * math.sin(phi), xb, yb
 
def gz_curve(beam, depth, draft, kg, deg_max=70, step=1):
    return [(d, righting_arm(beam, depth, draft, kg, math.radians(d))[0])
            for d in range(0, deg_max + 1, step)]
 
BEAM, DEPTH, DRAFT = 16.0, 8.0, 4.0
KG = 6.6
KB = DRAFT / 2
BM = BEAM ** 2 / (12 * DRAFT)
GM = KB + BM - KG
print(f"KB={KB:.3f} m  BM=B^2/12T={BM:.3f} m  KM={KB+BM:.3f} m  KG={KG:.3f} m  GM={GM:.3f} m")
print(f"deck edge immerses at phi = {math.degrees(math.atan(2*(DEPTH-DRAFT)/BEAM)):.1f} deg")
print()
print(" phi[deg]   GZ[m]   GM*sin(phi)[m]   error[%]")
for d, gz in gz_curve(BEAM, DEPTH, DRAFT, KG, 60, 5):
    lin = GM * math.sin(math.radians(d))
    err = f"{100 * (lin - gz) / gz:8.1f}" if abs(gz) > 0.01 else "       -"
    print(f"  {d:5.1f}   {gz:7.4f}   {lin:11.4f}   {err}")
 
curve = gz_curve(BEAM, DEPTH, DRAFT, KG, 70, 1)
dmax, gzmax = max(curve, key=lambda t: t[1])
vanish = next((d for d, g in curve if d > 5 and g <= 0.0), None)
print()
print(f"max GZ = {gzmax:.3f} m at {dmax} deg,  vanishing stability at {vanish} deg")
KB=2.000 m  BM=B^2/12T=5.333 m  KM=7.333 m  KG=6.600 m  GM=0.733 m
deck edge immerses at phi = 26.6 deg
 
 phi[deg]   GZ[m]   GM*sin(phi)[m]   error[%]
    0.0    0.0000        0.0000          -
    5.0    0.0657        0.0639       -2.7
   10.0    0.1417        0.1273      -10.2
   15.0    0.2394        0.1898      -20.7
   20.0    0.3716        0.2508      -32.5
   25.0    0.5550        0.3099      -44.2
   30.0    0.7207        0.3667      -49.1
   35.0    0.6823        0.4206      -38.4
   40.0    0.5196        0.4714       -9.3
   45.0    0.2828        0.5185       83.3
   50.0    0.0001        0.5618          -
   55.0   -0.3116        0.6007     -292.8
   60.0   -0.6406        0.6351     -199.1
 
max GZ = 0.727 m at 31 deg,  vanishing stability at 51 deg

At 5 degrees GZGZ is 0.0657 and GMsin⁡ϕGM\sin\phi is 0.0639, an error of −2.7%. At 25 degrees the pair is 0.5550 against 0.3099, and the error has opened to −44.2%. Just past deck immersion at 26.6 degrees, the 30 degree row shows a true GZGZ of 0.7207 against a linear 0.3667. Barely half. The waterplane keeps widening until the deck goes under, so the real righting arm outruns the linear estimate. The maximum righting arm, 0.727 m, sits at 31 degrees. At 45 degrees the error sign flips to +83.3%, and at 50 degrees the true GZGZ is 0.0001 while the linear formula still prints 0.5618. Past the vanishing angle of 51 degrees, the 55 and 60 degree rows are −0.3116 and −0.6406. The linear formula is still +0.6007 and +0.6351. Promising a positive arm after vanishing stability is the dangerous side of the estimate.

The free surface loss can be checked without cutting any polygon. Below deck immersion the sides are vertical, and the righting arm has a closed form. The loss is a virtual rise, so subtract δGM\delta GM from GMGM and feed that in.

import math
 
BEAM, DRAFT, KG = 16.0, 4.0, 6.6
NABLA = BEAM * DRAFT
BM = BEAM ** 2 / (12 * DRAFT)
GM = DRAFT / 2 + BM - KG
 
def free_surface_loss(rho_f, rho, b, n, nabla):
    """free surface loss [m] for a tank of width b split by n bulkheads"""
    i_free = n * (b / n) ** 3 / 12
    return rho_f * i_free / (rho * nabla)
 
def wall_sided_gz(gm, bm, phi):
    """righting arm while the sides are still vertical, before deck immersion"""
    return (gm + 0.5 * bm * math.tan(phi) ** 2) * math.sin(phi)
 
print(" tanks   i[m^4]   dGM[m]   GM_eff[m]   GZ(20deg)[m]")
for n in (1, 2, 3, 4):
    d_gm = free_surface_loss(1000.0, 1025.0, 12.0, n, NABLA)
    gz20 = wall_sided_gz(GM - d_gm, BM, math.radians(20))
    print(f"  {n:4d}  {n*(12.0/n)**3/12:7.1f}  {d_gm:7.3f}   {GM-d_gm:8.3f}   {gz20:11.3f}")
 tanks   i[m^4]   dGM[m]   GM_eff[m]   GZ(20deg)[m]
     1    144.0    2.195     -1.462        -0.379
     2     36.0    0.549      0.185         0.184
     3     16.0    0.244      0.489         0.288
     4      9.0    0.137      0.596         0.325

Raise the bulkhead count n from 1 to 2 and the red loss bar drops to a quarter, while the barge that was lying over stands up.

phi = 10.0 deg
Start at n = 1 and press release: the liquid slides to the low side and the barge never comes back. Now drag the bulkhead slider. The liquid volume never changes, the centre of gravity of the ship at rest never changes — only the width of each free surface does, and at n = 2 the red loss bar has already dropped to a quarter.

Yellow is the liquid. Its surface stays horizontal no matter what the hull does. At n = 1, release her and she never returns — she settles at an angle of loll. Add one bulkhead and the red loss bar drops to a quarter, and she stands up again.

One bulkhead takes ii from 144 to 36, exactly a quarter. With ρf=1000\rho_f = 1000, ρ=1025\rho = 1025, i=144i = 144 and ∇=64\nabla = 64, δGM=1000×144/(1025×64)=2.195\delta GM = 1000 \times 144 / (1025 \times 64) = 2.195 m. At n=4n = 4, i=9i = 9, δGM=0.137\delta GM = 0.137 m and the 20 degree arm is 0.325 m. That still sits short of the no-loss value 0.3716 m. The righting arm at 20 degrees flips sign, from −0.379 m to +0.184 m. The closed form used here, GZ=(GM+12BMtan⁡2ϕ)sin⁡ϕGZ = (GM + \tfrac{1}{2}BM\tan^2\phi)\sin\phi, agrees with the polygon calculation as long as the deck stays dry: with no loss at all it gives 0.3716 m at 20 degrees, the same number as the table above.

Before you float a hull in a 6-DOF VOF run#

Leave GM=0.733GM = 0.733 m on paper before the initial draft goes into the solver. If the free roll period Tϕ=2πk/g GMT_\phi = 2\pi k/\sqrt{g\,GM}, with kk the roll radius of gyration, misses that hand value, suspect the grid or the inertia that was typed in. BMBM is an x2x^2-weighted integral over the waterplane, so the cells near the sides do most of the work. Smear the free surface over two or three cells and the effective width shrinks, and the righting moment comes out small.

With the tanks filled, the no-bulkhead loss δGM=2.195\delta GM = 2.195 m is the quasi-static baseline. The effective GMGM on that row is −1.462-1.462 m. Liquid out of phase with the hull can heel her past that loss. An implicit free-surface treatment, Casulli–Zanolli's nested Newton, is what that run actually needs. The hand number is not the answer. It is the figure to hold while distrusting the roll the solver prints.

Share if you found it helpful.