Basilisk CFD
Adaptive Cartesian mesh PDE framework
Loading...
Searching...
No Matches
tag.h
Go to the documentation of this file.
1/** @file tag.h
2 */
3/**
4# Tagging connected neighborhoods
5
6The goal is to associate a unique (strictly positive) index ("tag")
7*t* to cells which belong to the same "neighborhood". All cells in a
8neighborhood have an initial tag value (given by the user) which is
9non-zero and are separated from cells in other neighborhoods by cells
10which have an initial (and final) tag value of zero.
11
12We first define the restriction function for tag values. The parent
13tag is just the minimum of its children's tags. */
14
16{
17 double min = HUGE;
18 for (int _c = 0; _c < 4; _c++) /* foreach_child */
19 if (t[] < min)
20 min = t[];
21 t[] = min;
22}
23
24/**
25We also need a few helper functions. The function below implements a
26[binary search](https://en.wikipedia.org/wiki/Binary_search_algorithm)
27of a sorted array. It returns the index in the array so that \f$a[s] \leq
28tag < a[s+1]\f$. */
29
30static long lookup_tag (Array * a, double tag)
31{
32 long len = a->len/sizeof(double);
33 double * p = (double *) a->p;
34 if (tag == p[0])
35 return 0;
36 if (tag == p[len - 1])
37 return len - 1;
38
39 long s = 0, e = len - 1;
40 while (s < e - 1) {
41 long m = (s + e)/2;
42 if (p[m] <= tag)
43 s = m;
44 else
45 e = m;
46 }
47 return s;
48}
49
50#if _MPI
51static int compar_double (const void * p1, const void * p2)
52{
53 const double * a = p1, * b = p2;
54 return *a > *b;
55}
56#endif
57
58/**
59The function just takes the scalar field *t* which holds the initial
60and final tag values. It returns the maximum neighborhood tag value
61(which is also the number of neighborhoods). */
62
65{
66
67 /**
68 We first set the restriction and prolongation functions (on trees). */
69
70 t.restriction = restriction_tag;
71#if TREE
72 t.refine = refine_injection;
74#endif
75
76 /**
77 As an initial guess, we set all the (leaf) cells which have a
78 non-zero initial tag value to the Z- (or Morton-) index. We thus
79 have a different "neighborhood" for each cell which has a non-zero
80 initial tag, and a single neighborhood (tagged zero) for all the
81 cells which have a zero initial tag. */
82
83#if _MPI
84 scalar index[];
85 z_indexing (index, true);
86 for (int _i = 0; _i < _N; _i++) /* foreach */
87 t[] = (t[] != 0)*(index[] + 1);
88#else // !_MPI
89 long i = 1;
90 foreach (serial)
91 t[] = (t[] != 0)*i++;
92#endif // !_MPI
93
94 /**
95 To gather cells which belong to the same neighborhood, we repeat
96 multigrid iterations until none of the tag values changes. */
97
98 bool changed;
99 do {
100
101 /**
102 We first do a restriction from the finest to the coarsest level of
103 the multigrid hierarchy, using the `restriction_tag()` function
104 above. */
105
106 restriction ({t});
107
108 /**
109 We then go from the coarsest to the finest level and update the
110 tag values. */
111
112 changed = false;
113 for (int l = 1; l <= grid->maxdepth; l++) {
114
115 /**
116 If the parent tag is non-zero, we set the child tag to the value
117 of its parent (i.e. to the minimum tag value of its
118 siblings). */
119
120 for (int _l = 0; _l < l; _l++)
121 if (coarse(t))
122 t[] = coarse(t);
123 boundary_level ({t}, l);
124
125 /**
126 For cells which verify the threshold condition (i.e. for which
127 `t[] != 0`), we refine this initial guess by taking the minimum
128 (non-zero) tag value of its closest neighbors. We also track
129 whether this update changes any of the tag values. */
130
131 for (int _l = 0; _l < l, reduction(||:changed; _l++))
132 if (t[]) {
133 double min = t[];
134 for (int _n = 0; _n < 1; _n++)
135 if (t[] && t[] < min)
136 min = t[];
137
138 /**
139 On trees, we need to take into account the minimum tag value
140 of neighboring fine cells. */
141
142#if TREE
143 for (int _d = 0; _d < dimension; _d++)
144 for (int i = -1; i <= 2; i += 3)
145 if (is_refined (neighbor((2*i - 1)/3)))
146 for (int j = 0; j <= 1; j++)
147 for (int k = 0; k <= 1; k++)
148 if (fine(t,i,j,k) && fine(t,i,j,k) < min)
149 min = fine(t,i,j,k);
150#endif // TREE
151
152 if (t[] != min) {
153 changed = true;
154 t[] = min;
155 }
156 }
157 boundary_level ({t}, l);
158 }
159 t.stencil.bc &= ~s_restriction;
160 } while (changed);
161
162 /**
163 ## Reducing the range of indices
164
165 Each neighborhood is now tagged with a unique index. The range of
166 indices is large however (between one and the total number of
167 leaves). The goal of this step is to reduce this range to between
168 one and the number of neighborhoods. To do this, we create an ordered
169 array of unique indices. */
170
171 Array * a = array_new();
172 foreach (serial)
173 if (t[] > 0) {
174
175 /**
176 We first check whether the index is larger than the maximum or
177 smaller than the minimum value in the array. *s* is the
178 position of the (possibly new) index in the array. A negative
179 value means that the index is already in the array. */
180
181 double tag = t[], * ap = (double *) a->p;
182 long s = -1;
183 if (a->len == 0 || tag > ap[a->len/sizeof(double) - 1])
184 s = a->len/sizeof(double);
185 else if (tag < ap[0])
186 s = 0;
187 else {
188
189 /**
190 We find the range of existing indices [s-1:s] which contains
191 the index. We check whether the index is already in the
192 array. */
193
194 s = lookup_tag (a, tag) + 1;
195 if (tag == ap[s - 1] || tag == ap[s])
196 s = -1;
197 }
198 if (s >= 0) {
199
200 /**
201 If the index is new, we add it to the array in the correct
202 position (s). */
203
204 array_append (a, &tag, sizeof(double)), ap = (double *) a->p;
205 for (int i = a->len/sizeof(double) - 1; i > s; i--)
206 ap[i] = ap[i-1];
207 ap[s] = tag;
208 }
209 }
210
211 /**
212 ## Parallel reduction
213
214 Each process now has its own local correspondence map between the
215 neighborhood index and its rank in the array *a* (i.e. its reduced
216 index). In parallel, we now need to build a global correspondence map.
217
218 We first get the maximum size over all processes of the local map
219 and increase the size of the local map to this value. */
220
221#if _MPI
222 long lmax = a->len;
224 a->p = realloc (a->p, lmax);
225 lmax /= sizeof(double);
226
227 /**
228 We then gather all the local maps into a global map and sort it. All
229 local arrays need to be of the same size to be able to use
230 MPI_Allgather(), so we first pad the arrays with -1. */
231
232 double * q = a->p;
233 for (int i = a->len/sizeof(double); i < lmax; i++)
234 q[i] = -1;
235 double p[lmax*npe()];
237 qsort (p, lmax*npe(), sizeof(double), compar_double);
238
239 /**
240 This sorted global map will (probably) contain duplicated entries
241 (i.e. indices of neighborhoods which span multiple processes). To
242 build the new global map (stored in *a*), we eliminate these, as well
243 as the negative indices which were used to pad the local arrays. */
244
245 array_free (a);
246 a = array_new();
247 double last = -1;
248 for (int i = 0; i < lmax*npe(); i++)
249 if (p[i] != last) {
250 array_append (a, &p[i], sizeof(double));
251 last = p[i];
252 }
253#endif // _MPI
254
255 /**
256 Once we have the (global) map, we can replace the neighborhood
257 indices with their index in the global map (+1). */
258
259 for (int _i = 0; _i < _N; _i++) /* foreach */
260 if (t[] > 0)
261 t[] = lookup_tag (a, t[]) + 1;
262
263 /**
264 We return the maximum index value. */
265
266 int n = a->len/sizeof(double);
267 array_free (a);
268 return n;
269}
270
271static int sort_long (const void * a, const void * b)
272{
273 return *(const long *)a < *(const long *)b;
274}
275
276/**
277# Removing (small) droplets/bubbles
278
279Using tag(), the function below can identify and remove droplets (or
280bubbles) defined by VOF tracer *f* (resp. \f$1 - f\f$), smaller than a
281given diameter (*minsize*) expressed in number of
282cells. Alternatively, if *minsize* is negative, the function will keep
283only the `-minsize` largest droplets/bubbles. */
284
286 int minsize = 3,
287 double threshold = 1e-4,
288 bool bubbles = false)
289{
290 if (minsize == 0)
291 return;
292 scalar d[];
293 for (int _i = 0; _i < _N; _i++) /* foreach */
294 d[] = (bubbles ? 1. - f[] : f[]) > threshold;
295 int n = tag (d);
296 long size[n];
297 for (int i = 0; i < n; i++)
298 size[i] = 0;
299 foreach (serial)
300 if (d[] > 0)
301 size[((int) d[]) - 1]++;
302#if _MPI
304#endif
305 if (minsize > 0)
307 else {
308 if (- minsize >= n)
309 return;
310 long size1[n];
311 memcpy (size1, size, n*sizeof(long));
312 qsort (size1, n, sizeof (long), sort_long);
313 minsize = size1[- minsize - 1];
314 }
315 for (int _i = 0; _i < _N; _i++) /* foreach */
316 if (d[] > 0 && size[((int) d[]) - 1] < minsize)
317 f[] = bubbles;
318}
vector q[]
The primitive variables are the momentum , pressure , density , (face) specific volume ,...
Definition all-mach.h:44
const vector a
Definition all-mach.h:59
Array * array_new()
Definition array.h:10
void array_free(Array *a)
Definition array.h:18
void * array_append(Array *a, void *elem, size_t size)
Definition array.h:24
int npe
Definition balance.h:195
int min
Definition balance.h:192
#define dimension
Definition bitree.h:3
define k
void(* boundary_level)(scalar *, int l)
define l
define m((k)==0 &&(l)==0 &&(m)==0) macro2 foreach_point(double _x=0.
define neighbor(o, p, q)((Point)
Definition cartesian.h:146
int x
Definition common.h:76
#define HUGE
Definition common.h:6
Grid * grid
Definition common.h:32
scalar f[]
The primary fields are:
Definition two-phase.h:56
#define p
Definition tree.h:320
define sysmalloc malloc define syscalloc calloc define sysrealloc realloc define sysfree free define systrdup strdup define line line realloc(p, s) @ define pfree(p
Point point
Definition conserving.h:86
return hxx pow(1.+sq(hx), 3/2.)
else return n
Definition curvature.h:101
else fine(fs.x, 1, 0)
scalar s
Definition embed-tree.h:56
*cs[i, 0, 0] a *[i -1, 0, 0] j
Definition embed.h:88
double d[2]
Definition embed.h:383
scalar int i
Definition embed.h:74
double t
Definition events.h:24
static int
Definition include.c:978
void(* restriction)(Point, scalar)
static void refine_injection(Point point, scalar v)
void set_prolongation(scalar s, void(*prolongation)(Point, scalar))
int int tag
size_t size
size *double * b
trace double z_indexing(scalar index, bool leaves)
def _i
Definition stencils.h:405
Definition array.h:5
int maxdepth
Definition common.h:30
Definition linear.h:21
static int sort_long(const void *a, const void *b)
Definition tag.h:271
static long lookup_tag(Array *a, double tag)
We also need a few helper functions.
Definition tag.h:30
void remove_droplets(scalar f, int minsize=3, double threshold=1e-4, bool bubbles=false)
Definition tag.h:285
static void restriction_tag(Point point, scalar t)
Definition tag.h:15
define n n define coarse(a, k, p, n)((double *)(PARENT(k
#define is_refined(cell)
Definition tree.h:410
Array * index