sandbox/kaitaotang/turb_inlet_imposed.h
Generation of artificial turbulent inlet conditions
See Xie and Castro (2008) for details regarding the synthetic digital filtering method
#include "grid/octree.h"
#include "navier-stokes/centered.h"
#include "two-phase.h"
#include "navier-stokes/conserving.h"
#include <sys/stat.h>
#include <sys/types.h>
int set_inlet_count = 0;
int n_yz, ngrid_yz, Nf_yz;
double stp; double t_pre = 0.;
#define N_max 1600
double Rtilde[3][N_max][N_max];
double psi_turb[3][N_max][N_max]; double psi_turb_old[3][N_max][N_max];
double ui[3][N_max][N_max];
scalar ux[]; scalar uy[]; scalar uz[];
// Take a 2D slice of the flow field at x = xp
void sliceYZ(double ** field, scalar s, double xp, int maxlevel){
int nn = (1 << maxlevel);
double stp = L0/(double)(nn);
for (int i = 0; i < nn; i++) {
double yp = stp*i + Y0 + stp/2.;
for (int j = 0; j < nn; j++) {
double zp = stp*j + Z0 + stp/2.;
Point point = locate (xp, yp, zp);
field[i][j] = point.level >= 0 ? s[] : nodata;
}
}
#if _MPI
MPI_Allreduce (MPI_IN_PLACE, field[0], sq(nn+1), MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
#endif
}
Take a 2D slice of the flow field s at y = yp
void sliceXZ(double ** field, scalar s, double yp, int maxlevel){
int nn = (1 << maxlevel);
double stp = L0/(double)(nn);
for (int i = 0; i < nn; i++) {
double xp = stp*i + X0 + stp/2.;
for (int j = 0; j < nn; j++) {
double zp = stp*j + Z0 + stp/2.;
Point point = locate (xp, yp, zp);
field[i][j] = point.level >= 0 ? s[] : nodata;
}
}
#if _MPI
MPI_Allreduce (MPI_IN_PLACE, field[0], sq(nn+1), MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
#endif
}
Take a 2D slice of the flow field s at z = zp
void sliceXY(double ** field, scalar s, double zp, int maxlevel){
int nn = (1 << maxlevel);
double stp = L0/(double)(nn);
for (int i = 0; i < nn; i++) {
double xp = stp*i + X0 + stp/2.;
for (int j = 0; j < nn; j++) {
double yp = stp*j + Y0 + stp/2.;
Point point = locate (xp, yp, zp);
field[i][j] = point.level >= 0 ? s[] : nodata;
}
}
#if _MPI
MPI_Allreduce (MPI_IN_PLACE, field[0], sq(nn+1), MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
#endif
}
Auxiliary function: normalise a matrix to zero mean and unit variance
void norm_matrix(double mat[3][N_max][N_max], int num_xy) {
double r_ave[3]; double r_sq_ave[3]; double r_var[3];
for (int i=0; i<3; i++) {
r_ave[i] = 0.; r_sq_ave[i] = 0.;
for (int j=0; j<num_xy; j++)
for (int k=0; k<num_xy; k++) {
r_ave[i] += mat[i][j][k];
r_sq_ave[i] += sq(mat[i][j][k]);
}
r_ave[i] /= sq(num_xy); r_sq_ave[i] /= sq(num_xy);
r_var[i] = r_sq_ave[i] - sq(r_ave[i]);
}
for (int i=0; i<3; i++)
for (int j=0; j<num_xy; j++)
for (int k=0; k<num_xy; k++) mat[i][j][k] = (mat[i][j][k] - r_ave[i]) / sqrt(r_var[i]);
}
void set_inlet(int lev, double L_turb, double u_ave, double u_rms) {
double b_yz[N_max];
ngrid_yz = 1 << lev; stp = L0/(double)(ngrid_yz);
// Filter length setup
n_yz = (int)(L_turb/stp); Nf_yz = 3 * n_yz;
if (set_inlet_count > 0) {
for (int i=0; i<3; i++)
for (int j=0; j<ngrid_yz; j++)
for (int k=0; k<ngrid_yz; k++) psi_turb_old[i][j][k] = psi_turb[i][j][k];
}
// Set up random fields and the Gaussian filter
if (pid() == 0) {
srand(time(NULL));
for (int i=0; i<3; i++)
for (int j=0; j<2*Nf_yz+ngrid_yz; j++)
for (int k=0; k<2*Nf_yz+ngrid_yz; k++) Rtilde[i][j][k] = 2*(rand()/(double)RAND_MAX)-1;
}
MPI_Bcast (&(Rtilde[0][0][0]), 3*sq(N_max), MPI_DOUBLE, 0, MPI_COMM_WORLD);
norm_matrix(Rtilde, 2*Nf_yz+ngrid_yz);
double norm_byz = 0.;
for (int i=0; i<2*Nf_yz; i++) {b_yz[i] = exp(-pi*fabs(i-Nf_yz)/2.0/n_yz); norm_byz += sq(b_yz[i]);}
for (int i=0; i<2*Nf_yz; i++) b_yz[i] /= sqrt(norm_byz);
// Apply filtering
for (int i=0; i<3; i++)
for (int j=0; j<ngrid_yz; j++)
for (int k=0; k<ngrid_yz; k++) {
psi_turb[i][j][k] = 0.;
if ((k + j*(ngrid_yz)) % npe() == pid()) {
for (int j_p=0; j_p<2*Nf_yz; j_p++)
for (int k_p=0; k_p<2*Nf_yz; k_p++) psi_turb[i][j][k] += b_yz[j_p]*b_yz[k_p] * Rtilde[i][j+j_p][k+k_p];
}
}
MPI_Allreduce (MPI_IN_PLACE, &(psi_turb[0][0][0]), 3*sq(N_max), MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
norm_matrix(psi_turb, ngrid_yz);
// Time advancing
if (set_inlet_count > 0) {
double T_La = L_turb/u_ave;
for (int i=0; i<3; i++)
for (int j=0; j<ngrid_yz; j++)
for (int k=0; k<ngrid_yz; k++)
psi_turb[i][j][k] = psi_turb_old[i][j][k] * exp(-pi*(t-t_pre)/4.0/T_La) + psi_turb[i][j][k] * pow(1 - exp(-pi*(t-t_pre)/2.0/T_La), 0.5);
norm_matrix(psi_turb, ngrid_yz);
}
// Compute the full velocity profile
for (int i=0; i<3; i++)
for (int j=0; j<ngrid_yz; j++)
for (int k=0; k<ngrid_yz; k++) ui[i][j][k] = psi_turb[i][j][k]*u_rms;
// Inlet flux correction
double u_in_ave[3] = {0., 0., 0.};
for (int i=0; i<3; i++)
for (int j=0; j<ngrid_yz; j++)
for (int k=0; k<ngrid_yz; k++) u_in_ave[i] += ui[i][j][k] / sq(ngrid_yz);
for (int i=0; i<3; i++)
for (int j=0; j<ngrid_yz; j++)
for (int k=0; k<ngrid_yz; k++) {
ui[i][j][k] -= u_in_ave[i];
if (i == 0) ui[i][j][k] += u_ave;
}
// Interpolate to the inlet
foreach() ux[] = uy[] = uz[] = 0.;
foreach_boundary(left) {
int i_tmp = (int)((y - stp/2. - Y0)/stp);
int j_tmp = (int)((z - stp/2. - Z0)/stp);
if (i_tmp < 0) i_tmp = 0; if (i_tmp > ngrid_yz-1) i_tmp = ngrid_yz-1;
if (j_tmp < 0) j_tmp = 0; if (j_tmp > ngrid_yz-1) j_tmp = ngrid_yz-1;
ux[] = ui[0][i_tmp][j_tmp];
uy[] = ui[1][i_tmp][j_tmp];
uz[] = ui[2][i_tmp][j_tmp];
}
u.n[left] = dirichlet(ux[]);
u.t[left] = dirichlet(uy[]);
u.r[left] = dirichlet(uz[]);
boundary ((scalar *){u});
t_pre = t;
set_inlet_count++;
if (set_inlet_count == INT_MAX) set_inlet_count = 1;
}
void match_inlet(int lev, double WLayer, double u_ave_in) {
int nn = (1 << lev);
double stp = L0/(double)(nn);
double ** ux_samp = matrix_new (nn+1, nn+1, sizeof(double));
double ** uy_samp = matrix_new (nn+1, nn+1, sizeof(double));
double ** uz_samp = matrix_new (nn+1, nn+1, sizeof(double));
sliceYZ(ux_samp, u.x, X0+WLayer, lev); sliceYZ(uy_samp, u.y, X0+WLayer, lev); sliceYZ(uz_samp, u.z, X0+WLayer, lev);
double ux_samp_ave = 0.0; int num_samp_c = 0;
foreach(reduction(+:ux_samp_ave) reduction(+:num_samp_c)) {
if (X0 + WLayer - Delta/2.0 <= x && x <= X0 + WLayer + Delta/2.0) {
ux_samp_ave += u.x[]; num_samp_c++;
}
}
ux_samp_ave /= num_samp_c;
double ux_intp_ave = 0.0; double s_in = 0;
foreach_boundary(left, reduction(+:ux_intp_ave) reduction(+:s_in)) {
int i_tmp = (int)((y - stp/2. - Y0)/stp);
int j_tmp = (int)((z - stp/2. - Z0)/stp);
if (i_tmp < 0) i_tmp = 0; if (i_tmp > nn) i_tmp = nn;
if (j_tmp < 0) j_tmp = 0; if (j_tmp > nn) j_tmp = nn;
ux[] = ux_samp[i_tmp][j_tmp]; ux_intp_ave += ux[]*sq(Delta); s_in += sq(Delta);
uy[] = uy_samp[i_tmp][j_tmp];
uz[] = uz_samp[i_tmp][j_tmp];
}
ux_intp_ave = ux_intp_ave / s_in;
foreach_boundary(left) ux[] = ux[] - ux_intp_ave + u_ave_in;
u.n[left] = dirichlet(ux[]);
u.t[left] = dirichlet(uy[]);
u.r[left] = dirichlet(uz[]);
matrix_free (ux_samp); matrix_free (uy_samp); matrix_free (uz_samp);
}