Rでコールブルック・ホワイトの式

コールブルック・ホワイトの式とは

コールブルック・ホワイトの式(Colebrook-White の式)は、円管内の乱流における管摩擦係数 \(\lambda\) を求めるための経験式です。

\[
\begin{eqnarray}
\frac{1}{\sqrt{\lambda}} &=& -2\log_{10}\left(\frac{\varepsilon}{3.71\,D_H}+\frac{2.51}{Re\sqrt{\lambda}}\right)
\end{eqnarray}
\tag{1}\]

ここで、各変数の定義は次のとおりです。

  • \(\lambda\)

    • ダルシー・ワイスバッハの管摩擦係数(無次元)
  • \(\varepsilon\)

    • 管の絶対粗度 [m]
  • \(D_H\)

    • 管の水力直径 [m]
  • \(Re\)

    • レイノルズ数(無次元)

水力直径は、流れの断面積 \(A\) [m\(^2\)] と濡れ縁長(潤辺)\(P\) [m] から

\[
\begin{eqnarray}
D_H &=& \frac{4A}{P}
\end{eqnarray}
\tag{2}\]

で定義され、円管で満水の場合は管の内径に一致します。また、\(\varepsilon/D_H\) は相対粗度です。

括弧の中の第1項は管壁の粗さの効果、第2項は粘性の効果を表しています。この式には2つの極限があります。

  • 滑面の極限(\(\varepsilon/D_H \to 0\))では、粗さの項が消えて \(\dfrac{1}{\sqrt{\lambda}} = -2\log_{10}\left(\dfrac{2.51}{Re\sqrt{\lambda}}\right)\) となり、\(\lambda\) は \(Re\) だけで決まります。
  • 完全粗面の極限(\(Re \to \infty\))では、粘性の項が消えて \(\dfrac{1}{\sqrt{\lambda}} = -2\log_{10}\left(\dfrac{\varepsilon}{3.71\,D_H}\right)\) となり、\(\lambda\) は \(\varepsilon/D_H\) だけで決まります。

適用上の注意は次のとおりです。

  • 対象は乱流域(目安として \(Re \gtrsim 4000\))です。層流では \(\lambda = 64/Re\) を用います。
  • 相対粗度 \(\varepsilon/D_H\) が極端に大きい場合(目安として 0.05 を超える場合)は、実験の範囲を外れます。
  • \(\lambda\) が両辺に現れる陰関数のため、\(\lambda\) を求めるには反復計算(数値的な求根)が必要です。一方、\(\varepsilon\)、\(D_H\)、\(Re\) を求める場合は、式を変形して陽に解くことができます。

陽に解いた式は次のとおりです。

\[
\begin{eqnarray}
Re &=& \frac{2.51}{\sqrt{\lambda}\left(10^{-\frac{1}{2\sqrt{\lambda}}}-\dfrac{\varepsilon}{3.71\,D_H}\right)} \\
\varepsilon &=& 3.71\,D_H\left(10^{-\frac{1}{2\sqrt{\lambda}}}-\frac{2.51}{Re\sqrt{\lambda}}\right) \\
D_H &=& \frac{\varepsilon}{3.71\left(10^{-\frac{1}{2\sqrt{\lambda}}}-\dfrac{2.51}{Re\sqrt{\lambda}}\right)}
\end{eqnarray}
\tag{3}\]

\(\lambda\) については、\(x = 1/\sqrt{\lambda}\) とおいて

\[
\begin{eqnarray}
g(x) &=& x + 2\log_{10}\left(\frac{\varepsilon}{3.71\,D_H}+\frac{2.51\,x}{Re}\right) \;=\; 0
\end{eqnarray}
\tag{4}\]

を解きます。

ここで \(x\) は \(1/\sqrt{\lambda}\) です。

\(g(x)\) は \(x>0\) で単調増加ですので、解は区間内にただ1つ存在し、uniroot() で求めることが可能です。

Rコード

管摩擦係数 lambda、管の絶対粗度 epsilon、管の水力直径 D_H、レイノルズ数 Re のうち、いずれか3つを指定すると、残りの1つを返す関数です。

指定しなかった(NULL の)引数が求める値になります。

# コールブルック・ホワイトの式で、4変数のうち未指定の1つを求める関数
# lambda : 管摩擦係数(無次元)
# epsilon: 管の絶対粗度 [m]
# D_H    : 管の水力直径 [m]
# Re     : レイノルズ数(無次元)
colebrook_white <- function(lambda = NULL, epsilon = NULL, D_H = NULL, Re = NULL) {
  # 引数の指定状況を確認する
  args <- list(lambda = lambda, epsilon = epsilon, D_H = D_H, Re = Re)
  unknown <- names(args)[vapply(args, is.null, logical(1))]
  if (length(unknown) != 1L) {
    stop("lambda, epsilon, D_H, Re のうち、ちょうど3つを指定してください。")
  }

  # 指定された値の妥当性を確認する
  given <- args[setdiff(names(args), unknown)]
  if (!all(vapply(given, is.numeric, logical(1)))) {
    stop("すべての引数は数値で指定してください。")
  }
  if (any(unlist(given) < 0, na.rm = TRUE)) {
    stop("負の値は指定できません。")
  }
  if (any(unlist(given[setdiff(names(given), "epsilon")]) == 0, na.rm = TRUE)) {
    stop("lambda, D_H, Re に 0 は指定できません(epsilon のみ 0 を許容します)。")
  }

  # 管摩擦係数を求める(陰関数のため uniroot で数値的に解く)
  # x = 1 / sqrt(lambda) とおき、g(x) = 0 の解を求める
  solve_lambda <- function(epsilon, D_H, Re) {
    a <- epsilon / (3.71 * D_H) # 相対粗度に由来する項
    b <- 2.51 / Re # 粘性に由来する項の係数
    if (a >= 1) {
      warning("epsilon / D_H が大きすぎるため、解を求められません。")
      return(NA_real_)
    }
    g <- function(x) x + 2 * log10(a + b * x)
    x <- uniroot(g, interval = c(1e-6, 1e3), tol = 1e-12)$root
    1 / x^2
  }

  result <- if (identical(unknown, "lambda")) {
    # 複数の値を同時に与えた場合も計算できるよう、mapply で要素ごとに解く
    mapply(solve_lambda, epsilon, D_H, Re)
  } else {
    # 陽に解ける3変数は、共通の項 10^(-1/(2*sqrt(lambda))) を使う
    s <- sqrt(lambda)
    p <- 10^(-1 / (2 * s))
    if (identical(unknown, "Re")) {
      denom <- s * (p - epsilon / (3.71 * D_H))
      invalid <- denom <= 0
      value <- 2.51 / denom
    } else if (identical(unknown, "epsilon")) {
      value <- 3.71 * D_H * (p - 2.51 / (Re * s))
      invalid <- value < 0
    } else {
      # 未指定が D_H の場合
      k <- p - 2.51 / (Re * s)
      invalid <- k <= 0 | epsilon == 0
      value <- epsilon / (3.71 * k)
    }
    if (any(invalid, na.rm = TRUE)) {
      warning(
        "与えた3つの値に対して解が存在しない組み合わせがあるため、NA を返します。",
        "(lambda が滑面管の値より小さい、または epsilon = 0 のときの D_H など)"
      )
      value[invalid] <- NA_real_
    }
    value
  }

  # 乱流域の外では式の適用範囲を外れるため、注意を促す
  re_check <- if (identical(unknown, "Re")) result else Re
  if (any(re_check < 4000, na.rm = TRUE)) {
    warning("Re が 4000 未満の値を含みます。コールブルック・ホワイトの式は乱流域(Re >= 4000 程度)が対象です。")
  }

  result
}

使用例

円管(\(D_H = 0.1\) m)、商業用鋼管(\(\varepsilon = 0.045\) mm)、\(Re = 10^5\) のときの管摩擦係数を求めます。

# 管摩擦係数 lambda を求める
lambda_value <- colebrook_white(epsilon = 0.045e-3, D_H = 0.1, Re = 1e5)
lambda_value
[1] 0.02011523

求めた \(\lambda\) を使って、残りの3変数を逆算し、元の値に戻ることを確認します。

# 逆算して元の値に戻るかを確認する
epsilon <- colebrook_white(lambda = lambda_value, D_H = 0.1, Re = 1e5) # epsilon
D_H <- colebrook_white(lambda = lambda_value, epsilon = 0.045e-3, Re = 1e5) # D_H
Re <- colebrook_white(lambda = lambda_value, epsilon = 0.045e-3, D_H = 0.1) # Re
list(epsilon = epsilon, D_H = D_H, Re = Re)
$epsilon
[1] 4.5e-05

$D_H
[1] 0.1

$Re
[1] 1e+05

複数の値を同時に与えることもできます。レイノルズ数を変えたときの \(\lambda\) の変化を見てみます。

# Re を変化させて lambda を求める
re_seq <- 10^(4:7)
data.frame(
  Re = re_seq,
  lambda = colebrook_white(epsilon = 0.045e-3, D_H = 0.1, Re = re_seq)
)
     Re     lambda
1 1e+04 0.03156739
2 1e+05 0.02011523
3 1e+06 0.01684944
4 1e+07 0.01635936

単調増加と解の一意性

方程式の形

コールブルック・ホワイトの式に \(x = 1/\sqrt{\lambda}\) を代入し、全てを左辺に移すと次のようになります。

\[
\begin{eqnarray}
g(x) &=& x + 2\log_{10}\left(a + b\,x\right) \;=\; 0
\end{eqnarray}
\tag{5}\]

\[
\begin{eqnarray}
a &=& \frac{\varepsilon}{3.71\,D_H} \;\ge\; 0, \qquad b \;=\; \frac{2.51}{Re} \;>\; 0
\end{eqnarray}
\tag{6}\]

\(a\) は相対粗度に由来する定数、\(b\) は粘性に由来する定数で、どちらも \(x\) には依存しません。

求める \(\lambda\) は、\(x>0\) の範囲にある \(g(x)=0\) の解から \(\lambda = 1/x^2\) で得られます。

単調増加であること

\(g(x)\) を \(x\) で微分します。

\(\log_{10} u = \ln u / \ln 10\) を使うと、次のようになります。

\[
\begin{eqnarray}
g'(x) &=& 1 + \frac{2}{\ln 10}\cdot\frac{b}{a + b\,x}
\end{eqnarray}
\tag{7}\]

\(x>0\) では \(a + b\,x > 0\) であり、\(b>0\) です。したがって第2項は正になり、

\[
\begin{eqnarray}
g'(x) &>& 1 \;>\; 0
\end{eqnarray}
\tag{8}\]

となります。つまり \(g(x)\) は \(x>0\) の全域で厳密に単調増加です。

解が少なくとも1つ存在すること

\(g(x)\) は \(x>0\) で連続です。区間の両端での符号を調べます。

  • \(x \to 0^+\) のとき、\(g(x) \to 2\log_{10} a\) です。\(a<1\)(\(\varepsilon/D_H < 3.71\))なら負になります。\(a=0\) の滑面管では \(g(x) \to -\infty\) です。
  • \(x \to \infty\) のとき、\(x\) の項が支配的になり、\(g(x) \to +\infty\) です。

両端で符号が異なるため、中間値の定理により \(g(x)=0\) となる \(x\) が少なくとも1つ存在します。

解がただ1つであること

厳密に単調増加な関数は、同じ値を2回とりません。

仮に \(x_1 < x_2\) がともに解なら \(g(x_1) < g(x_2)\) となり、\(g(x_1) = g(x_2) = 0\) と矛盾します。

よって解はちょうど1つです。

さらに、\(\lambda = 1/x^2\) は \(x>0\) で一対一の対応ですので、\(\lambda\) の解もただ1つに決まります。

コードとの対応

コードでは、探索区間を interval = c(1e-6, 1e3) としています。

この区間で符号が変わること(\(g(10^{-6})<0\) かつ \(g(10^3)>0\))が、uniroot() を使うための条件です。

  • \(g(10^3)>0\) は、\(Re\) が極端に大きくない限り、常に成り立ちます。
  • \(g(10^{-6})<0\) は、ほぼ \(a<1\) と同じ条件です。コードの if (a >= 1) は、この条件を確認するためのものです。

実用上は、相対粗度 \(\varepsilon/D_H\) が 0.05 程度以下であれば、この条件には余裕があります。

式 7 の導出

\(g(x)\) は2つの項の和ですので、項ごとに微分します。

\[
\begin{eqnarray}
g(x) &=& x + 2\log_{10}\left(a + b\,x\right)
\end{eqnarray}
\tag{9}\]

ここで \(a = \dfrac{\varepsilon}{3.71\,D_H}\)、\(b = \dfrac{2.51}{Re}\) は \(x\) に依存しない定数です。

項ごとの微分

微分の線形性より、次のようになります。

\[
\begin{eqnarray}
g'(x) &=& \frac{d}{dx}\,x \;+\; 2\,\frac{d}{dx}\log_{10}\left(a + b\,x\right)
\end{eqnarray}
\tag{10}\]

第1項は \[\dfrac{d}{dx}\,x = 1 \tag{11}\] です。

常用対数を自然対数に直す

底の変換公式 \(\log_{10} u = \dfrac{\ln u}{\ln 10}\) を使います。\(\ln 10\) は定数です。

\[
\begin{eqnarray}
\frac{d}{dx}\log_{10}\left(a + b\,x\right) &=& \frac{1}{\ln 10}\cdot\frac{d}{dx}\ln\left(a + b\,x\right)
\end{eqnarray}
\tag{12}\]

合成関数の微分

\(u = a + b\,x\) とおくと、\(\ln u\) の微分は合成関数の微分(連鎖律)から、

\[
\begin{eqnarray}
\frac{d}{dx}\ln u &=& \frac{1}{u}\cdot\frac{du}{dx}
\end{eqnarray}
\tag{13}\]

となります。\(a\) と \(b\) は定数ですので、

\[
\begin{eqnarray}
\frac{du}{dx} &=& \frac{d}{dx}\left(a + b\,x\right) \;=\; b
\end{eqnarray}
\tag{14}\]

です。したがって、

\[
\begin{eqnarray}
\frac{d}{dx}\ln\left(a + b\,x\right) &=& \frac{b}{a + b\,x}
\end{eqnarray}
\tag{15}\]

となります。

最終導出

式 10 に 式 11 、式 12、式 15 を代入します。

\[
\begin{eqnarray}
g'(x) &=& 1 + 2\cdot\frac{1}{\ln 10}\cdot\frac{b}{a + b\,x} \\
&=& 1 + \frac{2}{\ln 10}\cdot\frac{b}{a + b\,x}
\end{eqnarray}
\tag{16}\]

式 7 が導出されます。

補足(符号の確認)

\(x>0\) では \(a + b\,x > 0\) です(\(a \ge 0\)、\(b>0\) のため)。また \(\ln 10 \approx 2.303 > 0\) です。そのため第2項は常に正になり、

\[
\begin{eqnarray}
g'(x) &>& 1
\end{eqnarray}
\tag{17}\]

が成り立ちます。これが「\(g(x)\) は単調増加」の根拠です。

\(a \ge 1\) のとき解がない理由

\(x \to 0^+\) のとき \(g(x) \to 2\log_{10} a\) です。\(a \ge 1\) ならこの極限値は \(0\) 以上になります。

\(g(x)\) は \(x>0\) で厳密に単調増加ですので、\(x>0\) の全てで \(g(x)\) は \(2\log_{10} a\) より大きくなり、

\[
\begin{eqnarray}
g(x) &>& 2\log_{10} a \;\ge\; 0 \qquad (x>0)
\end{eqnarray}
\tag{18}\]

となります。

したがって \(g(x)=0\) となる \(x>0\) は存在しません(\(a=1\) の場合も、\(x=0\) に近づくだけで \(x>0\) では \(g(x)>0\) です)。

よって、解が存在する条件は次のとおりです。

\[
\begin{eqnarray}
\text{解が存在する} &\Longleftrightarrow& a < 1 \;\Longleftrightarrow\; \frac{\varepsilon}{D_H} < 3.71
\end{eqnarray}
\tag{19}\]

なお、

  • \(\varepsilon/D_H \ge 3.71\) は、粗さの高さが管の直径の3.7倍以上という意味で、物理的にありえません。円管では、粗さの高さが半径(\(\varepsilon/D_H = 0.5\))を超えることもほとんど想定されません。
  • コールブルック・ホワイトの式が実験で裏付けられている範囲は、\(\varepsilon/D_H \lesssim 0.05\) 程度です。\(a = \varepsilon/(3.71\,D_H)\) に直すと約 \(0.013\) で、\(1\) にはまだ遠く離れています。

コードでの扱い

solve_lambda() には if (a >= 1) の確認を入れてあり、その場合は警告を出して NA を返します。

以上です。