/** # Flow past a vertically oscillating cylinder for $Re=185$ and $f=0.8f_0$ This test case is inspired from the experimental work of [Gu et al., 1994](#gu1994) and the numerical simulations of [Guilmineau et al., 2002](#guilmineau2002). The authors studied the vortex switching induced by a harmonic forcing of a cylinder oscillating tranverselly to the freestream direction. Depending on the ratio $f/f_0$, the vortex shedding frequency should synchronize with the oscillation frequency after a few periods. This test case was also reproduced numerically by [Uhlman, 2005](#uhlman2005), [Kim et al., 2006](#kim2006), [Yang et al., 2009](#yang2009), [Young et al., 2009](#young2009), [Schneiders et al., 2013](#schneiders2013), [Meinke et al., 2013](#meinke2013) and [Xin et al., 2018](#xin2018), using the following numerical parameters: $f_0$ (for $U=1,\, D=1$) $f/f_0$ ----------------------- ------------------------ --------------------------- ----------------------------------------------------------------------- Guilmineau et al., 2002 0.195 0.8, 0.9, 1, 1.1, 1.12, 1.2 $\frac{U_{\infty}\Delta t}{D} = 2e^{-3}$, $\frac{D}{\Delta} = ?$ Uhlman, 2005 ? 0.8 $\frac{U_{\infty}\Delta t}{D} = 1e^{-2}$, $\frac{D}{\Delta} = 38$ Kim et al., 2006 ? 0.8, 0.9, 1, 1.1, 1.12, 1.2 $\frac{U_{\infty}\Delta t}{D} = ?$, $\frac{D}{\Delta} = 30$ Yang et al, 2009 0.156 0.8 $\frac{U_{\infty}\Delta t}{D} = 2e^{-3}$, $\frac{D}{\Delta} = 50$ Young et al, 2009 0.197/0.199 0.8, 0.9, 1, 1.1, 1.12, 1.2 $\frac{U_{\infty}\Delta t}{D} = ?$, $\frac{D}{\Delta} = 15-20$ Schneiders et al., 2013 0.195 0.8 $\frac{U_{\infty}\Delta t}{D} = ?\, (CFL=0.5)$, $\frac{D}{\Delta} = 50$ Meinke et al,. 2013 0.195 0.8, 0.9, 1, 1.1, 1.12, 1.2 $\frac{U_{\infty}\Delta t}{D} = ?\, (CFL=0.5)$, $\frac{D}{\Delta} = 33$ Xin et al., 2018 0.195 0.8, 0.9, 1, 1.1, 1.12, 1.2 $\frac{U_{\infty}\Delta t}{D} = 2e^{-2}$, $\frac{D}{\Delta} = 16$ We solve here the Navier-Stokes equations and add the cylinder using an [embedded boundary](/src/embed.h) for $Re=185$ and a forcing frequency $f_e=0.8 f_0$, where $f_0=0.195$ is the natural shedding frequency of the cylinder. Note that this is a relatively challenging test case as the motion of the cylinder is perpendicular to the direction of the flow, which results in difficulties in imposing the values in emerged cells. */ #include "grid/quadtree.h" #include "../myembed.h" #include "../mycentered.h" #include "../myembed-moving.h" #include "../myperfs.h" #include "view.h" /** ## Reference solution */ #define d (1.) #define Re (185.) // Particle Reynolds number #define p_f0 (0.195) // Natural vortex shedding frequency #define p_f (0.8*(p_f0)) // Oscillating frequency #define uref (max (1., 0.4*M_PI*(p_f))) // Reference velocity, uref #define tref (min ((d)/(uref), 1./(p_f))) // Reference time, tref /** We define the cylinder's imposed motion. */ # define p_acceleration(t,f) (0.8*sq(M_PI*(f))*cos(2.*M_PI*(f)*(t))) // Particle acceleration # define p_velocity(t,f) (0.4*M_PI*(f)*sin(2.*M_PI*(f)*(t))) // Particle velocity # define p_displacement(t,f) (-0.2*cos(2.*M_PI*(f)*(t))) // Particle displacement /** We also define the shape of the domain. */ #define cylinder(x,y) (sq (x) + sq (y) - sq ((d)/2.)) void p_shape (scalar c, face vector f, coord p) { vertex scalar phi[]; foreach_vertex() phi[] = (cylinder ((x - p.x), (y - p.y))); boundary ({phi}); fractions (phi, c, f); fractions_cleanup (c, f, smin = 1.e-14, cmin = 1.e-14); } /** ## Setup We need a field for viscosity so that the embedded boundary metric can be taken into account. */ face vector muv[]; /** We define the mesh adaptation parameters. */ #define lmin (7) // Min mesh refinement level (l=7 is 2pt/d) #define lmax (12) // Max mesh refinement level (l=12 is 64pt/d) #define cmax (1.e-2*(uref)) // Absolute refinement criteria for the velocity field /** We finally define a useful function that allows us to define a rectangular block mesh refinement (BMR) around the cylinder. */ #define rectangle(x,hx,y,hy) (union ( \ union ((x) - (hx)/2., -(x) - (hx)/2.), \ union ((y) - (hy)/2., -(y) - (hy)/2.)) \ ) int main () { /** The domain is $64\times 64$. */ L0 = 64.; size (L0); origin (-L0/2., -L0/2.); /** We set the maximum timestep. */ DT = 1.e-2*(tref); /** We set the tolerance of the Poisson solver. */ TOLERANCE = 1.e-6; TOLERANCE_MU = 1.e-6*(uref); /** We initialize the grid. */ N = 1 << (lmin); init_grid (N); run(); } /** ## Boundary conditions We use inlet boundary conditions. */ u.n[left] = dirichlet ((uref)); u.t[left] = dirichlet (0); p[left] = neumann (0); pf[left] = neumann (0); u.n[right] = neumann (0); u.t[right] = neumann (0); p[right] = dirichlet (0); pf[right] = dirichlet (0); /** We give boundary conditions for the face velocity to "potentially" improve the convergence of the multigrid Poisson solver. */ uf.n[left] = (uref); uf.n[bottom] = 0; uf.n[top] = 0; /** ## Properties */ event properties (i++) { foreach_face() muv.x[] = (uref)*(d)/(Re)*fm.x[]; boundary ((scalar *) {muv}); } /** ## Initial conditions */ event init (i = 0) { /** We set the viscosity field in the event *properties*. */ mu = muv; /** We use "third-order" [face flux interpolation](/src/embed.h). */ #if ORDER2 for (scalar s in {u, p, pf}) s.third = false; #else for (scalar s in {u, p, pf}) s.third = true; #endif // ORDER2 /** We use a slope-limiter to reduce the errors made in small-cells. */ #if SLOPELIMITER for (scalar s in {u, p, pf}) { s.gradient = minmod2; } #endif // SLOPELIMITER #if TREE /** When using *TREE* and in the presence of embedded boundaries, we should also define the gradient of *u* at the cell center of cut-cells. */ #endif // TREE /** We initialize the embedded boundary. */ /** We first define the particle's initial position. */ p_p.y = (p_displacement (0., (p_f))); #if TREE #if BMR int lvl = (lmin); double wxmin = (L0)/2.*(d), wxmax = 3.*(d); double xmin = wxmin/2. - 4.*(d), xmax = wxmax/2. - 1.*(d); double wymin = 8.*(d), wymax = 3.*(d); double ymin = 0., ymax = 0.; while (lvl < (lmax)) { refine ( (rectangle ((x - (p_p.x + (xmin + (xmax - xmin)/((lmax) - (lmin))*((lvl + 1) - (lmin))))), wxmin + (wxmax - wxmin)/((lmax) - (lmin))*((lvl + 1) - (lmin)), (y - (p_p.y + (ymin + (ymax - ymin)/((lmax) - (lmin))*((lvl + 1) - (lmin))))), wymin + (wymax - wymin)/((lmax) - (lmin))*((lvl + 1) - (lmin)))) <= 0. && level < (lvl + 1) ); p_shape (cs, fs, p_p); lvl ++; } /** In the case of the moving cylinder, we don't unrefine inside the cylinder. */ unrefine ((rectangle ((x - (p_p.x + xmin)), wxmin, (y - (p_p.y + ymin)), wymin)) > 0. && level > 1); #else // AMR /** When using *TREE*, we refine the mesh around the embedded boundary. */ astats ss; int ic = 0; do { ic++; p_shape (cs, fs, p_p); ss = adapt_wavelet ({cs}, (double[]) {1.e-30}, maxlevel = (lmax), minlevel = (1)); } while ((ss.nf || ss.nc) && ic < 100); #endif // BMR #endif // TREE p_shape (cs, fs, p_p); /** We initialize the particle's speed and accelerating. */ p_au.y = (p_acceleration (0., (p_f))); p_u.y = (p_velocity (0., (p_f))); /** We finally initialize the velocity. */ foreach() u.x[] = cs[]*(uref); boundary ((scalar *) {u}); } /** ## Embedded boundaries The cylinder's position is advanced to time $t + \Delta t$. */ event advection_term (i++) { p_au.y = (p_acceleration (t + dt, (p_f))); p_u.y = (p_velocity (t + dt, (p_f))); p_p.y = (p_displacement (t + dt, (p_f))); } /** ## Adaptive mesh refinement */ #if TREE && !BMR event adapt (i++) { adapt_wavelet ({cs,u}, (double[]) {1.e-2,(cmax),(cmax)}, maxlevel = (lmax), minlevel = (1)); /** We do not need here to reset the embedded fractions to avoid interpolation errors on the geometry as the is already done when moving the embedded boundaries. It might be necessary to do this however if surface forces are computed around the embedded boundaries. */ } #endif // TREE && !BMR /** ## Outputs */ event logfile (i++; t <= 20./(p_f)) { coord Fp, Fmu; embed_force (p, u, mu, &Fp, &Fmu); double CD = (Fp.x + Fmu.x)/(0.5*sq ((uref))*(d)); double CL = (Fp.y + Fmu.y)/(0.5*sq ((uref))*(d)); fprintf (stderr, "%d %g %g %d %d %d %d %d %d %g %g %g %g %g %g %g %g %g %g %g %g\n", i, t/(1./(p_f)), dt/(1./(p_f)), mgp.i, mgp.nrelax, mgp.minlevel, mgu.i, mgu.nrelax, mgu.minlevel, mgp.resb, mgp.resa, mgu.resb, mgu.resa, p_p.x/(d), p_p.y/(d), CD, CL, Fp.x, Fp.y, Fmu.x, Fmu.z); fflush (stderr); } /** ## Snapshots We also compute the distribution of the pressure coefficient $C_p$ and vorticity $\omega$ at the surface of the cylinder as it reaches: (i) the extreme upper position and (ii) the center position while moving downwards. */ void cpout (FILE * fp) { foreach(serial) if (cs[] > 0. && cs[] < 1.) { coord b, n; embed_geometry (point, &b, &n); double xe = x + b.x*Delta, ye = y + b.y*Delta; fprintf (fp, "%g %g %g\n", M_PI + atan2(ye - p_p.y, xe - p_p.x), // 1 embed_interpolate (point, p, b)/(0.5*sq (uref)*(d)), // 2 embed_vorticity (point, u, b, n) //3 ); fflush (fp); } } event snapshots (t = {0., 19.5/(p_f), 19.75/(p_f)}) { int phase = max(0, floorf ((t/(1./(p_f)) - 19.)*360)); char name2[80]; sprintf (name2, "cp-phi-%d-pid-%d", phase, pid()); FILE * fp = fopen (name2, "w"); cpout (fp); fclose (fp); scalar omega[]; vorticity (u, omega); /** We first plot the entire domain. */ view (fov = 20, camera = "front", tx = 0., ty = 0., bg = {1,1,1}, width = 800, height = 800); draw_vof ("cs", "fs", lw = 5); cells (); sprintf (name2, "mesh-phi-%d.png", phase); save (name2); draw_vof ("cs", "fs", filled = -1, lw = 5, fc = {1,1,1}); squares ("u.x", map = cool_warm); sprintf (name2, "ux-phi-%d.png", phase); save (name2); draw_vof ("cs", "fs", filled = -1, lw = 5, fc = {1,1,1}); squares ("u.y", map = cool_warm); sprintf (name2, "uy-phi-%d.png", phase); save (name2); draw_vof ("cs", "fs", filled = -1, lw = 5, fc = {1,1,1}); squares ("p", map = cool_warm); sprintf (name2, "p-phi-%d.png", phase); save (name2); draw_vof ("cs", "fs", filled = -1, lw = 5, fc = {1,1,1}); squares ("omega", map = cool_warm); sprintf (name2, "omega-phi-%d.png", phase); save (name2); /** We then zoom on the cylinder. */ view (fov = 2.5, camera = "front", tx = -(p_p.x + 3*(d))/(L0), ty = 0., bg = {1,1,1}, width = 800, height = 400); draw_vof ("cs", "fs", lw = 5); cells (); sprintf (name2, "mesh-zoom-phi-%d.png", phase); save (name2); draw_vof ("cs", "fs", filled = -1, lw = 5, fc = {1,1,1}); squares ("u.x", map = cool_warm); sprintf (name2, "ux-zoom-phi-%d.png", phase); save (name2); draw_vof ("cs", "fs", filled = -1, lw = 5, fc = {1,1,1}); squares ("u.y", map = cool_warm); sprintf (name2, "uy-zoom-phi-%d.png", phase); save (name2); draw_vof ("cs", "fs", filled = -1, lw = 5, fc = {1,1,1}); squares ("p", map = cool_warm); sprintf (name2, "p-zoom-phi-%d.png", phase); save (name2); draw_vof ("cs", "fs", filled = -1, lw = 5, fc = {1,1,1}); squares ("omega", map = cool_warm); sprintf (name2, "omega-zoom-phi-%d.png", phase); save (name2); } /** ## Results #### Pressure and vorticity for level = 12