sandbox/lchirco/signature.h
We want to detect thin sheets and ligaments in 2 phase flows. First, we compute the quadratic form f (x, x) = x_i x_j T_{ij}, where T_{ij} are the quadratic moments, by integrating over a shell of arbitrary thickness and radius. Finally, we compute the signature s of the quadratic form.
int find_moments_level(scalar f, double length, double target_delta){
int level = depth();
bool found = false;
while (level >= 0 && !found){
double num = 1<<level;
if (length/num > target_delta) found = true;
--;
level}
return level;
}
To compute the eigenvalues used in the signature method in 3D we use the GNU scientific library.
#include <gsl/gsl_math.h>
#include <gsl/gsl_eigen.h>
void eigen ( double data[], double tau[], double dim){
gsl_matrix_view m= gsl_matrix_view_array (data, dim, dim);
*eval = gsl_vector_complex_alloc (dim);
gsl_vector_complex *evec = gsl_matrix_complex_alloc (dim, dim);
gsl_matrix_complex
* w =
gsl_eigen_nonsymmv_workspace (dim);
gsl_eigen_nonsymmv_alloc
(&m.matrix, eval, evec, w);
gsl_eigen_nonsymmv
(w);
gsl_eigen_nonsymmv_free
(eval, evec,
gsl_eigen_nonsymmv_sort );
GSL_EIGEN_SORT_ABS_DESC
for (int i = 0; i < dim; i++)
{
gsl_complex eval_i= gsl_vector_complex_get (eval, i);
gsl_vector_complex_view evec_i= gsl_matrix_complex_column (evec, i);
// printf ("eigenvalue = %g + %gi\n", GSL_REAL(eval_i), GSL_IMAG(eval_i));
[i]=GSL_REAL(eval_i);
tau}
(eval);
gsl_vector_complex_free(evec);
gsl_matrix_complex_free
}
This function is used to compute the signature at a given level of
refinement lev
. It can be found using the function
find_moments_level
.
void compute_signature_neigh_level(scalar f, scalar phii, scalar s, int lev){
scalar int_xx[], int_xy[], int_yy[];
#if dimension == 3
scalar int_xz[], int_yz[], int_zz[];
#endif
vector Tij[], sign[];
(lev){
foreach_level[] = 0;
int_xx[] = 0;
int_xy[] = 0;
int_yy#if dimension == 3
[] = 0;
int_xz[] = 0;
int_yz[] = 0;
int_zz#endif
}
boundary ({phii});
We integrate over the cells of the 5x5 stencil and subtract the values of those in the 3x3 stencil. We can say the thickness of the shell is equal to the grid-size and the radius is twice the grid-size
double mom_xx, mom_yy, mom_xy;
#if dimension == 3
double mom_xz, mom_yz, mom_zz;
#endif
(lev){
foreach_level
= mom_xy = mom_yy = 0.;
mom_xx #if dimension == 3
= mom_yz = mom_zz = 0.;
mom_xz #endif
// if (phii[] > -0.99){
double xp = x;
double yp = y;
#if dimension == 3
double zp = z;
#endif
foreach_neighbor() {
+= sq(x - xp)*phii[];
mom_xx += sq(y - yp)*phii[];
mom_yy += (x - xp)*(y - yp)*phii[];
mom_xy #if dimension == 3
+= (x - xp)*(z - zp)*phii[];
mom_xz += (y - yp)*(z - zp)*phii[];
mom_yz += sq(z - zp)*phii[];
mom_zz #endif
}
foreach_neighbor(1) {
-= sq(x - xp)*phii[];
mom_xx -= sq(y - yp)*phii[];
mom_yy -= (x - xp)*(y - yp)*phii[];
mom_xy #if dimension == 3
-= (x - xp)*(z - zp)*phii[];
mom_xz -= (y - yp)*(z - zp)*phii[];
mom_yz -= sq(z - zp)*phii[];
mom_zz #endif
}
[] = mom_xx;
int_xx[] = mom_yy;
int_yy[] = mom_xy;
int_xy#if dimension == 3
[] = mom_xz;
int_xz[] = mom_yz;
int_yz[] = mom_zz;
int_zz#endif
}
(lev)
foreach_levelforeach_dimension()
.x[] = 0; Tij
We now compute the eigenvalues of the quadratic form. For a 2x2
symmetric matrix the eigenvalues can be found analytically. In the 3D
case we call the function eigen
that uses the GSL
library.
#if dimension == 2
double Txx, Tyy, Txy;
double tau[2]={0.};
(lev){
foreach_level= int_xx[]/sq(Delta);
Txx = int_yy[]/sq(Delta);
Tyy = int_xy[]/sq(Delta);
Txy double data[] = { Txx, Txy,
, Tyy };
Txy
eigen(data, tau, 2);
.x[] = tau[0];
Tij.y[] = tau[1];
Tij
}
#else //dimension == 3
double Txx, Tyy, Tzz, Txy, Txz, Tyz=0.;
double tau[3]={0.};
(lev){
foreach_level= int_xx[]/sq(Delta);
Txx = int_yy[]/sq(Delta);
Tyy = int_xy[]/sq(Delta);
Txy = int_zz[]/sq(Delta);
Tzz = int_xz[]/sq(Delta);
Txz = int_yz[]/sq(Delta);
Tyz double data[] = { Txx, Txy, Txz,
, Tyy, Tyz,
Txy, Tyz, Tzz };
Txzeigen(data, tau, 3);
.x[] = tau[0];
Tij.y[] = tau[1];
Tij.z[] = tau[2];
Tij}
#endif
The signature of the quadratic form are the signs of the eigenvalues.
(lev){
foreach_levelforeach_dimension(){
.x[] = fabs(Tij.x[])/(Tij.x[] + 1.e-10);
signif (fabs(Tij.x[]) < 10.) sign.x[] = 0;
}
}
We identify thin ligaments by looking at the signature. In 2D we can have the signatures:
- (+,+) bulk of phase
- (+,-) thin ligament
- (+,0) or (-,0) interface
- (-,-) outside of phase
#if dimension ==2
(lev){
foreach_levelif (sign.x[]*sign.y[] > 0.999 && sign.x[] > 0.999) s[] = 2; //bulk of phase
if (sign.x[]*sign.y[] > 0.999 && sign.x[] < -0.999) s[] = -1; //outside of phase
if (fabs(sign.x[]*sign.y[]) < 0.01) s[] = 0; //interface
if (sign.x[]*sign.y[] < -0.999) s[] = 1; //ligament
}
#else //dimension == 3
(lev){
foreach_level[] = 1; //thin
sif (sign.x[] == 0. || sign.y[] == 0. || sign.z[] == 0.) s[] = 0; //interface
if (sign.x[] > 0.999 && sign.y[] > 0.999 && sign.z[] > 0.999) s[] = 2; //bulk of phase
if (sign.x[] < -0.999 && sign.y[] < -0.999 && sign.z[] < -0.999) s[] = -1; //outside of phase
}
#endif
}
This function is used to perforate thin sheets. The scalar
s
must be filled using the
compute_signature_neigh_level
function.
void change_topology (scalar f, scalar s, scalar M, int lev, const int max_change, bool large){
double f_avg[max_change]; // average f in the neighbor
int num = 0.; int i_change = 0;
(time(NULL)); // Initialization of random seed.
srandint r;
Cache to_perf = {0};
#ifndef SQUARES
int i;
scalar hole[];
double xc[max_change], yc[max_change], zc[max_change];
#endif
(lev){
foreach_level[] = 0.;
M= rand() % 50;
r
if (s[] > 0.99 && s[] < 1.01 && r==0 && i_change < max_change){
[i_change] = 0.;
f_avg= 0;
num #ifndef SQUARES
[i_change] = x; yc[i_change] = y; zc[i_change] = z;
xc#endif
#ifdef SQUARES
if (large == false){
foreach_neighbor(1){ //compute average f
[i_change] += f[];
f_avgcache_append (&to_perf, point, i_change);
+= 1;
num }
}
else{
foreach_neighbor(){ //compute average f
[i_change] += f[];
f_avgcache_append (&to_perf, point, i_change);
+= 1;
num }
}
[i_change]/=num;
f_avgcache_shrink (&to_perf);
#endif
++;
i_change}
}
#ifndef SQUARES
printf("Holes are spheres \n");
vertex scalar phi[];
double Ddelta = 0.001/64.;
double R = 1.2*2.5*Ddelta;
foreach_vertex() {
[] = HUGE;
phifor (i = 0; i < max_change; i++){
[] = intersection (phi[], (sq(x - xc[i])/sq(1)+sq(y -yc[i])/sq(1)+sq(z -zc[i])/sq(1) - sq(R)));
phi// phi[] = intersection (phi[], (/*sq(x - xc[i]) +*/ (sq(y - yc[i]) + sq(z - zc[i]) - sq(R))));
}
}
boundary ({phi});
(phi, hole);
fractions foreach(){
if (f[] > 1e-4 && hole[] < 1 - 1.e-4){
[] = hole[];
f}
}
boundary({f});
#endif
#ifdef SQUARES
printf("Holes are squares \n");
foreach_cache(to_perf){
[] = -2.;
s[] = f[] - (1 - f_avg[_flags]);
Mif (f_avg[_flags] < 0.3) {
[] = -3.;
s[] = 1.;
f}
else[] = 0.;
f}
#endif
for (int ilev = lev; ilev < depth(); ilev++)
(ilev){
foreach_level.prolongation = M.prolongation = f.prolongation = refine_injection;
sif(is_refined(cell) && s[] == -2){
.prolongation (point, s);
s.prolongation (point, M);
M.prolongation (point, f);
f}
}
free(to_perf.p);
boundary(all);
}