Segmentation fault
いわゆるアドレス不正です。
ポインタの操作誤りや、配列外参照など。
デバッグモードでコンパイルすると、もう少し原因が絞り込める可能性もあります。
あと、吐かれたコアダンプを読めば、例外発生箇所や、その時のメモリやレジスタの内容が確認できます(それ相当のスキルが必要)。
あくまで一般論です。
コアダンプします。計算している変数をすべて調べたところkai_1_upとmainのなかのjのループを切るとコアダンプしないことがわかります。
しかし、配列上では特に問題なく定義しているようにもおもえ。gdbの方で確認しても目立った問題の方は見られませんでした。
前の質問の方で初期化し忘れているということを多くアドバイスをされ、初期化についてはいろいろ気を使いました。
Segmentation fault (コアダンプ)
//グラフェンナノリボンエネルギーバンド spinwave susceptibility
#define _USE_MATH_DEFINES
#include <stdio.h>
#include <math.h>
#include <complex.h>
#include <stdlib.h>
#define M 2 // 存在するサイト数 //M=24で5nmくらい
#define N 4 //サイト数×2 A+Bの個数
#define P 101 //リボン長 全電子数 P*
#define R 10
void zheev_( char*, char*, int*, double _Complex*, int*, double*, double _Complex*, int*, double*, int* );
double f(double x, int j);
double u_up[P+1][N][N]={0.0}, u_dw[P+1][N][N]={0.0}; //固有ベクトル
double u_up_1[P+1][N][N]={0.0}, u_dw_1[P+1][N][N]={0.0};
double Energy_dw_1[P+1][N]={0.0},Energy_up_1[P+1][N]={0.0};
int main(int argc, char** argv){
FILE* fp;
FILE* fp_1;
FILE* fp_2;
FILE* fp_3;
FILE* fp_4;
double b_pi=M_PI;
double U=1.0; //相互作用エネルギーの平均(eV) u/t
if ((fp = fopen("kai.txt", "w")) == NULL)
{
printf("Cannot open the file\n");
exit(1);
}
fp_1 = fopen("u_dw.txt", "r");
if(fp_1 == NULL) {
printf("ファイルを開くことが出来ませんでした.¥n");
}
for(int l=0; l<=P; l++){
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
fscanf(fp_1, "%lf", &(u_dw[l][i][j]) );
}
}
}
fp_2 = fopen("Energydw_1.txt", "r");
if(fp_2 == NULL) {
printf("ファイルを開くことが出来ませんでした.¥n");
}
for(int l=0; l<=P; l++){
for(int i=0;i<N;i++){
fscanf(fp_2, "%lf", &(Energy_dw_1[l][i]) );
}
}
fp_3 = fopen("Energyup_1.txt", "r");
if(fp_3 == NULL) {
printf("ファイルを開くことが出来ませんでした.¥n");
}
for(int l=0; l<=P; l++){
for(int i=0;i<N;i++){
fscanf(fp_3, "%lf", &(Energy_up_1[l][i]) );
}
}
fp_4 = fopen("u_up.txt", "r");
if(fp_4 == NULL) {
printf("ファイルを開くことが出来ませんでした.¥n");
}
for(int l=0; l<=P; l++){
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
fscanf(fp_4, "%lf", &(u_up[l][i][j]) );
}
}
}
double x_start1=1.8;
double x_start2=0.0;
/*double c=0.0;
for(int j=0;j<(P+1)/2;j++){
for(int i=0;i<R;i++){
c=x_start1/R*i;
double fc=0.0;
fc=f(c,j)/(P+1)-U;
// printf("x=%lf,q=%lf ,%lf\n",c,2*b_pi/(P+1)*j,fc);
fprintf(fp, "%lf %lf %lf\n", c,2.0*b_pi/(P+1)*j,fc);
}
}*/
double err = 100.0;
double a = x_start1;
double b = x_start2;
double c;
for(int j=0;j<(P+1)/2;j++){
while(err>0.00000001){
c = (a+b)/2.0;
double fc = f(c,j)/(P+1)-U;
err = fc*fc;
if(fc > 0){
b = c;
}
else{
a = c;
}
printf("%lf\n",c);
fprintf(fp, "%lf %lf %lf\n", c,2.0*b_pi/(P+1)*j,fc);
}
}
fclose(fp);
fclose(fp_1);
fclose(fp_2);
fclose(fp_3);
fclose(fp_4);
return 0;
}
double f(double x, int j){
double kai[N][N] = {0.0};
double kai_2[N][N][P + 1][M] = {0.0};
double kai_1[N][N][P + 1][M][M] = {0.0};
double kai_1_dw[N][N][P + 1][M][M] = {0.0}, kai_1_up[N][N][P + 1][M][M] = {0.0};
double kai_3[N][N][P + 1] = {0.0};
double B[N] = {0.0};
double Omega_up[P+1][M][M] = {0.0}, Omega_dw[P+1][M][M] = {0.0};
double U_up_plus[N][P+1][M][M] = {0.0}, U_up_minus[N][P+1][M][M] = {0.0};
double U_dw_plus[N][P+1][M][M] = {0.0}, U_dw_minus[N][P+1][M][M] = {0.0};
for(int v=0;v<=P;v++){ //wavenumber
for (int t=0;t<M;t++){ //valence
for(int s=M;s<N;s++){ //conductive
Omega_up[v][t][s]=x-Energy_dw_1[v][s]+Energy_up_1[(v+j)%(P+1)][t];
Omega_dw[v][t][s]=x+Energy_up_1[v][s]-Energy_dw_1[(v+j)%(P+1)][t];
}
}
}
for(int k=0;k<N;k++){
for(int h=0;h<N;h++){
for(int v=0;v<=P;v++){ //wavenumber
for (int t=0;t<M;t++){ //valence
for(int s=M;s<N;s++){ //conductive
U_up_plus[k][v][t][s]=conj(u_up[v][t][k])*u_dw[(v+j)%(P+1)][s][k];
U_up_minus[h][v][t][s]=conj(u_dw[(v+j)%(P+1)][s][h])*u_up[v][t][h];
U_dw_plus[k][v][t][s]=conj(u_dw[v][t][k])*u_up[(v+j)%(P+1)][s][k];
U_dw_minus[h][v][t][s]=conj(u_up[(v+j)%(P+1)][s][h])*u_dw[v][t][h];
}
}
}
}
}
for(int k=0;k<N;k++){
for(int h=0;h<N;h++){
for(int v=0;v<=P;v++){ //wavenumber
for (int t=0;t<M;t++){ //valence
for(int s=M;s<N;s++){ //conductive
kai_1_up[k][h][v][t][s]=U_up_plus[k][v][t][s]*U_up_minus[h][v][t][s]/Omega_up[v][t][s];
kai_1_dw[k][h][v][t][s]=U_dw_plus[k][v][t][s]*U_dw_minus[h][v][t][s]/Omega_dw[v][t][s];
kai_1[k][h][v][t][s]=-kai_1_up[k][h][v][t][s]+kai_1_dw[k][h][v][t][s];
kai_2[k][h][v][t]+=kai_1[k][h][v][t][s];
// printf("%2f\n",kai_1[k][h][v][t][s]);
}
kai_3[k][h][v]+=kai_2[k][h][v][t];
// printf("%2f \n",kai_2[k][h][v][t]);
}
kai[k][h]+=kai_3[k][h][v];
// printf("%2f \n",kai_3[k][h][v]);
}
// printf("%2f \n",kai[k][h]);
}
}
//感受率の対角化
// 複素行列(エルミート)の対角化 (入力行列の配列は対角化後にユニタリ行列になる)
char jobz = 'V' ;// 固有ベクトルを計算する
char uplo = 'U' ;// Aに上三角行列を格納
int n = N ;// 対角化する正方行列のサイズ
double _Complex A[N*N] ;// 対角化する行列。対角化後は固有ベクトルが並ぶ
for(int k=0;k<N;k++){
for(int h=0;h<N;h++){
A[N*k+h]=kai[k][h];
}
}
// fprintf(fp_1, "%2f %2f \n",A[N*k+h],kai[i][j][k][h]);
// printf("%2f %2f\n",A[N*h+k],kai[h][k]);
double w[N] ;// 実固有値が小さい順に入る
int lda = N ;// 対角化する正方行列のサイズ
double _Complex work[6*N] ;// 対角化する際に使用するメモリ
int lwork = 6*N ;// workの次元
double rwork[3*N-2] ;// 3*SIZE-2で固定
int info ;// 成功すれば0、失敗すれば0以外を返す
zheev_( &jobz, &uplo, &n, A, &lda, w, work, &lwork, rwork, &info );
for(int m=0;m<N;m++){
B[m]=w[m];
// printf("%2f\n",B[m]);
}
double kai_acc=0.0;
kai_acc=B[N-1];
return kai_acc;
}
printf やらデバッグやらはいろいろしましたが全く原因がわかりませんでした。
正直根本的なプログラム知識がないので、触ってはいけないメモリ領域に入っているのかもしれませんが、ご容赦ください。
環境についてはよくわかりませんが、
Ubuntu 20.04.5 LTS
でコンパイルには icc -qmkl -mcmodel=medium -shared-intel
を用いています。
Segmentation fault
いわゆるアドレス不正です。
ポインタの操作誤りや、配列外参照など。
デバッグモードでコンパイルすると、もう少し原因が絞り込める可能性もあります。
あと、吐かれたコアダンプを読めば、例外発生箇所や、その時のメモリやレジスタの内容が確認できます(それ相当のスキルが必要)。
あくまで一般論です。
@kisara11235
Questionerアドレス不正という言葉を初めて聞きました。
これについては勉強していくのですが、具体的にどのようにすればこの問題を回避できるのですか?原因が分からないです。
アドレス不正
今の時代「xxアドレス」という用語が沢山あるので正しい表現ではなかったですね。
プログラムが実行される中で、参照して良いメモリ領域や、書き込んで良いメモリ領域があって、メモリ領域の「どこ」を示すのが「アドレス」です。機械語だと「番地」とも言いますが、C言語だと「ポインタ」です。
自分は、ご提示されたプログラムを実行したり、デバッグしている分けではありませんので、今の段階で「どこに原因があって、どう修正するか」などをお答えできません。これを調べて修正していく作業が正しく「デバッグ」です。
前回の回答に書いた通り「一般論」をお答えしただけで、ご期待に添えずすみません。
これも、一般論ですが、デバッグ用にprintf();fflush();
を各所に入れて実行することで、最後に出力したprintfと出力され無かったprintfの、その間のどこかを少しずつ、間を狭めていって、落ちる箇所を特定することもよく使う技です。
もう一つ、一般論ですが、「プログラムのソースコードを何度確認しても問題が見つからないのに、実行すると落ちる。原因がわからない」ということもよくある話で、「落ちた」のが現実(事実)なので、「問題がない」と考えた人間にミスがあります。人間は一度「正しい」と思い込むと、「いや、それは違う」という気付きは、自らでは難しいので、実際にプログラムを実行させて「事実」を突き詰めて行く繰り返しが大事になります。
追伸;
gdbでコアダンプを入力できた気がするのですが、すぐに思い出せません。
落ちる:コアダンプを吐いて停止したという意味です。
printf();fflush();
:バッファリングされた情報が出力されな場合があるので、デバッグ時はfflush必須だと思います。
@kisara11235
Questionerアドバイスありがとうございます。自分の勉強不足だと思います。
もう少しいろいろとあたってみます。
調べたところスタック領域にメモリを入れすぎているというような感じでしょうか。
調べたところスタック領域にメモリを入れすぎているというような感じでしょうか。
その可能性もあります。関数f
のローカル変数ですか?
関数f
を呼びだす側の呼び出す直前と、関数f
の先頭にprintf
を入れてみれば分かりますね。
「呼び出したが、呼ばれてない」ならそこの可能性があるということです。
もし、それが原因であれば、
関数f
が再帰関数でなければ、static宣言だけで回避できるmalloc/mfree
を使って自分でメモリ割り当て解放が必要再帰関数とは、関数内で自分自身をさらに呼び出す関数を指します。つまり、関数f内でf()しているかどうか。見た感じ再帰では無いかな。
static宣言
関数f内のローカル変数の多次元変数のところだけ、static
と書く。
double f(double x, int j){
static double kai[N][N] = {0.0};
static double kai_2[N][N][P + 1][M] = {0.0};
static double kai_1[N][N][P + 1][M][M] = {0.0};
static double kai_1_dw[N][N][P + 1][M][M] = {0.0}, kai_1_up[N][N][P + 1][M][M] = {0.0};
static double kai_3[N][N][P + 1] = {0.0};
static double B[N] = {0.0};
static double Omega_up[P+1][M][M] = {0.0}, Omega_dw[P+1][M][M] = {0.0};
static double U_up_plus[N][P+1][M][M] = {0.0}, U_up_minus[N][P+1][M][M] = {0.0};
static double U_dw_plus[N][P+1][M][M] = {0.0}, U_dw_minus[N][P+1][M][M] = {0.0};
再帰関数でなければ、これ先にやってみるのが早いかも。です。
@kisara11235
Questionerおっしゃるとおり、再帰関数ではなく単純にfのローカル変数による問題であるように思えます。
下記のstatic宣言を行ったところコアダンプすることなくプログラムが起動しました。printf,fprintfもうまく動いておりました。
しかし、出てくるデータがかなりぐちゃぐちゃなものになっていました。↓
0.900000 3.018393 8568891.896867
1.080000 3.018393 8620983.916660
1.260000 3.018393 8673293.349268
1.440000 3.018393 8725820.872943
これは前に変数の初期化を忘れていたことで生じた問題でした。(どこが初期化し忘れているのか正直わかりませんが)
とりあえずコアダンプの問題は解決できました。ありがとうございます。
一つ書き忘れました。
kai[N][N] = {0.0};
この書き方は、多次元配列の全体のクリアにはなりません。
さらに、static
にすることで使い回しされるので、関数の先頭で、それぞれの次元数のループにて初期化が必要です。(もっと効率の良い初期化方法は、この後で書きます)
これをしないと、落ちなくなったとしても、今度は結果が期待した値にならないでしょうから。
すれ違いました。
@kisara11235
Questionerループに入れて初期化を行ったところ計算が正しくなされました。
自分の勉強不足があり、非常に初歩的なミスでした。
また、ここまでアドバイス等付き合っていただきありがとうございます。
多次元配列の初期化は、次元数が多くなるほどループ回数が激増し初期化の時間が増大します。次元数に依存せず、1行で書く方法をお教えします。
memset
関数を使います。アドレスとそこからの長さ(サイズ分)を一気に初期化する関数です。
double kai_1_dw[N][N][P + 1][M][M];
を例に説明します。
サイズは次元数の掛け算になります。kai_1_dwの場合は、サイズはsizeof(double)*N*N*(P+1)*M*M
です。
配列はポインタそのものですから、memset(kai_1_dw, 0, sizeof(double)*N*N*(P+1)*M*M);
と書きます。
配列と次の配列がメモリ上で連続している保証は無いので、それぞれの配列ごとに書く必要があります。
当然ですが、メモリ空間を超えるようなサイズは指定できません(定義もできないはず)。
kai_1_upとmainのなかのjのループを切るとコアダンプしない
「ループを切る」という表現が自分にはまったく理解できません。切る? cut?
もう少し、丁寧に説明することはできますか?
「コアダンプしない」方法があって、出力の結果も期待通りなら、「切って」良いのでは?とも思います。
@kisara11235
Questioner切るというのはjをループさせずに特定の値だけ吐き出すようにしたことです。j=10だけみたいな感じです。
分かりづらい表現を用いてしまい申し訳ございません。
短絡的にコアダンプを回避することがよくはないというのはもっともで、何が原因となったのかを探っているときに見つけた箇所について言及したのみです。
Omega_up[v][t][s],Omega_dw[v][t][s]
for(int s=M;s<N;s++){ //conductive
sがN(4)で回ってる。
double Omega_up[P+1][M][M]
M(2)で宣言してるのインデックスオーバー。
こうしたかった?
Omega_up[v][t][s-M]
Omega_dw[v][t][s-M]
もしくは
for(int s=0;s<(N-M);s++){ //conductive
こう?