/** ![Astronaut Scott Kelly played ping pong in space. Video courtesy of [NASA Johnson](https://www.youtube.com/watch?v=TLbhrMCM4_0) ($\leftarrow$ click to watch the full movie)](https://lh3.googleusercontent.com/-HPsDGunAGPQ/VqPMV9unuEI/AAAAAAAB5zY/AZOVAQaaey8vT47GYfijW3scP_2FZQZmQCJoC/w500-h429-n/SpacePingPong.gif) # A Bouncing Axisymetric Droplet On this page a quasi three dimensional (3D) simulation of the bouncing droplet is presented. The set-up is very similar to that of the planar (2D) version [here](bounce.c), but now in the 3D-ish, axisymmetric limit. */ #include "axi.h" #include "navier-stokes/centered.h" #include "two-phase.h" #include "tension.h" #include "view.h" /** ## The case set-up Our "vertical" axis, if that concept is suitable to use inside a space station, is now the swapped with the former horizontal axis. Hence, what used to be associated with the `bottom` boundary in the 2D example is now called `left`. Furthermore, the radial coordinate ($r$) is associated with the $y$-direction in Basilisk's axisymmetric formulation. */ f[left] = 0.; u.t[left] = dirichlet(0.); f[right] = 0.; u.t[right] = dirichlet(0.); /** The physical system consists of a liquid droplet with a radius $R$ that travels trough the air towards the hydrophobic wall with a velocity $U$. Gauged from the movie, we approximate $R \approx 2 \text{ cm}$ and $U\approx 3 \text{cm/s}. Together with the fluid properties of the air and water ($\rho_a,\rho_w,\mu_a,\mu_w, \sigma$) we can identify some dimensionless groups that describe the system. $$ Re = \frac{\rho_a UR}{\mu_a} \approx 30, $$ $$ We = \frac{\rho_w U^2R}{\sigma} \approx 0.2, $$ $$ \Pi_1 = \frac{\mu_w }{\mu_a} \approx 100, $$ $$ \Pi_1 = \frac{\rho_w }{\rho_a} \approx 1000 \rightarrow 100, $$ $$ D = 3 \rightarrow 2\text{; Axisymmetric} , $$ where $D$ stands here for the number of spatial dimensions of the system and the '$\rightarrow$' symbols indicate dimensionless groups where we make a consession with respect to the reality onboard the space station to keep the numerical experiment feasable on a single-core system. We set the model parameters such that we obtain the approximated ratios, aproximately. It is done in such a way that we have a normalized velocity scale ($U$), length scale ($R$) and gas phase density ($\rho_a$) scale. Furthermore, we set the maximum grid resolution to correspond to a $256^2$ equidistant grid. For some minimally desired consistency, we take special care such that the radial coordinate does not have any negative values. */ double R = 1.; double U = 1.; int maxlevel = 8; double temp = 50.; int main(){ mu2 = 1./30.; //gas phase mu1 = 100./30.; //liquid phase rho2 = 1.; //gas phase rho1 = 100;//liquid phase f.sigma = 500.; init_grid(1 << 7); L0 = 10; X0 = -L0/2; Y0 = 0.; run(); } /** ## initialization After we have refined the grid with a ring of high resolution mesh, the droplet is initialized and it is targeted at the left wall. */ event init(i = 0){ refine(sq(x) + sq(y) < sq(R + 0.25) && sq(x) + sq(y) > sq(R - 0.25) && level < maxlevel); fraction(f, sq(R) - sq(x) - sq(y)); foreach() u.x[] = -f[]*U; } /** ## Adaptation Since the advective interface tracking and resolving for the surface tencile waves only requires a high resolution mesh at the locations of the interface, it seems smart to use grid adaptivity, we will check it this was indeed the case. */ event adapt(i++) adapt_wavelet((scalar *){u,f}, (double []){0.02, 0.02, 0.001}, maxlevel); /** ## Output The general dynamics are visualized in a movie that shows the rebound of the droplet. This requires to define some additional functions. */ static void glvertex3d (bview * view, double x, double y, double z) { if (view->map) { coord p = {x, y, z}; view->map (&p); glVertex3d (p.x, p.y, p.z); } else glVertex3d (x, y, z); } struct _draw_vof_axi { char * c; char * s; bool edges; double larger; int filled; char * color; double min, max, spread; bool linear; colormap map; float fc[3], lc[3], lw; }; trace bool draw_vof_axi (struct _draw_vof_axi p) { scalar c = lookup_field (p.c); if (c.i < 0) { fprintf (stderr, "draw_vof(): no field named '%s'\n", p.c); return false; } face vector s = lookup_vector (p.s); colorize_args (p); double cmin = 1e-3; // do not reconstruct fragments smaller than this #if TREE // make sure we prolongate properly void (* prolongation) (Point, scalar) = c.prolongation; if (prolongation != fraction_refine) { c.prolongation = fraction_refine; boundary ({c}); } #endif // TREE bview * view = draw(); colorize() { foreach() if (cfilter (point, c, cmin)) { coord n = facet_normal (point, c, s); double alpha = plane_alpha (c[], n); coord v[2]; int m = facets (n, alpha, v); if (m == 2) { coord g[4]; g[0].x = v[0].x; g[0].y = v[0].y; g[0].z = -y; g[1].x = v[0].x; g[1].y = v[0].y; g[1].z = y; g[2].x = v[1].x; g[2].y = v[1].y; g[2].z = y; g[3].x = v[1].x; g[3].y = v[1].y; g[3].z = -y; color_facet (p); if (view->gfsview) glNormal3d (- n.x, - n.y, - n.z); else glNormal3d (n.x, n.y, n.z); for (double th=0; th < 2*pi ; th+=0.25){ glBegin (GL_POLYGON); for (int i = 0; i < 4; i++) { color_vertex (p, interp (point, g[i], col)); glvertex3d (view, x + g[i].x*Delta, cos(th)*(y + g[i].y*Delta) + sin(th)*(z + g[i].z*Delta), sin(th)*(y + g[i].y*Delta) + cos(th)*(z + g[i].z*Delta)); } glEnd (); view->ni++; } } } } #if TREE // revert prolongation if (prolongation != fraction_refine) { c.prolongation = prolongation; boundary ({c}); } #endif // TREE return true; } event viewer(t=0.05;t+=0.1;t<=temp){ view(psi = -pi/2, theta = 0.1, phi = 0.2); box(); draw_vof_axi("f"); save("f.mp4"); } /** The simulation does display the ping pong dynamics; ![Well done draw_fov_axi!](axibounce/f.mp4) The energy partitioning between kinetic and the surface free energy is again calculated. The formulation as was used in the planar simulation is modified to correct for the fact that the $y$ coordinate now represents the radial coordinate. */ void find_facets(Point point, scalar f, double xy[4]){ coord n; n = mycs (point, f); double alpha = plane_alpha (f[], n); coord segment[2]; if (facets (n, alpha, segment) == 2){ xy[0] = x + segment[0].x*Delta; xy[1] = y + segment[0].y*Delta; xy[2] = x + segment[1].x*Delta; xy[3] = y + segment[1].y*Delta; }else{ printf("Warning:\nCould not find facets; expect unexpected behaviour.\n"); } } event energy(i += 5){ static FILE * fpe = fopen("axienergy","w"); double e = 0; double a = 0; double xyf[4]; foreach(reduction(+:e) reduction(+:a)){ e += 0.5*(sq(Delta)*M_PI*2*y)*(sq(u.x[]) + sq(u.y[])) * ((rho1*f[]) + (rho2*(1-f[]))); if (f[] > 0.00001 && f[] < 0.9999){ find_facets(point, f, xyf); a += 2*M_PI*((xyf[1] + xyf[3])/2.)*pow(sq(xyf[0] - xyf[2]) + sq(xyf[1] - xyf[3]), 0.5); } } fprintf(fpe,"%g\t%d\t%g\t%g\t%g\t%g\n", t, i, e, (a-(4*M_PI*sq(R)))*f.sigma,a, ((a-(4*M_PI*sq(R)))*f.sigma) + e); printf("%g\t%d\t%g\t%g\t%g\t%g\n", t, i, e, (a - (4*M_PI*sq(R)))*f.sigma, a,((a - (4*M_PI*sq(R)))*f.sigma) + e); } /** We can view the results; ~~~gnuplot set xlabel 'time' set ylabel 'Energy' plot 'axienergy' u 1:3 w l lw 5 t 'Kinetic' ,\ 'axienergy' u 1:4 w l lw 3 t 'Potential' ,\ 'axienergy' u 1:6 w l lw 3 t 'Total' ~~~ Overall, things seem familiar and consistent to what was learned from the planar case. Differences may be spotted when doing a more quantitative analysis of the energy partioning. ## The next step In the movie, the droplet does not really appear to be axisymetric. It is generally quite challenging to make-up an a priori argument on how to dynamics would alter when the physical system is allotted with an additional spatial dimension. E.g. after having studied 2D turbulence (a la Kaichmann), would you be able to expect the main flow-behavioural characteristics of 3D turbulence (a la Kolmogorov)? Well, Not me! Therefore, it is required to break the axisymetry and study the flow in a fully-fleched 3D simulation. This study should also adresses the influence of the consession that was made with regards to the ratio of the phases' densities. */