#define QUADTREE 1 // OBSOLETE: for backward-compatibility
#define TREE 1
#include "multigrid-common.h"
// scalar attributes
attribute {
void (* refine) (Point, scalar);
}
// Tree methods
#define periodic_clamp(a, level) do { \
if (a < GHOSTS) a += 1 << level; \
else if (a >= GHOSTS + (1 << level)) a -= 1 << level; } while(0)
/*
A cache of refined cells is maintained (if not NULL).
*/
int refine_cell (Point point, scalar * list, int flag, Cache * refined)
{
int nr = 0;
#if TWO_ONE
/* refine neighborhood if required */
if (level > 0)
for (int k = 0; k != 2*child.x; k += child.x)
#if dimension > 1
for (int l = 0; l != 2*child.y; l += child.y)
#endif
#if dimension > 2
for (int m = 0; m != 2*child.z; m += child.z)
#endif
if (aparent(k,l,m).pid >= 0 && is_leaf(aparent(k,l,m))) {
Point p = point;
/* fixme: this should be made
independent from the tree implementation */
p.level = point.level - 1;
p.i = (point.i + GHOSTS)/2 + k;
periodic_clamp (p.i, p.level);
#if dimension > 1
p.j = (point.j + GHOSTS)/2 + l;
periodic_clamp (p.j, p.level);
#endif
#if dimension > 2
p.k = (point.k + GHOSTS)/2 + m;
periodic_clamp (p.k, p.level);
#endif
nr += refine_cell (p, list, flag, refined);
aparent(k,l,m).flags |= flag;
}
#endif
/* update neighborhood */
increment_neighbors (point);
int cflag = is_active(cell) ? (active|leaf) : leaf;
foreach_child()
cell.flags |= cflag;
/* initialise scalars */
for (scalar s in list)
if (is_local(cell) || s.face)
s.refine (point, s);
/* refine */
cell.flags &= ~leaf;
@if _MPI
if (is_border(cell)) {
foreach_child() {
bool bord = false;
foreach_neighbor() {
if (!is_local(cell) || (level > 0 && !is_local(aparent(0)))) {
bord = true; break;
}
if (is_refined_check())
foreach_child()
if (!is_local(cell)) {
bord = true; break;
}
if (bord)
break;
}
if (bord)
cell.flags |= border;
}
if (refined)
cache_append (refined, point, cell.flags);
nr++;
}
@endif
return nr;
}
attribute {
void (* coarsen) (Point, scalar);
}
bool coarsen_cell (Point point, scalar * list)
{
#if TWO_ONE
/* check that neighboring cells are not too fine.
check that children are not different boundaries */
int pid = cell.pid;
foreach_child()
if (cell.neighbors || (cell.pid < 0 && cell.pid != pid))
return false; // cannot coarsen
#endif
/* restriction/coarsening */
for (scalar s in list) {
s.restriction (point, s);
if (s.coarsen)
s.coarsen (point, s);
}
/* coarsen */
cell.flags |= leaf;
/* update neighborhood */
decrement_neighbors (point);
@if _MPI
if (!is_local(cell)) {
cell.flags &= ~(active|border);
foreach_neighbor(1)
if (cell.neighbors)
foreach_child()
if (allocated(0) && is_local(cell) && !is_border(cell))
cell.flags |= border;
}
@endif
return true;
}
void coarsen_cell_recursive (Point point, scalar * list)
{
#if TWO_ONE
/* recursively coarsen children cells */
foreach_child()
if (cell.neighbors)
foreach_neighbor(1)
if (is_refined (cell))
coarsen_cell_recursive (point, list);
#endif
assert (coarsen_cell (point, list));
}
void mpi_boundary_refine (scalar *);
void mpi_boundary_coarsen (int, int);
void mpi_boundary_update (scalar *);
static
scalar * list_add_depend (scalar * list, scalar s)
{
if (is_constant(s) || s.restriction == no_restriction)
return list;
for (scalar t in list)
if (t.i == s.i)
return list;
for (scalar d in s.depends)
list = list_add_depend (list, d);
return list_append (list, s);
}
typedef struct {
int nc, nf;
} astats;
trace
astats adapt_wavelet (scalar * slist, // list of scalars
double * max, // tolerance for each scalar
int maxlevel, // maximum level of refinement
int minlevel = 1, // minimum level of refinement
scalar * list = all) // list of fields to update
{
scalar * ilist = list;
if (is_constant(cm)) {
if (list == NULL || list == all)
list = list_copy (all);
boundary (list);
restriction (slist);
}
else {
if (list == NULL || list == all) {
list = list_copy ({cm, fm});
for (scalar s in all)
list = list_add (list, s);
}
boundary (list);
scalar * listr = list_concat (slist, {cm});
restriction (listr);
free (listr);
}
astats st = {0, 0};
scalar * listc = NULL;
for (scalar s in list)
listc = list_add_depend (listc, s);
// refinement
if (minlevel < 1)
minlevel = 1;
tree->refined.n = 0;
static const int refined = 1 << user, too_fine = 1 << (user + 1);
foreach_cell() {
if (is_active(cell)) {
static const int too_coarse = 1 << (user + 2);
if (is_leaf (cell)) {
if (cell.flags & too_coarse) {
cell.flags &= ~too_coarse;
refine_cell (point, listc, refined, &tree->refined);
st.nf++;
}
continue;
}
else { // !is_leaf (cell)
if (cell.flags & refined) {
// cell has already been refined, skip its children
cell.flags &= ~too_coarse;
continue;
}
// check whether the cell or any of its children is local
bool local = is_local(cell);
if (!local)
foreach_child()
if (is_local(cell)) {
local = true; break;
}
if (local) {
int i = 0;
static const int just_fine = 1 << (user + 3);
for (scalar s in slist) {
double emax = max[i++], sc[1 << dimension];
int c = 0;
foreach_child()
sc[c++] = s[];
s.prolongation (point, s);
c = 0;
foreach_child() {
double e = fabs(sc[c] - s[]);
if (e > emax && level < maxlevel) {
cell.flags &= ~too_fine;
cell.flags |= too_coarse;
}
else if ((e <= emax/1.5 || level > maxlevel) &&
!(cell.flags & (too_coarse|just_fine))) {
if (level >= minlevel)
cell.flags |= too_fine;
}
else if (!(cell.flags & too_coarse)) {
cell.flags &= ~too_fine;
cell.flags |= just_fine;
}
s[] = sc[c++];
}
}
foreach_child() {
cell.flags &= ~just_fine;
if (!is_leaf(cell)) {
cell.flags &= ~too_coarse;
if (level >= maxlevel)
cell.flags |= too_fine;
}
else if (!is_active(cell))
cell.flags &= ~too_coarse;
}
}
}
}
else // inactive cell
continue;
}
mpi_boundary_refine (listc);
// coarsening
// the loop below is only necessary to ensure symmetry of 2:1 constraint
for (int l = depth(); l >= 0; l--) {
foreach_cell()
if (!is_boundary(cell)) {
if (level == l) {
if (!is_leaf(cell)) {
if (cell.flags & refined)
// cell was refined previously, unset the flag
cell.flags &= ~(refined|too_fine);
else if (cell.flags & too_fine) {
if (is_local(cell) && coarsen_cell (point, listc))
st.nc++;
cell.flags &= ~too_fine; // do not coarsen parent
}
}
if (cell.flags & too_fine)
cell.flags &= ~too_fine;
else if (level > 0 && (aparent(0).flags & too_fine))
aparent(0).flags &= ~too_fine;
continue;
}
else if (is_leaf(cell))
continue;
}
mpi_boundary_coarsen (l, too_fine);
}
free (listc);
mpi_all_reduce (st.nf, MPI_INT, MPI_SUM);
mpi_all_reduce (st.nc, MPI_INT, MPI_SUM);
if (st.nc || st.nf)
mpi_boundary_update (list);
if (list != ilist)
free (list);
return st;
}
#define refine(cond) do { \
int refined; \
do { \
boundary (all); \
refined = 0; \
tree->refined.n = 0; \
foreach_leaf() \
if (cond) { \
refine_cell (point, all, 0, &tree->refined); \
refined++; \
continue; \
} \
mpi_all_reduce (refined, MPI_INT, MPI_SUM); \
if (refined) { \
mpi_boundary_refine (all); \
mpi_boundary_update (all); \
} \
} while (refined); \
} while(0)
static void refine_level (int depth)
{
int refined;
do {
refined = 0;
tree->refined.n = 0;
foreach_leaf()
if (level < depth) {
refine_cell (point, NULL, 0, &tree->refined);
refined++;
continue;
}
mpi_all_reduce (refined, MPI_INT, MPI_SUM);
if (refined) {
mpi_boundary_refine (NULL);
mpi_boundary_update (NULL);
}
} while (refined);
}
#define unrefine(cond) do { \
static const int too_fine = 1 << user; \
foreach_cell() { \
if (is_leaf(cell)) \
continue; \
if (is_local(cell) && (cond)) \
cell.flags |= too_fine; \
} \
for (int _l = depth(); _l >= 0; _l--) { \
foreach_cell() { \
if (is_leaf(cell)) \
continue; \
if (level == _l) { \
if (is_local(cell) && (cell.flags & too_fine)) { \
coarsen_cell (point, all); \
cell.flags &= ~too_fine; \
} \
continue; \
} \
} \
mpi_boundary_coarsen (_l, too_fine); \
} \
mpi_boundary_update (all); \
} while (0)
trace
static void halo_face (vectorl vl)
{
foreach_dimension()
for (scalar s in vl.x)
s.dirty = 2;
for (int l = depth() - 1; l >= 0; l--)
foreach_halo (prolongation, l)
foreach_dimension()
if (vl.x) {
#if dimension == 1
if (is_refined (neighbor(-1)))
for (scalar s in vl.x)
s[] = fine(s,0);
if (is_refined (neighbor(1)))
for (scalar s in vl.x)
s[1] = fine(s,2);
#elif dimension == 2
if (is_refined (neighbor(-1)))
for (scalar s in vl.x)
s[] = (fine(s,0,0) + fine(s,0,1))/2.;
if (is_refined (neighbor(1)))
for (scalar s in vl.x)
s[1] = (fine(s,2,0) + fine(s,2,1))/2.;
#else // dimension == 3
if (is_refined (neighbor(-1)))
for (scalar s in vl.x)
s[] = (fine(s,0,0,0) + fine(s,0,1,0) +
fine(s,0,0,1) + fine(s,0,1,1))/4.;
if (is_refined (neighbor(1)))
for (scalar s in vl.x)
s[1] = (fine(s,2,0,0) + fine(s,2,1,0) +
fine(s,2,0,1) + fine(s,2,1,1))/4.;
#endif
}
}
// Cartesian methods
static scalar tree_init_scalar (scalar s, const char * name)
{
s = multigrid_init_scalar (s, name);
s.refine = s.prolongation;
return s;
}
static void prolongation_vertex (Point point, scalar s)
{
#if dimension == 2
fine(s,1,1) = (s[] + s[1] + s[0,1] + s[1,1])/4.;
#else // dimension == 3
fine(s,1,1,1) = (s[] + s[1] + s[0,1] + s[1,1] +
s[0,0,1] + s[1,0,1] + s[0,1,1] + s[1,1,1])/8.;
#endif
for (int i = 0; i <= 1; i++) {
for (int j = 0; j <= 1; j++)
#if dimension == 3
for (int k = 0; k <= 1; k++)
if (allocated_child(2*i,2*j,2*k))
fine(s,2*i,2*j,2*k) = s[i,j,k];
#else // dimension != 3
if (allocated_child(2*i,2*j))
fine(s,2*i,2*j) = s[i,j];
#endif // dimension != 3
foreach_dimension()
if (neighbor(i).neighbors) {
#if dimension == 2
fine(s,2*i,1) = (s[i,0] + s[i,1])/2.;
#elif dimension == 3
fine(s,2*i,1,1) = (s[i,0,0] + s[i,1,0] + s[i,0,1] + s[i,1,1])/4.;
fine(s,2*i,1,0) = (s[i,0,0] + s[i,1,0])/2.;
fine(s,2*i,0,1) = (s[i,0,0] + s[i,0,1])/2.;
if (allocated_child(2*i,1,2))
fine(s,2*i,1,2) = (s[i,0,1] + s[i,1,1])/2.;
if (allocated_child(2*i,2,1))
fine(s,2*i,2,1) = (s[i,1,0] + s[i,1,1])/2.;
#endif // dimension == 3
}
}
}
static scalar tree_init_vertex_scalar (scalar s, const char * name)
{
s = multigrid_init_vertex_scalar (s, name);
s.refine = s.prolongation = prolongation_vertex;
return s;
}
static void tree_setup_vector (vector v)
{
foreach_dimension()
v.x.refine = v.x.prolongation;
}
static vector tree_init_vector (vector v, const char * name)
{
v = multigrid_init_vector (v, name);
tree_setup_vector (v);
return v;
}
foreach_dimension()
static void refine_face_x (Point point, scalar s)
{
vector v = s.v;
#if dimension <= 2
if (!is_refined(neighbor(-1)) &&
(is_local(cell) || is_local(neighbor(-1)))) {
double g1 = (v.x[0,+1] - v.x[0,-1])/8.;
for (int j = 0; j <= 1; j++)
fine(v.x,0,j) = v.x[] + (2*j - 1)*g1;
}
if (!is_refined(neighbor(1)) && neighbor(1).neighbors &&
(is_local(cell) || is_local(neighbor(1)))) {
double g1 = (v.x[1,+1] - v.x[1,-1])/8.;
for (int j = 0; j <= 1; j++)
fine(v.x,2,j) = v.x[1] + (2*j - 1)*g1;
}
if (is_local(cell)) {
double g1 = (v.x[0,+1] - v.x[0,-1] + v.x[1,+1] - v.x[1,-1])/16.;
for (int j = 0; j <= 1; j++)
fine(v.x,1,j) = (v.x[] + v.x[1])/2. + (2*j - 1)*g1;
}
#else // dimension > 2
if (!is_refined(neighbor(-1)) &&
(is_local(cell) || is_local(neighbor(-1)))) {
double g1 = (v.x[0,+1] - v.x[0,-1])/8.;
double g2 = (v.x[0,0,+1] - v.x[0,0,-1])/8.;
for (int j = 0; j <= 1; j++)
for (int k = 0; k <= 1; k++)
fine(v.x,0,j,k) = v.x[] + (2*j - 1)*g1 + (2*k - 1)*g2;
}
if (!is_refined(neighbor(1)) && neighbor(1).neighbors &&
(is_local(cell) || is_local(neighbor(1)))) {
double g1 = (v.x[1,+1] - v.x[1,-1])/8.;
double g2 = (v.x[1,0,+1] - v.x[1,0,-1])/8.;
for (int j = 0; j <= 1; j++)
for (int k = 0; k <= 1; k++)
fine(v.x,2,j,k) = v.x[1] + (2*j - 1)*g1 + (2*k - 1)*g2;
}
if (is_local(cell)) {
double g1 = (v.x[0,+1] - v.x[0,-1] + v.x[1,+1] - v.x[1,-1])/16.;
double g2 = (v.x[0,0,+1] - v.x[0,0,-1] + v.x[1,0,+1] - v.x[1,0,-1])/16.;
for (int j = 0; j <= 1; j++)
for (int k = 0; k <= 1; k++)
fine(v.x,1,j,k) = (v.x[] + v.x[1])/2. + (2*j - 1)*g1 + (2*k - 1)*g2;
}
#endif // dimension > 2
}
void refine_face (Point point, scalar s)
{
vector v = s.v;
foreach_dimension()
v.x.prolongation (point, v.x);
}
void refine_face_solenoidal (Point point, scalar s)
{
refine_face (point, s);
#if dimension > 1
if (is_local(cell)) {
// local projection, see section 3.3 of Popinet, JCP, 2009
vector v = s.v;
double d[1 << dimension], p[1 << dimension];
int i = 0;
foreach_child() {
d[i] = 0.;
foreach_dimension()
d[i] += v.x[1] - v.x[];
i++;
}
#if dimension == 2
p[0] = 0.;
p[1] = (3.*d[3] + d[0])/4. + d[2]/2.;
p[2] = (d[3] + 3.*d[0])/4. + d[2]/2.;
p[3] = (d[3] + d[0])/2. + d[2];
fine(v.x,1,1) += p[1] - p[0];
fine(v.x,1,0) += p[3] - p[2];
fine(v.y,0,1) += p[0] - p[2];
fine(v.y,1,1) += p[1] - p[3];
#elif dimension == 3
static double m[7][7] = {
{7./12,5./24,3./8,5./24,3./8,1./4,1./3},
{5./24,7./12,3./8,5./24,1./4,3./8,1./3},
{3./8,3./8,3./4,1./4,3./8,3./8,1./2},
{5./24,5./24,1./4,7./12,3./8,3./8,1./3},
{3./8,1./4,3./8,3./8,3./4,3./8,1./2},
{1./4,3./8,3./8,3./8,3./8,3./4,1./2},
{1./3,1./3,1./2,1./3,1./2,1./2,5./6}
};
p[0] = 0.;
for (int i = 0; i < 7; i++) {
p[i + 1] = 0.;
for (int j = 0; j < 7; j++)
p[i + 1] += m[i][j]*d[j+1];
}
for (int k = 0; k <= 1; k++) {
fine(v.x,1,0,k) += p[4+k] - p[0+k];
fine(v.x,1,1,k) += p[6+k] - p[2+k];
fine(v.y,0,1,k) += p[2+k] - p[0+k];
fine(v.y,1,1,k) += p[6+k] - p[4+k];
}
fine(v.z,0,0,1) += p[1] - p[0];
fine(v.z,0,1,1) += p[3] - p[2];
fine(v.z,1,0,1) += p[5] - p[4];
fine(v.z,1,1,1) += p[7] - p[6];
#endif // dimension == 3
}
#endif // dimension > 1
}
static vector tree_init_face_vector (vector v, const char * name)
{
v = cartesian_init_face_vector (v, name);
foreach_dimension()
v.x.restriction = v.x.refine = no_restriction;
v.x.restriction = restriction_face;
v.x.refine = refine_face;
foreach_dimension()
v.x.prolongation = refine_face_x;
return v;
}
static tensor tree_init_tensor (tensor t, const char * name)
{
t = multigrid_init_tensor (t, name);
foreach_dimension()
tree_setup_vector (t.x);
return t;
}
trace
static void tree_boundary_level (scalar * list, int l)
{
int depth = l < 0 ? depth() : l;
if (tree_is_full()) {
boundary_iterate (level, list, depth);
return;
}
scalar * listdef = NULL, * listc = NULL, * list2 = NULL, * vlist = NULL;
for (scalar s in list)
if (!is_constant (s)) {
if (s.restriction == restriction_average) {
listdef = list_add (listdef, s);
list2 = list_add (list2, s);
}
else if (s.restriction != no_restriction) {
listc = list_add (listc, s);
if (s.face)
foreach_dimension()
list2 = list_add (list2, s.v.x);
else {
list2 = list_add (list2, s);
if (s.restriction == restriction_vertex)
vlist = list_add (vlist, s);
}
}
}
if (vlist) // vertex scalars
#if dimension == 1
foreach_vertex (noauto)
if (is_refined(cell) || is_refined(neighbor(-1)))
for (scalar s in vlist)
s[] = is_vertex (child(0)) ? fine(s) : nodata;
#elif dimension == 2
foreach_vertex (noauto) {
if (is_refined(cell) || is_refined(neighbor(-1)) ||
is_refined(neighbor(0,-1)) || is_refined(neighbor(-1,-1))) {
// corner
for (scalar s in vlist)
s[] = is_vertex (child(0)) ? fine(s) : nodata;
}
else
foreach_dimension()
if (child.y == 1 &&
(is_prolongation(cell) || is_prolongation(neighbor(-1)))) {
// center of refined edge
for (scalar s in vlist)
s[] = is_vertex(neighbor(0,-1)) && is_vertex(neighbor(0,1)) ?
(s[0,-1] + s[0,1])/2. : nodata;
}
}
#else // dimension == 3
foreach_vertex (noauto) {
if (is_refined(cell) || is_refined(neighbor(-1)) ||
is_refined(neighbor(0,-1)) || is_refined(neighbor(-1,-1)) ||
is_refined(neighbor(0,0,-1)) || is_refined(neighbor(-1,0,-1)) ||
is_refined(neighbor(0,-1,-1)) || is_refined(neighbor(-1,-1,-1))) {
// corner
for (scalar s in vlist)
s[] = is_vertex (child(0)) ? fine(s) : nodata;
}
else
foreach_dimension() {
if (child.y == 1 && child.z == 1 &&
(is_prolongation(cell) || is_prolongation(neighbor(-1)))) {
// center of refined face
for (scalar s in vlist)
s[] = is_vertex(neighbor(0,-1,-1)) && is_vertex(neighbor(0,1,-1))
&& is_vertex(neighbor(0,-1,1)) && is_vertex(neighbor(0,1,1)) ?
(s[0,-1,-1] + s[0,1,-1] + s[0,-1,1] + s[0,1,1])/4. : nodata;
}
else if (child.x == -1 && child.z == -1 && child.y == 1 &&
(is_prolongation(cell) || is_prolongation(neighbor(-1)) ||
is_prolongation(neighbor(0,0,-1)) ||
is_prolongation(neighbor(-1,0,-1)))) {
// center of refined edge
for (scalar s in vlist)
s[] = is_vertex(neighbor(0,-1)) && is_vertex(neighbor(0,1)) ?
(s[0,-1] + s[0,1])/2. : nodata;
}
}
}
#endif // dimension == 3
free (vlist);
if (listdef || listc) {
boundary_iterate (restriction, list2, depth);
for (int l = depth - 1; l >= 0; l--) {
foreach_coarse_level(l, nowarning) {
for (scalar s in listdef)
restriction_average (point, s);
for (scalar s in listc)
s.restriction (point, s);
}
boundary_iterate (restriction, list2, l);
}
free (listdef);
free (listc);
free (list2);
}
scalar * listr = NULL;
vector * listf = NULL;
for (scalar s in list)
if (!is_constant (s) && s.refine != no_restriction) {
if (s.face)
listf = vectors_add (listf, s.v);
else
listr = list_add (listr, s);
}
if (listr || listf) {
boundary_iterate (level, list, 0);
for (int i = 0; i < depth; i++) {
foreach_halo (prolongation, i) {
for (scalar s in listr)
s.prolongation (point, s);
for (vector v in listf)
foreach_dimension()
v.x.prolongation (point, v.x);
}
boundary_iterate (level, list, i + 1);
}
free (listr);
free (listf);
}
}
double treex (Point point) {
if (level == 0)
return 0;
#if dimension == 2
double i = 2*child.x - child.y;
if (i <= 1 && i >= -1) i = -i;
#else
assert (false); // not implemented
double i = 0;
#endif
return treex(parent) + i/(1 << 2*(level - 1));
}
double treey (Point point) {
if (level == 0)
return 0;
return treey(parent) + 4./(1 << 2*(level - 1));
}
void output_tree (FILE * fp)
{
foreach_cell()
if (cell.neighbors)
foreach_child()
if (is_local(cell))
fprintf (fp, "%g %g\n%g %g\n\n",
treex(parent), treey(parent), treex(point), treey(point));
}
trace
void tree_check()
{
// checks the consistency of the tree
long nleaves = 0, nactive = 0;
foreach_cell_all() {
if (is_leaf(cell)) {
assert (cell.pid >= 0); // boundaries cannot be leaves
nleaves++;
}
if (is_local(cell))
assert (is_active(cell) || is_prolongation(cell));
if (is_active(cell))
nactive++;
// check number of refined neighbors
int neighbors = 0;
foreach_neighbor(1)
if (allocated(0) && is_refined(cell))
neighbors++;
assert (cell.neighbors == neighbors);
// checks that prolongation cells do not have children
if (!cell.neighbors)
assert (!allocated_child(0));
}
// checks that all active cells are reachable
long reachable = 0;
foreach_cell() {
if (is_active(cell))
reachable++;
else
continue;
}
assert (nactive == reachable);
// checks that all leaf cells are reachable
reachable = 0;
foreach_cell()
if (is_leaf(cell)) {
reachable++;
continue;
}
assert (nleaves == reachable);
}
trace
static void tree_restriction (scalar * list) {
boundary (list);
if (tree_is_full())
multigrid_restriction (list);
}
void tree_methods()
{
multigrid_methods();
init_scalar = tree_init_scalar;
init_vertex_scalar = tree_init_vertex_scalar;
init_vector = tree_init_vector;
init_face_vector = tree_init_face_vector;
init_tensor = tree_init_tensor;
boundary_level = tree_boundary_level;
boundary_face = halo_face;
restriction = tree_restriction;
}