/** #Way of solving the equation The goal of this library is to solve the static interface of an air bubble close to a liquid-air interface. This is solved in 3 parts. First, we solve the water-bubble interface from x=0 up to dzdx=1. Then we switch the axis to continue to solve the water-bubble interface up to the end. We change the axis because for a given x value, we can have 2 z values. We are not solving this problem with x as a function of z because it would mean to start with infinite derivative. Finally, we solve the tail of this function. To solve the tail, we will iterate until the starting point of the tail, between 2 steps goes belong a certain value. This library will solve the Young-Laplace equations. The equation link to the bottom part of the bubble is the following one: $$ \frac{1}{\kappa_1}+\frac{1}{\kappa_2} = Bo\times y + 2R_0 $$ Where $\kappa_i$ is a principal radius of curvature of the shape. $Bo$ is the Bond number. $Bo = \frac{\Delta\rho L^2 g}{\gamma}$ Here, $L$ is a characteristic length scale, $g$ is the gravity acceleration, $\gamma$ is the surface tension between the liquid and the air. We choose, as characteristic length scale, the curvature of the bubble at the bottom. The Young Laplace equation is solved on an axisymmetric bubble shape. The differential equation we have to solve is then: $$ \frac{\frac{d^2y}{dx^2}}{\left(1+\left(\frac{dy}{dx}\right)^2\right)^{3/2}} + \frac{\frac{dy}{dx}}{x\left(1+\left(\frac{dy}{dx}\right)^2\right)^{1/2}} = Bo\times y+2 $$ We won't solve the upper part of the bubble. We still need to solve the tail part of the bubble shape. The tail is also obtained by solving a dimensionless Young-Laplace's equation. This time, we have to solve the following equation: $$ \frac{1}{\kappa_1}+\frac{1}{\kappa_2} = Bo\times (z-L) $$ Here, $L$ is the dimensionless difference between the water level at $x=\infty$ and the lowest part of the bubble. To find the starting point of the tail, we will select one starting point, solve the differential equation, and apply a test on the solution to know if our point is below or above the solution. The method implemented here to solve the equation of the static droplet was partially described in 1962 bu H.M. Princen. For the differential equation solver, we will use a Runge Kutta algorithm, at order 4. #Geneal definition */ #include #include #include /** fstep and gstep are the step used in the resolution of the bubble shape. f is the first part (until dz/dx reach 1). For g, we reduce the step. Indeed, for the last point of the drop, we will reach a high derivative in absolute value. In order to have a good precisio, we reduce the step.*/ #define fstep (1./1000.) #define gstep (fstep/10.) #define tstep (fstep/5.) /** The size of the domain is 10. However, due to an accoustician request, we can increase it up to 30.*/ #if ACOUSTIC #define SIZE 30. #else #define SIZE 10. #endif /** The case f, g and tail are used to select the good function during the computation of the k_i coefficient.*/ #define fCase 1 #define gCase 2 #define tailCase 3 /** The stopCondition is the precision we want to have for the beginning of the tail.*/ #define stopCondition 0.00000000000001 /** We define a "stupid" value for the post-process of the data*/ #define stupid (-1000) /** Sometimes, we want to know where is the spherical cap, and its radius. By default, we don't want this output. However, this can be change by redifining out*/ #ifndef outCap #define outCap 0 #endif /** We define a struct that can store all the information for a circle which are the coordinate of */ typedef struct Circle Circle; struct Circle{ double x; double y; double r; }; /** #Some useful function The min function is not defined. We define it. It returns the min value between x and y*/ double minFunction(double x, double y){ if (x>y) return y; else return x; } /** Definition of trigonometric function that take angle in degrees as input*/ double cosd(double angle) { return cos(angle * M_PI/180.); } double sind(double angle) { return sin(angle * M_PI/180.); } /** The find function take 3 arguments: the number we want to find, an array and the value we are looking for. It will return the position of the closest value, by default. If all the value of the array are above the value we are looking for, we return the error code 200.*/ int find(double ToFind, double *array, int arraySize) { double found = -HUGE; int step = -1; double val = array[0]; int i = 0; while(val != stupid){ if (val < ToFind) { if (found x = xc; cap -> y = yc; cap -> r = rc; double thetaInit = atan(-alpha/beta); /** Initialisation of the circle resolution*/ int n = 1000; double x[n]; double y[n]; x[0] = xi; y[0] = yi; double thetaStep = (M_PI/2.-thetaInit)/(n-1); double theta = thetaInit; double sCapBub = 0; FILE * fp = fopen("cap", "w+"); if (out) fprintf(fp, "%g %g\n", x[0], y[0]); // fprintf(stdout, "départ cap: %g %g\n", x[0], y[0]); for (int i = 1; i0) decal = 1; int fsize = ceil(2./fstep); int gsize = ceil(2./gstep); /** Initialization of the resolution. x0 should be 0. But we have a division by x in the first step. So instead of having an error because of a division by 0, we set the initial value to something very small, close to 0.*/ double x0 = 0.00000001; double z0 = 0.; double dz0 = 0.; double xn=x0; double zn=z0; double dzn=dz0; /** Before starting solving the differential equation, we create the variables.*/ double k_1, k_2, k_3, k_4; double zn1, xn1, dzn1; /** Before we solve the equation, we pre-allocate the memory to store the solution. This will just concern the second part of the problem.*/ double* xValue = NULL; double* zValue = NULL; double* dzdxValue = NULL; double* phi = NULL; double* xBegin = NULL; double* zBegin = NULL; xBegin = malloc(fsize *sizeof(double)); zBegin = malloc(fsize *sizeof(double)); xValue = malloc(gsize *sizeof(double)); zValue = malloc(gsize *sizeof(double)); dzdxValue = malloc(gsize *sizeof(double)); phi = malloc(gsize *sizeof(double)); /** As we mentioned above, we will use a Runge Kutta algorithm at the rank 4.*/ int i = 0; xBegin[i] = 0.; zBegin[i] = 0.; double xBubbleMax = -HUGE; double zBubbleMax; while ( dzn<1.) { k_1 = k1(xn, zn, dzn, Bo, fCase, 0); k_2 = k2(xn, zn, dzn, Bo, k_1, fCase, 0); k_3 = k3(xn, zn, dzn, Bo, k_1, k_2, fCase, 0); k_4 = k4(xn, zn, dzn, Bo, k_2, k_3, fCase, 0); zn1 = yn1(zn, dzn, k_1, k_2, k_3, fCase); dzn1 = dyn1(dzn, k_1, k_2, k_3, k_4, fCase); xn1 = xn+fstep; i++; xBegin[i] = xn1; zBegin[i] = zn1; xn = xn1; zn = zn1; dzn = dzn1; } /** After the first part of the solveur, we add a "stupid" parameter. This will be used later when we would output the coordinate of the bubble shape into a coord structure. */ i++; xBegin[i] = stupid; zBegin[i] = stupid; /** We solved the first part of the problem. Now, we switch the axis. We initialize the new problem. We define the new parameters, dx/dz. The other values are already defined from the previous loop. Because we changed the reference axis when dz/dx = 1, the value of the derivative function dx/dz is also 1.*/ double dxn = 1.; double dxn1; xValue[0] = xn; zValue[0] = zn; dzdxValue[0] = 1.; /** phi corresponds to the angle between the local slope of the shape and the x axi.*/ phi[0] = atan(dzdxValue[0])*180./M_PI; int compt = 1; while (dxn >= (-1000.)) { k_1 = k1(zn, xn, dxn, Bo, gCase, 0); k_2 = k2(zn, xn, dxn, Bo, k_1, gCase, 0); k_3 = k3(zn, xn, dxn, Bo, k_1, k_2, gCase, 0); k_4 = k4(zn, xn, dxn, Bo, k_2, k_3, gCase, 0); xn1 = yn1(xn, dxn, k_1, k_2, k_3, gCase); dxn1 = dyn1(dxn, k_1, k_2, k_3, k_4, gCase); zn1 = zn+gstep; /** We store the value in our array. We will use them later to solve the tail part of the geometry. The integer compt (from the French "compter" which means to count) is used to identify the position in the array.*/ xValue[compt] = xn1; zValue[compt] = zn1; dzdxValue[compt] = 1./dxn1; phi[compt] = atan(dzdxValue[compt])*180./M_PI; /** If the value of phi is below 0, we add 180 to have a positive angle. The angle phi corresponds to the local angle between the interface and the x axi.*/ if (phi[compt]<0) phi[compt]+=180; /** We want to have the equivalent Bond number of Ghabache et al. For that, we need the maximum value of x. Then, we will use the find function to obtain the associated z value. The Bond we will use is linked to the numerical Bond with the following relationship: $$ Bo_{Ghabache} = Bo_{num}\times \frac{(x_{BubbleMax}^2z_{BubbleMax})^{2/3}}{R_0^2} $$ Where $R_0$ is the curvature at the bottom of the bubble. Since we assume, for this code that $R_0=1$, we are left with: $$ Bo_{Ghabache} = Bo_{num}\times (x_{BubbleMax}^2z_{BubbleMax})^{2/3} $$*/ if (xValue[compt]>= xBubbleMax) { xBubbleMax = xValue[compt]; zBubbleMax = zValue[compt]; *BondGhabache = Bo*pow(sq(xBubbleMax)*zBubbleMax,(2./3.)); } /** Initialization of the parameters for the next loop.*/ xn = xn1; zn = zn1; dxn = dxn1; compt++; } xValue[compt] = stupid; zValue[compt] = stupid; phi[compt] = stupid; /** As we did for the first part of the resolution, we add a "stupid" value at the end of the array for x and z. At this point, we have solved the bubble interface. We now have to solve the tail part. The tail interface is continuous with the bubble interface. We don't know where the tail is supposed to start. For the initialization, we take 180 as max value and 90 as min value. They correspond to the local angle of the interface where the tail start, with the x-axis. This angle is always above 90° and below 180°. phiC is the value of the angle we will use to solve the tail part of the function.*/ double phiMax = 180.; double phiMin = 90.; double phiC = (phiMin+phiMax)/2.; int position = find(phiC, phi, gsize); double xInit = (xValue[position]+xValue[position+1])/2.; double zInit = (zValue[position]+zValue[position+1])/2.; double dzdxInit = (dzdxValue[position]+dzdxValue[position+1])/2.; /** The loop will work on phiMax-phiMin. We will solve the tail equation with various initial condition until the difference between the 2 consecutive initial value of x and z is below 0.5*10^-5. Since we don't have yet a value for xInitOld and zInitOld, we will set them to 0*/ double xInitOld = 0.; double zInitOld = 0.; /** We will store the value of x and z in an array. We will allocate the memory in the while loop. We will also free this memory in the while loop, at the beginning of the loop. For the initialization, we will create the pointer.*/ double tailSize = SIZE/tstep; int tsize; tsize = ceil(tailSize); double* xTail = NULL; double* zTail = NULL; xTail = malloc(tsize *sizeof(double)); zTail = malloc(tsize *sizeof(double)); double ratio; double R, L = 0.; int j; while ((fabs(xInit-xInitOld)>stopCondition) && (fabs(zInit-zInitOld)>stopCondition)) { /** Initialization of the parameters R and L. R corresponds to the equivalent radius of the bubble. L is the height of the tail at infinity.*/ R = xInit/sind(phiC); L = 2./Bo*(2./R-1); /** Initialization of the parameters before the resolution. The step is the same as for the case f.*/ zn = zInit; xn = xInit; dzn = dzdxInit; xTail[0] = xn; zTail[0] = zn; double minZ = zn; j = 0; while (xn < 10) { j++; k_1 = k1(xn, zn, dzn, Bo, tailCase, L); k_2 = k2(xn, zn, dzn, Bo, k_1, tailCase, L); k_3 = k3(xn, zn, dzn, Bo, k_1, k_2, tailCase, L); k_4 = k4(xn, zn, dzn, Bo, k_2, k_3, tailCase, L); zn1 = yn1(zn, dzn, k_1, k_2, k_3, tailCase); dzn1 = dyn1(dzn, k_1, k_2, k_3, k_4, tailCase); xn1 = xn+tstep; /** Just after the resolution of the step, we compute the min value of the z values, in order to already have it after the resolution of the tail. This will be the test value for the next step. */ minZ = minFunction(minZ, zn1); /** As previous, we store the value of the function in an array.*/ xTail[j] = xn1; zTail[j] = zn1; xn = xn1; zn = zn1; dzn = dzn1; /** To avoid the problem related to an infinite number, we implement a test on dz, when dz is too big or too small.*/ if (dzn1>1000.) xn = SIZE+1; if (dzn1<-1000.) xn = SIZE+1; } /** We test the min value of the tail. If it's below L, then the tail is dropping under the axi z = L. We need to increase the angle. If not, it means that the tail is going up to +infty. We need to reduce the angle*/ if (minZ x = C.x; hollow -> y = C.y; hollow -> r = C.r; int m = 0; if (decal == 0) { while (xFinal[m] != stupid) { yFinal[m] = yFinal[m]/(effectiveRadius); xFinal[m] = xFinal[m]/(effectiveRadius); m++; } // fprintf(stdout, "normalisation, you must take the last %f\n",effectiveRadius); /** We transformed our shape. We may have reduced his size. In order to have a tail that will go at least at xn = 10 (which means yFinal must reach 10, at least), we will prolong the tail, with its last value.*/ double endTest = yFinal[m-1]; double xEnd = xFinal[m-1]; while (endTest < SIZE){ xFinal[m] = xEnd; yFinal[m]= yFinal[m-1] + tstep; endTest+=tstep; m++; } *size = m; xFinal[m]=stupid; yFinal[m]=stupid; } else if (decal == 1) { m = sizeBubbleTab; int i = 0; double xFinal2[1000 + tSize]; double yFinal2[1000 + tSize]; /** First, we add the spherical cap*/ while(capCoord[i].x != nodata) { xFinal2[i] = capCoord[i].y; yFinal2[i] = capCoord[i].x; i++; } m+=3; while (xFinal[m] != stupid){ yFinal2[i] = yFinal[m]/(effectiveRadius); xFinal2[i] = xFinal[m]/(effectiveRadius); m++; i++; } for (int j = 0; j delta/2.){ epsilon = velocityNoise(amplitude); yNoiseInit = yFinal[i]; } xFinal[i] = xFinal[i] + epsilon; i++; } #endif coord* sortie = NULL; sortie = input(xFinal, yFinal); /** Before stopping the code, we free the memory :)*/ free(xBegin); free(zBegin); free(xValue); free(zValue); free(dzdxValue); free(phi); free(xFinal); free(yFinal); free(xTail); free(zTail); return sortie; } /** #Use of this library We use this library in a test case, to compute the shape of a bursting bubble: * [a bubble shape](bubble.c) A more advance test case also use this library: * [volume of a bubble](bubbleRef.c) This library is also called by: * [the bursting bubble](burstingBubble.c) * [Input for a given Bond](findBond.h) #Known issues ## Divergence of the tail While solving the differential equations, if the input Bond number is too high, to code will not reach the end of the solving range (the tail will diverge before x=10). This is completely normal since the tail shape is highly dependent of the initial condition. A possible solution is to decrease the value of the stop condition, but this will increase the computation time. It's not really a problem: it's taking less than 2 seconds on a laptop. */