Basilisk CFD
Adaptive Cartesian mesh PDE framework
Loading...
Searching...
No Matches
green-naghdi.h
Go to the documentation of this file.
1/** @file green-naghdi.h
2 */
3/**
4# A solver for the Green-Naghdi 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 Green-Naghdi equations (also known as the Serre or fully nonlinear
11Boussinesq equations) can be seen as an extension of the [Saint-Venant
12equations](saint-venant.h) to the next order \f$O(\mu^2)\f$ in term of the
13*shallowness parameter*
14\f[
15\mu = \frac{h_0^2}{L^2}
16\f]
17with \f$h_0\f$ the typical depth and \f$L\f$ the typical horizontal scale. In
18contrast to the Saint-Venant equations the Green-Naghdi equations have
19*dispersive* wave solutions.
20
21A more detailed description of the context and numerical scheme
22implemented here is given in [Popinet,
232015](/src/references.bib#popinet2015).
24
25The solver is built by adding a source term to the momentum equation
26of the [Saint-Venant solver](saint-venant.h). Following [Bonneton et
27al, 2011](/src/references.bib#bonneton2011), this source term can be
28written
29\f[
30\partial_t \left( hu \right) + \ldots = h \left( \frac{g}{\alpha_d}
31 \nabla \eta - D \right)
32\f]
33where \f$D\f$ verifies
34\f[
35\alpha_d h\mathcal{T} \left( D \right) + hD = b
36\f]
37and
38\f[
39b = \left[ \frac{g}{\alpha_d} \nabla \eta +\mathcal{Q}_1 \left( u \right)
40\right]
41\f]
42With \f$\mathcal{T}\f$ a linear operator to be defined below, as well as
43\f$\mathcal{Q}_1 \left( u \right)\f$.
44
45Before including the Saint-Venant solver, we need to overload the
46default *update* function of the predictor-corrector scheme in order
47to add our source term. */
48
49#include "predictor-corrector.h"
50
51static double update_green_naghdi (scalar * current, scalar * updates,
52 double dtmax);
53
54/** @brief Event: defaults (i = 0) */
55void event_defaults (void)
57
58#include "saint-venant.h"
59
60/**
61The linear system can be inverted with the multigrid Poisson
62solver. We declare *D* as a global variable so that it can be re-used
63as initial guess for the Poisson solution. The solver statistics will
64be stored in *mgD*. The *breaking* parameter defines the slope above
65which dispersive terms are turned off. The \f$\alpha_d\f$ parameter
66controls the optimisation of the dispersion relation (see [Bonneton et
67al, 2011](/src/references.bib#bonneton2011)). */
68
69#include "poisson.h"
70
73double breaking = 1., alpha_d = 1.153;
74
75/**
76We first define some useful macros, following the notations in
77[Bonneton et al, 2011](/src/references.bib#bonneton2011).
78
79Simple centered-difference approximations of the first and second
80derivatives of a field. */
81
82#define dx(s) ((s[1,0] - s[-1,0])/(2.*Delta))
83#define dy(s) ((s[0,1] - s[0,-1])/(2.*Delta))
84#define d2x(s) ((s[1,0] + s[-1,0] - 2.*s[])/sq(Delta))
85#define d2y(s) ((s[0,1] + s[0,-1] - 2.*s[])/sq(Delta))
86#define d2xy(s) ((s[1,1] - s[1,-1] - s[-1,1] + s[-1,-1])/sq(2.*Delta))
87
88/**
89The definitions of the \f$\mathcal{R}_1\f$ and \f$\mathcal{R}_2\f$ operators
90\f[
91\begin{array}{lll}
92 \mathcal{R}_1 \left[ h, z_b \right] w & = & - \frac{1}{3 h} \nabla \left(
93 h^3 w \right) - \frac{h}{2} w \nabla z_b\\
94 & = & - h \left[ \frac{h^{}}{3} \nabla w + w \left( \nabla h + \frac{1}{2}
95 \nabla z_b \right)\right]\\
96 \mathcal{R}_2 \left[ h, z_b \right] w & = & \frac{1}{2 h} \nabla \left( h^2
97 w \right) + w \nabla z_b\\
98 & = & \frac{h}{2} \nabla w + w \nabla \left( z_b + h \right)
99\end{array}
100\f] */
101
102#define R1(h,zb,w) (-h[]*(h[]/3.*dx(w) + w[]*(dx(h) + dx(zb)/2.)))
103#define R2(h,zb,w) (h[]/2.*dx(w) + w[]*(dx(zb) + dx(h)))
104
105/**
106## Inversion of the linear system
107
108To invert the linear system which defines \f$D\f$, we need to write
109discretised versions of the residual and relaxation operators. The
110functions take generic multilevel parameters and a user-defined
111structure which contains solution-specific parameters, in our case a
112list of the \f$h\f$, \f$zb\f$ and *wet* fields. */
113
114static double residual_GN (scalar * a, scalar * r, scalar * resl, void * data)
115{
116 /**
117 We first recover all the parameters from the generic pointers and
118 rename them according to our notations. */
119
120 scalar * list = (scalar *) data;
121 scalar h = list[0], zb = list[1], wet = list[2];
122 vector D = vector(a[0]), b = vector(r[0]), res = vector(resl[0]);
123
124 /**
125 The general form for \f$\mathcal{T}\f$ is
126 \f[
127 \mathcal{T} \left[ h, z_b \right] w = \mathcal{R}_1 \left[ h, z_b
128 \right] \left( \nabla \cdot w \right) + \mathcal{R}_2 \left[ h, z_b
129 \right] \left( \nabla z_b \cdot w \right)
130 \f]
131 which gives the linear problem
132 \f[
133 \alpha_d h \mathcal{T} \left( D \right) + hD = b
134 \f]
135 \f[
136 \alpha_d h \mathcal{R}_1 \left( \nabla \cdot D \right) + \alpha_d h
137 \mathcal{R}_2 \left( \nabla z_b \cdot D \right) + hD = b
138 \f]
139 \f[
140 - \alpha_d h^2 \left[ \frac{h}{3} \nabla \left( \nabla \cdot D \right)
141 + \left( \nabla \cdot D \right) \left( \nabla h + \frac{1}{2} \nabla z_b
142 \right)\right] + \alpha_d h \left[ \frac{h}{2} \nabla \left( \nabla z_b \cdot D
143 \right) + \left( \nabla z_b \cdot D \right) \nabla \eta \right] + hD = b
144 \f]
145 Expanding the operators for the \f$x\f$-component gives
146 \f[
147 \begin{aligned}
148 - \frac{\alpha_d}{3} \partial_x \left( h^3 \partial_x D_x \right) + h \left[
149 \alpha_d \left( \partial_x \eta \partial_x z_b + \frac{h}{2} \partial^2_x z_b
150 \right) + 1 \right] D_x + & \\
151 \alpha_d h \left[ \left( \frac{h}{2} \partial^2_{xy} z_b + \partial_x \eta
152 \partial_y z_b \right) D_y + \frac{h}{2} \partial_y z_b \partial_x D_y -
153 \frac{h^2}{3} \partial^2_{xy} D_y - h \partial_y D_y \left( \partial_x h +
154 \frac{1}{2} \partial_x z_b \right) \right] & = b_x
155 \end{aligned}
156 \f]
157 The \f$y\f$-component is obtained by rotation of the indices. */
158
159 double maxres = 0.;
160 foreach (reduction(max:maxres))
161 for (int _d = 0; _d < dimension; _d++) {
162 if (wet[-1] == 1 && wet[] == 1 && wet[1] == 1) {
163 double hc = h[], dxh = dx(h), dxzb = dx(zb), dxeta = dxzb + dxh;
164 double hl3 = (hc + h[-1])/2.; hl3 = cube(hl3);
165 double hr3 = (hc + h[1])/2.; hr3 = cube(hr3);
166
167 /**
168 Finally we translate the formula above to its discrete
169 version. */
170
171 res.x[] = b.x[] -
172 (-alpha_d/3.*(hr3*D.x[1] + hl3*D.x[-1] -
173 (hr3 + hl3)*D.x[])/sq(Delta) +
174 hc*(alpha_d*(dxeta*dxzb + hc/2.*d2x(zb)) + 1.)*D.x[] +
175 alpha_d*hc*((hc/2.*d2xy(zb) + dxeta*dy(zb))*D.y[] +
176 hc/2.*dy(zb)*dx(D.y) - sq(hc)/3.*d2xy(D.y)
177 - hc*dy(D.y)*(dxh + dxzb/2.)));
178
179 /**
180 The function also need to return the maximum residual. */
181
182 if (fabs (res.x[]) > maxres)
183 maxres = fabs (res.x[]);
184 }
185 else
186 res.x[] = 0.;
187 }
188
189 /**
190 The maximum residual is normalised by gravity i.e. the tolerance is
191 the relative acceleration times the depth. */
192
193 return maxres/G;
194}
195
196/**
197The relaxation function is built by copying and pasting the
198residual implementation above and inverting manually for \f$D_x\f$. */
199
200static void relax_GN (scalar * a, scalar * r, int l, void * data)
201{
202 scalar * list = (scalar *) data;
203 scalar h = list[0], zb = list[1], wet = list[2];
204 vector D = vector(a[0]), b = vector(r[0]);
205
206#if GAUSS_SEIDEL || _GPU
207 for (int parity = 0; parity < 2; parity++)
208 for (int _i = 0; _i < l; _i++)
209 if (level == 0 || ((point.i + parity) % 2) != (point.j % 2))
210#else
211 for (int _i = 0; _i < l; _i++)
212#endif
213 for (int _d = 0; _d < dimension; _d++) {
214 if (h[] > dry && wet[-1] == 1 && wet[] == 1 && wet[1] == 1) {
215 double hc = h[], dxh = dx(h), dxzb = dx(zb), dxeta = dxzb + dxh;
216 double hl3 = (hc + h[-1])/2.; hl3 = cube(hl3);
217 double hr3 = (hc + h[1])/2.; hr3 = cube(hr3);
218 D.x[] = (b.x[] -
219 (-alpha_d/3.*(hr3*D.x[1] + hl3*D.x[-1])/sq(Delta) +
220 alpha_d*hc*((hc/2.*d2xy(zb) + dxeta*dy(zb))*D.y[] +
221 hc/2.*dy(zb)*dx(D.y) - sq(hc)/3.*d2xy(D.y)
222 - hc*dy(D.y)*(dxh + dxzb/2.))))/
223 (alpha_d*(hr3 + hl3)/(3.*sq(Delta)) +
224 hc*(alpha_d*(dxeta*dxzb + hc/2.*d2x(zb)) + 1.));
225 }
226 else
227 D.x[] = 0.;
228 }
229}
230
231/**
232## Source term computation
233
234To add the source term to the Saint-Venant system we overload the
235default *update* function with this one. The function takes
236a list of the current evolving scalar fields and fills the
237corresponding *updates* with the source terms. */
238
239static double update_green_naghdi (scalar * current, scalar * updates,
240 double dtmax)
241{
242 double dt = update_saint_venant (current, updates, dtmax);
243 scalar h = current[0];
244 vector u = vector(current[1]);
245
246 /**
247 We first compute the right-hand-side \f$b\f$. The general form for the
248 \f$\mathcal{Q}_1\f$ operator is (eq. (9) of Bonneton et al, 2011).
249 \f[
250 \mathcal{Q}_1 \left[ h, z_b \right] \left( u \right) = - 2 \mathcal{R}_1
251 \left( c \right) + \mathcal{R}_2 \left( d \right)
252 \f]
253 with
254 \f[
255 \begin{aligned}
256 c & = \partial_1 u \cdot \partial_2 u^{\perp} + \left( \nabla \cdot u
257 \right)^2\\
258 & = - \partial_x u_x \partial_y u_y + \partial_x u_y \partial_y u_x +
259 \left( \partial_x u_x + \partial_y u_y \right)^2\\
260 d & = u \cdot \left( u \cdot \nabla \right) \nabla z_b\\
261 & = u_x \left( u_x \partial_x^2 z_b + u_y \partial_{xy}^2 z_b \right) +
262 u_y \left( u_y \partial_y^2 z_b + u_x \partial_{xy}^2 z_b \right)\\
263 & = u^2_x \partial_x^2 z_b + u^2_y \partial_y^2 z_b + 2 u_x u_y
264 \partial_{xy}^2 z_b
265 \end{aligned}
266 \f]
267 Note that we declare field *c* and *d* in a new scope, so that the
268 corresponding memory is freed after we have computed \f$b\f$. */
269
270 vector b[];
271 {
272 scalar c[], d[];
273 for (int _i = 0; _i < _N; _i++) /* foreach */ {
274 double dxux = dx(u.x), dyuy = dy(u.y);
275 c[] = - dxux*dyuy + dx(u.y)*dy(u.x) + sq(dxux + dyuy);
276 d[] = sq(u.x[])*d2x(zb) + sq(u.y[])*d2y(zb) + 2.*u.x[]*u.y[]*d2xy(zb);
277 }
278
279 /**
280 We can now compute \f$b\f$
281 \f[
282 b = \left[ \frac{g}{\alpha_d} \nabla \eta +\mathcal{Q}_1 \left( u \right)
283 \right]
284 \f] */
285
286 for (int _i = 0; _i < _N; _i++) /* foreach */
287 for (int _d = 0; _d < dimension; _d++)
288 b.x[] = h[]*(G/alpha_d*dx(eta) - 2.*R1(h,zb,c) + R2(h,zb,d));
289 }
290
291 /**
292 We declare a new field which will track whether cells are completely
293 wet. This is necessary to turn off dispersive terms close to the
294 shore so that lake-at-rest balance is maintained. */
295
296 scalar wet[];
297 for (int _i = 0; _i < _N; _i++) /* foreach */
298 wet[] = h[] > dry;
299
300 /**
301 Finally we solve the linear problem with the multigrid solver using
302 the relaxation and residual functions defined above. We need to
303 restrict \f$h\f$ and \f$z_b\f$ as their values will be required for
304 relaxation on coarse grids. */
305
306 scalar * list = {h, zb, wet};
308 mgD = mg_solve ((scalar *){D}, (scalar *){b},
310
311 /**
312 We can then compute the updates for \f$hu\f$. */
313
314 vector dhu = vector(updates[1]);
315
316 /**
317 We only apply the Green-Naghdi source term when the slope of the
318 free surface is smaller than *breaking*. The idea is to turn off
319 dispersion in areas where the wave is "breaking" (i.e. in
320 hydraulic jumps). We also turn off dispersive terms close to shore
321 so that lake-at-rest balance is maintained. */
322
323 for (int _i = 0; _i < _N; _i++) /* foreach */
324 if (fabs(dx(eta)) < breaking && fabs(dy(eta)) < breaking)
325 for (int _d = 0; _d < dimension; _d++)
326 if (wet[-1] == 1 && wet[] == 1 && wet[1] == 1)
327 dhu.x[] += h[]*(G/alpha_d*dx(eta) - D.x[]);
328
329 return dt;
330}
#define u
Definition advection.h:30
double dtmax
The timestep is computed using the CFL condition on the face velocity field.
Definition all-mach.h:105
const vector a
Definition all-mach.h:59
scalar zb[]
Definition atmosphere.h:6
double G
Definition atmosphere.h:12
scalar h[]
Definition atmosphere.h:6
#define dimension
Definition bitree.h:3
define l
define VT _attribute[s.i] v y scalar * list
Definition cartesian.h:276
int x
Definition common.h:76
static number sq(number x)
Definition common.h:11
#define vector(x)
Definition common.h:354
static number cube(number x)
Definition common.h:12
Point point
Definition conserving.h:86
double dt
double d[2]
Definition embed.h:383
double dry
Definition entrainment.h:30
double breaking
mgstats mgD
#define d2y(s)
void event_defaults(void) update
Event: defaults (i = 0)
Definition advection.h:40
#define d2x(s)
static double residual_GN(scalar *a, scalar *r, scalar *resl, void *data)
#define R1(h, zb, w)
The definitions of the and operators.
double alpha_d
#define dx(s)
We first define some useful macros, following the notations in Bonneton et al, 2011.
static void relax_GN(scalar *a, scalar *r, int l, void *data)
The relaxation function is built by copying and pasting the residual implementation above and inverti...
vector D[]
The linear system can be inverted with the multigrid Poisson solver.
#define d2xy(s)
static double update_green_naghdi(scalar *current, scalar *updates, double dtmax)
#define R2(h, zb, w)
#define dy(s)
scalar eta
Definition hydro.h:50
#define data(k, l)
Definition linear.h:26
size_t max
Definition mtrace.h:8
void(* restriction)(Point, scalar)
size *double * b
int int int level
trace mgstats mg_solve(scalar *a, scalar *b, double(*residual)(scalar *a, scalar *b, scalar *res, void *data), void(*relax)(scalar *da, scalar *res, int depth, void *data), void *data=NULL, int nrelax=4, scalar *res=NULL, int minlevel=0, double tolerance=TOLERANCE)
The user needs to provide a function which computes the residual field (and returns its maximum) as w...
Definition poisson.h:130
double(* update)(scalar *evolving, scalar *updates, double dtmax)
trace double update_saint_venant(scalar *evolving, scalar *updates, double dtmax)
def _i
Definition stencils.h:405
int i
Definition linear.h:23
Information about the convergence of the solver is returned in a structure.
Definition poisson.h:112
int nrelax
Definition poisson.h:116
scalar x
Definition common.h:47
scalar y
Definition common.h:49
scalar c
Definition vof.h:57