PR

C言語でGnuplotを使って翼型を描く

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)の意味からポインタまで,コードを書きながら一通り独学できる名著

コメント