Basilisk CFD
Adaptive Cartesian mesh PDE framework
Loading...
Searching...
No Matches
implicit.h
Go to the documentation of this file.
1/** @file implicit.h
2 */
3/**
4# Time-implicit barotropic integration
5
6This implements a semi-implicit scheme for the evolution of the
7free-surface elevation \f$\eta\f$ of the [multilayer solver](README).
8The scheme can be summarised as
9\f[
10\begin{aligned}
11\frac{\eta^{n + 1} - \eta^n}{\Delta t} &
12 = - \sum_k \nabla \cdot [\theta_H (hu)_k^{n + 1} + (1 - \theta_H) (hu)^n_k] \\
13\frac{(hu)^{n + 1}_k - (hu)_k^n}{\Delta t} &
14 = - \Delta tgh^{n + 1 / 2}_k (\theta_H \nabla \eta^{n + 1} +
15 (1 - \theta_H) \nabla \eta^n)
16\end{aligned}
17\f]
18where \f$\theta_H\f$ is the "implicitness parameter" typically set to \f$1/2\f$.
19
20The resulting Poisson--Helmholtz equation for \f$\eta^{n+1}\f$ is solved
21using the multigrid Poisson solver. The convergence statistics are
22stored in `mgH`. */
23
24#include "poisson.h"
25
26mgstats mgH = { .minlevel = 1 };
27double theta_H = 0.5;
28
29#define IMPLICIT_H 1
30
31/**
32The scheme is unconditionally stable for gravity waves, so the gravity
33wave CFL is set to \f$\infty\f$, if it has not already been set (typically
34by the user). */
35
36/** @brief Event: defaults0 (i = 0) */
37void event_defaults0 (void)
38{
39 if (CFL_H == 1e40)
40 CFL_H = HUGE;
41 mgH.nrelax = 4;
42}
43
44/**
45The free-surface can be replaced with a "rigid lid" by setting `rigid`
46to `true`. */
47
48bool rigid = false;
49
50/**
51The rigid lid condition \f$\partial_t\eta = 0\f$ implies
52\f[
53\begin{aligned}
540 & = - \sum_k \nabla \cdot [\theta_H (hu)_k^{n + 1} + (1 - \theta_H) (hu)^n_k] \\
55\frac{(hu)^{n + 1}_k - (hu)_k^n}{\Delta t} &
56 = - \Delta tgh^{n + 1 / 2}_k (\theta_H \nabla \eta_r^{n + 1} +
57 (1 - \theta_H) \nabla \eta_r^n)
58\end{aligned}
59\f]
60where \f$\eta_r\f$ is the equivalent free-surface height (i.e. pressure)
61applied on the rigid lid. */
62
63/** @brief Event: defaults (i = 0) */
64void event_defaults (void)
65{
66 if (rigid)
67 eta_r = {0} /* new scalar */;
68}
69
70/**
71The relaxation and residual functions of the multigrid solver are
72derived from the Poisson--Helmholtz equation for \f$\eta^{n+1}\f$ derived
73from the equations above
74\f[
75\begin{aligned}
76 \beta\eta^{n + 1} + \nabla \cdot (\alpha \nabla \eta^{n + 1}) & = \beta\eta^n -
77 \Delta t \sum_k \nabla \cdot (hu)_k^{\star}\\
78 \alpha & \equiv - g (\theta \Delta t)^2 \sum_k h^{n + 1 / 2}_k\\
79 (hu)_k^{\star} & \equiv (hu)_k^n - \Delta tgh^{n + 1 / 2}_k \theta (1 -
80 \theta) \nabla \eta^n
81\end{aligned}
82\f]
83where \f$\beta = 1\f$ for a free surface and \f$\beta = 0\f$ for a rigid lid. */
84
86static void relax_hydro (scalar * ql, scalar * rhsl, int lev, void * data)
87{
88 scalar eta = ql[0], rhs_eta = rhsl[0];
89 vector alpha = *((vector *)data);
90#if _GPU // Gauss-Seidel relaxation
91 for (int parity = 0; parity < 2; parity++)
92 for (int _i = 0; _i < lev; _i++)
93 if (level == 0 || ((point.i + parity) % 2) != (point.j % 2))
94#else
95 for (int _i = 0; _i < lev; _i++)
96#endif
97 {
98 double d = rigid ? 0. : - cm[]*Delta;
99 double n = - cm[]*Delta*rhs_eta[];
100 eta[] = 0.;
101 for (int _d = 0; _d < dimension; _d++) {
102 n += alpha.x[0]*a_baro (eta, 0) - alpha.x[1]*a_baro (eta, 1);
104 d -= alpha.x[0]*a_baro (eta, 0) - alpha.x[1]*a_baro (eta, 1);
105 }
106 eta[] = d ? n/d : 0;
107 }
108}
109
110trace
111static double residual_hydro (scalar * ql, scalar * rhsl,
112 scalar * resl, void * data)
113{
114 scalar eta = ql[0], rhs_eta = rhsl[0], res_eta = resl[0];
115 vector alpha = *((vector *)data);
116 double maxres = 0.;
117 vector g[];
118 for (int _i = 0; _i < _N; _i++) /* foreach_face */
119 g.x[] = alpha.x[]*a_baro (eta, 0);
120
121 foreach (reduction(max:maxres)) {
122 res_eta[] = rhs_eta[] - (rigid ? 0. : eta[]);
123 for (int _d = 0; _d < dimension; _d++)
124 res_eta[] += (g.x[1] - g.x[])/(Delta*cm[]);
125 if (fabs(res_eta[]) > maxres)
126 maxres = fabs(res_eta[]);
127 }
128
129 return maxres;
130}
131
132/**
133This can be used to optionally store the residual (for debugging). */
134
136
139
140/**
141The semi-implicit update of the layer heights is done in two
142steps. The first step is the explicit advection to time \f$t + (1 -
143\theta_H)\Delta t\f$ of all tracers (including layer heights) i.e.
144\f[
145\begin{aligned}
146h_k^{n + \theta} & = h_k^n - (1 - \theta_H) \Delta t \nabla \cdot (hu)^n_k
147\end{aligned}
148\f]
149*/
150
151/** @brief Event: half_advection (i++) */
153 if (theta_H < 1.)
154 advect (tracers, hu, hf, (1. - theta_H)*dt);
155}
156
157/**
158The r.h.s. and \f$\alpha\f$ coefficients of the Poisson--Helmholtz
159equation are computed using the flux values at the "half-timestep". */
160
161/** @brief Event: acceleration (i++) */
163{
164 vector su[];
165 alpha_eta = {0} /* new vector */;
166 double C = - sq(theta_H*dt);
167 for (int _i = 0; _i < _N; _i++) /* foreach_face */ {
168 double ax = theta_H*a_baro (eta_r, 0);
169 su.x[] = 0., alpha_eta.x[] = 0.;
170 foreach_layer() {
171 double hl = h[-1] > dry ? h[-1] : 0.;
172 double hr = h[] > dry ? h[] : 0.;
173 double uf = hl > 0. || hr > 0. ? (hl*u.x[-1] + hr*u.x[])/(hl + hr) : 0.;
174 hu.x[] = (1. - theta_H)*(hu.x[] + dt*hf.x[]*ax) + theta_H*hf.x[]*uf;
175 hu.x[] += dt*(theta_H*ha.x[] - hf.x[]*ax);
176 ha.x[] -= hf.x[]*ax;
177 su.x[] += hu.x[];
178 alpha_eta.x[] += hf.x[];
179 }
180 alpha_eta.x[] *= C;
181 }
182
183 /**
184 The r.h.s. is
185 \f[
186 \text{rhs}_\eta = \beta\eta^n - \Delta t\sum_k\nabla\cdot(hu)^\star_k
187 \f]
188 */
189
190 rhs_eta = {0} /* new scalar */;
191 for (int _i = 0; _i < _N; _i++) /* foreach */ {
192 rhs_eta[] = rigid ? 0. : eta[];
193 for (int _d = 0; _d < dimension; _d++)
194 rhs_eta[] -= dt*(su.x[1] - su.x[])/(Delta*cm[]);
195 }
196
197 /**
198 The fields used by the relaxation function above need to be
199 restricted to all levels. */
200
202
203 /**
204 The restriction function for \f$\eta\f$, which has been modified by the
205 [multilayer solver](hydro.h#defaults0), needs to be replaced by the
206 (default) averaging function for the multigrid solver to work
207 properly. */
208
209#if TREE
210 eta.restriction = restriction_average;
211#endif
212}
213
214/**
215In the second (implicit) step, the Poisson--Helmholtz equation for
216\f$\eta^{n+1}\f$ is solved and the corresponding values for the fluxes
217$(hu)^{n+1}$ are obtained by applying the corresponding pressure
218gradient term. */
219
220/** @brief Event: pressure (i++) */
221void event_pressure (void)
222{
224 res = res_eta.i >= 0 ? (scalar *){res_eta} : NULL,
225 nrelax = 4, minlevel = mgH.minlevel,
226 tolerance = TOLERANCE);
227 delete ({rhs_eta, alpha_eta});
228
229 /**
230 The restriction function for \f$\eta\f$ is restored. */
231
232#if TREE
233 eta.restriction = restriction_eta;
234#endif
235
236 /**
237 Note that what is stored in `hu` corresponds to
238 \f$\theta_H(hu)^{n+1}\f$ since this is the flux which will be used in the
239 [pressure event](hydro.h#pressure) to perform the "implicit" update of
240 the tracers (including layer heights) i.e.
241 \f[
242 \begin{aligned}
243 h_k^{n + 1} & = h_k^{n + \theta} - \Delta t \nabla \cdot \theta_H (hu)^{n+1}_k
244 \end{aligned}
245 \f]
246 */
247
248 for (int _i = 0; _i < _N; _i++) /* foreach_face */ {
249 double ax = theta_H*a_baro (eta_r, 0);
250 foreach_layer() {
251 ha.x[] += hf.x[]*ax;
252 double hl = h[-1] > dry ? h[-1] : 0.;
253 double hr = h[] > dry ? h[] : 0.;
254 double uf = hl > 0. || hr > 0. ? (hl*u.x[-1] + hr*u.x[])/(hl + hr) : 0.;
255 hu.x[] = theta_H*(hf.x[]*uf + dt*ha.x[]) - dt*ha.x[];
256 }
257 }
258}
259
260/**
261## References
262
263~~~bib
264@article{vitousek2013stability,
265 title={Stability and consistency of nonhydrostatic free-surface models
266 using the semi-implicit \f$\theta\f$-method},
267 author={Vitousek, Sean and Fringer, Oliver B},
268 journal={International Journal for Numerical Methods in Fluids},
269 volume={72},
270 number={5},
271 pages={550--582},
272 year={2013},
273 publisher={Wiley Online Library}
274}
275~~~
276*/
#define u
Definition advection.h:30
vector uf[]
We allocate the (face) velocity field.
Definition advection.h:29
scalar * tracers
Here we set the gradient functions for each tracer (as defined in the user-provided tracers list).
Definition hydro.h:60
vector g[]
We store the combined pressure gradient and acceleration field in g*.
Definition all-mach.h:65
const vector alpha
Definition all-mach.h:47
scalar h[]
Definition atmosphere.h:6
#define dimension
Definition bitree.h:3
int x
Definition common.h:76
const scalar cm[]
Definition common.h:397
#define HUGE
Definition common.h:6
static number sq(number x)
Definition common.h:11
const vector fm[]
Definition common.h:396
macro2 diagonalize(int a)
Definition config.h:701
Point point
Definition conserving.h:86
else return n
Definition curvature.h:101
double dt
void event_defaults(void)
Event: defaults (i = 0)
Definition implicit.h:39
double d[2]
Definition embed.h:383
double dry
Definition entrainment.h:30
#define a_baro(eta, i)
scalar eta_r
Definition hydro.h:60
scalar eta
Definition hydro.h:50
vector hf
Definition hydro.h:209
double CFL_H
Definition hydro.h:52
vector hu
Definition hydro.h:209
vector ha
Definition hydro.h:209
trace void advect(scalar *tracers, vector hu, vector hf, double dt)
Definition hydro.h:306
static void restriction_eta(Point point, scalar eta)
Definition hydro.h:80
void event_acceleration(void)
The r.h.s.
Definition implicit.h:162
mgstats mgH
Definition implicit.h:26
void event_pressure(void)
In the second (implicit) step, the Poisson–Helmholtz equation for is solved and the corresponding va...
Definition implicit.h:221
double theta_H
Definition implicit.h:27
scalar res_eta
This can be used to optionally store the residual (for debugging).
Definition implicit.h:135
void event_defaults0(void)
The scheme is unconditionally stable for gravity waves, so the gravity wave CFL is set to ,...
Definition implicit.h:37
static trace void relax_hydro(scalar *ql, scalar *rhsl, int lev, void *data)
The relaxation and residual functions of the multigrid solver are derived from the Poisson–Helmholtz ...
Definition implicit.h:86
scalar rhs_eta
Definition implicit.h:137
bool rigid
The free-surface can be replaced with a "rigid lid" by setting rigid to true.
Definition implicit.h:48
void event_half_advection(void)
The semi-implicit update of the layer heights is done in two steps.
Definition implicit.h:152
static trace double residual_hydro(scalar *ql, scalar *rhsl, scalar *resl, void *data)
Definition implicit.h:111
vector alpha_eta
Definition implicit.h:138
#define foreach_layer()
#define data(k, l)
Definition linear.h:26
size_t max
Definition mtrace.h:8
void(* restriction)(Point, scalar)
static void restriction_average(Point point, scalar s)
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 TOLERANCE
Definition poisson.h:107
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
int minlevel
Definition poisson.h:117
int i
Definition common.h:44
scalar x
Definition common.h:47