/* flow12.c Copyright (C) 2003 Robert Geist, Karl Rasche, Robert Schalkoff, James Westall This uses the modifications, described in lb6.tex, to the local equilibrium distribution. Pressure is now independent of velocity (u^2) and under user control as p = (3v^2 rho)/(6+R), where R is a ratio of rest to non-rest density. The interaction between rest and non-rest densities is controlled by the matrix term 'b' in the last row/col. */ #include #include #include #define RMG_GRAPHICS 1 #define DIRECTIONS 7 #define WIDTH 1024 #define HEIGHT 65 #define INJECTION_RATE 0.333333333 /* * In lb6.tex notation, rho = 6A + Ahat. The ratio, R = Ahat/A, * controls pressure. Write A = rho*A_wt and Ahat = rho*Ahat_wt, * and then R = (Ahat_wt/A_wt) and Ahat_wt = 1 - 6*A_wt. Note * that A_wt is constrained to (0,1/6). * * Danger Wil Robinson! If A_wt is too high, pressure will be too high, * and the system will lose stability very quickly. Diddle with this * at your own risk. ... 1.0/17.92 works, 1.0/17.91 is too large. */ #define A_wt (1.0/18.0) #define Ahat_wt (1.0 - 6.0*A_wt) #define R (Ahat_wt/A_wt) #define REST_ALLOC (Ahat_wt/(6.0*A_wt)) #define NOMINAL_VISCOSITY 0.1 #define P_LAW_EXP -0.5 #define SQRT_3 1.732050807 #define lambda 1.0 /* lattice spacing */ #define tau (1.0/2.0) /* time step ... affects viscosity */ #define v (lambda/tau) /* unit velocity ... ditto */ #define FINAL_TIME (20.0*((double)(WIDTH))*tau) struct ivector { int x; /* col */ int y; /* row */ }; /* mapping to rectangular coordinates is even/odd row dependent <---- etc --- * --- x --- 0 <---- row 3 \ / \ / \ / \ \ / \ / \ / \ * --- x --- 0 --- x <---- row 2 / \ / \ / \ / / \ / \ / \ / --- * --- x --- 0 <---- row 1 \ / \ / \ / \ \ / \ / \ / \ * --- x --- 0 --- x <---- row 0 * => 0'th column x => odd column 0 => even column plan here is to use ci[k][row%2]; the basic scheme is that the rows are straight and the columns wiggle, since odd rows are offset 1/2 to the right of even rows, so: dir 0 is same row, +1 col., either parity dir 1 is up one row, same col. if even row, next col. if odd row dir 2 is up one row, prev. col. if even row, same col. if odd row dir 3 is same row, -1 col., either parity dir 4 is down one row, prev. col. if even row, same col. if odd row dir 5 is down one row, same col. if even row, next col. if odd row dir 6 is nowhere ... for rest density */ struct ivector ci[DIRECTIONS][2] = { 1,0,1,0, 0,1,1,1, -1,1,0,1, -1,0,-1,0, -1,-1,0,-1, 0,-1,1,-1, 0,0,0,0 }; struct vector { double x; /* col */ double y; /* row */ } vf[DIRECTIONS]; struct vector c[] = { 1.0, 0.0, 0.5, 0.5*SQRT_3, -0.5, 0.5*SQRT_3, -1.0, 0.0, -0.5, -0.5*SQRT_3, 0.5, -0.5*SQRT_3, 0.0, 0.0 }; /* coord here will just be output to viz routine */ struct lattice_point { struct vector coord; double f[DIRECTIONS]; double f0[DIRECTIONS]; struct vector u; double rho; double edot; /* second invariant of rate-of-deformation tensor */ int class; /* inside, boundary, or outside */ double *dist[DIRECTIONS]; /* where to send flow */ int edot_class; /* marker for edot boundary processing */ } lattice[HEIGHT][WIDTH]; struct lattice_point lost_source, lost_sink; #define NULLDIR (double *)(0) double omega[DIRECTIONS][DIRECTIONS]; void mark_geometry(); void pp_geometry(); void init_lattice(); void load_collision_matrix(double viscosity); void update_rho_and_u(); void update_f(double t); void update_f0(); void update_edot(); double genrand(); void dump(); main() { double t; t = 0.0; mark_geometry(); pp_geometry(); load_collision_matrix(NOMINAL_VISCOSITY); init_lattice(); while(t1000000.0){ fprintf(stderr,"lost stability\n"); exit(1); } } double dot(struct vector u, struct vector vv) { return(u.x*vv.x + u.y*vv.y); } void update_f0() { /* * new expression ... see lb6.tex, but it matches previous if A_wt = 1/12.0 */ struct vector u; double rho; int i,j,k; for(i=0;icoord.x,lpp1->coord.y, lpp1->u.x,lpp1->u.y,lpp1->rho); printf("%f %f %f %f %f\n",lpp2->coord.x,lpp2->coord.y, lpp2->u.x,lpp2->u.y,lpp2->rho); } } } void load_collision_matrix(double viscosity) { double a_0, a_60, a_120, a_180, b, c, e1; /* The row/col 6 entries are b b b b b b c. The eigenvectors for 0 are now: (1,1,1,1,1,1,1), (c_0x,c_1x,c_2x,c_3x,c_4x,c_5x,0), and (c_0y,c_1y,c_2y,c_3y,c_4y,c_5y,0), with associated constraints: a_0 + 2a_60 + 2a_120 + a_180 + b = 0 a_0 + a_60 - a_120 - a_180 = 0 6b + c = 0 There are three non-zero eigenvalues: e_1 = 6a_0 + 6a_60 + 2b; eigenvectors (1,0,-1,1,0,-1,0) and (1,-2,1,1,-2,1,0) e_2 = -6a_0 - 12a_60 -3b; eigenvector (1,-1,1,-1,1,-1,0) e_3 = -7b; eigenvector (1,1,1,1,1,1,-6) The eigenvalue e_1 is determined by the target viscosity: viscosity = ((-tau v^2)/4)(1/e_1 + 1/2); The others we force to be -1 (so I+Omega iterations converge quickly). This gives: a_0 = (1/3)e_1 - 4/21; a_60 = -(1/6)e_1 + 1/7; a_120 = -b - 2a_0 - 3a_60; a_180 = b + 3a_0 + 4a_60; b = 1/7 c = -6/7; */ e1 = -2.0*tau*v*v/(8.0*viscosity + tau*v*v); a_0 = e1/3.0 - 4.0/21.0; a_60 = -e1/6.0 + 1.0/7.0; b = 1.0/7.0; a_120 = -b - 2.0*a_0 - 3.0*a_60; a_180 = b + 3.0*a_0 + 4.0*a_60; c = -6.0/7.0; /* we need to do this often, so unroll the loop */ omega[0][0] = a_0; omega[0][1] = a_60; omega[0][2] = a_120; omega[0][3] = a_180; omega[0][4] = a_120; omega[0][5] = a_60; omega[0][6] = b; omega[1][0] = a_60; omega[1][1] = a_0; omega[1][2] = a_60; omega[1][3] = a_120; omega[1][4] = a_180; omega[1][5] = a_120; omega[1][6] = b; omega[2][0] = a_120; omega[2][1] = a_60; omega[2][2] = a_0; omega[2][3] = a_60; omega[2][4] = a_120; omega[2][5] = a_180; omega[2][6] = b; omega[3][0] = a_180; omega[3][1] = a_120; omega[3][2] = a_60; omega[3][3] = a_0; omega[3][4] = a_60; omega[3][5] = a_120; omega[3][6] = b; omega[4][0] = a_120; omega[4][1] = a_180; omega[4][2] = a_120; omega[4][3] = a_60; omega[4][4] = a_0; omega[4][5] = a_60; omega[4][6] = b; omega[5][0] = a_60; omega[5][1] = a_120; omega[5][2] = a_180; omega[5][3] = a_120; omega[5][4] = a_60; omega[5][5] = a_0; omega[5][6] = b; omega[6][0] = b; omega[6][1] = b; omega[6][2] = b; omega[6][3] = b; omega[6][4] = b; omega[6][5] = b; omega[6][6] = c; } /* use current u() to estimate second invariant of rate-of-deformation tensor; we need est. of partials of u.x and partials of u.y; now, a problem with getting the partial of either with respect to y is that the next lattice value in the y direction with the same x coordinate is 2 rows away ... only IN nodes and SOURCE nodes need edots; for SOURCE nodes (subset of column 0, by def.), we copy from column 1 after calculating those; the only remaining problem will be the nodes where one of the next lattice values in y direction is outside the rectangle or of class OUT ... note bug: if (i,j) is missing lower y, i.e. (i-2,j), we assume (i+2,j) and (i+4,j) are ok and interpolate ... so channels must be wide enough */ #define EPS 0.0001 #define EDOT_OK 0 #define MISSING_LOW 1 #define MISSING_HIGH 2 void update_edot() { double edot_xx, edot_xy, edot_yx, edot_yy; double p_xy, p_yx; double slope; int i,j; for(i=0;iHEIGHT-1){ lattice[i][j].edot_class = MISSING_HIGH; continue; } if(lattice[i+2][j].class==OUT){ lattice[i][j].edot_class = MISSING_HIGH; continue; } lattice[i][j].edot_class=EDOT_OK; edot_xx = (lattice[i][j+1].u.x - lattice[i][j-1].u.x)/ (lattice[i][j+1].coord.x - lattice[i][j-1].coord.x); edot_yy = (lattice[i+2][j].u.y - lattice[i-2][j].u.y)/ (lattice[i+2][j].coord.y - lattice[i-2][j].coord.y); p_yx = (lattice[i][j+1].u.y - lattice[i][j-1].u.y)/ (lattice[i][j+1].coord.x - lattice[i][j-1].coord.x); p_xy = (lattice[i+2][j].u.x - lattice[i-2][j].u.x)/ (lattice[i+2][j].coord.y - lattice[i-2][j].coord.y); edot_xy = edot_yx = (p_xy + p_yx)/2.0; lattice[i][j].edot = sqrt(edot_xx*edot_xx + 2.0*edot_xy*edot_yx +edot_yy*edot_yy); if(lattice[i][j].edot