C言語でGnuplotを使ってグラフを描けるようになるまでの備忘録
Gnuplotのインストールからテストプログラムの実行まで.筆者はガチ勢ではないので詳しいことは偉い人に聞いてほしい.
↓前回の記事
この記事で説明したソースコードはGithubにアップしてある
≫mtkbirdman.com/CFD/
フローチャートとソースコード
参考にしたサイト
≫C言語でGnuplotを動かす・・・ベースにしたソースコード
≫4 C言語からgnuplotを操作する・・・配列の描画
≫工学ナビの中の人の研究と周辺 C言語のプログラムでgnuplotにグラフを描かせる(Windows)・・・LINUXとWindowsの違い
≫GNUPLOTを用いたグラフ作成・・・Gnuplotのコマンド
フローチャートとソースコードを以下に示す
ソースコードはGithubも参照
≫mtkbirdman.com/CFD/plot.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define GNUPLOT_PATH "C:/gnuplot/bin/gnuplot.exe" //gnuplot.exeへのPATH
int main()
{
FILE *gp,*fp; //gp:gnuplot pointer, fp:file pointer
char foil_name[256],file_name[256],foil_data[256],file_format[]=".txt",*ch;
int i,j,file_size=0;
double val[1024][4];
//翼型名を入力
printf("Input foil name\n>>");
fgets(foil_name,sizeof(foil_name),stdin); //キーボード入力
foil_name[strlen(foil_name)-1]='\0'; //改行コード"\n"を削除
sprintf(file_name,"%s%s",foil_name,file_format); //翼型名とファイル形式を結合
//翼型のテキストファイルを開く
if((fp=fopen(file_name,"r"))==NULL){ //失敗したとき
fprintf(stderr,"Not Found %s.",file_name); //標準出力エラー
exit(EXIT_FAILURE); //プログラムの異常終了を知らせる
}else{ //成功したとき
while(fgets(foil_data,sizeof(foil_data),fp)!=NULL){ //ファイルの最終行まで読み込む
ch=strtok(foil_data," \n"); //最初の値を区切り文字で分割
for(i=0;i<256;i++){
if(ch==NULL){ //End of string
break;
}else{
val[file_size][i]=atof(ch); //文字列から数値へ変換
}
ch=strtok(NULL," \n"); //次の値を分割
}
file_size++; //file_sizeの更新
}
}
fclose(fp); //テキストファイルを閉じる
//Gnuplotのコマンドを起動する
if((gp=_popen(GNUPLOT_PATH,"w"))==NULL){ //パイプを使ってGnuplotを起動する
fprintf(stderr,"Not Found %s.",GNUPLOT_PATH);
exit(EXIT_FAILURE);
}
//Gnuplotにコマンドを送る
fprintf(gp, "set xrange [0:1]\n"); //範囲の設定
fprintf(gp, "set yrange [-0.5:0.5]\n");
fprintf(gp,"set size square\n");
fprintf(gp, "plot '-' with lines\n"); //配列に入っている値を表示
for(i=1;i<=file_size-1;i++){
fprintf(gp,"%f\t%f\n",val[0][i],val[1][i]); //値の書き込み
}
fprintf(gp,"e\n"); //配列の終了
fflush(gp); //バッファにため込まれているデータを解放する(必須)
system("pause");
fprintf(gp, "exit\n"); //Gnuplotを終了する
_pclose(gp);
}
プログラムの解説
変数
以下の変数を使用する
FILE *gp,*fp; //gp:gnuplot pointer, fp:file pointer
char foil_name[256],file_name[256],foil_data[256],file_format[]=".txt",*ch;
int i,j,file_size=0;
double val[1024][4];
*gpと*fpはファイルポインタ.C言語ではパイプとかいう機能を使ってGnuplotを動かせるらしい.すごいね
翼型名foil_nameはキーボードから入力し,ファイル形式file_formatと結合してファイル名file_name(翼型名.txt)を作る.動的配列にするのが面倒なのでそれなりに大きい配列を用意しておく
*chはテキストファイルから読み込んだ文字列を数値に分割するときのchar型のポインタ変数
カウンターはi,jを使い,翼型の座標点数はfile_sizeで計算する
テキストファイルから読み込んだ値はdouble型配列val[節点番号][xとy]に格納する
C言語はRow-major orderで多次元配列の値を格納しているらしいので,今後巨大な配列を扱うなら一番外側の次元からアクセスしていくと計算速度が速くなる(はず)
≫多次元配列のメモリレイアウト方式について - Qiita
≫【C言語】配列へのアクセス順序による処理速度の違い【キャッシュ】 _ だえうホームページ
翼型名を入力
翼型名をキーボードから入力する
≫1行の文字列として入力する - 苦しんで覚えるC言語・・・キーボード入力
≫文字列処理関数 - 苦しんで覚えるC言語・・・文字列の結合
//翼型名を入力
printf("Input foil name\n>>");
fgets(foil_name,sizeof(foil_name),stdin); //キーボード入力
foil_name[strlen(foil_name)-1]='\0'; //改行コード"\n"を削除
sprintf(file_name,"%s%s",foil_name,file_format); //翼型名とファイル形式を結合
キーボードからの入力はfgets関数を使うといい
ただし,fgets関数は入力した文字列の最後に改行コード"\n"を挿入するので,このままfoil_nameとfile_formatを結合すると,例えばfile_name="DAE21\n.txt"のようにファイル名が2行にわたってしまう
これを防ぐために4行目のコードで改行コード"\n"を削除している
≫fgets()の改行を削除する _ 超兄貴の開発メモ
翼型のテキストファイルを開く
テキストファイルのデータを配列に格納する
テキストファイルを開いて翼型のデータを読み込み,配列に格納する
≫テキストファイルの読み書き - 苦しんで覚えるC言語・・・ファイルの開閉
≫1行の文字列として入力する - 苦しんで覚えるC言語・・・文字列の分割
//翼型のテキストファイルを開く
if((fp=fopen(file_name,"r"))==NULL){ //失敗したとき
fprintf(stderr,"Not Found %s.",file_name); //標準出力エラー
exit(EXIT_FAILURE); //プログラムの異常終了を知らせる
}else{ //成功したとき
while(fgets(foil_data,sizeof(foil_data),fp)!=NULL){ //ファイルの最終行まで読み込む
ch=strtok(foil_data," \n"); //最初の値を区切り文字で分割
for(i=0;i<256;i++){
if(ch==NULL){ //End of string
break;
}else{
val[file_size][i]=atof(ch); //文字列から数値へ変換
}
ch=strtok(NULL," \n"); //次の値を分割
}
file_size++; //file_sizeの更新
}
}
fclose(fp); //テキストファイルを閉じる
fopen関数でfile_nameのファイルを開く.ファイルの取得に失敗したときはfopen関数がNULLを返してくれるので,if関数で場合分けして強制終了する
main.cがあるディレクトリ上にテキストファイルを置いておくことを忘れずに
テキストファイルを行ごとに読み込むにはfgets関数を使い,最終行までのファイルの読み込みはwhile文で行う
最終行に達して読み込むものがなくなったらfgets関数がNULLを返してくれるので,繰り返しを終了する
fgets関数ではテキストデータを文字列として読み込むので,strtok関数を使って区切り文字で分割してからatof関数でdouble型に変換して配列に格納する
詳しくは上に挙げたリンクを参照してほしい
Gnuplotを起動する
Gnuplotにコマンドを送る
Gnuplotを起動してコマンドを送り,グラフを描く
//Gnuplotのコマンドを起動する
if((gp=_popen(GNUPLOT_PATH,"w"))==NULL){ //パイプを使ってGnuplotを起動する
fprintf(stderr,"Not Found %s.",GNUPLOT_PATH);
exit(EXIT_FAILURE);
}
//Gnuplotにコマンドを送る
fprintf(gp, "set xrange [0:1]\n"); //範囲の設定
fprintf(gp, "set yrange [-0.5:0.5]\n");
fprintf(gp,"set size square\n");
fprintf(gp, "plot '-' with lines\n"); //配列に入っている値を表示
for(i=1;i<=file_size-1;i++){
fprintf(gp,"%f\t%f\n",val[0][i],val[1][i]); //値の書き込み
}
fprintf(gp,"e\n"); //配列の終了
fflush(gp); //バッファにため込まれているデータを解放する(必須)
system("pause");
fprintf(gp, "exit\n"); //Gnuplotを終了する
_pclose(gp);
WindowsでGnuplotを開くには_popen関数を使う.Gnuplotの起動に失敗したときは強制終了する
8~15行目のような,Gnuplotでの細かい設定のコマンドなどは調べれば色々と出てくる
≫gnuplotコマンド集
Gnuplotのコマンドをfprintf関数を使ってファイルポインタgpに書き込めば,Gnuplotを操作することができる
ちなみに,XFLR5で出力したtxtファイルでは1行目に翼型名が入っているので,Gnuplotにデータを書き込むループ(12~14行)は2行目から開始している
プログラムの実行
実際にVS code上でプログラムを実行してみる
「Ctrl+@」でターミナルを開き,「gcc (プログラム名)」でコンパイル,「a.exe」で実行する
キーボードからの入力待ちになるので,翼型名を入力してEnterキーを押すとGnuplotが開く
再びVS codeのウィンドウをアクティブにして,何かしらのキーを押せばプログラムが終了する
まとめ
Gnuplotを使ってグラフを描画するサンプルコードを書いてみた
これからCFDのプログラムに挑戦しようと思うので,結果の可視化に浸かってみようと思う
↓C言語の勉強はこの本が圧倒的におすすめ
int main(void)の意味からポインタまで,コードを書きながら一通り独学できる名著
コメント