-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathembed-tree.h
More file actions
399 lines (345 loc) · 12.1 KB
/
embed-tree.h
File metadata and controls
399 lines (345 loc) · 12.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
/**
# Embedded boundaries on adaptive trees
This file defines the restriction/prolongation functions which are
necessary to implement [embedded boundaries](embed.h) on adaptive
meshes.
## Volume fraction field *cs*
For the embedded fraction field *cs*, the function below is modelled
closely on the volume fraction refinement function
[fraction_refine()](fractions.h#fraction_refine). */
static void embed_fraction_refine (Point point, scalar cs)
{
double cc = cs[];
/**
If the cell is empty or full, simple injection from the coarse cell
value is used. */
if (cc <= 0. || cc >= 1.) {
foreach_child()
cs[] = cc;
}
else {
/**
If the cell contains the embedded boundary, we reconstruct the
boundary using VOF linear reconstruction and a normal estimated
from the surface fractions. */
coord n = facet_normal (point, cs, fs);
double alpha = plane_alpha (cc, n);
foreach_child() {
static const coord a = {0.,0.,0.}, b = {.5,.5,.5};
coord nc;
foreach_dimension()
nc.x = child.x*n.x;
cs[] = rectangle_fraction (nc, alpha, a, b);
}
}
}
/**
## Surface fractions field *fs*
The embedded surface fractions *fs* are reconstructed using this
function. */
foreach_dimension()
static void embed_face_fraction_refine_x (Point point, scalar s)
{
vector fs = s.v;
/**
If the cell is empty or full, simple injection from the coarse cell
value is used. */
if (cs[] <= 0. || cs[] >= 1.) {
/**
We need to make sure that the fine cells face fractions match
those of their neighbours. */
for (int j = 0; j <= 1; j++)
for (int k = 0; k <= 1; k++)
fine(fs.x,1,j,k) = cs[];
for (int i = 0; i <= 1; i++)
if (!is_refined(neighbor(2*i-1)) && neighbor(2*i-1).neighbors &&
(is_local(cell) || is_local(neighbor(2*i-1))))
for (int j = 0; j <= 1; j++)
for (int k = 0; k <= 1; k++)
fine(fs.x,2*i,j,k) = fs.x[i];
}
else {
/**
If the cell contains the embedded boundary, we reconstruct the
boundary using VOF linear reconstruction and a normal estimated
from the surface fractions. */
coord n = facet_normal (point, cs, fs);
double alpha = plane_alpha (cs[], n);
/**
We need to reconstruct the face fractions *fs* for the fine cells.
For the fine face fractions contained within the coarse cell,
we compute the intersections directly using the VOF
reconstruction. */
#if dimension == 2
/**
In 2D, we obtain the face fractions by taking into
account the orientation of the normal. */
if (2.*fabs(alpha) < fabs(n.y)) {
double yc = alpha/n.y;
int i = yc > 0.;
fine(fs.x,1,1 - i) = n.y < 0. ? 1. - i : i;
fine(fs.x,1,i) = n.y < 0. ? i - 2.*yc : 1. - i + 2.*yc;
}
else
fine(fs.x,1,0) = fine(fs.x,1,1) = alpha > 0.;
#else // dimension == 3
/**
in 3D, we use the 2D projection of the reconstruction. */
for (int j = 0; j <= 1; j++)
for (int k = 0; k <= 1; k++)
if (!fine(cs,0,j,k) || !fine(cs,1,j,k))
fine(fs.x,1,j,k) = 0.;
else {
static const coord a = {0.,0.,0.}, b = {.5,.5,.5};
coord nc;
nc.x = 0., nc.y = (2.*j - 1.)*n.y, nc.z = (2.*k - 1.)*n.z;
fine(fs.x,1,j,k) = rectangle_fraction (nc, alpha, a, b);
}
#endif // dimension == 3
/**
For the fine face fractions coincident with the faces of the
coarse cell, we obtain the intersection position from the
coarse cell face fraction. */
for (int i = 0; i <= 1; i++)
if (neighbor(2*i-1).neighbors &&
(is_local(cell) || is_local(neighbor(2*i-1)))) {
if (!is_refined(neighbor(2*i-1))) {
if (fs.x[i] <= 0. || fs.x[i] >= 1.)
for (int j = 0; j <= 1; j++)
for (int k = 0; k <= 1; k++)
fine(fs.x,2*i,j,k) = fs.x[i];
else {
#if dimension == 2
/**
In 2D the orientation is obtained by looking at the values
of face fractions in the transverse direction. */
double a = fs.y[0,1] <= 0. || fs.y[2*i-1,1] <= 0. ||
fs.y[] >= 1. || fs.y[2*i-1] >= 1.;
if ((2.*a - 1)*(fs.x[i] - 0.5) > 0.) {
fine(fs.x,2*i,0) = a;
fine(fs.x,2*i,1) = 2.*fs.x[i] - a;
}
else {
fine(fs.x,2*i,0) = 2.*fs.x[i] + a - 1.;
fine(fs.x,2*i,1) = 1. - a;
}
#else // dimension == 3
/**
In 3D we reconstruct the face fraction from the projection
of the cell interface reconstruction, as above. */
for (int j = 0; j <= 1; j++)
for (int k = 0; k <= 1; k++) {
static const coord a = {0.,0.,0.}, b = {.5,.5,.5};
coord nc;
nc.x = 0., nc.y = (2.*j - 1.)*n.y, nc.z = (2.*k - 1.)*n.z;
fine(fs.x,2*i,j,k) =
rectangle_fraction (nc, alpha - n.x*(2.*i - 1.)/2., a, b);
}
#endif // dimension == 3
}
}
/**
The face fractions of empty children cells must be zero. */
for (int j = 0; j <= 1; j++)
#if dimension > 2
for (int k = 0; k <= 1; k++)
#endif
if (fine(fs.x,2*i,j,k) && !fine(cs,i,j,k))
fine(fs.x,2*i,j,k) = 0.;
}
}
}
/**
## Restriction of cell-centered fields
We now define restriction and prolongation functions for cell-centered
fields. The goal is to define second-order operators which do not use
any values from cells entirely contained within the embedded boundary
(for which *cs = 0*).
When restricting it is unfortunately not always possible to obtain a
second-order interpolation. This happens when the parent cell does not
contain enough child cells not entirely contained within the embedded
boundary. In these cases, some external information (i.e. a boundary
gradient condition) is required to be able to maintain second-order
accuracy. This information can be passed by defining the
*embed_gradient()* function of the field being restricted. */
attribute {
void (* embed_gradient) (Point, scalar, coord *);
}
static inline void restriction_embed_linear (Point point, scalar s)
{
// 0 children
if (!cs[]) {
s[] = 0.;
return;
}
/**
We first try to interpolate "diagonally". If enough child cells are
defined (i.e. have non-zero embedded fractions), we return the
corresponding value. */
double val = 0., nv = 0.;
for (int i = 0; i <= 1; i++)
#if dimension > 2
for (int j = 0; j <= 1; j++)
#endif
if (fine(cs,0,i,j) && fine(cs,1,!i,!j))
val += (fine(s,0,i,j) + fine(s,1,!i,!j))/2., nv++;
if (nv > 0.) {
s[] = val/nv;
return;
}
/**
Otherwise, we use the average of the child cells which are defined
(there is at least one). */
coord p = {0.,0.,0.};
foreach_child()
if (cs[])
p.x += x, p.y += y, p.z += z, val += s[], nv++;
assert (nv > 0.);
s[] = val/nv;
/**
If the gradient is defined and if the variable is not using
homogeneous boundary conditions, we improve the interpolation using
this information. */
if (s.embed_gradient && s.boundary[0] != s.boundary_homogeneous[0]) {
coord o = {x,y,z}, g;
s.embed_gradient (point, s, &g);
foreach_dimension()
s[] += (o.x - p.x/nv)*g.x;
}
}
/**
## Refinement/prolongation of cell-centered fields
For refinement, we use either bilinear interpolation, if the required
four coarse cell values are defined or trilinear interpolation if only
three coarse cell values are defined. If less that three coarse cell
values are defined ("pathological cases" below), we try to estimate
gradients in each direction and add the corresponding correction. */
static inline void refine_embed_linear (Point point, scalar s)
{
foreach_child() {
if (!cs[])
s[] = 0.;
else {
assert (coarse(cs));
int i = (child.x + 1)/2, j = (child.y + 1)/2;
#if dimension == 2
if (coarse(fs.x,i) && coarse(fs.y,0,j) &&
(coarse(cs) == 1. || coarse(cs,child.x) == 1. ||
coarse(cs,0,child.y) == 1. || coarse(cs,child.x,child.y) == 1.)) {
assert (coarse(cs,child.x) && coarse(cs,0,child.y));
if (coarse(fs.x,i,child.y) && coarse(fs.y,child.x,j)) {
// bilinear interpolation
assert (coarse(cs,child.x,child.y));
s[] = (9.*coarse(s) +
3.*(coarse(s,child.x) + coarse(s,0,child.y)) +
coarse(s,child.x,child.y))/16.;
}
else
// triangular interpolation
s[] = (2.*coarse(s) + coarse(s,child.x) + coarse(s,0,child.y))/4.;
}
else if (coarse(cs,child.x,child.y) &&
((coarse(fs.x,i) && coarse(fs.y,child.x,j)) ||
(coarse(fs.y,0,j) && coarse(fs.x,i,child.y)))) {
// diagonal interpolation
s[] = (3.*coarse(s) + coarse(s,child.x,child.y))/4.;
}
#else // dimension == 3
int k = (child.z + 1)/2;
if (coarse(fs.x,i) > 0.25 && coarse(fs.y,0,j) > 0.25 &&
coarse(fs.z,0,0,k) > 0.25 &&
(coarse(cs) == 1. || coarse(cs,child.x) == 1. ||
coarse(cs,0,child.y) == 1. || coarse(cs,child.x,child.y) == 1. ||
coarse(cs,0,0,child.z) == 1. || coarse(cs,child.x,0,child.z) == 1. ||
coarse(cs,0,child.y,child.z) == 1. ||
coarse(cs,child.x,child.y,child.z) == 1.)) {
assert (coarse(cs,child.x) && coarse(cs,0,child.y) &&
coarse(cs,0,0,child.z));
if (coarse(fs.x,i,child.y) && coarse(fs.y,child.x,j) &&
coarse(fs.z,child.x,child.y,k) &&
coarse(fs.z,child.x,0,k) && coarse(fs.z,0,child.y,k)) {
assert (coarse(cs,child.x,child.y) && coarse(cs,child.x,0,child.z) &&
coarse(cs,0,child.y,child.z) &&
coarse(cs,child.x,child.y,child.z));
// bilinear interpolation
s[] = (27.*coarse(s) +
9.*(coarse(s,child.x) + coarse(s,0,child.y) +
coarse(s,0,0,child.z)) +
3.*(coarse(s,child.x,child.y) + coarse(s,child.x,0,child.z) +
coarse(s,0,child.y,child.z)) +
coarse(s,child.x,child.y,child.z))/64.;
}
else
// tetrahedral interpolation
s[] = (coarse(s) + coarse(s,child.x) + coarse(s,0,child.y) +
coarse(s,0,0,child.z))/4.;
}
else if (coarse(cs,child.x,child.y,child.z) &&
((coarse(fs.z,child.x,child.y,k) &&
((coarse(fs.x,i) && coarse(fs.y,child.x,j)) ||
(coarse(fs.y,0,j) && coarse(fs.x,i,child.y))))
||
(coarse(fs.z,0,0,k) &&
((coarse(fs.x,i,0,child.z) && coarse(fs.y,child.x,j,child.z)) ||
(coarse(fs.y,0,j,child.z) && coarse(fs.x,i,child.y,child.z))))
||
(coarse(fs.z,child.x,0,k) &&
coarse(fs.x,i) && coarse(fs.y,child.x,j,child.z))
||
(coarse(fs.z,0,child.y,k) &&
coarse(fs.y,0,j) && coarse(fs.x,i,child.y,child.z))
))
// diagonal interpolation
s[] = (3.*coarse(s) + coarse(s,child.x,child.y,child.z))/4.;
#endif // dimension == 3
else {
// Pathological cases, use 1D gradients.
s[] = coarse(s);
foreach_dimension() {
if (coarse(fs.x,(child.x + 1)/2) && coarse(cs,child.x))
s[] += (coarse(s,child.x) - coarse(s))/4.;
else if (coarse(fs.x,(- child.x + 1)/2) && coarse(cs,- child.x))
s[] -= (coarse(s,- child.x) - coarse(s))/4.;
}
}
}
}
}
/**
## Refinement/prolongation of face-centered velocity
This function is modelled on
[*refine_face_x()*](/src/grid/tree-common.h#refine_face_x) and is
typically used to refine the values of the face-centered velocity
field *uf*. It uses linear interpolation, taking into account the
weighting by the embedded fractions *fs*. */
foreach_dimension()
void refine_embed_face_x (Point point, scalar s)
{
vector v = s.v;
for (int i = 0; i <= 1; i++)
if (neighbor(2*i - 1).neighbors &&
(is_local(cell) || is_local(neighbor(2*i - 1)))) {
double g1 = fs.x[i] >= 1. && fs.x[i,+1] && fs.x[i,-1] ?
(v.x[i,+1]/fs.x[i,+1] - v.x[i,-1]/fs.x[i,-1])/8. : 0.;
double g2 = fs.x[i] >= 1. && fs.x[i,0,+1] && fs.x[i,0,-1] ?
(v.x[i,0,+1]/fs.x[i,0,+1] - v.x[i,0,-1]/fs.x[i,0,-1])/8. : 0.;
for (int j = 0; j <= 1; j++)
for (int k = 0; k <= 1; k++)
fine(v.x,2*i,j,k) = fs.x[i] ?
fine(fs.x,2*i,j,k)*(v.x[i]/fs.x[i] +
(2*j - 1)*g1 + (2*k - 1)*g2) : 0.;
}
if (is_local(cell)) {
double g1 = (fs.x[0,+1] + fs.x[1,+1]) && (fs.x[0,-1] + fs.x[1,-1]) ?
((v.x[0,+1] + v.x[1,+1])/(fs.x[0,+1] + fs.x[1,+1]) -
(v.x[0,-1] + v.x[1,-1])/(fs.x[0,-1] + fs.x[1,-1]))/8. : 0.;
double g2 = (fs.x[1,0,+1] + fs.x[0,0,+1]) && (fs.x[1,0,-1] + fs.x[0,0,-1]) ?
((v.x[0,0,+1] + v.x[1,0,+1])/(fs.x[1,0,+1] + fs.x[0,0,+1]) -
(v.x[0,0,-1] + v.x[1,0,-1])/(fs.x[1,0,-1] + fs.x[0,0,-1]))/8. : 0.;
for (int j = 0; j <= 1; j++)
for (int k = 0; k <= 1; k++)
fine(v.x,1,j,k) = fs.x[] + fs.x[1] ?
fine(fs.x,1,j,k)*((v.x[] + v.x[1])/(fs.x[] + fs.x[1]) +
(2*j - 1)*g1 + (2*k - 1)*g2) : 0.;
}
}