MAIA bb96820c
Multiphysics at AIA
Loading...
Searching...
No Matches
lbbndcnddxqy.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The m-AIA AUTHORS
2//
3// This file is part of m-AIA (https://git.rwth-aachen.de/aia/m-AIA/m-AIA)
4//
5// SPDX-License-Identifier: LGPL-3.0-only
6
7#include "lbbndcnddxqy.h"
8
9#include <algorithm>
10#include <cmath>
11#include <limits>
12#include <sstream>
13#include <unordered_set>
14#include "COMM/mpioverride.h"
16#include "INCLUDE/maiatypes.h"
17#include "IO/context.h"
18#include "UTIL/parallelfor.h"
19#include "lbfunctions.h"
20#include "lbsyseqn.h"
21
22using namespace lbconstants;
23
29template <MInt nDim, MInt nDist, class SysEqn>
31 TRACE();
32
33 NEW_TIMER_GROUP(tgrp_BC3D, "Boundary Condition 3D");
34 NEW_TIMER(t_BC3DAll, "complete BC setup", tgrp_BC3D);
35 RECORD_TIMER_START(t_BC3DAll);
36
37 m_log << std::endl;
38 m_log << "#########################################################################################################"
39 "#############"
40 << std::endl;
41 m_log << "## " << nDim
42 << "D Boundary Conditions "
43 " ##"
44 << std::endl;
45 m_log << "#########################################################################################################"
46 "#############"
47 << std::endl
48 << std::endl;
49
50 NEW_SUB_TIMER(t_initMembers, "init members", t_BC3DAll);
51 RECORD_TIMER_START(t_initMembers);
52
53 m_solver = static_cast<LbSolverDxQy<nDim, nDist, SysEqn>*>(solver);
54
55 // for prescibed rho at inflow / outflow boundaries
56 m_rho1 = m_solver->m_rho1;
57 m_rho2 = m_solver->m_rho2;
58
70 m_lbControlInflow = 0;
71 m_lbControlInflow = Context::getSolverProperty<MInt>("lbControlInflow", m_solverId, AT_, &m_lbControlInflow);
72
84 m_zeroInflowVelocity = 1.0;
85 m_zeroInflowVelocity =
86 Context::getSolverProperty<MFloat>("zeroInflowVelocity", m_solverId, AT_, &m_zeroInflowVelocity);
87
88 RECORD_TIMER_STOP(t_initMembers);
89
90 if(m_solver->isActive()) {
91 NEW_SUB_TIMER(t_calcWDist, "calculate wall distances", t_BC3DAll);
92 g_tc_geometry.push_back(std::pair<MString, MInt>("calculate wall distances", t_calcWDist));
93 RECORD_TIMER_START(t_calcWDist);
94 if(m_calcSublayerDist) {
95 mAlloc(m_distIntersectionElementId, m_solver->a_noCells(), nDist, "distIntersectionElementId", -1, AT_);
96 }
97 calculateWallDistances(); // needed for bounce back with inclined walls
98 RECORD_TIMER_STOP(t_calcWDist);
99
100 NEW_SUB_TIMER(t_parInflow, "apply parabolic inflow", t_BC3DAll);
101 g_tc_geometry.push_back(std::pair<MString, MInt>("apply parabolic inflow", t_parInflow));
102 RECORD_TIMER_START(t_parInflow);
103
104 if(m_lbControlInflow) {
105 switch(m_lbControlInflow) {
106 case 1:
107 parabolicInflow(1);
108 break;
109 case 2:
110 parabolicInflow(2);
111 break;
112 default:
113 m_log << "LbBndCndDxQy::() unknown index for parabolic inflow " << std::endl;
114 std::stringstream errorMessage;
115 errorMessage << "LbBndCndDxQy::() unknown index for parabolic inflow";
116 DEBUG("lbbndcnddxqy::parabolicInflow() unknown index for parabolic inflow" << std::endl,
117 MAIA_DEBUG_ASSERTION);
118 TERMM(1, errorMessage.str());
119 }
120 }
121 RECORD_TIMER_STOP(t_parInflow);
122 }
123
124 m_maxDeltaRho = 1.0 - m_rho1;
125 m_rhoLast = m_rho1;
126 m_lRho = 0.0;
127
128 m_currentTimeStep = globalTimeStep;
129
142 const MString bounceBackSchemeMb_default = "BOUZIDI_QUADRATIC";
143 bounceBackSchemeMb = string2enum(
144 Context::getSolverProperty<MString>("bounceBackSchemeMb", m_solverId, AT_, &bounceBackSchemeMb_default));
145
146 switch(bounceBackSchemeMb) {
147 case(BOUZIDI_LINEAR):
148#ifdef WAR_NVHPC_PSTL
149 m_bounceBackFunctionMbCase = 0;
150#else
152#endif
153 break;
154 case(BOUZIDI_QUADRATIC):
155#ifdef WAR_NVHPC_PSTL
156 m_bounceBackFunctionMbCase = 1;
157#else
159#endif
160 break;
161 case(YU_QUADRATIC):
162#ifdef WAR_NVHPC_PSTL
163 m_bounceBackFunctionMbCase = 2;
164#else
166#endif
167 break;
168 default:
169 TERMM(1, "Bounceback Scheme Mb not set!");
170 break;
171 }
172
184 m_refillMethodOrder = 2;
185 m_refillMethodOrder = Context::getSolverProperty<MInt>("refillMethodOrder", m_solverId, AT_, &m_refillMethodOrder);
186
187 RECORD_TIMER_STOP(t_BC3DAll);
188
189 if(m_solver->m_geometry->m_debugParGeom) {
190 DISPLAY_ALL_GROUP_TIMERS(tgrp_BC3D);
191 m_log << std::endl;
192 }
193
194 m_log << std::endl << std::endl;
195}
196
200template <MInt nDim, MInt nDist, class SysEqn>
202 if(m_calcSublayerDist) mDeallocate(m_distIntersectionElementId);
203 if(m_oldWallTemp != nullptr) mDeallocate(m_oldWallTemp);
204 if(m_mucousDist != nullptr) mDeallocate(m_mucousDist);
205 if(m_fluidDist != nullptr) mDeallocate(m_fluidDist);
206}
207
212template <MInt nDim, MInt nDist, class SysEqn>
214
228template <MInt nDim, MInt nDist, class SysEqn>
230 TRACE();
231
232 std::ofstream ofl;
233 std::stringstream fname;
234 fname << "rim_s" << segmentId << "_" << m_solver->domainId() << ".vtp";
235 ofl.open(fname.str().c_str());
236
237 //================== VTKFile =================
238 ofl << "<?xml version=\"1.0\"?>" << std::endl;
239 ofl << "<VTKFile type=\"PolyData\" version=\"0.1\" byte_order=\"LittleEndian\">" << std::endl;
240 ofl << "<PolyData>" << std::endl;
241 ofl << "<Piece NumberOfPoints=\"" << num << "\" NumberOfVerts=\"" << num << "\" NumberOfLines=\"" << 1 << "\">"
242 << std::endl;
243
244 ofl << "<PointData TCoords=\"Texture Coordinates\">" << std::endl;
245 ofl << "<DataArray type=\"Float32\" Name=\"Array_Position\" NumberOfComponents=\"1\" format=\"ascii\">" << std::endl;
246 for(MInt i = 0; i < num; i++)
247 ofl << i << " ";
248 ofl << std::endl;
249 ofl << "</DataArray>" << std::endl;
250 ofl << "</PointData>" << std::endl;
251
252 //================== Points =================
253 ofl << "<Points>" << std::endl;
254 ofl << "<DataArray type=\"Float32\" NumberOfComponents=\"3\" format=\"ascii\">" << std::endl;
255 for(MInt i = 0; i < num; i++) {
256 for(MInt d = 0; d < nDim; d++) {
257 ofl << vertices[i][d] << " ";
258 }
259 }
260 ofl << std::endl;
261 ofl << "</DataArray>" << std::endl;
262 ofl << "</Points>" << std::endl;
263 //================== /Points =================
264
265 /*
266 //================== Verts ===================
267 ofl << "<Verts>" << std::endl;
268 ofl << "<DataArray type=\"Int64\" Name=\"connectivity\" format=\"ascii\">" << std::endl;
269 for(MInt i = 0; i < num; i++)
270 ofl << i << " ";
271 ofl << "0" << std::endl;
272 ofl << "</DataArray>" << std::endl;
273 ofl << "<DataArray type=\"Int64\" Name=\"offsets\" format=\"ascii\">" << std::endl;
274 for(MInt i = 1; i <= num; i++)
275 ofl << i << " ";
276 ofl << num+1 << std::endl;
277 ofl << "</DataArray>" << std::endl;
278 ofl << "</Verts>" << std::endl;
279 //================== /Verts ==================
280 */
281 //================== Lines ===================
282 ofl << "<Lines>" << std::endl;
283 ofl << "<DataArray type=\"Int64\" Name=\"connectivity\" format=\"ascii\">" << std::endl;
284 for(MInt i = 0; i < num; i++)
285 ofl << i << " ";
286 ofl << "0" << std::endl;
287 ofl << "</DataArray>" << std::endl;
288 ofl << "<DataArray type=\"Int64\" Name=\"offsets\" format=\"ascii\">" << std::endl;
289 // for(MInt i = 1; i <= num; i++)
290 // ofl << i << " ";
291 ofl << num + 1 << std::endl;
292 ofl << "</DataArray>" << std::endl;
293 ofl << "</Lines>" << std::endl;
294 //================== /Lines ==================
295
296 ofl << "</Piece>" << std::endl;
297 ofl << "</PolyData>" << std::endl;
298 ofl << "</VTKFile>" << std::endl;
299 ofl.close();
300}
301
310template <MInt nDim, MInt nDist, class SysEqn>
312 TRACE();
313
314 m_log << " + Calculating wall distances..." << std::endl;
315 m_log << " - method: " << m_interpolationDistMethod << std::endl;
316 m_log << " - grid cut method: " << m_gridCutTest << std::endl;
317
318 // init the volumes
319 for(auto& i : m_bndCells) {
320 i.m_isFluid = false;
321 }
322
323 if(m_interpolationDistMethod == "sphere" || m_interpolationDistMethod == "circle") {
324 const MInt nDim_ = (m_interpolationDistMethod == "sphere") ? 3 : 2;
325 for(MInt i = 0; i < (MInt)m_bndCells.size(); i++) {
326 const MInt currentId = m_bndCells[i].m_cellId;
327
328 const MFloat cellHalfLength = m_solver->c_cellLengthAtLevel(m_solver->a_level(currentId) + 1);
329 const MFloat cellRadiusSq =
330 std::inner_product(&m_solver->a_coordinate(currentId, 0), &m_solver->a_coordinate(currentId, nDim_),
331 &m_solver->a_coordinate(currentId, 0), .0);
332
333 //--Determine whether cell center is inside of fluid or not---------------
334 // const MFloat radius = 0.5;
335 const MFloat radiusSq = 0.25;
336 m_bndCells[i].m_isFluid = (cellRadiusSq > radiusSq);
337
338 //--Calculate distance for each distribution------------------------------
339 // init distances
340 m_bndCells[i].m_distances.clear();
341 m_bndCells[i].m_distances.resize(IPOW3[nDim] - 1, 0.0);
342 for(MInt dist = 0; dist < nDist - 1; dist++) {
343 const MFloat dxSq = Ld::distType(dist) * 4.0 * cellHalfLength * cellHalfLength;
344
345 // Set distance to a large value...
346 m_bndCells[i].m_distances[dist] = std::numeric_limits<MFloat>::max();
347
348 // solve quadratic equation for q: R^2 = (x_cell + q*c_dist)^2
349 // if cell belongs to the wall q>1/2
350 // otherwise q<=1/2
351
352 MFloat qValue = 0.0; // normalized wall distance: q = distance between cell center and wall / dx
353 for(MInt j = 0; j < nDim_; j++) {
354 qValue -=
355 m_solver->a_coordinate(currentId, j) * ((MFloat)Ld::idFld(dist, j) - 1.0) * 2.0 * cellHalfLength / dxSq;
356 }
357
358 const MFloat tmpQValue = sqrt(qValue * qValue + (radiusSq - cellRadiusSq) / dxSq);
359
360 if(qValue + tmpQValue >= 0.0 && fabs(qValue + tmpQValue) <= 0.5) {
361 qValue += tmpQValue;
362 m_bndCells[i].m_distances[dist] = qValue;
363 }
364 if(qValue - tmpQValue >= 0.0 && fabs(qValue - tmpQValue) <= 0.5) {
365 qValue -= tmpQValue;
366 m_bndCells[i].m_distances[dist] = qValue;
367 }
368 }
369 }
370 }
371
372 else if(m_interpolationDistMethod == "perpOp" || m_interpolationDistMethod == "cylinder") {
373 // TODO labels:LB This method differs from the 2D and all other 3D variants, such
374 // that all inOutSegments boundaries are completely set to m_isFluid = false.
375 // In the other cases this is calculated, too, which may lead to confusion!
376
377 // find ids of non-periodic and non-inOutSegments (until now this is wall)
378 std::vector<MInt> wallIds;
379 for(MInt i = 0; i < (MInt)(m_bndCndSegIds.size()); i++) {
380 MBool is_periodic = false;
381 if(m_noPeriodicSegments != 0)
382 for(MInt j = 0; j < m_noPeriodicSegments; j++)
383 if(m_bndCndSegIds[i] == m_periodicSegmentsIds[j]) {
384 is_periodic = true;
385 break;
386 }
387
388 MBool is_inout = false;
389 for(MInt j = 0; j < m_noInOutSegments; j++)
390 if(m_bndCndSegIds[i] == m_inOutSegmentsIds[j]) {
391 is_inout = true;
392 break;
393 }
394
395 if(!is_periodic & !is_inout) wallIds.push_back(i);
396 }
397
398 if(wallIds.size() > 0) {
399 std::unordered_set<MInt> reconsider;
400
401 for(auto wallId : wallIds) {
402 m_log << " - BC " << m_bndCndIds[wallId] << std::endl;
403 m_log << " * number or cells: " << (m_bndCndOffsets[wallId + 1] - m_bndCndOffsets[wallId]) << std::endl;
404 m_log << " * segment id: " << m_bndCndSegIds[wallId] << std::endl;
405
406 for(MInt i = m_bndCndOffsets[wallId]; i < m_bndCndOffsets[wallId + 1]; i++) {
407 const MInt currentId = m_bndCells[i].m_cellId;
408
409 //--Calculate distance for each distribution--------------------------
410 const MFloat cellHalfLength = m_solver->c_cellLengthAtLevel(m_solver->a_level(currentId) + 1);
411
412 MFloat d[nDim], e[nDim];
413 MFloat target[2 * nDim];
414 for(MInt j = 0; j < nDim; j++) {
415 target[j] = m_solver->a_coordinate(currentId, j) - cellHalfLength;
416 target[j + nDim] = m_solver->a_coordinate(currentId, j) + cellHalfLength;
417
418 // Take cell center as first point of discrete velocity trajectories
419 d[j] = m_solver->a_coordinate(currentId, j);
420 }
421
422 // get all triangles in currentCell (target)
423 std::vector<MInt> nodeList;
424 m_solver->m_geometry->getIntersectionElements(target, nodeList, cellHalfLength,
425 &m_solver->a_coordinate(currentId, 0));
426
427 // init distances
428 m_bndCells[i].m_distances.clear();
429 m_bndCells[i].m_distances.resize(IPOW3[nDim] - 1, 0.0);
430
431 // run over all distributions and calculate distances
432 for(MInt dist = 0; dist < IPOW3[nDim] - 1; dist++) {
433 const MFloat dx = F2 * SQRT[Ld::distType(dist)] * cellHalfLength;
434
435 // Set distance to a large value...
436 m_bndCells[i].m_distances[dist] = std::numeric_limits<MFloat>::max();
437
438 // Construct second point of discrete velocity trajectories
439 for(MInt j = 0; j < nDim; j++)
440 e[j] = d[j] + ((MFloat)Ld::idFld(dist, j) - 1.0) * cellHalfLength;
441
442 MFloat targetsmall[2 * nDim];
443 for(MInt k = 0; k < nDim; k++) {
444 targetsmall[k] = d[k];
445 targetsmall[k + nDim] = e[k];
446 }
447
448 MFloat min_dist;
449 MBool has_cut = m_solver->m_geometry->getClosestLineIntersectionLength(m_bndCndIds[wallId], nodeList,
450 targetsmall, &min_dist);
451 if(has_cut)
452 m_bndCells[i].m_distances[dist] =
453 min_dist / dx; // normalized wall distance: q = distance between cell center and wall / dx
454 else
455 m_bndCells[i].m_distances[dist] = 1.0;
456 }
457
458 //--Determine whether cell center is inside of fluid or not-----------
459 // In the following the state (fluid/solid) of each boundary cell is
460 // detected based on the state of the neighboring cells being located
461 // in direction of a cut.
462 m_bndCells[i].m_isFluid = false;
463 MBool hasBndryCutNghbr = false;
464 MBool hasNoCutNghbr = false;
465 MBool hasFluidCutNghbr = false;
466 for(MInt dist = 0; dist < IPOW3[nDim] - 1; dist++) {
467 if(m_bndCells[i].m_distances[dist] < 1.0) {
468 if(m_solver->a_hasNeighbor(currentId, dist)) {
469 const MInt nghId = m_solver->c_neighborId(currentId, dist);
470 if(m_solver->a_isBndryCell(nghId)) {
471 hasBndryCutNghbr = true;
472 } else {
473 // In case of any non-boundary cut neighbor, the state is
474 // directly definable (it is non-fluid) -> break
475 hasFluidCutNghbr = true;
476 break;
477 }
478 } else {
479 // Having no cut neighbor does not directly specify the state.
480 // In case of intersecting boundaries a neighbor can be missing
481 // for fluid as well as for non-fluid state.
482 hasNoCutNghbr = true;
483 }
484 }
485 }
486 if(hasFluidCutNghbr)
487 m_bndCells[i].m_isFluid = false;
488 else if(hasBndryCutNghbr)
489 reconsider.insert(i);
490 else if(hasNoCutNghbr)
491 m_bndCells[i].m_isFluid = true;
492 // else :
493 // In this case something must have gone wrong previously. A cut with
494 // an existing nghbr, which is neither a boundary nor simple fluid
495 // cells is new to my knowledge.
496 }
497 }
498 // Boundary cells that only have cut-neighbors which are boundary cells,
499 // too, need to be reconsidered. This might has to be done multiple times
500 // until the state of at least one neighbor is known for each reconsidered
501 // cell.
502 MUint reconsiderSize = 0;
503 while(reconsiderSize != reconsider.size()) {
504 reconsiderSize = reconsider.size();
505 m_log << " - number of cells to reconsider: " << reconsiderSize << std::endl;
506 auto r = reconsider.begin();
507 while(r != reconsider.end()) {
508 const MInt cellId = m_bndCells[*r].m_cellId;
509 MBool chk = false;
510 for(MInt dist = 0; dist < IPOW3[nDim] - 1; dist++) {
511 if(m_bndCells[*r].m_distances[dist] < 1.0 && m_solver->a_hasNeighbor(cellId, dist)) {
512 const MInt nghId = m_solver->c_neighborId(cellId, dist);
513 if(m_solver->a_isBndryCell(nghId)) {
514 const MInt bndIdNeigh = m_solver->a_bndId(nghId);
515 if(reconsider.find(bndIdNeigh) == reconsider.end()) {
516 // only one cut between cellId and nghId
517 if(approx(m_bndCells[bndIdNeigh].m_distances[Ld::oppositeDist(dist)], 1.0, MFloatEps)) {
518 m_bndCells[*r].m_isFluid = !m_bndCells[bndIdNeigh].m_isFluid; // cellId has opposite state of nghId
519 chk = true;
520 break;
521 }
522 }
523 }
524 }
525 }
526 if(chk)
527 reconsider.erase(r++);
528 else
529 r++;
530 }
531 }
532 }
533
534 // analytical correction of wall distance for unit cylinder aligned with origin z-axis
535 if(m_interpolationDistMethod == "cylinder") {
536 // get correct ID
537 const MInt cylinderBcId = Context::getSolverProperty<MInt>("cylinderBcId", m_solverId, AT_);
538 MInt cylinderId = -1;
539 for(MInt i = 0; i < (MInt)(m_bndCndIds.size()); i++) {
540 if(m_bndCndIds[i] == cylinderBcId) {
541 cylinderId = i;
542 break;
543 }
544 }
545 if(cylinderId != -1) {
546 const MFloat radius = 0.5;
547 const MFloat radiusSqr = radius * radius;
548
549 for(MInt i = m_bndCndOffsets[cylinderId]; i < m_bndCndOffsets[cylinderId + 1]; i++) {
550 const MInt currentId = m_bndCells[i].m_cellId;
551
552 const MFloat cellHalfLength = m_solver->c_cellLengthAtLevel(m_solver->a_level(currentId) + 1);
553
554 const MFloat cellRadiusSq = m_solver->a_coordinate(currentId, 0) * m_solver->a_coordinate(currentId, 0)
555 + m_solver->a_coordinate(currentId, 1) * m_solver->a_coordinate(currentId, 1);
556
557 //--Determine whether cell center is inside of fluid or not---------------
558 m_bndCells[i].m_isFluid = cellRadiusSq > radiusSqr;
559
560 //--Calculate distance for each distribution------------------------------
561 // init distances
562 m_bndCells[i].m_distances.clear();
563 m_bndCells[i].m_distances.resize(IPOW3[nDim] - 1, 0.0);
564 for(MInt dist = 0; dist < nDist - 1; dist++) {
565 MFloat dx;
566 if(dist < 6)
567 dx = F2 * cellHalfLength;
568 else if(dist < 18)
569 dx = SQRT2 * F2 * cellHalfLength;
570 else
571 dx = SQRT3 * F2 * cellHalfLength;
572 const MFloat dxSqr = dx * dx;
573
574 // Set distance to a large value...
575 m_bndCells[i].m_distances[dist] = std::numeric_limits<MFloat>::max();
576
577 // solve quadratic equation for q: R^2 = (x_cell + q*c_dist)^2
578 // if cell belongs to the wall q>1/2
579 // otherwise q<=1/2
580
581 MFloat qValue = 0.0; // normalized wall distance: q = distance between cell center and wall / dx
582 const MInt nDim_cyl = 2; // only consider x and y coordinates
583 for(MInt j = 0; j < nDim_cyl; j++) {
584 qValue -= m_solver->a_coordinate(currentId, j) * ((MFloat)Ld::idFld(dist, j) - 1.0) * 2.0 * cellHalfLength
585 / dxSqr;
586 }
587
588 const MFloat tmpQValue = sqrt(qValue * qValue - (cellRadiusSq - radiusSqr) / dxSqr);
589
590 if(qValue + tmpQValue >= 0.0 && fabs(qValue + tmpQValue) <= 0.5) {
591 m_bndCells[i].m_distances[dist] = qValue + tmpQValue;
592 } else if(qValue - tmpQValue >= 0.0 && fabs(qValue - tmpQValue) <= 0.5) {
593 m_bndCells[i].m_distances[dist] = qValue - tmpQValue;
594 }
595 }
596 }
597 }
598 }
599 }
600
601 // distance determination for x-oriented pipe
602 else if(m_interpolationDistMethod == "pipe") {
603 }
604
605 else if(m_interpolationDistMethod == "STD") {
606 //--I./II. find cut distance in bounding box of 2*dx---------------------------
607 constexpr MFloat relEps = 1e-16;
608 const MFloat eps = relEps * m_solver->c_cellLengthAtLevel(m_solver->maxLevel());
609 auto getInsideOutsideAndDistances = [&](const MInt i, const MInt index) {
610 auto& bndCell = m_bndCells[i];
611 const MInt currentId = bndCell.m_cellId;
612 //--I. in-/outside check--------------------------------------------------
613 bndCell.m_isFluid = !(m_solver->m_geometry->pointIsInside2(&m_solver->a_coordinate(currentId, 0)));
614 //--determine distances for each bndry cell's distribution--------------
615 // get all triangles that intersect currentId
616 const MFloat cellLength = m_solver->c_cellLengthAtLevel(m_solver->a_level(currentId));
617 // A fluid cell searches for the closest cut, whereas a solid one searches
618 // largest cut distance.
619 // This assures that in case of multiple cuts between solid-fluid bndCells
620 // both find the same cut closest to the fluid region. If q > 0.5 for the
621 // fluid cell and a nghbr solid cell is existing, the cut from the fluid
622 // is removed to avoid multiple execution.
623 // In case of multiple cuts between solid-solid bndCells (p.e.thin gap)
624 // this results in q > 0.5 for both cells.
625 const MFloat bbHalfLenght = cellLength;
626 MFloat target[2 * nDim]; // bounding box of the currentId
627 MFloat d[nDim]; // starting point of each ray
628 for(MInt j = 0; j < nDim; j++) {
629 target[j] = m_solver->a_coordinate(currentId, j) - bbHalfLenght;
630 target[j + nDim] = m_solver->a_coordinate(currentId, j) + bbHalfLenght;
631 d[j] = m_solver->a_coordinate(currentId, j);
632 }
633 std::vector<MInt> nodeList;
634 if(m_gridCutTest == "SAT")
635 m_solver->m_geometry->getIntersectionElements(target, nodeList, bbHalfLenght,
636 &m_solver->a_coordinate(currentId, 0));
637 else
638 m_solver->m_geometry->getIntersectionElements(target, nodeList);
639 // init distances
640 bndCell.m_distances.clear();
641 bndCell.m_distances.resize(IPOW3[nDim] - 1, 0.0);
642 // Loop over each distribution
643 for(MInt dist = 0; dist < nDist - 1; dist++) {
644 // smallest cut distance:
645 MFloat e[nDim]; // end point of current ray
646 for(MInt j = 0; j < nDim; j++) {
647 e[j] = d[j] + ((MFloat)Ld::idFld(dist, j) - 1.0) * bbHalfLenght;
648 }
649 MFloat trgDistance =
650 (bndCell.m_isFluid) ? std::numeric_limits<MFloat>::max() : std::numeric_limits<MFloat>::lowest();
651 MInt trgNodeId = -1;
652 for(MInt n = 0; n < (signed)nodeList.size(); n++) {
653 MFloat distance = 0.0;
654 MFloat** const v = m_solver->m_geometry->elements[nodeList[n]].m_vertices;
655 // TODO labels:LB D2Qx: use getLineTriangleIntersectionSimpleDistance
656 // alternativ that is working for 2D.
657 const MBool hasCut =
658 m_solver->m_geometry->getLineTriangleIntersectionSimpleDistance(d, e, v[0], v[1], v[2], &distance);
659 if(hasCut) {
660 distance = (distance < 0.0) ? 0.0 : distance;
661 // With the following it shall be assured, that the closest
662 // distance is found. Within a tolerance eps the bndry Id
663 // belonging to the currentId is preferred.
664 if(fabs(distance - trgDistance) < eps) {
665 if(m_solver->m_geometry->elements[nodeList[n]].m_bndCndId == m_bndCndIds[index]) {
666 trgDistance = distance;
667 trgNodeId = n;
668 }
669 } else if((bndCell.m_isFluid && distance < trgDistance) || // Choose smallest distance for fluid cell
670 (!bndCell.m_isFluid && distance > trgDistance) // Choose largest distance for solid cell
671 ) {
672 trgDistance = distance;
673 trgNodeId = n;
674 }
675 }
676 }
677 if(trgNodeId > -1 && m_solver->m_geometry->elements[nodeList[trgNodeId]].m_bndCndId == m_bndCndIds[index]) {
678 const MFloat dx = SQRT[Ld::distType(dist)] * cellLength;
679 bndCell.m_distances[dist] = trgDistance / dx;
680 if(m_calcSublayerDist) {
681 m_distIntersectionElementId[currentId][dist] = nodeList[trgNodeId];
682 }
683 } else {
684 bndCell.m_distances[dist] = std::numeric_limits<MFloat>::max();
685 }
686 }
687 // // << DBG
688 // std::stringstream ss;
689 // constexpr MInt dbgGlobalId = 132150;
690 // //constexpr MInt dbgGlobalId2 = 131454; // in dir 22
691 // constexpr MInt dbgGlobalId2 = 37446; // in dir 21
692 // if( m_solver->c_globalId(currentId) == dbgGlobalId ||
693 // m_solver->c_globalId(currentId) == dbgGlobalId2
694 // ) {
695 // MInt dbgId = currentId;
696 // ss << "DBG: " << m_solver->c_globalId(dbgId) << ", " << bndCell.m_isFluid << ", "
697 // << m_solver->a_isHalo(dbgId) << std::endl;
698 // ss << "DBG: nghbrStates:" << std::endl;
699 // for(MInt z = 0; z < nDist; z++) {
700 // ss << " " << z << " : ";
701 // ss << bndCell.m_distances[z] << ", ";
702 // const MBool chk = m_solver->a_hasNeighbor(dbgId, z);
703 // const MInt nghbrId = chk ? m_solver->c_neighborId(dbgId, z) : -1;
704 // ss << m_solver->c_globalId(nghbrId) << ", ";
705 // if(chk)
706 // ss << m_solver->a_isBndryCell(nghbrId) << ", " << m_solver->a_isHalo(nghbrId);
707 // else
708 // ss << "nan"
709 // << " nan";
710 // ss << std::endl;
711 // }
712 // std::cout << ss.str();
713 // }
714 // // DBG >>
715 };
716
717 for(MInt index = 0; index < (MInt)m_bndCndSegIds.size(); index++) {
718 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
719 getInsideOutsideAndDistances(i, index);
720 }
721 }
722 //--III. check for fluid split cells: appending new m_bndCells--------------
723 const MInt oldNumberOfBndCells = m_bndCells.size();
724 MBool needResorting = false;
725 for(MInt i = 0; i < oldNumberOfBndCells; i++) {
726 if(m_bndCells[i].m_isFluid) {
727 const MInt currentId = m_bndCells[i].m_cellId;
728 for(MInt j = 0; j < nDist - 1; j++) {
729 if(m_bndCells[i].m_distances[j] > 0.5) continue;
730 if(!m_solver->a_hasNeighbor(currentId, j)) continue;
731 const MInt nghbrId = m_solver->c_neighborId(currentId, j);
732 // TODO labels:LB miro: halo cell is inactive
733 if(m_solver->a_isBndryCell(nghbrId) || !m_solver->a_isActive(nghbrId) || m_solver->a_isHalo(nghbrId))
734 continue;
735 // add new boundary cell
736 needResorting = true;
737 m_bndCells.emplace_back();
738 auto& newBndCell = m_bndCells.back();
739 newBndCell.m_cellId = nghbrId;
740 newBndCell.m_isFluid = true;
741 newBndCell.m_segmentId.push_back(m_bndCells[i].m_segmentId[0]);
742 newBndCell.m_bndCndId.push_back(m_bndCells[i].m_bndCndId[0]);
743 m_solver->a_isBndryCell(nghbrId) = true;
744 // Determine in-/outside and cuts for new cells (All cuts will be
745 // q > 0.5, but at least some will be q < 1.0 )
746 const MInt index = this->m_mapBndCndSegId2Index[m_bndCells[i].m_segmentId[0]];
747 const MInt newBndCellId = m_bndCells.size() - 1;
748 getInsideOutsideAndDistances(newBndCellId, index);
749 // Remove cuts if neighbor is solid boundary cell to avoid double execution
750 for(MInt dist = 0; dist < nDist - 1; dist++) {
751 if(newBndCell.m_distances[dist] < 1.0) {
752 if(m_solver->a_hasNeighbor(nghbrId, dist)) {
753 const MInt nghbrId2 = m_solver->c_neighborId(nghbrId, dist);
754 if(m_solver->a_isBndryCell(nghbrId2)) {
755 const MInt nghbrBndId2 = m_solver->a_bndId(nghbrId2);
756 if(m_bndCells[nghbrBndId2].m_isFluid == false) {
757 newBndCell.m_distances[dist] = std::numeric_limits<MFloat>::max();
758 }
759 }
760 }
761 }
762 }
763 }
764 }
765 }
766 //--IV. sort boundary cells if split cells are found------------------------
767 if(needResorting) {
768 m_log << " ## redoing sortBoundaryCells" << std::endl;
769 this->sortBoundaryCells();
770 m_log << " redoing sortBoundaryCells ## " << std::endl;
771 }
772 //--V. Correct double execution by solid-fluid------------------------------
773 const MInt newNumberOfBndCells = m_bndCells.size();
774 maia::parallelFor(0, newNumberOfBndCells, [&](MInt i) {
775 auto& bndCell = m_bndCells[i];
776 const MInt currentId = bndCell.m_cellId;
777 if(bndCell.m_isFluid) { // fluid
778 for(MInt j = 0; j < nDist - 1; j++) {
779 const MInt opp = Ld::oppositeDist(j);
780 const MBool cutj = bndCell.m_distances[j] < 1.0;
781 const MBool cutopp = bndCell.m_distances[opp] < 1.0;
782 // j
783 if(cutj) {
784 // perform simple bounce back if cut in opposite directions
785 if(bndCell.m_distances[j] <= 0.5) {
786 if(cutopp) bndCell.m_distances[j] = 0.5;
787 }
788 // remove cut, if solid neighbor to avoid multiple execution
789 else { // 0.5 < q < 1.0
790 if(m_solver->a_hasNeighbor(currentId, j)) {
791 const MInt nghbrId = m_solver->c_neighborId(currentId, j);
792 if(m_solver->a_isBndryCell(nghbrId)) {
793 if(m_bndCells[m_solver->a_bndId(nghbrId)].m_isFluid == false) {
794 bndCell.m_distances[j] = std::numeric_limits<MFloat>::max();
795 }
796 }
797 }
798 }
799 }
800 // opp
801 if(cutopp) {
802 // perform simple bounce back if cut in opposite directions
803 if(bndCell.m_distances[opp] <= 0.5) {
804 if(cutj) bndCell.m_distances[opp] = 0.5;
805 }
806 // remove cut, if solid neighbor to avoid multiple execution
807 else { // 0.5 < q < 1.0
808 if(m_solver->a_hasNeighbor(currentId, opp)) {
809 const MInt nghbrId = m_solver->c_neighborId(currentId, opp);
810 if(m_solver->a_isBndryCell(nghbrId)) {
811 if(m_bndCells[m_solver->a_bndId(nghbrId)].m_isFluid == false) {
812 bndCell.m_distances[opp] = std::numeric_limits<MFloat>::max();
813 }
814 }
815 }
816 }
817 }
818 }
819 } else { // solid
820 // remove cuts in direction of other solid boundary cell
821 for(MInt j = 0; j < nDist - 1; j++) {
822 if(bndCell.m_distances[j] < 0.5) {
823 if(m_solver->a_hasNeighbor(currentId, j)) {
824 const MInt nghbrId = m_solver->c_neighborId(currentId, j);
825 if(m_solver->a_isBndryCell(nghbrId)) {
826 if(m_bndCells[m_solver->a_bndId(nghbrId)].m_isFluid == false) {
827 bndCell.m_distances[j] = std::numeric_limits<MFloat>::max();
828 }
829 }
830 }
831 }
832 }
833 }
834 });
835 }
836 // Dump out distance field and inside outside state
837 if(m_outputWallDistanceField) {
838 std::stringstream fileName;
839 fileName << m_solver->restartDir() << "lbDistanceField" << m_solver->getIdentifier() << ParallelIo::fileExt();
840 ParallelIo parallelIo(fileName.str(), maia::parallel_io::PIO_REPLACE, m_solver->mpiComm());
841 //--define global attributes
842 parallelIo.setAttribute(m_solver->solverId(), "solverId");
843 parallelIo.setAttribute(m_solver->gridInputFileName(), "gridFile", "");
844 //--define arrays
845 const MPI_Offset firstGlobalId = m_solver->domainOffset(m_solver->domainId());
846 const MPI_Offset localNoCells = m_solver->noInternalCells();
847 parallelIo.setOffset(localNoCells, firstGlobalId);
848 addWallDistanceFieldToOutputFile(parallelIo, true, false);
849 addWallDistanceFieldToOutputFile(parallelIo, false, true);
850 m_outputWallDistanceField = false;
851 }
852}
853
854// TODO labels:LB D2Qx: Make this unified with 3D
855// TODO labels:LB Does not need SysEqn, in fact. Is resolved as soon as 2D 3D are unified.
856template <MInt nDim_, MInt nDist_, class SysEqn>
858 TRACE();
859
860 constexpr MInt nDim = 2;
861 constexpr MInt nDist = 9;
862 // init the volumes
863 for(MInt i = 0; i < (MInt)m_bndCells.size(); i++) {
864 m_bndCells[i].m_isFluid = false;
865 }
866
867 if(m_interpolationDistMethod == "perpOp") {
868 MBool triangleIntersection;
869 MFloat target[6] = {0, 0, 0, 2, 2, 4};
870 MFloat cellHalfLength = 0.0;
871 MFloat currentDistance = 0;
872 // MFloat dxB2;
873
874 MFloat a[2]; // point in plane
875 MFloat b[2]; // point in plane
876 MFloat c[2]; // start of piercing edge
877 MFloat d[2]; // end of piercing edge
878 MFloat s2, gamma; // s1; For pierce point calculation
879 // MFloat pP[2]; uncomment for piercePoint calculation
880
881 // find ids of non-periodic and non-inOutSegments (until now this is wall)
882 std::vector<MInt> wallIds;
883 for(MInt i = 0; i < (MInt)(m_bndCndSegIds.size()); i++) {
884 MBool is_periodic = false;
885 if(m_noPeriodicSegments != 0)
886 for(MInt j = 0; j < m_noPeriodicSegments; j++)
887 if(m_bndCndSegIds[i] == m_periodicSegmentsIds[j]) {
888 is_periodic = true;
889 break;
890 }
891
892 MBool is_inout = false;
893 for(MInt j = 0; j < m_noInOutSegments; j++)
894 if(m_bndCndSegIds[i] == m_inOutSegmentsIds[j]) {
895 is_inout = true;
896 break;
897 }
898
899 if(!is_periodic & !is_inout) wallIds.push_back(i);
900 }
901
902 for(auto wallId : wallIds) {
903 for(MInt i = m_bndCndOffsets[wallId]; i < m_bndCndOffsets[wallId + 1]; i++) {
904 const MInt currentId = m_bndCells[i].m_cellId;
905
906 //--Determine whether cell center is inside of fluid or not-------------
907 // TODO labels:LB,toenhance dxqy: this does not conform with 3D yet ! Should be adapted
908 if(m_solver->m_geometry->pointIsInside(&m_solver->a_coordinate(currentId, 0))) {
909 // m_log << " BndCell " << i << " [" << currentId << "] is inside geometry. " << std::endl;
910 m_bndCells[i].m_isFluid = false;
911 } else {
912 // m_log << " BndCell " << i << " [" << currentId << "] is outside geometry. " << std::endl;
913 m_bndCells[i].m_isFluid = true;
914 }
915
916 //--Calculate distance for each distribution---------------------------
917 // init distances
918 m_bndCells[i].m_distances.clear();
919 m_bndCells[i].m_distances.resize(IPOW3[nDim] - 1, 0.0);
920
921 // Define corners of current cell in target
922 for(MInt j = 0; j < nDim; j++) {
923 cellHalfLength = m_solver->c_cellLengthAtLevel(m_solver->a_level(currentId) + 1);
924 target[j] = m_solver->a_coordinate(currentId, j) - cellHalfLength;
925 target[j + nDim] = m_solver->a_coordinate(currentId, j) + cellHalfLength;
926
927 // Take cell center as first point of discrete velocity trajectories
928 c[j] = m_solver->a_coordinate(currentId, j);
929 }
930
931 // get all triangles in currentCell (target)
932 std::vector<MInt> nodeList;
933 if(m_gridCutTest == "SAT")
934 m_solver->m_geometry->getIntersectionElements(target, nodeList, cellHalfLength,
935 &m_solver->a_coordinate(currentId, 0));
936 else
937 m_solver->m_geometry->getIntersectionElements(target, nodeList);
938
939 triangleIntersection = false;
940 for(MInt dist = 0; dist < 8; dist++) {
941 // Set distance to a large value...
942 m_bndCells[i].m_distances[dist] = m_solver->c_cellLengthAtLevel(0);
943 // if(dist < 4)
944 // dxB2 = cellHalfLength;
945 // else
946 // dxB2 = SQRT2 * cellHalfLength;
947
948 // Construct second point of discrete velocity trajectories
949 for(MInt j = 0; j < nDim; j++) {
950 d[j] = c[j] + ((MFloat)Ld::idFld(dist, j) - 1.0) * cellHalfLength;
951 }
952 currentDistance = 1;
953 m_bndCells[i].m_distances[dist] = currentDistance;
954 // Check for intersection with discrete velocity trajectories
955 for(MInt n = 0; n < (signed)nodeList.size(); n++) {
956 if(m_solver->m_geometry->elements[nodeList[n]].m_bndCndId != m_bndCndIds[wallId]) continue;
957 if(m_solver->m_geometry->edgeTriangleIntersection(m_solver->m_geometry->elements[nodeList[n]].m_vertices[0],
958 m_solver->m_geometry->elements[nodeList[n]].m_vertices[1],
959 0, c, d)) {
960 triangleIntersection = true;
961 // Calculate Distance
962 for(MInt k = 0; k < nDim; k++) {
963 a[k] = m_solver->m_geometry->elements[nodeList[n]].m_vertices[0][k];
964 b[k] = m_solver->m_geometry->elements[nodeList[n]].m_vertices[1][k];
965 // d and e are already set
966 }
967 gamma = (b[0] - a[0]) * (c[1] - d[1]) - (c[0] - d[0]) * (b[1] - a[1]);
968
969 // s1 = ((c[0]-d[0]) * (a[1]-c[1]) - (c[1]-d[1])*(a[0]-c[0])) / gamma;
970
971
972 s2 = ((b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1])) / gamma;
973
974 // 1. b Pierce point pP in plane j:
975 /*for (MInt k = 0; k < nDim; k++){
976 if(s1*s1 < s2*s2)
977 pP[k] = c[k] + s2 * ( d[k] - c[k] );
978 else
979 pP[k] = a[k] + s1 * ( b[k] - a[k] );
980 }*/
981 // Take only the magnitude !
982 if(s2 < 0.0) s2 = -s2;
983
984 if(s2 > 1.0) continue;
985
986 currentDistance = s2 * F1B2;
987 // Take shortest distance
988 m_bndCells[i].m_distances[dist] = (currentDistance < m_bndCells[i].m_distances[dist])
989 ? currentDistance
990 : m_bndCells[i].m_distances[dist];
991
992 // see pierce point calculation in geometry.cpp
993 }
994 }
995 }
996 if(!triangleIntersection) {
997 m_log << " BndCell[" << i << "] has no triangle intersection!" << std::endl;
998 }
999 }
1000 }
1001 }
1002
1003 else if(m_interpolationDistMethod == "circle") {
1004 MFloat radius = 0.5;
1005 MFloat x = 0.0;
1006 MFloat y = 0.0;
1007 std::cout << "Prescribe analytic circle with radius: " << radius << ", x:" << x << ", y:" << y << std::endl;
1008
1009 MFloat cellHalfLength, cellRadiusSq, dxB2, dxSq;
1010 MInt currentId;
1011
1012 // Loop over m_bndCells
1013 for(MInt i = 0; i < (MInt)m_bndCells.size(); i++) {
1014 currentId = m_bndCells[i].m_cellId;
1015 cellHalfLength = m_solver->grid().cellLengthAtLevel(m_solver->a_level(currentId) + 1);
1016 cellRadiusSq = (m_solver->a_coordinate(currentId, 0) - x) * (m_solver->a_coordinate(currentId, 0) - x)
1017 + (m_solver->a_coordinate(currentId, 1) - y) * (m_solver->a_coordinate(currentId, 1) - y);
1018
1019 // Mark cells as in- or outside the wall.
1020 if(cellRadiusSq <= radius * radius) {
1021 m_bndCells[i].m_isFluid = false; // cell is not inside fluid
1022 } else {
1023 m_bndCells[i].m_isFluid = true; // cell belongs to fluid
1024 }
1025
1026 // Prescribe distance to wall for every PPDF
1027 m_bndCells[i].m_distances.clear();
1028 m_bndCells[i].m_distances.resize(IPOW3[nDim] - 1, 0.0);
1029 for(MInt dist = 0; dist < nDist - 1; dist++) {
1030 // Set distance to a large valule
1031 m_bndCells[i].m_distances[dist] = m_solver->grid().cellLengthAtLevel(0);
1032
1033 // Compute squared length and length for each direction
1034 if(dist < 4) {
1035 dxSq = 4.0 * cellHalfLength * cellHalfLength;
1036 dxB2 = cellHalfLength;
1037 } else {
1038 dxSq = 2.0 * 2.0 * cellHalfLength * cellHalfLength; // Diagonal directions
1039 dxB2 = SQRT2 * cellHalfLength;
1040 }
1041
1042 // Intersection circle directions delivers quadratic equation:
1043 // d: lenght cell center -- cut
1044 // a: x-component of d
1045 // b: y-component of d
1046 // cx: x-coord of cell center
1047 // cy: y-coord of cell center
1048 // rc: radius of cell center
1049 // 3 cases: 1) diagonal: a=b 2) horizontal a=d,b=0 3) vertikal: b=d,a=0
1050 // E.g. for diagonal case
1051 // d = 1/sqrt(2)(cx+cy)+-sqrt((1/sqrt(2)(cx+cy))^2-rc^2+r^2)
1052 // with p1 = 1/sqrt(2)(cx+cy) this can be generalized for all 3 cases
1053 // d = p1+-sqrt(p1^2+r^2-rc^2) = p1+-p2 e.g. p=cx for dir 0.
1054 // Finally, d is computed normalized as q=d/cellLength_of_direction
1055 MFloat p1 = 0;
1056 MFloat p2 = 0;
1057 for(MInt j = 0; j < nDim; j++) {
1058 p1 -= m_solver->a_coordinate(currentId, j) * (Ld::idFld(dist, j) - 1.0) * 2.0 * cellHalfLength / dxSq;
1059 }
1060 p2 = sqrt(p1 * p1 + (radius * radius - cellRadiusSq) / dxSq);
1061
1062 // Quadratic equation has two solutions. Either q>1/2 or q<=1/2 is correct.
1063 if(p1 + p2 >= 0.0 && fabs(p1 + p2) <= 0.5) {
1064 // In d2q9, the not normalized wall distance is stored
1065 m_bndCells[i].m_distances[dist] = (p1 + p2) * 2.0 * dxB2;
1066 }
1067 if(p1 - p2 >= 0.0 && fabs(p1 - p2) <= 0.5) {
1068 m_bndCells[i].m_distances[dist] = (p1 - p2) * 2.0 * dxB2;
1069 }
1070 }
1071 }
1072 }
1073
1074 else {
1075 std::stringstream ss;
1076 ss << "ERROR: Unknown interpolationDistMethod: " << m_interpolationDistMethod << std::endl;
1077 m_log << ss.str();
1078 TERMM(1, ss.str());
1079 }
1080}
1081
1082template <>
1083inline void LbBndCndDxQy<2, 9, maia::lb::LbSysEqnCompressible<2, 9>>::calculateWallDistances() {
1084 calculateWallDistances2D();
1085}
1086
1087template <>
1089 calculateWallDistances2D();
1090}
1091
1092template <MInt nDim, MInt nDist, class SysEqn>
1094 const MBool writeHeader,
1095 const MBool writeData) {
1096 // total #of cells in the grid == global cell index of the last cell in the last process:
1097 const MInt totalNoCells = m_solver->domainOffset(m_solver->noDomains());
1098 const MInt internalCells = m_solver->noInternalCells();
1099 if(writeHeader) {
1100 // write header
1101 {
1102 const MString varName = "isFluid";
1103 parallelIo.defineArray(maia::parallel_io::PIO_INT, varName, totalNoCells);
1104 parallelIo.setAttribute(varName, "name", varName);
1105 }
1106 for(MInt i = 0; i < nDist - 1; i++) {
1107 const MString varName = "distance_" + std::to_string(i);
1108 parallelIo.defineArray(maia::parallel_io::PIO_FLOAT, varName, totalNoCells);
1109 parallelIo.setAttribute(varName, "name", varName);
1110 }
1111 }
1112 if(writeData) {
1113 { // write isFluid data
1114 MIntScratchSpace tmp(internalCells, AT_, "tmp");
1115 const MString varName = "isFluid";
1116 for(MInt i = 0; i < internalCells; i++) {
1117 tmp[i] = -1;
1118 }
1119 for(auto& bndCell : m_bndCells) {
1120 if(m_solver->a_isHalo(bndCell.m_cellId)) continue;
1121 tmp[bndCell.m_cellId] = (bndCell.m_isFluid) ? 1 : 0;
1122 }
1123 parallelIo.writeArray(tmp.getPointer(), varName);
1124 }
1125 MFloatScratchSpace tmp(internalCells, AT_, "tmp");
1126 for(MInt j = 0; j < nDist - 1; j++) {
1127 const MString varName = "distance_" + std::to_string(j);
1128 for(MInt i = 0; i < internalCells; i++) {
1129 tmp[i] = -1.0;
1130 }
1131 for(auto& bndCell : m_bndCells) {
1132 if(m_solver->a_isHalo(bndCell.m_cellId)) continue;
1133 tmp[bndCell.m_cellId] = (bndCell.m_distances[j] < 2.0) ? bndCell.m_distances[j] : -1.0; // set -1.0 for no cut
1134 }
1135 parallelIo.writeArray(tmp.getPointer(), varName);
1136 }
1137 }
1138}
1139
1147template <MInt nDim, MInt nDist, class SysEqn>
1149 TRACE();
1150
1151 m_log << " + Generating parabolic inflow..." << std::endl;
1152
1153 MInt noInflowSegments = 0;
1154 MInt* inflowSegmentIds = nullptr;
1155
1162 if(Context::propertyExists("inflowSegmentIds", m_solverId)) {
1163 noInflowSegments = Context::propertyLength("inflowSegmentIds", m_solverId);
1164 mAlloc(inflowSegmentIds, noInflowSegments, "inflowSegmentIds", AT_);
1165 for(MInt i = 0; i < noInflowSegments; i++) {
1166 inflowSegmentIds[i] = Context::getSolverProperty<MInt>("inflowSegmentIds", m_solverId, AT_, i);
1167 }
1168 } else {
1169 m_log << " - none found" << std::endl << std::endl;
1170 return;
1171 }
1172
1173 MInt num, currentId, nearestNode, neighborNode0, neighborNode1;
1174 MFloat v1[nDim] = {0.0};
1175 MFloat v2[nDim] = {0.0};
1176 MFloat v3[nDim] = {0.0};
1177 MFloat dmin, dtemp, maxRadius;
1178
1179 MFloat v1_length, v2_length, v3_length, angle0, angle1, proj0, proj1;
1180
1181
1182 // loop over all Segments
1183 for(MInt i = 0; i < noInflowSegments; i++) {
1184 MInt own = m_solver->m_geometry->m_ownSegmentId[inflowSegmentIds[i]];
1185 MFloat** a = nullptr;
1186
1187 if(m_solver->m_geometry->m_parallelGeometry && nDim == 3)
1188 // in this case all need to participate
1189 a = allOwnersGetBoundaryVertices(inflowSegmentIds[i], &num);
1190 else if(own)
1191 a = m_solver->m_geometry->GetBoundaryVertices(inflowSegmentIds[i], nullptr, nullptr, 0, &num);
1192
1193 // do the following only if this segment Id exists in this domain!
1194 if(own) {
1195 MInt id = -1;
1196
1197 for(MInt j = 0; j < (MInt)(m_bndCndSegIds.size()); j++)
1198 if(m_bndCndSegIds[j] == inflowSegmentIds[i]) id = j;
1199
1200 if(id == -1) continue;
1201 m_log << " - segment " << inflowSegmentIds[i] << std::endl;
1202
1203 // for debugging
1204 // writeBoundaryVTK(a,num,inflowSegmentIds[i]);
1205
1206 maxRadius = m_solver->m_geometry->getBndMaxRadius(a, num);
1207 m_log << " * max. radius: " << maxRadius << std::endl;
1208
1209 // loop over all m_bndCells in Segment
1210 for(MInt j = m_bndCndOffsets[id]; j < m_bndCndOffsets[id + 1]; j++) {
1211 currentId = m_bndCells[j].m_cellId;
1212
1213 nearestNode = 0;
1214 dmin = 0;
1215 for(MInt k = 0; k < nDim; k++) {
1216 dmin += (m_solver->a_coordinate(currentId, k) - a[0][k]) * (m_solver->a_coordinate(currentId, k) - a[0][k]);
1217 }
1218
1219 // run over all rim-nodes and look for nearest node
1220 for(MInt k = 1; k < num; k++) {
1221 dtemp = 0;
1222 for(MInt l = 0; l < nDim; l++) {
1223 dtemp +=
1224 (m_solver->a_coordinate(currentId, l) - a[k][l]) * (m_solver->a_coordinate(currentId, l) - a[k][l]);
1225 }
1226 if(dtemp < dmin) {
1227 dmin = dtemp;
1228 nearestNode = k;
1229 }
1230 }
1231
1232 // vector from nearest node to cell
1233 v1_length = 0;
1234 for(MInt k = 0; k < nDim; k++) {
1235 v1[k] = m_solver->a_coordinate(currentId, k) - a[nearestNode][k];
1236 v1_length += v1[k] * v1[k];
1237 }
1238 v1_length = sqrt(v1_length);
1239
1240 dmin = v1_length;
1241
1242
1243 // vector from nearest node to neighbor node
1244 neighborNode0 = (nearestNode + 1) % num;
1245
1246 if(nearestNode == 0)
1247 neighborNode1 = num - 1;
1248 else
1249 neighborNode1 = nearestNode - 1;
1250
1251 v2_length = 0;
1252 v3_length = 0;
1253 for(MInt k = 0; k < nDim; k++) {
1254 v2[k] = a[neighborNode0][k] - a[nearestNode][k];
1255 v3[k] = a[neighborNode1][k] - a[nearestNode][k];
1256 v2_length += v2[k] * v2[k];
1257 v3_length += v3[k] * v3[k];
1258 }
1259 v2_length = sqrt(v2_length);
1260 v3_length = sqrt(v3_length);
1261
1262 // check if projections are both negative, then we are done
1263 proj0 = 0.0;
1264 proj1 = 0.0;
1265 for(MInt d = 0; d < nDim; d++) {
1266 proj0 += (v1[d] * v2[d]);
1267 proj1 += (v1[d] * v3[d]);
1268 }
1269
1270 if(proj0 <= 0 && proj1 <= 0)
1271 dmin = v1_length;
1272 else {
1273 // Get angles:
1274 angle0 = acos(proj0 / (v1_length * v2_length));
1275 angle1 = acos(proj1 / (v1_length * v3_length));
1276
1277 if(angle0 <= angle1)
1278 dmin = sin(angle0) * v1_length;
1279 else
1280 dmin = sin(angle1) * v1_length;
1281 }
1282
1283 m_bndCells[j].m_multiplier = dmin;
1284 }
1285
1286 switch(index) {
1287 case 1: // poiseuille profile for pipe flow
1288
1289 m_log << " * poiseuille inflow: |v|=Ma*c and v ~ r^2" << std::endl;
1290
1291 // normalize distance and calculate parabolic profile
1292 for(MInt j = m_bndCndOffsets[id]; j < m_bndCndOffsets[id + 1]; j++) {
1293 m_bndCells[j].m_multiplier = m_bndCells[j].m_multiplier / maxRadius;
1294 m_bndCells[j].m_multiplier = 1.0 - m_bndCells[j].m_multiplier;
1295 m_bndCells[j].m_multiplier = 2.0 * (1.0 - (m_bndCells[j].m_multiplier * m_bndCells[j].m_multiplier));
1296 }
1297 break;
1298
1299 case 2: // r^4 profile (broader than poiseuille)
1300
1301 m_log << " * poiseille inflow (broader): |v|=Ma*c and v ~ r^4" << std::endl;
1302
1303 // normalize distance and calculate parabolic profile
1304 for(MInt j = m_bndCndOffsets[id]; j < m_bndCndOffsets[id + 1]; j++) {
1305 m_bndCells[j].m_multiplier = m_bndCells[j].m_multiplier / maxRadius;
1306 m_bndCells[j].m_multiplier = 1.0 - m_bndCells[j].m_multiplier;
1307 m_bndCells[j].m_multiplier = 1.5
1308 * (1.0
1309 - (m_bndCells[j].m_multiplier * m_bndCells[j].m_multiplier
1310 * m_bndCells[j].m_multiplier * m_bndCells[j].m_multiplier));
1311 }
1312 break;
1313
1314 default:
1315 m_log << "lbbndcnddxqy::parabolicInflow() unknown index for parabolic inflow " << std::endl;
1316 std::stringstream errorMessage;
1317 errorMessage << "lbbndcnddxqy::parabolicInflow() unknown index for parabolic inflow";
1318 DEBUG("lbbndcnddxqy::parabolicInflow() unknown index for parabolic inflow" << std::endl,
1319 MAIA_DEBUG_ASSERTION);
1320 TERMM(1, errorMessage.str());
1321 }
1322 }
1323 }
1324 mDeallocate(inflowSegmentIds);
1325}
1326
1335template <MInt nDim, MInt nDist, class SysEqn>
1337 TRACE();
1338
1339 MInt own;
1340 MInt sumowners;
1341 MInt firstOwner;
1342 MIntScratchSpace owners(m_solver->noDomains(), AT_, "owners");
1343
1344 m_log << " * segment owned by: ";
1345 m_solver->m_geometry->determineSegmentOwnership(segmentId, &own, &sumowners, &firstOwner, owners.getPointer());
1346 for(MInt d = 0; d < m_solver->noDomains(); d++)
1347 if(owners[d] > 0) m_log << d << " ";
1348
1349 m_log << std::endl;
1350 m_log << " * sum of owners: " << sumowners << std::endl;
1351 m_log << " * root of communication: " << firstOwner << std::endl;
1352
1353
1354 // my domain owns the whole segment
1355 if(sumowners == 1)
1356 return m_solver->m_geometry->GetBoundaryVertices(segmentId, nullptr, nullptr, 0, num);
1357 else {
1358 // build communicator
1359 MPI_Comm charComm;
1360 m_solver->createMPIComm(owners.getPointer(), sumowners, &charComm);
1361
1362 if(own) {
1363 // collect the triangles for testing first
1364
1365 MInt offStart = 0;
1366 MInt offEnd = 0;
1367
1368 if(m_solver->m_geometry->m_parallelGeometry) {
1369 offStart = m_solver->m_geometry->m_segmentOffsets[segmentId];
1370 offEnd = m_solver->m_geometry->m_segmentOffsets[segmentId + 1];
1371 } else {
1372 offStart = m_solver->m_geometry->m_segmentOffsetsWithoutMB[segmentId];
1373 offEnd = m_solver->m_geometry->m_segmentOffsetsWithoutMB[segmentId + 1];
1374 }
1375 MInt numElements = offEnd - offStart;
1376
1377 m_log << " * number of local triangles: " << numElements << std::endl;
1378 m_log << " * segment offsets: " << offStart << " - " << offEnd << std::endl;
1379
1380 // (normals + vertices)
1381 MInt noTriInfo = nDim * nDim;
1382 MIntScratchSpace myOriginalIds(numElements, AT_, "myOriginalIds");
1383 MFloatScratchSpace segTriangles(noTriInfo * numElements, AT_, "segTriangles");
1384
1385 for(MInt t = offStart, i = 0, j = 0; t < offEnd; t++, i++) {
1386 myOriginalIds[i] = m_solver->m_geometry->elements[t].m_originalId;
1387 for(MInt v = 0; v < nDim; v++)
1388 for(MInt d = 0; d < nDim; d++, j++)
1389 segTriangles[j] = m_solver->m_geometry->elements[t].m_vertices[v][d];
1390 }
1391
1392 MIntScratchSpace numElemPerCPU(sumowners, AT_, "numElemPerCPU");
1393 MPI_Allgather(&numElements, 1, MPI_INT, numElemPerCPU.getPointer(), 1, MPI_INT, charComm, AT_, "numElements",
1394 "numElemPerCPU.getPointer()");
1395 MIntScratchSpace numTriInfoPerCPU(sumowners, AT_, "numTriInfoPerCPU");
1396
1397 m_log << " * triangles per domain: ";
1398 MInt sumallelem = 0;
1399 for(MInt i = 0; i < sumowners; i++) {
1400 m_log << numElemPerCPU[i] << " ";
1401 sumallelem += numElemPerCPU[i];
1402 numTriInfoPerCPU[i] = numElemPerCPU[i] * noTriInfo;
1403 }
1404 m_log << std::endl;
1405 m_log << " * sum of global triangles: " << sumallelem << std::endl;
1406
1407 MIntScratchSpace displOrig(sumowners, AT_, "displOrig");
1408 MIntScratchSpace displTris(sumowners, AT_, "displTris");
1409 displOrig[0] = 0;
1410 displTris[0] = 0;
1411 for(MInt d = 1; d < sumowners; d++) {
1412 displOrig[d] = displOrig[d - 1] + numElemPerCPU[d - 1];
1413 displTris[d] = displTris[d - 1] + numTriInfoPerCPU[d - 1];
1414 }
1415
1416 MIntScratchSpace allOriginalIds(sumallelem, AT_, "allOriginalIds");
1417 MPI_Allgatherv(myOriginalIds.getPointer(), numElements, MPI_INT, allOriginalIds.getPointer(),
1418 numElemPerCPU.getPointer(), displOrig.getPointer(), MPI_INT, charComm, AT_,
1419 "myOriginalIds.getPointer()", "allOriginalIds.getPointer()");
1420
1421 MFloatScratchSpace allSegTriangles(noTriInfo * sumallelem, AT_, "allSegTriangles");
1422 MPI_Allgatherv(segTriangles.getPointer(), noTriInfo * numElements, MPI_DOUBLE, allSegTriangles.getPointer(),
1423 numTriInfoPerCPU.getPointer(), displTris.getPointer(), MPI_DOUBLE, charComm, AT_,
1424 "segTriangles.getPointer()", "allSegTriangles.getPointer()");
1425
1426 std::set<MInt> uniqueTriangles;
1427 for(MInt i = 0; i < sumallelem; i++)
1428 uniqueTriangles.insert(allOriginalIds[i]);
1429
1430 MInt noUniqueTris = uniqueTriangles.size();
1431 m_log << " * sum of unique triangles: " << noUniqueTris << std::endl;
1432
1433 MIntScratchSpace dbl(noUniqueTris, AT_, "dbl");
1434 for(MInt i = 0; i < noUniqueTris; i++)
1435 dbl[i] = 0;
1436
1437 // this contains the offsets in the list of triangles which we want to use for the calculation
1438 MIntScratchSpace keepOffsets(noUniqueTris, AT_, "keepOffsets");
1439 for(MInt i = 0, j = 0; i < sumallelem; i++) {
1440 MInt dist = distance(uniqueTriangles.begin(), uniqueTriangles.find(allOriginalIds[i]));
1441 if(dist != noUniqueTris && dbl[dist] == 0) {
1442 keepOffsets[j] = i * noTriInfo;
1443 dbl[dist] = 1;
1444 j++;
1445 }
1446 }
1447
1448 return m_solver->m_geometry->GetBoundaryVertices(segmentId, allSegTriangles.getPointer(),
1449 keepOffsets.getPointer(), noUniqueTris, num);
1450 } else
1451 return nullptr;
1452 }
1453}
1454
1467template <MInt nDim, MInt nDist, class SysEqn>
1469 TRACE();
1470
1471 const MInt pCellId = m_solver->m_G0CellList[cellIndex];
1472 MFloat rho = m_solver->a_variable(pCellId, PV->RHO);
1473 std::array<MFloat, nDim> uW{};
1474 getBoundaryVelocityMb(cellIndex, uW.data());
1475
1476 // case 1: boundary cell is inside fluid
1477 if(m_solver->a_levelSetFunctionMB(pCellId, set) > 0) {
1478 for(MInt j = 0; j < nDist - 1; j++) {
1479#ifdef WAR_NVHPC_PSTL
1480 const MFloat q = m_distances[cellIndex][j];
1481 const MInt opposite = m_solver->m_oppositeDist[j];
1482#else
1483 const MFloat q = getDistanceMb(pCellId, cellIndex, j);
1484 const MInt opposite = Ld::oppositeDist(j);
1485#endif
1486
1487 if(m_solver->a_hasProperty(pCellId, Cell::IsHalo)) continue;
1488
1489 // if q <= 0.5, perform bounce back
1490 if(q <= 0.5) {
1491 if(m_solver->a_hasNeighbor(pCellId, opposite) != 0) {
1492 const MFloat F2q = F2 * q;
1493 m_solver->a_oldDistribution(pCellId, opposite) = (F2q * m_solver->a_distribution(pCellId, j))
1494 + ((F1 - F2q) * m_solver->a_oldDistribution(pCellId, j))
1495 + firstMomentSourceTerm(uW.data(), rho, opposite);
1496#ifndef WAR_NVHPC_PSTL
1497 incrementForces(pCellId, cellIndex, j, uW.data());
1498#endif
1499 }
1500 // this is the case in which we have no neighbor in the direction we want to set (strange case)
1501 // can in my opinion only appear if neighbors were set wrong or at an interface
1502 else {
1503 m_solver->a_oldDistribution(pCellId, opposite) =
1504 m_solver->a_distribution(pCellId, j) + firstMomentSourceTerm(uW.data(), rho, opposite);
1505 }
1506 }
1507
1508 // this is the case in which we do not have a neighbor and no cut, do simple bounce back (strange case)
1509 // can in my opinoin only appear if something has gone wrong either with the grid or with the distance calculation
1510 // else if(m_bndCells[cellId].m_distances[j] > 0.5 && m_solver->a_hasNeighbor(pCellId, j) == 0)
1511 // else if( m_wallBoundaryCellList[cellId].m_distances[j] > 0.5 && m_solver->a_hasNeighbor(pCellId, j) == 0)
1512 else if(q > 0.5 && m_solver->a_hasNeighbor(pCellId, j) == 0) {
1513 m_solver->a_oldDistribution(pCellId, opposite) =
1514 m_solver->a_distribution(pCellId, j) + firstMomentSourceTerm(uW.data(), rho, opposite);
1515 }
1516 }
1517 }
1518 // case 2: boundary cell is outside fluid
1519 else {
1520 for(MInt j = 0; j < nDist - 1; j++) {
1521#ifdef WAR_NVHPC_PSTL
1522 const MFloat q = m_distances[cellIndex][j];
1523 const MInt opposite = m_solver->m_oppositeDist[j];
1524#else
1525 const MFloat q = getDistanceMb(pCellId, cellIndex, j);
1526 const MInt opposite = Ld::oppositeDist(j);
1527#endif
1528 // do we have a neighbor at all?
1529 if(m_solver->a_hasNeighbor(pCellId, j)) {
1530 // make sure that the neighbor is not a halo cell
1531 const MInt neighborId = m_solver->c_neighborId(pCellId, j);
1532 rho = m_solver->a_variable(neighborId, PV->RHO);
1533 if(!m_solver->a_hasProperty(neighborId, Cell::IsHalo)) {
1534 const MBool nbndid = m_solver->a_isG0CandidateOfSet(neighborId, (set - m_solver->m_levelSetId));
1535
1536 // if q < 0.5, perform bounce back (this is meant from the outer cell, the inner cell then has q >= 0.5)
1537 if(q < 0.5) {
1538 const MFloat F2q = F2 * (F1 - q);
1539
1540 // check if my neighbor is an inside cell or a boundary cell with the cell center inside
1541 if(!nbndid || m_solver->a_levelSetFunctionMB(neighborId, set) > 0) {
1542 m_solver->a_oldDistribution(neighborId, j) =
1543 (m_solver->a_distribution(neighborId, opposite) / F2q)
1544 + (m_solver->a_distribution(neighborId, j) * (F2q - F1) / F2q)
1545 + (F1 / F2q) * firstMomentSourceTerm(uW.data(), rho, j);
1546
1547#ifndef WAR_NVHPC_PSTL
1548 incrementForces(neighborId, cellIndex, opposite, uW.data());
1549#endif
1550 }
1551 }
1552 }
1553 }
1554 } // end of the loop over all PPDF directions in case 2
1555 // The outer cell does not belong to the flow field, thus its incoming distributions are overwritten.
1556 // It is possible that there are cells without any cutting velocity! These are considered too.
1557 m_solver->setEqDists(pCellId, F1, uW.data());
1558 }
1559}
1560
1561
1574template <MInt nDim, MInt nDist, class SysEqn>
1576 TRACE();
1577
1578 const MInt pCellId = m_solver->m_G0CellList[cellIndex];
1579 MFloat rho = m_solver->a_variable(pCellId, PV->RHO);
1580 std::array<MFloat, nDim> uW{};
1581 getBoundaryVelocityMb(cellIndex, uW.data());
1582
1583 // case 1: boundary cell is inside fluid
1584 if(m_solver->a_levelSetFunctionMB(pCellId, set) > 0) {
1585 for(MInt j = 0; j < nDist - 1; j++) {
1586#ifdef WAR_NVHPC_PSTL
1587 const MFloat q = m_distances[cellIndex][j];
1588 const MInt opposite = m_solver->m_oppositeDist[j];
1589#else
1590 const MFloat q = getDistanceMb(pCellId, cellIndex, j);
1591 const MInt opposite = Ld::oppositeDist(j);
1592#endif
1593
1594 if(m_solver->a_hasProperty(pCellId, Cell::IsHalo)) continue;
1595
1596 // if q <= 0.5, perform bounce back
1597 if(q <= 0.5) {
1598 if(m_solver->a_hasNeighbor(pCellId, opposite)) {
1599 const MInt neighborId = m_solver->c_neighborId(pCellId, opposite);
1600 // const MInt nneighborId = m_solver->c_neighborId(neighborId, opposite);
1601 // std::cout << "nn" << nneighborId << std::endl;
1602 const MFloat F2q = F2 * q;
1603 const MFloat Fq = q;
1604
1605 MFloat quadratic = Fq * (F2q + 1) * m_solver->a_distribution(pCellId, j)
1606 + (1 + F2q) * (1 - F2q) * m_solver->a_distribution(neighborId, j)
1607 - Fq * (1 - F2q) * m_solver->a_oldDistribution(neighborId, j)
1608 + firstMomentSourceTerm(uW.data(), rho, opposite);
1609
1610 m_solver->a_oldDistribution(pCellId, opposite) = quadratic;
1611#ifndef WAR_NVHPC_PSTL
1612 incrementForces(pCellId, cellIndex, j, uW.data());
1613#endif
1614 }
1615 // this is the case in which we have no neighbor in the direction we want to set (strange case)
1616 // can in my opinion only appear if neighbors were set wrong or at an interface
1617 else {
1618 m_solver->a_oldDistribution(pCellId, opposite) =
1619 m_solver->a_distribution(pCellId, j) + firstMomentSourceTerm(uW.data(), rho, opposite);
1620 }
1621 }
1622
1623 // this is the case in which we do not have a neighbor and no cut, do simple bounce back (strange case)
1624 // can in my opinoin only appear if something has gone wrong either with the grid or with the distance calculation
1625 else if(q > 0.5 && m_solver->a_hasNeighbor(pCellId, j) == 0) {
1626 m_solver->a_oldDistribution(pCellId, opposite) =
1627 m_solver->a_distribution(pCellId, j) + firstMomentSourceTerm(uW.data(), rho, opposite);
1628 }
1629 }
1630 }
1631 // case 2: boundary cell is outside fluid
1632 else {
1633 for(MInt j = 0; j < nDist - 1; j++) {
1634#ifdef WAR_NVHPC_PSTL
1635 const MFloat q = m_distances[cellIndex][j];
1636 const MInt opposite = m_solver->m_oppositeDist[j];
1637#else
1638 const MFloat q = getDistanceMb(pCellId, cellIndex, j);
1639 const MInt opposite = Ld::oppositeDist(j);
1640#endif
1641
1642 // do we have a neighbor at all?
1643 if(m_solver->a_hasNeighbor(pCellId, j)) {
1644 // make sure that the neighbor is not a halo cell
1645 const MInt neighborId = m_solver->c_neighborId(pCellId, j);
1646 rho = m_solver->a_variable(neighborId, PV->RHO);
1647 if(!m_solver->a_hasProperty(neighborId, Cell::IsHalo)) {
1648 const MBool nbndid = m_solver->a_isG0CandidateOfSet(neighborId, (set - m_solver->m_levelSetId));
1649
1650 // if q < 0.5, perform bounce back (this is meant from the outer cell, the inner cell then has q >= 0.5)
1651 if(q < 0.5) {
1652 const MFloat F2q = F2 * (F1 - q);
1653 const MFloat Fq = (1 - q);
1654
1655 // check if my neighbor is an inside cell or a boundary cell with the cell center inside
1656 if(!nbndid || m_solver->a_levelSetFunctionMB(neighborId, set) > 0) {
1657 if(m_solver->a_hasNeighbor(neighborId, j)) {
1658 const MInt nneighborId = m_solver->c_neighborId(neighborId, j);
1659
1660 MFloat quadratic =
1661 1 / Fq / (F2q + 1)
1662 * (m_solver->a_distribution(neighborId, opposite) + firstMomentSourceTerm(uW.data(), rho, j))
1663 + (F2q - 1) / Fq * m_solver->a_distribution(neighborId, j)
1664 - (F2q - 1) / (F2q + 1) * m_solver->a_distribution(nneighborId, j);
1665
1666 m_solver->a_oldDistribution(neighborId, j) = quadratic;
1667 } else {
1668 // Linear fallback for confined direction
1669 MFloat linear = (m_solver->a_distribution(neighborId, opposite) / F2q)
1670 + (m_solver->a_distribution(neighborId, j) * (F2q - F1) / F2q)
1671 + (F1 / F2q) * firstMomentSourceTerm(uW.data(), rho, j);
1672
1673 m_solver->a_oldDistribution(neighborId, j) = linear;
1674 }
1675#ifndef WAR_NVHPC_PSTL
1676 incrementForces(neighborId, cellIndex, opposite, uW.data());
1677#endif
1678 }
1679 }
1680 }
1681 }
1682 } // end of the loop over all PPDF directions in case 2
1683 // The outer cell does not belong to the flow field, thus its incoming distributions are overwritten.
1684 // It is possible that there are cells without any cutting velocity! These are considered too.
1685 m_solver->setEqDists(pCellId, F1, uW.data());
1686 }
1687}
1688
1689
1702template <MInt nDim, MInt nDist, class SysEqn>
1704 TRACE();
1705
1706 const MInt pCellId = m_solver->m_G0CellList[cellIndex];
1707 MFloat rho = m_solver->a_variable(pCellId, PV->RHO);
1708 std::array<MFloat, nDim> uW{};
1709 getBoundaryVelocityMb(cellIndex, uW.data());
1710
1711 // case 1: boundary cell is inside fluid
1712 if(m_solver->a_levelSetFunctionMB(pCellId, set) > 0) {
1713 for(MInt j = 0; j < nDist - 1; j++) {
1714#ifdef WAR_NVHPC_PSTL
1715 const MFloat q = m_distances[cellIndex][j];
1716 const MInt opposite = m_solver->m_oppositeDist[j];
1717#else
1718 const MFloat q = getDistanceMb(pCellId, cellIndex, j);
1719 const MInt opposite = Ld::oppositeDist(j);
1720#endif
1721
1722 if(m_solver->a_hasProperty(pCellId, Cell::IsHalo)) continue;
1723
1724 // if q <= 0.5, perform bounce back
1725 if(q <= 0.5) {
1726 if(m_solver->a_hasNeighbor(pCellId, opposite) != 0) {
1727 const MInt neighborId = m_solver->c_neighborId(pCellId, opposite);
1728 const MInt nneighborId = m_solver->c_neighborId(neighborId, opposite);
1729
1730 const MFloat F2q = F2 * q;
1731 const MFloat Fq = q;
1732
1733 // Calculate temporary distribution at position which will propagrate exactly to the wall
1734 const MFloat wall = Fq * (Fq + 1) / 2 * m_solver->a_distribution(pCellId, j)
1735 + (1 - Fq) * (1 + Fq) * m_solver->a_distribution(neighborId, j)
1736 - Fq * (1 - Fq) / 2 * m_solver->a_distribution(nneighborId, j);
1737
1738 // Instantaneous bounce-back at the wall
1739 const MFloat wall_bb = wall + firstMomentSourceTerm(uW.data(), rho, opposite);
1740
1741 // Interpolate bounce-back value
1742 const MFloat interp = 2 / (1 + Fq) / (2 + Fq) * wall_bb
1743 + F2q / (1 + Fq) * m_solver->a_distribution(pCellId, opposite)
1744 - Fq / (2 + Fq) * m_solver->a_distribution(neighborId, opposite);
1745
1746 m_solver->a_oldDistribution(pCellId, opposite) = interp;
1747#ifndef WAR_NVHPC_PSTL
1748 incrementForces(pCellId, cellIndex, j, uW.data());
1749#endif
1750 }
1751 // this is the case in which we have no neighbor in the direction we want to set (strange case)
1752 // can in my opinion only appear if neighbors were set wrong or at an interface
1753 else {
1754 m_solver->a_oldDistribution(pCellId, opposite) =
1755 m_solver->a_distribution(pCellId, j) + firstMomentSourceTerm(uW.data(), rho, opposite);
1756 }
1757 }
1758
1759 // this is the case in which we do not have a neighbor and no cut, do simple bounce back (strange case)
1760 // can in my opinoin only appear if something has gone wrong either with the grid or with the distance calculation
1761 else if(q > 0.5 && m_solver->a_hasNeighbor(pCellId, j) == 0) {
1762 m_solver->a_oldDistribution(pCellId, opposite) =
1763 m_solver->a_distribution(pCellId, j) + firstMomentSourceTerm(uW.data(), rho, opposite);
1764 }
1765 }
1766 }
1767 // case 2: boundary cell is outside fluid
1768 else {
1769 for(MInt j = 0; j < nDist - 1; j++) {
1770#ifdef WAR_NVHPC_PSTL
1771 const MFloat q = m_distances[cellIndex][j];
1772 const MInt opposite = m_solver->m_oppositeDist[j];
1773#else
1774 const MFloat q = getDistanceMb(pCellId, cellIndex, j);
1775 const MInt opposite = Ld::oppositeDist(j);
1776#endif
1777
1778 // do we have a neighbor at all?
1779 if(m_solver->a_hasNeighbor(pCellId, j)) {
1780 // make sure that the neighbor is not a halo cell
1781 const MInt neighborId = m_solver->c_neighborId(pCellId, j);
1782 rho = m_solver->a_variable(neighborId, PV->RHO);
1783 if(!m_solver->a_hasProperty(neighborId, Cell::IsHalo)) {
1784 const MBool nbndid = m_solver->a_isG0CandidateOfSet(neighborId, (set - m_solver->m_levelSetId));
1785
1786 // if q < 0.5, perform bounce back (this is meant from the outer cell, the inner cell then has q >= 0.5)
1787 if(q < 0.5) {
1788 const MFloat F2q = F2 * (F1 - q);
1789 const MFloat Fq = (1 - q);
1790
1791 // check if my neighbor is an inside cell or a boundary cell with the cell center inside
1792 if(!nbndid || m_solver->a_levelSetFunctionMB(neighborId, set) > 0) {
1793 const MInt nneighborId = m_solver->c_neighborId(neighborId, j);
1794 const MInt nnneighborId = m_solver->c_neighborId(nneighborId, j);
1795
1796 // Calculate temporary distribution at position which will propagrate exactly to the wall
1797 const MFloat wall = Fq * (Fq + 1) / 2 * m_solver->a_distribution(neighborId, opposite)
1798 + (1 - Fq) * (1 + Fq) * m_solver->a_distribution(nneighborId, opposite)
1799 - Fq * (1 - Fq) / 2 * m_solver->a_distribution(nnneighborId, opposite);
1800
1801 // Instantaneous bounce-back at the wall
1802 const MFloat wall_bb = wall + firstMomentSourceTerm(uW.data(), rho, j);
1803
1804
1805 // Interpolate bounce-back value
1806 const MFloat interp = 2 / (1 + Fq) / (2 + Fq) * wall_bb
1807 + F2q / (1 + Fq) * m_solver->a_distribution(neighborId, j)
1808 - Fq / (2 + Fq) * m_solver->a_distribution(nneighborId, j);
1809
1810
1811 m_solver->a_oldDistribution(neighborId, j) = interp;
1812#ifndef WAR_NVHPC_PSTL
1813 incrementForces(neighborId, cellIndex, opposite, uW.data());
1814#endif
1815 }
1816 }
1817 }
1818 }
1819 } // end of the loop over all PPDF directions in case 2
1820 // The outer cell does not belong to the flow field, thus its incoming distributions are overwritten.
1821 // It is possible that there are cells without any cutting velocity! These are considered too.
1822 m_solver->setEqDists(pCellId, F1, uW.data());
1823 }
1824}
1825
1826
1842template <MInt nDim, MInt nDist, class SysEqn>
1843inline void LbBndCndDxQy<nDim, nDist, SysEqn>::incrementForces(const MInt cellId, const MInt mbCellId, const MInt j,
1844 const MFloat* uW) {
1845 const MInt opposite = Ld::oppositeDist(j);
1846
1847 const MFloat& Fin = m_solver->a_distribution(cellId, j);
1848 const MFloat& Fout = m_solver->a_oldDistribution(cellId, opposite);
1849
1850 // Galilei invariant momentum exchange method
1851 for(MInt d = 0; d < nDim; d++) {
1852 const MFloat Vin = Ld::ppdfDir(j, d) - uW[d];
1853 const MFloat Vout = Ld::ppdfDir(opposite, d) - uW[d];
1854
1855 m_boundaryCellsMb.force(mbCellId, j, d) = (Fin * Vin - Fout * Vout);
1856 }
1857}
1858
1859
1868template <MInt nDim, MInt nDist, class SysEqn>
1870 for(MInt n = 0; n < nDim; n++) {
1871 uW[n] = m_boundaryCellsMb.velocity(mbCellId, n);
1872 }
1873}
1874
1883template <MInt nDim, MInt nDist, class SysEqn>
1885 for(MInt d = 0; d < nDim; d++)
1886 uW[d] = F0;
1887 for(MInt i = 0; i < m_lbNoMovingWalls; i++) {
1888 if(m_segIdMovingWalls[i] == m_bndCndSegIds[index]) {
1889 for(MInt d = 0; d < nDim; d++) {
1890 uW[d] = m_lbWallVelocity[i * nDim + d];
1891 }
1892 break;
1893 }
1894 }
1895}
1896
1905template <MInt nDim, MInt nDist, class SysEqn>
1907 MFloat wT = m_solver->m_initTemperatureKelvin;
1908 for(MInt i = 0; i < m_lbNoHeatedWalls; i++) {
1909 if(m_segIdHeatedWalls[i] == m_bndCndSegIds[index]) {
1910 wT = m_lbWallTemperature[i];
1911 break;
1912 }
1913 }
1914 return wT;
1915}
1916
1930template <MInt nDim, MInt nDist, class SysEqn>
1932 const MInt dist) {
1933#ifdef WAR_NVHPC_PSTL
1934 MFloat ppdfDir[nDim] = {F0};
1935 for(MInt d = 0; d < nDim; d++) {
1936 ppdfDir[d] = m_solver->m_ppdfDir[dist * nDim + d];
1937 }
1938 const MFloat scalarProduct = std::inner_product(uW, uW + nDim, &ppdfDir[0], .0);
1939 const MFloat weight = m_solver->m_tp[m_solver->m_distType[dist]];
1940#else
1941 const MFloat scalarProduct = std::inner_product(uW, uW + nDim, lbDescriptor::ppdfDir<nDim>[dist], .0);
1942 const MFloat weight = Ld::tp(Ld::distType(dist));
1943#endif
1944
1945 return 2.0 * F1BCSsq * weight * rho * scalarProduct;
1946}
1947
1948
1961template <MInt nDim, MInt nDist, class SysEqn>
1963 TRACE();
1964
1965 const MInt noMbCells = m_boundaryCellsMb.size();
1966
1967#ifdef WAR_NVHPC_PSTL
1968 mAlloc(m_distances, noMbCells, nDist - 1, "distances", F1, AT_);
1969 for(MInt i = 0; i < noMbCells; i++) {
1970 const MInt pCellId = m_solver->m_G0CellList[i];
1971 for(MInt d = 0; d < nDist - 1; d++) {
1972 m_distances[i][d] = getDistanceMb(pCellId, i, d);
1973 }
1974 }
1975#endif
1976
1977 maia::parallelFor<true>(0, noMbCells, [=](MInt mbId) {
1978
1979#ifdef WAR_NVHPC_PSTL
1980 // Perform bounce back set by properties
1981 if(m_bounceBackFunctionMbCase == 0) {
1982 interpolatedBounceBackMb_Bouzidi_lin(mbId, set);
1983 } else if(m_bounceBackFunctionMbCase == 1) {
1984 interpolatedBounceBackMb_Bouzidi_qua(mbId, set);
1985 } else {
1986 interpolatedBounceBackMb_Yu_qua(mbId, set);
1987 }
1988#else
1989 // Perform bounce back set by properties
1990 (this->*bounceBackFunctionMb)(mbId, set);
1991#endif
1992
1993 if(m_solver->m_isThermal) {
1994 interpolatedBounceBackMb_Bouzidi_lin_thermal(mbId, set);
1995 }
1996 if(m_solver->m_isTransport) {
1997 interpolatedBounceBackMb_Bouzidi_lin_transport(mbId, set);
1998 }
1999 });
2000
2001#ifdef WAR_NVHPC_PSTL
2002 mDeallocate(m_distances);
2003#endif
2004 if(m_calcWallForces && m_solver->m_currentNoG0Cells > 0) {
2005 calculateWallForcesMb(set);
2006 }
2007}
2008
2022template <MInt nDim, MInt nDist, class SysEqn>
2024 TRACE();
2025
2026 MInt noMbCells = m_boundaryCellsMb.size();
2027 if(m_solver->noDomains() > 1) {
2028 MPI_Allreduce(&noMbCells, &noMbCells, 1, MPI_INT, MPI_SUM, m_solver->mpiComm(), AT_, "noMbCells", "noMbCells");
2029 }
2030 if(!m_solver->domainId()) {
2031 std::cout << "Apply free surface bc to " << noMbCells << " no mbCells" << std::endl;
2032 }
2033
2034 for(MInt mbCellId = 0; mbCellId < m_boundaryCellsMb.size(); mbCellId++) {
2035 const MInt pCellId = m_boundaryCellsMb.cellId(mbCellId);
2036
2037 ASSERT(m_solver->a_isG0CandidateOfSet(pCellId, (set - m_solver->m_levelSetId)), "Inconsistent collector!");
2038
2039 interpolatedAntiBounceBackMb_Bouzidi_qua(mbCellId, set);
2040 }
2041}
2042
2043
2060template <MInt nDim, MInt nDist, class SysEqn>
2062 TRACE();
2063
2064 std::array<MFloat, nDim> uW{};
2065 getBoundaryVelocity(index, uW.data());
2066
2067#ifdef WAR_NVHPC_PSTL
2068 const MInt globalTimeStep_ = globalTimeStep;
2069 MInt begin = m_bndCndOffsets[index];
2070 MInt end = m_bndCndOffsets[index + 1];
2071 MInt offset = end - begin;
2072
2073 maia::parallelFor<true>(0, offset, [=](MInt id) {
2074 MInt i = begin + id;
2075 if((globalTimeStep_) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) return;
2076#else
2077 maia::parallelFor(m_bndCndOffsets[index], m_bndCndOffsets[index + 1], [=](MInt i) {
2078 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) return;
2079#endif
2080 // since it is a rhsBnd it should be performed after propagation
2081 // TODO labels:LB,totest Not tested yet for a locally refined mesh
2082
2083 interpolatedBounceBackSingleSpecies(i, uW.data());
2084 });
2085
2086 if(m_calcWallForces) {
2087 calculateWallForces(index);
2088 }
2089}
2090
2091
2105template <MInt nDim, MInt nDist, class SysEqn>
2107 TRACE();
2108
2109#ifdef WAR_NVHPC_PSTL
2110 const MInt globalTimeStep_ = globalTimeStep;
2111 const MInt begin = m_bndCndOffsets[index];
2112 const MInt end = m_bndCndOffsets[index + 1];
2113 const MInt offset = end - begin;
2114
2115 maia::parallelFor<true>(0, offset, [=](MInt id) {
2116 const MInt i = begin + id;
2117 if((globalTimeStep_) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) return;
2118#else
2119 maia::parallelFor(m_bndCndOffsets[index], m_bndCndOffsets[index + 1], [=](MInt i) {
2120 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) return;
2121#endif
2122 // TODO labels:LB,totest Not tested yet for a locally refined mesh
2123
2124 if(m_solver->a_isHalo(m_bndCells[i].m_cellId)) return;
2125
2126 for(MInt j = 0; j < nDist - 1; j++) {
2127#ifdef WAR_NVHPC_PSTL
2128 const MInt opposite = m_solver->m_oppositeDist[j];
2129#else
2130 const MInt opposite = Ld::oppositeDist(j);
2131#endif
2132 if(m_solver->a_hasNeighbor(m_bndCells[i].m_cellId, opposite) == 0) {
2133 // leave out interface cells
2134 if(m_solver->c_parentId(m_bndCells[i].m_cellId) > -1) {
2135 if(m_solver->a_hasNeighbor(m_solver->c_parentId(m_bndCells[i].m_cellId), opposite)) continue;
2136 }
2137 m_solver->a_oldDistribution(m_bndCells[i].m_cellId, j) =
2138 m_solver->a_distribution(m_bndCells[i].m_cellId, opposite);
2139 }
2140 }
2141 });
2142}
2143
2144
2158template <MInt nDim, MInt nDist, class SysEqn>
2160 TRACE();
2161
2162 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
2163 const MInt cellId = m_bndCells[i].m_cellId;
2164 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(cellId)) != 0) continue;
2165 // since it is a rhsBnd it should be performed after propagation
2166 // TODO labels:LB,totest Not tested yet for a locally refined mesh
2167
2168 // leave out halo cells
2169 if(m_solver->a_isHalo(cellId)) continue;
2170
2171 for(MInt j = 0; j < nDist - 1; j++) {
2172 if(m_solver->a_hasNeighbor(cellId, Ld::oppositeDist(j)) == 0) {
2173 m_solver->a_oldDistribution(cellId, j) = m_solver->a_oldDistribution(cellId, Ld::oppositeDist(j));
2174 }
2175 }
2176 }
2177}
2178
2179
2192template <MInt nDim, MInt nDist, class SysEqn>
2194 TRACE();
2195
2196 // For now testing only the D3Q19 algorithm
2197 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
2198 // if( (globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0 )
2199 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
2200 // since it is a rhsBnd it should be performed after propagation
2201 // TODO labels:LB,totest Not tested yet for a locally refined mesh
2202
2203 const MInt cellId = m_bndCells[i].m_cellId;
2204
2205 // extrapolate inner density values
2206 MInt neighborId{};
2207 if(m_solver->a_hasNeighbor(cellId, 2) == 0) {
2208 neighborId = m_solver->c_neighborId(cellId, 3);
2209 } else {
2210 neighborId = m_solver->c_neighborId(cellId, 2);
2211 }
2212
2213 MFloat rho = m_solver->a_variable(neighborId, PV->RHO);
2214
2215 const MFloat old_rho = m_solver->a_oldVariable(cellId, PV->RHO);
2216
2217 // non-reflecting bc (Finck, Haenel 2008)
2218 // rho = ( old_rho + F1BCS * ( sqrt(tmp2) - sqrt(old_tmp2) ) + m_rho1 ) / 2.0 ;
2219 rho = (old_rho + rho) / 2.0;
2220
2221 // set velocity to zero
2222 const std::array<MFloat, nDim> u{};
2223 const MFloat squaredVelocity{};
2224
2225 m_solver->setEqDists(cellId, rho, squaredVelocity, u.data());
2226
2227 m_solver->a_variable(cellId, PV->RHO) = rho;
2228 for(MInt n = 0; n < nDim; n++) {
2229 m_solver->a_variable(cellId, PV->VV[n]) = u[n];
2230 }
2231 }
2232}
2233
2250template <MInt nDim, MInt nDist, class SysEqn>
2252 TRACE();
2253
2254 MFloat F2q, rhoF, uF[nDim], uBF[nDim], tmpUF, tmpUBF, tmp2UF, b[2 * nDim];
2255
2256 const MInt dist1 = Ld::distFld(0);
2257 const MInt dist2 = Ld::distFld(1) + Ld::distFld(0);
2258
2259 MInt id, k, tpIndex;
2260 MFloat fEq, xi;
2261 MInt nghbrId, tmpDistId;
2262
2263 // field for force evaluation
2264 static std::ofstream ofl;
2265
2266 ScratchSpace<MFloat> distributions(nDist - 1, AT_, "distributions");
2267 for(MInt i = 0; i < nDist - 1; i++) {
2268 distributions[i] = 0.0;
2269 }
2270
2271
2272 //---------------------------------------------------------------------------------
2273 // go through m_bndcells with wallbndcnd;
2274 // the halo cells have to be included, since they may be of relevance in case 2
2275 //---------------------------------------------------------------------------------
2276 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
2277 // if((globalTimeStep - 1 ) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0 )
2278 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
2279 // since it is a rhsBnd it should be performed after propagation
2280 // TODO labels:LB,totest Not tested yet for a locally refined mesh
2281
2282 id = m_bndCells[i].m_cellId;
2283
2284 // If there are several levels of boundary cells use only the highest
2285 // nearest neighbors, must be of the same level
2286 if(m_solver->a_level(id) < m_solver->maxLevel()) continue;
2287
2288 m_omega = 2.0 / (1.0 + 6.0 * m_solver->a_nu(id) * FFPOW2(m_solver->maxLevel() - m_solver->a_level(id)));
2289
2290 rhoF = 1.0; // standard value which is usually overwritten in the following. Only in exceptional case it remains 1
2291
2292 //----------------------------------------------------------------------------------------------------
2293 // perform bounce back for distributions piercing the wall - distinquish between inner and outer cells
2294 //----------------------------------------------------------------------------------------------------
2295
2296 //---------------------------------------------------------------------------------
2297 // 1. boundary cell is inside fluid
2298 //---------------------------------------------------------------------------------
2299 if(m_bndCells[i].m_isFluid) {
2300 rhoF = m_solver->a_variable(id, PV->RHO);
2301 for(MInt n = 0; n < nDim; n++) {
2302 uF[n] = m_solver->a_variable(id, PV->VV[n]);
2303 b[2 * n] = -uF[n];
2304 b[2 * n + 1] = uF[n];
2305 }
2306
2307 tmp2UF = std::inner_product(&uF[0], &uF[nDim], &uF[0], .0);
2308
2309 for(MInt j = 0; j < nDist - 1; j++) {
2310 tmpUF = F0;
2311 tpIndex = 0;
2312 if(j < dist1) {
2313 // dxB2 = cellHalfLength;
2314 tmpUF = b[j];
2315 tpIndex = 1;
2316 } else {
2317 if(j < dist2) {
2318 // dxB2 = SQRT2 * cellHalfLength;
2319 k = j - Ld::distFld(0);
2320 tmpUF = (b[Ld::mFld1(2 * k)] + b[Ld::mFld1(2 * k + 1)]);
2321 tpIndex = 2;
2322 } else {
2323 // dxB2 = SQRT3 * cellHalfLength;
2324 if(nDim == 3) {
2325 k = j - (Ld::distFld(0) + Ld::distFld(1));
2326 tmpUF = (b[Ld::mFld2(3 * k)] + b[Ld::mFld2(3 * k + 1)] + b[Ld::mFld2(3 * k + 2)]);
2327 tpIndex = 3;
2328 }
2329 }
2330 }
2331
2332 //---------------------------------------------------------------------------------
2333 // if q < 0.5, perform bounce back
2334 //---------------------------------------------------------------------------------
2335
2336 if(m_bndCells[i].m_distances[j] < 0.5) {
2337 F2q = F2 * m_bndCells[i].m_distances[j]; // q = distance / dx; distance: distance between cell center and wall
2338
2339 if(m_solver->a_hasNeighbor(id, Ld::oppositeDist(j)) > 0) {
2340 nghbrId = m_solver->c_neighborId(id, Ld::oppositeDist(j));
2341
2342 // extrapolate velocity from next inner cell
2343 tmpUBF = F0;
2344 for(MInt n = 0; n < nDim; n++) {
2345 uBF[n] = m_solver->a_variable(nghbrId, n);
2346 tmpUBF += (Ld::idFld(j, n) - 1) * uBF[n];
2347 }
2348 } else {
2349 tmpUBF = tmpUF;
2350 }
2351
2352 fEq = Ld::tp(tpIndex) * rhoF
2353 * (1.0 + tmpUBF * F1BCSsq + tmpUF * tmpUF * F1BCSsq * F1BCSsq * F1B2 - tmp2UF * F1BCSsq * F1B2);
2354 xi = m_omega * (F2q - 1.0) / (1.0 - 2.0 * m_omega);
2355
2356 // perform interpolated bounce back
2357 m_solver->a_oldDistribution(id, Ld::oppositeDist(j)) =
2358 (1.0 - xi) * m_solver->a_distribution(id, j) + xi * fEq;
2359
2360 // add to momentum sum
2361 distributions[Ld::oppositeDist(j)] +=
2362 m_solver->a_oldDistribution(id, Ld::oppositeDist(j)) + m_solver->a_distribution(id, j);
2363
2364 }
2365
2366 // if the cell is located at a corner, there might be distributions missing
2367 else {
2368 // In this case simple bounce back is used. This could decrease the overall accuracy.
2369 if(m_solver->a_hasNeighbor(id, j) == 0) {
2370 // do simple bounce back
2371 m_solver->a_oldDistribution(id, Ld::oppositeDist(j)) = m_solver->a_distribution(id, j);
2372
2373 // add to momentum sum
2374 distributions[Ld::oppositeDist(j)] +=
2375 m_solver->a_oldDistribution(id, Ld::oppositeDist(j)) + m_solver->a_distribution(id, j);
2376 }
2377 }
2378
2379 } // end of loop over all directions
2380
2381 }
2382
2383 //---------------------------------------------------------------------------------
2384 // 2. boundary cell is outside fluid
2385 //---------------------------------------------------------------------------------
2386
2387 // The outer cell does not belong to the flow field, thus the macroscopic values are held constant.
2388 // It is possible that there are cells without any cutting velocity! Those have to be controlled, too.
2389 else {
2390 for(MInt j = 0; j < nDist - 1; j++) {
2391 // if ( j < 6 )
2392 // dxB2 = cellHalfLength;
2393 // else {
2394 // if ( j < 18)
2395 // dxB2 = SQRT2 * cellHalfLength;
2396 // else
2397 // dxB2 = SQRT3 * cellHalfLength;
2398 // }
2399
2400 //----------------------------------------------------------------------------------------------------------------
2401 // if q_innerNeighbor = 1 - q_bndCell >= 0.5, perform bounceback on inner neighbor
2402 // if the inner neighbor is a halo cell, it can be skipped
2403 //----------------------------------------------------------------------------------------------------------------
2404 // if ( m_solver->a_hasNeighbor(id, j) > 0 && m_solver->c_neighborId(id, j) < m_solver->noInternalCells()
2405 // ) {
2406 if(m_solver->a_hasNeighbor(id, j) > 0 && !m_solver->a_isHalo(m_solver->c_neighborId(id, j))) {
2407 if(m_bndCells[i].m_distances[j] <= 0.5) {
2408 F2q = F2 - F2 * m_bndCells[i].m_distances[j]; // q = 1 - 0.5*(distance/cellHalfLength);
2409
2410 nghbrId = m_solver->c_neighborId(id, j);
2411
2412 rhoF = m_solver->a_variable(nghbrId, PV->RHO);
2413 for(MInt n = 0; n < nDim; n++) {
2414 uF[n] = m_solver->a_variable(nghbrId, PV->VV[n]);
2415 b[2 * n] = -uF[n];
2416 b[2 * n + 1] = uF[n];
2417 }
2418
2419 tmp2UF = std::inner_product(&uF[0], &uF[nDim], &uF[0], .0);
2420
2421 tmpUF = F0;
2422 tpIndex = 0;
2423 if(j < dist1) {
2424 tmpUF = b[Ld::oppositeDist(j)];
2425 tpIndex = 1;
2426 } else {
2427 if(j < dist2) {
2428 k = Ld::oppositeDist(j) - Ld::distFld(0);
2429 tmpUF = (b[Ld::mFld1(2 * k)] + b[Ld::mFld1(2 * k + 1)]);
2430 tpIndex = 2;
2431 } else {
2432 if(nDim == 3) {
2433 k = Ld::oppositeDist(j) - (Ld::distFld(0) + Ld::distFld(1));
2434 tmpUF = (b[Ld::mFld2(3 * k)] + b[Ld::mFld2(3 * k + 1)] + b[Ld::mFld2(3 * k + 2)]);
2435 tpIndex = 3;
2436 }
2437 }
2438 }
2439
2440 tmpUBF = 0.0;
2441 for(MInt dim = 0; dim < nDim; dim++) {
2442 uBF[dim] = (1.0 - 3.0 / F2q) * uF[dim];
2443 tmpUBF += (Ld::idFld(Ld::oppositeDist(j), dim) - 1) * uBF[dim];
2444 }
2445
2446 fEq = Ld::tp(tpIndex) * rhoF
2447 * (1.0 + tmpUBF * F1BCSsq + tmpUF * tmpUF * F1BCSsq * F1BCSsq * F1B2 - tmp2UF * F1BCSsq * F1B2);
2448 xi = m_omega * (F2q - 1.0) / (1 + 0.5 * m_omega);
2449 // xi = m_omega * (F2q - 1.0);
2450
2451 // perform interpolated bounce back
2452 m_solver->a_oldDistribution(nghbrId, j) =
2453 (1.0 - xi) * m_solver->a_distribution(nghbrId, Ld::oppositeDist(j)) + xi * fEq;
2454
2455 // add to momentum sum
2456 distributions[j] +=
2457 m_solver->a_oldDistribution(nghbrId, j) + m_solver->a_distribution(nghbrId, Ld::oppositeDist(j));
2458 // distributions[j] += 2.0 * m_solver->a_distribution(neighborId, Ld::oppositeDist(j));
2459 }
2460 }
2461
2462 } // end of loop over all directions
2463
2464 // The outer cell does not belong to the flow field, thus its incoming distributions are overwritten.
2465 // It is possible that there are cells without any cutting velocity! These are considered too.
2466 m_solver->a_variable(id, PV->RHO) = 1.0;
2467 m_solver->a_oldVariable(id, PV->RHO) = 1.0;
2468
2469 for(MInt n = 0; n < nDim; n++) {
2470 m_solver->a_variable(id, n) = 0.0;
2471 m_solver->a_oldVariable(id, n) = 0.0;
2472 }
2473
2474 // Calculation of eq-distributions in bnd cell
2475 //--------------------------------------------
2476 // Calculation of distributions for directions with only one component
2477 for(MInt j = 0; j < Ld::distFld(0); j++) {
2478 m_solver->a_oldDistribution(id, j) = Ld::tp(1);
2479 }
2480
2481 // Calculation of distributions for directions with two components
2482 tmpDistId = Ld::distFld(0);
2483 for(MInt j = 0; j < Ld::distFld(1); j++) {
2484 m_solver->a_oldDistribution(id, tmpDistId + j) = Ld::tp(2);
2485 }
2486
2487 // Calculation of distributions for directions with three components
2488 tmpDistId = Ld::distFld(0) + Ld::distFld(1);
2489 for(MInt j = 0; j < Ld::distFld(2); j++) {
2490 m_solver->a_oldDistribution(id, tmpDistId + j) = Ld::tp(3);
2491 }
2492
2493 // Calculation of distribution for rest particle distribution (center)
2494 m_solver->a_oldDistribution(id, Ld::lastId()) = Ld::tp(0);
2495 }
2496
2497 } // end of loop over all m_bndcells
2498}
2499
2506template <MInt nDim, MInt nDist, class SysEqn>
2508 TRACE();
2509
2510 MFloat q, rhoF, uF[nDim], tmpUF, tmpUBF, tmp2UF, tmp2UBF = 0.0, b[2 * nDim];
2511 MFloat uBF[nDim] = {F0};
2512 for(MInt d = 0; d < nDim; d++) {
2513 uBF[d] = std::numeric_limits<MFloat>::max();
2514 }
2515
2516 MInt id = 0, k, tpIndex;
2517 MFloat fEqW, fEqF;
2518 MInt nghbrId, tmpDistId;
2519
2520 // field for force evaluation
2521 static std::ofstream ofl;
2522
2523 ScratchSpace<MFloat> distributions(nDist - 1, AT_, "distributions");
2524 for(MInt i = 0; i < nDist - 1; i++) {
2525 distributions[i] = 0.0;
2526 }
2527
2528
2529 // go through m_bndcells with wallbndcnd
2530 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
2531 // if((globalTimeStep - 1 ) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0 )
2532 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
2533 // since it is a rhsBnd it should be performed after propagation
2534 // TODO labels:LB,totest Not tested yet for a locally refined mesh
2535
2536 // If there are several levels of boundary cells use only the highest
2537 // nearest neighbors, must be of the same level
2538 if(m_solver->a_level(id) < m_solver->maxLevel()) continue;
2539
2540 id = m_bndCells[i].m_cellId;
2541
2542 m_omega = 2.0 / (1.0 + 6.0 * m_solver->a_nu(id) * FFPOW2(m_solver->maxLevel() - m_solver->a_level(id)));
2543
2544 rhoF = 1.0; // standard value which is usually overwritten in the following. Only in exceptional case it remains 1
2545
2546 //----------------------------------------------------------------------------------------------------
2547 // perform bounce back for distributions piercing the wall - distinquish between inner and outer cells
2548 //----------------------------------------------------------------------------------------------------
2549
2550 //---------------------------------------------------------------------------------
2551 // 1. boundary cell is inside fluid
2552 //---------------------------------------------------------------------------------
2553 if(m_bndCells[i].m_isFluid) {
2554 rhoF = m_solver->a_variable(id, PV->RHO);
2555 tmp2UF = 0.0;
2556 for(MInt d = 0; d < nDim; d++) {
2557 uF[d] = m_solver->a_variable(id, d);
2558 tmp2UF += uF[d] * uF[d];
2559 b[2 * d] = -uF[d];
2560 b[2 * d + 1] = uF[d];
2561 }
2562
2563 for(MInt j = 0; j < nDist - 1; j++) {
2564 if(j < Ld::distFld(0)) {
2565 // dxB2 = cellHalfLength;
2566 tmpUF = b[j];
2567 tpIndex = 1;
2568 } else {
2569 if(j < (Ld::distFld(0) + Ld::distFld(1))) {
2570 // dxB2 = SQRT2 * cellHalfLength;
2571 k = j - Ld::distFld(0);
2572 tmpUF = (b[Ld::mFld1(2 * k)] + b[Ld::mFld1(2 * k + 1)]);
2573 tpIndex = 2;
2574 } else {
2575 // dxB2 = SQRT3 * cellHalfLength;
2576 k = j - (Ld::distFld(0) + Ld::distFld(1));
2577 tmpUF = (b[Ld::mFld2(3 * k)] + b[Ld::mFld2(3 * k + 1)] + b[Ld::mFld2(3 * k + 2)]);
2578 tpIndex = 3;
2579 }
2580 }
2581
2582 //---------------------------------------------------------------------------------
2583 // if q < 0.5, perform bounce back
2584 //---------------------------------------------------------------------------------
2585
2586 if(m_bndCells[i].m_distances[j] < 0.5) {
2587 q = m_bndCells[i].m_distances[j]; // q = distance / dx; distance: distance between cell center and wall
2588
2589 if(m_solver->a_hasNeighbor(id, Ld::oppositeDist(j)) > 0) {
2590 nghbrId = m_solver->c_neighborId(id, Ld::oppositeDist(j));
2591
2592 // extrapolate velocity from next inner cell
2593 uBF[0] = (q - 1.0) * m_solver->a_variable(id, PV->U)
2594 + (1.0 - q) * (q - 1.0) * m_solver->a_variable(nghbrId, PV->U) / (q + 1.0);
2595 uBF[1] = (q - 1.0) * m_solver->a_variable(id, PV->V)
2596 + (1.0 - q) * (q - 1.0) * m_solver->a_variable(nghbrId, PV->V) / (q + 1.0);
2597
2598 IF_CONSTEXPR(nDim == 3)
2599 uBF[2] = (q - 1.0) * m_solver->a_variable(id, PV->W)
2600 + (1.0 - q) * (q - 1.0) * m_solver->a_variable(nghbrId, PV->W) / (q + 1.0);
2601
2602 tmpUBF = 0.0;
2603 tmp2UBF = 0.0;
2604 for(MInt d = 0; d < nDim; d++) {
2605 tmpUBF += (Ld::idFld(j, d) - 1) * uBF[d];
2606 tmp2UBF += uBF[d] * uBF[d];
2607 }
2608 } else {
2609 tmpUBF = 0.0;
2610 for(MInt dim = 0; dim < nDim; dim++) {
2611 uBF[dim] = (q - 1.0) * uF[dim] / q;
2612 tmpUBF += (Ld::idFld(Ld::oppositeDist(j), dim) - 1) * uBF[dim];
2613 tmp2UBF += uBF[dim] * uBF[dim];
2614 }
2615 }
2616
2617 fEqW = Ld::tp(tpIndex) * rhoF
2618 * (1.0 + tmpUBF * F1BCSsq + tmpUBF * tmpUBF * F1BCSsq * F1BCSsq * F1B2 - tmp2UBF * F1BCSsq * F1B2);
2619 fEqF = Ld::tp(tpIndex) * rhoF
2620 * (1.0 + tmpUF * F1BCSsq + tmpUF * tmpUF * F1BCSsq * F1BCSsq * F1B2 - tmp2UF * F1BCSsq * F1B2);
2621
2622 // perform relaxation to extrapolated values
2623 m_solver->a_oldDistribution(id, Ld::oppositeDist(j)) =
2624 (1.0 - m_omega) * (m_solver->a_distribution(id, Ld::oppositeDist(j)) - fEqF) + fEqW;
2625
2626 // add to momentum sum
2627 distributions[Ld::oppositeDist(j)] +=
2628 m_solver->a_oldDistribution(id, Ld::oppositeDist(j)) + m_solver->a_distribution(id, j);
2629
2630
2631 }
2632
2633 // if the cell is located at a corner, there might be distributions missing
2634 else {
2635 // In this case simple bounce back is used. This could decrease the overall accuracy.
2636 if(m_solver->a_hasNeighbor(id, j) == 0) {
2637 m_solver->a_oldDistribution(id, Ld::oppositeDist(j)) = m_solver->a_distribution(id, j);
2638
2639 // add to momentum sum
2640 distributions[Ld::oppositeDist(j)] +=
2641 m_solver->a_oldDistribution(id, Ld::oppositeDist(j)) + m_solver->a_distribution(id, j);
2642 }
2643 }
2644
2645 } // end of loop over all directions
2646
2647 }
2648
2649 //---------------------------------------------------------------------------------
2650 // 2. boundary cell is outside fluid
2651 //---------------------------------------------------------------------------------
2652
2653 // The outer cell does not belong to the flow field, thus the macroscopic values are held constant.
2654 // It is possible that there are cells without any cutting velocity! Those have to be controlled, too.
2655 else {
2656 for(MInt j = 0; j < nDist - 1; j++) {
2657 // if ( j < 6 )
2658 // dxB2 = cellHalfLength;
2659 // else {
2660 // if ( j < 18)
2661 // dxB2 = SQRT2 * cellHalfLength;
2662 // else
2663 // dxB2 = SQRT3 * cellHalfLength;
2664 // }
2665
2666 //----------------------------------------------------------------------------------------------------------------
2667 // if q_innerNeighbor = 1 - q_bndCell > 0.5, perform bounceback on inner neighbor
2668 // if the inner neighbor is a halo cell, it can be skipped
2669 //----------------------------------------------------------------------------------------------------------------
2670
2671 if(m_solver->a_hasNeighbor(id, j) > 0 && !m_solver->a_isHalo(m_solver->c_neighborId(id, j))) {
2672 if(m_bndCells[i].m_distances[j] <= 0.5) {
2673 q = F1 - m_bndCells[i].m_distances[j]; // q = 1 - 0.5*(distance/cellHalfLength);
2674
2675 nghbrId = m_solver->c_neighborId(id, j);
2676
2677 rhoF = m_solver->a_variable(nghbrId, PV->RHO);
2678 tmp2UF = 0.0;
2679 for(MInt d = 0; d < nDim; d++) {
2680 uF[d] = m_solver->a_variable(id, d);
2681 tmp2UF += uF[d] * uF[d];
2682 b[2 * d] = -uF[d];
2683 b[2 * d + 1] = uF[d];
2684 }
2685
2686 if(j < Ld::distFld(0)) {
2687 tmpUF = b[Ld::oppositeDist(j)];
2688 tpIndex = 1;
2689 } else {
2690 if(j < (Ld::distFld(0) + Ld::distFld(1))) {
2691 k = Ld::oppositeDist(j) - Ld::distFld(0);
2692 tmpUF = (b[Ld::mFld1(2 * k)] + b[Ld::mFld1(2 * k + 1)]);
2693 tpIndex = 2;
2694 } else {
2695 k = Ld::oppositeDist(j) - (Ld::distFld(0) + Ld::distFld(1));
2696 tmpUF = (b[Ld::mFld2(3 * k)] + b[Ld::mFld2(3 * k + 1)] + b[Ld::mFld2(3 * k + 2)]);
2697 tpIndex = 3;
2698 }
2699 }
2700
2701 // 0.75 <= q
2702 if(q >= F3B4) {
2703 tmpUBF = 0.0;
2704 for(MInt dim = 0; dim < nDim; dim++) {
2705 uBF[dim] = (q - 1.0) * uF[dim] / q;
2706 tmpUBF += (Ld::idFld(Ld::oppositeDist(j), dim) - 1) * uBF[dim];
2707 tmp2UBF += uBF[dim] * uBF[dim];
2708 }
2709
2710 }
2711
2712 // 0.5 < q < 0.75
2713 else {
2714 if(m_solver->a_hasNeighbor(id, Ld::oppositeDist(j)) > 0) {
2715 nghbrId = m_solver->c_neighborId(id, Ld::oppositeDist(j));
2716
2717 // extrapolate velocity from next inner cell
2718 uBF[0] = (q - 1.0) * m_solver->a_variable(id, PV->U)
2719 + (1.0 - q) * (q - 1.0) * m_solver->a_variable(nghbrId, PV->U) / (q + 1.0);
2720 uBF[1] = (q - 1.0) * m_solver->a_variable(id, PV->V)
2721 + (1.0 - q) * (q - 1.0) * m_solver->a_variable(nghbrId, PV->V) / (q + 1.0);
2722 IF_CONSTEXPR(nDim == 3)
2723 uBF[2] = (q - 1.0) * m_solver->a_variable(id, PV->W)
2724 + (1.0 - q) * (q - 1.0) * m_solver->a_variable(nghbrId, PV->W) / (q + 1.0);
2725 tmpUBF = 0.0;
2726 tmp2UBF = 0.0;
2727 for(MInt d = 0; d < nDim; d++) {
2728 tmpUBF += (Ld::idFld(j, d) - 1) * uBF[d];
2729 tmp2UBF += uBF[d] * uBF[d];
2730 }
2731 } else {
2732 tmpUBF = 0.0;
2733 for(MInt dim = 0; dim < nDim; dim++) {
2734 uBF[dim] = (q - 1.0) * uF[dim] / q;
2735 tmpUBF += (Ld::idFld(Ld::oppositeDist(j), dim) - 1) * uBF[dim];
2736 tmp2UBF += uBF[dim] * uBF[dim];
2737 }
2738 }
2739 }
2740
2741 fEqW = Ld::tp(tpIndex) * rhoF
2742 * (1.0 + tmpUBF * F1BCSsq + tmpUBF * tmpUBF * F1BCSsq * F1BCSsq * F1B2 - tmp2UBF * F1BCSsq * F1B2);
2743 fEqF = Ld::tp(tpIndex) * rhoF
2744 * (1.0 + tmpUF * F1BCSsq + tmpUF * tmpUF * F1BCSsq * F1BCSsq * F1B2 - tmp2UF * F1BCSsq * F1B2);
2745
2746 // perform relaxation to extrapolated values
2747 m_solver->a_oldDistribution(nghbrId, j) =
2748 (1.0 - m_omega) * (m_solver->a_distribution(nghbrId, j) - fEqF) + fEqW;
2749
2750 // add to momentum sum
2751 distributions[j] +=
2752 m_solver->a_oldDistribution(nghbrId, j) + m_solver->a_distribution(nghbrId, Ld::oppositeDist(j));
2753 // distributions[j] += 2.0 * m_solver->a_distribution(neighborId, Ld::oppositeDist(j));
2754 }
2755 }
2756
2757 } // end of loop over all directions
2758
2759 // The outer cell does not belong to the flow field, thus its incoming distributions are overwritten.
2760 // It is possible that there are cells without any cutting velocity! These are considered too.
2761 m_solver->a_variable(id, PV->RHO) = 1.0;
2762 for(MInt d = 0; d < nDim; d++) {
2763 m_solver->a_variable(id, d) = 0.0;
2764 }
2765
2766 m_solver->a_oldVariable(id, PV->RHO) = 1.0;
2767 for(MInt d = 0; d < nDim; d++) {
2768 m_solver->a_oldVariable(id, d) = 0.0;
2769 }
2770
2771 // Calculation of eq-distributions in bnd cell
2772 //--------------------------------------------
2773 // Calculation of distributions for directions with only one component
2774 for(MInt j = 0; j < Ld::distFld(0); j++) {
2775 m_solver->a_oldDistribution(id, j) = Ld::tp(1);
2776 }
2777
2778 // Calculation of distributions for directions with two components
2779 tmpDistId = Ld::distFld(0);
2780 for(MInt j = 0; j < Ld::distFld(1); j++) {
2781 m_solver->a_oldDistribution(id, tmpDistId + j) = Ld::tp(2);
2782 }
2783
2784 // Calculation of distributions for directions with three components
2785 tmpDistId = Ld::distFld(0) + Ld::distFld(1);
2786 for(MInt j = 0; j < Ld::distFld(2); j++) {
2787 m_solver->a_oldDistribution(id, tmpDistId + j) = Ld::tp(3);
2788 }
2789
2790 // Calculation of distribution for rest particle distribution (center)
2791 m_solver->a_oldDistribution(id, Ld::lastId()) = Ld::tp(0);
2792 }
2793
2794 } // end of loop over all m_bndcells
2795}
2796
2814template <MInt nDim, MInt nDist, class SysEqn>
2816 TRACE();
2817
2818 const MFloat wT = getBoundaryTemperature(index);
2819
2820 std::array<MFloat, nDim> uW{};
2821 getBoundaryVelocity(index, uW.data());
2822
2823 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
2824 // if((globalTimeStep - 1 ) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0 )
2825 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
2826 // since it is a rhsBnd it should be performed after propagation
2827 // TODO labels:LB,totest Not tested yet for a locally refined mesh
2828
2829 interpolatedBounceBackSingleSpecies(i, uW.data());
2830 const MInt pCellId = m_bndCells[i].m_cellId;
2831
2832 // Set constant temperature
2833 calculateEqDistsWallSingleSpeciesThermal(pCellId, wT);
2834 }
2835}
2836
2853template <MInt nDim, MInt nDist, class SysEqn>
2855 TRACE();
2856
2857 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
2858 const MInt currentId = m_bndCells[i].m_cellId;
2859 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(currentId)) != 0) continue;
2860 // since it is a rhsBnd it should be performed after propagation
2861 // TODO labels:LB,totest Not tested yet for a locally refined mesh
2862
2863 // leave out halo cells
2864 if(m_solver->a_isHalo(currentId)) continue;
2865
2866 for(MInt j = 0; j < nDist - 1; j++) {
2867 if(m_solver->a_hasNeighbor(currentId, Ld::oppositeDist(j)) == 0) {
2868 m_solver->a_oldDistribution(currentId, j) = m_solver->a_oldDistribution(currentId, Ld::oppositeDist(j));
2869 m_solver->a_oldDistributionThermal(currentId, j) =
2870 m_solver->a_oldDistributionThermal(currentId, Ld::oppositeDist(j));
2871 }
2872 }
2873 }
2874}
2875
2888template <MInt nDim, MInt nDist, class SysEqn>
2890 TRACE();
2891
2892 //---------------------------------------------------------------------------------
2893 // go through m_bndcells with wallbndcnd;
2894 // the halo cells have to be included, since they may be of relevance in case 2
2895 //---------------------------------------------------------------------------------
2896 MInt p = 0;
2897 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++, p++) {
2898 const MInt currentId = m_bndCells[i].m_cellId;
2899 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(currentId)) != 0) continue;
2900 // since it is a rhsBnd it should be performed after propagation
2901 // TODO labels:LB,totest Not tested yet for a locally refined mesh
2902
2903 // Set constant temperature
2904 // m_solver->a_variable(currentId, PV->T) = m_solver->m_initTemperatureKelvin;
2905 // m_solver->a_oldVariable(currentId, PV->T) = m_solver->m_initTemperatureKelvin;
2906
2907 // If there are several levels of boundary cells use only the highest
2908 // nearest neighbors, must be of the same level
2909 if(m_solver->a_level(currentId) < m_solver->maxLevel()) continue;
2910
2911 //----------------------------------------------------------------------------------------------------
2912 // perform bounce back for distributions piercing the wall - distinquish between inner and outer cells
2913 //----------------------------------------------------------------------------------------------------
2914
2915 //---------------------------------------------------------------------------------
2916 // case 1: boundary cell is inside fluid
2917 //---------------------------------------------------------------------------------
2918 if(m_bndCells[i].m_isFluid) {
2919 for(MInt j = 0; j < nDist - 1; j++) {
2920 // if ( j < 6 )
2921 // dxB2 = cellHalfLength;
2922 // else {
2923 // if ( j < 18)
2924 // dxB2 = SQRT2 * cellHalfLength;
2925 // else
2926 // dxB2 = SQRT3 * cellHalfLength;
2927 // }
2928
2929 //---------------------------------------------------------------------------------
2930 // if q <= 0.5, perform bounce back
2931 //---------------------------------------------------------------------------------
2932
2933 if(m_bndCells[i].m_distances[j] <= 0.5) {
2934 const MFloat F2q =
2935 F2 * m_bndCells[i].m_distances[j]; // q = distance / dx; distance: distance between cell center and wall
2936 if(m_solver->a_hasNeighbor(currentId, Ld::oppositeDist(j)) > 0) {
2937 m_solver->a_oldDistribution(currentId, Ld::oppositeDist(j)) =
2938 (F2q * m_solver->a_distribution(currentId, j))
2939 + ((1 - F2q) * m_solver->a_oldDistribution(currentId, j));
2940 m_solver->a_oldDistributionThermal(currentId, Ld::oppositeDist(j)) =
2941 (F2q * m_solver->a_distributionThermal(currentId, j))
2942 + ((1 - F2q) * m_solver->a_oldDistributionThermal(currentId, j));
2943 }
2944 // If opposite neighbors are missing, an incoming distribution has to be extrapolated from another cell inside
2945 // the fluid This could decrease the overall accuracy
2946 else {
2947 // do simple bounce back
2948 m_solver->a_oldDistribution(currentId, Ld::oppositeDist(j)) = m_solver->a_distribution(currentId, j);
2949 m_solver->a_oldDistributionThermal(currentId, Ld::oppositeDist(j)) =
2950 m_solver->a_distributionThermal(currentId, j);
2951 }
2952 }
2953 // if no cut was found and there is no neighbor
2954 // then cell is located at a corner.
2955 else {
2956 // In this case simple bounce back is used. This could decrease the overall accuracy.
2957 if(m_solver->a_hasNeighbor(currentId, j) == 0) {
2958 m_solver->a_oldDistribution(currentId, Ld::oppositeDist(j)) = m_solver->a_distribution(currentId, j);
2959 m_solver->a_oldDistributionThermal(currentId, Ld::oppositeDist(j)) =
2960 m_solver->a_distributionThermal(currentId, j);
2961 }
2962 }
2963 } // end of loop over all directions
2964 }
2965
2966 //---------------------------------------------------------------------------------
2967 // case 2: boundary cell is outside fluid
2968 //---------------------------------------------------------------------------------
2969
2970 else {
2971 for(MInt j = 0; j < nDist - 1; j++) {
2972 // if ( j < 6 )
2973 // dxB2 = cellHalfLength;
2974 // else {
2975 // if ( j < 18)
2976 // dxB2 = SQRT2 * cellHalfLength;
2977 // else
2978 // dxB2 = SQRT3 * cellHalfLength;
2979 // }
2980
2981 //----------------------------------------------------------------------------------------------------------------
2982 // if q_innerNeighbor = 1 - q_bndCell > 0.5, perform bounceback on inner neighbor
2983 // if the inner neighbor is a halo cell, it can be skipped
2984 //----------------------------------------------------------------------------------------------------------------
2985 // if ( m_solver->a_hasNeighbor(currentId, j) > 0 && m_solver->c_neighborId(currentId, j) <
2986 // m_solver->noInternalCells()
2987 // )
2988 if(m_solver->a_hasNeighbor(currentId, j) > 0 && !m_solver->a_isHalo(m_solver->c_neighborId(currentId, j))) {
2989 const MInt neighborId = m_solver->c_neighborId(currentId, j);
2990
2991 const MFloat F2q = F2 - F2 * m_bndCells[i].m_distances[j]; // q = 1 - 0.5*(distance/cellHalfLength);
2992
2993 if(m_bndCells[i].m_distances[j] < 0.5) {
2994 m_solver->a_oldDistribution(neighborId, j) =
2995 (m_solver->a_distribution(neighborId, Ld::oppositeDist(j)) / F2q)
2996 + (m_solver->a_distribution(neighborId, j) * (F2q - 1.0) / F2q);
2997 m_solver->a_oldDistributionThermal(neighborId, j) =
2998 (m_solver->a_distributionThermal(neighborId, Ld::oppositeDist(j)) / F2q)
2999 + (m_solver->a_distributionThermal(neighborId, j) * (F2q - 1.0) / F2q);
3000 }
3001 }
3002
3003 } // end of loop over all directions
3004
3005 // The outer cell does not belong to the flow field, thus its incoming distributions are overwritten.
3006 // It is possible that there are cells without any cutting velocity! These are considered too.
3007
3008 m_solver->a_variable(currentId, PV->RHO) = 1.0;
3009 m_solver->a_oldVariable(currentId, PV->RHO) = 1.0;
3010 for(MInt dir = 0; dir < nDim; dir++) {
3011 m_solver->a_variable(currentId, PV->U + dir) = 0.0;
3012 m_solver->a_oldVariable(currentId, PV->U + dir) = 0.0;
3013 }
3014 m_solver->a_variable(currentId, PV->T) = m_solver->m_initTemperatureKelvin;
3015 m_solver->a_oldVariable(currentId, PV->T) = m_solver->m_initTemperatureKelvin;
3016
3017 const MFloat T = m_solver->a_variable(currentId, PV->T);
3018
3019 // Calculation of eq-distributions in bnd cell
3020 //--------------------------------------------
3021 // Calculation of distributions for directions with only one component
3022 for(MInt j = 0; j < Ld::distFld(0); j++) {
3023 m_solver->a_oldDistribution(currentId, j) = Ld::tp(1);
3024 // new
3025 m_solver->a_oldDistributionThermal(currentId, j) = Ld::tp(1) * F1BCSsq * T * CSsq;
3026 }
3027
3028 // Calculation of distributions for directions with two components
3029 MInt tmpDistId = Ld::distFld(0);
3030 for(MInt j = 0; j < Ld::distFld(1); j++) {
3031 m_solver->a_oldDistribution(currentId, tmpDistId + j) = Ld::tp(2);
3032 // m_solver->a_oldDistributionThermal(currentId, j) = Ld::tp(1) * F1BCSsq * T * CSsq;
3033 m_solver->a_oldDistributionThermal(currentId, tmpDistId + j) = Ld::tp(2) * F1BCSsq * T * CSsq;
3034 }
3035
3036 // Calculation of distributions for directions with three components
3037 tmpDistId = Ld::distFld(0) + Ld::distFld(1);
3038 for(MInt j = 0; j < Ld::distFld(2); j++) {
3039 m_solver->a_oldDistribution(currentId, tmpDistId + j) = Ld::tp(3);
3040 // m_solver->a_oldDistributionThermal(currentId, tmpDistId + j) = Ld::tp(2) * F1BCSsq * T * CSsq;
3041 m_solver->a_oldDistributionThermal(currentId, tmpDistId + j) = Ld::tp(3) * F1BCSsq * T * CSsq;
3042 }
3043
3044 // Calculation of distribution for rest particle distribution (center)
3045 m_solver->a_oldDistribution(currentId, Ld::lastId()) = Ld::tp(0);
3046 m_solver->a_oldDistributionThermal(currentId, Ld::lastId()) = Ld::tp(0) * T;
3047 }
3048 } // end of loop over all m_bndcells
3049}
3050
3066template <MInt nDim, MInt nDist, class SysEqn>
3068 TRACE();
3069
3070 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
3071 const MInt currentId = m_bndCells[i].m_cellId;
3072 // if( (globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(currentId)) != 0 )
3073 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(currentId)) != 0) continue;
3074 // since it is a rhsBnd it should be performed after propagation
3075 // TODO labels:LB,totest Not tested yet for a locally refined mesh
3076
3077 // extrapolate inner density values
3078 const MInt neighborDir = (m_solver->a_hasNeighbor(currentId, 2) == 0) ? 3 : 2;
3079 const MInt neighborId = m_solver->c_neighborId(currentId, neighborDir);
3080
3081 // get old values
3082 const MFloat old_rho = m_solver->a_oldVariable(currentId, PV->RHO);
3083 const MFloat old_T = m_solver->m_initTemperatureKelvin;
3084
3085 // non-reflecting bc (Finck, Haenel 2008)
3086 // rho = ( old_rho + F1BCS * ( sqrt(tmp2) - sqrt(old_tmp2) ) + m_rho1 ) / 2.0 ;
3087 const MFloat rho = F1B2 * (m_solver->a_variable(neighborId, PV->RHO) + old_rho);
3088 const MFloat T = F1B2 * (m_solver->a_variable(neighborId, PV->T) + old_T);
3089
3090
3091 // Calculation of eq-distributions in bnd cell
3092 //--------------------------------------------
3093
3094 // Calculation of distributions for directions with only one component
3095 for(MInt j = 0; j < Ld::distFld(0); j++) {
3096 m_solver->a_oldDistribution(currentId, j) = Ld::tp(1) * F1BCSsq * rho * CSsq;
3097 m_solver->a_distribution(currentId, j) = m_solver->a_oldDistribution(currentId, j);
3098
3099 m_solver->a_oldDistributionThermal(currentId, j) = Ld::tp(1) * F1BCSsq * T * CSsq;
3100 m_solver->a_distributionThermal(currentId, j) = m_solver->a_oldDistributionThermal(currentId, j);
3101 }
3102
3103 // Calculation of distributions for directions with two components
3104 MInt tmpDistId = Ld::distFld(0);
3105 for(MInt j = 0; j < Ld::distFld(1); j++) {
3106 m_solver->a_oldDistribution(currentId, tmpDistId + j) = Ld::tp(2) * F1BCSsq * rho * CSsq;
3107 m_solver->a_distribution(currentId, tmpDistId + j) = m_solver->a_oldDistribution(currentId, tmpDistId + j);
3108
3109 m_solver->a_oldDistributionThermal(currentId, tmpDistId + j) = Ld::tp(2) * F1BCSsq * T * CSsq;
3110 m_solver->a_distributionThermal(currentId, tmpDistId + j) =
3111 m_solver->a_oldDistributionThermal(currentId, tmpDistId + j);
3112 }
3113
3114 // Calculation of distributions for directions with three components
3115 tmpDistId = Ld::distFld(0) + Ld::distFld(1);
3116 for(MInt j = 0; j < Ld::distFld(2); j++) {
3117 m_solver->a_oldDistribution(currentId, tmpDistId + j) = Ld::tp(3) * F1BCSsq * rho * CSsq;
3118 m_solver->a_distribution(currentId, tmpDistId + j) = m_solver->a_oldDistribution(currentId, tmpDistId + j);
3119
3120 m_solver->a_oldDistributionThermal(currentId, tmpDistId + j) = Ld::tp(3) * F1BCSsq * T * CSsq;
3121 m_solver->a_distributionThermal(currentId, tmpDistId + j) =
3122 m_solver->a_oldDistributionThermal(currentId, tmpDistId + j);
3123 }
3124
3125 // Calculation of distribution for rest particle distribution (center)
3126 m_solver->a_oldDistribution(currentId, Ld::lastId()) = Ld::tp(0) * rho;
3127 m_solver->a_distribution(currentId, Ld::lastId()) = m_solver->a_oldDistribution(currentId, Ld::lastId());
3128
3129 m_solver->a_oldDistributionThermal(currentId, Ld::lastId()) = Ld::tp(0) * T;
3130 m_solver->a_distributionThermal(currentId, Ld::lastId()) =
3131 m_solver->a_oldDistributionThermal(currentId, Ld::lastId());
3132
3133 m_solver->a_variable(currentId, PV->RHO) = rho;
3134 for(MInt dir = 0; dir < nDim; dir++) {
3135 m_solver->a_variable(currentId, PV->U + dir) = 0.0;
3136 }
3137 m_solver->a_variable(currentId, PV->T) = T;
3138 }
3139}
3140
3151template <MInt nDim, MInt nDist, class SysEqn>
3153 TRACE();
3154
3155 MInt id;
3156 MInt tmpDistId;
3157
3158 //---------------------------------------------------------------------------------
3159 // go through m_bndcells with wallbndcnd;
3160 // the halo cells have to be included, since they may be of relevance in case 2
3161 //---------------------------------------------------------------------------------
3162 MInt p = 0;
3163 // MFloat min_x=16.0;
3164 // MFloat min_z=7.0;
3165 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++, p++) {
3166 // if((globalTimeStep - 1 ) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0 )
3167 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
3168 // since it is a rhsBnd it should be performed after propagation
3169 // TODO labels:LB,totest Not tested yet for a locally refined mesh
3170
3171 id = m_bndCells[i].m_cellId;
3172
3173 // Set constant temperature
3174 m_solver->a_variable(id, PV->T) = m_solver->m_initTemperatureKelvin;
3175 m_solver->a_oldVariable(id, PV->T) = m_solver->m_initTemperatureKelvin;
3176
3177 for(MInt d = 0; d < nDim; d++) {
3178 m_solver->a_variable(id, d) = 0.0;
3179 }
3180
3181 MFloat T = m_solver->a_variable(id, PV->T);
3182 MFloat rho = m_solver->a_variable(id, PV->RHO);
3183
3184 for(MInt j = 0; j < Ld::distFld(0); j++) {
3185 m_solver->a_oldDistribution(id, j) = Ld::tp(1) * F1BCSsq * rho * CSsq;
3186 m_solver->a_oldDistributionThermal(id, j) = Ld::tp(1) * F1BCSsq * T * CSsq;
3187 }
3188
3189 // Calculation of eq-distributions for directions with two components
3190 tmpDistId = Ld::distFld(0);
3191 for(MInt j = 0; j < Ld::distFld(1); j++) {
3192 m_solver->a_oldDistribution(id, tmpDistId + j) = Ld::tp(2) * F1BCSsq * rho * CSsq;
3193 m_solver->a_oldDistributionThermal(id, tmpDistId + j) = Ld::tp(2) * F1BCSsq * T * CSsq;
3194 }
3195
3196 // Calculation of eq-distributions for directions with three components
3197 tmpDistId = Ld::distFld(0) + Ld::distFld(1);
3198 for(MInt j = 0; j < Ld::distFld(2); j++) {
3199 m_solver->a_oldDistribution(id, tmpDistId + j) = Ld::tp(3) * F1BCSsq * rho * CSsq;
3200 m_solver->a_oldDistributionThermal(id, tmpDistId + j) = Ld::tp(3) * F1BCSsq * T * CSsq;
3201 }
3202
3203 m_solver->a_oldDistribution(id, Ld::lastId()) = Ld::tp(0) * rho;
3204 m_solver->a_oldDistributionThermal(id, Ld::lastId()) = Ld::tp(0) * T;
3205 }
3206}
3207
3208
3228template <MInt nDim, MInt nDist, class SysEqn>
3230 TRACE();
3231
3232 MInt tmpDistId;
3233
3234 constexpr MInt lb_proj_zplane_neigh[27] = {-1, -1, -1, -1, 26, -1, -1, -1, -1, -1, 0, -1, 1, -1,
3235 2, -1, 3, -1, 6, -1, 7, -1, 8, -1, 9, -1, -1};
3236
3237 MInt nocells = m_bndCndOffsets[index + 1] - m_bndCndOffsets[index];
3238 ScratchSpace<MFloat> ghosts(nocells, nDist, AT_, "ghosts");
3239
3240 MInt bndId = 0;
3241 MFloat T_inner = 0.0, T_outer = 0.0, T_wall = m_solver->m_initTemperatureKelvin;
3242
3243 for(MInt num = 0; num < nocells; num++) {
3244 bndId = num + m_bndCndOffsets[index];
3245 T_inner = m_solver->a_variable(m_bndCells[bndId].m_cellId, PV->T);
3246
3247 T_outer = 2 * T_wall - T_inner;
3248
3249 for(MInt j = 0; j < Ld::distFld(0); j++)
3250 ghosts(num, j) = Ld::tp(1) * F1BCSsq * T_outer * CSsq;
3251
3252 // Calculation of eq-distributions for directions with two components
3253 tmpDistId = Ld::distFld(0);
3254 for(MInt j = 0; j < Ld::distFld(1); j++)
3255 ghosts(num, tmpDistId + j) = Ld::tp(2) * F1BCSsq * T_outer * CSsq;
3256
3257 // Calculation of eq-distributions for directions with three components
3258 tmpDistId = Ld::distFld(0) + Ld::distFld(1);
3259 for(MInt j = 0; j < Ld::distFld(2); j++)
3260 ghosts(num, tmpDistId + j) = Ld::tp(3) * F1BCSsq * T_outer * CSsq;
3261
3262 ghosts(num, Ld::lastId()) = Ld::tp(0) * T_outer;
3263 }
3264
3265 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
3266 // if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0 )
3267 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
3268 // since it is a rhsBnd it should be performed after propagation
3269 // TODO labels:LB,totest Not tested yet for a locally refined mesh
3270
3271 // leave out halo cells
3272 // if (m_bndCells[i].m_cellId >= m_solver->noInternalCells())
3273 // continue;
3274
3275 if(m_solver->a_isHalo(m_bndCells[i].m_cellId)) continue;
3276
3277
3278 MInt proj_dir = 0;
3279 MInt proj_neigh = 0;
3280 for(MInt j = 0; j < nDist - 1; j++) {
3281 if(m_solver->a_hasNeighbor(m_bndCells[i].m_cellId, Ld::oppositeDist(j)) == 0) {
3282 m_solver->a_oldDistribution(m_bndCells[i].m_cellId, j) =
3283 m_solver->a_distribution(m_bndCells[i].m_cellId, Ld::oppositeDist(j));
3284
3285 proj_dir = lb_proj_zplane_neigh[Ld::oppositeDist(j)];
3286
3287 if(proj_dir < 0) continue;
3288
3289 if(proj_dir == 26)
3290 proj_neigh = i - m_bndCndOffsets[index];
3291 else {
3292 for(MInt l = m_bndCndOffsets[index]; l < m_bndCndOffsets[index + 1]; l++)
3293 if(m_bndCells[l].m_cellId == m_solver->c_neighborId(m_bndCells[i].m_cellId, proj_dir)) {
3294 proj_neigh = l - m_bndCndOffsets[index];
3295 break;
3296 }
3297 }
3298
3299
3300 if(proj_neigh >= 0 && proj_neigh < nocells)
3301 m_solver->a_oldDistributionThermal(m_bndCells[i].m_cellId, j) = ghosts(proj_neigh, j);
3302 }
3303 }
3304 }
3305
3306
3307 // for(MInt i = 0; i < nocells; i++) delete[] ghosts[i];
3308 // delete[] ghosts;
3309}
3310
3311
3338template <MInt nDim, MInt nDist, class SysEqn>
3340 TRACE();
3341
3342 MInt start = m_bndCndOffsets[index];
3343 MInt end = m_bndCndOffsets[index + 1];
3344
3345 MFloat T_wall = m_solver->m_initTemperatureKelvin;
3346 MFloat eps = 0.0000000000001;
3347
3348 std::array<MFloat, nDim> uW{};
3349 getBoundaryVelocity(index, uW.data());
3350
3351 for(MInt i = start; i < end; i++) {
3352 // if((globalTimeStep - 1 ) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0 )
3353 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
3354 // since it is a rhsBnd it should be performed after propagation
3355 // TODO labels:LB,totest Not tested yet for a locally refined mesh
3356
3357 // 1. do an interpolated bounce back as done in BC 2000 for the velocity and the
3358 // density distributions
3359 interpolatedBounceBackSingleSpecies(i, uW.data());
3360
3361 MInt currentId = m_bndCells[i].m_cellId;
3362 const MInt pCellId = currentId;
3363
3364 // 2. do an interpolated prescription of the PPDFs for the temperature
3365
3366 //---------------------------------------------------------------------------------
3367 // case 1: boundary cell is inside fluid
3368 //---------------------------------------------------------------------------------
3369 if(m_bndCells[i].m_isFluid) {
3370 for(MInt j = 0; j < nDist - 1; j++) {
3371 // if we do not have a neighbor in the current direction
3372 if(m_solver->a_hasNeighbor(pCellId, j) == 0) {
3373 MFloat T_inner = m_solver->a_variable(currentId, PV->T);
3374 MFloat T_outer = 0.0;
3375 if(m_bndCells[i].m_distances[j] > eps) {
3376 T_outer = (T_wall - T_inner) / (m_bndCells[i].m_distances[j]) + T_inner;
3377 // Calculate single eq-distribution in direction j and overwrite neighbor
3378 m_solver->a_oldDistributionThermal(currentId, Ld::oppositeDist(j)) =
3379 Ld::tp(Ld::distType(j)) * F1BCSsq * T_outer * CSsq;
3380
3381 } else {
3382 // Calculate single eq-distribution in direction j and overwrite neighbor
3383 m_solver->a_oldDistributionThermal(currentId, Ld::oppositeDist(j)) =
3384 Ld::tp(Ld::distType(j)) * F1BCSsq * T_wall * CSsq;
3385 }
3386 }
3387 }
3388 }
3389
3390 //---------------------------------------------------------------------------------
3391 // case 2: boundary cell is outside fluid
3392 //---------------------------------------------------------------------------------
3393 else {
3394 for(MInt j = 0; j < nDist; j++) {
3395 // if we have a neighbor
3396 if(m_solver->a_hasNeighbor(pCellId, j) != 0)
3397 if(!m_solver->a_isBndryCell(m_solver->c_neighborId(pCellId, j))) {
3398 MInt neighborId = m_solver->c_neighborId(pCellId, j);
3399
3400 MFloat T_inner = m_solver->a_variable(neighborId, PV->T);
3401 MFloat T_outer = (T_wall - T_inner) / (1.0 - m_bndCells[i].m_distances[j]) + T_inner;
3402
3403 // Calculate single eq-distribution in direction j and overwrite neighbor
3404 m_solver->a_oldDistributionThermal(neighborId, j) = Ld::tp(Ld::distType(j)) * F1BCSsq * T_outer * CSsq;
3405 }
3406 }
3407 }
3408 }
3409}
3410
3421template <MInt nDim, MInt nDist, class SysEqn>
3422template <MInt direction>
3424 TRACE();
3425
3426 // constexpr sanity check
3427 if constexpr(direction > nDim - 1) {
3428 std::stringstream ss;
3429 ss << "Function not defined for nDim=" << nDim << " and direction =" << direction << std::endl;
3430 TERMM(1, ss.str());
3431 return;
3432 }
3433
3434 // set wall velocity
3435 std::array<MFloat, nDim> uW;
3436 std::array<MFloat, 2 * nDim> c;
3437 uW.fill(0.0);
3438 uW[direction / 2] = m_Ma * LBCS;
3439 for(MInt d = 0; d < nDim; d++) {
3440 c[2 * d] = -uW[d];
3441 c[2 * d + 1] = uW[d];
3442 }
3443
3444 // go through m_bndcells with wallbndcnd
3445 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
3446 const MInt id = m_bndCells[i].m_cellId;
3447
3448 // leave out halo cells
3449 if(m_solver->a_isHalo(id)) continue;
3450
3451 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
3452 // TODO labels:LB,totest Not tested yet for a locally refined mesh
3453
3454 // neighborId = m_solver->c_neighborId(id, Ld::oppositeDist(direction) );
3455
3456 // uW[0] = F2B3 * m_solver->a_variable(id, PV->U) - F1B2 * m_solver->a_variable(neighborId, PV->U);
3457 // uW[1] = F2B3 * m_solver->a_variable(id, PV->V) - F1B2 * m_solver->a_variable(neighborId, PV->V);
3458 // uW[2] = F2B3 * m_solver->a_variable(id, PV->W) - F1B2 * m_solver->a_variable(neighborId, PV->W);
3459
3460 const MFloat rhoF = m_solver->a_variable(id, PV->RHO);
3461
3462 /*
3463 for( MInt j = 0; j < Ld::dxQyFld(); j++){
3464
3465 currentDist = Ld::componentFld( Ld::oppositeDist(direction) , j);
3466
3467 if ( currentDist < 6 ) {
3468 tmpUW = c[currentDist];
3469 tpIndex = 1;
3470 }
3471 else {
3472 if ( currentDist < 18){
3473 k = currentDist - Ld::distFld(0);
3474 tmpUW = (c[Ld::mFld1(2*k)] + c[Ld::mFld1(2*k+1)]);
3475 tpIndex = 2;
3476 }
3477 else {
3478 k = currentDist - (Ld::distFld(0) + Ld::distFld(1));
3479 tmpUW = (c[Ld::mFld2(3*k)] + c[Ld::mFld2(3*k+1)] + c[Ld::mFld2(3*k+2)] );
3480 tpIndex = 3;
3481 }
3482 }
3483
3484 // 1. bounce back part
3485 // if(m_solver->a_hasNeighbor(id, currentDist) > 0){
3486 m_solver->a_oldDistribution(id, currentDist) = m_solver->a_distribution(id, Ld::oppositeDist(currentDist));
3487 // }
3488 // else{//if cell is at corner, extrapolate distribution from inner cell
3489 // for (MInt k=0; k < nDist-1; k++){
3490 // if(m_solver->a_hasNeighbor(id, k) > 0 && !m_solver->a_onlyBoundary(m_solver->c_neighborId(id, k)) )
3491 // m_solver->a_oldDistribution(id, currentDist) = m_solver->a_oldDistribution(m_solver->c_neighborId(id, k),
3492 Ld::oppositeDist(currentDist));
3493 // }
3494 // }
3495
3496 // 2. add momentum
3497 m_solver->a_oldDistribution(id, currentDist) += 2.0 * Ld::tp(tpIndex) * rhoF * F1BCSsq * tmpUW;// the
3498 minus corresponds to the opposite direction
3499
3500 }
3501 */
3502 for(MInt currentDist = 0; currentDist < nDist - 1; currentDist++) {
3503 // 1. bounce back part
3504 if(m_solver->a_hasNeighbor(id, Ld::oppositeDist(currentDist)) == 0) {
3505 m_solver->a_oldDistribution(id, currentDist) = m_solver->a_distribution(id, Ld::oppositeDist(currentDist));
3506 }
3507 // else{//if cell is at corner, extrapolate distribution from inner cell
3508 // for (MInt k=0; k < nDist-1; k++){
3509 // if(m_solver->a_hasNeighbor(id, k) > 0 && !m_solver->a_onlyBoundary(m_solver->c_neighborId(id, k)) )
3510 // m_solver->a_oldDistribution(id, currentDist) = m_solver->a_oldDistribution(m_solver->c_neighborId(id,
3511 // k), Ld::oppositeDist(currentDist));
3512 // }
3513 // }
3514
3515 // 2. add momentum
3516 MFloat tmpUW;
3517 if(currentDist < Ld::distFld(0)) {
3518 tmpUW = c[currentDist];
3519 } else if(currentDist < Ld::distFld(0) + Ld::distFld(1)) {
3520 const MInt k = currentDist - Ld::distFld(0);
3521 tmpUW = (c[Ld::mFld1(2 * k)] + c[Ld::mFld1(2 * k + 1)]);
3522 } else {
3523 const MInt k = currentDist - (Ld::distFld(0) + Ld::distFld(1));
3524 tmpUW = (c[Ld::mFld2(3 * k)] + c[Ld::mFld2(3 * k + 1)] + c[Ld::mFld2(3 * k + 2)]);
3525 }
3526 m_solver->a_oldDistribution(id, currentDist) += 2.0 * Ld::tp(Ld::distType(currentDist)) * rhoF * F1BCSsq * tmpUW;
3527 }
3528
3529
3530 } // end of loop over all m_bndcells
3531}
3532
3533
3550template <MInt nDim, MInt nDist, class SysEqn>
3552 TRACE();
3553
3554 const MFloat wT = getBoundaryTemperature(index);
3555
3556 std::array<MFloat, nDim> uW{};
3557 getBoundaryVelocity(index, uW.data());
3558
3559#ifdef WAR_NVHPC_PSTL
3560 const MInt globalTimeStep_ = globalTimeStep;
3561 MInt begin = m_bndCndOffsets[index];
3562 MInt end = m_bndCndOffsets[index + 1];
3563 MInt offset = end - begin;
3564
3565 maia::parallelFor<true>(0, offset, [=](MInt id) {
3566 MInt i = begin + id;
3567 if((globalTimeStep_) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) return;
3568#else
3569 maia::parallelFor(m_bndCndOffsets[index], m_bndCndOffsets[index + 1], [=](MInt i) {
3570 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) return;
3571#endif
3572 // since it is a rhsBnd it should be performed after propagation
3573 // TODO labels:LB,totest Not tested yet for a locally refined mesh
3574
3575 interpolatedBounceBackSingleSpecies(i, uW.data());
3576
3577 interpolatedBounceBackSingleSpeciesThermal(i, wT, uW.data());
3578 });
3579}
3580
3593template <MInt nDim, MInt nDist, class SysEqn>
3595 TRACE();
3596
3597 std::array<MFloat, nDim> uW{};
3598 getBoundaryVelocity(index, uW.data());
3599
3600 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
3601 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
3602
3603 MFloat wT = F0;
3604
3605 const MInt plugFlowDir = (bcMode == 0 && nDim == 3) ? 2 : 0;
3606 const MBool restingFluid = (bcMode == 0 && nDim == 2);
3607 const MInt pCellId = m_bndCells[i].m_cellId;
3608
3609 if(bcMode == 0) {
3610 if(nDim == 3) {
3611 // wave number and heat flux at the wall
3612 MFloat k = F2 * M_PI / (m_referenceLength * 2.0);
3613
3614 // since it is a rhsBnd it should be performed after propagation
3615 MFloat dz = m_solver->c_cellLengthAtLevel(m_solver->a_level(m_bndCells[i].m_cellId));
3616 MFloat z = m_solver->a_coordinate(m_bndCells[i].m_cellId, 2) / dz;
3617 z = z - m_referenceLength;
3618
3619 // 3D Pipe flow
3620 wT = cos(k * z);
3621 } else {
3622 // since it is a rhsBnd it should be performed after propagation
3623 MFloat x = m_solver->a_coordinate(m_bndCells[i].m_cellId, 0);
3624 MFloat y = m_solver->a_coordinate(m_bndCells[i].m_cellId, 1);
3625
3626 MFloat phi = atan(y / x);
3627 MFloat n = 2.0;
3628 wT = cos(n * phi);
3629 }
3630 // perform bounce back
3631 interpolatedBounceBackSingleSpeciesThermal(i, wT, uW.data());
3632 } else if(bcMode == 1) {
3633 // wave number and heat flux at the wall
3634 MFloat k = F2 * M_PI / (m_referenceLength);
3635
3636 // since it is a rhsBnd it should be performed after propagation
3637 MFloat dx = m_solver->c_cellLengthAtLevel(m_solver->a_level(m_bndCells[i].m_cellId));
3638 MFloat x = m_solver->a_coordinate(m_bndCells[i].m_cellId, 0) / dx;
3639
3640 // channel flow
3641 // heat flux in y direction
3642 wT = cos(k * x);
3643
3644 // perform bounce back
3645 interpolatedBounceBackSingleSpeciesThermal(i, wT, uW.data());
3646 } else if(bcMode == 2) {
3647 // wave number and heat flux at the wall
3648 MFloat k = F2 * M_PI / m_referenceLength;
3649 // since it is a rhsBnd it should be performed after propagation
3650 MFloat dx = m_solver->c_cellLengthAtLevel(m_solver->a_level(m_bndCells[i].m_cellId));
3651 MFloat x = m_solver->a_coordinate(m_bndCells[i].m_cellId, 0) / dx;
3652
3653 // channel flow
3654 // heat flux in y direction
3655 MFloat wq = (m_solver->m_kappa / m_referenceLength) * cos(k * x);
3656
3657 // perform bounce back
3658 // TODO: So far only valid for 2D simulations
3659 ASSERT(nDim == 2, "Not implemented for 3D channels yet.");
3660 interpolatedBounceBackSingleSpeciesThermalFlux(i, wq, index);
3661 }
3662
3663 // Set the density to 1.0 and the velocity to obtain a plug flow
3664 MFloat l_rho = 1.0;
3665 MFloat l_uu[nDim];
3666 for(MInt d = 0; d < nDim; d++)
3667 l_uu[d] = F0;
3668
3669 if(!restingFluid) l_uu[plugFlowDir] = m_Ma * LBCS;
3670
3671 MFloat squaredVelocity = std::inner_product(&l_uu[0], &l_uu[nDim], &l_uu[0], .0);
3672 m_solver->setEqDists(pCellId, l_rho, squaredVelocity, l_uu);
3673
3674 m_solver->a_variable(pCellId, PV->RHO) = l_rho;
3675 for(MInt n = 0; n < nDim; n++)
3676 m_solver->a_variable(pCellId, PV->VV[n]) = l_uu[n];
3677 }
3678}
3679
3700template <MInt nDim, MInt nDist, class SysEqn>
3702 TRACE();
3703
3704 std::array<MFloat, nDim> uW{};
3705 getBoundaryVelocity(index, uW.data());
3706
3707 // loop over cells and call coupled boundary condition
3708 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
3709 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
3710
3711 // -----------------------------------------------------
3712 // 1. Calculate wall concentration and wall temperature
3713 // -----------------------------------------------------
3714 MFloat Cw[nDist] = {F0};
3715 MFloat Tw[nDist] = {F0};
3716
3717 calculateWallInterface(i, Cw, Tw);
3718
3719 // ------------------------------------
3720 // 2. Perform interpolated bounce back
3721 // ------------------------------------
3722
3723 if(!m_mucosaModel.plugFlow) {
3724 // perform interpolated bounce back
3725 interpolatedBounceBackSingleSpecies(i, uW.data()); // no-slip velocity condition
3726 } else {
3727 // Set the density to 1.0 and the velocity to obtain a plug flow
3728 const MInt pCellId = m_bndCells[i].m_cellId;
3729 MFloat l_rho = 1.0;
3730 MFloat l_uu[nDim] = {0.0};
3731 if(nDim == 3) l_uu[2] = m_Ma * LBCS;
3732 MFloat squaredVelocity = std::inner_product(&l_uu[0], &l_uu[nDim], &l_uu[0], .0);
3733 m_solver->setEqDists(pCellId, l_rho, squaredVelocity, l_uu);
3734
3735 m_solver->a_variable(pCellId, PV->RHO) = l_rho;
3736 for(MInt n = 0; n < nDim; n++)
3737 m_solver->a_variable(pCellId, PV->VV[n]) = l_uu[n];
3738 }
3739
3740 interpolatedBounceBackSingleSpeciesTransport(i, Cw, uW.data());
3741 interpolatedBounceBackSingleSpeciesThermal(i, Tw, uW.data());
3742 }
3743}
3744
3757template <MInt nDim, MInt nDist, class SysEqn>
3759 TRACE();
3760
3767 m_mucosaModel.plugFlow = Context::getSolverProperty<MInt>("plugFlow", m_solverId, AT_);
3768
3777 MInt segmentId = Context::getSolverProperty<MInt>("charSegId", m_solverId, AT_);
3778
3779 // Get the boundary points of the segment
3780 MInt num = 0;
3781 MFloat** bndVs = m_solver->m_geometry->GetBoundaryVertices(segmentId, nullptr, nullptr, 0, &num);
3782
3783 // Calculate circumference
3784 MFloat circ = m_solver->m_geometry->calcCircumference(bndVs, num);
3785
3786 // Get size of surface
3787 MFloat size = m_solver->m_geometry->GetBoundarySize(segmentId);
3788
3789 // Get the hydraulic diameter
3790 MFloat tracheaDiameter = 4 * size / circ;
3791
3792 // define reference variables for conversion between dimensioned and dimensionless
3793 // length of one cell in relation to the referenceLength in meter
3794 m_mucosaModel.refDx = tracheaDiameter / m_referenceLength;
3795
3803 MFloat conductivity = 0.025;
3804 conductivity = Context::getSolverProperty<MFloat>("conductivity", m_solverId, AT_, &conductivity);
3805
3813 MFloat density = 1.225;
3814 density = Context::getSolverProperty<MFloat>("density", m_solverId, AT_, &density);
3815
3823 MFloat viscosity = 16.45 * 1e-6;
3824 viscosity = Context::getSolverProperty<MFloat>("viscosity", m_solverId, AT_, &viscosity);
3825
3833 MFloat mucosaCond = 0.6;
3834 mucosaCond = Context::getSolverProperty<MFloat>("mucosaCond", m_solverId, AT_, &mucosaCond);
3835
3843 MFloat organSideTemp = 37.0;
3844 organSideTemp = Context::getSolverProperty<MFloat>("organSideTemp", m_solverId, AT_, &organSideTemp);
3845
3853 MFloat mucosaThickness = 0.001;
3854 mucosaThickness = Context::getSolverProperty<MFloat>("mucosaThickness", m_solverId, AT_, &mucosaThickness);
3855
3863 MFloat mucosaDiff = 2.6 * 1e-5;
3864 mucosaDiff = Context::getSolverProperty<MFloat>("mucosaDiffusivity", m_solverId, AT_, &mucosaDiff);
3865
3873 MFloat bndryDiff = 3.0 * 1e-5;
3874 bndryDiff = Context::getSolverProperty<MFloat>("bndryDiffusivity", m_solverId, AT_, &bndryDiff);
3875
3876 // reference velocity in m/s
3877 MFloat refU = (m_solver->m_Re * viscosity) / tracheaDiameter;
3878
3879 // reference temperature in celsius
3880 m_mucosaModel.refT = organSideTemp;
3881
3882 // reference water fraction
3883 // Inthavong et al (2018) "Wet surface wall model for latent heat exchange during evaporation"
3884 MFloat T = m_mucosaModel.refT;
3885 MFloat refWaterFrac = (2.027 + 0.6036 * T - 0.010972 * T * T + 0.0006312 * T * T * T) * 1e-3;
3886
3887 // reference concentration
3888 m_mucosaModel.refC = refWaterFrac * density;
3889
3890 // reference diffusivity
3891 m_mucosaModel.refDiff = (refU * m_mucosaModel.refDx) / (m_Ma * LBCS);
3892
3893 // reference conductivity
3894 m_mucosaModel.refCondF = conductivity;
3895
3896 // concentration in the interior of the mucosa
3897 MFloat organSideConc = m_mucosaModel.refC;
3898
3899 // non-dimensional parameters of the nasal cavity flow
3900 m_mucosaModel.mucosaThickness = mucosaThickness / m_mucosaModel.refDx;
3901 m_mucosaModel.T_o = organSideTemp / m_mucosaModel.refT;
3902 m_mucosaModel.C_o = organSideConc / m_mucosaModel.refC;
3903 m_mucosaModel.condRatio = mucosaCond / conductivity;
3904 m_mucosaModel.diffRatio = mucosaDiff / bndryDiff;
3905
3906 // initialize an array for the old wall temperatures
3907 mAlloc(m_oldWallTemp, m_solver->a_noCells(), nDist, "oldWallTemp", m_mucosaModel.T_o, AT_);
3908
3909 // -----------------------------------------------
3910 // Calculate distances inside the mucousLayer
3911 // -----------------------------------------------
3912
3913 mAlloc(m_mucousDist, m_solver->a_noCells(), nDist, "mucousDist", -1.0, AT_);
3914 mAlloc(m_fluidDist, m_solver->a_noCells(), nDist, "fluidDist", -1.0, AT_);
3915 calculateSublayerDistances(index);
3916}
3917
3927template <MInt nDim, MInt nDist, class SysEqn>
3929 TRACE();
3930 // obtain a map where the number of missing distributions per cell are stored
3931 ibbBndIds.push_back(index);
3932
3933 MIntScratchSpace noMissDist(m_solver->a_noCells(), AT_, "noMissDist");
3934 noMissDist.fill(0);
3935
3936 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
3937 const MInt bndCellId = i;
3938 const MInt pCellId = m_bndCells[bndCellId].m_cellId;
3939 // case 1: boundary cell is fluid cell
3940 if(m_bndCells[bndCellId].m_isFluid) {
3941 if(m_solver->a_hasProperty(pCellId, Cell::IsHalo)) {
3942 noMissDist(pCellId) = -1;
3943 continue;
3944 }
3945 for(MInt j = 0; j < nDist - 1; j++) {
3946 const MInt opposite = Ld::oppositeDist(j);
3947 if(m_bndCells[bndCellId].m_distances[j] <= 0.5 && m_solver->a_hasNeighbor(pCellId, opposite) != 0) {
3948 MInt neighborId = m_solver->c_neighborId(pCellId, opposite);
3949 MInt nbndid = m_solver->a_bndId(neighborId);
3950 if(nbndid == -1 || m_bndCells[nbndid].m_isFluid) noMissDist(pCellId) = noMissDist(pCellId) + 1;
3951 }
3952 }
3953 }
3954 // case 2: boundary cell is solid cell
3955 else {
3956 for(MInt j = 0; j < nDist - 1; j++) {
3957 if(m_solver->a_hasNeighbor(pCellId, j) && m_bndCells[bndCellId].m_distances[j] < 0.5) {
3958 const MInt neighborId = m_solver->c_neighborId(pCellId, j);
3959 if(!m_solver->a_hasProperty(neighborId, Cell::IsHalo)) {
3960 if(m_bndCells[bndCellId].m_distances[j] < 0.5) {
3961 MInt nbndid = m_solver->a_bndId(neighborId);
3962 if(nbndid == -1 || m_bndCells[nbndid].m_isFluid) {
3963 if(m_solver->a_hasNeighbor(neighborId, j)) {
3964 const MInt neighbor2Id = m_solver->c_neighborId(neighborId, j);
3965 MInt n2bndid = m_solver->a_bndId(neighbor2Id);
3966 if(n2bndid == -1 || m_bndCells[n2bndid].m_isFluid)
3967 noMissDist(neighborId) = noMissDist(neighborId) + 1;
3968 }
3969 }
3970 }
3971 }
3972 }
3973 }
3974 }
3975 }
3976
3977 for(MInt i = 0; i < m_solver->a_noCells(); i++) {
3978 MInt no = noMissDist(i);
3979 noMissDistBnd.push_back(no);
3980 }
3981}
3982
3993template <MInt nDim, MInt nDist, class SysEqn>
3995 TRACE();
3996
3997 MFloat F2q, rhoF, uF[nDim], uBF[nDim], tmpUF, tmpUBF, tmp2UF, b[2 * nDim], tmpUW, c[2 * nDim];
3998 MFloat uW[nDim] = {F0};
3999 getBoundaryVelocity(index, uW);
4000
4001 MInt id, k, tpIndex;
4002 MFloat fEq, xi;
4003 MInt nghbrId;
4004
4005 for(MInt d = 0; d < nDim; d++) {
4006 c[2 * d] = -uW[d];
4007 c[2 * d + 1] = uW[d];
4008 }
4009
4010 const MFloat wT = getBoundaryTemperature(index);
4011
4012 // go through m_bndcells with wallbndcnd
4013 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
4014 // if((globalTimeStep - 1 ) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0 )
4015 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
4016 // since it is a rhsBnd it should be performed after propagation
4017 // TODO labels:LB,totest Not tested yet for a locally refined mesh
4018
4019 id = m_bndCells[i].m_cellId;
4020
4021 m_omega = 2.0 / (1.0 + 6.0 * m_solver->a_nu(id) * FFPOW2(m_solver->maxLevel() - m_solver->a_level(id)));
4022
4023 rhoF = 1.0; // standard value which is usually overwritten in the following. Only in exceptional case it remains 1
4024
4025 //----------------------------------------------------------------------------------------------------
4026 // perform bounce back for distributions piercing the wall - distinquish between inner and outer cells
4027 //----------------------------------------------------------------------------------------------------
4028
4029 //---------------------------------------------------------------------------------
4030 // 1. boundary cell is inside fluid
4031 //---------------------------------------------------------------------------------
4032 if(m_bndCells[i].m_isFluid) {
4033 if(m_densityFluctuations)
4034 rhoF = 1.0 + m_solver->a_variable(id, PV->RHO);
4035 else
4036 rhoF = m_solver->a_variable(id, PV->RHO);
4037
4038 for(MInt n = 0; n < nDim; n++) {
4039 uF[n] = m_solver->a_variable(id, PV->VV[n]);
4040 b[2 * n] = -uF[n];
4041 b[2 * n + 1] = uF[n];
4042 }
4043
4044 tmp2UF = std::inner_product(&uF[0], &uF[nDim], &uF[0], .0);
4045
4046 for(MInt j = 0; j < nDist - 1; j++) {
4047 if(j < 6) {
4048 tmpUF = b[j];
4049 tmpUW = c[j];
4050 tpIndex = 1;
4051 } else {
4052 if(j < 18) {
4053 k = j - Ld::distFld(0);
4054 tmpUF = (b[Ld::mFld1(2 * k)] + b[Ld::mFld1(2 * k + 1)]);
4055 tmpUW = (c[Ld::mFld1(2 * k)] + c[Ld::mFld1(2 * k + 1)]);
4056 tpIndex = 2;
4057 } else {
4058 k = j - (Ld::distFld(0) + Ld::distFld(1));
4059 tmpUF = (b[Ld::mFld2(3 * k)] + b[Ld::mFld2(3 * k + 1)] + b[Ld::mFld2(3 * k + 2)]);
4060 tmpUW = (c[Ld::mFld2(3 * k)] + c[Ld::mFld2(3 * k + 1)] + c[Ld::mFld2(3 * k + 2)]);
4061 tpIndex = 3;
4062 }
4063 }
4064
4065 //---------------------------------------------------------------------------------
4066 // if q < 0.5, perform bounce back
4067 //---------------------------------------------------------------------------------
4068
4069 if(m_bndCells[i].m_distances[j] < 0.5) {
4070 F2q = F2 * m_bndCells[i].m_distances[j];
4071
4072 if(m_solver->a_hasNeighbor(id, Ld::oppositeDist(j)) > 0) {
4073 nghbrId = m_solver->c_neighborId(id, Ld::oppositeDist(j));
4074
4075 tmpUBF = 0.0;
4076 for(MInt n = 0; n < nDim; n++) {
4077 uBF[n] = m_solver->a_variable(nghbrId, PV->VV[n]);
4078 tmpUBF += (Ld::idFld(j, n) - 1) * uBF[n];
4079 }
4080 } else {
4081 tmpUBF = tmpUF;
4082 }
4083
4084 fEq = Ld::tp(tpIndex) * rhoF
4085 * (1.0 + tmpUBF * F1BCSsq + tmpUF * tmpUF * F1BCSsq * F1BCSsq * F1B2 - tmp2UF * F1BCSsq * F1B2);
4086 xi = m_omega * (F2q - 1.0) / (1.0 - 2.0 * m_omega);
4087 // xi = m_omega * (F2q - 1.0) / (1.0 - m_omega);
4088
4089 // perform interpolated bounce back
4090 m_solver->a_oldDistribution(id, Ld::oppositeDist(j)) =
4091 (1.0 - xi) * m_solver->a_distribution(id, j) + xi * fEq
4092 - 2.0 * Ld::tp(tpIndex) * rhoF * F1BCSsq * tmpUW; // the minus corresponds to the opposite direction
4093
4094 }
4095
4096 // if the cell is located at a corner, there might be distributions missing
4097 else {
4098 // In this case simple bounce back is used. This could decrease the overall accuracy.
4099 if(m_solver->a_hasNeighbor(id, j) == 0) {
4100 m_solver->a_oldDistribution(id, Ld::oppositeDist(j)) = m_solver->a_distribution(id, j);
4101 }
4102 }
4103
4104 } // end of loop over all directions
4105
4106 }
4107
4108 //---------------------------------------------------------------------------------
4109 // 2. boundary cell is outside fluid
4110 // if the inner neighbor is a halo cell, it can be skipped
4111 //---------------------------------------------------------------------------------
4112
4113 // The outer cell does not belong to the flow field, thus the macroscopic values are held constant.
4114 // It is possible that there are cells without any cutting velocity! Those have to be controlled, too.
4115 else {
4116 for(MInt j = 0; j < nDist - 1; j++) {
4117 //----------------------------------------------------------------------------------------------------------------
4118 // if q_innerNeighbor = 1 - q_bndCell >= 0.5, perform bounceback on inner neighbor
4119 //----------------------------------------------------------------------------------------------------------------
4120 if(m_solver->a_hasNeighbor(id, j) > 0 && !m_solver->a_isHalo(m_solver->c_neighborId(id, j))) {
4121 if(m_bndCells[i].m_distances[j] <= 0.5) {
4122 F2q = F2 - F2 * m_bndCells[i].m_distances[j]; // q = 1 - 0.5*(distance/cellHalfLength);
4123
4124 nghbrId = m_solver->c_neighborId(id, j);
4125
4126 if(m_densityFluctuations)
4127 rhoF = 1.0 + m_solver->a_variable(nghbrId, PV->RHO);
4128 else
4129 rhoF = m_solver->a_variable(nghbrId, PV->RHO);
4130
4131 for(MInt n = 0; n < nDim; n++) {
4132 uF[n] = m_solver->a_variable(nghbrId, PV->VV[n]);
4133 b[2 * n] = -uF[n];
4134 b[2 * n + 1] = uF[n];
4135 }
4136
4137 tmp2UF = std::inner_product(&uF[0], &uF[nDim], &uF[0], .0);
4138
4139 if(j < 6) {
4140 tmpUF = b[Ld::oppositeDist(j)];
4141 tmpUW = c[Ld::oppositeDist(j)];
4142 tpIndex = 1;
4143 } else {
4144 if(j < 18) {
4145 k = Ld::oppositeDist(j) - Ld::distFld(0);
4146 tmpUF = (b[Ld::mFld1(2 * k)] + b[Ld::mFld1(2 * k + 1)]);
4147 tmpUW = (c[Ld::mFld1(2 * k)] + c[Ld::mFld1(2 * k + 1)]);
4148 tpIndex = 2;
4149 } else {
4150 k = Ld::oppositeDist(j) - (Ld::distFld(0) + Ld::distFld(1));
4151 tmpUF = (b[Ld::mFld2(3 * k)] + b[Ld::mFld2(3 * k + 1)] + b[Ld::mFld2(3 * k + 2)]);
4152 tmpUW = (c[Ld::mFld2(3 * k)] + c[Ld::mFld2(3 * k + 1)] + c[Ld::mFld2(3 * k + 2)]);
4153 tpIndex = 3;
4154 }
4155 }
4156
4157 tmpUBF = 0.0;
4158 for(MInt dim = 0; dim < nDim; dim++) {
4159 uBF[dim] = (1.0 - 3.0 / F2q) * uF[dim] + (3.0 / F2q) * uW[dim];
4160 tmpUBF += (Ld::idFld(Ld::oppositeDist(j), dim) - 1) * uBF[dim];
4161 }
4162
4163 fEq = Ld::tp(tpIndex) * rhoF
4164 * (1.0 + tmpUBF * F1BCSsq + tmpUF * tmpUF * F1BCSsq * F1BCSsq * F1B2 - tmp2UF * F1BCSsq * F1B2);
4165 xi = m_omega * (F2q - 1.0) / (1 + 0.5 * m_omega);
4166 // xi = m_omega * (F2q - 1.0);
4167
4168 // perform interpolated bounce back
4169 m_solver->a_oldDistribution(nghbrId, j) =
4170 (1.0 - xi) * m_solver->a_distribution(nghbrId, Ld::oppositeDist(j)) + xi * fEq
4171 - 2.0 * Ld::tp(tpIndex) * rhoF * F1BCSsq * tmpUW; // the minus corresponds to the opposite direction
4172 }
4173 }
4174
4175 } // end of loop over all directions
4176
4177 calculateEqDistsWallSingleSpecies(id);
4178 }
4179
4180 interpolatedBounceBackSingleSpeciesThermal(i, wT, uW);
4181
4182 } // end of loop over all m_bndcells
4183}
4184
4194template <MInt nDim, MInt nDist, class SysEqn>
4196 TRACE();
4197
4198 MInt currentId, neighborId1, neighborId2;
4199
4200 MInt ind = m_mapSegIdsInOutCnd[index];
4201
4202 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
4203 // if( (globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0 )
4204 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
4205 // since it is a rhsBnd it should be performed after propagation
4206 // TODO labels:LB,totest Not tested yet for a locally refined mesh
4207
4208 currentId = m_bndCells[i].m_cellId;
4209
4210 // extrapolate inner distributions
4211 if(m_exDirs[index][0] >= 0 && m_solver->a_hasNeighbor(currentId, m_exDirs[ind][0]) > 0) {
4212 neighborId1 = m_solver->c_neighborId(currentId, m_exDirs[ind][0]);
4213 if(m_exDirs[index][1] >= 0 && m_solver->a_hasNeighbor(currentId, m_exDirs[ind][1]) > 0) {
4214 neighborId2 = m_solver->c_neighborId(currentId, m_exDirs[ind][1]);
4215 for(MInt j = 0; j < nDist - 1; j++) {
4216 m_solver->a_oldDistribution(currentId, j) =
4217 m_exWeights[ind][0] * m_solver->a_oldDistribution(neighborId1, j)
4218 + m_exWeights[ind][1] * m_solver->a_oldDistribution(neighborId2, j);
4219 }
4220 } else {
4221 for(MInt j = 0; j < nDist - 1; j++) {
4222 m_solver->a_oldDistribution(currentId, j) = m_solver->a_oldDistribution(neighborId1, j);
4223 }
4224 }
4225 } else {
4226 if(m_exDirs[index][1] >= 0 && m_solver->a_hasNeighbor(currentId, m_exDirs[ind][1]) > 0) {
4227 neighborId2 = m_solver->c_neighborId(currentId, m_exDirs[ind][1]);
4228 for(MInt j = 0; j < nDist - 1; j++) {
4229 m_solver->a_oldDistribution(currentId, j) = m_solver->a_oldDistribution(neighborId2, j);
4230 }
4231 }
4232 }
4233 }
4234}
4235
4247template <MInt nDim, MInt nDist, class SysEqn>
4249 const MFloat* const uW) {
4250 TRACE();
4251
4252 const MInt pCellId = m_bndCells[cellId].m_cellId;
4253 MFloat l_rho = m_solver->a_variable(pCellId, PV->RHO);
4254
4255 // case 1: boundary cell is inside fluid
4256 if(m_bndCells[cellId].m_isFluid) {
4257 for(MInt j = 0; j < nDist - 1; j++) {
4258 // if q < 1.0, perform bounce back
4259 if(m_bndCells[cellId].m_distances[j] < 1.0) {
4260#ifdef WAR_NVHPC_PSTL
4261 const MInt opposite = m_solver->m_oppositeDist[j];
4262#else
4263 const MInt opposite = Ld::oppositeDist(j);
4264#endif
4265 if(m_bndCells[cellId].m_distances[opposite] < 1.0) {
4266 // if cut in opposite dir -> perform simple bounce back to avoid
4267 // multiple definition
4268 m_solver->a_oldDistribution(pCellId, opposite) =
4269 m_solver->a_distribution(pCellId, j) + firstMomentSourceTerm(uW, l_rho, opposite);
4270 } else {
4271 if(m_bndCells[cellId].m_distances[j] <= 0.5) {
4272 // TODO labels:LB it might be that no neighbor exist in opposite dir. For this
4273 // case a special treatment setting oldDist previously is demanded.
4274 // p.e.: intersecting of this and another BC ..
4275 // - ..inlet/outlet: if using calcluateEqDist(..) a_oldDistribution as
4276 // well as a_distribution are set and hence no problem appears
4277 // - ..slipFlow: has to be performed before this BC
4278 const MFloat F2q = F2 * m_bndCells[cellId].m_distances[j];
4279 m_solver->a_oldDistribution(pCellId, opposite) = F2q * m_solver->a_distribution(pCellId, j)
4280 + (F1 - F2q) * m_solver->a_oldDistribution(pCellId, j)
4281 + firstMomentSourceTerm(uW, l_rho, opposite);
4282 } else {
4283 // if 0.5 < q < 1.0, perform bounce back, similar to bounce back from
4284 // a solid boundary cell's point of view
4285 const MFloat F2q = F2 * m_bndCells[cellId].m_distances[j];
4286 m_solver->a_oldDistribution(pCellId, opposite) =
4287 (m_solver->a_distribution(pCellId, j) / F2q)
4288 + (m_solver->a_distribution(pCellId, opposite) * (F2q - F1) / F2q)
4289 + firstMomentSourceTerm(uW, l_rho, opposite);
4290 }
4291 }
4292 }
4293 }
4294 }
4295
4296 // case 2: boundary cell is outside fluid
4297 else {
4298 for(MInt j = 0; j < nDist - 1; j++) {
4299 // if q < 0.5, perform bounce back (this is meant from the outer cell, the inner cell then has q >= 0.5)
4300 if(m_bndCells[cellId].m_distances[j] < 0.5) {
4301 // do we have a non-halo fluid neighbor?
4302 if(m_solver->a_hasNeighbor(pCellId, j)) {
4303 const MInt neighborId = m_solver->c_neighborId(pCellId, j);
4304 if(!m_solver->a_isHalo(neighborId) && m_solver->a_isActive(neighborId)) {
4305 l_rho = m_solver->a_variable(neighborId, PV->RHO);
4306#ifdef WAR_NVHPC_PSTL
4307 const MInt opposite = m_solver->m_oppositeDist[j];
4308#else
4309 const MInt opposite = Ld::oppositeDist(j);
4310#endif
4311 const MFloat F2q = F2 * (F1 - m_bndCells[cellId].m_distances[j]);
4312 m_solver->a_oldDistribution(neighborId, j) = (m_solver->a_distribution(neighborId, opposite) / F2q)
4313 + (m_solver->a_distribution(neighborId, j) * (F2q - F1) / F2q)
4314 + (F1 / F2q) * firstMomentSourceTerm(uW, l_rho, j);
4315 }
4316 }
4317 }
4318 }
4319
4320 // The outer cell does not belong to the flow field, thus its incoming distributions are overwritten.
4321 // It is possible that there are cells without any cutting velocity! These are considered too.
4322 m_solver->setEqDists(pCellId, 1.0, uW);
4323 for(MInt d = 0; d < nDim; d++) {
4324 m_solver->a_variable(pCellId, d) = uW[d];
4325 m_solver->a_oldVariable(pCellId, d) = uW[d];
4326 }
4327 m_solver->a_variable(pCellId, nDim) = F1;
4328 m_solver->a_oldVariable(pCellId, nDim) = F1;
4329 }
4330}
4331
4332
4343template <MInt nDim, MInt nDist, class SysEqn>
4345 const MFloat* const wTPtr,
4346 const MFloat* const uW) {
4347 TRACE();
4348
4349 const MInt pCellId = m_bndCells[cellId].m_cellId;
4350
4351 std::array<MBool, nDist - 1> ibb{};
4352 ibb.fill(false);
4353
4354 // case 1: boundary cell is inside fluid
4355 if(m_bndCells[cellId].m_isFluid) {
4356 for(MInt j = 0; j < nDist - 1; j++) {
4357#ifdef WAR_NVHPC_PSTL
4358 const MInt opposite = m_solver->m_oppositeDist[j];
4359#else
4360 const MInt opposite = Ld::oppositeDist(j);
4361#endif
4362 MFloat s = F0;
4363 if(m_solver->m_innerEnergy) {
4364 s = zerothMomentSourceTerm<1>(pCellId, opposite, wTPtr[j], uW);
4365 } else if(m_solver->m_totalEnergy) {
4366 s = zerothMomentSourceTerm<2>(pCellId, opposite, wTPtr[j], uW);
4367 } else {
4368 s = zerothMomentSourceTerm<0>(pCellId, opposite, wTPtr[j], uW);
4369 }
4370 const MFloat sourceTerm = s;
4371 // if q <= 0.5, perform bounce back
4372 if(m_bndCells[cellId].m_distances[j] <= 0.5) {
4373 if(m_solver->a_hasNeighbor(pCellId, opposite) != 0) {
4374 MFloat F2q = F2 * m_bndCells[cellId].m_distances[j];
4375 m_solver->a_oldDistributionThermal(pCellId, opposite) =
4376 (-F2q * m_solver->a_distributionThermal(pCellId, j))
4377 + ((F2q - F1) * m_solver->a_oldDistributionThermal(pCellId, j)) + sourceTerm;
4378 ibb[j] = true;
4379 }
4380 // this is the case in which we have no neighbor in the direction we want to set (strange case)
4381 // can in my opinion only appear if neighbors were set wrong or at an interface
4382 else
4383 m_solver->a_oldDistributionThermal(pCellId, opposite) = m_solver->a_distributionThermal(pCellId, j);
4384 }
4385
4386 // this is the case in which we do not have a neighbor and no cut, do simple bounce back (strange case)
4387 // can in my opinoin only appear if something has gone wrong either with the grid or with the distance calculation
4388 else if(m_bndCells[cellId].m_distances[j] > 0.5 && m_solver->a_hasNeighbor(pCellId, j) == 0)
4389 m_solver->a_oldDistributionThermal(pCellId, opposite) = m_solver->a_distributionThermal(pCellId, j);
4390 }
4391 }
4392 // case 2: boundary cell is outside fluid
4393 else {
4394 for(MInt j = 0; j < nDist - 1; j++) {
4395#ifdef WAR_NVHPC_PSTL
4396 const MInt opposite = m_solver->m_oppositeDist[j];
4397#else
4398 const MInt opposite = Ld::oppositeDist(j);
4399#endif
4400 // do we have a neighbor at all?
4401 if(m_solver->a_hasNeighbor(pCellId, j)) {
4402 // make sure that the neighbor is not a halo cell
4403 MInt neighborId = m_solver->c_neighborId(pCellId, j);
4404
4405 MFloat s = F0;
4406 if(m_solver->m_innerEnergy) {
4407 s = zerothMomentSourceTerm<1>(neighborId, j, wTPtr[j], uW);
4408 } else if(m_solver->m_totalEnergy) {
4409 s = zerothMomentSourceTerm<2>(neighborId, j, wTPtr[j], uW);
4410 } else {
4411 s = zerothMomentSourceTerm<0>(neighborId, j, wTPtr[j], uW);
4412 }
4413 const MFloat sourceTerm = s;
4414
4415 if(m_bndCells[cellId].m_distances[j] < 0.5) {
4416 ibb[j] = true; // must be set here otherwise the wT is computed wrongly
4417 }
4418 if(!m_solver->a_hasProperty(neighborId, Cell::IsHalo)) {
4419 MInt nbndid = m_solver->a_bndId(neighborId);
4420
4421 // if q < 0.5, perform bounce back (this is meant from the outer cell, the inner cell then has q >= 0.5)
4422 if(m_bndCells[cellId].m_distances[j] < 0.5) {
4423 MFloat F2q = F2 * (F1 - m_bndCells[cellId].m_distances[j]);
4424
4425 // check if my neighbor is an inside cell or a boundary cell with the cell center inside
4426 if(nbndid == -1 || m_bndCells[nbndid].m_isFluid) {
4427 m_solver->a_oldDistributionThermal(neighborId, j) =
4428 -(m_solver->a_distributionThermal(neighborId, opposite) / F2q)
4429 + (m_solver->a_distributionThermal(neighborId, j) * (F2q - F1) / F2q) + sourceTerm / F2q;
4430 }
4431 }
4432 }
4433 }
4434 }
4435
4436 // The outer cell does not belong to the flow field, thus its incoming distributions are overwritten.
4437 // It is possible that there are cells without any cutting velocity! These are considered too.
4438 MFloat wTMean = F0;
4439 MInt counterT = 0;
4440 for(MInt j = 0; j < nDist - 1; j++) {
4441 if(ibb[j]) {
4442 wTMean += wTPtr[j];
4443 counterT++;
4444 }
4445 }
4446 if(counterT != 0) {
4447 wTMean /= counterT;
4448 } else {
4449 wTMean = 1.0; // this case should not happen;
4450 }
4451 m_solver->setEqDistsThermal(pCellId, wTMean, F1, uW);
4452 m_solver->a_variable(pCellId, PV->T) = wTMean;
4453 m_solver->a_oldVariable(pCellId, PV->T) = wTMean;
4454 }
4455}
4456
4467template <MInt nDim, MInt nDist, class SysEqn>
4469 const MFloat* const wCPtr,
4470 const MFloat* const uW) {
4471 TRACE();
4472
4473 const MInt pCellId = m_bndCells[cellId].m_cellId;
4474
4475 std::array<MBool, nDist - 1> ibb{};
4476 ibb.fill(false);
4477
4478 // case 1: boundary cell is inside fluid
4479 if(m_bndCells[cellId].m_isFluid) {
4480 for(MInt j = 0; j < nDist - 1; j++) {
4481#ifdef WAR_NVHPC_PSTL
4482 const MInt opposite = m_solver->m_oppositeDist[j];
4483#else
4484 const MInt opposite = Ld::oppositeDist(j);
4485#endif
4486 const MFloat sourceTerm = zerothMomentSourceTerm<0>(pCellId, opposite, wCPtr[j], uW);
4487 // if q <= 0.5, perform bounce back
4488 if(m_bndCells[cellId].m_distances[j] <= 0.5) {
4489 if(m_solver->a_hasNeighbor(pCellId, opposite) != 0) {
4490 MFloat F2q = F2 * m_bndCells[cellId].m_distances[j];
4491 m_solver->a_oldDistributionTransport(pCellId, opposite) =
4492 (-F2q * m_solver->a_distributionTransport(pCellId, j))
4493 + ((F2q - F1) * m_solver->a_oldDistributionTransport(pCellId, j)) + sourceTerm;
4494 ibb[j] = true;
4495 }
4496 // this is the case in which we have no neighbor in the direction we want to set (strange case)
4497 // can in my opinion only appear if neighbors were set wrong or at an interface
4498 else
4499 m_solver->a_oldDistributionTransport(pCellId, opposite) = m_solver->a_distributionTransport(pCellId, j);
4500 }
4501
4502 // this is the case in which we do not have a neighbor and no cut, do simple bounce back (strange case)
4503 // can in my opinoin only appear if something has gone wrong either with the grid or with the distance calculation
4504 else if(m_bndCells[cellId].m_distances[j] > 0.5 && m_solver->a_hasNeighbor(pCellId, j) == 0)
4505 m_solver->a_oldDistributionTransport(pCellId, opposite) = m_solver->a_distributionTransport(pCellId, j);
4506 }
4507 }
4508 // case 2: boundary cell is outside fluid
4509 else {
4510 for(MInt j = 0; j < nDist - 1; j++) {
4511#ifdef WAR_NVHPC_PSTL
4512 const MInt opposite = m_solver->m_oppositeDist[j];
4513#else
4514 const MInt opposite = Ld::oppositeDist(j);
4515#endif
4516 const MFloat sourceTerm = zerothMomentSourceTerm<0>(cellId, j, wCPtr[j], uW);
4517 // do we have a neighbor at all?
4518 if(m_solver->a_hasNeighbor(pCellId, j)) {
4519 // make sure that the neighbor is not a halo cell
4520 MInt neighborId = m_solver->c_neighborId(pCellId, j);
4521 if(m_bndCells[cellId].m_distances[j] < 0.5) {
4522 ibb[j] = true; // this must be set here otherwise wCMean is computed wrong
4523 }
4524 if(!m_solver->a_hasProperty(neighborId, Cell::IsHalo)) {
4525 MInt nbndid = m_solver->a_bndId(neighborId);
4526
4527 // if q < 0.5, perform bounce back (this is meant from the outer cell, the inner cell then has q >= 0.5)
4528 if(m_bndCells[cellId].m_distances[j] < 0.5) {
4529 MFloat F2q = F2 * (F1 - m_bndCells[cellId].m_distances[j]);
4530
4531 // check if my neighbor is an inside cell or a boundary cell with the cell center inside
4532 if(nbndid == -1 || m_bndCells[nbndid].m_isFluid) {
4533 m_solver->a_oldDistributionTransport(neighborId, j) =
4534 -(m_solver->a_distributionTransport(neighborId, opposite) / F2q)
4535 + (m_solver->a_distributionTransport(neighborId, j) * (F2q - F1) / F2q) + sourceTerm / F2q;
4536 }
4537 }
4538 }
4539 }
4540 }
4541
4542 // The outer cell does not belong to the flow field, thus its incoming distributions are overwritten.
4543 // It is possible that there are cells without any cutting velocity! These are considered too.
4544 MFloat wCMean = F0;
4545 MInt counterC = 0;
4546 for(MInt j = 0; j < nDist - 1; j++) {
4547 if(ibb[j]) {
4548 wCMean += wCPtr[j];
4549 counterC++;
4550 }
4551 }
4552 if(counterC != 0) {
4553 wCMean /= counterC;
4554 } else {
4555 wCMean = 1.0; // this case should not happen;
4556 }
4557 m_solver->setEqDistsTransport(pCellId, wCMean, uW);
4558 m_solver->a_variable(pCellId, nDim + 2) = wCMean;
4559 m_solver->a_oldVariable(pCellId, nDim + 2) = wCMean;
4560 }
4561}
4562
4579template <MInt nDim, MInt nDist, class SysEqn>
4580template <MInt mode>
4582 const MFloat* uW) {
4583 TRACE();
4584
4585 MFloat sourceTerm = F0;
4586 const MFloat rho = m_solver->a_variable(pCellId, PV->RHO);
4587 const MFloat D = (MFloat)nDim;
4588#ifdef WAR_NVHPC_PSTL
4589 const MFloat distType = (MFloat)m_solver->m_distType[dist];
4590 const MFloat weight = m_solver->m_tp[m_solver->m_distType[dist]];
4591 MFloat sP = F0;
4592 MFloat sV = F0;
4593 for(MInt d = 0; d < nDim; d++) {
4594 sP += m_solver->m_ppdfDir[dist * nDim + d] * uW[d];
4595 sV += uW[d] * uW[d];
4596 }
4597 const MFloat tmp = sP * sP * F1B2mulF1BCSsq * F1BCSsq;
4598 IF_CONSTEXPR(mode == 0) { sourceTerm = F2 * weight * var * (F1 + tmp - sV * F1B2mulF1BCSsq); }
4599 IF_CONSTEXPR(mode == 1) { sourceTerm = weight * var * D * rho * (distType + tmp - sV * F1B2mulF1BCSsq); }
4600 IF_CONSTEXPR(mode == 2) {
4601 const MFloat E = D * var * F1B2;
4602 sourceTerm = F2 * weight * rho
4603 * (tmp - sV + F1B2 * (distType - D * CSsq) + E * (F1 + tmp * F1B2mulF1BCSsq - sV * F1B2mulF1BCSsq));
4604 }
4605#else
4606 const MFloat weight = Ld::tp(Ld::distType(dist));
4607 const MFloat distType = (MFloat)Ld::distType(dist);
4608 const MFloat sP = std::inner_product(uW, uW + nDim, lbDescriptor::ppdfDir<nDim>[dist], .0);
4609 const MFloat sV = std::inner_product(uW, uW + nDim, uW, .0);
4610 const MFloat tmp = sP * sP * F1B2mulF1BCSsq * F1BCSsq;
4611 IF_CONSTEXPR(mode == 0) { sourceTerm = F2 * weight * var * (F1 + tmp - sV * F1B2mulF1BCSsq); }
4612 IF_CONSTEXPR(mode == 1) { sourceTerm = weight * var * D * rho * (distType + tmp - sV * F1B2mulF1BCSsq); }
4613 IF_CONSTEXPR(mode == 2) {
4614 const MFloat E = D * var * F1B2;
4615 sourceTerm = F2 * weight * rho
4616 * (tmp - sV + F1B2 * (distType - D * CSsq) + E * (F1 + tmp * F1B2mulF1BCSsq - sV * F1B2mulF1BCSsq));
4617 }
4618#endif
4619 return sourceTerm;
4620}
4621
4634template <MInt nDim, MInt nDist, class SysEqn>
4636 MInt bcIndex) {
4637 TRACE();
4638
4639 const MInt pCellId = m_bndCells[cellId].m_cellId;
4640 const MFloat l_dt = FPOW2(m_solver->maxLevel() - m_solver->a_level(pCellId)); // local time step
4641 const MFloat LB_dx = 1.0; // local grid space in LB units
4642 const MInt noCells = m_solver->a_noCells();
4643
4644 MInt noMissDist = 0;
4645 MFloat l_q = qT; // Heat flux normal to wall
4646
4647 // case 1: boundary cell is inside fluid
4648 if(m_bndCells[cellId].m_isFluid) {
4649 // Calculate the factor to split the flux
4650 MInt bndId = 0;
4651 for(MInt n = 0; n < (MInt)ibbBndIds.size(); n++) {
4652 if(ibbBndIds.at(n) == bcIndex) {
4653 bndId = n;
4654 break;
4655 }
4656 }
4657 noMissDist = noMissDistBnd.at(bndId * noCells + pCellId);
4658 if(m_solver->a_hasProperty(pCellId, Cell::IsHalo))
4659 return;
4660 else if(noMissDist == 0) {
4661 std::cout << "ERROR this should not happen!" << std::endl;
4662 }
4663 const MFloat splittingFactor = 1.0 / (1.0 * noMissDist); // in a fluid cell this is constant!
4664 MFloat fluxInDistDir = splittingFactor * l_q; // split flux in the distribution directions
4665
4666 // now loop over the distributions
4667 for(MInt j = 0; j < nDist - 1; j++) {
4668 const MInt opposite = Ld::oppositeDist(j);
4669
4670 if(m_bndCells[cellId].m_distances[j] <= 0.5) {
4671 // Now perform bounce back
4672 if(m_solver->a_hasNeighbor(pCellId, opposite) != 0) {
4673 MInt neighborId = m_solver->c_neighborId(pCellId, opposite);
4674 MInt nbndid = m_solver->a_bndId(neighborId);
4675
4676 // check if my neighbor is an inside cell or a boundary cell with the cell center inside the fluid
4677 if(nbndid == -1 || m_bndCells[nbndid].m_isFluid) {
4678 MFloat FFq = F2 * m_bndCells[cellId].m_distances[j] + F1;
4679 MFloat Fq = F2 * m_bndCells[cellId].m_distances[j] - F1;
4680 m_solver->a_oldDistributionThermal(pCellId, opposite) =
4681 m_solver->a_distributionThermal(pCellId, j)
4682 - (Fq / FFq) * m_solver->a_distributionThermal(neighborId, j)
4683 + (Fq / FFq) * m_solver->a_distributionThermal(pCellId, opposite)
4684 + (F2 / FFq) * (l_dt / LB_dx) * fluxInDistDir;
4685 }
4686 // this is a strange case where we don't have valid neighbors; do simple bounce back
4687 else {
4688 m_solver->a_oldDistributionThermal(pCellId, opposite) = m_solver->a_distributionThermal(pCellId, j);
4689 }
4690 }
4691 // this is a strange case where we don't have any neighbors in the direction where we want to set the
4692 // distribution; do simple bounce back
4693 else {
4694 m_solver->a_oldDistributionThermal(pCellId, opposite) = m_solver->a_distributionThermal(pCellId, j);
4695 }
4696 }
4697 // this is the case in which we do not have a neighbor and no cut, do simple bounce back (strange case)
4698 else if(m_bndCells[cellId].m_distances[j] > 0.5 && m_solver->a_hasNeighbor(pCellId, j) == 0) {
4699 m_solver->a_oldDistributionThermal(pCellId, opposite) = m_solver->a_distributionThermal(pCellId, j);
4700 }
4701 }
4702 }
4703
4704 // case 2: boundary cell is outside fluid
4705 else {
4706 for(MInt j = 0; j < nDist - 1; j++) {
4707 const MInt opposite = Ld::oppositeDist(j);
4708
4709 // do we have a neighbor at all?
4710 if(m_solver->a_hasNeighbor(pCellId, j)) {
4711 // make sure that the neighbor is not a halo cell
4712 MInt neighborId = m_solver->c_neighborId(pCellId, j);
4713 if(!m_solver->a_hasProperty(neighborId, Cell::IsHalo)) {
4714 MInt nbndid = m_solver->a_bndId(neighborId);
4715 // if q < 0.5, perform bounce back for the neighbor fluid cell, where the fluid cell has q > 0.5
4716 if(m_bndCells[cellId].m_distances[j] < 0.5) {
4717 // Calculate q for the neighbor cell
4718 MFloat nei_q = F1 - m_bndCells[cellId].m_distances[j];
4719 // check if my neighbor is an inside cell or a boundary cell with cell center inside the fluid
4720 if(nbndid == -1 || m_bndCells[nbndid].m_isFluid) {
4721 // check if there is a neighbor of the neighbor cell since we need that cell too for the interpolation
4722 if(m_solver->a_hasNeighbor(neighborId, j)) {
4723 MInt neighbor2Id = m_solver->c_neighborId(neighborId, j);
4724 MInt n2bndid = m_solver->a_bndId(neighbor2Id);
4725 // check if the neighbors neighbor cell is a valid cell
4726 if(n2bndid == -1 || m_bndCells[n2bndid].m_isFluid) {
4727 // Calculate factor to split the flux
4728 // Here the splitting factors could differ for each distributions, thus the calculation is performed
4729 // inside the loop
4730 MInt bndId = 0;
4731 for(MInt n = 0; n < (MInt)ibbBndIds.size(); n++) {
4732 if(ibbBndIds.at(n) == bcIndex) {
4733 bndId = n;
4734 break;
4735 }
4736 }
4737 noMissDist = noMissDistBnd.at(bndId * noCells + neighborId);
4738 if(noMissDist == 0) {
4739 std::cout << "ERROR this should not happen!" << std::endl;
4740 }
4741 MFloat splittingFactor = 1.0 / (1.0 * noMissDist);
4742 MFloat fluxInDistDir = splittingFactor * l_q;
4743
4744 // perform interpolated bounce back
4745 MFloat FFq = F2 * nei_q + F1;
4746 MFloat Fq = F2 * nei_q - F1;
4747 m_solver->a_oldDistributionThermal(neighborId, j) =
4748 m_solver->a_distributionThermal(neighborId, opposite)
4749 - (Fq / FFq) * m_solver->a_distributionThermal(neighbor2Id, opposite)
4750 + (Fq / FFq) * m_solver->a_distributionThermal(neighborId, j)
4751 + (F2 / FFq) * (l_dt / LB_dx) * fluxInDistDir;
4752 }
4753 // if there is no second neighbor, then do simple bounce back
4754 else {
4755 m_solver->a_oldDistributionThermal(neighborId, j) =
4756 m_solver->a_distributionThermal(neighborId, opposite);
4757 }
4758 }
4759 // if there is no second neighbor, then do simple bounce back
4760 else {
4761 m_solver->a_oldDistributionThermal(neighborId, j) =
4762 m_solver->a_distributionThermal(neighborId, opposite);
4763 }
4764 }
4765 }
4766 }
4767 }
4768 }
4769
4770 // The outer cell does not belong to the flow field, thus its incomming distributions are overwritten.
4771 // It is possible that there are cells without any cutting velocity! There are considered too.
4772 std::vector<MFloat> wallTemp;
4773 for(MInt j = 0; j < nDist - 1; j++) {
4774 if(m_bndCells[cellId].m_distances[j] < 0.5) {
4775 if(m_solver->a_hasNeighbor(pCellId, j)) {
4776 MFloat temp = m_solver->a_variable(m_solver->c_neighborId(pCellId, j), PV->T);
4777 wallTemp.push_back(temp);
4778 }
4779 }
4780 }
4781 const MInt noCuts = (MInt)wallTemp.size();
4782 MFloat sumTemp = F0;
4783 for(MInt n = 0; n < (signed)wallTemp.size(); n++) {
4784 sumTemp += wallTemp.at(n);
4785 }
4786 MFloat wT = sumTemp / ((MInt)noCuts);
4787 calculateEqDistsWallSingleSpeciesThermal(pCellId, wT);
4788 }
4789}
4790
4799template <MInt nDim, MInt nDist, class SysEqn>
4801 TRACE();
4802
4803 for(MInt cellId = m_bndCndOffsets[index]; cellId < m_bndCndOffsets[index + 1]; cellId++) {
4804 const MInt pCellId = m_bndCells[cellId].m_cellId;
4805 // const MFloat l_dx = m_solver->c_cellLengthAtLevel(m_solver->a_level(pCellId));
4806 for(MInt j = 0; j < nDist - 1; j++) {
4807 const MInt opposite = Ld::oppositeDist(j);
4808 // skip the cases where no bounce back is performed
4809 if(m_bndCells[cellId].m_isFluid) {
4810 if(m_solver->a_hasProperty(pCellId, Cell::IsHalo)) continue;
4811 if(m_bndCells[cellId].m_distances[j] > 0.5) {
4812 continue;
4813 } else {
4814 if(m_solver->a_hasNeighbor(pCellId, opposite) == 0) continue;
4815 }
4816 } else {
4817 if(!m_solver->a_hasNeighbor(pCellId, j)) {
4818 continue;
4819 } else {
4820 if(m_bndCells[cellId].m_distances[j] > 0.5) {
4821 continue;
4822 } else {
4823 MInt nbndid = m_solver->a_bndId(m_solver->c_neighborId(pCellId, j));
4824 if(!(nbndid == -1 || m_bndCells[nbndid].m_isFluid)) continue;
4825 }
4826 }
4827 }
4828
4829 // in order to compute the wall temperature we first need the distance in the sublayer from the wall cut point
4830 // along each distribution. For this we need the normal vector in each wall cut point which points into the fluid
4831 // domain
4832 MFloat wallNormal[nDim] = {F0};
4833
4834 MFloat lDir = F0;
4835 for(MInt d = 0; d < nDim; d++)
4836 lDir += Ld::ppdfDir(j, d) * Ld::ppdfDir(j, d);
4837 lDir = sqrt(lDir); // this is to correct the different length for the diagonal and normal directions
4838 MFloat dist2Wall; // distance from the cell center to the wall in cartesian coordinates
4839 if(m_bndCells[cellId].m_isFluid) {
4840 dist2Wall = m_bndCells[cellId].m_distances[j] * lDir;
4841 } else {
4842 dist2Wall = (1.0 - m_bndCells[cellId].m_distances[j]) * lDir;
4843 }
4844
4845 if(m_calcSublayerDist) {
4846 MFloat** const v = m_solver->m_geometry->elements[m_distIntersectionElementId[pCellId][j]].m_vertices;
4847 MFloat edge[nDim];
4848 MFloat edge2[nDim];
4849 if(nDim == 2) {
4850 for(MInt dim = 0; dim < nDim; dim++)
4851 edge[dim] = v[0][dim] - v[1][dim];
4852 wallNormal[0] = edge[1];
4853 wallNormal[1] = -edge[0];
4854 } else if(nDim == 3) {
4855 for(MInt dim = 0; dim < nDim; dim++) {
4856 edge[dim] = v[0][dim] - v[1][dim];
4857 edge2[dim] = v[0][dim] - v[2][dim];
4858 }
4859 wallNormal[0] = edge[1] * edge2[2] - edge[2] * edge2[1];
4860 wallNormal[1] = edge[2] * edge2[0] - edge[0] * edge2[2];
4861 wallNormal[2] = edge[0] * edge2[1] - edge[1] * edge2[0];
4862 }
4863 }
4864
4865 // direction where the cut was found
4866 MFloat bounceBackDir[nDim];
4867 for(MInt d = 0; d < nDim; d++) {
4868 bounceBackDir[d] = Ld::ppdfDir(j, d);
4869 }
4870
4871 // dot product and calculate length of normal vector and direction vector
4872 MFloat dp = F0;
4873 MFloat lv = F0;
4874 MFloat lb = F0;
4875 for(MInt d = 0; d < nDim; d++) {
4876 dp += bounceBackDir[d] * wallNormal[d];
4877 lv += wallNormal[d] * wallNormal[d];
4878 lb += bounceBackDir[d] * bounceBackDir[d];
4879 }
4880 lv = sqrt(lv);
4881 lb = sqrt(lb);
4882
4883 // invert normal vector if needed
4884 if(dp < 0) {
4885 for(MInt d = 0; d < nDim; d++) {
4886 wallNormal[d] = -wallNormal[d];
4887 }
4888 dp = -dp;
4889 }
4890
4891 // Check if the normal vector is valid
4892 if(approx(lv, 0.0, MFloatEps)) continue;
4893
4894 // Now calculate the angle between the normal vector and the distribution
4895 MFloat phi = acos(dp / (lv * lb));
4896
4897 // The distance in the sublayer is given as
4898 m_mucousDist[pCellId][j] = m_mucosaModel.mucosaThickness / cos(phi);
4899 m_fluidDist[pCellId][j] = dist2Wall;
4900 }
4901 }
4902}
4903
4913template <MInt nDim, MInt nDist, class SysEqn>
4915 TRACE();
4916 const MInt pCellId = m_bndCells[cellId].m_cellId;
4917 MInt fluidCellId = pCellId;
4918
4919 for(MInt j = 0; j < nDist - 1; j++) {
4920 const MInt opposite = Ld::oppositeDist(j);
4921 // skip the cases where no bounce back is performed
4922 if(m_bndCells[cellId].m_isFluid) {
4923 if(m_solver->a_hasProperty(pCellId, Cell::IsHalo)) continue;
4924 if(m_bndCells[cellId].m_distances[j] > 0.5) {
4925 continue;
4926 } else {
4927 if(!m_solver->a_hasNeighbor(pCellId, opposite)) continue;
4928 }
4929 } else {
4930 if(!m_solver->a_hasNeighbor(pCellId, j)) {
4931 continue;
4932 } else {
4933 if(m_bndCells[cellId].m_distances[j] > 0.5) {
4934 continue;
4935 } else {
4936 MInt nbndid = m_solver->a_bndId(m_solver->c_neighborId(pCellId, j));
4937 if(!(nbndid == -1 || m_bndCells[nbndid].m_isFluid)) {
4938 continue;
4939 } else {
4940 fluidCellId = m_solver->c_neighborId(pCellId, j);
4941 }
4942 }
4943 }
4944 }
4945
4946 // ----------------------------------------------
4947 // 1. Calculate Cw
4948 // ----------------------------------------------
4949
4950 // Get the necessary dimensioned variables
4951 MFloat d_Cf = m_solver->a_variable(fluidCellId, PV->C) * m_mucosaModel.refC;
4952 MFloat d_C_o = m_mucosaModel.C_o * m_mucosaModel.refC;
4953 MFloat fD_mD = m_fluidDist[pCellId][j] / m_mucousDist[pCellId][j];
4954
4955 MFloat d_Cw = (d_Cf + m_mucosaModel.diffRatio * fD_mD * d_C_o) / (1.0 + m_mucosaModel.diffRatio * fD_mD);
4956 wallConc[j] = d_Cw / m_mucosaModel.refC;
4957
4958 // ----------------------------------------------
4959 // 2. Calculate latent heat (if flag is on)
4960 // ----------------------------------------------
4961
4962 MFloat d_latentSourceTerm = F0;
4963 if(m_latentHeat) {
4964 // First, get the old wall temperature
4965 MFloat Tw_old = m_oldWallTemp[pCellId][j];
4966
4967 // Convert the dimensionless variables
4968 MFloat d_Tw_old = Tw_old * m_mucosaModel.refT;
4969 MFloat d_dist2Wall = m_fluidDist[pCellId][j] * m_mucosaModel.refDx;
4970 MFloat d_diff = m_solver->m_diffusivity * m_mucosaModel.refDiff;
4971 MFloat d_cond_f = m_mucosaModel.refCondF;
4972
4973 // Calculate the specific latent heat
4974 // Inthavong et al (2018) "Wet surface wall model for latent heat exchange during evaporation"
4975 MFloat d_h_lh = (2500.8 - 2.3641 * d_Tw_old + 1.5893 * 1e-3 * d_Tw_old * d_Tw_old
4976 - 6.1434 * 1e-6 * d_Tw_old * d_Tw_old * d_Tw_old)
4977 * 1000;
4978
4979 // Compute the water flux across the interface
4980 MFloat d_j_flux = (d_Cw - d_Cf) / d_dist2Wall * d_diff;
4981
4982 // Compute the latent heat along this distribution
4983 MFloat d_q_latent = -d_j_flux * d_h_lh;
4984
4985 d_latentSourceTerm = (d_q_latent * d_dist2Wall / d_cond_f) / (1.0 + m_mucosaModel.condRatio * fD_mD);
4986 }
4987
4988 // ----------------------------------------------
4989 // 3. Calculate Tw, Cw
4990 // ----------------------------------------------
4991
4992 // Finally, compute the wall temperature
4993 // Again first dimensinon the necessary variables
4994 MFloat d_Tf = m_solver->a_variable(fluidCellId, PV->T) * m_mucosaModel.refT;
4995 MFloat d_T_o = m_mucosaModel.T_o * m_mucosaModel.refT;
4996
4997 // Calculate the wall temperature in celsius
4998 MFloat d_Tw =
4999 (d_Tf + m_mucosaModel.condRatio * fD_mD * d_T_o) / (1.0 + m_mucosaModel.condRatio * fD_mD) + d_latentSourceTerm;
5000
5001 // Calculate the dimensionless wall temperature
5002 wallTemp[j] = d_Tw / m_mucosaModel.refT;
5003
5004 // save the old wall temperature
5005 if(m_latentHeat) {
5006 m_oldWallTemp[pCellId][j] = wallTemp[j];
5007 }
5008 }
5009}
5010
5022template <MInt nDim, MInt nDist, class SysEqn>
5024 const MInt set) {
5025 TRACE();
5026
5027 const MInt pCellId = m_boundaryCellsMb.cellId(cellIndex);
5028
5029 const MFloat rho = m_boundaryCellsMb.density(cellIndex);
5030
5031 std::array<MFloat, nDim> uBoundary{};
5032 getBoundaryVelocityMb(cellIndex, uBoundary.data());
5033 const MFloat squaredVelocity = std::inner_product(uBoundary.begin(), uBoundary.end(), uBoundary.begin(), .0);
5034
5035 std::array<MFloat, nDist> eqDist{};
5036 sysEqn().calcEqDists(rho, squaredVelocity, uBoundary.data(), eqDist.data());
5037
5038 // case 1: boundary cell is inside fluid
5039 if(m_solver->a_levelSetFunctionMB(pCellId, set) > 0) {
5040 for(MInt j = 0; j < nDist - 1; j++) {
5041 const MFloat q = getDistanceMb(pCellId, cellIndex, j);
5042 const MInt opposite = Ld::oppositeDist(j);
5043
5044 if(m_solver->a_hasProperty(pCellId, Cell::IsHalo)) {
5045 continue;
5046 }
5047
5048 // if q <= 0.5, perform bounce back
5049 if(q <= 0.5) {
5050 if(m_solver->a_hasNeighbor(pCellId, opposite)) {
5051 const MInt neighborId = m_solver->c_neighborId(pCellId, opposite);
5052
5053 const MFloat F2q = F2 * q;
5054 const MFloat Fq = q;
5055
5056 const MFloat quadratic = -Fq * (F2q + 1) * m_solver->a_distribution(pCellId, j)
5057 - (1 + F2q) * (1 - F2q) * m_solver->a_oldDistribution(pCellId, j)
5058 + Fq * (1 - F2q) * m_solver->a_oldDistribution(neighborId, j) + eqDist[j]
5059 + eqDist[opposite];
5060
5061 m_solver->a_oldDistribution(pCellId, opposite) = quadratic;
5062 }
5063 // this is the case in which we have no neighbor in the direction we want to set (strange case)
5064 // can in my opinion only appear if neighbors were set wrong or at an interface
5065 else {
5066 m_solver->a_oldDistribution(pCellId, opposite) =
5067 -m_solver->a_distribution(pCellId, j) + eqDist[j] + eqDist[opposite];
5068 }
5069 }
5070 // this is the case in which we do not have a neighbor and no cut, do simple bounce back (strange case)
5071 // can in my opinoin only appear if something has gone wrong either with the grid or with the distance calculation
5072 // else if(m_bndCells[cellId].m_distances[j] > 0.5 && m_solver->a_hasNeighbor(pCellId, j) == 0)
5073 // else if( m_wallBoundaryCellList[cellId].m_distances[j] > 0.5 && m_solver->a_hasNeighbor(pCellId, j) == 0)
5074 else if(q > 0.5 && m_solver->a_hasNeighbor(pCellId, j) == 0) {
5075 m_solver->a_oldDistribution(pCellId, opposite) =
5076 -m_solver->a_distribution(pCellId, j) + eqDist[j] + eqDist[opposite];
5077 }
5078 }
5079 }
5080 // case 2: boundary cell is outside fluid
5081 else {
5082 for(MInt j = 0; j < nDist - 1; j++) {
5083 const MFloat q = getDistanceMb(pCellId, cellIndex, j);
5084
5085 const MInt opposite = Ld::oppositeDist(j);
5086
5087 // do we have a neighbor at all?
5088 if(m_solver->a_hasNeighbor(pCellId, j)) {
5089 // make sure that the neighbor is not a halo cell
5090 const MInt neighborId = m_solver->c_neighborId(pCellId, j);
5091
5092 // if(!m_solver->a_hasProperty(neighborId, Cell::IsHalo)){
5093
5094 const MBool nbndid = m_solver->a_isG0CandidateOfSet(neighborId, (set - m_solver->m_levelSetId));
5095
5096 // if q < 0.5, perform bounce back (this is meant from the outer cell, the inner cell then has q >= 0.5)
5097 if(q < 0.5) {
5098 const MFloat F2q = F2 * (F1 - q);
5099 const MFloat Fq = (1 - q);
5100
5101 // check if my neighbor is an inside cell or a boundary cell with the cell center inside
5102 if(!nbndid || m_solver->a_levelSetFunctionMB(neighborId, set) > 0) {
5103 const MInt nneighborId = m_solver->c_neighborId(neighborId, j);
5104
5105 const MFloat quadratic =
5106 1 / Fq / (F2q + 1) * (-m_solver->a_distribution(neighborId, opposite) + eqDist[j] + eqDist[opposite])
5107 + (F2q - 1) / Fq * m_solver->a_distribution(neighborId, j)
5108 - (F2q - 1) / (F2q + 1) * m_solver->a_distribution(nneighborId, j);
5109
5110 m_solver->a_oldDistribution(neighborId, j) = quadratic;
5111 }
5112 }
5113 //}
5114 }
5115 } // end of the loop over all PPDF directions in case 2
5116
5117 // The outer cell does not belong to the flow field, thus its incoming distributions are overwritten.
5118 // It is possible that there are cells without any cutting velocity! These are considered too.
5119 m_solver->setEqDists(pCellId, 1.0, squaredVelocity, uBoundary.data());
5120 }
5121}
5122
5123
5131template <MInt nDim, MInt nDist, class SysEqn>
5132ATTRIBUTES1(ATTRIBUTE_NO_AUTOVEC)
5134 const MFloat rho = 1.0;
5135 const std::array<MFloat, nDim> vel{0.0};
5136
5137 // Set macroscopic variables
5138 m_solver->a_variable(pCellId, PV->RHO) = rho;
5139 m_solver->a_oldVariable(pCellId, PV->RHO) = rho;
5140 for(MInt n = 0; n < nDim; n++) {
5141 m_solver->a_variable(pCellId, n) = vel[n];
5142 m_solver->a_oldVariable(pCellId, n) = vel[n];
5143 }
5144
5145 m_solver->setEqDists(pCellId, rho, vel.data());
5146}
5147
5156template <MInt nDim, MInt nDist, class SysEqn>
5157ATTRIBUTES1(ATTRIBUTE_NO_AUTOVEC)
5159 const MFloat wT) {
5160 TRACE();
5161
5162 m_solver->a_variable(pCellId, PV->T) = wT;
5163 m_solver->a_oldVariable(pCellId, PV->T) = wT;
5164
5165 constexpr std::array<MFloat, nDim> u{};
5166 m_solver->setEqDistsThermal(pCellId, wT, F1, F0, u.data());
5167}
5168
5177template <MInt nDim, MInt nDist, class SysEqn>
5178ATTRIBUTES1(ATTRIBUTE_NO_AUTOVEC)
5180 const MFloat wC) {
5181 TRACE();
5182
5183 m_solver->a_variable(pCellId, PV->C) = wC;
5184 m_solver->a_oldVariable(pCellId, PV->C) = wC;
5185
5186 constexpr std::array<MFloat, nDim> u{};
5187 m_solver->setEqDistsTransport(pCellId, wC, 0.0, u.data());
5188}
5189
5201template <MInt nDim, MInt nDist, class SysEqn>
5202inline void LbBndCndDxQy<nDim, nDist, SysEqn>::extrapolateVariable(const MInt index, const MInt pCellId, const MInt var,
5203 MFloat* const p_var) {
5204 TRACE();
5205
5206 if(m_exDirs[index][0] >= 0 && m_solver->a_hasNeighbor(pCellId, m_exDirs[index][0]) > 0) {
5207 MInt neighborId1 = m_solver->c_neighborId(pCellId, m_exDirs[index][0]);
5208
5209 if(m_exDirs[index][1] >= 0 && m_solver->a_hasNeighbor(pCellId, m_exDirs[index][1]) > 0) {
5210 MInt neighborId2 = m_solver->c_neighborId(pCellId, m_exDirs[index][1]);
5211
5212 MFloat ex0 = m_exWeights[index][0];
5213 MFloat ex1 = m_exWeights[index][1];
5214
5215 *p_var = ex0 * m_solver->a_variable(neighborId1, var) + ex1 * m_solver->a_variable(neighborId2, var);
5216 } else
5217 *p_var = m_solver->a_variable(neighborId1, var);
5218 } else {
5219 if(m_exDirs[index][1] >= 0 && m_solver->a_hasNeighbor(pCellId, m_exDirs[index][1]) > 0) {
5220 MInt neighborId2 = m_solver->c_neighborId(pCellId, m_exDirs[index][1]);
5221 *p_var = m_solver->a_variable(neighborId2, var);
5222 }
5223 }
5224}
5225
5237template <MInt nDim, MInt nDist, class SysEqn>
5238inline void LbBndCndDxQy<nDim, nDist, SysEqn>::extrapolateVelocities(MInt index, const MInt pCellId, MFloat* l_uu) {
5239 TRACE();
5240
5241 if(m_exDirs[index][0] >= 0 && m_solver->a_hasNeighbor(pCellId, m_exDirs[index][0]) > 0) {
5242 const MInt neighborId1 = m_solver->c_neighborId(pCellId, m_exDirs[index][0]);
5243
5244 if(m_exDirs[index][1] >= 0 && m_solver->a_hasNeighbor(pCellId, m_exDirs[index][1]) > 0) {
5245 const MInt neighborId2 = m_solver->c_neighborId(pCellId, m_exDirs[index][1]);
5246
5247 const MFloat ex0 = m_exWeights[index][0];
5248 const MFloat ex1 = m_exWeights[index][1];
5249
5250 for(MInt n = 0; n < nDim; n++) {
5251 l_uu[n] = ex0 * m_solver->a_variable(neighborId1, n) + ex1 * m_solver->a_variable(neighborId2, n);
5252 }
5253
5254 } else {
5255 for(MInt n = 0; n < nDim; n++) {
5256 l_uu[n] = m_solver->a_variable(neighborId1, n);
5257 }
5258 }
5259 } else if(m_exDirs[index][1] >= 0 && m_solver->a_hasNeighbor(pCellId, m_exDirs[index][1]) > 0) {
5260 const MInt neighborId2 = m_solver->c_neighborId(pCellId, m_exDirs[index][1]);
5261
5262 for(MInt n = 0; n < nDim; n++) {
5263 l_uu[n] = m_solver->a_variable(neighborId2, n);
5264 }
5265 } else {
5266#ifndef WAR_NVHPC_PSTL
5267 TERMM(1, "No value is extrapolated !");
5268#endif
5269 }
5270}
5271
5285template <MInt nDim, MInt nDist, class SysEqn>
5287 TRACE();
5288
5289 MInt ind = m_mapSegIdsInOutCnd[index];
5290 MFloat ma_x_F1BCS = m_Ma * LBCS;
5291 const MInt pvrho = PV->RHO;
5292
5293#ifdef WAR_NVHPC_PSTL
5294 const MInt globalTimeStep_ = globalTimeStep - 1;
5295 MInt begin = m_bndCndOffsets[index];
5296 MInt end = m_bndCndOffsets[index + 1];
5297 MInt offset = end - begin;
5298
5299 maia::parallelFor<true>(0, offset, [=](MInt id) {
5300 MInt i = begin + id;
5301 if((globalTimeStep_) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) return;
5302#else
5303 maia::parallelFor(m_bndCndOffsets[index], m_bndCndOffsets[index + 1], [=](MInt i) {
5304 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) return;
5305#endif
5306
5307 LbGridBoundaryCell<nDim>* bndCell = &m_bndCells[i];
5308 const MInt currentId = (*bndCell).m_cellId;
5309 const MInt pCellId = currentId;
5310
5311 MFloat l_rho = 1.0;
5312 MFloat l_uu[nDim] = {0.0};
5313
5314 // extrapolate density
5315 extrapolateVariable(ind, pCellId, pvrho, &l_rho);
5316
5317 for(MInt n = 0; n < nDim; n++) {
5318 l_uu[n] = m_initialVelocityVecs[ind][n] * ma_x_F1BCS * (*bndCell).m_multiplier * m_zeroInflowVelocity;
5319 }
5320
5321 // calulate the equilibrium distribution functions for the new density
5322 m_solver->setEqDists(pCellId, l_rho, l_uu);
5323
5324 m_solver->a_variable(pCellId, pvrho) = l_rho;
5325 for(MInt n = 0; n < nDim; n++) {
5326#ifdef WAR_NVHPC_PSTL
5327 m_solver->a_variable(pCellId, n) = l_uu[n];
5328#else
5329 m_solver->a_variable(pCellId, PV->VV[n]) = l_uu[n];
5330#endif
5331 }
5332 });
5333 writeBCOutput(index);
5334}
5335
5350// TODO labels:LB Remove this and update Testcases to bc10000 (2D_cylinder...)
5351template <MInt nDim, MInt nDist, class SysEqn>
5353 TRACE();
5354
5355 MInt ind = m_mapSegIdsInOutCnd[index];
5356 MFloat ma_x_F1BCS = m_Ma * LBCS;
5357
5358 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
5359 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
5360
5361 LbGridBoundaryCell<nDim>* bndCell = &m_bndCells[i];
5362 const MInt currentId = (*bndCell).m_cellId;
5363 const MInt pCellId = currentId;
5364
5365 MFloat l_rho = 1.0;
5366 MFloat l_uu[nDim] = {0.0};
5367
5368 // extrapolate density
5369 extrapolateVariable(ind, pCellId, PV->RHO, &l_rho);
5370
5371 if(nDim == 2) {
5372 l_rho = (m_solver->a_variable(pCellId, PV->RHO) + l_rho) * F1B2;
5373 }
5374
5375 for(MInt n = 0; n < nDim; n++) {
5376 l_uu[n] = m_initialVelocityVecs[ind][n] * ma_x_F1BCS * (*bndCell).m_multiplier * m_zeroInflowVelocity;
5377 }
5378
5379 // calulate the equilibrium distribution functions for the new density
5380 m_solver->setEqDists(pCellId, l_rho, l_uu);
5381
5382 //#####################################################################
5383 // WARNING!!!
5384 // Why is it not used in 2D
5385 IF_CONSTEXPR(nDim == 3) {
5386 for(MInt n = 0; n < nDim; n++) {
5387 m_solver->a_variable(pCellId, PV->VV[n]) = l_uu[n];
5388 }
5389 m_solver->a_variable(pCellId, PV->RHO) = l_rho;
5390 }
5391 //#####################################################################
5392 }
5393 // writeBCOutput(index);
5394}
5395
5411template <MInt nDim, MInt nDist, class SysEqn>
5413 TRACE();
5414
5415 // For now testing only the D3Q19 algorithm
5416 MFloat rho = 1.0, u[nDim];
5417 MFloat tmpWidth;
5418 MInt ind = m_mapSegIdsInOutCnd[index];
5419 ScratchSpace<MFloat> bBox(2 * nDim, AT_, "bBox");
5420 MFloat* bBoxPtr = &bBox[0];
5421 m_solver->m_geometry->getBoundingBox(bBoxPtr);
5422
5423 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
5424 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
5425
5426 MInt currentId = m_bndCells[i].m_cellId;
5427
5428 extrapolateVariable(ind, currentId, PV->RHO, &rho);
5429
5430 // This is different to bc1000
5431 tmpWidth = 0.5 * fabs(bBox[nDim + 1] - bBox[1]);
5432
5433 const MFloat relPos = m_solver->a_coordinate(currentId, 1) / tmpWidth;
5434 const MFloat linear = relPos;
5435 const MFloat parabola = (1.0 - relPos * relPos);
5436
5437 u[0] = m_Ma * LBCS
5438 * ((1.0 - m_solver->m_CouettePoiseuilleRatio) * linear + m_solver->m_CouettePoiseuilleRatio * parabola);
5439 u[1] = 0.0;
5440 IF_CONSTEXPR(nDim == 3) u[2] = 0.0;
5441
5442 const MFloat squaredVelocity = std::inner_product(&u[0], &u[nDim], &u[0], .0);
5443 m_solver->setEqDists(currentId, rho, squaredVelocity, u);
5444 }
5445}
5446
5455template <MInt nDim, MInt nDist, class SysEqn>
5456void LbBndCndDxQy<nDim, nDist, SysEqn>::bc10003(MInt index) {
5457 TRACE();
5458
5459 MFloat rho = F1;
5460 MFloat l_uu[nDim] = {F0};
5461 MInt currentId;
5462 MInt ind = m_mapSegIdsInOutCnd[index];
5463
5464 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
5465 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
5466
5467 currentId = m_bndCells[i].m_cellId;
5468
5469 MFloat nu = m_solver->a_nu(currentId);
5470 m_omega = 2.0 / (1.0 + 6.0 * nu * FFPOW2(m_solver->maxLevel() - m_solver->a_level(currentId)));
5471
5472 // 1. Calculate eq-Distributions from actual macroscopic values
5473 //--------------------------------------------
5474
5475 // macroscopic values
5476 rho = m_solver->a_variable(currentId, PV->RHO);
5477 MFloat squaredVelocity = F0;
5478 for(MInt d = 0; d < nDim; d++) {
5479 l_uu[d] = m_solver->a_variable(currentId, d);
5480 squaredVelocity += l_uu[d] * l_uu[d];
5481 }
5482
5483 std::array<MFloat, nDist> eDistributions{};
5484 sysEqn().calcEqDists(rho, l_uu, eDistributions.data());
5485
5486 // 2. Calculation of non-eq-parts3
5487 //--------------------------------------------
5488 std::array<MFloat, nDist> nePart{};
5489 for(MInt j = 0; j < 8; j++) {
5490 nePart[j] = m_solver->a_distribution(currentId, j) - eDistributions[j];
5491 }
5492
5493 for(MInt n = 0; n < nDim; n++) {
5494 l_uu[n] = m_initialVelocityVecs[ind][n] * m_Ma * F1BCS * m_bndCells[i].m_multiplier * m_zeroInflowVelocity;
5495 }
5496
5497 // 3. Calculation of incoming distributions in bnd cell
5498 //--------------------------------------------
5499
5500 std::array<MFloat, nDist> newEqDists{};
5501 sysEqn().calcEqDists(rho, l_uu, newEqDists.data());
5502
5503 for(MInt j = 0; j < nDist - 1; j++) {
5504 m_solver->a_oldDistribution(currentId, j) = newEqDists[j] + (F1 - m_omega) * nePart[j];
5505 }
5506 }
5507}
5508
5523template <MInt nDim, MInt nDist, class SysEqn>
5525 TRACE();
5526
5527 const MInt ind = m_mapSegIdsInOutCnd[index];
5528 std::array<MFloat, 2 * nDim> bBox{};
5529 m_solver->m_geometry->getBoundingBox(bBox.data());
5530 const MFloat channelHalfHeight = fabs(bBox[nDim + 1] - bBox[1]) * F1B2;
5531
5532 const MFloat exp = F1 + F1 / m_solver->m_n;
5533
5534 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
5535 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
5536
5537 MInt currentId = m_bndCells[i].m_cellId;
5538
5539 MFloat rho = 1.0;
5540 extrapolateVariable(ind, currentId, PV->RHO, &rho);
5541
5542 const MFloat relPos = fabs(m_solver->a_coordinate(currentId, 1) / channelHalfHeight - F1);
5543 const MFloat parabola = F1 - pow(relPos, exp);
5544
5545 std::array<MFloat, nDim> u{m_Ma * LBCS * parabola};
5546
5547 const MFloat squaredVelocity = std::inner_product(&u[0], &u[nDim], &u[0], .0);
5548 m_solver->setEqDists(currentId, rho, squaredVelocity, u.data());
5549
5550 // Saving bc results in m_solver
5551 for(MInt n = 0; n < nDim; n++) {
5552 m_solver->a_variable(currentId, PV->VV[n]) = u[n];
5553 }
5554 m_solver->a_variable(currentId, PV->RHO) = rho;
5555 }
5556}
5557
5573template <MInt nDim, MInt nDist, class SysEqn>
5575 TRACE();
5576
5577 // For now testing only the D3Q19 algorithm
5578 const MFloat T = 1.0;
5579 MInt ind = m_mapSegIdsInOutCnd[index];
5580 std::array<MFloat, 2 * nDim> bBox{};
5581 m_solver->m_geometry->getBoundingBox(bBox.data());
5582
5583 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
5584 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
5585
5586 MInt currentId = m_bndCells[i].m_cellId;
5587
5588 MFloat rho = 1.0;
5589 extrapolateVariable(ind, currentId, PV->RHO, &rho);
5590
5591 // This is different to bc1000
5592 // Channel height
5593 const MFloat tmpWidth = fabs(bBox[nDim + 1] - bBox[1]);
5594 // WARNING, I dont understand this, but without is the testcase does not work
5595 const MFloat bottom = bBox[1];
5596
5597 const MFloat relPos = (m_solver->a_coordinate(currentId, 1) - bottom) / tmpWidth;
5598 const MFloat parabola = (relPos - relPos * relPos);
5599
5600 const MFloat linear = relPos;
5601 std::array<MFloat, nDim> l_uu{};
5602 l_uu[0] = m_Ma * LBCS
5603 * ((1.0 - m_solver->m_CouettePoiseuilleRatio) * linear + m_solver->m_CouettePoiseuilleRatio * parabola)
5604 * rho;
5605 l_uu[1] = 0.0;
5606 IF_CONSTEXPR(nDim == 3) { l_uu[2] = 0.0; }
5607
5608 // TODO: As long as rho * u is saved as a primitive variable in the thermal collision steps,
5609 // rho * u should be set in the BCs as well. The Eq dists, however, require only u.
5610 std::array<MFloat, nDim> u{};
5611 for(MInt d = 0; d < nDim; d++) {
5612 u[d] = l_uu[d] / rho;
5613 }
5614 const MFloat squaredVelocity = std::inner_product(&u[0], &u[nDim], &u[0], .0);
5615
5616 // TODO: This is not correct. It should be changed in a separat branch, since the modification of testcases is
5617 // required
5618 m_solver->setEqDists(currentId, rho, squaredVelocity, u.data());
5619 m_solver->setEqDistsThermal(currentId, T, rho, squaredVelocity, u.data());
5620
5621 for(MInt n = 0; n < nDim; n++) {
5622 m_solver->a_variable(currentId, PV->VV[n]) = l_uu[n];
5623 }
5624 m_solver->a_variable(currentId, PV->RHO) = rho;
5625 m_solver->a_variable(currentId, PV->T) = T;
5626 }
5627 writeBCOutput(index);
5628}
5629
5635template <MInt nDim, MInt nDist, class SysEqn>
5637 TRACE();
5638
5639 MFloat F2q, rhoF, uF[nDim], uBF[nDim], tmpUF, tmpUBF, tmp2UF, b[2 * nDim], tmpUW, c[2 * nDim];
5640 MFloat uW[nDim] = {F0};
5641 getBoundaryVelocity(index, uW);
5642
5643 MInt id, k, tpIndex;
5644 MFloat fEq, xi;
5645 MInt nghbrId;
5646
5647 for(MInt d = 0; d < nDim; d++) {
5648 c[2 * d] = -uW[d];
5649 c[2 * d + 1] = uW[d];
5650 }
5651
5652 // go through m_bndCells with wallbndcnd
5653 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
5654 // if((globalTimeStep - 1 ) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0 )
5655 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
5656 // since it is a rhsBnd it should be performed after propagation
5657 // TODO labels:LB,totest Not tested yet for a locally refined mesh
5658
5659 id = m_bndCells[i].m_cellId;
5660
5661 m_omega = 2.0 / (1.0 + 6.0 * m_solver->a_nu(id) * FFPOW2(m_solver->maxLevel() - m_solver->a_level(id)));
5662
5663 rhoF = 1.0; // standard value which is usually overwritten in the following. Only in exceptional case it remains 1
5664
5665 //----------------------------------------------------------------------------------------------------
5666 // perform bounce back for distributions piercing the wall - distinquish between inner and outer cells
5667 //----------------------------------------------------------------------------------------------------
5668
5669 //---------------------------------------------------------------------------------
5670 // 1. boundary cell is inside fluid
5671 //---------------------------------------------------------------------------------
5672 if(m_bndCells[i].m_isFluid) {
5673 if(m_densityFluctuations)
5674 rhoF = 1.0 + m_solver->a_variable(id, PV->RHO);
5675 else
5676 rhoF = m_solver->a_variable(id, PV->RHO);
5677
5678 for(MInt n = 0; n < nDim; n++) {
5679 uF[n] = m_solver->a_variable(id, PV->VV[n]);
5680 b[2 * n] = -uF[n];
5681 b[2 * n + 1] = uF[n];
5682 }
5683
5684 tmp2UF = std::inner_product(&uF[0], &uF[nDim], &uF[0], .0);
5685
5686 for(MInt j = 0; j < nDist - 1; j++) {
5687 if(j < Ld::distFld(0)) {
5688 tmpUF = b[j];
5689 tmpUW = c[j];
5690 tpIndex = 1;
5691 } else {
5692 if(j < (Ld::distFld(0) + Ld::distFld(1))) {
5693 k = j - Ld::distFld(0);
5694 tmpUF = (b[Ld::mFld1(2 * k)] + b[Ld::mFld1(2 * k + 1)]);
5695 tmpUW = (c[Ld::mFld1(2 * k)] + c[Ld::mFld1(2 * k + 1)]);
5696 tpIndex = 2;
5697 } else {
5698 k = j - (Ld::distFld(0) + Ld::distFld(1));
5699 tmpUF = (b[Ld::mFld2(3 * k)] + b[Ld::mFld2(3 * k + 1)] + b[Ld::mFld2(3 * k + 2)]);
5700 tmpUW = (c[Ld::mFld2(3 * k)] + c[Ld::mFld2(3 * k + 1)] + c[Ld::mFld2(3 * k + 2)]);
5701 tpIndex = 3;
5702 }
5703 }
5704
5705 //---------------------------------------------------------------------------------
5706 // if q < 0.5, perform bounce back
5707 //---------------------------------------------------------------------------------
5708
5709 if(m_bndCells[i].m_distances[j] < 0.5) {
5710 F2q = F2 * m_bndCells[i].m_distances[j];
5711
5712 if(m_solver->a_hasNeighbor(id, Ld::oppositeDist(j)) > 0) {
5713 nghbrId = m_solver->c_neighborId(id, Ld::oppositeDist(j));
5714
5715 tmpUBF = F0;
5716 for(MInt d = 0; d < nDim; d++) {
5717 uBF[d] = m_solver->a_variable(nghbrId, d);
5718 tmpUBF += (Ld::idFld(j, d) - 1) * uBF[d];
5719 }
5720 } else {
5721 tmpUBF = tmpUF;
5722 }
5723
5724 fEq = Ld::tp(tpIndex) * rhoF
5725 * (1.0 + tmpUBF * F1BCSsq + tmpUF * tmpUF * F1BCSsq * F1BCSsq * F1B2 - tmp2UF * F1BCSsq * F1B2);
5726 xi = m_omega * (F2q - 1.0) / (1.0 - 2.0 * m_omega);
5727 // xi = m_omega * (F2q - 1.0) / (1.0 - m_omega);
5728
5729 // perform interpolated bounce back
5730 m_solver->a_oldDistribution(id, Ld::oppositeDist(j)) =
5731 (1.0 - xi) * m_solver->a_distribution(id, j) + xi * fEq
5732 - 2.0 * Ld::tp(tpIndex) * rhoF * F1BCSsq * tmpUW; // the minus corresponds to the opposite direction
5733
5734 }
5735
5736 // if the cell is located at a corner, there might be distributions missing
5737 else {
5738 // In this case simple bounce back is used. This could decrease the overall accuracy.
5739 if(m_solver->a_hasNeighbor(id, j) == 0) {
5740 m_solver->a_oldDistribution(id, Ld::oppositeDist(j)) = m_solver->a_distribution(id, j);
5741 }
5742 }
5743
5744 } // end of loop over all directions
5745
5746 }
5747
5748 //---------------------------------------------------------------------------------
5749 // 2. boundary cell is outside fluid
5750 // if the inner neighbor is a halo cell, it can be skipped
5751 //---------------------------------------------------------------------------------
5752
5753 // The outer cell does not belong to the flow field, thus the macroscopic values are held constant.
5754 // It is possible that there are cells without any cutting velocity! Those have to be controlled, too.
5755 else {
5756 for(MInt j = 0; j < nDist - 1; j++) {
5757 //----------------------------------------------------------------------------------------------------------------
5758 // if q_innerNeighbor = 1 - q_bndCell >= 0.5, perform bounceback on inner neighbor
5759 //----------------------------------------------------------------------------------------------------------------
5760 if(m_solver->a_hasNeighbor(id, j) > 0 && !m_solver->a_isHalo(m_solver->c_neighborId(id, j))) {
5761 if(m_bndCells[i].m_distances[j] <= 0.5) {
5762 F2q = F2 - F2 * m_bndCells[i].m_distances[j]; // q = 1 - 0.5*(distance/cellHalfLength);
5763
5764 nghbrId = m_solver->c_neighborId(id, j);
5765
5766 if(m_densityFluctuations)
5767 rhoF = 1.0 + m_solver->a_variable(nghbrId, PV->RHO);
5768 else
5769 rhoF = m_solver->a_variable(nghbrId, PV->RHO);
5770
5771 for(MInt n = 0; n < nDim; n++) {
5772 uF[n] = m_solver->a_variable(nghbrId, PV->VV[n]);
5773 b[2 * n] = -uF[n];
5774 b[2 * n + 1] = uF[n];
5775 }
5776
5777 tmp2UF = std::inner_product(&uF[0], &uF[nDim], &uF[0], .0);
5778
5779 if(j < Ld::distFld(0)) {
5780 tmpUF = b[Ld::oppositeDist(j)];
5781 tmpUW = c[Ld::oppositeDist(j)];
5782 tpIndex = 1;
5783 } else {
5784 if(j < (Ld::distFld(0) + Ld::distFld(1))) {
5785 k = Ld::oppositeDist(j) - Ld::distFld(0);
5786 tmpUF = (b[Ld::mFld1(2 * k)] + b[Ld::mFld1(2 * k + 1)]);
5787 tmpUW = (c[Ld::mFld1(2 * k)] + c[Ld::mFld1(2 * k + 1)]);
5788 tpIndex = 2;
5789 } else {
5790 k = Ld::oppositeDist(j) - (Ld::distFld(0) + Ld::distFld(1));
5791 tmpUF = (b[Ld::mFld2(3 * k)] + b[Ld::mFld2(3 * k + 1)] + b[Ld::mFld2(3 * k + 2)]);
5792 tmpUW = (c[Ld::mFld2(3 * k)] + c[Ld::mFld2(3 * k + 1)] + c[Ld::mFld2(3 * k + 2)]);
5793 tpIndex = 3;
5794 }
5795 }
5796
5797 tmpUBF = 0.0;
5798 for(MInt dim = 0; dim < nDim; dim++) {
5799 uBF[dim] = (1.0 - 3.0 / F2q) * uF[dim] + (3.0 / F2q) * uW[dim];
5800 tmpUBF += (Ld::idFld(Ld::oppositeDist(j), dim) - 1) * uBF[dim];
5801 }
5802
5803 fEq = Ld::tp(tpIndex) * rhoF
5804 * (1.0 + tmpUBF * F1BCSsq + tmpUF * tmpUF * F1BCSsq * F1BCSsq * F1B2 - tmp2UF * F1BCSsq * F1B2);
5805 xi = m_omega * (F2q - 1.0) / (1.0 + 0.5 * m_omega);
5806 // xi = m_omega * (F2q - 1.0);
5807
5808 // perform interpolated bounce back
5809 m_solver->a_oldDistribution(nghbrId, j) =
5810 (1.0 - xi) * m_solver->a_distribution(nghbrId, Ld::oppositeDist(j)) + xi * fEq
5811 - 2.0 * Ld::tp(tpIndex) * rhoF * F1BCSsq * tmpUW; // the minus corresponds to the opposite direction
5812 }
5813 }
5814
5815 } // end of loop over all directions
5816 m_solver->setEqDists(id, F1, uW);
5817 }
5818 } // end of loop over all m_bndCells
5819 if(m_calcWallForces) calculateWallForces(index);
5820}
5821
5831template <MInt nDim, MInt nDist, class SysEqn>
5833 TRACE();
5834
5835 if(globalTimeStep % m_calcWallForcesInterval != 0) return;
5836
5837 std::array<MFloat, nDim> forceWall_;
5838 forceWall_.fill(0.0);
5839
5840 MFloat uW[nDim] = {F0};
5841 getBoundaryVelocity(index, uW);
5842
5843 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
5844 const MInt pCellId = m_bndCells[i].m_cellId;
5845
5846 if(m_solver->a_level(pCellId) != m_solver->maxLevel()) continue;
5847
5848 // case 1: boundary cell is inside fluid
5849 if(m_bndCells[i].m_isFluid) {
5850 if(m_solver->a_hasProperty(pCellId, Cell::IsHalo)) {
5851 continue;
5852 }
5853
5854 for(MInt j = 0; j < nDist - 1; j++) {
5855 const MInt opposite = Ld::oppositeDist(j);
5856
5857 MFloat Vin[nDim], Vout[nDim];
5858 for(MInt d = 0; d < nDim; d++) {
5859 Vin[d] = Ld::ppdfDir(j, d) - uW[d];
5860 Vout[d] = Ld::ppdfDir(opposite, d) - uW[d];
5861 }
5862
5863 // if q < 1.0, bounce-back was performed
5864 if(m_bndCells[i].m_distances[j] < 1.0) {
5865 if(m_solver->a_hasNeighbor(pCellId, opposite)) {
5866 for(MInt d = 0; d < nDim; d++) {
5867 forceWall_[d] += m_solver->a_distribution(pCellId, j) * Vin[d]
5868 - m_solver->a_oldDistribution(pCellId, opposite) * Vout[d];
5869 }
5870 }
5871 }
5872 }
5873 }
5874 // case 2: boundary cell is inside solid
5875 else {
5876 for(MInt j = 0; j < nDist - 1; j++) {
5877 // do we have a neighbor at all?
5878 if(m_solver->a_hasNeighbor(pCellId, j)) {
5879 // make sure that the neighbor is not a halo cell
5880 const MInt neighborId = m_solver->c_neighborId(pCellId, j);
5881 if(m_solver->a_hasProperty(neighborId, Cell::IsHalo)) {
5882 continue;
5883 }
5884 const MInt opposite = Ld::oppositeDist(j);
5885
5886 MFloat Vin[nDim], Vout[nDim];
5887 for(MInt d = 0; d < nDim; d++) {
5888 Vin[d] = Ld::ppdfDir(opposite, d) - uW[d];
5889 Vout[d] = Ld::ppdfDir(j, d) - uW[d];
5890 }
5891
5892 // if q < 0.5, perform bounce back (this is meant from the outer cell, the inner cell then has q >= 0.5)
5893 const MFloat q = m_bndCells[i].m_distances[j];
5894 if(q < 0.5) {
5895 // check if my neighbor is an inside cell or a boundary cell with the cell center inside
5896 const MInt nbndid = m_solver->a_bndId(neighborId);
5897 if(nbndid == -1 || m_bndCells[nbndid].m_isFluid) {
5898 // Add differences of PPDFS before and after collision to momentum sum
5899 for(MInt d = 0; d < nDim; d++) {
5900 forceWall_[d] += m_solver->a_distribution(neighborId, opposite) * Vin[d]
5901 - m_solver->a_oldDistribution(neighborId, j) * Vout[d];
5902 }
5903 }
5904 }
5905 }
5906 }
5907 }
5908 }
5909
5910 std::array<MFloat, nDim> forceWall;
5911 forceWall.fill(0.0);
5912
5913 const MInt segId = this->m_mapIndex2BndCndSegId[index];
5914 const auto& cwfc = this->m_mapWallForceContainer[segId];
5915 // communication if required
5916 if(cwfc.noComm > 1) {
5917 MPI_Reduce(forceWall_.data(), forceWall.data(), nDim, MPI_DOUBLE, MPI_SUM, 0, cwfc.comm, AT_, "forceWall_",
5918 "forceWall");
5919 } else {
5920 forceWall = forceWall_;
5921 }
5922
5923 // output
5924 if(cwfc.isRoot) {
5925 std::FILE* forceFile;
5926 forceFile = fopen(cwfc.fileName.c_str(), "a+");
5927 fprintf(forceFile, "%10d\t", globalTimeStep);
5928 for(MInt d = 0; d < nDim; d++) {
5929 fprintf(forceFile, "%15e\t", forceWall[d]);
5930 }
5931 fprintf(forceFile, "\n");
5932 fclose(forceFile);
5933 }
5934}
5935
5945template <MInt nDim, MInt nDist, class SysEqn>
5947 TRACE();
5948 ASSERT(set < m_solver->m_maxNoSets, "ERROR: wrong set is choosen");
5949
5950 if(globalTimeStep % m_calcWallForcesInterval != 0) return;
5951
5952 std::array<MFloat, nDim> forceWall_;
5953 forceWall_.fill(0.0);
5954
5955 for(MInt cellIndex = 0; cellIndex < m_boundaryCellsMb.size(); cellIndex++) {
5956 const MInt pCellId = m_boundaryCellsMb.cellId(cellIndex);
5957 MFloat uW[nDim] = {F0};
5958 getBoundaryVelocityMb(cellIndex, uW);
5959
5960 // case 1: boundary cell is inside fluid
5961 if(m_solver->a_levelSetFunctionMB(pCellId, set) > 0) {
5962 if(m_solver->a_hasProperty(pCellId, Cell::IsHalo)) {
5963 continue;
5964 }
5965 for(MInt j = 0; j < nDist - 1; j++) {
5966 const MInt opposite = Ld::oppositeDist(j);
5967
5968 MFloat Vin[nDim], Vout[nDim];
5969 for(MInt d = 0; d < nDim; d++) {
5970 Vin[d] = Ld::ppdfDir(j, d) - uW[d];
5971 Vout[d] = Ld::ppdfDir(opposite, d) - uW[d];
5972 }
5973
5974 const MFloat q = getDistanceMb(pCellId, cellIndex, j);
5975
5976 if(q <= 0.5) {
5977 if(m_solver->a_hasNeighbor(pCellId, opposite)) {
5978 for(MInt d = 0; d < nDim; d++) {
5979 forceWall_[d] += m_solver->a_distribution(pCellId, j) * Vin[d]
5980 - m_solver->a_oldDistribution(pCellId, opposite) * Vout[d];
5981 }
5982 }
5983 }
5984 }
5985 } else {
5986 for(MInt j = 0; j < nDist - 1; j++) {
5987 // do we have a neighbor at all?
5988 if(m_solver->a_hasNeighbor(pCellId, j)) {
5989 // make sure that the neighbor is not a halo cell
5990 const MInt neighborId = m_solver->c_neighborId(pCellId, j);
5991 if(m_solver->a_hasProperty(neighborId, Cell::IsHalo)) {
5992 continue;
5993 }
5994 const MInt opposite = Ld::oppositeDist(j);
5995
5996 MFloat Vin[nDim], Vout[nDim];
5997 for(MInt d = 0; d < nDim; d++) {
5998 Vin[d] = Ld::ppdfDir(opposite, d) - uW[d];
5999 Vout[d] = Ld::ppdfDir(j, d) - uW[d];
6000 }
6001
6002 // if q < 0.5, perform bounce back (this is meant from the outer cell, the inner cell then has q >= 0.5)
6003 const MFloat q = getDistanceMb(pCellId, cellIndex, j);
6004 if(q < 0.5) {
6005 // check if my neighbor is an inside cell or a boundary cell with the cell center inside
6006 const MBool nbndid = m_solver->a_isG0CandidateOfSet(neighborId, (set - m_solver->m_levelSetId));
6007 if((!nbndid || m_solver->a_levelSetFunctionMB(neighborId, set) > 0)) {
6008 // Add differences of PPDFS before and after collision to momentum sum
6009 for(MInt d = 0; d < nDim; d++) {
6010 forceWall_[d] += m_solver->a_distribution(neighborId, opposite) * Vin[d]
6011 - m_solver->a_oldDistribution(neighborId, j) * Vout[d];
6012 }
6013 }
6014 }
6015 }
6016 }
6017 }
6018 }
6019
6020 std::array<MFloat, nDim> forceWall;
6021 forceWall.fill(0.0);
6022
6023 // communication if required
6024 MBool isRoot = true;
6025 if(m_solver->noDomains() > 1) {
6026 MPI_Reduce(forceWall_.data(), forceWall.data(), nDim, MPI_DOUBLE, MPI_SUM, 0, m_BCWallMBComm, AT_, "forceWall_",
6027 "forceWall");
6028 isRoot = m_solver->domainId() == m_BCWallMBNeighbors[0];
6029 } else {
6030 forceWall = forceWall_;
6031 }
6032
6033 // output
6034 if(isRoot) {
6035 std::FILE* forceFile;
6036 forceFile = fopen(m_forceFile.c_str(), "a+");
6037 fprintf(forceFile, "%10d\t", globalTimeStep);
6038 for(MInt d = 0; d < nDim; d++) {
6039 fprintf(forceFile, "%15e\t", forceWall[d]);
6040 }
6041 // drag coefficient for a sphere
6042 // const MFloat factor = 1 / (0.5 * m_Ma * m_Ma * F1B3 * PI * m_referenceLength * m_referenceLength * 0.25);
6043 // for(MInt d = 0; d < nDim; d++) {
6044 // fprintf(forceFile, "%e\t", forceWall[d]*factor);
6045 // }
6046 fprintf(forceFile, "\n");
6047 fclose(forceFile);
6048 }
6049}
6050
6065template <MInt nDim, MInt nDist, class SysEqn>
6067 TRACE();
6068
6069 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
6070 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
6071
6072 const MInt currentId = m_bndCells[i].m_cellId;
6073 const MInt pCellId = currentId;
6074
6075 const MFloat l_rho = 1.0;
6076 MFloat l_uu[nDim] = {0.0};
6077 l_uu[0] = m_Ma * LBCS;
6078
6079 const MFloat squaredVelocity = std::inner_product(&l_uu[0], &l_uu[nDim], &l_uu[0], .0);
6080
6081 // calulate the equilibrium distribution functions
6082 m_solver->setEqDists(pCellId, l_rho, squaredVelocity, l_uu);
6083
6084 m_solver->a_variable(pCellId, PV->RHO) = l_rho;
6085 for(MInt n = 0; n < nDim; n++) {
6086 m_solver->a_variable(pCellId, PV->VV[n]) = l_uu[n];
6087 }
6088 }
6089}
6090
6104template <MInt nDim, MInt nDist, class SysEqn>
6106 TRACE();
6107
6108 const MInt ind = m_mapSegIdsInOutCnd[index];
6109 const MFloat ma_x_F1BCS = m_Ma * LBCS;
6110 const MInt pvrho = PV->RHO;
6111 const MInt pvt = PV->T;
6112
6113#ifdef WAR_NVHPC_PSTL
6114 const MInt globalTimeStep_ = globalTimeStep - 1;
6115 const MInt begin = m_bndCndOffsets[index];
6116 const MInt end = m_bndCndOffsets[index + 1];
6117 const MInt offset = end - begin;
6118
6119 maia::parallelFor<true>(0, offset, [=](MInt id) {
6120 const MInt i = begin + id;
6121 if((globalTimeStep_) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) return;
6122#else
6123 maia::parallelFor(m_bndCndOffsets[index], m_bndCndOffsets[index + 1], [=](MInt i) {
6124 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) return;
6125#endif
6126
6127 LbGridBoundaryCell<nDim>* bndCell = &m_bndCells[i];
6128 MInt currentId = (*bndCell).m_cellId;
6129 const MInt pCellId = currentId;
6130
6131 MFloat l_rho = 1.0;
6132
6133 // extrapolate density
6134 extrapolateVariable(ind, pCellId, pvrho, &l_rho);
6135
6136 std::array<MFloat, nDim> l_uu{};
6137 for(MInt n = 0; n < nDim; n++) {
6138 l_uu[n] = m_initialVelocityVecs[ind][n] * ma_x_F1BCS * (*bndCell).m_multiplier * m_zeroInflowVelocity * l_rho;
6139 }
6140
6141 // TODO: As long as rho * u is saved as a primitive variable in the thermal collision steps,
6142 // rho * u should be set in the BCs as well. The Eq dists, however, require only u.
6143 std::array<MFloat, nDim> u{};
6144 for(MInt n = 0; n < nDim; n++) {
6145 u[n] = l_uu[n] / l_rho;
6146 }
6147 const MFloat squaredVelocity = std::inner_product(&u[0], &u[nDim], &u[0], .0);
6148
6149 // calulate the equilibrium distribution functions for the new density
6150 m_solver->setEqDists(pCellId, l_rho, squaredVelocity, u.data());
6151 MFloat l_t = 1.0;
6152 m_solver->setEqDistsThermal(pCellId, l_t, l_rho, squaredVelocity, u.data());
6153
6154 for(MInt n = 0; n < nDim; n++) {
6155 m_solver->a_variable(pCellId, n) = l_uu[n];
6156 }
6157 m_solver->a_variable(pCellId, pvrho) = l_rho;
6158 m_solver->a_variable(pCellId, pvt) = l_t;
6159#ifdef WAR_NVHPC_PSTL
6160 });
6161#else
6162 });
6163#endif
6164 writeBCOutput(index);
6165}
6166
6180template <MInt nDim, MInt nDist, class SysEqn>
6182 TRACE();
6183
6184 const MInt ind = m_mapSegIdsInOutCnd[index];
6185 const MInt x_pos = m_solver->m_referenceLength * m_solver->m_blasiusPos;
6186 const MInt pvrho = PV->RHO;
6187
6188 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
6189 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
6190
6191 LbGridBoundaryCell<nDim>* bndCell = &m_bndCells[i];
6192 const MInt currentId = (*bndCell).m_cellId;
6193 const MInt pCellId = currentId;
6194
6195 // Use Blasius
6196 const MFloat eta = (*bndCell).m_eta;
6197 const MInt pos = (eta / m_blasius_delta);
6198
6199 MFloat l_rho = 1.0;
6200
6201 // extrapolate density
6202 extrapolateVariable(ind, pCellId, pvrho, &l_rho);
6203
6204 std::array<MFloat, nDim> l_u{};
6205 std::array<MFloat, nDim> u{};
6206 const MFloat l_t = m_solver->m_initTemperatureKelvin - m_blasius[pos][2];
6207 l_u[0] = (m_Ma * LBCS) * m_blasius[pos][2] * l_rho;
6208 IF_CONSTEXPR(nDim == 3) l_u[1] = 0.0 * l_rho;
6209 l_u[nDim - 1] =
6210 0.5 * sqrt((m_Ma * LBCS) * m_solver->m_nu / x_pos) * (eta * m_blasius[pos][2] - m_blasius[pos][1]) * l_rho;
6211
6212 // TODO: As long as rho * u is saved as a primitive variable in the thermal collision steps,
6213 // rho * u should be set in the BCs as well. The Eq dists, however, require only u.
6214 for(MInt n = 0; n < nDim; n++) {
6215 u[n] = l_u[n] / l_rho;
6216 }
6217 const MFloat squaredVelocity = std::inner_product(&u[0], &u[nDim], &u[0], .0);
6218
6219 // calulate the equilibrium distribution functions for the new density
6220 m_solver->setEqDists(pCellId, l_rho, squaredVelocity, u.data());
6221 m_solver->setEqDistsThermal(pCellId, l_t, l_rho, squaredVelocity, u.data());
6222
6223 m_solver->a_variable(pCellId, PV->U) = l_u[0];
6224 m_solver->a_variable(pCellId, PV->V) = l_u[1];
6225 IF_CONSTEXPR(nDim == 3) m_solver->a_variable(pCellId, PV->W) = l_u[2];
6226 m_solver->a_variable(pCellId, PV->RHO) = l_rho;
6227 m_solver->a_variable(pCellId, PV->T) = l_t;
6228 }
6229}
6230
6239template <MInt nDim, MInt nDist, class SysEqn>
6240void LbBndCndDxQy<nDim, nDist, SysEqn>::bc10090(MInt index) {
6241 TRACE();
6242
6243 const MInt ind = m_mapSegIdsInOutCnd[index];
6244 const MFloat ma_x_F1BCS = m_Ma * LBCS;
6245
6246 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
6247 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
6248
6249 LbGridBoundaryCell<nDim>* bndCell = &m_bndCells[i];
6250 const MInt currentId = (*bndCell).m_cellId;
6251 const MInt pCellId = currentId;
6252
6253 MFloat l_rho = 1.0;
6254 const MFloat l_t = 1.0;
6255
6256 // extrapolate density
6257 extrapolateVariable(ind, pCellId, PV->RHO, &l_rho);
6258
6259 MFloat l_c = 0.0;
6260 std::array<MFloat, nDim> l_uu{};
6261 std::array<MFloat, nDim> u{};
6262 for(MInt n = 0; n < nDim; n++) {
6263 l_uu[n] = m_initialVelocityVecs[ind][n] * ma_x_F1BCS * (*bndCell).m_multiplier * m_zeroInflowVelocity * l_rho;
6264 }
6265
6266 // TODO: As long as rho * u is saved as a primitive variable in the thermal collision steps,
6267 // rho * u should be set in the BCs as well. The Eq dists, however, require only u.
6268 for(MInt n = 0; n < nDim; n++) {
6269 u[n] = l_uu[n] / l_rho;
6270 }
6271 const MFloat squaredVelocity = std::inner_product(&u[0], &u[nDim], &u[0], .0);
6272
6273 // calculate the equlibrium distribution functions for the new density
6274 m_solver->setEqDists(pCellId, l_rho, squaredVelocity, u.data());
6275 m_solver->setEqDistsThermal(pCellId, l_t, l_rho, squaredVelocity, u.data());
6276 m_solver->setEqDistsTransport(pCellId, l_c, squaredVelocity, u.data());
6277
6278 for(MInt n = 0; n < nDim; n++) {
6279 m_solver->a_variable(pCellId, PV->VV[n]) = l_uu[n];
6280 }
6281 m_solver->a_variable(pCellId, PV->RHO) = l_rho;
6282 if(m_solver->m_isThermal) m_solver->a_variable(pCellId, PV->T) = l_t;
6283 m_solver->a_variable(pCellId, PV->C) = l_c;
6284 }
6285 writeBCOutput(index);
6286}
6287
6297template <MInt nDim, MInt nDist, class SysEqn>
6299 TRACE();
6300
6301 const MInt ind = m_mapSegIdsInOutCnd[index];
6302 constexpr MFloat rho0 = 1.0;
6303 const MFloat trgRhoU = rho0 * m_Ma * LBCS;
6304
6305 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
6306 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
6307
6308 auto& bndCell = m_bndCells[i];
6309 const MInt cellId = bndCell.m_cellId;
6310 MFloat rho = 1.0;
6311 extrapolateVariable(ind, cellId, PV->RHO, &rho);
6312 const MFloat trgU = trgRhoU / rho;
6313 MFloat u[nDim];
6314 for(MInt n = 0; n < nDim; n++) {
6315 u[n] = trgU * m_initialVelocityVecs[ind][n];
6316 }
6317 m_solver->setEqDists(cellId, rho, u);
6318 for(MInt n = 0; n < nDim; n++) {
6319 m_solver->a_variable(cellId, PV->U + n) = u[n];
6320 }
6321 m_solver->a_variable(cellId, PV->RHO) = rho;
6322 }
6323 // writeBCOutput(index);
6324}
6325
6340template <MInt nDim, MInt nDist, class SysEqn>
6342 TRACE();
6343
6344 MInt ind = m_mapSegIdsInOutCnd[index];
6345 MFloat pulsatile = -1.0 * sin(2.0 * PI * m_pulsatileFrequency * globalTimeStep);
6346 MFloat ma_x_pulsatile_F1BCS = m_Ma * pulsatile * LBCS;
6347 const MInt pvrho = PV->RHO;
6348
6349#ifdef WAR_NVHPC_PSTL
6350 const MInt globalTimeStep_ = globalTimeStep - 1;
6351 MInt begin = m_bndCndOffsets[index];
6352 MInt end = m_bndCndOffsets[index + 1];
6353 MInt offset = end - begin;
6354
6355 maia::parallelFor<true>(0, offset, [=](MInt id) {
6356 MInt i = begin + id;
6357 if((globalTimeStep_) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) return;
6358#else
6359 maia::parallelFor(m_bndCndOffsets[index], m_bndCndOffsets[index + 1], [=](MInt i) {
6360 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) return;
6361#endif
6362
6363 LbGridBoundaryCell<nDim>* bndCell = &m_bndCells[i];
6364 const MInt currentId = (*bndCell).m_cellId;
6365 const MInt pCellId = currentId;
6366
6367 MFloat l_rho = 1.0;
6368 MFloat l_uu[nDim] = {0.0};
6369
6370 // extrapolate density
6371 extrapolateVariable(ind, pCellId, pvrho, &l_rho);
6372
6373 for(MInt n = 0; n < nDim; n++) {
6374 l_uu[n] = m_initialVelocityVecs[ind][n] * (*bndCell).m_multiplier * ma_x_pulsatile_F1BCS;
6375 }
6376
6377 // TODO: This is not correct. It should be changed in a separat branch, since the modification of testcases is
6378 // required
6379 for(MInt n = 0; n < nDim; n++) {
6380 l_uu[n] *= l_rho;
6381 }
6382 const MFloat squaredVelocity = std::inner_product(&l_uu[0], &l_uu[nDim], &l_uu[0], .0);
6383
6384 // calulate the equilibrium distribution functions for the new density
6385 m_solver->setEqDists(pCellId, l_rho, squaredVelocity, l_uu);
6386
6387#ifdef WAR_NVHPC_PSTL
6388 for(MInt n = 0; n < nDim; n++) {
6389 m_solver->a_variable(pCellId, n) = l_uu[n];
6390 }
6391#else
6392 for(MInt n = 0; n < nDim; n++) {
6393 m_solver->a_variable(pCellId, PV->VV[n]) = l_uu[n];
6394 }
6395#endif
6396 m_solver->a_variable(pCellId, pvrho) = l_rho;
6397#ifdef WAR_NVHPC_PSTL
6398 });
6399#else
6400 });
6401#endif
6402 writeBCOutput(index);
6403}
6404
6405
6415template <MInt nDim, MInt nDist, class SysEqn>
6417 TRACE();
6418
6419 // For now testing only the D3Q19 algorithm
6420
6421 MInt ind = m_mapSegIdsInOutCnd[index];
6422
6423 const MFloat velMag = sqrt(std::inner_product(&m_initialVelocityVecs[ind][0], &m_initialVelocityVecs[ind][nDim],
6424 &m_initialVelocityVecs[ind][0], 0.0));
6425 const MFloat rho = 1.0;
6426
6427 std::array<MFloat, nDim> u{};
6428 for(MInt n = 0; n < nDim; n++) {
6429 u[n] = m_Ma * F1BCS * m_initialVelocityVecs[ind][n] / velMag;
6430 }
6431
6432 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
6433 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
6434
6435 const MInt cellId = m_bndCells[i].m_cellId;
6436
6437 m_solver->setEqDists(cellId, rho, u.data());
6438
6439 m_solver->a_oldVariable(cellId, PV->RHO) = rho;
6440 m_solver->a_variable(cellId, PV->RHO) = rho;
6441 for(MInt n = 0; n < nDim; n++) {
6442 m_solver->a_oldVariable(cellId, PV->VV[n]) = u[n];
6443 m_solver->a_variable(cellId, PV->VV[n]) = u[n];
6444 }
6445 }
6446}
6447
6448
6476template <MInt nDim, MInt nDist, class SysEqn>
6478 TRACE();
6479
6480 const MInt ind = m_mapSegIdsInOutCnd[index];
6481 const MInt pvrho = PV->RHO;
6482
6483#ifdef WAR_NVHPC_PSTL
6484 const MInt globalTimeStep_ = globalTimeStep - 1;
6485 const MInt begin = m_bndCndOffsets[index];
6486 const MInt end = m_bndCndOffsets[index + 1];
6487 const MInt offset = end - begin;
6488
6489 maia::parallelFor<true>(0, offset, [=](MInt id) {
6490 MInt i = begin + id;
6491 if((globalTimeStep_) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) return;
6492#else
6493 maia::parallelFor(m_bndCndOffsets[index], m_bndCndOffsets[index + 1], [=](MInt i) {
6494 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) return;
6495#endif
6496
6497 const MInt currentId = m_bndCells[i].m_cellId;
6498 const MInt pCellId = currentId;
6499
6500 MFloat l_uu[nDim] = {0.0};
6501
6502 extrapolateVelocities(ind, pCellId, &l_uu[0]);
6503
6504 // non-reflecting bc (Finck, Haenel 2008)
6505 MFloat l_rho = 1.0;
6506 // TODO labels:LB Remove the if constexpr(nDim) and update Testcases (2D_channel...)
6507 if constexpr(nDim == 3) {
6508 MFloat old_squared_velocity = F0;
6509 MFloat squaredVelocity = F0;
6510 for(MInt n = 0; n < nDim; n++) {
6511 squaredVelocity += (l_uu[n] * l_uu[n]);
6512 const MFloat l_old_u = m_solver->a_oldVariable(pCellId, n);
6513 old_squared_velocity += (l_old_u * l_old_u);
6514 }
6515 const MFloat old_rho = m_solver->a_oldVariable(pCellId, pvrho);
6516 if(m_densityFluctuations)
6517 l_rho = (old_rho + F1BCS * (sqrt(squaredVelocity) - sqrt(old_squared_velocity)) + (m_rho1 - 1.0)) / 2.0;
6518 else
6519 l_rho = (old_rho + F1BCS * (sqrt(squaredVelocity) - sqrt(old_squared_velocity)) + m_rho1) / 2.0;
6520 } else {
6521 l_rho = (1.0 + m_solver->a_variable(pCellId, pvrho)) * F1B2;
6522 }
6523
6524 m_solver->setEqDists(pCellId, l_rho, l_uu);
6525
6526 m_solver->a_variable(pCellId, pvrho) = l_rho;
6527#ifdef WAR_NVHPC_PSTL
6528 for(MInt n = 0; n < nDim; n++) {
6529 m_solver->a_variable(pCellId, n) = l_uu[n];
6530 }
6531#else
6532 for(MInt n = 0; n < nDim; n++) {
6533 m_solver->a_variable(pCellId, PV->VV[n]) = l_uu[n];
6534 }
6535#endif
6536 });
6537 writeBCOutput(index);
6538}
6539
6551template <MInt nDim, MInt nDist, class SysEqn>
6553 TRACE();
6554
6555 MInt cellsWeigthed = 0;
6556 MInt weight = 0;
6557 // For the calculation of the mean density the different sizes of the
6558 // cells must be considered.
6559 MInt maxLevel = m_solver->a_level(m_bndCells[m_bndCndOffsets[index]].m_cellId);
6560 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
6561 maxLevel =
6562 (maxLevel > m_solver->a_level(m_bndCells[i].m_cellId)) ? maxLevel : m_solver->a_level(m_bndCells[i].m_cellId);
6563 }
6564
6565 MFloat meanDensity = 0.0;
6566 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
6567 weight = pow(IPOW2(maxLevel - m_solver->a_level(m_bndCells[i].m_cellId)), nDim);
6568 if(m_solver->c_noChildren(m_bndCells[i].m_cellId) == 0) {
6569 for(MInt j = 0; j < nDist; j++) {
6570 meanDensity += weight * m_solver->a_oldDistribution(m_bndCells[i].m_cellId, j);
6571 }
6572 cellsWeigthed += weight;
6573 }
6574 }
6575 meanDensity = 1.0 - meanDensity / cellsWeigthed;
6576
6577 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
6578 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
6579 for(MInt j = 0; j < nDist; j++) {
6580 m_solver->a_oldDistribution(m_bndCells[i].m_cellId, j) += Ld::tp(Ld::distType(j)) * meanDensity;
6581 }
6582 }
6583}
6584
6592template <MInt nDim, MInt nDist, class SysEqn>
6593void LbBndCndDxQy<nDim, nDist, SysEqn>::dnt(MInt index, MInt direction) {
6594 TRACE();
6595
6596 if(direction > 2 * nDim) {
6597 TERMM(1, "invalid direction for dnt!");
6598 }
6599
6600 MFloat rho, u[nDim], uInner[nDim];
6601 MInt currentId, currentDist;
6602
6603 MFloatScratchSpace eDistributions(nDist, AT_, "eDistributions");
6604 MFloatScratchSpace eDistributions1(nDist, AT_, "eDistributions1");
6605 MFloat M;
6606 MInt neighborId;
6607
6608 const MInt d = std::floor(direction / 2.0);
6609
6610 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
6611 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
6612 // since it is a rhsBnd it should be performed after propagation
6613 // TODO labels:LB,totest Not tested yet for a locally refined mesh
6614
6615 currentId = m_bndCells[i].m_cellId;
6616
6617 // leave out halo cells
6618 if(m_solver->a_isHalo(currentId)) continue;
6619
6620 neighborId = m_solver->c_neighborId(currentId, Ld::oppositeDist(direction));
6621
6622 m_omega =
6623 2.0 / (1.0 + 6.0 * m_solver->a_nu(currentId) * FFPOW2(m_solver->maxLevel() - m_solver->a_level(currentId)));
6624
6625 // extrapolate inner velocity
6626 for(MInt dim = 0; dim < nDim; dim++) {
6627 uInner[dim] = m_solver->a_variable(neighborId, PV->VV[dim]);
6628 }
6629
6630 MFloat zeroVel[nDim] = {0.0};
6631
6632 sysEqn().calcEqDists(1.0, 0.0, zeroVel, &eDistributions1[0]);
6633
6634 // prescribe velocity (extrapolated) via BFL rule
6635 for(MInt j = 0; j < Ld::dxQyFld(); j++) {
6636 currentDist = Ld::componentFld(Ld::oppositeDist(direction), j);
6637
6638 m_solver->a_oldDistribution(currentId, currentDist) =
6639 m_solver->a_oldDistribution(currentId, Ld::oppositeDist(currentDist));
6640
6641 for(MInt k = 0; k < nDim; k++) {
6642 m_solver->a_oldDistribution(currentId, currentDist) +=
6643 6 * eDistributions1[currentDist] * (Ld::idFld(currentDist, k) - 1.0) * uInner[k];
6644 }
6645 }
6646
6647 // calculate eq-distributions from actual variables
6648
6649 rho = m_solver->a_variable(m_bndCells[i].m_cellId, PV->RHO);
6650 for(MInt dim = 0; dim < nDim; dim++) {
6651 u[dim] = m_solver->a_variable(m_bndCells[i].m_cellId, PV->VV[dim]);
6652 }
6653
6654 sysEqn().calcEqDists(rho, u, &eDistributions[0]);
6655
6656 // ----------------------------------
6657 // calculate diagonal Matrix M from eq-distributions
6658 // by now only for BGK-collisions!!!
6659 // ----------------------------------
6660
6661 M = (Ld::idFld(direction, d) - 1.0) * (-m_solver->a_nu(m_bndCells[i].m_cellId) * m_omega)
6662 * (m_solver->a_oldDistribution(m_bndCells[i].m_cellId, direction) - eDistributions[direction]);
6663
6664 // calculate eq-distributions for rho = 1 and actual velocity
6665 sysEqn().calcEqDists(1.0, u, &eDistributions1[0]);
6666
6667 // ---------------------------------
6668 // overwrite incoming distribution along coordinate axes
6669 // ---------------------------------
6670 if(m_solver->a_hasNeighbor(currentId, direction) == 0) {
6671 m_solver->a_oldDistribution(m_bndCells[i].m_cellId, Ld::oppositeDist(direction)) =
6672 eDistributions1[Ld::oppositeDist(direction)] + M
6673 + (m_solver->a_oldDistribution(m_bndCells[i].m_cellId, direction) - eDistributions[direction]);
6674 }
6675 }
6676}
6677
6685template <MInt nDim, MInt nDist, class SysEqn>
6687 TRACE();
6688
6689 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
6690 const MInt currentId = m_bndCells[i].m_cellId;
6691 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(currentId)) != 0) continue;
6692
6693 // leave out halo cells
6694 if(m_solver->a_isHalo(currentId)) continue;
6695
6696 // macroscopic values
6697 const MFloat rho = m_solver->a_variable(currentId, PV->RHO);
6698 std::array<MFloat, nDim> u;
6699 std::array<MFloat, nDim> oldU;
6700 for(MInt d = 0; d < nDim; d++) {
6701 u[d] = m_solver->a_variable(currentId, PV->VV[d]);
6702 oldU[d] = m_solver->a_oldVariable(currentId, PV->VV[d]);
6703 }
6704 const MFloat squaredVelocity = std::inner_product(u.begin(), u.end(), u.begin(), 0.0);
6705
6706 // WARNING!!! The density should be calculated as in 3d
6707 // However, result is correct
6708 // TODO labels:LB Remove the if and update Testcases (2D_cylinder...)
6709 MFloat rhoNonRefl;
6710 if constexpr(nDim == 3) {
6711 // non-reflecting bc (Finck, Haenel 2007, Eq.(12-13))
6712 const MFloat oldSquaredVelocity = std::inner_product(oldU.begin(), oldU.end(), oldU.begin(), 0.0);
6713 const MFloat rho_out = (m_densityFluctuations) ? 0.0 : 1.0;
6714 rhoNonRefl = (rho + F1BCS * (sqrt(squaredVelocity) - sqrt(oldSquaredVelocity)) + rho_out) / 2.0;
6715 } else {
6716 rhoNonRefl = (rho + 1.0) / 2.0;
6717 }
6718
6719 // Calculate current and new equilibrium distribution
6720 std::array<MFloat, nDist> eqDist, eqNew;
6721 if(m_solver->isCompressible()) {
6722 sysEqn().calcEqDists(rho, squaredVelocity, u.data(), eqDist.data());
6723 sysEqn().calcEqDists(rhoNonRefl, squaredVelocity, u.data(), eqNew.data());
6724 } else {
6725 sysEqn().calcEqDists(rho, squaredVelocity, u.data(), eqDist.data());
6726 sysEqn().calcEqDists(rhoNonRefl, squaredVelocity, u.data(), eqNew.data());
6727 }
6728
6729 // Add new equilibrium and old non-equilibrium
6730 const MInt lvlDiff = m_solver->maxLevel() - m_solver->a_level(currentId);
6731 const MFloat omega = 1.0 / (0.5 + F1BCSsq * m_solver->a_nu(currentId) * FFPOW2(lvlDiff));
6732 for(MInt j = 0; j < nDist - 1; j++) {
6733 const MFloat neqDist = m_solver->a_distribution(currentId, j) - eqDist[j];
6734 m_solver->a_oldDistribution(currentId, j) = eqNew[j] + (1 - omega) * neqDist;
6735 }
6736 }
6737 writeBCOutput(index);
6738}
6739
6767template <MInt nDim, MInt nDist, class SysEqn>
6769 TRACE();
6770
6771 MInt ind = m_mapSegIdsInOutCnd[index];
6772 const MInt pvrho = PV->RHO;
6773
6774 const MFloat gamma = lb_gamma2;
6775#ifdef WAR_NVHPC_PSTL
6776 const MInt globalTimeStep_ = globalTimeStep - 1;
6777 MInt begin = m_bndCndOffsets[index];
6778 MInt end = m_bndCndOffsets[index + 1];
6779 MInt offset = end - begin;
6780
6781 maia::parallelFor<true>(0, offset, [=](MInt id) {
6782 MInt i = begin + id;
6783 if((globalTimeStep_) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) return;
6784#else
6785 maia::parallelFor(m_bndCndOffsets[index], m_bndCndOffsets[index + 1], [=](MInt i) {
6786 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) return;
6787#endif
6788
6789 MFloat l_uu[nDim] = {0.0};
6790
6791 const MInt currentId = m_bndCells[i].m_cellId;
6792 const MInt pCellId = currentId;
6793
6794 // extrapolate inner velocity values
6795 extrapolateVelocities(ind, pCellId, &l_uu[0]);
6796
6797 // okay, now recalculate density
6798 const MFloat l_old_rho = m_solver->a_oldVariable(pCellId, pvrho);
6799
6800 const MFloat squaredVelocity = std::inner_product(&l_uu[0], &l_uu[nDim], &l_uu[0], .0);
6801
6802 // this formula comes from Ingolf Hoerschlers dissitation
6803 const MFloat l_rho =
6804 pow((1.0 - (gamma - 1.0) * (1.0 / (2.0 * gamma)) * (1.0 / (l_old_rho * l_old_rho)) * 3.0 * squaredVelocity),
6805 (gamma / (gamma - 1.0)));
6806
6807 // calulate the equilibrium distribution functions for the new density
6808 m_solver->setEqDists(pCellId, l_rho, squaredVelocity, l_uu);
6809
6810#ifdef WAR_NVHPC_PSTL
6811 for(MInt n = 0; n < nDim; n++) {
6812 m_solver->a_variable(pCellId, n) = l_uu[n];
6813 }
6814#else
6815 for(MInt n = 0; n < nDim; n++) {
6816 m_solver->a_variable(pCellId, PV->VV[n]) = l_uu[n];
6817 }
6818#endif
6819 m_solver->a_variable(pCellId, pvrho) = l_rho;
6820#ifdef WAR_NVHPC_PSTL
6821 });
6822#else
6823 });
6824#endif
6825 writeBCOutput(index);
6826}
6827
6840template <MInt nDim, MInt nDist, class SysEqn>
6842 TRACE();
6843
6844 MInt ind = m_mapSegIdsInOutCnd[index];
6845
6846 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
6847 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
6848
6849 MFloat l_uu[nDim] = {0.0};
6850
6851 const MInt currentId = m_bndCells[i].m_cellId;
6852
6853 const MInt pCellId = currentId;
6854
6855 // extrapolate inner velocity values
6856 extrapolateVelocities(ind, pCellId, &l_uu[0]);
6857
6858 // hold density
6859 const MFloat l_rho = m_rho1;
6860
6861 // calulate the equilibrium distribution functions for the new density
6862 m_solver->setEqDists(pCellId, l_rho, l_uu);
6863
6864 m_solver->a_variable(pCellId, PV->U) = l_uu[0];
6865 m_solver->a_variable(pCellId, PV->V) = l_uu[1];
6866 if(nDim == 3) m_solver->a_variable(pCellId, PV->W) = l_uu[2];
6867 m_solver->a_variable(pCellId, PV->RHO) = l_rho;
6868 }
6869 writeBCOutput(index);
6870}
6871
6885template <MInt nDim, MInt nDist, class SysEqn>
6887 TRACE();
6888
6889 MInt ind = m_mapSegIdsInOutCnd[index];
6890 MFloat mean_velocity = 0.0;
6891 MInt numberOfCellsPerDomain = 0;
6892
6893 // first interpolate velocities and find out mean velocity
6894 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
6895 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
6896
6897 MFloat l_uu[nDim] = {0.0};
6898 const MInt currentId = m_bndCells[i].m_cellId;
6899 const MInt pCellId = currentId;
6900
6901 if(m_solver->c_noChildren(pCellId) == 0 && !m_solver->a_hasProperty(currentId, Cell::IsHalo)) {
6902 // extrapolate inner velocity values
6903 extrapolateVelocities(ind, pCellId, &l_uu[0]);
6904
6905 for(MInt n = 0; n < nDim; n++) {
6906 m_solver->a_variable(pCellId, PV->VV[n]) = l_uu[n];
6907 l_uu[n] /= m_rhoLast;
6908 }
6909 for(MInt n = 0; n < nDim; n++) {
6910 mean_velocity += (m_bndNormals[ind][n] * l_uu[n]);
6911 }
6912
6913 numberOfCellsPerDomain++;
6914 }
6915 }
6916
6917 MFloat mean_velocity_all = 0.0;
6918
6919 MInt totalNumberOfCells = 0;
6920 if(m_noBCNeighbors[m_mapBndCndIdSegId[index]] > 1) {
6921 MPI_Allreduce(&numberOfCellsPerDomain, &totalNumberOfCells, 1, MPI_INT, MPI_SUM,
6922 m_BCComm[m_mapBndCndIdSegId[index]], AT_, "numberOfCellsPerDomain",
6923 "totalNumberOfCells"); // without Halo cells
6924
6925 // Get the sum by communication in my group
6926 MPI_Allreduce(&mean_velocity, &mean_velocity_all, 1, MPI_DOUBLE, MPI_SUM, m_BCComm[m_mapBndCndIdSegId[index]], AT_,
6927 "mean_velocity", "mean_velocity_all");
6928 mean_velocity_all /= totalNumberOfCells;
6929 } else {
6930 totalNumberOfCells = numberOfCellsPerDomain;
6931 mean_velocity_all = mean_velocity / totalNumberOfCells;
6932 }
6933
6934 // okay, no we can calculate the new local Reynolds number:
6935 MFloat l_Re = mean_velocity_all * m_referenceLength / m_solver->m_nu;
6936 MFloat Re_diff = m_solver->m_Re - l_Re;
6937 MFloat eps = 0.01;
6938
6939 // how do we have to set the local density to obtain the real Reynolds number?
6940 MFloat l_rho = 0.0;
6941 if(globalTimeStep == 1) {
6942 l_rho = m_rho1;
6943 // this defines the initial step-size
6944 m_deltaRho = 1.0 - l_rho;
6945 m_maxDeltaRho = m_deltaRho;
6946 } else {
6947 l_rho = m_rhoLast;
6948 }
6949
6950 // get new density
6951 if(!(fabs(Re_diff) < eps)) {
6952 // we still need no increase the Reynolds number, but how far?
6953 // this means we have to decrease the density more
6954 if(Re_diff > 0) {
6955 // this is the value by which we have to scale the decrease of the density
6956 MFloat scaleRe = (m_solver->m_Re - l_Re) / (m_solver->m_Re - m_ReLast);
6957
6958 m_deltaRho = fabs(scaleRe * m_deltaRho);
6959 if(m_deltaRho > m_maxDeltaRho) m_deltaRho = m_maxDeltaRho;
6960 // else if(m_deltaRho < 0.00000001)
6961 // m_deltaRho = 0.00000001;
6962
6963 l_rho -= m_deltaRho;
6964
6965 }
6966 // we have to decrease the Reynolds number again, but how far?
6967 // this means we have to increase the density again
6968 else if(Re_diff < 0) {
6969 // this is the value by which we have to scale the decrease of the density
6970 MFloat scaleRe = (l_Re - m_solver->m_Re) / (l_Re - m_ReLast);
6971
6972 m_deltaRho = fabs(scaleRe * m_deltaRho);
6973 if(m_deltaRho > m_maxDeltaRho) m_deltaRho = m_maxDeltaRho;
6974
6975 l_rho += m_deltaRho;
6976 }
6977 }
6978
6979 if(this->m_calcBcResidual && m_solver->domainId() == m_BCneighbors[m_mapBndCndIdSegId[index]][0]) {
6980 m_BCResidualStream[m_mapBndCndIdSegId[index]]
6981 << globalTimeStep << " final Re: " << m_solver->m_Re << " local Re: " << l_Re
6982 << " delta Re: " << (m_solver->m_Re - l_Re) << " local rho: " << l_rho << " delta rho: " << m_deltaRho
6983 << " max delta rho: " << m_maxDeltaRho << " nu " << m_solver->m_nu << " #Cells " << totalNumberOfCells
6984 << std::endl;
6985 }
6986 m_rhoLast = l_rho;
6987 m_ReLast = l_Re;
6988
6989 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
6990 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
6991
6992 const MInt currentId = m_bndCells[i].m_cellId;
6993
6994 const MInt pCellId = currentId;
6995
6996 MFloat l_uu[nDim] = {0.0};
6997 for(MInt n = 0; n < nDim; n++) {
6998 l_uu[n] = m_solver->a_variable(pCellId, PV->VV[n]);
6999 }
7000 m_solver->a_variable(pCellId, PV->RHO) = l_rho;
7001
7002 // calulate the equilibrium distribution functions for the new density
7003 m_solver->setEqDists(pCellId, l_rho, l_uu);
7004 }
7005 writeBCOutput(index);
7006}
7007
7019template <MInt nDim, MInt nDist, class SysEqn>
7021 TRACE();
7022
7023 MInt ind = m_mapSegIdsInOutCnd[index];
7024
7025 MFloat l_Re = m_ReLast;
7026 // do only adaption if we meet the interval criterion or we need to report to file
7027 if(globalTimeStep % m_localReCutInterval == 0 || globalTimeStep % m_localReCutReportInterval == 0) {
7028 // calculate local mean velocity if we have a cut with the calculation plane
7029 MFloat mean_velocity = 0.0;
7030 if(m_hasLocalReCut) {
7031 for(MInt i = 0; i < (MInt)m_localReCutCells.size(); i++) {
7032 MInt pCellId = m_localReCutCells[i];
7033 MFloat density = m_solver->a_variable(pCellId, PV->RHO);
7034
7035 MFloat l_uu[nDim] = {0.0};
7036
7037 for(MInt n = 0; n < nDim; n++) {
7038 m_solver->a_variable(pCellId, PV->VV[n]) = l_uu[n];
7039 l_uu[n] /= density;
7040 }
7041 for(MInt n = 0; n < nDim; n++) {
7042 mean_velocity += (m_bndNormals[ind][n] * l_uu[n]);
7043 }
7044 }
7045 }
7046
7047 // Get the sum by communication in my group
7048 MFloat mean_velocity_all;
7049 MPI_Allreduce(&mean_velocity, &mean_velocity_all, 1, MPI_DOUBLE, MPI_SUM, m_BCComm[m_mapBndCndIdSegId[index]], AT_,
7050 "mean_velocity", "mean_velocity_all");
7051
7052 mean_velocity_all /= m_totalNoBcCells[m_mapBndCndIdSegId[index]];
7053
7054 // okay, now we can calculate the new local Reynolds number:
7055 l_Re = mean_velocity_all * m_localReCutDiameter / m_solver->m_nu;
7056 MFloat Re_diff = m_localReCutRe - l_Re;
7057
7058 MFloat eps = 0.01;
7059
7060 // update rho only if we meet the interval
7061 if(globalTimeStep % m_localReCutInterval == 0) {
7062 // shall we update the density?
7063 if(!(fabs(Re_diff) < eps)) {
7064 MFloat s = Re_diff / m_localReCutRe;
7065
7066 // we still need no increase the Reynolds number, but how far?
7067 // this means we have to decrease the density more
7068 if(Re_diff > 0) {
7069 // this is the value by which we have to scale the decrease of the density
7070 MFloat scaleRe = Re_diff / (m_localReCutRe - m_ReLast);
7071
7072 if(fabs(s) < m_localReCutAdpPerc) {
7073 m_deltaRho = fabs(scaleRe * m_deltaRho);
7074 if(m_deltaRho > m_maxDeltaRho) m_deltaRho = m_maxDeltaRho;
7075 }
7076 m_lRho -= m_deltaRho;
7077
7078 }
7079 // we have to decrease the Reynolds number again, but how far?
7080 // this means we have to increase the density again
7081
7082 else if(Re_diff < 0) {
7083 // this is the value by which we have to scale the decrease of the density
7084 MFloat scaleRe = Re_diff / (l_Re - m_ReLast);
7085
7086 if(fabs(s) < m_localReCutAdpPerc) {
7087 m_deltaRho = fabs(scaleRe * m_deltaRho);
7088 if(m_deltaRho > m_maxDeltaRho) m_deltaRho = m_maxDeltaRho;
7089 }
7090 m_lRho += m_deltaRho;
7091 }
7092 }
7093 m_rhoLast = m_lRho;
7094 m_ReLast = l_Re;
7095 }
7096
7097 if(this->m_calcBcResidual && m_solver->domainId() == m_firstBCinComm
7098 && globalTimeStep % m_localReCutReportInterval == 0)
7099 m_BCResidualStream[m_mapBndCndIdSegId[index]] << globalTimeStep << "\t" << m_localReCutRe << "\t" << l_Re << "\t"
7100 << m_ReLast << "\t" << Re_diff << "\t" << m_lRho << "\t"
7101 << m_rhoLast << "\t" << m_deltaRho << std::endl;
7102 }
7103}
7104
7119template <MInt nDim, MInt nDist, class SysEqn>
7121 TRACE();
7122
7123 if(globalTimeStep == 1) {
7124 m_lRho = m_rho1;
7125 m_deltaRho = 1.0 - m_lRho;
7126
7134 m_deltaRho = Context::getSolverProperty<MFloat>("deltaRho", m_solverId, AT_, &m_deltaRho);
7135
7136 m_maxDeltaRho = m_deltaRho;
7137 } else
7138 m_lRho = m_rhoLast;
7139
7140
7141 // rho is only recalculated if the BC is called for the first time on this domain at this time step
7142 if(globalTimeStep != m_currentTimeStep) {
7143 recalcRho(index);
7144 m_currentTimeStep = globalTimeStep;
7145 }
7146
7147 MInt ind = m_mapSegIdsInOutCnd[index];
7148 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
7149 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
7150
7151 const MInt currentId = m_bndCells[i].m_cellId;
7152 const MInt pCellId = currentId;
7153
7154 MFloat l_uu[nDim] = {0.0};
7155
7156 extrapolateVelocities(ind, pCellId, &l_uu[0]);
7157
7158 m_solver->setEqDists(pCellId, m_lRho, l_uu);
7159
7160 m_solver->a_variable(pCellId, PV->RHO) = m_lRho;
7161 for(MInt n = 0; n < nDim; n++) {
7162 m_solver->a_variable(pCellId, PV->VV[n]) = l_uu[n];
7163 }
7164 }
7165 writeBCOutput(index);
7166}
7167
7181template <MInt nDim, MInt nDist, class SysEqn>
7183 TRACE();
7184
7185 const MInt ind = m_mapSegIdsInOutCnd[index];
7186
7187 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
7188 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
7189
7190 std::array<MFloat, nDim> l_uu{};
7191 std::array<MFloat, nDim> u{};
7192 // the wall-BC gets the higher temerpature, the inflow temperature is the reference temperature
7193 const MFloat l_t = 1.0;
7194
7195 const MInt currentId = m_bndCells[i].m_cellId;
7196
7197 const MInt pCellId = currentId;
7198
7199 // extrapolate inner velocity values
7200 extrapolateVelocities(ind, pCellId, &l_uu[0]);
7201
7202 // okay, now recalculate density
7203 const MFloat l_old_rho = m_solver->a_oldVariable(pCellId, PV->RHO);
7204
7205 const MFloat squaredVelocityRho = std::inner_product(&l_uu[0], &l_uu[nDim], &l_uu[0], .0);
7206
7207 // this formula comes from Ingolf Hoerschlers dissitation
7208 MFloat l_rho = pow(
7209 (1.0
7210 - (lb_gamma2 - 1.0) * (1.0 / (2.0 * lb_gamma2)) * (1.0 / (l_old_rho * l_old_rho)) * 3.0 * squaredVelocityRho),
7211 (lb_gamma2 / (lb_gamma2 - 1.0)));
7212
7213 // TODO: As long as rho * u is saved as a primitive variable in the thermal collision steps,
7214 // rho * u should be set in the BCs as well. The Eq dists, however, require only u.
7215 for(MInt n = 0; n < nDim; n++) {
7216 u[n] = l_uu[n] / l_rho;
7217 }
7218 // calulate the equilibrium distribution functions for the new density
7219 m_solver->setEqDists(pCellId, l_rho, u.data());
7220 m_solver->setEqDistsThermal(pCellId, l_t, l_rho, u.data());
7221
7222 if(m_solver->m_isTransport) {
7223 MFloat l_c = F1;
7224 m_solver->setEqDistsTransport(pCellId, l_c, u.data());
7225 m_solver->a_variable(pCellId, PV->C) = l_c;
7226 }
7227
7228 m_solver->a_variable(pCellId, PV->RHO) = l_rho;
7229 m_solver->a_variable(pCellId, PV->T) = l_t;
7230 for(MInt n = 0; n < nDim; n++) {
7231 m_solver->a_variable(pCellId, PV->VV[n]) = l_uu[n];
7232 }
7233 }
7234 writeBCOutput(index);
7235}
7236
7249template <MInt nDim, MInt nDist, class SysEqn>
7251 TRACE();
7252
7253 MInt ind = m_mapSegIdsInOutCnd[index];
7254
7255 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
7256 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
7257
7258 MFloat l_t = 0.0;
7259 std::array<MFloat, nDim> l_uu{};
7260 std::array<MFloat, nDim> u{};
7261
7262 const MInt currentId = m_bndCells[i].m_cellId;
7263 const MInt pCellId = currentId;
7264
7265 // extrapolate inner velocity values
7266 extrapolateVelocities(ind, pCellId, &l_uu[0]);
7267 extrapolateVariable(ind, pCellId, PV->T, &l_t);
7268
7269 // this formula comes from Ingolf Hoerschlers dissitation
7270 MFloat l_rho = m_rho1;
7271
7272 // TODO: As long as rho * u is saved as a primitive variable in the thermal collision steps,
7273 // rho * u should be set in the BCs as well. The Eq dists, however, require only u.
7274 for(MInt n = 0; n < nDim; n++) {
7275 u[n] = l_uu[n] / l_rho;
7276 }
7277 const MFloat squaredVelocity = std::inner_product(&u[0], &u[nDim], &u[0], .0);
7278
7279 // calulate the equilibrium distribution functions for the new density
7280 m_solver->setEqDists(pCellId, l_rho, squaredVelocity, u.data());
7281 m_solver->setEqDistsThermal(pCellId, l_t, l_rho, squaredVelocity, u.data());
7282
7283 m_solver->a_variable(pCellId, PV->RHO) = l_rho;
7284 m_solver->a_variable(pCellId, PV->T) = l_t;
7285 for(MInt n = 0; n < nDim; n++) {
7286 m_solver->a_variable(pCellId, PV->VV[n]) = l_uu[n];
7287 }
7288 }
7289 writeBCOutput(index);
7290}
7291
7305template <MInt nDim, MInt nDist, class SysEqn>
7307 TRACE();
7308
7309 MInt ind = m_mapSegIdsInOutCnd[index];
7310 MFloat mean_velocity = 0.0;
7311 MInt numberOfCellsPerDomain = 0;
7312
7313 // first interpolate velocities and find out mean velocity
7314 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
7315 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
7316
7317 const MInt currentId = m_bndCells[i].m_cellId;
7318 const MInt pCellId = currentId;
7319
7320 if(m_solver->c_noChildren(pCellId) == 0 && !m_solver->a_hasProperty(currentId, Cell::IsHalo)) {
7321 MFloat l_uu[nDim] = {0.0};
7322
7323 // extrapolate inner velocity values
7324 extrapolateVelocities(ind, pCellId, &l_uu[0]);
7325
7326 for(MInt n = 0; n < nDim; n++) {
7327 m_solver->a_variable(pCellId, PV->VV[n]) = l_uu[n];
7328 l_uu[n] /= m_rhoLast;
7329 }
7330
7331 for(MInt n = 0; n < nDim; n++) {
7332 mean_velocity += (m_bndNormals[ind][n] * l_uu[n]);
7333 }
7334 numberOfCellsPerDomain++;
7335 }
7336 }
7337
7338 MFloat mean_velocity_all = 0.0;
7339
7340 // MInt totalNumberOfCells = m_totalNoBcCells[m_mapBndCndIdSegId[index]]; Including Halo cells
7341 MInt totalNumberOfCells = 0;
7342 if(m_noBCNeighbors[m_mapBndCndIdSegId[index]] > 1) {
7343 MPI_Allreduce(&numberOfCellsPerDomain, &totalNumberOfCells, 1, MPI_INT, MPI_SUM,
7344 m_BCComm[m_mapBndCndIdSegId[index]], AT_, "numberOfCellsPerDomain",
7345 "totalNumberOfCells"); // without Halo cells
7346
7347 // Get the sum by communication in my group
7348 MPI_Allreduce(&mean_velocity, &mean_velocity_all, 1, MPI_DOUBLE, MPI_SUM, m_BCComm[m_mapBndCndIdSegId[index]], AT_,
7349 "mean_velocity", "mean_velocity_all");
7350 mean_velocity_all /= totalNumberOfCells;
7351 } else {
7352 totalNumberOfCells = numberOfCellsPerDomain;
7353 mean_velocity_all = mean_velocity / totalNumberOfCells;
7354 }
7355
7356 // okay, no we can calculate the new local Reynolds number:
7357 MFloat l_Re = mean_velocity_all * m_referenceLength / m_solver->m_nu;
7358 MFloat Re_diff = m_solver->m_Re - l_Re;
7359 MFloat eps = 0.01;
7360
7361 // how do we have to set the local density to obtain the real Reynolds number?
7362 MFloat l_rho = 0.0;
7363 if(globalTimeStep == 1) {
7364 l_rho = m_rho1;
7365 // this defines the initial step-size
7366 m_deltaRho = 1.0 - l_rho;
7367 m_maxDeltaRho = m_deltaRho;
7368 } else {
7369 l_rho = m_rhoLast;
7370 }
7371
7372 // get new density
7373 if(!(fabs(Re_diff) < eps)) {
7374 // we still need no increase the Reynolds number, but how far?
7375 // this means we have to decrease the density more
7376 if(Re_diff > 0) {
7377 // this is the value by which we have to scale the decrease of the density
7378 MFloat scaleRe = (m_solver->m_Re - l_Re) / (m_solver->m_Re - m_ReLast);
7379
7380 m_deltaRho = fabs(scaleRe * m_deltaRho);
7381 if(m_deltaRho > m_maxDeltaRho)
7382 m_deltaRho = m_maxDeltaRho;
7383 else if(m_deltaRho < 0.00000001)
7384 m_deltaRho = 0.00000001;
7385
7386 l_rho -= m_deltaRho;
7387
7388 }
7389 // we have to decrease the Reynolds number again, but how far?
7390 // this means we have to increase the density again
7391 else if(Re_diff < 0) {
7392 // this is the value by which we have to scale the decrease of the density
7393 MFloat scaleRe = (l_Re - m_solver->m_Re) / (l_Re - m_ReLast);
7394
7395 m_deltaRho = fabs(scaleRe * m_deltaRho);
7396 if(m_deltaRho > m_maxDeltaRho)
7397 m_deltaRho = m_maxDeltaRho;
7398 else if(m_deltaRho < 0.00000001)
7399 m_deltaRho = 0.00000001;
7400
7401 l_rho += m_deltaRho;
7402 }
7403 }
7404
7405 if(this->m_calcBcResidual && m_solver->domainId() == m_BCneighbors[m_mapBndCndIdSegId[index]][0]) {
7406 m_BCResidualStream[m_mapBndCndIdSegId[index]]
7407 << globalTimeStep << " final Re: " << m_solver->m_Re << " local Re: " << l_Re
7408 << " delta Re: " << (m_solver->m_Re - l_Re) << " local rho: " << l_rho << " delta rho: " << m_deltaRho
7409 << " max delta rho: " << m_maxDeltaRho << " nu " << m_solver->m_nu << " kappa " << m_solver->m_kappa
7410 << " #Cells " << totalNumberOfCells << std::endl;
7411 }
7412 m_rhoLast = l_rho;
7413 m_ReLast = l_Re;
7414
7415 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
7416 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
7417
7418 MInt currentId = m_bndCells[i].m_cellId;
7419
7420 const MInt pCellId = currentId;
7421
7422 MFloat l_t = 0.0;
7423
7424 extrapolateVariable(ind, currentId, PV->T, &l_t);
7425
7426 m_solver->a_variable(pCellId, PV->RHO) = l_rho;
7427 m_solver->a_variable(pCellId, PV->T) = l_t;
7428 MFloat l_uu[nDim] = {0.0};
7429 for(MInt n = 0; n < nDim; n++) {
7430 l_uu[n] = m_solver->a_variable(pCellId, PV->VV[n]) / l_rho;
7431 }
7432
7433 const MFloat squaredVelocity = std::inner_product(&l_uu[0], &l_uu[nDim], &l_uu[0], .0);
7434
7435 // calulate the equilibrium distribution functions for the new density
7436 m_solver->setEqDists(pCellId, l_rho, squaredVelocity, l_uu);
7437 m_solver->setEqDistsThermal(pCellId, l_t, l_rho, squaredVelocity, l_uu);
7438
7439 if(m_solver->m_isTransport) {
7440 MFloat l_c = 0.0;
7441 extrapolateVariable(ind, currentId, PV->C, &l_c);
7442 m_solver->setEqDistsTransport(pCellId, l_c, squaredVelocity, l_uu);
7443 }
7444 }
7445 writeBCOutput(index);
7446}
7447
7457template <MInt nDim, MInt nDist, class SysEqn>
7459 TRACE();
7460
7461 MInt ind = m_mapSegIdsInOutCnd[index];
7462 MFloat mean_velocity = 0.0;
7463 MFloat mean_rho = 0.0;
7464 MFloat mean_oldRho = 0.0;
7465 MInt numberOfCellsPerDomain = 0;
7466
7467 // first calculate rhoLast and oldRhoLast
7468 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
7469 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
7470
7471 const MInt pCellId = m_bndCells[i].m_cellId;
7472
7473 if(m_solver->c_noChildren(pCellId) == 0 && !m_solver->a_hasProperty(pCellId, Cell::IsHalo)) {
7474 MFloat l_uu[nDim] = {0.0};
7475 MFloat l_rho = 0.0;
7476 MFloat l_oldRho = 0.0;
7477
7478 for(MInt n = 0; n < nDim; n++) {
7479 l_uu[n] = m_solver->a_variable(pCellId, PV->VV[n]);
7480 }
7481
7482 l_rho = m_solver->a_variable(pCellId, PV->RHO);
7483 l_oldRho = m_solver->a_oldVariable(pCellId, PV->RHO);
7484
7485 mean_rho += l_rho;
7486 mean_oldRho += l_oldRho;
7487
7488 for(MInt n = 0; n < nDim; n++) {
7489 mean_velocity += (m_bndNormals[ind][n] * l_uu[n]);
7490 }
7491
7492 numberOfCellsPerDomain++;
7493 }
7494 }
7495
7496 MFloat mean_rho_all = 0.0;
7497 MFloat mean_oldRho_all = 0.0;
7498 MFloat mean_velocity_all = 0.0;
7499
7500 // MInt totalNumberOfCells = m_totalNoBcCells[m_mapBndCndIdSegId[index]]; Including Halo cells
7501 MInt totalNumberOfCells = 0;
7502 if(m_noBCNeighbors[m_mapBndCndIdSegId[index]] > 1) {
7503 MPI_Allreduce(&numberOfCellsPerDomain, &totalNumberOfCells, 1, MPI_INT, MPI_SUM,
7504 m_BCComm[m_mapBndCndIdSegId[index]], AT_, "numberOfCellsPerDomain",
7505 "totalNumberOfCells"); // without Halo cells
7506
7507 // Get the sum by communication in my group
7508 MPI_Allreduce(&mean_rho, &mean_rho_all, 1, MPI_DOUBLE, MPI_SUM, m_BCComm[m_mapBndCndIdSegId[index]], AT_,
7509 "mean_rho", "mean_rho_all");
7510 mean_rho_all /= totalNumberOfCells;
7511
7512 // Get the sum by communication in my group
7513 MPI_Allreduce(&mean_oldRho, &mean_oldRho_all, 1, MPI_DOUBLE, MPI_SUM, m_BCComm[m_mapBndCndIdSegId[index]], AT_,
7514 "mean_oldRho", "mean_oldRho_all");
7515 mean_oldRho_all /= totalNumberOfCells;
7516
7517 // Get the sum by communication in my group
7518 MPI_Allreduce(&mean_velocity, &mean_velocity_all, 1, MPI_DOUBLE, MPI_SUM, m_BCComm[m_mapBndCndIdSegId[index]], AT_,
7519 "mean_velocity", "mean_velocity_all");
7520 mean_velocity_all /= totalNumberOfCells;
7521 } else {
7522 totalNumberOfCells = numberOfCellsPerDomain;
7523 mean_rho_all = mean_rho / totalNumberOfCells;
7524 mean_oldRho_all = mean_oldRho / totalNumberOfCells;
7525 mean_velocity_all = mean_velocity / totalNumberOfCells;
7526 }
7527
7528 // Set m_rhoLast and m_deltaRho
7529 m_rhoLast = mean_rho_all;
7530 m_deltaRho = fabs(mean_rho_all - mean_oldRho_all);
7531
7532 // Calculate the local Reynolds number:
7533 MFloat l_Re = mean_velocity_all / m_rhoLast * m_referenceLength / m_solver->m_nu;
7534
7535 // Set m_ReLast
7536 m_ReLast = l_Re;
7537}
7538
7549template <MInt nDim, MInt nDist, class SysEqn>
7551 TRACE();
7552
7553 MInt ind = m_mapSegIdsInOutCnd[index];
7554
7555 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
7556 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
7557
7558 const MInt currentId = m_bndCells[i].m_cellId;
7559 const MInt pCellId = currentId;
7560
7561 std::array<MFloat, nDim> l_uu{};
7562 std::array<MFloat, nDim> l_old_uu{};
7563 std::array<MFloat, nDim> u{};
7564
7565 extrapolateVelocities(ind, pCellId, &l_uu[0]);
7566
7567 MFloat old_rho = m_solver->a_oldVariable(pCellId, PV->RHO);
7568 for(MInt n = 0; n < nDim; n++) {
7569 l_old_uu[n] = m_solver->a_oldVariable(pCellId, PV->VV[n]);
7570 }
7571
7572 const MFloat squaredVelocity = std::inner_product(&l_uu[0], &l_uu[nDim], &l_uu[0], .0);
7573
7574 const MFloat old_squared_velocity = std::inner_product(&l_old_uu[0], &l_old_uu[nDim], &l_old_uu[0], .0);
7575
7576 // non-reflecting bc (Finck, Haenel 2008)
7577 MFloat l_rho = 1.0;
7578 if(m_densityFluctuations)
7579 l_rho = (old_rho + F1BCS * (sqrt(squaredVelocity) - sqrt(old_squared_velocity)) + (m_rho1 - 1.0)) / 2.0;
7580 else
7581 l_rho = (old_rho + F1BCS * (sqrt(squaredVelocity) - sqrt(old_squared_velocity)) + m_rho1) / 2.0;
7582
7583 // TODO: As long as rho * u is saved as a primitive variable in the thermal collision steps,
7584 // rho * u should be set in the BCs as well. The Eq dists, however, require only u.
7585 for(MInt n = 0; n < nDim; n++) {
7586 u[n] = l_uu[n] / l_rho;
7587 }
7588 MFloat l_t = 1.0;
7589 m_solver->setEqDists(pCellId, l_rho, u.data());
7590 m_solver->setEqDistsThermal(pCellId, l_t, l_rho, u.data());
7591
7592 m_solver->a_variable(pCellId, PV->RHO) = l_rho;
7593 m_solver->a_variable(pCellId, PV->T) = l_t;
7594 for(MInt n = 0; n < nDim; n++) {
7595 m_solver->a_variable(pCellId, PV->VV[n]) = l_uu[n];
7596 }
7597 }
7598}
7599
7612template <MInt nDim, MInt nDist, class SysEqn>
7614 TRACE();
7615
7616 MInt ind = m_mapSegIdsInOutCnd[index];
7617 const MInt pvrho = PV->RHO;
7618 const MInt pvt = PV->T;
7619
7620#ifdef WAR_NVHPC_PSTL
7621 const MInt globalTimeStep_ = globalTimeStep - 1;
7622 MInt begin = m_bndCndOffsets[index];
7623 MInt end = m_bndCndOffsets[index + 1];
7624 MInt offset = end - begin;
7625
7626 maia::parallelFor<true>(0, offset, [=](MInt id) {
7627 MInt i = begin + id;
7628 if((globalTimeStep_) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) return;
7629#else
7630 maia::parallelFor(m_bndCndOffsets[index], m_bndCndOffsets[index + 1], [=](MInt i) {
7631 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) return;
7632#endif
7633
7634 const MInt currentId = m_bndCells[i].m_cellId;
7635 const MInt pCellId = currentId;
7636
7637 std::array<MFloat, nDim> l_uu{};
7638 std::array<MFloat, nDim> l_old_uu{};
7639 std::array<MFloat, nDim> u{};
7640 MFloat l_t = 1.0;
7641
7642 extrapolateVelocities(ind, pCellId, &l_uu[0]);
7643 extrapolateVariable(ind, pCellId, PV->T, &l_t);
7644
7645 MFloat old_rho = m_solver->a_oldVariable(pCellId, pvrho);
7646 for(MInt n = 0; n < nDim; n++) {
7647 l_old_uu[n] = m_solver->a_oldVariable(pCellId, n);
7648 }
7649
7650 const MFloat squaredVelocity = std::inner_product(&l_uu[0], &l_uu[nDim], &l_uu[0], .0);
7651
7652 const MFloat old_squared_velocity = std::inner_product(&l_old_uu[0], &l_old_uu[nDim], &l_old_uu[0], .0);
7653
7654 // non-reflecting bc (Finck, Haenel 2008)
7655 MFloat l_rho = 1.0;
7656 if(m_densityFluctuations)
7657 l_rho = (old_rho + F1BCS * (sqrt(squaredVelocity) - sqrt(old_squared_velocity)) + (m_rho1 - 1.0)) / 2.0;
7658 else
7659 l_rho = (old_rho + F1BCS * (sqrt(squaredVelocity) - sqrt(old_squared_velocity)) + m_rho1) / 2.0;
7660
7661 // TODO: As long as rho * u is saved as a primitive variable in the thermal collision steps,
7662 // rho * u should be set in the BCs as well. The Eq dists, however, require only u.
7663 for(MInt n = 0; n < nDim; n++) {
7664 u[n] = l_uu[n] / l_rho;
7665 }
7666 m_solver->setEqDists(pCellId, l_rho, u.data());
7667 m_solver->setEqDistsThermal(pCellId, l_t, l_rho, u.data());
7668
7669 m_solver->a_variable(pCellId, pvrho) = l_rho;
7670 m_solver->a_variable(pCellId, pvt) = l_t;
7671#ifdef WAR_NVHPC_PSTL
7672 for(MInt n = 0; n < nDim; n++) {
7673 m_solver->a_variable(pCellId, n) = l_uu[n];
7674 }
7675 });
7676#else
7677 for(MInt n = 0; n < nDim; n++) {
7678 m_solver->a_variable(pCellId, PV->VV[n]) = l_uu[n];
7679 }
7680 });
7681#endif
7682 writeBCOutput(index);
7683}
7684
7694template <MInt nDim, MInt nDist, class SysEqn>
7696 TRACE();
7697
7698 MInt ind = m_mapSegIdsInOutCnd[index];
7699
7700 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
7701 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
7702
7703 const MInt currentId = m_bndCells[i].m_cellId;
7704 const MInt pCellId = currentId;
7705
7706 std::array<MFloat, nDim> l_uu{};
7707 std::array<MFloat, nDim> l_old_uu{};
7708 std::array<MFloat, nDim> u{};
7709 MFloat l_c = 1.0;
7710 MFloat l_t = 1.0;
7711
7712 extrapolateVelocities(ind, pCellId, &l_uu[0]);
7713 if(m_solver->m_isThermal) {
7714 extrapolateVariable(ind, pCellId, PV->T, &l_t);
7715 }
7716 extrapolateVariable(ind, pCellId, PV->C, &l_c);
7717
7718 MFloat old_rho = m_solver->a_oldVariable(pCellId, PV->RHO);
7719 for(MInt n = 0; n < nDim; n++) {
7720 l_old_uu[n] = m_solver->a_oldVariable(pCellId, PV->VV[n]);
7721 }
7722
7723 const MFloat squaredVelocity = std::inner_product(&l_uu[0], &l_uu[nDim], &l_uu[0], .0);
7724
7725 const MFloat old_squared_velocity = std::inner_product(&l_old_uu[0], &l_old_uu[nDim], &l_old_uu[0], .0);
7726
7727 // non-reflecting bc (Finck, Haenel 2008)
7728 MFloat l_rho = 1.0;
7729 if(m_densityFluctuations)
7730 l_rho = (old_rho + F1BCS * (sqrt(squaredVelocity) - sqrt(old_squared_velocity)) + (m_rho1 - 1.0)) / 2.0;
7731 else
7732 l_rho = (old_rho + F1BCS * (sqrt(squaredVelocity) - sqrt(old_squared_velocity)) + m_rho1) / 2.0;
7733
7734 // TODO: As long as rho * u is saved as a primitive variable in the thermal collision steps,
7735 // rho * u should be set in the BCs as well. The Eq dists, however, require only u.
7736 for(MInt n = 0; n < nDim; n++) {
7737 u[n] = l_uu[n] / l_rho;
7738 }
7739 // calulate the equilibrium distribution functions for the new density
7740 m_solver->setEqDists(pCellId, l_rho, u.data());
7741 m_solver->setEqDistsThermal(pCellId, l_t, l_rho, u.data());
7742 m_solver->setEqDistsTransport(pCellId, l_c, u.data());
7743
7744 m_solver->a_variable(pCellId, PV->RHO) = l_rho;
7745 if(m_solver->m_isThermal) m_solver->a_variable(pCellId, PV->T) = l_t;
7746 m_solver->a_variable(pCellId, PV->C) = l_c;
7747 for(MInt n = 0; n < nDim; n++) {
7748 m_solver->a_variable(pCellId, PV->VV[n]) = l_uu[n];
7749 }
7750 }
7751}
7752
7760template <MInt nDim, MInt nDist, class SysEqn>
7762 TRACE();
7763
7764 MFloat rho = F0;
7765 std::array<MFloat, nDim> u{};
7766 for(MInt d = 0; d < nDim; d++) {
7767 u[d] = std::numeric_limits<MFloat>::max();
7768 }
7769
7770 MFloat tmp;
7771 MFloat b[2 * nDim] = {0.0};
7772 MInt tmpDistId, currentId;
7773
7774 MInt ind = m_mapSegIdsInOutCnd[index];
7775
7776 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
7777 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
7778
7779 currentId = m_bndCells[i].m_cellId;
7780
7781 m_omega =
7782 2.0 / (1.0 + 6.0 * m_solver->a_nu(currentId) * FFPOW2(m_solver->maxLevel() - m_solver->a_level(currentId)));
7783 // --------------------------------------------------------------------
7784 // perform collision step once more for boundary cell (with new eq-distributions)
7785
7786 rho = m_solver->a_variable(currentId, PV->RHO);
7787
7788 for(MInt d = 0; d < nDim; d++) {
7789 u[d] = m_initialVelocityVecs[ind][d] * m_Ma * LBCS * m_bndCells[i].m_multiplier;
7790 b[2 * d] = -u[d];
7791 b[2 * d + 1] = u[d];
7792 }
7793
7794 const MFloat tmp2 = std::inner_product(&u[0], &u[nDim], &u[0], .0);
7795
7796 // Calculation of distributions for directions with only one component
7797 for(MInt j = 0; j < Ld::distFld(0); j++) {
7798 m_solver->a_distribution(currentId, j) =
7799 m_solver->a_oldDistribution(currentId, j)
7800 + m_omega
7801 * (Ld::tp(1) * F1BCSsq * (rho * CSsq + b[j] + b[j] * b[j] * F1BCSsq * F1B2 - tmp2 * F1B2)
7802 - m_solver->a_oldDistribution(currentId, j));
7803 }
7804
7805 // Calculation of distributions for directions with two components
7806 tmpDistId = Ld::distFld(0);
7807 for(MInt j = 0; j < Ld::distFld(1); j++) {
7808 tmp = (b[Ld::mFld1(2 * j)] + b[Ld::mFld1(2 * j + 1)]);
7809 m_solver->a_distribution(currentId, tmpDistId + j) =
7810 m_solver->a_oldDistribution(currentId, tmpDistId + j)
7811 + m_omega
7812 * (Ld::tp(2) * F1BCSsq * (rho * CSsq + tmp + tmp * tmp * F1BCSsq * F1B2 - tmp2 * F1B2)
7813 - m_solver->a_oldDistribution(currentId, tmpDistId + j));
7814 }
7815
7816 // Calculation of distributions for directions with three components
7817 tmpDistId = Ld::distFld(0) + Ld::distFld(1);
7818 for(MInt j = 0; j < Ld::distFld(2); j++) {
7819 tmp = (b[Ld::mFld2(3 * j)] + b[Ld::mFld2(3 * j + 1)] + b[Ld::mFld2(3 * j + 2)]);
7820 m_solver->a_distribution(currentId, tmpDistId + j) =
7821 m_solver->a_oldDistribution(currentId, tmpDistId + j)
7822 + m_omega
7823 * (Ld::tp(3) * F1BCSsq * (rho * CSsq + tmp + tmp * tmp * F1BCSsq * F1B2 - tmp2 * F1B2)
7824 - m_solver->a_oldDistribution(currentId, tmpDistId + j));
7825 }
7826 // Calculation of distribution for rest particle distribution (center)
7827 m_solver->a_distribution(currentId, Ld::lastId()) =
7828 m_solver->a_oldDistribution(currentId, Ld::lastId())
7829 + m_omega * (Ld::tp(0) * (rho - F1B2 * F1BCSsq * tmp2) - m_solver->a_oldDistribution(currentId, Ld::lastId()));
7830
7831
7832 // --------------------------------------------------------------------
7833 // extrapolation of incoming distributions
7834
7835 for(MInt j = 0; j < nDist - 1; j++) {
7836 if(m_solver->a_hasNeighbor(currentId, Ld::oppositeDist(j)) == 0) {
7837 m_solver->a_oldDistribution(currentId, j) = m_solver->a_distribution(currentId, j);
7838 }
7839 }
7840
7841 m_solver->a_variable(currentId, PV->RHO) = rho;
7842 for(MInt d = 0; d < nDim; d++) {
7843 m_solver->a_variable(currentId, d) = u[d];
7844 }
7845 }
7846}
7847
7853template <MInt nDim, MInt nDist, class SysEqn>
7855 TRACE();
7856
7857 // For now testing only the D3Q19 algorithm
7858 MFloat rho;
7859 std::array<MFloat, nDim> u{};
7860 MFloat tmp, tmp2;
7861 std::array<MFloat, 2 * nDim> b{};
7862 MInt tmpDistId, currentId;
7863
7864 MFloatScratchSpace eDistributions(nDist, AT_, "eDistributions");
7865 MFloatScratchSpace nePart(nDist, AT_, "nePart");
7866
7867 MInt ind = m_mapSegIdsInOutCnd[index];
7868
7869 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
7870 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
7871
7872 currentId = m_bndCells[i].m_cellId;
7873
7874 m_omega =
7875 2.0 / (1.0 + 6.0 * m_solver->a_nu(currentId) * FFPOW2(m_solver->maxLevel() - m_solver->a_level(currentId)));
7876
7877 // 1. Calculate eq-Distributions from actual macroscopic values
7878 //--------------------------------------------
7879
7880 // Calculate macroscopic values
7881
7882
7883 // for( MInt j = 0; j < nDist - 1; j ++){
7884 // if (m_solver->a_hasNeighbor(currentId, j) > 0 && !m_solver->a_onlyBoundary(m_solver->c_neighborId(currentId,
7885 // j))){ if (m_solver->a_onlyBoundary(m_solver->c_neighborId(currentId, j)) == true) cerr << "extrapolation
7886 // from boundary cell!" << endl; neighborId = m_solver->c_neighborId(currentId, j); break;
7887 // }
7888 // }
7889
7890 // // extrapolate inner density values
7891 // if (m_solver->a_hasNeighbor(currentId, 2) == 0) {
7892 // neighborId = m_solver->c_neighborId(currentId, 3);
7893 // }
7894 // else {
7895 // neighborId = m_solver->c_neighborId(currentId, 2);
7896 // }
7897
7898 // rho = m_solver->a_variable(neighborId, PV->RHO);
7899 // u[0] = m_solver->a_variable(neighborId, PV->U);
7900 // u[1] = m_solver->a_variable(neighborId, PV->V);
7901 // u[2] = m_solver->a_variable(neighborId, PV->W);
7902
7903 // macroscopic values
7904 rho = m_solver->a_variable(currentId, PV->RHO);
7905 for(MInt n = 0; n < nDim; n++) {
7906 u[n] = m_solver->a_variable(currentId, PV->VV[n]);
7907 b[2 * n] = -u[n];
7908 b[2 * n + 1] = u[n];
7909 }
7910
7911 tmp2 = std::inner_product(&u[0], &u[nDim], &u[0], .0);
7912
7913 // Calculation of eq-distributions for directions with only one component
7914 for(MInt j = 0; j < Ld::distFld(0); j++) {
7915 eDistributions[j] = Ld::tp(1) * F1BCSsq * (rho * CSsq + b[j] + b[j] * b[j] * F1BCSsq * F1B2 - tmp2 * F1B2);
7916 }
7917 // Calculation of eq-distributions for directions with two components
7918 tmpDistId = Ld::distFld(0);
7919 for(MInt j = 0; j < Ld::distFld(1); j++) {
7920 tmp = (b[Ld::mFld1(2 * j)] + b[Ld::mFld1(2 * j + 1)]);
7921 eDistributions[tmpDistId + j] =
7922 Ld::tp(2) * F1BCSsq * (rho * CSsq + tmp + tmp * tmp * F1BCSsq * F1B2 - tmp2 * F1B2);
7923 }
7924 // Calculation of eq-distributions for directions with three components
7925 tmpDistId = Ld::distFld(0) + Ld::distFld(1);
7926 for(MInt j = 0; j < Ld::distFld(2); j++) {
7927 tmp = (b[Ld::mFld2(3 * j)] + b[Ld::mFld2(3 * j + 1)] + b[Ld::mFld2(3 * j + 2)]);
7928 eDistributions[tmpDistId + j] =
7929 Ld::tp(3) * F1BCSsq * (rho * CSsq + tmp + tmp * tmp * F1BCSsq * F1B2 - tmp2 * F1B2);
7930 }
7931
7932 // 2. Calculation of non-eq-parts3
7933 //--------------------------------------------
7934 for(MInt j = 0; j < nDist; j++) {
7935 nePart[j] = m_solver->a_oldDistribution(currentId, j) - eDistributions[j];
7936 }
7937
7938 // 3. Calculation of incoming distributions in bnd cell
7939 //--------------------------------------------
7940
7941 // if (m_lbControlInflow == 0) {
7942 // for(MInt d=0; d < nDim; d++){
7943 // u[d] = m_initialVelocityVecs[ind][d] * m_Ma * LBCS ;
7944 // }
7945 // }
7946 // else {
7947 // for(MInt d=0; d < nDim; d++){
7948 // u[d] = m_initialVelocityVecs[ind][d] * m_Ma * LBCS * m_bndCells[i].m_multiplier ;
7949 // }
7950 // }
7951
7952 // set velocity to zero
7953 for(MInt d = 0; d < nDim; d++) {
7954 u[d] = m_initialVelocityVecs[ind][d] * m_Ma * LBCS * m_bndCells[i].m_multiplier;
7955 b[2 * d] = -u[d];
7956 b[2 * d + 1] = u[d];
7957 }
7958
7959 tmp2 = std::inner_product(&u[0], &u[nDim], &u[0], .0);
7960
7961 // Calculation of distributions for directions with only one component
7962 for(MInt j = 0; j < Ld::distFld(0); j++) {
7963 m_solver->a_oldDistribution(m_bndCells[i].m_cellId, j) =
7964 Ld::tp(1) * F1BCSsq * (rho * CSsq + b[j] + b[j] * b[j] * F1BCSsq * F1B2 - tmp2 * F1B2)
7965 + ((1 - m_omega) * nePart[j]);
7966 // m_solver->a_distribution(m_bndCells[i].m_cellId, j) = Ld::tp(1) * F1BCSsq * ( rho * CSsq + b[j] +
7967 // b[j] * b[j] * F1BCSsq * F1B2 - tmp2 * F1B2) + ((1-m_omega) * nePart[j]);
7968 }
7969
7970 // Calculation of distributions for directions with two components
7971 tmpDistId = Ld::distFld(0);
7972 for(MInt j = 0; j < Ld::distFld(1); j++) {
7973 tmp = (b[Ld::mFld1(2 * j)] + b[Ld::mFld1(2 * j + 1)]);
7974 m_solver->a_oldDistribution(m_bndCells[i].m_cellId, tmpDistId + j) =
7975 Ld::tp(2) * F1BCSsq * (rho * CSsq + tmp + tmp * tmp * F1BCSsq * F1B2 - tmp2 * F1B2)
7976 + ((1 - m_omega) * nePart[tmpDistId + j]);
7977 // m_solver->a_distribution(m_bndCells[i].m_cellId, tmpDistId + j) = Ld::tp(2) * F1BCSsq * ( rho * CSsq +
7978 // tmp + tmp * tmp * F1BCSsq * F1B2 - tmp2 * F1B2) + ((1-m_omega) * nePart[tmpDistId + j]);
7979 }
7980
7981 // Calculation of distributions for directions with three components
7982 tmpDistId = Ld::distFld(0) + Ld::distFld(1);
7983 for(MInt j = 0; j < Ld::distFld(2); j++) {
7984 tmp = (b[Ld::mFld2(3 * j)] + b[Ld::mFld2(3 * j + 1)] + b[Ld::mFld2(3 * j + 2)]);
7985 m_solver->a_oldDistribution(m_bndCells[i].m_cellId, tmpDistId + j) =
7986 Ld::tp(3) * F1BCSsq * (rho * CSsq + tmp + tmp * tmp * F1BCSsq * F1B2 - tmp2 * F1B2)
7987 + ((1 - m_omega) * nePart[tmpDistId + j]);
7988 // m_solver->a_distribution(m_bndCells[i].m_cellId, tmpDistId + j) = Ld::tp(3) * F1BCSsq * ( rho * CSsq +
7989 // tmp + tmp * tmp * F1BCSsq * F1B2 - tmp2 * F1B2) + ((1-m_omega) * nePart[tmpDistId + j]);
7990 }
7991 }
7992}
7993
8002template <MInt nDim, MInt nDist, class SysEqn>
8004 TRACE();
8005
8006 // For now testing only the D3Q19 algorithm
8007 MFloat rho, u[nDim], old_u[nDim];
8008 MFloat tmp = F0;
8009 MFloat tmp2 = F0;
8010 MFloat old_tmp2 = F0;
8011 MFloat b[2 * nDim];
8012 MInt tmpDistId, currentId;
8013 MFloatScratchSpace eDistributions(nDist, AT_, "eDistributions");
8014 MFloatScratchSpace nePart(nDist, AT_, "nePart");
8015
8016 MInt ind = m_mapSegIdsInOutCnd[index];
8017
8018 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
8019 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
8020
8021 currentId = m_bndCells[i].m_cellId;
8022
8023 // leave out halo cells
8024 // if (currentId >= m_solver->noInternalCells())
8025 // continue;
8026 if(m_solver->a_isHalo(currentId)) continue;
8027
8028 MFloat l_t = 1.0;
8029
8030 extrapolateVariable(ind, currentId, PV->T, &l_t);
8031
8032 MFloat nu =
8033 m_Ma * LBCS / m_solver->m_Re * m_referenceLength * FFPOW2(m_solver->maxLevel() - m_solver->a_level(currentId));
8034 m_omega = 2.0 / (1.0 + 6.0 * nu);
8035 // 1. Calculate eq-Distributions from actual macroscopic values
8036 //--------------------------------------------
8037
8038 // macroscopic values
8039 rho = m_solver->a_variable(currentId, PV->RHO);
8040 u[0] = m_solver->a_variable(currentId, PV->U);
8041 u[1] = m_solver->a_variable(currentId, PV->V);
8042 IF_CONSTEXPR(nDim == 3) u[2] = m_solver->a_variable(currentId, PV->W);
8043
8044 old_u[0] = m_solver->a_oldVariable(currentId, PV->U);
8045 old_u[1] = m_solver->a_oldVariable(currentId, PV->V);
8046 IF_CONSTEXPR(nDim == 3) old_u[2] = m_solver->a_oldVariable(currentId, PV->W);
8047
8048 tmp2 = F0;
8049 old_tmp2 = F0;
8050 for(MInt d = 0; d < nDim; d++) {
8051 tmp2 += (u[d] * u[d]);
8052 old_tmp2 += (old_u[d] * old_u[d]);
8053 b[2 * d] = -u[d];
8054 b[2 * d + 1] = u[d];
8055 }
8056
8057 // Calculation of eq-distributions for directions with only one component
8058 for(MInt j = 0; j < Ld::distFld(0); j++) {
8059 eDistributions[j] = Ld::tp(1) * F1BCSsq * (rho * CSsq + b[j] + b[j] * b[j] * F1BCSsq * F1B2 - tmp2 * F1B2);
8060 }
8061 // Calculation of eq-distributions for directions with two components
8062 tmpDistId = Ld::distFld(0);
8063 for(MInt j = 0; j < Ld::distFld(1); j++) {
8064 tmp = (b[Ld::mFld1(2 * j)] + b[Ld::mFld1(2 * j + 1)]);
8065 eDistributions[tmpDistId + j] =
8066 Ld::tp(2) * F1BCSsq * (rho * CSsq + tmp + tmp * tmp * F1BCSsq * F1B2 - tmp2 * F1B2);
8067 }
8068 // Calculation of eq-distributions for directions with three components
8069 tmpDistId = Ld::distFld(0) + Ld::distFld(1);
8070 for(MInt j = 0; j < Ld::distFld(2); j++) {
8071 tmp = (b[Ld::mFld2(3 * j)] + b[Ld::mFld2(3 * j + 1)] + b[Ld::mFld2(3 * j + 2)]);
8072 eDistributions[tmpDistId + j] =
8073 Ld::tp(3) * F1BCSsq * (rho * CSsq + tmp + tmp * tmp * F1BCSsq * F1B2 - tmp2 * F1B2);
8074 }
8075
8076 // 2. Calculation of non-eq-parts3
8077 //--------------------------------------------
8078 for(MInt j = 0; j < nDist - 1; j++) {
8079 nePart[j] = m_solver->a_distribution(currentId, j);
8080 }
8081 for(MInt j = 0; j < nDist - 1; j++) {
8082 nePart[j] -= eDistributions[j];
8083 }
8084 // had to be split leads to compiler error with optimization
8085
8086 // WARNING!!! The density should be calculated as in 3d
8087 // However, result is correct
8088 IF_CONSTEXPR(nDim == 3) {
8089 if(m_densityFluctuations) {
8090 rho = (rho + F1BCS * (sqrt(tmp2) - sqrt(old_tmp2))) / 2.0;
8091 } else {
8092 // rho = 1.0;
8093 // rho = ( m_solver->a_variable(m_bndCells[i].m_cellId, PV->RHO) + rho ) / 2.0 ;
8094 // WARNING!!! The density should be calculated as in 3d
8095 // However, result is correct
8096 rho = (rho + F1BCS * (sqrt(tmp2) - sqrt(old_tmp2)) + 1.0) / 2.0;
8097 }
8098 }
8099 else {
8100 rho = (rho + 1.0) / 2.0;
8101 }
8102
8103 // 3. Calculation of incoming distributions in bnd cell
8104 //--------------------------------------------
8105
8106 // Calculation of distributions for directions with only one component
8107 for(MInt j = 0; j < Ld::distFld(0); j++) {
8108 // if (!m_solver->a_hasNeighbor(currentId, Ld::oppositeDist(j))){
8109 m_solver->a_oldDistribution(m_bndCells[i].m_cellId, j) =
8110 Ld::tp(1) * F1BCSsq * (rho * CSsq + b[j] + b[j] * b[j] * F1BCSsq * F1B2 - tmp2 * F1B2)
8111 + ((1 - m_omega) * nePart[j]);
8112 //}
8113 }
8114
8115 // Calculation of distributions for directions with two components
8116 tmpDistId = Ld::distFld(0);
8117 for(MInt j = 0; j < Ld::distFld(1); j++) {
8118 // if (!m_solver->a_hasNeighbor(currentId, Ld::oppositeDist(tmpDistId + j))){
8119 tmp = (b[Ld::mFld1(2 * j)] + b[Ld::mFld1(2 * j + 1)]);
8120 m_solver->a_oldDistribution(m_bndCells[i].m_cellId, tmpDistId + j) =
8121 Ld::tp(2) * F1BCSsq * (rho * CSsq + tmp + tmp * tmp * F1BCSsq * F1B2 - tmp2 * F1B2)
8122 + ((1 - m_omega) * nePart[tmpDistId + j]);
8123 //}
8124 }
8125
8126 // Calculation of distributions for directions with three components
8127 tmpDistId = Ld::distFld(0) + Ld::distFld(1);
8128 for(MInt j = 0; j < Ld::distFld(2); j++) {
8129 // if (!m_solver->a_hasNeighbor(currentId, Ld::oppositeDist(tmpDistId + j))){
8130 tmp = (b[Ld::mFld2(3 * j)] + b[Ld::mFld2(3 * j + 1)] + b[Ld::mFld2(3 * j + 2)]);
8131 m_solver->a_oldDistribution(m_bndCells[i].m_cellId, tmpDistId + j) =
8132 Ld::tp(3) * F1BCSsq * (rho * CSsq + tmp + tmp * tmp * F1BCSsq * F1B2 - tmp2 * F1B2)
8133 + ((1 - m_omega) * nePart[tmpDistId + j]);
8134 // }
8135 }
8136
8137 m_solver->setEqDistsThermal(currentId, l_t, rho, u);
8138 }
8139 writeBCOutput(index);
8140}
8141
8142
8154template <MInt nDim, MInt nDist, class SysEqn>
8155template <MInt direction>
8157 TRACE();
8158
8159 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
8160 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
8161 for(MInt j = 0; j < nDist - 1; j++) {
8162 if(m_solver->a_hasNeighbor(m_bndCells[i].m_cellId, Ld::oppositeDist(j)) == 0) {
8163 const MInt neighbor1 = m_solver->c_neighborId(m_bndCells[i].m_cellId, Ld::oppositeDist(direction));
8164 m_solver->a_oldDistribution(m_bndCells[i].m_cellId, j) = m_solver->a_oldDistribution(neighbor1, j);
8165 }
8166 }
8167 }
8168}
8169
8170
8189template <MInt nDim, MInt nDist, class SysEqn>
8190template <MBool thermal>
8192 TRACE();
8193 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
8194 const MInt cellId = m_bndCells[i].m_cellId;
8195 const MInt maxLvlDiff = m_solver->maxLevel() - m_solver->a_level(cellId);
8196 //--check: is BC required for this cell now?--------------------------------
8197 if(globalTimeStep % IPOW2(maxLvlDiff) != 0 || m_solver->a_isHalo(cellId) || !m_solver->a_isActive(cellId)) {
8198 continue;
8199 }
8200 for(MInt trgDist = 0; trgDist < nDist - 1; trgDist++) {
8201 //--check: is BC required for this distribution?--------------------------
8202 const MInt srcDir1 = Ld::oppositeDist(trgDist); // init with original propagation src direction
8203 if(!(m_bndCells[i].m_distances[srcDir1] <= 0.5)) {
8204 continue; // No cut in opposite dir, hence continue with next dist.
8205 }
8206 //--get srcDir1 vector with values {0,1,2}---------------------------------
8207 std::array<MInt, nDim> srcDirVec, srcDistVec;
8208 for(MInt d = 0; d < nDim; d++) {
8209 srcDirVec[d] = Ld::idFld(srcDir1, d); // opposite trgDistVec
8210 srcDistVec[d] = Ld::idFld(trgDist, d); // init with value of trgDistVec
8211 }
8212 //--get srcDistVec--------------------------------------------------------
8213 // check for neighbors in those Cartesian directions the srcDirVec is composed of
8214 MBool normalPropagation = true;
8215 for(MInt d = 0; d < nDim; d++) {
8216 if(srcDirVec[d] == 1) continue;
8217 // else: mirror if no neighbor exist in this direction
8218 const MInt tmpDir = (srcDirVec[d] / 2) + (d * 2); // convert {0,2} into id of Cartesian dir. That is
8219 // {0,1}, {2,3}, or {4,5} for x,y, or z respectively
8220 if(m_bndCells[i].m_distances[tmpDir] <= 0.5) {
8221 srcDistVec[d] = 2 - srcDistVec[d]; // mirror
8222 normalPropagation = false;
8223 }
8224 }
8225 if(normalPropagation) continue;
8226 //--get srcDirVec---------------------------------------------------------
8227 // srcDirVector is in the opposite direction of the vector which is
8228 // located between srcDistVec and trgDistVec
8229 for(MInt d = 0; d < nDim; d++) {
8230 srcDirVec[d] = 2 - (Ld::idFld(trgDist, d) + srcDistVec[d]) / 2;
8231 }
8232 //--convert from vec to distribution id-----------------------------------
8233 const MInt srcDist = Ld::dirFld(srcDistVec[0], srcDistVec[1], srcDistVec[2]);
8234 const MInt srcDir2 = Ld::dirFld(srcDirVec[0], srcDirVec[1], srcDirVec[2]);
8235 //--set value-------------------------------------------------------------
8236 // I) determine cell state
8237 enum CellState { NORMAL, AT_INTERFACE, WALL, UNDEFINED };
8238 CellState state;
8239 MInt srcCell;
8240 if(srcDir2 == Ld::lastId()) {
8241 srcCell = cellId;
8242 state = NORMAL;
8243 } else if(m_solver->a_hasNeighbor(cellId, srcDir2)) {
8244 srcCell = m_solver->c_neighborId(cellId, srcDir2);
8245 if(m_solver->a_isActive(srcCell)) {
8246 state = NORMAL;
8247 } else {
8248 if(m_solver->m_isRefined && m_solver->a_isInterfaceParent(cellId)) {
8249 state = AT_INTERFACE;
8250 } else {
8251 state = WALL;
8252 }
8253 }
8254 } else if(m_solver->m_isRefined && m_solver->a_isInterfaceChild(cellId)) {
8255 const MInt parentId = m_solver->c_parentId(cellId);
8256 if(m_solver->a_hasNeighbor(parentId, srcDir2)) {
8257 state = AT_INTERFACE;
8258 } else {
8259 state = UNDEFINED;
8260 }
8261 } else {
8262 // In case of missing neighbor (even for interface parent cell) no
8263 // behavior can be defined. This must come from the BC defined from
8264 // the missing direction.
8265 state = UNDEFINED;
8266 }
8267 // TODO labels:LB miro: Until here, everthing should be precomputed and stored in
8268 // advance (state, srcDist, trgDist, srcCell).
8269
8270 // II) perfrom calculation depending on cell state
8271 switch(state) {
8272 default:
8273 case NORMAL: {
8274 m_solver->a_oldDistribution(cellId, trgDist) = m_solver->a_distribution(srcCell, srcDist);
8275 if constexpr(thermal) {
8276 m_solver->a_oldDistributionThermal(cellId, trgDist) = m_solver->a_distributionThermal(srcCell, srcDist);
8277 }
8278 break;
8279 }
8280 case AT_INTERFACE: {
8281 m_solver->a_oldDistribution(cellId, trgDist) = m_solver->a_oldDistribution(cellId, srcDist);
8282 if constexpr(thermal) {
8283 m_solver->a_oldDistributionThermal(cellId, trgDist) = m_solver->a_oldDistributionThermal(cellId, srcDist);
8284 }
8285 break;
8286 }
8287 case WALL: {
8288 // interpolated bounce back: here only for solid nghbr cell
8289 // TODO labels:LB problem if nghbr belongs to more than 1 boundary. Indeed,
8290 // this is the case here. So wall type has to be set as last BC in
8291 // input file -.-
8292 const MInt solidNghbrBndId = m_solver->a_bndId(srcCell);
8293 if(m_bndCells[solidNghbrBndId].m_isFluid == false && m_bndCells[solidNghbrBndId].m_distances[srcDist] < 0.5) {
8294 const MFloat F2q = F2 * (F1 - m_bndCells[solidNghbrBndId].m_distances[srcDist]);
8295 const MInt oppositeDir = Ld::oppositeDist(trgDist);
8296 m_solver->a_oldDistribution(cellId, trgDist) =
8297 (m_solver->a_distribution(cellId, oppositeDir) / F2q)
8298 + (m_solver->a_distribution(cellId, trgDist) * (F2q - F1) / F2q);
8299 if constexpr(thermal) {
8300 m_solver->a_oldDistributionThermal(cellId, trgDist) =
8301 (m_solver->a_distributionThermal(cellId, oppositeDir) / F2q)
8302 + (m_solver->a_distributionThermal(cellId, trgDist) * (F2q - F1) / F2q);
8303 }
8304 } else {
8305 // SOMETHING has to be went wrong
8306 }
8307 break;
8308 }
8309 case UNDEFINED: {
8310 break;
8311 }
8312 }
8313 }
8314 }
8315}
8316
8325template <MInt nDim, MInt nDist, class SysEqn>
8326template <MInt direction>
8328 TRACE();
8329
8330 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
8331 if((globalTimeStep) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
8332 for(MInt j = 0; j < nDist - 1; j++) {
8333 if(m_solver->a_hasNeighbor(m_bndCells[i].m_cellId, Ld::oppositeDist(j)) == 0) {
8334 const MInt neighbor1 = m_solver->c_neighborId(m_bndCells[i].m_cellId, Ld::oppositeDist(direction));
8335 const MInt neighbor2 = m_solver->c_neighborId(neighbor1, Ld::oppositeDist(direction));
8336 m_solver->a_oldDistribution(m_bndCells[i].m_cellId, j) =
8337 2.0 * m_solver->a_oldDistribution(neighbor1, j) - m_solver->a_oldDistribution(neighbor2, j);
8338 }
8339 }
8340 }
8341}
8342
8356template <MInt nDim, MInt nDist, class SysEqn>
8358 TRACE();
8359
8360 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
8361 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
8362
8363 const MInt currentId = m_bndCells[i].m_cellId;
8364
8365 const MFloat rho = m_solver->a_variable(currentId, PV->RHO);
8366#ifdef WAR_NVHPC_PSTL
8367 MFloat vel[nDim] = {F0};
8368 for(MInt d = 0; d < nDim; d++) {
8369 vel[d] = m_solver->a_variable(currentId, d);
8370 }
8371 const MFloat* const u = vel;
8372#else
8373 const MFloat* const u = &m_solver->a_variable(currentId, PV->U);
8374#endif
8375
8376 // Calculate symmetric part of eq-distributions at the border
8377 //---------------------------------------------------
8378 const MFloat tmp2 = std::inner_product(&u[0], &u[nDim], &u[0], 0.0);
8379 MFloat b[2 * nDim];
8380 for(MInt n = 0; n < nDim; n++) {
8381 b[2 * n] = -u[n];
8382 b[2 * n + 1] = u[n];
8383 }
8384
8385 // Calculation of eq. distributions for directions with only one component
8386 MFloat eqSym[nDist - 1];
8387 for(MInt j = 0; j < Ld::distFld(0); j++) {
8388 eqSym[j] = Ld::tp(1) * F1BCSsq * (rho * CSsq + b[j] * b[j] * F1BCSsq * F1B2 - tmp2 * F1B2);
8389 }
8390
8391 // Calculation of eq. distributions for directions with two components
8392 MInt tmpDistId = Ld::distFld(0);
8393 for(MInt j = 0; j < Ld::distFld(1); j++) {
8394 const MInt tmp = (b[Ld::mFld1(2 * j)] + b[Ld::mFld1(2 * j + 1)]);
8395 eqSym[tmpDistId + j] = Ld::tp(2) * F1BCSsq * (rho * CSsq + tmp * tmp * F1BCSsq * F1B2 - tmp2 * F1B2);
8396 }
8397
8398 // Calculation of eq. distributions for directions with three components
8399 tmpDistId = Ld::distFld(0) + Ld::distFld(1);
8400 for(MInt j = 0; j < Ld::distFld(2); j++) {
8401 const MInt tmp = (b[Ld::mFld2(3 * j)] + b[Ld::mFld2(3 * j + 1)] + b[Ld::mFld2(3 * j + 2)]);
8402 eqSym[tmpDistId + j] = Ld::tp(3) * F1BCSsq * (rho * CSsq + tmp * tmp * F1BCSsq * F1B2 - tmp2 * F1B2);
8403 }
8404
8405 for(MInt j = 0; j < nDist - 1; j++) {
8406 if(!m_solver->a_hasNeighbor(currentId, j))
8407 m_solver->a_oldDistribution(currentId, Ld::oppositeDist(j)) =
8408 -m_solver->a_distribution(currentId, j) + 2.0 * eqSym[j];
8409 }
8410 }
8411}
8412
8422template <MInt nDim, MInt nDist, class SysEqn>
8424 TRACE();
8425
8426 if(!this->m_calcBcResidual) return;
8427 MInt ind = m_mapSegIdsInOutCnd[index];
8428
8429 MFloat mean_velocity = 0.0;
8430 MFloat mean_rho = 0.0;
8431 MFloat mean_u[nDim] = {0.0};
8432 MInt numberOfCellsPerDomain = 0;
8433 MFloat mean_p_d = 0.0;
8434 MFloat mean_p_stat = 0.0;
8435 MFloat mean_t = 0.0;
8436
8437 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
8438 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(m_bndCells[i].m_cellId)) != 0) continue;
8439
8440 MInt pCellId = m_bndCells[i].m_cellId;
8441
8442 if(m_solver->c_noChildren(pCellId) == 0 && !m_solver->a_hasProperty(pCellId, Cell::IsHalo)) {
8443 MFloat l_rho = m_solver->a_variable(pCellId, PV->RHO);
8444 MFloat l_uu[nDim] = {0.0};
8445 for(MInt n = 0; n < nDim; n++) {
8446 l_uu[n] = m_solver->a_variable(pCellId, PV->VV[n]) / l_rho;
8447 }
8448
8449 MFloat l_t = 1.0;
8450 if(m_solver->m_isThermal) {
8451 l_t = m_solver->a_variable(pCellId, PV->T);
8452 }
8453
8454 for(MInt n = 0; n < nDim; n++) {
8455 mean_velocity += (m_bndNormals[ind][n] * l_uu[n]);
8456 }
8457 const MFloat squaredVelocity = std::inner_product(&l_uu[0], &l_uu[nDim], &l_uu[0], .0);
8458 mean_rho += l_rho;
8459 numberOfCellsPerDomain++;
8460
8461 for(MInt n = 0; n < nDim; n++) {
8462 mean_u[n] += l_uu[n];
8463 }
8464
8465 mean_p_d += F1B2 * l_rho * squaredVelocity;
8466 mean_p_stat += F1B3 * l_rho;
8467 mean_t += l_t;
8468 }
8469 }
8470
8471 MFloat mean_velocity_all = 0.0;
8472 MFloat mean_rho_all = 0.0;
8473 MFloat mean_u_all[nDim] = {0.0};
8474 MFloat mean_p_d_all = 0.0;
8475 MFloat mean_p_stat_all = 0.0;
8476 MFloat mean_t_all = 0.0;
8477 // MInt totalNumberOfCells = m_totalNoBcCells[m_mapBndCndIdSegId[index]]; Including Halo cells
8478 MInt totalNumberOfCells = 0;
8479 switch(m_noBCNeighbors[m_mapBndCndIdSegId[index]]) {
8480 case 0: {
8481 break;
8482 }
8483
8484 case 1: {
8485 totalNumberOfCells = numberOfCellsPerDomain;
8486 mean_velocity_all = mean_velocity / totalNumberOfCells;
8487 mean_rho_all = mean_rho / totalNumberOfCells;
8488
8489 for(MInt n = 0; n < nDim; n++) {
8490 mean_u_all[n] = mean_u[n] / totalNumberOfCells;
8491 }
8492
8493 mean_p_d_all = mean_p_d / totalNumberOfCells;
8494 mean_p_stat_all = mean_p_stat / totalNumberOfCells;
8495 if(m_solver->m_isThermal) {
8496 mean_t_all = mean_t / totalNumberOfCells;
8497 } else {
8498 mean_t_all = 1.0;
8499 }
8500 MFloat ReynoldsNr = m_referenceLength * mean_velocity_all / m_solver->m_nu;
8501
8502 if(m_solver->domainId() == m_BCneighbors[m_mapBndCndIdSegId[index]][0]) {
8503 m_BCResidualStream[m_mapBndCndIdSegId[index]].open(m_BCOutputFileName[m_mapBndCndIdSegId[index]],
8504 std::ios_base::app);
8505 m_BCResidualStream[m_mapBndCndIdSegId[index]] << globalTimeStep << " p_dyn " << mean_p_d_all << " p_stat "
8506 << mean_p_stat_all << " Mag " << mean_velocity_all << " v_x "
8507 << mean_u_all[0] << " v_y " << mean_u_all[1];
8508 IF_CONSTEXPR(nDim == 3) m_BCResidualStream[m_mapBndCndIdSegId[index]] << " v_z " << mean_u_all[2];
8509
8510 m_BCResidualStream[m_mapBndCndIdSegId[index]] << " rho " << mean_rho_all << " nu " << m_solver->m_nu << " Re "
8511 << ReynoldsNr;
8512
8513 if(m_solver->m_isThermal) {
8514 m_BCResidualStream[m_mapBndCndIdSegId[index]] << " t " << mean_t_all << " kappa " << m_solver->m_kappa;
8515 }
8516 m_BCResidualStream[m_mapBndCndIdSegId[index]] << " #Cells " << totalNumberOfCells << std::endl;
8517 m_BCResidualStream[m_mapBndCndIdSegId[index]].close();
8518 }
8519 break;
8520 }
8521
8522 default: {
8523 MPI_Allreduce(&numberOfCellsPerDomain, &totalNumberOfCells, 1, MPI_INT, MPI_SUM,
8524 m_BCComm[m_mapBndCndIdSegId[index]], AT_, "numberOfCellsPerDomain",
8525 "totalNumberOfCells"); // without Halo cells
8526 // Get the sum by communication in my group
8527 MPI_Allreduce(&mean_velocity, &mean_velocity_all, 1, MPI_DOUBLE, MPI_SUM, m_BCComm[m_mapBndCndIdSegId[index]],
8528 AT_, "mean_velocity", "mean_velocity_all");
8529 MPI_Allreduce(&mean_rho, &mean_rho_all, 1, MPI_DOUBLE, MPI_SUM, m_BCComm[m_mapBndCndIdSegId[index]], AT_,
8530 "mean_rho", "mean_rho_all");
8531 MPI_Allreduce(mean_u, mean_u_all, nDim, MPI_DOUBLE, MPI_SUM, m_BCComm[m_mapBndCndIdSegId[index]], AT_, "mean_u",
8532 "mean_u_all");
8533 MPI_Allreduce(&mean_p_d, &mean_p_d_all, 1, MPI_DOUBLE, MPI_SUM, m_BCComm[m_mapBndCndIdSegId[index]], AT_,
8534 "mean_p_d", "mean_p_d_all");
8535 MPI_Allreduce(&mean_p_stat, &mean_p_stat_all, 1, MPI_DOUBLE, MPI_SUM, m_BCComm[m_mapBndCndIdSegId[index]], AT_,
8536 "mean_p_stat", "mean_p_stat_all");
8537
8538 if(m_solver->m_isThermal) {
8539 MPI_Allreduce(&mean_t, &mean_t_all, 1, MPI_DOUBLE, MPI_SUM, m_BCComm[m_mapBndCndIdSegId[index]], AT_, "mean_t",
8540 "mean_t_all");
8541 mean_t_all /= totalNumberOfCells;
8542 } else {
8543 mean_t_all = 1.0;
8544 }
8545
8546 mean_velocity_all /= totalNumberOfCells;
8547 mean_rho_all /= totalNumberOfCells;
8548
8549 for(MInt n = 0; n < nDim; n++) {
8550 mean_u_all[n] /= totalNumberOfCells;
8551 }
8552
8553 mean_p_d_all /= totalNumberOfCells;
8554 mean_p_stat_all /= totalNumberOfCells;
8555
8556 MFloat ReynoldsNr = m_referenceLength * mean_velocity_all / m_solver->m_nu;
8557
8558 if(m_solver->domainId() == m_BCneighbors[m_mapBndCndIdSegId[index]][0]) {
8559 m_BCResidualStream[m_mapBndCndIdSegId[index]].open(m_BCOutputFileName[m_mapBndCndIdSegId[index]],
8560 std::ios_base::app);
8561 m_BCResidualStream[m_mapBndCndIdSegId[index]]
8562 << globalTimeStep << " p_dyn " << mean_p_d_all
8563 << " p_stat "
8564 // << mean_p_stat_all << " v_n,BC " <<
8565 // mean_velocity_all << " v_x "
8566 << mean_p_stat_all << " Mag " << mean_velocity_all << " v_x " << mean_u_all[0] << " v_y " << mean_u_all[1];
8567 IF_CONSTEXPR(nDim == 3) m_BCResidualStream[m_mapBndCndIdSegId[index]] << " v_z " << mean_u_all[2];
8568
8569 m_BCResidualStream[m_mapBndCndIdSegId[index]] << " rho " << mean_rho_all << " nu " << m_solver->m_nu << " Re "
8570 << ReynoldsNr;
8571 if(m_solver->m_isThermal) {
8572 m_BCResidualStream[m_mapBndCndIdSegId[index]] << " t " << mean_t_all << " kappa " << m_solver->m_kappa;
8573 }
8574 m_BCResidualStream[m_mapBndCndIdSegId[index]] << " #Cells " << totalNumberOfCells << std::endl;
8575 m_BCResidualStream[m_mapBndCndIdSegId[index]].close();
8576 }
8577 }
8578 }
8579}
8580
8588template <MInt nDim, MInt nDist, class SysEqn>
8590 TRACE();
8591 if(m_refillMethodOrder == 1) {
8593 } else {
8595 }
8596}
8597
8605template <MInt nDim, MInt nDist, class SysEqn>
8607 TRACE();
8608
8609 ASSERT(m_solver->noHaloLayers() > 1,
8610 "linear extrapolation for refillEmergedCell, at least 2 halo layers are required!");
8611
8612 const MInt mbCell = m_boundaryCellMappingMb[pCellId];
8613 ASSERT(mbCell > -1, "Cell " << pCellId << " is no G0 Cell, thus cant be refilled!");
8614
8615 // Find direction with shortest path to boundary
8616 MInt minDistDir = -1;
8617 MFloat minWallDistance = F2;
8618 for(MInt dist = 0; dist < nDist - 1; dist++) {
8619 if(minWallDistance > m_boundaryCellsMb.distance(mbCell, dist)) {
8620 minWallDistance = m_boundaryCellsMb.distance(mbCell, dist);
8621 minDistDir = Ld::oppositeDist(dist);
8622 }
8623 }
8624
8625 MFloat l_rho = F0;
8626 std::array<MFloat, nDim> l_vv{};
8627
8628 if(m_solver->a_hasNeighbor(pCellId, minDistDir)
8629 && m_solver->a_isActive(m_solver->c_neighborId(pCellId, minDistDir))) {
8630 const MInt nghbor = m_solver->c_neighborId(pCellId, minDistDir);
8631 if(m_solver->a_hasNeighbor(nghbor, minDistDir)
8632 && m_solver->a_isActive(m_solver->c_neighborId(nghbor, minDistDir))) {
8633 const MInt nnghbor = m_solver->c_neighborId(nghbor, minDistDir);
8634
8635 // Two neighbors found
8636 for(MInt j = 0; j < nDist; j++) {
8637 m_solver->a_oldDistribution(pCellId, j) =
8638 F2 * m_solver->a_oldDistribution(nghbor, j) - F1 * m_solver->a_oldDistribution(nnghbor, j);
8639
8640 m_solver->a_distribution(pCellId, j) =
8641 F2 * m_solver->a_distribution(nghbor, j) - F1 * m_solver->a_distribution(nnghbor, j);
8642 }
8643 } else {
8644 // One neighbor found
8645 for(MInt j = 0; j < nDist; j++) {
8646 m_solver->a_oldDistribution(pCellId, j) = m_solver->a_oldDistribution(nghbor, j);
8647 m_solver->a_distribution(pCellId, j) = m_solver->a_distribution(nghbor, j);
8648 }
8649 }
8650 }
8651
8652 m_solver->calculateMacroscopicVariables(pCellId, l_rho, l_vv.data());
8653
8654 m_solver->a_variable(pCellId, PV->RHO) = l_rho;
8655 for(MInt n = 0; n < nDim; n++) {
8656 m_solver->a_variable(pCellId, PV->VV[n]) = l_vv[n];
8657 }
8658}
8659
8667template <MInt nDim, MInt nDist, class SysEqn>
8669 TRACE();
8670
8671 ASSERT(m_solver->noHaloLayers() > 2,
8672 "quadratic extrapolation for refillEmergedCell, at least 3 halo layers are required!");
8673
8674 const MInt mbCell = m_boundaryCellMappingMb[pCellId];
8675 ASSERT(mbCell > -1, "Cell " << pCellId << " is no G0 Cell, thus cant be refilled!");
8676
8677 // Find direction with shortest path to boundary
8678 MInt minDistDir = -1;
8679 MFloat minWallDistance = F2;
8680 for(MInt dist = 0; dist < nDist - 1; dist++) {
8681 if(minWallDistance > m_boundaryCellsMb.distance(mbCell, dist)) {
8682 minWallDistance = m_boundaryCellsMb.distance(mbCell, dist);
8683 minDistDir = Ld::oppositeDist(dist);
8684 }
8685 }
8686
8687 MFloat l_rho = F0;
8688 std::array<MFloat, nDim> l_vv{};
8689
8690 if(m_solver->a_hasNeighbor(pCellId, minDistDir)
8691 && m_solver->a_isActive(m_solver->c_neighborId(pCellId, minDistDir))) {
8692 const MInt nghbor = m_solver->c_neighborId(pCellId, minDistDir);
8693 if(m_solver->a_hasNeighbor(nghbor, minDistDir)
8694 && m_solver->a_isActive(m_solver->c_neighborId(nghbor, minDistDir))) {
8695 const MInt nnghbor = m_solver->c_neighborId(nghbor, minDistDir);
8696 if(m_solver->a_hasNeighbor(nnghbor, minDistDir)
8697 && m_solver->a_isActive(m_solver->c_neighborId(nnghbor, minDistDir))) {
8698 const MInt nnnghbor = m_solver->c_neighborId(nnghbor, minDistDir);
8699
8700 // Three neighbors found
8701 for(MInt j = 0; j < nDist; j++) {
8702 m_solver->a_oldDistribution(pCellId, j) = F3 * m_solver->a_oldDistribution(nghbor, j)
8703 - F3 * m_solver->a_oldDistribution(nnghbor, j)
8704 + F1 * m_solver->a_oldDistribution(nnnghbor, j);
8705
8706 m_solver->a_distribution(pCellId, j) = F3 * m_solver->a_distribution(nghbor, j)
8707 - F3 * m_solver->a_distribution(nnghbor, j)
8708 + F1 * m_solver->a_distribution(nnnghbor, j);
8709 }
8710
8711 } else {
8712 // Two neighbors found
8713 for(MInt j = 0; j < nDist; j++) {
8714 m_solver->a_oldDistribution(pCellId, j) =
8715 F2 * m_solver->a_oldDistribution(nghbor, j) - F1 * m_solver->a_oldDistribution(nnghbor, j);
8716
8717 m_solver->a_distribution(pCellId, j) =
8718 F2 * m_solver->a_distribution(nghbor, j) - F1 * m_solver->a_distribution(nnghbor, j);
8719 }
8720 }
8721
8722 } else {
8723 // One neighbor found
8724 for(MInt j = 0; j < nDist; j++) {
8725 m_solver->a_oldDistribution(pCellId, j) = m_solver->a_oldDistribution(nghbor, j);
8726 m_solver->a_distribution(pCellId, j) = m_solver->a_distribution(nghbor, j);
8727 }
8728 }
8729 }
8730
8731 m_solver->calculateMacroscopicVariables(pCellId, l_rho, l_vv.data());
8732
8733 m_solver->a_variable(pCellId, PV->RHO) = l_rho;
8734 for(MInt n = 0; n < nDim; n++) {
8735 m_solver->a_variable(pCellId, PV->VV[n]) = l_vv[n];
8736 }
8737}
8738
8739template <MInt nDim, MInt nDist, class SysEqn>
8741 const MInt set) {
8742 TRACE();
8743
8744 const MInt pCellId = m_solver->m_G0CellList[cellIndex];
8745 MFloat l_t = m_solver->m_initTemperatureKelvin;
8746 std::array<MFloat, nDim> uW{};
8747 getBoundaryVelocityMb(cellIndex, uW.data());
8748
8749 // case 1: boundary cell is inside fluid
8750 if(m_solver->a_levelSetFunctionMB(pCellId, set) > 0) {
8751 for(MInt j = 0; j < nDist - 1; j++) {
8752#ifdef WAR_NVHPC_PSTL
8753 const MFloat q = m_distances[cellIndex][j];
8754 const MInt opposite = m_solver->m_oppositeDist[j];
8755#else
8756 const MFloat q = getDistanceMb(pCellId, cellIndex, j);
8757 const MInt opposite = Ld::oppositeDist(j);
8758#endif
8759 MFloat s = F0;
8760 if(m_solver->m_innerEnergy) {
8761 s = zerothMomentSourceTerm<1>(pCellId, opposite, l_t, uW.data());
8762 } else if(m_solver->m_totalEnergy) {
8763 s = zerothMomentSourceTerm<2>(pCellId, opposite, l_t, uW.data());
8764 } else {
8765 s = zerothMomentSourceTerm<0>(pCellId, opposite, l_t, uW.data());
8766 }
8767 const MFloat sourceTerm = s;
8768
8769 if(m_solver->a_hasProperty(pCellId, Cell::IsHalo)) continue;
8770
8771 // if q <= 0.5, perform bounce back
8772 if(q <= 0.5) {
8773 if(m_solver->a_hasNeighbor(pCellId, opposite) != 0) {
8774 const MFloat F2q = F2 * q;
8775 m_solver->a_oldDistributionThermal(pCellId, opposite) =
8776 (-F2q * m_solver->a_distributionThermal(pCellId, j))
8777 + ((F2q - F1) * m_solver->a_oldDistributionThermal(pCellId, j)) + sourceTerm;
8778
8779 } else {
8780 // this is the case in which we have no neighbor in the direction we want to set (strange case)
8781 // can in my opinion only appear if neighbors were set wrong or at an interface
8782 m_solver->a_oldDistributionThermal(pCellId, opposite) =
8783 -m_solver->a_distributionThermal(pCellId, j) + sourceTerm;
8784 }
8785 }
8786 // this is the case in which we do not have a neighbor and no cut, do simple bounce back (strange case)
8787 // can in my opinoin only appear if something has gone wrong either with the grid or with the distance calculation
8788 // else if(bndCells[cellId].m_distances[j] > 0.5 && m_solver->a_hasNeighbor(pCellId, j) == 0)
8789 // else if( m_wallBoundaryCellList[cellId].m_distances[j] > 0.5 && m_solver->a_hasNeighbor(pCellId, j) == 0)
8790 else if(q > 0.5 && m_solver->a_hasNeighbor(pCellId, j) == 0) {
8791 m_solver->a_oldDistributionThermal(pCellId, opposite) =
8792 m_solver->a_distributionThermal(pCellId, j) + sourceTerm;
8793 }
8794 }
8795 }
8796
8797 // case 2: boundary cell is outside fluid
8798 else {
8799 for(MInt j = 0; j < nDist - 1; j++) {
8800#ifdef WAR_NVHPC_PSTL
8801 const MFloat q = m_distances[cellIndex][j];
8802 const MInt opposite = m_solver->m_oppositeDist[j];
8803#else
8804 const MFloat q = getDistanceMb(pCellId, cellIndex, j);
8805 const MInt opposite = Ld::oppositeDist(j);
8806#endif
8807
8808 // do we have a neighbor at all?
8809 if(m_solver->a_hasNeighbor(pCellId, j)) {
8810 // make sure that the neighbor is not a halo cell
8811 const MInt neighborId = m_solver->c_neighborId(pCellId, j);
8812 if(!m_solver->a_hasProperty(neighborId, Cell::IsHalo)) {
8813 const MBool nbndid = m_solver->a_isG0CandidateOfSet(neighborId, (set - m_solver->m_levelSetId));
8814
8815 MFloat s = F0;
8816 if(m_solver->m_innerEnergy) {
8817 s = zerothMomentSourceTerm<1>(neighborId, j, l_t, uW.data());
8818 } else if(m_solver->m_totalEnergy) {
8819 s = zerothMomentSourceTerm<2>(neighborId, j, l_t, uW.data());
8820 } else {
8821 s = zerothMomentSourceTerm<0>(neighborId, j, l_t, uW.data());
8822 }
8823 const MFloat sourceTerm = s;
8824 // if q < 0.5, perform bounce back (this is meant from the outer cell, the inner cell then has q >= 0.5)
8825 if(q < 0.5) {
8826 const MFloat F2q = F2 * (F1 - q);
8827
8828 // check if my neighbor is an inside cell or a boundary cell with the cell center inside
8829 if(!nbndid || m_solver->a_levelSetFunctionMB(neighborId, set) > 0) {
8830 m_solver->a_oldDistributionThermal(neighborId, j) =
8831 -(m_solver->a_distributionThermal(neighborId, opposite) / F2q)
8832 + (m_solver->a_distributionThermal(neighborId, j) * (F2q - F1) / F2q) + (F1 / F2q) * sourceTerm;
8833 }
8834 }
8835 }
8836 }
8837 }
8838
8839 // The outer cell does not belong to the flow field, thus its incoming distributions are overwritten.
8840 // It is possible that there are cells without any cutting velocity! These are considered too.
8841 m_solver->setEqDistsThermal(pCellId, l_t, F1, uW.data());
8842
8843 m_solver->a_variable(pCellId, PV->T) = l_t;
8844 m_solver->a_oldVariable(pCellId, PV->T) = l_t;
8845 }
8846}
8847
8848template <MInt nDim, MInt nDist, class SysEqn>
8850 const MInt set) {
8851 TRACE();
8852
8853 const MInt pCellId = m_solver->m_G0CellList[cellIndex];
8854 MFloat l_c = m_solver->m_initCon;
8855 std::array<MFloat, nDim> uW{};
8856 getBoundaryVelocityMb(cellIndex, uW.data());
8857
8858 // case 1: boundary cell is inside fluid
8859 if(m_solver->a_levelSetFunctionMB(pCellId, set) > 0) {
8860 for(MInt j = 0; j < nDist - 1; j++) {
8861#ifdef WAR_NVHPC_PSTL
8862 const MFloat q = m_distances[cellIndex][j];
8863 const MInt opposite = m_solver->m_oppositeDist[j];
8864#else
8865 const MFloat q = getDistanceMb(pCellId, cellIndex, j);
8866 const MInt opposite = Ld::oppositeDist(j);
8867#endif
8868 const MFloat sourceTerm = zerothMomentSourceTerm<0>(pCellId, opposite, l_c, uW.data());
8869
8870 if(m_solver->a_hasProperty(pCellId, Cell::IsHalo)) continue;
8871
8872 // if q <= 0.5, perform bounce back
8873 if(q <= 0.5) {
8874 if(m_solver->a_hasNeighbor(pCellId, opposite) != 0) {
8875 const MFloat F2q = F2 * q;
8876 m_solver->a_oldDistributionTransport(pCellId, opposite) =
8877 (-F2q * m_solver->a_distributionTransport(pCellId, j))
8878 + ((F2q - F1) * m_solver->a_oldDistributionTransport(pCellId, j)) + sourceTerm;
8879
8880 } else {
8881 // this is the case in which we have no neighbor in the direction we want to set (strange case)
8882 // can in my opinion only appear if neighbors were set wrong or at an interface
8883 m_solver->a_oldDistributionTransport(pCellId, opposite) =
8884 -m_solver->a_distributionTransport(pCellId, j) + sourceTerm;
8885 }
8886 }
8887 // this is the case in which we do not have a neighbor and no cut, do simple bounce back (strange case)
8888 // can in my opinoin only appear if something has gone wrong either with the grid or with the distance calculation
8889 // else if(bndCells[cellId].m_distances[j] > 0.5 && m_solver->a_hasNeighbor(pCellId, j) == 0)
8890 // else if( m_wallBoundaryCellList[cellId].m_distances[j] > 0.5 && m_solver->a_hasNeighbor(pCellId, j) == 0)
8891 else if(q > 0.5 && m_solver->a_hasNeighbor(pCellId, j) == 0) {
8892 m_solver->a_oldDistributionTransport(pCellId, opposite) =
8893 m_solver->a_distributionTransport(pCellId, j) + sourceTerm;
8894 }
8895 }
8896 }
8897
8898 // case 2: boundary cell is outside fluid
8899 else {
8900 for(MInt j = 0; j < nDist - 1; j++) {
8901#ifdef WAR_NVHPC_PSTL
8902 const MFloat q = m_distances[cellIndex][j];
8903 const MInt opposite = m_solver->m_oppositeDist[j];
8904#else
8905 const MFloat q = getDistanceMb(pCellId, cellIndex, j);
8906 const MInt opposite = Ld::oppositeDist(j);
8907#endif
8908
8909 // do we have a neighbor at all?
8910 if(m_solver->a_hasNeighbor(pCellId, j)) {
8911 // make sure that the neighbor is not a halo cell
8912 const MInt neighborId = m_solver->c_neighborId(pCellId, j);
8913 if(!m_solver->a_hasProperty(neighborId, Cell::IsHalo)) {
8914 const MBool nbndid = m_solver->a_isG0CandidateOfSet(neighborId, (set - m_solver->m_levelSetId));
8915
8916 const MFloat sourceTerm = zerothMomentSourceTerm<0>(neighborId, j, l_c, uW.data());
8917 // if q < 0.5, perform bounce back (this is meant from the outer cell, the inner cell then has q >= 0.5)
8918 if(q < 0.5) {
8919 const MFloat F2q = F2 * (F1 - q);
8920
8921 // check if my neighbor is an inside cell or a boundary cell with the cell center inside
8922 if(!nbndid || m_solver->a_levelSetFunctionMB(neighborId, set) > 0) {
8923 m_solver->a_oldDistributionTransport(neighborId, j) =
8924 -(m_solver->a_distributionTransport(neighborId, opposite) / F2q)
8925 + (m_solver->a_distributionTransport(neighborId, j) * (F2q - F1) / F2q) + (F1 / F2q) * sourceTerm;
8926 }
8927 }
8928 }
8929 }
8930 }
8931
8932 // The outer cell does not belong to the flow field, thus its incoming distributions are overwritten.
8933 // It is possible that there are cells without any cutting velocity! These are considered too.
8934 m_solver->setEqDistsTransport(pCellId, l_c, uW.data());
8935 m_solver->a_variable(pCellId, PV->C) = l_c;
8936 m_solver->a_oldVariable(pCellId, PV->C) = l_c;
8937 }
8938}
8939
8945template <MInt nDim, MInt nDist, class SysEqn>
8947 // TODO labels:LB Calculate velocities from oldDistribution, BUT BE CAREFUL, LAST HALO LAYER NOT SYNCHED!
8948 // since a_variable is from the beginning of the timestep (pre coll, pre prop)
8949 const MInt setId = 0;
8950
8951 for(MInt cellIndex = 0; cellIndex < m_boundaryCellsMb.size(); cellIndex++) {
8952 for(MInt dist = 0; dist < nDist - 1; dist++) {
8953 ASSERT(m_boundaryCellsMb.distance(cellIndex, dist) >= 0.0, "boundary cell collector: no valid dist");
8954 }
8955 }
8956
8957 for(MInt cellIndex = 0; cellIndex < m_boundaryCellsMb.size(); cellIndex++) {
8958 const MInt pCellId = m_boundaryCellsMb.cellId(cellIndex);
8959
8960 // Find minWallDistDir
8961 MInt minDistDir = -1;
8962 MFloat minWallDistance = F2;
8963 for(MInt dist = 0; dist < nDist - 1; dist++) {
8964 if(minWallDistance > m_boundaryCellsMb.distance(cellIndex, dist)) {
8965 minWallDistance = m_boundaryCellsMb.distance(cellIndex, dist);
8966 minDistDir = dist;
8967 }
8968 }
8969
8970 ASSERT(minDistDir > -1, "minDistDir not found!");
8971
8972 minWallDistance = getDistanceMb(pCellId, cellIndex, minDistDir);
8973
8974 ASSERT(minDistDir > -1, "No min dist found.");
8975
8976 if(m_solver->a_levelSetFunctionMB(pCellId, setId) > 0) {
8977 // Interface cell is inside the fluid
8978
8979 if(m_solver->a_hasProperty(pCellId, Cell::IsHalo)) {
8980 continue;
8981 }
8982
8983 const MInt opposite = Ld::oppositeDist(minDistDir);
8984 const MFloat q = minWallDistance;
8985 const MInt neighbor = m_solver->c_neighborId(pCellId, opposite);
8986
8987 if(neighbor > -1) {
8988 // Neighbor exists: linear extrapolation to surface
8989 for(MInt n = 0; n < nDim; n++) {
8990 m_boundaryCellsMb.velocity(cellIndex, n) =
8991 m_solver->a_variable(pCellId, n) * (1 + q) - m_solver->a_variable(neighbor, n) * q;
8992 }
8993
8994 } else {
8995 // Neighbor doesnt exists: nearest neighbor value set to surface
8996 std::cerr << "Velocity extrapolation to surface: zero order fallback. (inside)"
8997 << "(gid " << m_solver->c_globalId(pCellId) << " )" << std::endl;
8998 for(MInt n = 0; n < nDim; n++) {
8999 m_boundaryCellsMb.velocity(cellIndex, n) = m_solver->a_variable(pCellId, n);
9000 }
9001 }
9002 } else {
9003 // Interface cell is outside the fluid
9004
9005 const MFloat q = 1 - minWallDistance;
9006 const MInt neighbor = m_solver->c_neighborId(pCellId, minDistDir);
9007
9008 const MInt nneighbor = m_solver->c_neighborId(neighbor, minDistDir);
9009
9010 if(nneighbor > -1) {
9011 // Next neighbor exists: linear extrapolation to surface
9012 for(MInt n = 0; n < nDim; n++) {
9013 m_boundaryCellsMb.velocity(cellIndex, n) =
9014 m_solver->a_variable(neighbor, n) * (2 - q) - m_solver->a_variable(nneighbor, n) * (1 - q);
9015 }
9016
9017 } else {
9018 for(MInt n = 0; n < nDim; n++) {
9019 m_boundaryCellsMb.velocity(cellIndex, n) = m_solver->a_variable(neighbor, n);
9020 }
9021 }
9022 }
9023 }
9024
9025 // SANITY CHECKS
9026 for(MInt mbCell = 0; mbCell < m_boundaryCellsMb.size(); mbCell++) {
9027 const MInt pCellId = m_boundaryCellsMb.cellId(mbCell);
9028 if(m_solver->a_hasProperty(pCellId, Cell::IsHalo)) {
9029 continue;
9030 }
9031
9032 if(std::isnan(m_boundaryCellsMb.velocity(mbCell, 0)) || std::isnan(m_boundaryCellsMb.velocity(mbCell, 1))) {
9033 std::cout << "vel nan for "
9034 << "(gid " << m_solver->c_globalId(pCellId) << " )" << std::endl;
9035 TERMM(1, "Velocity extrapolation nan");
9036 }
9037 }
9038}
9039
9052template <MInt nDim, MInt nDist, class SysEqn>
9053template <MUint type>
9054inline void LbBndCndDxQy<nDim, nDist, SysEqn>::calcCharValues(const MInt index, const MInt cellId, MFloat& rho_b,
9055 MFloat* u_b) {
9056 TRACE();
9057#ifdef WAR_NVHPC_PSTL
9058 MFloat var[nDim + 1] = {F0};
9059 for(MInt d = 0; d < nDim + 1; d++) {
9060 var[d] = m_solver->a_variable(cellId, d);
9061 }
9062 MFloat* const ptr0 = var;
9063#else
9064 MFloat* const ptr0 = &m_solver->a_variable(cellId, 0);
9065#endif
9066 const MFloat F1Brho0 = 1.0 / ptr0[PV->RHO];
9067 MFloat rho_t{};
9068 MFloat u_t[nDim]{};
9069 //--calculate time derivative-----------------------------------------------
9070 // Heubes et al. 2012: http://dx.doi.org/10.1016/j.cam.2013.09.019
9071 // Defined switching factor gamma (here factorThomson) which yield pure
9072 // Thomson for gamma=1.0 or pure LODI for gamma=0.0 (as proposed by Izquierdo
9073 // et al.). By Heubes et al. it was found out that the convex combination of
9074 // both approaches using gamma=0.75 is superior.
9075 constexpr MFloat factorThomson = 1.0;
9076
9077 constexpr MFloat sigma = 0.59;
9078 constexpr MFloat rho_trg = 1.0;
9079 const MFloat k_relax =
9080 sigma * (1 - m_Ma * m_Ma) * LBCS / (m_domainLength * FFPOW2(m_solver->maxLevel() - m_solver->a_level(cellId)));
9081 MFloat L_approx[nDim]{};
9082 switch(type) {
9083 case 1: { // relax against velocity
9084 const MInt ind = m_mapSegIdsInOutCnd[index];
9085 for(MInt i = 0; i < nDim; i++) {
9086 const MFloat u_trg = m_initialVelocityVecs[ind][i] * m_Ma * LBCS; //* m_bndCells[].m_multiplier;
9087 L_approx[i] = k_relax * (ptr0[PV->VV[i]] - u_trg);
9088 }
9089 break;
9090 }
9091 case 2: { // relax against pressure
9092 const MFloat L_approx_ = k_relax * CSsq * (ptr0[PV->RHO] - rho_trg);
9093 for(MInt i = 0; i < nDim; i++) {
9094 L_approx[i] = L_approx_;
9095 }
9096 break;
9097 }
9098 default: { // otherwise zero incoming wave
9099 break;
9100 }
9101 }
9102 const MInt pvVv[3] = {PV->VV[0], PV->VV[1], PV->VV[2]};
9103 for(MInt c = 0; c < nDim; c++) {
9104 const MInt dir1 = 2 * c; // in negative axis dir
9105 const MInt dir2 = 2 * c + 1; // in positive axis dir
9106 const MBool isTangentialDir = (m_solver->a_hasNeighbor(cellId, dir1) && m_solver->a_hasNeighbor(cellId, dir2));
9107 if(isTangentialDir) {
9108 continue;
9109 const MInt nghbrId1 = m_solver->c_neighborId(cellId, dir1);
9110 const MInt nghbrId2 = m_solver->c_neighborId(cellId, dir2);
9111#ifdef WAR_NVHPC_PSTL
9112 MFloat var_n1[nDim + 1] = {F0};
9113 for(MInt d = 0; d < nDim + 1; d++) {
9114 var_n1[d] = m_solver->a_variable(nghbrId1, d);
9115 }
9116 MFloat* const ptr1 = var_n1;
9117 MFloat var_n2[nDim + 1] = {F0};
9118 for(MInt d = 0; d < nDim + 1; d++) {
9119 var_n2[d] = m_solver->a_variable(nghbrId2, d);
9120 }
9121 MFloat* const ptr2 = var_n2;
9122#else
9123 MFloat* const ptr1 = &m_solver->a_variable(nghbrId1, 0);
9124 MFloat* const ptr2 = &m_solver->a_variable(nghbrId2, 0);
9125#endif
9126 const MFloat F1Brho1 = 1.0 / ptr1[PV->RHO];
9127 const MFloat F1Brho2 = 1.0 / ptr2[PV->RHO];
9128 // calculate derivatives based on central differences
9129 // ...(3)--1--0--2--(4)...
9130 const MFloat rho_x = 0.5 * (ptr2[PV->RHO] - ptr1[PV->RHO]);
9131 MFloat u_x[nDim];
9132 for(MInt j = 0; j < nDim; j++) {
9133 u_x[j] = 0.5 * (ptr2[PV->VV[j]] * F1Brho2 - ptr1[PV->VV[j]] * F1Brho1);
9134 }
9135 // time derivative part for this direction
9136 rho_t -= factorThomson * (ptr0[pvVv[c]] * F1Brho0 * rho_x + u_x[c] * ptr0[PV->RHO]);
9137 u_t[c] -= factorThomson * (F1Brho0 * CSsq * rho_x);
9138 for(MInt j = 0; j < nDim; j++) {
9139 u_t[j] -= factorThomson * (F1Brho0 * ptr0[pvVv[c]] * u_x[j]);
9140 }
9141 // Add artificial damping to otherwise undamped central difference
9142 /*
9143 constexpr MFloat eps = 1.0e-2;
9144 if(m_solver->a_hasNeighbor(nghbrId1, dir1) && m_solver->a_hasNeighbor(nghbrId2, dir2)) {
9145 const MInt nghbrId3 = m_solver->c_neighborId(nghbrId1, dir1);
9146 const MInt nghbrId4 = m_solver->c_neighborId(nghbrId2, dir2);
9147 MFloat* const ptr3 = &m_solver->a_variable(nghbrId3, 0);
9148 MFloat* const ptr4 = &m_solver->a_variable(nghbrId4, 0);
9149 const MFloat F1Brho3 = 1.0 / ptr3[PV->RHO];
9150 const MFloat F1Brho4 = 1.0 / ptr4[PV->RHO];
9151 rho_t -=
9152 eps * (ptr3[PV->RHO] - 4.0 * ptr1[PV->RHO] + 6.0 * ptr0[PV->RHO] - 4.0 * ptr2[PV->RHO] + ptr4[PV->RHO]);
9153 for(MInt j = 0; j < nDim; j++) {
9154 u_t[j] -= eps
9155 * (ptr3[PV->VV[j]] * F1Brho3 - 4.0 * ptr1[PV->VV[j]] * F1Brho1 + 6.0 * ptr0[PV->VV[j]] * F1Brho2
9156 - 4.0 * ptr2[PV->VV[j]] * F1Brho2 + ptr4[PV->VV[j]] * F1Brho4);
9157 }
9158 } else {
9159 rho_t -= eps * (ptr1[PV->RHO] - F2 * ptr0[PV->RHO] + ptr2[PV->RHO]);
9160 for(MInt j = 0; j < nDim; j++) {
9161 u_t[j] -= eps * (ptr1[PV->VV[j]] * F1Brho1 - F2 * ptr0[PV->VV[j]] * F1Brho0 + ptr2[PV->VV[j]] * F1Brho2);
9162 }
9163 }
9164 */
9165 } else {
9166 MInt dir;
9167 if(m_solver->a_hasNeighbor(cellId, dir1)) {
9168 dir = dir1;
9169 } else if(m_solver->a_hasNeighbor(cellId, dir2)) {
9170 dir = dir2;
9171 } else {
9172 TERMM(1, "Boundary cell with no neighbor in normal direction?");
9173 }
9174 const MInt nghbrId1 = m_solver->c_neighborId(cellId, dir);
9175 const MInt nghbrId2 = m_solver->c_neighborId(nghbrId1, dir);
9176 const MInt sign = (dir % 2 == 0) ? 1 : -1; // negativ for -x bndry
9177#ifdef WAR_NVHPC_PSTL
9178 MFloat var_n1[nDim + 1];
9179 for(MInt d = 0; d < nDim + 1; d++) {
9180 var_n1[d] = m_solver->a_variable(nghbrId1, d);
9181 }
9182 MFloat* const ptr1 = var_n1;
9183 MFloat var_n2[nDim + 1];
9184 for(MInt d = 0; d < nDim + 1; d++) {
9185 var_n2[d] = m_solver->a_variable(nghbrId2, d);
9186 }
9187 MFloat* const ptr2 = var_n2;
9188#else
9189 MFloat* const ptr1 = &m_solver->a_variable(nghbrId1, 0);
9190 MFloat* const ptr2 = &m_solver->a_variable(nghbrId2, 0);
9191#endif
9192 const MFloat F1Brho1 = 1.0 / ptr1[PV->RHO];
9193 const MFloat F1Brho2 = 1.0 / ptr2[PV->RHO];
9194 // calculate derivatives based on one-sided differences
9195 // |--0--1--2--...
9196 const MFloat rho_x = sign * (F3B2 * ptr0[PV->RHO] - F2 * ptr1[PV->RHO] + F1B2 * ptr2[PV->RHO]);
9197 MFloat u_x[nDim];
9198 for(MInt j = 0; j < nDim; j++) {
9199 // if dir is pointing in negative direction (dir%2==0) -> backward difference, otherwise forward
9200 u_x[j] =
9201 sign
9202 * (F3B2 * ptr0[PV->VV[j]] * F1Brho0 - F2 * ptr1[PV->VV[j]] * F1Brho1 + F1B2 * ptr2[PV->VV[j]] * F1Brho2);
9203 }
9204 // assemble L - wave amplitude variation, while vanishing incoming waves
9205 MFloat L[nDim + 1]{}; // for 3D corresponding to {u_n-c, u_t1, u_t2, u_n+c}
9206 const MBool flowPointsInwards = (ptr0[pvVv[c]] * sign < 0);
9207 if(flowPointsInwards) {
9208 MInt id = 1;
9209 for(MInt j = 0; j < nDim; j++) {
9210 if(j != c) {
9211 L[id] = L_approx[j];
9212 id++;
9213 }
9214 }
9215 } else {
9216 MInt id = 1;
9217 for(MInt j = 0; j < nDim; j++) {
9218 if(j != c) {
9219 L[id] = ptr0[pvVv[c]] * F1Brho0 * u_x[j];
9220 id++;
9221 }
9222 }
9223 }
9224 if(sign > 0) {
9225 // boundary outward normal points in positive axis direction
9226 L[0] = L_approx[c];
9227 L[nDim] = (ptr0[pvVv[c]] * F1Brho0 + LBCS) * (CSsq * rho_x + LBCS * ptr0[PV->RHO] * u_x[c]);
9228 } else {
9229 // boundary outward normal points in negative axis direction
9230 L[0] = (ptr0[pvVv[c]] * F1Brho0 - LBCS) * (CSsq * rho_x - LBCS * ptr0[PV->RHO] * u_x[c]);
9231 L[nDim] = L_approx[c];
9232 }
9233 // time derivative part for this direction
9234 rho_t -= F1B2 * F1BCSsq * (L[nDim] + L[0]);
9235 MInt id = 1;
9236 for(MInt j = 0; j < nDim; j++) {
9237 if(j != c) {
9238 u_t[j] -= L[id];
9239 id++;
9240 } else {
9241 u_t[c] -= F1B2 * F1BCS * F1Brho0 * (L[nDim] - L[0]);
9242 }
9243 }
9244 }
9245 }
9246 //--calc characteristic values----------------------------------------------
9247 rho_b = m_solver->a_variable(cellId, PV->RHO) + rho_t;
9248 for(MInt j = 0; j < nDim; j++) {
9249 u_b[j] = m_solver->a_variable(cellId, PV->VV[j]) * F1Brho0 + u_t[j];
9250 }
9251}
9252
9266template <MInt nDim, MInt nDist, class SysEqn>
9267inline void LbBndCndDxQy<nDim, nDist, SysEqn>::calcCharValuesOnFace(const MInt index, const MInt /*direction*/,
9268 const MInt bndCellId, MFloat& rho_b, MFloat* u_b) {
9269 constexpr MFloat deltaT = 1;
9270 constexpr MFloat sigma = 0.59;
9271 constexpr MFloat kappa = 1.0; // in original kappa=1.2 is used in combination with corrected LBE
9272 constexpr MFloat sqrtKappa = 1.0;
9273 // speed of sound c_s = sqrt(kappa*p/rho) where kappa = 1.2 is used at the boundary
9274 // const MFloat sqrtKappa = sqrt(kappa);
9275
9276 MFloat* const formerVariables = this->m_bndCndData[index].data;
9277 const MInt noBcVars = this->m_bndCndData[index].noVars;
9278 const MInt offset = m_bndCndOffsets[index];
9279
9280 const MInt currentId = m_bndCells[bndCellId].m_cellId;
9281 const MInt iMo = (bndCellId - offset) * noBcVars;
9282
9283 const MFloat k_relax =
9284 sigma * (1 - m_Ma * m_Ma) * sqrtKappa * LBCS
9285 / (m_domainLength
9286 * FFPOW2(m_solver->maxLevel()
9287 - m_solver->a_level(currentId))); // m_domainLength represents the distance between the inlet
9288 // and the outlet on the highest level of refinement
9289
9290 const MFloat rho_trg = (m_densityFluctuations) ? 0.0 : 1.0; // target pressure at boundary
9291 const MFloat rho_offset = (m_densityFluctuations) ? 1.0 : 0.0;
9292
9293 // ...
9294 auto subTimeDerivatives = [&](MInt direction2, MFloat& rho_t, MFloat* u_t) {
9295 const MInt nghbrId = m_solver->c_neighborId(currentId, Ld::oppositeDist(direction2));
9296 // x_b is the location between fluid and boundary center. x_b-i is i-th fluid cell
9297 // compute derivatives:
9298 // (x_b, t-1) : formerVariables
9299 // (x_b-1, t-1) : [a_variable(currentId,..) + a_oldVariable(currentId,..)]/2
9300 // (x_b-2, t-1) : [a_variable(nghbrId,..) + a_oldVariable(nghbrId,..)] /2
9301 const MFloat der_rho =
9302 F1B3
9303 * (8 * formerVariables[iMo + PV->RHO]
9304 - 9 * F1B2 * (m_solver->a_variable(currentId, PV->RHO) + m_solver->a_oldVariable(currentId, PV->RHO))
9305 + F1B2 * (m_solver->a_variable(nghbrId, PV->RHO) + m_solver->a_oldVariable(nghbrId, PV->RHO)));
9306 const MFloat der_p = kappa * CSsq * der_rho;
9307 const MFloat der_u =
9308 F1B3
9309 * (8 * formerVariables[iMo + PV->U]
9310 - 9 * F1B2 * (m_solver->a_variable(currentId, PV->U) + m_solver->a_oldVariable(currentId, PV->U))
9311 + F1B2 * (m_solver->a_variable(nghbrId, PV->U) + m_solver->a_oldVariable(nghbrId, PV->U)));
9312 const MFloat der_v =
9313 F1B3
9314 * (8 * formerVariables[iMo + PV->V]
9315 - 9 * F1B2 * (m_solver->a_variable(currentId, PV->V) + m_solver->a_oldVariable(currentId, PV->V))
9316 + F1B2 * (m_solver->a_variable(nghbrId, PV->V) + m_solver->a_oldVariable(nghbrId, PV->V)));
9317 const MFloat der_w =
9318 F1B3
9319 * (8 * formerVariables[iMo + PV->W]
9320 - 9 * F1B2 * (m_solver->a_variable(currentId, PV->W) + m_solver->a_oldVariable(currentId, PV->W))
9321 + F1B2 * (m_solver->a_variable(nghbrId, PV->W) + m_solver->a_oldVariable(nghbrId, PV->W)));
9322
9323 // L: wave amplitude variations determined for (x_b, t-1/2)
9324 // incoming ones are approximated
9325 // | BC+: BC-: // in Izquierdo2008
9326 MFloat L1 = F0; // u_n - c_s | incoming outgoing // L1
9327 MFloat L2 = F0; // energy | outgoing incoming // L3 (=0 for isothermal)
9328 MFloat L3 = F0; // u_t1 | outgoing incoming // L2.1
9329 MFloat L4 = F0; // u_t2 | outgoing incoming // L2.2 (since it's 3D)
9330 MFloat L5 = F0; // u_n + c_s | outgoing incoming // L4
9331
9332 switch(direction2) {
9333 case 0:
9334 // negative derivatives due to negative direction!
9335 L1 = (formerVariables[iMo + PV->U] - sqrtKappa * LBCS)
9336 * (-der_p - (formerVariables[iMo + PV->RHO] + rho_offset) * (-der_u) * sqrtKappa * LBCS);
9337 L2 = 0.0;
9338 L3 = k_relax * kappa * CSsq * (formerVariables[iMo + PV->RHO] - rho_trg);
9339 L4 = L3;
9340 L5 = L3;
9341
9342 // compute u1,u2,u3 using the characteristic wave amplitudes
9343 u_t[0] += (L5 - L1) * F1B2 * (F1BCS / sqrtKappa) / (formerVariables[iMo + PV->RHO] + rho_offset);
9344 u_t[1] += L3;
9345 u_t[2] += L4;
9346 break;
9347 case 1:
9348 L1 = k_relax * kappa * CSsq * (formerVariables[iMo + PV->RHO] - rho_trg);
9349 // L2 = formerVariables[iMo + PV->U] * (POW2(LBCS) * der_rho - der_p); // = 0 for isothermal LB
9350 L2 = 0.0;
9351 L3 = formerVariables[iMo + PV->U] * der_v;
9352 L4 = formerVariables[iMo + PV->U] * der_w;
9353 L5 = (formerVariables[iMo + PV->U] + sqrtKappa * LBCS)
9354 * (der_p + (formerVariables[iMo + PV->RHO] + rho_offset) * der_u * sqrtKappa * LBCS);
9355
9356 // compute u1,u2,u3 using the characteristic wave amplitudes
9357 u_t[0] += (L5 - L1) * F1B2 * (F1BCS / sqrtKappa) / (formerVariables[iMo + PV->RHO] + rho_offset);
9358 u_t[1] += L3;
9359 u_t[2] += L4;
9360 break;
9361 case 2:
9362 // negative derivatives due to negative direction!
9363 L1 = (formerVariables[iMo + PV->V] - sqrtKappa * LBCS)
9364 * (-der_p - (formerVariables[iMo + PV->RHO] + rho_offset) * (-der_v) * sqrtKappa * LBCS);
9365 L2 = 0.0;
9366 L3 = k_relax * kappa * CSsq * (formerVariables[iMo + PV->RHO] - rho_trg);
9367 L4 = L3;
9368 L5 = L3;
9369
9370 // compute u1,u2,u3 using the characteristic wave amplitudes
9371 u_t[0] += L3;
9372 u_t[1] += (L5 - L1) * F1B2 * (F1BCS / sqrtKappa) / (formerVariables[iMo + PV->RHO] + rho_offset);
9373 u_t[2] += L4;
9374 break;
9375 case 3:
9376 L1 = k_relax * kappa * CSsq * (formerVariables[iMo + PV->RHO] - rho_trg);
9377 L2 = 0.0;
9378 L3 = formerVariables[iMo + PV->V] * der_u;
9379 L4 = formerVariables[iMo + PV->V] * der_w;
9380 L5 = (formerVariables[iMo + PV->V] + sqrtKappa * LBCS)
9381 * (der_p + (formerVariables[iMo + PV->RHO] + rho_offset) * der_v * sqrtKappa * LBCS);
9382
9383 // compute u1,u2,u3 using the characteristic wave amplitudes
9384 u_t[0] += L3;
9385 u_t[1] += (L5 - L1) * F1B2 * (F1BCS / sqrtKappa) / (formerVariables[iMo + PV->RHO] + rho_offset);
9386 u_t[2] += L4;
9387 break;
9388 case 4:
9389 // negative derivatives due to negative direction!
9390 L1 = (formerVariables[iMo + PV->W] - sqrtKappa * LBCS)
9391 * (-der_p - (formerVariables[iMo + PV->RHO] + rho_offset) * (-der_w) * sqrtKappa * LBCS);
9392 L2 = 0.0;
9393 L3 = k_relax * kappa * CSsq * (formerVariables[iMo + PV->RHO] - rho_trg);
9394 L4 = L3;
9395 L5 = L3;
9396
9397 // compute u1,u2,u3 using the characteristic wave amplitudes
9398 u_t[0] += L3;
9399 u_t[1] += L4;
9400 u_t[2] += (L5 - L1) * F1B2 * (F1BCS / sqrtKappa) / (formerVariables[iMo + PV->RHO] + rho_offset);
9401 break;
9402 case 5:
9403 L1 = k_relax * kappa * CSsq * (formerVariables[iMo + PV->RHO] - rho_trg);
9404 L2 = 0.0;
9405 L3 = formerVariables[iMo + PV->W] * der_u;
9406 L4 = formerVariables[iMo + PV->W] * der_v;
9407 L5 = (formerVariables[iMo + PV->W] + sqrtKappa * LBCS)
9408 * (der_p + (formerVariables[iMo + PV->RHO] + rho_offset) * der_w * sqrtKappa * LBCS);
9409
9410 // compute u1,u2,u3 using the characteristic wave amplitudes
9411 u_t[0] += L3;
9412 u_t[1] += L4;
9413 u_t[2] += (L5 - L1) * F1B2 * (F1BCS / sqrtKappa) / (formerVariables[iMo + PV->RHO] + rho_offset);
9414 break;
9415 default:
9416 TERMM(1, "Unrealistic case, please check your Boundary Conditions!!!");
9417 break;
9418 }
9419 // compute rho using the characteristic wave amplitudes
9420 rho_t = (F1BCSsq / kappa) * (F1B2 * (L5 + L1) + L2);
9421 };
9422 MFloat rho_t = 0;
9423 MFloat u_t[3]{}; // TODO labels:LB dxqy: only for 3D defined, yet
9424 for(MInt d = 0; d < nDim * 2; d++) {
9425 if(m_solver->a_hasNeighbor(currentId, d) == 0) {
9426 subTimeDerivatives(d, rho_t, u_t);
9427 }
9428 }
9429 // forward euler step
9430 rho_b = formerVariables[iMo + PV->RHO] - deltaT * rho_t;
9431 u_b[0] = formerVariables[iMo + PV->U] - deltaT * u_t[0];
9432 u_b[1] = formerVariables[iMo + PV->V] - deltaT * u_t[1];
9433 u_b[2] = formerVariables[iMo + PV->W] - deltaT * u_t[2];
9434 // save variables in virtual boundary cell for next timestep
9435 formerVariables[iMo + PV->RHO] = rho_b;
9436 formerVariables[iMo + PV->U] = u_b[0];
9437 formerVariables[iMo + PV->V] = u_b[1];
9438 formerVariables[iMo + PV->W] = u_b[2];
9439}
9440
9449template <MInt nDim, MInt nDist, class SysEqn>
9450inline void LbBndCndDxQy<nDim, nDist, SysEqn>::charVelocity(MInt index, MInt direction) {
9451 TRACE();
9452
9453 constexpr MFloat kappa = 1.0; // in original kappa=1.2 is used in combination with corrected LBE
9454
9455 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
9456 const MInt currentId = m_bndCells[i].m_cellId;
9457 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(currentId)) != 0
9458 || m_solver->a_isHalo(currentId) || !m_solver->a_hasNeighbor(currentId, Ld::oppositeDist(direction))) {
9459 continue;
9460 }
9461
9462 //--determine macroscopic values based on LODI------------------------------
9463 MFloat rho_b, u_b[3];
9464 calcCharValuesOnFace(index, direction, i, rho_b, &u_b[0]);
9465
9466 //--perform bounce back with velocity term----------------------------------
9467 const MFloat rho_offset = (m_densityFluctuations) ? 1.0 : 0.0;
9468 MFloat c[2 * nDim];
9469 for(MInt n = 0; n < nDim; n++) {
9470 c[2 * n] = -u_b[n];
9471 c[2 * n + 1] = u_b[n];
9472 }
9473
9474 for(MInt j = 0; j < Ld::dxQyFld(); j++) {
9475 const MInt dir = Ld::componentFld(direction, j);
9476 if(m_solver->a_hasNeighbor(currentId, dir) == 0) {
9477 MInt tpIndex;
9478 MFloat tmpUW;
9479 if(dir < Ld::distFld(0)) {
9480 tmpUW = c[dir];
9481 tpIndex = 1;
9482 } else {
9483 if(dir < (Ld::distFld(0) + Ld::distFld(1))) {
9484 const MInt k = dir - Ld::distFld(0);
9485 tmpUW = (c[Ld::mFld1(2 * k)] + c[Ld::mFld1(2 * k + 1)]);
9486 tpIndex = 2;
9487 } else {
9488 const MInt k = dir - (Ld::distFld(0) + Ld::distFld(1));
9489 tmpUW = (c[Ld::mFld2(3 * k)] + c[Ld::mFld2(3 * k + 1)] + c[Ld::mFld2(3 * k + 2)]);
9490 tpIndex = 3;
9491 }
9492 }
9493
9494 m_solver->a_oldDistribution(currentId, Ld::oppositeDist(dir)) =
9495 m_solver->a_distribution(currentId, dir)
9496 - 2.0 * Ld::tp(tpIndex) * (rho_b + rho_offset) * (F1BCSsq / kappa) * tmpUW;
9497 }
9498 }
9499 }
9500}
9501
9502template <>
9503inline void LbBndCndDxQy<2, 9, maia::lb::LbSysEqnIncompressible<2, 9>>::charVelocity(MInt index, MInt direction) {
9504 TRACE();
9505
9506 constexpr MInt nDim = 2;
9507
9508 MFloat L1, L2, L3, L4;
9509 L1 = L2 = L3 = L4 = F0;
9510 MFloat rho, u1, u2, ub;
9511 MFloat deltaT = 1;
9512 MFloat sigma = 1;
9513
9514 MFloat kappa = 1.2;
9515 MFloat sqrtKappa = sqrt(1.2); // speed of sound c_s = sqrt(kappa*p/rho) where kappa = 1.2 is used at the boundary
9516 MInt currentID, nghbrID, tpIndex, k;
9517 // MFloat tmpWidth = m_referenceLength * (m_solver->c_cellLengthAtLevel(m_solver->m_solver->maxLevel()]);
9518 MFloat k_relax = sigma * (1 - m_Ma * m_Ma) * sqrtKappa * LBCS / m_solver->c_cellLengthAtLevel(0);
9519 MFloat der_u, der_v, der_p;
9520 MFloat c[4], tmpUW;
9521
9522 MFloat* const formerVariables = this->m_bndCndData[index].data;
9523 const MInt offset = m_bndCndOffsets[index];
9524
9525 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
9526 currentID = m_bndCells[i].m_cellId;
9527 nghbrID = m_solver->c_neighborId(currentID, Ld::oppositeDist(direction));
9528 constexpr MInt noBcVars = nDim + 1;
9529 const MInt iMo = (i - offset) * noBcVars;
9530
9531 // parabolic inflow
9532 // ub = 1.5 * m_Ma * sqrtKappa * LBCS * (1.0 - (2*m_solver->a_coordinate(currentID,
9533 // 1)/tmpWidth)*(2*m_solver->a_coordinate(currentID, 1)/tmpWidth));
9534
9535 // uniform inflow
9536 ub = m_Ma * LBCS;
9537
9538 // calculate incoming wave amplitudes. to obtain L1 spatial derivatives der_p and der_u are needed.
9539 // t^= t + 1/2 => t^ - 1 = t - 1/2 => Averaging of m_variables and m_oldVariables
9540
9541 if(direction == 0) {
9542 der_p = F1B3 * CSsq
9543 * (-8 * formerVariables[iMo + PV->RHO]
9544 + 9 * F1B2 * (m_solver->a_variable(currentID, PV->RHO) + m_solver->a_oldVariable(currentID, PV->RHO))
9545 - F1B2 * (m_solver->a_oldVariable(nghbrID, PV->RHO) + m_solver->a_variable(nghbrID, PV->RHO)));
9546
9547
9548 der_u = F1B3
9549 * (-8 * formerVariables[iMo + PV->U]
9550 + 9 * F1B2 * (m_solver->a_variable(currentID, PV->U) + m_solver->a_oldVariable(currentID, PV->U))
9551 - F1B2 * (m_solver->a_variable(nghbrID, PV->U) + m_solver->a_oldVariable(nghbrID, PV->U)));
9552
9553
9554 der_v = F1B3
9555 * (-8 * formerVariables[iMo + PV->V]
9556 + 9 * F1B2 * (m_solver->a_variable(currentID, PV->V) + m_solver->a_oldVariable(currentID, PV->V))
9557 - F1B2 * (m_solver->a_variable(nghbrID, PV->V) + m_solver->a_oldVariable(nghbrID, PV->V)));
9558
9559
9560 L1 = (formerVariables[iMo + PV->U] - sqrtKappa * LBCS)
9561 * (der_p - formerVariables[iMo + PV->RHO] * der_u * sqrtKappa * LBCS);
9562 L2 = k_relax * F1B3 * (formerVariables[iMo + PV->U] - 0); // set u_y=0
9563 L3 = L2;
9564 L4 = L2;
9565 } else if(direction == 1) {
9566 der_p = CSsq * F1B3
9567 * (8 * formerVariables[iMo + PV->RHO]
9568 - 9 * F1B2 * (m_solver->a_variable(currentID, PV->RHO) + m_solver->a_oldVariable(currentID, PV->RHO))
9569 + F1B2 * (m_solver->a_oldVariable(nghbrID, PV->RHO) + m_solver->a_variable(nghbrID, PV->RHO)));
9570
9571
9572 der_u = F1B3
9573 * (8 * formerVariables[iMo + PV->U]
9574 - 9 * F1B2 * (m_solver->a_variable(currentID, PV->U) + m_solver->a_oldVariable(currentID, PV->U))
9575 + F1B2 * (m_solver->a_variable(nghbrID, PV->U) + m_solver->a_oldVariable(nghbrID, PV->U)));
9576
9577
9578 der_v = F1B3
9579 * (8 * formerVariables[iMo + PV->V]
9580 - 9 * F1B2 * (m_solver->a_variable(currentID, PV->V) + m_solver->a_oldVariable(currentID, PV->V))
9581 + F1B2 * (m_solver->a_variable(nghbrID, PV->V) + m_solver->a_oldVariable(nghbrID, PV->V)));
9582
9583 L1 = k_relax * F1B3 * (formerVariables[iMo + PV->U] - ub);
9584 L2 = formerVariables[iMo + PV->U] * der_v;
9585 L3 = 0;
9586 L4 = (formerVariables[iMo + PV->U] + sqrtKappa * LBCS)
9587 * (der_p + formerVariables[iMo + PV->RHO] * der_u * sqrtKappa * LBCS);
9588 } else {
9589 TERMM(1, "Direction can only be 0 or 1 in D2Q9!");
9590 }
9591
9592 // compute u1,u2 and rho by using the characteristic wave amplitudes
9593 u1 = formerVariables[iMo + PV->U] - deltaT * F1B2 / formerVariables[iMo + PV->RHO] * F1BCS / sqrtKappa * (L4 - L1);
9594 u2 = formerVariables[iMo + PV->V] - deltaT * L2;
9595 rho = formerVariables[iMo + PV->RHO] - deltaT * F1B2 / kappa * F1BCSsq * (L4 + L1)
9596 - F1BCSsq * L3 / kappa; // where 1.2 is kappa²
9597
9598 c[0] = -ub;
9599 c[1] = ub;
9600 c[2] = F0;
9601 c[3] = F0;
9602
9603 for(MInt j = 0; j < 8; j++) {
9604 if(m_solver->a_hasNeighbor(currentID, j) == 0) {
9605 if(j < 4) {
9606 tmpUW = c[j];
9607 tpIndex = 1;
9608 } else {
9609 if(j < 8) {
9610 k = j - Ld::distFld(0);
9611 tmpUW = (c[Ld::mFld1(2 * k)] + c[Ld::mFld1(2 * k + 1)]);
9612 tpIndex = 2;
9613 }
9614 }
9615
9616 m_solver->a_oldDistribution(currentID, Ld::oppositeDist(j)) =
9617 m_solver->a_distribution(currentID, j)
9618 - 2.0 * Ld::tp(tpIndex) * rho * F1BCSsq * tmpUW; // the minus corresponds to the opposite direction
9619 }
9620 }
9621
9622 // // only for bounce back in corners!!! (upper and lower boundary need to be walls)
9623 // if (m_solver->a_hasNeighbor(currentID, 3) == 0) {
9624 // m_solver->a_oldDistribution(currentID, 6) = m_solver->a_distribution(currentID, 4);
9625 // m_solver->a_oldDistribution(currentID, 2) = m_solver->a_distribution(currentID, 3);
9626 // m_solver->a_oldDistribution(currentID, 5) = m_solver->a_distribution(currentID, 7);
9627 // }
9628
9629 // if (m_solver->a_hasNeighbor(currentID, 2) == 0) {
9630 // m_solver->a_oldDistribution(currentID, 4) = m_solver->a_distribution(currentID, 6);
9631 // m_solver->a_oldDistribution(currentID, 3) = m_solver->a_distribution(currentID, 2);
9632 // m_solver->a_oldDistribution(currentID, 7) = m_solver->a_distribution(currentID, 5);
9633 // }
9634
9635 // set new boundary variables
9636 formerVariables[iMo + 0] = u1;
9637 formerVariables[iMo + 1] = u2;
9638 formerVariables[iMo + 2] = rho;
9639 }
9640}
9641
9650template <MInt nDim, MInt nDist, class SysEqn>
9652 TRACE();
9653
9654 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
9655 const MInt currentId = m_bndCells[i].m_cellId;
9656 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(currentId)) != 0
9657 || m_solver->a_isHalo(currentId)) {
9658 continue;
9659 }
9660 //--LODI calculation--------------------------------------------------------
9661 MFloat rho_b, u_b[nDim];
9662 calcCharValues<1>(index, currentId, rho_b, &u_b[0]);
9663 //--set equilibrium state---------------------------------------------------
9664#ifdef WAR_NVHPC_PSTL
9665 MFloat oldDist[nDist] = {F0};
9666 for(MInt d = 0; d < nDist; d++) {
9667 oldDist[d] = m_solver->a_oldDistribution(currentId, d);
9668 }
9669 sysEqn().calcEqDists(rho_b, &u_b[0], oldDist);
9670 for(MInt d = 0; d < nDist; d++) {
9671 m_solver->a_oldDistribution(currentId, d) = oldDist[d];
9672 }
9673#else
9674 sysEqn().calcEqDists(rho_b, &u_b[0], &m_solver->a_oldDistribution(currentId, 0));
9675#endif
9676 }
9677}
9678
9688template <MInt nDim, MInt nDist, class SysEqn>
9689inline void LbBndCndDxQy<nDim, nDist, SysEqn>::charPressure(MInt index, const MInt direction) {
9690 TRACE();
9691
9692 // This boundary condition constitutes the following steps:
9693 // 1) Determining the macroscopic variables at the outlet based on LODI
9694 // (linearized one-dimensional invsicd) equation : rho_w, u_w
9695 // 2) Performing pressure antibounce-back with previosly calculated rho_w, u_w
9696
9697 constexpr MFloat kappa = 1.0; // in original kappa=1.2 is used in combination with corrected LBE
9698
9699 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
9700 const MInt currentId = m_bndCells[i].m_cellId;
9701 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(currentId)) != 0
9702 || m_solver->a_isHalo(currentId) || !m_solver->a_hasNeighbor(currentId, Ld::oppositeDist(direction))) {
9703 continue;
9704 }
9705
9706 //--determine macroscopic values based on LODI------------------------------
9707 MFloat rho_b, u_b[3];
9708 calcCharValuesOnFace(index, direction, i, rho_b, &u_b[0]);
9709
9710 //--PAB---------------------------------------------------------------------
9711 //-Calculate symmetric part of eq-distributions at the boundary
9712 const MFloat squaredU_b = std::inner_product(&u_b[0], &u_b[nDim], &u_b[0], .0);
9713
9714 const MInt tmpDistId0 = Ld::distFld(0);
9715 const MInt tmpDistId1 = Ld::distFld(1);
9716 const MInt tmpDistId2 = Ld::distFld(2);
9717
9718 MFloat b[2 * nDim];
9719 for(MInt n = 0; n < nDim; n++) {
9720 b[2 * n] = -u_b[n];
9721 b[2 * n + 1] = u_b[n];
9722 }
9723
9724 MFloat eqSym[nDist - 1];
9725 // Calculation of eq. distributions for directions with only one component
9726 for(MInt j = 0; j < tmpDistId0; j++) {
9727 eqSym[j] = Ld::tp(1) * (F1BCSsq / kappa)
9728 * (rho_b * (CSsq * kappa) + b[j] * b[j] * (F1BCSsq / kappa) * F1B2 - squaredU_b * F1B2);
9729 }
9730 // Calculation of eq. distributions for directions with two components
9731 for(MInt j = 0; j < tmpDistId1; j++) {
9732 const MFloat tmp = (b[Ld::mFld1(2 * j)] + b[Ld::mFld1(2 * j + 1)]);
9733 eqSym[tmpDistId0 + j] = Ld::tp(2) * (F1BCSsq / kappa)
9734 * (rho_b * (CSsq * kappa) + tmp * tmp * (F1BCSsq / kappa) * F1B2 - squaredU_b * F1B2);
9735 }
9736 // Calculation of eq. distributions for directions with three components
9737 {
9738 const MInt tmpDistId = tmpDistId0 + tmpDistId1;
9739 for(MInt j = 0; j < tmpDistId2; j++) {
9740 const MFloat tmp = (b[Ld::mFld2(3 * j)] + b[Ld::mFld2(3 * j + 1)] + b[Ld::mFld2(3 * j + 2)]);
9741 eqSym[tmpDistId + j] = Ld::tp(3) * (F1BCSsq / kappa)
9742 * (rho_b * (CSsq * kappa) + tmp * tmp * (F1BCSsq / kappa) * F1B2 - squaredU_b * F1B2);
9743 }
9744 }
9745
9746 // Calculate eq-distributions at boundary cell center (for correction-term)
9747 //------------------------------------------------------------
9748#ifdef WAR_NVHPC_PSTL
9749 MFloat u[nDim] = {F0};
9750 for(MInt d = 0; d < nDim; d++) {
9751 u[d] = m_solver->a_variable(currentId, d);
9752 }
9753 const MFloat* const l_uu = u;
9754#else
9755 const MFloat* const l_uu = &m_solver->a_variable(currentId, PV->VV[0]);
9756#endif
9757 const MFloat l_rho = m_solver->a_variable(currentId, PV->RHO);
9758 for(MInt n = 0; n < nDim; n++) {
9759 b[2 * n] = -l_uu[n];
9760 b[2 * n + 1] = l_uu[n];
9761 }
9762 const MFloat squaredU = std::inner_product(&l_uu[0], &l_uu[nDim], &l_uu[0], .0);
9763
9764 MFloat eqSymC[nDist - 1];
9765 // Calculation of eq. distributions for directions with only one component
9766 for(MInt j = 0; j < tmpDistId0; j++) {
9767 eqSymC[j] =
9768 Ld::tp(1) * (F1BCSsq / kappa) * (l_rho * (CSsq * kappa) + b[j] * b[j] * F1BCSsq * F1B2 - squaredU * F1B2);
9769 }
9770
9771 // Calculation of eq. distributions for directions with two components
9772 {
9773 const MInt tmpDistId = tmpDistId0;
9774 for(MInt j = 0; j < tmpDistId1; j++) {
9775 const MFloat tmp = (b[Ld::mFld1(2 * j)] + b[Ld::mFld1(2 * j + 1)]);
9776 eqSymC[tmpDistId + j] = Ld::tp(2) * (F1BCSsq / kappa)
9777 * (l_rho * (CSsq * kappa) + tmp * tmp * (F1BCSsq / kappa) * F1B2 - squaredU * F1B2);
9778 }
9779 }
9780
9781 // Calculation of eq. distributions for directions with three components
9782 {
9783 const MInt tmpDistId = tmpDistId0 + tmpDistId1;
9784 for(MInt j = 0; j < tmpDistId2; j++) {
9785 const MFloat tmp = (b[Ld::mFld2(3 * j)] + b[Ld::mFld2(3 * j + 1)] + b[Ld::mFld2(3 * j + 2)]);
9786 eqSymC[tmpDistId + j] = Ld::tp(3) * (F1BCSsq / kappa)
9787 * (l_rho * (CSsq * kappa) + tmp * tmp * (F1BCSsq / kappa) * F1B2 - squaredU * F1B2);
9788 }
9789 }
9790
9791 // symmetric distribution function for 2nd order correction term
9792 //---------------------------------------------------
9793 MFloat meanDist[nDist - 1];
9794 // Calculate average for opposite directions
9795 for(MInt j = 0; j < nDist - 1; j++) {
9796 meanDist[j] =
9797 F1B2
9798 * (m_solver->a_oldDistribution(currentId, j) + m_solver->a_oldDistribution(currentId, Ld::oppositeDist(j)));
9799 }
9800
9801 // set missing incoming distributions (normal to the boundary)
9802 //--------------------------------------
9803 const MFloat omega =
9804 2.0 / (1.0 + 6.0 * m_solver->a_nu(currentId) * FFPOW2(m_solver->maxLevel() - m_solver->a_level(currentId)));
9805
9806 for(MInt j = 0; j < Ld::dxQyFld(); j++) {
9807 const MInt dir = Ld::componentFld(direction, j);
9808 m_solver->a_oldDistribution(currentId, Ld::oppositeDist(dir)) =
9809 -m_solver->a_distribution(currentId, dir) + 2 * eqSym[dir] + (2 - omega) * (meanDist[dir] - eqSymC[dir]);
9810 }
9811 }
9812}
9813
9814template <>
9815inline void LbBndCndDxQy<2, 9, maia::lb::LbSysEqnIncompressible<2, 9>>::charPressure(MInt index, MInt direction) {
9816 TRACE();
9817
9818 constexpr MInt nDim = 2;
9819
9820 MFloat L1, L2, L3, L4;
9821 L1 = L2 = L3 = L4 = F0;
9822 MFloat rho, u1, u2, rho_b, u1c, u2c, rhoc;
9823 MFloat deltaT = 1;
9824 MFloat sigma = 1;
9825
9826 MFloat F9B2 = 9.0 / 2.0;
9827
9828 MFloat kappa = 1.2;
9829 MFloat sqrtKappa = sqrt(kappa); // speed of sound c_s = sqrt(kappa*p/rho) where kappa = 1.2 is used at the boundary
9830 MInt currentID, nghbrID;
9831 MFloat k_relax;
9832 MFloat der_u, der_p, der_v;
9833 MFloat eqD[9];
9834 MFloat eqDc[8];
9835 MFloat meanDist[8];
9836
9837 MFloat* const formerVariables = this->m_bndCndData[index].data;
9838 const MInt offset = m_bndCndOffsets[index];
9839
9840 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
9841 currentID = m_bndCells[i].m_cellId;
9842 constexpr MInt noBcVars = nDim + 1;
9843 const MInt iMo = (i - offset) * noBcVars;
9844
9845 k_relax = sigma * (1 - m_Ma * m_Ma) * sqrtKappa * LBCS
9846 / (m_domainLength
9847 * FFPOW2(m_solver->maxLevel()
9848 - m_solver->a_level(currentID))); // m_domainLength represents the distance between the inlet
9849 // and the outlet on the highest level of refinement
9850
9851 m_omega =
9852 2.0 / (1.0 + 6.0 * m_solver->a_nu(currentID) * FFPOW2(m_solver->maxLevel() - m_solver->a_level(currentID)));
9853
9854 // if t<5 set formerVariables equal to variables
9855 if(globalTimeStep < 5) {
9856 formerVariables[iMo + 0] = m_solver->a_variable(currentID, PV->U);
9857 formerVariables[iMo + 1] = m_solver->a_variable(currentID, PV->V);
9858 formerVariables[iMo + 2] = m_solver->a_variable(currentID, PV->RHO);
9859 } else {
9860 nghbrID = m_solver->c_neighborId(currentID, Ld::oppositeDist(direction));
9861
9862 if(m_solver->a_hasNeighbor(currentID, direction) == 0) {
9863 rho_b = 1;
9864
9865 if(direction == 1) {
9866 der_p =
9867 CSsq * F1B3
9868 * (8 * formerVariables[iMo + PV->RHO]
9869 - 9 * F1B2 * (m_solver->a_variable(currentID, PV->RHO) + m_solver->a_oldVariable(currentID, PV->RHO))
9870 + F1B2 * (m_solver->a_oldVariable(nghbrID, PV->RHO) + m_solver->a_variable(nghbrID, PV->RHO)));
9871
9872
9873 der_u = F1B3
9874 * (8 * formerVariables[iMo + PV->U]
9875 - 9 * F1B2 * (m_solver->a_variable(currentID, PV->U) + m_solver->a_oldVariable(currentID, PV->U))
9876 + F1B2 * (m_solver->a_variable(nghbrID, PV->U) + m_solver->a_oldVariable(nghbrID, PV->U)));
9877
9878
9879 der_v = F1B3
9880 * (8 * formerVariables[iMo + PV->V]
9881 - 9 * F1B2 * (m_solver->a_variable(currentID, PV->V) + m_solver->a_oldVariable(currentID, PV->V))
9882 + F1B2 * (m_solver->a_variable(nghbrID, PV->V) + m_solver->a_oldVariable(nghbrID, PV->V)));
9883
9884 L1 = k_relax * F1B3 * (formerVariables[iMo + PV->RHO] - rho_b);
9885 L2 = formerVariables[iMo + PV->U] * der_v;
9886 L3 = 0;
9887 L4 = (formerVariables[iMo + PV->U] + sqrtKappa * LBCS)
9888 * (der_p + formerVariables[iMo + PV->RHO] * der_u * sqrtKappa * LBCS);
9889 } else if(direction == 0) {
9890 der_p =
9891 F1B3 * CSsq
9892 * (-8 * formerVariables[iMo + PV->RHO]
9893 + 9 * F1B2 * (m_solver->a_variable(currentID, PV->RHO) + m_solver->a_oldVariable(currentID, PV->RHO))
9894 - F1B2 * (m_solver->a_oldVariable(nghbrID, PV->RHO) + m_solver->a_variable(nghbrID, PV->RHO)));
9895
9896
9897 der_u = F1B3
9898 * (-8 * formerVariables[iMo + PV->U]
9899 + 9 * F1B2 * (m_solver->a_variable(currentID, PV->U) + m_solver->a_oldVariable(currentID, PV->U))
9900 - F1B2 * (m_solver->a_variable(nghbrID, PV->U) + m_solver->a_oldVariable(nghbrID, PV->U)));
9901
9902
9903 der_v = F1B3
9904 * (-8 * formerVariables[iMo + PV->V]
9905 + 9 * F1B2 * (m_solver->a_variable(currentID, PV->V) + m_solver->a_oldVariable(currentID, PV->V))
9906 - F1B2 * (m_solver->a_variable(nghbrID, PV->V) + m_solver->a_oldVariable(nghbrID, PV->V)));
9907
9908
9909 L1 = (formerVariables[iMo + PV->U] - sqrtKappa * LBCS)
9910 * (der_p - formerVariables[iMo + PV->RHO] * der_u * sqrtKappa * LBCS);
9911 L2 = k_relax * F1B3 * (formerVariables[iMo + PV->RHO] - rho_b);
9912 L3 = L2;
9913 L4 = L2;
9914 } else {
9915 TERMM(1, "Direction can only be 0 or 1 in D2Q9!");
9916 }
9917
9918 // compute u1,u2 and rho using the characteristic wave amplitudes
9919 u1 = formerVariables[iMo + PV->U]
9920 - deltaT * F1B2 / formerVariables[iMo + PV->RHO] * F1BCS / sqrtKappa * (L4 - L1);
9921 u2 = formerVariables[iMo + PV->V] - deltaT * L2;
9922 rho = formerVariables[iMo + PV->RHO] - deltaT * F1B2 / kappa * F1BCSsq * (L4 + L1)
9923 - F1BCSsq * L3 / kappa; // where 1.2 is kappa²
9924
9925 // symmetric equilibriumfunctions rho_0=1 for the boundary
9926 eqD[0] = F1B9 * (rho + F9B2 * ((-u1) * (-u1) - F1B3 * (u1 * u1 + u2 * u2)));
9927 eqD[1] = F1B9 * (rho + F9B2 * ((u1) * (u1)-F1B3 * (u1 * u1 + u2 * u2)));
9928 eqD[2] = F1B9 * (rho + F9B2 * ((-u2) * (-u2) - F1B3 * (u1 * u1 + u2 * u2)));
9929 eqD[3] = F1B9 * (rho + F9B2 * ((u2) * (u2)-F1B3 * (u1 * u1 + u2 * u2)));
9930 eqD[4] = F1B36 * (rho + F9B2 * ((u1 + u2) * (u1 + u2) - F1B3 * (u1 * u1 + u2 * u2)));
9931 eqD[5] = F1B36 * (rho + F9B2 * ((u1 - u2) * (u1 - u2) - F1B3 * (u1 * u1 + u2 * u2)));
9932 eqD[6] = F1B36 * (rho + F9B2 * ((-u1 - u2) * (-u1 - u2) - F1B3 * (u1 * u1 + u2 * u2)));
9933 eqD[7] = F1B36 * (rho + F9B2 * ((-u1 + u2) * (-u1 + u2) - F1B3 * (u1 * u1 + u2 * u2)));
9934 eqD[8] = 0;
9935
9936 u1c = m_solver->a_variable(currentID, PV->U);
9937 u2c = m_solver->a_variable(currentID, PV->V);
9938 rhoc = m_solver->a_variable(currentID, PV->RHO);
9939
9940 // symmetric equilibriumfunctions rho_0=1 for the boundary
9941 eqDc[0] = F1B9 * (rhoc + F9B2 * ((-u1c) * (-u1c) - F1B3 * (u1c * u1c + u2c * u2c)));
9942 eqDc[1] = F1B9 * (rhoc + F9B2 * ((u1c) * (u1c)-F1B3 * (u1c * u1c + u2c * u2c)));
9943 eqDc[2] = F1B9 * (rhoc + F9B2 * ((-u2c) * (-u2c) - F1B3 * (u1c * u1c + u2c * u2c)));
9944 eqDc[3] = F1B9 * (rhoc + F9B2 * ((u2c) * (u2c)-F1B3 * (u1c * u1c + u2c * u2c)));
9945 eqDc[4] = F1B36 * (rhoc + F9B2 * ((u1c + u2c) * (u1c + u2c) - F1B3 * (u1c * u1c + u2c * u2c)));
9946 eqDc[5] = F1B36 * (rhoc + F9B2 * ((u1c - u2c) * (u1c - u2c) - F1B3 * (u1c * u1c + u2c * u2c)));
9947 eqDc[6] = F1B36 * (rhoc + F9B2 * ((-u1c - u2c) * (-u1c - u2c) - F1B3 * (u1c * u1c + u2c * u2c)));
9948 eqDc[7] = F1B36 * (rhoc + F9B2 * ((-u1c + u2c) * (-u1c + u2c) - F1B3 * (u1c * u1c + u2c * u2c)));
9949
9950 // symmetric distribution function
9951 // Calculate average for opposite directions
9952 for(MInt j = 0; j < 8; j++) {
9953 meanDist[j] = F1B2
9954 * (m_solver->a_oldDistribution(currentID, j)
9955 + m_solver->a_oldDistribution(currentID, Ld::oppositeDist(j)));
9956 }
9957
9958 for(MInt j = 0; j < 8; j++) {
9959 if(m_solver->a_hasNeighbor(currentID, j) == 0)
9960 m_solver->a_oldDistribution(currentID, Ld::oppositeDist(j)) =
9961 -m_solver->a_distribution(currentID, j) + 2 * eqD[j] + (2 - m_omega) * (meanDist[j] - eqDc[j]);
9962 }
9963
9964 // only for bounce back in corners!!! (upper and lower boundary need to be walls)
9965 if(m_solver->a_hasNeighbor(currentID, 3) == 0) {
9966 m_solver->a_oldDistribution(currentID, 6) = m_solver->a_distribution(currentID, 4);
9967 m_solver->a_oldDistribution(currentID, 2) = m_solver->a_distribution(currentID, 3);
9968 m_solver->a_oldDistribution(currentID, 5) = m_solver->a_distribution(currentID, 7);
9969 }
9970
9971 if(m_solver->a_hasNeighbor(currentID, 2) == 0) {
9972 m_solver->a_oldDistribution(currentID, 4) = m_solver->a_distribution(currentID, 6);
9973 m_solver->a_oldDistribution(currentID, 3) = m_solver->a_distribution(currentID, 2);
9974 m_solver->a_oldDistribution(currentID, 7) = m_solver->a_distribution(currentID, 5);
9975 }
9976
9977 // set new boundary variables
9978 formerVariables[iMo + 0] = u1;
9979 formerVariables[iMo + 1] = u2;
9980 formerVariables[iMo + 2] = rho;
9981 }
9982 }
9983 }
9984}
9985
9992template <MInt nDim, MInt nDist, class SysEqn>
9994 TRACE();
9995
9996 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
9997 const MInt currentId = m_bndCells[i].m_cellId;
9998 if((globalTimeStep - 1) % IPOW2(m_solver->maxLevel() - m_solver->a_level(currentId)) != 0
9999 || m_solver->a_isHalo(currentId)) {
10000 continue;
10001 }
10002 //--LODI calculation--------------------------------------------------------
10003 MFloat rho_b, u_b[nDim];
10004 calcCharValues<2>(index, currentId, rho_b, &u_b[0]);
10005 //--set equilibrium state---------------------------------------------------
10006#ifdef WAR_NVHPC_PSTL
10007 MFloat oldDist[nDist] = {F0};
10008 for(MInt d = 0; d < nDist; d++) {
10009 oldDist[d] = m_solver->a_oldDistribution(currentId, d);
10010 }
10011 sysEqn().calcEqDists(rho_b, &u_b[0], oldDist);
10012 for(MInt d = 0; d < nDist; d++) {
10013 m_solver->a_oldDistribution(currentId, d) = oldDist[d];
10014 }
10015#else
10016 sysEqn().calcEqDists(rho_b, &u_b[0], &m_solver->a_oldDistribution(currentId, 0));
10017#endif
10018 }
10019}
10020
10028template <MInt nDim, MInt nDist, class SysEqn>
10029inline void LbBndCndDxQy<nDim, nDist, SysEqn>::charPressure2(MInt index, MInt direction) {
10030 TRACE();
10031
10032 MFloat L1, L2, L3, L4, L5;
10033 MFloat rho, u[3] = {0.0, 0.0, 0.0}, rho_b;
10034 MFloat nghbr_rho, nghbr_u[3] = {0.0, 0.0, 0.0};
10035
10036 MFloat deltaT = 1;
10037 MFloat sigma = 0.59;
10038
10039 MFloat tmp, tmp2, b[6];
10040 MInt tmpDistId;
10041
10042 MFloat kappa = 1.2;
10043 MFloat sqrtKappa = sqrt(1.2); // speed of sound c_s = sqrt(kappa*p/rho) where kappa = 1.2 is used at the boundary
10044
10045 MInt currentID, nghbrID;
10046
10047 MFloat k_relax; // relaxation factor
10048
10049 MFloat der_u, der_p, der_v, der_w;
10050
10051 MFloat rho_offset;
10052
10053 MFloatScratchSpace nePart(nDist - 1, AT_, "nePart");
10054 MFloatScratchSpace eDistributions(nDist - 1, AT_, "eDistributions");
10055
10056 // MInt ind = m_mapSegIdsInOutCnd[index];
10057
10058 L1 = F0;
10059 L2 = F0;
10060 L3 = F0;
10061 L4 = F0;
10062 L5 = F0;
10063
10064 for(MInt i = m_bndCndOffsets[index]; i < m_bndCndOffsets[index + 1]; i++) {
10065 currentID = m_bndCells[i].m_cellId;
10066 nghbrID = m_solver->c_neighborId(currentID, Ld::oppositeDist(direction));
10067
10068 k_relax = sigma * (1 - m_Ma * m_Ma) * sqrtKappa * LBCS
10069 / (m_domainLength
10070 * FFPOW2(m_solver->maxLevel()
10071 - m_solver->a_level(currentID))); // m_domainLength represents the distance between the inlet
10072 // and the outlet on the highest level of refinement
10073
10074 m_omega =
10075 2.0 / (1.0 + 6.0 * m_solver->a_nu(currentID) * FFPOW2(m_solver->maxLevel() - m_solver->a_level(currentID)));
10076
10077 if(m_densityFluctuations) {
10078 rho_b = 0.0;
10079 rho_offset = 1.0;
10080 } else {
10081 rho_b = 1.0;
10082 rho_offset = 0.0;
10083 }
10084
10085 nghbr_rho = m_solver->a_variable(nghbrID, PV->RHO);
10086 nghbr_u[0] = m_solver->a_variable(nghbrID, PV->U);
10087 nghbr_u[1] = m_solver->a_variable(nghbrID, PV->V);
10088 nghbr_u[2] = m_solver->a_variable(nghbrID, PV->W);
10089
10090 // determine non-equilibrium components in neighbor cell
10091 // -------------------------------------------------------
10092 tmp2 = nghbr_u[0] * nghbr_u[0] + nghbr_u[1] * nghbr_u[1] + nghbr_u[2] * nghbr_u[2];
10093
10094 b[0] = -u[0];
10095 b[1] = u[0];
10096 b[2] = -u[1];
10097 b[3] = u[1];
10098 b[4] = -u[2];
10099 b[5] = u[2];
10100
10101 // Calculation of eq-distributions for directions with only one component
10102 for(MInt j = 0; j < Ld::distFld(0); j++) {
10103 eDistributions[j] = Ld::tp(1) * F1BCSsq * (nghbr_rho * CSsq + b[j] + b[j] * b[j] * F1BCSsq * F1B2 - tmp2 * F1B2);
10104 }
10105 // Calculation of eq-distributions for directions with two components
10106 tmpDistId = Ld::distFld(0);
10107 for(MInt j = 0; j < Ld::distFld(1); j++) {
10108 tmp = (b[Ld::mFld1(2 * j)] + b[Ld::mFld1(2 * j + 1)]);
10109 eDistributions[tmpDistId + j] =
10110 Ld::tp(2) * F1BCSsq * (nghbr_rho * CSsq + tmp + tmp * tmp * F1BCSsq * F1B2 - tmp2 * F1B2);
10111 }
10112 // Calculation of eq-distributions for directions with three components
10113 tmpDistId = Ld::distFld(0) + Ld::distFld(1);
10114 for(MInt j = 0; j < Ld::distFld(2); j++) {
10115 tmp = (b[Ld::mFld2(3 * j)] + b[Ld::mFld2(3 * j + 1)] + b[Ld::mFld2(3 * j + 2)]);
10116 eDistributions[tmpDistId + j] =
10117 Ld::tp(3) * F1BCSsq * (nghbr_rho * CSsq + tmp + tmp * tmp * F1BCSsq * F1B2 - tmp2 * F1B2);
10118 }
10119 // Determine non-eq parts
10120 for(MInt j = 0; j < nDist - 1; j++) {
10121 nePart[j] = m_solver->a_distribution(currentID, j) - eDistributions[j];
10122 }
10123
10124 // Introduce characteristics:
10125 // ------------------------------------------
10126 // compute derivatives:
10127 der_p = CSsq * (m_solver->a_oldVariable(currentID, PV->RHO) - m_solver->a_oldVariable(nghbrID, PV->RHO));
10128 der_u = (m_solver->a_oldVariable(currentID, PV->U) - m_solver->a_oldVariable(nghbrID, PV->U));
10129 der_v = (m_solver->a_oldVariable(currentID, PV->V) - m_solver->a_oldVariable(nghbrID, PV->V));
10130 der_w = (m_solver->a_oldVariable(currentID, PV->W) - m_solver->a_oldVariable(nghbrID, PV->W));
10131
10132 switch(direction) {
10133 case 0:
10134 // negative derivatives due to negative direction!
10135 L1 = (m_solver->a_oldVariable(currentID, PV->U) - sqrtKappa * LBCS)
10136 * (-der_p - (m_solver->a_oldVariable(currentID, PV->RHO) + rho_offset) * (-der_u) * sqrtKappa * LBCS);
10137 L2 = k_relax * F1B3 * (m_solver->a_oldVariable(currentID, PV->RHO) - rho_b);
10138 L3 = L2;
10139 L4 = L2;
10140 L5 = L2;
10141
10142 // compute u1,u2,u3 using the characteristic wave amplitudes
10143 u[0] = m_solver->a_oldVariable(currentID, PV->U)
10144 - deltaT * F1B2 / (m_solver->a_oldVariable(currentID, PV->RHO) + rho_offset) * F1BCS / sqrtKappa
10145 * (L5 - L1);
10146 u[1] = m_solver->a_oldVariable(currentID, PV->V) - deltaT * L3;
10147 u[2] = m_solver->a_oldVariable(currentID, PV->W) - deltaT * L4;
10148 break;
10149
10150 case 1:
10151 L1 = k_relax * F1B3 * (m_solver->a_oldVariable(currentID, PV->RHO) - rho_b);
10152 L2 = 0;
10153 L3 = m_solver->a_oldVariable(currentID, PV->U) * der_v;
10154 L4 = m_solver->a_oldVariable(currentID, PV->U) * der_w;
10155 L5 = (m_solver->a_oldVariable(currentID, PV->U) + sqrtKappa * LBCS)
10156 * (der_p + (m_solver->a_oldVariable(currentID, PV->RHO) + rho_offset) * der_u * sqrtKappa * LBCS);
10157
10158 // compute u1,u2,u3 using the characteristic wave amplitudes
10159 u[0] = m_solver->a_oldVariable(currentID, PV->U)
10160 - deltaT * F1B2 / (m_solver->a_oldVariable(currentID, PV->RHO) + rho_offset) * F1BCS / sqrtKappa
10161 * (L5 - L1);
10162 u[1] = m_solver->a_oldVariable(currentID, PV->V) - deltaT * L3;
10163 u[2] = m_solver->a_oldVariable(currentID, PV->W) - deltaT * L4;
10164 break;
10165
10166 case 2:
10167 // negative derivatives due to negative direction!
10168 L1 = (m_solver->a_oldVariable(currentID, PV->V) - sqrtKappa * LBCS)
10169 * (-der_p - (m_solver->a_oldVariable(currentID, PV->RHO) + rho_offset) * (-der_v) * sqrtKappa * LBCS);
10170 L2 = k_relax * F1B3 * (m_solver->a_oldVariable(currentID, PV->RHO) - rho_b);
10171 L3 = L2;
10172 L4 = L2;
10173 L5 = L2;
10174
10175 // compute u1,u2,u3 using the characteristic wave amplitudes
10176 u[0] = m_solver->a_oldVariable(currentID, PV->U) - deltaT * L3;
10177 u[1] = m_solver->a_oldVariable(currentID, PV->V)
10178 - deltaT * F1B2 / (m_solver->a_oldVariable(currentID, PV->RHO) + rho_offset) * F1BCS / sqrtKappa
10179 * (L5 - L1);
10180 u[2] = m_solver->a_oldVariable(currentID, PV->W) - deltaT * L4;
10181 break;
10182
10183 case 3:
10184 L1 = k_relax * F1B3 * (m_solver->a_oldVariable(currentID, PV->RHO) - rho_b);
10185 L2 = 0;
10186 L3 = m_solver->a_oldVariable(currentID, PV->V) * der_u;
10187 L4 = m_solver->a_oldVariable(currentID, PV->V) * der_w;
10188 L5 = (m_solver->a_oldVariable(currentID, PV->V) + sqrtKappa * LBCS)
10189 * (der_p + (m_solver->a_oldVariable(currentID, PV->RHO) + rho_offset) * der_v * sqrtKappa * LBCS);
10190
10191 // compute u1,u2,u3 using the characteristic wave amplitudes
10192 u[0] = m_solver->a_oldVariable(currentID, PV->U) - deltaT * L3;
10193 u[1] = m_solver->a_oldVariable(currentID, PV->V)
10194 - deltaT * F1B2 / (m_solver->a_oldVariable(currentID, PV->RHO) + rho_offset) * F1BCS / sqrtKappa
10195 * (L5 - L1);
10196 u[2] = m_solver->a_oldVariable(currentID, PV->W) - deltaT * L4;
10197 break;
10198
10199 case 4:
10200 // negative derivatives due to negative direction!
10201 L1 = (m_solver->a_oldVariable(currentID, PV->W) - sqrtKappa * LBCS)
10202 * (-der_p - (m_solver->a_oldVariable(currentID, PV->RHO) + rho_offset) * (-der_w) * sqrtKappa * LBCS);
10203 L2 = k_relax * F1B3 * (m_solver->a_oldVariable(currentID, PV->RHO) - rho_b);
10204 L3 = L2;
10205 L4 = L2;
10206 L5 = L2;
10207
10208 // compute u1,u2,u3 using the characteristic wave amplitudes
10209 u[0] = m_solver->a_oldVariable(currentID, PV->U) - deltaT * L3;
10210 u[1] = m_solver->a_oldVariable(currentID, PV->V) - deltaT * L4;
10211 u[2] = m_solver->a_oldVariable(currentID, PV->W)
10212 - deltaT * F1B2 / (m_solver->a_oldVariable(currentID, PV->RHO) + rho_offset) * F1BCS / sqrtKappa
10213 * (L5 - L1);
10214 break;
10215
10216 case 5:
10217 L1 = k_relax * F1B3 * (m_solver->a_oldVariable(currentID, PV->RHO) - rho_b);
10218 L2 = 0;
10219 L3 = m_solver->a_oldVariable(currentID, PV->W) * der_u;
10220 L4 = m_solver->a_oldVariable(currentID, PV->W) * der_v;
10221 L5 = (m_solver->a_oldVariable(currentID, PV->W) + sqrtKappa * LBCS)
10222 * (der_p + (m_solver->a_oldVariable(currentID, PV->RHO) + rho_offset) * der_w * sqrtKappa * LBCS);
10223
10224 // compute u1,u2,u3 using the characteristic wave amplitudes
10225 u[0] = m_solver->a_oldVariable(currentID, PV->U) - deltaT * L3;
10226 u[1] = m_solver->a_oldVariable(currentID, PV->V) - deltaT * L4;
10227 u[2] = m_solver->a_oldVariable(currentID, PV->W)
10228 - deltaT * F1B2 / (m_solver->a_oldVariable(currentID, PV->RHO) + rho_offset) * F1BCS / sqrtKappa
10229 * (L5 - L1);
10230 break;
10231
10232 default:
10233 TERMM(1, "Unrealistic case, please check your Boundary Conditions!!!");
10234 }
10235
10236 // compute rho using the characteristic wave amplitudes
10237 rho = m_solver->a_oldVariable(currentID, PV->RHO) - deltaT * F1B2 / kappa * F1BCSsq * (L5 + L1)
10238 - F1BCSsq * L2 / kappa;
10239
10240 // set new variables in boundary cell
10241 m_solver->a_variable(currentID, PV->U) = u[0];
10242 m_solver->a_variable(currentID, PV->V) = u[1];
10243 m_solver->a_variable(currentID, PV->W) = u[2];
10244 m_solver->a_variable(currentID, PV->RHO) = rho;
10245
10246 // Calculation of missing incoming distributions in bnd cell
10247 //--------------------------------------------
10248
10249 tmp2 = u[0] * u[0] + u[1] * u[1] + u[2] * u[2];
10250
10251 b[0] = -u[0];
10252 b[1] = u[0];
10253 b[2] = -u[1];
10254 b[3] = u[1];
10255 b[4] = -u[2];
10256 b[5] = u[2];
10257
10258 // Calculation of distributions for directions with only one component
10259 for(MInt j = 0; j < Ld::distFld(0); j++) {
10260 if(!m_solver->a_hasNeighbor(currentID, Ld::oppositeDist(j))) {
10261 m_solver->a_oldDistribution(currentID, j) =
10262 Ld::tp(1) * F1BCSsq * (rho * CSsq + b[j] + b[j] * b[j] * F1BCSsq * F1B2 - tmp2 * F1B2)
10263 + ((1 - m_omega) * nePart[j]);
10264 }
10265 }
10266
10267 // Calculation of distributions for directions with two components
10268 tmpDistId = Ld::distFld(0);
10269 for(MInt j = 0; j < Ld::distFld(1); j++) {
10270 if(!m_solver->a_hasNeighbor(currentID, Ld::oppositeDist(tmpDistId + j))) {
10271 tmp = (b[Ld::mFld1(2 * j)] + b[Ld::mFld1(2 * j + 1)]);
10272 m_solver->a_oldDistribution(currentID, tmpDistId + j) =
10273 Ld::tp(2) * F1BCSsq * (rho * CSsq + tmp + tmp * tmp * F1BCSsq * F1B2 - tmp2 * F1B2)
10274 + ((1 - m_omega) * nePart[tmpDistId + j]);
10275 }
10276 }
10277
10278 // Calculation of distributions for directions with three components
10279 tmpDistId = Ld::distFld(0) + Ld::distFld(1);
10280 for(MInt j = 0; j < Ld::distFld(2); j++) {
10281 if(!m_solver->a_hasNeighbor(currentID, Ld::oppositeDist(tmpDistId + j))) {
10282 tmp = (b[Ld::mFld2(3 * j)] + b[Ld::mFld2(3 * j + 1)] + b[Ld::mFld2(3 * j + 2)]);
10283 m_solver->a_oldDistribution(currentID, tmpDistId + j) =
10284 Ld::tp(3) * F1BCSsq * (rho * CSsq + tmp + tmp * tmp * F1BCSsq * F1B2 - tmp2 * F1B2)
10285 + ((1 - m_omega) * nePart[tmpDistId + j]);
10286 }
10287 }
10288 }
10289}
10290
10291template <>
10292inline void LbBndCndDxQy<2, 9, maia::lb::LbSysEqnIncompressible<2, 9>>::charPressure2(MInt /*index*/,
10293 MInt /*direction*/) {
10294 TERMM(1, "This Boundary Condition is not implemented for 2D");
10295}
void mAlloc(T *&a, const MLong N, const MString &objectName, MString function)
allocates memory for one-dimensional array 'a' of size N
Definition: alloc.h:173
MBool mDeallocate(T *&a)
deallocates the memory previously allocated for element 'a'
Definition: alloc.h:544
static MInt propertyLength(const MString &name, MInt solverId=m_noSolvers)
Returns the number of elements of a property.
Definition: context.cpp:538
static MBool propertyExists(const MString &name, MInt solver=m_noSolvers)
This function checks if a property exists in general.
Definition: context.cpp:494
void slidingWall(MInt index)
void heatFluxBc(MInt index, MInt bcMode)
void bc20004(MInt index)
LbBndCndDxQy(LbSolver< nDim > *solver)
void writeBoundaryVTK(MFloat **vertices, MInt num, MInt segmentId)
Writes the rim of a boundary as VTP to file.
void bc20005(MInt index)
void bc10004(MInt index)
void bc20025(MInt index)
void bc10000(MInt index)
void bc20026(MInt index)
void recalcRho(MInt index)
void refillEmergedCell(const MInt)
void bc20000(MInt index)
void parabolicInflow(MInt index)
void bc20002(MInt index)
void extrapolateVelocities(MInt index, const MInt pCellId, MFloat *l_uu)
void bc10080(MInt index)
void bc40046(MInt index)
void bc20030(MInt index)
void bc10046(MInt index)
void bc40120(MInt index)
void bc0(MInt index)
void bc20022(MInt index)
void charPressure2(MInt index, MInt direction)
void bc40020(MInt index)
void bc40000(MInt index)
void slipFlow(MInt index)
void charPressure(MInt index, MInt direction)
void bc40072(MInt index)
void refillEmergedCellNormalExtrapolationLinear(const MInt)
void bc40073(MInt index)
void dnt(MInt index, MInt direction)
void interpolatedBounceBackSingleSpeciesThermal(const MInt cellId, const MFloat *const wTPtr, const MFloat *const uW)
void bc10070(MInt index)
void bc40110(MInt index)
void calculateWallForcesMb(MInt set)
void bc40081(MInt index)
void bc40080(MInt index)
void bc20501_init(MInt index)
void calculateSublayerDistances(MInt index)
MFloat firstMomentSourceTerm(const MFloat *const uW, const MFloat rho, const MInt dist)
Momentum source term used in bounce back schemes for moving boundaries.
void interpolatedBounceBackMb_Bouzidi_qua(const MInt cellIndex, const MInt set)
Interpolated bounce back for moving walls using the quadratic BFL scheme.
void getBoundaryVelocity(const MInt index, MFloat *uW)
Reads boundary velocity from properties.
void bc40082(MInt index)
void getBoundaryVelocityMb(const MInt cellId, MFloat *uW)
Reads boundary velocity from the moving boundary cell collector.
void calculateWallForces(MInt index)
void bc10022(MInt index)
void bc20501(MInt index)
void calculateEqDistsWallSingleSpeciesThermal(const MInt pCellId, MFloat wT)
void outflow(MInt index)
MFloat getBoundaryTemperature(const MInt index)
Reads boundary temperature from properties.
void interpolatedBounceBackMb_Bouzidi_lin_thermal(const MInt cellIndex, const MInt set)
void calculateWallDistances2D()
void bc20220(MInt index)
void calculateWallInterface(MInt cellId, MFloat *wallConc, MFloat *wallTemp)
void bc30000(MInt index)
void extrapolateVariable(const MInt index, const MInt pCellId, const MInt var, MFloat *const p_var)
void interpolatedBounceBackSingleSpecies(const MInt cellId, const MFloat *const uW)
void bc10002(MInt index)
void bc20230(MInt index)
void bc20024(MInt index)
void interpolatedBounceBackMb_Yu_qua(const MInt cellIndex, const MInt set)
Interpolated bounce back for moving walls using the quadratic scheme by Yu et al.
void interpolatedBounceBackSingleSpeciesTransport(const MInt cellId, const MFloat *const wCPtr, const MFloat *const uW)
void bc10020(MInt index)
void bc20020(MInt index)
void bc10111(MInt index)
MFloat zerothMomentSourceTerm(const MInt pCellId, const MInt dist, const MFloat var, const MFloat *uW)
void bc20027(MInt index)
void bc10050(MInt index)
void interpolatedBounceBackMb_Bouzidi_lin_transport(const MInt cellIndex, const MInt set)
void bc40030(MInt index)
void bc40070(MInt index)
void calculateEqDistsWallSingleSpeciesTransport(const MInt pCellId, MFloat wC)
void extrapolateVelocitiesMb()
void bc10060(MInt index)
void bc40130(MInt index)
void bc66666(MInt set)
void calcCharValuesOnFace(const MInt index, const MInt direction, const MInt bndCellId, MFloat &rho_b, MFloat *u_b)
void bc40072_40082_init(MInt index)
void bc10010(MInt index)
void interpolatedBounceBackSingleSpeciesThermalFlux(const MInt cellId, const MFloat qT, MInt bcIndex)
void bc40071(MInt index)
void bc20003(MInt index)
void pab(MInt index)
void bc40100(MInt index)
void calculateEqDistsWallSingleSpecies(const MInt pCellId)
void incrementForces(const MInt cellId, const MInt j, const MFloat *uW, MFloat *forces)
void charVelocity(MInt index, MInt direction)
void interpolatedBounceBackMb_Bouzidi_lin(const MInt cellIndex, const MInt set)
Interpolated bounce back for moving walls using the linear BFL scheme.
void outflowLinear(MInt index)
MFloat ** allOwnersGetBoundaryVertices(MInt segmentId, MInt *num)
Obtain all boundary vertices for a given segmentId.
void interpolatedAntiBounceBackMb_Bouzidi_qua(const MInt cellIndex, const MInt set)
void calculateWallDistances()
calculates the intersection point between the boundary wall and the trajectories of the distribution ...
void addWallDistanceFieldToOutputFile(ParallelIo &parallelIo, const MBool writeHeader, const MBool writeData) override
void refillEmergedCellNormalExtrapolationQuadratic(const MInt)
virtual ~LbBndCndDxQy()
Destructor for the nDim/nDist specific boundary condition.
void calcCharValues(const MInt index, const MInt bndCellId, MFloat &rho_b, MFloat *u_b)
void writeBCOutput(MInt index)
void bc10001(MInt index)
void bc20001(MInt index)
void bc20023(MInt index)
void bcIBBNeumannInit(MInt index)
void bc66668(MInt set)
void bc10061(MInt index)
This class represents all LB models.
Definition: lbsolverdxqy.h:29
void setEqDists(const MInt cellId, const MFloat rho, const MFloat *const velocity)
Set BOTH distributions to equilibrium.
Definition: lbsolverdxqy.h:401
This class is a ScratchSpace.
Definition: scratch.h:758
T * getPointer() const
Deprecated: use begin() instead!
Definition: scratch.h:316
MInt string2enum(MString theString)
This global function translates strings in their corresponding enum values (integer values)....
Definition: enums.cpp:20
@ no
Definition: enums.h:208
@ BOUZIDI_QUADRATIC
Definition: enums.h:428
@ YU_QUADRATIC
Definition: enums.h:428
@ BOUZIDI_LINEAR
Definition: enums.h:428
MBool approx(const T &, const U &, const T)
Definition: functions.h:272
ATTRIBUTES1(ATTRIBUTE_NO_AUTOVEC) void FvCartesianSolverXD< nDim_
resets the solver
MInt globalTimeStep
InfoOutFile m_log
std::vector< std::pair< MString, MInt > > g_tc_geometry
constexpr MLong IPOW2(MInt x)
constexpr MFloat FPOW2(MInt x)
constexpr MFloat FFPOW2(MInt x)
int32_t MInt
Definition: maiatypes.h:62
uint32_t MUint
Definition: maiatypes.h:63
std::basic_string< char > MString
Definition: maiatypes.h:55
double MFloat
Definition: maiatypes.h:52
bool MBool
Definition: maiatypes.h:58
MInt id
Definition: maiatypes.h:71
int MPI_Allgatherv(const void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, const int recvcounts[], const int displs[], MPI_Datatype recvtype, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Allgatherv
int MPI_Reduce(const void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Reduce
int MPI_Allreduce(const void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Allreduce
int MPI_Allgather(const void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Allgather
constexpr MInt distType[Q]
const MFloat lb_gamma2
Definition: lbconstants.h:30
void const MInt cellId
Definition: collector.h:239
constexpr std::underlying_type< FcCell >::type p(const FcCell property)
Converts property name to underlying integer value.
T linear(const T a, const T b, const T x)
Linear slope filter.
Definition: filter.h:83
T cos(const T a, const T b, const T x)
Cosine slope filter.
Definition: filter.h:125
IdType index(const FloatType *const x, const IdType level)
Return Hilbert index for given location and level in 2D or 3D.
Definition: hilbert.h:165
const MInt PIO_REPLACE
Definition: parallelio.h:36
const MInt PIO_INT
Definition: parallelio.h:48
const MInt PIO_FLOAT
Definition: parallelio.h:46
void parallelFor(MInt begin, MInt end, UnaryFunction &&f)
Wrapper function for parallel for loop.
Definition: parallelfor.h:147
PARALLELIO_DEFAULT_BACKEND ParallelIo
Definition: parallelio.h:292
MFloat dist(const Point< DIM > &p, const Point< DIM > &q)
Definition: pointbox.h:54
This class contains the necessary data to define a boundary cell for the LB method.
Definition: contexttypes.h:19
define array structures