sandbox/cselcuk/dlmfd_functions.h
Helper functions for the fictitious-domain method with distributed
Lagrangian multipliers.
Structure for the coordinates of the fictitious domain’s boundary.
typedef struct {
double *x;
double *y;
double *z;
int m;
int nm;
} SolidBodyBoundary;
Geometric parameters of the particle.
typedef struct {
coord center;
double radius;
int ncorners, allPoints, allFaces;
double ** cornersCoord;
long int ** cornersIndex;
long int * numPointsOnFaces;
/*3 principals vectors of the cube */
coord u1, v1, w1;
coord mins, maxs;
} GeomParameter;
typedef struct {
SolidBodyBoundary s;
GeomParameter g;
GeomParameter gnm1;
#if DLM_Moving_particle
double kn, en, vzero, wished_ratio;
coord normalvector;
coord gravity;
double M, Ip[6], rho_s, Vp, DLMFD_couplingfactor;
#if TRANSLATION
coord U, Unm1, qU, tU;
#endif
#if ROTATION
coord w, wnm1, qw, tw;
#endif
#endif
size_t pnum, iswall, iscube;
coord wallmax, wallmin, wallpos;
coord imposedU, imposedw;
Cache Interior;
Cache reduced_domain;
coord adforce;
long tcells, tmultipliers;
} particle;
Function that allocates in memory the SolidBodyBoundary structure.
void allocate_SolidBodyBoundary (SolidBodyBoundary *sbm, const int m) {
sbm->x = (double*) calloc( m, sizeof(double));
sbm->y = (double*) calloc( m, sizeof(double));
if ( dimension == 3 )
sbm->z = (double*) calloc( m, sizeof(double));
else
sbm->z = NULL;
sbm->m = m;
}
void reallocate_SolidBodyBoundary (SolidBodyBoundary *sbm, const int m) {
sbm->x = (double*) realloc (sbm->x, m*sizeof(double));
sbm->y = (double*) realloc (sbm->y, m*sizeof(double));
if (dimension == 3)
sbm->z = (double*) realloc (sbm->z, m*sizeof(double));
sbm->m = m;
}
Function that de-allocates in memory the SolidBodyBoundary structure.
void free_SolidBodyBoundary (SolidBodyBoundary *sbm) {
free(sbm->x); sbm->x = NULL;
free(sbm->y); sbm->y = NULL;
if ( dimension == 3 ){free(sbm->z); sbm->z = NULL;}
}
Function that allocates an initial Cache structure
void allocate_Cache (Cache * p) {
p->n = 0;
p->nm = 0;
if (p->n >= p->nm) {
p->p = (Index *) calloc (5, sizeof(int));
}
}
Set of functions for the sphere/circle as fictitious domain
void compute_nboundary (const GeomParameter gcp, int * nb) {
coord pos[6];
Cache poscache = {0};
Point lpoint;
pos[0].x = gcp.center.x + gcp.radius + X0;
pos[0].y = gcp.center.y + Y0;
pos[1].x = gcp.center.x - gcp.radius + X0;
pos[1].y = gcp.center.y + Y0;
pos[2].x = gcp.center.x + X0;
pos[2].y = gcp.center.y + gcp.radius + Y0;
pos[3].x = gcp.center.x + X0;
pos[3].y = gcp.center.y - gcp.radius + Y0;
#if dimension == 3
pos[0].z = gcp.center.z + Z0;
pos[1].z = gcp.center.z + Z0;
pos[2].z = gcp.center.z + Z0;
pos[3].z = gcp.center.z + Z0;
pos[4].x = gcp.center.x + X0;
pos[4].y = gcp.center.y + Y0;
pos[4].z = gcp.center.z + gcp.radius + Z0;
pos[5].x = gcp.center.x + X0;
pos[5].y = gcp.center.y + Y0;
pos[5].z = gcp.center.z - gcp.radius + Z0;
#endif
*nb = 0;
lpoint.level = -1;
int ip = 0;
/* We assume that around the sphere at least one point has to be at
maximum level of refinement. */
for (ip = 0; ip < dimension*2; ip++) {
#if dimension == 3
lpoint = locate (pos[ip].x, pos[ip].y, pos[ip].z);
#elif dimension ==2
lpoint = locate (pos[ip].x, pos[ip].y);
#endif
if (lpoint.level == depth())
break;
}
Multiple threads can have a different point at the same level of refinement. This is fine, they will do the same computation for *nb. This problem does not exist in serial but the code below still works.
if (lpoint.level == depth()) {
Only this thread creates the Cache …
cache_append(&poscache, lpoint, 0);
and only this thread computes the number of boundary points …
foreach_cache(poscache) {
#if dimension == 2
*nb += (int){floor(gcp.radius*2*pi/(sqrt(2)*Delta))};
/* Debug periodic boundaries, we use only one multiplier */
/* *nb =1.; */
#elif dimension == 3
*nb += (int){floor(pow( 3.809 * gcp.radius / (sqrt(3)*Delta), 2.))};
#endif
}
and finally, this thread destroys the cache.
free(poscache.p);
}
#if _MPI
MPI_Barrier(MPI_COMM_WORLD);
mpi_all_reduce (*nb, MPI_INT, MPI_MAX);
#endif
if(*nb == 0)
printf("nboundary = 0: No boundary points !!!\n");
}
void create_FD_Interior_Particle (particle * p, vector Index_lambda, vector shift, scalar flag) {
GeomParameter gci = p->g;
Cache * fd = &(p->Interior);
Create the cache for the interior points
foreach() {
#if dimension == 2
if ((sq(x - (gci.center.x + X0)) + sq(y - (gci.center.y + Y0))) < sq(gci.radius)) {
cache_append (fd, point, 0);
/* tagg cell with the number of the particle */
if ((int)Index_lambda.y[] == -1)
Index_lambda.y[] = p->pnum;
}
if (Period.x) {
if ((sq(x - L0 - (gci.center.x + X0)) + sq(y - (gci.center.y + Y0))) < sq(gci.radius)) {
cache_append (fd, point, 0);
/* tagg cell with the number of the particle */
if ((int)Index_lambda.y[] == -1)
Index_lambda.y[] = p->pnum;
if ((int)Index_lambda.y[] == p->pnum)
shift.x[] = L0;
}
if ((sq(x + L0 - (gci.center.x + X0)) + sq(y - (gci.center.y + Y0))) < sq(gci.radius)) {
cache_append (fd, point, 0);
/* tagg cell with the number of the particle */
if ((int)Index_lambda.y[] == -1)
Index_lambda.y[] = p->pnum;
if ((int)Index_lambda.y[] == p->pnum)
shift.x[] = -L0;
}
}
if (Period.y) {
if ((sq(x - (gci.center.x + X0)) + sq(y - L0 - (gci.center.y + Y0))) < sq(gci.radius)) {
cache_append (fd, point, 0);
/* tagg cell with the number of the particle */
if ((int)Index_lambda.y[] == -1) {
Index_lambda.y[] = p->pnum;
if (flag[] < 1)
shift.y[] = L0;
}
}
if ((sq(x - (gci.center.x + X0)) + sq(y + L0 - (gci.center.y + Y0))) < sq(gci.radius)) {
cache_append (fd, point, 0);
/* tagg cell with the number of the particle */
if ((int)Index_lambda.y[] == -1) {
Index_lambda.y[] = p->pnum;
if (flag[] < 1)
shift.y[] = -L0;
}
}
}
#endif
#if dimension == 3
if ((sq(x - (gci.center.x + X0)) + sq(y - (gci.center.y + Y0)) + sq(z - (gci.center.z + Z0))) < sq(gci.radius)) {
cache_append (fd, point, 0);
/* tagg cell with the number of the particle */
if ((int)Index_lambda.y[] == -1)
Index_lambda.y[] = p->pnum;
}
if (Period.x) {
if ((sq(x - L0 - (gci.center.x + X0) ) + sq(y - (gci.center.y + Y0)) + sq(z - (gci.center.z + Z0))) < sq(gci.radius)) {
cache_append (fd, point, 0);
/* tagg cell with the number of the particle */
if ((int)Index_lambda.y[] == -1)
Index_lambda.y[] = p->pnum;
if ((int)Index_lambda.y[] == p->pnum)
shift.x[] = L0;
}
if ((sq(x + L0 - (gci.center.x + X0) ) + sq(y - (gci.center.y + Y0)) + sq(z - (gci.center.z + Z0))) < sq(gci.radius)) {
cache_append (fd, point, 0);
/* tagg cell with the number of the particle */
if ((int)Index_lambda.y[] == -1)
Index_lambda.y[] = p->pnum;
if ((int)Index_lambda.y[] == p->pnum)
shift.x[] = -L0;
}
}
if (Period.y) {
if ((sq(x - (gci.center.x + X0)) + sq(y - L0 - (gci.center.y + Y0)) + sq(z - (gci.center.z + Z0))) < sq(gci.radius)) {
cache_append (fd, point, 0);
/* tagg cell with the number of the particle */
if ((int)Index_lambda.y[] == -1) {
Index_lambda.y[] = p->pnum;
if (flag[] < 1)
shift.y[] = L0;
}
}
if ((sq(x - (gci.center.x + X0)) + sq(y + L0 - (gci.center.y + Y0)) + sq(z - (gci.center.z + Z0))) < sq(gci.radius)) {
cache_append (fd, point, 0);
/* tagg cell with the number of the particle */
if ((int)Index_lambda.y[] == -1) {
Index_lambda.y[] = p->pnum;
if (flag[] < 1)
shift.y[] = -L0;
}
}
}
if (Period.z) {
if ((sq(x - (gci.center.x + X0)) + sq(y - (gci.center.y + Y0)) + sq(z - L0 - (gci.center.z + Z0))) < sq(gci.radius)) {
cache_append (fd, point, 0);
/* tagg cell with the number of the particle */
if ((int)Index_lambda.y[] == -1) {
Index_lambda.y[] = p->pnum;
if (flag[] < 1)
shift.z[] = L0;
}
}
if ((sq(x - (gci.center.x + X0)) + sq(y - (gci.center.y + Y0)) + sq(z + L0 - (gci.center.z + Z0))) < sq(gci.radius)) {
cache_append (fd, point, 0);
/* tagg cell with the number of the particle */
if ((int)Index_lambda.y[] == -1) {
Index_lambda.y[] = p->pnum;
if (flag[] < 1)
shift.z[] = -L0;
}
}
}
#endif
}
cache_shrink (fd);
}
void create_FD_Boundary_Particle (GeomParameter gcp, SolidBodyBoundary * dlm_bd, const int nsphere, vector pshift) {
int m = nsphere;
coord pos;
Point lpoint;
coord shift = {0., 0., 0.};
coord ori = {X0, Y0, Z0};
#if dimension == 2
int i;
double theta[m];
double radius = gcp.radius;
coord fact_theta[m];
for (i = 0; i < m; i++) {
theta[i] = i*2*pi/m;
fact_theta[i].x = cos (theta[i]);
fact_theta[i].y = sin (theta[i]);
/* Assign positions x,y on a circle's boundary */
pos.x = radius*fact_theta[i].x + gcp.center.x + X0;
pos.y = radius*fact_theta[i].y + gcp.center.y + Y0;
/* Check if the point falls outside of the domain */
foreach_dimension() {
if (Period.x) {
shift.x = 0.;
if (pos.x > (L0 + ori.x)) {
pos.x -= L0;
shift.x = -L0;
}
if (pos.x < (0. + ori.x)) {
pos.x += L0;
shift.x = L0;
}
}
dlm_bd->x[i] = pos.x;
}
Cache poscache = {0};
lpoint = locate (pos.x, pos.y);
if (lpoint.level > -1) {
cache_append (&poscache, lpoint, 0);
foreach_cache (poscache) {
foreach_dimension() {
pshift.x[] = shift.x;
}
}
free (poscache.p);
}
}
#elif dimension == 3
/* Lay out points on a sphere with a Z oriented spiral scheme */
/* This portion of code is recovered from Peligriff and adapted */
/* More information of this method can be found at: */
/* Saff, E. & Kuijlaars, A. Distributing many points on a sphere The
Mathematical Intelligencer, 1997 */
double spiral_spacing_correction = 0;
foreach_level(depth())
spiral_spacing_correction = 2.*Delta/sqrt(3.);
double hydro_radius = gcp.radius;
size_t k;
/* Number of points */
size_t NSpiral = m;
/* Spiral points construction */
double hk, thetak, phik, phikm1 = 0., TwoPi = 2. * pi,
Cspiral = 3.6 / sqrt( NSpiral ), dphi = 0.;
for (k = 0; k < NSpiral; ++k) {
hk = - 1. + 2. * (double)(k) / ( NSpiral - 1. );
thetak = acos( hk ) ;
if ( k == 0 ) {
phik = 0.;
thetak = pi - 0.5 * spiral_spacing_correction / hydro_radius ;
}
else if ( k == NSpiral - 1 ) {
phik = phikm1 + 1. * dphi;
if ( phik > TwoPi ) phik -= TwoPi ;
thetak = 0.5 * spiral_spacing_correction / hydro_radius ;
}
else {
dphi = Cspiral / sqrt( 1. - hk * hk ) ;
phik = phikm1 + dphi ;
if ( phik > TwoPi ) phik -= TwoPi ;
}
phikm1 = phik ;
if ( k == 1 ) thetak -= 0.4 * spiral_spacing_correction / hydro_radius ;
if ( k == NSpiral - 2 ) {
phik -= 0.1 * dphi ;
thetak += 0.25 * spiral_spacing_correction / hydro_radius ;
}
pos.x = hydro_radius * cos( phik ) * sin( thetak ) + gcp.center.x + X0;
pos.y = hydro_radius * sin( phik ) * sin( thetak ) + gcp.center.y + Y0;
pos.z = hydro_radius * cos( thetak ) + gcp.center.z + Z0;
foreach_dimension() {
if (Period.x) {
shift.x = 0.;
if (pos.x > (L0 + ori.x)) {
pos.x -= L0;
shift.x = -L0;
}
if (pos.x < (0. + ori.x)) {
pos.x += L0;
shift.x = L0;
}
}
}
Cache poscache = {0};
/* find the cell of this multiplier */
lpoint = locate (pos.x, pos.y, pos.z);
if (lpoint.level > -1) {
cache_append(&poscache, lpoint, 0);
foreach_cache(poscache) {
foreach_dimension() {
pshift.x[] = shift.x;
}
}
free(poscache.p);
}
dlm_bd->x[k] = pos.x;
dlm_bd->y[k] = pos.y;
dlm_bd->z[k] = pos.z;
}
#endif
boundary((scalar*){pshift});
}
Set of functions for the walls as fictitious domain
void create_FD_Interior_Wall (particle *p, vector index, scalar flag) {
coord wallmin, wallmax;
Cache * c;
Create the cache of the interior points for a wall
c = &(p->Interior);
wallmin = p->wallmin;
wallmax = p->wallmax;
foreach() {
#if dimension == 2
if ((x >= wallmin.x) && (y >= wallmin.y) && (x <= wallmax.x) && (y <= wallmax.y)) {
index.y[] = p->pnum;
cache_append (c, point, 0);
}
#elif dimension == 3
if ((x > wallmin.x) && (y > wallmin.y) && (z > wallmin.z) && (x < wallmax.x) && (y < wallmax.y) && (z < wallmax.z)) {
if ((int)index.y[] == -1 && flag[] < 1)
index.y[] = p->pnum;
cache_append (c, point, 0);
}
#endif
}
cache_shrink (c);
}
void create_FD_Boundary_Wall (particle *p) {
SolidBodyBoundary * sbm;
int m = 0;
coord wallpos;
double h = 0;
foreach_level(depth())
h = Delta;
sbm = &(p->s);
m = sbm->m;
wallpos = p->wallpos;
for (int i = 0; i < m; i++) {
sbm->x[i] = (3.*h/4.) + i*h;
sbm->y[i] = wallpos.y;
}
for (int i = m/2; i < m; i++) {
sbm->x[i] -= h/2.;
}
}
void compute_nboundary_Wall (coord wallpos, int * nb) {
double mindelta = 0.;
foreach_level(depth())
mindelta = Delta;
*nb = floor(wallpos.x/mindelta);
}
Set of functions for the cube as fictitious domain
void compute_nboundary_Cube_v2 (GeomParameter * gcp, int * nb, int * lN) {
Cache poscache = {0};
Point lpoint;
*nb = 0;
coord pos = {0., 0., 0.};
int ip = 0;
while (*nb == 0) {
pos.x = gcp->cornersCoord[ip][0];
pos.y = gcp->cornersCoord[ip][1];
#if dimension == 3
pos.z = gcp->cornersCoord[ip][2];
lpoint = locate(pos.x, pos.y, pos.z);
#elif dimension ==2
lpoint = locate(pos.x, pos.y);
#endif
Only one thread has the point in its domain (works in serial too).
if (lpoint.level > -1) {
Only this thread creates the Cache …
cache_append(&poscache, lpoint, 0);
and only this thread computes the number of boundary points …
/* Grains sends an equivalent radius that is multiplied by sqrt(3)/2 */
double lengthedge = 2.*(gcp->radius)/sqrt(3.);
/* printf ("lengthedge = %f on thread %d\n", lengthedge, pid() ); */
foreach_cache (poscache) {
*lN = floor (sqrt(3.)*lengthedge/(2.*Delta));
}
#if dimension == 2
/* number of points required for the 4 edges of the square */
*nb += (*lN-2)*4;
/* number of points required for the face */
*nb += (*lN-2)*(*lN-2);
/* number of points required for the 4 corners */
*nb += 4;
#elif dimension == 3
/* number of points required for the 12 edges of the cube */
*nb += (*lN-2)*12;
/* number of points required for the 6 faces of the cube */
*nb += 6*(*lN-2)*(*lN-2);
/* number of points required for the 8 corners */
*nb += 8;
#endif
and finally, this thread destroys the cache.
free(poscache.p);
}
#if _MPI
MPI_Barrier(MPI_COMM_WORLD);
mpi_all_reduce(*nb, MPI_INT, MPI_MAX);
mpi_all_reduce(*lN, MPI_INT, MPI_MAX);
#endif
if (ip < gcp->ncorners)
ip++;
else
break;
}
/* printf("lN number of points for a complete edge:%d\n",*lN); */
if (*nb == 0)
fprintf(stderr,"nboundary = 0: No boundary points for the cube/square !!!\n");
}
void distribute_points_edge_Cube_v2 (coord const corner1, coord const corner2, SolidBodyBoundary * dlm_bd, int const lN, int const istart) {
# if dimension == 3
if (lN > 0) {
coord dinc;
foreach_dimension() {
dinc.x = (corner2.x - corner1.x)/(lN-1);
}
/* printf( "length edge corner to corner = %f\n", sqrt( sq(corner2.x - corner1.x) + sq(corner2.y - corner1.y)+ sq(corner2.z - corner1.z))); */
for (int i = 1; i <= lN-2; i++) {
dlm_bd->x[istart + i -1] = corner1.x + (double)i * dinc.x;
dlm_bd->y[istart + i -1] = corner1.y + (double)i * dinc.y;
dlm_bd->z[istart + i -1] = corner1.z + (double)i * dinc.z;
}
}
#endif
}
bool is_it_in_cube_v2 (coord * u, coord * v, coord * w, coord * mins, coord * maxs, coord * checkpt) {
/* a point x with coord (x,y,z) lies in the rectangle if the 3
following conditions are satisfied */
/* 1- u.p0 <= u.x <= u.p3 */
/* 2- v.p0 <= v.x <= v.p1 */
/* 3- w.p0 <= w.w <= w.p4 */
double x = checkpt->x;
double y = checkpt->y;
double z = checkpt->z;
double checkval = 0.;
coord u1 = *u;
coord v1 = *v;
coord w1 = *w;
coord cpt = {0., 0., 0.};
bool isin = false;
checkval = u1.x*x + u1.y*y + u1.z*z;
cpt.x = checkval;
checkval = v1.x*x + v1.y*y + v1.z*z;
cpt.y = checkval;
checkval = w1.x*x + w1.y*y + w1.z*z;
cpt.z = checkval;
if ((cpt.x >= maxs->x) && (cpt.x <= mins->x) && (cpt.y >= maxs->y) && (cpt.y <= mins->y) && (cpt.z >= maxs->z) && (cpt.z <= mins->z) )
{
isin = true;
}
return isin;
}
void compute_principal_vectors_Cubes (particle * p) {
GeomParameter * gcp = &(p->g);
int nfaces = gcp->allFaces;
int npoints;
/* get the 3 directions u1, u2, u3 of the cube with such that */
/* u1 = corner0 - corner3; */
/* u2 = corner0 - corner1; */
/* u3 = corner0 - corner4; */
coord corner0 = {0, 0, 0}, corner1 = {0, 0, 0}, corner3 = {0, 0, 0}, corner4 = {0, 0, 0};
coord u1, v1, w1;
coord mins, maxs;
/* find the coordinates of these 4 corners */
for (int i = 0; i < nfaces; i++) {
npoints = gcp->numPointsOnFaces[i];
for (int j = 0; j < npoints; j++) {
/* printf("index = %lu\n", gcp->cornersIndex[i][j]); */
if (gcp->cornersIndex[i][j] == 0) {
long int ii = gcp->cornersIndex[i][j];
corner0.x = gcp->cornersCoord[ii][0];
corner0.y = gcp->cornersCoord[ii][1];
corner0.z = gcp->cornersCoord[ii][2];
/* printf("index = %lu with coordinate (%f,%f,%f)\n", gcp->cornersIndex[i][j], corner0.x, corner0.y, corner0.z); */
}
if (gcp->cornersIndex[i][j] == 1) {
long int ii = gcp->cornersIndex[i][j];
corner1.x = gcp->cornersCoord[ii][0];
corner1.y = gcp->cornersCoord[ii][1];
corner1.z = gcp->cornersCoord[ii][2];
/* printf("index = %lu with coordinate (%f,%f,%f)\n", gcp->cornersIndex[i][j], corner1.x, corner1.y, corner1.z); */
}
if (gcp->cornersIndex[i][j] == 3) {
long int ii = gcp->cornersIndex[i][j];
corner3.x = gcp->cornersCoord[ii][0];
corner3.y = gcp->cornersCoord[ii][1];
corner3.z = gcp->cornersCoord[ii][2];
/* printf("index = %lu with coordinate (%f,%f,%f)\n", gcp->cornersIndex[i][j], corner3.x, corner3.y, corner3.z); */
}
if (gcp->cornersIndex[i][j] == 4) {
long int ii = gcp->cornersIndex[i][j];
corner4.x = gcp->cornersCoord[ii][0];
corner4.y = gcp->cornersCoord[ii][1];
corner4.z = gcp->cornersCoord[ii][2];
/* printf("index = %lu with coordinate (%f,%f,%f)\n", gcp->cornersIndex[i][j], corner4.x, corner4.y, corner4.z); */
}
}
}
foreach_dimension() {
u1.x = corner0.x - corner3.x;
v1.x = corner0.x - corner1.x;
w1.x = corner0.x - corner4.x;
mins.x = 0.;
maxs.x = 0.;
}
gcp->u1 = u1;
gcp->v1 = v1;
gcp->w1 = w1;
double minval = 0., maxval = 0.;
foreach_dimension() {
minval += u1.x*corner0.x;
maxval += u1.x*corner3.x;
}
mins.x = (minval); maxs.x = (maxval);
minval = 0; maxval = 0;
foreach_dimension() {
minval += v1.x*corner0.x;
maxval += v1.x*corner1.x;
}
mins.y = (minval); maxs.y = (maxval);
minval = 0; maxval = 0;
foreach_dimension() {
minval += w1.x*corner0.x;
maxval += w1.x*corner4.x;
}
mins.z = (minval); maxs.z = (maxval);
gcp->mins = mins;
gcp->maxs = maxs;
}
/* void compute_principal_mins_maxs_Cubes (particle * p) { */
/* GeomParameter * gcp = &(p->g); */
/* coord u1 = gcp->u1; */
/* coord v1 = gcp->v1; */
/* coord w1 = gcp->w1; */
/* } */
void create_FD_Boundary_Cube_v2 (GeomParameter * gcp, SolidBodyBoundary * dlm_bd, const int m, const int lN, vector pshift) {
int nfaces = gcp->allFaces;
int iref, i1, i2, ichoice;
ichoice = 0;
int isb = 0;
int npoints;
/* Add first interrior points on surfaces */
for (int i = 0; i < nfaces; i++) {
npoints = gcp->numPointsOnFaces[i];
iref = gcp->cornersIndex[i][ichoice];
i1 = gcp->cornersIndex[i][ichoice + 1];
i2 = gcp->cornersIndex[i][npoints-1];
/* printf("on face %d, iref = %d, i1 = %d, i2 = %d\n", i, iref, i1, i2); */
coord refcorner = {gcp->cornersCoord[iref][0], gcp->cornersCoord[iref][1],gcp->cornersCoord[iref][2]} ;
coord dir1 = {gcp->cornersCoord[i1][0], gcp->cornersCoord[i1][1],gcp->cornersCoord[i1][2]};
coord dir2 = {gcp->cornersCoord[i2][0], gcp->cornersCoord[i2][1],gcp->cornersCoord[i2][2]};
/* printf ("corresponding coord for ref = (%f,%f,%f)\n",refcorner.x, refcorner.y, refcorner.z); */
/* printf ("corresponding coord for i1 = (%f,%f,%f)\n", dir1.x, dir1.y, dir1.z); */
/* printf ("corresponding coord for i2 = (%f,%f,%f)\n", dir2.x, dir2.y, dir2.z); */
foreach_dimension() {
dir1.x -= refcorner.x;
dir2.x -= refcorner.x;
dir1.x /= (lN-1);
dir2.x /= (lN-1);
}
for (int ii = 1; ii <= lN-2; ii++) {
for (int jj = 1; jj <= lN-2; jj++) {
dlm_bd->x[isb] = refcorner.x + (double) ii * dir1.x + (double) jj * dir2.x;
dlm_bd->y[isb] = refcorner.y + (double) ii * dir1.y + (double) jj * dir2.y;
dlm_bd->z[isb] = refcorner.z + (double) ii * dir1.z + (double) jj * dir2.z;
isb++;
}
}
}
int allindextable[8][8] = {{0}};
int j1,jm1;
/* Add points on the edges without the corners*/
for (int i = 0; i < nfaces; i++) {
npoints = gcp->numPointsOnFaces[i];
i1 = gcp->cornersIndex[i][1];
for (int j = 1; j < npoints; j++) {
jm1 = gcp->cornersIndex[i][j-1];
j1 = gcp->cornersIndex[i][j];
if (jm1 > j1) {
if (allindextable[jm1][j1] == 0) {
coord c1 = {gcp->cornersCoord[jm1][0], gcp->cornersCoord[jm1][1], gcp->cornersCoord[jm1][2]};
coord c2 = {gcp->cornersCoord[j1][0], gcp->cornersCoord[j1][1], gcp->cornersCoord[j1][2]};
distribute_points_edge_Cube_v2 (c1, c2, dlm_bd, lN, isb);
allindextable[jm1][j1] = 1;
isb +=lN-2;
}
}
else {
if (allindextable[j1][jm1] == 0) {
coord c1 = {gcp->cornersCoord[j1][0], gcp->cornersCoord[j1][1], gcp->cornersCoord[j1][2]};
coord c2 = {gcp->cornersCoord[jm1][0], gcp->cornersCoord[jm1][1], gcp->cornersCoord[jm1][2]};
distribute_points_edge_Cube_v2 (c1, c2, dlm_bd, lN, isb);
allindextable[j1][jm1] = 1;
isb +=lN-2;
}
}
/* printf ("isb after face %d = %d\n", i, isb); */
}
}
/* printf ("isb after all edges = %d\n", isb); */
/* Add the final 8 corners points */
for (int i = 0; i < gcp->ncorners; i++) {
dlm_bd->x[isb] = gcp->cornersCoord[i][0];
dlm_bd->y[isb] = gcp->cornersCoord[i][1];
dlm_bd->z[isb] = gcp->cornersCoord[i][2];
isb++;
}
/* printf ("isb after 8 corners = %d\n", isb); */
}
void create_FD_Interior_Cube_v2 (particle *p, vector Index_lambda, vector pshift) {
Cache * c;
Create the cache of the interior points for a cube
c = &(p->Interior);
/* compute the 3 principal vector of the cube */
compute_principal_vectors_Cubes (p);
/* a point x with coord (x,y,z) lies in the cube if the 3
following conditions are satisfied */
/* 1- u.p0 <= u.x <= u.p3 */
/* 2- v.p0 <= v.x <= v.p1 */
/* 3- w.p0 <= w.w <= w.p4 */
coord checkpt;
coord u1 = p->g.u1;
coord v1 = p->g.v1;
coord w1 = p->g.w1;
coord mins = p->g.mins;
coord maxs = p->g.maxs;
/* Min/Max coordinates for the AABB (Axed-Aligned-Bounding-Box) */
coord mincoord = {HUGE, HUGE, HUGE};
coord maxcoord = {-HUGE, -HUGE, -HUGE};
double ** table = p->g.cornersCoord;
for (int ii = 0; ii < p->g.ncorners; ii++) {
if (mincoord.x > table[ii][0])
mincoord.x = table[ii][0];
if (mincoord.y > table[ii][1])
mincoord.y = table[ii][1];
if (mincoord.z > table[ii][2])
mincoord.z = table[ii][2];
if (maxcoord.x < table[ii][0])
maxcoord.x = table[ii][0];
if (maxcoord.y < table[ii][1])
maxcoord.y = table[ii][1];
if (maxcoord.z < table[ii][2])
maxcoord.z = table[ii][2];
}
/* if (pid() == 0) { */
/* printf ("mincoord = (%f,%f,%f)\n",mincoord.x, mincoord.y, mincoord.z); */
/* printf ("maxcoord = (%f,%f,%f)\n",maxcoord.x, maxcoord.y, maxcoord.z); */
/* } */
foreach() {
checkpt.x = x;
checkpt.y = y;
checkpt.z = z;
/* Check only if the point is in the AABB (Axed-Aligned-Bounding-Box) */
if ((x > mincoord.x) && (x < maxcoord.x))
if ((y > mincoord.y) && (y < maxcoord.y))
if ((z > mincoord.z) && (z < maxcoord.z))
/* If yes: check if it is inside the cube now */
if (is_it_in_cube_v2 (&u1, &v1, &w1, &mins, &maxs, &checkpt)) {
cache_append (c, point, 0);
/* tagg cell with the number of the particle */
if ((int)Index_lambda.y[] == -1)
Index_lambda.y[] = p->pnum;
}
/* /\* For periodic boundaries, we have to check for the image-cubes as well *\/ */
/* if (Period.x) */
/* if (mincoord.x < 0 + X0) */
}
/* foreach_dimension() { */
/* if (Period.x) { */
/* } */
/* } */
cache_shrink (c);
}
void free_particles (particle * pp, const int n) {
for (int k = 0; k < n; k++) {
SolidBodyBoundary * sbm = &(pp[k].s);
free_SolidBodyBoundary(sbm);
Cache * c = &(pp[k].Interior);
free(c->p);
c = &(pp[k].reduced_domain);
free(c->p);
/* free corner coordinates tables */
double * cc;
GeomParameter * gg = &(pp[k].g);
for (int i = 0; i < gg->ncorners; i++) {
cc = &(gg->cornersCoord[i][0]);
if(cc) {
free(cc);
}
}
free(gg->cornersCoord);
/* free corner's indices tables */
for (int i = 0; i < gg->allFaces; i++) {
free(gg->cornersIndex[i]);
}
free(gg->cornersIndex);
free(gg->numPointsOnFaces);
}
}
Function that creates the scalar field where the cells contain the indices of the dlmfd points. If there is no dlmfd point it is tagged -1.
void create_index_lambda_scalar (const SolidBodyBoundary dlm_bd, vector Index_lambda, const int kk) {
Point lpoint;
int i;
Cache *fdlocal;
fdlocal = (Cache *){calloc(dlm_bd.m, sizeof(Cache))};
for (i = 0; i < dlm_bd.m; i++) {
#if dimension == 2
lpoint = locate(dlm_bd.x[i], dlm_bd.y[i]);
#elif dimension == 3
lpoint = locate(dlm_bd.x[i], dlm_bd.y[i], dlm_bd.z[i]);
#endif
/* */
if ((lpoint.level) == depth()) {
/* Create a cache for each fictitious domain's boundary point */
cache_append(&fdlocal[i], lpoint, 0);
foreach_cache(fdlocal[i]) {
/* Tag cell only if it was not tagged by another particle */
if (Index_lambda.x[] < 0){
Index_lambda.x[] = i;
Index_lambda.y[] = kk;
if (level != depth()) {
printf("On thread %d, point dlmfd %d at (%f, %f, %f) is in a cell that has not the maximum level of refinnement %d, it is on level %d \n", pid(), i, x, y, z, depth(), level);
}
}
}
}
}
for (i = 0; i < dlm_bd.m; i++) {
free(fdlocal[i].p);
}
free (fdlocal);
boundary ((scalar*) {Index_lambda});
}
#include "weights_functions_backup.h"
# ifndef STENCIL_EXTERIOR
# define STENCIL_EXTERIOR 0
# endif
# ifndef STENCIL_INTERIOR
# define STENCIL_INTERIOR 0
# endif
Function that returns the weight associated to a Lagrange multipliers for a cell within a foreach() loop (ie for a “real” or local cell) associated to a Lagrange multiplier in it’s neighborhood. The neighboor can be in another process (i.e be a ghost cell for the current thread).
double reversed_weight (particle * pp, const coord weightcellpos, const coord lambdacellpos, const coord lambdapos, const double delta, const coord pshift, const double a, const double b) {
/* this function has to be embedded within a double foreach_cache() and foreach_neighbor() loop */
coord rel = {0., 0., 0.};
coord relnl = {0., 0., 0.};
int NCX = 0, CX = 0, weight_id = 0;
size_t goflag = 0;
double weight = 0.;
GeomParameter gcb = pp->g;
GeomParameter gcbdum = gcb;
coord lambdaposdum = lambdapos;
/* Shift artificially the center of the particle and position of the
Lagrange multipliers for periodic boundary cases */
foreach_dimension() {
gcbdum.center.x = gcb.center.x + a*pshift.x;
lambdaposdum.x = lambdapos.x + b*pshift.x;
}
/* compute relative vector X_boundary - X_local = rel from the cell (containning the boundary) position to the boundary's (analytical) position .i.e vector goes from first argument to the second */
compute_relative_vector (lambdacellpos, lambdaposdum, &rel);
/* reset dials integers */
NCX = 0; CX = 0; weight_id = 0; goflag = 0;
/* assign first normal (gives the quadrant) */
assign_dial (rel, &CX);
/* assign fictitious-boundary's normal (use boundary's analytical position) */
assign_dial_fd_boundary (pp, lambdaposdum, gcbdum, delta, &NCX);
if (pp->iswall) {
#if STENCIL_EXTERIOR
/* Stencils oriented toward the wall */
if (lambdapos.y > 0) {
if (lambdapos.x < L0/2)
NCX = 1;
if (lambdapos.x > L0/2)
NCX = 2;
}
if (lambdapos.y < 0) {
if (lambdapos.x < L0/2)
NCX = 4;
if (lambdapos.x > L0/2)
NCX = 3;
}
#endif
#if STENCIL_INTERIOR
/* Second normal oriented toward the fluid */
if (lambdapos.y > 0) {
if (lambdapos.x < L0/2) {
NCX = 4;
}
if (lambdapos.x > L0/2) {
NCX = 3;
}
}
if (lambdapos.y < 0) {
if (lambdapos.x < L0/2) {
NCX = 1;
}
if (lambdapos.x > L0/2) {
NCX = 2;
}
}
#endif
}
/* compute relative vector from the neighbor cell to the local cell */
compute_relative_vector (weightcellpos, lambdacellpos , &relnl);
/* Assign weight ids. */
assign_weight_id_quad_outward (NCX, CX, relnl, delta, &weight_id, &goflag);
if (goflag == 1) {
weight = compute_weight_Quad (weight_id, lambdaposdum, lambdacellpos, NCX, CX, delta);
}
return weight;
}
void remove_too_close_multipliers (particle * p, vector index_lambda) {
for (int k = 0; k < NPARTICLES; k++) {
SolidBodyBoundary dlm_lambda_to_desactivate;
int allocated = 1;
allocate_SolidBodyBoundary (&dlm_lambda_to_desactivate, allocated*BSIZE);
int countalloc = 0;
int other_part;
int is_in_other_particle;
foreach_level(depth()) {
int direct_neigh = 0; is_in_other_particle = 0; other_part = -1;
if (((int)index_lambda.x[] > -1) && level == depth() && is_leaf(cell) && (p[k].pnum == (int)index_lambda.y[])) {
/* check here if this multiplier is not in another particle's
domain, if yes desactive it */
for (int l = 0; l < NPARTICLES; l++) {
if (l != p[k].pnum) {
particle * other_particle = &(p[l]);
/* printf("thread %d, this is particle %zu checkin on particle %zu iteration %d\n", pid(), p[k].pnum, other_particle->pnum, l); */
/* Check particle's type */
if (other_particle->iscube) {
compute_principal_vectors_Cubes (other_particle);
coord u = other_particle->g.u1;
coord v = other_particle->g.v1;
coord w = other_particle->g.w1;
coord mins = other_particle->g.mins;
coord maxs = other_particle->g.maxs;
/* current cell's position */
coord checkpt = {x, y, z};
if (is_it_in_cube_v2 (&u, &v, &w, &mins, &maxs, &checkpt)) {
is_in_other_particle = 1; other_part = other_particle->pnum;
break;
}
}
/* if sphere */
else {
GeomParameter gp = other_particle->g;
if (is_it_in_sphere (x, y, z, gp)) {
is_in_other_particle = 1; other_part = other_particle->pnum;
break;
}
}
}
}
if (is_in_other_particle) {
/* printf("thread %d, this is particle %zu which has a cell in particle %d\n", pid(), p[k].pnum, other_part); */
index_lambda.x[] = -1; index_lambda.y[] = other_part;
}
/* Check if two (or more) Lagrange multipliers (from different
particles) are in the same neighborhoods (i.e a 5x5 stencil
in 2D), if yes desactivate them. Otherwise the cells of
their stencils may be located in each other's domain */
direct_neigh = 0;
foreach_neighbor() {
if (((int)index_lambda.x[] > -1) && level == depth() && is_leaf(cell) && (p[k].pnum != (int)index_lambda.y[])) {
direct_neigh = 1;
/* We may catch and identical multiplier more than once here */
/* Add the multiplier(s) indices to a list because MPI imposes that we can't modify the cell within a foreach_neighbor loop */
if (countalloc >= allocated*BSIZE) {
allocated ++;
reallocate_SolidBodyBoundary (&dlm_lambda_to_desactivate, allocated*BSIZE);
}
dlm_lambda_to_desactivate.x[countalloc] = x;
dlm_lambda_to_desactivate.y[countalloc] = y;
#if dimension == 3
dlm_lambda_to_desactivate.z[countalloc] = z;
#endif
countalloc ++;
}
}
/* Desactivate the local multiplier here */
if (direct_neigh) {
index_lambda.x[] = -1; index_lambda.y[] = -1;
}
}
}
/* if (countalloc > 0) */
/* printf("there is %d points to desactivate on thread %d\n", countalloc, pid()); */
Cache c = {0};
Point lpoint;
#if _MPI
int size = npe();
int counts[size];
/* Each thread tells the root how many multipliers it holds */
MPI_Gather(&countalloc, 1, MPI_INT, &counts, 1, MPI_INT, 0, MPI_COMM_WORLD);
/* if (pid() == 0) */
/* for (int i = 0; i < size; i++) */
/* printf("particle %d, this is root receiving %d elements by thread %d\n",k, counts[i], i); */
/* Displacements in the receive buffer for MPI_GATHERV */
int disps[size];
/* Displacement for the first chunk of data - 0 */
for (int i = 0; i < size; i++)
disps[i] = (i > 0) ? (disps[i-1] + counts[i-1]) : 0;
double * alldatax = NULL;
double * alldatay = NULL;
#if dimension == 3
double * alldataz = NULL;
#endif
int m = 0;
/* Threads 0 allocates and compute the total number of multipliers */
if (pid() == 0) {
/* disps[size-1] + counts[size-1] == total number of elements */
/* printf("thread %d : disps[size-1]+counts[size-1] = %d\n", pid(), disps[size-1]+counts[size-1]); */
m = disps[size-1] + counts[size-1];
alldatax = (double*) calloc (m, sizeof(double));
alldatay = (double*) calloc (m, sizeof(double));
#if dimension == 3
alldataz = (double*) calloc (m, sizeof(double));
#endif
}
/* Gather on thread 0 the coordinates of the multipliers */
MPI_Gatherv (&dlm_lambda_to_desactivate.x[0], countalloc, MPI_DOUBLE, &alldatax[0], counts, disps, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Gatherv (&dlm_lambda_to_desactivate.y[0], countalloc, MPI_DOUBLE, &alldatay[0], counts, disps, MPI_DOUBLE, 0, MPI_COMM_WORLD);
#if dimension == 3
MPI_Gatherv (&dlm_lambda_to_desactivate.z[0], countalloc, MPI_DOUBLE, &alldataz[0], counts, disps, MPI_DOUBLE, 0, MPI_COMM_WORLD);
#endif
/* send the total number of multipliers to all threads */
mpi_all_reduce (m, MPI_INT, MPI_MAX);
/* allocate now alldatax, alldatay, alldataz on threads !=0 */
if (pid() != 0) {
alldatax = (double*) calloc (m, sizeof(double));
alldatay = (double*) calloc (m, sizeof(double));
#if dimension == 3
alldataz = (double*) calloc (m, sizeof(double));
#endif
}
/* Now broadcast alldatax,alldatay,alldataz from thread 0 to other threads */
MPI_Bcast (alldatax, m, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Bcast (alldatay, m, MPI_DOUBLE, 0, MPI_COMM_WORLD);
#if dimension == 3
MPI_Bcast (alldataz, m, MPI_DOUBLE, 0, MPI_COMM_WORLD);
#endif
/* for (int i = 0; i < m; i++) { */
/* #if dimension == 2 */
/* printf("thread %d: particle %d, coord of the multiplier %d to be removed (%g,%g,%g)\n", pid(), k, i, alldatax[i], alldatay[i], 0.); */
/* #elif dimension ==3 */
/* printf("thread %d: particle %d, coord of the multiplier %d to be removed (%g,%g,%g)\n", pid(), k, i, alldatax[i], alldatay[i], alldataz[i]); */
/* #endif */
/* } */
for (int i = 0; i < m; i++) {
#if dimension == 2
lpoint = locate(alldatax[i], alldatay[i]);
#elif dimension == 3
lpoint = locate(alldatax[i], alldatay[i], alldataz[i]);
#endif
if (lpoint.level > -1) cache_append(&c, lpoint, 0);
}
free(alldatax);
free(alldatay);
#if dimension == 3
free(alldataz);
#endif
#elif _MPI == 0
for (int i = 0; i < countalloc; i++) {
#if dimension == 2
lpoint = locate(dlm_lambda_to_desactivate.x[i], dlm_lambda_to_desactivate.y[i]);
#elif dimension == 3
lpoint = locate(dlm_lambda_to_desactivate.x[i], dlm_lambda_to_desactivate.y[i], dlm_lambda_to_desactivate.z[i]);
#endif
if (lpoint.level > -1) cache_append(&c, lpoint, 0);
}
#endif /* End if _MPI */
/* Finally desactivate the multiplicators */
int counter = 0;
foreach_cache(c) {
if (index_lambda.x[] > -1 && index_lambda.y[] > -1) {
index_lambda.x[] = -1;
index_lambda.y[] = -1;
counter ++;
}
}
/* printf("thread %d: removed %d multipliers \n", pid(), counter); */
free(c.p);
free_SolidBodyBoundary(&dlm_lambda_to_desactivate);
} /* End loop NPARTICLES */
boundary((scalar*) {index_lambda});
}
Function that tags cells associated to a Lagrange multiplier on the boundary of the fictitious domain.
void reverse_fill_flagfield (particle * p, scalar f, vector index_lambda, const int cacheflag) {
for (int k = 0; k < NPARTICLES; k++) {
coord rel = {0., 0., 0.};
coord relnl = {0., 0., 0.};
int NCX, CX, weight_id;
size_t goflag = 0;
coord lambdacellpos = {0., 0., 0.};
coord lambdapos = {0., 0., 0.};
coord localcellpos = {0., 0., 0.};
SolidBodyBoundary dlm_lambda = p[k].s;
GeomParameter gcb = p[k].g;
Cache * reduced_domain = &(p[k].reduced_domain);
GeomParameter gcbdum = gcb;
coord lambdaposdum = lambdapos;
foreach_level(depth()) {
localcellpos.x = x;
localcellpos.y = y;
#if dimension == 3
localcellpos.z = z;
#endif
goflag = 0;
// Check if there is a Lagrange multiplier in the neigborhood of this cell;
foreach_neighbor() {
if (((int)index_lambda.x[] > -1) && level == depth() && is_leaf(cell) && (p[k].pnum == (int)index_lambda.y[])) {
lambdacellpos.x = x; lambdacellpos.y = y;
lambdapos.x = dlm_lambda.x[(int)index_lambda.x[]];
lambdapos.y = dlm_lambda.y[(int)index_lambda.x[]];
#if dimension == 3
lambdacellpos.z = z;
lambdapos.z = dlm_lambda.z[(int)index_lambda.x[]];
#endif
/* Shift artificially the center of the particle and position of the
Lagrange multipliers for periodic boundary cases */
coord ppshift = {0., 0., 0.};
foreach_dimension() {
ppshift.x = 0.;
if (lambdacellpos.x < 0.) ppshift.x = -L0;
if (lambdacellpos.x > L0) ppshift.x = +L0;
gcbdum.center.x = gcb.center.x + ppshift.x;
lambdaposdum.x = lambdapos.x + ppshift.x;
}
/* compute relative vector X_boundary - X_local = rel from the cell (containning the boundary) position to the boundary's (analytical) position */
compute_relative_vector (lambdacellpos, lambdaposdum, &rel);
/* reset dials integers */
NCX = 0; CX = 0; weight_id = 0;
/* assign first normal (gives the quadrant) */
assign_dial (rel, &CX);
/* assign fictitious-boundary's normal (use boundary's position) */
assign_dial_fd_boundary (&p[k], lambdaposdum, gcbdum, Delta, &NCX);
if (p->iswall) {
#if STENCIL_EXTERIOR
/* Stencils oriented toward the wall */
if (lambdapos.y > 0) {
if (lambdapos.x < L0/2)
NCX = 1;
if (lambdapos.x > L0/2)
NCX = 2;
}
if (lambdapos.y <0) {
if (lambdapos.x < L0/2)
NCX = 4;
if (lambdapos.x > L0/2)
NCX = 3;
}
#endif
#if STENCIL_INTERIOR
/* Stencils oriented toward the fluid */
if ((lambdapos.y) > 0) {
if (lambdapos.x < L0/2)
NCX = 4;
if (lambdapos.x > L0/2)
NCX = 3;
}
if (lambdapos.y <0) {
if (lambdapos.x < L0/2) {
NCX = 1;
}
if (lambdapos.x > L0/2)
NCX = 2;
}
#endif
}
/* compute relative vector neighbor to the local cell */
compute_relative_vector (localcellpos, lambdacellpos , &relnl);
/* Assign weight ids. */
assign_weight_id_quad_outward (NCX, CX, relnl, Delta, &weight_id, &goflag);
} // end if (lambda.x[] > -1)
} // end foreach_neigboor loop
/* If the cell belongs to the stencil of a Lagrange multiplier tagg it and create a reduced domain to optimize the stencil-traversal cost */
if (goflag == 1) {
f[] = 1;
if (cacheflag == 1)
cache_append (reduced_domain, point, 0);
}
}
boundary ({f, index_lambda.x, index_lambda.y});
}// loop on particles id
}
Function that initialize the particles and the scalar/vector fields needed to the method
void allocate_and_init_particles (particle * p, const int n, vector e, scalar g, scalar h, vector pshift) {
foreach() {
e.x[] = -1;// index_lambda.x => lambda indices
e.y[] = -1;// particle structure/fd number;
e.z[] = -1;// constrained cells (-1:not constrained)
g[] = 0; // flagfield
h[] = 0; // flagfield_mailleur
/* Reset the shift vector for the periodic case */
if (Period.x || Period.y || Period.z) {
foreach_dimension() {
pshift.x[] = 0.;
}
}
}
boundary ({g, h});
boundary ((scalar *){e, pshift});
for (int k = 0; k < n; k++) {
Cache * c = NULL;
#if debugBD == 0
GeomParameter gci = p[k].g;
int m = 0;
/* default case, sphere */
if (!(p[k].iswall) && !(p[k].iscube)) {
compute_nboundary (gci, &m);
allocate_SolidBodyBoundary(&(p[k].s), m);
create_FD_Boundary_Particle (gci, &(p[k].s), m, pshift);
}
if (p[k].iswall) {
coord wallpos = p[k].wallmax;
compute_nboundary_Wall (wallpos, &m);
allocate_SolidBodyBoundary(&(p[k].s), m);
create_FD_Boundary_Wall (&p[k]);
}
if (p[k].iscube) {
int lN = 0;
compute_nboundary_Cube_v2 (&gci, &m, &lN);
allocate_SolidBodyBoundary(&(p[k].s), m);
create_FD_Boundary_Cube_v2 (&gci, &(p[k].s), m, lN, pshift);
}
create_index_lambda_scalar ((p[k].s), e, k);
c = &(p[k].reduced_domain);
allocate_Cache(c);
#endif
#if debugInterior == 0
c = &(p[k].Interior);
allocate_Cache(c);
#endif
}
}
#if DLM_Moving_particle
#if TRANSLATION
void compute_contact_distance (particle * p, const coord gci, double * delta) {
GeomParameter gc = p->g;
double radius = gc.radius;
*delta = gci.y - radius;
}
double compute_fwo (const double en, const double v, const double wo) {
double k = log(en)/sqrt(sq(pi) + sq(log(en)));
double g = atan(-sqrt(1 - sq(k))/k);
double val;
return val = v*exp(k*g/sqrt(1- sq(k)))*sin(g)/(wo*sqrt(1 - sq(k)));
}
double derivative_fwo (const double en, const double v, const double wo) {
double k = log(en)/sqrt(sq(pi) + sq(log(en)));
double g = atan(-sqrt(1 - sq(k))/k);
double val;
return val = -v*exp(k*g/sqrt(1 - sq(k)))*sin(g)/(sqrt(1 - sq(k))*sq(wo));
}
void compute_wo (particle * p) {
/* compute an estimate of (kn,gamman) derived from (deltamax,en) */
/* Guess value for wo */
double wo = 100.;
double epsilo = 1e-2;
int maxiter = 200;
double aa = 0.;
double bb = 0.;
GeomParameter g = p->g;
double r = g.radius;
double deltamax = (p->wished_ratio)*r;
double en = p->en;
double v = p->vzero;
/* Newton iteration to find the root of delta(w0) */
if (en < 1) {
for (int pp = 1; pp <= maxiter; pp++) {
aa = compute_fwo (wo, v, en) - deltamax;
bb = derivative_fwo (wo, v, en);
if (abs(aa-deltamax)/deltamax < epsilo) {
break;
}
wo += -aa/bb;
}
p->kn = (p->M)*sq(wo);
}
else {
/* if en = 1 the formula simplifies as such */
p->kn = (p->M)*sq(p->vzero)/(sq(deltamax));
}
}
void compute_Fontact (coord * Fc, particle * p, coord * gci, coord * U, const double gamman) {
double delta_colision = 0.;
foreach_dimension() {
(*Fc).x = 0.;
}
compute_contact_distance(p, *gci, &delta_colision);
if (delta_colision < 0.) {
double kn = p->kn;
double M = p->M;
coord vrel = *U;
coord Fel = {0., 0., 0.};
coord Fdm = {0., 0., 0.};
coord normalvec = p->normalvector;
foreach_dimension() {
/* compute Hookean elastic restoring force */
Fel.x = -kn*delta_colision*normalvec.x;
/* compute viscous dynamic force */
Fdm.x = -2.*gamman*M*fabs(vrel.x);
/* Fc is the sum of these two forces */
(*Fc).x = Fel.x + Fdm.x;
}
}
}
void granular_subproblem (particle * p, const int gravity_flag, const double dt, const double rho_f) {
// Mini Granular solver, which solves (1 - rho_f/rho_s)MdU/dt = (1-rho_f/rho_s)Mg + F_c
// (1 - rho_f/rho_s) Ip dw/dt = sum_all_particles (r x F_c) - (1 - rho_f/rho_s)w x Ip w
// where F_c is the contact force, g the gravity acceleration and M the particle's mass and Ip the inertia tensor
double wo = 0. ;
double gamman = 0.;
double T_c = 0., dtg = 0., M = 0., kn = 0., en = 0.;
int miter, gi;
coord Uold, Xold;
coord k1, k2, k3, k4, Xtemp, Utemp;
double fsf;
/* particle's structure pointers */
GeomParameter * gci;
GeomParameter * gcinm1;
coord * U;
coord * Unm1;
coord * gravity;
coord decal = {X0, Y0, Z0};
for (int k = 0; k < NPARTICLES; k++) {
fsf = (1. - (rho_f)/(p[k].rho_s));
miter = 0;
M = p[k].M;
kn = p[k].kn;
en = p[k].en;
/* compute wo and gamman */
wo = sqrt(kn/M); // wo = sqrt(2kn/M) for sphere-sphere contact and wo = sqrt(kn/M) for sphere-wall contact (Powder tech. Rakotonirina 2018)
gamman = -wo*log(en)/sqrt(sq(pi) + sq(log(en)));
/* compute the contact time Tc */
T_c = pi/(sqrt(sq(wo) - sq(gamman)));
/* set granular timestep */
dtg = T_c/20.;
miter = ceil(dt/dtg);
dtg = dt/miter;
if (k == 0) fprintf (stderr,"Tc = %g, dt = %20.18f, dtg = %20.18f, Tc/dtg = %g, miter = %d, particle = %d\n",T_c, dt, dtg, T_c/dtg, miter, k);
/* get particle structure pointers */
/* position */
gcinm1 = &(p[k].gnm1);
gci = &(p[k].g);
/* translational velocity */
U = &(p[k].U);
Unm1 = &(p[k].Unm1);
gravity = &(p[k].gravity);
/* fprintf (stderr,"gravity = (%f,%f,%f)\n",(*gravity).x,(*gravity).y,(*gravity).z); */
if (gravity_flag) {
/* Before solving the granular problem: save previous particle's
position and velocity (predictor step
with gravity, first subproblem after N-S) */
foreach_dimension() {
(*Unm1).x = (*U).x;
/* Check if the domain is periodic, if yes shift the particle's
position when the end of the domain is reached */
if (Period.x) {
if ((*gci).center.x > (L0 + decal.x)) {
(*gci).center.x -= L0;
}
if ((*gci).center.x < (0. + decal.x)) {
(*gci).center.x += L0;
}
}
(*gcinm1).center.x = (*gci).center.x;
}
}
else {
/* corrector step without gravity: fourth subproblem after the
fictitious domain problem (correction step) */
foreach_dimension() {
(*gci).center.x = (*gcinm1).center.x;
}
}
/* integrating in time with rk4 */
for (gi = 1; gi <= miter; gi++) {
Uold = (*U);
Xold = (*gci).center;
/* compute k1 */
compute_Fontact (&k1, &p[k], &Xold, &Uold, gamman);
foreach_dimension() {
k1.x /= (fsf*M);
k1.x += gravity_flag*(*gravity).x;
Xtemp.x = Xold.x + 0.5*dtg*Uold.x;
Utemp.x = Uold.x + 0.5*dtg*k1.x;
}
/* compute k2 */
compute_Fontact (&k2, &p[k], &Xtemp, &Utemp, gamman);
foreach_dimension() {
k2.x /= (fsf*M);
k2.x += gravity_flag*(*gravity).x;
Xtemp.x = Xold.x + 0.5*dtg*Uold.x + 0.25*sq(dtg)*k1.x;
Utemp.x = Uold.x + 0.5*dtg*k2.x;
}
/* compute k3 */
compute_Fontact (&k3, &p[k], &Xtemp, &Utemp, gamman);
foreach_dimension() {
k3.x /= (fsf*M);
k3.x += gravity_flag*(*gravity).x;
Xtemp.x = Xold.x + dtg*Uold.x + 0.5*sq(dtg)*k2.x;
Utemp.x = Uold.x + dtg*k3.x;
}
/* compute k4 */
compute_Fontact (&k4, &p[k], &Xtemp, &Utemp, gamman);
foreach_dimension() {
k4.x /= (fsf*M);
k4.x += gravity_flag*(*gravity).x;
}
foreach_dimension () {
(*gci).center.x = Xold.x + dtg*Uold.x + sq(dtg)*(k1.x + k2.x + k3.x)/6.;
/* Check if the domain is periodic, if yes shift the particle's
position when the end of the domain is reached */
if (Period.x) {
if ((*gci).center.x > (L0 + decal.x)) {
(*gci).center.x -= L0;
}
if ((*gci).center.x < (0. + decal.x)) {
(*gci).center.x += L0;
}
}
(*U).x = Uold.x + dtg*(k1.x + 2*k2.x + 2*k3.x + k4.x)/6.;
}
}
if (k == 0) {
if (gravity_flag) {
fprintf (stderr,"Prediction: particle-0's velocity on thread %d is (%20.18f, %20.18f, %20.18f)\n",pid(), (*U).x, (*U).y, (*U).z);
fprintf (stderr,"Prediction: particle-0's position on thread %d is (%20.18f, %20.18f, %20.18f)\n",pid(), (*gci).center.x, (*gci).center.y, (*gci).center.z);
}
else {
fprintf(stderr,"Correction: particle-0's velocity on thread %d is (%20.18f, %20.18f, %20.18f)\n",pid(), (*U).x, (*U).y, (*U).z);
fprintf(stderr,"Correction: particle-0's position on thread %d is (%20.18f, %20.18f, %20.18f)\n",pid(), (*gci).center.x, (*gci).center.y, (*gci).center.z);
}
}
}
}
#endif
#endif
Diagnostic functions.
void writer_headers (FILE * pdata, FILE * sl) {
#if _MPI
if(pid() == 0)
#endif
{
/* Write header for particles positions and velocities */
#if dimension == 2
fprintf(pdata, "#t\t position.x\t position.y\t U.x\t U.y\t w.z\n");
fprintf(sl, "#t\t F.x\t F.y\t T.z\n");
#elif dimension == 3
fprintf (pdata, "#t\t position.x\t position.y\t position.z\t U.x\t U.y\t U.z\t w.x\t w.y\t w.z\n");
fprintf (sl, "#t\t F.x\t F.y\t F.z\t T.x\t T.y\t T.z\n");
#endif
fflush(pdata);
fflush(sl);
}
}
void particle_data (particle * p, const double t, const int i, FILE ** pdata) {
for (int k = 0; k < NPARTICLES; k++) {
GeomParameter * GCi = &(p[k].g);
#if DLM_Moving_particle
#if TRANSLATION
coord * U = &(p[k].U);
#endif
#if ROTATION
coord * w = &(p[k].w);
#endif
#endif
#if dimension == 2
if(pid() == 0) {
fprintf(pdata[k], "%20.18f\t %20.18f\t %20.18f %20.18f\t %20.18f\t %20.18f \n", t, (*GCi).center.x, (*GCi).center.y
#if DLM_Moving_particle
#if TRANSLATION
, (*U).x, (*U).y
#elif TRANSLATION == 0
, 0., 0.
#endif
#if ROTATION
, (*w).z
#elif ROTATION == 0
, 0.
#endif
#elif DLM_Moving_particle == 0
, 0., 0.
, 0.
#endif
);
fflush(pdata[k]);
}
#elif dimension == 3
if(pid() == 0) {
fprintf(pdata[k], "%20.18f\t %20.18f\t %20.18f\t %20.18f\t %20.18f\t %20.18f %20.18f\t %20.18f\t %20.18f\t %20.18f\n", t, (*GCi).center.x, (*GCi).center.y, (*GCi).center.z
#if DLM_Moving_particle
#if TRANSLATION
, (*U).x, (*U).y, (*U).z
#elif TRANSLATION == 0
, 0., 0., 0.
#endif
#if ROTATION
,(*w).x, (*w).y, (*w).z
#elif ROTATION == 0
, 0., 0., 0.
#endif
#elif DLM_Moving_particle == 0
, 0., 0., 0.
, 0., 0., 0.
#endif
);
fflush(pdata[k]);
}
#endif
}
}
coord sumLambda (particle * p, FILE ** sl, const double t, const double dt, scalar Flagfield, vector Lambda, vector Index_lambda, const double rho_f, vector pshift) {
/* Compute hydrodynamic force & torque and write to a file */
/* Fh = <lambda,V>_P + (rho_f/rho_s)MdU/dt */
/* Mh = <lambda,xi^GM>_P + (rho_f/rho_s).( Idom/dt + om x (I.om)) */
/* The inertia tensor is: */
/* Ip[0] = Ixx */
/* Ip[1] = Iyy */
/* Ip[2] = Izz */
/* Ip[3] = Ixy */
/* Ip[4] = Ixz */
/* Ip[5] = Iyz */
coord lambdasumint;
coord lambdasumboundary;
coord lambdasum;
coord crossLambdaSumInt;
coord crossLambdaSumBoundary;
coord crossLambdaSum;
Cache * Interior[NPARTICLES];
Cache * Boundary[NPARTICLES];
GeomParameter * gci;
SolidBodyBoundary * sbm;
/* Loop over all particles */
for (int k = 0; k < NPARTICLES; k++) {
foreach_dimension() {
lambdasumint.x = 0.;
lambdasumboundary.x = 0.;
lambdasum.x = 0;
crossLambdaSumInt.x = 0.;
crossLambdaSumBoundary.x = 0.;
crossLambdaSum.x = 0.;
}
#if dimension ==2
lambdasumint.z = 0.;
lambdasumboundary.z = 0.;
lambdasum.z = 0;
crossLambdaSumInt.z = 0.;
crossLambdaSumBoundary.z = 0.;
crossLambdaSum.z = 0.;
#endif
/* Interior domain of the particle */
Interior[k] = &(p[k].Interior);
/* Boundary domain of the particle */
Boundary[k] = &(p[k].reduced_domain);
gci = &(p[k].g);
sbm = &(p[k].s);
coord lambdapos;
#if DLM_Moving_particle
double rho_s = p[k].rho_s;
#if TRANSLATION
double M = p[k].M;
coord Unm1 = p[k].Unm1;
coord U = p[k].U;
#endif
#if ROTATION
coord wnm1 = p[k].wnm1;
coord w = p[k].w;
coord Iom, omIom, Idomdt;
#endif
#endif
// Particule's interior multipliers
foreach_cache((*Interior[k])) {
if (Flagfield[] < 1 && k == Index_lambda.y[]) {
/* Compute Fh = <lambda,V>_P */
/* For moving particles the additional term +
(rho_f/rho_s).M.dU/dt is added after the mpi_calls below */
foreach_dimension() {
lambdasumint.x += Lambda.x[];
}
/* Compute Mh = <lambda,xi^GM>_P */
/* For moving particles, the additional term +
(rho_f/rho_s).(I.dom/dt + om ^ (I.om)) is added after mpi
calls below */
/* Modify temporerly the particle center position for periodic boundary condition */
foreach_dimension()
(*gci).center.x += pshift.x[];
crossLambdaSumInt.x += (Lambda.z[]*(y - (*gci).center.y) - Lambda.y[]*(z - (*gci).center.z));
crossLambdaSumInt.y += (Lambda.x[]*(z - (*gci).center.z) - Lambda.z[]*(x - (*gci).center.x));
crossLambdaSumInt.z += (Lambda.y[]*(x - (*gci).center.x) - Lambda.x[]*(y - (*gci).center.y));
foreach_dimension()
(*gci).center.x -= pshift.x[];
}
}//end foreach_cache()
// Particle's boundary multipliers
foreach_cache((*Boundary[k])) {
if ((Index_lambda.x[] > -1) && (p[k].pnum == (int)Index_lambda.y[])) {
lambdapos.x = (*sbm).x[(int)Index_lambda.x[]];
lambdapos.y = (*sbm).y[(int)Index_lambda.x[]];
lambdapos.z = 0.;
#if dimension == 3
lambdapos.z = (*sbm).z[(int)Index_lambda.x[]];
#endif
/* Compute Fh = <lambda,V>_P */
foreach_dimension() {
lambdasumboundary.x += Lambda.x[];
}
/* Modify temporerly the particle center position for periodic boundary condition */
foreach_dimension()
(*gci).center.x += pshift.x[];
/* Compute Mh = <lambda,xi^GM>_P */
crossLambdaSumBoundary.x += (Lambda.z[]*(lambdapos.y - (*gci).center.y) - Lambda.y[]*(lambdapos.z - (*gci).center.z));
crossLambdaSumBoundary.y += (Lambda.x[]*(lambdapos.z - (*gci).center.z) - Lambda.z[]*(lambdapos.x - (*gci).center.x));
crossLambdaSumBoundary.z += (Lambda.y[]*(lambdapos.x - (*gci).center.x) - Lambda.x[]*(lambdapos.y - (*gci).center.y));
foreach_dimension()
(*gci).center.x -= pshift.x[];
}
}
#if _MPI
foreach_dimension() {
mpi_all_reduce (lambdasumint.x, MPI_DOUBLE, MPI_SUM);
mpi_all_reduce (lambdasumboundary.x, MPI_DOUBLE, MPI_SUM);
mpi_all_reduce (crossLambdaSumInt.x, MPI_DOUBLE, MPI_SUM);
mpi_all_reduce (crossLambdaSumBoundary.x, MPI_DOUBLE, MPI_SUM);
}
#if dimension == 2
mpi_all_reduce (crossLambdaSumInt.z, MPI_DOUBLE, MPI_SUM);
mpi_all_reduce (crossLambdaSumBoundary.z, MPI_DOUBLE, MPI_SUM);
#endif
#endif
/* The Force term (rho_f/rho_s)MdU/dt is added here for
translating particles */
#if DLM_Moving_particle
#if TRANSLATION
foreach_dimension()
lambdasumint.x += (rho_f/rho_s)*M*(U.x - Unm1.x)/dt;
#endif
/* The Torque term (rho_f/rho_s).(Idom/dt + om ^ (I.om)) is added
here for rotating particles */
#if ROTATION
/* I.om term */
Iom.x = p[k].Ip[0]*w.x + p[k].Ip[3]*w.y + p[k].Ip[4]*w.z;
Iom.y = p[k].Ip[3]*w.x + p[k].Ip[1]*w.y + p[k].Ip[5]*w.z;
Iom.z = p[k].Ip[4]*w.x + p[k].Ip[5]*w.y + p[k].Ip[2]*w.z;
/* om^(I.om) term */
omIom.x = w.y*Iom.z - w.z*Iom.y;
omIom.y = w.z*Iom.x - w.x*Iom.z;
omIom.z = w.x*Iom.y - w.y*Iom.x;
/* Idom/dt term; */
Idomdt.x = p[k].Ip[0]*(w.x - wnm1.x) + p[k].Ip[3]*(w.y - wnm1.y) + p[k].Ip[4]*(w.z - wnm1.z);
Idomdt.y = p[k].Ip[3]*(w.x - wnm1.x) + p[k].Ip[1]*(w.y - wnm1.y) + p[k].Ip[5]*(w.z - wnm1.z);
Idomdt.z = p[k].Ip[4]*(w.x - wnm1.x) + p[k].Ip[5]*(w.y - wnm1.y) + p[k].Ip[2]*(w.z - wnm1.z);
crossLambdaSumInt.x += (rho_f/rho_s)*((Idomdt.x)/dt + omIom.x);
crossLambdaSumInt.y += (rho_f/rho_s)*((Idomdt.y)/dt + omIom.y);
crossLambdaSumInt.z += (rho_f/rho_s)*((Idomdt.z)/dt + omIom.z);
#endif
#endif
foreach_dimension() {
lambdasum.x = lambdasumint.x + lambdasumboundary.x;
crossLambdaSum.x = crossLambdaSumInt.x + crossLambdaSumBoundary.x;
}
#if dimension == 2
crossLambdaSum.z = crossLambdaSumInt.z + crossLambdaSumBoundary.z;
if(pid() == 0) {
fprintf(sl[k], "%f\t %20.18f\t %20.18f\t %20.18f\n", t, lambdasum.x, lambdasum.y, crossLambdaSum.z);
fflush(sl[k]);
}
#elif dimension == 3
if(pid() == 0) {
fprintf(sl[k], "%20.18f\t %20.18f\t %20.18f\t %20.18f\t %20.18f\t %20.18f\t %20.18f\n", t, lambdasum.x, lambdasum.y, lambdasum.z,
crossLambdaSum.x, crossLambdaSum.y, crossLambdaSum.z);
fflush(sl[k]);
}
#endif
}
return lambdasum;
}
void init_file_pointers (FILE ** p, FILE ** d, const size_t rflag) {
char name[800];
char name2[800];
#if _MPI
if (pid() == 0)
#endif
{
for (int k = 0; k < NPARTICLES; k++) {
sprintf (name, "particle-data-%d", k);
sprintf (name2, "sum_lambda-%d", k);
if (!rflag) {
p[k] = fopen (name, "w");
d[k] = fopen (name2, "w");
/* if (k == (NPARTICLES - 1)) { */
/* printf ("pointer sum lambda= %p\n", (void *)d[k]); */
/* printf ("pointer particle data = %p\n", (void *)p[k]); */
/* } */
/* Write headers in these files */
writer_headers (p[k], d[k]);
}
else {
p[k] = fopen (name, "a");
d[k] = fopen (name2, "a");
}
}
}
}
void vorticity_3D (const vector u, vector omega) {
foreach()
foreach_dimension()
omega.x[] = ( (u.z[0,1,0] - u.z[0,-1,0]) - (u.y[0,0,1] - u.y[0,0,-1]) )/2.*Delta;
boundary((scalar *){omega});
}
void compute_flowrate_right (FILE * pf, const vector u, const int level) {
coord velo = {0., 0., 0.};
foreach_dimension()
velo.x = 0.;
#if !adaptive
Cache intDomain = {0};
Point lpoint;
foreach() {
if (x > (L0 - Delta)) {
lpoint = locate(x, y, z);
cache_append(&intDomain, lpoint, 0);
}
}
cache_shrink(&intDomain);
foreach_cache(intDomain){
foreach_dimension()
velo.x += sq(Delta)*u.x[];
}
free(intDomain.p);
#endif
#if adaptive
double hh = L0/pow(2,level);
double xi = 0., yj = 0., zval = 0.;
int ii = 0, jj = 0;
foreach_level(level) {
zval = L0 - 0.5*Delta + Z0;
}
/* printf("zz = %f, h = %f\n", zval, hh); */
for (ii = 0; ii < pow(2,level); ii++) {
xi = 0.5*hh + ii*hh + X0;
for (jj = 0; jj < pow(2,level); jj++) {
yj = 0.5*hh + jj*hh + Y0;
coord uu = {0., 0., 0.};
foreach_dimension() {
uu.x = interpolate (u.x, xi, yj, zval);
if (uu.x < nodata)
velo.x += sq(hh)*uu.x;
}
/* printf("thread %d, u = (%f,%f,%f)\n",pid(), interpolate(u.x, xi, yj, zz), interpolate(u.y, xi, yj, zz), interpolate(u.z, xi, yj, zz)); */
}
}
#endif
/* printf("thread %d, velo = (%f,%f,%f)\n",pid(), velo.x, velo.y, velo.z); */
#if _MPI
foreach_dimension ()
mpi_all_reduce (velo.x, MPI_DOUBLE, MPI_SUM);
#endif
fprintf(pf, "%f\t %20.18f\t %20.18f\t %20.18f\n", t, velo.x, velo.y, velo.z);
fflush(pf);
}
void pressure_law (scalar pres, FILE * ppf, const coord hv, const double t) {
double plaw = 0.;
plaw = interpolate (pres, hv.x, hv.y, hv.z);
#if _MPI
MPI_Barrier(MPI_COMM_WORLD);
if (plaw == nodata)
plaw = 0.;
MPI_Barrier(MPI_COMM_WORLD);
if (plaw != nodata)
mpi_all_reduce(plaw, MPI_DOUBLE, MPI_SUM);
#endif
if (pid() == 0) {
fprintf(ppf, "%20.18f\t %20.18f\n", t, plaw);
fflush(ppf);
}
}
dump/resto particle’s structure functions
void dump_particle (particle * p) {
FILE * fp;
char * file = "dump_particle";
if (file && (fp = fopen (file, "w")) == NULL) {
perror (file);
exit (1);
}
assert (fp);
fwrite(p, sizeof(*p), NPARTICLES, fp);
fclose(fp);
}
bool restore_particle (particle * p) {
FILE * fp;
char * file = "dump_particle";
if (file && (fp = fopen (file, "r")) == NULL)
return false;
assert (fp);
if (fread (p, sizeof(*p), NPARTICLES, fp) < 1) {
fprintf (ferr, "restore_particle(): error reading particle data\n");
exit (1);
}
fclose(fp);
return true;
}
void inverse3by3matrix(double oldMatrix[3][3], double ** inversedMatrix) {
double block1 = oldMatrix[1][1]*oldMatrix[2][2] - oldMatrix[1][2]*oldMatrix[2][1];
double block2 = oldMatrix[1][2]*oldMatrix[2][0] - oldMatrix[1][0]*oldMatrix[2][2];
double block3 = oldMatrix[1][0]*oldMatrix[2][1] - oldMatrix[1][1]*oldMatrix[2][0];
double determinat = oldMatrix[0][0]*block1 + oldMatrix[0][1]*block2 + oldMatrix[0][2]*block3;
/* m[i][j], *(*(m + i) + j) */
*(*(inversedMatrix + 0) + 0) = block1/determinat;
*(*(inversedMatrix + 0) + 1) = (oldMatrix[0][2]*oldMatrix[2][1] - oldMatrix[0][1]*oldMatrix[2][2])/determinat;
*(*(inversedMatrix + 0) + 2) = (oldMatrix[0][1]*oldMatrix[1][2] - oldMatrix[0][2]*oldMatrix[1][1])/determinat;
*(*(inversedMatrix + 1) + 0) = block2 / determinat;
*(*(inversedMatrix + 1) + 1) = (oldMatrix[0][0]*oldMatrix[2][2] - oldMatrix[0][2]*oldMatrix[2][0])/determinat;
*(*(inversedMatrix + 1) + 2) = (oldMatrix[0][2]*oldMatrix[1][0] - oldMatrix[0][0]*oldMatrix[1][2])/determinat;
*(*(inversedMatrix + 2) + 0) = block3/determinat;
*(*(inversedMatrix + 2) + 1) = (oldMatrix[0][1]*oldMatrix[2][0] - oldMatrix[0][0]*oldMatrix[2][1])/determinat;
*(*(inversedMatrix + 2) + 2) = (oldMatrix[0][0]*oldMatrix[1][1] - oldMatrix[0][1]*oldMatrix[1][0])/determinat ;
}
int totalcells() {
int t = 0;
foreach() {
t++;
}
#if _MPI
mpi_all_reduce (t, MPI_INTEGER, MPI_SUM);
#endif
return t;
}
int total_dlmfd_cells (particle * p, const int np) {
int apts = 0;
for (int k = 0; k < np; k++) {
#if debugInterior == 0
apts += p[k].Interior.n;
#endif
#if debugBD == 0
apts += p[k].reduced_domain.n;
#endif
}
#if _MPI
mpi_all_reduce (apts, MPI_INTEGER, MPI_SUM);
#endif
return apts;
}
int total_dlmfd_multipliers (particle * p, const int np) {
int apts = 0;
for (int k = 0; k < np; k++) {
#if debugInterior == 0
apts += p[k].Interior.n;
#endif
}
#if _MPI
mpi_all_reduce (apts, MPI_INTEGER, MPI_SUM);
#endif
for (int k = 0; k < np; k++) {
#if debugBD == 0
SolidBodyBoundary * bla;
bla = &(p[k].s);
apts += bla->m;
#endif
}
return apts;
}
double getDelta() {
/* Return the smallest grid size */
/* ----------------------------- */
int ii = 0;
double dd = 0.;
foreach_level(depth()) {
ii++;
dd = Delta;
if (ii > 0)
break;
}
#if _MPI
mpi_all_reduce (dd, MPI_DOUBLE, MPI_MIN);
#endif
return dd;
}