Basilisk CFD
Adaptive Cartesian mesh PDE framework
Loading...
Searching...
No Matches
conservation.h
Go to the documentation of this file.
1/** @file conservation.h
2 */
3/**
4# A generic solver for systems of conservation laws
5
6Using the ideas of [Kurganov and Tadmor,
72000](references.bib#kurganov2000) it is possible to write a generic
8solver for systems of conservation laws of the form
9\f[
10\partial_t\left(\begin{array}{c}
11 s_i\\
12 \mathbf{v}_j\\
13 \end{array}\right) + \nabla\cdot\left(\begin{array}{c}
14 \mathbf{F}_i\\
15 \mathbf{T}_j\\
16 \end{array}\right) = 0
17\f]
18where \f$s_i\f$ is a list of scalar fields, \f$\mathbf{v}_j\f$ a list of
19vector fields and \f$\mathbf{F}_i\f$, \f$\mathbf{T}_j\f$ are the corresponding
20vector (resp. tensor) fluxes.
21
22Note that the [Saint-Venant solver](saint-venant.h) is a particular
23case of this generic algorithm.
24
25The user must provide the lists of conserved scalar and vector fields
26*/
27
28extern scalar * scalars;
29extern vector * vectors;
30
31/**
32as well as a function which, given the state of each quantity,
33returns the fluxes and the minimum/maximum eigenvalues
34(i.e. `eigenvalue[0]`/`eigenvalue[1]`) of the corresponding Riemann
35problem. */
36
37void flux (const double * state, double * flux, double * eigenvalue);
38
39/**
40## Time-integration
41
42### Setup
43
44Time integration will be done with a generic
45[predictor-corrector](predictor-corrector.h) scheme. */
46
47#include "predictor-corrector.h"
48
49/**
50The generic time-integration scheme in `predictor-corrector.h` needs
51to know which fields are updated i.e. all the scalars + the components
52of all the vector fields. It also needs a function to compute the
53time-derivatives of the evolving variables. */
54
57
58/** @brief Event: defaults (i = 0) */
59void event_defaults (void)
60{
63
64 /**
65 We switch to a pure minmod limiter by default for increased
66 robustness. */
67
68 theta = 1.;
69
70 /**
71 With the MUSCL scheme we use the CFL depends on the dimension of the
72 problem. */
73
74 if (CFL > 1./dimension)
75 CFL = 1./dimension;
76
77 /**
78 On trees we need to replace the default bilinear
79 refinement/prolongation with linear so that reconstructed values
80 also use slope limiting. */
81
82 #if TREE
83 for (int _s = 0; _s < 1; _s++) /* scalar in evolving */ {
84 s.refine = refine_linear;
87 }
88 #endif
89}
90
91/**
92At the end of the run we need to free the list (to avoid a memory
93leak). */
94
95event cleanup (i = end) free (evolving);
96
97/**
98User initialisation happens here. */
99
100event init (i = 0);
101
102/**
103### Computing fluxes
104
105The core of the central-upwind scheme (see e.g. section 3.1 of
106[Kurganov & Levy, 2002](references.bib#kurganov2002)) is the
107approximate solution of the Riemann problem given by the left and
108right states to get the fluxes `f`. */
109
110static double riemann (const double * right, const double * left,
111 double Delta, double * f, int len,
112 double dtmax)
113{
114 double fr[len], fl[len], er[2], el[2];
115 flux (right, fr, er);
116 flux (left, fl, el);
117 double ap = max(er[1], el[1]); ap = max(ap, 0.);
118 double am = min(er[0], el[0]); am = min(am, 0.);
119 double a = max(ap, -am);
120
121 if (a > 0.) {
122 for (int i = 0; i < len; i++)
123 f[i] = (ap*fl[i] - am*fr[i] + ap*am*(right[i] - left[i]))/(ap - am);
124 double dt = CFL*Delta/a;
125 if (dt < dtmax)
126 dtmax = dt;
127 }
128 else
129 for (int i = 0; i < len; i++)
130 f[i] = 0.;
131 return dtmax;
132}
133
135{
136 /**
137 The gradients of each quantity are stored in a list of dynamically-allocated
138 fields. First-order reconstruction is used for the gradient fields. */
139
140 vector * slopes = NULL;
141 for (int _s = 0; _s < 1; _s++) /* scalar in conserved */ {
142 vector slope = {0} /* new vector */;
143 for (int _d = 0; _d < dimension; _d++) {
144 slope.x.gradient = zero;
145 #if TREE
146 slope.x.prolongation = refine_linear;
147 #endif
148 }
150 }
152
153 /**
154 We allocated fields for storing fluxes for each scalar and vector
155 quantity. */
156
157 vector * lflux = NULL;
158 int len = list_len (conserved);
159 for (int _s = 0; _s < 1; _s++) /* scalar in conserved */ {
160 vector f1 = {0} /* new vector */;
162 }
163
164 /**
165 The predictor-corrector scheme treats all fields as scalars (stored in
166 the `conserved` list). We need to recover vector and tensor quantities
167 from these lists. To do so, knowing the number of scalar fields, we
168 split the scalar list into a list of scalars and a list of vectors. */
169
171
173 if (scalars) scalars[scalars_len].i = -1;
175
176 /**
177 We then do the same for the gradients i.e. split the list of vectors
178 into a list of vectors and a list of tensors. */
179
183
184 /**
185 And again for the fluxes. */
186
190
191 /**
192 We are ready to compute the fluxes through each face of the domain. */
193
194 for (int _i = 0; _i < _N; _i++) /* foreach_face */) {
195
196 /**
197 #### Left/right state reconstruction
198
199 We use the central values of each scalar/vector quantity and the
200 pre-computed gradients to compute the left and right states. */
201
202 double r[len], l[len]; // right/left Riemann states
203 double f[len]; // fluxes for each conserved quantity
204 double dx = Delta/2.;
205 int i = 0;
206 scalar s;
207 vector g;
208 for (s,g in scalars,scalar_slopes) {
209 r[i] = s[] - dx*g.x[];
210 l[i++] = s[-1] + dx*g.x[-1];
211 }
212 vector v;
213 tensor t;
214 for (v,t in vectors,vector_slopes) {
215 r[i] = v.x[] - dx*t.x.x[];
216 l[i++] = v.x[-1] + dx*t.x.x[-1];
217 #if dimension > 1
218 r[i] = v.y[] - dx*t.y.x[];
219 l[i++] = v.y[-1] + dx*t.y.x[-1];
220 #endif
221 #if dimension > 2
222 r[i] = v.z[] - dx*t.z.x[];
223 l[i++] = v.z[-1] + dx*t.z.x[-1];
224 #endif
225 }
226
227 /**
228 #### Riemann problem
229
230 We then call the generic Riemann solver and store the resulting fluxes
231 in the pre-allocated fields. */
232
233 dtmax = riemann (r, l, Delta*cm[]/fm.x[], f, len, dtmax);
234 i = 0;
235 for (int _fs = 0; _fs < 1; _fs++) /* vector in scalar_fluxes */
236 fs.x[] = fm.x[]*f[i++];
237 for (int _fv = 0; _fv < 1; _fv++) /* tensor in vector_fluxes */ {
238 fv.x.x[] = fm.x[]*f[i++];
239 #if dimension > 1
240 fv.y.x[] = fm.x[]*f[i++];
241 #endif
242 #if dimension > 2
243 fv.z.x[] = fm.x[]*f[i++];
244 #endif
245 }
246 }
247
248 /**
249 #### Update
250
251 The update for each scalar quantity is the divergence of the fluxes. */
252
253 for (int _i = 0; _i < _N; _i++) /* foreach */ {
254 scalar ds;
255 vector f;
256 for (ds,f in updates,lflux) {
257 ds[] = 0.;
258 for (int _d = 0; _d < dimension; _d++)
259 ds[] += (f.x[] - f.x[1])/(cm[]*Delta);
260 }
261 }
262
263 /**
264 #### Cleanup
265
266 We finally deallocate the memory used to store lists and gradient
267 fields. */
268
269 free (scalars);
270 free (vectors);
275 delete ((scalar *) slopes);
276 free (slopes);
277 delete ((scalar *) lflux);
278 free (lflux);
279
280 return dtmax;
281}
vector g[]
We store the combined pressure gradient and acceleration field in g*.
Definition all-mach.h:65
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
int min
Definition balance.h:192
#define dimension
Definition bitree.h:3
define l
free(list1)
int x
Definition common.h:76
double zero(double s0, double s1, double s2)
Definition common.h:116
vector * vectors_copy(vector *l)
Definition common.h:280
const scalar cm[]
Definition common.h:397
scalar * list_concat(scalar *l1, scalar *l2)
Definition common.h:234
scalar * list_copy(scalar *l)
Definition common.h:225
int list_len(scalar *list)
Definition common.h:180
#define vector(x)
Definition common.h:354
tensor * tensors_from_vectors(vector *v)
Definition common.h:320
@ left
Definition common.h:123
@ right
Definition common.h:123
const vector fm[]
Definition common.h:396
vector * vectors_from_scalars(scalar *s)
Definition common.h:289
vector * vectors_append(vector *list, vector v)
Definition common.h:258
scalar f[]
The primary fields are:
Definition two-phase.h:56
double update_conservation(scalar *conserved, scalar *updates, double dtmax)
event cleanup(i=end) free(evolving)
At the end of the run we need to free the list (to avoid a memory leak).
vector * vectors
scalar * evolving
The generic time-integration scheme in predictor-corrector.h needs to know which fields are updated i...
static double riemann(const double *right, const double *left, double Delta, double *f, int len, double dtmax)
scalar * scalars
void event_defaults(void)
Event: defaults (i = 0)
event init(i=0)
User initialisation happens here.
double dt
scalar s
Definition embed-tree.h:56
double v[2]
Definition embed.h:383
vector fs[]
Definition embed.h:22
scalar int i
Definition embed.h:74
double t
Definition events.h:24
#define dx(s)
We first define some useful macros, following the notations in Bonneton et al, 2011.
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))
double(* update)(scalar *evolving, scalar *updates, double dtmax)
def _i
Definition stencils.h:405
int i
Definition common.h:44
vector x
Definition common.h:67
scalar x
Definition common.h:47
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
double CFL
Definition utils.h:10
double theta
This is the generalised minmod limiter.
Definition utils.h:223
int state
scalar flux[]
Definition vof.h:166