gnuplotで配列を使う

gnuplotで配列はver5.0の時点では実装されていません。
文字を利用することで疑似的な配列が使えます。

まとめ


配列の名前を ” a ” として考えます。

a = ''
do for [i = 1:5] {
    a = a . sprintf('%f ', 0.2*i)
}

plot for [i=1:5] word(a,i)*x

とスクリプトを書き、プロットすると
arraygnuplot2
という画像が得られます。

配列の利用


gnuplotでは配列が実装されていません。
doループがver4.6から実装されたことから、これから実装されるかもしれません。

今、
\(
y=0.2x,\;\;0.4x,\;\;0.6x,\;\;0.8x,\;\;x
\)
の合計5つの関数をプロットしたいとします。
もしも素直に考えるならば、スクリプトは

plot for [i=1:5] i*0.2*x

です。これくらいは簡単ですが、複雑になった場合はどうでしょう。そのために疑似配列の使い方を知っておきましょう。

gnuplotで配列は実装されていないので、配列に似たもの(疑似配列と呼ぶことにします)を作ります。
普通のプログラミングでの”配列”のイメージは、番号付けした変数をデータの数だけ用意し、そこに代入していくイメージです。すなわち、
\(a(1)=0.2,\;\; a(2)=0.4,\;\; a(3)=0.6,\;\; a(4)=0.8,\;\; a(5)=1\)
という具合にです。

が、gnuplotでの疑似配列は、空白によって値と値が分けられた1つの文字型変数を用いて作ります。すなわち、
\(a= ” 0.2\;\; 0.4\;\; 0.6\;\; 0.8\;\; 1″ \)
を用意し、呼び出し時に
word(配列名、番号)
というgnuplotに用意されている関数を用いてそれぞれの要素にアクセスします。

この疑似配列aの作り方はこうです。

a = ''
do for [i = 1:5] {
    a = a . sprintf('%f ', 0.2*i)
}

1行目は配列”a”の初期化です。
2~4行目は配列”a”に実際に値を入れていきます。
この形を覚えておけば配列を作ることができます。

配列の呼び出しは、word(,)を用います。1つ目の引数に対象の疑似配列、2つ目の引数に何番目の要素を呼び出すかを指定します。以下のように、

a = ''
do for [i = 1:5] {
    a = a . sprintf('%f ', 0.2*i)
}
plot for [i=1:5] word(a,i)*x

とすればグラフとして書き出されます。

[adsense1]

疑似配列の四則演算


疑似配列の要素に数字を加えたいとします。
その場合は

b = ''
do for [i=1:5] {
   b = b . sprintf('%f ',word(a,i)+1)
}

とすれば疑似配列bに疑似配列aのそれぞれの要素に1加えた値が格納されます。
※同じ配列aにその結果を格納したい場合は最後にa=bを付け加えてください。

疑似配列同士の和を取りたければ、

c = ''
do for [i=1:5] {
   c = c . sprintf('%f ',word(a,i)+word(b,i))
}

とすれば疑似配列\(c(i)\)に(疑似配列\(a(i)\))+(疑似配列\(b(i)\))の値が格納されます。
差ならば-, 積ならば*, 商ならば/に変えてください。

実際に、以下のスクリプトを動かしてみます。

a = ''
do for [i = 1:5] {
    a = a . sprintf('%f ', 0.2*i)
}
print a

b = ''
do for [i=1:5] {
   b = b . sprintf('%f ',word(a,i)+1)
}
print b

c = ''
do for [i=1:5] {
   c = c . sprintf('%f ',word(a,i)*word(b,i))
}
print c

とすると実行結果

gnuplot> load "array.plt"
0.200000 0.400000 0.600000 0.800000 1.000000
1.200000 1.400000 1.600000 1.800000 2.000000
0.240000 0.560000 0.960000 1.440000 2.000000
gnuplot>

を得ます。

応用例


半径の違う複数の球
A = ''
do for [i = 1:5] {
    A = A . sprintf('%f ', 1e0/i)
}

set parametric
set ur[0:pi]
set vr[0:2*pi]
set key below
set ticslevel 0

set view equal xyz

splot for [i=5:1:-1] word(A,i)*sin(u)*cos(v),word(A,i)*sin(u)*sin(v),word(A,i)*cos(u)

というスクリプトを用いますと、半径の違う球が掛けます。
sp_argnuplot_c

アニメーション
num=5
A = ''
do for [i = 1:num] {
    A = A . sprintf('%f ', 1e0/i)
}

do for [j=1:119] {
   t=0.05*j
   set view 68,j*1.5,1,1
   A=''
   do for [i = 1:num] {
       A = A . sprintf('%f ', 1e0/sqrt(1+10*sin(t+i*(2e0*pi)/num)**2))
       }
   splot for [i= num:1:-1 ] word(A,i)*sin(u)*cos(v),word(A,i)*sin(u)*sin(v),word(A,i)*cos(u)
}

を表示に関する設定をして実行すると下のアニメーションが得られます。
sphere3

参考文献


Operate on 1D arrays in gnuplot

[adsense2][adsense3]

角度に依存して色を付ける(gnuplot)

gnuplotです。
gnuplotで、関数、もしくはデータでsplotすると3列目のデータに依存してカラーリングがされます。新たに4列目にデータを加え、splotをする時に

splot "test.d" u 1:2:3:4 w pm3d

とすると、4列目に従ったカラーリングがされます。

データの場合


例えば、以下のデータ(球面調和関数\(l=1,m=0\))を持っていたとします。
(ここにテキストデータ”y10.d”(500kB)を置きました。)
1列目:\(\sin{\theta}\cos{\phi}\cdot Y_{l,m}\), 2列目:\(\sin{\theta}\sin{\phi}\cdot Y_{l,m}\), 3列目:\(\cos{\theta}\cdot Y_{l,m}\), 4列目:\(\theta\), 5列目:\(\phi\)
そのまま出力する場合、以下のようなスクリプトで、以下の画像が取得できるはずです。

set pm3d
set pm3d depthorder
unset sur
set view equal xyz
set ticslevel 0
splot "y10.d" u 1:2:3

Ylm_eq_Y10_c

これを角度\(\theta\)に依存させて、形は変えずに色を付けましょう。
splotするときに、カラーを4列目に沿って付けるとすれば、

splot "y10.d" u 1:2:3:4 w pm3d

であり、もしも角度\(\phi\)に依存させて描くのであれば

splot "y10.d" u 1:2:3:5 w pm3d

でokです。実際に書いて、出力するとそれぞれ以下のような画像が出力されます。
y10_normaly10_thetay10_phi
※左から3列目(\(Y_{l,m}\)の値)に従って付けたカラー、4列目\(\theta\)に従って付けたカラー、5列目\(\phi\)に従って付けたカラー
となります。

※球面調和関数の出力に関して
球面調和関数は球面座標で\(\theta,\phi\)の関数\(Y_{l,m}(\theta,\phi)\)です。
ただし、今出力したい座標はデカルト座標(\(x,y,z\))です。デカルト座標で球面座標で記述される画像を得たい場合は単位ベクトル\(\; \vec{r}/|\vec{r}|\;\)に球面座標の関数を掛ければよいのです。
よって
\(
\displaystyle Y_{l,m}(\theta,\phi)\cdot \frac{\vec{r}}{|\vec{r}|}
\)
を表示すればよい、すなわちgnuplot上では(\(r=1\)の場合)、
1列目に \(x\;\;\to \;\; \sin{\theta}\cos{\phi}\cdot Y_{l,m}(\theta,\phi)\)
2列目に \(y\;\;\to \;\; \sin{\theta}\sin{\phi}\cdot Y_{l,m}(\theta,\phi)\)
3列目に \(z\;\;\to \;\; \cos{\theta}\cdot Y_{l,m}(\theta,\phi)\)
を表示させればokです。

多価関数の場合


続いて、多価関数として有名なリーマン面を出力します。
通常のリーマン面(3列目の高さがカラーの基準)を表示させるには

set pm3d depthorder
set parametric
set ur[0.01:5]
set vr[0:4*pi]
i={0,1}
splot u*cos(v),u*sin(v),real(sqrt(u)*exp(i*0.5*v))

とすれば下にある、左側の画像が得られます。角度に依存したカラーリングをしたい場合、以下のスクリプトでokです。

set isosamples 150
set pm3d depthorder

set table "param1.d"
set parametric
set ur[0.01:5]
set vr[0:4*pi]
i={0,1}
splot u,v,real(sqrt(u)*exp(i*0.5*v))
unset table

set table "param2.d"
plot "param1.d" u ($1*cos($2)):($1*sin($2)):3:($2) w vector
unset table

splot "param2.d" u 1:2:3:4 w pm3d

データでは無く、解析的な関数を直接、別の列を基準にして色付け、出力させることはできないようです。なので、関数を数値的なファイルに出力させるコマンド

set table "data.d"
    ...(ここに数値的に出力させたいグラフをプロット)
unset table

を書いて出力を行わせています。

また、角度を出力させるので周期的なカラーリングが良いと思います。色相環を元に下のカラーマップを使うといいでしょう。

set palette defined(0"#ff0000",1"#ff8000",2"#ffff00",3"#80ff00",4"#00ff00",5"#00ff80",6"#00ffff",7"#0080ff",8"#0000ff",9"#8000ff",10"#ff00ff",11"#ff0080",12"#ff0000")

上のスクリプトを実行すると右図のように、角度に依存したカラーリングがされます。
RiemannRiemann_angle
Riemann_compare2_c

※Riemann面について
Riemann面は
\(
f(z)=Re(\sqrt{z})
\)
で表される多価関数です。
gnuplotで単純に考えて上のグラフをプロットしようとすると、

i={0,1}
splot real(sqrt(x+i*y))

で良いはずですが、これを実際にプロットするとこうなります。
riemann_miss_c

本来は多価関数なのですが、それゆえに上記方法ではgnuplotでは記述されません。
極座標を用いて以下のように考えます。
\(
\begin{align}
f(z)&=\sqrt{z} \\
&=\sqrt{r}e^{i\frac{1}{2}\theta} (0\le \theta\lt 4\pi)\\
\end{align}
\)
であるため、gnuplotでは媒介変数表示u,vを用いて
\((x,y,z)=(u\cos{v},u\sin{v},\sqrt{u}e^{i\frac{1}{2}v})\)
\((u:0\sim \infty, v:0\sim 4\pi)\)
とすればいいのです。

参考文献

gnuplot:値に応じて線に色付け -TN’s blog
gnuplotで数値をファイルに書き出す(table)
How to output smooth cspline curve as a data file -Stack Overflow

gnuplotで極座標のグラフを書く

gnuplotで極座標形式\((r,\theta)\)で、こんな感じの2次元のグラフ
GPB-Concept-Dgrm-lg
Spacetime is warped and twisted by the
mass and spin of the earth. -GRAVITY PROBE B Testing Einstein’s Universeより

の、緑色のようなグラフを書きたいと思ったのが始まりです。
グラフの式を以下のように適当に書きます。
\(
\displaystyle \displaystyle f(r)=-\frac{1}{r^2+1}
\)

とモデル化しましょう。これを2つの方法でプロットします。

[adsense1]

gnuplot上で

    splot -1/(x*x+y*y+1)

とすると、
lorentz_c
というように出力されます。

上の場合、これはグリッドがx,y方向に切られています。動径方向と角度方向に切りたい場合はどうすればいいでしょうか。

gnuplotでこれを実現する方法は少なくとも2つあります。

  1. データとしてファイルを作った後、splot
  2. 媒介変数表示を使う

です。今回は2番目の媒介変数表示を使って表示させます。

とりあえず、これで目的のグラフを得ることができます。

set parametric
set ur[0:5]
set vr[0:2*pi]
splot u*sin(v),u*cos(v),-1/(u**2+1)

でこのようになります。
lorentz_sph_c

仕組みは極座標とは
\(
\begin{align}
x&=r\cos\theta \\
y&=r\sin\theta
\end{align}
\)
なので、媒介変数urに、vθをとみなせば書くことができます。

[adsense2]

3dプロットでの破綻


表面をカラー表示にしたいと思った時に、カラーに破綻が起こりました。

    set parametric
    splot u*sin(v),u*cos(v),-1/(u**2+1)
    set ur[0:5]
    set vr[0:2*pi]
    set pm3d at s
    unset sur
    rep

とするとこうなりました。
break_c

これを解決するには

set pm3d depthorder

を追加してreplotすれば破綻しなくなります。set hidden3d のカラー版ですね。
depthorder_c

参考
gnuplot demo script: surface2.dem

バレル内部でのBB弾の方程式

バレル内部でのBB弾の運動方程式です。

目的は、

BB弾は何秒間バレル内部に存在しているのか?
バレルが長いと減速になりうるのか?

を知ることです。詳細は弾道計算本をご覧ください。

弾道計算(BB弾)の理論
弾道計算(BB弾)の結果
弾道計算の結果2, 比較と詳細データ
弾道計算(BB弾)のコード(fortran90)
弾道計算のコード(Excel)
バレル内部でのBB弾の方程式←今ここ


電動ガンの場合です。
バレル内部1
空気抵抗は初速0[m/s]から90[m/s]前後にまで加速されるわけですから、空気抵抗の粘性抵抗、慣性抵抗どちらの項も無視することはできないでしょう。

空気の漏れも考えます。

この条件下では、バレル内部の運動方程式は以下のように導くことができます。

内部圧力変化と力


ピストン-BB弾間の圧力変化による力\(F_V(t)\)は、
\(
F_V(t)=S\cdot P(t)
\)

と書けます。ここで\(P(t)\)はピストン-BB弾間の内部圧力です。
\(P(t)\)は、断熱変化を仮定すると、ピストン-BB弾間体積\(V(t)\)を用いて
\(
P(t)V^{7/5}(t)=\text{const}
\)

の関係があります。体積\(V(t)\)は
\(
V(t)=x(t)S_b-x_0(t)S_s
\)

と書けます。ここで、ピストンの位置による時間変化を\(x_0(t)\)、BB弾の位置を\(x(t)\)、バレルの断面積を\(S_b\)、シリンダーの断面積を\(S_s\)としました。
この式は時刻に依存せずに決まるので、時刻\(t=0\)の時に大気圧\(P_0\)で、体積\(V_0\)であれば、任意の時刻での圧力\(P(t)\)は
\(
\displaystyle P(t)=P_0 \left(\frac{V_0}{V(t)}\right)^{7/5}
\)

と書けます。

空気の漏れについて


十分短い時間の間、時刻\(t\)において内部圧力\(P(t)\), 空気の密度\(\rho\)とすると、漏れ出る空気の速度\(v(t)\)はベルヌーイの定理から
\(
\begin{align}
P(t)&=\frac{1}{2}\rho v(t)^2+P_0 \nonumber \\
&\to v(t)=\pm \sqrt{\frac{2}{\rho}(P(t)-P_0)}
\end{align}
\)
が成立するとします。厳密には、ピストンの速度は漏れ出る空気の速度に対して無視できるほど小さい、という仮定の下で成立します。漏れ出る流量\(Q\)は、バレル-BB弾の隙間の断面積\(A\)、実験とのズレを調節する無次元の流量係数\(c’\)を用いて\(Q=c’ A v(t)\)と書けます。
また、断熱過程を圧力-体積の関係を用いたいので、漏れ出る空気はBB弾-ピストン間の体積の増加として扱います。

フルシリンダーの場合の運動方程式


その他、空気抵抗による力を入れると、BB弾の位置\(x(t)\), ピストンの位置\(x_0(t)\), 体積\(V(t)\)の運動方程式は
\(
\begin{align}
m\frac{d^2}{dt^2}x(t)&=\left[P(t)-P_0\right]S_b-\frac{1}{2}C_d \rho \pi R^2 |v(t)|^2\cdot\frac{v(t)}{|v(t)|} \label{bbin1}\\
m_s\frac{d^2}{dt^2}x_0(t)&=-k\left[x_0(t)-x_B-l\right]-[P(t)-P_0]S_s -F_f\label{bbin2} \\
\frac{d}{dt}V(t)&=v(t)S_b-v_0(t)S_s+c'(S_b-\pi R^2)\mathrm{sgn}(P(t)-P_0)\sqrt{\frac{2}{\rho}|P(t)-P_0|} \label{bbin3}
\end{align}
\)
と導くことが出来ます。

ここで、\(P_0\)は大気圧、\(\eta\)は粘性率、\(R\)はBB弾の半径、\(C_d\)は抗力係数、\(\rho\)は空気の密度, \(S_b\)はバレルの断面積、\(S_b\)はシリンダーの断面積、\(k\)はピストンのばね定数、\(l\)はばねの自然長、\(F_f\)はピストン-シリンダーの摩擦、\(v(t),v_0(t)\)はそれぞれBB弾の速度、ピストンの速度を意味します。

空気抵抗に関する詳細は球体の空気抵抗と係数をご覧ください。

ここで1つ言えることは、外部と内部の圧力が一定になる最適なバレル長というものが存在する、ということです。

2019/01/20 追記)
本の頒布日より1年以上たちました。
計算結果は載せないつもりでしたが、載せたくなりましたので少しだけ載せておきます。
まず、実測データと上で立てたモデルの運動方程式の比較をします。
比較対象はインターネット上で公開されていた二つのデータです。

実測データとの比較



1つ目(図の←)はさばなび様で公開されていた記事
https://www.saba-navi.com/2015/10/29/laboratory_work_barrel_cut/で実測されていたデータで、バレル長の長さと初速の関係です。ただし、既にリンク切れのようです。
ピストンの重さの記述が無かったので、典型的とされている重さ25gを仮定して計算しています。

2つ目(図の→)は石岡様のホームページ
○電動ガンバレル、シリンダの組み合わせによる初速実験
の結果との比較です。

どちらの計算結果もまぁまぁ合っていることから、私が仮定したモデルは見当はずれなものではないということが分かるかと思います。

バレル内部の様子


さて、上のモデルが正しいとした時、運動方程式を解いてバレルの動きや内圧を考えてみましょう。
解いてみますと、こういった図が得られます。

図は、東京マルイM16を想定した時のBB弾の位置、速度、ピストンの位置、内圧の時間変化です。
計算のパラメータは、
BB弾の重さ0.20g
ピストンの重さ24g
ばね定数431N/m
ばねの自然長150mm
押し切られた時のばねの長さ120mm
BB弾の半径5.95mm
バレルの直径6.05mm
とした時の結果です。
この場合、生み出せる最高初速は96m/s(バレル長63cmの時)。
例えばバレル長が50cmの時、ピストンが動いてからBB弾が射出されるまでにかかる時間は0.013秒ということが分かります。
すなわち、上のピストンの重さやBB弾の重さ、ばねの強さ、シリンダー容量の場合は最適なバレル長は63cmであるということです。
また、内圧の上昇によるピストンのブレーキなども計算できていることが分かるかと思います。

また、漏れ出る空気の量は\(3000[\mathrm{mm^3}]\)だということが見積もられました。この量はM16の場合は本来のシリンダー容量の15%程度です。

典型的なバレル長である50cmの場合、BB弾は射出されるまでに約0.013秒かかります。内圧の上昇によるブレーキがかからず、同じ過程でばねが引かれると仮定すると、ピストンは約0.010秒で戻ると考えられます。
理論上の効率的な射出サイクルの限界は

(1発)÷(0.013秒+0.010秒)≒ 43発/秒

という事です。現在販売されている東京マルイのハイサイクルは25発/秒なので、理論上あと1.7倍早くすることが可能です。

BB弾の重さを0.25gにした結果や、ばねの強さを変えた時の結果は
弾道計算本として計算結果をまとめた本に載せているので、そちらをご参照ください。本の詳細については弾道計算本の自家通販をご覧ください。
また時間が経ったらここに載せる…かもしれません。

補遺


補遺1
理想気体として扱える条件は、
低い気圧(分子の数が少なく、衝突等が無視できる)かつ高い温度(分子間力が分子の運動エネルギーに比べて無視できる)
です。
どうやら実在気体では10気圧以下、室温以上でこの条件は良く満たされ、理想気体とのずれは1%以内のようです[1]。
[1]を引用すると、

一般に,沸点の低い酸素・窒素・水素・ヘリウム等は,室温またはそれ以上の温度で10atm以下の圧力の場合,理想気体の値の1%以内で理想気体に近い性質を示す。

とあります。ピストンで空気が圧縮されたとき、BB弾とピストン間の圧力が10気圧以上にならなければ良い近似だと言えるでしょう。

参考文献


[1] 実在気体 -第2節 気体の状態方程式
[2]PERFECT HIT -TOKYO MARUI

『集弾性アップへの道』 BB弾とバレル内部に隙間があることが写真で確認できます。