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