OpenLB 1.7
Loading...
Searching...
No Matches
freeEnergyCoupling2D.h
Go to the documentation of this file.
1/* This file is part of the OpenLB library
2 *
3 * Copyright (C) 2018 Robin Trunk, Sam Avis
4 * E-mail contact: info@openlb.net
5 * The most recent release of OpenLB can be downloaded at
6 * <http://www.openlb.net/>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this program; if not, write to the Free
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22*/
23
24#ifndef FREE_ENERGY_COUPLING_2D_H
25#define FREE_ENERGY_COUPLING_2D_H
26
27namespace olb {
28
30
31 // Possible scope options: PerCell, PerBlock, PerCellWithParameters
33
34 struct ALPHA: public descriptors::FIELD_BASE<1> { };
35 struct KAPPA1: public descriptors::FIELD_BASE<1> { };
36 struct KAPPA2: public descriptors::FIELD_BASE<1> { };
37 struct KAPPA3: public descriptors::FIELD_BASE<1> { };
38
40
41
42 template <typename CELLS, typename PARAMETERS>
43 void apply(CELLS& cells, PARAMETERS& parameters) any_platform {
44
45 using V = typename CELLS::template value_t<names::A>::value_t;
46 using DESCRIPTOR = typename CELLS::template value_t<names::A>::descriptor_t;
47
48 // Get the cell of the first lattice
49 auto& cellA = cells.template get<names::A>();
50
51 // Get the cell of the second lattice
52 auto& cellB = cells.template get<names::B>();
53
54 V rhoA = cellA.computeRho();
55 V rhoB = cellB.computeRho();
56
57 V densitySum = rhoA + rhoB;
58 V densityDifference = rhoA - rhoB;
59
60 V kappa1 = parameters.template get<KAPPA1>();
61 V kappa2 = parameters.template get<KAPPA2>();
62
63 V term1 = 0.125 * kappa1 * (densitySum)
64 * (densitySum-1.) * (densitySum-2.);
65
66 V term2 = 0.125 * kappa2 * (densityDifference)
67 * (densityDifference-1.) * (densityDifference-2.);
68
69 V laplaceRho1 = -12.0 * rhoA;
70 V laplaceRho2 = -12.0 * rhoB;
71 V laplaceRho3 = 0.; // Only used in case of three fluids / lattices
72
73 auto alpha = parameters.template get<ALPHA>();
74
75 // Three fluids / lattices involved: A, B, and C
76 if constexpr (CELLS::map_t::keys_t::template contains<names::C>()){
77 // Cell of the third lattice:
78 auto& cellC = cells.template get<names::C>();
79
80 V rhoC = cellC.computeRho();
81
82 densitySum -= rhoC;
83 densityDifference -= rhoC;
84
85 V kappa3 = parameters.template get<KAPPA3>();
86 V term3 = kappa3 * rhoC * (rhoC - 1.) * (2. * rhoC - 1.);
87
88 laplaceRho3 = -12.0 * rhoC;
89
90 /*
91 * The neighbours of the currently looked at cell
92 * (i.e. cell 0) are ordered as following:
93 *
94 * 1 - 8 - 7
95 * | | |
96 * 2 - 0 - 6
97 * | | |
98 * 3 - 4 - 5
99 */
100 for(int iPop=1; iPop<DESCRIPTOR::q; iPop++) {
101
102 auto nextCellA = cellA.neighbor(descriptors::c<DESCRIPTOR>(iPop));
103 auto nextCellB = cellB.neighbor(descriptors::c<DESCRIPTOR>(iPop));
104 auto nextCellC = cellC.neighbor(descriptors::c<DESCRIPTOR>(iPop));
105
106 V rhoA = nextCellA.computeRho();
107 V rhoB = nextCellB.computeRho();
108 V rhoC = nextCellC.computeRho();
109
110 if(iPop % 2 == 0) {
111 laplaceRho1 += (2. * rhoA);
112 laplaceRho2 += (2. * rhoB);
113 laplaceRho3 += (2. * rhoC);
114 } else {
115 laplaceRho1 += rhoA;
116 laplaceRho2 += rhoB;
117 laplaceRho3 += rhoC;
118 }
119 }
120
121 laplaceRho1 *= 0.25;
122 laplaceRho2 *= 0.25;
123 laplaceRho3 *= 0.25;
124
125 cellC.template setField<descriptors::CHEM_POTENTIAL>( - term1 - term2 + term3
126 + 0.25*alpha*alpha*( (kappa2 + kappa1) * laplaceRho1
127 -(kappa2 - kappa1) * laplaceRho2
128 -(kappa2 + kappa1 + 4.*kappa3) * laplaceRho3 ));
129
130
131 } else { // Only 2 fluids / lattices involved: A and B
132
133 for(int iPop=1; iPop<DESCRIPTOR::q; iPop++) {
134
135 auto nextCellA = cellA.neighbor(descriptors::c<DESCRIPTOR>(iPop));
136 auto nextCellB = cellB.neighbor(descriptors::c<DESCRIPTOR>(iPop));
137
138 V rhoA = nextCellA.computeRho();
139 V rhoB = nextCellB.computeRho();
140
141 if(iPop % 2 == 0) {
142 laplaceRho1 += (2. * rhoA);
143 laplaceRho2 += (2. * rhoB);
144 } else {
145 laplaceRho1 += rhoA;
146 laplaceRho2 += rhoB;
147 }
148 }
149
150 laplaceRho1 *= 0.25;
151 laplaceRho2 *= 0.25;
152
153 }
154
155 cellA.template setField<descriptors::CHEM_POTENTIAL>(term1 + term2
156 + 0.25*alpha*alpha*( (kappa2 - kappa1) * laplaceRho2
157 +(kappa2 + kappa1) * (laplaceRho3 - laplaceRho1) ));
158
159 cellB.template setField<descriptors::CHEM_POTENTIAL>(term1 - term2
160 + 0.25*alpha*alpha*( (kappa2 - kappa1) * (laplaceRho1 - laplaceRho3)
161 -(kappa2 + kappa1) * laplaceRho2 ));
162
163 }
164
165};
166
167
168
169
171
172 // Possible scope options: PerCell, PerBlock, PerCellWithParameters
174
175 template <typename CELLS>
176 void apply(CELLS& cells) any_platform {
177
178 using V = typename CELLS::template value_t<names::A>::value_t;
179 using DESCRIPTOR = typename CELLS::template value_t<names::A>::descriptor_t;
180
181 // Get the cell of the first lattice
182 auto& cellA = cells.template get<names::A>();
183
184 // Get the cell of the second lattice
185 auto& cellB = cells.template get<names::B>();
186
187 V u[2] { };
188 V phi = cellA.computeRho();
189 V rho = cellB.computeRho();
190
191 /*
192 * Calculatations for lattice A. The neighbours of the currently
193 * looked at cell (i.e. cell 0) are ordered as following:
194 *
195 * 1 - 8 - 7
196 * | | |
197 * 2 - 0 - 6
198 * | | |
199 * 3 - 4 - 5
200 *
201 */
202 auto cellA1 = cellA.neighbor(descriptors::c<DESCRIPTOR>(1));
203 auto cellA2 = cellA.neighbor(descriptors::c<DESCRIPTOR>(2));
204 auto cellA3 = cellA.neighbor(descriptors::c<DESCRIPTOR>(3));
205
206 auto cellA7 = cellA.neighbor(descriptors::c<DESCRIPTOR>(7));
207 auto cellA6 = cellA.neighbor(descriptors::c<DESCRIPTOR>(6));
208 auto cellA5 = cellA.neighbor(descriptors::c<DESCRIPTOR>(5));
209
210 // 1/12 * ( -A1 -4*A2 -A3 +A7 +4*A6 +A5)
211 V gradMuPhiX = 1./12. * (
212 - cellA1.template getField<descriptors::CHEM_POTENTIAL>()
213 - 4. * cellA2.template getField<descriptors::CHEM_POTENTIAL>()
214 - cellA3.template getField<descriptors::CHEM_POTENTIAL>()
215 + cellA7.template getField<descriptors::CHEM_POTENTIAL>()
216 + 4. * cellA6.template getField<descriptors::CHEM_POTENTIAL>()
217 + cellA5.template getField<descriptors::CHEM_POTENTIAL>());
218
219 auto cellA8 = cellA.neighbor(descriptors::c<DESCRIPTOR>(8));
220 auto cellA4 = cellA.neighbor(descriptors::c<DESCRIPTOR>(4));
221
222 // 1/12 * ( +A1 +4*A8 +A7 -A3 -4*A4 -A5)
223 V gradMuPhiY = 1./12. * (
224 + cellA1.template getField<descriptors::CHEM_POTENTIAL>()
225 + 4. * cellA8.template getField<descriptors::CHEM_POTENTIAL>()
226 + cellA7.template getField<descriptors::CHEM_POTENTIAL>()
227 - cellA3.template getField<descriptors::CHEM_POTENTIAL>()
228 - 4. * cellA4.template getField<descriptors::CHEM_POTENTIAL>()
229 - cellA5.template getField<descriptors::CHEM_POTENTIAL>());
230
231 /*
232 * Calculatations for lattice B. The neighbours of the currently
233 * looked at cell (i.e. cell 0) are ordered as following:
234 *
235 * 1 - 8 - 7
236 * | | |
237 * 2 - 0 - 6
238 * | | |
239 * 3 - 4 - 5
240 */
241 auto cellB1 = cellB.neighbor(descriptors::c<DESCRIPTOR>(1));
242 auto cellB2 = cellB.neighbor(descriptors::c<DESCRIPTOR>(2));
243 auto cellB3 = cellB.neighbor(descriptors::c<DESCRIPTOR>(3));
244
245 auto cellB7 = cellB.neighbor(descriptors::c<DESCRIPTOR>(7));
246 auto cellB6 = cellB.neighbor(descriptors::c<DESCRIPTOR>(6));
247 auto cellB5 = cellB.neighbor(descriptors::c<DESCRIPTOR>(5));
248
249 // 1/12 * ( -B1 -4*B2 -B3 +B7 +4*B6 +B5)
250 V gradMuRhoX = 1./12. * (
251 - cellB1.template getField<descriptors::CHEM_POTENTIAL>()
252 - 4. * cellB2.template getField<descriptors::CHEM_POTENTIAL>()
253 - cellB3.template getField<descriptors::CHEM_POTENTIAL>()
254 + cellB7.template getField<descriptors::CHEM_POTENTIAL>()
255 + 4. * cellB6.template getField<descriptors::CHEM_POTENTIAL>()
256 + cellB5.template getField<descriptors::CHEM_POTENTIAL>());
257
258 auto cellB8 = cellB.neighbor(descriptors::c<DESCRIPTOR>(8));
259 auto cellB4 = cellB.neighbor(descriptors::c<DESCRIPTOR>(4));
260
261 // 1/12 * ( +B1 +4*B8 +B7 -B3 -4*B4 -B5)
262 V gradMuRhoY = 1./12. * (
263 + cellB1.template getField<descriptors::CHEM_POTENTIAL>()
264 + 4. * cellB8.template getField<descriptors::CHEM_POTENTIAL>()
265 + cellB7.template getField<descriptors::CHEM_POTENTIAL>()
266 - cellB3.template getField<descriptors::CHEM_POTENTIAL>()
267 - 4. * cellB4.template getField<descriptors::CHEM_POTENTIAL>()
268 - cellB5.template getField<descriptors::CHEM_POTENTIAL>());
269
270
271
272 // Get the cell of the third lattice, if it is provided
273 if constexpr (CELLS::map_t::keys_t::template contains<names::C>()){
274 auto& cellC = cells.template get<names::C>();
275 V psi = cellC.computeRho();
276
277 auto cellC1 = cellC.neighbor(descriptors::c<DESCRIPTOR>(1));
278 auto cellC2 = cellC.neighbor(descriptors::c<DESCRIPTOR>(2));
279 auto cellC3 = cellC.neighbor(descriptors::c<DESCRIPTOR>(3));
280
281 auto cellC7 = cellC.neighbor(descriptors::c<DESCRIPTOR>(7));
282 auto cellC6 = cellC.neighbor(descriptors::c<DESCRIPTOR>(6));
283 auto cellC5 = cellC.neighbor(descriptors::c<DESCRIPTOR>(5));
284
285
286 // 1/12 * ( -C1 -4*C2 -C3 +C7 +4*C6 +C5)
287 V gradMuPsiX = 1./12. * (
288 - cellC1.template getField<descriptors::CHEM_POTENTIAL>()
289 - 4 * cellC2.template getField<descriptors::CHEM_POTENTIAL>()
290 - cellC3.template getField<descriptors::CHEM_POTENTIAL>()
291 + cellC7.template getField<descriptors::CHEM_POTENTIAL>()
292 + 4 * cellC6.template getField<descriptors::CHEM_POTENTIAL>()
293 + cellC5.template getField<descriptors::CHEM_POTENTIAL>());
294
295 auto cellC8 = cellC.neighbor(descriptors::c<DESCRIPTOR>(8));
296 auto cellC4 = cellC.neighbor(descriptors::c<DESCRIPTOR>(4));
297
298 // 1/12 * ( +C1 +4*C8 +C7 -C3 -4*C4 -C5)
299 V gradMuPsiY = 1./12. * (
300 + cellC1.template getField<descriptors::CHEM_POTENTIAL>()
301 + 4 * cellC8.template getField<descriptors::CHEM_POTENTIAL>()
302 + cellC7.template getField<descriptors::CHEM_POTENTIAL>()
303 - cellC3.template getField<descriptors::CHEM_POTENTIAL>()
304 - 4 * cellC4.template getField<descriptors::CHEM_POTENTIAL>()
305 - cellC5.template getField<descriptors::CHEM_POTENTIAL>());
306
307 cellB.template setField<descriptors::FORCE>({
308 - rho*gradMuRhoX - phi*gradMuPhiX - psi*gradMuPsiX,
309 - rho*gradMuRhoY - phi*gradMuPhiY - psi*gradMuPsiY
310 });
311
312 cellB.computeU(u);
313 cellA.template setField<descriptors::FORCE>(u);
314 cellC.template setField<descriptors::FORCE>(u);
315
316 } else {
317
318 cellB.template setField<descriptors::FORCE>({
319 - rho*gradMuRhoX - phi*gradMuPhiX,
320 - rho*gradMuRhoY - phi*gradMuPhiY
321 });
322
323 cellB.computeU(u);
324 cellA.template setField<descriptors::FORCE>(u);
325 }
326
327 }
328};
329
330
331
332
334
335 // Possible scope options: PerCell, PerBlock, PerCellWithParameters
337
338 template <typename CELLS>
339 void apply(CELLS& cells) any_platform {
340
341 using V = typename CELLS::template value_t<names::A>::value_t;
342 using DESCRIPTOR = typename CELLS::template value_t<names::A>::descriptor_t;
343
344 // Get the cell of the first lattice
345 auto& cellA = cells.template get<names::A>();
346
347 // Get the cell of the second lattice
348 auto& cellB = cells.template get<names::B>();
349
350 V u[DESCRIPTOR::d];
351 cellB.computeU(u);
352 cellA.defineU(u);
353
354 // If relevant, get the cell of the third lattice
355 if constexpr (CELLS::map_t::keys_t::template contains<names::C>()){
356 auto& cellC = cells.template get<names::C>();
357 cellC.defineU(u);
358 }
359 }
360};
361
362
363
365 // Possible scope options: PerCell, PerBlock, PerCellWithParameters
367
368 struct RHO: public descriptors::FIELD_BASE<1> { };
369
371
372 template <typename CELLS, typename PARAMETERS>
373 void apply(CELLS& cells, PARAMETERS& parameters) any_platform {
374
375 using V = typename CELLS::template value_t<names::A>::value_t;
376
377 auto outletRho = parameters.template get<RHO>();
378
379 // Get the cell of the first lattice
380 auto& cellA = cells.template get<names::A>();
381
382 // Get the cell of the second lattice
383 auto& cellB = cells.template get<names::B>();
384
385 V rho0, phi;
386 rho0 = cellA.computeRho();
387 phi = cellB.computeRho();
388
389 cellA.defineRho(outletRho);
390
391 V temp = phi * outletRho / rho0;
392 cellB.defineRho(temp);
393
394
395 // Get the cell of the third lattice, in case it exists
396 if constexpr (CELLS::map_t::keys_t::template contains<names::C>()){
397 auto& cellC = cells.template get<names::C>();
398
399 V psi = cellC.computeRho();
400 temp = psi * outletRho / rho0;
401 cellC.defineRho(temp);
402 }
403
404 }
405};
406
407} // namespace olb
408
409#endif
Top level namespace for all of OpenLB.
OperatorScope
Block-wide operator application scopes.
Definition operator.h:54
@ PerCell
Per-cell application, i.e. OPERATOR::apply is passed a CELL concept implementation.
@ PerCellWithParameters
Per-cell application with parameters, i.e. OPERATOR::apply is passed a CELL concept implementation an...
#define any_platform
Define preprocessor macros for device-side functions, constant storage.
Definition platform.h:78
void apply(CELLS &cells, PARAMETERS &parameters) any_platform
static constexpr OperatorScope scope
void apply(CELLS &cells, PARAMETERS &parameters) any_platform
static constexpr OperatorScope scope
void apply(CELLS &cells) any_platform
static constexpr OperatorScope scope
void apply(CELLS &cells) any_platform
static constexpr OperatorScope scope
Base of a field whose size is defined by [C,U_1,...,U_N]^T * [1,V_1,...V_N].
Plain wrapper for list of types.
Definition meta.h:276