OpenLB 1.8.1
Loading...
Searching...
No Matches
freeSurfacePostProcessor2D.hh
Go to the documentation of this file.
1/* This file is part of the OpenLB library
2 *
3 * Copyright (C) 2021 Claudius Holeksa
4 * 2024-2025 Danial Khazaeipoul
5 * E-mail contact: info@openlb.net
6 * The most recent release of OpenLB can be downloaded at
7 * <http://www.openlb.net/>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public
20 * License along with this program; if not, write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23*/
24
25#ifndef FREE_SURFACE_POST_PROCESSOR_2D_HH
26#define FREE_SURFACE_POST_PROCESSOR_2D_HH
27
29#include "core/blockLattice.h"
30
31namespace olb {
32
36template<typename T, typename DESCRIPTOR>
37template<typename CELL>
39
40 using namespace olb::FreeSurface;
41
42 // Reset CELL_FLAGS and TEMP_MASS_EXCHANGE here, as they are needed in the final post-processor step
43 setCellFlags(cell, FreeSurface::Flags::None);
44 Vector<T, DESCRIPTOR::q> zero_mass_exchange{};
45 cell.template setField<FreeSurface::TEMP_MASS_EXCHANGE>(zero_mass_exchange);
46
47 // Skip if the current cell is not an interface cell.
48 if (!isCellType(cell, FreeSurface::Type::Interface)) {
49 return;
50 }
51
52 // Check if the current interface cell has gas or fluid neighbours
53 FreeSurface::NeighbourInfo nbrInfo = getNeighbourInfo(cell);
54 bool hasNoGasNeighbours = !nbrInfo.has_gas_neighbours;
55 bool hasNoFluidNeighbours = !nbrInfo.has_fluid_neighbours;
56 bool isHealthyInterfaceCell = isHealthyInterface(cell);
57
58 // The cell has only interface cells as neighbours
59 if (hasNoGasNeighbours && hasNoFluidNeighbours) {
60 hasNoGasNeighbours = false;
61 hasNoFluidNeighbours = false;
62 isHealthyInterfaceCell = true;
63 }
64
65 // The notation used here differs from that in N. Thuerey's dissertation (2007), specifically in Section 4.1.
66 // This is because the free-surface post-processors are scheduled for execution during the PostStream stage,
67 // which occurs after the collideAndStream step. However, mass exchange computations require access to
68 // post-collision distribution functions at this stage.
69 // Since the post-collision distribution functions are not directly available here, we rely on the fact that,
70 // for the current cell, the post-collision f_(i) is equal to that of the corresponding neighbouring cell
71 // post-stream, i.e., cell[i](*) = nbrCell[i].
72 // Keep in mind that each cell has DESCRIPTOR::q neighbours, and each neighbor contains DESCRIPTOR::q
73 // distribution functions.
74 T mass_exchange = T(0);
75 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
76 auto nbrCell = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
77
78 // Skip if the neighbour is a gas cell, i.e., no mass exchange
79 if (isCellType(nbrCell, FreeSurface::Type::Gas)) { continue; }
80
81 // Mass exchange with a liquid neighbour, i.e., computed uisng the difference between incoming and outgoing DFs
82 if (isCellType(nbrCell, FreeSurface::Type::Fluid)) {
83 mass_exchange += cell[descriptors::opposite<DESCRIPTOR>(iPop)] - nbrCell[iPop];
84 continue;
85 }
86
87 // Check if the neighbour cell has gas or fluid neighbours
88 FreeSurface::NeighbourInfo nbrNeighbourInfo = getNeighbourInfo(nbrCell);
89 bool nbrHasNoGasNeighbours = !nbrNeighbourInfo.has_gas_neighbours;
90 bool nbrHasNoFluidNeighbours = !nbrNeighbourInfo.has_fluid_neighbours;
91 bool nbrIsHealthyInterfaceCell = isHealthyInterface(nbrCell);
92
93 // Neighbour cell has only interface cells as neighbours
94 if (nbrHasNoGasNeighbours && nbrHasNoFluidNeighbours) {
95 nbrHasNoGasNeighbours = false;
96 nbrHasNoFluidNeighbours = false;
97 nbrIsHealthyInterfaceCell = true;
98 }
99
100 T pdf_difference = T(0);
101 if (isCellType(nbrCell, FreeSurface::Type::Interface)) {
102 // Simple mass exchange if both current cell and neighbour cell are healthy interfaces
103 if (isHealthyInterfaceCell && nbrIsHealthyInterfaceCell) {
104 pdf_difference = cell[descriptors::opposite<DESCRIPTOR>(iPop)] - nbrCell[iPop];
105 }
106 else {
107 // A healthy interface cell with a neighbour that has no gas neighbours itself,
108 // or an interface cell with no liquid neighbours, but its neighbor has a liquid neighbour.
109 // make this cell empty, ref. N. Thuerey dissertation, 2007.
110 if ((isHealthyInterfaceCell && nbrHasNoGasNeighbours) || (hasNoFluidNeighbours && !nbrHasNoFluidNeighbours)) {
111 pdf_difference = -nbrCell[iPop];
112 }
113 else {
114 // A healthy interface cell with a neighbour that has no fluid neighbours itself,
115 // or an interface cell with no gas neighbours, but its neighbour has a gas neighbour.
116 // make the neighbour cell empty, ref. N. Thuerey dissertation, 2007.
117 if ((isHealthyInterfaceCell && nbrHasNoFluidNeighbours) || (hasNoGasNeighbours && !nbrHasNoGasNeighbours)) {
118 pdf_difference = cell[descriptors::opposite<DESCRIPTOR>(iPop)];
119 }
120 else {
121 // An interface cell with no liquid neighbors, whose neighbor also has no liquid neighbors,
122 // or an interface cell with no gas neighbors, whose neighbor also has no liquid neighbors.
123 // ref. N. Thuerey dissertation, 2007.
124 if ((hasNoFluidNeighbours && nbrHasNoFluidNeighbours) || (hasNoGasNeighbours && nbrHasNoGasNeighbours)) {
125 pdf_difference = cell[descriptors::opposite<DESCRIPTOR>(iPop)] - nbrCell[iPop];
126 }
127 }
128 }
129 }
130 }
131
132 const T epsilon_average = T(0.5) * (getClampedEpsilon(cell) + getClampedEpsilon(nbrCell));
133 mass_exchange += pdf_difference * epsilon_average;
134 }
135
136 // Update the mass of interface cell
137 const auto mass = cell.template getField<FreeSurface::MASS>();
138 cell.template setField<FreeSurface::MASS>(mass + mass_exchange);
139}
140
144template<typename T, typename DESCRIPTOR>
145template<typename CELL, typename PARAMETERS>
147
148 using namespace olb::FreeSurface;
149
150 const bool drop_isolated_cells = params.template get<FreeSurface::DROP_ISOLATED_CELLS>();
151 const auto transition = params.template get<FreeSurface::TRANSITION>();
152 const auto lonely_threshold = params.template get<FreeSurface::LONELY_THRESHOLD>();
153 const bool has_surface_tension = params.template get<FreeSurface::HAS_SURFACE_TENSION>();
154 const auto surface_tension = params.template get<FreeSurface::SURFACE_TENSION_PARAMETER>();
155
156 if (!isCellType(cell, FreeSurface::Type::Interface)) {
157 return;
158 }
159
160 FreeSurface::NeighbourInfo nbrInfo = getNeighbourInfo(cell);
161
162 // Adjust gas density, using Laplace pressure but without support for the bubble model
163 T deltaRho = T(0);
164 if (has_surface_tension && nbrInfo.has_gas_neighbours) {
165 const T curvature = computeCurvature(cell);
166 deltaRho = T(6) * surface_tension * curvature;
167 }
168 const T bubble_density = T(1);
169 const T gas_density = bubble_density - deltaRho;
170
171 // Replace the distribution functions of the gas cell using the updated density.
172 // A race condition is not expected here since only the distribution functions
173 // of an interface cell are modified, while only the distribution functions of
174 // a neighboring gas cell are read.
176 for (int iPop = 1; iPop < DESCRIPTOR::q; iPop++) {
177 auto nbrCell = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
178
179 // Distribution functions of an interface cell are reconstructed only for the missing directions,
180 // i.e., incoming DFs from gas cells, using the pressure anti bounce back boundary condition.
181 // Note: the notation used here differs from that in N. Thuerey's dissertation (2007), refer to the
182 // explanation given above.
183 if (isCellType(nbrCell, FreeSurface::Type::Gas)) {
184 constexpr bool usePrecise = true;
185 Vector<T, DESCRIPTOR::d> prev_velocity = cell.template getField<FreeSurface::PREVIOUS_VELOCITY>();
186
187 // (1) Precise formulation of the pressure anti bounce back boundary condition: feq_(i) + feq_(-i) - nbr(i)
188 // This is mathematically identical to the regular formulation, but the linear term of the 2nd order
189 // equilibrium equation is manually eliminated due to its cancellation during summation, i.e., c_(-i).u = -(c_(i).u).
190 // This formulation offers consistent floating-point rounding behaviour during arithmetic operations.
191 if constexpr (usePrecise) {
192 const T uSqr = util::normSqr<T, DESCRIPTOR::d>(prev_velocity);
193 Vector<T, DESCRIPTOR::d> direction{};
194 for (int i = 0; i < DESCRIPTOR::d; ++i) { direction[i] = descriptors::c<DESCRIPTOR>(iPop, i); }
195 const T c_u = util::dotProduct(direction, prev_velocity);
196
198 T(2) * gas_density * descriptors::t<T, DESCRIPTOR>(iPop) * (T(1) + T(4.5) * c_u * c_u - T(1.5) * uSqr)
199 - T(2) * descriptors::t<T, DESCRIPTOR>(iPop)
200 - nbrCell[iPop];
201 }
202 // (2) Regular formulation of the pressure anti bounce back boundary condition: feq_(i) + feq_(-i) - nbr(i)
203 // This formulation might suffer from inconsistent floating-point rounding behaviour during arithmetic
204 // operations, where the expression c_(-i).u = -(c_(i).u) could become untrue.
205 else {
206 T u[DESCRIPTOR::d] = {prev_velocity[0], prev_velocity[1]};
208 equilibrium<DESCRIPTOR>::secondOrder(iPop, gas_density, u)
210 - nbrCell[iPop];
211 }
212 }
213 else {
216 }
217 }
218
219 // Update interafce cell populations using the computed dfs in previous step.
220 for (int iPop = 1; iPop < DESCRIPTOR::q; iPop++) { cell[iPop] = dfs[iPop]; }
221
222 // Using the updated mass and density, find and set appropriate flag on the current interface cell
223 const auto rho = cell.computeRho();
224 const auto mass = cell.template getField<FreeSurface::MASS>();
225
226 // An interface cell is flagged as toGas if its volume fraction is below the threshold
227 if (mass < -transition * rho) {
228 setCellFlags(cell, FreeSurface::Flags::ToGas);
229 return;
230 }
231
232 // An interface cell is flagged as toFluid if its volume fraction is above the (1.0 + threshold)
233 if (mass > (T(1) + transition) * rho) {
234 setCellFlags(cell, FreeSurface::Flags::ToFluid);
235 return;
236 }
237
238 // An interface cell with no fluid neighbours is flagged as toGas:
239 // (1) If its volume fraction is below the (lonely threshold) and has interface neighbours (not isolated)
240 // (2) If it has no interface neighbours (isolated) and drop_isolated_cells is set
241 if (!nbrInfo.has_fluid_neighbours) {
242 if (mass < lonely_threshold * rho && nbrInfo.interface_neighbours != 0) {
243 setCellFlags(cell, FreeSurface::Flags::ToGas);
244 return;
245 }
246
247 if (nbrInfo.interface_neighbours == 0 && drop_isolated_cells) {
248 setCellFlags(cell, FreeSurface::Flags::ToGas);
249 return;
250 }
251 }
252
253 // An interface cell with no gas neighbours is flagged as toFluid:
254 // (1) If its volume fraction is above the (1.0 - lonely threshold) and has interface neighbours (not isolated)
255 // (2) If it has no interface neighbours (isolated) and drop_isolated_cells is set
256 if (!nbrInfo.has_gas_neighbours) {
257 if (mass > (T(1) - lonely_threshold) * rho && nbrInfo.interface_neighbours != 0) {
258 setCellFlags(cell, FreeSurface::Flags::ToFluid);
259 return;
260 }
261
262 if (nbrInfo.interface_neighbours == 0 && drop_isolated_cells) {
263 setCellFlags(cell, FreeSurface::Flags::ToFluid);
264 return;
265 }
266 }
267}
268
269/*
270 * Free Surface Processor 4: ToFluid
271 */
272template<typename T, typename DESCRIPTOR>
273template<typename CELL>
275 using namespace olb::FreeSurface;
276
277 // Convert a gas cell to interface if it is in the neighborhood of a toFluid neighbour cell
278 if (isCellType(cell, FreeSurface::Type::Gas) && hasNeighbourFlags(cell, FreeSurface::Flags::ToFluid)) {
279 // Initialize distribution functions for a cell converted to interface from gas, using the
280 // equilibrium refilling method, i.e., the average velocity and density of neighboring cells.
281 setCellFlags(cell, FreeSurface::Flags::NewInterface);
282 T rho_average = T(0);
283 T u_average[DESCRIPTOR::d] = {T(0), T(0)};
284 std::uint8_t count = std::uint8_t(0);
285
286 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
287 auto nbrCell = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
288
289 // Only consider neighbouring cells which are fluid or interface cells.
290 // Note: newly converted interface cells are not considered here, i.e., CELL_TYPE is not updated yet.
291 if (isCellType(nbrCell, FreeSurface::Type::Fluid) || isCellType(nbrCell, FreeSurface::Type::Interface)) {
292 T rho_tmp = T(0);
293 T u_tmp[DESCRIPTOR::d] = {T(0), T(0)};
294 ++count;
295 nbrCell.computeRhoU(rho_tmp, u_tmp);
296 rho_average += rho_tmp;
297 for (std::size_t i = 0; i < DESCRIPTOR::d; ++i) { u_average[i] += u_tmp[i]; }
298 }
299 }
300
301 if (count > std::uint8_t(0)) {
302 rho_average /= static_cast<T>(count);
303 for (std::size_t i = 0; i < DESCRIPTOR::d; ++i) { u_average[i] /= static_cast<T>(count); }
304 }
305 else {
306 // If no valid neighbouring cells are found
307 rho_average = T(1);
308 for (std::size_t i = 0; i < DESCRIPTOR::d; ++i) { u_average[i] = T(0); }
309 }
310
311 cell.iniEquilibrium(rho_average, u_average);
312 }
313
314 // If an interface cell with a ToGas flag has a neighbouring ToFluid cell, unset the ToGas flag
315 if (hasCellFlags(cell, FreeSurface::Flags::ToGas)) {
316 if (hasNeighbourFlags(cell, FreeSurface::Flags::ToFluid)) {
317 setCellFlags(cell, FreeSurface::Flags::None);
318 }
319 }
320}
321
325template<typename T, typename DESCRIPTOR>
326template<typename CELL>
328 using namespace olb::FreeSurface;
329
330 if (isCellType(cell, FreeSurface::Type::Fluid)) {
331 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
332 auto nbrCell = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
333
334 // Convert a liquid cell to interface if it is in the neighborhood of a toGas cell
335 if (hasCellFlags(nbrCell, FreeSurface::Flags::ToGas)) {
336 setCellFlags(cell, FreeSurface::Flags::NewInterface);
337 T rho = cell.computeRho();
338 cell.template setField<FreeSurface::MASS>(rho);
339
340 // Cell already flagged as NewInterface, skip the remaining neighbours.
341 break;
342 }
343 }
344 }
345}
346
350template<typename T, typename DESCRIPTOR>
351template<typename CELL>
353 using namespace olb::FreeSurface;
354
355 // Note that EPSILON cannot be set in this operator because it is needed for the normal computation,
356 // thus MASS is set here and EPSILON is set by the next operator.
357 // Skip if the current cell is not an interface cell.
358 if (!isCellType(cell, FreeSurface::Type::Interface)) {
359 return;
360 }
361
362 T mass_excess = T(0);
363 // If an interface cell is flagged as toGas,
364 if (hasCellFlags(cell, FreeSurface::Flags::ToGas)) {
365 // Get mass of the current interface cell
366 const T mass = cell.template getField<FreeSurface::MASS>();
367 mass_excess = mass;
368 cell.template setField<FreeSurface::MASS>(T(0));
369 }
370 // If an interface cell is flagged as toFluid,
371 else if (hasCellFlags(cell, FreeSurface::Flags::ToFluid)) {
372 // Get density and mass of the current interface cell
373 const T rho = cell.computeRho();
374 const T mass = cell.template getField<FreeSurface::MASS>();
375 mass_excess = mass - rho;
376 cell.template setField<FreeSurface::MASS>(rho);
377 }
378 else { return; }
379
380 // Redistribute the excess mass among the neighbouring interface cells, note that
381 // if no neighbouring interface cells are found, the excess mass is lost or gained
382 // by the current cell.
383 // TODO: Thuerey Paper says we can't use new interface cells or flagged cells
384 std::uint8_t oldIntefaceNbrs = std::uint8_t(0);
385 std::uint8_t newIntefaceNbrs = std::uint8_t(0);
386 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
387 auto nbrCell = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
388
389 // Note that CELL_TYPE is not updated yet, and if no flag is set for this neighbour cell, it is an old interface cell
390 // This means that this neighbour cell will remain an interface cell for the next time step
391 if (isCellType(nbrCell, FreeSurface::Type::Interface) && hasCellFlags(nbrCell, FreeSurface::Flags::None)) {
392 ++oldIntefaceNbrs;
393 }
394 // If the neighbour cell is flagged as Newinterface, it is a new interface cell
395 else if (hasCellFlags(nbrCell, FreeSurface::Flags::NewInterface)) {
396 ++newIntefaceNbrs;
397 }
398 }
399
400 // Return if no interface cell is available to distribute the excess mass, i.e., the mass is lost or gained.
401 const std::uint8_t intefaceNbrs = newIntefaceNbrs + oldIntefaceNbrs;
402 if (intefaceNbrs == std::uint8_t(0)) { return; }
403
404 // Distribution of excess mass among the neighbouring interface cells, weighted or evenly.
405 bool enableAllInterfaces = true;
406 constexpr bool weightedDistribution = true;
407 Vector<T, DESCRIPTOR::q> weights{};
408 T weightsSum = T(0);
409
410 // (1) Distribute excess mass among the neighbouring interface cells, weighted by normal vector.
411 if constexpr (weightedDistribution) {
412 // Compute the weights for the mass excess distribution using normal vector of the current interface cell.
413 auto normal = computeInterfaceNormal(cell);
414 computeMassExcessWeights(cell, normal, enableAllInterfaces, weights);
415
416 // Calculate sum of the computed weights
417 for (const auto& weight : weights) { weightsSum += weight; }
418
419 if (util::fabs(weightsSum) < zeroThreshold<T>()) {
420 // (a) If no old interface cell is available in normal direction,
421 // fall back to weighted all interface cells distribution model.
422 if (!enableAllInterfaces) {
423 enableAllInterfaces = true;
424 weightsSum = T(0);
425 computeMassExcessWeights(cell, normal, enableAllInterfaces, weights);
426 for (const auto& weight : weights) { weightsSum += weight; }
427
428 // (b) If no interface cell is available in normal direction,
429 // fall back to evenly all interface cells distribution model.
430 if (util::fabs(weightsSum) < zeroThreshold<T>()) {
431 for (auto& weight : weights) { weight = T(1); }
432 weightsSum = T(intefaceNbrs);
433 }
434 }
435 // (c) If all interface cells are selected but none is available in normal direction,
436 // fall back to evenly all interface cells distribution model.
437 else {
438 for (auto& weight : weights) { weight = T(1); }
439 weightsSum = T(intefaceNbrs);
440 }
441 }
442 }
443 // (2) Distribute excess mass among the neighbouring interface cells, evenly.
444 else {
445 // (a) Use old neighbouring interface cells to distribute excess mass.
446 if (!enableAllInterfaces && oldIntefaceNbrs > std::uint8_t(0)) {
447 for (auto& weight : weights) { weight = T(1); }
448 weightsSum = T(oldIntefaceNbrs);
449 }
450 // (b) Use all neighbouring interface cells to distribute excess mass.
451 else {
452 enableAllInterfaces = true;
453 for (auto& weight : weights) { weight = T(1); }
454 weightsSum = T(intefaceNbrs);
455 }
456 }
457
458 Vector<T, DESCRIPTOR::q> mass_exchange{};
459 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
460 auto nbrCell = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
461
462 if (isCellType(nbrCell, FreeSurface::Type::Interface) && hasCellFlags(nbrCell, FreeSurface::Flags::None)) {
463 mass_exchange[iPop] = mass_excess * weights[iPop] / weightsSum;
464 }
465 else if (hasCellFlags(nbrCell, FreeSurface::Flags::NewInterface) && enableAllInterfaces) {
466 mass_exchange[iPop] = mass_excess * weights[iPop] / weightsSum;
467 }
468 else {
469 mass_exchange[iPop] = T(0);
470 }
471 }
472
473 cell.template setField<FreeSurface::TEMP_MASS_EXCHANGE>(mass_exchange);
474}
475
479template<typename T, typename DESCRIPTOR>
480template<typename CELL>
482
483 using namespace olb::FreeSurface;
484
485 // Convert flagged cells to appropriate cell types
486 if (hasCellFlags(cell, FreeSurface::Flags::ToFluid)) {
487 setCellType(cell, FreeSurface::Type::Fluid);
488 cell.template setField<FreeSurface::EPSILON>(T(1));
489 }
490 else if (hasCellFlags(cell, FreeSurface::Flags::ToGas)) {
491 setCellType(cell, FreeSurface::Type::Gas);
492 cell.template setField<FreeSurface::EPSILON>(T(0));
493 }
494 else if (hasCellFlags(cell, FreeSurface::Flags::NewInterface)) {
495 setCellType(cell, FreeSurface::Type::Interface);
496 }
497 else {
498 // if (hasCellFlags(cell, FreeSurface::Flags::None) == true),
499 // no further action is needed, as no conversion is triggered.
500 }
501
502 // Mass excess is distributed to old and new interface cells only,
503 // thus skip the non-interface cells.
504 if (!isCellType(cell, FreeSurface::Type::Interface)) {
505 return;
506 }
507
508 // Collection of mass excess in a pulling step for the interface cells
509 T collected_excess = T(0);
510 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
511 auto nbrCell = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
512
513 if (hasCellFlags(nbrCell, FreeSurface::Flags::ToFluid | FreeSurface::Flags::ToGas)) {
514 auto tempMassExchange = nbrCell.template getField<FreeSurface::TEMP_MASS_EXCHANGE>();
515 collected_excess += tempMassExchange[descriptors::opposite<DESCRIPTOR>(iPop)];
516 }
517 }
518
519 T mass_tmp = cell.template getField<FreeSurface::MASS>();
520 mass_tmp += collected_excess;
521 T rho = T(0);
522 T u_tmp[DESCRIPTOR::d] = {T(0), T(0)};
523 cell.computeRhoU(rho, u_tmp);
524 Vector<T,DESCRIPTOR::d> u_vel{u_tmp[0], u_tmp[1]};
525 cell.template setField<FreeSurface::EPSILON>(mass_tmp / rho);
526 cell.template setField<FreeSurface::MASS>(mass_tmp);
527 cell.template setField<FreeSurface::PREVIOUS_VELOCITY>(u_vel);
528}
529
530// Setup
531template<typename T, typename DESCRIPTOR>
536
537template<typename T, typename DESCRIPTOR>
539 _sLattice.template addPostProcessor<FreeSurface::Stage0>(
541 _sLattice.template addPostProcessor<FreeSurface::Stage1>(
543 _sLattice.template addPostProcessor<FreeSurface::Stage2>(
545 _sLattice.template addPostProcessor<FreeSurface::Stage3>(
547 _sLattice.template addPostProcessor<FreeSurface::Stage4>(
549 _sLattice.template addPostProcessor<FreeSurface::Stage5>(
551
552 {
553 // Communicate fields: EPSILON, CELL_TYPE, POPULATION, and PREVIOUS_VELOCITY
554 auto& communicator = _sLattice.getCommunicator(FreeSurface::Stage0());
555 communicator.requestOverlap(_sLattice.getOverlap());
556 communicator.template requestField<FreeSurface::EPSILON>();
557 communicator.template requestField<FreeSurface::CELL_TYPE>();
558 communicator.template requestField<descriptors::POPULATION>();
559 communicator.template requestField<FreeSurface::PREVIOUS_VELOCITY>();
560 communicator.exchangeRequests();
561 }
562
563 {
564 // Communicate fields: MASS and PREVIOUS_VELOCITY
565 auto& communicator = _sLattice.getCommunicator(FreeSurface::Stage1());
566 communicator.requestOverlap(_sLattice.getOverlap());
567 communicator.template requestField<FreeSurface::MASS>();
568 communicator.template requestField<FreeSurface::PREVIOUS_VELOCITY>();
569 communicator.exchangeRequests();
570 }
571
572 {
573 // Communicate fields: CELL_FLAGS and POPULATION
574 auto& communicator = _sLattice.getCommunicator(FreeSurface::Stage2());
575 communicator.requestOverlap(_sLattice.getOverlap());
576 communicator.template requestField<FreeSurface::CELL_FLAGS>();
577 communicator.template requestField<descriptors::POPULATION>();
578 communicator.exchangeRequests();
579 }
580
581 {
582 // Communicate fields: CELL_FLAGS
583 auto& communicator = _sLattice.getCommunicator(FreeSurface::Stage3());
584 communicator.requestOverlap(_sLattice.getOverlap());
585 communicator.template requestField<FreeSurface::CELL_FLAGS>();
586 communicator.exchangeRequests();
587 }
588
589 {
590 // Communicate fields: CELL_FLAGS
591 auto& communicator = _sLattice.getCommunicator(FreeSurface::Stage4());
592 communicator.requestOverlap(_sLattice.getOverlap());
593 communicator.template requestField<FreeSurface::CELL_FLAGS>();
594 communicator.exchangeRequests();
595 }
596
597 {
598 // Communicate fields: TEMP_MASS_EXCHANGE
599 auto& communicator = _sLattice.getCommunicator(FreeSurface::Stage5());
600 communicator.requestOverlap(_sLattice.getOverlap());
601 communicator.template requestField<FreeSurface::TEMP_MASS_EXCHANGE>();
602 communicator.exchangeRequests();
603 }
604
605 // Add custom tasks to be executed post stream
606 _sLattice.template addCustomTask<stage::PostStream>([&]() {
607 _sLattice.executePostProcessors(FreeSurface::Stage0());
608 _sLattice.executePostProcessors(FreeSurface::Stage1());
609 _sLattice.executePostProcessors(FreeSurface::Stage2());
610 _sLattice.executePostProcessors(FreeSurface::Stage3());
611 _sLattice.executePostProcessors(FreeSurface::Stage4());
612 _sLattice.executePostProcessors(FreeSurface::Stage5());
613 });
614}
615
616}
617#endif
FreeSurface2DSetup(SuperLattice< T, DESCRIPTOR > &sLattice)
Free Surface Processor 7 Finishes up left over cell conversions and prepares the state for the next s...
void apply(CELL &cell) any_platform
Free Surface Processor 7: Finalize Conversion.
Free Surface Processor 2-3 Interface Reconstruction Replaces incoming DFs by calculating equilibrium ...
void apply(CELL &cell, PARAMETERS &params) any_platform
Free Surface Processor 2-3: Interface Reconstruction.
Free Surface Processor 6 Calculates mass excess from the cell type conversions and distributes them t...
void apply(CELL &cell) any_platform
Free Surface Processor 6: Mass Excess.
Free Surface Processor 1 Mass Flow Cleans up leftover flags from the previous simulation step.
void apply(CELL &cell) any_platform
Free Surface Processor 1: Mass Flow.
Free Surface Processor 5 ToGas Converts cells to interface from fluid if a neighbouring cell was conv...
void apply(CELL &cell) any_platform
Free Surface Processor 5: ToGas.
Super class maintaining block lattices for a cuboid decomposition.
Plain old scalar vector.
constexpr T t(unsigned iPop, tag::CUM) any_platform
Definition cum.h:108
constexpr int c(unsigned iPop, unsigned iDim) any_platform
Definition functions.h:83
constexpr int opposite(unsigned iPop) any_platform
Definition functions.h:95
auto normSqr(const ARRAY_LIKE &u) any_platform
Compute norm square of a d-dimensional vector.
Definition util.h:145
T dotProduct(const Vector< T, D > &a, const Vector< T, D > &b)
dot product
Expr fabs(Expr x)
Definition expr.cpp:230
Top level namespace for all of OpenLB.
static V secondOrder(int iPop, const RHO &rho, const U &u, const USQR &uSqr) any_platform
Computation of equilibrium distribution, second order in u.
Definition lbm.h:51
Identity type to pass non-constructible types as value.
Definition meta.h:79