MAIA bb96820c
Multiphysics at AIA
Loading...
Searching...
No Matches
lbsolver.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 "lbsolver.h"
8
9#include <cstring>
10#include <functional>
11#include <unordered_map>
12#include <utility>
13#include "COMM/mpioverride.h"
15#include "IO/logtable.h"
16#include "IO/parallelio.h"
17#include "UTIL/maiamath.h"
18#include "UTIL/parallelfor.h"
19#include "globals.h"
20#include "lbbndcnd.h"
21#include "lbconstants.h"
22#include "lbfunctions.h"
23#include "lbgridboundarycell.h"
24#include "lbinterfacecell.h"
25#include "lblatticedescriptor.h"
26#include "lbparentcell.h"
27
28using namespace std::placeholders;
29using namespace std;
30
31// the swap endian directive uses changes the endian for the binary VTK output
32#define SWAP_ENDIAN
33
34using namespace lbconstants;
35
36template <MInt nDim>
37LbSolver<nDim>::LbSolver(MInt id, MInt dist, GridProxy& gridProxy_, Geometry<nDim>& geometry_, const MPI_Comm comm)
38 : maia::CartesianSolver<nDim, LbSolver<nDim>>(id, gridProxy_, comm, true)
39
48 ,
49 m_geometry(&geometry_),
51
52 m_noDistributions(dist) {
53 TRACE();
54
55 initTimer();
56 RECORD_TIMER_START(m_t.solver);
57
58 m_geometryIntersection = new GeometryIntersection<nDim>(&grid(), m_geometry);
59
60 auto solverMethodEnum = string2enum(solverMethod());
61 if(solverMethodEnum == MAIA_LATTICE_BGK_THERMAL || solverMethodEnum == MAIA_LATTICE_BGK_INNERENERGY
62 || solverMethodEnum == MAIA_LATTICE_BGK_TOTALENERGY || solverMethodEnum == MAIA_LATTICE_BGK_THERMAL_TRANSPORT
63 || solverMethodEnum == MAIA_LATTICE_BGK_INNERENERGY_TRANSPORT
64 || solverMethodEnum == MAIA_LATTICE_BGK_TOTALENERGY_TRANSPORT) {
65 m_cells.setThermal(true);
66 m_isThermal = 1;
67 } else {
68 m_cells.setThermal(false);
69 }
70 if(solverMethodEnum == MAIA_LATTICE_BGK_TRANSPORT || solverMethodEnum == MAIA_LATTICE_BGK_THERMAL_TRANSPORT
71 || solverMethodEnum == MAIA_LATTICE_BGK_INNERENERGY_TRANSPORT
72 || solverMethodEnum == MAIA_LATTICE_BGK_TOTALENERGY_TRANSPORT) {
73 m_cells.setTransport(true);
74 m_isTransport = 1;
75 } else {
76 m_cells.setTransport(false);
77 }
78 if(solverMethodEnum == MAIA_LATTICE_BGK_INNERENERGY || solverMethodEnum == MAIA_LATTICE_BGK_INNERENERGY_TRANSPORT) {
79 m_innerEnergy = 1;
80 } else if(solverMethodEnum == MAIA_LATTICE_BGK_TOTALENERGY
81 || solverMethodEnum == MAIA_LATTICE_BGK_TOTALENERGY_TRANSPORT) {
82 m_totalEnergy = 1;
83 }
84
85 // update number Variables
86 m_cells.setNoVariables();
87
95 m_isEELiquid = false;
96 m_isEELiquid = Context::getSolverProperty<MBool>("EELiquid", m_solverId, AT_, &m_isEELiquid);
97 if(m_isEELiquid) {
98 MInt l_savePrevVars = 0;
99 l_savePrevVars = Context::getBasicProperty<MInt>("alphaConvergenceCheck", AT_, &l_savePrevVars);
100 m_cells.setSaveUOtherPhase(true);
101 m_cells.setSaveVolumeFraction(true);
102 m_cells.setSavePrevVars(l_savePrevVars > 0);
103 m_cells.setSaveNuT(true);
104 m_cells.setSaveOldNu(true);
105
113 m_EELiquid.restartWithoutAlpha = false;
114 m_EELiquid.restartWithoutAlpha =
115 Context::getSolverProperty<MBool>("LBRestartWithoutAlpha", m_solverId, AT_, &m_EELiquid.restartWithoutAlpha);
116 }
117
118 if(Context::propertyExists("nonNewtonianModel", m_solverId)) {
119 m_cells.setSaveOldNu(true);
120 }
121
122 {
123 MBool updateAfterPropagation = false;
124 if(m_isEELiquid) {
125 updateAfterPropagation = true;
126 }
127
135 updateAfterPropagation =
136 Context::getSolverProperty<MBool>("updateAfterPropagation", m_solverId, AT_, &updateAfterPropagation);
137 if(updateAfterPropagation) {
138 if(solverMethodEnum != MAIA_LATTICE_CUMULANT) {
139 TERMM(1, "For now only implemented for the cumulant collision step!"); // TODO labels:LB,DOC daniell
140 } else {
141 m_updateMacroscopicLocation = POSTPROPAGATION;
142 }
143 }
144 }
145
146 m_cells.setNoDistributions(m_noDistributions);
147
148 // Instead of noCells() grid().tree().capacity() was used in m_cells.reset() before;
149 // it was changed because there was no difference at that time and to avoid direct access of the gird()
150 // m_cells.reset(grid().noCells());
151 m_cells.reset(grid().raw().treeb().capacity());
152 m_cells.append(grid().noCells());
153
154 // Update cell collector with info from grid
155 updateCellCollectorFromGrid();
156
157 grid().findEqualLevelNeighborsParDiagonal(false);
158
159 // Initialize status flags for adaptation
160 m_adaptationSinceLastRestart = false;
161 m_adaptationSinceLastSolution = false;
162
163 // Allocate memory for recalcIds if adaptation is used
171 m_adaptation = false;
172 m_adaptation = Context::getSolverProperty<MBool>("adaptation", m_solverId, AT_, &m_adaptation);
173
174 if(m_adaptation) {
175 mAlloc(this->m_recalcIds, maxNoGridCells(), "m_recalcIds", -1, AT_);
176 for(MInt i = 0; i < maxNoGridCells(); i++) {
177 this->m_recalcIds[i] = i;
178 }
179 } else {
180 this->m_recalcIds = nullptr;
181 }
182
183 // Read property cards for lb adaptation
184 if(m_adaptation) {
192 this->m_singleAdaptation = true;
193 this->m_singleAdaptation =
194 Context::getSolverProperty<MBool>("singleAdaptation", m_solverId, AT_, &this->m_singleAdaptation);
195
203 m_adaptationInitMethod = "INIT_FILIPPOVA";
204 m_adaptationInitMethod =
205 Context::getSolverProperty<MString>("adaptationInitMethod", m_solverId, AT_, &m_adaptationInitMethod);
206
207 if(!domainId()) {
208 cerr << "adaptation initialization method is: " << m_adaptationInitMethod << endl;
209 }
210 }
211
212 // Initialize grid file name and path
213 m_reinitFileName = this->grid().gridInputFileName().c_str();
214 m_reinitFilePath = outputDir() + m_reinitFileName;
215
223 MInt noSpecies = 0;
224 noSpecies = Context::getSolverProperty<MInt>("noSpecies", m_solverId, AT_, &noSpecies);
225
226 CV = new MConservativeVariables<nDim>(noSpecies);
227 PV = new MPrimitiveVariables<nDim>(noSpecies);
228
229 // Set number of variables
230 // TODO: Do avoid a linear combination of different implemented methods
231 // we should introduce sys equations at some point. In a first step
232 // maybe it makes sense to transform m_istTransport into an enum which
233 // holds value for 'thermal', 'humidity', or 'whateverInFutureMayAppear'
234 // (pre-step for sysEqn like class)
235 if(m_isTransport && !m_isThermal) {
236 m_noVariables = nDim + 1 + 2 * m_isTransport;
237 } else {
238 m_noVariables = nDim + 1 + m_isThermal + m_isTransport;
239 }
240
241 m_isRefined = (grid().maxUniformRefinementLevel() < maxLevel());
242
243 MString fileName;
244
245 RECORD_TIMER_START(m_t.initSolver);
246 m_log << endl;
247 m_log << "#########################################################################################################"
248 "############"
249 << endl;
250 m_log << "## Initializing LB solver "
251 " ##"
252 << endl;
253 m_log << "#########################################################################################################"
254 "############"
255 << endl;
256
268 m_nonBlockingComm = false;
269
270 m_nonBlockingComm = Context::getSolverProperty<MBool>("nonBlockingComm", m_solverId, AT_, &m_nonBlockingComm);
271
272 // Starting with flow solution
273 switch(dist) {
274 case 9:
275 break;
276 case 15:
277 break;
278 case 19:
279 break;
280 case 27:
281 break;
282 default:
283 stringstream errorMessage;
284 errorMessage << " LbSolver::() Invalid no of distributions : " << dist << " Exiting...";
285 TERMM(1, errorMessage.str());
286 }
287
288 if(grid().isActive()) {
289 // list of active cells
290 // m_activeCellList = new MInt[grid().noCells()];
291 mAlloc(m_activeCellList, grid().noCells(), "m_activeCellList", AT_);
292 }
293
305 m_solutionOffset = 0;
306 m_solutionOffset = Context::getSolverProperty<MInt>("solutionOffset", m_solverId, AT_, &m_solutionOffset);
307
339 m_initMethod = Context::getSolverProperty<MString>("initMethod", m_solverId, AT_);
354 // 1. read as string
355 MString interpolationType = "LINEAR_INTERPOLATION";
356
357 interpolationType = Context::getSolverProperty<MString>("interpolationType", m_solverId, AT_, &interpolationType);
358
359 // 2. recast as LbInterpolationType
360 m_interpolationType = (LbInterpolationType)string2enum(interpolationType);
361
376 m_CouettePoiseuilleRatio = F0;
377 m_CouettePoiseuilleRatio =
378 Context::getSolverProperty<MFloat>("CouettePoiseuilleRatio", m_solverId, AT_, &m_CouettePoiseuilleRatio);
379
392 m_calcTotalPressureGradient = 0;
393 m_calcTotalPressureGradient =
394 Context::getSolverProperty<MInt>("calcTotalPressureGradient", m_solverId, AT_, &m_calcTotalPressureGradient);
395
410 m_densityFluctuations = false;
411 m_densityFluctuations =
412 Context::getSolverProperty<MBool>("densityFluctuations", m_solverId, AT_, &m_densityFluctuations);
413
428 m_calculateDissipation = false;
429 m_calculateDissipation =
430 Context::getSolverProperty<MBool>("calculateDissipation", m_solverId, AT_, &m_calculateDissipation);
431
444 m_FftInit = false;
445 m_FftInit = Context::getSolverProperty<MBool>("FFTInit", m_solverId, AT_, &m_FftInit);
446
447 m_fftInterval =
448 Context::propertyExists("fftInterval", 0) ? Context::getSolverProperty<MInt>("fftInterval", m_solverId, AT_) : 0;
449
462 for(MInt dir = 0; dir < nDim; dir++) {
463 m_arraySize[dir] = 2;
464 m_arraySize[dir] = Context::getSolverProperty<MInt>("arraySize", m_solverId, AT_, &m_arraySize[dir], dir);
465 }
466
475 m_noPeakModes = 1;
476 m_noPeakModes = Context::getSolverProperty<MInt>("m_noPeakModes", m_solverId, AT_, &m_noPeakModes);
477
478 if(m_FftInit && m_noPeakModes == 0) {
479 stringstream errorMessage;
480 errorMessage << " noPeakModes was not defined for FFTInit, exiting";
481 TERMM(1, errorMessage.str());
482 }
483
484 // ------------------------------------
485 // subgrid properties
486 // ------------------------------------
487
495 m_Cs = 0.1;
496 m_Cs = Context::getSolverProperty<MFloat>("smagorinskyConstant", m_solverId, AT_, &m_Cs);
497
505 m_deltaX = 1.0;
506 m_deltaX = Context::getSolverProperty<MFloat>("filterWidth", m_solverId, AT_, &m_deltaX);
507
508 // LES related fields
509 if(solverMethodEnum == MAIA_LATTICE_BGKI_SMAGORINSKY || solverMethodEnum == MAIA_LATTICE_BGKI_SMAGORINSKY2
510 || solverMethodEnum == MAIA_LATTICE_BGKI_SMAGO_WALL || solverMethodEnum == MAIA_LATTICE_BGKI_DYNAMIC_SMAGO
511 || solverMethodEnum == MAIA_LATTICE_BGK_INIT || solverMethodEnum == MAIA_LATTICE_RBGK_SMAGORINSKY
512 || solverMethodEnum == MAIA_LATTICE_RBGK_DYNAMIC_SMAGO || solverMethodEnum == MAIA_LATTICE_BGK) {
513 // Allocate space for momentum flux tensor
514 mAlloc(m_momentumFlux, a_noCells(), nDim * nDim, "m_momentumFlux", F0, AT_);
515 // Allocate space for SGS tensors
516 mAlloc(m_MijMij, a_noCells(), "m_MijMij", F0, AT_);
517 mAlloc(m_MijLij, a_noCells(), "m_MijLij", F0, AT_);
518 }
519
520 //------------------------------------
521 // Thermal properties
522 //------------------------------------
523
536 m_Pr = 0.72;
537 m_Pr = Context::getSolverProperty<MFloat>("Pr", m_solverId, AT_, &m_Pr);
538
547 m_initTemperatureKelvin = 1.0546;
548 m_initTemperatureKelvin =
549 Context::getSolverProperty<MFloat>("initTemperatureKelvin", m_solverId, AT_, &m_initTemperatureKelvin);
550
560 m_blasiusPos = 0.1;
561 m_blasiusPos = Context::getSolverProperty<MFloat>("blasiusPos", m_solverId, AT_, &m_blasiusPos);
562
563 //------------------------------------
564 // Transport properties
565 //------------------------------------
566
575 m_Pe = 100;
576 m_Pe = Context::getSolverProperty<MFloat>("Pe", m_solverId, AT_, &m_Pe);
577
586 m_initCon = 1.0;
587 m_initCon = Context::getSolverProperty<MFloat>("initCon", m_solverId, AT_, &m_initCon);
588
589 //------------------------------------
590
599 m_alpha = 0.0;
600 m_alpha = Context::getSolverProperty<MFloat>("alpha", m_solverId, AT_, &m_alpha);
601
610 m_saveDerivatives = false;
611 if(Context::propertyExists("saveDerivatives", m_solverId))
612 m_saveDerivatives = Context::getSolverProperty<MBool>("saveDerivatives", m_solverId, AT_);
613
614 // ------------------------------------
615
623 m_tanhInit = false;
624 m_tanhInit = Context::getSolverProperty<MBool>("tanhInit", m_solverId, AT_, &m_tanhInit);
625
626 if(m_tanhInit) {
627 // the follwoing must be defined, otherwise exit with error
628
635 m_initRe = Context::getSolverProperty<MFloat>("initRe", m_solverId, AT_);
636
643 m_initTime = Context::getSolverProperty<MInt>("initTime", m_solverId, AT_);
644
651 m_initStartTime = Context::getSolverProperty<MInt>("initStartTime", m_solverId, AT_);
652 }
653
654 // ------------------------------------
655
664 m_Ma = 0.1;
665 m_Ma = Context::getSolverProperty<MFloat>("Ma", m_solverId, AT_, &m_Ma);
666
676 m_Re = 100.0;
677 m_Re = Context::getSolverProperty<MFloat>("Re", m_solverId, AT_, &m_Re);
678
679 if(Context::propertyExists("ReTau", m_solverId)) {
680 m_ReTau = Context::getSolverProperty<MFloat>("ReTau", m_solverId, AT_);
681 } else {
682 m_Re = Context::getSolverProperty<MFloat>("Re", m_solverId, AT_);
683 }
684
692 m_rho1 = 1.0;
693 m_rho1 = Context::getSolverProperty<MFloat>("rho1", m_solverId, AT_, &m_rho1);
694
702 m_rho2 = 1.0;
703 m_rho2 = Context::getSolverProperty<MFloat>("rho2", m_solverId, AT_, &m_rho2);
704
705 // ------------------------------------
706
707 // Calculate cell lengths
708 if(grid().isActive()) {
709 mAlloc(m_cellLength, maxLevel() + 1, "m_cellLength", -F1, AT_);
710 for(MInt level = 0; level < maxLevel() + 1; level++) {
711 m_cellLength[level] = c_cellLengthAtLevel(level);
712 }
713 }
714
715 {
716 // Sanity check for current implementation
717 /*if(isActive() && (grid().maxRefinementLevel() != maxLevel())) {
718 TERMM(1,
719 "WARNING: Local max level and possible "
720 "maxRefinementLevel from the property file are not consistent.");
721 }*/
722 // reference lengths
723 MInt chk = 0;
724 stringstream ss1;
725 if(Context::propertyExists("referenceLength", m_solverId)) {
733 const MFloat referenceLengthSTL =
734 Context::getSolverProperty<MFloat>("referenceLength", m_solverId, AT_, &referenceLengthSTL);
735 const MFloat lengthMaxRefinementLvl = c_cellLengthAtLevel(grid().maxRefinementLevel());
736 m_referenceLengthSTL = referenceLengthSTL;
737 m_referenceLength = referenceLengthSTL / lengthMaxRefinementLvl;
738 ss1 << "referenceLength ";
739 chk++;
740 }
741 if(Context::propertyExists("referenceLengthLB", m_solverId)) {
749 m_referenceLength = Context::getSolverProperty<MFloat>("referenceLengthLB", m_solverId, AT_);
750 ss1 << "referenceLengthLB ";
751 chk++;
752 }
753 if(Context::propertyExists("referenceLengthSegId", m_solverId)) {
761 m_referenceLengthSegId = Context::getSolverProperty<MInt>("referenceLengthSegId", m_solverId, AT_);
762 m_referenceLength = calculateReferenceLength(m_referenceLengthSegId);
763 ss1 << "referenceLengthSegId ";
764 chk++;
765 }
766 if(chk == 0) {
767 stringstream ss2;
768 ss2 << "One of the following properties must be given: ";
769 ss2 << "referenceLength referenceLengthLB referenceLengthSegId";
770 TERMM(1, ss2.str());
771 } else if(chk != 1) {
772 stringstream ss2;
773 ss2 << "Redundant conflicting input properties: ";
774 ss2 << ss1.str();
775 TERMM(1, ss2.str());
776 }
777
778 if(grid().isActive()) {
779 chk = 0;
780 m_domainLength = FPOW2(maxLevel()) / reductionFactor();
781 if(Context::propertyExists("domainLength", m_solverId)) {
789 const MFloat domainLengthSTL =
790 Context::getSolverProperty<MFloat>("domainLength", m_solverId, AT_, &domainLengthSTL);
791 const MFloat lengthMaxRefinementLvl = c_cellLengthAtLevel(grid().maxRefinementLevel());
792 m_domainLength = domainLengthSTL / lengthMaxRefinementLvl;
793 chk++;
794 }
795 if(Context::propertyExists("domainLengthLB", m_solverId)) {
803 m_domainLength = Context::getSolverProperty<MFloat>("domainLengthLB", m_solverId, AT_, &m_domainLength);
804 chk++;
805 }
806 if(chk > 1) {
807 TERMM(1, "Redundant conflicting input properties: domainLength, domainLengthLB");
808 }
809 }
810 }
811
812 // if external forcing is turned off, the forcing terms are set to zero
813 mAlloc(m_Fext, m_noDistributions, "m_Fext", F0, AT_);
814
815 mAlloc(m_EELiquid.Fg, m_noDistributions, "m_EELiquid.Fg", F0, AT_);
816
817 for(MInt mi = 0; mi < m_noDistributions; mi++) {
818 m_EELiquid.Fg[mi] = F0;
819 }
820
821 if(m_isEELiquid) {
822 if(!Context::propertyExists("EELiquidGravity", m_solverId)
823 || !Context::propertyExists("EELiquidGravityAccel", m_solverId))
824 TERMM(1, "Missing property EELiquidGravity or EELiquidGravityAccel!");
825
833 m_EELiquid.initialAlpha = 0.0;
834 m_EELiquid.initialAlpha =
835 Context::getSolverProperty<MFloat>("initialAlpha", m_solverId, AT_, &m_EELiquid.initialAlpha);
836
844 m_EELiquid.alphaInf = m_EELiquid.initialAlpha;
845 m_EELiquid.alphaInf = Context::getSolverProperty<MFloat>("alphaInf", m_solverId, AT_, &m_EELiquid.alphaInf);
846
847 if(domainId() == 0) cerr << "LB Solver EELiquid!" << endl;
848 }
849
857 m_EELiquid.gravity = false;
858 m_EELiquid.gravity = Context::getSolverProperty<MBool>("EELiquidGravity", m_solverId, AT_, &m_EELiquid.gravity);
859
867 m_initDensityGradient = false;
868 m_initDensityGradient =
869 Context::getSolverProperty<MBool>("initDensityGradient", m_solverId, AT_, &m_initDensityGradient);
870
871 if(m_EELiquid.gravity) {
872 if(!Context::propertyExists("EELiquidGravityAccel", m_solverId)) {
873 TERMM(1, "Missing property EELiquidGravityAccel!");
874 }
875 for(MInt i = 0; i < nDim; i++) {
884 m_EELiquid.gravityAccelM[i] = 0.0;
885 m_EELiquid.gravityAccelM[i] = Context::getSolverProperty<MFloat>("EELiquidGravityAccel", m_solverId, AT_, i);
886 }
887 }
888
889 m_externalForcing = false;
890 if(Context::propertyExists("volumeAcceleration", m_solverId) || Context::propertyExists("Ga", m_solverId)) {
891 m_externalForcing = true;
892
893 if(Context::propertyExists("volumeAcceleration", m_solverId)) {
894 for(MInt i = 0; i < nDim; i++) {
904 m_volumeAccel[i] += Context::getSolverProperty<MFloat>("volumeAcceleration", m_solverId, AT_, i);
905 }
906 }
915 if(Context::propertyExists("Ga", m_solverId)) {
916 m_Ga = Context::getSolverProperty<MFloat>("Ga", m_solverId, AT_, &m_Ga);
917 const MFloat nu = m_Ma / F1BCS / m_Re * m_referenceLength;
918 const MFloat gravity = POW2(m_Ga) * POW2(nu) / POW3(m_referenceLength);
919 m_volumeAccel[1] -= gravity;
920 }
921 }
922
933 m_externalForcing = Context::getSolverProperty<MBool>("externalForcing", m_solverId, AT_, &m_externalForcing);
934
935 m_particleMomentumCoupling = false;
936 m_particleMomentumCoupling =
937 Context::getSolverProperty<MBool>("particleMomentumCoupling", m_solverId, AT_, &m_particleMomentumCoupling);
938
939 m_saveExternalForces = false;
940 m_saveExternalForces =
941 Context::getSolverProperty<MBool>("saveExternalForces", m_solverId, AT_, &m_saveExternalForces);
942
943 /*m_gravity = POW2(m_Ma * LBCS) / POW2(m_Fr) / m_referenceLength;
944 m_Ga = Context::getSolverProperty<MFloat>("Ga", m_solverId, AT_, &m_Ga);*/
945
962 m_velocityControl.dir = -1;
963 m_velocityControl.dir = Context::getSolverProperty<MInt>("velocityControl", m_solverId, AT_, &m_velocityControl.dir);
964 if(m_velocityControl.dir < -1 || m_velocityControl.dir > 2) {
965 TERMM(1, "Invalid Cartesian direction for velocityControl!");
966 }
967 if(m_velocityControl.dir >= 0) {
968 m_controlVelocity = true;
969 }
970
978 m_velocityControl.restart = false;
979 m_velocityControl.restart =
980 Context::getSolverProperty<MBool>("velocityControlRestart", m_solverId, AT_, &m_velocityControl.restart);
981
990 m_velocityControl.interval = 100;
991 m_velocityControl.interval =
992 Context::getSolverProperty<MInt>("velocityControlInterval", m_solverId, AT_, &m_velocityControl.interval);
993 if(m_velocityControl.interval < 1) {
994 TERMM(1, "Invalid velocityControlInterval!");
995 }
996
1006 m_velocityControl.KT = 1.0;
1007 m_velocityControl.KT =
1008 Context::getSolverProperty<MFloat>("velocityControlKT", m_solverId, AT_, &m_velocityControl.KT);
1009
1019 m_velocityControl.KI = 10000.0;
1020 m_velocityControl.KI =
1021 Context::getSolverProperty<MFloat>("velocityControlKI", m_solverId, AT_, &m_velocityControl.KI);
1022
1032 m_velocityControl.KD = 10.0;
1033 m_velocityControl.KD =
1034 Context::getSolverProperty<MFloat>("velocityControlKD", m_solverId, AT_, &m_velocityControl.KD);
1035
1036 if(m_controlVelocity) {
1037 for(MInt i = 0; i < nDim; i++) {
1038 m_volumeAccelBase[i] = m_volumeAccel[i];
1039 }
1040 }
1041
1049 m_solidLayerExtension = false;
1050 m_solidLayerExtension = Context::getSolverProperty<MBool>("solidLayer", m_solverId, AT_, &m_solidLayerExtension);
1051
1060 m_writeLsData = false;
1061 m_writeLsData = Context::getSolverProperty<MBool>("writeLsData", m_solverId, AT_, &m_writeLsData);
1062
1070 m_useOnlyCollectedLS = false;
1071 m_useOnlyCollectedLS =
1072 Context::getSolverProperty<MBool>("useOnlyCollectedLs", m_solverId, AT_, &m_useOnlyCollectedLS);
1073
1081 m_allowBndryAsG0 = false;
1082 m_allowBndryAsG0 = Context::getSolverProperty<MBool>("allowBndryAsG0", m_solverId, AT_, &m_allowBndryAsG0);
1083
1084 // allocate and initialize temporary residual and variable arrays :
1085 mAlloc(m_rescoordinates, nDim + 1, nDim, "m_rescoordinates", F0, AT_);
1086 mAlloc(m_residual, nDim + 1, "m_residual", F0, AT_);
1087 mAlloc(m_tmpResidual, nDim + 1, "m_tmpResidual", F0, AT_);
1088 mAlloc(m_tmpResidualLvl, nDim + 1, "m_tmpResidualLvl", 0, AT_);
1089 mAlloc(m_maxResId, nDim + 1, "m_ResId", 0, AT_);
1090
1091 // Calculate simulation setup variables
1092 m_finalRe = m_Re;
1093 if(m_tanhInit) {
1094 m_Re = m_initRe;
1095 m_tanhScaleFactor = 1.0 / (1.0 - (tanh(-2.5) + 1.0));
1096 }
1097
1098 m_nu = m_Ma * LBCS / m_Re * m_referenceLength;
1099 m_omega = 2.0 / (1.0 + 6.0 * m_nu);
1100
1101
1102 if(Context::propertyExists("nonNewtonianModel", m_solverId)) {
1103 m_nonNewtonian = true;
1104 const MString modelString = Context::getSolverProperty<MString>("nonNewtonianModel", m_solverId, AT_);
1105 const auto model = string2enum(modelString);
1106 if(model == CARREAU) {
1107 m_n = Context::getSolverProperty<MFloat>("nonNewtonian_n_bndry", m_solverId, AT_);
1108 } else if(model == POWERLAW) {
1109 m_n = Context::getSolverProperty<MFloat>("nonNewtonian_n", m_solverId, AT_);
1110 } else {
1111 std::stringstream ss;
1112 ss << "nonNewtonianModel : " << modelString << " is not implemented!" << std::endl;
1113 TERMM(1, ss.str());
1114 }
1115 }
1116
1117 if(m_isThermal != 0) {
1118 m_kappa = m_nu / m_Pr;
1119 m_omegaT = 2.0 / (1.0 + 6.0 * m_kappa);
1120 }
1121
1122 if(m_isTransport != 0) {
1123 m_diffusivity = m_nu * (m_Re / m_Pe);
1124 m_omegaD = 2.0 / (1.0 + 6.0 * m_diffusivity);
1125 }
1126
1127 // pulsatile frequency
1128 m_pulsatileFrequency = m_nu * ((m_alpha * m_alpha) / (m_referenceLength * m_referenceLength));
1129
1130 RECORD_TIMER_STOP(m_t.initSolver);
1131
1132 // interface cell treatment
1133 if(m_isRefined || m_adaptation) {
1146 m_correctInterfaceBcCells =
1147 Context::getSolverProperty<MBool>("correctInterfaceBcCells", m_solverId, AT_, &m_correctInterfaceBcCells);
1148 if(grid().isActive()) {
1149 treatInterfaceCells();
1150 }
1151 }
1152
1153 // set active cells
1154 if(grid().isActive()) {
1155 setActiveCellList();
1156 }
1157
1158 // load restart file if necessary
1159 m_initRestart = false;
1160 m_initFromCoarse = false;
1161 if(m_restartFile) {
1174 m_initRestart = Context::getSolverProperty<MBool>("initRestart", m_solverId, AT_);
1175
1188 m_initFromCoarse = Context::getSolverProperty<MBool>("initFromCoarse", m_solverId, AT_, &m_initFromCoarse);
1189 }
1190
1206 m_isInitRun = false;
1207 m_isInitRun = Context::getSolverProperty<MBool>("isInitRun", m_solverId, AT_, &m_isInitRun);
1208
1209 MString lbInterfaceMethod = "FILIPPOVA";
1210 lbInterfaceMethod = Context::getSolverProperty<MString>("interfaceMethod", m_solverId, AT_, &lbInterfaceMethod);
1211
1212 m_log << endl;
1213 m_log << "#########################################################################################################"
1214 "#############"
1215 << endl;
1216 m_log << "## Methods and flow field init "
1217 " ##"
1218 << endl;
1219 m_log << "#########################################################################################################"
1220 "#############"
1221 << endl
1222 << endl;
1223 m_log << " + Initializing methods..." << endl;
1224 m_log << " - solver method: " << solverMethod() << endl;
1225 m_log << " - interface method: " << lbInterfaceMethod << endl << endl;
1226
1227 switch(string2enum(lbInterfaceMethod)) {
1228 case FILIPPOVA:
1229 if(m_isThermal && !m_isTransport) {
1230 m_propagationStepMethod = &LbSolver::propagation_step_thermal;
1231 } else if(!m_isThermal && m_isTransport) {
1232 m_propagationStepMethod = &LbSolver::propagation_step_transport;
1233 } else if(m_isThermal && m_isTransport) {
1234 m_propagationStepMethod = &LbSolver::propagation_step_thermaltransport;
1235 } else {
1236 m_propagationStepMethod = &LbSolver::propagation_step;
1237 }
1238 break;
1239 case ROHDE:
1240 if(m_isThermal && !m_isTransport) {
1241 m_propagationStepMethod = &LbSolver::propagation_step_thermal_vol;
1242 } else if(!m_isThermal && m_isTransport) {
1243 m_propagationStepMethod = &LbSolver::propagation_step_transport_vol;
1244 } else if(m_isThermal && m_isTransport) {
1245 m_propagationStepMethod = &LbSolver::propagation_step_thermaltransport_vol;
1246 } else {
1247 m_propagationStepMethod = &LbSolver::propagation_step_vol;
1248 }
1249 break;
1250 default: {
1251 TERMM(1, "Unknown lb interface method: Exiting!");
1252 }
1253 }
1254
1255 MInt nonBlockingComm = static_cast<MInt>(m_nonBlockingComm);
1256
1257 if(nonBlockingComm != 0) {
1258 TERMM(1, "Due to the solver reconstruction, this exchange method is not available at the moment");
1259 m_exchangeMethod = &LbSolver::exchangeLbNB;
1260 } else {
1261 m_exchangeMethod = &LbSolver::exchangeLb;
1262 }
1263
1264 switch(solverMethodEnum) {
1265 case MAIA_LATTICE_BGK:
1266 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1267 m_solutionStepMethod = &LbSolver::bgki_collision_step;
1268 break;
1270 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1271 m_solutionStepMethod = &LbSolver::bgki_collision_step_Guo_forcing;
1272 break;
1274 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1275 m_solutionStepMethod = &LbSolver::bgki_init_collision_step;
1276 break;
1277 case MAIA_LATTICE_BGKC:
1278 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1279 m_solutionStepMethod = &LbSolver::bgkc_collision_step;
1280 m_updateMacroscopicLocation =
1281 (m_updateMacroscopicLocation == INCOLLISION) ? PRECOLLISION : m_updateMacroscopicLocation;
1282 m_isCompressible = true; // TODO labels:LB move to sysEqn-like class
1283 break;
1285 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1286 m_solutionStepMethod = &LbSolver::bgki_smagorinsky_collision_step;
1287 break;
1289 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1290 m_solutionStepMethod = &LbSolver::bgki_smagorinsky_collision_step2;
1291 break;
1293 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1294 m_solutionStepMethod = &LbSolver::bgki_smago_wall_collision_step;
1295 break;
1297 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1298 m_solutionStepMethod = &LbSolver::bgki_dynamic_smago_collision_step;
1299 break;
1301 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1302 m_solutionStepMethod = &LbSolver::rbgk_dynamic_smago_collision_step;
1303 break;
1305 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1306 m_solutionStepMethod = &LbSolver::bgki_euler_collision_step;
1307 break;
1309 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1310 m_solutionStepMethod = &LbSolver::bgki_thermal_collision_step;
1311 m_isCompressible = true; // TODO labels:LB move to sysEqn-like class
1312 break;
1314 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1315 m_solutionStepMethod = &LbSolver::bgki_innerEnergy_collision_step;
1316 m_isCompressible = true; // TODO labels:LB move to sysEqn-like class
1317 break;
1319 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1320 m_solutionStepMethod = &LbSolver::bgki_totalEnergy_collision_step;
1321 m_isCompressible = true; // TODO labels:LB move to sysEqn-like class
1322 break;
1324 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1325 m_solutionStepMethod = &LbSolver::bgkc_transport_collision_step;
1326 m_isCompressible = true; // TODO labels:LB move to sysEqn-like class
1327 break;
1329 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1330 m_solutionStepMethod = &LbSolver::bgkc_thermal_transport_collision_step;
1331 m_isCompressible = true; // TODO labels:LB move to sysEqn-like class
1332 break;
1334 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1335 m_solutionStepMethod = &LbSolver::bgkc_innerenergy_transport_collision_step;
1336 m_isCompressible = true; // TODO labels:LB move to sysEqn-like class
1337 break;
1339 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1340 m_solutionStepMethod = &LbSolver::bgkc_totalenergy_transport_collision_step;
1341 m_isCompressible = true; // TODO labels:LB move to sysEqn-like class
1342 break;
1343 case MAIA_LATTICE_RBGK:
1344 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1345 m_solutionStepMethod = &LbSolver::rbgk_collision_step;
1346 break;
1348 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1349 m_solutionStepMethod = &LbSolver::rbgk_smagorinsky_collision_step;
1350 break;
1351 case MAIA_LATTICE_MRT:
1352 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1353 m_solutionStepMethod = &LbSolver::mrt_collision_step;
1354 m_updateMacroscopicLocation =
1355 (m_updateMacroscopicLocation == INCOLLISION) ? PRECOLLISION : m_updateMacroscopicLocation;
1356 m_isCompressible = true; // TODO labels:LB move to sysEqn-like class
1357 break;
1358 case MAIA_LATTICE_MRT2:
1359 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1360 m_solutionStepMethod = &LbSolver::mrt2_collision_step;
1361 break;
1362 case MAIA_LATTICE_CLB:
1363 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1364 m_solutionStepMethod = &LbSolver::clb_collision_step;
1365 break;
1367 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1368 m_solutionStepMethod = &LbSolver::clb_smagorinsky_collision_step;
1369 break;
1371 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1372 m_solutionStepMethod = &LbSolver::mrt_smagorinsky_collision_step;
1373 break;
1375 m_initializeMethod = &LbSolver::initializeLatticeBgk;
1376 m_solutionStepMethod = &LbSolver::cumulant_collision_step;
1377 m_updateMacroscopicLocation =
1378 (m_updateMacroscopicLocation == INCOLLISION) ? PRECOLLISION : m_updateMacroscopicLocation;
1379 m_isCompressible = true; // TODO labels:LB move to sysEqn-like class
1380 break;
1381 default: {
1382 TERMM(1, "Unknown LB method: Exiting!");
1383 }
1384 }
1385
1386 // If m_restartFile is set, the function pointer m_initializeMethod will be overriden
1387 // with the loadRestartFile, because the distributions and variables will
1388 // be taken from the restart file. If one of the properties initRestart or initFromCoarse
1389 // is set, the funktion restartInitLb will be used instead of loadRestartFile, since the
1390 // distributions have to be calculated.
1391 if(m_restartFile || m_initFromRestartFile) {
1392 m_initMethod = "FROM_RESTART_FILE";
1393 m_initializeMethod = &LbSolver::loadRestartFile;
1394 if(m_initRestart || m_initFromCoarse) {
1395 m_initializeMethod = &LbSolver::restartInitLb;
1396 }
1397 }
1398 if(Context::propertyExists("UrmsInit") || Context::propertyExists("ReLambda")) {
1399 getReLambdaAndUrmsInit();
1400
1401 if(m_innerBandWidth != nullptr) mDeallocate(m_innerBandWidth);
1402 mAlloc(m_innerBandWidth, grid().maxRefinementLevel(), "m_innerBandWidth", F0, AT_);
1403 if(m_outerBandWidth != nullptr) mDeallocate(m_outerBandWidth);
1404 mAlloc(m_outerBandWidth, grid().maxRefinementLevel(), "m_outerBandWidth", F0, AT_);
1405 if(m_bandWidth != nullptr) mDeallocate(m_bandWidth);
1406 mAlloc(m_bandWidth, grid().maxRefinementLevel(), "m_bandWidth", 0, AT_);
1407
1420 MFloat distFac[2] = {18.0, 9.0};
1421 for(MInt i = 0; i < 2; i++) {
1422 distFac[i] = Context::getSolverProperty<MFloat>("mbBandWidth", m_solverId, AT_, &distFac[i], i);
1423 }
1424 m_outerBandWidth[grid().maxRefinementLevel() - 1] = distFac[0] * c_cellLengthAtLevel(grid().maxRefinementLevel());
1425 m_bandWidth[grid().maxRefinementLevel() - 1] = distFac[0];
1426 for(MInt i = grid().maxRefinementLevel() - 2; i >= 0; i--) {
1427 m_outerBandWidth[i] = m_outerBandWidth[i + 1] + (distFac[1] * c_cellLengthAtLevel(i + 1));
1428 m_bandWidth[i] = (m_bandWidth[i + 1] / 2) + 1 + distFac[1];
1429 }
1430 for(MInt i = 0; i < grid().maxRefinementLevel(); i++) {
1431 m_innerBandWidth[i] = -m_outerBandWidth[i];
1432 m_log << "bandwidth level " << i << ": " << m_innerBandWidth[i] << " " << m_outerBandWidth[i] << endl;
1433 }
1434 }
1435 // Initialise a txt-file for the residual
1436 // Only process 0 should create a txt.file
1437 if(domainId() == 0) {
1438 // Create a txt file
1439 m_resFileName = "maia_res";
1440 m_resFileName += ".txt";
1441 mRes.open(m_resFileName, std::ofstream::app);
1442
1443 // Print header
1444 mRes << "This is the residual file of the LB solver." << std::endl;
1445 mRes << std::endl;
1446 mRes << "#-----------------------------------------------------------------" << std::endl;
1447 mRes << "#---SOLVER INFORMATION " << std::endl;
1448 mRes << std::endl;
1449 mRes << "Number of ranks: " << noDomains() << std::endl;
1450 mRes << "Used solver method: " << solverMethod() << std::endl;
1451 mRes << "Used initialization method: " << m_initMethod << std::endl;
1452 mRes << "Number dimensions: " << nDim << std::endl;
1453 mRes << "Number of distributions: " << m_noDistributions << std::endl;
1454 mRes << "Minimal spatial refinement level: " << minLevel() << std::endl;
1455 mRes << "Maximal spatial refinement level: " << maxLevel() << std::endl;
1456 mRes << std::endl;
1457 mRes << "#-----------------------------------------------------------------" << std::endl;
1458 mRes << "#---RESIDUAL INFORMATION " << std::endl;
1459 mRes << std::endl;
1460 mRes << "Residual intervall: " << m_residualInterval << std::endl;
1461 mRes << std::endl;
1462 mRes << "#-----------------------------------------------------------------" << std::endl;
1463 mRes << "#---FLOW PARAMETERS " << std::endl;
1464 mRes << std::endl;
1465 mRes << "Mach number in the simulation: " << m_Ma << std::endl;
1466 mRes << "Reynolds number in the simulation: " << m_Re << std::endl;
1467 mRes << std::endl;
1468 mRes << "#-----------------------------------------------------------------" << std::endl;
1469 mRes << std::endl << std::endl;
1470 mRes << "Calculated resudial for each interval: " << std::endl;
1471
1472 mRes.close();
1473 }
1474}
1475
1476template <MInt nDim>
1478 TRACE();
1479
1480 resetComm();
1481
1482 mDeallocate(m_residual);
1483 mDeallocate(m_tmpResidual);
1484 mDeallocate(m_tmpResidualLvl);
1485 mDeallocate(m_maxResId);
1486 mDeallocate(m_rescoordinates);
1487
1488 mDeallocate(m_Fext);
1489
1490 mDeallocate(m_EELiquid.Fg);
1491
1492 mDeallocate(m_momentumFlux);
1493 mDeallocate(m_MijMij);
1494 mDeallocate(m_MijLij);
1495
1496 mDeallocate(m_cellLength);
1497
1498 if(m_interfaceChildren.size() != 0) {
1499 m_interfaceChildren.clear();
1500 }
1501 if(m_interfaceParents.size() != 0) {
1502 m_interfaceParents.clear();
1503 }
1504 mDeallocate(m_activeCellList);
1505
1506 RECORD_TIMER_STOP(m_t.solver);
1507
1508 averageTimer();
1509}
1510
1515template <MInt nDim>
1517 TRACE();
1518
1519 NEW_TIMER_GROUP(tg_solver, "LB Solver (solverId=" + std::to_string(m_solverId) + ")");
1520 NEW_TIMER_NOCREATE(m_t.solver, "complete solver", tg_solver);
1521
1522 NEW_SUB_TIMER_NOCREATE(m_t.initSolver, "init solver", m_t.solver);
1523 NEW_SUB_TIMER_NOCREATE(m_t.solutionStep, "SolutionStep", m_t.solver);
1524 NEW_SUB_TIMER_NOCREATE(m_t.collision, "Collision", m_t.solutionStep);
1525 NEW_SUB_TIMER_NOCREATE(m_t.collisionBC, "CollisionBC", m_t.solutionStep);
1526 NEW_SUB_TIMER_NOCREATE(m_t.propagation, "Propagation", m_t.solutionStep);
1527 NEW_SUB_TIMER_NOCREATE(m_t.propagationBC, "PropagationBC", m_t.solutionStep);
1528 NEW_SUB_TIMER_NOCREATE(m_t.exchange, "Exchange", m_t.solutionStep);
1529 NEW_SUB_TIMER_NOCREATE(m_t.packing, "Packing", m_t.exchange);
1530 NEW_SUB_TIMER_NOCREATE(m_t.unpacking, "Unpacking", m_t.exchange);
1531 NEW_SUB_TIMER_NOCREATE(m_t.communication, "Communication", m_t.exchange);
1532 NEW_SUB_TIMER_NOCREATE(m_t.residual, "Residuum", m_t.solutionStep);
1533 NEW_SUB_TIMER_NOCREATE(m_t.srcTerms, "SourceTerms", m_t.solutionStep);
1534
1535 NEW_SUB_TIMER_NOCREATE(m_t.findG0Cells, "Find G0 cells", m_t.solver);
1536 NEW_SUB_TIMER_NOCREATE(m_t.resetListsMb, "Reset lists MB", m_t.findG0Cells);
1537 NEW_SUB_TIMER_NOCREATE(m_t.findG0Candidates, "Find G0 candidates", m_t.findG0Cells);
1538 NEW_SUB_TIMER_NOCREATE(m_t.geomNodal, "Geometry intersection compute Nodal", m_t.findG0Cells);
1539 NEW_SUB_TIMER_NOCREATE(m_t.geomExchange, "Geometry intersection exchange", m_t.findG0Cells);
1540 NEW_SUB_TIMER_NOCREATE(m_t.calcNodalValues, "Calc nodal values", m_t.findG0Cells);
1541
1542 NEW_SUB_TIMER_NOCREATE(m_t.prepComm, "prepare communication", m_t.solver);
1543
1544 NEW_SUB_TIMER_NOCREATE(m_t.fft, "FFT", m_t.solver);
1545}
1546
1551template <MInt nDim>
1553 TRACE();
1554 if(!grid().isActive()) return;
1555
1556 // Determine timer operation
1557 MString m_timerType = "max";
1558 m_timerType = Context::getSolverProperty<MString>("timerType", m_solverId, AT_, &m_timerType);
1559
1560 // 0) map timer ids for safety
1561 std::vector<MInt> timerIds_;
1562 timerIds_.reserve(17);
1563 timerIds_.emplace_back(m_t.solver);
1564 timerIds_.emplace_back(m_t.initSolver);
1565 timerIds_.emplace_back(m_t.solutionStep);
1566 timerIds_.emplace_back(m_t.collision);
1567 timerIds_.emplace_back(m_t.collisionBC);
1568 timerIds_.emplace_back(m_t.propagation);
1569 timerIds_.emplace_back(m_t.propagationBC);
1570 timerIds_.emplace_back(m_t.srcTerms);
1571 timerIds_.emplace_back(m_t.exchange);
1572 timerIds_.emplace_back(m_t.residual);
1573 timerIds_.emplace_back(m_t.findG0Cells);
1574 timerIds_.emplace_back(m_t.resetListsMb);
1575 timerIds_.emplace_back(m_t.findG0Candidates);
1576 timerIds_.emplace_back(m_t.geomNodal);
1577 timerIds_.emplace_back(m_t.geomExchange);
1578 timerIds_.emplace_back(m_t.calcNodalValues);
1579 timerIds_.emplace_back(m_t.prepComm);
1580 timerIds_.emplace_back(m_t.fft);
1581 const MInt noTimers = timerIds_.size();
1582
1583 // 1) fill buffer with local timer values
1584 std::vector<MFloat> timerValues_;
1585 timerValues_.reserve(noTimers);
1586 for(MInt i = 0; i < noTimers; i++) {
1587 timerValues_.emplace_back(RETURN_TIMER_TIME(timerIds_[i]));
1588 }
1589
1590 // 2) collect values from all ranks
1591 if(m_timerType == "average") {
1592 MPI_Allreduce(MPI_IN_PLACE, timerValues_.data(), noTimers, maia::type_traits<MFloat>::mpiType(), MPI_SUM, mpiComm(),
1593 AT_, "MPI_IN_PLACE", "timerValues_");
1594 } else {
1595 MPI_Allreduce(MPI_IN_PLACE, timerValues_.data(), noTimers, maia::type_traits<MFloat>::mpiType(), MPI_MAX, mpiComm(),
1596 AT_, "MPI_IN_PLACE", "timerValues_");
1597 }
1598
1599 // 3) perform averaging on timer and4) set new timer values
1600 if(m_timerType == "average") {
1601 const MInt noDomains_ = noDomains();
1602 for(MInt i = 0; i < noTimers; i++) {
1603 const MFloat meanValue = timerValues_[i] / noDomains_;
1604 SET_RECORD(timerIds_[i], meanValue);
1605 }
1606 } else {
1607 for(MInt i = 0; i < noTimers; i++) {
1608 SET_RECORD(timerIds_[i], timerValues_[i]);
1609 }
1610 }
1611}
1612
1613template <MInt nDim>
1615 std::map<MString, MInt> scalingVars;
1616
1617 scalingVars["noCells"] = a_noCells();
1618 scalingVars["noInternalCells"] = noInternalCells();
1619 scalingVars["noHaloCells"] = a_noCells() - grid().noInternalCells();
1620 scalingVars["noActiveCells"] = m_currentMaxNoCells;
1621
1622 std::vector<MInt> recvBuffer;
1623 recvBuffer.resize(noDomains());
1624 for(const auto& [name, value] : scalingVars) {
1626 0, mpiComm(), AT_, "send", "recv");
1627
1628 if(!domainId()) {
1629 // average
1630 MFloat avgValue = 0;
1631 avgValue = std::accumulate(recvBuffer.begin(), recvBuffer.end(), avgValue);
1632 avgValue /= noDomains();
1633 std::cout << "Average " + name + " per Domain are: " << avgValue << std::endl;
1634
1635 // maximum
1636 const MInt maxValue = *std::max_element(recvBuffer.begin(), recvBuffer.end());
1637 std::cout << "Max " + name + " per Domain are: " << maxValue << std::endl;
1638
1639 // minimum
1640 const MInt minValue = *std::min_element(recvBuffer.begin(), recvBuffer.end());
1641 std::cout << "Min " + name + " per Domain are: " << minValue << std::endl;
1642 }
1643 }
1644}
1645
1650template <MInt nDim>
1652 // Store level information in cell collector for faster access
1653 for(MInt i = 0; i < a_noCells(); i++) {
1654 m_cells.level(i) = c_level(i);
1655 ASSERT(a_level(i) = c_level(i), "");
1656 a_isHalo(i) = false;
1657 a_isWindow(i) = false;
1658 }
1659 for(MInt i = 0; i < noNeighborDomains(); i++) {
1660 for(MInt c = 0; c < noHaloCells(i); c++) {
1661 a_isHalo(haloCell(i, c)) = true;
1662 }
1663 }
1664 for(MInt i = 0; i < noNeighborDomains(); i++) {
1665 for(MInt c = 0; c < noWindowCells(i); c++) {
1666 a_isWindow(windowCell(i, c)) = true;
1667 }
1668 }
1669}
1670
1671
1684template <MInt nDim>
1686 TRACE();
1687
1688 RECORD_TIMER_START(m_t.prepComm);
1689
1690 m_log << endl;
1691 m_log << "#########################################################################################################"
1692 "#############"
1693 << endl;
1694 m_log << "## Communication "
1695 " ##"
1696 << endl;
1697 m_log << "#########################################################################################################"
1698 "#############"
1699 << endl;
1700
1712 m_reducedComm = false;
1713 m_reducedComm = Context::getSolverProperty<MBool>("reducedComm", m_solverId, AT_, &m_reducedComm);
1714
1715 if(m_reducedComm) {
1716 // 1. set the right function pointers
1717 m_gatherMethod = &LbSolver::gatherReduced;
1718 m_scatterMethod = &LbSolver::scatterReduced;
1719 m_sendMethod = &LbSolver::sendReduced;
1720 m_receiveMethod = &LbSolver::receiveReduced;
1721 prepareCommunicationReduced();
1722 } else {
1723 // 1. Set the right function pointers
1724 m_gatherMethod = &LbSolver::gatherNormal;
1725 m_scatterMethod = &LbSolver::scatterNormal;
1726 m_sendMethod = &LbSolver::sendNormal;
1727 m_receiveMethod = &LbSolver::receiveNormal;
1728 prepareCommunicationNormal();
1729 }
1730 m_log << " lbsolver: domain " << domainId() << ", has " << a_noCells() << " cells" << endl;
1731
1732 RECORD_TIMER_STOP(m_t.prepComm);
1733}
1734
1757template <MInt nDim>
1759 TRACE();
1760
1761 m_log << " + Preparing normal communication ..." << endl << endl;
1762
1763 // 2. Set the data solver sizes and allocate space for the offsets
1764 if(m_isRefined || m_isEELiquid) {
1765 // Transfer PVs, old PVs, thermal and normal PPDFs : sum = 4 offsets
1766 if(m_isThermal && !m_isTransport) {
1767 m_noElementsTransfer = 4;
1768 mAlloc(m_dataBlockSizes, m_noElementsTransfer, "m_noDataBlockSizes", 0, AT_);
1769 mAlloc(m_baseAddresses, m_noElementsTransfer, "m_baseAddresses", AT_);
1770
1771 m_dataBlockSizes[0] = m_noDistributions;
1772 m_dataBlockSizes[1] = m_noDistributions;
1773 m_dataBlockSizes[2] = nDim + 2;
1774 m_dataBlockSizes[3] = nDim + 2;
1775
1776 m_baseAddresses[0] = &a_distribution(0, 0);
1777 m_baseAddresses[1] = &a_distributionThermal(0, 0);
1778 m_baseAddresses[2] = &a_variable(0, 0);
1779 m_baseAddresses[3] = &a_oldVariable(0, 0);
1780 } else if(m_isTransport && !m_isThermal) {
1781 m_noElementsTransfer = 4;
1782 mAlloc(m_dataBlockSizes, m_noElementsTransfer, "m_noDataBlockSizes", 0, AT_);
1783 mAlloc(m_baseAddresses, m_noElementsTransfer, "m_baseAddresses", AT_);
1784
1785 m_dataBlockSizes[0] = m_noDistributions;
1786 m_dataBlockSizes[1] = m_noDistributions;
1787 m_dataBlockSizes[2] = nDim + 2;
1788 m_dataBlockSizes[3] = nDim + 2;
1789
1790 m_baseAddresses[0] = &a_distribution(0, 0);
1791 m_baseAddresses[1] = &a_distributionTransport(0, 0);
1792 m_baseAddresses[2] = &a_variable(0, 0);
1793 m_baseAddresses[3] = &a_oldVariable(0, 0);
1794 } else if(m_isThermal && m_isTransport) {
1795 m_noElementsTransfer = 5;
1796 mAlloc(m_dataBlockSizes, m_noElementsTransfer, "m_noDataBlockSizes", 0, AT_);
1797 mAlloc(m_baseAddresses, m_noElementsTransfer, "m_baseAddresses", AT_);
1798
1799 m_dataBlockSizes[0] = m_noDistributions;
1800 m_dataBlockSizes[1] = m_noDistributions;
1801 m_dataBlockSizes[2] = m_noDistributions;
1802 m_dataBlockSizes[3] = nDim + 3;
1803 m_dataBlockSizes[4] = nDim + 3;
1804
1805 m_baseAddresses[0] = &a_distribution(0, 0);
1806 m_baseAddresses[1] = &a_distributionThermal(0, 0);
1807 m_baseAddresses[2] = &a_distributionTransport(0, 0);
1808 m_baseAddresses[3] = &a_variable(0, 0);
1809 m_baseAddresses[4] = &a_oldVariable(0, 0);
1810 }
1811 // Transfer PVs, oldPVs and normal PPDFs : sum = 3 offsets
1812 else {
1813 m_noElementsTransfer = 3;
1814 mAlloc(m_dataBlockSizes, m_noElementsTransfer, "m_noDataBlockSizes", 0, AT_);
1815 mAlloc(m_baseAddresses, m_noElementsTransfer, "m_baseAddresses", AT_);
1816
1817 m_dataBlockSizes[0] = m_noDistributions;
1818 m_dataBlockSizes[1] = nDim + 1;
1819 m_dataBlockSizes[2] = nDim + 1;
1820
1821 m_baseAddresses[0] = &a_distribution(0, 0);
1822 m_baseAddresses[1] = &a_variable(0, 0);
1823 m_baseAddresses[2] = &a_oldVariable(0, 0);
1824 }
1825 } else {
1826 // Transfer thermal and normal PPDFs : sum = 2 offsets
1827 if(m_isThermal && !m_isTransport) {
1828 m_noElementsTransfer = 2;
1829 mAlloc(m_dataBlockSizes, m_noElementsTransfer, "m_noDataBlockSizes", 0, AT_);
1830 mAlloc(m_baseAddresses, m_noElementsTransfer, "m_baseAddresses", AT_);
1831
1832 m_dataBlockSizes[0] = m_noDistributions;
1833 m_dataBlockSizes[1] = m_noDistributions;
1834
1835 m_baseAddresses[0] = &a_distribution(0, 0);
1836 m_baseAddresses[1] = &a_distributionThermal(0, 0);
1837 } else if(m_isTransport && !m_isThermal) {
1838 m_noElementsTransfer = 2;
1839 mAlloc(m_dataBlockSizes, m_noElementsTransfer, "m_noDataBlockSizes", 0, AT_);
1840 mAlloc(m_baseAddresses, m_noElementsTransfer, "m_baseAddresses", AT_);
1841
1842 m_dataBlockSizes[0] = m_noDistributions;
1843 m_dataBlockSizes[1] = m_noDistributions;
1844
1845 m_baseAddresses[0] = &a_distribution(0, 0);
1846 m_baseAddresses[1] = &a_distributionTransport(0, 0);
1847
1848 } else if(m_isTransport && m_isThermal) {
1849 m_noElementsTransfer = 3;
1850 mAlloc(m_dataBlockSizes, m_noElementsTransfer, "m_noDataBlockSizes", 0, AT_);
1851 mAlloc(m_baseAddresses, m_noElementsTransfer, "m_baseAddresses", AT_);
1852
1853 m_dataBlockSizes[0] = m_noDistributions;
1854 m_dataBlockSizes[1] = m_noDistributions;
1855 m_dataBlockSizes[2] = m_noDistributions;
1856
1857 m_baseAddresses[0] = &a_distribution(0, 0);
1858 m_baseAddresses[1] = &a_distributionThermal(0, 0);
1859 m_baseAddresses[2] = &a_distributionTransport(0, 0);
1860
1861 }
1862 // Transfer normal PPDFs : sum = 1 offset
1863 else {
1864 // This is needed if gradients are calculated (e.g. for LODI equations)
1865 m_noElementsTransfer = 2;
1866 mAlloc(m_dataBlockSizes, m_noElementsTransfer, "m_noDataBlockSizes", 0, AT_);
1867 mAlloc(m_baseAddresses, m_noElementsTransfer, "m_baseAddresses", AT_);
1868
1869 m_dataBlockSizes[0] = m_noDistributions;
1870 m_dataBlockSizes[1] = nDim + 1;
1871
1872 m_baseAddresses[0] = &a_distribution(0, 0);
1873 m_baseAddresses[1] = &a_variable(0, 0);
1874 }
1875 }
1876
1877 // calculate the total size of the data to be transferred per cell
1878 m_dataBlockSizeTotal = 0;
1879 for(MInt i = 0; i < m_noElementsTransfer; i++)
1880 m_dataBlockSizeTotal += m_dataBlockSizes[i];
1881
1882 // 3. Allocate the offsets
1883 if(noNeighborDomains() > 0) {
1884 mAlloc(m_nghbrOffsetsWindow, noNeighborDomains(), m_noElementsTransfer, "m_nghbrOffsetsWindow", 0, AT_);
1885 mAlloc(m_nghbrOffsetsHalo, noNeighborDomains(), m_noElementsTransfer, "m_nghbrOffsetsHalo", 0, AT_);
1886 }
1887 for(MInt n = 0; n < noNeighborDomains(); n++) {
1888 for(MInt v = 1; v < m_noElementsTransfer; v++) {
1889 m_nghbrOffsetsWindow[n][v] = m_nghbrOffsetsWindow[n][v - 1] + noWindowCells(n) * m_dataBlockSizes[v - 1];
1890 m_nghbrOffsetsHalo[n][v] = m_nghbrOffsetsHalo[n][v - 1] + noHaloCells(n) * m_dataBlockSizes[v - 1];
1891 }
1892 }
1893
1894 if(noNeighborDomains() > 0) {
1895 // Allocate the buffers
1896 ScratchSpace<MInt> haloCellsCnt(noNeighborDomains(), AT_, "noHaloCells");
1897 ScratchSpace<MInt> windowCellsCnt(noNeighborDomains(), AT_, "noWindowCells");
1898 for(MInt d = 0; d < noNeighborDomains(); d++) {
1899 haloCellsCnt[d] = noHaloCells(d);
1900 windowCellsCnt[d] = noWindowCells(d);
1901 }
1902 mAlloc(m_sendBuffers, noNeighborDomains(), &windowCellsCnt[0], m_dataBlockSizeTotal, "m_sendBuffers", AT_);
1903 mAlloc(m_receiveBuffers, noNeighborDomains(), &haloCellsCnt[0], m_dataBlockSizeTotal, "m_receiveBuffers", AT_);
1904
1905 // blocking communication
1906 if(!m_nonBlockingComm) {
1907 mAlloc(mpi_request, noNeighborDomains(), "mpi_request", AT_);
1908 }
1909 // non-blocking communication
1910 else {
1911 mAlloc(mpi_requestS, noNeighborDomains(), "mpi_requestS", AT_);
1912 mAlloc(mpi_requestR, noNeighborDomains(), "mpi_requestR", AT_);
1913 }
1914 }
1915
1916 // 4. Print the information
1917 printCommunicationMethod();
1918}
1919
1932template <MInt nDim>
1934 TRACE();
1935
1936 m_log << " + Preparing reduced communication ..." << endl << endl;
1937
1938 // blocking communication
1939 if(noNeighborDomains() > 0) {
1940 if(!m_nonBlockingComm) {
1941 mAlloc(mpi_request, noNeighborDomains(), "mpi_request", AT_);
1942 } else {
1943 mAlloc(mpi_requestS, noNeighborDomains(), "mpi_requestS", AT_);
1944 mAlloc(mpi_requestR, noNeighborDomains(), "mpi_requestR", AT_);
1945 }
1946
1947 ScratchSpace<MInt> noHalosPerDomain(noNeighborDomains(), AT_, "noHalosPerDomain");
1948 ScratchSpace<MInt> noWindowsPerDomain(noNeighborDomains(), AT_, "noWindowsPerDomain");
1949 for(MInt n = 0; n < noNeighborDomains(); n++) {
1950 noHalosPerDomain[n] = noHaloCells(n) * (m_noDistributions - 1) + 1;
1951 noWindowsPerDomain[n] = noWindowCells(n) * (m_noDistributions - 1) + 1;
1952 }
1953
1954 mAlloc(m_noWindowDistDataPerDomain, noNeighborDomains(), "m_noWindowDistDataPerDomain", 0, AT_);
1955 mAlloc(m_noHaloDistDataPerDomain, noNeighborDomains(), "m_noHaloDistDataPerDomain", 0, AT_);
1956
1957 mAlloc(m_windowDistsForExchange, noNeighborDomains(), &noWindowsPerDomain[0], "m_windowDistsForExchange", AT_);
1958 mAlloc(m_haloDistsForExchange, noNeighborDomains(), &noHalosPerDomain[0], "m_haloDistsForExchange", AT_);
1959 }
1960
1961 mAlloc(m_needsFurtherExchange, a_noCells(), "needsFurtherExchange", 0, AT_);
1962 markCellsForAdditionalComm();
1963
1964 if(m_isRefined) {
1965 // 2. count the elements per neighbor domain that we need to transfer
1966 m_log << " - counting PPDFs per neighbor" << endl;
1967
1968 for(MInt n = 0; n < noNeighborDomains(); n++) {
1969 MInt haloStart = domainOffset(neighborDomain(n));
1970 MInt haloEnd = domainOffset(neighborDomain(n) + 1);
1971
1972 MInt cnt = 0;
1973 for(MInt j = 0; j < noWindowCells(n); j++) {
1974 if(m_needsFurtherExchange[windowCell(n, j)] > 0) {
1975 for(MInt d = 0; d < m_noDistributions - 1; d++) {
1976 m_noWindowDistDataPerDomain[n]++;
1977 m_windowDistsForExchange[n][j * (m_noDistributions - 1) + d] = cnt;
1978 cnt++;
1979 }
1980 } else {
1981 for(MInt d = 0; d < m_noDistributions - 1; d++) {
1982 m_windowDistsForExchange[n][j * (m_noDistributions - 1) + d] = -1;
1983 MInt ngh = c_neighborId(windowCell(n, j), d);
1984 if(ngh != -1 && a_isHalo(ngh)) {
1985 MInt global = c_globalId(ngh);
1986 if(global >= haloStart && global < haloEnd) {
1987 m_noWindowDistDataPerDomain[n]++;
1988 m_windowDistsForExchange[n][j * (m_noDistributions - 1) + d] = cnt;
1989 cnt++;
1990 }
1991 }
1992 }
1993 }
1994 }
1995 cnt = 0;
1996 for(MInt j = 0; j < noHaloCells(n); j++) {
1997 if(m_needsFurtherExchange[haloCell(n, j)] > 0) {
1998 for(MInt d = 0; d < m_noDistributions - 1; d++) {
1999 m_noHaloDistDataPerDomain[n]++;
2000 m_haloDistsForExchange[n][j * (m_noDistributions - 1) + d] = cnt;
2001 cnt++;
2002 }
2003 } else {
2004 for(MInt d = 0; d < m_noDistributions - 1; d++) {
2005 m_haloDistsForExchange[n][j * (m_noDistributions - 1) + d] = -1;
2006 MInt ngh = c_neighborId(haloCell(n, j), d);
2007 if(ngh != -1 && a_isWindow(ngh)) {
2008 m_noHaloDistDataPerDomain[n]++;
2009 m_haloDistsForExchange[n][j * (m_noDistributions - 1) + d] = cnt;
2010 cnt++;
2011 }
2012 }
2013 }
2014 }
2015
2016 m_log << " * neighbor " << neighborDomain(n) << ":" << endl;
2017 m_log << " = windows :" << m_noWindowDistDataPerDomain[n] << endl;
2018 m_log << " = halos :" << m_noHaloDistDataPerDomain[n] << endl;
2019 }
2020
2021 // 3. fill the arrays depending on the number of elements
2022 if(m_isThermal && !m_isTransport) {
2023 m_noDistsTransfer = 2;
2024 m_noVarsTransfer = 2;
2025 m_noElementsTransfer = m_noDistsTransfer + m_noVarsTransfer;
2026 } else if(m_isTransport && !m_isThermal) {
2027 m_noDistsTransfer = 2;
2028 m_noVarsTransfer = 2;
2029 m_noElementsTransfer = m_noDistsTransfer + m_noVarsTransfer;
2030 } else if(m_isTransport && m_isThermal) {
2031 m_noDistsTransfer = 3;
2032 m_noVarsTransfer = 2;
2033 m_noElementsTransfer = m_noDistsTransfer + m_noVarsTransfer;
2034 } else {
2035 m_noDistsTransfer = 1;
2036 m_noVarsTransfer = 2;
2037 m_noElementsTransfer = m_noDistsTransfer + m_noVarsTransfer;
2038 }
2039 } else {
2040 // 2. count the elements per neighbor domain that we need to transfer
2041 m_log << " - counting PPDFs per neighbor" << endl;
2042
2043 for(MInt n = 0; n < noNeighborDomains(); n++) {
2044 MInt haloStart = domainOffset(neighborDomain(n));
2045 MInt haloEnd = domainOffset(neighborDomain(n) + 1);
2046
2047 MInt cnt = 0;
2048 for(MInt j = 0; j < noWindowCells(n); j++) {
2049 if(!c_isLeafCell(windowCell(n, j))) {
2050 for(MInt d = 0; d < m_noDistributions - 1; d++) {
2051 m_windowDistsForExchange[n][j * (m_noDistributions - 1) + d] = -1;
2052 }
2053 } else {
2054 if(m_needsFurtherExchange[windowCell(n, j)] > 0) {
2055 for(MInt d = 0; d < m_noDistributions; d++) {
2056 m_noWindowDistDataPerDomain[n]++;
2057 m_windowDistsForExchange[n][j * (m_noDistributions - 1) + d] = cnt;
2058 cnt++;
2059 }
2060 } else {
2061 for(MInt d = 0; d < m_noDistributions - 1; d++) {
2062 m_windowDistsForExchange[n][j * (m_noDistributions - 1) + d] = -1;
2063 MInt ngh = c_neighborId(windowCell(n, j), d);
2064 if(ngh != -1 && a_isHalo(ngh)) {
2065 MInt global = c_globalId(ngh);
2066 if(global >= haloStart && global < haloEnd) {
2067 m_noWindowDistDataPerDomain[n]++;
2068 m_windowDistsForExchange[n][j * (m_noDistributions - 1) + d] = cnt;
2069 cnt++;
2070 }
2071 }
2072 }
2073 }
2074 }
2075 }
2076 cnt = 0;
2077 for(MInt j = 0; j < noHaloCells(n); j++) {
2078 if(!c_isLeafCell(haloCell(n, j))) {
2079 for(MInt d = 0; d < m_noDistributions - 1; d++) {
2080 m_haloDistsForExchange[n][j * (m_noDistributions - 1) + d] = -1;
2081 }
2082 } else {
2083 if(m_needsFurtherExchange[haloCell(n, j)] > 0) {
2084 for(MInt d = 0; d < m_noDistributions; d++) {
2085 m_noHaloDistDataPerDomain[n]++;
2086 m_haloDistsForExchange[n][j * (m_noDistributions - 1) + d] = cnt;
2087 cnt++;
2088 }
2089 } else {
2090 for(MInt d = 0; d < m_noDistributions - 1; d++) {
2091 m_haloDistsForExchange[n][j * (m_noDistributions - 1) + d] = -1;
2092 MInt ngh = c_neighborId(haloCell(n, j), d);
2093 if(ngh != -1 && a_isWindow(ngh)) {
2094 m_noHaloDistDataPerDomain[n]++;
2095 m_haloDistsForExchange[n][j * (m_noDistributions - 1) + d] = cnt;
2096 cnt++;
2097 }
2098 }
2099 }
2100 }
2101 }
2102
2103 m_log << " * neighbor " << neighborDomain(n) << ":" << endl;
2104 m_log << " = windows :" << m_noWindowDistDataPerDomain[n] << endl;
2105 m_log << " = halos :" << m_noHaloDistDataPerDomain[n] << endl;
2106 }
2107
2108 // 3. fill the arrays depending on the number of elements
2109 if(m_isThermal && !m_isTransport) {
2110 m_noDistsTransfer = 2;
2111 m_noVarsTransfer = 0;
2112 m_noElementsTransfer = m_noDistsTransfer + m_noVarsTransfer;
2113 } else if(m_isTransport && !m_isThermal) {
2114 m_noDistsTransfer = 2;
2115 m_noVarsTransfer = 0;
2116 m_noElementsTransfer = m_noDistsTransfer + m_noVarsTransfer;
2117 } else if(m_isTransport && m_isThermal) {
2118 m_noDistsTransfer = 3;
2119 m_noVarsTransfer = 0;
2120 m_noElementsTransfer = m_noDistsTransfer + m_noVarsTransfer;
2121 } else {
2122 m_noDistsTransfer = 1;
2123 m_noVarsTransfer = 1;
2124 m_noElementsTransfer = m_noDistsTransfer + m_noVarsTransfer;
2125 }
2126 }
2127
2128 // 4. allocate the buffers
2129 if(noNeighborDomains() > 0) {
2130 ScratchSpace<MInt> haloDataPerDomain(noNeighborDomains(), AT_, "noHaloCells");
2131 ScratchSpace<MInt> windowDataPerDomain(noNeighborDomains(), AT_, "noWindowCells");
2132
2133 for(MInt n = 0; n < noNeighborDomains(); n++) {
2134 windowDataPerDomain(n) =
2135 m_noWindowDistDataPerDomain[n] * m_noDistsTransfer + noWindowCells(n) * m_noVariables * m_noVarsTransfer;
2136 haloDataPerDomain(n) =
2137 m_noHaloDistDataPerDomain[n] * m_noDistsTransfer + noHaloCells(n) * m_noVariables * m_noVarsTransfer;
2138 }
2139
2140 mAlloc(m_sendBuffers, noNeighborDomains(), &windowDataPerDomain[0], "m_sendBuffers", AT_);
2141 mAlloc(m_receiveBuffers, noNeighborDomains(), &haloDataPerDomain[0], "m_receiveBuffers", AT_);
2142 }
2143
2144 // 5. print the communication information
2145 printCommunicationMethod();
2146}
2147
2159template <MInt nDim>
2161 TRACE();
2162
2163 // All interface cells need further exchange, i.e. all distributions need to be exchanged
2164 MInt periodicSimulation = 0;
2165 for(MInt i = 0; i < a_noCells(); i++) {
2166 if(grid().isPeriodic(i)) {
2167 periodicSimulation = 1;
2168 break;
2169 }
2170 }
2171 MPI_Allreduce(MPI_IN_PLACE, &periodicSimulation, 1, MPI_INT, MPI_MAX, mpiComm(), AT_, "MPI_IN_PLACE",
2172 "periodicSimulation");
2173
2174 if(periodicSimulation) {
2175 MInt noPeriodicHalos = 0;
2176 MIntScratchSpace noPeriodicHalosPerDomain(noNeighborDomains(), AT_, "noDomains");
2177 std::vector<MInt> myGlobIdsPeriHalos;
2178 for(MInt i = 0; i < a_noCells(); i++) {
2179 if(!a_isHalo(i)) continue;
2180 if(grid().isPeriodic(i)) {
2181 myGlobIdsPeriHalos.push_back(c_globalId(i));
2182 noPeriodicHalos++;
2183 }
2184 }
2185
2186 for(MInt i = 0; i < noNeighborDomains(); i++) {
2187 MPI_Issend(&noPeriodicHalos, 1, MPI_INT, neighborDomain(i), 0, mpiComm(), &mpi_request[i], AT_,
2188 "noPeriodicHalos");
2189 }
2190 MPI_Status status;
2191 for(MInt i = 0; i < noNeighborDomains(); i++) {
2192 MPI_Recv(&noPeriodicHalosPerDomain(i), 1, MPI_INT, neighborDomain(i), 0, mpiComm(), &status, AT_,
2193 "noPeriodicHalosPerDomain(i)");
2194 }
2195 for(MInt i = 0; i < noNeighborDomains(); i++) {
2196 MPI_Wait(&mpi_request[i], &status, AT_);
2197 }
2198 MInt** allGlobIdsPeriHalos{};
2199 mAlloc(allGlobIdsPeriHalos, noNeighborDomains(), &noPeriodicHalosPerDomain[0], "allGlobalIdsPeriHalos", AT_);
2200
2201 for(MInt i = 0; i < noNeighborDomains(); i++) {
2202 MPI_Issend(myGlobIdsPeriHalos.data(), noPeriodicHalos, MPI_INT, neighborDomain(i), 0, mpiComm(), &mpi_request[i],
2203 AT_, "noPeriodicHalos");
2204 }
2205 for(MInt i = 0; i < noNeighborDomains(); i++) {
2206 MPI_Recv(allGlobIdsPeriHalos[i], noPeriodicHalosPerDomain(i), MPI_INT, neighborDomain(i), 0, mpiComm(), &status,
2207 AT_, "noPeriodicHalosPerDomain(i)");
2208 }
2209 for(MInt i = 0; i < noNeighborDomains(); i++) {
2210 MPI_Wait(&mpi_request[i], &status, AT_);
2211 }
2212
2213 for(MInt i = 0; i < a_noCells(); i++) {
2214 if(a_isHalo(i)) continue;
2215 MInt globalId = c_globalId(i);
2216 MBool found = false;
2217 for(MInt d = 0; d < noNeighborDomains(); d++) {
2218 for(MInt halo = 0; halo < noPeriodicHalosPerDomain(d); halo++) {
2219 if(allGlobIdsPeriHalos[d][halo] == globalId) {
2220 m_needsFurtherExchange[i] = 1;
2221 break;
2222 }
2223 }
2224 if(found) break;
2225 }
2226 }
2227 mDeallocate(allGlobIdsPeriHalos);
2228 }
2229
2230 for(MInt i = 0; i < a_noCells(); i++) {
2231 if(a_isHalo(i)) continue;
2232 if(a_isInterfaceParent(i)) m_needsFurtherExchange[i] = 1;
2233 if(a_isInterfaceChild(i)) m_needsFurtherExchange[i] = 1;
2234 if(a_isBndryCell(i)) m_needsFurtherExchange[i] = 1;
2235 if(c_parentId(i) > 0) {
2236 if(a_isInterfaceParent(c_parentId(i))) m_needsFurtherExchange[i] = 1;
2237 }
2238 for(MInt n = 0; n < IPOW3[nDim] - 1; n++) {
2239 if(c_neighborId(i, n) > -1) {
2240 if(a_isActive(i) != a_isActive(c_neighborId(i, n))) {
2241 m_needsFurtherExchange[i] = 1;
2242 break;
2243 }
2244 }
2245 }
2246 }
2247 this->exchangeData(&(m_needsFurtherExchange[0]), 1);
2248
2249 // Also the neighbors of the interface cells need special treatment
2250 for(MInt i = 0; i < a_noCells(); i++) {
2251 if(a_isHalo(i)) continue;
2252 if(m_needsFurtherExchange[i]) continue;
2253 for(MInt d = 0; d < m_noDistributions - 1; d++) {
2254 MInt nghbr = c_neighborId(i, d);
2255 if(nghbr > -1) {
2256 if(m_needsFurtherExchange[nghbr] == 1) m_needsFurtherExchange[i] = 2;
2257 }
2258 }
2259 }
2260
2261 this->exchangeData(&(m_needsFurtherExchange[0]), 1);
2262 // Also the neighbors neighbors of the interface cells need special treatment
2263 for(MInt i = 0; i < a_noCells(); i++) {
2264 if(a_isHalo(i)) continue;
2265 if(m_needsFurtherExchange[i]) continue;
2266 for(MInt d = 0; d < m_noDistributions - 1; d++) {
2267 MInt nghbr = c_neighborId(i, d);
2268 if(nghbr > -1) {
2269 if(m_needsFurtherExchange[nghbr] == 2) m_needsFurtherExchange[i] = 3;
2270 }
2271 }
2272 }
2273 this->exchangeData(&(m_needsFurtherExchange[0]), 1);
2274}
2275
2276
2283template <MInt nDim>
2285 TRACE();
2286
2287 if(!m_nonBlockingComm)
2288 m_log << " + Communication method: blocking " << (m_reducedComm ? " (reduced)" : " (normal)") << endl << endl;
2289 else
2290 m_log << " + Communication method: non-blocking" << (m_reducedComm ? " (reduced)" : " (normal)") << endl << endl;
2291
2292 m_log << " + Elements to be transferred: " << m_noElementsTransfer << " ( ";
2293
2294 if(m_isRefined) {
2295 m_log << "PVs, oldPVs, PPDFs";
2296 if(m_isThermal)
2297 m_log << ", thPPDFs )" << endl;
2298 else
2299 m_log << " )" << endl;
2300 } else {
2301 m_log << "PPDFs";
2302 if(m_isThermal)
2303 m_log << ", thPPDFs )" << endl;
2304 else
2305 m_log << " )" << endl;
2306 }
2307 if(!m_reducedComm)
2308 for(MInt i = 0; i < m_noElementsTransfer; i++)
2309 m_log << " - size [" << i << "] : " << m_dataBlockSizes[i] << endl;
2310
2311 if(!m_reducedComm) m_log << " - total size: " << m_dataBlockSizeTotal << endl << endl;
2312
2313 m_log << " + Neighboring domains are:" << endl;
2314
2315 for(MInt i = 0; i < noNeighborDomains(); i++) {
2316 m_log << " - domain id " << neighborDomain(i) << ":" << endl;
2317 m_log << " * window: " << noWindowCells(i) << endl;
2318 m_log << " * halo: " << noHaloCells(i) << endl;
2319 m_log << " * window data offsets: ";
2320
2321 if(!m_reducedComm) {
2322 for(MInt v = 0; v < m_noElementsTransfer; v++)
2323 m_log << m_nghbrOffsetsWindow[i][v] << " ";
2324 m_log << endl;
2325
2326 m_log << " * halo data offsets: ";
2327 for(MInt v = 0; v < m_noElementsTransfer; v++)
2328 m_log << m_nghbrOffsetsHalo[i][v] << " ";
2329 m_log << endl;
2330 }
2331
2332 if(!m_reducedComm) {
2333 m_log << " * total window buffer: " << (noWindowCells(i) * m_dataBlockSizeTotal * sizeof(MFloat)) << " bytes"
2334 << endl;
2335 m_log << " * total halo buffer : " << (noHaloCells(i) * m_dataBlockSizeTotal * sizeof(MFloat)) << " bytes"
2336 << endl;
2337 } else {
2338 m_log << " * total window buffer: " << m_noWindowDistDataPerDomain[i] * sizeof(MFloat) << " bytes" << endl;
2339 m_log << " * total halo buffer : " << m_noHaloDistDataPerDomain[i] * sizeof(MFloat) << " bytes" << endl;
2340 }
2341 }
2342
2343 m_log << endl << endl;
2344}
2345
2356template <MInt nDim>
2357void LbSolver<nDim>::createMPIComm(const MInt* ranks, MInt noRanks, MPI_Comm* comm) {
2358 TRACE();
2359
2360 MIntScratchSpace ownerList(noRanks, AT_, "ownerList");
2361 for(MInt d = 0, l = 0; d < noDomains(); d++) {
2362 if(ranks[d] > 0) {
2363 ownerList[l] = d;
2364 l++;
2365 }
2366 }
2367
2368 MPI_Group tmp_group;
2369 MPI_Group group;
2370 MPI_Comm_group(mpiComm(), &tmp_group, AT_, "tmp_group");
2371 MPI_Group_incl(tmp_group, noRanks, ownerList.getPointer(), &group, AT_);
2372
2373 MPI_Comm_create(mpiComm(), group, comm, AT_, "comm");
2374}
2375
2385template <MInt nDim>
2386inline void LbSolver<nDim>::writeSegmentBoundaryVTK(MFloat** bndVs, MInt num) {
2387 TRACE();
2388
2389 stringstream name;
2390 name << "out/" << domainId() << "_boundary.vtk";
2391 ofstream st;
2392 st.open(name.str());
2393 st << "# vtk DataFile Version 3.0" << endl;
2394 st << "vtk output" << endl;
2395 st << "ASCII" << endl;
2396 st << "DATASET POLYDATA" << endl;
2397 st << "POINTS " << num << " float" << endl;
2398
2399 for(MInt i = 0; i < num; i++) {
2400 for(MInt d = 0; d < 3; d++)
2401 st << bndVs[i][d] << " ";
2402 st << endl;
2403 }
2404 st << endl;
2405 st << "LINES " << 1 << " " << num + 2 << endl;
2406 st << num + 1 << " ";
2407 for(MInt i = 0; i < num; i++) {
2408 st << i << " ";
2409 }
2410 st << "0" << endl;
2411
2412 st.close();
2413}
2414
2422template <MInt nDim>
2424 TRACE();
2425 constexpr MFloat L_ref = 1.0; // since it is used this way in FvSolver
2426 const MFloat dx = c_cellLengthAtLevel(maxLevel());
2427 m_dt = dx * m_Ma * LBCS / L_ref;
2428 m_time = getCurrentTimeStep() * m_dt;
2429}
2430
2439template <MInt nDim>
2441 TRACE();
2442
2443 MInt own;
2444 MInt sumowners;
2445 MInt firstOwner;
2446 MIntScratchSpace owners(noDomains(), AT_, "owners");
2447
2448 m_log << " * segment owned by: ";
2449 m_geometry->determineSegmentOwnership(segmentId, &own, &sumowners, &firstOwner, owners.getPointer());
2450 for(MInt d = 0; d < noDomains(); d++)
2451 if(owners[d] > 0) m_log << d << " ";
2452
2453 m_log << endl;
2454 m_log << " * sum of owners: " << sumowners << endl;
2455 m_log << " * root of communication: " << firstOwner << endl;
2456
2457
2458 // my domain owns the whole segment
2459 if(sumowners == 1) {
2460 MFloat result = 0.0;
2461 if(own) result = calcCharLenAll(segmentId);
2462
2463 MPI_Bcast(&result, 1, MPI_DOUBLE, firstOwner, mpiComm(), AT_, "result");
2464
2465 return result;
2466 }
2467
2468 // we need to share the geometry
2469 else {
2470 MFloat diameter;
2471
2472 // build communicator
2473 MPI_Comm charComm;
2474 createMPIComm(owners.getPointer(), sumowners, &charComm);
2475
2476 if(own) {
2477 // collect the triangles for testing first
2478 MInt offStart = m_geometry->m_segmentOffsets[segmentId];
2479 MInt offEnd = m_geometry->m_segmentOffsets[segmentId + 1];
2480 MInt numElements = offEnd - offStart;
2481
2482 m_log << " * number of local triangles: " << numElements << endl;
2483 m_log << " * segment offsets: " << m_geometry->m_segmentOffsets[segmentId] << " - "
2484 << m_geometry->m_segmentOffsets[segmentId + 1] << endl;
2485
2486 // (normals + vertices)
2487 MInt noTriInfo = nDim * nDim;
2488 MIntScratchSpace myOriginalIds(numElements, AT_, "myOriginalIds");
2489 MFloatScratchSpace segTriangles(noTriInfo * numElements, AT_, "segTriangles");
2490
2491 for(MInt t = offStart, i = 0, j = 0; t < offEnd; t++, i++) {
2492 myOriginalIds[i] = m_geometry->elements[t].m_originalId;
2493 for(MInt v = 0; v < nDim; v++)
2494 for(MInt d = 0; d < nDim; d++, j++)
2495 segTriangles[j] = m_geometry->elements[t].m_vertices[v][d];
2496 }
2497
2498 MIntScratchSpace numElemPerCPU(sumowners, AT_, "numElemPerCPU");
2499 MPI_Allgather(&numElements, 1, MPI_INT, numElemPerCPU.getPointer(), 1, MPI_INT, charComm, AT_, "numElements",
2500 "numElemPerCPU.getPointer()");
2501 MIntScratchSpace numTriInfoPerCPU(sumowners, AT_, "numTriInfoPerCPU");
2502
2503 m_log << " * triangles per domain: ";
2504 MInt sumallelem = 0;
2505 for(MInt i = 0; i < sumowners; i++) {
2506 m_log << numElemPerCPU[i] << " ";
2507 sumallelem += numElemPerCPU[i];
2508 numTriInfoPerCPU[i] = numElemPerCPU[i] * noTriInfo;
2509 }
2510 m_log << endl;
2511 m_log << " * sum of global triangles: " << sumallelem << endl;
2512
2513
2514 MIntScratchSpace displOrig(sumowners, AT_, "displOrig");
2515 MIntScratchSpace displTris(sumowners, AT_, "displTris");
2516 displOrig[0] = 0;
2517 displTris[0] = 0;
2518 for(MInt d = 1; d < sumowners; d++) {
2519 displOrig[d] = displOrig[d - 1] + numElemPerCPU[d - 1];
2520 displTris[d] = displTris[d - 1] + numTriInfoPerCPU[d - 1];
2521 }
2522
2523 MIntScratchSpace allOriginalIds(sumallelem, AT_, "allOriginalIds");
2524 MPI_Gatherv(myOriginalIds.getPointer(), numElements, MPI_INT, allOriginalIds.getPointer(),
2525 numElemPerCPU.getPointer(), displOrig.getPointer(), MPI_INT, 0, charComm, AT_,
2526 "myOriginalIds.getPointer()", "allOriginalIds.getPointer()");
2527
2528 MFloatScratchSpace allSegTriangles(noTriInfo * sumallelem, AT_, "allSegTriangles");
2529 MPI_Gatherv(segTriangles.getPointer(), noTriInfo * numElements, MPI_DOUBLE, allSegTriangles.getPointer(),
2530 numTriInfoPerCPU.getPointer(), displTris.getPointer(), MPI_DOUBLE, 0, charComm, AT_,
2531 "segTriangles.getPointer()", "allSegTriangles.getPointer()");
2532
2533 // now the firstOwner has received all relevant triangle information, calculate the characteristic length
2534 if(domainId() == firstOwner) {
2535 set<MInt> uniqueTriangles;
2536 for(MInt i = 0; i < sumallelem; i++)
2537 uniqueTriangles.insert(allOriginalIds[i]);
2538
2539 MInt noUniqueTris = uniqueTriangles.size();
2540 m_log << " * sum of unique triangles: " << noUniqueTris << endl;
2541
2542 MIntScratchSpace dbl(noUniqueTris, AT_, "dbl");
2543 for(MInt i = 0; i < noUniqueTris; i++)
2544 dbl[i] = 0;
2545
2546 // this contains the offsets in the list of triangles which we want to use for the calculation
2547 MIntScratchSpace keepOffsets(noUniqueTris, AT_, "keepOffsets");
2548 for(MInt i = 0, j = 0; i < sumallelem; i++) {
2549 MInt dist = distance(uniqueTriangles.begin(), uniqueTriangles.find(allOriginalIds[i]));
2550 if(dist != noUniqueTris && dbl[dist] == 0) {
2551 keepOffsets[j] = i * noTriInfo;
2552 dbl[dist] = 1;
2553 j++;
2554 }
2555 }
2556
2557 MInt num;
2558 MFloat** bndVs = m_geometry->GetBoundaryVertices(segmentId, allSegTriangles.getPointer(),
2559 keepOffsets.getPointer(), noUniqueTris, &num);
2560
2561 if(m_geometry->m_debugParGeom) writeSegmentBoundaryVTK(bndVs, num);
2562
2563 // Calculate circumference
2564 MFloat circ = m_geometry->calcCircumference(bndVs, num);
2565 m_log << " * circumference: " << circ << endl;
2566
2567 // Get size of surface
2568 MFloat size = m_geometry->GetBoundarySize(allSegTriangles.getPointer(), keepOffsets.getPointer(), noUniqueTris);
2569 m_log << " * area: " << size << endl;
2570
2571 // Get the hydraulic diameter
2572 MFloat hydraulic_diam = 4 * size / circ;
2573 m_log << " * hydraulic diameter: " << hydraulic_diam << endl;
2574
2575 // Get bounding box
2576 std::array<MFloat, nDim * 2> bBox;
2577 m_geometry->getBoundingBox(bBox.data());
2578
2579 // Find min and max in bounding box
2580 MFloat maxlength = 0.0;
2581
2582 for(MInt i = 0; i < nDim; i++)
2583 if(fabs(bBox[i + nDim] - bBox[i]) > maxlength) maxlength = fabs(bBox[i + nDim] - bBox[i]);
2584
2585 diameter = hydraulic_diam / (maxlength / FPOW2(maxLevel())) / reductionFactor();
2586 }
2587 }
2588 MPI_Bcast(&diameter, 1, MPI_DOUBLE, firstOwner, mpiComm(), AT_, "diameter");
2589
2590 return diameter;
2591 }
2592 return 0.0;
2593}
2594
2616template <MInt nDim>
2617inline MFloat LbSolver<nDim>::calcCharLenAll(MInt segmentId) {
2618 TRACE();
2619
2620 // Get the boundary points of the segment
2621 MInt num = 0;
2622 MFloat** bndVs = m_geometry->GetBoundaryVertices(segmentId, nullptr, nullptr, 0, &num);
2623
2624 // Calculate circumference
2625 MFloat circ = m_geometry->calcCircumference(bndVs, num);
2626
2627 // Get size of surface
2628 MFloat size = m_geometry->GetBoundarySize(segmentId);
2629
2630 // Get the hydraulic diameter
2631 MFloat hydraulic_diam = 4 * size / circ;
2632
2633 // Get bounding box
2634 std::array<MFloat, nDim * 2> bBox;
2635 m_geometry->getBoundingBox(bBox.data());
2636
2637 // Find min and max in bounding box
2638 MFloat maxlength = 0.0;
2639
2640 for(MInt i = 0; i < nDim; i++)
2641 if(fabs(bBox[i + nDim] - bBox[i]) > maxlength) maxlength = fabs(bBox[i + nDim] - bBox[i]);
2642
2643 m_log << " * circumference: " << circ << endl;
2644 m_log << " * area: " << size << endl;
2645 m_log << " * hydraulic diameter: " << hydraulic_diam << endl;
2646
2647 return hydraulic_diam / (maxlength / FPOW2(maxLevel())) / reductionFactor();
2648}
2649
2658template <MInt nDim>
2660 TRACE();
2661
2662 m_log << " + Calculating characteristic length:" << endl;
2663 m_log << " - type: " << (m_geometry->m_parallelGeometry ? "parallel" : "serial") << endl;
2664 m_log << " - segment id: " << segmentId << endl;
2665
2666 if constexpr(nDim == 2) {
2667 stringstream errorMsg;
2668 errorMsg << "ERROR: no implementation for the calulation of the characteristic length in 2D!" << endl;
2669 m_log << errorMsg.str();
2670 TERMM(1, errorMsg.str());
2671 }
2672
2673 MFloat ret = 0.0;
2674 if(m_geometry->m_parallelGeometry)
2675 ret = calcCharLenParallelSplit(segmentId);
2676 else
2677 ret = calcCharLenAll(segmentId);
2678
2679 m_log << " * charcteristic length: " << ret << " [cell units]" << endl;
2680
2681 return ret;
2682}
2683
2684template <MInt nDim>
2686 TRACE();
2687
2688 static ofstream ofl;
2689 MInt nghbrId = 0;
2690
2691 // write only the first timesteps
2692 if(globalTimeStep < FPOW2(maxLevel())) {
2693 ofl.open("cellInfo.dat", ios_base::app);
2694
2695 ofl << " cell informations: " << endl;
2696 ofl << " -------------------" << endl;
2697 ofl << " id=" << cellId << ", domain=" << domainId() << ", level=" << a_level(cellId) << endl;
2698 ofl << " (x,y,z)=(" << a_coordinate(cellId, 0) << ", " << a_coordinate(cellId, 1) << ", " << a_coordinate(cellId, 2)
2699 << ")" << endl;
2700 ofl << " neighbors: " << endl;
2701 for(MInt k = 0; k < 26; k++) {
2702 nghbrId = c_neighborId(cellId, k);
2703 ofl << " " << k << ": noNghbrs=" << a_hasNeighbor(cellId, k) << ", id=" << nghbrId << endl;
2704 ofl << " (x,y,z)=(" << a_coordinate(nghbrId, 0) << ", " << a_coordinate(nghbrId, 1) << ", "
2705 << a_coordinate(nghbrId, 2) << ")" << endl;
2706 }
2707 ofl << " children: " << endl;
2708 ofl << " noChildren: " << c_noChildren(cellId) << endl;
2709 for(MInt k = 0; k < IPOW2(nDim); k++) {
2710 ofl << " " << k << ": " << c_childId(cellId, k) << endl;
2711 }
2712 ofl << " parent: " << c_parentId(cellId) << endl;
2713
2714 if(a_isInterface(cellId)) {
2715 for(MInt k = 0; k < 26; k++) {
2716 nghbrId = c_neighborId(c_parentId(cellId), k);
2717 ofl << " " << k << ": parent noNghbrs=" << a_hasNeighbor(c_parentId(cellId), k) << ", id=" << nghbrId
2718 << endl;
2719 ofl << " (x,y,z)=(" << a_coordinate(nghbrId, 0) << ", " << a_coordinate(nghbrId, 1) << ", "
2720 << a_coordinate(nghbrId, 2) << ")" << endl;
2721 }
2722 }
2723
2724 ofl.close();
2725 }
2726}
2727
2728template <MInt nDim>
2729void LbSolver<nDim>::writeGridToTecFile(const MChar* fileName) {
2730 TRACE();
2731 // open a file
2732 MInt tmpCounter = 0;
2733 ofstream ofl;
2734 ofl.open(fileName);
2735
2736
2737 if(ofl) {
2738 if constexpr(nDim == 3) {
2739 // write header
2740 ofl << " TITLE = \" AMAZONAS Tecfile \" " << endl;
2741 ofl << R"( VARIABLES = "X", "Y", "Z", "U", "V", "W", "RHO")" << endl;
2742 ofl << " ZONE N=" << m_gridPoints->size() << ", ";
2743 ofl << " E=" << m_extractedCells->size() << ", ";
2744 ofl << " ZONETYPE="
2745 << "FEBRICK"
2746 << ", ";
2747 ofl << " DATAPACKING="
2748 << "SOLVER" << endl;
2749 ofl << " VARLOCATION=([4, 5, 6, 7]=CELLCENTERED)" << endl;
2750 // write the points to the datafile
2751 } else {
2752 // write header
2753 ofl << " TITLE = \" AMAZONAS Tecfile \" " << endl;
2754 ofl << R"( VARIABLES = "X", "Y", "U", "V", "RHO")" << endl;
2755 ofl << " ZONE N=" << m_gridPoints->size() << ", ";
2756 ofl << " E=" << m_extractedCells->size() << ", ";
2757 ofl << " ZONETYPE="
2758 << "FEQUADRILATERAL"
2759 << ", ";
2760 ofl << " DATAPACKING="
2761 << "SOLVER" << endl;
2762 ofl << " VARLOCATION=([3, 4, 5]=CELLCENTERED)" << endl;
2763 // write the points to the datafile
2764 }
2765
2766 for(MInt axisId = 0; axisId < nDim; axisId++) {
2767 for(MInt pointId = 0; pointId < m_gridPoints->size(); pointId++) {
2768 ofl << m_gridPoints->a[pointId].m_coordinates[axisId] << " ";
2769 tmpCounter++;
2770 if(tmpCounter == 10) {
2771 ofl << endl; // don't write everything in one line
2772 tmpCounter = 0;
2773 }
2774 }
2775 }
2776 ofl << endl;
2777
2778 tmpCounter = 0;
2779 for(MInt varId = 0; varId < nDim + 1; varId++) {
2780 for(MInt cellId = 0; cellId < m_extractedCells->size(); cellId++) {
2781 ofl << a_variable(m_extractedCells->a[cellId].m_cellId, varId) << " ";
2782
2783 tmpCounter++;
2784 if(tmpCounter == 8) {
2785 ofl << endl; // don't write everything in one line
2786 tmpCounter = 0;
2787 }
2788 }
2789 ofl << endl;
2790 }
2791 ofl << endl;
2792 ofl << endl;
2793
2794 tmpCounter = 0;
2795 // Write the cells to the datafile, i.e. write all the points of which
2796 // the cells consist. (TECPLOT needs FORTRAN COUNTING i.e. starting from 1, not 0
2797 // and TECPLOT needs a special point order (see manual))
2798 for(MInt cellId = 0; cellId < m_extractedCells->size(); cellId++) {
2799 ofl << m_extractedCells->a[cellId].m_pointIds[2] + 1 << " ";
2800 ofl << m_extractedCells->a[cellId].m_pointIds[3] + 1 << " ";
2801 ofl << m_extractedCells->a[cellId].m_pointIds[1] + 1 << " ";
2802 ofl << m_extractedCells->a[cellId].m_pointIds[0] + 1 << " ";
2803 if constexpr(nDim == 3) {
2804 ofl << m_extractedCells->a[cellId].m_pointIds[6] + 1 << " ";
2805 ofl << m_extractedCells->a[cellId].m_pointIds[7] + 1 << " ";
2806 ofl << m_extractedCells->a[cellId].m_pointIds[5] + 1 << " ";
2807 ofl << m_extractedCells->a[cellId].m_pointIds[4] + 1 << " ";
2808 }
2809 ofl << endl;
2810 }
2811 } // end if the file could be opened
2812 else {
2813 cerr << "ERROR! COULD NOT OPEN FILE " << fileName << " for writing! " << endl;
2814 }
2815}
2816
2817template <MInt nDim>
2818void LbSolver<nDim>::writeGridToVtkFile(const MChar* fileName) {
2819 TRACE();
2820 // open a file
2821
2822 ofstream ofl;
2823 MInt noFieldElements = nDim + 1;
2824
2825#define WRITE_CELLIDS
2826#define WRITE_OLD_VARIABLES
2827#define WRITE_DISTRIBUTIONS
2828#ifdef WRITE_CELLIDS
2829 noFieldElements++;
2830#endif
2831#ifdef WRITE_OLD_VARIABLES
2832 noFieldElements += nDim + 1;
2833#endif
2834#ifdef WRITE_DISTRIBUTIONS
2835 noFieldElements += m_noDistributions;
2836#endif
2837
2838 ofl.open(fileName);
2839 ofl.precision(12);
2840
2841 if(ofl) {
2842 // write header
2843 ofl << "# vtk DataFile Version 3.0 " << endl;
2844 ofl << "Amazonas output file" << endl;
2845 ofl << "BINARY " << endl;
2846 ofl << "DATASET UNSTRUCTURED_GRID " << endl;
2847 ofl << "POINTS " << m_gridPoints->size() << " float" << endl;
2848
2849 // ofl.close();
2850 // ofl.open(fileName, ios_base::out|ios_base::app|ios_base::binary);
2851 float tmpCoordinate = 0;
2852 for(MInt pointId = 0; pointId < m_gridPoints->size(); pointId++) {
2853 for(MInt axisId = 0; axisId < nDim; axisId++) {
2854 tmpCoordinate = (float)m_gridPoints->a[pointId].m_coordinates[axisId];
2855#ifdef SWAP_ENDIAN
2856 tmpCoordinate = floatSwap(tmpCoordinate);
2857#endif
2858 ofl.write(reinterpret_cast<char*>(&tmpCoordinate), sizeof(float));
2859 // ofl.write(reinterpret_cast<char *> (m_gridPoints->a[pointId].m_coordinates), sizeof(MFloat)*nDim);
2860 }
2861
2862 // Add dummy dimension for 2D
2863 if constexpr(nDim == 2) {
2864#ifdef SWAP_ENDIAN
2865 tmpCoordinate = floatSwap(0.0);
2866#endif
2867 ofl.write(reinterpret_cast<char*>(&tmpCoordinate), sizeof(float));
2868 }
2869 }
2870 ofl.close();
2871 ofl.open(fileName, ios_base::app);
2872 ofl << endl;
2873 // ------------------------------------------
2874 MInt number = IPOW2(nDim);
2875 ofl << "CELLS " << m_extractedCells->size() << " " << m_extractedCells->size() * (number + 1) << endl;
2876 // Write the cells to the datafile, i.e. write all the points of which
2877 // the cells consist.
2878 ofl.close();
2879 ofl.open(fileName, ios_base::out | ios_base::app | ios_base::binary);
2880
2881 MInt pointId = 0;
2882#ifdef SWAP_ENDIAN
2883 number = intSwap(number);
2884#endif
2885 for(MInt cellId = 0; cellId < m_extractedCells->size(); cellId++) {
2886 ofl.write(reinterpret_cast<char*>(&number), sizeof(MInt));
2887 // ofl.write(reinterpret_cast<char *> (m_extractedCells->a[ cellId ].m_pointIds), sizeof(MInt)*number);
2888 for(MInt points = 0; points < IPOW2(nDim); points++) {
2889 pointId = m_extractedCells->a[cellId].m_pointIds[points];
2890#ifdef SWAP_ENDIAN
2891 pointId = intSwap(pointId);
2892#endif
2893 ofl.write(reinterpret_cast<char*>(&pointId), sizeof(MInt));
2894 }
2895 }
2896 ofl.close();
2897 ofl.open(fileName, ios_base::app);
2898 ofl << endl;
2899 ofl << "CELL_TYPES " << m_extractedCells->size() << endl;
2900 ofl.close();
2901 ofl.open(fileName, ios_base::out | ios_base::app | ios_base::binary);
2902 if constexpr(nDim == 3) {
2903 pointId = 11; // use VTK_VOXEL in 3D
2904 } else {
2905 pointId = 8; // use VTK_PIXEL in 2D
2906 }
2907
2908#ifdef SWAP_ENDIAN
2909 pointId = intSwap(pointId);
2910#endif
2911 for(MInt cellId = 0; cellId < m_extractedCells->size(); cellId++) {
2912 ofl.write(reinterpret_cast<char*>(&pointId), sizeof(MInt));
2913 }
2914
2915 ofl.close();
2916 ofl.open(fileName, ios_base::app);
2917 ofl << endl;
2918 ofl << "CELL_DATA " << m_extractedCells->size() << endl;
2919 ofl << "SCALARS scalars double" << endl;
2920 ofl << "LOOKUP_TABLE default" << endl;
2921
2922 ofl.close();
2923 ofl.open(fileName, ios_base::out | ios_base::app | ios_base::binary);
2924 MFloat fnumber = 1.0;
2925#ifdef SWAP_ENDIAN
2926 fnumber = doubleSwap(fnumber);
2927#endif
2928 for(MInt cellId = 0; cellId < m_extractedCells->size(); cellId++) {
2929 ofl.write(reinterpret_cast<char*>(&fnumber), sizeof(MFloat));
2930 }
2931 ofl.close();
2932
2933#ifdef WRITE_VECTOR_DATA
2934 // Write velocities as vector
2935 ofl.open(fileName, ios_base::app);
2936 ofl << endl;
2937 ofl << "VECTORS vectors double" << endl;
2938 ofl.close();
2939
2940 ofl.open(fileName, ios_base::out | ios_base::app | ios_base::binary);
2941 for(MInt cellId = 0; cellId < m_extractedCells->size(); cellId++) {
2942 for(MInt varId = 0; varId < nDim; varId++) {
2943 fnumber = a_variable(m_extractedCells->a[cellId].m_cellId, varId);
2944#ifdef SWAP_ENDIAN
2945 fnumber = doubleSwap(fnumber);
2946#endif
2947 ofl.write(reinterpret_cast<char*>(&fnumber), sizeof(MFloat));
2948 }
2949 }
2950
2951 ofl.close();
2952#endif
2953
2954 // Write all variables as field data
2955 ofl.open(fileName, ios_base::app);
2956 ofl << endl;
2957 ofl << "FIELD FieldData " << noFieldElements << endl;
2958
2959 for(MInt varId = 0; varId < nDim + 1; varId++) {
2960 ofl.close();
2961 ofl.open(fileName, ios_base::app);
2962 ofl << endl;
2963 ofl << "variables" << varId << " 1 " << m_extractedCells->size() << " double" << endl;
2964 ofl.close();
2965 ofl.open(fileName, ios_base::out | ios_base::app | ios_base::binary);
2966 for(MInt cellId = 0; cellId < m_extractedCells->size(); cellId++) {
2967 // if(varId==nDim)//subtract 1 from density
2968 // fnumber = a_variable(m_extractedCells->a[cellId].m_cellId, varId);
2969 // else
2970 // if(varId==nDim)//write actual domainId
2971 // fnumber = domainId();
2972 // else
2973 fnumber = a_variable(m_extractedCells->a[cellId].m_cellId, varId);
2974#ifdef SWAP_ENDIAN
2975 fnumber = doubleSwap(fnumber);
2976#endif
2977 ofl.write(reinterpret_cast<char*>(&fnumber), sizeof(MFloat));
2978 }
2979 }
2980
2981#ifdef WRITE_OLD_VARIABLES
2982 for(MInt varId = 0; varId < nDim + 1; varId++) {
2983 ofl.close();
2984 ofl.open(fileName, ios_base::app);
2985 ofl << endl;
2986 ofl << "residual" << varId << " 1 " << m_extractedCells->size() << " double" << endl;
2987 ofl.close();
2988 ofl.open(fileName, ios_base::out | ios_base::app | ios_base::binary);
2989 for(MInt cellId = 0; cellId < m_extractedCells->size(); cellId++) {
2990 fnumber = a_variable(m_extractedCells->a[cellId].m_cellId, varId)
2991 - a_oldVariable(m_extractedCells->a[cellId].m_cellId, varId);
2992#ifdef SWAP_ENDIAN
2993 fnumber = doubleSwap(fnumber);
2994#endif
2995 ofl.write(reinterpret_cast<char*>(&fnumber), sizeof(MFloat));
2996 }
2997 }
2998#endif
2999#ifdef WRITE_DISTRIBUTIONS
3000 /***********WRITE OUT CELL DISTRIBUTIONS***********************/
3001 for(MInt distId = 0; distId < m_noDistributions; distId++) {
3002 ofl.close();
3003 ofl.open(fileName, ios_base::app);
3004 ofl << endl;
3005 ofl << "distributions" << distId << " 1 " << m_extractedCells->size() << " double" << endl;
3006 ofl.close();
3007 ofl.open(fileName, ios_base::out | ios_base::app | ios_base::binary);
3008 for(MInt cellId = 0; cellId < m_extractedCells->size(); cellId++) {
3009 fnumber = a_distribution(m_extractedCells->a[cellId].m_cellId, distId);
3010#ifdef SWAP_ENDIAN
3011 fnumber = doubleSwap(fnumber);
3012#endif
3013 ofl.write(reinterpret_cast<char*>(&fnumber), sizeof(MFloat));
3014 }
3015 }
3016#endif
3017
3018#ifdef WRITE_CELLIDS
3019 /***********WRITE OUT CELL IDS***********************/
3020 MInt inumber = 0;
3021 ofl.close();
3022 ofl.open(fileName, ios_base::app);
3023 ofl << endl;
3024 ofl << "cellIds "
3025 << " 1 " << m_extractedCells->size() << " int" << endl;
3026 ofl.close();
3027 ofl.open(fileName, ios_base::out | ios_base::app | ios_base::binary);
3028 for(MInt cellId = 0; cellId < m_extractedCells->size(); cellId++) {
3029 inumber = m_extractedCells->a[cellId].m_cellId;
3030#ifdef SWAP_ENDIAN
3031 inumber = intSwap(inumber);
3032#endif
3033 ofl.write(reinterpret_cast<char*>(&inumber), sizeof(MInt));
3034 }
3035#endif
3036 ofl.close();
3037 } // end if the file could be opened
3038 else {
3039 cerr << "ERROR! COULD NOT OPEN FILE " << fileName << " for writing! " << endl;
3040 }
3041}
3042
3043template <MInt nDim>
3044void LbSolver<nDim>::writeGridToVtkFileAscii(const MChar* fileName) {
3045 TRACE();
3046 // open a file
3047 MInt tmpCounter = 0;
3048 ofstream ofl;
3049 ofl.open(fileName);
3050
3051 if(ofl) {
3052 if constexpr(nDim == 3) {
3053 // write header
3054 ofl << "# vtk DataFile Version 2.0 " << endl;
3055 ofl << "Amazonas output file" << endl;
3056 ofl << "ASCII " << endl;
3057 ofl << "DATASET UNSTRUCTURED_GRID " << endl;
3058 ofl << "POINTS " << m_gridPoints->size() << " float" << endl;
3059
3060 for(MInt pointId = 0; pointId < m_gridPoints->size(); pointId++) {
3061 for(MInt axisId = 0; axisId < nDim; axisId++) {
3062 ofl << m_gridPoints->a[pointId].m_coordinates[axisId] << " ";
3063 }
3064 ofl << " ";
3065 tmpCounter++;
3066 if(tmpCounter == 10) {
3067 ofl << endl; // don't write everything in one line
3068 tmpCounter = 0;
3069 }
3070 }
3071 ofl << endl;
3072 ofl << endl;
3073
3074 ofl << "CELLS " << m_extractedCells->size() << " " << m_extractedCells->size() * 9 << endl;
3075 // Write the cells to the datafile, i.e. write all the points of which
3076 // the cells consist.
3077 for(MInt cellId = 0; cellId < m_extractedCells->size(); cellId++) {
3078 ofl << "8 ";
3079 ofl << m_extractedCells->a[cellId].m_pointIds[0] << " ";
3080 ofl << m_extractedCells->a[cellId].m_pointIds[1] << " ";
3081 ofl << m_extractedCells->a[cellId].m_pointIds[2] << " ";
3082 ofl << m_extractedCells->a[cellId].m_pointIds[3] << " ";
3083 if constexpr(nDim == 3) {
3084 ofl << m_extractedCells->a[cellId].m_pointIds[4] << " ";
3085 ofl << m_extractedCells->a[cellId].m_pointIds[5] << " ";
3086 ofl << m_extractedCells->a[cellId].m_pointIds[6] << " ";
3087 ofl << m_extractedCells->a[cellId].m_pointIds[7] << " ";
3088 }
3089 ofl << endl;
3090 }
3091
3092 ofl << "CELL_TYPES " << m_extractedCells->size() << endl;
3093 for(MInt cellId = 0; cellId < m_extractedCells->size(); cellId++) {
3094 ofl << "11 " << endl; // VTK_VOXEL
3095 }
3096 ofl << "CELL_DATA " << m_extractedCells->size() << endl;
3097 ofl << "SCALARS scalars float" << endl;
3098 ofl << "LOOKUP_TABLE default" << endl;
3099 for(MInt cellId = 0; cellId < m_extractedCells->size(); cellId++) {
3100 ofl << "1.0" << endl; // VTK_VOXEL
3101 }
3102 ofl << "FIELD FieldData " << nDim + 1 << endl;
3103 // Write variables
3104 for(MInt varId = 0; varId < nDim + 1; varId++) {
3105 ofl << "variables" << varId << " 1 " << m_extractedCells->size() << " float" << endl;
3106 tmpCounter = 0;
3107 for(MInt cellId = 0; cellId < m_extractedCells->size(); cellId++) {
3108 ofl << a_variable(m_extractedCells->a[cellId].m_cellId, varId) << " ";
3109 tmpCounter++;
3110 }
3111 ofl << endl; // don't write everything in one line
3112 }
3113 ofl << endl;
3114
3115 // Write variables old variables
3116 ofl << "FIELD FieldData " << nDim + 1 << endl;
3117 for(MInt varId = 0; varId < nDim + 1; varId++) {
3118 ofl << "oldVariables" << varId << " 1 " << m_extractedCells->size() << " float" << endl;
3119 tmpCounter = 0;
3120 for(MInt cellId = 0; cellId < m_extractedCells->size(); cellId++) {
3121 ofl << a_oldVariable(m_extractedCells->a[cellId].m_cellId, varId) << " ";
3122 tmpCounter++;
3123 }
3124 ofl << endl; // don't write everything in one line
3125 }
3126 ofl << endl;
3127 }
3128 } // end if the file could be opened
3129 else {
3130 cerr << "ERROR! COULD NOT OPEN FILE " << fileName << " for writing! " << endl;
3131 }
3132}
3133
3155template <MInt nDim>
3157 TRACE();
3158
3159 if constexpr(nDim != 2 && nDim != 3) {
3160 cerr << " In global function loadGridFlowVariablesThermalPar: wrong number of dimensions !" << endl;
3161 exit(0);
3162 return;
3163 }
3164
3165 using namespace maia::parallel_io;
3166 ParallelIo parallelIo(fileName, PIO_READ, mpiComm());
3167
3168 // read time
3169 if(!m_initFromRestartFile) {
3170 parallelIo.getAttribute(&m_time, "time");
3171 parallelIo.getAttribute(&globalTimeStep, "globalTimeStep");
3172 }
3173
3174 if(m_velocityControl.restart) {
3175 parallelIo.getAttribute(&m_velocityControl.lastGlobalAvgV, "lastGlobalAvgV");
3176 parallelIo.getAttribute(&m_velocityControl.previousError, "previousError");
3177 parallelIo.getAttribute(&m_velocityControl.integratedError, "integratedError");
3178 parallelIo.getAttribute(&m_velocityControl.derivedError, "derivedError");
3179 for(MInt i = 0; i < nDim; i++) {
3180 parallelIo.getAttribute(&m_volumeAccel[i], "volumeAcceleration_" + std::to_string(i));
3181 }
3182 }
3183
3184 MInt coarse_count = 0;
3185 parallelIo.readScalar(&coarse_count, "noCells");
3186
3187 // 1. count all non-leaf cells and determine offsets for reading the file
3188 MInt local_count = 0;
3189 for(MInt i = 0; i < noInternalCells(); i++) {
3190 if(c_noChildren(i) > 0) local_count++;
3191 }
3192
3193 MIntScratchSpace nonFine_count(noDomains(), AT_, "nonFineCount");
3194 for(MInt d = 0; d < noDomains(); d++)
3195 if(d == domainId())
3196 nonFine_count[d] = local_count;
3197 else
3198 nonFine_count[d] = 0;
3199
3200 MPI_Allreduce(MPI_IN_PLACE, nonFine_count.getPointer(), noDomains(), MPI_INT, MPI_SUM, mpiComm(), AT_, "MPI_IN_PLACE",
3201 "nonFine_count.getPointer()");
3202
3203 MInt off = 0;
3204 for(MInt d = 0; d < domainId(); d++)
3205 off += nonFine_count[d];
3206
3207 MInt all = 0;
3208 for(MInt d = 0; d < noDomains(); d++)
3209 all += nonFine_count[d];
3210
3211 m_log << " - loading information: " << endl;
3212 m_log << " * number of all cells: " << all << endl;
3213 m_log << " * number of local cells: " << local_count << endl;
3214 m_log << " * domain offset: " << off << endl;
3215
3216 const MPI_Offset firstGlobalId = off;
3217 const MPI_Offset localNoCells = local_count;
3218 parallelIo.setOffset(localNoCells, firstGlobalId);
3219
3220 m_log << " - determining ids of the coarse cells" << endl;
3221 // 2. determine ids of the coarser cells
3222 MIntScratchSpace coarse_ids(local_count, AT_, "coarse_ids");
3223 for(MInt i = 0, j = 0; i < noInternalCells(); ++i)
3224 if(c_noChildren(i) > 0) {
3225 coarse_ids[j] = i;
3226 j++;
3227 }
3228
3229 m_log << " - reading variables " << endl;
3230 // 3. read variables
3231 {
3232 MFloatScratchSpace tmparray(local_count, AT_, "tmparray");
3233
3234 // first read all variables
3235 for(MInt v = 0; v < m_noVariables; v++) {
3236 MString name = "variables" + to_string(v);
3237
3238 parallelIo.readArray(tmparray.getPointer(), name);
3239 for(MInt i = 0; i < local_count; ++i)
3240 a_variable(coarse_ids[i], v) = tmparray.p[i];
3241 }
3242
3243 // now read all oldVariables
3244 for(MInt v = 0; v < m_noVariables; v++) {
3245 MString name = "oldVariables" + to_string(v);
3246
3247 parallelIo.readArray(tmparray.getPointer(), name);
3248 for(MInt i = 0; i < local_count; ++i)
3249 a_variable(coarse_ids[i], v) = tmparray.p[i];
3250 }
3251 }
3252
3253 m_log << " - exchanging coarse window cells" << endl;
3254 // 4. exchange coarse windows
3255 // 4.1 collect windows and halos on a coarse level
3256 m_log << " * collect windows and halos on a coarse level " << endl;
3257 vector<vector<MInt>> coarseWindowPD;
3258 vector<vector<MInt>> coarseHaloPD;
3259 for(MInt d = 0; d < noNeighborDomains(); d++) {
3260 vector<MInt> cW;
3261 for(MInt w = 0; w < noWindowCells(d); w++)
3262 if(a_level(windowCell(d, w)) == maxLevel() - 1) cW.push_back(windowCell(d, w));
3263
3264 vector<MInt> cH;
3265 for(MInt h = 0; h < noHaloCells(d); h++)
3266 if(a_level(haloCell(d, h)) == maxLevel() - 1) cH.push_back(haloCell(d, h));
3267
3268 coarseWindowPD.push_back(cW);
3269 coarseHaloPD.push_back(cH);
3270 }
3271
3272 m_log << " = number of coarse window cells for " << noNeighborDomains() << " neighboring domains" << endl;
3273 for(MInt d = 0; d < noNeighborDomains(); d++)
3274 m_log << " # domain " << neighborDomain(d) << ":" << coarseWindowPD[d].size() << endl;
3275 m_log << " = number of coarse halo cells for " << noNeighborDomains() << " neighboring domains" << endl;
3276 for(MInt d = 0; d < noNeighborDomains(); d++)
3277 m_log << " # domain " << neighborDomain(d) << ":" << coarseHaloPD[d].size() << endl;
3278
3279 // 4.2 construct send buffer and send
3280 m_log << " * constructing send buffer with size: ";
3281 MInt totalWindowBuf = 0;
3282 MIntScratchSpace windowOff(noNeighborDomains(), AT_, "windowOff");
3283 for(MInt d = 0; d < noNeighborDomains(); d++) {
3284 windowOff[d] = totalWindowBuf;
3285 totalWindowBuf += coarseWindowPD[d].size() * 2 * m_noVariables;
3286 }
3287
3288 m_log << totalWindowBuf << endl;
3289
3290 MFloatScratchSpace sendBuf(totalWindowBuf, AT_, "sendBuf");
3291 for(MInt d = 0; d < noNeighborDomains(); d++) {
3292 MInt pos = windowOff[d];
3293 for(MInt c = 0; c < (MInt)coarseWindowPD[d].size(); c++) {
3294 for(MInt v = 0; v < m_noVariables; v++, pos++)
3295 sendBuf[pos] = a_variable(coarseWindowPD[d][c], v);
3296 for(MInt v = 0; v < m_noVariables; v++, pos++)
3297 sendBuf[pos] = a_oldVariable(coarseWindowPD[d][c], v);
3298 }
3299 MPI_Issend(&sendBuf[windowOff[d]], coarseWindowPD[d].size() * 2 * m_noVariables, MPI_DOUBLE, neighborDomain(d), 0,
3300 mpiComm(), &mpi_request[d], AT_, "sendBuf[windowOff[d]]");
3301 }
3302
3303 // 4.3 construct receive buffer and receive
3304 m_log << " * constructing receive buffer and receive" << endl;
3305 MInt totalHaloBuf = 0;
3306 MIntScratchSpace haloOff(noNeighborDomains(), AT_, "haloOff");
3307 for(MInt d = 0; d < noNeighborDomains(); d++) {
3308 haloOff[d] = totalHaloBuf;
3309 totalHaloBuf += coarseHaloPD[d].size() * 2 * m_noVariables;
3310 }
3311
3312 MFloatScratchSpace receiveBuf(totalHaloBuf, AT_, "receiveBuf");
3313
3314 MPI_Status status;
3315 for(MInt d = 0; d < noNeighborDomains(); d++)
3316 MPI_Recv(&receiveBuf[haloOff[d]], coarseHaloPD[d].size() * 2 * m_noVariables, MPI_DOUBLE, neighborDomain(d), 0,
3317 mpiComm(), &status, AT_, "receiveBuf[haloOff[d]]");
3318
3319 for(MInt d = 0; d < noNeighborDomains(); d++)
3320 MPI_Wait(&mpi_request[d], &status, AT_);
3321
3322 // 4.4 distribute received information
3323 m_log << " * distributing received information" << endl;
3324 for(MInt d = 0; d < noNeighborDomains(); d++) {
3325 MInt pos = haloOff[d];
3326 for(MInt c = 0; c < (MInt)coarseHaloPD[d].size(); c++) {
3327 for(MInt v = 0; v < m_noVariables; v++, pos++)
3328 a_variable(coarseHaloPD[d][c], v) = receiveBuf[pos];
3329 for(MInt v = 0; v < m_noVariables; v++, pos++)
3330 a_oldVariable(coarseHaloPD[d][c], v) = receiveBuf[pos];
3331 }
3332 }
3333
3334 // 5. fill finer cells
3335 m_log << " - filling all finer cells" << endl;
3336 MInt elems = IPOW2(nDim);
3337 MIntScratchSpace dirs(elems, AT_, "elems");
3338
3339 if constexpr(nDim == 2) {
3340 for(MInt d = 0; d < elems; d++) {
3341 dirs[d] = childPos2D[d];
3342 }
3343 } else {
3344 for(MInt d = 0; d < elems; d++) {
3345 dirs[d] = childPos3D[d];
3346 }
3347 }
3348
3349 // 5.1 fill all cells surrounded by neighbors by interpolation all others by the restart values
3350 m_log << " * filling all cells with nieghbors" << endl;
3351 for(MInt i = 0; i < noInternalCells(); i++)
3352 if(a_level(i) == maxLevel()) {
3353 MInt parent = c_parentId(i);
3354
3355 // find myself under my parent
3356 MInt l = 0;
3357 for(MInt c = 0; c < elems; c++)
3358 if(c_childId(parent, c) == i) {
3359 l = c;
3360 break;
3361 }
3362
3363 MInt dir = dirs[l];
3364
3365 // great neighbor in direction
3366 if(a_hasNeighbor(parent, dir)) {
3367 MInt parentNeighbor = c_neighborId(parent, dir);
3368 for(MInt v = 0; v < m_noVariables; v++) {
3369 a_variable(i, v) = F3B4 * a_variable(parent, v) + F1B4 * a_variable(parentNeighbor, v);
3370 a_oldVariable(i, v) = F3B4 * a_oldVariable(parent, v) + F1B4 * a_oldVariable(parentNeighbor, v);
3371 }
3372 }
3373 // otherwise fill with value from restart
3374 else {
3375 for(MInt v = 0; v < m_noVariables; v++) {
3376 a_variable(i, v) = a_variable(parent, v);
3377 a_oldVariable(i, v) = a_oldVariable(parent, v);
3378 }
3379 }
3380 }
3381
3382 // 5.2 fill finer halo cells
3383 m_log << " * filling halos" << endl;
3384
3385 MInt noNeigh = pow(nDim, nDim);
3386 for(MInt d = 0; d < noNeighborDomains(); d++)
3387 for(MInt h = 0; h < noHaloCells(d); h++) {
3388 MInt haloId = haloCell(d, h);
3389
3390 if(a_level(haloId) == maxLevel()) {
3391 // find a neighbor to the halo cell which is a window cell
3392 MInt haloWinNeigh = -1;
3393 MInt dir = 0;
3394 for(; dir < noNeigh; dir++)
3395 if(a_hasNeighbor(haloId, dir) && a_isWindow(c_neighborId(haloId, dir))) {
3396 haloWinNeigh = c_neighborId(haloId, dir);
3397 break;
3398 }
3399
3400 // found a window cell
3401 if(haloWinNeigh >= 0) {
3402 MInt winParent = c_parentId(haloWinNeigh);
3403 const MInt opp =
3404 LbLatticeDescriptorBase<3>::oppositeDist(dir); // TODO labels:LB dxqy: 3 replaceable by nDim ?
3405
3406 // check neighbor of the parent of the window cell in opoosite direction
3407 MInt parent = -1;
3408 if(winParent >= 0 && a_hasNeighbor(winParent, opp)) parent = c_neighborId(winParent, opp);
3409
3410 if(parent < 0) parent = winParent;
3411
3412 for(MInt v = 0; v < m_noVariables; v++) {
3413 a_variable(haloId, v) = a_variable(parent, v);
3414 a_oldVariable(haloId, v) = a_oldVariable(parent, v);
3415 }
3416 }
3417 }
3418 }
3419}
3420
3433template <MInt nDim>
3435 TRACE();
3436 if constexpr(nDim != 2 && nDim != 3) {
3437 cerr << " In global function loadGridFlowVariablesThermalPar: wrong number of dimensions !" << endl;
3438 exit(0);
3439 return;
3440 }
3441
3442 using namespace maia::parallel_io;
3443 ParallelIo parallelIo(fileName, PIO_READ, mpiComm());
3444
3445 // read time
3446 if(!m_initFromRestartFile) {
3447 parallelIo.getAttribute(&m_time, "time");
3448 parallelIo.getAttribute(&globalTimeStep, "globalTimeStep");
3449 }
3450
3451 if(m_velocityControl.restart) {
3452 parallelIo.getAttribute(&m_velocityControl.lastGlobalAvgV, "lastGlobalAvgV");
3453 parallelIo.getAttribute(&m_velocityControl.previousError, "previousError");
3454 parallelIo.getAttribute(&m_velocityControl.integratedError, "integratedError");
3455 parallelIo.getAttribute(&m_velocityControl.derivedError, "derivedError");
3456 for(MInt i = 0; i < nDim; i++) {
3457 parallelIo.getAttribute(&m_volumeAccel[i], "volumeAcceleration_" + std::to_string(i));
3458 }
3459 }
3460
3461 MInt pv_veloc[nDim];
3462 if constexpr(nDim == 2) {
3463 pv_veloc[0] = PV->U;
3464 pv_veloc[1] = PV->V;
3465 } else {
3466 pv_veloc[0] = PV->U;
3467 pv_veloc[1] = PV->V;
3468 pv_veloc[2] = PV->W;
3469 }
3470 MInt pvrho = PV->RHO;
3471 MInt pvt = PV->T;
3472 MInt pvc = PV->C;
3473 MString rhoName;
3474 MString oldrhoName;
3475 MString temperatureName;
3476 MString oldtemperatureName;
3477 MString concentrationName;
3478 MString oldconcentrationName;
3479 MString EELiquidName;
3480 MString distributions;
3481 MString oldDistributions;
3482 MString distributionsThermal;
3483 MString oldDistributionsThermal;
3484 MString distributionsTransport;
3485 MString oldDistributionsTransport;
3486 if constexpr(nDim == 3) {
3487 rhoName = "variables3";
3488 oldrhoName = "oldVariables3";
3489 temperatureName = "variables4";
3490 oldtemperatureName = "oldVariables4";
3491 concentrationName = "variables5";
3492 oldconcentrationName = "oldVariables5";
3493 EELiquidName = "oldVariables4";
3494 } else {
3495 rhoName = "variables2";
3496 oldrhoName = "oldVariables2";
3497 temperatureName = "variables3";
3498 oldtemperatureName = "oldVariables3";
3499 concentrationName = "variables4";
3500 oldconcentrationName = "oldVariables4";
3501 }
3502
3503
3504 // Set file offsets (first globalId and #of cells to be read by this process):
3505 const MPI_Offset firstGlobalId = domainOffset(domainId());
3506 const MPI_Offset localNoCells = noInternalCells();
3507 parallelIo.setOffset(localNoCells, firstGlobalId);
3508
3509
3510 // first read 2D variables
3511 {
3512 MFloatScratchSpace tmparray(noInternalCells(), AT_, "tmparray");
3513
3514 // Velocity u
3515 parallelIo.readArray(tmparray.getPointer(), "variables0");
3516 for(MInt i = 0; i < noInternalCells(); ++i)
3517 a_variable(i, pv_veloc[0]) = tmparray[i];
3518
3519 // Velocity old_u
3520 parallelIo.readArray(tmparray.getPointer(), "oldVariables0");
3521 for(MInt i = 0; i < noInternalCells(); ++i)
3522 a_oldVariable(i, pv_veloc[0]) = tmparray[i];
3523
3524 // Velocity v
3525 parallelIo.readArray(tmparray.getPointer(), "variables1");
3526 for(MInt i = 0; i < noInternalCells(); ++i)
3527 a_variable(i, pv_veloc[1]) = tmparray.p[i];
3528
3529 // Velocity old_v
3530 parallelIo.readArray(tmparray.getPointer(), "oldVariables1");
3531 for(MInt i = 0; i < noInternalCells(); ++i)
3532 a_oldVariable(i, pv_veloc[1]) = tmparray.p[i];
3533
3534 // Density rho
3535 parallelIo.readArray(tmparray.getPointer(), rhoName);
3536 for(MInt i = 0; i < noInternalCells(); ++i)
3537 a_variable(i, pvrho) = tmparray.p[i];
3538
3539 // Density old_rho
3540 parallelIo.readArray(tmparray.getPointer(), oldrhoName);
3541 for(MInt i = 0; i < noInternalCells(); ++i)
3542 a_oldVariable(i, pvrho) = tmparray.p[i];
3543
3544 if(m_isThermal) {
3545 // Temperature t
3546 parallelIo.readArray(tmparray.getPointer(), temperatureName);
3547 for(MInt i = 0; i < noInternalCells(); ++i)
3548 a_variable(i, pvt) = tmparray.p[i];
3549
3550 // Temperature old_t
3551 parallelIo.readArray(tmparray.getPointer(), oldtemperatureName);
3552 for(MInt i = 0; i < noInternalCells(); ++i)
3553 a_oldVariable(i, pvt) = tmparray.p[i];
3554 }
3555 if(m_isTransport) {
3556 // Concentration c
3557 parallelIo.readArray(tmparray.getPointer(), concentrationName);
3558 for(MInt i = 0; i < noInternalCells(); ++i)
3559 a_variable(i, pvc) = tmparray.p[i];
3560
3561 // Concentration old_c
3562 parallelIo.readArray(tmparray.getPointer(), oldconcentrationName);
3563 for(MInt i = 0; i < noInternalCells(); ++i)
3564 a_oldVariable(i, pvc) = tmparray.p[i];
3565 }
3566 if(m_isEELiquid) {
3567 if(!m_EELiquid.restartWithoutAlpha) {
3568 parallelIo.readArray(tmparray.getPointer(), EELiquidName);
3569 for(MInt i = 0; i < noInternalCells(); ++i)
3570 a_alphaGas(i) = tmparray.p[i];
3571 } else {
3572 for(MInt i = 0; i < noInternalCells(); ++i)
3573 a_alphaGas(i) = 0.0;
3574 }
3575 }
3576 }
3577
3578 // now read 3D variables
3579 if constexpr(nDim == 3) {
3580 MFloatScratchSpace tmparray(noInternalCells(), AT_, "tmparray");
3581
3582 // Velocity w
3583 parallelIo.readArray(tmparray.getPointer(), "variables2");
3584 for(MInt i = 0; i < noInternalCells(); ++i)
3585 a_variable(i, pv_veloc[2]) = tmparray.p[i];
3586
3587 // Velocity old_w
3588 parallelIo.readArray(tmparray.getPointer(), "oldVariables2");
3589 for(MInt i = 0; i < noInternalCells(); ++i)
3590 a_oldVariable(i, pv_veloc[2]) = tmparray.p[i];
3591 }
3592
3593
3594 // Distributions
3595 for(MInt j = 0; j < m_noDistributions; j++) {
3596 distributions = "distributions" + to_string(j);
3597
3598 MFloatScratchSpace tmp_distributions(noInternalCells(), AT_, "tmp_distributions");
3599 parallelIo.readArray(tmp_distributions.getPointer(), distributions);
3600 for(MInt i = 0; i < noInternalCells(); ++i)
3601 a_distribution(i, j) = tmp_distributions.p[i];
3602 }
3603
3604 // old Distributions
3605 for(MInt j = 0; j < m_noDistributions; j++) {
3606 oldDistributions = "oldDistributions" + to_string(j);
3607
3608 MFloatScratchSpace tmp_oldDistributions(noInternalCells(), AT_, "tmp_oldDistributions");
3609 parallelIo.readArray(tmp_oldDistributions.getPointer(), oldDistributions);
3610 for(MInt i = 0; i < noInternalCells(); ++i)
3611 a_oldDistribution(i, j) = tmp_oldDistributions.p[i];
3612 }
3613
3614 if(m_isThermal) {
3615 // Thermal Distributions
3616 for(MInt j = 0; j < m_noDistributions; j++) {
3617 distributionsThermal = "distributionsThermal" + to_string(j);
3618
3619 MFloatScratchSpace tmp_distributionsThermal(noInternalCells(), AT_, "tmp_distributionsThermal");
3620 parallelIo.readArray(tmp_distributionsThermal.getPointer(), distributionsThermal);
3621 for(MInt i = 0; i < noInternalCells(); ++i)
3622 a_distributionThermal(i, j) = tmp_distributionsThermal.p[i];
3623 }
3624
3625 // old Thermal Distributions
3626 for(MInt j = 0; j < m_noDistributions; j++) {
3627 oldDistributionsThermal = "oldDistributionsThermal" + to_string(j);
3628
3629 MFloatScratchSpace tmp_oldDistributionsThermal(noInternalCells(), AT_, "tmp_oldDistributionsThermal");
3630 parallelIo.readArray(tmp_oldDistributionsThermal.getPointer(), oldDistributionsThermal);
3631 for(MInt i = 0; i < noInternalCells(); ++i)
3632 a_oldDistributionThermal(i, j) = tmp_oldDistributionsThermal.p[i];
3633 }
3634
3635 for(MInt i = 0; i < a_noCells(); i++) {
3636 a_kappa(i) = m_kappa;
3637 }
3638 }
3639
3640 if(m_isTransport) {
3641 // Transport Distributions
3642 for(MInt j = 0; j < m_noDistributions; j++) {
3643 distributionsTransport = "distributionsTransport" + to_string(j);
3644
3645 MFloatScratchSpace tmp_distributionsTransport(noInternalCells(), AT_, "tmp_distributionsTransport");
3646 parallelIo.readArray(tmp_distributionsTransport.getPointer(), distributionsTransport);
3647 for(MInt i = 0; i < noInternalCells(); ++i)
3648 a_distributionTransport(i, j) = tmp_distributionsTransport.p[i];
3649 }
3650
3651 // old Transport Distributions
3652 for(MInt j = 0; j < m_noDistributions; j++) {
3653 oldDistributionsTransport = "oldDistributionsTransport" + to_string(j);
3654
3655 MFloatScratchSpace tmp_oldDistributionsTransport(noInternalCells(), AT_, "tmp_oldDistributionsTransport");
3656 parallelIo.readArray(tmp_oldDistributionsTransport.getPointer(), oldDistributionsTransport);
3657 for(MInt i = 0; i < noInternalCells(); ++i)
3658 a_oldDistributionTransport(i, j) = tmp_oldDistributionsTransport.p[i];
3659 }
3660
3661 for(MInt i = 0; i < a_noCells(); i++) {
3662 a_diffusivity(i) = m_diffusivity;
3663 }
3664 }
3665
3666 if(!m_nonNewtonian) {
3667 for(MInt i = 0; i < a_noCells(); i++) {
3668 initNu(i, m_nu);
3669 }
3670 } else {
3671 MFloatScratchSpace tmp_nu(noInternalCells(), AT_, "tmp_nu");
3672 parallelIo.readArray(tmp_nu.getPointer(), "viscosity");
3673 for(MInt i = 0; i < noInternalCells(); i++) {
3674 initNu(i, tmp_nu(i));
3675 }
3676 this->exchangeData(&a_nu(0));
3677 }
3678
3679 m_bndCnd->bcDataReadRestartData(parallelIo);
3680
3681 // do an exchange to have the right values in the halo cells
3682 if(noDomains() > 1) {
3683 exchange(1);
3684 exchangeOldDistributions();
3685 } else if(noNeighborDomains() > 0) {
3686 // TODO labels:LB Periodic domain on noDomain == 1 also creates halos that
3687 // demand for an exchange. This has to be treaten somehow
3688 std::stringstream ss;
3689 ss << "WARNING: Serial run and periodic (?) is problematic as halo cells are"
3690 " not exchanged even though periodic BC make usage of it!"
3691 << std::endl;
3692 m_log << ss.str();
3693 std::cerr << ss.str();
3694 }
3695}
3696
3703template <MInt nDim>
3705 TRACE();
3706
3707 MInt noTransfer = 0;
3708
3709 if(m_isThermal && !m_isTransport) {
3710 noTransfer = 2;
3711 } else if(m_isTransport && !m_isThermal) {
3712 noTransfer = 2;
3713 } else if(m_isTransport && m_isThermal) {
3714 noTransfer = 3;
3715 } else {
3716 noTransfer = 1;
3717 }
3718
3719 MInt sumwin = 0;
3720 MInt sumhalo = 0;
3721 for(MInt n = 0; n < noNeighborDomains(); n++) {
3722 sumwin += noWindowCells(n);
3723 sumhalo += noHaloCells(n);
3724 }
3725
3726 MFloatScratchSpace sendBuf(noTransfer * m_noDistributions * sumwin, AT_, "sendBuf");
3727 MFloatScratchSpace recBuf(noTransfer * m_noDistributions * sumhalo, AT_, "recBuf");
3728
3729 MIntScratchSpace sendNeighOffset(noNeighborDomains(), AT_, "sendNeighOffset");
3730 MIntScratchSpace recNeighOffset(noNeighborDomains(), AT_, "recNeighOffset");
3731 sendNeighOffset[0] = 0;
3732 recNeighOffset[0] = 0;
3733
3734 for(MInt n = 1; n < noNeighborDomains(); n++) {
3735 sendNeighOffset[n] = sendNeighOffset[n - 1] + noWindowCells(n - 1) * noTransfer * m_noDistributions;
3736 recNeighOffset[n] = recNeighOffset[n - 1] + noHaloCells(n - 1) * noTransfer * m_noDistributions;
3737 }
3738
3739 for(MInt n = 0; n < noNeighborDomains(); n++) {
3740 MInt sndBuf = sendNeighOffset[n];
3741 for(MInt j = 0; j < noWindowCells(n); j++) {
3742 for(MInt dist = 0; dist < m_noDistributions; dist++) {
3743 MInt offset = j * m_noDistributions + dist;
3744 sendBuf[sndBuf + offset] = a_oldDistribution(windowCell(n, j), dist);
3745 }
3746 }
3747 if(m_isThermal) {
3748 sndBuf += m_noDistributions * noWindowCells(n);
3749 for(MInt j = 0; j < noWindowCells(n); j++) {
3750 for(MInt dist = 0; dist < m_noDistributions; dist++) {
3751 MInt offset = j * m_noDistributions + dist;
3752 sendBuf[sndBuf + offset] = a_oldDistributionThermal(windowCell(n, j), dist);
3753 }
3754 }
3755 }
3756 if(m_isTransport) {
3757 sndBuf += m_noDistributions * noWindowCells(n);
3758 for(MInt j = 0; j < noWindowCells(n); j++) {
3759 for(MInt dist = 0; dist < m_noDistributions; dist++) {
3760 MInt offset = j * m_noDistributions + dist;
3761 sendBuf[sndBuf + offset] = a_oldDistributionTransport(windowCell(n, j), dist);
3762 }
3763 }
3764 }
3765 }
3766
3767 // 2. Send the gathered information.
3768 for(MInt n = 0; n < noNeighborDomains(); n++) {
3769 MInt bufsize = noWindowCells(n) * noTransfer * m_noDistributions;
3770 MPI_Issend(&sendBuf[sendNeighOffset[n]], bufsize, MPI_DOUBLE, neighborDomain(n), 0, mpiComm(), &mpi_request[n], AT_,
3771 "sendBuf[sendNeighOffset[n]]");
3772 }
3773
3774 // 3. Receive data from neighbors.
3775 MPI_Status status;
3776
3777 for(MInt n = 0; n < noNeighborDomains(); n++) {
3778 MInt bufsize = noHaloCells(n) * noTransfer * m_noDistributions;
3779 MPI_Recv(&recBuf[recNeighOffset[n]], bufsize, MPI_DOUBLE, neighborDomain(n), 0, mpiComm(), &status, AT_,
3780 "recBuf[recNeighOffset[n]]");
3781 }
3782
3783 for(MInt n = 0; n < noNeighborDomains(); n++)
3784 MPI_Wait(&mpi_request[n], &status, AT_);
3785
3786 // 4. scatter the received data
3787 for(MInt n = 0; n < noNeighborDomains(); n++) {
3788 MInt recBuffer = recNeighOffset[n];
3789 for(MInt j = 0; j < noHaloCells(n); j++) {
3790 for(MInt dist = 0; dist < m_noDistributions; dist++) {
3791 MInt offset = j * m_noDistributions + dist;
3792 a_oldDistribution(haloCell(n, j), dist) = recBuf[recBuffer + offset];
3793 }
3794 }
3795 if(m_isThermal) {
3796 recBuffer += m_noDistributions * noHaloCells(n);
3797 for(MInt j = 0; j < noHaloCells(n); j++) {
3798 for(MInt dist = 0; dist < m_noDistributions; dist++) {
3799 MInt offset = j * m_noDistributions + dist;
3800 a_oldDistributionThermal(haloCell(n, j), dist) = recBuf[recBuffer + offset];
3801 }
3802 }
3803 }
3804 if(m_isTransport) {
3805 recBuffer += m_noDistributions * noHaloCells(n);
3806 for(MInt j = 0; j < noHaloCells(n); j++) {
3807 for(MInt dist = 0; dist < m_noDistributions; dist++) {
3808 MInt offset = j * m_noDistributions + dist;
3809 a_oldDistributionTransport(haloCell(n, j), dist) = recBuf[recBuffer + offset];
3810 }
3811 }
3812 }
3813 }
3814}
3815
3827template <MInt nDim>
3828void LbSolver<nDim>::saveRestartWithDistributionsPar(const MChar* fileName, const MChar* gridInputFileName,
3829 MInt* recalcIdTree) {
3830 TRACE();
3831 if constexpr(nDim != 2 && nDim != 3) {
3832 cerr << " In global function saveRestartWithDistributionsPar: wrong number of dimensions !" << endl;
3833 exit(0);
3834 return;
3835 }
3836
3837 using namespace maia::parallel_io;
3838 ParallelIo parallelIo(fileName, PIO_REPLACE, mpiComm());
3839 parallelIo.defineScalar(PIO_INT, "noCells");
3840
3841 // total #of cells in the grid == global cell index of the last cell in the last process:
3842 const MInt totalNoCells = domainOffset(noDomains());
3843
3844 //--specify helper functions
3845 auto defFloatArray = [&](const MString arrayName, const MString varName, const MInt length) {
3846 parallelIo.defineArray(PIO_FLOAT, arrayName, length);
3847 parallelIo.setAttribute(varName, "name", arrayName);
3848 };
3849 auto defIntArray = [&](const MString arrayName, const MString varName, const MInt length) {
3850 parallelIo.defineArray(PIO_INT, arrayName, length);
3851 parallelIo.setAttribute(varName, "name", arrayName);
3852 };
3853 // function: define macroscopic variables
3854 auto defineMacroscopicVariables = [&](const MString name, const MString prefix, const MBool old) {
3855 // velocities
3856 const MString velNames[3] = {"u", "v", "w"};
3857 for(MInt d = 0; d != nDim; ++d) {
3858 defFloatArray(name + std::to_string(d), prefix + velNames[d], totalNoCells);
3859 }
3860 // density
3861 defFloatArray(name + std::to_string(nDim), prefix + "rho", totalNoCells);
3862 // temperature
3863 if(!m_isTransport && m_isThermal) {
3864 defFloatArray(name + std::to_string(nDim + 1), prefix + "t", totalNoCells);
3865 }
3866 if(m_isTransport && !m_isThermal) {
3867 defFloatArray(name + std::to_string(nDim + 1), prefix + "c", totalNoCells);
3868 }
3869 if(m_isTransport && m_isThermal) {
3870 defFloatArray(name + std::to_string(nDim + 1), prefix + "t", totalNoCells);
3871 defFloatArray(name + std::to_string(nDim + 2), prefix + "c", totalNoCells);
3872 }
3873 if(m_writeLsData && !old) {
3874 if(m_isThermal) {
3875 defIntArray(name + std::to_string(nDim + 2), prefix + "isActive", totalNoCells);
3876 for(MInt set = 0; set < m_maxNoSets; set++) {
3877 defFloatArray(name + std::to_string(nDim + 3 + set), prefix + "G_" + std::to_string(set), totalNoCells);
3878 }
3879 for(MInt set = 0; set < m_maxNoSets; set++) {
3880 defIntArray(name + std::to_string(nDim + 3 + m_maxNoSets + set), prefix + "Body_" + std::to_string(set),
3881 totalNoCells);
3882 }
3883 } else {
3884 defIntArray(name + std::to_string(nDim + 1), prefix + "isActive", totalNoCells);
3885 for(MInt set = 0; set < m_maxNoSets; set++) {
3886 defFloatArray(name + std::to_string(nDim + 2 + set), prefix + "G_" + std::to_string(set), totalNoCells);
3887 }
3888 for(MInt set = 0; set < m_maxNoSets; set++) {
3889 defIntArray(name + std::to_string(nDim + 2 + m_maxNoSets + set), prefix + "Body_" + std::to_string(set),
3890 totalNoCells);
3891 }
3892 }
3893 }
3894 if(m_isEELiquid) {
3895 defFloatArray(name + std::to_string(nDim + 1), prefix + "alphaG", totalNoCells);
3896 }
3897 };
3898
3899 auto defineLocalViscosity = [&](const MString& name) { defFloatArray(name, name, totalNoCells); };
3900
3901 // function: define distributions
3902 auto defineDistributions = [&, totalNoCells](const MString name) {
3903 for(MInt j = 0; j != m_noDistributions; ++j) {
3904 defFloatArray(name + std::to_string(j), name + std::to_string(j), totalNoCells);
3905 if(m_isThermal) {
3906 const MString thermalName = name + "Thermal" + std::to_string(j);
3907 defFloatArray(thermalName, thermalName, totalNoCells);
3908 }
3909 if(m_isTransport) {
3910 const MString transportName = name + "Transport" + std::to_string(j);
3911 defFloatArray(transportName, transportName, totalNoCells);
3912 }
3913 }
3914 };
3915 // function: write macroscopic variable
3916 auto writeMacroscopicVariable = [&](const MInt index, const MInt suffix) {
3917 MFloatScratchSpace tmp(noInternalCells(), AT_, "tmp");
3918 for(MInt i = 0; i < noInternalCells(); ++i) {
3919 tmp[i] = a_variable(recalcIdTree != nullptr ? recalcIdTree[i] : i, index);
3920 }
3921 parallelIo.writeArray(tmp.getPointer(), "variables" + std::to_string(suffix));
3922 };
3923 // function: write old macroscopic variable
3924 auto writeMacroscopicOldVariable = [&](const MInt index, const MInt suffix) {
3925 MFloatScratchSpace tmp(noInternalCells(), AT_, "tmp");
3926 for(MInt i = 0; i < noInternalCells(); ++i) {
3927 tmp[i] = a_oldVariable(recalcIdTree != nullptr ? recalcIdTree[i] : i, index);
3928 }
3929 parallelIo.writeArray(tmp.getPointer(), "oldVariables" + std::to_string(suffix));
3930 };
3931 // function: write old macroscopic variable
3932 auto writeMacroscopicActiveState = [&](const MInt suffix) {
3933 MIntScratchSpace tmp(noInternalCells(), AT_, "tmp");
3934 for(MInt i = 0; i < noInternalCells(); ++i) {
3935 tmp[i] = (MInt)a_isActive(recalcIdTree != nullptr ? recalcIdTree[i] : i);
3936 }
3937 parallelIo.writeArray(tmp.getPointer(), "variables" + std::to_string(suffix));
3938 };
3939 auto writeLevelSet = [&](const MInt set, const MInt suffix) {
3940 MFloatScratchSpace tmp(noInternalCells(), AT_, "tmp");
3941 for(MInt i = 0; i < noInternalCells(); i++) {
3942 tmp[i] = a_levelSetFunctionMB(recalcIdTree != nullptr ? recalcIdTree[i] : i, set);
3943 }
3944 parallelIo.writeArray(tmp.getPointer(), "variables" + std::to_string(suffix));
3945 };
3946 auto writeBodyId = [&](const MInt set, const MInt suffix) {
3947 MIntScratchSpace tmp(noInternalCells(), AT_, "tmp");
3948 for(MInt i = 0; i < noInternalCells(); i++) {
3949 tmp[i] = a_associatedBodyIds(recalcIdTree != nullptr ? recalcIdTree[i] : i, set);
3950 }
3951 parallelIo.writeArray(tmp.getPointer(), "variables" + std::to_string(suffix));
3952 };
3953 auto writeViscosity = [&]() {
3954 MFloatScratchSpace tmp(noInternalCells(), AT_, "tmp");
3955 for(MInt i = 0; i < noInternalCells(); ++i) {
3956 tmp[i] = a_nu(recalcIdTree != nullptr ? recalcIdTree[i] : i);
3957 }
3958 parallelIo.writeArray(tmp.getPointer(), "viscosity");
3959 };
3960
3961 //--define arrays
3962 defineMacroscopicVariables("variables", "", false);
3963 defineMacroscopicVariables("oldVariables", "old_", true);
3964 if(m_nonNewtonian) defineLocalViscosity("viscosity");
3965 defineDistributions("distributions");
3966 defineDistributions("oldDistributions");
3967 m_bndCnd->bcDataWriteRestartHeader(parallelIo);
3968
3969 //--define global attributes
3970 parallelIo.setAttribute(solverId(), "solverId");
3971 parallelIo.setAttribute(gridInputFileName, "gridFile", "");
3972 parallelIo.setAttribute(m_time, "time");
3973 parallelIo.setAttribute(globalTimeStep, "globalTimeStep");
3974 parallelIo.setAttribute(m_Re, "ReynoldsNumber");
3975 parallelIo.setAttribute(m_Ma, "MachNumber");
3976
3977 if(m_controlVelocity) {
3978 parallelIo.setAttribute(m_velocityControl.lastGlobalAvgV, "lastGlobalAvgV");
3979 parallelIo.setAttribute(m_velocityControl.previousError, "previousError");
3980 parallelIo.setAttribute(m_velocityControl.integratedError, "integratedError");
3981 parallelIo.setAttribute(m_velocityControl.derivedError, "derivedError");
3982 for(MInt i = 0; i < nDim; i++) {
3983 parallelIo.setAttribute(m_volumeAccel[i], "volumeAcceleration_" + std::to_string(i));
3984 }
3985 }
3986
3987 //--write scalars
3988 parallelIo.writeScalar(totalNoCells, "noCells");
3989
3990 //--write arrays
3991 // Set file offsets (first globalId and #of cells to be written by this process):
3992 const MPI_Offset firstGlobalId = domainOffset(domainId());
3993 const MPI_Offset localNoCells = noInternalCells();
3994 parallelIo.setOffset(localNoCells, firstGlobalId);
3995
3996 // Macroscopic Variables
3997 MInt suffix = 0;
3998 writeMacroscopicVariable(PV->U, suffix++);
3999 writeMacroscopicVariable(PV->V, suffix++);
4000 if constexpr(nDim == 3) writeMacroscopicVariable(PV->W, suffix++);
4001 writeMacroscopicVariable(PV->RHO, suffix++);
4002 if(m_isThermal) writeMacroscopicVariable(PV->T, suffix++);
4003 if(m_isTransport) writeMacroscopicVariable(PV->C, suffix++);
4004 if(m_writeLsData) {
4005 writeMacroscopicActiveState(suffix++);
4006 for(MInt set = 0; set < m_maxNoSets; set++) {
4007 writeLevelSet(set, suffix++);
4008 }
4009 for(MInt set = 0; set < m_maxNoSets; set++) {
4010 writeBodyId(set, suffix++);
4011 }
4012 }
4013 if(m_isEELiquid) {
4014 MFloatScratchSpace tmp_alphaG(noInternalCells(), AT_, "tmp_alphaG");
4015 for(MInt i = 0; i < noInternalCells(); ++i) {
4016 const MInt id = recalcIdTree != nullptr ? recalcIdTree[i] : i;
4017 tmp_alphaG[i] = a_alphaGas(id);
4018 }
4019 parallelIo.writeArray(tmp_alphaG.getPointer(), "variables" + std::to_string(suffix++));
4020 }
4021 if(m_nonNewtonian) writeViscosity();
4022
4023 suffix = 0;
4024 writeMacroscopicOldVariable(PV->U, suffix++);
4025 writeMacroscopicOldVariable(PV->V, suffix++);
4026 if constexpr(nDim == 3) writeMacroscopicOldVariable(PV->W, suffix++);
4027 writeMacroscopicOldVariable(PV->RHO, suffix++);
4028 if(m_isThermal) writeMacroscopicOldVariable(PV->T, suffix++);
4029 if(m_isTransport) writeMacroscopicOldVariable(PV->C, suffix++);
4030 // Distributions
4031 for(MInt j = 0; j < m_noDistributions; j++) {
4032 const MString distributions = "distributions" + to_string(j);
4033
4034 MFloatScratchSpace tmp_distributions(noInternalCells(), AT_, "tmp_distributions");
4035 for(MInt i = 0; i < noInternalCells(); ++i)
4036 tmp_distributions[i] = a_distribution(recalcIdTree != nullptr ? recalcIdTree[i] : i, j);
4037 parallelIo.writeArray(tmp_distributions.getPointer(), distributions);
4038 }
4039
4040 // old Distributions
4041 for(MInt j = 0; j < m_noDistributions; j++) {
4042 const MString oldDistributions = "oldDistributions" + to_string(j);
4043
4044 MFloatScratchSpace tmp_oldDistributions(noInternalCells(), AT_, "tmp_oldDistributions");
4045 for(MInt i = 0; i < noInternalCells(); ++i)
4046 tmp_oldDistributions[i] = a_oldDistribution(recalcIdTree != nullptr ? recalcIdTree[i] : i, j);
4047 parallelIo.writeArray(tmp_oldDistributions.getPointer(), oldDistributions);
4048 }
4049
4050 if(m_isThermal) {
4051 // Thermal Distributions
4052 for(MInt j = 0; j < m_noDistributions; j++) {
4053 const MString distributionsThermal = "distributionsThermal" + to_string(j);
4054
4055 MFloatScratchSpace tmp_distributionsThermal(noInternalCells(), AT_, "tmp_distributionsThermal");
4056 for(MInt i = 0; i < noInternalCells(); ++i)
4057 tmp_distributionsThermal[i] = a_distributionThermal(recalcIdTree != nullptr ? recalcIdTree[i] : i, j);
4058 parallelIo.writeArray(tmp_distributionsThermal.getPointer(), distributionsThermal);
4059 }
4060
4061 // old Thermal Distributions
4062 for(MInt j = 0; j < m_noDistributions; j++) {
4063 const MString oldDistributionsThermal = "oldDistributionsThermal" + to_string(j);
4064
4065 MFloatScratchSpace tmp_oldDistributionsThermal(noInternalCells(), AT_, "tmp_oldDistributionsThermal");
4066 for(MInt i = 0; i < noInternalCells(); ++i)
4067 tmp_oldDistributionsThermal[i] = a_oldDistributionThermal(recalcIdTree != nullptr ? recalcIdTree[i] : i, j);
4068 parallelIo.writeArray(tmp_oldDistributionsThermal.getPointer(), oldDistributionsThermal);
4069 }
4070 }
4071
4072 if(m_isTransport) {
4073 // Transport Distributions
4074 for(MInt j = 0; j < m_noDistributions; j++) {
4075 const MString distributionsTransport = "distributionsTransport" + to_string(j);
4076
4077 MFloatScratchSpace tmp_distributionsTransport(noInternalCells(), AT_, "tmp_distributionsTransport");
4078 for(MInt i = 0; i < noInternalCells(); ++i)
4079 tmp_distributionsTransport[i] = a_distributionTransport(recalcIdTree != nullptr ? recalcIdTree[i] : i, j);
4080 parallelIo.writeArray(tmp_distributionsTransport.getPointer(), distributionsTransport);
4081 }
4082
4083 // old Thermal Distributions
4084 for(MInt j = 0; j < m_noDistributions; j++) {
4085 const MString oldDistributionsTransport = "oldDistributionsTransport" + to_string(j);
4086
4087 MFloatScratchSpace tmp_oldDistributionsTransport(noInternalCells(), AT_, "tmp_oldDistributionsTransport");
4088 for(MInt i = 0; i < noInternalCells(); ++i)
4089 tmp_oldDistributionsTransport[i] = a_oldDistributionTransport(recalcIdTree != nullptr ? recalcIdTree[i] : i, j);
4090 parallelIo.writeArray(tmp_oldDistributionsTransport.getPointer(), oldDistributionsTransport);
4091 }
4092 }
4093 // Former variables
4094 m_bndCnd->bcDataWriteRestartData(parallelIo);
4095}
4096
4102template <MInt nDim>
4104 TRACE();
4105
4106 stringstream PvFileName;
4107 PvFileName << outputDir() << "PV_" << getIdentifier() << globalTimeStep << ParallelIo::fileExt();
4108 if(domainId() == 0)
4109 std::cerr << "Writing output file: PV_" << getIdentifier() << globalTimeStep << " at m_time " << this->m_time
4110 << std::endl;
4111
4112
4113 // If grid is adapted, write new grid file and compute recalcIdTree.
4114 // Different ids in solver/grid and new grid file are mapped by recalcIdTree.
4115 if(m_adaptationSinceLastSolution) {
4116 m_reinitFileName = "grid" + std::to_string(globalTimeStep) + ".Netcdf";
4117 m_reinitFilePath = outputDir() + m_reinitFileName;
4118 saveAdaptedGridFile(this->m_recalcIds);
4119 }
4120 std::vector<MInt> recalcCellIdsSolver(0);
4121 MInt noCells;
4122 MInt noInternalCellIds;
4123 std::vector<MInt> reorderedCellIds(0);
4124 this->calcRecalcCellIdsSolver(this->m_recalcIds, noCells, noInternalCellIds, recalcCellIdsSolver, reorderedCellIds);
4125 m_adaptationSinceLastSolution = false;
4126 saveUVWRhoTOnlyPar(PvFileName.str().c_str(), m_reinitFileName.c_str(), recalcCellIdsSolver.data());
4127}
4128
4137template <MInt nDim>
4138void LbSolver<nDim>::saveAdaptedGridFile(MInt* const p_recalcCellIds) {
4139 // Reset p_recalcCellIds
4140 for(MInt i = 0; i < this->maxNoGridCells(); i++) {
4141 p_recalcCellIds[i] = -1;
4142 }
4143
4144 // Write new Grid and get new recalcIdTree
4145 grid().raw().saveGrid(this->m_reinitFilePath.c_str(), p_recalcCellIds);
4146}
4147
4160template <MInt nDim>
4161void LbSolver<nDim>::saveUVWRhoTOnlyPar(const MChar* fileName, const MChar* gridInputFileName, MInt* recalcIdTree) {
4162 TRACE();
4163
4164 if(m_saveDerivatives) {
4165 exchange(1);
4166 }
4167
4168 using namespace maia::parallel_io;
4169 ASSERT(nDim == 2 || nDim == 3, "wrong number of dimensions!");
4170
4171 ParallelIo parallelIo(fileName, PIO_REPLACE, mpiComm());
4172 parallelIo.defineScalar(PIO_INT, "noCells");
4173 parallelIo.setAttribute(solverId(), "solverId");
4174 // total #of cells in the grid == global cell index of the last cell in the last process:
4175 const MInt totalNoCells = domainOffset(noDomains());
4176
4177 // defines the array and creates the corresponding attributes
4178 auto defFloatArray = [&](const MString& arrayName, const MString& varName) {
4179 parallelIo.defineArray(PIO_FLOAT, arrayName, totalNoCells);
4180 parallelIo.setAttribute(varName, "name", arrayName);
4181 };
4182
4183 auto defineMacroscopicVariables = [&](const MString& name, const MString& prefix) {
4184 MInt counter = 0;
4185 // define variables
4186 if constexpr(nDim == 2) {
4187 const MString namesStd[3] = {"u", "v", "rho"};
4188
4189 for(; counter < 3; counter++)
4190 defFloatArray(name + std::to_string(counter), prefix + namesStd[counter]);
4191 } else {
4192 const MString namesStd[4] = {"u", "v", "w", "rho"};
4193
4194 for(; counter < 4; counter++)
4195 defFloatArray(name + std::to_string(counter), prefix + namesStd[counter]);
4196 }
4197
4198 if(m_isThermal) {
4199 const MString temperature = "t";
4200 defFloatArray(name + std::to_string(counter), prefix + temperature);
4201 counter++;
4202 }
4203
4204 if(m_isTransport) {
4205 const MString concentration = "c";
4206 defFloatArray(name + std::to_string(counter), prefix + concentration);
4207 counter++;
4208 }
4209 if(m_particleMomentumCoupling && m_saveExternalForces) {
4210 const MString externalForcing[3] = {"F_ext_x", "F_ext_y", "F_ext_z"};
4211 for(MInt d = 0; d < nDim; d++, counter++)
4212 defFloatArray(name + std::to_string(counter), prefix + externalForcing[d]);
4213 }
4214
4215 if(m_isEELiquid) {
4216 const MString EELiquid = "alphaG";
4217 defFloatArray(name + std::to_string(counter), prefix + EELiquid);
4218 counter++;
4219 }
4220
4221 if(m_saveDerivatives) {
4222 if constexpr(nDim == 2) {
4223 // define derivatives:
4224 const MString names[10] = {"du/dx", "du/dy", "dv/dx", "dv/dy", "dp_t/dx",
4225 "dp_t/dy", "vorticity_z", "Q-criterion", "Delta-criterion", "Lambda2"};
4226
4227 for(MInt d = 0; d < 10; d++, counter++)
4228 defFloatArray(name + std::to_string(counter), prefix + names[d]);
4229 }
4230
4231 else {
4232 // define derivatives:
4233 const MString names[19] = {"du/dx", "du/dy", "du/dz", "dv/dx", "dv/dy",
4234 "dv/dz", "dw/dx", "dw/dy", "dw/dz", "dp_t/dx",
4235 "dp_t/dy", "dp_t/dz", "vorticity_x", "vorticity_y", "vorticity_z",
4236 "vorticity_mag", "Q-criterion", "Delta-criterion", "Lambda2"};
4237
4238 for(MInt d = 0; d < 19; d++, counter++)
4239 defFloatArray(name + std::to_string(counter), prefix + names[d]);
4240
4241 if(m_isThermal) {
4242 const MString namesT[3] = {"dT/dx", "dT/dy", "dT/dz"};
4243 for(MInt d = 0; d < nDim; d++, counter++)
4244 defFloatArray(name + std::to_string(counter), prefix + namesT[d]);
4245 }
4246
4247 if(m_isTransport) {
4248 const MString namesC[3] = {"dC/dx", "dC/dy", "dC/dz"};
4249 for(MInt d = 0; d < nDim; d++, counter++)
4250 defFloatArray(name + std::to_string(counter), prefix + namesC[d]);
4251 }
4252 }
4253 }
4254 };
4255
4256 defineMacroscopicVariables("variables", "");
4257
4258 // Creating global attributes
4259 MString gridFileName = gridInputFileName;
4260 parallelIo.setAttribute(gridFileName, "gridFile", "");
4261 parallelIo.setAttribute(m_time, "time");
4262 parallelIo.setAttribute(globalTimeStep, "globalTimeStep");
4263 parallelIo.setAttribute(m_Re, "ReynoldsNumber");
4264 parallelIo.setAttribute(m_Ma, "MachNumber");
4265
4266 if(m_controlVelocity) {
4267 parallelIo.setAttribute(m_velocityControl.lastGlobalAvgV, "lastGlobalAvgV");
4268 parallelIo.setAttribute(m_velocityControl.previousError, "previousError");
4269 parallelIo.setAttribute(m_velocityControl.integratedError, "integratedError");
4270 parallelIo.setAttribute(m_velocityControl.derivedError, "derivedError");
4271 for(MInt i = 0; i < nDim; i++) {
4272 parallelIo.setAttribute(m_volumeAccel[i], "volumeAcceleration_" + std::to_string(i));
4273 }
4274 }
4275
4276 // number of Cells.
4277 parallelIo.writeScalar(totalNoCells, "noCells");
4278
4279 // Set file offsets (first globalId and #of cells to be written by this process):
4280 const MPI_Offset firstGlobalId = domainOffset(domainId());
4281 const MPI_Offset localNoCells = noInternalCells();
4282 parallelIo.setOffset(localNoCells, firstGlobalId);
4283
4284 MString name = "variables";
4285
4286 MInt start_derivatives = m_noVariables;
4287 // Write macroscopic variables:
4288 {
4289 MInt counter = 0;
4290 ScratchSpace<MFloat> buffer(localNoCells, FUN_, "buffer");
4291 // write velocities and density:
4292 for(MInt d = 0; d != nDim + 1; ++d) {
4293 for(MInt cellId = 0; cellId < localNoCells; cellId++) {
4294 buffer[cellId] = a_variable(recalcIdTree != nullptr ? recalcIdTree[cellId] : cellId, d);
4295 }
4296 parallelIo.writeArray(&buffer[0], name + std::to_string(counter++));
4297 }
4298
4299 // write temperature:
4300 if(m_isThermal && !m_isTransport) {
4301 for(MInt cellId = 0; cellId < localNoCells; cellId++) {
4302 buffer[cellId] = a_variable(recalcIdTree != nullptr ? recalcIdTree[cellId] : cellId, PV->T);
4303 }
4304 parallelIo.writeArray(&buffer[0], name + std::to_string(counter++));
4305 }
4306 if(m_isTransport && !m_isThermal) {
4307 for(MInt cellId = 0; cellId < localNoCells; cellId++) {
4308 buffer[cellId] = a_variable(recalcIdTree != nullptr ? recalcIdTree[cellId] : cellId, PV->C);
4309 }
4310 parallelIo.writeArray(&buffer[0], name + std::to_string(nDim + 1));
4311 }
4312 if(m_isTransport && m_isThermal) {
4313 for(MInt cellId = 0; cellId < localNoCells; cellId++) {
4314 buffer[cellId] = a_variable(recalcIdTree != nullptr ? recalcIdTree[cellId] : cellId, PV->T);
4315 }
4316 parallelIo.writeArray(&buffer[0], name + std::to_string(nDim + 1));
4317
4318 for(MInt cellId = 0; cellId < localNoCells; cellId++) {
4319 buffer[cellId] = a_variable(recalcIdTree != nullptr ? recalcIdTree[cellId] : cellId, PV->C);
4320 }
4321 parallelIo.writeArray(&buffer[0], name + std::to_string(nDim + 2));
4322 }
4323
4324 // write external Force:
4325 if(m_particleMomentumCoupling && m_saveExternalForces) {
4326 for(MInt d = 0; d < nDim; d++) {
4327 for(MInt cellId = 0; cellId < localNoCells; cellId++) {
4328 buffer[cellId] = a_externalForces(recalcIdTree != nullptr ? recalcIdTree[cellId] : cellId, d);
4329 }
4330 parallelIo.writeArray(&buffer[0], name + std::to_string(counter++));
4331 }
4332 start_derivatives += nDim;
4333 }
4334
4335 if(m_isEELiquid) {
4336 for(MInt cellId = 0; cellId < localNoCells; cellId++) {
4337 buffer[cellId] = a_alphaGas(recalcIdTree != nullptr ? recalcIdTree[cellId] : cellId);
4338 }
4339 parallelIo.writeArray(&buffer[0], name + std::to_string(counter++));
4340 start_derivatives += 1;
4341 }
4342 }
4343
4344 // write derivatives
4345 if(m_saveDerivatives) {
4346 // d1 = du, dv, dw; d2 = dx, dy, dz
4347 ScratchSpace<MFloat> derivatives(nDim * nDim + nDim, localNoCells, FUN_, "derivatives");
4348 for(MInt d1 = 0; d1 < nDim; ++d1)
4349 for(MInt d2 = 0; d2 < nDim; ++d2)
4350 for(MInt cellId = 0; cellId < localNoCells; cellId++)
4351 derivatives(d1 * nDim + d2, cellId) = calculateDerivative(cellId, d1, d2);
4352
4353 for(MInt d = 0; d < nDim; d++) {
4354 for(MInt cellId = 0; cellId < localNoCells; cellId++) {
4355 if(m_calcTotalPressureGradient != 0) {
4356 derivatives(nDim * nDim + d, cellId) = calculatePressureDerivative(cellId, d);
4357 } else {
4358 derivatives(nDim * nDim + d, cellId) = calculateDerivative(cellId, PV->RHO, d);
4359 }
4360 }
4361 }
4362
4363 // write derivatives to disk
4364 for(MInt d1 = 0; d1 < nDim; ++d1)
4365 for(MInt d2 = 0; d2 < nDim; ++d2)
4366 parallelIo.writeArray(&derivatives(d1 * nDim + d2, 0),
4367 name + std::to_string(start_derivatives + d1 * nDim + d2));
4368
4369 for(MInt d = 0; d < nDim; d++) {
4370 parallelIo.writeArray(&derivatives(nDim * nDim + d, 0),
4371 name + std::to_string(start_derivatives + nDim * nDim + d));
4372 }
4373
4374 MInt start_vor;
4375 MInt start_vor_mag;
4376 MInt start_q;
4377 MInt start_d;
4378 MInt start_lambda2;
4379
4380 if constexpr(nDim == 2) {
4381 start_vor = start_derivatives + nDim * nDim + nDim;
4382 start_q = start_vor + 1;
4383 start_d = start_q + 1;
4384 start_lambda2 = start_d + 1;
4385 } else {
4386 start_vor = start_derivatives + nDim * nDim + nDim;
4387 start_vor_mag = start_vor + nDim;
4388 start_q = start_vor_mag + 1;
4389 start_d = start_q + 1;
4390 start_lambda2 = start_d + 1;
4391 }
4392 // all vorticity components
4393 {// create vorticity elements and write to disk
4394 // also calculate the magnitude of the vorticity
4395 if constexpr(nDim == 2) {
4396 ScratchSpace<MFloat> vorticity_c(localNoCells, FUN_, "vorticity_c");
4397 for(MInt cellId = 0; cellId < localNoCells; cellId++) {
4398 vorticity_c[cellId] = derivatives(2, cellId) - derivatives(1, cellId);
4399 }
4400 parallelIo.writeArray(&vorticity_c[0], name + std::to_string(start_vor));
4401 }
4402 if constexpr(nDim == 3) {
4403 ScratchSpace<MFloat> vorticity_mag(localNoCells, FUN_, "vorticity_mag");
4404 for(MInt cellId = 0; cellId < localNoCells; cellId++) {
4405 vorticity_mag[cellId] = 0.0;
4406 }
4407
4408 for(MInt d = 0; d < nDim; ++d) {
4409 MInt v_c1 = (d + 1) % (nDim);
4410 MInt v_c2 = (d + 2) % (nDim);
4411 ScratchSpace<MFloat> vorticity_c(localNoCells, FUN_, "vorticity_c");
4412 for(MInt cellId = 0; cellId < localNoCells; cellId++) {
4413 vorticity_c[cellId] = derivatives(v_c2 * nDim + v_c1, cellId) - derivatives(v_c1 * nDim + v_c2, cellId);
4414 vorticity_mag[cellId] += vorticity_c[cellId] * vorticity_c[cellId];
4415 }
4416
4417 parallelIo.writeArray(&vorticity_c[0], name + std::to_string(start_vor + d));
4418 }
4419
4420 // write vorticity magnitude
4421 for(MInt cellId = 0; cellId < localNoCells; cellId++) {
4422 vorticity_mag[cellId] = sqrt(vorticity_mag[cellId]);
4423 }
4424
4425 parallelIo.writeArray(&vorticity_mag[0], name + std::to_string(start_vor_mag));
4426 }
4427}
4428 // q-criterion and delta-criterion
4429 {
4430 // first q-criterion
4431 ScratchSpace<MFloat> q_criterion(localNoCells, FUN_, "q_criterion");
4432 MFloatScratchSpace strain(nDim, nDim, FUN_, "strain");
4433 MFloatScratchSpace vorticity(nDim, nDim, FUN_, "vorticity");
4434
4435 for(MInt cellId = 0; cellId < localNoCells; cellId++) {
4436 getStrainTensor(derivatives, cellId, strain);
4437 getVorticityTensor(derivatives, cellId, vorticity);
4438
4439 MFloat strainFrobSq = maia::math::frobeniusMatrixNormSquared(strain, nDim, nDim);
4440 MFloat vorticityFrobSq = maia::math::frobeniusMatrixNormSquared(vorticity, nDim, nDim);
4441
4442 q_criterion[cellId] = 0.5 * (vorticityFrobSq - strainFrobSq);
4443 }
4444
4445 parallelIo.writeArray(&q_criterion[0], name + std::to_string(start_q));
4446
4447 // second delta-criterion (requires q-criterion)
4448 {
4449 ScratchSpace<MFloat> d_criterion(localNoCells, FUN_, "d_criterion");
4450
4451 for(MInt cellId = 0; cellId < localNoCells; cellId++) {
4452 std::array<std::array<MFloat, nDim>, nDim> dyad;
4453
4454 for(MInt i = 0; i < nDim; i++) {
4455 for(MInt j = 0; j < nDim; j++) {
4456 dyad[i][j] = derivatives(j * nDim + i, cellId);
4457 }
4458 }
4459 MFloat det = maia::math::determinant(dyad);
4460 d_criterion[cellId] = pow((q_criterion[cellId] / 3.0), 3.0) + (0.5 * det) * (0.5 * det);
4461 }
4462 parallelIo.writeArray(&d_criterion[0], name + std::to_string(start_d));
4463 }
4464
4465 // third lambda 2
4466 {
4467 ScratchSpace<MFloat> lambda_2(localNoCells, FUN_, "lambda_2");
4468 MFloatScratchSpace strain_sq(nDim, nDim, FUN_, "strain_sq");
4469 MFloatScratchSpace vorticity_sq(nDim, nDim, FUN_, "vorticity_sq");
4470 MFloatScratchSpace sv(nDim, nDim, FUN_, "sv");
4471
4472 for(MInt cellId = 0; cellId < localNoCells; cellId++) {
4473 getStrainTensor(derivatives, cellId, strain);
4474 getVorticityTensor(derivatives, cellId, vorticity);
4475
4476 maia::math::multiplyMatricesSq(strain, strain, strain_sq, nDim);
4477 maia::math::multiplyMatricesSq(vorticity, vorticity, vorticity_sq, nDim);
4478
4479 maia::math::addMatrices(strain_sq, vorticity_sq, sv, nDim, nDim);
4480
4481 if constexpr(nDim == 3) {
4482 MFloat matrix[3][3];
4483 for(MInt i = 0; i < nDim; i++) {
4484 for(MInt j = 0; j < nDim; j++) {
4485 matrix[i][j] = sv(i, j);
4486 }
4487 }
4488
4489 MFloat evs[3]{};
4490 maia::math::calcEigenValues(matrix, evs);
4491 MInt l2_index = 0;
4492 for(MInt i = 0; i < nDim; i++) {
4493 if((evs[i] > evs[((i == 0) ? 0 : (i - 1)) % nDim] && evs[i] < evs[(i + 1) % nDim])
4494 || (evs[i] > evs[(i + 1) % nDim] && evs[i] < evs[((i == 0) ? 0 : (i - 1)) % nDim])) {
4495 l2_index = i;
4496 break;
4497 }
4498 }
4499 lambda_2[cellId] = evs[l2_index];
4500 } else {
4501 TERMM(-1, "Code is not correct for 2D applications");
4502 }
4503 }
4504 parallelIo.writeArray(&lambda_2[0], name + std::to_string(start_lambda2));
4505 }
4506 if(m_isThermal && !m_isTransport) {
4507 ScratchSpace<MFloat> derivativesT(nDim, localNoCells, FUN_, "derivativesT");
4508
4509 for(MInt d = 0; d < nDim; d++) {
4510 for(MInt cellId = 0; cellId < localNoCells; cellId++)
4511 derivativesT(d, cellId) = calculateDerivative(cellId, PV->T, d);
4512 }
4513 for(MInt d = 0; d < nDim; d++) {
4514 parallelIo.writeArray(&derivativesT(d, 0), name + std::to_string(start_lambda2 + 1 + d));
4515 }
4516 }
4517 if(m_isTransport && !m_isThermal) {
4518 ScratchSpace<MFloat> derivativesC(nDim, localNoCells, FUN_, "derivativesC");
4519
4520 for(MInt d = 0; d < nDim; d++) {
4521 for(MInt cellId = 0; cellId < localNoCells; cellId++)
4522 derivativesC(d, cellId) = calculateDerivative(cellId, PV->C, d);
4523 }
4524 for(MInt d = 0; d < nDim; d++) {
4525 parallelIo.writeArray(&derivativesC(d, 0), name + std::to_string(start_lambda2 + 1 + d));
4526 }
4527 }
4528 if(m_isTransport && m_isThermal) {
4529 ScratchSpace<MFloat> derivativesT(nDim, localNoCells, FUN_, "derivativesT");
4530
4531 for(MInt d = 0; d < nDim; d++) {
4532 for(MInt cellId = 0; cellId < localNoCells; cellId++)
4533 derivativesT(d, cellId) = calculateDerivative(cellId, PV->T, d);
4534 }
4535 for(MInt d = 0; d < nDim; d++) {
4536 parallelIo.writeArray(&derivativesT(d, 0), name + std::to_string(start_lambda2 + 1 + d));
4537 }
4538
4539 ScratchSpace<MFloat> derivativesC(nDim, localNoCells, FUN_, "derivativesC");
4540
4541 for(MInt d = 0; d < nDim; d++) {
4542 for(MInt cellId = 0; cellId < localNoCells; cellId++)
4543 derivativesC(d, cellId) = calculateDerivative(cellId, PV->C, d);
4544 }
4545 for(MInt d = 0; d < nDim; d++) {
4546 parallelIo.writeArray(&derivativesC(d, 0), name + std::to_string(start_lambda2 + 2 + d));
4547 }
4548 }
4549 }
4550 }
4551}
4552
4560template <MInt nDim>
4562 TRACE();
4563 const MFloat Lb = m_referenceLength;
4564 // cout << "Lb: " << Lb << endl;
4565 MFloat kpRatio = F4; // peak wave number of prescribed spectrum
4566 kpRatio = Context::getSolverProperty<MFloat>("kpRatio", this->m_solverId, AT_, &kpRatio);
4567 if(Context::propertyExists("UrmsInit")) {
4568 m_UrmsInit = Context::getSolverProperty<MFloat>("UrmsInit", this->m_solverId, AT_);
4569 m_ReLambda = F1 / (F2 * PI * kpRatio) * sqrt(F5 / F6) * m_Re; // assuming that Re = Lb * m_UrmsInit / nu
4570 /*see literature: Lattice Boltzmann simulation of turbulent flow laden with finite-size particles
4571 Hui Gao, Hui Li, Lian-Ping Wang*/
4572 } else if(Context::propertyExists("ReLambda")) {
4573 m_ReLambda = Context::getSolverProperty<MFloat>("ReLambda", this->m_solverId, AT_);
4574 m_UrmsInit = m_ReLambda * 2 * PI * kpRatio * m_nu * sqrt(F6 / F5) / Lb;
4575 } else {
4576 mTerm(1, AT_, "UrmsInit or ReLambda and nu must be specified in property file");
4577 }
4578}
4579
4580template <MInt nDim>
4582 mTerm(1, AT_, "computeFFTStatistics not implemented for 2D");
4583}
4584
4592template <>
4594 TRACE();
4595 constexpr MInt nDim = 3;
4596
4597 std::array<MFloat, nDim * 2> bBox;
4598 m_geometry->getBoundingBox(bBox.data());
4599
4600 MInt noVel = nDim;
4601
4602 // fft-domain dimensions
4603 // this holds the size of the domain in number of cells on lowest level
4604 const MInt fftLevel = grid().maxUniformRefinementLevel();
4605 if(fftLevel > grid().maxUniformRefinementLevel()) mTerm(1, AT_, "Non-isotropic mesh is not implemented, yet");
4606 if(fftLevel < minLevel()) mTerm(1, AT_, "Parents missing for fftLevel < minLevel()");
4607
4608 const MFloat dx = c_cellLengthAtLevel(fftLevel);
4609 const MFloat dxeps = 0.1 * dx;
4610 MInt nx = (bBox[3] - bBox[0] + dxeps) / dx;
4611 MInt ny = (bBox[4] - bBox[1] + dxeps) / dx;
4612 MInt nz = (bBox[5] - bBox[2] + dxeps) / dx;
4613
4614 // MFloatScratchSpace coupling(a_noCells(), nDim, AT_, "coupling");
4615 MFloatScratchSpace pVariables(a_noCells(), noVel, AT_, "pVariables");
4616 // MFloatScratchSpace cVariables(a_noCells(), m_noVars, AT_, "cVariables");
4617 MFloatScratchSpace oldVariables(a_noCells(), noVel, AT_, "oldVariables");
4618 /*MInt filterLvlLaplace = fftLevel;
4619 filterLvlLaplace = Context::getSolverProperty<MInt>("filterLvlLaplace", m_solverId, AT_, &filterLvlLaplace);
4620 */
4621 // MFloat couplingCheck = determineCoupling(coupling);
4622 for(MInt cellId = 0; cellId < a_noCells(); cellId++) {
4623 for(MInt i = 0; i < noVel; i++) {
4624 pVariables(cellId, i) = a_variable(cellId, i);
4625 // cVariables(cellId, i) = a_variable(cellId, i);
4626 oldVariables(cellId, i) = a_oldVariable(cellId, i);
4627 }
4628 }
4629 /* if(domainId() == 0)
4630 cerr << "coupling check " << couplingCheck << " " << m_couplingRate << " " << couplingCheck / (DX * DX * DX)
4631 << " " << m_couplingRate / (DX * DX * DX) << " " << DX * m_couplingRate / POW3(DX * m_UInfinity) << endl; */
4632
4633 // Apply volumetric filter only for particle resolved simulations with local grid refinement
4634 /* if(maxRefinementLevel() != fftLevel) {
4635 for(MInt cellId = 0; cellId < a_noCells(); cellId++) {
4636 if(a_hasProperty(cellId, SolverCell::IsSplitCell)) continue;
4637 if(a_isPeriodic(cellId)) continue;
4638 if(m_bndryCellIds->a[cellId] < -1) continue;
4639 if(a_isHalo(cellId)) continue;
4640 if(a_level(cellId) != fftLevel) continue;
4641 if(!a_hasProperty(cellId, SolverCell::IsSplitChild) && c_noChildren(cellId) > 0) {
4642 reduceData(cellId, &coupling(0), nDim, false);
4643 reduceData(cellId, &(pVariables(0, 0)), m_noVars);
4644 reduceData(cellId, &(cVariables(0, 0)), m_noVars);
4645 reduceData(cellId, &(oldVariables(0, 0)), m_noVars);
4646 }
4647 }
4648 } */
4649
4650 MInt noLocDat = 0;
4651 for(MInt cellId = 0; cellId < a_noCells(); cellId++) {
4652 if(a_isHalo(cellId)) continue;
4653 if(a_level(cellId) != fftLevel) continue;
4654 noLocDat++;
4655 }
4656 MFloatScratchSpace velDat(noLocDat, 3 * nDim, AT_, "velDat");
4657 MIntScratchSpace velPos(noLocDat, AT_, "velPos");
4658
4659 noLocDat = 0;
4660
4661 // set UrmsInit and ReLambda once after iterative init of rho
4662 if(globalTimeStep == 0 || globalTimeStep / m_fftInterval == 1) {
4663 getReLambdaAndUrmsInit();
4664 if(domainId() == 0) cerr << "UrmsInit: " << m_UrmsInit << endl << "ReLambdaInit: " << m_ReLambda << endl;
4665 }
4666
4667 // const MFloat dt = dx * m_Ma * LBCS / m_UrmsInit;
4668 // const MFloat velocityFactor = m_UrmsInit * F1BCS / m_Ma;
4669 // const MFloat nu = m_UrmsInit * nx / m_Re;
4670 /* if(domainId() == 0)
4671 cout << "viscosity for computeEnergySpectrum: " << m_nu << endl; */
4672
4673 for(MInt cellId = 0; cellId < a_noCells(); cellId++) {
4674 if(a_isHalo(cellId)) continue;
4675 if(a_level(cellId) != fftLevel) continue;
4676
4677 MFloat actualCellLength = c_cellLengthAtCell(cellId);
4678 MInt xPos = floor((F1B2 * nx + (a_coordinate(cellId, 0) - F1B2 * (actualCellLength)) / dx) + 0.1);
4679 MInt yPos = floor((F1B2 * ny + (a_coordinate(cellId, 1) - F1B2 * (actualCellLength)) / dx) + 0.1);
4680 MInt zPos = floor((F1B2 * nz + (a_coordinate(cellId, 2) - F1B2 * (actualCellLength)) / dx) + 0.1);
4681 if(xPos > nx - 1 || xPos < 0 || yPos > ny - 1 || yPos < 0 || zPos > nz - 1 || zPos < 0) {
4682 cerr << "ERROR: wrong array position!" << endl;
4683 cerr << "pos=" << xPos << ", " << yPos << ", " << zPos << endl;
4684 cerr << "coorda = (" << a_coordinate(cellId, 0) << ", " << a_coordinate(cellId, 1) << ", "
4685 << a_coordinate(cellId, 2) << ")" << endl;
4686 cerr << "actuallength=" << actualCellLength << endl;
4687 cerr << "minlevel=" << minLevel() << ", maxLevel=" << maxLevel() << endl;
4688 cerr << "lenght on level0=" << c_cellLengthAtLevel(0) << endl;
4689 mTerm(1, AT_, "Wrong array position");
4690 }
4691
4692 // TODO: adjust for lb usage
4693 MInt pos = zPos + nz * (yPos + ny * xPos);
4694 for(MInt i = 0; i < nDim; i++) {
4695 // Transform velocity data from LB to physical space, because spectrum is computed in physical space
4696 velDat(noLocDat, i) = pVariables(cellId, PV->VV[i]);
4697 velDat(noLocDat, nDim + i) = a_externalForces(cellId, i); // cellVolume in lb units is 1
4698 // MFloat u1 = cVariables(cellId, CV->RHO_VV[i]) / cVariables(cellId, CV->RHO);
4699 // MFloat u0 = oldVariables(cellId, CV->RHO_VV[i]) / oldVariables(cellId, CV->RHO);
4700 MFloat u1 = pVariables(cellId, PV->VV[i]);
4701 MFloat u0 = oldVariables(cellId, PV->VV[i]);
4702 velDat(noLocDat, 2 * nDim + i) = ((u1 - u0) / F1); // rate of change beweeten two timeteps
4703 }
4704 velPos(noLocDat) = pos;
4705 noLocDat++;
4706 }
4707
4708 maia::math::computeEnergySpectrum(velDat, velPos, 3 * nDim, noLocDat, nx, ny, nz, m_nu, mpiComm());
4709}
4710
4711template <MInt nDim>
4712inline void LbSolver<nDim>::getStrainTensor(MFloatScratchSpace& derivatives, MInt cellId, MFloatScratchSpace& strain) {
4713 // TRACE();
4714
4715 // diagonal
4716 for(MInt d1 = 0; d1 < nDim; d1++)
4717 strain(d1, d1) = derivatives(d1 * nDim + d1, cellId);
4718
4719 // off-diagonal
4720 for(MInt d1 = 0; d1 < nDim - 1; d1++)
4721 for(MInt d2 = d1 + 1; d2 < nDim; d2++) {
4722 MInt a = d1 * nDim + d2;
4723 MInt b = d2 * nDim + d1;
4724 strain(d1, d2) = 0.5 * (derivatives(a, cellId) + derivatives(b, cellId));
4725 strain(d2, d1) = strain(d1, d2);
4726 }
4727}
4728
4729
4730template <MInt nDim>
4731inline void LbSolver<nDim>::getVorticityTensor(MFloatScratchSpace& derivatives, MInt cellId,
4732 MFloatScratchSpace& vorticity) {
4733 // TRACE();
4734
4735 // diagonal
4736 for(MInt d1 = 0; d1 < nDim; d1++)
4737 vorticity(d1, d1) = 0.0;
4738
4739 // off-diagonal
4740 for(MInt d1 = 0; d1 < nDim - 1; d1++)
4741 for(MInt d2 = d1 + 1; d2 < nDim; d2++) {
4742 MInt a = d1 * nDim + d2;
4743 MInt b = d2 * nDim + d1;
4744 vorticity(d1, d2) = 0.5 * (derivatives(a, cellId) - derivatives(b, cellId));
4745 vorticity(d2, d1) = 0.5 * (derivatives(b, cellId) - derivatives(a, cellId));
4746 }
4747}
4748
4766template <MInt nDim>
4768 // TRACE();
4769 MInt lbdir1 = 2 * spaceDir;
4770 MInt lbdir2 = lbdir1 + 1;
4771
4772 MInt left = c_neighborId(cellId, lbdir1);
4773 MInt right = c_neighborId(cellId, lbdir2);
4774 MInt leftleft = (c_neighborId(cellId, lbdir1) > -1) ? c_neighborId(left, lbdir1) : -1;
4775 MInt rightright = (c_neighborId(cellId, lbdir2) > -1) ? c_neighborId(right, lbdir2) : -1;
4776 MFloat gradient = F0;
4777
4778 MFloat pt = a_variable(cellId, PV->RHO) * F1B3
4779 + F1B2 / a_variable(cellId, PV->RHO)
4780 * (a_variable(cellId, PV->U) * a_variable(cellId, PV->U)
4781 + a_variable(cellId, PV->V) * a_variable(cellId, PV->V)
4782 + a_variable(cellId, PV->W) * a_variable(cellId, PV->W));
4783
4784 if((left > -1) && (right > -1)) {
4785 MFloat ptLeft =
4786 a_variable(left, PV->RHO) * F1B3
4787 + F1B2 / a_variable(left, PV->RHO)
4788 * (a_variable(left, PV->U) * a_variable(left, PV->U) + a_variable(left, PV->V) * a_variable(left, PV->V)
4789 + a_variable(left, PV->W) * a_variable(left, PV->W));
4790
4791 MFloat ptRight = a_variable(right, PV->RHO) * F1B3
4792 + F1B2 / a_variable(right, PV->RHO)
4793 * (a_variable(right, PV->U) * a_variable(right, PV->U)
4794 + a_variable(right, PV->V) * a_variable(right, PV->V)
4795 + a_variable(right, PV->W) * a_variable(right, PV->W));
4796
4797 if(leftleft > -1 && rightright > -1) { // use central differences 4th order
4798
4799 MFloat ptLeftLeft = a_variable(leftleft, PV->RHO) * F1B3
4800 + F1B2 / a_variable(leftleft, PV->RHO)
4801 * (a_variable(leftleft, PV->U) * a_variable(leftleft, PV->U)
4802 + a_variable(leftleft, PV->V) * a_variable(leftleft, PV->V)
4803 + a_variable(leftleft, PV->W) * a_variable(leftleft, PV->W));
4804
4805 MFloat ptRightRight = a_variable(rightright, PV->RHO) * F1B3
4806 + F1B2 / a_variable(rightright, PV->RHO)
4807 * (a_variable(rightright, PV->U) * a_variable(rightright, PV->U)
4808 + a_variable(rightright, PV->V) * a_variable(rightright, PV->V)
4809 + a_variable(rightright, PV->W) * a_variable(rightright, PV->W));
4810
4811 gradient = (-ptRightRight + 8.0 * ptRight - 8.0 * ptLeft + ptLeftLeft) / 12.0;
4812
4813 } else {
4814 if(leftleft > -1) { // backward differences 3nd order
4815
4816 MFloat ptLeftLeft = a_variable(leftleft, PV->RHO) * F1B3
4817 + F1B2 / a_variable(leftleft, PV->RHO)
4818 * (a_variable(leftleft, PV->U) * a_variable(leftleft, PV->U)
4819 + a_variable(leftleft, PV->V) * a_variable(leftleft, PV->V)
4820 + a_variable(leftleft, PV->W) * a_variable(leftleft, PV->W));
4821
4822 gradient = (2.0 * ptRight + 3.0 * pt - 6.0 * ptLeft + ptLeftLeft) / 6.0;
4823 } else if(rightright > -1) { // forward differences 3nd order
4824
4825 MFloat ptRightRight = a_variable(rightright, PV->RHO) * F1B3
4826 + F1B2 / a_variable(rightright, PV->RHO)
4827 * (a_variable(rightright, PV->U) * a_variable(rightright, PV->U)
4828 + a_variable(rightright, PV->V) * a_variable(rightright, PV->V)
4829 + a_variable(rightright, PV->W) * a_variable(rightright, PV->W));
4830
4831 gradient = (-ptRightRight + 6.0 * ptRight - 3.0 * pt - 2.0 * ptLeft) / 6.0;
4832 } else { // use central differences 2nd order
4833 gradient = (ptRight - ptLeft) / 2.0;
4834 }
4835 }
4836 } else { // use forward or backward differences 1st order
4837 // backward
4838 if(left > -1) {
4839 MFloat ptLeft =
4840 a_variable(left, PV->RHO) * F1B3
4841 + F1B2 / a_variable(left, PV->RHO)
4842 * (a_variable(left, PV->U) * a_variable(left, PV->U) + a_variable(left, PV->V) * a_variable(left, PV->V)
4843 + a_variable(left, PV->W) * a_variable(left, PV->W));
4844
4845 gradient = pt - ptLeft;
4846 }
4847 // forward
4848 else if(right > -1) {
4849 MFloat ptRight = a_variable(right, PV->RHO) * F1B3
4850 + F1B2 / a_variable(right, PV->RHO)
4851 * (a_variable(right, PV->U) * a_variable(right, PV->U)
4852 + a_variable(right, PV->V) * a_variable(right, PV->V)
4853 + a_variable(right, PV->W) * a_variable(right, PV->W));
4854
4855 gradient = ptRight - pt;
4856 }
4857 }
4858 return gradient;
4859}
4860
4861template <MInt nDim>
4863 TRACE();
4864
4865 if(g_multiSolverGrid) {
4866 TERMM(1, "Does not work with multi-solver grid concept");
4867 }
4868
4879 MInt reductionLevel = Context::getSolverProperty<MInt>("pp_reductionLevel", m_solverId, AT_);
4880
4881 // 1. Reduce the grid
4882 // In C++11 prefer lambdas over bind
4883#if defined(MAIA_MS_COMPILER)
4884 MInt recalcIdTreeSize =
4885 grid().raw().reduceToLevel(reductionLevel, [&](const MInt cellId) { this->interpolateToParentCells(cellId); });
4886#else
4887 // In C++03: prefer boost::bind, std::bind1st is deprecated.
4888 MInt recalcIdTreeSize =
4889 grid().raw().reduceToLevel(reductionLevel, std::bind(&LbSolver<nDim>::interpolateToParentCells, this, _1));
4890#endif
4891
4892 MChar buf1[16];
4893 MChar buf2[16];
4894 MString preName = "";
4895 MString preName2 = "";
4896 MString preName3 = "";
4897 sprintf(buf1, "%d", (globalTimeStep - 1));
4898 sprintf(buf2, "%d", reductionLevel);
4899
4900 preName.append("Lvl");
4901 preName.append(buf2);
4902 preName.append("_");
4903 preName2 = preName;
4904 preName2.append(buf1);
4905 preName2.append("_");
4906 preName3 = preName + "PV_";
4907 preName3.append(buf1);
4908 preName3 = preName3 + ParallelIo::fileExt();
4909
4910 MString tmpFileNameGrid = preName2 + grid().gridInputFileName();
4911 MString tmpFileNameGridPath = outputDir() + tmpFileNameGrid;
4912 MString tmpOutputFileName = outputDir() + preName3;
4913
4914 // This contains the recalculated Ids sorted by the tree after the save-call
4915 MIntScratchSpace recalcIdTree(recalcIdTreeSize, AT_, "recalcIdTree");
4916 grid().raw().saveGrid(tmpFileNameGridPath.c_str(), grid().raw().m_haloCells, grid().raw().m_windowCells,
4917 grid().raw().m_azimuthalHaloCells, grid().raw().m_azimuthalUnmappedHaloCells,
4918 grid().raw().m_azimuthalWindowCells, recalcIdTree.begin());
4919
4920 // 3. Call function to write interpolated solution
4921 saveUVWRhoTOnlyPar(tmpOutputFileName.c_str(), tmpFileNameGrid.c_str(), recalcIdTree.begin());
4922
4923 // 4. Exit MAIA
4924 TERMM(0, AT_);
4925}
4926
4936template <MInt nDim>
4938 TRACE();
4939 vars = &(a_variable(cellId, 0));
4940}
4941
4952template <MInt nDim>
4954 TRACE();
4955
4956 if(parentlevel == grid().maxLevel()) {
4957 TERMM(1, "Interpolation to parent cells residing on the finest level makes no sense, exiting ... ");
4958 }
4959
4960 for(MInt id = 0; id < noInternalCells(); id++) {
4961 // skip cells not on parentlevel
4962 if(a_level(id) != parentlevel || c_isLeafCell(id)) continue;
4963
4964 for(MInt v = 0; v < m_noVariables; v++) {
4965 a_variable(id, v) = .0;
4966 }
4967
4968 MFloat avg_factor = 1.0 / c_noChildren(id);
4969
4970 for(MInt ch = 0; ch < IPOW2(nDim); ch++) {
4971 // skip
4972 if(c_childId(id, ch) == -1) continue;
4973
4974 // sum up
4975 for(MInt v = 0; v < m_noVariables; v++) {
4976 a_variable(id, v) += avg_factor * (a_variable(c_childId(id, ch), v));
4977 }
4978 }
4979 }
4980}
4981
4994template <MInt nDim>
4996 TRACE();
4997 if constexpr(nDim != 2 && nDim != 3) {
4998 cerr << " In global function loadRestartWithoutDistributionsPar: wrong number of dimensions !" << endl;
4999 exit(0);
5000 return;
5001 }
5002
5003 // File loading.
5004 using namespace maia::parallel_io;
5005 ParallelIo parallelIo(fileName, PIO_READ, mpiComm());
5006
5007 // read time
5008 parallelIo.getAttribute(&m_time, "time");
5009 parallelIo.getAttribute(&globalTimeStep, "globalTimeStep");
5010
5011 if(m_velocityControl.restart) {
5012 parallelIo.getAttribute(&m_velocityControl.lastGlobalAvgV, "lastGlobalAvgV");
5013 parallelIo.getAttribute(&m_velocityControl.previousError, "previousError");
5014 parallelIo.getAttribute(&m_velocityControl.integratedError, "integratedError");
5015 parallelIo.getAttribute(&m_velocityControl.derivedError, "derivedError");
5016 for(MInt i = 0; i < nDim; i++) {
5017 parallelIo.getAttribute(&m_volumeAccel[i], "volumeAcceleration_" + std::to_string(i));
5018 }
5019 }
5020
5021 // This should be the same for all the variables
5022 MPI_Offset dimLen = noInternalCells();
5023 MPI_Offset start = domainOffset(domainId());
5024 parallelIo.setOffset(dimLen, start);
5025 // Load our variables
5026
5027 { // Velocity u
5028 MFloatScratchSpace tmp_velocityU((MInt)dimLen, AT_, "tmp_velocityU");
5029 parallelIo.readArray(tmp_velocityU.getPointer(), "variables0");
5030 for(MInt i = 0; i < (MInt)dimLen; ++i)
5031 a_variable(i, PV->U) = tmp_velocityU.p[i];
5032 }
5033
5034 { // Velocity v
5035 MFloatScratchSpace tmp_velocityV((MInt)dimLen, AT_, "tmp_velocityV");
5036 parallelIo.readArray(tmp_velocityV.getPointer(), "variables1");
5037 for(MInt i = 0; i < (MInt)dimLen; ++i)
5038 a_variable(i, PV->V) = tmp_velocityV.p[i];
5039 }
5040
5041 if constexpr(nDim == 3) {
5042 { // Velocity w
5043 MFloatScratchSpace tmp_velocityW((MInt)dimLen, AT_, "tmp_velocityW");
5044 parallelIo.readArray(tmp_velocityW.getPointer(), "variables2");
5045 for(MInt i = 0; i < (MInt)dimLen; ++i)
5046 a_variable(i, PV->W) = tmp_velocityW.p[i];
5047 }
5048
5049 { // Density rho
5050 MFloatScratchSpace tmp_rho((MInt)dimLen, AT_, "tmp_rho");
5051 parallelIo.readArray(tmp_rho.getPointer(), "variables3");
5052 for(MInt i = 0; i < (MInt)dimLen; ++i)
5053 a_variable(i, PV->RHO) = tmp_rho.p[i];
5054 }
5055
5056 if(m_isThermal && !m_isTransport) {
5057 // Temperature t
5058 MFloatScratchSpace tmp_t((MInt)dimLen, AT_, "tmp_t");
5059 parallelIo.readArray(tmp_t.getPointer(), "variables4");
5060 for(MInt i = 0; i < (MInt)dimLen; ++i)
5061 a_variable(i, PV->T) = tmp_t.p[i];
5062 }
5063 if(m_isTransport && !m_isThermal) {
5064 // Concentration c
5065 MFloatScratchSpace tmp_c((MInt)dimLen, AT_, "tmp_c");
5066 parallelIo.readArray(tmp_c.getPointer(), "variables4");
5067 for(MInt i = 0; i < (MInt)dimLen; ++i)
5068 a_variable(i, PV->C) = tmp_c.p[i];
5069 }
5070 if(m_isTransport && m_isThermal) {
5071 // Temperature t
5072 MFloatScratchSpace tmp_t((MInt)dimLen, AT_, "tmp_t");
5073 parallelIo.readArray(tmp_t.getPointer(), "variables4");
5074 for(MInt i = 0; i < (MInt)dimLen; ++i)
5075 a_variable(i, PV->T) = tmp_t.p[i];
5076 // Concentration c
5077 MFloatScratchSpace tmp_c((MInt)dimLen, AT_, "tmp_c");
5078 parallelIo.readArray(tmp_c.getPointer(), "variables5");
5079 for(MInt i = 0; i < (MInt)dimLen; ++i)
5080 a_variable(i, PV->C) = tmp_c.p[i];
5081 }
5082 if(m_isEELiquid) {
5083 MFloatScratchSpace tmp_alphaG((MInt)dimLen, AT_, "tmp_alphaG");
5084 parallelIo.readArray(tmp_alphaG.getPointer(), "variables4");
5085 for(MInt i = 0; i < (MInt)dimLen; ++i)
5086 a_alphaGas(i) = tmp_alphaG.p[i];
5087 }
5088 } else {
5089 { // Density rho
5090 MFloatScratchSpace tmp_rho((MInt)dimLen, AT_, "tmp_rho");
5091 parallelIo.readArray(tmp_rho.getPointer(), "variables2");
5092 for(MInt i = 0; i < (MInt)dimLen; ++i)
5093 a_variable(i, PV->RHO) = tmp_rho.p[i];
5094 }
5095
5096 if(m_isThermal && !m_isTransport) {
5097 // Temperature t
5098 MFloatScratchSpace tmp_t((MInt)dimLen, AT_, "tmp_t");
5099 parallelIo.readArray(tmp_t.getPointer(), "variables3");
5100 for(MInt i = 0; i < (MInt)dimLen; ++i)
5101 a_variable(i, PV->T) = tmp_t.p[i];
5102 }
5103 if(m_isTransport && !m_isThermal) {
5104 // Concentration c
5105 MFloatScratchSpace tmp_c((MInt)dimLen, AT_, "tmp_c");
5106 parallelIo.readArray(tmp_c.getPointer(), "variables3");
5107 for(MInt i = 0; i < (MInt)dimLen; ++i)
5108 a_variable(i, PV->C) = tmp_c.p[i];
5109 }
5110 if(m_isTransport && m_isThermal) {
5111 // Temperature t
5112 MFloatScratchSpace tmp_t((MInt)dimLen, AT_, "tmp_t");
5113 parallelIo.readArray(tmp_t.getPointer(), "variables3");
5114 for(MInt i = 0; i < (MInt)dimLen; ++i)
5115 a_variable(i, PV->T) = tmp_t.p[i];
5116 // Concentration c
5117 MFloatScratchSpace tmp_c((MInt)dimLen, AT_, "tmp_c");
5118 parallelIo.readArray(tmp_c.getPointer(), "variables4");
5119 for(MInt i = 0; i < (MInt)dimLen; ++i)
5120 a_variable(i, PV->C) = tmp_c.p[i];
5121 }
5122 }
5123
5124 { // old Velocity u
5125 MFloatScratchSpace tmp_oldVelocityU((MInt)dimLen, AT_, "tmp_oldVelocityU");
5126 parallelIo.readArray(tmp_oldVelocityU.getPointer(), "oldVariables0");
5127 for(MInt i = 0; i < (MInt)dimLen; ++i) {
5128 a_oldVariable(i, PV->U) = tmp_oldVelocityU.p[i];
5129 }
5130 }
5131
5132 { // old Velocity v
5133 MFloatScratchSpace tmp_oldVelocityV((MInt)dimLen, AT_, "tmp_oldVelocityV");
5134 parallelIo.readArray(tmp_oldVelocityV.getPointer(), "oldVariables1");
5135 for(MInt i = 0; i < (MInt)dimLen; ++i)
5136 a_oldVariable(i, PV->V) = tmp_oldVelocityV.p[i];
5137 }
5138
5139 if constexpr(nDim == 3) {
5140 { // old Velocity w
5141 MFloatScratchSpace tmp_oldVelocityW((MInt)dimLen, AT_, "tmp_oldVelocityW");
5142 parallelIo.readArray(tmp_oldVelocityW.getPointer(), "oldVariables2");
5143 for(MInt i = 0; i < (MInt)dimLen; ++i)
5144 a_oldVariable(i, PV->W) = tmp_oldVelocityW.p[i];
5145 }
5146
5147 { // old Density rho
5148 MFloatScratchSpace tmp_oldRho((MInt)dimLen, AT_, "tmp_oldRho");
5149 parallelIo.readArray(tmp_oldRho.getPointer(), "oldVariables3");
5150 for(MInt i = 0; i < (MInt)dimLen; ++i)
5151 a_oldVariable(i, PV->RHO) = tmp_oldRho.p[i];
5152 }
5153
5154 if(m_isThermal && !m_isTransport) {
5155 { // old Temperature t
5156 MFloatScratchSpace tmp_oldT((MInt)dimLen, AT_, "tmp_oldT");
5157 parallelIo.readArray(tmp_oldT.getPointer(), "oldVariables4");
5158 for(MInt i = 0; i < (MInt)dimLen; ++i)
5159 a_oldVariable(i, PV->T) = tmp_oldT.p[i];
5160 }
5161 }
5162 if(m_isTransport && !m_isThermal) {
5163 { // old Concentration c
5164 MFloatScratchSpace tmp_oldC((MInt)dimLen, AT_, "tmp_oldC");
5165 parallelIo.readArray(tmp_oldC.getPointer(), "oldVariables4");
5166 for(MInt i = 0; i < (MInt)dimLen; ++i)
5167 a_oldVariable(i, PV->C) = tmp_oldC.p[i];
5168 }
5169 }
5170 if(m_isTransport && m_isThermal) {
5171 { // old Temperature t
5172 MFloatScratchSpace tmp_oldT((MInt)dimLen, AT_, "tmp_oldT");
5173 parallelIo.readArray(tmp_oldT.getPointer(), "oldVariables4");
5174 for(MInt i = 0; i < (MInt)dimLen; ++i)
5175 a_oldVariable(i, PV->T) = tmp_oldT.p[i];
5176 }
5177 { // old Concentration c
5178 MFloatScratchSpace tmp_oldC((MInt)dimLen, AT_, "tmp_oldC");
5179 parallelIo.readArray(tmp_oldC.getPointer(), "oldVariables5");
5180 for(MInt i = 0; i < (MInt)dimLen; ++i)
5181 a_oldVariable(i, PV->C) = tmp_oldC.p[i];
5182 }
5183 }
5184 } else {
5185 { // old Density rho
5186 MFloatScratchSpace tmp_oldRho((MInt)dimLen, AT_, "tmp_oldRho");
5187 parallelIo.readArray(tmp_oldRho.getPointer(), "oldVariables2");
5188 for(MInt i = 0; i < (MInt)dimLen; ++i)
5189 a_oldVariable(i, PV->RHO) = tmp_oldRho.p[i];
5190 }
5191
5192 if(m_isThermal && !m_isTransport) {
5193 // old Temperature t
5194 MFloatScratchSpace tmp_oldT((MInt)dimLen, AT_, "tmp_oldT");
5195 parallelIo.readArray(tmp_oldT.getPointer(), "oldVariables3");
5196 for(MInt i = 0; i < (MInt)dimLen; ++i)
5197 a_oldVariable(i, PV->T) = tmp_oldT.p[i];
5198 }
5199 if(m_isTransport && !m_isThermal) {
5200 // old Concentration c
5201 MFloatScratchSpace tmp_oldC((MInt)dimLen, AT_, "tmp_oldC");
5202 parallelIo.readArray(tmp_oldC.getPointer(), "oldVariables3");
5203 for(MInt i = 0; i < (MInt)dimLen; ++i)
5204 a_oldVariable(i, PV->C) = tmp_oldC.p[i];
5205 }
5206 if(m_isTransport && m_isThermal) {
5207 // old Temperature t
5208 MFloatScratchSpace tmp_oldT((MInt)dimLen, AT_, "tmp_oldT");
5209 parallelIo.readArray(tmp_oldT.getPointer(), "oldVariables3");
5210 for(MInt i = 0; i < (MInt)dimLen; ++i)
5211 a_oldVariable(i, PV->T) = tmp_oldT.p[i];
5212 // old Concentration c
5213 MFloatScratchSpace tmp_oldC((MInt)dimLen, AT_, "tmp_oldC");
5214 parallelIo.readArray(tmp_oldC.getPointer(), "oldVariables4");
5215 for(MInt i = 0; i < (MInt)dimLen; ++i)
5216 a_oldVariable(i, PV->C) = tmp_oldC.p[i];
5217 }
5218 }
5219
5220 m_bndCnd->bcDataReadRestartData(parallelIo);
5221}
5222
5229template <MInt nDim>
5231 TRACE();
5232 // do nothing, all diagonals are created in createMultiSolverInformationPar(), see cartesiangrid.h
5233}
5234
5239template <MInt nDim>
5241 TRACE();
5242
5243 stringstream fileName;
5244 stringstream grid_;
5245
5246 fileName << outputDir() << "restart_" << getIdentifier() << globalTimeStep << ParallelIo::fileExt();
5247 grid_ << grid().gridInputFileName();
5248
5249 saveRestartWithDistributionsPar(fileName.str().c_str(), grid().gridInputFileName().c_str());
5250}
5251
5256template <MInt nDim>
5258 TRACE();
5259
5260 NEW_TIMER_GROUP(t_restart, "Restart");
5261 NEW_TIMER(t_restartAll, "restart file loading", t_restart);
5262 RECORD_TIMER_START(t_restartAll);
5263
5264 m_log << "#########################################################################################################"
5265 "#############"
5266 << endl;
5267 m_log << "## Loading restart file "
5268 " ##"
5269 << endl;
5270 m_log << "#########################################################################################################"
5271 "#############"
5272 << endl
5273 << endl;
5274
5275
5276 MBool initializeRestart = false;
5277 initializeRestart = Context::getSolverProperty<MBool>("initRestart", m_solverId, AT_, &initializeRestart);
5278 stringstream varFileName;
5279
5280 m_log << " + Restart file information:" << endl;
5281 m_log << " - restart time step: " << m_restartTimeStep << endl;
5282 if(m_initFromCoarse)
5283 m_log << " - type: "
5284 << "from coarse mac. variables " << endl;
5285 else
5286 m_log << " - type: " << (initializeRestart ? "from mac. variables" : "from distributions") << endl;
5287
5288 varFileName << restartDir() << "restart_" << getIdentifier() << m_restartTimeStep << ParallelIo::fileExt();
5289
5290 m_log << " - file name: " << varFileName.str() << endl << endl;
5291 m_log << " + Loading the file ..." << endl;
5292
5293 if(m_initFromCoarse)
5294 loadRestartWithoutDistributionsParFromCoarse((varFileName.str()).c_str());
5295 else {
5296 // restart with distributions
5297 if(!initializeRestart) loadRestartWithDistributionsPar((varFileName.str()).c_str());
5298 // restart only from macroscopic variables
5299 else
5300 loadRestartWithoutDistributionsPar((varFileName.str()).c_str());
5301 }
5302 m_log << endl;
5303
5304 if(m_velocityControl.restart) initVolumeForces();
5305
5306 RECORD_TIMER_STOP(t_restartAll);
5307}
5308
5309//----------------------------------------------------------------------------
5310
5325template <MInt nDim>
5327 // 1. Wait for unfinished sending operations from last iteration.
5328 if(noNeighborDomains() <= 0) return;
5329
5330 if(mode == 0) {
5331 // TODO labels:LB,COMM As soon as the steps of the collision step are separated
5332 // the implementation of the non-blocking communication can go on.
5333 // At the moment the communication cannot be hidden behind anything.
5334 // 0. Calculate the macroscopic variables for all cells first.
5335 // They are needed for the boundary conditons
5336 // RECORD_TIMER_START(m_t.collision);
5337 // for(MInt i = 0; i < m_currentMaxNoCells; i++) {
5338 // MInt pCellId = m_activeCellList[i];
5339 // MFloat rho = 0;
5340 // Mfloat u[nDim] = {F0};
5341 // this->calculateMacroscopicVariables(pCellId, &rho, u);
5342 // a_variables(pCellId, PV->RHO) = rho;
5343 // for(MInt d = 0; d < nDim; d++) {
5344 // a_variables(pCellId, d) = u[d];
5345 // RECORD_TIMER_STOP(m_t.collision);
5346
5347 // 1. Start the receive of data from neighbors.
5348 RECORD_TIMER_START(m_t.communication);
5349 (this->*m_receiveMethod)();
5350 RECORD_TIMER_STOP(m_t.communication);
5351 // 2. Perform the collision of the window cells
5352 // RECORD_TIMER_START(m_t.collision);
5353 // (this->*m_solutionStepMethod)(m_activeWinCellList, m_currentMaxNoWinCells);
5354 // RECORD_TIMER_STOP(m_t.collision);
5355
5356 // 3. Gather all information from the cells in a send buffer.
5357 RECORD_TIMER_START(m_t.packing);
5358 (this->*m_gatherMethod)();
5359 RECORD_TIMER_STOP(m_t.packing);
5360
5361 // 4. Send the gathered information. In the meantime the collision for all cells except
5362 // window cells is done
5363 RECORD_TIMER_START(m_t.communication);
5364 (this->*m_sendMethod)();
5365 RECORD_TIMER_STOP(m_t.communication);
5366
5367 } else {
5368 RECORD_TIMER_START(m_t.communication);
5369 MPI_Waitall(noNeighborDomains(), &mpi_requestR[0], MPI_STATUS_IGNORE, AT_);
5370 RECORD_TIMER_STOP(m_t.communication);
5371
5372 // 5. Scatter all data from the receive buffer to the cells.
5373 RECORD_TIMER_START(m_t.unpacking);
5374 (this->*m_scatterMethod)();
5375 RECORD_TIMER_STOP(m_t.unpacking);
5376
5377 RECORD_TIMER_START(m_t.communication);
5378 MPI_Waitall(noNeighborDomains(), &mpi_requestS[0], MPI_STATUS_IGNORE, AT_);
5379 RECORD_TIMER_STOP(m_t.communication);
5380 }
5381}
5382
5396template <MInt nDim>
5398 TRACE();
5399
5400 if(mode == 0) return; // only executed for non-blocking
5401
5402 // 1. Gather all information from the cells in a send buffer.
5403 RECORD_TIMER_START(m_t.packing);
5404 (this->*m_gatherMethod)();
5405 RECORD_TIMER_STOP(m_t.packing);
5406
5407 RECORD_TIMER_START(m_t.communication);
5408 // 2. Send the gathered information.
5409 (this->*m_sendMethod)();
5410 // 3. Receive data from neighbors.
5411 (this->*m_receiveMethod)();
5412 RECORD_TIMER_STOP(m_t.communication);
5413
5414 // 4. Scatter all data from the receive buffer to the cells.
5415 RECORD_TIMER_START(m_t.unpacking);
5416 (this->*m_scatterMethod)();
5417 RECORD_TIMER_STOP(m_t.unpacking);
5418}
5419
5420template <MInt nDim>
5421void LbSolver<nDim>::exchange(MInt mode) {
5422 // TRACE();
5423 RECORD_TIMER_START(m_t.solutionStep);
5424 RECORD_TIMER_START(m_t.exchange);
5425 (this->*m_exchangeMethod)(mode);
5426 RECORD_TIMER_STOP(m_t.exchange);
5427 RECORD_TIMER_STOP(m_t.solutionStep);
5428}
5429
5430
5431//----------------------------------------------------------------------------
5432
5433
5445template <MInt nDim>
5446inline void LbSolver<nDim>::gatherNormal() {
5447 TRACE();
5448
5449 for(MInt n = 0; n < noNeighborDomains(); n++) {
5450 for(MInt var = 0; var < m_noElementsTransfer; var++) {
5451 MInt sendBufferCounter = m_nghbrOffsetsWindow[n][var];
5452 if(var == 0) {
5453 maia::parallelFor(0, noWindowCells(n), [=](MInt j) {
5454 for(MInt d = 0; d < m_noDistributions; d++) {
5455 MInt id = windowCell(n, j);
5456 MInt offset = sendBufferCounter + (j * m_noDistributions + d);
5457 m_sendBuffers[n][offset] = a_distribution(id, d);
5458 }
5459 });
5460 } else if(var == 1 && m_isThermal) {
5461 maia::parallelFor(0, noWindowCells(n), [=](MInt j) {
5462 for(MInt d = 0; d < m_noDistributions; d++) {
5463 MInt id = windowCell(n, j);
5464 MInt offset = sendBufferCounter + (j * m_noDistributions + d);
5465 m_sendBuffers[n][offset] = a_distributionThermal(id, d);
5466 }
5467 });
5468 } else if(var == 1 && m_isTransport) {
5469 maia::parallelFor(0, noWindowCells(n), [=](MInt j) {
5470 for(MInt d = 0; d < m_noDistributions; d++) {
5471 MInt id = windowCell(n, j);
5472 MInt offset = sendBufferCounter + (j * m_noDistributions + d);
5473 m_sendBuffers[n][offset] = a_distributionTransport(id, d);
5474 }
5475 });
5476 } else if(var == 2 && m_isThermal && m_isTransport) {
5477 maia::parallelFor(0, noWindowCells(n), [=](MInt j) {
5478 for(MInt d = 0; d < m_noDistributions; d++) {
5479 MInt id = windowCell(n, j);
5480 MInt offset = sendBufferCounter + (j * m_noDistributions + d);
5481 m_sendBuffers[n][offset] = a_distributionTransport(id, d);
5482 }
5483 });
5484 } else if(var == 1 || (var == 2 && (m_isThermal || m_isTransport))
5485 || (var == 3 && m_isThermal && m_isTransport)) {
5486 maia::parallelFor(0, noWindowCells(n), [=](MInt j) {
5487 for(MInt v = 0; v < m_noVariables; v++) {
5488 MInt id = windowCell(n, j);
5489 MInt offset = sendBufferCounter + (j * m_noVariables + v);
5490 m_sendBuffers[n][offset] = a_variable(id, v);
5491 }
5492 });
5493 } else {
5494 maia::parallelFor(0, noWindowCells(n), [=](MInt j) {
5495 for(MInt v = 0; v < m_noVariables; v++) {
5496 MInt id = windowCell(n, j);
5497 MInt offset = sendBufferCounter + (j * m_noVariables + v);
5498 m_sendBuffers[n][offset] = a_oldVariable(id, v);
5499 }
5500 });
5501 }
5502 // for(MInt j = 0; j < noWindowCells(n); j++) {
5503 // memcpy((void*)&m_sendBuffers[n][sendBufferCounter],
5504 // m_baseAddresses[var] + (m_dataBlockSizes[var] * windowCell(n, j)),
5505 // m_dataBlockSizes[var] * sizeof(MFloat));
5506 // sendBufferCounter += m_dataBlockSizes[var];
5507 // }
5508 }
5509 }
5510}
5511
5523template <MInt nDim>
5524inline void LbSolver<nDim>::gatherReduced() {
5525 TRACE();
5526
5527 for(MInt n = 0; n < noNeighborDomains(); n++) {
5528 for(MInt var = 0; var < m_noElementsTransfer; var++) {
5529 if(var == 0) {
5530 maia::parallelFor<true>(0, noWindowCells(n), [=](MInt j) {
5531 for(MInt d = 0; d < (m_noDistributions - 1); d++) {
5532 if(m_windowDistsForExchange[n][j * (m_noDistributions - 1) + d] > -1) {
5533 MInt offset = m_windowDistsForExchange[n][j * (m_noDistributions - 1) + d];
5534 m_sendBuffers[n][offset] = a_distribution(windowCell(n, j), d);
5535 }
5536 }
5537 });
5538 } else if(var == 1 && m_isThermal) {
5539 maia::parallelFor<true>(0, noWindowCells(n), [=](MInt j) {
5540 for(MInt d = 0; d < (m_noDistributions - 1); d++) {
5541 if(m_windowDistsForExchange[n][j * (m_noDistributions - 1) + d] > -1) {
5542 MInt offset =
5543 m_windowDistsForExchange[n][j * (m_noDistributions - 1) + d] + m_noWindowDistDataPerDomain[n];
5544 m_sendBuffers[n][offset] = a_distributionThermal(windowCell(n, j), d);
5545 }
5546 }
5547 });
5548 } else if(var == 1 && m_isTransport) {
5549 maia::parallelFor<true>(0, noWindowCells(n), [=](MInt j) {
5550 for(MInt d = 0; d < (m_noDistributions - 1); d++) {
5551 if(m_windowDistsForExchange[n][j * (m_noDistributions - 1) + d] > -1) {
5552 MInt offset =
5553 m_windowDistsForExchange[n][j * (m_noDistributions - 1) + d] + m_noWindowDistDataPerDomain[n];
5554 m_sendBuffers[n][offset] = a_distributionTransport(windowCell(n, j), d);
5555 }
5556 }
5557 });
5558 } else if(var == 2 && m_isThermal && m_isTransport) {
5559 maia::parallelFor<true>(0, noWindowCells(n), [=](MInt j) {
5560 for(MInt d = 0; d < (m_noDistributions - 1); d++) {
5561 if(m_windowDistsForExchange[n][j * (m_noDistributions - 1) + d] > -1) {
5562 MInt offset =
5563 m_windowDistsForExchange[n][j * (m_noDistributions - 1) + d] + 2 * m_noWindowDistDataPerDomain[n];
5564 m_sendBuffers[n][offset] = a_distributionTransport(windowCell(n, j), d);
5565 }
5566 }
5567 });
5568 } else if(var == 1 || (var == 2 && (m_isThermal || m_isTransport))
5569 || (var == 3 && m_isThermal && m_isTransport)) {
5570 maia::parallelFor<true>(0, noWindowCells(n), [=](MInt j) {
5571 for(MInt v = 0; v < m_noVariables; v++) {
5572 MInt id = windowCell(n, j);
5573 MInt offset = (j * m_noVariables + v) + (m_noDistsTransfer * m_noWindowDistDataPerDomain[n]);
5574 m_sendBuffers[n][offset] = a_variable(id, v);
5575 }
5576 });
5577 } else {
5578 maia::parallelFor<true>(0, noWindowCells(n), [=](MInt j) {
5579 for(MInt v = 0; v < m_noVariables; v++) {
5580 MInt id = windowCell(n, j);
5581 MInt offset = (j * m_noVariables + v) + (m_noDistsTransfer * m_noWindowDistDataPerDomain[n])
5582 + (noWindowCells(n) * m_noVariables);
5583 m_sendBuffers[n][offset] = a_oldVariable(id, v);
5584 }
5585 });
5586 }
5587 }
5588 // MInt totalData =
5589 // m_noDistsTransfer * m_noWindowDistDataPerDomain[n] + noWindowCells(n) * m_noVariables * m_noVarsTransfer;
5590 // for(MInt j = 0; j < totalData; j++) {
5591 // memcpy((void*)&m_sendBuffers[n][j], m_commPtWindow[n][j], sizeof(MFloat));
5592 // }
5593 }
5594}
5595
5607template <MInt nDim>
5608inline void LbSolver<nDim>::scatterNormal() {
5609 TRACE();
5610
5611 for(MInt n = 0; n < noNeighborDomains(); n++) {
5612 for(MInt var = 0; var < m_noElementsTransfer; var++) {
5613 MInt receiveBufferCounter = m_nghbrOffsetsHalo[n][var];
5614 if(var == 0) {
5615 maia::parallelFor(0, noHaloCells(n), [=](MInt j) {
5616 for(MInt d = 0; d < m_noDistributions; d++) {
5617 MInt id = haloCell(n, j);
5618 MInt offset = receiveBufferCounter + (j * m_noDistributions + d);
5619 a_distribution(id, d) = m_receiveBuffers[n][offset];
5620 }
5621 });
5622 } else if(var == 1 && m_isThermal) {
5623 maia::parallelFor(0, noHaloCells(n), [=](MInt j) {
5624 for(MInt d = 0; d < m_noDistributions; d++) {
5625 MInt id = haloCell(n, j);
5626 MInt offset = receiveBufferCounter + (j * m_noDistributions + d);
5627 a_distributionThermal(id, d) = m_receiveBuffers[n][offset];
5628 }
5629 });
5630 } else if(var == 1 && m_isTransport) {
5631 maia::parallelFor(0, noHaloCells(n), [=](MInt j) {
5632 for(MInt d = 0; d < m_noDistributions; d++) {
5633 MInt id = haloCell(n, j);
5634 MInt offset = receiveBufferCounter + (j * m_noDistributions + d);
5635 a_distributionTransport(id, d) = m_receiveBuffers[n][offset];
5636 }
5637 });
5638 } else if(var == 2 && m_isThermal && m_isTransport) {
5639 maia::parallelFor(0, noHaloCells(n), [=](MInt j) {
5640 for(MInt d = 0; d < m_noDistributions; d++) {
5641 MInt id = haloCell(n, j);
5642 MInt offset = receiveBufferCounter + (j * m_noDistributions + d);
5643 a_distributionTransport(id, d) = m_receiveBuffers[n][offset];
5644 }
5645 });
5646 } else if(var == 1 || (var == 2 && (m_isThermal || m_isTransport))
5647 || (var == 3 && m_isThermal && m_isTransport)) {
5648 maia::parallelFor(0, noHaloCells(n), [=](MInt j) {
5649 for(MInt v = 0; v < m_noVariables; v++) {
5650 MInt id = haloCell(n, j);
5651 MInt offset = receiveBufferCounter + (j * m_noVariables + v);
5652 a_variable(id, v) = m_receiveBuffers[n][offset];
5653 }
5654 });
5655 } else {
5656 maia::parallelFor(0, noHaloCells(n), [=](MInt j) {
5657 for(MInt v = 0; v < m_noVariables; v++) {
5658 MInt id = haloCell(n, j);
5659 MInt offset = receiveBufferCounter + (j * m_noVariables + v);
5660 a_oldVariable(id, v) = m_receiveBuffers[n][offset];
5661 }
5662 });
5663 }
5664 // for(MInt j = 0; j < noHaloCells(n); j++) {
5665 // memcpy(m_baseAddresses[var] + (m_dataBlockSizes[var] * haloCell(n, j)),
5666 // (void*)&m_receiveBuffers[n][receiveBufferCounter],
5667 // m_dataBlockSizes[var] * sizeof(MFloat));
5668 // receiveBufferCounter += m_dataBlockSizes[var];
5669 // }
5670 }
5671 }
5672}
5673
5685template <MInt nDim>
5686inline void LbSolver<nDim>::scatterReduced() {
5687 TRACE();
5688
5689 for(MInt n = 0; n < noNeighborDomains(); n++) {
5690 for(MInt var = 0; var < m_noElementsTransfer; var++) {
5691 if(var == 0) {
5692 maia::parallelFor<true>(0, noHaloCells(n), [=](MInt j) {
5693 for(MInt d = 0; d < (m_noDistributions - 1); d++) {
5694 if(m_haloDistsForExchange[n][j * (m_noDistributions - 1) + d] > -1) {
5695 MInt offset = m_haloDistsForExchange[n][j * (m_noDistributions - 1) + d];
5696 a_distribution(haloCell(n, j), d) = m_receiveBuffers[n][offset];
5697 }
5698 }
5699 });
5700 } else if(var == 1 && m_isThermal) {
5701 maia::parallelFor<true>(0, noHaloCells(n), [=](MInt j) {
5702 for(MInt d = 0; d < (m_noDistributions - 1); d++) {
5703 if(m_haloDistsForExchange[n][j * (m_noDistributions - 1) + d] > -1) {
5704 MInt offset = m_haloDistsForExchange[n][j * (m_noDistributions - 1) + d] + m_noHaloDistDataPerDomain[n];
5705 a_distributionThermal(haloCell(n, j), d) = m_receiveBuffers[n][offset];
5706 }
5707 }
5708 });
5709 } else if(var == 1 && m_isTransport) {
5710 maia::parallelFor<true>(0, noHaloCells(n), [=](MInt j) {
5711 for(MInt d = 0; d < (m_noDistributions - 1); d++) {
5712 if(m_haloDistsForExchange[n][j * (m_noDistributions - 1) + d] > -1) {
5713 MInt offset = m_haloDistsForExchange[n][j * (m_noDistributions - 1) + d] + m_noHaloDistDataPerDomain[n];
5714 a_distributionTransport(haloCell(n, j), d) = m_receiveBuffers[n][offset];
5715 }
5716 }
5717 });
5718 } else if(var == 2 && m_isThermal && m_isTransport) {
5719 maia::parallelFor<true>(0, noHaloCells(n), [=](MInt j) {
5720 for(MInt d = 0; d < (m_noDistributions - 1); d++) {
5721 if(m_haloDistsForExchange[n][j * (m_noDistributions - 1) + d] > -1) {
5722 MInt offset =
5723 m_haloDistsForExchange[n][j * (m_noDistributions - 1) + d] + 2 * m_noHaloDistDataPerDomain[n];
5724 a_distributionTransport(haloCell(n, j), d) = m_receiveBuffers[n][offset];
5725 }
5726 }
5727 });
5728 } else if(var == 1 || (var == 2 && (m_isThermal || m_isTransport))
5729 || (var == 3 && m_isThermal && m_isTransport)) {
5730 maia::parallelFor<true>(0, noHaloCells(n), [=](MInt j) {
5731 for(MInt v = 0; v < m_noVariables; v++) {
5732 MInt id = haloCell(n, j);
5733 MInt offset = (j * m_noVariables + v) + m_noDistsTransfer * m_noHaloDistDataPerDomain[n];
5734 a_variable(id, v) = m_receiveBuffers[n][offset];
5735 }
5736 });
5737 } else {
5738 maia::parallelFor<true>(0, noHaloCells(n), [=](MInt j) {
5739 for(MInt v = 0; v < m_noVariables; v++) {
5740 MInt id = haloCell(n, j);
5741 MInt offset = (j * m_noVariables + v) + m_noDistsTransfer * m_noHaloDistDataPerDomain[n]
5742 + (noHaloCells(n) * m_noVariables);
5743 a_oldVariable(id, v) = m_receiveBuffers[n][offset];
5744 }
5745 });
5746 }
5747 }
5748 // MInt totalData =
5749 // m_noDistsTransfer * m_noHaloDistDataPerDomain[n] + noHaloCells(n) * m_noVariables * m_noVarsTransfer;
5750 // for(MInt j = 0; j < totalData; j++) {
5751 // memcpy(m_commPtHalo[n][j], (void*)&m_receiveBuffers[n][j], sizeof(MFloat));
5752 // }
5753 }
5754}
5755
5764template <MInt nDim>
5766 MInt bufSize = 0;
5767 for(MInt i = 0; i < noNeighborDomains(); i++) {
5768 bufSize = noWindowCells(i) * m_dataBlockSizeTotal;
5769 MPI_Issend(m_sendBuffers[i], bufSize, MPI_DOUBLE, neighborDomain(i), 0, mpiComm(), &mpi_request[i], AT_,
5770 "m_sendBuffers[i]");
5771 }
5772}
5773
5782template <MInt nDim>
5784 TRACE();
5785
5786 for(MInt i = 0; i < noNeighborDomains(); i++) {
5787 MInt totalData =
5788 m_noDistsTransfer * m_noWindowDistDataPerDomain[i] + noWindowCells(i) * m_noVariables * m_noVarsTransfer;
5789 if(totalData == 0) continue;
5790 MPI_Issend(m_sendBuffers[i], totalData, MPI_DOUBLE, neighborDomain(i), 0, mpiComm(), &mpi_request[i], AT_,
5791 "m_sendBuffers[i]");
5792 }
5793}
5794
5803template <MInt nDim>
5805 TRACE();
5806
5807 // Test if send has been performed
5808 MPI_Status status;
5809
5810 for(MInt i = 0; i < noNeighborDomains(); i++) {
5811 const MInt bufSize = noHaloCells(i) * m_dataBlockSizeTotal;
5812 MPI_Recv(m_receiveBuffers[i], bufSize, MPI_DOUBLE, neighborDomain(i), 0, mpiComm(), &status, AT_,
5813 "m_receiveBuffers[i]");
5814 }
5815 for(MInt i = 0; i < noNeighborDomains(); i++)
5816 MPI_Wait(&mpi_request[i], &status, AT_);
5817}
5818
5827template <MInt nDim>
5829 TRACE();
5830
5831 // Test if send has been performed
5832 MPI_Status status;
5833
5834 for(MInt i = 0; i < noNeighborDomains(); i++) {
5835 MInt totalData =
5836 m_noDistsTransfer * m_noHaloDistDataPerDomain[i] + noHaloCells(i) * m_noVariables * m_noVarsTransfer;
5837 if(totalData == 0) continue;
5838 MPI_Recv(m_receiveBuffers[i], totalData, MPI_DOUBLE, neighborDomain(i), 0, mpiComm(), &status, AT_,
5839 "m_receiveBuffers[i]");
5840 }
5841 for(MInt i = 0; i < noNeighborDomains(); i++) {
5842 MInt totalData =
5843 m_noDistsTransfer * m_noHaloDistDataPerDomain[i] + noHaloCells(i) * m_noVariables * m_noVarsTransfer;
5844 if(totalData == 0) continue;
5845 MPI_Wait(&mpi_request[i], MPI_STATUS_IGNORE, AT_);
5846 }
5847}
5848
5862template <MInt nDim>
5864 TRACE();
5865
5866 m_log << endl;
5867 m_log << " + Setting active cell list" << endl;
5868
5869 setIsActiveDefaultStates();
5870 fillActiveCellList();
5871}
5872
5877template <MInt nDim>
5879 TRACE();
5880
5881 // m_activeCellList = m_allInnerCells;
5882 m_activeCellList = nullptr;
5883 m_currentMaxNoCells = (m_cells.size()
5884 // - m_overallNoWindowCells
5885 // - m_overallNoHaloCells
5886 );
5887}
5888
5893template <MInt nDim>
5895 TRACE();
5896
5897 // m_activeCellList = m_allWindowCells;
5898 m_activeCellList = nullptr;
5899 // m_currentMaxNoCells = m_overallNoWindowCells;
5900 m_currentMaxNoCells = 0;
5901}
5902
5911template <MInt nDim>
5913 TRACE();
5914
5915 // m_currentMaxNoCells = m_cells.size() - m_overallNoHaloCells;
5916 m_currentMaxNoCells = m_cells.size();
5917}
5918
5919template <MInt nDim>
5921 TRACE();
5922
5923 // determine interface cells and interpolation information
5924 NEW_SUB_TIMER(t_ifaceCells, "interface cells", m_t.solver);
5925 RECORD_TIMER_START(t_ifaceCells);
5926
5927 m_log << endl;
5928 m_log << " + Interface cell generation" << endl;
5929
5930 resetInterfaceCells();
5931 initializeInterfaceCells();
5932 buildInterfaceCells();
5933 setInterpolationNeighbors();
5934 setInterpolationCoefficients();
5935
5936 RECORD_TIMER_STOP(t_ifaceCells);
5937}
5938
5939template <MInt nDim>
5941 TRACE();
5942 if(m_isRefined && m_correctInterfaceBcCells) {
5943 setInterpolationNeighborsBC();
5944 setInterpolationCoefficientsBC();
5945 }
5946}
5947
5953template <MInt nDim>
5955 TRACE();
5956
5957 const MInt interfaceLevels = maxLevel() - minLevel();
5958
5959 if(interfaceLevels == 0) return;
5960
5971 m_interfaceCellSize = 0.5;
5972 m_interfaceCellSize = Context::getSolverProperty<MFloat>("interfaceCellSize", m_solverId, AT_, &m_interfaceCellSize);
5973
5974 if(m_interfaceCellSize > noInternalCells())
5975 TERMM(1,
5976 " LbSolver::initializeInterfaceCells: You have requested more space for interface cells than for "
5977 "normal "
5978 "internal cells. What is the sense of that? Exiting!");
5979
5980 MInt nointerfaceCellSize = (MInt)(m_interfaceCellSize * noInternalCells());
5981
5982 m_log << " - initializing interface cells for levels " << minLevel() << " to " << maxLevel() << endl;
5983 m_log << " * currently using a size of " << nointerfaceCellSize << " for interface child and parent cells"
5984 << endl;
5985 m_log << " * ratio: " << m_interfaceCellSize << endl;
5986 m_log << " * collector size: "
5987 << (MFloat)(IPOW2(nDim) * sizeof(MFloat) + IPOW2(nDim) * sizeof(MInt)) * (MFloat)nointerfaceCellSize
5988 / (1024.0 * 1024.0)
5989 << " MB" << endl;
5990
5991
5992 // ----------- build collector for interface child cells
5993 for(MInt l = 0; l < interfaceLevels; l++) {
5994 m_interfaceChildren.emplace_back();
5995 }
5996
5997 for(MInt i = 0; i < interfaceLevels; i++) {
5998 m_interfaceChildren[i] = new Collector<LbInterfaceCell>(nointerfaceCellSize, nDim, m_noDistributions);
5999 }
6000
6001 // ----------- build collector for interface parent cells
6002 for(MInt l = 0; l < interfaceLevels; l++) {
6003 m_interfaceParents.emplace_back();
6004 }
6005
6006 for(MInt i = 0; i < interfaceLevels; i++) {
6007 m_interfaceParents[i] = new Collector<LbParentCell>(nointerfaceCellSize, nDim, m_noDistributions);
6008 }
6009}
6010
6014template <MInt nDim>
6016 TRACE();
6017
6018 m_log << " - resetting interface cells" << endl;
6019 const MInt interfaceLevels = maxLevel() - minLevel();
6020
6021 if(interfaceLevels == 0) return;
6022
6023 MInt noCells = m_cells.size();
6024
6025 // reset interface marker for all cells
6026 // for (MInt i = 0; i < noInternalCells(); i++){
6027 for(MInt i = 0; i < noCells; i++) {
6028 a_isInterfaceChild(i) = false;
6029 a_isInterfaceParent(i) = false;
6030 }
6031
6032 mDeallocate(m_noInterfaceChildren);
6033 mDeallocate(m_noInterfaceParents);
6034
6035 if(m_interfaceChildren.size() != 0) {
6036 for(auto&& children : m_interfaceChildren) {
6037 delete children;
6038 }
6039 m_interfaceChildren.clear();
6040 }
6041 if(m_interfaceParents.size() != 0) {
6042 for(auto&& parents : m_interfaceParents) {
6043 delete parents;
6044 }
6045 m_interfaceParents.clear();
6046 }
6047}
6048
6054template <MInt nDim>
6056 TRACE();
6057
6058 m_log << " - building interface cells" << endl;
6059
6060 const MInt interfaceLevels = maxLevel() - minLevel();
6061
6062 if(interfaceLevels == 0) return;
6063
6064 MInt noCells = m_cells.size();
6065
6066 MInt pointedId = -1;
6067 MInt parentId = -1;
6068 MInt maxNoNghbrs;
6069
6070 MBool emptyParent;
6071
6072 if constexpr(nDim == 3) {
6073 maxNoNghbrs = 26;
6074 } else {
6075 maxNoNghbrs = 8;
6076 }
6077
6078 // 1.a) ----------- Find interface children ----------------
6079
6080 m_log << " * finding interface children" << endl;
6081
6082 mAlloc(m_noInterfaceChildren, interfaceLevels, "m_noInterfaceChildren", 0, AT_);
6083
6084 // a) mark interface cells
6085 for(MInt i = 0; i < noInternalCells(); i++) {
6086 // skip cells on lowest level
6087 if(a_level(i) == minLevel()) continue;
6088
6089 // // skip boundary cells
6090 // if (a_onlyBoundary(i))
6091 // continue;
6092
6093 // if cell has a parent cell
6094 if(c_parentId(i) > -1) {
6095 parentId = c_parentId(i);
6096 pointedId = i;
6097
6098 const MInt currentInterfaceLevel = a_level(pointedId) - minLevel() - 1;
6099
6100 for(MInt dist = 0; dist < maxNoNghbrs; dist++) {
6101 // if an equal-level neighbor doesn't exist but a parent-neighbor exists
6102 if(a_hasNeighbor(pointedId, dist) == 0 && a_hasNeighbor(parentId, dist)) {
6103 // if parent neighbor has no children an interface was found
6104 if(c_isLeafCell(c_neighborId(parentId, dist))) {
6105 // cell is interface cell
6106 // -> mark cell and increase counter
6107 a_isInterfaceChild(pointedId) = true;
6108 m_noInterfaceChildren[currentInterfaceLevel]++;
6109 break;
6110 }
6111 }
6112 }
6113
6114 if(!m_correctInterfaceBcCells && a_isInterfaceChild(pointedId)) {
6115 // unmark cells which lie at a non-periodic boundary of the domain
6116 for(MInt dist = 0; dist < maxNoNghbrs; dist++) {
6117 if(a_hasNeighbor(pointedId, dist) == 0 && a_hasNeighbor(parentId, dist) == 0) {
6118 a_isInterfaceChild(pointedId) = false;
6119 m_noInterfaceChildren[currentInterfaceLevel]--;
6120 break;
6121 }
6122 }
6123 }
6124 }
6125 }
6126
6127 for(MInt level = 0; level < interfaceLevels; level++)
6128 if(m_noInterfaceChildren[level] > 0)
6129 m_log << " # found children for level " << minLevel() + level + 1 << ": " << m_noInterfaceChildren[level]
6130 << endl;
6131
6132
6133 // 1.b) ------------- write interface children to collector ------------
6134
6135 m_log << " # writing children to collector " << endl;
6136
6137 for(MInt i = 0; i < noCells; i++) { // XXX exclude halo cellls?
6138
6139 // skip all unmarked cells
6140 if(!a_isInterfaceChild(i)) continue;
6141
6142 parentId = c_parentId(i);
6143 pointedId = i;
6144
6145 // append cell to collector
6146 const MInt currentInterfaceLevel = a_level(i) - minLevel() - 1;
6147 m_interfaceChildren[currentInterfaceLevel]->append();
6148
6149 auto& currentInterfaceCell =
6150 m_interfaceChildren[currentInterfaceLevel]->a[m_interfaceChildren[currentInterfaceLevel]->size() - 1];
6151 currentInterfaceCell.m_cellId = i;
6152
6153 // Determine position in parent cell
6154 currentInterfaceCell.m_position = 0;
6155 for(MInt dim = 0; dim < nDim; dim++) {
6156 if(a_coordinate(pointedId, dim) < a_coordinate(parentId, dim)) {
6157 currentInterfaceCell.m_position += 0;
6158 } else {
6159 currentInterfaceCell.m_position += IPOW2(dim);
6160 }
6161 }
6162 }
6163
6164 // 2.a) ----------- Find interface parents ----------------
6165
6166 m_log << " * finding interface parents" << endl;
6167
6168 mAlloc(m_noInterfaceParents, interfaceLevels, "m_noInterfaceParents", 0, AT_);
6169
6170 // 2.a1) mark interface parent cells
6171 for(MInt i = 0; i < noCells; i++) { // XXX exclude halo cellls?
6172
6173 // skip finest cells
6174 if(a_level(i) == maxLevel()) continue;
6175 // skip non-parent cells
6176 if(c_isLeafCell(i)) continue;
6177
6178 for(MInt k = 0; k < IPOW2(nDim); k++) {
6179 if(c_childId(i, k) < 0 || c_childId(i, k) >= noCells) {
6180 if(c_childId(i, k) != -1) cout << "STRANGE ID " << c_childId(i, k) << endl;
6181 continue;
6182 }
6183 if(a_isInterfaceChild(c_childId(i, k))) {
6184 // Cell is interface parent
6185 // -> mark cell and increase counter if number of children is correct
6186 if(c_noChildren(i) != IPOW2(nDim) && i < noInternalCells()) {
6187 stringstream errorMessage;
6188 errorMessage << " LbInterface::identifyInterfaceCells: Insufficient number of children in interface "
6189 "parent cell with id "
6190 << i << ", Exiting!";
6191 TERMM(1, errorMessage.str());
6192 }
6193
6194 a_isInterfaceParent(i) = true;
6195
6196 const MInt currentInterfaceLevel = a_level(i) - minLevel();
6197 m_noInterfaceParents[currentInterfaceLevel]++;
6198
6199 break;
6200 }
6201 }
6202 }
6203
6204 for(MInt level = 0; level < interfaceLevels; level++)
6205 if(m_noInterfaceParents[level] > 0)
6206 m_log << " # found parents for level " << minLevel() + level + 1 << ": " << m_noInterfaceParents[level]
6207 << endl;
6208
6209
6210 m_log << " * adding halo cells " << endl;
6211
6212 // 2.a2) add halo cells
6213 for(MInt i = noInternalCells(); i < noCells; i++) {
6214 // skip finest cells
6215 if(a_level(i) == maxLevel()) continue;
6216
6217 // skip non-parent cells
6218 if(c_isLeafCell(i)) continue;
6219
6220 // if children are missing, skip the parent cell
6221 emptyParent = false;
6222 for(MInt child = 0; child < IPOW2(nDim); child++) {
6223 if(c_childId(i, child) < 0) {
6224 emptyParent = true;
6225 break;
6226 }
6227 }
6228 if(emptyParent) continue;
6229
6230 for(MInt k = 0; k < maxNoNghbrs; k++) {
6231 if(a_hasNeighbor(i, k) > 0 && c_isLeafCell(c_neighborId(i, k))) {
6232 a_isInterfaceParent(i) = true;
6233
6234 const MInt currentInterfaceLevel = a_level(i) - minLevel();
6235 m_noInterfaceParents[currentInterfaceLevel]++;
6236 break;
6237 }
6238 }
6239 }
6240
6241 for(MInt level = 0; level < interfaceLevels; level++)
6242 if(m_noInterfaceParents[level] > 0)
6243 m_log << " # new number of parents for level " << minLevel() + level + 1 << ": "
6244 << m_noInterfaceParents[level] << endl;
6245
6246 m_log << " # writing parents to collector " << endl;
6247
6248 // 2.b) ------------- write interface parents to collector --------------
6249 // for (MInt i = 0; i < noInternalCells(); i++){
6250 for(MInt i = 0; i < noCells; i++) {
6251 pointedId = i;
6252
6253 // skip all unmarked cells
6254 if(!a_isInterfaceParent(i)) continue;
6255
6256 // append cell to collector
6257 const MInt currentInterfaceLevel = a_level(i) - minLevel();
6258 m_interfaceParents[currentInterfaceLevel]->append();
6259
6260 auto& currentParentCell =
6261 m_interfaceParents[currentInterfaceLevel]->a[m_interfaceParents[currentInterfaceLevel]->size() - 1];
6262 currentParentCell.m_cellId = i;
6263 }
6264}
6265
6270template <MInt nDim>
6272 TRACE();
6273
6274 m_log << " - setting interpolation coefficients" << endl;
6275
6276 MInt noInterpolationNghbrs;
6277 noInterpolationNghbrs = IPOW2(nDim);
6278 ScratchSpace<MFloat> interpolationCoefficients(IPOW2(nDim), noInterpolationNghbrs, AT_, "interpolationCoefficients");
6279
6280 switch(m_interpolationType) {
6281 case LINEAR_INTERPOLATION: {
6282 for(MInt i = 0; i < IPOW2(nDim); i++) {
6283 for(MInt j = 0; j < noInterpolationNghbrs; j++) {
6284 interpolationCoefficients(i, j) = LbLatticeDescriptorBase<nDim>::linearInterpolationCoefficients(i, j);
6285 }
6286 }
6287 break;
6288 }
6290 // not implemented yet
6291 TERMM(1,
6292 " In function LbInterfaceD2Q9::setInterpolationCoefficients() : quadratic interpolation is not "
6293 "implemented yet!");
6294 break;
6295 }
6296 case CUBIC_INTERPOLATION: {
6297 // not implemented yet
6298 TERMM(1,
6299 " In function LbInterfaceD2Q9::setInterpolationCoefficients() : cubic interpolation is not "
6300 "implemented yet!");
6301 break;
6302 }
6303 default: {
6304 TERMM(1, " In function LbInterfaceD2Q9::setInterpolationCoefficients() : Unknown interpolation type!");
6305 }
6306 }
6307
6308 MInt position = 0;
6309 for(MInt i = 0; i < (maxLevel() - minLevel()); i++) {
6310 for(MInt j = 0; j < m_interfaceChildren[i]->size(); j++) {
6311 position = m_interfaceChildren[i]->a[j].m_position;
6312 for(MInt k = 0; k < noInterpolationNghbrs; k++) {
6313 m_interfaceChildren[i]->a[j].m_interpolationCoefficients[k] = interpolationCoefficients(position, k);
6314 }
6315 }
6316 }
6317}
6318
6319/*\brief sets the neighbors for interpolation at grid interfaces
6320 *
6321 *\author Georg Eitel-Amor
6322 */
6323template <MInt nDim>
6325 TRACE();
6326
6327 m_log << " - setting interpolation neighbors" << endl;
6328
6329 MInt position = 0;
6330 MInt currentId = 0;
6331
6332 // ------- 2D ---------
6333 if constexpr(nDim == 2) {
6334 // by now only linear interpolation
6335 switch(m_interpolationType) {
6336 case LINEAR_INTERPOLATION: {
6337 for(MInt i = 0; i < maxLevel() - minLevel(); i++) {
6338 for(MInt j = 0; j < m_noInterfaceChildren[i]; j++) {
6339 currentId = m_interfaceChildren[i]->a[j].m_cellId;
6340 position = m_interfaceChildren[i]->a[j].m_position;
6341 for(MInt k = 0; k < IPOW2(nDim); k++) {
6342 const MInt dir = LbLatticeDescriptorBase<nDim>::intNghbrArray(position, k);
6343 if(dir < IPOW3[nDim] - 1) {
6344 // Access interpolation neighbor via child or parent cell
6345 // (depending on which is connected)
6346 if(a_hasNeighbor(currentId, dir) == 0) {
6347 m_interfaceChildren[i]->a[j].m_interpolationNeighbors[k] = c_neighborId(c_parentId(currentId), dir);
6348 } else {
6349 m_interfaceChildren[i]->a[j].m_interpolationNeighbors[k] = c_parentId(c_neighborId(currentId, dir));
6350 }
6351 } else {
6352 m_interfaceChildren[i]->a[j].m_interpolationNeighbors[k] = c_parentId(currentId);
6353 }
6354 }
6355 }
6356 }
6357 break;
6358 }
6360 // not implemented yet
6361 TERMM(1,
6362 " In function LbInterfaceD2Q9::setInterpolationCoefficients() : quadratic interpolation not "
6363 "implemented yet!");
6364 break;
6366 // not implemented yet
6367 TERMM(1,
6368 " In function LbInterfaceD2Q9::setInterpolationCoefficients() : cubic interpolation not "
6369 "implemented yet!");
6370 break;
6371 default: {
6372 TERMM(1, " In function LbInterfaceD2Q9::setInterpolationCoefficients() : Unknown interpolation type!");
6373 }
6374 }
6375 }
6376 // ------- 3D ---------
6377 else {
6378 // by now only linear interpolation
6379 for(MInt i = 0; i < maxLevel() - minLevel(); i++) {
6380 for(MInt j = 0; j < m_noInterfaceChildren[i]; j++) {
6381 currentId = m_interfaceChildren[i]->a[j].m_cellId;
6382 position = m_interfaceChildren[i]->a[j].m_position;
6383 for(MInt k = 0; k < IPOW2(nDim); k++) {
6384 const MInt dir = LbLatticeDescriptorBase<nDim>::intNghbrArray(position, k);
6385 if(dir < IPOW3[nDim] - 1) {
6386 // interpolation neighbor is neighbor of parent
6387 const MInt parentId = c_parentId(currentId);
6388 const MInt nghbrId = c_neighborId(parentId, dir);
6389 m_interfaceChildren[i]->a[j].m_interpolationNeighbors[k] = nghbrId;
6390 } else {
6391 // interpolation neighbor is own parent
6392 m_interfaceChildren[i]->a[j].m_interpolationNeighbors[k] = c_parentId(currentId);
6393 }
6394 }
6395 }
6396 }
6397 }
6398}
6399
6404template <MInt nDim>
6406 switch(nDim) {
6407 case 2: { //--2D------------------------------------------------------------
6408 // TODO labels:LB,toenhance needs to be extended to 2D
6409 std::cout << "WARNING: Refined boundary cells are problematic in 2D! "
6410 << "This is not implemented, yet." << std::endl;
6411 break;
6412 }
6413 case 3: { //--3D------------------------------------------------------------
6414 for(MInt i = 0; i < (maxLevel() - minLevel()); i++) {
6415 // for(MInt j = 0; j < m_interfaceChildren[i]->size(); j++) {
6416 for(MInt j = 0; j < m_noInterfaceChildren[i]; j++) {
6417 const MInt cellId = m_interfaceChildren[i]->a[j].m_cellId;
6418 // check: if at least one interpolNghbr is missing or is a BC cell
6419 // if this is the case -> adjust stencil
6420 // adjustment is for now done by simply shifting the complete stencil
6421 // befor rechecking whether shifted one is working. Shifting is
6422 // maximal done once for each Cartesian direction. If then no correct
6423 // stencil is found an error is called !
6424 MBool performCorrection = false;
6425 constexpr MInt maxNoNghbrs = IPOW3[nDim] - 1;
6426 std::array<MBool, maxNoNghbrs> validShiftDirection;
6427 validShiftDirection.fill(true);
6428 for(MInt k = 0; k < m_interfaceChildren[i]->a[j].m_noInterpolationNeighbors; k++) {
6429 const MInt nghbrId = m_interfaceChildren[i]->a[j].m_interpolationNeighbors[k];
6430 if(nghbrId < 0 || !a_isActive(nghbrId) || a_isBndryCell(nghbrId)) {
6431 performCorrection = true;
6432 // check which Cartesian directions are non-valid for shift
6433 // ..from parent cell's point of view or from original interpolNghbr's point of view
6434 const MInt srcId = (nghbrId < 0) ? c_parentId(cellId) : nghbrId;
6435 // check which direction are non-valid for shift
6436 for(MInt d = 0; d < maxNoNghbrs; d++) {
6437 if(a_hasNeighbor(srcId, d)) {
6438 const MInt srcNghbrId = c_neighborId(srcId, d);
6439 if(!a_isActive(srcNghbrId) || a_isBndryCell(srcNghbrId)) {
6440 validShiftDirection[d] = false;
6441 }
6442 } else {
6443 validShiftDirection[d] = false;
6444 }
6445 }
6446 }
6447 }
6448 // now shift in first validShiftDirection
6449 if(performCorrection) {
6450 MBool validShift = false;
6451 for(MInt d = 0; d < maxNoNghbrs; d++) {
6452 if(validShiftDirection[d]) {
6453 validShift = true;
6454 for(MInt k = 0; k < m_interfaceChildren[i]->a[j].m_noInterpolationNeighbors; k++) {
6455 const MInt nghbrId = m_interfaceChildren[i]->a[j].m_interpolationNeighbors[k];
6456 if(nghbrId > -1) {
6457 m_interfaceChildren[i]->a[j].m_interpolationNeighbors[k] = c_neighborId(nghbrId, d);
6458 } else {
6459 std::array<MInt, nDim> newDirVec;
6460 const MInt position = m_interfaceChildren[i]->a[j].m_position;
6461 for(MInt l = 0; l < nDim; l++) {
6462 // shift original direction vector by shift vector
6463 // TODO labels:LB dxqy: get orignal direction less deterministic !!
6467 }
6468 const MInt newDir = LbLatticeDescriptorBase<3>::dirFld(newDirVec[0], newDirVec[1],
6469 newDirVec[2]); // TODO labels:LB dxqy
6470 const MInt parentId = c_parentId(cellId);
6471 if(newDir == IPOW3[nDim] - 1) {
6472 m_interfaceChildren[i]->a[j].m_interpolationNeighbors[k] = parentId;
6473 } else {
6474 m_interfaceChildren[i]->a[j].m_interpolationNeighbors[k] = c_neighborId(parentId, newDir);
6475 }
6476 }
6477 }
6478 break;
6479 }
6480 }
6481 if(!validShift) {
6482 std::stringstream err;
6483 err << "ERROR: interpolation stencil for BC interface could not be corrected!";
6484 err << " [globalId: " << c_globalId(cellId) << "]" << std::endl;
6485 TERMM(1, err.str());
6486 } else {
6487 // store to reset interpolation coefficients later
6488 InterfaceLink tmp = {i, j};
6489 m_interfaceChildrenBc.push_back(tmp);
6490 }
6491 } // end correction
6492 }
6493 }
6494 break;
6495 }
6496 default: {
6497 TERMM(1, "Only nDim=2 and nDim=3 allowed!");
6498 }
6499 }
6500}
6501
6506template <MInt nDim>
6508 // if(m_interfaceChildrenBc.size() <= 0) return;
6509 // constexpr MInt noInterpolationNghbrs = IPOW2(nDim);
6510 switch(nDim) {
6511 case 2: { //--2D------------------------------------------------------------
6512 // TODO labels:LB,toenhance needs to be extended to 2D
6513 // A warning is already called in setInterpolationNeighborsBC()
6514 break;
6515 }
6516 case 3: { //--3D------------------------------------------------------------
6517 for(auto link : m_interfaceChildrenBc) {
6518 const MInt i = link.levelId;
6519 const MInt j = link.interfaceId;
6520 // weigths are determined for a 8 interpolation neighbors, which are
6521 // ordered in a cube based on tri-linear interpolation
6522 // 1) determine 3 weigths for first nghbr for each Cartesian dir
6523 std::array<MFloat, nDim> c0;
6524 const MInt cellId = m_interfaceChildren[i]->a[j].m_cellId;
6525 const MInt cellId0 = m_interfaceChildren[i]->a[j].m_interpolationNeighbors[0];
6526 for(MInt d = 0; d < nDim; d++) {
6527 const MInt cellId1 = m_interfaceChildren[i]->a[j].m_interpolationNeighbors[IPOW2(d)];
6528 const MFloat x = a_coordinate(cellId, d);
6529 const MFloat x0 = a_coordinate(cellId0, d);
6530 const MFloat x1 = a_coordinate(cellId1, d);
6531 c0[d] = (x1 - x) / (x1 - x0);
6532 }
6533 // 2) assemble final weigth by combination of c0
6534 for(MInt m = 0; m < 8; m++) {
6535 MFloat weight = 1.0;
6536 MInt tmp = m;
6537 for(MInt d = nDim - 1; d > -1; d--) {
6538 const MInt chk0 = tmp / IPOW2(d);
6539 tmp = tmp % IPOW2(d);
6540 const MBool chk = (chk0 == 0);
6541 weight *= (chk) ? c0[d] : 1 - c0[d];
6542 }
6543 m_interfaceChildren[i]->a[j].m_interpolationCoefficients[m] = weight;
6544 }
6545 }
6546 break;
6547 }
6548 default: {
6549 TERMM(1, "Only nDim=2 and nDim=3 allowed!");
6550 }
6551 }
6552}
6553
6564template <MInt nDim>
6566 MString filename = outputDir() + "PV_";
6567 MChar buf[10];
6568 sprintf(buf, "%d", timeStep);
6569 filename.append(buf);
6570 filename += ParallelIo::fileExt();
6571
6572 loadRestartWithoutDistributionsPar(filename.c_str());
6573}
6574
6586template <MInt nDim>
6587void LbSolver<nDim>::getSampleVariables(MInt cellId, const MFloat*& vars) {
6588#ifdef WAR_NVHPC_PSTL
6589 // TODO labels:LB,GPU resolve following TERMM
6590 TERMM(1, "getSampleVariables: var runs out of scope after this function call!");
6591 if(m_isThermal) {
6592 MFloat var[nDim + 2] = {F0};
6593 for(MInt d = 0; d < nDim + 2; d++) {
6594 var[d] = a_variable(cellId, d);
6595 }
6596 vars = var;
6597 } else {
6598 MFloat var[nDim + 1] = {F0};
6599 for(MInt d = 0; d < nDim + 1; d++) {
6600 var[d] = a_variable(cellId, d);
6601 }
6602 vars = var;
6603 }
6604#else
6605 vars = a_variables_ptr(cellId);
6606#endif
6607}
6608
6609template <MInt nDim>
6610void LbSolver<nDim>::getSampleVariables(const MInt cellId, std::vector<MFloat>& vars) {
6611 const MInt noVars = vars.size();
6612 ASSERT(noVars <= m_noVariables, "noVars > m_noVariables");
6613 for(MInt varId = 0; varId < noVars; varId++) {
6614 vars[varId] = a_variable(cellId, varId);
6615 }
6616}
6617
6618template <MInt nDim>
6619MBool LbSolver<nDim>::getSampleVarsDerivatives(const MInt cellId, std::vector<MFloat>& vars) {
6620 auto isValid = [&](const MInt l_cellId, const MInt l_dir) -> MInt {
6621 if(l_cellId != -1) {
6622 const MInt nghbrId = c_neighborId(l_cellId, l_dir);
6623 if(nghbrId > -1 && a_isActive(nghbrId)) {
6624 return nghbrId;
6625 }
6626 }
6627 return -1;
6628 };
6629 const MFloat F1bdx = FFPOW2(maxLevel() - a_level(cellId)); // LB units
6630 for(MInt dir = 0; dir < nDim; dir++) {
6631 const MInt dir0 = 2 * dir;
6632 const MInt dir1 = 2 * dir + 1;
6633 // 1) set coefficients
6634 constexpr MInt noCellsInStencil = 5;
6635 std::array<MFloat, noCellsInStencil> coef;
6636 std::array<MInt, noCellsInStencil> cellIds;
6637 // 1.1) get cellIds and coefficients
6638 cellIds[1] = isValid(cellId, dir0); // left
6639 cellIds[0] = isValid(cellIds[1], dir0); // leftleft
6640 cellIds[2] = cellId; // middle
6641 cellIds[3] = isValid(cellId, dir1); // right
6642 cellIds[4] = isValid(cellIds[3], dir1); // rightright
6643 getDerivativeStencilAndCoefficient(cellIds, coef);
6644 // 1.2) scale coefficients with correct dx in case of refined
6645 for(MInt i = 0; i < noCellsInStencil; i++) {
6646 coef[i] *= F1bdx;
6647 }
6648 // 2) calculate gradients
6649 // 2.1) .. velocity
6650 for(MInt veloId = 0; veloId < nDim; veloId++) {
6651 const MInt index = nDim * veloId + dir;
6652 vars[index] = 0.0;
6653 for(MInt i = 0; i < noCellsInStencil; i++) {
6654 vars[index] += cellIds[i] < 0 ? 0.0 : coef[i] * a_variable(cellIds[i], veloId);
6655 }
6656 }
6657 // 2.2) .. density
6658 for(MInt varId = nDim; varId < m_noVariables; varId++) {
6659 const MInt index = nDim * varId + dir;
6660 vars[index] = 0.0;
6661 for(MInt i = 0; i < noCellsInStencil; i++) {
6662 vars[index] += cellIds[i] < 0 ? 0.0 : coef[i] * a_variable(cellIds[i], varId);
6663 }
6664 }
6665 // 2.3) .. alpha?
6666 if(m_isEELiquid) {
6667 const MInt index = m_noVariables * nDim + dir;
6668 for(MInt i = 0; i < noCellsInStencil; i++) {
6669 vars[index] += cellIds[i] < 0 ? 0.0 : coef[i] * a_alphaGas(cellIds[i]);
6670 }
6671 }
6672 }
6673 return true;
6674}
6675
6676template <MInt nDim>
6677void LbSolver<nDim>::storeOldDistributions() {
6678 if(!m_cells.savePrevVars()) {
6679 TERMM(1, "Cell collector not configured to stor previous values!");
6680 }
6681 for(MInt cellId = 0; cellId < a_noCells(); cellId++) {
6682 for(MInt distr = 0; distr < m_noDistributions; distr++) {
6683 a_previousDistribution(cellId, distr) = a_oldDistribution(cellId, distr);
6684 }
6685 }
6686}
6687
6688template <MInt nDim>
6689void LbSolver<nDim>::storeOldVariables() {
6690 if(!m_cells.savePrevVars()) {
6691 TERMM(1, "Cell collector not configured to stor previous values!");
6692 }
6693 for(MInt cellId = 0; cellId < a_noCells(); cellId++) {
6694 a_previousVariable(cellId, PV->RHO) = a_oldVariable(cellId, PV->RHO);
6695 a_previousVariable(cellId, PV->U) = a_oldVariable(cellId, PV->U);
6696 a_previousVariable(cellId, PV->V) = a_oldVariable(cellId, PV->V);
6697 a_previousVariable(cellId, PV->W) = a_oldVariable(cellId, PV->W);
6698 }
6699}
6700
6701template <MInt nDim>
6702void LbSolver<nDim>::initNu(const MInt cellId, const MFloat nu) {
6703 a_nu(cellId) = nu;
6704 if(m_cells.saveNuT()) {
6705 a_nuT(cellId) = 0.0;
6706 if(m_cells.saveOldNu()) {
6707 a_oldNuT(cellId) = 0.0;
6708 }
6709 }
6710 if(m_cells.saveOldNu()) {
6711 a_oldNu(cellId) = nu;
6712 }
6713}
6714
6716template <MInt nDim>
6717MBool LbSolver<nDim>::solutionStep() {
6718 TRACE();
6719
6720 RECORD_TIMER_START(m_t.solutionStep);
6721
6722 // calls the exchange method
6723 RECORD_TIMER_START(m_t.exchange);
6724 (this->*m_exchangeMethod)(0);
6725 RECORD_TIMER_STOP(m_t.exchange);
6726
6727 // update macroscopic variables
6728 if(m_updateMacroscopicLocation == PRECOLLISION) {
6729 updateVariablesFromOldDist_preCollision();
6730 }
6731
6732 // update/reset viscosity
6733 updateViscosity();
6734
6735 // pre-collison source term call
6736 RECORD_TIMER_START(m_t.srcTerms);
6737 preCollisionSrcTerm();
6738 RECORD_TIMER_STOP(m_t.srcTerms);
6739
6740 // collision step
6741 RECORD_TIMER_START(m_t.collision);
6742 (this->*m_solutionStepMethod)();
6743 RECORD_TIMER_STOP(m_t.collision);
6744
6745 // post-collison source term call
6746 RECORD_TIMER_START(m_t.srcTerms);
6747 postCollisionSrcTerm();
6748 RECORD_TIMER_STOP(m_t.srcTerms);
6749
6750 // post-collision boundary conditions
6751 RECORD_TIMER_START(m_t.collisionBC);
6752 postCollisionBc();
6753 RECORD_TIMER_STOP(m_t.collisionBC);
6754
6755 // calls the exchange method
6756 RECORD_TIMER_START(m_t.exchange);
6757 (this->*m_exchangeMethod)(1);
6758 RECORD_TIMER_STOP(m_t.exchange);
6759
6760 // propagation step
6761 RECORD_TIMER_START(m_t.propagation);
6762 (this->*m_propagationStepMethod)();
6763 RECORD_TIMER_STOP(m_t.propagation);
6764
6765 // TODO labels:LB @johannes/julian: I made this step in updateVariablesFromOldDist_preCollison. Preference for here?
6766 if(m_isInitRun) {
6767 this->initRunCorrection();
6768 }
6769
6770 // set number outer Bnd cells for lblpt coupler
6771 m_noOuterBndryCells = this->m_bndCnd->m_bndCells.size();
6772 // post-propagation boundary conditions
6773 RECORD_TIMER_START(m_t.propagationBC);
6774 postPropagationBc();
6775 RECORD_TIMER_STOP(m_t.propagationBC);
6776
6777 writeInfo();
6778
6779 if(m_updateMacroscopicLocation == POSTPROPAGATION) {
6780 updateVariablesFromOldDist();
6781 }
6782
6783 RECORD_TIMER_STOP(m_t.solutionStep);
6784
6785 return true;
6786}
6787
6788template <MInt nDim>
6789void LbSolver<nDim>::writeInfo() {
6790 TRACE();
6791
6792 if(!isActive()) return;
6793 RECORD_TIMER_START(m_t.residual);
6794
6795 if(globalTimeStep % m_residualInterval == 0) {
6796 maxResidual();
6797 }
6798 RECORD_TIMER_STOP(m_t.residual);
6799}
6800
6802template <MInt nDim>
6803void LbSolver<nDim>::initSolver() {
6804 TRACE();
6805
6806 // set cell properties
6807 resetActiveCellList();
6808
6809 // reset interface BC cell interpolation neighbors
6810 // TODO labels:LB this can not be called by treatInterfaceCell since a_isBndry is not
6811 // set correctly in advance.
6812 correctInterfaceBcCells();
6813
6814 if(m_externalForcing) {
6815 initPressureForce();
6816 }
6817 if(m_EELiquid.gravity || m_externalForcing) {
6818 initVolumeForces();
6819 }
6820
6821 if(grid().isActive()) {
6822 initSrcTermController();
6823 (this->*m_initializeMethod)();
6824 m_bndCnd->initializeBcData();
6825 initSrcTerms();
6826 }
6827
6828 m_log << endl << endl;
6829}
6830
6831
6834
6838template <MInt nDim>
6839void LbSolver<nDim>::prepareAdaptation() {
6840 TRACE();
6841 if(!m_refinedParents.empty()) m_refinedParents.clear();
6842}
6843
6848template <MInt nDim>
6849void LbSolver<nDim>::setSensors(std::vector<std::vector<MFloat>>& sensors, std::vector<MFloat>& sensorWeight,
6850 std::vector<std::bitset<64>>& sensorCellFlag, std::vector<MInt>& sensorSolverId) {
6851 cerr0 << "Set " << this->m_noSensors << " Sensors for Adaptation" << endl;
6852
6853 TRACE();
6854 ASSERT(m_freeIndices.empty(), "");
6855
6856 // The sensors are added at the end of the previous sensor-vectors!
6857 // Implement sensor in the for loop below in this order.
6858 const auto sensorOffset = (signed)sensors.size();
6859 ASSERT(sensorOffset == 0 || grid().raw().treeb().noSolvers() > 1, "");
6860 sensors.resize(sensorOffset + this->m_noSensors, vector<MFloat>(grid().raw().m_noInternalCells, F0));
6861 sensorWeight.resize(sensorOffset + this->m_noSensors, -1);
6862 sensorCellFlag.resize(grid().raw().m_noInternalCells, sensorOffset + this->m_noSensors);
6863 sensorSolverId.resize(sensorOffset + this->m_noSensors, solverId());
6864 ASSERT(sensorOffset + this->m_noSensors < CartesianGrid<nDim>::m_maxNoSensors, "Increase bitset size!");
6865
6866 if(!isActive()) {
6867 for(MInt sen = 0; sen < this->m_noSensors; sen++) {
6868 sensorWeight[sensorOffset + sen] = this->m_sensorWeight[sen];
6869 }
6870 return;
6871 }
6872
6873 // only set sensors if the adaptation was globally triggered in buildLevelSetTubeCG!
6874 if(m_adaptation) {
6875 // Perform exchange to update Halos that are involved in gradient computation
6876 if(noNeighborDomains() > 0) {
6877 exchange(1);
6878 exchangeOldDistributions();
6879 }
6880
6881 if(domainId() == 0) {
6882 cerr << "Setting sensor for the lb-solver adaptation!" << endl;
6883 }
6884
6885 for(MInt sen = 0; sen < this->m_noSensors; sen++) {
6886 (this->*(this->m_sensorFnPtr[sen]))(sensors, sensorCellFlag, sensorWeight, sensorOffset, sen);
6887 }
6888 }
6889}
6890
6891
6896template <MInt nDim>
6897void LbSolver<nDim>::getSolverSamplingProperties(std::vector<MInt>& samplingVarIds,
6898 std::vector<MInt>& noSamplingVars,
6899 std::vector<std::vector<MString>>& samplingVarNames,
6900 const MString featureName) {
6901 TRACE();
6902
6903 // Read sampling variable names
6904 std::vector<MString> varNamesList;
6905 MInt noVars = readSolverSamplingVarNames(varNamesList, featureName);
6906
6907 // Set default sampling variables if none specified
6908 if(noVars == 0) {
6909 varNamesList.emplace_back("LB_PV");
6910 noVars = 1;
6911 }
6912
6913 for(MInt i = 0; i < noVars; i++) {
6914 const MInt samplingVar = string2enum(varNamesList[i]);
6915 std::vector<MString> varNames;
6916
6917 auto samplingVarIt = std::find(samplingVarIds.begin(), samplingVarIds.end(), samplingVar);
6918 if(samplingVarIt != samplingVarIds.end()) {
6919 TERMM(1, "Sampling variable '" + varNamesList[i] + "' already specified.");
6920 }
6921
6922 switch(samplingVar) {
6923 case LB_PV: {
6924 ASSERT(m_noVariables == nDim + 1,
6925 "Error: additional primitive variables not supported for sampling data feature yet");
6926
6927 samplingVarIds.push_back(LB_PV);
6928 noSamplingVars.push_back(m_noVariables + 1); //+1 because of addition of p
6929
6930 varNames.resize(m_noVariables + 1); //+1 because of addition of p
6931 varNames[PV->U] = "u";
6932 varNames[PV->V] = "v";
6933 if constexpr(nDim == 3) {
6934 varNames[PV->W] = "w";
6935 }
6936 varNames[PV->P] = "p"; // add a p variable to be sampled, that can be induced from rho
6937 varNames[PV->RHO] = "rho";
6938
6939 samplingVarNames.push_back(varNames);
6940 break;
6941 }
6942 default: {
6943 TERMM(1, "Unknown sampling variable: " + varNamesList[i]);
6944 break;
6945 }
6946 }
6947 }
6948}
6949
6954template <MInt nDim>
6955void LbSolver<nDim>::finalizeAdaptation() {
6956 TRACE();
6957
6958 if(!grid().isActive()) return;
6959 if(m_maxNoSets > -1) {
6960 mDeallocate(m_sendBufferMB);
6961 mDeallocate(m_receiveBufferMB);
6962 mDeallocate(m_associatedBodyIds);
6963 mDeallocate(m_levelSetValues);
6964 mDeallocate(m_G0CellMapping);
6965 mDeallocate(m_isG0CandidateOfSet);
6966
6967 // TODO: noNeighborDomains > 0 instead?
6968 if(noDomains() > 1) {
6969 MInt sumwin = 0;
6970 MInt sumhalo = 0;
6971 for(MInt d = 0; d < noNeighborDomains(); d++) {
6972 sumwin += noWindowCells(d);
6973 sumhalo += noHaloCells(d);
6974 }
6975 mAlloc(m_sendBufferMB, sumwin, "m_sendBufferMB", 0, AT_);
6976 mAlloc(m_receiveBufferMB, sumhalo, "m_receiveBufferMB", 0, AT_);
6977 }
6978
6979 // body properties
6980 mAlloc(m_associatedBodyIds, m_noLevelSetsUsedForMb * a_noCells(), "m_associatedBodyIds", -1, AT_);
6981 mAlloc(m_levelSetValues, m_noLevelSetsUsedForMb * a_noCells(), "m_levelSetValues", F0, AT_);
6982 mAlloc(m_G0CellMapping, a_noCells(), "m_G0CellMapping", -1, AT_);
6983
6984 MInt startSet = m_levelSetId; // if bodyToSetMode = 11 the 0th set is the collected set, which should be skiped
6985 MInt arrayLength = m_maxNoSets - startSet;
6986 mAlloc(m_isG0CandidateOfSet, a_noCells(), arrayLength, "m_isG0CandidateOfSet", false, AT_);
6987
6988 // TODO: noNeighborDomains > 0 instead?
6989 if(noDomains() > 1) {
6990 grid().updateLeafCellExchange();
6991 }
6992
6993 resetActiveCellList();
6994 }
6995
6996 // These status flag distinguishes if an adaptation was performed or not.
6997 // If adaptation was performed, recalcIds need to be passed to saveUVWRhoTOnlyPar
6998 // because the ids in the newly written grid file differ from the
6999 // ids used in solver/grid.
7000 m_adaptationSinceLastRestart = true;
7001 m_adaptationSinceLastSolution = true;
7002}
7003
7016template <MInt nDim>
7017void LbSolver<nDim>::postAdaptation() {
7018 TRACE();
7019
7020 for(MInt i = 0; i < grid().raw().noNeighborDomains(); i++) {
7021 for(MInt j = 0; j < grid().raw().noHaloCells(i); j++) {
7022 MInt gridId = grid().raw().m_haloCells[i][j];
7023 MInt l_solverId = this->grid().tree().grid2solver(gridId);
7024 if(l_solverId < 0) {
7025 continue;
7026 }
7027
7028 if(!grid().raw().treeb().solver(gridId, m_solverId)) {
7029 cerr << "removing a Halo-cell " << endl;
7030 this->removeCellId(l_solverId);
7031 }
7032 }
7033 }
7034
7035 // In meshAdaptation() the order of window and halo cells can change
7036 // even if no adaptation happens
7037 this->compactCells();
7038
7039 if(!g_multiSolverGrid) {
7040 for(MInt gridCellId = 0; gridCellId < grid().raw().treeb().size(); gridCellId++) {
7041 ASSERT(grid().tree().solver2grid(gridCellId) == gridCellId, "");
7042 ASSERT(grid().tree().grid2solver(gridCellId) == gridCellId, "");
7043 }
7044 }
7045
7046 grid().updateOther();
7047 updateDomainInfo(grid().domainId(), grid().noDomains(), grid().mpiComm(), AT_);
7048
7049 if(!grid().isActive()) return;
7050
7051 m_cells.size(c_noCells());
7052 m_freeIndices.clear();
7053
7054 if(!g_multiSolverGrid) ASSERT(a_noCells() == c_noCells() && c_noCells() == grid().raw().treeb().size(), "");
7055
7056 // FIXME labels:LB
7057 // set the cell weights.... i dont know why this is done by the solvers right now
7058 for(MInt i = 0; i < grid().raw().treeb().size(); i++) {
7059 grid().raw().treeb().weight(i) = 1;
7060 }
7061
7062 updateCellCollectorFromGrid();
7063
7064 grid().findEqualLevelNeighborsParDiagonal(false);
7065
7066 m_isRefined = (grid().maxUniformRefinementLevel() < maxLevel());
7067
7068 resetComm();
7069
7070 if(m_reducedComm) {
7071 prepareCommunicationReduced();
7072 } else {
7073 prepareCommunicationNormal();
7074 }
7075
7076 resetCellLists();
7077
7078 // Do an exchange to have the right values in halo cells
7079 if(noNeighborDomains() > 0) {
7080 exchange(1);
7081 exchangeOldDistributions();
7082 }
7083
7084 restartBndCnd();
7085
7086 // Reset level set
7087
7088 if(!grid().isActive()) return;
7089 if(m_maxNoSets > -1) {
7090 mDeallocate(m_associatedBodyIds);
7091 mDeallocate(m_levelSetValues);
7092 mDeallocate(m_G0CellMapping);
7093 mDeallocate(m_isG0CandidateOfSet);
7094
7095 mAlloc(m_associatedBodyIds, m_noLevelSetsUsedForMb * a_noCells(), "m_associatedBodyIds", -1, AT_);
7096 mAlloc(m_levelSetValues, m_noLevelSetsUsedForMb * a_noCells(), "m_levelSetValues", F0, AT_);
7097 mAlloc(m_G0CellMapping, a_noCells(), "m_G0CellMapping", -1, AT_);
7098
7099 MInt startSet = m_levelSetId; // if bodyToSetMode = 11 the 0th set is the collected set, which should be skiped
7100 MInt arrayLength = m_maxNoSets - startSet;
7101 mAlloc(m_isG0CandidateOfSet, a_noCells(), arrayLength, "m_isG0CandidateOfSet", false, AT_);
7102 }
7103
7104 // Initialize refined cells after solver is updated because spatial interpolation
7105 // accross domain interfaces can become necessary. Also see refineCell.
7106 if(globalTimeStep < 1) {
7107 resetActiveCellList(1); // needs to be done here, since now bndry cells are known
7108 (this->*m_initializeMethod)();
7109 m_bndCnd->initializeBcData();
7110 } else {
7111 initializeRefinedCellsPerLevel();
7112 resetActiveCellList(1);
7113 m_bndCnd->initializeBcData();
7114 initializeNewInterfaceParents();
7115 }
7116
7117 // New cells have been initialized, reset flag
7118 for(MInt i = 0; i < a_noCells(); i++) {
7119 a_hasProperty(i, SolverCell::WasNewlyCreated) = false;
7120 }
7121
7122 ASSERT(m_freeIndices.empty(), "");
7123}
7124
7135template <MInt nDim>
7136void LbSolver<nDim>::refineCell(const MInt gridCellId) {
7137 // Get solver cell id of the parent
7138 MInt solverParentId = grid().tree().grid2solver(gridCellId);
7139 if(!g_multiSolverGrid) ASSERT(solverParentId == gridCellId, "");
7140
7141 ASSERT(grid().raw().a_hasProperty(gridCellId, Cell::WasRefined), "");
7142
7143 // Loop over newly created children of the parent cell
7144 for(MInt child = 0; child < grid().m_maxNoChilds; child++) {
7145 // Get grid cell id of new child
7146 MInt gridChildId = grid().raw().a_childId(gridCellId, child);
7147 if(gridChildId == -1) continue;
7148
7149 if(!g_multiSolverGrid) ASSERT(grid().raw().a_hasProperty(gridChildId, Cell::WasNewlyCreated), "");
7150
7151 // If solver is inactive all cells musst be halo cells!
7152 if(!grid().isActive()) ASSERT(grid().raw().a_isHalo(gridChildId), "");
7153 // If child exists in grid but is not located inside solver geometry
7154 if(!grid().solverFlag(gridChildId, solverId())) continue;
7155
7156 // Create cell id for new child in solver
7157 const MInt solverChildId = this->createCellId(gridChildId);
7158 a_hasProperty(solverChildId, SolverCell::WasNewlyCreated) = true;
7159 }
7160 m_refinedParents.insert(solverParentId);
7161}
7162
7163
7170template <MInt nDim>
7171void LbSolver<nDim>::removeChilds(const MInt gridCellId) {
7172 TRACE();
7173
7174 // Get solver id of the parent that will be coarsen
7175 MInt solverParentId = grid().tree().grid2solver(gridCellId);
7176 ASSERT(solverParentId > -1 && solverParentId < m_cells.size(), "solverParentId is: " << solverParentId);
7177 ASSERT(c_noChildren(solverParentId) > 0, "");
7178 if(!g_multiSolverGrid) ASSERT(solverParentId == gridCellId, "");
7179
7180 // SOLVER SPECIFIC PART
7181 // Initialize variables of parent from its children
7182 this->removeChildsLb(solverParentId);
7183
7184 // Remove solver ids of deleted children
7185 for(MInt c = 0; c < grid().m_maxNoChilds; c++) {
7186 MInt childId = c_childId(solverParentId, c);
7187 if(childId < 0) continue;
7188 this->removeCellId(childId);
7189 }
7190 if(!g_multiSolverGrid) {
7191 ASSERT((grid().raw().treeb().size() - m_cells.size()) <= grid().m_maxNoChilds, "");
7192 }
7193}
7194
7198template <MInt nDim>
7199void LbSolver<nDim>::removeCell(const MInt gridCellId) {
7200 TRACE();
7201
7202 // Get solver id of the parent that will be coarsen
7203 MInt cellId = grid().tree().grid2solver(gridCellId);
7204 if(cellId > -1) {
7205 ASSERT(cellId < m_cells.size(), "cellId is: " << cellId);
7206 ASSERT(c_noChildren(cellId) == 0, "");
7207
7208 this->removeCellId(cellId);
7209 }
7210}
7211
7212// this function should be moved to Solver as soon as cartesiansolver.h has been removed!!!
7213// this function should be moved to Solver as soon as cartesiansolver.h has been removed!!!
7214// this function should be moved to Solver as soon as cartesiansolver.h has been removed!!!
7215template <MInt nDim>
7216void LbSolver<nDim>::resizeGridMap() {
7217 grid().resizeGridMap(m_cells.size());
7218}
7219
7225template <MInt nDim>
7226void LbSolver<nDim>::swapCells(const MInt cellId0, const MInt cellId1) {
7227 if(cellId1 == cellId0) return;
7228
7229 for(MInt v = 0; v < m_noVariables; v++) {
7230 std::swap(a_variable(cellId1, v), a_variable(cellId0, v));
7231 std::swap(a_oldVariable(cellId1, v), a_oldVariable(cellId0, v));
7232 }
7233 for(MInt dir = 0; dir < m_noDistributions; dir++) {
7234 std::swap(a_distribution(cellId1, dir), a_distribution(cellId0, dir));
7235 std::swap(a_oldDistribution(cellId1, dir), a_oldDistribution(cellId0, dir));
7236 }
7237 if(m_isThermal) {
7238 for(MInt dir = 0; dir < m_noDistributions; dir++) {
7239 std::swap(a_distributionThermal(cellId1, dir), a_distributionThermal(cellId0, dir));
7240 std::swap(a_oldDistributionThermal(cellId1, dir), a_oldDistributionThermal(cellId0, dir));
7241 }
7242 }
7243 if(m_isTransport) {
7244 for(MInt dir = 0; dir < m_noDistributions; dir++) {
7245 std::swap(a_distributionTransport(cellId1, dir), a_distributionTransport(cellId0, dir));
7246 std::swap(a_oldDistributionTransport(cellId1, dir), a_oldDistributionTransport(cellId0, dir));
7247 }
7248 }
7249 std::swap(m_cells.allProperties(cellId1), m_cells.allProperties(cellId0));
7250 std::swap(a_nu(cellId1), a_nu(cellId0));
7251 std::swap(a_kappa(cellId1), a_kappa(cellId0));
7252 std::swap(a_diffusivity(cellId1), a_diffusivity(cellId0));
7253
7254 std::swap(a_bndId(cellId1), a_bndId(cellId0));
7255 std::swap(a_level(cellId1), a_level(cellId0));
7256
7257 if(!m_refinedParents.empty()) {
7258 auto it0 = m_refinedParents.find(cellId0);
7259 auto it1 = m_refinedParents.find(cellId1);
7260 if(it0 != m_refinedParents.end() && it1 == m_refinedParents.end()) {
7261 // nothing to be done
7262 } else if(it0 != m_refinedParents.end()) {
7263 m_refinedParents.erase(it0);
7264 m_refinedParents.insert(cellId1);
7265 } else if(it1 != m_refinedParents.end()) {
7266 m_refinedParents.erase(it1);
7267 m_refinedParents.insert(cellId0);
7268 }
7269 }
7270}
7271
7277template <MInt nDim>
7278void LbSolver<nDim>::swapProxy(const MInt cellId0, const MInt cellId1) {
7279 grid().swapGridIds(cellId0, cellId1);
7280}
7281
7287template <MInt nDim>
7288void LbSolver<nDim>::resetComm() {
7289 if(m_reducedComm) {
7290 mDeallocate(m_noWindowDistDataPerDomain);
7291 mDeallocate(m_noHaloDistDataPerDomain);
7292 mDeallocate(m_nghbrOffsetsWindow);
7293 mDeallocate(m_nghbrOffsetsHalo);
7294 mDeallocate(m_sendBuffers);
7295 mDeallocate(m_receiveBuffers);
7296 mDeallocate(m_needsFurtherExchange);
7297 mDeallocate(m_windowDistsForExchange);
7298 mDeallocate(m_haloDistsForExchange);
7299 if(!m_nonBlockingComm) {
7300 mDeallocate(mpi_request);
7301 } else {
7302 mDeallocate(mpi_requestS);
7303 mDeallocate(mpi_requestR);
7304 }
7305 } else {
7306 mDeallocate(m_dataBlockSizes);
7307 mDeallocate(m_baseAddresses);
7308 mDeallocate(m_nghbrOffsetsWindow);
7309 mDeallocate(m_nghbrOffsetsHalo);
7310 mDeallocate(m_sendBuffers);
7311 mDeallocate(m_receiveBuffers);
7312 if(!m_nonBlockingComm) {
7313 mDeallocate(mpi_request);
7314 } else {
7315 mDeallocate(mpi_requestS);
7316 mDeallocate(mpi_requestR);
7317 }
7318 }
7319}
7320
7325template <MInt nDim>
7326void LbSolver<nDim>::resetCellLists(MBool resize /*= true*/) {
7327 if(resize) {
7328 // resize list of active cells
7329 mDeallocate(m_activeCellList);
7330 mAlloc(m_activeCellList, grid().noCells(), "m_activeCellList", AT_);
7331 }
7332
7333 if(m_isRefined) {
7334 resetInterfaceCells();
7335 initializeInterfaceCells();
7336 buildInterfaceCells();
7337 setInterpolationNeighbors();
7338 setInterpolationCoefficients();
7339 }
7340}
7341
7347template <MInt nDim>
7348MBool LbSolver<nDim>::prepareRestart(MBool writeRestart, MBool& writeGridRestart) {
7349 TRACE();
7350
7351 writeGridRestart = false;
7352
7353 if(((globalTimeStep % m_restartInterval) == 0) || writeRestart) {
7354 writeRestart = true;
7355
7356 if(m_adaptationSinceLastRestart) {
7357 writeGridRestart = true;
7358 }
7359 }
7360
7361 return writeRestart;
7362}
7363
7369template <MInt nDim>
7370void LbSolver<nDim>::reIntAfterRestart(MBool doneRestart) {
7371 TRACE();
7372
7373 if(doneRestart) {
7374 m_adaptationSinceLastRestart = false;
7375 }
7376}
7377
7382template <MInt nDim>
7383void LbSolver<nDim>::writeRestartFile(const MBool writeRestart, const MBool /*writeBackup*/, const MString gridFileName,
7384 MInt* recalcIdTree) {
7385 TRACE();
7386
7387 if(writeRestart) {
7388 stringstream fileName;
7389 fileName << outputDir() << "restart_" << getIdentifier() << globalTimeStep << ParallelIo::fileExt();
7390
7391 std::vector<MInt> recalcCellIdsSolver(0);
7392 MInt noCells;
7393 MInt noInternalCellIds;
7394 std::vector<MInt> reorderedCellIds(0);
7395 this->calcRecalcCellIdsSolver(recalcIdTree, noCells, noInternalCellIds, recalcCellIdsSolver, reorderedCellIds);
7396 MInt* pointerRecalcIds = (recalcIdTree == nullptr) ? nullptr : recalcCellIdsSolver.data();
7397 saveRestartWithDistributionsPar(fileName.str().c_str(), gridFileName.c_str(), pointerRecalcIds);
7398 }
7399}
7400
7405template <MInt nDim>
7406void LbSolver<nDim>::setCellWeights(MFloat* solverCellWeight) {
7407 TRACE();
7408 const MInt noCellsGrid = grid().raw().treeb().size();
7409 const MInt offset = noCellsGrid * solverId();
7410
7411 for(MInt cellId = 0; cellId < a_noCells(); cellId++) {
7412 const MInt gridCellId = grid().tree().solver2grid(cellId);
7413 const MInt id = gridCellId + offset;
7414 solverCellWeight[id] = F1; // 0.2;
7415 }
7416}
7417
7418template <MInt nDim>
7419void LbSolver<nDim>::initializeNewInterfaceParents() {
7420 const MInt interfaceLevels = maxLevel() - minLevel();
7421
7422 for(MInt level = 0; level < interfaceLevels; level++) {
7423 for(MInt p = 0; p < m_noInterfaceParents[level]; p++) {
7424 auto& parent = m_interfaceParents[level]->a[p];
7425 const MInt parentId = parent.m_cellId;
7426 if(!a_wasActive(parentId) && a_isActive(parentId)) {
7427 this->removeChildsLb(parentId);
7428 }
7429 }
7430 }
7431}
7432
7442template <MInt nDim>
7443void LbSolver<nDim>::initializeRefinedCellsPerLevel() {
7444 for(MInt level = grid().maxUniformRefinementLevel(); level < grid().maxLevel(); level++) {
7445 // Exchange to update Halo cells that are needed for spatial interpolation
7446 // TODO labels:LB Find out how to exchange cells only on the current level!
7447 if(noNeighborDomains() > 0) {
7448 exchange(1);
7449 exchangeOldDistributions();
7450 }
7451
7452 for(auto&& parentId : m_refinedParents) {
7453 if(c_level(parentId) != level) continue;
7454 if(a_isHalo(parentId)) continue;
7455 if(!g_multiSolverGrid)
7456 if(!grid().raw().a_hasProperty(parentId, Cell::WasRefined)) continue;
7457
7458 // Gather children of the refined parent cell and call initialization method
7459 std::vector<MInt> childIds(grid().m_maxNoChilds, -1);
7460 for(MInt child = 0; child < grid().m_maxNoChilds; child++) {
7461 MInt childId = c_childId(parentId, child);
7462 if(childId == -1) {
7463 continue;
7464 }
7465 if(!g_multiSolverGrid) ASSERT(grid().raw().a_hasProperty(childId, Cell::WasNewlyCreated), "");
7466
7467 childIds[child] = childId;
7468 }
7469 this->refineCellLb(parentId, childIds.data());
7470 }
7471 }
7472}
7473
7474template <MInt nDim>
7475void LbSolver<nDim>::initSolverSamplingVariables(const std::vector<MInt>& varIds,
7476 const std::vector<MInt>& noSamplingVars) {
7477 TRACE();
7478
7479 // TODO labels:LB,DLB enable use with balancing
7480 if(m_isInitSamplingVars) {
7481 m_log << "Sampling variables already initialized." << std::endl;
7482 return;
7483 }
7484
7485 for(MUint i = 0; i < varIds.size(); i++) {
7486 MFloat** varPointer = nullptr;
7487 MInt dataBlockSize = noSamplingVars[i];
7488
7489 // No additional storage for primitive variables
7490 if(varIds[i] == LB_PV) {
7491 dataBlockSize = 0;
7492 }
7493
7494 if(dataBlockSize > 0) {
7495 mAlloc(varPointer, a_noCells(), dataBlockSize, "m_samplingVariables_" + std::to_string(varIds[i]), 0.0, AT_);
7496 }
7497 m_samplingVariables.push_back(varPointer);
7498 }
7499
7500 // Set flag to avoid multiple initializations when using multiple sampling features
7501 m_isInitSamplingVars = true;
7502}
7503
7504template <MInt nDim>
7505void LbSolver<nDim>::calcSamplingVariables(const std::vector<MInt>& varIds, const MBool NotUsed(exchange)) {
7506 for(MUint i = 0; i < varIds.size(); i++) {
7507 switch(varIds[i]) {
7508 case LB_PV: {
7509 // Nothing to do
7510 break;
7511 }
7512 default: {
7513 TERMM(1, "Invalid variable id");
7514 break;
7515 }
7516 }
7517 }
7518}
7519
7520template <MInt nDim>
7521void LbSolver<nDim>::interpolateVariablesInCell(const MInt cellId, const MFloat* /*position*/,
7522 MFloat* interpolationResult) {
7523 // TODO labels:LB Add trilinear or least square interpolation
7524 interpolationResult[PV->RHO] = a_variable(cellId, PV->RHO);
7525 for(MInt d = 0; d < nDim; d++) {
7526 interpolationResult[PV->U + d] = a_variable(cellId, PV->U + d);
7527 }
7528 interpolationResult[PV->P] = interpolationResult[PV->RHO] * CSsq;
7529}
7530
7532template <MInt nDim>
7533void LbSolver<nDim>::calcSamplingVarAtPoint(const MFloat* point, const MInt id, const MInt sampleVarId, MFloat* state,
7534 const MBool NotUsed(interpolate)) {
7535 // Note: interpolateVariablesInCell() requires variables for all cells
7536 switch(sampleVarId) {
7537 case LB_PV: {
7538 interpolateVariablesInCell(id, point, state);
7539 break;
7540 }
7541 default: {
7542 TERMM(1, "Invalid variable id");
7543 break;
7544 }
7545 }
7546}
7547
7548
7549template <MInt nDim>
7550void LbSolver<nDim>::saveSolverSolution(MBool /*forceOutput*/, const MBool /*finalTimeStep*/) {
7551 if(globalTimeStep % m_solutionInterval == 0 && globalTimeStep >= m_solutionOffset) saveOutput();
7552
7553 if(m_fftInterval > 0 && globalTimeStep % m_fftInterval == 0
7554 && (m_initMethod == "LB_TURBULENCE_ISOTROPIC_INIT" || m_initMethod == "FROM_RESTART_FILE")) {
7555 RECORD_TIMER_START(m_t.fft);
7556 computeFFTStatistics();
7557 RECORD_TIMER_STOP(m_t.fft);
7558 }
7559}
7560
7561template <MInt nDim>
7562void LbSolver<nDim>::finalizeInitSolver() {
7563 if(!isActive()) return;
7564 if(m_updateMacroscopicLocation == POSTPROPAGATION && !m_restartFile) {
7565 updateVariablesFromOldDist();
7566 }
7567
7568 outputInitSummary();
7569}
7570
7576template <MInt nDim>
7577void LbSolver<nDim>::outputInitSummary() {
7578 using namespace maia::logtable;
7579
7580 // TODO labels:LB,IO This should be moved into an appropriate sysEqn-like structure...
7581 const MInt noVars = nDim + 1;
7582 std::vector<MString> cvs{};
7583 cvs.push_back("rho");
7584 cvs.push_back("rho_u");
7585 cvs.push_back("rho_v");
7586 if(nDim == 3) {
7587 cvs.push_back("rho_w");
7588 }
7589
7590 // INIT SUMMARY FRAME
7591 Frame summary(" SOLVER " + std::to_string(solverId()) + " INITIALIZATION SUMMARY AT TIME STEP "
7592 + std::to_string(globalTimeStep));
7593
7594 // PROBLEM SUMMARY GROUP
7595 Group& problem = summary.addGroup(" PROBLEM SUMMARY");
7596 problem.addData("System of Equations", solverMethod());
7597 problem.addData("Number of dimensions", nDim);
7598 Data& vars = problem.addData("Number of variables", noVars);
7599
7600 MBool first = true;
7601 for(auto&& cv : cvs) {
7602 if(first) {
7603 vars.addData("Conservative variable name(s)", cv);
7604 } else {
7605 vars.addData("", cv);
7606 }
7607 first = false;
7608 }
7609
7610 problem.addData("Reynolds Number", m_Re);
7611 problem.addData("Mach Number", m_Ma);
7612 problem.addData("Reference length (LB)", m_referenceLength);
7613
7614 problem.addData("Restart", m_restartFile);
7615 if(m_restartFile) {
7616 const MString restartFileName =
7617 restartDir() + "restart_" + getIdentifier() + std::to_string(m_restartTimeStep) + ParallelIo::fileExt();
7618 problem.addData("Initial condition", restartFileName);
7619 } else {
7620 Data& initData = problem.addData("Initial condition", m_initMethod);
7621 if(m_tanhInit) {
7622 initData.addData("tanhInit Re", m_initRe);
7623 initData.addData("tanhInit start time", m_initStartTime);
7624 initData.addData("tanhInit time", m_initTime);
7625 }
7626 }
7627 problem.addData("Start time (non-dimensionalized)", globalTimeStep);
7628 problem.addData("Final time (non-dimensionalized)", globalTimeStep + g_timeSteps);
7629
7630 // DISCRETIZATION SUMMARY GROUP
7631 Group& discret = summary.addGroup("DISCRETIZATION SUMMARY");
7632 discret.addData("Number of distributions", m_noDistributions);
7633 discret.addData("Collision operator", solverMethod());
7634 discret.addBlank();
7635 Data& omega = discret.addData("Collision frequency", m_omega);
7636 omega.addData("Relaxation time", 1.0 / m_omega);
7637 omega.addData("Num. viscosity", m_nu);
7638 if(m_isThermal) {
7639 Data& omegaT = discret.addData("Collision frequency thermal", m_omegaT);
7640 omegaT.addData("Relaxation time thermal", 1.0 / m_omegaT);
7641 omegaT.addData("Num. diffusion thermal", m_kappa);
7642 }
7643 if(m_isTransport) {
7644 Data& omegaD = discret.addData("Collision frequency transport", m_omegaD);
7645 omegaD.addData("Relaxation time transport", 1.0 / m_omegaD);
7646 omegaD.addData("Num. diffusion transport", m_diffusivity);
7647 }
7648 discret.addBlank();
7649 discret.addData("External forcing", m_externalForcing);
7650
7651 discret.addBlank();
7652 MString interfaceMethod = "FILIPPOVA";
7653 interfaceMethod = Context::getSolverProperty<MString>("interfaceMethod", m_solverId, AT_, &interfaceMethod);
7654 if(m_isRefined) {
7655 discret.addData("Interface method", interfaceMethod);
7656 }
7657 discret.addData("Adaptation init method", m_adaptationInitMethod);
7658
7659 // PARALLELIZATION SUMMARY
7660 Group& parallel = summary.addGroup("PARALLELIZATION SUMMARY");
7661 parallel.addData("Domain id", domainId());
7662 parallel.addData("Number of neighbor domains", grid().noNeighborDomains());
7663 parallel.addData("Total number of domains", noDomains());
7664
7665 // GRID SUMMARY LOCAL
7666 Group& gridLocal = summary.addGroup("GRID SUMMARY (LOCAL)");
7667 Data& minlvl = gridLocal.addData("Minimum grid level", grid().minLevel());
7668 minlvl.addData("cell length", c_cellLengthAtLevel(minLevel()));
7669
7670 Data& maxlvl = gridLocal.addData("Maximum grid level", grid().maxLevel());
7671 maxlvl.addData("cell length", c_cellLengthAtLevel(maxLevel()));
7672
7673 gridLocal.addBlank();
7674 Data& noCellsLocal = gridLocal.addData("Number of cells", a_noCells());
7675 noCellsLocal.addData("internal cells", grid().noInternalCells());
7676 noCellsLocal.addData("halo cells", a_noCells() - grid().noInternalCells());
7677 gridLocal.addData("Number of active cells", m_currentMaxNoCells);
7678 gridLocal.addData("Max. number of cells (collector size)", grid().raw().treeb().capacity());
7679 gridLocal.addData("Memory utilization", 100.0 * grid().noCells() / grid().raw().treeb().capacity());
7680 gridLocal.addBlank();
7681 if(m_isRefined) {
7682 const MInt levelRange = maxLevel() - minLevel();
7683 const MInt totalNoInterfaceChildren =
7684 std::accumulate(&m_noInterfaceChildren[0], &m_noInterfaceChildren[levelRange], 0);
7685 Data& ic = gridLocal.addData("Number of interface children", totalNoInterfaceChildren);
7686 for(MInt i = 0; i < levelRange; i++) {
7687 const MInt noInterfaceChildren = m_noInterfaceChildren[i];
7688 if(noInterfaceChildren > 0) {
7689 ic.addData("on level " + std::to_string(minLevel() + i + 2), noInterfaceChildren);
7690 }
7691 }
7692 const MInt totalNoInterfaceParents =
7693 std::accumulate(&m_noInterfaceParents[0], &m_noInterfaceParents[levelRange], 0);
7694 Data& ip = gridLocal.addData("Number of interface parents", totalNoInterfaceParents);
7695 for(MInt i = 0; i < levelRange; i++) {
7696 const MInt noInterfaceParents = m_noInterfaceParents[i];
7697 if(noInterfaceParents > 0) {
7698 ip.addData("on level " + std::to_string(minLevel() + i + 1), noInterfaceParents);
7699 }
7700 }
7701 }
7702
7703 // Finally build string for summary frame
7704 MString s = summary.buildString();
7705
7706 if(domainId() == 0) {
7707 std::cout << s;
7708 }
7709 m_log << s << std::endl;
7710}
7711
7712
7713template <MInt nDim>
7714void LbSolver<nDim>::preTimeStep() {
7715 TRACE();
7716
7717 // update m_time for output
7718 updateTime();
7719}
7720
7721
7722//#################################################################################
7723// MOVING BOUNDARY PART STARTS HERE!!!!!!
7724//#################################################################################
7725
7726template <MInt nDim>
7727void LbSolver<nDim>::preCoupleLs(std::vector<MInt>& maxGCellLevels) {
7728 TRACE();
7729
7730 if(!grid().isActive()) {
7731 return;
7732 }
7733
7734 RECORD_TIMER_START(m_t.findG0Cells);
7735
7736 this->exchangeData(&a_levelSetFunctionMB(0, 0));
7737
7738 MInt startSet = m_levelSetId; // if bodyToSetMode = 11 the 0th set is the collected set, which should be skiped
7739
7740 resetActiveCellList(2);
7741
7742 if(m_reducedComm) {
7743 resetComm();
7744 prepareCommunicationReduced();
7745 }
7746
7747 RECORD_TIMER_START(m_t.findG0Candidates);
7748 findG0Candidates(maxGCellLevels); // findes all the G0Candidates of all Sets
7749 RECORD_TIMER_STOP(m_t.findG0Candidates);
7750
7751 MBool gapClosure = false;
7752 m_G0Candidates.clear();
7753
7754 MInt candidates = 0;
7755 for(MInt i = 0; i < a_noCells(); i++) {
7756 MBool isG0Candidate = false;
7757 for(MInt set = 0; set < (m_maxNoSets - startSet); set++) {
7758 if(a_isG0CandidateOfSet(i, set)) {
7759 isG0Candidate = true;
7760 break;
7761 }
7762 }
7763 m_G0CellMapping[i] = -1;
7764 if(isG0Candidate) {
7765 if(a_isHalo(i)) continue;
7766 m_G0Candidates.emplace_back();
7767 m_G0Candidates[candidates].cellId = i;
7768 m_G0CellMapping[i] = candidates;
7769 candidates++;
7770 }
7771 }
7772
7773 MBoolScratchSpace isGapCell(a_noCells(), AT_, "isGapCell");
7774 isGapCell.fill(false);
7775
7776 RECORD_TIMER_START(m_t.geomNodal);
7777 m_geometryIntersection->computeNodalValues(m_G0Candidates, &m_G0CellMapping[0], &a_levelSetFunctionMB(0, 0),
7778 &a_associatedBodyIds(0, 0), &isGapCell(0), gapClosure);
7779 RECORD_TIMER_STOP(m_t.geomNodal);
7780
7781 if(noNeighborDomains() > 0) {
7782 RECORD_TIMER_START(m_t.geomExchange);
7783
7784 // Save pointer to leafWindow/leafHaloCells per neighborDomain to get 'const MInt**' argument
7785 ScratchSpace<const MInt*> p_leafWindowCells(noNeighborDomains(), AT_, "p_leafWindowCells");
7786 ScratchSpace<const MInt*> p_leafHaloCells(noNeighborDomains(), AT_, "p_leafHaloCells");
7787 ScratchSpace<MInt> noLeafWindowCells(noNeighborDomains(), AT_, "noLeafWindowCells");
7788 ScratchSpace<MInt> noLeafHaloCells(noNeighborDomains(), AT_, "noLeafHaloCells");
7789 for(MInt d = 0; d < noNeighborDomains(); d++) {
7790 p_leafWindowCells[d] = &grid().leafWindowCell(d, 0);
7791 p_leafHaloCells[d] = &grid().leafHaloCell(d, 0);
7792 noLeafWindowCells[d] = grid().noLeafWindowCells(d);
7793 noLeafHaloCells[d] = grid().noLeafHaloCells(d);
7794 }
7795
7796 m_geometryIntersection->exchangeNodalValues(p_leafWindowCells.data(), noLeafWindowCells.data(),
7797 p_leafHaloCells.data(), m_G0Candidates, &m_G0CellMapping[0]);
7798 RECORD_TIMER_STOP(m_t.geomExchange);
7799 }
7800
7801 if(m_G0CellList != nullptr) {
7802 mDeallocate(m_G0CellList);
7803 }
7804 if(m_nodalGValues != nullptr) {
7805 mDeallocate(m_nodalGValues);
7806 }
7807
7808 if(!m_G0Candidates.empty()) {
7809 MInt noNodalGValues = m_noLevelSetsUsedForMb * (IPOW3[nDim] - 1);
7810 m_noG0CandidatesTotal = (MInt)m_G0Candidates.size();
7811 mAlloc(m_G0CellList, m_noG0CandidatesTotal, "m_G0CellList", -1, AT_);
7812 mAlloc(m_nodalGValues, m_noG0CandidatesTotal, noNodalGValues, "m_nodalGValues", F0, AT_);
7813 } else {
7814 m_noG0CandidatesTotal = 0;
7815 }
7816
7817 // reset Mapping
7818 std::fill_n(m_G0CellMapping, a_noCells(), -1);
7819
7820 RECORD_TIMER_START(m_t.calcNodalValues);
7821 calcNodalLsValues();
7822 RECORD_TIMER_STOP(m_t.calcNodalValues);
7823
7824 RECORD_TIMER_STOP(m_t.findG0Cells);
7825}
7826
7827template <MInt nDim>
7828void LbSolver<nDim>::createBndryToBodyMapping(maia::coupling::Mapping& bndryToBodyMapping,
7829 maia::coupling::Mapping& bodyToBndryMapping) {
7830 // TODO labels:LB Use collector!
7831 // Create Mapping bndryCell -> bodyId
7832 bndryToBodyMapping.clear();
7833 bodyToBndryMapping.clear();
7834 for(MInt i = 0; i < m_currentNoG0Cells; i++) {
7835 const MInt bndryCellId = m_G0CellList[i];
7836 if(a_associatedBodyIds(bndryCellId, 0) >= 0) {
7837 bndryToBodyMapping.insert(i) = a_associatedBodyIds(bndryCellId, 0);
7838 bodyToBndryMapping.insert(a_associatedBodyIds(bndryCellId, 0)) = i;
7839 }
7840 }
7841
7842 // Sanity checks
7843 for(MInt candidate = 0; candidate < m_currentNoG0Cells; candidate++) {
7844 const MInt cellId = m_G0CellList[candidate];
7845 ASSERT(cellId > -1, "G0CellList broken!");
7846 ASSERT(m_G0CellMapping[cellId] > -1, "G0CellMapping broken!");
7847 std::stringstream err;
7848 err << "G0CellMapping broken " << m_G0CellMapping[cellId] << " not eq to " << candidate;
7849 ASSERT(m_G0CellMapping[cellId] == candidate, err.str());
7850 }
7851}
7852
7853
7858template <MInt nDim>
7859void LbSolver<nDim>::resetActiveCellList(MInt mode) {
7860 TRACE();
7861 //--store wasActive state-----------------------------------------------------
7862 for(MInt i = 0; i < a_noCells(); i++) {
7863 a_wasActive(i) = a_isActive(i);
7864 }
7865 //--initialization of a_isActive----------------------------------------------
7866 if(mode != 2) {
7867 setIsActiveDefaultStates();
7868 }
7869 setInActiveBndryCells();
7870 if(mode != 1) {
7871 setInActiveMBCells();
7872 }
7873 // TODO: Between the above and the below part, coupler might be called in
7874 // future. Then the upper and lower part has to be split into several
7875 // functions. (resetActiveStates and resetActiveCellList)
7876 //--reset activeCellList------------------------------------------------------
7877 fillActiveCellList();
7878}
7879
7880// Reset external forces vector for each cell
7881template <MInt nDim>
7882void LbSolver<nDim>::resetExternalSources() {
7883 TRACE();
7884 maia::parallelFor(0, a_noCells(), [&](MInt cellId) {
7885 for(MInt dim = 0; dim < nDim; dim++) {
7886 a_externalForces(cellId, dim) = 0.0;
7887 }
7888 });
7889}
7890
7891template <MInt nDim>
7892void LbSolver<nDim>::exchangeExternalSources() {
7893 TRACE();
7894
7895 ASSERT(m_particleMomentumCoupling, "");
7896
7897 if(noDomains() > 1 && m_particleMomentumCoupling) {
7898 maia::mpi::reverseExchangeAddData(grid().neighborDomains(), grid().haloCells(), grid().windowCells(), mpiComm(),
7899 &a_externalForces(0, 0), nDim);
7900 }
7901}
7902
7908template <MInt nDim>
7909inline void LbSolver<nDim>::setIsActiveDefaultStates() {
7910 TRACE();
7911 const MInt noCells = m_cells.size();
7912 if(m_isRefined) {
7913 for(MInt i = 0; i < noCells; i++) {
7914 const MBool l_isActive = c_isLeafCell(i) || a_isInterfaceParent(i);
7915 a_isActive(i) = (l_isActive) ? 1 : 0;
7916 }
7917 } else {
7918 for(MInt i = 0; i < noCells; i++) {
7919 const MBool l_isActive = c_isLeafCell(i);
7920 a_isActive(i) = (l_isActive) ? 1 : 0;
7921 }
7922 }
7923 if(m_solidLayerExtension) {
7924 if(m_initialActiveCells != nullptr) {
7925 mDeallocate(m_initialActiveCells);
7926 }
7927 mAlloc(m_initialActiveCells, noCells, "m_initialActiveCells", 1, AT_);
7928 for(MInt i = 0; i < noCells; i++) {
7929 if(a_isActive(i)) {
7930 a_isActive(i) = !m_geometry->pointIsInside2(&a_coordinate(i, 0));
7931 }
7932 m_initialActiveCells[i] = a_isActive(i);
7933 }
7934 }
7935}
7936
7941template <MInt nDim>
7942inline void LbSolver<nDim>::setInActiveBndryCells() {
7943 TRACE();
7944 // find ids of wall boundaries
7945 vector<MInt> wallIds;
7946 for(MInt i = 0; i < (MInt)(m_bndCnd->m_bndCndSegIds.size()); i++) {
7947 MBool is_periodic = false;
7948 if(m_bndCnd->m_noPeriodicSegments != 0)
7949 for(MInt j = 0; j < m_bndCnd->m_noPeriodicSegments; j++)
7950 if(m_bndCnd->m_bndCndSegIds[i] == m_bndCnd->m_periodicSegmentsIds[j]) {
7951 is_periodic = true;
7952 break;
7953 }
7954 MBool is_inout = false;
7955 for(MInt j = 0; j < m_bndCnd->m_noInOutSegments; j++)
7956 if(m_bndCnd->m_bndCndSegIds[i] == m_bndCnd->m_inOutSegmentsIds[j]) {
7957 is_inout = true;
7958 break;
7959 }
7960 if(!is_periodic & !is_inout) wallIds.push_back(i);
7961 }
7962 // set non-fluid cell to inactive
7963 for(auto wallId : wallIds) {
7964 for(MInt i = m_bndCnd->m_bndCndOffsets[wallId]; i < m_bndCnd->m_bndCndOffsets[wallId + 1]; i++) {
7965 if(m_bndCnd->m_bndCells[i].m_isFluid == false) {
7966 const MInt id = m_bndCnd->m_bndCells[i].m_cellId;
7967 a_isActive(id) = false;
7968 }
7969 }
7970 }
7971}
7972
7973
7978template <MInt nDim>
7979inline void LbSolver<nDim>::setInActiveMBCells() {
7980 TRACE();
7981 if(m_maxNoSets == -1 || m_levelSetValues == nullptr) return;
7982 for(MInt i = 0; i < a_noCells(); i++) {
7983 if(m_solidLayerExtension) {
7984 if(!m_initialActiveCells[i]) continue;
7985 }
7986 for(MInt set = 0; set < m_maxNoSets; set++) {
7987 if(a_associatedBodyIds(i, set) < 0) continue;
7988 if(!c_isLeafCell(i)) continue;
7989 if(a_levelSetFunctionMB(i, set) < 0) {
7990 a_isActive(i) = 0;
7991 } else {
7992 a_isActive(i) = 1;
7993 }
7994 }
7995 }
7996}
7997
8002template <MInt nDim>
8003inline void LbSolver<nDim>::fillActiveCellList() {
8004 if(!isActive()) return;
8005 const MInt noLevels = maxLevel() - minLevel() + 1;
8006
8007 // counting active cells - total and per level
8008 m_currentMaxNoCells = 0;
8009 ScratchSpace<MInt> noActiveCellsPerLevel(noLevels, AT_, "noActiveCellsPerLevel");
8010 noActiveCellsPerLevel.fill(0);
8011 for(MInt i = 0; i < a_noCells(); i++) {
8012 if(a_isActive(i)) {
8013 const MInt lvl = a_level(i) - minLevel();
8014 m_activeCellList[m_currentMaxNoCells] = i;
8015 noActiveCellsPerLevel[lvl]++;
8016 m_currentMaxNoCells++;
8017 }
8018 }
8019 // fill left entries with non-valid value
8020 for(MInt i = m_currentMaxNoCells; i < a_noCells(); i++) {
8021 m_activeCellList[i] = -1;
8022 }
8023 // m_log << " + number of active cells: " << m_currentMaxNoCells << endl << endl;
8024 // fill offset for better accessing later
8025 if(m_activeCellListLvlOffset != nullptr) {
8026 mDeallocate(m_activeCellListLvlOffset);
8027 }
8028 mAlloc(m_activeCellListLvlOffset, noLevels + 1, "m_activeCellListLvlOffset", 0, AT_);
8029 m_activeCellListLvlOffset[0] = 0;
8030 for(MInt i = 1; i < noLevels + 1; i++) {
8031 m_activeCellListLvlOffset[i] = m_activeCellListLvlOffset[i - 1] + noActiveCellsPerLevel[i - 1];
8032 }
8033 // sort active cells per level TODO labels:LB,toenhance sorting algorithm might be worth improving (HEAP-sort?)
8034 {
8035 MInt level = minLevel();
8036 MInt i = 0;
8037 for(MInt l = 0; l < noLevels - 1; l++) {
8038 MInt j = i + 1;
8039 for(; i < m_activeCellListLvlOffset[l + 1]; i++) {
8040 if(a_level(m_activeCellList[i]) == level) {
8041 j = i + 1;
8042 continue; // already at correct position
8043 }
8044 for(; j < m_currentMaxNoCells; j++) {
8045 if(a_level(m_activeCellList[j]) == level) {
8046 std::swap(m_activeCellList[i], m_activeCellList[j]);
8047 break; // now with correct level, next position i
8048 }
8049 }
8050 }
8051 level++;
8052 }
8053 }
8054}
8055
8062template <MInt nDim>
8063void LbSolver<nDim>::findG0Candidates(std::vector<MInt>& maxGCellLevels) {
8064 TRACE();
8065
8066 const MInt startSet = m_levelSetId;
8067
8068 for(MInt set = startSet; set < m_maxNoSets; set++) {
8069 const MInt positionInArray = set - startSet;
8070 const MFloat maxDistanceG0 = 4.0 * c_cellLengthAtLevel(maxGCellLevels[set]);
8071
8072 for(MInt i = 0; i < a_noCells(); i++) {
8073 a_isG0CandidateOfSet(i, positionInArray) = false;
8074 if(a_isHalo(i)) continue;
8075 if(!c_isLeafCell(i)) continue;
8076 if(a_associatedBodyIds(i, set) == -2) continue;
8077 // if a_associatedBodyId is set to -2 it means that the corresponding cell is
8078 // not cell belonging to the LS solver. Hence, no valid LS values and body Ids
8079 // can be given here. Cells which are located next to cells which are not
8080 // owned by the LS solver require special treatment. These cells should not
8081 // be G0 candidates because their nodel G-value cannot be calculated accurately.
8082 // Hence, these cells are filltered out.
8083 MBool hasNeighborInPureLbDomain = false;
8084 for(MInt n = 0; n < IPOW3[nDim] - 1; n++) {
8085 if(c_neighborId(i, n) < 0) {
8086 continue;
8087 } else {
8088 if(a_associatedBodyIds(c_neighborId(i, n), set) == -2) {
8089 hasNeighborInPureLbDomain = true;
8090 a_associatedBodyIds(i, set) = -1;
8091 break;
8092 }
8093 }
8094 }
8095 if(hasNeighborInPureLbDomain) {
8096 a_isG0CandidateOfSet(i, positionInArray) = false;
8097 } else {
8098 if(m_allowBndryAsG0) {
8099 if((fabs(a_levelSetFunctionMB(i, set)) < maxDistanceG0)) {
8100 a_isG0CandidateOfSet(i, positionInArray) = true;
8101 } else {
8102 a_isG0CandidateOfSet(i, positionInArray) = false;
8103 }
8104 } else {
8105 if((fabs(a_levelSetFunctionMB(i, set)) < maxDistanceG0) && !a_isBndryCell(i)) {
8106 a_isG0CandidateOfSet(i, positionInArray) = true;
8107 } else {
8108 a_isG0CandidateOfSet(i, positionInArray) = false;
8109 }
8110 }
8111 }
8112 }
8113 }
8114
8115 if(noDomains() > 1) {
8116 exchangeG0Candidates();
8117 }
8118}
8119
8120
8127template <MInt nDim>
8128void LbSolver<nDim>::exchangeG0Candidates() {
8129 TRACE();
8130
8131 MInt startSet = m_levelSetId; // if bodyToSetMode = 11 the 0th set is the collected set, which should be skiped
8132 for(MInt set = 0; set < (m_maxNoSets - startSet); set++) {
8133 // 1. Gathered information (to which level set does the cell belong) of the window cells.
8134 MInt sendNeighOffset = 0;
8135 for(MInt n = 0; n < noNeighborDomains(); n++) {
8136 for(MInt j = 0; j < noWindowCells(n); j++) {
8137 m_sendBufferMB[sendNeighOffset + j] = a_isG0CandidateOfSet(windowCell(n, j), set);
8138 }
8139 sendNeighOffset += noWindowCells(n);
8140 }
8141
8142 // 2. Send the gathered information.
8143 sendNeighOffset = 0;
8144 for(MInt n = 0; n < noNeighborDomains(); n++) {
8145 MInt bufsize = noWindowCells(n);
8146 MPI_Issend(&m_sendBufferMB[sendNeighOffset], bufsize, MPI_CXX_BOOL, neighborDomain(n), 0, mpiComm(),
8147 &mpi_request[n], AT_, "m_sendBufferMB[sendNeighOffset]");
8148 sendNeighOffset += noWindowCells(n);
8149 }
8150
8151 // 3. Receive data from neighbors.
8152 MPI_Status status;
8153 MInt recNeighOffset = 0;
8154 for(MInt n = 0; n < noNeighborDomains(); n++) {
8155 MInt bufsize = noHaloCells(n);
8156 MPI_Recv(&m_receiveBufferMB[recNeighOffset], bufsize, MPI_CXX_BOOL, neighborDomain(n), 0, mpiComm(), &status, AT_,
8157 "m_receiveBufferMB[recNeighOffset]");
8158 recNeighOffset += noHaloCells(n);
8159 }
8160
8161 for(MInt n = 0; n < noNeighborDomains(); n++)
8162 MPI_Wait(&mpi_request[n], &status, AT_);
8163
8164 // 4. scatter the received data
8165 recNeighOffset = 0;
8166 for(MInt n = 0; n < noNeighborDomains(); n++) {
8167 for(MInt j = 0; j < noHaloCells(n); j++) {
8168 a_isG0CandidateOfSet(haloCell(n, j), set) = m_receiveBufferMB[recNeighOffset + j];
8169 }
8170 recNeighOffset += noHaloCells(n);
8171 }
8172 }
8173}
8174
8183template <MInt nDim>
8184MInt LbSolver<nDim>::setUpLbInterpolationStencil(const MInt cellId, MInt* interpolationCells, MFloat* point) {
8185 TRACE();
8186
8187 std::function<MBool(const MInt, const MInt)> alwaysTrue = [&](const MInt, const MInt) { return true; };
8188
8189 return this->setUpInterpolationStencil(cellId, interpolationCells, point, alwaysTrue, false);
8190}
8191
8192template <MInt nDim>
8193MFloat LbSolver<nDim>::interpolateFieldDataLb(MInt* interpolationCells, MFloat* point, MInt set,
8194 std::function<MFloat(MInt, MInt)> scalarField,
8195 std::function<MFloat(MInt, MInt)> coordinate) {
8196 TRACE();
8197
8198 return this->template interpolateFieldData<true>(interpolationCells, point, set, scalarField, coordinate);
8199}
8200
8201template <MInt nDim>
8202void LbSolver<nDim>::initializeMovingBoundaries() {
8203 TRACE();
8204
8205 //########################################################################
8206 // start moving boundary properties
8207 //########################################################################
8208
8222 m_trackMovingBndry = true;
8223 m_trackMovingBndry = Context::getSolverProperty<MBool>("trackMovingBndry", m_solverId, AT_, &m_trackMovingBndry);
8224
8237 m_trackMbStart = 0;
8238 m_trackMbStart = Context::getSolverProperty<MInt>("trackMbStart", m_solverId, AT_, &m_trackMbStart);
8239
8252 m_trackMbEnd = numeric_limits<MInt>::max();
8253 m_trackMbEnd = Context::getSolverProperty<MInt>("trackMbEnd", m_solverId, AT_, &m_trackMbEnd);
8254
8255 if(m_sendBufferMB != nullptr) mDeallocate(m_sendBufferMB);
8256 if(m_receiveBufferMB != nullptr) mDeallocate(m_receiveBufferMB);
8257 if(noDomains() > 1 && grid().isActive()) {
8258 MInt sumwin = 0;
8259 MInt sumhalo = 0;
8260 for(MInt d = 0; d < noNeighborDomains(); d++) {
8261 sumwin += noWindowCells(d);
8262 sumhalo += noHaloCells(d);
8263 }
8264 mAlloc(m_sendBufferMB, sumwin, "m_sendBufferMB", 0, AT_);
8265 mAlloc(m_receiveBufferMB, sumhalo, "m_receiveBufferMB", 0, AT_);
8266 }
8267 //########################################################################
8268 // end moving boundary properties
8269 //########################################################################
8270
8271 //########################################################################
8272 // start updating ActiveCellList for Moving Boundaries
8273 //########################################################################
8274
8275 // body properties
8276 if(m_associatedBodyIds != nullptr) mDeallocate(m_associatedBodyIds);
8277 mAlloc(m_associatedBodyIds, m_noLevelSetsUsedForMb * a_noCells(), "m_associatedBodyIds", -1, AT_);
8278 if(m_levelSetValues != nullptr) mDeallocate(m_levelSetValues);
8279 mAlloc(m_levelSetValues, m_noLevelSetsUsedForMb * a_noCells(), "m_levelSetValues", F0, AT_);
8280
8281 MInt startSet = m_levelSetId; // if bodyToSetMode = 11 the 0th set is the collected set, which should be skiped
8282 MInt arrayLength = m_maxNoSets - startSet;
8283 if(m_isG0CandidateOfSet != nullptr) mDeallocate(m_isG0CandidateOfSet);
8284 mAlloc(m_isG0CandidateOfSet, a_noCells(), arrayLength, "m_isG0CandidateOfSet", false, AT_);
8285
8286 //########################################################################
8287 // end updating ActiveCellList for Moving Boundaries
8288 //########################################################################
8289
8290 if(m_G0CellMapping != nullptr) mDeallocate(m_G0CellMapping);
8291 mAlloc(m_G0CellMapping, a_noCells(), "m_G0CellMapping", -1, AT_);
8292 if(m_innerBandWidth != nullptr) mDeallocate(m_innerBandWidth);
8293 mAlloc(m_innerBandWidth, grid().maxRefinementLevel(), "m_innerBandWidth", F0, AT_);
8294 if(m_bandWidth != nullptr) mDeallocate(m_bandWidth);
8295 mAlloc(m_bandWidth, grid().maxRefinementLevel(), "m_bandWidth", 0, AT_);
8296 if(m_outerBandWidth != nullptr) mDeallocate(m_outerBandWidth);
8297 mAlloc(m_outerBandWidth, grid().maxRefinementLevel(), "m_outerBandWidth", F0, AT_);
8298
8310 MFloat distFac[2] = {18.0, 9.0};
8311 for(MInt i = 0; i < 2; i++) {
8312 distFac[i] = Context::getSolverProperty<MFloat>("mbBandWidth", m_solverId, AT_, &distFac[i], i);
8313 }
8314
8315 m_outerBandWidth[grid().maxRefinementLevel() - 1] = distFac[0] * c_cellLengthAtLevel(grid().maxRefinementLevel());
8316 m_bandWidth[grid().maxRefinementLevel() - 1] = distFac[0];
8317 for(MInt i = grid().maxRefinementLevel() - 2; i >= 0; i--) {
8318 m_outerBandWidth[i] = m_outerBandWidth[i + 1] + (distFac[1] * c_cellLengthAtLevel(i + 1));
8319 m_bandWidth[i] = (m_bandWidth[i + 1] / 2) + 1 + distFac[1];
8320 }
8321 for(MInt i = 0; i < grid().maxRefinementLevel(); i++) {
8322 m_innerBandWidth[i] = -m_outerBandWidth[i];
8323 m_log << "bandwidth level " << i << ": " << m_innerBandWidth[i] << " " << m_outerBandWidth[i] << endl;
8324 }
8325
8337 m_refineDiagonals = true;
8338 m_refineDiagonals = Context::getSolverProperty<MBool>("refineDiagonals", m_solverId, AT_, &m_refineDiagonals);
8339
8340 if(noNeighborDomains() > 0 && grid().isActive()) {
8341 grid().updateLeafCellExchange();
8342 }
8343}
8344
8348template <MInt nDim>
8349void LbSolver<nDim>::balancePre() {
8350 // Update the grid proxy for this solver
8351 grid().update();
8352
8353 // Just reset parallelization information if solver is not active and cleanup containers
8354 if(!isActive()) {
8355 updateDomainInfo(-1, -1, MPI_COMM_NULL, AT_);
8356 return;
8357 }
8358
8359 // Set new domain info for solver
8360 updateDomainInfo(grid().domainId(), grid().noDomains(), grid().mpiComm(), AT_);
8361
8362 // Clear cell collector
8363 m_cells.clear();
8364
8365 // Resize cell collector to internal cells
8366 m_cells.append(grid().noInternalCells());
8367
8368 // Now we are for the cell collector to be filled
8369 m_setCellDataFinished = false;
8370}
8371
8375template <MInt nDim>
8376void LbSolver<nDim>::balancePost() {
8377 // Cell collector data has been set
8378 m_setCellDataFinished = true;
8379
8380 // Nothing to do if solver is not active
8381 if(!isActive()) {
8382 return;
8383 }
8384
8385 // append the halo-cells
8386 m_cells.append(c_noCells() - m_cells.size());
8387
8388 updateCellCollectorFromGrid();
8389
8390 grid().findEqualLevelNeighborsParDiagonal(false);
8391
8392 resetCellLists();
8393
8394 // Restart boundary condition object to update cell lists
8395 restartBndCnd();
8396 resetActiveCellList();
8397
8398 resetComm();
8399
8400 if(m_reducedComm) {
8401 prepareCommunicationReduced();
8402 } else {
8403 prepareCommunicationNormal();
8404 }
8405
8406 // Do an exchange to have the right values in halo cells
8407 if(noNeighborDomains() > 0) {
8408 exchange(1);
8409 exchangeOldDistributions();
8410 }
8411
8412 // If grid adaptation is used make sure that a grid file is written with the next restart file
8413 // since the cells will be sorted during balacing, while the old grid after adaptation will
8414 // contain the unsorted grid cells
8415 if(m_adaptation) {
8416 m_adaptationSinceLastRestart = true;
8417 m_adaptationSinceLastSolution = true;
8418 }
8419}
8420
8426template <MInt nDim>
8427MInt LbSolver<nDim>::noLoadTypes() const {
8428 const MInt noLevels = maxLevel() - minLevel() + 1;
8429 const MInt noBndryTypes = 1;
8430
8431 return noLevels + noBndryTypes;
8432}
8433
8439template <MInt nDim>
8440void LbSolver<nDim>::getDefaultWeights(MFloat* weights, std::vector<MString>& names) const {
8441 TRACE();
8442
8443 std::cerr << "WARNING: Using powers of 0.5 as weights. Could lead to large number of cells for cases with large "
8444 "refinement level range."
8445 << std::endl;
8446
8447 MInt count = 0;
8448
8449 const MInt noLevels = maxLevel() - minLevel() + 1;
8450 for(MInt level = 0; level < noLevels; level++) {
8451 const MInt lvlDiff = noLevels - level;
8452 weights[count] = FFPOW2(lvlDiff);
8453 std::stringstream ss;
8454 ss << "lb_leaf_cell_level_" << minLevel() + level;
8455 names[count] = ss.str();
8456 count++;
8457 }
8458
8459 // Boundary cells
8460 weights[count] = 1.0;
8461 names[count] = "lb_bndry_cell";
8462}
8463
8464/* \brief Return the load of a single cell
8465 * \author Miro Gondrum
8466 * \date 15.01.2021
8467 * \param[in] cellId Requested grid cell id
8468 * \param[in] weights Computational weights for different simulation components
8469 * \return Cell load
8470 */
8471template <MInt nDim>
8472MFloat LbSolver<nDim>::getCellLoad(const MInt gridCellId, const MFloat* const weights) const {
8473 TRACE();
8474 ASSERT(isActive(), "solver is not active");
8475
8476 // Convert to solver cell id and check
8477 const MInt cellId = grid().tree().grid2solver(gridCellId);
8478 if(cellId < 0) {
8479 return 0;
8480 }
8481 // sanity check
8482 if(cellId < 0 || cellId >= grid().noInternalCells()) {
8483 TERMM(1, "The given cell id is invalid.");
8484 }
8485
8486 // lb specific part
8487 MFloat cellLoad = 0.0; // Default cell load
8488 if(a_isActive(cellId)) {
8489 const MInt relLevel = c_level(cellId) - minLevel();
8490 cellLoad += weights[relLevel];
8491 }
8492
8493 const MInt noLevels = maxLevel() - minLevel() + 1;
8494 if(a_isBndryCell(cellId)) {
8495 cellLoad += weights[noLevels];
8496 }
8497
8498 return cellLoad;
8499}
8500
8501// Get total number of loadQuantities for this domain...
8502template <MInt nDim>
8503void LbSolver<nDim>::getLoadQuantities(MInt* const loadQuantities) const {
8504 TRACE();
8505
8506 // Reset
8507 std::fill_n(&loadQuantities[0], noLoadTypes(), 0);
8508
8509 // Nothing to do if solver is not active
8510 if(!isActive()) {
8511 return;
8512 }
8513
8514 const MInt noLevels = maxLevel() - minLevel() + 1;
8515
8516 for(MInt level = 0; level < noLevels; level++) {
8517 const MInt noActiveCellsOfLevel = m_activeCellListLvlOffset[level + 1] - m_activeCellListLvlOffset[level];
8518 loadQuantities[level] = noActiveCellsOfLevel;
8519 }
8520
8521 MInt noBoundaryCells = 0.0;
8522 for(MInt i = 0; i < a_noCells(); i++) {
8523 noBoundaryCells += a_isBndryCell(i);
8524 }
8525 loadQuantities[noLevels] = noBoundaryCells;
8526}
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 MBool propertyExists(const MString &name, MInt solver=m_noSolvers)
This function checks if a property exists in general.
Definition: context.cpp:494
void open(const MString &filename, const MString &projectName, MInt fileType=0, MPI_Comm mpiComm=MPI_COMM_WORLD, MBool rootOnlyHardwired=false)
Opens a file by passing the parameters to InfoOut_<xyz>FileBuffer::open(...).
Definition: infoout.cpp:975
Storage of the Position of the Conservative Variables (RHO, RHO_VV, RHO_E) in the value vectors of th...
Definition: variables.h:31
Storage of the Position of the Primitive Variables (u, v, w, T, p) in the value vectors of the solver...
Definition: variables.h:110
This class is a ScratchSpace.
Definition: scratch.h:758
MInt string2enum(MString theString)
This global function translates strings in their corresponding enum values (integer values)....
Definition: enums.cpp:20
@ POWERLAW
Definition: enums.h:263
@ CARREAU
Definition: enums.h:263
@ LB_PV
Definition: enums.h:386
LbInterpolationType
Definition: enums.h:265
@ LINEAR_INTERPOLATION
Definition: enums.h:265
@ CUBIC_INTERPOLATION
Definition: enums.h:265
@ QUADRATIC_INTERPOLATION
Definition: enums.h:265
@ LB_TURBULENCE_ISOTROPIC_INIT
Definition: enums.h:234
@ LOCAL
Definition: enums.h:188
@ ROHDE
Definition: enums.h:258
@ FILIPPOVA
Definition: enums.h:258
@ MAIA_LATTICE_BGK_TRANSPORT
Definition: enums.h:87
@ MAIA_LATTICE_BGKI_EULER_2D
Definition: enums.h:83
@ MAIA_LATTICE_MRT2
Definition: enums.h:95
@ MAIA_LATTICE_BGK
Definition: enums.h:74
@ MAIA_LATTICE_CUMULANT
Definition: enums.h:98
@ MAIA_LATTICE_BGKI_SMAGORINSKY
Definition: enums.h:78
@ MAIA_LATTICE_BGK_TOTALENERGY_TRANSPORT
Definition: enums.h:90
@ MAIA_LATTICE_BGK_TOTALENERGY
Definition: enums.h:86
@ MAIA_LATTICE_BGK_INNERENERGY
Definition: enums.h:85
@ MAIA_LATTICE_BGK_THERMAL_TRANSPORT
Definition: enums.h:88
@ MAIA_LATTICE_RBGK_SMAGORINSKY
Definition: enums.h:92
@ MAIA_LATTICE_BGKC
Definition: enums.h:77
@ MAIA_LATTICE_CLB_SMAGORINSKY
Definition: enums.h:97
@ MAIA_LATTICE_BGK_INIT
Definition: enums.h:75
@ MAIA_LATTICE_RBGK_DYNAMIC_SMAGO
Definition: enums.h:82
@ MAIA_LATTICE_BGKI_SMAGORINSKY2
Definition: enums.h:79
@ MAIA_LATTICE_CLB
Definition: enums.h:96
@ MAIA_LATTICE_BGK_INNERENERGY_TRANSPORT
Definition: enums.h:89
@ MAIA_LATTICE_BGKI_DYNAMIC_SMAGO
Definition: enums.h:81
@ MAIA_LATTICE_RBGK
Definition: enums.h:91
@ MAIA_LATTICE_MRT_SMAGORINSKY
Definition: enums.h:93
@ MAIA_LATTICE_BGK_THERMAL
Definition: enums.h:84
@ MAIA_LATTICE_BGKI_SMAGO_WALL
Definition: enums.h:80
@ MAIA_LATTICE_MRT
Definition: enums.h:94
@ MAIA_LATTICE_BGK_GUO_FORCING
Definition: enums.h:76
@ LB
Definition: enums.h:278
void mTerm(const MInt errorCode, const MString &location, const MString &message)
Definition: functions.cpp:29
float floatSwap(float f)
Definition: functions.h:365
constexpr Real POW3(const Real x)
Definition: functions.h:123
constexpr Real POW2(const Real x)
Definition: functions.h:119
int intSwap(int f)
Definition: functions.h:351
double doubleSwap(double f)
Definition: functions.h:379
MInt globalTimeStep
MBool g_multiSolverGrid
InfoOutFile m_log
@ POSTPROPAGATION
Definition: lbsolver.h:34
@ INCOLLISION
Definition: lbsolver.h:34
@ PRECOLLISION
Definition: lbsolver.h:34
constexpr MLong IPOW2(MInt x)
constexpr MFloat FPOW2(MInt x)
int32_t MInt
Definition: maiatypes.h:62
std::basic_string< char > MString
Definition: maiatypes.h:55
double MFloat
Definition: maiatypes.h:52
bool MBool
Definition: maiatypes.h:58
char MChar
Definition: maiatypes.h:56
MInt id
Definition: maiatypes.h:71
int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status, const MString &name, const MString &varname)
same as MPI_Recv
int MPI_Comm_create(MPI_Comm comm, MPI_Group group, MPI_Comm *newcomm, const MString &name, const MString &varname)
same as MPI_Comm_create, but updates the number of MPI communicators
int MPI_Issend(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request, const MString &name, const MString &varname)
same as MPI_Issend
int MPI_Group_incl(MPI_Group group, int n, const int ranks[], MPI_Group *newgroup, const MString &name)
same as MPI_Group_incl
int MPI_Comm_group(MPI_Comm comm, MPI_Group *group, const MString &name, const MString &varname)
same as MPI_Comm_group
int MPI_Wait(MPI_Request *request, MPI_Status *status, const MString &name)
same as MPI_Wait
int MPI_Gatherv(const void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, const int recvcounts[], const int displs[], MPI_Datatype recvtype, int root, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Gatherv
int MPI_Waitall(int count, MPI_Request *request, MPI_Status *status, const MString &name)
same as MPI_Waitall
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_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm, const MString &name, const MString &varname)
same as MPI_Bcast
int MPI_Gather(const void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Gather
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
void const MInt cellId
Definition: collector.h:239
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
MFloat determinant(std::array< T, N > &m)
Definition: maiamath.cpp:130
MFloat distance(const MFloat *a, const MFloat *b)
Definition: maiamath.h:249
MFloat frobeniusMatrixNormSquared(MFloatScratchSpace &m, MInt dim1, MInt dim2)
Definition: maiamath.h:328
void addMatrices(MFloatScratchSpace &m1, MFloatScratchSpace &m2, MFloatScratchSpace &result, MInt dim1, MInt dim2)
Definition: maiamath.h:319
void multiplyMatricesSq(MFloatScratchSpace &m1, MFloatScratchSpace &m2, MFloatScratchSpace &result, MInt dim)
Definition: maiamath.h:279
void calcEigenValues(MFloat A[3][3], MFloat w[3])
Definition: maiamath.cpp:475
void computeEnergySpectrum(MFloatScratchSpace &velocity, MIntScratchSpace &indices, const ptrdiff_t howmany, MInt locSize, MInt nx, MInt ny, MInt nz, MFloat viscosity, const MPI_Comm comm)
Compute energy spectrum on unity cube.
Definition: maiafftw.h:1051
void exchangeData(const MInt noNghbrDomains, const MInt *const nghbrDomains, const MInt *const noHaloCells, const MInt **const, const MInt *const noWindowCells, const MInt **const windowCells, const MPI_Comm comm, const U *const data, U *const haloBuffer, const MInt noDat=1)
Generic exchange of data.
Definition: mpiexchange.h:295
Namespace for auxiliary functions/classes.
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
LB lattice descriptor for arrays depending on D.
static constexpr MInt idFld(MInt i, MInt j)
static constexpr MInt oppositeDist(MInt i)
static constexpr MInt intNghbrArray(MInt i, MInt j)
static constexpr MFloat linearInterpolationCoefficients(MInt i, MInt j)
static constexpr MInt dirFld(MInt i, MInt j, MInt k)
Definition: contexttypes.h:19