OpenLB 1.8.1
Loading...
Searching...
No Matches
setBouzidiBoundary.h
Go to the documentation of this file.
1/* This file is part of the OpenLB library
2 *
3 * Copyright (C) 2022 Michael Crocoll, Adrian Kummerlaender, Shota Ito, Anas Selmi
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 SET_BOUZIDI_BOUNDARY_H
25#define SET_BOUZIDI_BOUNDARY_H
26
27#include "bouzidiFields.h"
30#include "setBoundary2D.h"
31
32// M. Bouzidi, M. Firdaouss, and P. Lallemand.
33// Momentum transfer of a Boltzmann-lattice fluid with boundaries.
34// Physics of Fluids 13, 3452 (2001).
35// DOI: 10.1063/1.1399290
36
37// D. Yu, R. Mei, and W. Shyy.
38// A Unified Boundary Treatment in Lattice Boltzmann Method.
39// Session: FD-27: CFD Methodology III (2012)
40// DOI: 10.2514/6.2003-953
41
44
45namespace olb {
46
49public:
51
52 int getPriority() const {
53 return -1;
54 }
55
56 template <typename CELL, typename V = typename CELL::value_t>
57 void apply(CELL& x_b) any_platform {
58 using DESCRIPTOR = typename CELL::descriptor_t;
59 const auto q = x_b.template getFieldPointer<descriptors::BOUZIDI_DISTANCE>();
60 for (int iPop = 1; iPop < descriptors::q<DESCRIPTOR>(); ++iPop) {
61 // update missing population if valid bouzidi distance
62 if (q[iPop] > V{0}) {
63 const auto c = descriptors::c<DESCRIPTOR>(iPop);
64 const int iPop_opposite = descriptors::opposite<DESCRIPTOR>(iPop);
65 auto x_s = x_b.neighbor(c); // solid side neighbor
66 auto x_f = x_b.neighbor(descriptors::c<DESCRIPTOR>(iPop_opposite)); // fluid side neighbor opposite to the missing population
67
68 x_b[iPop_opposite] = (q[iPop] <= V{0.5}) // cut is closer to the fluid cell
69 * (V{2} * q[iPop] * x_s[iPop] + (V{1} - V{2} * q[iPop]) * x_b[iPop])
70 + (q[iPop] > V{0.5}) // cut is closer to the solid cell
71 * (V{0.5} / q[iPop] * x_s[iPop] + V{0.5} * (V{2} * q[iPop] - V{1}) / q[iPop] * x_f[iPop_opposite]);
72 }
73 // if intersection point is on the cell then fall back to full-way bounce back
74 else if (q[iPop] == V{0}) {
75 const int iPop_opposite = descriptors::opposite<DESCRIPTOR>(iPop);
76 x_b[iPop_opposite] = x_b[iPop];
77 }
78 }
79 }
80};
81
83public:
85
86 int getPriority() const {
87 return -1;
88 }
89
90 template <typename CELL, typename V = typename CELL::value_t>
91 void apply(CELL& x_b) any_platform {
92 using DESCRIPTOR = typename CELL::descriptor_t;
93 const auto q = x_b.template getFieldPointer<descriptors::BOUZIDI_DISTANCE>();
94 const auto phi_d = x_b.template getFieldPointer<descriptors::BOUZIDI_ADE_DIRICHLET>();
95 V f = V{1/3}; // D2Q5
96 if (descriptors::q<DESCRIPTOR>() == 7) {
97 f = V{0.25}; // D3Q7
98 }
99 for (int iPop = 1; iPop < descriptors::q<DESCRIPTOR>(); ++iPop) {
100 // update missing population if valid bouzidi distance
101 if (q[iPop] > V{0}) {
102 const auto c = descriptors::c<DESCRIPTOR>(iPop);
103 const int iPop_opposite = descriptors::opposite<DESCRIPTOR>(iPop);
104 auto x_s = x_b.neighbor(c); // solid side neighbor
105 auto x_f = x_b.neighbor(descriptors::c<DESCRIPTOR>(iPop_opposite)); // fluid side neighbor opposite to the missing population
106 auto source_d = phi_d[iPop] * f; // source term set by the dirichlet condition
107 auto t_i = descriptors::t<V,DESCRIPTOR>(iPop);
108 auto t_iopp = descriptors::t<V,DESCRIPTOR>(iPop_opposite);
109
110 x_b[iPop_opposite] = (q[iPop] <= V{0.5}) // cut is closer to the fluid cell
111 * (V{-2} * q[iPop] * (x_s[iPop] + t_i) + (V{2} * q[iPop] - V{1}) * (x_b[iPop] + t_i) + source_d)
112 + (q[iPop] > V{0.5}) // cut is closer to the solid cell
113 * (V{-1} / (V{2} * q[iPop]) * (x_s[iPop] + t_i) + (V{1} - V{1} / (V{2} * q[iPop])) * (x_f[iPop_opposite] + t_iopp) + (V{1} / (V{2} * q[iPop])) * source_d)
114 - t_iopp;
115 }
116 // if intersection point is on the cell then fall back to full-way bounce back
117 else if (q[iPop] == V{0}) {
118 const int iPop_opposite = descriptors::opposite<DESCRIPTOR>(iPop);
119 auto source_d = phi_d[iPop] * f;
120 auto t_i = descriptors::t<V,DESCRIPTOR>(iPop);
121 auto t_iopp = descriptors::t<V,DESCRIPTOR>(iPop_opposite);
122 x_b[iPop_opposite] = -(x_b[iPop] + t_i) + source_d - t_iopp;
123 }
124 }
125 }
126};
127
130public:
132
133 int getPriority() const {
134 return -1;
135 }
136
137 template <typename CELL, typename V = typename CELL::value_t>
138 void apply(CELL& x_b) any_platform {
139 using DESCRIPTOR = typename CELL::descriptor_t;
140 const auto q = x_b.template getFieldPointer<descriptors::BOUZIDI_DISTANCE>();
141 const auto veloCoeff = x_b.template getFieldPointer<descriptors::BOUZIDI_VELOCITY>();
142 for (int iPop=1; iPop < descriptors::q<DESCRIPTOR>(); ++iPop) {
143 // update missing population if valid bouzidi distance
144 if (q[iPop] > 0) {
145 const auto c = descriptors::c<DESCRIPTOR>(iPop);
146 const int iPop_opposite = descriptors::opposite<DESCRIPTOR>(iPop);
147 auto x_s = x_b.neighbor(c); // solid side neighbor
148 auto x_f = x_b.neighbor(descriptors::c<DESCRIPTOR>(iPop_opposite)); // fluid side neighbor opposite to the missing population
149 auto veloTerm = veloCoeff[iPop] * (descriptors::t<V,DESCRIPTOR>(iPop)) * (descriptors::invCs2<V,DESCRIPTOR>());
150
151 x_b[iPop_opposite] = (q[iPop] <= V{0.5}) // cut is closer to the fluid cell
152 * (V{2} * q[iPop] * x_s[iPop] + (V{1} - V{2} * q[iPop]) * x_b[iPop] - V{2} * veloTerm)
153 + (q[iPop] > V{0.5}) // cut is closer to the solid cell
154 * (V{0.5} / q[iPop] * x_s[iPop] + V{0.5} * (V{2} * q[iPop] - V{1}) / q[iPop] * x_f[iPop_opposite] - V{1}/q[iPop] * veloTerm);
155 }
156 // if intersection point is on the cell then fall back to full-way bounce back
157 else if (q[iPop] == V{0}) {
158 const int iPop_opposite = descriptors::opposite<DESCRIPTOR>(iPop);
159 auto veloTerm = veloCoeff[iPop] * (descriptors::t<V,DESCRIPTOR>(iPop)) * (descriptors::invCs2<V,DESCRIPTOR>());
160 x_b[iPop_opposite] = x_b[iPop] - V{2} * veloTerm;
161 }
162 }
163 }
164};
165
166// Post processor for the zero-velocity Yu IBB scheme
168public:
170
171 int getPriority() const {
172 return -1;
173 }
174
175 template <typename CELL, typename V = typename CELL::value_t>
176 void apply(CELL& x_b) any_platform {
177 using DESCRIPTOR = typename CELL::descriptor_t;
178 const auto q = x_b.template getFieldPointer<descriptors::BOUZIDI_DISTANCE>();
179 for (int iPop=1; iPop < descriptors::q<DESCRIPTOR>(); ++iPop) {
180 if (q[iPop] >= 0) {
181 const auto c = descriptors::c<DESCRIPTOR>(iPop);
182 const int iPop_opposite = descriptors::opposite<DESCRIPTOR>(iPop);
183 auto x_s = x_b.neighbor(c); // solid cell inside obstacle material
184 auto x_f = x_b.neighbor(descriptors::c<DESCRIPTOR>(iPop_opposite)); // fluid boundary cell
185 auto f_tmp = x_b[iPop] + q[iPop]*(x_s[iPop] - x_b[iPop]); // population at fictitious ghost particle
186 x_b[iPop_opposite] = f_tmp + q[iPop]/(V{1}+q[iPop]) * (x_f[iPop_opposite] - f_tmp);
187 }
188 }
189 }
190};
191
193template<typename T, typename DESCRIPTOR, typename OPERATOR = BouzidiPostProcessor>
195 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>&& boundaryIndicator,
197 IndicatorF<T,DESCRIPTOR::d>& indicatorAnalyticalBoundary)
198{
199 int _overlap = 1;
200 OstreamManager clout(std::cout, "BouzidiBoundarySetter");
201 auto& load = sLattice.getLoadBalancer();
202 for (int iC=0; iC < load.size(); ++iC) {
204 (bulkIndicator->getBlockIndicatorF(iC)).getBlockGeometry(),
205 boundaryIndicator->getBlockIndicatorF(iC),
206 bulkIndicator->getBlockIndicatorF(iC),
207 indicatorAnalyticalBoundary);
208 }
209 addPoints2CommBC<T,DESCRIPTOR>(sLattice, std::forward<decltype(boundaryIndicator)>(boundaryIndicator), _overlap);
210 clout.setMultiOutput(false);
211}
212
214template<typename T, typename DESCRIPTOR, typename OPERATOR = BouzidiPostProcessor>
216 SuperGeometry<T,DESCRIPTOR::d>& superGeometry,
217 int materialOfSolidObstacle,
218 IndicatorF<T,DESCRIPTOR::d>& indicatorAnalyticalBoundary,
219 std::vector<int> bulkMaterials = std::vector<int>(1,1))
220{
221 //Getting the indicators by material numbers and calling the superLattice method via the indicators:
223 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>(superGeometry.getMaterialIndicator(materialOfSolidObstacle)),
224 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>(superGeometry.getMaterialIndicator(std::move(bulkMaterials))),
225 indicatorAnalyticalBoundary);
226}
227
228
230template<typename T, typename DESCRIPTOR, typename OPERATOR = BouzidiPostProcessor>
232 BlockGeometry<T,DESCRIPTOR::d>& blockGeometry,
233 BlockIndicatorF<T,DESCRIPTOR::d>& boundaryIndicator,
235 IndicatorF<T,DESCRIPTOR::d>& indicatorAnalyticalBoundary,
236 bool verbose = false)
237{
238 OstreamManager clout(std::cout, "BouzidiBoundarySetter");
239 clout.setMultiOutput(true);
240
241 const T deltaR = blockGeometry.getDeltaR();
242 // for each solid cell: all of its fluid neighbors need population updates
243 block.forSpatialLocations([&](LatticeR<DESCRIPTOR::d> solidLatticeR) {
244 // Check if cell is solid cell
245 if (boundaryIndicator(solidLatticeR)) {
246 for (int iPop=1; iPop < DESCRIPTOR::q; ++iPop) {
247
248 Vector<T,DESCRIPTOR::d> boundaryLatticeR(solidLatticeR + descriptors::c<DESCRIPTOR>(iPop));
249 const auto c = descriptors::c<DESCRIPTOR>(iPop);
250 const auto iPop_opposite = descriptors::opposite<DESCRIPTOR>(iPop);
251
252 if (blockGeometry.isInside(boundaryLatticeR)) {
253 auto boundaryPhysR = blockGeometry.getPhysR(boundaryLatticeR);
254
255 // check if neighbor is fluid cell
256 if (bulkIndicator(boundaryLatticeR)) {
257 T dist = -1; // distance to boundary
258 T qIpop = -1; // normed distance (Bouzidi distance) to boundary
260 auto direction = -deltaR * c; // vector pointing from the boundary cell to the solid cell
261
262 // Check if distance calculation was performed correctly
263 if (indicatorAnalyticalBoundary.distance(dist, boundaryPhysR.data(), direction, blockGeometry.getIcGlob())) {
264 qIpop = dist / norm;
265
266 // if distance function returned a dist. not suitable for Bouzidi -> fall-back
267 if ((qIpop < 0) || (qIpop > 1)) {
268 if(verbose) {
269 clout << "Error, non suitable dist. at lattice: (" << boundaryLatticeR << "), physical: (" << blockGeometry.getPhysR(boundaryLatticeR) << "), direction " << iPop << ". Fall-back to bounce-back." << std::endl;
270 }
271
272 // fall-back: half-way bounce back
273 qIpop = 0.5;
274 }
275 }
276 // if distance function couldn't compute any distance -> fall-back
277 else {
278 if(verbose) {
279 clout << "Error, no boundary found at lattice:(" << boundaryLatticeR << "), physical: (" << blockGeometry.getPhysR(boundaryLatticeR) << "), direction: " << iPop << ".Fall-back to bounce-back." << std::endl;
280 }
281
282 // fall-back: half-way bounce back
283 qIpop = 0.5;
284 }
285
286 // double check
287 if (qIpop >= 0) {
288 // Bouzidi require the fluid side neighbor of the boundary cell also to be fluid
289 if (bulkIndicator(boundaryLatticeR + descriptors::c<DESCRIPTOR>(iPop))) {
290 // Standard case, c.f. Bouzidi paper, setting Bouzidi-distance
291 block.get(boundaryLatticeR).template setFieldComponent<descriptors::BOUZIDI_DISTANCE>(iPop_opposite, qIpop);
292 }
293 else {
294 // If no fluid cell found: fall-back to bounce-back
295 block.get(boundaryLatticeR).template setFieldComponent<descriptors::BOUZIDI_DISTANCE>(iPop_opposite, T{0.5});
296 }
297 // Initialize velocity coefficients if necessary
298 if constexpr (std::is_same_v<OPERATOR, BouzidiVelocityPostProcessor>) {
299 block.get(boundaryLatticeR).template setFieldComponent<descriptors::BOUZIDI_VELOCITY>(iPop_opposite, 0);
300 }
301 // Initialize ade dirichlet coefficients if necessary
302 if constexpr (std::is_same_v<OPERATOR, BouzidiAdeDirichletPostProcessor>) {
303 block.get(boundaryLatticeR).template setFieldComponent<descriptors::BOUZIDI_ADE_DIRICHLET>(iPop_opposite, 0);
304 }
305 // Setting up the post processor, if this cell does not have one yet.
306 if (!block.isPadding(boundaryLatticeR)) {
307 block.addPostProcessor(typeid(stage::PostStream),
308 boundaryLatticeR,
310 }
311 }
312 }
313 // if neigbour cell is not fluid
314 else {
315 // check if neighbor cell is not solid
316 if (blockGeometry.getMaterial(boundaryLatticeR) != 0) {
317 // fall-back to half-way bounce-back
318 block.get(boundaryLatticeR).template setFieldComponent<descriptors::BOUZIDI_DISTANCE>(iPop_opposite, T{0.5});
319 // Initialize velocity coefficients if necessary
320 if constexpr (std::is_same_v<OPERATOR, BouzidiVelocityPostProcessor>) {
321 block.get(boundaryLatticeR).template setFieldComponent<descriptors::BOUZIDI_VELOCITY>(iPop_opposite, 0);
322 }
323 // Initialize velocity coefficients if necessary
324 if constexpr (std::is_same_v<OPERATOR, BouzidiAdeDirichletPostProcessor>) {
325 block.get(boundaryLatticeR).template setFieldComponent<descriptors::BOUZIDI_ADE_DIRICHLET>(iPop_opposite, 0);
326 }
327 if (!block.isPadding(boundaryLatticeR)) {
328 block.addPostProcessor(typeid(stage::PostStream),
329 boundaryLatticeR,
331 }
332 }
333 }
334 }
335 }
336 }
337 });
338}
339
341template<typename T, typename DESCRIPTOR>
343 SuperGeometry<T,DESCRIPTOR::d>& superGeometry, int material,
345 std::vector<int> bulkMaterials = std::vector<int>(1,1))
346{
347 setBouzidiVelocity<T,DESCRIPTOR>(sLattice, superGeometry.getMaterialIndicator(material), u, bulkMaterials);
348}
349
351template<typename T, typename DESCRIPTOR>
353 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>&& boundaryIndicator,
355 std::vector<int> bulkMaterials = std::vector<int>(1,1))
356{
357 setBouzidiVelocity<T,DESCRIPTOR>(sLattice, std::forward<decltype(boundaryIndicator)>(boundaryIndicator),
358 boundaryIndicator->getSuperGeometry().getMaterialIndicator(std::move(bulkMaterials)),
359 u);
360}
361
363template<typename T, typename DESCRIPTOR>
365 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>&& boundaryIndicator,
368{
369 auto& load = sLattice.getLoadBalancer();
370 auto& cuboidDecomposition = boundaryIndicator->getSuperGeometry().getCuboidDecomposition();
371 for (int iCloc = 0; iCloc < load.size(); ++iCloc) {
372 auto& cuboid = cuboidDecomposition.get(load.glob(iCloc));
374 boundaryIndicator->getBlockIndicatorF(iCloc),
375 bulkIndicator->getBlockIndicatorF(iCloc),
376 u,
377 cuboid);
378 }
379}
380
382template<typename T, typename DESCRIPTOR>
384 BlockIndicatorF<T,DESCRIPTOR::d>& boundaryIndicator,
388{
389 const T deltaR = cuboid.getDeltaR();
390 block.forSpatialLocations([&](LatticeR<DESCRIPTOR::d> solidLatticeR) {
391 if (boundaryIndicator(solidLatticeR)) {
392 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
393 Vector<T,DESCRIPTOR::d> boundaryLatticeR(solidLatticeR + descriptors::c<DESCRIPTOR>(iPop));
394 if (block.isInside(boundaryLatticeR)) {
395 const int iPop_opposite = descriptors::opposite<DESCRIPTOR>(iPop);
396 auto x_b = block.get(boundaryLatticeR);
397 const auto opp_bouzidi_dist = x_b.template getFieldComponent<descriptors::BOUZIDI_DISTANCE>(iPop_opposite);
398
399 // check if distance from the fluid cell to the solid cell is a valid bouzidi distance
400 if (opp_bouzidi_dist >= 0) {
401 T wallVelocity[DESCRIPTOR::d] = { };
402 T physicalIntersection[DESCRIPTOR::d] = { };
403 auto boundaryPhysR = cuboid.getPhysR(boundaryLatticeR);
404
405 // calculating the intersection of the boundary with the missing link in physical coordinates
406 for ( int i = 0; i< DESCRIPTOR::d; ++i) {
407 physicalIntersection[i] = boundaryPhysR[i] + opp_bouzidi_dist * deltaR * descriptors::c<DESCRIPTOR>(iPop_opposite,i);
408 }
409
410 //Calculating the velocity at the wall intersection
411 u(wallVelocity, physicalIntersection);
412 const auto c = descriptors::c<DESCRIPTOR>(iPop_opposite);
413 T vel_coeff = c * Vector<T,DESCRIPTOR::d>(wallVelocity);
414
415 // set computed velocity into the bouzidi velocity field
416 block.get(boundaryLatticeR).template setFieldComponent<descriptors::BOUZIDI_VELOCITY>(iPop_opposite, vel_coeff);
417 }
418 }
419 }
420 }
421 });
422}
423
425template<typename T, typename DESCRIPTOR, bool thermalCreep = false>
427 SuperGeometry<T,DESCRIPTOR::d>& superGeometry, int material,
428 IndicatorF<T,DESCRIPTOR::d>& indicatorAnalyticalBoundary,
429 std::vector<int> bulkMaterials = std::vector<int>(1,1))
430{
431 setBouzidiKnudsenSlipVelocity<T,DESCRIPTOR,thermalCreep>(sLattice, superGeometry.getMaterialIndicator(material), indicatorAnalyticalBoundary, bulkMaterials);
432}
433
435template<typename T, typename DESCRIPTOR, bool thermalCreep = false>
437 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>&& boundaryIndicator,
438 IndicatorF<T,DESCRIPTOR::d>& indicatorAnalyticalBoundary,
439 std::vector<int> bulkMaterials = std::vector<int>(1,1))
440{
441 setBouzidiKnudsenSlipVelocity<T,DESCRIPTOR,thermalCreep>(sLattice, std::forward<decltype(boundaryIndicator)>(boundaryIndicator),
442 boundaryIndicator->getSuperGeometry().getMaterialIndicator(std::move(bulkMaterials)),
443 indicatorAnalyticalBoundary);
444}
445
447template<typename T, typename DESCRIPTOR, bool thermalCreep = false>
449 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>&& boundaryIndicator,
451 IndicatorF<T,DESCRIPTOR::d>& indicatorAnalyticalBoundary)
452{
453 auto& load = sLattice.getLoadBalancer();
454 auto& cuboidDecomposition = boundaryIndicator->getSuperGeometry().getCuboidDecomposition();
455 for (int iCloc = 0; iCloc < load.size(); ++iCloc) {
456 auto& cuboid = cuboidDecomposition.get(load.glob(iCloc));
458 boundaryIndicator->getBlockIndicatorF(iCloc),
459 bulkIndicator->getBlockIndicatorF(iCloc),
460 cuboid,
461 indicatorAnalyticalBoundary);
462 }
463 addPoints2CommBC<T,DESCRIPTOR>(sLattice, std::forward<decltype(boundaryIndicator)>(boundaryIndicator), 5);
464}
465
467template<typename T, typename DESCRIPTOR, bool thermalCreep = false>
469 BlockIndicatorF<T,DESCRIPTOR::d>& boundaryIndicator,
472 IndicatorF<T,DESCRIPTOR::d>& indicatorAnalyticalBoundary)
473{
474 const T deltaR = cuboid.getDeltaR();
475 block.forSpatialLocations([&](LatticeR<DESCRIPTOR::d> solidLatticeR) {
476 if (boundaryIndicator(solidLatticeR)) {
477 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
478 Vector<T,DESCRIPTOR::d> boundaryLatticeR(solidLatticeR + descriptors::c<DESCRIPTOR>(iPop));
479 if (block.isInside(boundaryLatticeR)) {
480 if ( bulkIndicator(boundaryLatticeR)) {
481 auto boundaryPhysR = cuboid.getPhysR(boundaryLatticeR);
482 Vector<T,DESCRIPTOR::d> normal = indicatorAnalyticalBoundary.surfaceNormal(boundaryPhysR, deltaR);
483 for ( int i = 0; i< DESCRIPTOR::d; ++i) {
484 block.get(boundaryLatticeR).template setFieldComponent<descriptors::NORMAL>(i,-normal[i]);
485 }
486 if(thermalCreep) {
487 Vector<T,DESCRIPTOR::d> normalRound {};
488 for(int iD = 0; iD < DESCRIPTOR::d; iD++){
489 normalRound[iD] = util::round(normal[iD]);
490 }
491 auto wallLatticeR = boundaryLatticeR + normalRound;
492 if(boundaryIndicator(wallLatticeR)) {
493 Vector<T,DESCRIPTOR::d> tempDerivative {};
494 T temp = block.get(wallLatticeR).template getField<descriptors::TEMPERATURE>();
495 if constexpr(DESCRIPTOR::d==2) {
496 using DESCR = descriptors::D2Q5<>;
497 for( int iPop=1; iPop < DESCR::q; iPop++) {
498 Vector<T,DESCRIPTOR::d> solidNeighborR(wallLatticeR + descriptors::c<DESCR>(iPop));
499 if (boundaryIndicator(solidNeighborR)) {
500 T tempNeighbor = block.get(solidNeighborR).template getField<descriptors::TEMPERATURE>();
501 for( int iD = 0; iD<DESCRIPTOR::d; iD++) {
502 if( descriptors::c<DESCR>(iPop,iD) > 0) {
503 tempDerivative[iD] = tempNeighbor - temp;
504 }
505 if( descriptors::c<DESCR>(iPop,iD) < 0) {
506 tempDerivative[iD] = temp - tempNeighbor;
507 }
508 }
509 }
510 }
511 } else {
512 using DESCR = descriptors::D3Q7<>;
513 for( int iPop=1; iPop < DESCR::q; iPop++) {
514 Vector<T,DESCRIPTOR::d> solidNeighborR(wallLatticeR + descriptors::c<DESCR>(iPop));
515 if (boundaryIndicator(solidNeighborR)) {
516 T tempNeighbor = block.get(solidNeighborR).template getField<descriptors::TEMPERATURE>();
517 for( int iD = 0; iD<DESCRIPTOR::d; iD++) {
518 if( descriptors::c<DESCR>(iPop,iD) > 0) {
519 tempDerivative[iD] = tempNeighbor - temp;
520 }
521 if( descriptors::c<DESCR>(iPop,iD) < 0) {
522 tempDerivative[iD] = temp - tempNeighbor;
523 }
524 }
525 }
526 }
527 }
528 tempDerivative -= (tempDerivative*normal)*normal;
529 block.get(boundaryLatticeR).template setField<descriptors::TEMPGRADIENT>(tempDerivative);
530 }
531 }
532 }
533 }
534 }
535 }
536 });
537}
538
539template<typename T, typename DESCRIPTOR>
541 SuperGeometry<T,DESCRIPTOR::d>& superGeometry, int material,
542 T phi_d,
543 std::vector<int> bulkMaterials = std::vector<int>(1,1))
544{
545 setBouzidiAdeDirichlet<T,DESCRIPTOR>(sLattice, superGeometry.getMaterialIndicator(material), phi_d, bulkMaterials);
546}
547
548template<typename T, typename DESCRIPTOR>
550 SuperGeometry<T,DESCRIPTOR::d>& superGeometry, int material,
552 std::vector<int> bulkMaterials = std::vector<int>(1,1))
553{
554 setBouzidiAdeDirichlet<T,DESCRIPTOR>(sLattice, superGeometry.getMaterialIndicator(material), phi_d, bulkMaterials);
555}
556
557template<typename T, typename DESCRIPTOR>
559 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>&& boundaryIndicator,
561 T phi_d)
562{
563 auto& load = sLattice.getLoadBalancer();
564 auto& cuboidDecomposition = boundaryIndicator->getSuperGeometry().getCuboidDecomposition();
565 for (int iCloc = 0; iCloc < load.size(); ++iCloc) {
566 auto& cuboid = cuboidDecomposition.get(load.glob(iCloc));
568 boundaryIndicator->getBlockIndicatorF(iCloc),
569 bulkIndicator->getBlockIndicatorF(iCloc),
570 phi_d,
571 cuboid);
572 }
573}
574
575template<typename T, typename DESCRIPTOR>
577 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>&& boundaryIndicator,
580{
581 auto& load = sLattice.getLoadBalancer();
582 auto& cuboidDecomposition = boundaryIndicator->getSuperGeometry().getCuboidDecomposition();
583 for (int iCloc = 0; iCloc < load.size(); ++iCloc) {
584 auto& cuboid = cuboidDecomposition.get(load.glob(iCloc));
586 boundaryIndicator->getBlockIndicatorF(iCloc),
587 bulkIndicator->getBlockIndicatorF(iCloc),
588 phi_d,
589 cuboid);
590 }
591}
592
593template<typename T, typename DESCRIPTOR>
595 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>&& boundaryIndicator,
596 T phi_d,
597 std::vector<int> bulkMaterials = std::vector<int>(1,1))
598{
599 setBouzidiAdeDirichlet<T,DESCRIPTOR>(sLattice, std::forward<decltype(boundaryIndicator)>(boundaryIndicator),
600 boundaryIndicator->getSuperGeometry().getMaterialIndicator(std::move(bulkMaterials)),
601 phi_d);
602}
603
604template<typename T, typename DESCRIPTOR>
606 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>&& boundaryIndicator,
608 std::vector<int> bulkMaterials = std::vector<int>(1,1))
609{
610 setBouzidiAdeDirichlet<T,DESCRIPTOR>(sLattice, std::forward<decltype(boundaryIndicator)>(boundaryIndicator),
611 boundaryIndicator->getSuperGeometry().getMaterialIndicator(std::move(bulkMaterials)),
612 phi_d);
613}
614
615template<typename T, typename DESCRIPTOR>
617 BlockIndicatorF<T,DESCRIPTOR::d>& boundaryIndicator,
619 T phi_d,
621{
622 block.forSpatialLocations([&](LatticeR<DESCRIPTOR::d> solidLatticeR) {
623 if (boundaryIndicator(solidLatticeR)) {
624 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
625 Vector<T,DESCRIPTOR::d> boundaryLatticeR(solidLatticeR + descriptors::c<DESCRIPTOR>(iPop));
626 if (block.isInside(boundaryLatticeR)) {
627 const int iPop_opposite = descriptors::opposite<DESCRIPTOR>(iPop);
628 auto x_b = block.get(boundaryLatticeR);
629 const auto opp_bouzidi_dist = x_b.template getFieldComponent<descriptors::BOUZIDI_DISTANCE>(iPop_opposite);
630
631 // check if distance from the fluid cell to the solid cell is a valid bouzidi distance
632 if (opp_bouzidi_dist >= 0) {
633 // set computed velocity into the bouzidi velocity field
634 block.get(boundaryLatticeR).template setFieldComponent<descriptors::BOUZIDI_ADE_DIRICHLET>(iPop_opposite, phi_d);
635 }
636 }
637 }
638 }
639 });
640}
641
642template<typename T, typename DESCRIPTOR>
644 BlockIndicatorF<T,DESCRIPTOR::d>& boundaryIndicator,
648{
649 block.forSpatialLocations([&](LatticeR<DESCRIPTOR::d> solidLatticeR) {
650 if (boundaryIndicator(solidLatticeR)) {
651 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
652 Vector<T,DESCRIPTOR::d> boundaryLatticeR(solidLatticeR + descriptors::c<DESCRIPTOR>(iPop));
653 if (block.isInside(boundaryLatticeR)) {
654 const int iPop_opposite = descriptors::opposite<DESCRIPTOR>(iPop);
655 auto x_b = block.get(boundaryLatticeR);
656 const auto opp_bouzidi_dist = x_b.template getFieldComponent<descriptors::BOUZIDI_DISTANCE>(iPop_opposite);
657
658 // check if distance from the fluid cell to the solid cell is a valid bouzidi distance
659 if (opp_bouzidi_dist >= 0) {
660 T phi_at_cell[1] { };
661 auto boundaryPhysR = cuboid.getPhysR(boundaryLatticeR);
662
663 phi_d(phi_at_cell, boundaryPhysR.data());
664
665 // set computed velocity into the bouzidi velocity field
666 block.get(boundaryLatticeR).template setFieldComponent<descriptors::BOUZIDI_ADE_DIRICHLET>(iPop_opposite, phi_at_cell[0]);
667 }
668 }
669 }
670 }
671 });
672}
673
679// Set Bouzidi slip velocity boundary on material cells of sLattice
680template<typename T, typename DESCRIPTOR>
682 SuperGeometry<T,DESCRIPTOR::d>& superGeometry, int material,
683 IndicatorF<T,DESCRIPTOR::d>& indicatorAnalyticalBoundary,
684 int gradientOrder = 1,
685 std::vector<int> bulkMaterials = std::vector<int>(1,1),
686 bool isOuterWall = true, bool useDiscreteNormal = false)
687{
689 sLattice, superGeometry.getMaterialIndicator(material),
690 indicatorAnalyticalBoundary, gradientOrder, bulkMaterials, isOuterWall,
691 useDiscreteNormal);
692}
693
695template<typename T, typename DESCRIPTOR>
697 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>&& boundaryIndicator,
698 IndicatorF<T,DESCRIPTOR::d>& indicatorAnalyticalBoundary,
699 int gradientOrder = 1,
700 std::vector<int> bulkMaterials = std::vector<int>(1,1),
701 bool isOuterWall = true, bool useDiscreteNormal = false)
702{
704 sLattice, std::forward<decltype(boundaryIndicator)>(boundaryIndicator),
705 indicatorAnalyticalBoundary,
706 boundaryIndicator->getSuperGeometry().getMaterialIndicator(
707 std::move(bulkMaterials)),
708 gradientOrder, isOuterWall, useDiscreteNormal);
709}
710
712template<typename T, typename DESCRIPTOR>
714 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>&& boundaryIndicator,
715 IndicatorF<T,DESCRIPTOR::d>& indicatorAnalyticalBoundary,
717 int gradientOrder = 1,
718 bool isOuterWall = true,
719 bool useDiscreteNormal = false)
720{
721 auto& load = sLattice.getLoadBalancer();
722 auto& cuboidDecomposition =
723 boundaryIndicator->getSuperGeometry().getCuboidDecomposition();
724 for (int iCloc = 0; iCloc < load.size(); ++iCloc) {
725 auto& cuboid = cuboidDecomposition.get(load.glob(iCloc));
727 boundaryIndicator->getBlockIndicatorF(iCloc),
728 indicatorAnalyticalBoundary,
729 bulkIndicator->getBlockIndicatorF(iCloc),
730 cuboid, gradientOrder, isOuterWall, useDiscreteNormal);
731 }
732 int _overlap = 3;
734 sLattice, std::forward<decltype(boundaryIndicator)>(boundaryIndicator),
735 _overlap);
736}
737
739template<typename T, typename DESCRIPTOR>
741 BlockIndicatorF<T,DESCRIPTOR::d>& boundaryIndicator,
742 IndicatorF<T,DESCRIPTOR::d>& indicatorAnalyticalBoundary,
745 int gradientOrder = 1,
746 bool isOuterWall = true,
747 bool useDiscreteNormal = false)
748{
749 assert((gradientOrder==0) || (gradientOrder==1) || (gradientOrder==2));
750 const T deltaR = cuboid.getDeltaR();
751 block.template setParameter<descriptors::FD_DEG>(gradientOrder);
752 auto& blockGeometryStructure = bulkIndicator.getBlockGeometry();
753 // For each solid cell: all of its fluid neighbors need population updates
754 block.forSpatialLocations([&](LatticeR<DESCRIPTOR::d> solidLatticeR) {
755 // Check if cell is solid cell
756 if (boundaryIndicator(solidLatticeR)) {
757 Vector<int, 3> discreteNormal =
758 blockGeometryStructure.getStatistics().computeNormal(
759 solidLatticeR[0], solidLatticeR[1], solidLatticeR[2]);
760 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
761 Vector<T, DESCRIPTOR::d> boundaryLatticeR(
762 solidLatticeR + descriptors::c<DESCRIPTOR>(iPop));
763 if (block.isInside(boundaryLatticeR)) {
764 const int iPop_opposite = descriptors::opposite<DESCRIPTOR>(iPop);
765 auto x_b = block.get(boundaryLatticeR);
766 //int material = blockGeometryStructure.getMaterial(boundaryLatticeR);
767 const auto opp_bouzidi_dist = x_b.template getFieldComponent<descriptors::BOUZIDI_DISTANCE>(iPop_opposite);
768
769 // check if distance from the fluid cell to the solid cell is a valid bouzidi distance
770 if (opp_bouzidi_dist >= 0 && (bulkIndicator(boundaryLatticeR))){ // || boundaryIndicator(boundaryLatticeR))) { // )){ // Try if( bulkIndicator(boundaryLatticeR))opp_bouzidi_dist >= 0
771 auto boundaryPhysR = cuboid.getPhysR(boundaryLatticeR);
772 IndicatorLayer3D<T> indicatorAnalyticalBoundaryLayer(
773 indicatorAnalyticalBoundary, 0.5 * deltaR);
774 if (!useDiscreteNormal) {
775 Vector<T, 3> normal =
776 indicatorAnalyticalBoundaryLayer.surfaceNormal(boundaryPhysR.data(),
777 deltaR);
778 if (isOuterWall) {
779 normal = -1 * normal;
780 }
781 Vector<T, 3> ceilNormal(ceil(normal[0]), ceil(normal[1]),
782 ceil(normal[2]));
783 Vector<T, 3> floorNormal(floor(normal[0]), floor(normal[1]),
784 floor(normal[2]));
785 if (bulkIndicator(ceilNormal + boundaryLatticeR) ||
786 bulkIndicator(floorNormal + boundaryLatticeR)) {
787 block.get(boundaryLatticeR)
788 .template setField<descriptors::NORMAL>(
789 {normal[0], normal[1], normal[2]});
790 }
791 }
792 else {
793 block.get(boundaryLatticeR)
794 .template setField<descriptors::NORMAL>({(T) discreteNormal[0],
795 (T) discreteNormal[1],
796 (T) discreteNormal[2]});
797 }
798
799 block.get(boundaryLatticeR)
800 .template setFieldComponent<descriptors::BOUZIDI_VELOCITY>(
801 iPop_opposite, T(0));
802 }
803 }
804 }
805 }
806 });
807}
808
809
810template<typename T, typename DESCRIPTOR>
812 SuperGeometry<T,DESCRIPTOR::d>& superGeometry, int material,
814 std::vector<int> bulkMaterials = std::vector<int>(1,1))
815{
817 sLattice, superGeometry.getMaterialIndicator(material), phi_d,
818 bulkMaterials);
819}
820
821template<typename T, typename DESCRIPTOR>
823 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>&& boundaryIndicator,
826{
827 auto& load = sLattice.getLoadBalancer();
828 auto& cuboidDecomposition =
829 boundaryIndicator->getSuperGeometry().getCuboidDecomposition();
830 for (int iCloc = 0; iCloc < load.size(); ++iCloc) {
831 auto& cuboid = cuboidDecomposition.get(load.glob(iCloc));
833 boundaryIndicator->getBlockIndicatorF(iCloc),
834 bulkIndicator->getBlockIndicatorF(iCloc),
835 phi_d,
836 cuboid);
837 }
838}
839
840
841template<typename T, typename DESCRIPTOR>
843 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>&& boundaryIndicator,
845 std::vector<int> bulkMaterials = std::vector<int>(1,1))
846{
848 sLattice, std::forward<decltype(boundaryIndicator)>(boundaryIndicator),
849 boundaryIndicator->getSuperGeometry().getMaterialIndicator(
850 std::move(bulkMaterials)),
851 phi_d);
852}
853
854
855template<typename T, typename DESCRIPTOR>
857 BlockIndicatorF<T,DESCRIPTOR::d>& boundaryIndicator,
861{
862 auto& blockGeometryStructure = bulkIndicator.getBlockGeometry();
863 std::vector<int> discreteNormalOutwards(4, 0);
864 block.forSpatialLocations([&](LatticeR<DESCRIPTOR::d> solidLatticeR) {
865 if (boundaryIndicator(solidLatticeR)) {
866
867 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
868 Vector<T, DESCRIPTOR::d> boundaryLatticeR(
869 solidLatticeR + descriptors::c<DESCRIPTOR>(iPop));
870 if (block.isInside(boundaryLatticeR)) {
871 const int iPop_opposite = descriptors::opposite<DESCRIPTOR>(iPop);
872 auto x_b = block.get(boundaryLatticeR);
873 const auto opp_bouzidi_dist =
874 x_b.template getFieldComponent<descriptors::BOUZIDI_DISTANCE>(
875 iPop_opposite);
876
877 // check if distance from the fluid cell to the solid cell is a valid bouzidi distance
878 if ((opp_bouzidi_dist >= 0) && (bulkIndicator(boundaryLatticeR))) {
879 T phi_at_cell[1] {};
880 auto boundaryPhysR = cuboid.getPhysR(boundaryLatticeR);
881
882 phi_d(phi_at_cell, boundaryPhysR.data());
883 discreteNormalOutwards =
884 blockGeometryStructure.getStatistics().getType(
885 solidLatticeR[0], solidLatticeR[1], solidLatticeR[2]);
886 // set computed velocity into the bouzidi velocity field
887 block.get(boundaryLatticeR)
888 .template setFieldComponent<descriptors::BOUZIDI_WALL_TEMP>(
889 iPop_opposite, phi_at_cell[0]);
890 block.get(boundaryLatticeR)
891 .template setField<descriptors::NORMAL>(
892 {-discreteNormalOutwards[1], -discreteNormalOutwards[2],
893 -discreteNormalOutwards[3]});
894 }
895 }
896 }
897 }
898 });
899}
900
902template<typename T, typename DESCRIPTOR, typename OPERATOR = BouzidiPostProcessor>
904 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>&& boundaryIndicator,
906 IndicatorF<T,DESCRIPTOR::d>& indicatorAnalyticalBoundary)
907{
908 int _overlap = 1;
909 OstreamManager clout(std::cout, "BouzidiPhaseFieldSetter");
910 auto& load = sLattice.getLoadBalancer();
911 for (int iC=0; iC < load.size(); ++iC) {
913 (bulkIndicator->getBlockIndicatorF(iC)).getBlockGeometry(),
914 boundaryIndicator->getBlockIndicatorF(iC),
915 bulkIndicator->getBlockIndicatorF(iC),
916 indicatorAnalyticalBoundary);
917 }
919 auto& communicator = sLattice.getCommunicator(stage::PostStream());
920 communicator.template requestField<descriptors::STATISTIC>();
921 communicator.template requestField<descriptors::BOUNDARY>();
922
923 SuperIndicatorBoundaryNeighbor<T,DESCRIPTOR::d> neighborIndicator(std::forward<decltype(boundaryIndicator)>(boundaryIndicator), _overlap);
924 communicator.requestOverlap(_overlap, neighborIndicator);
925 communicator.exchangeRequests();
926 //addPoints2CommBC<T,DESCRIPTOR>(sLattice, std::forward<decltype(boundaryIndicator)>(boundaryIndicator), _overlap);
927 clout.setMultiOutput(false);
928}
929
930
932template<typename T, typename DESCRIPTOR, typename OPERATOR = BouzidiPostProcessor>
934 SuperGeometry<T,DESCRIPTOR::d>& superGeometry,
935 int materialOfSolidObstacle,
936 IndicatorF<T,DESCRIPTOR::d>& indicatorAnalyticalBoundary,
937 std::vector<int> bulkMaterials = std::vector<int>(1,1))
938{
939 //Getting the indicators by material numbers and calling the superLattice method via the indicators:
941 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>(superGeometry.getMaterialIndicator(materialOfSolidObstacle)),
942 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>(superGeometry.getMaterialIndicator(std::move(bulkMaterials))),
943 indicatorAnalyticalBoundary);
944}
945
946
948template<typename T, typename DESCRIPTOR, typename OPERATOR = BouzidiPostProcessor>
950 BlockGeometry<T,DESCRIPTOR::d>& blockGeometry,
951 BlockIndicatorF<T,DESCRIPTOR::d>& boundaryIndicator,
953 IndicatorF<T,DESCRIPTOR::d>& indicatorAnalyticalBoundary,
954 bool verbose = false)
955{
956 OstreamManager clout(std::cout, "BouzidiPhaseFieldSetter");
957 clout.setMultiOutput(true);
958
959 const T deltaR = blockGeometry.getDeltaR();
960 // for each solid cell: all of its fluid neighbors need population updates
961 block.forSpatialLocations([&](LatticeR<DESCRIPTOR::d> solidLatticeR) {
962 // Check if cell is solid cell
963
964 if ( blockGeometry.getNeighborhoodRadius(solidLatticeR) >= 1
965 && boundaryIndicator(solidLatticeR)) {
966 std::vector<int> discreteNormal(DESCRIPTOR::d+1, 0);
967 discreteNormal = boundaryIndicator.getBlockGeometry().getStatistics().getType(solidLatticeR[0],solidLatticeR[1]);
968
969 for (int iPop=1; iPop < DESCRIPTOR::q; ++iPop) {
970 Vector<T,DESCRIPTOR::d> boundaryLatticeR(solidLatticeR + descriptors::c<DESCRIPTOR>(iPop));
971 const auto c = descriptors::c<DESCRIPTOR>(iPop);
972 const auto iPop_opposite = descriptors::opposite<DESCRIPTOR>(iPop);
973
974 if (blockGeometry.isInside(boundaryLatticeR)) {
975 auto boundaryPhysR = blockGeometry.getPhysR(boundaryLatticeR);
976 // check if neighbor is fluid cell
977 if (bulkIndicator(boundaryLatticeR.data())) {
978 T dist = -1; // distance to boundary
979 T qIpop = -1; // normed distance (Bouzidi distance) to boundary
981 auto direction = -deltaR * c; // vector pointing from the boundary cell to the solid cell
982
983 // Check if distance calculation was performed correctly
984 if (indicatorAnalyticalBoundary.distance(dist, boundaryPhysR, direction, blockGeometry.getIcGlob())) {
985 qIpop = dist / norm;
986
987 // if distance function returned a dist. not suitable for Bouzidi -> fall-back
988 if ((qIpop < 0) || (qIpop > 1)) {
989 if(verbose) {
990 clout << "Error, non suitable dist. at lattice: (" << boundaryLatticeR << "), physical: (" << blockGeometry.getPhysR(boundaryLatticeR) << "), direction " << iPop << ". Fall-back to bounce-back." << std::endl;
991 }
992
993 // fall-back: half-way bounce back
994 qIpop = 0.5;
995 }
996 }
997 // if distance function couldn't compute any distance -> fall-back
998 else {
999 if(verbose) {
1000 clout << "Error, no boundary found at lattice:(" << boundaryLatticeR << "), physical: (" << blockGeometry.getPhysR(boundaryLatticeR) << "), direction: " << iPop << ".Fall-back to bounce-back." << std::endl;
1001 }
1002
1003 // fall-back: half-way bounce back
1004 qIpop = 0.5;
1005 }
1006 //clout << "Distance to boundary at lattice:(" << boundaryLatticeR << ") and direction (" << iPop << "): " << qIpop << std::endl;
1007 // double check
1008 if (qIpop >= 0) {
1009 // Bouzidi require the fluid side neighbor of the boundary cell also to be fluid
1010 if (bulkIndicator(boundaryLatticeR + descriptors::c<DESCRIPTOR>(iPop))) {
1011 // Standard case, c.f. Bouzidi paper, setting Bouzidi-distance
1012 block.get(boundaryLatticeR).template setFieldComponent<descriptors::BOUZIDI_DISTANCE>(iPop_opposite, qIpop);
1013 }
1014 else {
1015 // If no fluid cell found: fall-back to bounce-back
1016 block.get(boundaryLatticeR).template setFieldComponent<descriptors::BOUZIDI_DISTANCE>(iPop_opposite, T{0.5});
1017 }
1018 // Setting up the post processor, if this cell does not have one yet.
1019 if (!block.isPadding(boundaryLatticeR)) {
1020 block.addPostProcessor(typeid(stage::PostStream),
1021 boundaryLatticeR,
1023 }
1024 }
1025 }
1026 // if neigbour cell is not fluid
1027 else {
1028 // check if neighbor cell is not solid
1029 if (blockGeometry.getMaterial(boundaryLatticeR) != 0) {
1030 // fall-back to half-way bounce-back
1031 block.get(boundaryLatticeR).template setFieldComponent<descriptors::BOUZIDI_DISTANCE>(iPop_opposite, T{0.5});
1032 if (!block.isPadding(boundaryLatticeR)) {
1033 block.addPostProcessor(typeid(stage::PostStream),
1034 boundaryLatticeR,
1036 }
1037 }
1038 }
1039 }
1040 }
1041 T discreteNormalSum=0;
1042 for (int iD=0; iD<DESCRIPTOR::d; iD++) {
1043 discreteNormalSum += abs(discreteNormal[iD+1]);
1044 }
1045 if (discreteNormalSum == 0) {
1046 block.addPostProcessor(
1048 } else {
1049 block.addPostProcessor(
1050 typeid(stage::PreCoupling), solidLatticeR,
1052 }
1053 }
1054 });
1055}
1056
1057
1059template<typename T, typename DESCRIPTOR, typename OPERATOR = BouzidiPostProcessor>
1061 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>&& boundaryIndicator,
1063 IndicatorF<T,DESCRIPTOR::d>& indicatorAnalyticalBoundary)
1064{
1065 int _overlap = 1;
1066 OstreamManager clout(std::cout, "BouzidiWellBalancedSetter");
1067 auto& load = sLattice.getLoadBalancer();
1068 for (int iC=0; iC < load.size(); ++iC) {
1070 (bulkIndicator->getBlockIndicatorF(iC)).getBlockGeometry(),
1071 boundaryIndicator->getBlockIndicatorF(iC),
1072 bulkIndicator->getBlockIndicatorF(iC),
1073 indicatorAnalyticalBoundary);
1074 }
1076 auto& communicator = sLattice.getCommunicator(stage::PostStream());
1077 communicator.template requestField<descriptors::STATISTIC>();
1078 communicator.template requestField<descriptors::BOUNDARY>();
1079
1080 SuperIndicatorBoundaryNeighbor<T,DESCRIPTOR::d> neighborIndicator(std::forward<decltype(boundaryIndicator)>(boundaryIndicator), _overlap);
1081 communicator.requestOverlap(_overlap, neighborIndicator);
1082 communicator.exchangeRequests();
1083 //addPoints2CommBC<T,DESCRIPTOR>(sLattice, std::forward<decltype(boundaryIndicator)>(boundaryIndicator), _overlap);
1084 clout.setMultiOutput(false);
1085}
1086
1087
1089template<typename T, typename DESCRIPTOR, typename OPERATOR = BouzidiPostProcessor>
1091 SuperGeometry<T,DESCRIPTOR::d>& superGeometry,
1092 int materialOfSolidObstacle,
1093 IndicatorF<T,DESCRIPTOR::d>& indicatorAnalyticalBoundary,
1094 std::vector<int> bulkMaterials = std::vector<int>(1,1))
1095{
1096 //Getting the indicators by material numbers and calling the superLattice method via the indicators:
1098 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>(superGeometry.getMaterialIndicator(materialOfSolidObstacle)),
1099 FunctorPtr<SuperIndicatorF<T,DESCRIPTOR::d>>(superGeometry.getMaterialIndicator(std::move(bulkMaterials))),
1100 indicatorAnalyticalBoundary);
1101}
1102
1103
1105template<typename T, typename DESCRIPTOR, typename OPERATOR = BouzidiPostProcessor>
1107 BlockGeometry<T,DESCRIPTOR::d>& blockGeometry,
1108 BlockIndicatorF<T,DESCRIPTOR::d>& boundaryIndicator,
1109 BlockIndicatorF<T,DESCRIPTOR::d>& bulkIndicator,
1110 IndicatorF<T,DESCRIPTOR::d>& indicatorAnalyticalBoundary,
1111 bool verbose = false)
1112{
1113 OstreamManager clout(std::cout, "BouzidiWellBalancedSetter");
1114 clout.setMultiOutput(true);
1115
1116 const T deltaR = blockGeometry.getDeltaR();
1117 // for each solid cell: all of its fluid neighbors need population updates
1118 block.forSpatialLocations([&](LatticeR<DESCRIPTOR::d> solidLatticeR) {
1119 // Check if cell is solid cell
1120 if ( blockGeometry.getNeighborhoodRadius(solidLatticeR) >= 1
1121 && boundaryIndicator(solidLatticeR)) {
1122 std::vector<int> discreteNormal(DESCRIPTOR::d+1, 0);
1123 if constexpr ( DESCRIPTOR::d==2 ) {
1124 discreteNormal = boundaryIndicator.getBlockGeometry().getStatistics().getType(solidLatticeR[0],solidLatticeR[1]);
1125 block.addPostProcessor(
1127 Vector<int,DESCRIPTOR::d>(discreteNormal.data() + 1)));
1128 }
1129 else if constexpr ( DESCRIPTOR::d==3 ) {
1130 discreteNormal = boundaryIndicator.getBlockGeometry().getStatistics().getType(solidLatticeR[0],solidLatticeR[1],solidLatticeR[2]);
1131 block.addPostProcessor(
1133 Vector<int,DESCRIPTOR::d>(discreteNormal.data() + 1)));
1134 }
1135
1136 for (int iPop=1; iPop < DESCRIPTOR::q; ++iPop) {
1137 Vector<T,DESCRIPTOR::d> boundaryLatticeR(solidLatticeR + descriptors::c<DESCRIPTOR>(iPop));
1138 const auto c = descriptors::c<DESCRIPTOR>(iPop);
1139 const auto iPop_opposite = descriptors::opposite<DESCRIPTOR>(iPop);
1140
1141 if (blockGeometry.isInside(boundaryLatticeR)) {
1142 auto boundaryPhysR = blockGeometry.getPhysR(boundaryLatticeR);
1143 // check if neighbor is fluid cell
1144 if (bulkIndicator(boundaryLatticeR.data())) {
1145 T dist = -1; // distance to boundary
1146 T qIpop = -1; // normed distance (Bouzidi distance) to boundary
1148 auto direction = -deltaR * c; // vector pointing from the boundary cell to the solid cell
1149
1150 // Check if distance calculation was performed correctly
1151 if (indicatorAnalyticalBoundary.distance(dist, boundaryPhysR, direction, blockGeometry.getIcGlob())) {
1152 qIpop = dist / norm;
1153
1154 // if distance function returned a dist. not suitable for Bouzidi -> fall-back
1155 if ((qIpop < 0) || (qIpop > 1)) {
1156 if(verbose) {
1157 clout << "Error, non suitable dist. at lattice: (" << boundaryLatticeR << "), physical: (" << blockGeometry.getPhysR(boundaryLatticeR) << "), direction " << iPop << ". Fall-back to bounce-back." << std::endl;
1158 }
1159
1160 // fall-back: half-way bounce back
1161 qIpop = 0.5;
1162 }
1163 }
1164 // if distance function couldn't compute any distance -> fall-back
1165 else {
1166 if(verbose) {
1167 clout << "Error, no boundary found at lattice:(" << boundaryLatticeR << "), physical: (" << blockGeometry.getPhysR(boundaryLatticeR) << "), direction: " << iPop << ".Fall-back to bounce-back." << std::endl;
1168 }
1169
1170 // fall-back: half-way bounce back
1171 qIpop = 0.5;
1172 }
1173 //clout << "Distance to boundary at lattice:(" << boundaryLatticeR << ") and direction (" << iPop << "): " << qIpop << std::endl;
1174 // double check
1175 if (qIpop >= 0) {
1176 // Bouzidi require the fluid side neighbor of the boundary cell also to be fluid
1177 if (bulkIndicator(boundaryLatticeR + descriptors::c<DESCRIPTOR>(iPop))) {
1178 // Standard case, c.f. Bouzidi paper, setting Bouzidi-distance
1179 block.get(boundaryLatticeR).template setFieldComponent<descriptors::BOUZIDI_DISTANCE>(iPop_opposite, qIpop);
1180 }
1181 else {
1182 // If no fluid cell found: fall-back to bounce-back
1183 block.get(boundaryLatticeR).template setFieldComponent<descriptors::BOUZIDI_DISTANCE>(iPop_opposite, T{0.5});
1184 }
1185 // Setting up the post processor, if this cell does not have one yet.
1186 if (!block.isPadding(boundaryLatticeR)) {
1187 block.addPostProcessor(typeid(stage::PostStream),
1188 boundaryLatticeR,
1190 }
1191 }
1192 }
1193 // if neigbour cell is not fluid
1194 else {
1195 // check if neighbor cell is not solid
1196 if (blockGeometry.getMaterial(boundaryLatticeR) != 0) {
1197 // fall-back to half-way bounce-back
1198 block.get(boundaryLatticeR).template setFieldComponent<descriptors::BOUZIDI_DISTANCE>(iPop_opposite, T{0.5});
1199 if (!block.isPadding(boundaryLatticeR)) {
1200 block.addPostProcessor(typeid(stage::PostStream),
1201 boundaryLatticeR,
1203 }
1204 }
1205 }
1206 }
1207 }
1208 }
1209 });
1210}
1211
1212
1213} // namespace olb
1214
1215#endif
AnalyticalF are applications from DD to XD, where X is set by the constructor.
Representation of a block geometry.
Vector< T, D > getPhysR(LatticeR< D > latticeR) const
int getMaterial(LatticeR< D > latticeR) const
returns the (iX,iY) entry in the 2D scalar field
int getIcGlob() const
Read only access to the global iC number which is given !=-1 if the block geometries are part of a su...
T getDeltaR() const
Read only access to the voxel size given in SI units (meter)
CellDistance getNeighborhoodRadius(LatticeR< D > latticeR) const
Return maximum valid neighborhood sphere radius w.r.t. latticeR.
bool isInside(LatticeR< D > latticeR) const
Return whether location is valid.
static constexpr OperatorScope scope
void apply(CELL &x_b) any_platform
Post processor for the zero-velocity Bouzidi boundary.
static constexpr OperatorScope scope
void apply(CELL &x_b) any_platform
Post processor for the velocity Bouzidi boundary.
static constexpr OperatorScope scope
void apply(CELL &x_b) any_platform
T getDeltaR() const
Returns spacing of cuboid nodes.
Definition cuboid.h:76
Vector< T, D > getPhysR(LatticeR< D > latticeR) const
Definition cuboid.h:90
Smart pointer for managing the various ways of passing functors around.
Definition functorPtr.h:60
virtual Vector< S, 3 > surfaceNormal(const Vector< S, 3 > &pos, const S meshSize)
Return surface normal Uses the fastest, but potentially less accurate method.
indicator function for a layer
class for marking output with some text
void setMultiOutput(bool b)
enable message output for all MPI processes, disabled by default
Representation of a statistic for a parallel 2D geometry.
std::unique_ptr< SuperIndicatorF< T, D > > getMaterialIndicator(std::vector< int > &&materials)
Returns a material indicator using the given vector of materials.
Super class maintaining block lattices for a cuboid decomposition.
SuperCommunicator< T, SuperLattice > & getCommunicator(STAGE stage=STAGE())
Return communicator for given communication stage.
BlockLattice< T, DESCRIPTOR > & getBlock(int locC)
Return BlockLattice with local index locC.
LoadBalancer< T > & getLoadBalancer()
Read and write access to the load balancer.
Plain old scalar vector.
constexpr const T * data() const any_platform
Definition vector.h:172
void apply(CELL &x_b) any_platform
static constexpr OperatorScope scope
PostProcessorPromise< T, DESCRIPTOR > promisePostProcessorForNormal(Vector< int, 2 > n)
constexpr int q() any_platform
Definition functions.h:140
constexpr T invCs2() any_platform
Definition functions.h:107
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
ADf< T, DIM > round(const ADf< T, DIM > &a)
Definition aDiff.h:928
T norm(const std::vector< T > &a)
l2 norm of a vector of arbitrary length
Top level namespace for all of OpenLB.
void setBouzidiVelocity(SuperLattice< T, DESCRIPTOR > &sLattice, SuperGeometry< T, DESCRIPTOR::d > &superGeometry, int material, AnalyticalF< DESCRIPTOR::d, T, T > &u, std::vector< int > bulkMaterials=std::vector< int >(1, 1))
Set Bouzidi velocity boundary on material cells of sLattice.
void setBouzidiTemperatureJump(SuperLattice< T, DESCRIPTOR > &sLattice, SuperGeometry< T, DESCRIPTOR::d > &superGeometry, int material, AnalyticalF< DESCRIPTOR::d, T, T > &phi_d, std::vector< int > bulkMaterials=std::vector< int >(1, 1))
std::conditional_t< D==2, SuperIndicatorBoundaryNeighbor2D< T >, SuperIndicatorBoundaryNeighbor3D< T > > SuperIndicatorBoundaryNeighbor
Definition aliases.h:297
void addPoints2CommBC(SuperLattice< T, DESCRIPTOR > &sLattice, FunctorPtr< SuperIndicatorF2D< T > > &&indicator, int _overlap)
Adds needed Cells to the Communicator _commBC in SuperLattice.
void setBouzidiBoundary(SuperLattice< T, DESCRIPTOR > &sLattice, FunctorPtr< SuperIndicatorF< T, DESCRIPTOR::d > > &&boundaryIndicator, FunctorPtr< SuperIndicatorF< T, DESCRIPTOR::d > > &&bulkIndicator, IndicatorF< T, DESCRIPTOR::d > &indicatorAnalyticalBoundary)
Set Bouzidi boundary on indicated cells of sLattice.
void setBouzidiPhaseField(SuperLattice< T, DESCRIPTOR > &sLattice, FunctorPtr< SuperIndicatorF< T, DESCRIPTOR::d > > &&boundaryIndicator, FunctorPtr< SuperIndicatorF< T, DESCRIPTOR::d > > &&bulkIndicator, IndicatorF< T, DESCRIPTOR::d > &indicatorAnalyticalBoundary)
Set Bouzidi boundary with contact angle for phase field models on indicated cells of sLattice.
std::conditional_t< D==2, BlockIndicatorF2D< T >, BlockIndicatorF3D< T > > BlockIndicatorF
Definition aliases.h:207
std::conditional_t< D==2, IndicatorF2D< T >, IndicatorF3D< T > > IndicatorF
Definition aliases.h:247
std::enable_if_t< std::is_arithmetic< T >::type::value, T > abs(T x) any_platform
Definition util.h:464
void setBouzidiWellBalanced(SuperLattice< T, DESCRIPTOR > &sLattice, FunctorPtr< SuperIndicatorF< T, DESCRIPTOR::d > > &&boundaryIndicator, FunctorPtr< SuperIndicatorF< T, DESCRIPTOR::d > > &&bulkIndicator, IndicatorF< T, DESCRIPTOR::d > &indicatorAnalyticalBoundary)
Set Bouzidi boundary with contact angle for phase field models on indicated cells of sLattice.
OperatorScope
Block-wide operator application scopes.
@ PerCell
Per-cell application, i.e. OPERATOR::apply is passed a CELL concept implementation.
constexpr T norm(const ScalarVector< T, D, IMPL > &a) any_platform
Euclidean vector norm.
std::conditional_t< D==2, SuperIndicatorF2D< T >, SuperIndicatorF3D< T > > SuperIndicatorF
Definition aliases.h:197
void setBouzidiAdeDirichlet(SuperLattice< T, DESCRIPTOR > &sLattice, SuperGeometry< T, DESCRIPTOR::d > &superGeometry, int material, T phi_d, std::vector< int > bulkMaterials=std::vector< int >(1, 1))
void setBouzidiKnudsenSlipVelocity(SuperLattice< T, DESCRIPTOR > &sLattice, SuperGeometry< T, DESCRIPTOR::d > &superGeometry, int material, IndicatorF< T, DESCRIPTOR::d > &indicatorAnalyticalBoundary, std::vector< int > bulkMaterials=std::vector< int >(1, 1))
Set Bouzidi velocity boundary on material cells of sLattice.
void setBouzidiSlipVelocity(SuperLattice< T, DESCRIPTOR > &sLattice, SuperGeometry< T, DESCRIPTOR::d > &superGeometry, int material, IndicatorF< T, DESCRIPTOR::d > &indicatorAnalyticalBoundary, int gradientOrder=1, std::vector< int > bulkMaterials=std::vector< int >(1, 1), bool isOuterWall=true, bool useDiscreteNormal=false)
Bouzidi General/Knudsen Slip Velocity 3D Sets slip velocity on general walls.
#define any_platform
Define preprocessor macros for device-side functions, constant storage.
Definition platform.h:77
D2Q5 lattice.
Definition common.h:84
D3Q7 lattice.
Definition common.h:173
Identity type to pass non-constructible types as value.
Definition meta.h:79
Communication after propagation.
Definition stages.h:36
Communication prior to coupling.
Definition stages.h:42