Skip to content
cfd-lab:~/ja/posts/2026-08-13-metacentric-h…online
NOTE #130DAY THU 유체역학DATE 2026.08.13READ 7 min read#Metacenter#Buoyancy#Free-Surface#Fluid-Mechanics#FSI

重心が上にあるのに転覆しない — メタセンタ高さ GM と自由表面影響

GM は水線面の慣性モーメントが稼ぐ余裕です。タンク内の自由表面ひとつが、その余裕を丸ごと奪うことがあります。

コンテナ船の重心は、水没部分の中心より数メートル上にあります。重い側が上に載っているのに、船は自分で起き上がります。逆に重心が浮心より下にあるのに転覆する台船(barge)もあります。上下関係だけでは何も決まらないということです。この記事では、その判定を実際に下す量 — メタセンタ高さ GMGM — がどこから来るのか、そしてタンクの液体がなぜその値を丸ごと消してしまうのかを扱います。

重心が浮心より上にある

まず記号を揃えます。KK は竜骨(keel、船底中心)、GG は重心、BB は浮心です。浮心は水没体積の図心(centroid)です。アルキメデスが述べたのは大きさだけです。浮力の大きさは押しのけた水の重さに等しく、作用線は水没体積の図心を通ります。

まっすぐ浮いているとき、GGBB は同じ鉛直線上にあります。重力と浮力は大きさが等しく向きが逆なので、モーメントはゼロです。この状態では GG が上でも下でも何も起こりません。安定性は静止状態ではなく、わずかに傾けたときに何が生じるかで定義されます。

傾けると事情が変わります。BB は水没形状の図心なので、形状が変われば一緒に移動します。GG は船と一緒に回転するだけで、船の中では動きません。二つの鉛直線がずれることでモーメントの腕ができます。これを復原挺(righting arm)GZGZ と呼び、排水量 Δ\Delta に対して復原モーメントは M=ΔGZM = \Delta \cdot GZ です。

傾ければ水没形状が変わる

下のシミュレーションで実際に傾けてみましょう。幅・喫水・重心高さを変え、release at 15 deg を押して戻ってくるかを見ます。

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(緑)が傾けた側へ引かれ、GG(黄)を通る鉛直線との間隔が GZGZ です。紫の MM は浮力の作用線が船体中心線と交わる点です。KG スライダを 7.33 m より上に上げると MMGG の下に下がり、船は戻る代わりに片側に倒れたまま止まります。

BM=I/BM = I/\nabla — 水線面の慣性モーメントがすべて#

小さな傾斜角 ϕ\phi では、水没形状の変化はくさび二つに整理されます。片舷で水面上に出たくさびと、反対舷で新たに沈んだくさびです。排水量は変わらないので二つの体積は等しく、左右に離れています。この体積移動が BB を動かします。

水線面(waterplane、水面が船体を切った断面)の微小帯 dAdA が中心線から xx だけ離れていれば、その帯が生む体積変化は xϕdAx\phi\,dA、モーメントは x2ϕdAx^2\phi\,dA です。全部足すと断面二次モーメントが出ます。

BB=ϕAx2dA=Iϕ,BM=I\overline{BB'} = \frac{\phi \int_A x^{2}\,dA}{\nabla} = \frac{I\,\phi}{\nabla}, \qquad BM = \frac{I}{\nabla}

II は中心線まわりの水線面の断面二次モーメント、\nabla は排水体積です。幅 bb の長方形断面なら単位長さあたり I=b3/12I = b^3/12=bT\nabla = b\,T なので

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

となります。ここで幅が三乗で入り、分母の \nabla で一度打ち消されて二乗が残る点が重要です。幅を 20% 広げれば BMBM は 44% 大きくなります。カヌーが不安定で筏が安定なのはこのためです。そして判定式は一行で終わります。

GM=KB+BMKGGM = KB + BM - KG

KBKB は浮心高さ、KGKG は重心高さです。GM>0GM > 0 なら復原、GM<0GM < 0 なら転覆です。幅 b=16b = 16 m、喫水 T=4T = 4 m の台船なら KB=2.0KB = 2.0 m、BM=5.333BM = 5.333 m なので KM=7.333KM = 7.333 m です。KGKG が 6.6 m なら余裕は 0.733 m しかありません。

GMsinϕGM \sin\phi が裏切る角度#

GMGMϕ0\phi \to 0 での傾きです。小角度では GZGMsinϕGZ \approx GM \sin\phi がよく合います。問題はこの近似がいつ崩れるかです。

崩れる地点は幾何が決めます。水線面が変わらない間だけ II が一定だからです。上の台船は乾舷(freeboard、水面上の船体高さ)が 4 m なので、tanϕ=2×4/16\tan\phi = 2 \times 4/16、つまり 26.6° で甲板端が水に浸かります。その瞬間から水線面はもう広がれず、むしろ縮みます。BMBM が崩れ、GZGZ 曲線は折れます。

上のシミュレーションで hold heel を押し、角度を 30° 超まで動かすと、緑の実線(厳密値)が紫の点線(GMsinϕGM\sin\phi)から離れていくのが見えます。二つの曲線の符号が分かれる角度が消失角です。

タンクの液体が GM を丸ごと奪う#

次に船内に液体タンクを入れます。バラストタンク、燃料タンク、貨物タンク、消火水で浸かった車両甲板まで、すべて同じ問題です。

液体の重さは傾けても変わりません。移動するだけです。ところがその移動が GG を低い側の舷へ引っ張ります。計算の構造は BMBM とまったく同じです。タンク内の自由表面もひとつの水線面であり、傾けばくさび二つが入れ替わります。

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

ii はタンク自由表面の断面二次モーメント、ρf\rho_f はタンク液体の密度、ρ\rho は外の水の密度です。この値は船体から実際に引くのではなく、GG がその分だけ上がったものとして扱います(仮想上昇、virtual rise)。**タンクにどれだけ液体が入っているかは式に現れません。**あふれるほど満たして自由表面がなくなるか、完全に空になるかしない限り、20 cm でも 2 m でも損失は同じです。

下でバルクヘッドのスライダを動かしてみましょう。

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.

黄色がタンクの液体です。液面は船体がどう傾いても水平を保ちます。n = 1 で放すと船は戻らず、片側に倒れたまま釣り合います(定傾斜、angle of loll)。隔壁を一枚立てた瞬間に赤い損失バーが 1/4 に減り、船は起き上がります。

隔壁一枚が損失を 1/4 にする#

btb_t のタンクを nn 個に分ければ、各区画の幅は bt/nb_t/n で、区画が nn 個です。

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

損失は 1/n1/n ではなく 1/n21/n^2 で減ります。幅の三乗が個数の一乗に勝つからです。上の台船に幅 12 m のタンクが一つあれば i=144i = 144 m⁴、=64\nabla = 64 m³/m なので δGM=2.195\delta GM = 2.195 m です。GMGM の 0.733 m はとうに消えています。中央に隔壁を一枚立てるだけで 0.549 m に落ち、船は再び起き上がります。

タンカーの中央に縦隔壁が立っているのはこのためです。積載量も重心高さも変わりません。自由表面の幅だけが半分になります。

Python で描いた GZ 曲線と消失角#

GMGM は接線の傾き一つにすぎません。大角度の GZGZ は、水没多角形を直接切って図心を求めるほうが速いです。船体断面を多角形として置き、水没面積が排水面積と等しくなる水線高さを二分法で探し、その多角形の図心を計算します。

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):
    """傾斜角 phi のとき、地球基準の高さが 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):
    """水没面積が 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

5° での誤差は 2.7% です。30° では GMsinϕGM\sin\phi が実際の GZGZ の半分しかありません。甲板が浸かるまで水線面が広がり続け、実際の復原力が線形予測より大きくなるからです。ところが 45° を超えると符号が逆転し、今度は線形近似が危険側に嘘をつきます。50° で実際の GZGZ はゼロなのに、GMsinϕGM\sin\phi はまだ 0.56 m を約束します。

自由表面損失は多角形を切らなくても確認できます。甲板が浸かるまでは舷側が垂直なので、復原挺に閉じた式があるからです。損失は仮想上昇なので、GMGM から δGM\delta GM を引いて入れるだけです。

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):
    """隔壁 n 枚で分けた幅 b のタンクの自由表面損失 [m]"""
    i_free = n * (b / n) ** 3 / 12
    return rho_f * i_free / (rho * nabla)
 
def wall_sided_gz(gm, bm, phi):
    """甲板が浸かる前、舷側が垂直な範囲の復原挺"""
    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

隔壁一枚で ii が 144 から 36 へ、ちょうど 1/4 になります。20° での復原挺は −0.379 m から +0.184 m へ符号が変わります。ここで使った閉じた式 GZ=(GM+12BMtan2ϕ)sinϕGZ = (GM + \tfrac{1}{2}BM\tan^2\phi)\sin\phi は、甲板が浸かる前の範囲では多角形計算と一致します。損失がないときの 20° の値 0.3716 m が上の表と同じです。

6-DOF VOF 解析に船を浮かべる前に#

浮体を CFD で解いた瞬間、これらの値はすべて検証基準になります。

第一に、初期喫水を合わせる前に GMGM を手計算しておきましょう。VOF で自由表面を捉え(界面捕捉手法の比較)、6-DOF 剛体運動を付ければ(クォータニオンによる剛体 FEM)、船は自分で揺れます。その自由減衰振動の周期 Tϕ=2πk/gGMT_\phi = 2\pi k/\sqrt{g\,GM} が手計算値と合わなければ、格子か入力した慣性モーメントを疑うべきです。kk は横揺れの回転半径です。

第二に、格子解像度が BMBM を直接削ります。BMBM は水線面の x2x^2 重み積分なので、舷側近くのセルが最も大きく寄与します。自由表面が二、三セルにわたって滲むと有効水線面幅がぼやけ、復原力が過小評価されます。喫水線付近を局所的に細かく取るほうが安上がりです。

第三に、タンクを満たしたまま解析するなら、自由表面損失は解析の中で自動的に出てきます。スロッシングと一緒に出てくるということです。液体が船体運動と位相がずれると、準静的な δGM\delta GM より悪くも良くもなり得ます。自由水面を陰的に扱う手法(Casulli–Zanolli の入れ子 Newton 法)が要るのもこの地点です。手計算はそこでも有効です。ただし答えとしてではなく、結果を疑うための基準線として使います。

役に立ったらシェアしてください。