OpenLB 1.7
Loading...
Searching...
No Matches
freeSurfacePostProcessor3D.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 * 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_SURFACE_POST_PROCESSOR_3D_HH
25#define FREE_SURFACE_POST_PROCESSOR_3D_HH
26
28#include "core/blockLattice.h"
29
30#include <cmath>
31#include <algorithm>
32
33namespace olb {
34
35// LocalProcessor 1
36
37// Read
38
39// range 0
40// Mass
41// CellType
42
43// range 1
44// DFs
45// Epsilon
46// CellType
47
48// Write (always range 0)
49// Mass
50// CellFlags
51// DFs (only replacing from incoming gas stream)
52
53template <typename T, typename DESCRIPTOR>
54template <typename CELL, typename PARAMETERS>
56
57 using namespace olb::FreeSurface;
58
59 const bool drop_isolated_cells = vars.template get<FreeSurface::DROP_ISOLATED_CELLS>();
60 const T transition = vars.template get<FreeSurface::TRANSITION>();
61 const T lonely_threshold = vars.template get<FreeSurface::LONELY_THRESHOLD>();
62 const bool has_surface_tension = vars.template get<FreeSurface::HAS_SURFACE_TENSION>();
63 const T surface_tension_parameter = vars.template get<FreeSurface::SURFACE_TENSION_PARAMETER>();
64 //const T force_conversion_factor = vars.template get<FreeSurface::FORCE_CONVERSION_FACTOR>();
65 //const T lattice_size = vars.template get<FreeSurface::LATTICE_SIZE>();
66
67 /*
68 * Minor "hack". Remove all cell flags here, because it is needed in the last processor due to pulling steps in processor 6 and 7
69 */
70 setCellFlags(cell, static_cast<FreeSurface::Flags>(0));
71
72 /*
73 * This processor only works on interface types
74 */
75 if (isCellType(cell, FreeSurface::Type::Interface )) {
76 T mass_tmp = cell.template getField<FreeSurface::MASS>();
77
78 FreeSurface::NeighbourInfo neighbour_info = getNeighbourInfo(cell);
79
80 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop){
81 auto cellC = cell.neighbor({descriptors::c<DESCRIPTOR>(iPop, 0),
82 descriptors::c<DESCRIPTOR>(iPop, 1),
83 descriptors::c<DESCRIPTOR>(iPop, 2)});
84 int iPop_op = descriptors::opposite<DESCRIPTOR>(iPop);
85
86 /*
87 * Iterate over neighbours and perform a mass exchange. Interface to fluid are simple cases.
88 * Interface to interface has to be symmetric and multiple cases are for artifact reduction
89 * from Thuerey
90 * Added a distinction between the amount of interface nodes. Weight consideration seems to cause artifacts.
91 */
92 if( isCellType(cellC, FreeSurface::Type::Fluid)) {
93 mass_tmp += cell[iPop_op] - cellC[iPop];
94 } else if ( isCellType(cellC, FreeSurface::Type::Interface)) {
95 FreeSurface::NeighbourInfo neighbour_neighbour_info = getNeighbourInfo(cellC);
96
97 T mass_flow = 0.;
98
99 if( !neighbour_info.has_fluid_neighbours){
100 if(!neighbour_neighbour_info.has_fluid_neighbours){
101 if(neighbour_info.interface_neighbours < neighbour_neighbour_info.interface_neighbours){
102 mass_flow = -cellC[iPop];// - descriptors::t<T,DESCRIPTOR>(iPop);
103 }else if(neighbour_info.interface_neighbours > neighbour_neighbour_info.interface_neighbours){
104 mass_flow = cell[iPop_op];// + descriptors::t<T,DESCRIPTOR>(iPop_op);
105 }else{
106 mass_flow = cell[iPop_op] - cellC[iPop];
107 }
108 }else {
109 mass_flow = -cellC[iPop];// - descriptors::t<T,DESCRIPTOR>(iPop);
110 }
111 }else if(!neighbour_info.has_gas_neighbours){
112 if(!neighbour_neighbour_info.has_gas_neighbours){
113 if(neighbour_info.interface_neighbours < neighbour_neighbour_info.interface_neighbours){
114 mass_flow = cell[iPop_op];// + descriptors::t<T,DESCRIPTOR>(iPop_op);
115 }else if(neighbour_info.interface_neighbours > neighbour_neighbour_info.interface_neighbours){
116 mass_flow = -cellC[iPop];// - descriptors::t<T,DESCRIPTOR>(iPop);
117 }else{
118 mass_flow = cell[iPop_op] - cellC[iPop];
119 }
120 }else {
121 mass_flow = cell[iPop_op];// + descriptors::t<T,DESCRIPTOR>(iPop_op);
122 }
123 }else {
124 if(!neighbour_neighbour_info.has_fluid_neighbours){
125 mass_flow = cell[iPop_op];// + descriptors::t<T,DESCRIPTOR>(iPop_op);
126 }else if(!neighbour_neighbour_info.has_gas_neighbours){
127 mass_flow = -cellC[iPop];// - descriptors::t<T,DESCRIPTOR>(iPop);
128 }else {
129 mass_flow = cell[iPop_op] - cellC[iPop];
130 }
131 }
132
133 /*
134 * Exchange depends on how filled the interfaces are
135 */
136 mass_tmp += mass_flow * 0.5 * (getClampedEpsilon(cell) + getClampedEpsilon(cellC));
137 }
138 }
139
140 cell.template setField<FreeSurface::MASS>(mass_tmp);
141
142 // Former 2 Step
143
144 // Because I need the distribution functions of the last step I will write results in a temporary
145 // array, before copying it back into the DFs
146
148
149 T curvature = 0.;
150
151 if(has_surface_tension){
152 FreeSurface::NeighbourInfo info = getNeighbourInfo(cell);
153 if(info.has_gas_neighbours){
154 curvature = calculateSurfaceTensionCurvature(cell);
155 }
156
157 }
158
159 // Gas pressure adjusting
160 T gas_pressure = 1. - 6. * surface_tension_parameter * curvature;
161
162 for(int iPop=1; iPop < DESCRIPTOR::q; iPop++) {
163 auto cellC = cell.neighbor({descriptors::c<DESCRIPTOR>(iPop, 0),
164 descriptors::c<DESCRIPTOR>(iPop, 1),
165 descriptors::c<DESCRIPTOR>(iPop, 2)});
166 int iPop_op = descriptors::opposite<DESCRIPTOR>(iPop);
167
168 /*
169 * Gas replacement
170 */
171 if ( isCellType(cellC, FreeSurface::Type::Gas )) {
172 Vector<T, DESCRIPTOR::d> u_vel = cell.template getField<FreeSurface::PREVIOUS_VELOCITY>();
173 T u[DESCRIPTOR::d];
174 for(size_t u_i = 0; u_i < DESCRIPTOR::d; ++u_i){
175 u[u_i] = u_vel[u_i];
176 }
177 //T uSqr = util::normSqr<T,DESCRIPTOR::d>(u);
178 dfs[iPop_op] = equilibrium<DESCRIPTOR>::secondOrder(iPop, gas_pressure, u)
179 + equilibrium<DESCRIPTOR>::secondOrder(iPop_op, gas_pressure, u)
180 - cellC[iPop];
181 }else {
182 dfs[iPop_op] = cell[iPop_op];
183 }
184 }
185
186 for(int iPop=1; iPop<DESCRIPTOR::q; iPop++) {
187 cell[iPop] = dfs[iPop];
188 }
189
190 // Former 3 Step
191 /*
192 * Based on the mass calculation, flag this interface cell as toFluid or toGas if set boundaries are met
193 */
194 T rho = cell.computeRho();
195
196 // Check if transition needs to happen.
197 if ( mass_tmp < -transition * rho || (mass_tmp < lonely_threshold * rho && !neighbour_info.has_fluid_neighbours) ){
198 setCellFlags(cell, FreeSurface::Flags::ToGas);
199 }
200 else if ( mass_tmp > (1. + transition)*rho || ( mass_tmp > (1-lonely_threshold) * rho && !neighbour_info.has_gas_neighbours) ){
201 setCellFlags(cell, FreeSurface::Flags::ToFluid);
202 }else if(drop_isolated_cells && (neighbour_info.interface_neighbours == 0)){
203 if(!neighbour_info.has_gas_neighbours){
204 setCellFlags(cell, FreeSurface::Flags::ToFluid);
205 }else if(!neighbour_info.has_fluid_neighbours){
206 //setCellFlags(cell, FreeSurface::Flags::ToGas);
207 }
208 }
209 }
210}
211
212// Processor 4
213
214// Read
215// range 0
216// CellType
217// CellFlags
218
219// range 1
220// CellType
221// CellFlags
222// DFs
223
224// Write (always range 0)
225// CellFlags
226// DFs
227template <typename T, typename DESCRIPTOR>
228template <typename CELL>
230 using namespace olb::FreeSurface;
231 /*
232 * Initializing new interface cells with DFs from surrounding fluid and interface cells
233 * Just takes the arithmetic average.
234 */
235 if(isCellType(cell, FreeSurface::Type::Gas)){
236 if(hasNeighbourFlags(cell, FreeSurface::Flags::ToFluid)){
237 setCellFlags(cell, FreeSurface::Flags::NewInterface);
238 T rho_avg = 0.;
239 T u_avg[DESCRIPTOR::d] = {0., 0.};
240
241 std::size_t ctr = 0;
242
243 for(int iPop=1; iPop<DESCRIPTOR::q; iPop++) {
244 auto cellC = cell.neighbor({descriptors::c<DESCRIPTOR>(iPop,0),
245 descriptors::c<DESCRIPTOR>(iPop,1),
246 descriptors::c<DESCRIPTOR>(iPop,2)});
247
248 if (isCellType(cellC, FreeSurface::Type::Fluid) || isCellType(cellC, FreeSurface::Type::Interface)){
249 T rho_tmp = 0.;
250 T u_tmp[DESCRIPTOR::d] = {0., 0.};
251 ++ctr;
252 cellC.computeRhoU(rho_tmp, u_tmp);
253 rho_avg += rho_tmp;
254 for(size_t i = 0; i < DESCRIPTOR::d; ++i){
255 u_avg[i] += u_tmp[i];
256 }
257 }
258 }
259
260 if(ctr > 0){
261 rho_avg /= static_cast<T>(ctr);
262 for(size_t i = 0; i < DESCRIPTOR::d; ++i){
263 u_avg[i] /= static_cast<T>(ctr);
264 }
265 }
266
267 cell.iniEquilibrium(rho_avg, u_avg);
268 }
269 }else if(hasCellFlags(cell, FreeSurface::Flags::ToGas)){
270 /*
271 * If a toGas cell has a neighbouring toFluid cell, unset the toGas flag
272 */
273 if(hasNeighbourFlags(cell, FreeSurface::Flags::ToFluid)){
274 setCellFlags(cell, static_cast<FreeSurface::Flags>(0));
275 }
276 }
277}
278
279// LocalProcessor 5
280
281// Read
282// range 0
283// CellType
284// DFs
285
286// range 1
287// CellFlags
288
289// Write (always range 0)
290// CellFlags
291
292template <typename T, typename DESCRIPTOR>
293template <typename CELL>
295 using namespace olb::FreeSurface;
296 /*
297 * For the to be converted toGas cells, set the neighbours to interface cells
298 */
299 if ( isCellType(cell, FreeSurface::Type::Fluid)
300 && hasNeighbourFlags(cell, FreeSurface::Flags::ToGas)){
301 setCellFlags(cell, FreeSurface::Flags::NewInterface);
302 T rho = cell.computeRho();
303 cell.template setField<FreeSurface::MASS>(rho);
304 }
305}
306
307// LocalProcessor 6
308
309// Read
310// range 0
311// CellType
312// CellFlags
313// DFs
314// Mass
315
316// range 1
317// Epsilon
318// CellType
319// CellFlags
320
321// Write (always range 0)
322// Mass
323// TempMassExchange
324template <typename T, typename DESCRIPTOR>
325template <typename CELL>
327 using namespace olb::FreeSurface;
328 if( !isCellType(cell, FreeSurface::Type::Interface) ){
329 return;
330 }
331
332 T rho = cell.computeRho();
333 T mass = cell.template getField<FreeSurface::MASS>( );
334 T mass_excess = 0.;
335
336 auto normal = computeParkerYoungInterfaceNormal(cell);
337 // redistribute excess mass
338
342 if(hasCellFlags(cell, FreeSurface::Flags::ToGas)){
343 mass_excess = mass;
344 cell.template setField<FreeSurface::MASS>( 0. );
345 normal *= -1;
346 } else if (hasCellFlags(cell, FreeSurface::Flags::ToFluid)) {
347 mass_excess = mass - rho;
348 cell.template setField<FreeSurface::MASS>( rho );
349 } else {
350 return;
351 }
352
353 std::array<T,DESCRIPTOR::q> products;
354 products[0] = 0.;
355 T product_sum = 0.;
356 std::size_t product_total = 0;
357
358 for(int iPop=1; iPop<DESCRIPTOR::q; iPop++) {
359 auto cellC = cell.neighbor({descriptors::c<DESCRIPTOR>(iPop,0),
360 descriptors::c<DESCRIPTOR>(iPop,1),
361 descriptors::c<DESCRIPTOR>(iPop,2)});
362 products[iPop] = 0.;
363
364 // Thuerey Paper says we can't use new interface cells
365 // or flagged cells
366 // But surface tension showed us that it has anisotropic effects
367 if( (isCellType(cellC, FreeSurface::Type::Interface) && (!hasCellFlags(cellC,
368 static_cast<FreeSurface::Flags>(255)) /*|| hasCellFlags(cellC, FreeSurface::Flags::ToFluid)*/ ))
369 /*|| isCellType(cellC, FreeSurface::Type::Fluid)*/
370 ){
371 products[iPop] = (normal[0] * descriptors::c<DESCRIPTOR>(iPop, 0) + normal[1] * descriptors::c<DESCRIPTOR>(iPop,1) + normal[2] * descriptors::c<DESCRIPTOR>(iPop,2));
372 if(products[iPop] <= 0){
373 products[iPop] = 0.;
374 }
375 ++product_total;
376 product_sum += products[iPop];
377 }
378 }
379
380 /* Prepare Mass excess push */
381 Vector<T,DESCRIPTOR::q> mass_excess_vector{};
382 mass_excess_vector[0] = 0.;
383 /*
384 if(product_sum > 0){
385 T fraction = 1./ product_sum;
386
387 for(int iPop = 1; iPop < DESCRIPTOR::q; ++iPop){
388 mass_excess_vector[iPop] = mass_excess * products[iPop] * fraction;
389 }
390 cell.template setField<FreeSurface::TEMP_MASS_EXCHANGE>( mass_excess_vector );
391 }
392 else*/
393 if (product_total > 0) {
394 T product_fraction = 1. / product_total;
395 for(int iPop=1; iPop < DESCRIPTOR::q; iPop++) {
396 auto cellC = cell.neighbor({descriptors::c<DESCRIPTOR>(iPop,0),
397 descriptors::c<DESCRIPTOR>(iPop,1),
398 descriptors::c<DESCRIPTOR>(iPop,2)});
399 if( (isCellType(cellC, FreeSurface::Type::Interface) && (!hasCellFlags(cellC,
400 static_cast<FreeSurface::Flags>(255)) /*|| hasCellFlags(cellC, FreeSurface::Flags::ToFluid)*/ ))
401 /*|| isCellType(cellC, FreeSurface::Type::Fluid)*/
402 ){
403 mass_excess_vector[iPop] = mass_excess * product_fraction;
404 } else {
405 mass_excess_vector[iPop] = 0.;
406 }
407 }
408 cell.template setField<FreeSurface::TEMP_MASS_EXCHANGE>( mass_excess_vector );
409 } else {
410 mass_excess_vector[0] = mass_excess;
411 for(int iPop=1; iPop < DESCRIPTOR::q; iPop++) {
412 mass_excess_vector[iPop] = 0.;
413 }
414 cell.template setField<FreeSurface::TEMP_MASS_EXCHANGE>( mass_excess_vector );
415 }
416}
417
418
419// LocalProcessor 7
420
421// Read
422// range 0
423// CellFlags
424// CellType
425// DFs
426// Mass
427// Epsilon
428
429// range 1
430// CellFlags
431// TempMassExchange
432
433// Write (always range 0)
434// Epsilon
435// CellType
436// Mass
437
438template <typename T, typename DESCRIPTOR>
439template <typename CELL>
441 using namespace olb::FreeSurface;
442 /* Convert flagged cells to appropriate cell types */
443 FreeSurface::Flags flags = static_cast<FreeSurface::Flags>(cell.template getField<FreeSurface::CELL_FLAGS>());
444
445 switch(flags){
447 {
449 setCellType(cell, FreeSurface::Type::Fluid);
450 cell.template setField<FreeSurface::EPSILON>( 1. );
451 T mass_tmp = cell.template getField<FreeSurface::MASS>();
452 mass_tmp += cell.template getField<FreeSurface::TEMP_MASS_EXCHANGE>()[0];
453 cell.template setField<FreeSurface::MASS>(mass_tmp);
454
455 }
456 break;
458 {
460 setCellType(cell, FreeSurface::Type::Gas);
461 cell.template setField<FreeSurface::EPSILON>( 0. );
462 T mass_tmp = cell.template getField<FreeSurface::MASS>();
463 mass_tmp += cell.template getField<FreeSurface::TEMP_MASS_EXCHANGE>()[0];
464 cell.template setField<FreeSurface::MASS>(mass_tmp);
465
466 }
467 break;
469 {
470 setCellType(cell, FreeSurface::Type::Interface);
471 }
472 break;
473 default:
474 break;
475 }
476
477 FreeSurface::Type type = static_cast<FreeSurface::Type>(cell.template getField<FreeSurface::CELL_TYPE>());
478
479 /* Collection of mass excess in a pulling step */
480 switch(type){
482 {
483 T collected_excess = 0.;
484 for(int iPop = 1; iPop < DESCRIPTOR::q; ++iPop){
485 auto cellC = cell.neighbor({descriptors::c<DESCRIPTOR>(iPop,0),
486 descriptors::c<DESCRIPTOR>(iPop,1),
487 descriptors::c<DESCRIPTOR>(iPop,2)});
488 auto tempMassExchange = cellC.template getFieldPointer<FreeSurface::TEMP_MASS_EXCHANGE>();
489
490 if (hasCellFlags(cellC,
493 int iPop_op = descriptors::opposite<DESCRIPTOR>(iPop);
494 collected_excess += tempMassExchange[iPop_op];
495 }
496 }
497
498 T mass_tmp = cell.template getField<FreeSurface::MASS>();
499
500 mass_tmp += collected_excess;
501 T rho;
502 T u_tmp[DESCRIPTOR::d];
503 cell.computeRhoU(rho, u_tmp);
504
505 Vector<T,DESCRIPTOR::d> u_vel{u_tmp[0], u_tmp[1], u_tmp[2]};
506
507 cell.template setField<FreeSurface::EPSILON>( mass_tmp / rho );
508 cell.template setField<FreeSurface::MASS>(mass_tmp);
509 cell.template setField<FreeSurface::PREVIOUS_VELOCITY>(u_vel);
510 }
511 break;
513 {
514 T collected_excess = 0.;
515
516 for(int iPop = 1; iPop < DESCRIPTOR::q; ++iPop){
517 auto cellC = cell.neighbor({descriptors::c<DESCRIPTOR>(iPop,0),
518 descriptors::c<DESCRIPTOR>(iPop,1),
519 descriptors::c<DESCRIPTOR>(iPop,2)});
520 auto tempMassExchange = cellC.template getFieldPointer<FreeSurface::TEMP_MASS_EXCHANGE>();
521 if (hasCellFlags(cellC,
524 int iPop_op = descriptors::opposite<DESCRIPTOR>(iPop);
525 collected_excess += tempMassExchange[iPop_op];
526 }
527 }
528
529 T mass_tmp = cell.template getField<FreeSurface::MASS>();
530 mass_tmp += collected_excess;
531 cell.template setField<FreeSurface::MASS>(mass_tmp);
532 }
533 break;
534 default:
535 break;
536 }
537}
538
539// Setup
540template<typename T, typename DESCRIPTOR>
545
546template<typename T, typename DESCRIPTOR>
548 sLattice.template addPostProcessor<FreeSurface::Stage0>(
550 sLattice.template addPostProcessor<FreeSurface::Stage1>(
552 sLattice.template addPostProcessor<FreeSurface::Stage2>(
554 sLattice.template addPostProcessor<FreeSurface::Stage3>(
556 sLattice.template addPostProcessor<FreeSurface::Stage4>(
558
559 {
560 // Communicate DFs, Epsilon and Cell Types
561 auto& communicator = sLattice.getCommunicator(FreeSurface::Stage0());
562 communicator.requestOverlap(2);
563 communicator.template requestField<FreeSurface::EPSILON>();
564 communicator.template requestField<FreeSurface::CELL_TYPE>();
565 communicator.template requestField<descriptors::POPULATION>();
566 communicator.exchangeRequests();
567 }
568
569 {
570 // Communicate DFs, Cell Flags
571 auto& communicator = sLattice.getCommunicator(FreeSurface::Stage1());
572 communicator.requestOverlap(2);
573 communicator.template requestField<FreeSurface::CELL_FLAGS>();
574 communicator.template requestField<descriptors::POPULATION>();
575 communicator.exchangeRequests();
576 }
577
578 {
579 // Communicate Cell Flags
580 auto& communicator = sLattice.getCommunicator(FreeSurface::Stage2());
581 communicator.requestOverlap(2);
582 communicator.template requestField<FreeSurface::CELL_FLAGS>();
583 communicator.exchangeRequests();
584 }
585
586 {
587 // Communicate Cell Flags
588 auto& communicator = sLattice.getCommunicator(FreeSurface::Stage3());
589 communicator.requestOverlap(2);
590 communicator.template requestField<FreeSurface::CELL_FLAGS>();
591 communicator.exchangeRequests();
592 }
593
594 {
595 // Communicate TempMassExchange
596 auto& communicator = sLattice.getCommunicator(FreeSurface::Stage4());
597 communicator.requestOverlap(2);
598 communicator.template requestField<FreeSurface::TEMP_MASS_EXCHANGE>();
599 communicator.exchangeRequests();
600 }
601
602 sLattice.template addCustomTask<stage::PostStream>([&]() {
603 sLattice.executePostProcessors(FreeSurface::Stage0());
604 sLattice.executePostProcessors(FreeSurface::Stage1());
605 sLattice.executePostProcessors(FreeSurface::Stage2());
606 sLattice.executePostProcessors(FreeSurface::Stage3());
607 sLattice.executePostProcessors(FreeSurface::Stage4());
608 });
609}
610}
611#endif
FreeSurface3DSetup(SuperLattice< T, DESCRIPTOR > &sLattice)
Free Surface Processor 7 Finishes up left over cell conversions and prepares the state for the next s...
Free Surface Processor 6 Calculates mass excess from the cell type conversions and distributes them t...
Free Surface Processor 1-3 Mass Flow Cleans up leftover flags from the previous simulation step.
void apply(CELL &cell, PARAMETERS &parameters) any_platform
Free Surface Processor 5 ToGas Converts cells to interface from fluid if a neighbouring cell was conv...
Super class maintaining block lattices for a cuboid decomposition.
SuperCommunicator< T, SuperLattice > & getCommunicator(STAGE stage=STAGE())
Return communicator for given communication stage.
Plain old scalar vector.
Definition vector.h:47
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