#include "navier-stokes.h" #include "io.h" #include #include #include // compute solution as a function of time int uk( int K1, int K2, int N1, int N2, unsigned int nsteps, double nu, double delta, double L, _Complex double* u0, _Complex double* g, bool irreversible, unsigned int print_freq, unsigned int starting_time, unsigned int nthreads, FILE* savefile ){ _Complex double* u; _Complex double* tmp1; _Complex double* tmp2; _Complex double* tmp3; unsigned int t; fft_vect fft1; fft_vect fft2; fft_vect ifft; int kx,ky; ns_init_tmps(&u, &tmp1, &tmp2, &tmp3, &fft1, &fft2, &ifft, K1, K2, N1, N2, nthreads); // copy initial condition copy_u(u, u0, K1, K2); // print column headers printf("# 1:i 2:t "); t=3; for(kx=-K1;kx<=K1;kx++){ for (ky=-K2;ky<=K2;ky++){ printf(" %6d:(%4d,%4d)r ",t,kx,ky); t++; printf(" %6d:(%4d,%4d)i ",t,kx,ky); t++; } } // iterate for(t=starting_time;t print_freq-short_len || t % print_freq == 0)){ avg_print_short_e+=energy/short_len; avg_print_short_a+=alpha/short_len; avg_print_short_en+=enstrophy/short_len; } if(t % print_freq ==0 && t>starting_time){ // compute averages if (t > running_avg_window + starting_time) { avg_e=save_print_short_e[nr_print_in_window]*((double)short_len/running_avg_window); avg_a=save_print_short_a[nr_print_in_window]*((double)short_len/running_avg_window); avg_en=save_print_short_en[nr_print_in_window]*((double)short_len/running_avg_window); for(i=0;i running_avg_window #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" avg_e+=save_print_e[i]*((double)print_freq/running_avg_window); avg_a+=save_print_a[i]*((double)print_freq/running_avg_window); avg_en+=save_print_en[i]*((double)print_freq/running_avg_window); #pragma GCC diagnostic pop } } // memorize averages for future use // need to keep track of case ==0, otherwise nr_print_in_window-1 is not an unsigned int if(nr_print_in_window>0){ for(i=0;irunning_avg_window + starting_time && t%print_freq==0){ fprintf(stderr,"%d % .8e % .8e % .8e % .8e % .8e % .8e % .8e\n",t,t*delta, avg_a, avg_e, avg_en, alpha, energy, enstrophy); printf("%8d % .15e % .15e % .15e % .15e % .15e % .15e % .15e\n",t,t*delta, avg_a, avg_e, avg_en, alpha, energy, enstrophy); } // catch abort signal if (g_abort){ // print u to stderr if no savefile if (savefile==NULL){ savefile=stderr; } fprintf(savefile,"# Interrupted computation. Resume with\n"); // command to resume fprintf(savefile,"#! "); fprintf(savefile, cmd_string); // params // allocate buffer for params char* params=calloc(sizeof(char), strlen(params_string)); strcpy(params, params_string); remove_entry(params, "starting_time"); remove_entry(params, "init"); remove_entry(params, "nsteps"); fprintf(savefile," -p \"%s;starting_time=%u;nsteps=%u;init=file:%s\"", params, t+1, nsteps-t-1, savefile_string); free(params); fprintf(savefile," energy\n"); break; } } // save final entry to savefile write_u(u, K1, K2, savefile); if(running_avg_window!=0){ free(save_print_e); free(save_print_a); free(save_print_en); free(save_print_short_e); free(save_print_short_a); free(save_print_short_en); } ns_free_tmps(u, tmp1, tmp2, tmp3, fft1, fft2, ifft); return(0); } // compute solution as a function of time, but do not print anything (useful for debugging) int quiet( int K1, int K2, int N1, int N2, unsigned int nsteps, double nu, double delta, double L, _Complex double* u0, _Complex double* g, bool irreversible, unsigned int nthreads, FILE* savefile ){ _Complex double* u; _Complex double* tmp1; _Complex double* tmp2; _Complex double* tmp3; unsigned int t; fft_vect fft1; fft_vect fft2; fft_vect ifft; ns_init_tmps(&u, &tmp1, &tmp2, &tmp3, &fft1, &fft2, &ifft, K1, K2, N1, N2, nthreads); // copy initial condition copy_u(u, u0, K1, K2); // iterate for(t=0;tfft=fftw_malloc(sizeof(fftw_complex)*N1*N2); fft1->fft_plan=fftw_plan_dft_2d(N1,N2, fft1->fft, fft1->fft, FFTW_FORWARD, FFTW_MEASURE); fft2->fft=fftw_malloc(sizeof(fftw_complex)*N1*N2); fft2->fft_plan=fftw_plan_dft_2d(N1,N2, fft2->fft, fft2->fft, FFTW_FORWARD, FFTW_MEASURE); ifft->fft=fftw_malloc(sizeof(fftw_complex)*N1*N2); ifft->fft_plan=fftw_plan_dft_2d(N1,N2, ifft->fft, ifft->fft, FFTW_BACKWARD, FFTW_MEASURE); return 0; } // release vectors int ns_free_tmps( _Complex double* u, _Complex double* tmp1, _Complex double* tmp2, _Complex double* tmp3, fft_vect fft1, fft_vect fft2, fft_vect ifft ){ // free memory fftw_destroy_plan(fft1.fft_plan); fftw_destroy_plan(fft2.fft_plan); fftw_destroy_plan(ifft.fft_plan); fftw_free(fft1.fft); fftw_free(fft2.fft); fftw_free(ifft.fft); fftw_cleanup_threads(); free(tmp3); free(tmp2); free(tmp1); free(u); return 0; } // copy u0 to u int copy_u( _Complex double* u, _Complex double* u0, int K1, int K2 ){ int i; for(i=0;i<(K1+1)*(2*K2+1);i++){ u[i]=u0[i]; } return 0; } // next time step int ns_step( _Complex double* u, int K1, int K2, int N1, int N2, double nu, double delta, double L, _Complex double* g, fft_vect fft1, fft_vect fft2, fft_vect ifft, _Complex double* tmp1, _Complex double* tmp2, _Complex double* tmp3, bool irreversible ){ int kx,ky; // k1 ns_rhs(tmp1, u, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible); // add to output for(kx=0;kx<=K1;kx++){ for(ky=-K2;ky<=K2;ky++){ tmp3[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+delta/6*tmp1[klookup_sym(kx,ky,K2)]; } } // u+h*k1/2 for(kx=0;kx<=K1;kx++){ for(ky=-K2;ky<=K2;ky++){ tmp2[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+delta/2*tmp1[klookup_sym(kx,ky,K2)]; } } // k2 ns_rhs(tmp1, tmp2, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible); // add to output for(kx=0;kx<=K1;kx++){ for(ky=-K2;ky<=K2;ky++){ tmp3[klookup_sym(kx,ky,K2)]+=delta/3*tmp1[klookup_sym(kx,ky,K2)]; } } // u+h*k2/2 for(kx=0;kx<=K1;kx++){ for(ky=-K2;ky<=K2;ky++){ tmp2[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+delta/2*tmp1[klookup_sym(kx,ky,K2)]; } } // k3 ns_rhs(tmp1, tmp2, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible); // add to output for(kx=0;kx<=K1;kx++){ for(ky=-K2;ky<=K2;ky++){ tmp3[klookup_sym(kx,ky,K2)]+=delta/3*tmp1[klookup_sym(kx,ky,K2)]; } } // u+h*k3 for(kx=0;kx<=K1;kx++){ for(ky=-K2;ky<=K2;ky++){ tmp2[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+delta*tmp1[klookup_sym(kx,ky,K2)]; } } // k4 ns_rhs(tmp1, tmp2, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible); // add to output for(kx=0;kx<=K1;kx++){ for(ky=-K2;ky<=K2;ky++){ u[klookup_sym(kx,ky,K2)]=tmp3[klookup_sym(kx,ky,K2)]+delta/6*tmp1[klookup_sym(kx,ky,K2)]; } } return(0); } // right side of Irreversible/Reversible Navier-Stokes equation int ns_rhs( _Complex double* out, _Complex double* u, int K1, int K2, int N1, int N2, double nu, double L, _Complex double* g, fft_vect fft1, fft_vect fft2, fft_vect ifft, bool irreversible ){ int kx,ky; int i; double alpha; // compute convolution term ns_T(u,K1,K2,N1,N2,fft1,fft2,ifft); if (irreversible) { alpha=nu; } else { alpha=compute_alpha(u,K1,K2,N1,N2,g,L,ifft.fft); } /* // compare convolution term (store result in fft1.fft) ns_T_nofft(fft1.fft, u, K1, K2, N1, N2); double cmp=0.; for(i=0;i=4*K1+1 and N2>=4*K2+1) if (N1<4*K1+1 || N2<4*K2+1){ fprintf(stderr,"error: N1 and N2 need t be >= 4*K1+1 and 4*K2+1 respectively\n"); return(-1); } for(kx=-2*K1;kx<=2*K1;kx++){ for(ky=-2*K2;ky<=2*K2;ky++){ // init out[klookup(kx,ky,N1,N2)]=0.; for(px=-K1;px<=K1;px++){ for(py=-K2;py<=K2;py++){ qx=kx-px; qy=ky-py; // cutoff in q if(qx>=-K1 && qx<=K1 && qy>=-K2 && qy<=K2 && qx*qx+qy*qy>0 && px*px+py*py>0){ out[klookup(kx,ky,N1,N2)]+=(-qx*py+qy*px)*sqrt(qx*qx+qy*qy)/sqrt(px*px+py*py)*getval_sym(u, px,py,K2)*getval_sym(u, qx,qy,K2); } } } } } // enforce T(u,-k)=T(u,k)^* for(kx=-K1;kx<=K1;kx++){ for(ky=-K2;ky<=K2;ky++){ out[klookup(kx,ky,N1,N2)]=(out[klookup(kx,ky,N1,N2)]+conj(out[klookup(-kx,-ky,N1,N2)]))/2; } } return 0; } // compute alpha double compute_alpha( _Complex double* u, int K1, int K2, int N1, int N2, _Complex double* g, double L, _Complex double* T ){ _Complex double num=0; double denom=0; int kx,ky; num=0.; denom=0.; for(kx=-K1;kx<=K1;kx++){ for(ky=-K2;ky<=K2;ky++){ num+=(L*L/4/M_PI/M_PI*(kx*kx+ky*ky)*getval_sym(g, kx,ky,K2)+sqrt(kx*kx+ky*ky)*T[klookup(kx,ky,N1,N2)])*conj(getval_sym(u, kx,ky,K2)); denom+=__real__ (kx*kx+ky*ky)*(kx*kx+ky*ky)*getval_sym(u, kx,ky,K2)*conj(getval_sym(u, kx,ky,K2)); } } return __real__ num/denom; } /* // compute alpha double compute_alpha( _Complex double* u, int K1, int K2, _Complex double* g ){ _Complex double num=0; _Complex double denom=0; int kx,ky; for(kx=-K1;kx<=K1;kx++){ for(ky=-K2;ky<=K2;ky++){ denom+=(kx*kx+ky*ky)*(kx*kx+ky*ky)*getval_sym(u, kx,ky,K2)*conj(getval_sym(u, kx,ky,K2))*(1+(ky!=0?kx*kx/ky/ky:0)); num+=(kx*kx+ky*ky)*getval_sym(u, kx,ky,K2)*conj(g[getval(kx,ky,K1,K2)])*(1+(ky!=0?kx*kx/ky/ky:0)); } } return __real__ (num/denom); }*/ // compute energy double compute_energy( _Complex double* u, int K1, int K2 ){ int kx,ky; double out=0.; for(kx=-K1;kx<=K1;kx++){ for (ky=-K2;ky<=K2;ky++){ out+=__real__ (getval_sym(u, kx,ky,K2)*conj(getval_sym(u, kx,ky,K2))); } } return out; } // compute enstrophy double compute_enstrophy( _Complex double* u, int K1, int K2, double L ){ int kx,ky; double out=0.; for(kx=-K1;kx<=K1;kx++){ for (ky=-K2;ky<=K2;ky++){ out+=__real__ (4*M_PI*M_PI/L/L*(kx*kx+ky*ky)*getval_sym(u, kx,ky,K2)*conj(getval_sym(u, kx,ky,K2))); } } return out; } // get index for kx,ky in array of size S int klookup( int kx, int ky, int S1, int S2 ){ return (kx>=0 ? kx : S1+kx)*S2 + (ky>=0 ? ky : S2+ky); } // get index for kx,ky in array of size K1,K2 in which only the terms with kx>=0 are stored int klookup_sym( int kx, int ky, int K2 ){ if (kx<0) { fprintf(stderr, "bug!: attempting to access a symmetrized vector at kx<0\n Contact Ian at ian.jauslin@rutgers.edu!\n"); exit(-1); } return kx*(2*K2+1) + (ky>=0 ? ky : (2*K2+1)+ky); } // get u_{kx,ky} from a vector u in which only the values for kx>=0 are stored, assuming u_{-k}=u_k^* _Complex double getval_sym( _Complex double* u, int kx, int ky, int K2 ){ return (kx>=0 ? u[klookup_sym(kx,ky,K2)] : conj(u[klookup_sym(-kx,-ky,K2)])); }