// Dieses Programm integriert die Diffusionsgleichung mittels // ADI(Alternativ Direction Implicit)-Methode und gibt die Ergebnisse in // Form von PNG-Grafiken aus, aus denen sich z.B. mit Programmen wie // ffmpeg kleine Filme erzeugen lassen. Die Anzahl der ausgegebenen // Bilder läßt sich über die Variable "fps" steuern (siehe unten). Für // die lineare Algebra wird dabei die Bibliothek GLS (Gnu // Scientific-Library) verwendet, weswegen mit "-ldislin -lgsl // -lgslcblas" gelinkt werden muss. #include <math.h> #include <gsl/gsl_linalg.h> #include <gsl/gsl_blas.h> // Die folgende Zeile muss evtl. an Eure lokale Dislin-Installation // angepasst werden. #include "/usr/local/dislin/dislin.h" // Eine nützliche Definition zum Runden #define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) // Zeitintervall [t_0,t_0] double t_0 = 0.0; double t_1 = 1.0; // Raumbereich [x_0,x_1]x[y_0,y_1] double x_0 = 0.0; double x_1 = 1.0; double y_0 = 0.0; double y_1 = 1.0; // Raum- und Zeitschritt. Aus diesen Größen wird weiter unten die // Anzahl der Schritte berechnet. double dx = 0.02; double dy = 0.02; double dt = 0.001; // Diffusionskonstante double Diff = 0.1; // fps ist die Anzahl der Bilder, die pro simulierter Sekunde ausgegeben werden sollen int fps = 200; // Die Funktionen startwert und startableitung werden zu Beginn des // Programms verwendet, um die Anfangsbedingung festzulegen. double startwert(int i, int j) { double y = i * dx; double x = j * dy; return exp(-20.0*pow(x-0.5,2)-20.0*pow(y-0.5,2)); } int main () { // Die Anzahl der räumlichen und zeitlichen Iterationsschritte wird // aus der Größe der entsprechenden Intervalle und den Schrittweiten // berechnet. int x_steps = round( (x_1 - x_0) / dx); int y_steps = round( (y_1 - y_0) / dy); int t_steps = round( (t_1 - t_0) / dt); printf("\nx_steps=%d y_steps=%d t_steps=%d\n",x_steps, y_steps, t_steps); // Die Variablen fname und titel werden für die Ausgabe mit Dislin // benötigt (siehe unten). char fname[80]; char titel[80]; // Je nach Wahl der globalen Variablen fps und dt wird die Zahl der // zeitlichen Iterationen berechnet, die zwischen zwei Ausgaben // durchgeführt werden sollen. int steps_pro_frame = round(1.0 / fps / dt); if ( !steps_pro_frame ) { steps_pro_frame = 1; } // Die Zeitintegration erfolgt durch Lösung eines linearen // Gleichungssystems. Im Folgenden werden die dafür benötigten Vektoren und // eine Matrix definiert. int vektorlaenge = (x_steps + 1)*(y_steps + 1); gsl_vector *u = gsl_vector_alloc(vektorlaenge); gsl_vector *u_halb = gsl_vector_alloc(vektorlaenge); gsl_vector *u_temp = gsl_vector_alloc(vektorlaenge); gsl_vector *gamma = gsl_vector_alloc(vektorlaenge); gsl_matrix *A = gsl_matrix_alloc(vektorlaenge, vektorlaenge); gsl_matrix *B = gsl_matrix_alloc(vektorlaenge, vektorlaenge); gsl_matrix *C = gsl_matrix_alloc(vektorlaenge, vektorlaenge); gsl_matrix *D = gsl_matrix_alloc(vektorlaenge, vektorlaenge); // Die folgenden drei Größen werden gsl-intern für die LU-Zerlegung // benötigt. gsl_permutation *p_A = gsl_permutation_alloc(vektorlaenge); gsl_permutation *p_C = gsl_permutation_alloc(vektorlaenge); int sign = 0; int i = 0; // i ist der x-Index int j = 0; // j ist der y-Index int k = 0; // k ist der Zeitindex // Division und Potenzierung ist relativ teuer, sollte deshalb nicht // öfter als nötig berechnet werden! double alpha = Diff * dt / 2.0 / dx / dx; double beta = Diff * dt / 2.0 / dy / dy; // Das Feld wird entsprechend der Anfangsbedingung mit startwert() // initialisiert. for (i = 0; i < vektorlaenge; i++) { gsl_vector_set(u, i, startwert(i / (x_steps+1), i % (x_steps+1))); } // Erstelle die Matrix A for ( i=0; i<vektorlaenge; i++ ) { gsl_matrix_set(A, i, i, 1.0+2.0*alpha); if ( (i % (x_steps + 1) != 0) && (i % (x_steps + 1) != x_steps) ) { gsl_matrix_set(A, i, i-1, -alpha); gsl_matrix_set(A, i, i+1, -alpha); } else if ( i % (x_steps + 1) == 0 ) { gsl_matrix_set(A, i, i+1, -2.0*alpha); } else { gsl_matrix_set(A, i, i-1, -2.0*alpha); } } // Erstelle die Matrix B for ( i=0; i<vektorlaenge; i++ ) { gsl_matrix_set(B, i, i, 1.0-2.0*beta); if ( (i > x_steps ) && (i < vektorlaenge - (x_steps + 1)) ) { gsl_matrix_set(B, i, i+x_steps+1, beta); gsl_matrix_set(B, i, i-x_steps-1, beta); } else if ( i <= x_steps ) { gsl_matrix_set(B, i, i+x_steps+1, 2.0*beta); } else { gsl_matrix_set(B, i, i-x_steps-1, 2.0*beta); } } // Erstelle die Matrix C for ( i=0; i<vektorlaenge; i++ ) { gsl_matrix_set(C, i, i, 1.0+2.0*beta); if ( (i > x_steps ) && (i < vektorlaenge - (x_steps + 1)) ) { gsl_matrix_set(C, i, i+x_steps+1, -beta); gsl_matrix_set(C, i, i-x_steps-1, -beta); } else if ( i <= x_steps ) { gsl_matrix_set(C, i, i+x_steps+1, -2.0*beta); } else { gsl_matrix_set(C, i, i-x_steps-1, -2.0*beta); } } // Erstelle die Matrix D for ( i=0; i<vektorlaenge; i++ ) { gsl_matrix_set(D, i, i, 1.0-2.0*alpha); if ( (i % (x_steps + 1) != 0) && (i % (x_steps + 1) != x_steps) ) { gsl_matrix_set(D, i, i-1, alpha); gsl_matrix_set(D, i, i+1, alpha); } else if ( i % (x_steps + 1) == 0 ) { gsl_matrix_set(D, i, i+1, 2.0*alpha); } else { gsl_matrix_set(D, i, i-1, 2.0*alpha); } } // Führe zunächst die LU-Zerlegung der Matrizen A und C durch. Dieser // Teil des Programms nimmt die meiste Zeit in Anspruch. printf("\nLU-Zerlegung von A beginnt!\n"); gsl_linalg_LU_decomp(A, p_A, &sign); printf("\nLU-Zerlegung von C beginnt!\n"); gsl_linalg_LU_decomp(C, p_C, &sign); printf("\nLU-Zerlegung beendet!\n"); for ( k = 0; k < t_steps; k++ ) { //---{ Ausgabe mit Dislin }----------------------------------------- if ( k % steps_pro_frame == 0) { scrmod("REVERSE"); metafl("PNG"); filmod("DELETE"); sprintf(fname, "ADI%d.png", round(k / steps_pro_frame) ); setfil(fname); disini(); texmod("ON"); sprintf(titel, "%d Stuetzstellen, $t=%f$",x_steps + 1, t_0 + (k + 1) * dt); titlin(titel, 4); graf3d(x_0, x_1, x_0, (x_1 - x_0)/10.0, y_0, y_1, y_0, (y_1 - y_0)/10.0, -0.2, 1.0, -0.2, 0.2); title(); double masse = 0.0; for (i = 0; i<vektorlaenge; i++) { masse += gsl_vector_get(u, i); } printf("\n Masse: %f",masse); float darst[y_steps + 1][x_steps + 1]; for (i = 0; i<=y_steps; i++) { for (j = 0; j<=x_steps; j++) { darst[i][j] = gsl_vector_get(u, i * (x_steps+1) + j); } } surmat((float *) darst, y_steps + 1, x_steps + 1, 1, 1); disfin(); } //------------------------------------------------------------------ //---{ Der eigentliche Zeitschritt }------------------------------- gsl_blas_dgemv(CblasNoTrans, 1.0, B, u, 0.0, u_temp); gsl_linalg_LU_solve(A, p_A, u_temp, u_halb); gsl_blas_dgemv(CblasNoTrans, 1.0, D, u_halb, 0.0, u_temp); gsl_linalg_LU_solve(C, p_C, u_temp, u); //----------------------------------------------------------------- } // Zuletzt kann der reservierte Speicher wieder freigegeben werden. gsl_vector_free(u); gsl_vector_free(u_halb); gsl_vector_free(u_temp); gsl_matrix_free(A); gsl_matrix_free(B); gsl_matrix_free(C); gsl_matrix_free(D); return 1; }