Basilisk CFD
Adaptive Cartesian mesh PDE framework
Loading...
Searching...
No Matches
centered.h
Go to the documentation of this file.
1/** @file centered.h
2 */
3/**
4# Incompressible Navier--Stokes solver (centered formulation)
5
6We wish to approximate numerically the incompressible,
7variable-density Navier--Stokes equations
8\f[
9\partial_t\mathbf{u}+\nabla\cdot(\mathbf{u}\otimes\mathbf{u}) =
10\frac{1}{\rho}\left[-\nabla p + \nabla\cdot(2\mu\mathbf{D})\right] +
11\mathbf{a}
12\f]
13\f[
14\nabla\cdot\mathbf{u} = 0
15\f]
16with the deformation tensor
17\f$\mathbf{D}=[\nabla\mathbf{u} + (\nabla\mathbf{u})^T]/2\f$.
18
19The scheme implemented here is close to that used in Gerris ([Popinet,
202003](/src/references.bib#popinet2003), [Popinet,
212009](/src/references.bib#popinet2009), [Lagrée et al,
222011](/src/references.bib#lagree2011)).
23
24We will use the generic time loop, a CFL-limited timestep, the
25Bell-Collela-Glaz advection scheme and the implicit viscosity
26solver. If embedded boundaries are used, a different scheme is used
27for viscosity. */
28
29#include "run.h"
30#include "timestep.h"
31#include "bcg.h"
32#if EMBED
33# include "viscosity-embed.h"
34#else
35# include "viscosity.h"
36#endif
37
38/**
39The primary variables are the centered pressure field \f$p\f$ and the
40centered velocity field \f$\mathbf{u}\f$. The centered vector field
41\f$\mathbf{g}\f$ will contain pressure gradients and acceleration terms.
42
43We will also need an auxilliary face velocity field \f$\mathbf{u}_f\f$ and
44the associated centered pressure field \f$p_f\f$. */
45
47vector u[], g[];
49vector uf[];
50
51/**
52In the case of variable density, the user will need to define both the
53face and centered specific volume fields (\f$\alpha\f$ and \f$\alpha_c\f$
54respectively) i.e. \f$1/\rho\f$. If not specified by the user, these
55fields are set to one i.e. the density is unity.
56
57Viscosity is set by defining the face dynamic viscosity \f$\mu\f$; default
58is zero.
59
60The face field \f$\mathbf{a}\f$ defines the acceleration term; default is
61zero.
62
63The statistics for the (multigrid) solution of the pressure Poisson
64problems and implicit viscosity are stored in *mgp*, *mgpf*, *mgu*
65respectively.
66
67If *stokes* is set to *true*, the velocity advection term
68\f$\nabla\cdot(\mathbf{u}\otimes\mathbf{u})\f$ is omitted. This is a
69reference to [Stokes flows](http://en.wikipedia.org/wiki/Stokes_flow)
70for which inertia is negligible compared to viscosity. */
71
74mgstats mgp = {0}, mgpf = {0}, mgu = {0};
75bool stokes = false;
76
77/**
78## Boundary conditions
79
80For the default symmetric boundary conditions, we need to ensure that
81the normal component of the velocity is zero after projection. This
82means that, at the boundary, the acceleration \f$\mathbf{a}\f$ must be
83balanced by the pressure gradient. Taking care of boundary orientation
84and staggering of \f$\mathbf{a}\f$, this can be written */
85
86#if EMBED
87# define neumann_pressure(i) (alpha.n[i] ? a.n[i]*fm.n[i]/alpha.n[i] : \
88 a.n[i]*rho[]/(cm[] + SEPS))
89#else
90# define neumann_pressure(i) (a.n[i]*fm.n[i]/alpha.n[i])
91#endif
92
95
96#if AXI
97uf.n[bottom] = 0.;
98uf.t[bottom] = dirichlet(0); // since uf is multiplied by the metric which
99 // is zero on the axis of symmetry
101#else // !AXI
102# if dimension > 1
105# endif
106# if dimension > 2
109# endif
110#endif // !AXI
111
112/**
113For [embedded boundaries on trees](/src/embed-tree.h), we need to
114define the pressure gradient for prolongation of pressure close to
115embedded boundaries. */
116
117#if TREE && EMBED
119{
120 for (int _d = 0; _d < dimension; _d++)
121 g->x = rho[]/(cm[] + SEPS)*(a.x[] + a.x[1])/2.;
122}
123#endif // TREE && EMBED
124
125/**
126## Initial conditions */
127
128/** @brief Event: defaults (i = 0) */
129void event_defaults (void)
130{
131
132 /**
133 We reset the multigrid parameters to their default values. */
134
135 mgp = (mgstats){0};
136 mgpf = (mgstats){0};
137 mgu = (mgstats){0};
138
139 CFL = 0.8;
140
141 /**
142 The pressures are never dumped. */
143
144 p.nodump = pf.nodump = true;
145
146 /**
147 The default density field is set to unity (times the metric and the
148 solid factors). */
149
150 if (alpha.x.i == unityf.x.i) {
151 alpha = fm;
152 rho = cm;
153 }
154 else if (!is_constant(alpha.x)) {
156 for (int _i = 0; _i < _N; _i++) /* foreach_face */
157 alphav.x[] = fm.x[];
158 }
159
160 /**
161 On trees, refinement of the face-centered velocity field needs to
162 preserve the divergence-free condition. */
163
164#if TREE
165 uf.x.refine = refine_face_solenoidal;
166
167 /**
168 When using [embedded boundaries](/src/embed.h), the restriction and
169 prolongation operators need to take the boundary into account. */
170
171#if EMBED
172 uf.x.refine = refine_face;
173 for (int _d = 0; _d < dimension; _d++)
174 uf.x.prolongation = refine_embed_face_x;
175 for (int _s = 0; _s < 1; _s++) /* scalar in {p, pf, u, g} */ {
176 s.restriction = restriction_embed_linear;
177 s.refine = s.prolongation = refine_embed_linear;
178 s.depends = list_add (s.depends, cs);
179 }
180 for (int _s = 0; _s < 1; _s++) /* scalar in {p, pf} */
181 s.embed_gradient = pressure_embed_gradient;
182#endif // EMBED
183#endif // TREE
184
185 /**
186 We set the dimensions of the velocity field. */
187
188 for (int _i = 0; _i < _N; _i++) /* foreach */
189 for (int _d = 0; _d < dimension; _d++)
190 /* dim: u.x[] == Delta/t */;
191}
192
193
194/**
195We had some objects to display by default. */
196
197/** @brief Event: default_display (i = 0) */
199 display ("squares (color = 'u.x', spread = -1);");
200
201/**
202After user initialisation, we initialise the face velocity and fluid
203properties. */
204
205double dtmax;
206
207/** @brief Event: init (i = 0) */
208void event_init (void)
209{
210 /* trash: uf */;
211 for (int _i = 0; _i < _N; _i++) /* foreach_face */
212 uf.x[] = fm.x[]*face_value (u.x, 0);
213
214 /**
215 We update fluid properties. */
216
217 event ("properties");
218
219 /**
220 We set the initial timestep (this is useful only when restoring from
221 a previous run). */
222
223 dtmax = DT;
224 event ("stability");
225}
226
227/**
228## Time integration
229
230The timestep for this iteration is controlled by the CFL condition,
231applied to the face centered velocity field \f$\mathbf{u}_f\f$; and the
232timing of upcoming events. */
233
234event set_dtmax (i++,last) dtmax = DT;
235
236/** @brief Event: stability (i++,last) */
237void event_stability (void) {
238 dt = dtnext (stokes ? dtmax : timestep (uf, dtmax));
239}
240
241/**
242If we are using VOF or diffuse tracers, we need to advance them (to
243time \f$t+\Delta t/2\f$) here. Note that this assumes that tracer fields
244are defined at time \f$t-\Delta t/2\f$ i.e. are lagging the
245velocity/pressure fields by half a timestep. */
246
247event vof (i++,last);
248event tracer_advection (i++,last);
249event tracer_diffusion (i++,last);
250
251/**
252The fluid properties such as specific volume (fields \f$\alpha\f$ and
253\f$\alpha_c\f$) or dynamic viscosity (face field \f$\mu_f\f$) -- at time
254\f$t+\Delta t/2\f$ -- can be defined by overloading this event. */
255
256event properties (i++,last);
257
258/**
259### Predicted face velocity field
260
261For second-order in time integration of the velocity advection term
262\f$\nabla\cdot(\mathbf{u}\otimes\mathbf{u})\f$, we need to define the face
263velocity field \f$\mathbf{u}_f\f$ at time \f$t+\Delta t/2\f$. We use a version
264of the Bell-Collela-Glaz [advection scheme](/src/bcg.h) and the
265pressure gradient and acceleration terms at time \f$t\f$ (stored in vector
266\f$\mathbf{g}\f$). */
267
269{
270 vector du;
271 for (int _d = 0; _d < dimension; _d++) {
272 scalar s = {0} /* new scalar */;
273 du.x = s;
274 }
275
276 if (u.x.gradient)
277 for (int _i = 0; _i < _N; _i++) /* foreach */
278 for (int _d = 0; _d < dimension; _d++) {
279#if EMBED
280 if (!fs.x[] || !fs.x[1])
281 du.x[] = 0.;
282 else
283#endif
284 du.x[] = u.x.gradient (u.x[-1], u.x[], u.x[1])/Delta;
285 }
286 else
287 for (int _i = 0; _i < _N; _i++) /* foreach */
288 for (int _d = 0; _d < dimension; _d++) {
289#if EMBED
290 if (!fs.x[] || !fs.x[1])
291 du.x[] = 0.;
292 else
293#endif
294 du.x[] = (u.x[1] - u.x[-1])/(2.*Delta);
295 }
296
297 /* trash: uf */;
298 for (int _i = 0; _i < _N; _i++) /* foreach_face */ {
299 double un = dt*(u.x[] + u.x[-1])/(2.*Delta), s = sign(un);
300 int i = -(s + 1.)/2.;
301 uf.x[] = u.x[i] + (g.x[] + g.x[-1])*dt/4. + s*(1. - s*un)*du.x[i]*Delta/2.;
302 #if dimension > 1
303 if (fm.y[i,0] && fm.y[i,1]) {
304 double fyy = u.y[i] < 0. ? u.x[i,1] - u.x[i] : u.x[i] - u.x[i,-1];
305 uf.x[] -= dt*u.y[i]*fyy/(2.*Delta);
306 }
307 #endif
308 #if dimension > 2
309 if (fm.z[i,0,0] && fm.z[i,0,1]) {
310 double fzz = u.z[i] < 0. ? u.x[i,0,1] - u.x[i] : u.x[i] - u.x[i,0,-1];
311 uf.x[] -= dt*u.z[i]*fzz/(2.*Delta);
312 }
313 #endif
314 uf.x[] *= fm.x[];
315 }
316
317 delete ((scalar *){du});
318}
319
320/**
321### Advection term
322
323We predict the face velocity field \f$\mathbf{u}_f\f$ at time \f$t+\Delta
324t/2\f$ then project it to make it divergence-free. We can then use it to
325compute the velocity advection term, using the standard
326Bell-Collela-Glaz advection scheme for each component of the velocity
327field. */
328
329/** @brief Event: advection_term (i++,last) */
331{
332 if (!stokes) {
333 prediction();
334 mgpf = project (uf, pf, alpha, dt/2., mgpf.nrelax);
335 advection ((scalar *){u}, uf, dt, (scalar *){g});
336 }
337}
338
339/**
340### Viscous term
341
342We first define a function which adds the pressure gradient and
343acceleration terms. */
344
345static void correction (double dt)
346{
347 for (int _i = 0; _i < _N; _i++) /* foreach */
348 for (int _d = 0; _d < dimension; _d++)
349 u.x[] += dt*g.x[];
350}
351
352/**
353The viscous term is computed implicitly. We first add the pressure
354gradient and acceleration terms, as computed at time \f$t\f$, then call
355the implicit viscosity solver. We then remove the acceleration and
356pressure gradient terms as they will be replaced by their values at
357time \f$t+\Delta t\f$. */
358
359/** @brief Event: viscous_term (i++,last) */
361{
362 if (constant(mu.x) != 0.) {
363 correction (dt);
364 mgu = viscosity (u, mu, rho, dt, mgu.nrelax);
365 correction (-dt);
366 }
367
368 /**
369 We reset the acceleration field (if it is not a constant). */
370
371 if (!is_constant(a.x)) {
372 vector af = a;
373 /* trash: af */;
374 for (int _i = 0; _i < _N; _i++) /* foreach_face */
375 af.x[] = 0.;
376 }
377}
378
379/**
380### Acceleration term
381
382The acceleration term \f$\mathbf{a}\f$ needs careful treatment as many
383equilibrium solutions depend on exact balance between the acceleration
384term and the pressure gradient: for example Laplace's balance for
385surface tension or hydrostatic pressure in the presence of gravity.
386
387To ensure a consistent discretisation, the acceleration term is
388defined on faces as are pressure gradients and the centered combined
389acceleration and pressure gradient term \f$\mathbf{g}\f$ is obtained by
390averaging.
391
392The (provisionary) face velocity field at time \f$t+\Delta t\f$ is
393obtained by interpolation from the centered velocity field. The
394acceleration term is added. */
395
396/** @brief Event: acceleration (i++,last) */
398{
399 /* trash: uf */;
400 for (int _i = 0; _i < _N; _i++) /* foreach_face */
401 uf.x[] = fm.x[]*(face_value (u.x, 0) + dt*a.x[]);
402}
403
404/**
405## Approximate projection
406
407This function constructs the centered pressure gradient and
408acceleration field *g* using the face-centered acceleration field *a*
409and the cell-centered pressure field *p*. */
410
412{
413
414 /**
415 We first compute a face field \f$\mathbf{g}_f\f$ combining both
416 acceleration and pressure gradient. */
417
418 vector gf[];
419 for (int _i = 0; _i < _N; _i++) /* foreach_face */
420 gf.x[] = fm.x[]*a.x[] - alpha.x[]*(p[] - p[-1])/Delta;
421
422 /**
423 We average these face values to obtain the centered, combined
424 acceleration and pressure gradient field. */
425
426 /* trash: g */;
427 for (int _i = 0; _i < _N; _i++) /* foreach */
428 for (int _d = 0; _d < dimension; _d++)
429 g.x[] = (gf.x[] + gf.x[1])/(fm.x[] + fm.x[1] + SEPS);
430}
431
432/**
433To get the pressure field at time \f$t + \Delta t\f$ we project the face
434velocity field (which will also be used for tracer advection at the
435next timestep). Then compute the centered gradient field *g*. */
436
437/** @brief Event: projection (i++,last) */
439{
440 mgp = project (uf, p, alpha, dt, mgp.nrelax);
442
443 /**
444 We add the gradient field *g* to the centered velocity field. */
445
446 correction (dt);
447}
448
449/**
450Some derived solvers need to hook themselves at the end of the
451timestep. */
452
453event end_timestep (i++, last);
454
455/**
456## Adaptivity
457
458After mesh adaptation fluid properties need to be updated. When using
459[embedded boundaries](/src/embed.h) the fluid fractions and face
460fluxes need to be checked for inconsistencies. */
461
462#if TREE
463/** @brief Event: adapt (i++,last) */
464void event_adapt (void) {
465#if EMBED
467 for (int _i = 0; _i < _N; _i++) /* foreach_face */
468 if (uf.x[] && !fs.x[])
469 uf.x[] = 0.;
470#endif
471 event ("properties");
472}
473#endif
474
475/**
476## See also
477
478* [Double projection](double-projection.h)
479* [Performance monitoring](perfs.h)
480*/
#define u
Definition advection.h:30
trace double timestep(void)
Definition atmosphere.h:37
vector un[]
Definition atmosphere.h:5
trace void advection(scalar *tracers, vector u, double dt, scalar *src=NULL)
The function below uses the tracer_fluxes function to integrate the advection equation,...
Definition bcg.h:71
#define dimension
Definition bitree.h:3
void event_advection_term(void)
Event: advection_term (i++,last)
Definition centered.h:330
void event_acceleration(void)
Event: acceleration (i++,last)
Definition centered.h:397
vector g[]
Definition centered.h:47
void event_default_display(void) display("squares (color
We had some objects to display by default.
void event_stability(void)
Event: stability (i++,last)
Definition centered.h:237
event properties(i++, last)
The fluid properties such as specific volume (fields and ) or dynamic viscosity (face field ) – at t...
double dtmax
After user initialisation, we initialise the face velocity and fluid properties.
Definition centered.h:205
const scalar rho
Definition centered.h:73
void event_projection(void)
To get the pressure field at time we project the face velocity field (which will also be used for tr...
Definition centered.h:438
scalar p[]
The primary variables are the centered pressure field and the centered velocity field .
Definition centered.h:46
bool stokes
Definition centered.h:75
event tracer_diffusion(i++, last)
const vector mu
In the case of variable density, the user will need to define both the face and centered specific vol...
Definition centered.h:72
void spread
Definition centered.h:199
void event_init(void)
Event: init (i = 0)
Definition centered.h:208
#define neumann_pressure(i)
Definition centered.h:90
void event_viscous_term(void)
The viscous term is computed implicitly.
Definition centered.h:360
mgstats mgpf
Definition centered.h:74
mgstats mgu
Definition centered.h:74
event vof(i++, last)
If we are using VOF or diffuse tracers, we need to advance them (to time ) here.
void prediction()
Definition centered.h:268
const vector alpha
Definition centered.h:72
void event_defaults(void)
For embedded boundaries on trees, we need to define the pressure gradient for prolongation of pressur...
Definition centered.h:129
vector uf[]
We allocate the (face) velocity field.
Definition centered.h:47
scalar pf[]
Definition centered.h:48
void event_adapt(void)
Event: adapt (i++,last)
Definition centered.h:464
event end_timestep(i++, last)
Some derived solvers need to hook themselves at the end of the timestep.
void centered_gradient(scalar p, vector g)
Definition centered.h:411
mgstats mgp
Definition centered.h:74
event set_dtmax(i++, last) dtmax
event tracer_advection(i++, last)
static void correction(double dt)
Definition centered.h:345
const vector a
Definition centered.h:72
int x
Definition common.h:76
#define face_value(a, i)
Definition common.h:406
const scalar cm[]
Definition common.h:397
void display(const char *commands, bool overwrite=false)
Definition common.h:527
scalar * list_add(scalar *list, scalar s)
Definition common.h:208
const scalar unity[]
Definition common.h:391
static int sign(number x)
Definition common.h:13
const vector zerof[]
Definition common.h:389
@ top
Definition common.h:123
@ bottom
Definition common.h:123
@ left
Definition common.h:123
@ right
Definition common.h:123
const vector fm[]
Definition common.h:396
const vector unityf[]
Definition common.h:390
#define SEPS
Definition common.h:402
vector alphav[]
Definition two-phase.h:105
Point point
Definition conserving.h:86
trace bool squares(char *color, char *z=NULL, double min=0, double max=0, double spread=0, bool linear=false, Colormap map=jet, float fc[3]={0}, float lc[3]={0}, bool expr=false, coord n={0, 0, 1}, double alpha=0, float lw=1, bool cbar=false, float size=15, float pos[2]={-.95, -.95}, char *label="", double lscale=1, bool horizontal=false, bool border=false, bool mid=false, float fsize=50, char *format="%g", int levels=50)
Definition draw.h:1231
double dt
static void restriction_embed_linear(Point point, scalar s)
Definition embed-tree.h:212
static void refine_embed_linear(Point point, scalar s)
Definition embed-tree.h:270
scalar s
Definition embed-tree.h:56
scalar cs[]
The volume and area fractions are stored in these fields.
Definition embed.h:21
macro2 double neumann(double expr, Point point=point, scalar s=_s, bool *data=data)
Definition embed.h:732
vector fs[]
Definition embed.h:22
scalar int i
Definition embed.h:74
macro2 double dirichlet(double expr, Point point=point, scalar s=_s, bool *data=data)
For ease of use, we replace the Neumann and Dirichlet functions with macros so that they can be used ...
Definition embed.h:717
trace int fractions_cleanup(scalar c, vector s, double smin=0., bool opposite=false)
Definition embed.h:295
double dtnext(double dt)
Definition events.h:276
void event(const char *name)
Definition events.h:264
trace mgstats project(vector uf, scalar p, const vector alpha=unityf, double dt=1., int nrelax=4)
Definition poisson.h:483
def _i
Definition stencils.h:405
Definition linear.h:21
Definition common.h:78
Information about the convergence of the solver is returned in a structure.
Definition poisson.h:112
int nrelax
Definition poisson.h:116
int i
Definition common.h:44
scalar x
Definition common.h:47
scalar y
Definition common.h:49
void refine_face_solenoidal(Point point, scalar s)
void refine_face(Point point, scalar s)
double CFL
Definition utils.h:10
double DT
Definition utils.h:10
Array * color
trace mgstats viscosity(vector u, vector mu, scalar rho, double dt, int nrelax=4, scalar *res=NULL)