Basilisk CFD
Adaptive Cartesian mesh PDE framework
Loading...
Searching...
No Matches
henry.h
Go to the documentation of this file.
1/** @file henry.h
2 */
3/**
4# Advection/diffusion of a soluble tracer
5
6We consider the transport and diffusion of a tracer \f$c\f$ with different
7solubilities in the two-phases described by
8[two-phase.h](/src/two-phase.h).
9
10The diffusion coefficients in the two phases are \f$D_1\f$ and \f$D_2\f$ and
11the jump in concentration at the interface is given by
12\f[
13c_1 = \alpha c_2
14\f]
15The advection/diffusion equation for \f$c\f$ can then be written
16\f[
17\partial_t c + \nabla\cdot(\mathbf{u} c) =
18 \nabla\cdot\left(D\nabla c - D \frac{c(\alpha - 1)}
19 {\alpha f + (1 - f)}\nabla f\right)
20\f]
21with \f$f\f$ the volume fraction and
22\f[
23D = \frac{D_1 D_2}{D_2 f + D_1 (1 - f)}
24\f]
25the harmonic mean of the diffusion coefficients (see [Haroun et al.,
262010](#haroun2010)).
27
28The diffusion coefficients and solubility are attributes of each
29tracer.
30
31The *stracers* list of soluble tracers must be defined by the calling
32code. */
33
35 double D1, D2, alpha; //D1 for f = 1, D2 for f = 0
36 scalar phi1, phi2; // private
37}
38
39extern scalar * stracers;
40
41/**
42## Defaults
43
44On trees we need to ensure conservation of the tracer when
45refining/coarsening. */
46
47#if TREE
48/** @brief Event: defaults (i = 0) */
49void event_defaults (void)
50{
51 for (int _s = 0; _s < 1; _s++) /* scalar in stracers */ {
52#if EMBED
53 s.refine = refine_embed_linear;
55#else
56 s.refine = refine_linear;
57#endif
59 }
60}
61#endif // TREE
62
63/**
64## Advection
65
66To avoid numerical diffusion through the interface we use the [VOF
67tracer transport scheme](/src/vof.h) for the temporary fields
68\f$\phi_1\f$ and \f$\phi_2\f$, see section 3.2 of [Farsoiya et al.,
692021](#farsoiya2021). */
70
72
73/** @brief Event: vof (i++) */
74void event_vof (void)
75{
76 phi_tracers = f.tracers;
77 for (int _c = 0; _c < 1; _c++) /* scalar in stracers */ {
78 scalar phi1 = {0} /* new scalar */, phi2 = {0} /* new scalar */;
79 c.phi1 = phi1, c.phi2 = phi2;
82 phi2.inverse = true;
83
84 f.tracers = list_append (f.tracers, phi1);
85 f.tracers = list_append (f.tracers, phi2);
86
87 /**
88 \f$\phi_1\f$ and \f$\phi_2\f$ are computed from \f$c\f$ as
89 \f[
90 \phi_1 = c \frac{\alpha f}{\alpha f + (1 - f)}
91 \f]
92 \f[
93 \phi_2 = c \frac{1 - f}{\alpha f + (1 - f)}
94 \f]
95 */
96
97 for (int _i = 0; _i < _N; _i++) /* foreach */ {
98 double a = c[]/(f[]*c.alpha + (1. - f[]));
99 phi1[] = a*f[]*c.alpha;
100 phi2[] = a*(1. - f[]);
101 }
102 }
103}
104
105/**
106## Diffusion
107
108We first define the relaxation and residual functions needed to solve
109the implicit discrete system
110\f[
111\frac{c^{n + 1} - c^n}{\Delta t} =
112\nabla\cdot (D \nabla c^{n + 1} + \beta c^{n + 1})
113\f]
114see section 3.2 of [Farsoiya et al., 2021](#farsoiya2021).
115
116Note that these functions are close to that in [poisson.h]() and
117[diffusion.h]() but with the additional term \f$\nabla\cdot (\beta c^{n
118+ 1})\f$. */
119
124
125static void h_relax (scalar * al, scalar * bl, int l, void * data)
126{
127 scalar a = al[0], b = bl[0];
128 struct HDiffusion * p = (struct HDiffusion *) data;
129 vector D = p->D, beta = p->beta;
130
131 scalar c = a;
132 for (int _i = 0; _i < l; _i++) {
133 double n = - sq(Delta)*b[], d = cm[]/dt*sq(Delta);
134 for (int _d = 0; _d < dimension; _d++) {
135 n += D.x[1]*a[1] + D.x[]*a[-1] +
136 Delta*(beta.x[1]*a[1] - beta.x[]*a[-1])/2.;
137 d += D.x[1] + D.x[] - Delta*(beta.x[1] - beta.x[])/2.;
138 }
139 c[] = n/d;
140 }
141}
142
143static double h_residual (scalar * al, scalar * bl, scalar * resl, void * data)
144{
145 scalar a = al[0], b = bl[0], res = resl[0];
146 struct HDiffusion * p = (struct HDiffusion *) data;
147 vector D = p->D, beta = p->beta;
148 double maxres = 0.;
149#if TREE
150 /* conservative coarse/fine discretisation (2nd order) */
151 vector g[];
152 for (int _i = 0; _i < _N; _i++) /* foreach_face */
153 g.x[] = D.x[]*face_gradient_x (a, 0) + beta.x[]*face_value (a, 0);
154 foreach (reduction(max:maxres)) {
155 res[] = b[] + cm[]/dt*a[];
156 for (int _d = 0; _d < dimension; _d++)
157 res[] -= (g.x[1] - g.x[])/Delta;
158 if (fabs (res[]) > maxres)
159 maxres = fabs (res[]);
160 }
161#else // !TREE
162 /* "naive" discretisation (only 1st order on trees) */
163 foreach (reduction(max:maxres)) {
164 res[] = b[] + cm[]/dt*a[];
165 for (int _d = 0; _d < dimension; _d++)
166 res[] -= (D.x[1]*face_gradient_x (a, 1) -
167 D.x[0]*face_gradient_x (a, 0) +
168 beta.x[1]*face_value (a, 1) -
169 beta.x[0]*face_value (a, 0))/Delta;
170 if (fabs (res[]) > maxres)
171 maxres = fabs (res[]);
172 }
173#endif // !TREE
174 return maxres;
175}
176
177/** @brief Event: tracer_diffusion (i++) */
179{
180 free (f.tracers);
181 f.tracers = phi_tracers;
182 for (int _c = 0; _c < 1; _c++) /* scalar in stracers */ {
183
184 /**
185 The advected concentration is computed from \f$\phi_1\f$ and \f$\phi_2\f$ as
186 \f[
187 c = \phi_1 + \phi_2
188 \f]
189 and these fields are then discarded. */
190
191 scalar phi1 = c.phi1, phi2 = c.phi2, r[];
192 for (int _i = 0; _i < _N; _i++) /* foreach */ {
193 c[] = phi1[] + phi2[];
194 r[] = - cm[]*c[]/dt;
195 }
196 delete ({phi1, phi2});
197
198 /**
199 The diffusion equation for \f$c\f$ is then solved using the multigrid
200 solver and the residual and relaxation functions defined above. */
201
202 vector D[], beta[];
203 for (int _i = 0; _i < _N; _i++) /* foreach_face */ {
204 double ff = (f[] + f[-1])/2.;
205 D.x[] = fm.x[]*c.D1*c.D2/(c.D1*(1. - ff) + ff*c.D2);
206 beta.x[] = - D.x[]*(c.alpha - 1.)/
207 (ff*c.alpha + (1. - ff))*(f[] - f[-1])/Delta;
208 }
209
210 restriction ({D, beta, cm});
211 struct HDiffusion q;
212 q.D = D;
213 q.beta = beta;
214 mg_solve ({c}, {r}, h_residual, h_relax, &q);
215 }
216}
217
218/**
219## References
220
221~~~bib
222@article{haroun2010,
223 title = {Volume of fluid method for interfacial reactive mass transfer:
224 application to stable liquid film},
225 author = {Haroun, Y and Legendre, D and Raynal, L},
226 journal = {Chemical Engineering Science},
227 volume = {65},
228 number = {10},
229 pages = {2896--2909},
230 year = {2010},
231 doi = {10.1016/j.ces.2010.01.012}
232}
233
234@hal{farsoiya2021, hal-03227997}
235~~~
236*/
vector g[]
We store the combined pressure gradient and acceleration field in g*.
Definition all-mach.h:65
vector q[]
The primitive variables are the momentum , pressure , density , (face) specific volume ,...
Definition all-mach.h:44
const vector alpha
Definition all-mach.h:47
const vector a
Definition all-mach.h:59
#define dimension
Definition bitree.h:3
define l
free(list1)
int x
Definition common.h:76
#define face_gradient_x(a, i)
Definition common.h:403
void(* scalar_clone)(scalar, scalar)
Definition common.h:352
#define face_value(a, i)
Definition common.h:406
const scalar cm[]
Definition common.h:397
static number sq(number x)
Definition common.h:11
scalar * list_append(scalar *list, scalar s)
Definition common.h:188
const vector fm[]
Definition common.h:396
scalar f[]
The primary fields are:
Definition two-phase.h:56
#define p
Definition tree.h:320
else return n
Definition curvature.h:101
double dt
static void refine_embed_linear(Point point, scalar s)
Definition embed-tree.h:270
scalar s
Definition embed-tree.h:56
double d[2]
Definition embed.h:383
vector D[]
The linear system can be inverted with the multigrid Poisson solver.
vector beta[]
Definition hele-shaw.h:40
scalar phi1
Definition henry.h:36
scalar phi2
Definition henry.h:36
void event_vof(void)
Event: vof (i++)
Definition henry.h:74
void event_tracer_diffusion(void)
Event: tracer_diffusion (i++)
Definition henry.h:178
scalar * stracers
attribute
Definition henry.h:34
static void h_relax(scalar *al, scalar *bl, int l, void *data)
Definition henry.h:125
void event_defaults(void)
Event: defaults (i = 0)
Definition henry.h:49
static scalar * phi_tracers
Definition henry.h:71
static double h_residual(scalar *al, scalar *bl, scalar *resl, void *data)
Definition henry.h:143
#define data(k, l)
Definition linear.h:26
size_t max
Definition mtrace.h:8
static void restriction_volume_average(Point point, scalar s)
void(* restriction)(Point, scalar)
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))
size *double * b
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
def _i
Definition stencils.h:405
vector D
Definition henry.h:121
vector beta
Definition henry.h:122
scalar x
Definition common.h:47
scalar c
Definition vof.h:57