スキップしてメイン コンテンツに移動

制限三体問題シミュレータ by Rust

// 制限三体問題シミュレータ
//
// 座標系は連星系の重心を原点とし, 平均運動nで回転する系.
// 従ってe=0のとき連星は静止するが, e \neq 0も計算できる.
// 連星の長半径aと平均運動nが1となる単位系を用いる.
//
// 必要なパラメータは
//     連星の質量比 q = M_2 / M_1
//     連星の離心率 e
//     計算終了時刻 t_end
// と初期条件 x: [f64;3], v: [f64;3] の計九つ. 上ふたつを
// コマンドライン引数としてセットすることもできる.
//

// use std::env; // コマンドライン引数を使用する場合はコメントアウトを外す

use std::f64::consts::PI;

fn main() {
 // パラメータの設定
 // let args: Vec = env::args().collect();
 // let q: f64 = args[1].parse::().unwrap();
 // let e: f64 = args[1].parse::().unwrap();
 let q: f64 = 0.01;
 let e: f64 = 0.;
 let t_end: f64 = 128.*PI;

 // 初期条件
 let mut t: f64 = 0.;
 let mut x: [f64;3] = [ 0.5-q/(1.+q), 3f64.sqrt()/2., 0. ];
 let mut v: [f64;3] = [ 0., 0., 0. ];
 println!("{} {} {} {}", t, x[0], x[1], x[2] );

 // アウトプット間隔
 let t_output: f64 = 0.0625;
 let mut t_record: f64 = t_output;

 // 数値積分
 while t < t_end {
  rk4( &mut t, &mut x, &mut v, [q,e] );
  if t >= t_record {
   println!("{} {} {} {}", t, x[0], x[1], x[2] );
   t_record += t_output;
  };
 };
}


fn true_anomaly ( t: f64, e: f64 ) -> f64 {
 let mut ecc: f64 = t;
 let mut delta_e: f64 = - ( ecc - e*ecc.sin() - t ) / ( 1. - e*ecc.cos() );
 while delta_e.abs() > 1e-4 {
  ecc += delta_e;
  delta_e = - ( ecc - e*ecc.sin() - t ) / ( 1. - e*ecc.cos() );
 };
 ( ((1.+e)/(1.-e)).sqrt() * (ecc/2.).tan() ).atan() * 2.
}


fn force( t: f64, x: [f64;3], v: [f64;3], param: [f64;2] ) -> [f64;3] {
 // param: [f64;2] = [ q, e ];
 let x1: [f64;3];
 let x2: [f64;3];
 { // 連星の位置を計算
  let f: f64 = true_anomaly( t, param[1] );
  let r: f64 = (1.-param[1].powi(2))/(1.+param[1]*f.cos());
  x1 = [ -param[0]/(1.+param[0])*r*(f-t).cos(), -param[0]/(1.+param[0])*r*(f-t).sin(), 0. ];
  x2 = [ 1./(1.+param[0])*r*(f-t).cos(), 1./(1.+param[0])*r*(f-t).sin(), 0. ];
 }
 let mut f: [f64;3] = [ x[0]+2.*v[1], x[1]-2.*v[0], 0. ];
 { // x1とx2からの力
  let r1: f64 = 1./(1.+param[0])*( (x[0]-x1[0]).powi(2) + (x[1]-x1[1]).powi(2) + (x[2]-x1[2]).powi(2) ).powf(-1.5);
  for k in 0..3 { f[k] += - r1*(x[k]-x1[k]); }
  let r2: f64 = param[0]/(1.+param[0])*( (x[0]-x2[0]).powi(2) + (x[1]-x2[1]).powi(2) + (x[2]-x2[2]).powi(2) ).powf(-1.5);
  for k in 0..3 { f[k] += - r2*(x[k]-x2[k]); }
 }
 f
}


fn rk4( t: &mut f64, x: &mut [f64;3], v: &mut [f64;3], param: [f64;2] ) {
 let x1: [f64;3] = *v;
 let v1: [f64;3] = force( *t, *x, *v, param );

 let h: f64 = 0.0625*((1f64.min(1./v1[0].abs())).min(1./v1[1].abs())).min(1./v1[2].abs());

 let x2: [f64;3] = [ v[0]+v1[0]*h*0.5, v[1]+v1[1]*h*0.5, v[2]+v1[2]*h*0.5 ];
 let v2: [f64;3] = {
  let xp: [f64;3] = [ x[0]+x1[0]*h*0.5, x[1]+x1[1]*h*0.5, x[2]+x1[2]*h*0.5 ];
  force( *t+h*0.5, xp, x2, param )
 };

 let x3: [f64;3] = [ v[0]+v2[0]*h*0.5, v[1]+v2[1]*h*0.5, v[2]+v2[2]*h*0.5 ];
 let v3: [f64;3] = {
  let xp: [f64;3] = [ x[0]+x2[0]*h*0.5, x[1]+x2[1]*h*0.5, x[2]+x2[2]*h*0.5 ];
  force( *t+h*0.5, xp, x3, param )
 };

 let x4: [f64;3] = [ v[0]+v3[0]*h, v[1]+v3[1]*h, v[2]+v3[2]*h ];
 let v4: [f64;3] = {
  let xp: [f64;3] = [ x[0]+x3[0]*h, x[1]+x3[1]*h, x[2]+x3[2]*h ];
  force( *t+h*0.5, xp, x4, param )
 };

 *t += h;
 for k in 0..3 {
  x[k] += ( x1[k] + 2.*( x2[k] + x3[k] ) + x4[k] ) * h / 6.;
  v[k] += ( v1[k] + 2.*( v2[k] + v3[k] ) + v4[k] ) * h / 6.;
 }
}

コメント

このブログの人気の投稿

matplotlib.histのnormedが変

以下の内容はPython 3.5.2 + matplotlib 1.5.1およびPython 3.6.2 + matplotlib 2.0.2で検証した. 作業日は2017年10月22日, 前者はUbuntu 16.04 on Win10 (WSL), 後者はDebian 8.9 (Anaconda). 規格化したいのにできない なにか数値の列 data があったとして, そのヒストグラムをmatplotlibでプロットしたいとする. 普通に plt.hist( data ) とすると, これは縦軸が各bin内に入るデータ点が何個あるかを表すことになる. これをデータ総数 len(data) で規格化したプロットにしようと思って plt.hist( data, normed=True ) または normed=1 とかやっちゃうと, 思った通りのアウトプットにならずに頭を傾げることになる. 例えば: import numpy as np import matplotlib.pyplot as plt data = np.random.normal(0,0.1,1000) weights = np.ones(len(data))/len(data) plt.hist( data, weights=weights ) plt.show() アウトプットは で, 縦軸が1を超えるとか, 意味がわからない. 原因 matplotlibのドキュメント を見ても何も書いてない. これは numpyのドキュメント に答えが書いてあるからで, 要するに normed オプションは事実上 density オプションと等しく, これは縦軸を確率分布関数と思って規格化するオプションである, と. 従って, normed=True オプションを指定すると, binの 面積 が1に規格化されることになる. いま欲しいものは値の 総和 が1に規格化されたアウトプットなのだから, binの幅が1でない限り, 欲しい結果は得られない. 対策 代わりに weights オプションを指定すればこの問題は解決できる. これは data の1つの値の重みを指定するパラメー

UbuntuのPDF編集ツールの使い方まとめ

PDFtkやpoppler-utilsに含まれるツールを使ってPDFを編集するコマンドのまとめです. 0. インストール sudo apt install pdftk sudo apt install poppler-utils UbuntuあるいはBash on Windowsならaptコマンドで一発. 1. PDFの文書情報の表示 pdfinfo (ファイル名) 2. PDFの分割と結合 pdftk (入力ファイル) cat (ページの指定) output (出力ファイル) ページ数の指定は1-12 14-endみたいな形で書けばよい. 入力ファイルを複数指定してページ数の指定を省略すればPDFの結合ができる. 3. PDFをJPEG/PNGに変換 pdftoppm [オプション] (入力ファイル名) (出力ファイル名の接頭辞) JPEGが欲しい場合-jpegを, PNGが欲しい場合-pngをオプションに指定する. デフォルトの解像度はDPI150で粗すぎるのでオプションで-r 300ないし-r 600を指定すべき. 白黒画像にしたい場合は-monoないし-grayを指定 (個人的には-grayのが好み). 複数ページのPDFを変換すると, 出力ファイルは (出力ファイル名の接頭辞)-1.jpg (出力ファイル名の接頭辞)-2.jpg みたいな形で生成される. 4. PDFからテキストを抽出 pdftotext (入力ファイル名) (出力ファイル名) 出力ファイルの文字コードはUTF-8, 改行コードはLF. 出力ファイル名は省略可能. 5. PDFから画像を抽出 pdfimages [オプション] (入力ファイル名) (出力ファイル名の接頭辞) オプションについて: -pngでPNG, -jでJPEG, -tiffでTIFF形式で出力される (オプションなしだとPPM形式) 6. PDFを回転 pdftk (入力ファイル名) cat 1-end(向き) output (出力ファイル名) 向きとしてはleft, right, downまたはnorth, east, west, southが使用可能. ※向きを指定する前にスペースを入れるとエラーになる. 関連ページ

源ノ明朝/源ノ角ゴシックをLuaLaTeXで使用する (Windows)

源ノ明朝 (Source Han Serif), 源ノ角ゴシック (Source Han Sans) はAdobeとGoogleが作成したCJKフォントで, オープンソースフォントとして公開されています (ライセンスはSIL Open Source License 1.1). 以下では, LuaTeXエンジンでこのフォントを (日本語で) 使用する方法を説明します. あるいは, otfファイルがダウンロードできれば, どのようなフォントにも適用可能です (PDFにフォント埋め込みすることに関してライセンスに注意が必要ですが). Windows10を前提としますが, 他の環境 (Linux/Mac) でもほぼ同じ方法で導入できるんじゃないでしょうか. 使用したのはTeX Live 2016 (LuaTeX-0.95) です.