Basilisk CFD
Adaptive Cartesian mesh PDE framework
Loading...
Searching...
No Matches
saint-venant.h
Go to the documentation of this file.
1/** @file saint-venant.h
2 */
3/**
4# A solver for the Saint-Venant equations
5
6<div class="message">
7Note that the [multilayer solver](layered/hydro.h) provides the same
8functionality and should be prefered for most applications.</div>
9
10The
11[Saint-Venant equations](http://en.wikipedia.org/wiki/Shallow_water_equations)
12can be written in integral form as the hyperbolic system of
13conservation laws
14\f[
15 \partial_t \int_{\Omega} \mathbf{q} d \Omega =
16 \int_{\partial \Omega} \mathbf{f} (
17 \mathbf{q}) \cdot \mathbf{n}d \partial
18 \Omega - \int_{\Omega} hg \nabla z_b
19\f]
20where \f$\Omega\f$ is a given subset of space, \f$\partial \Omega\f$ its boundary and
21\f$\mathbf{n}\f$ the unit normal vector on this boundary. For
22conservation of mass and momentum in the shallow-water context, \f$\Omega\f$ is a
23subset of bidimensional space and \f$\mathbf{q}\f$ and
24\f$\mathbf{f}\f$ are written
25\f[
26 \mathbf{q} = \left(\begin{array}{c}
27 h\\
28 hu_x\\
29 hu_y
30 \end{array}\right),
31 \;\;\;\;\;\;
32 \mathbf{f} (\mathbf{q}) = \left(\begin{array}{cc}
33 hu_x & hu_y\\
34 hu_x^2 + \frac{1}{2} gh^2 & hu_xu_y\\
35 hu_xu_y & hu_y^2 + \frac{1}{2} gh^2
36 \end{array}\right)
37\f]
38where \f$\mathbf{u}\f$ is the velocity vector, \f$h\f$ the water depth and
39\f$z_b\f$ the height of the topography. See also [Popinet,
402011](/src/references.bib#popinet2011) for a more detailed
41introduction.
42
43## User variables and parameters
44
45The primary fields are the water depth \f$h\f$, the bathymetry \f$z_b\f$ and
46the flow speed \f$\mathbf{u}\f$. \f$\eta\f$ is the water level i.e. \f$z_b +
47h\f$. Note that the order of the declarations is important as \f$z_b\f$
48needs to be refined before \f$h\f$ and \f$h\f$ before \f$\eta\f$. */
49
50scalar zb[], h[], eta[];
52
53/**
54The only physical parameter is the acceleration of gravity *G*. Cells
55are considered "dry" when the water depth is less than the *dry*
56parameter (this should not require tweaking). */
57
58double G = 1.;
59double dry = 1e-10;
60
61/**
62By default there is only a single layer i.e. this is the classical
63Saint-Venant system above. This can be changed by setting *nl* to a
64different value. The extension of the Saint-Venant system to multiple
65layers is implemented in [multilayer.h](). */
66
67#if !LAYERS
68int nl = 1;
69#endif
70
71#include "multilayer.h"
72
73/**
74## Time-integration
75
76### Setup
77
78Time integration will be done with a generic
79[predictor-corrector](predictor-corrector.h) scheme. */
80
81#include "predictor-corrector.h"
82
83/**
84The generic time-integration scheme in *predictor-corrector.h* needs to
85know which fields are updated. The list will be constructed in the
86*defaults* event below. */
87
89
90/**
91We need to overload the default *advance* function of the
92predictor-corrector scheme, because the evolving variables (\f$h\f$ and
93\f$\mathbf{u}\f$) are not the conserved variables \f$h\f$ and
94\f$h\mathbf{u}\f$. */
95
98 scalar * updates, double dt)
99{
100 // recover scalar and vector fields from lists
101 scalar hi = sinput[0], ho = soutput[0], dh = updates[0];
102 vector * uol = (vector *) &soutput[1];
103
104 // new fields in ho[], uo[]
105 for (int _i = 0; _i < _N; _i++) /* foreach */ {
106 double hold = hi[];
107 ho[] = hold + dt*dh[];
108 eta[] = zb[] + ho[];
109 if (ho[] > dry) {
110 for (int l = 0; l < nl; l++) {
114 for (int _d = 0; _d < dimension; _d++)
115 uo.x[] = (hold*ui.x[] + dt*dhu.x[])/ho[];
116 }
117 /**
118 In the case of [multiple
119 layers](multilayer.h#viscous-friction-between-layers) we add the
120 viscous friction between layers. */
121
122 if (nl > 1)
124 }
125 else // dry
126 for (int l = 0; l < nl; l++) {
128 for (int _d = 0; _d < dimension; _d++)
129 uo.x[] = 0.;
130 }
131 }
132}
133
134/**
135When using an adaptive discretisation (i.e. a tree)., we need
136to make sure that \f$\eta\f$ is maintained as \f$z_b + h\f$ whenever cells are
137refined or restricted. */
138
139#if TREE
141{
142 for (int _c = 0; _c < 4; _c++) /* foreach_child */
143 eta[] = zb[] + h[];
144}
145
147{
148 eta[] = zb[] + h[];
149}
150#endif
151
152/**
153### Computing fluxes
154
155Various approximate Riemann solvers are defined in [riemann.h](). */
156
157#include "riemann.h"
158
159trace
161{
162
163 /**
164 We first recover the currently evolving height and velocity (as set
165 by the predictor-corrector scheme). */
166
167 scalar h = evolving[0], dh = updates[0];
168 vector u = avector (evolving[1], evolving[2]);
169
170 /**
171 *Fh* and *Fq* will contain the fluxes for \f$h\f$ and \f$h\mathbf{u}\f$
172 respectively and *S* is necessary to store the asymmetric topographic
173 source term. */
174
175 vector Fh[], S[];
176 tensor Fq[];
177
178 /**
179 The gradients are stored in locally-allocated fields. First-order
180 reconstruction is used for the gradient fields. */
181
182 vector gh[], geta[];
183 tensor gu[];
184 for (int _s = 0; _s < 1; _s++) /* scalar in {gh, geta, gu} */ {
185 s.gradient = zero;
186 #if TREE
187 s.prolongation = refine_linear;
188 #endif
189 }
190 gradients ({h, eta, u}, {gh, geta, gu});
191
192 /**
193 We go through each layer. */
194
195 for (int l = 0; l < nl; l++) {
196
197 /**
198 We recover the velocity field for the current layer and compute
199 its gradient (for the first layer the gradient has already been
200 computed above). */
201
203 if (l > 0)
204 gradients ((scalar *) {u}, (vector *) {gu});
205
206 /**
207 The faces which are "wet" on at least one side are traversed. */
208
209 for (int _i = 0; _i < _N; _i++) /* foreach_face */) {
210 double hi = h[], hn = h[-1];
211 if (hi > dry || hn > dry) {
212
213 /**
214 #### Left/right state reconstruction
215
216 The gradients computed above are used to reconstruct the left
217 and right states of the primary fields \f$h\f$, \f$\mathbf{u}\f$,
218 \f$z_b\f$. The "interface" topography \f$z_{lr}\f$ is reconstructed
219 using the hydrostatic reconstruction of [Audusse et al,
220 2004](/src/references.bib#audusse2004) */
221
222 double dx = Delta/2.;
223 double zi = eta[] - hi;
224 double zl = zi - dx*(geta.x[] - gh.x[]);
225 double zn = eta[-1] - hn;
226 double zr = zn + dx*(geta.x[-1] - gh.x[-1]);
227 double zlr = max(zl, zr);
228
229 double hl = hi - dx*gh.x[];
230 double up = u.x[] - dx*gu.x.x[];
231 double hp = max(0., hl + zl - zlr);
232
233 double hr = hn + dx*gh.x[-1];
234 double um = u.x[-1] + dx*gu.x.x[-1];
235 double hm = max(0., hr + zr - zlr);
236
237 /**
238 #### Riemann solver
239
240 We can now call one of the approximate Riemann solvers to get
241 the fluxes. */
242
243 double fh, fu, fv;
244 kurganov (hm, hp, um, up, Delta*cm[]/fm.x[], &fh, &fu, &dtmax);
245 fv = (fh > 0. ? u.y[-1] + dx*gu.y.x[-1] : u.y[] - dx*gu.y.x[])*fh;
246
247 /**
248 #### Topographic source term
249
250 In the case of adaptive refinement, care must be taken to ensure
251 well-balancing at coarse/fine faces (see [notes/balanced.tm]()). */
252
253 #if TREE
254 if (is_prolongation(cell)) {
255 hi = coarse(h);
256 zi = coarse(zb);
257 }
258 if (is_prolongation(neighbor(-1))) {
259 hn = coarse(h,-1);
260 zn = coarse(zb,-1);
261 }
262 #endif
263
264 double sl = G/2.*(sq(hp) - sq(hl) + (hl + hi)*(zi - zl));
265 double sr = G/2.*(sq(hm) - sq(hr) + (hr + hn)*(zn - zr));
266
267 /**
268 #### Flux update */
269
270 Fh.x[] = fm.x[]*fh;
271 Fq.x.x[] = fm.x[]*(fu - sl);
272 S.x[] = fm.x[]*(fu - sr);
273 Fq.y.x[] = fm.x[]*fv;
274 }
275 else // dry
276 Fh.x[] = 0., Fq.x.x[] = S.x[] = Fq.y.x[] = 0.;
277 }
278
279 /**
280 #### Updates for evolving quantities
281
282 We store the divergence of the fluxes in the update fields. Note that
283 these are updates for \f$h\f$ and \f$h\mathbf{u}\f$ (not \f$\mathbf{u}\f$). */
284
286 double layerl = layer[l]; // fixme: arrays with length would be nice
287 for (int _i = 0; _i < _N; _i++) /* foreach */ {
288 double dhl = layerl*(Fh.x[1,0] - Fh.x[] + Fh.y[0,1] - Fh.y[])/(cm[]*Delta);
289 dh[] = - dhl + (l > 0 ? dh[] : 0.);
290 for (int _d = 0; _d < dimension; _d++)
291 dhu.x[] = (Fq.x.x[] + Fq.x.y[] - S.x[1,0] - Fq.x.y[0,1])/(cm[]*Delta);
292
293 /**
294 For [multiple layers](multilayer.h#fluxes-between-layers) we
295 need to store the divergence in each layer. */
296
297 if (l < nl - 1) {
298 scalar div = wl[l];
299 div[] = dhl;
300 }
301
302 /**
303 We also need to add the metric terms. They can be written (see
304 eq. (8) of [Popinet, 2011](references.bib#popinet2011))
305 \f[
306 S_g = h \left(\begin{array}{c}
307 0\\
308 \frac{g}{2} h \partial_{\lambda} m_{\theta} + f_G u_y\\
309 \frac{g}{2} h \partial_{\theta} m_{\lambda} - f_G u_x
310 \end{array}\right)
311 \f]
312 with
313 \f[
314 f_G = u_y \partial_{\lambda} m_{\theta} - u_x \partial_{\theta} m_{\lambda}
315 \f]
316 */
317
318 double dmdl = (fm.x[1,0] - fm.x[])/(cm[]*Delta);
319 double dmdt = (fm.y[0,1] - fm.y[])/(cm[]*Delta);
320 double fG = u.y[]*dmdl - u.x[]*dmdt;
321 dhu.x[] += h[]*(G*h[]/2.*dmdl + fG*u.y[]);
322 dhu.y[] += h[]*(G*h[]/2.*dmdt - fG*u.x[]);
323 }
324 }
325
326 /**
327 For [multiple layers](multilayer.h#fluxes-between-layers) we need to
328 add fluxes between layers. */
329
330 if (nl > 1)
331 vertical_fluxes ((vector *) &evolving[1], (vector *) &updates[1], wl, dh);
332
333 return dtmax;
334}
335
336/**
337## Initialisation and cleanup
338
339We use the main time loop (in the predictor-corrector scheme) to setup
340the initial defaults. */
341
342/** @brief Event: defaults (i = 0) */
343void event_defaults (void)
344{
345 assert (ul == NULL && wl == NULL);
346 assert (nl > 0);
347 ul = vectors_append (ul, u);
348 for (int l = 1; l < nl; l++) {
349 scalar w = {0} /* new scalar */;
350 vector u = {0} /* new vector */;
351 for (int _d = 0; _d < dimension; _d++)
352 u.x.l = l;
353 w.l = l;
354 ul = vectors_append (ul, u);
355 wl = list_append (wl, w);
356 }
357
358 evolving = list_concat ({h}, (scalar *) ul);
359 for (int _i = 0; _i < _N; _i++) /* foreach */
360 for (int _s = 0; _s < 1; _s++) /* scalar in evolving */
361 s[] = 0.;
362
363 /**
364 By default, all the layers have the same relative thickness. */
365
366 layer = qmalloc (nl, double);
367 for (int l = 0; l < nl; l++)
368 layer[l] = 1./nl;
369
370 /**
371 We overload the default 'advance' and 'update' functions of the
372 predictor-corrector scheme and setup the prolongation and restriction
373 methods on trees. */
374
377
378 /**
379 On trees we make sure that slope-limiting is also used for
380 refinement and prolongation. The prolongation/restriction functions
381 for \f$\eta\f$ are set and they depend on boundary conditions on \f$z_b\f$
382 and \f$h\f$. */
383
384#if TREE
385 for (int _s = 0; _s < 1; _s++) /* scalar in {h,zb,u,eta} */ {
386 s.refine = refine_linear;
389 }
390 eta.refine = refine_eta;
392 eta.depends = list_copy ({zb,h});
393#endif
394
395 /**
396 We setup the default display. */
397
398 display ("squares (color = 'h > 0 ? eta : nodata', spread = -1);");
399}
400
401/**
402The event below will happen after all the other initial events to take
403into account user-defined field initialisations. */
404
405/** @brief Event: init (i = 0) */
406void event_init (void)
407{
408 for (int _i = 0; _i < _N; _i++) /* foreach */ {
409 eta[] = zb[] + h[];
410 /* dim: h[] == Delta */;
411 /* dim: u.x[] == Delta/DT */;
412 }
413}
414
415/**
416At the end of the simulation, we free the memory allocated in the
417*defaults* event. */
418
419/** @brief Event: cleanup (i = end, last) */
420void event_cleanup (void) {
421 free (evolving);
422 free (layer);
423 free (ul), ul = NULL;
424 free (wl), wl = NULL;
425}
426
427/**
428# "Radiation" boundary conditions
429
430This can be used to implement open boundary conditions at low
431[Froude numbers](http://en.wikipedia.org/wiki/Froude_number). The idea
432is to set the velocity normal to the boundary so that the water level
433relaxes towards its desired value (*ref*). */
434
435#define radiation(ref) (sqrt (G*max(h[],0.)) - sqrt(G*max((ref) - zb[], 0.)))
436
437#include "elevation.h"
438#include "gauges.h"
double dtmax
The timestep is computed using the CFL condition on the face velocity field.
Definition all-mach.h:105
scalar hn[]
Definition atmosphere.h:6
#define dimension
Definition bitree.h:3
define l
define neighbor(o, p, q)((Point)
Definition cartesian.h:146
free(list1)
int x
Definition common.h:76
double zero(double s0, double s1, double s2)
Definition common.h:116
const scalar cm[]
Definition common.h:397
void display(const char *commands, bool overwrite=false)
Definition common.h:527
scalar * list_concat(scalar *l1, scalar *l2)
Definition common.h:234
static number sq(number x)
Definition common.h:11
scalar * list_copy(scalar *l)
Definition common.h:225
#define avector(x, y,...)
Definition common.h:568
scalar * list_append(scalar *list, scalar s)
Definition common.h:188
const vector fm[]
Definition common.h:396
vector * vectors_append(vector *list, vector v)
Definition common.h:258
vector w[]
#define qmalloc(size, type)
#define assert(a)
Definition config.h:107
Point point
Definition conserving.h:86
double dt
scalar s
Definition embed-tree.h:56
#define dx(s)
We first define some useful macros, following the notations in Bonneton et al, 2011.
double dh
Definition heights.h:331
scalar S
Definition gotm.h:16
size_t max
Definition mtrace.h:8
static void restriction_volume_average(Point point, scalar s)
static void refine_linear(Point point, scalar s)
void set_prolongation(scalar s, void(*prolongation)(Point, scalar))
void set_restriction(scalar s, void(*restriction)(Point, scalar))
define is_active() cell(true) @define is_leaf(cell)(point.level
double * layer
Definition multilayer.h:44
void vertical_fluxes(vector *evolving, vector *updates, scalar *divl, scalar dh)
Definition multilayer.h:202
scalar * wl
Definition multilayer.h:43
void vertical_viscosity(Point point, double h, vector *ul, double dt)
For stability, we discretise the viscous friction term implicitly as.
Definition multilayer.h:91
vector * ul
Definition multilayer.h:42
double(* update)(scalar *evolving, scalar *updates, double dtmax)
static void(* advance)(scalar *output, scalar *input, scalar *updates, double dt)
void kurganov(double hm, double hp, double um, double up, double Delta, double *fh, double *fq, double *dtmax)
Definition riemann.h:42
int nl
By default there is only a single layer i.e.
vector u[]
scalar * evolving
The generic time-integration scheme in predictor-corrector.h needs to know which fields are updated.
double dry
scalar h[]
scalar zb[]
double G
The only physical parameter is the acceleration of gravity G.
void event_init(void)
The event below will happen after all the other initial events to take into account user-defined fiel...
static void refine_eta(Point point, scalar eta)
When using an adaptive discretisation (i.e.
trace double update_saint_venant(scalar *evolving, scalar *updates, double dtmax)
scalar eta[]
static trace void advance_saint_venant(scalar *soutput, scalar *sinput, scalar *updates, double dt)
We need to overload the default advance function of the predictor-corrector scheme,...
void event_cleanup(void)
At the end of the simulation, we free the memory allocated in the defaults* event.
void event_defaults(void)
Event: defaults (i = 0)
static void restriction_eta(Point point, scalar eta)
def _i
Definition stencils.h:405
Definition linear.h:21
scalar x
Definition common.h:47
scalar y
Definition common.h:49
define n n define coarse(a, k, p, n)((double *)(PARENT(k
#define is_prolongation(cell)
Definition tree.h:411
bool * zn
Definition tree.h:1273
void gradients(scalar *f, vector *g)
Given a list of scalar fields f, this function fills the gradient fields g with the corresponding gra...
Definition utils.h:247