MAIA bb96820c
Multiphysics at AIA
Loading...
Searching...
No Matches
lpt.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 "lpt.h"
8#include <chrono>
9#include <cmath>
10#include <memory>
11#include <random>
12#include <thread>
13#include "COMM/mpioverride.h"
14#include "IO/parallelio.h"
15#include "lpt_props.h"
16#include "lptcollision.h"
18
19using namespace std;
20using namespace maia;
21using namespace maia::lpt;
22
30template <MInt nDim>
31LPT<nDim>::LPT(const MInt solverId_, GridProxy& gridProxy_, Geometry<nDim>& geometry_, const MPI_Comm comm)
32 : maia::CartesianSolver<nDim, LPT<nDim>>(solverId_, gridProxy_, comm, true), m_geometry(&geometry_) {
33 TRACE();
34
35 // create state files
36 m_material = std::make_unique<MaterialState<nDim>>(solverId());
37
38 // set wether the LPT-solver is used non-dimensionally
39 // this needs to be known before the initialisation of the solver!
40 m_nonDimensional = false;
41 m_nonDimensional = Context::getSolverProperty<MBool>("nonDimensionaliseLPT", solverId(), AT_, &m_nonDimensional);
42
43 // To set particle release Time for particles in isoTropic Turbulence
44 // Release Timestep is determined by a Skewness of 0.44 of fluid velocity
45 // skips Solution step until desired skewness is reached
46 m_skewnessTimeStep = Context::getSolverProperty<MInt>("skewnessTimeStep", solverId(), AT_, &m_skewnessTimeStep);
47
48 // creates the LPT Solver timers
49 initializeTimers();
50}
51
52template <MInt nDim>
54
55 m_time = 0.0;
56
57 // 1) read all properties
58 readModelProps();
59
60 // deleted in new version
61 // readAdditionalProperties();
62
63 m_cells.setLptCollectorCoupling(m_massCoupling, m_momentumCoupling, m_heatCoupling);
64
65 if(m_evaporation) {
66 m_cells.setLptCollectorNoSpecies(1);
67 }
68
69 if(m_ellipsoids) {
70 m_cells.setLptCollectorSlopes();
71 }
72
73 // load properties related to intial generation of particles
74 if(m_spawnParticles) {
75 readSpawnProps();
76 }
77
78 if(m_momentumCoupling || m_heatCoupling) {
79 readMomentumCouplingProps();
80 }
81 if(m_ellipsoids) {
82 readEllipsoidProps();
83 }
84
85 initParticleVector();
86
87 if(m_activeSecondaryBUp || m_activePrimaryBUp) {
88 m_sprayModel = std::unique_ptr<SprayModel<nDim>>(new SprayModel<nDim>());
89 m_sprayModel->init(this);
90 m_log << "Spray model has been activated." << std::endl;
91 }
92
93 // If solver inactive only add dummy cells and mark all cells as halo cells
94 // Inactive solvers can not have internal cells
95 if(!isActive()) {
96 m_cells.clear();
97 m_cells.reset(grid().maxNoCells());
98 this->setHaloCellsOnInactiveRanks();
99 initBndryCells();
100 return;
101 }
102
103 initCollector();
104
105 initBndryCells();
106
107 initGridProperties();
108
109 findSpawnCellId();
110
111 updateExchangeCells();
112 grid().updateLeafCellExchange();
113 initMPI();
114
115 grid().findEqualLevelNeighborsParDiagonal(false);
116
117 initialCondition();
118
119 if(a_timeStepComputationInterval() > 0 && globalTimeStep % a_timeStepComputationInterval() == 0) {
120 forceTimeStep(computeTimeStep());
121 }
122
123 countParticlesInCells();
124
125 // LPT-statistics
126 MInt noCells = a_noCells();
127 MInt minCells = noCells;
128 MInt maxCells = noCells;
129 MInt noPart = a_noParticles();
130 MInt minParts = noPart;
131 MInt maxParts = noPart;
132
133 MPI_Allreduce(MPI_IN_PLACE, &noCells, 1, MPI_INT, MPI_SUM, mpiComm(), AT_, "INPLACE", "noCells");
134 MPI_Allreduce(MPI_IN_PLACE, &noPart, 1, MPI_INT, MPI_SUM, mpiComm(), AT_, "INPLACE", "noPart");
135 MPI_Allreduce(MPI_IN_PLACE, &maxCells, 1, MPI_INT, MPI_MAX, mpiComm(), AT_, "INPLACE", "maxCells");
136 MPI_Allreduce(MPI_IN_PLACE, &minCells, 1, MPI_INT, MPI_MIN, mpiComm(), AT_, "INPLACE", "minCells");
137 MPI_Allreduce(MPI_IN_PLACE, &maxParts, 1, MPI_INT, MPI_MAX, mpiComm(), AT_, "INPLACE", "maxParts");
138 MPI_Allreduce(MPI_IN_PLACE, &minParts, 1, MPI_INT, MPI_MIN, mpiComm(), AT_, "INPLACE", "minParts");
139
140 if(domainId() == 0) {
141 cerr << "LPT cell-statistics : " << minCells << " - " << maxCells << " avg " << noCells / noDomains() << endl;
142 cerr << "LPT particle-statistics : " << minParts << " - " << maxParts << " avg " << noPart / noDomains() << endl;
143 }
144}
145
152template <MInt nDim>
154 TRACE();
155
156 m_cells.clear();
157 m_cells.reset(grid().maxNoCells());
158 ASSERT(grid().tree().size() < grid().maxNoCells(), "Increase collector size!");
159 for(MInt cellId = 0; cellId < grid().noInternalCells(); cellId++) {
160 m_cells.append();
161 a_isHalo(cellId) = false;
162 a_isValidCell(cellId) = true;
163 a_isWindow(cellId) = false;
164 a_regridTrigger(cellId) = false;
165 a_noParticlesInCell(cellId) = 0;
166 a_noEllipsoidsInCell(cellId) = 0;
167 a_bndryCellId(cellId) = -1;
168 for(MInt n = 0; n < nDim; n++) {
169 a_fluidVelocity(cellId, n) = 0.0;
170 }
171 a_fluidDensity(cellId) = 0.0;
172 a_fluidPressure(cellId) = 0.0;
173 a_fluidTemperature(cellId) = 0.0;
174 if(m_evaporation) {
175 a_fluidSpecies(cellId) = 0.0;
176 }
177
178 if(m_momentumCoupling) {
179 for(MInt i = 0; i < nDim; i++) {
180 a_momentumFlux(cellId, i) = 0;
181 }
182 a_workFlux(cellId) = 0;
183 }
184 if(m_heatCoupling) {
185 a_heatFlux(cellId) = 0;
186 }
187 if(m_massCoupling) {
188 a_massFlux(cellId) = 0;
189 }
190 if(m_ellipsoids) {
191 for(MInt varId = 0; varId < nDim; varId++) {
192 for(MInt dir = 0; dir < nDim; dir++) {
193 a_velocitySlope(cellId, varId, dir) = F0;
194 }
195 }
196 }
197 }
198 for(MInt cellId = grid().noInternalCells(); cellId < grid().tree().size(); cellId++) {
199 m_cells.append();
200 a_isHalo(cellId) = true;
201 a_isWindow(cellId) = false;
202 a_isValidCell(cellId) = true;
203 a_regridTrigger(cellId) = false;
204 a_noParticlesInCell(cellId) = 0;
205 a_noEllipsoidsInCell(cellId) = 0;
206 a_bndryCellId(cellId) = -1;
207 for(MInt n = 0; n < nDim; n++) {
208 a_fluidVelocity(cellId, n) = 0.0;
209 }
210 a_fluidDensity(cellId) = 0.0;
211 a_fluidPressure(cellId) = 0.0;
212 a_fluidTemperature(cellId) = 0.0;
213 if(m_evaporation) {
214 a_fluidSpecies(cellId) = 0.0;
215 }
216 if(m_momentumCoupling) {
217 for(MInt i = 0; i < nDim; i++) {
218 a_momentumFlux(cellId, i) = 0;
219 }
220 a_workFlux(cellId) = 0;
221 }
222 if(m_heatCoupling) {
223 a_heatFlux(cellId) = 0;
224 }
225 if(m_massCoupling) {
226 a_massFlux(cellId) = 0;
227 }
228 if(m_ellipsoids) {
229 for(MInt varId = 0; varId < nDim; varId++) {
230 for(MInt dir = 0; dir < nDim; dir++) {
231 a_velocitySlope(cellId, varId, dir) = F0;
232 }
233 }
234 }
235 }
236}
237
246template <MInt nDim>
248 TRACE();
249
250 // if(isActive()) {
251 m_partList.reserve(static_cast<MUlong>(m_maxNoParticles));
252 if(m_ellipsoids) m_partListEllipsoid.reserve(static_cast<MUlong>(m_maxNoParticles));
253 // }
254
255 // set common/base static values
256
268 MInt intOrder = 2;
269 intOrder = Context::getSolverProperty<MInt>("particleInterpolationOrder", solverId(), AT_, &intOrder);
270 m_partList[0].s_interpolationOrder = intOrder;
271
272 MInt intMethod = 1;
273 intMethod = Context::getSolverProperty<MInt>("particleInterpolationMethod", solverId(), AT_, &intMethod);
274 m_partList[0].s_interpolationMethod = intMethod;
275
276 MFloat distFactorImp = 0.5;
277 distFactorImp =
278 Context::getSolverProperty<MFloat>("particleInterpolationDistFactor", solverId(), AT_, &distFactorImp);
279 m_partList[0].s_distFactorImp = distFactorImp;
280
281 m_partList[0].s_backPtr = this;
282
283 // set ellipsoids static values
284 if(m_ellipsoids) {
285 m_partListEllipsoid[0].s_interpolationOrder = intOrder;
286 m_partListEllipsoid[0].s_interpolationMethod = intMethod;
287 m_partListEllipsoid[0].s_backPtr = this;
288 m_partListEllipsoid[0].s_distFactorImp = distFactorImp;
289 }
290
291
292 // set particle static values:
293
294 if(m_nonDimensional) {
295 const MFloat Re = Context::getSolverProperty<MFloat>("Re", solverId(), AT_);
296 m_partList[0].s_Re = Re;
297 if(m_ellipsoids) m_partListEllipsoid[0].s_Re = Re;
298
299 m_FrMag = 0;
300 for(MInt i = 0; i < nDim; i++) {
301 const MFloat Frm = Context::getSolverProperty<MFloat>("Frm", solverId(), AT_, i);
302 m_partList[0].s_Frm[i] = Frm;
303 if(m_ellipsoids) m_partListEllipsoid[0].s_Frm[i] = Frm;
304 m_FrMag += POW2(Frm);
305 }
306 m_FrMag = sqrt(m_FrMag);
307
308 if(m_heatCoupling || m_evaporation) {
309 m_partList[0].s_Pr = Context::getSolverProperty<MFloat>("Pr", solverId(), AT_);
310 m_partList[0].s_Sc = Context::getSolverProperty<MFloat>("Sc", solverId(), AT_);
311 }
312 if(m_activeSecondaryBUp) m_partList[0].s_We = Context::getSolverProperty<MFloat>("We", solverId(), AT_);
313
314 } else {
315 m_partList[0].s_Re = F1;
316 if(m_ellipsoids) m_partListEllipsoid[0].s_Re = F1;
317 MFloat defaultGravity = 0.0;
318 MFloat gravity = 0;
319 for(MInt i = 0; i < nDim; i++) {
320 gravity = Context::getSolverProperty<MFloat>("particleGravity", solverId(), AT_, &defaultGravity, i);
321 m_partList[0].s_Frm[i] = gravity;
322 if(m_ellipsoids) m_partListEllipsoid[0].s_Frm[i] = gravity;
323 m_FrMag += POW2(gravity);
324 }
325 m_FrMag = sqrt(m_FrMag);
326 }
327
339 MFloat lengthFactor = F1;
340 lengthFactor = Context::getSolverProperty<MFloat>("lengthFactor", solverId(), AT_, &lengthFactor);
341 m_partList[0].s_lengthFactor = lengthFactor;
342 if(m_ellipsoids) m_partListEllipsoid[0].s_lengthFactor = lengthFactor;
343}
344
351template <MInt nDim>
353 TRACE();
354
355 // if(!m_wallCollisions) return;
356
357 // init data structure
358 constexpr MInt maxNoSurfaces = 1;
359 mAlloc(m_bndryCells, m_maxNoBndryCells, nDim, 0, 0, maxNoSurfaces, 0, "m_bndryCells", AT_);
360
361 // fill on own data
362 m_noOuterBndryCells = 0;
363 m_bndryCells->resetSize(0);
364}
365
373template <MInt nDim>
374void LPT<nDim>::initMPI(const MBool fullReinit) {
375 TRACE();
376
377 if(noDomains() < 1) {
378 return;
379 }
380
381 const MInt noNghbrDomains = grid().noNeighborDomains();
382
383 if(fullReinit) {
384 m_queueToSend.resize(noNghbrDomains);
385 m_sendSize.resize(noNghbrDomains);
386 m_recvSize.resize(noNghbrDomains);
387 m_sendBuffer.resize(noNghbrDomains);
388 m_recvBuffer.resize(noNghbrDomains);
389 m_intSendBuffer.resize(noNghbrDomains);
390 m_intRecvBuffer.resize(noNghbrDomains);
391 m_mpi_reqSendFloat.resize(noNghbrDomains);
392 m_mpi_reqRecvFloat.resize(noNghbrDomains);
393 m_mpi_reqSendSize.resize(noNghbrDomains);
394 m_mpi_reqSendInt.resize(noNghbrDomains);
395 m_mpi_reqRecvInt.resize(noNghbrDomains);
396 m_mpi_statusProbe.resize(noNghbrDomains);
397
398 // if particles and ellipsoids can be present, the larger buffer size is chosen
399 const MInt bufferSize = !m_ellipsoids ? bufSize<LPTSpherical<nDim>>()
400 : mMax(bufSize<LPTSpherical<nDim>>(), bufSize<LPTEllipsoidal<nDim>>());
401 const MInt intBufferSize = !m_ellipsoids
402 ? intBufSize<LPTSpherical<nDim>>()
403 : mMax(intBufSize<LPTSpherical<nDim>>(), intBufSize<LPTEllipsoidal<nDim>>());
404
405 for(MInt i = 0; i < noNghbrDomains; i++) {
406 m_sendSize[i] = 0;
407 m_recvSize[i] = 0;
408 m_sendBuffer[i] = make_unique<MFloat[]>(bufferSize);
409 m_recvBuffer[i] = make_unique<MFloat[]>(bufferSize);
410 m_intSendBuffer[i] = make_unique<MInt[]>(intBufferSize);
411 m_intRecvBuffer[i] = make_unique<MInt[]>(intBufferSize);
412 }
413
414 // initialize the buffer
415 for(MInt i = 0; i < noNghbrDomains; i++) {
416 for(MInt j = 0; j < bufferSize; j++) {
417 m_sendBuffer[i][j] = 0.0;
418 m_recvBuffer[i][j] = 0.0;
419 }
420 for(MInt j = 0; j < intBufferSize; j++) {
421 m_intSendBuffer[i][j] = 0;
422 m_intRecvBuffer[i][j] = 0;
423 }
424 }
425
426
427 if(m_nonBlockingComm) {
428 m_noSourceTerms = 0;
429 if(m_momentumCoupling) m_noSourceTerms++;
430 if(m_momentumCoupling) m_noSourceTerms = m_noSourceTerms + nDim + 1;
431 if(m_heatCoupling) m_noSourceTerms++;
432
433 m_sourceSend.resize(noNghbrDomains);
434 m_sourceRecv.resize(noNghbrDomains);
435
436 if(m_sourceSendRequest != nullptr) mDeallocate(m_sourceSendRequest);
437 mAlloc(m_sourceSendRequest, noNghbrDomains, "sourceSendReq", AT_);
438 if(m_sourceRecvRequest != nullptr) mDeallocate(m_sourceRecvRequest);
439 mAlloc(m_sourceRecvRequest, noNghbrDomains, "sourceRecvReq", AT_);
440
441 if(m_flowSendRequest != nullptr) {
442 mDeallocate(m_flowSendRequest);
443 }
444 mAlloc(m_flowSendRequest, noNghbrDomains, "flowSendReq", AT_);
445 if(m_flowRecvRequest != nullptr) {
446 mDeallocate(m_flowRecvRequest);
447 }
448 mAlloc(m_flowRecvRequest, noNghbrDomains, "flowRecvReq", AT_);
449
450 if(m_checkAdaptationSendRequest != nullptr) {
451 mDeallocate(m_checkAdaptationSendRequest);
452 }
453 mAlloc(m_checkAdaptationSendRequest, noDomains(), "checkAdapSendReq", AT_);
454 if(m_checkAdaptationRecvRequest != nullptr) {
455 mDeallocate(m_checkAdaptationRecvRequest);
456 }
457 mAlloc(m_checkAdaptationRecvRequest, noDomains(), "checkAdapRecvReq", AT_);
458
459 m_checkAdaptationSend.resize(noDomains());
460 m_checkAdaptationRecv.resize(noDomains());
461
462 if(m_ellipsoids) {
463 if(m_slopesSendRequest != nullptr) {
464 mDeallocate(m_slopesSendRequest);
465 }
466 mAlloc(m_slopesSendRequest, noNghbrDomains, "slopesSendReq", AT_);
467 if(m_slopesRecvRequest != nullptr) {
468 mDeallocate(m_slopesRecvRequest);
469 }
470 mAlloc(m_slopesRecvRequest, noNghbrDomains, "slopesRecvReq", AT_);
471 }
472 }
473 }
474
475 if(m_nonBlockingComm) {
476 MInt maxNoHaloCells = 0;
477 MInt maxNoWindowCells = 0;
478
479 ScratchSpace<MInt> windowCellsCnt(noNeighborDomains(), AT_, "noWindowCells");
480 ScratchSpace<MInt> haloCellsCnt(noNeighborDomains(), AT_, "noHaloCells");
481
482 for(MInt i = 0; i < noNghbrDomains; i++) {
483 maxNoHaloCells = mMax(maxNoHaloCells, grid().noLeafHaloCells(i));
484 maxNoWindowCells = mMax(maxNoWindowCells, grid().noLeafWindowCells(i));
485 m_sourceSendRequest[i] = MPI_REQUEST_NULL;
486 m_sourceRecvRequest[i] = MPI_REQUEST_NULL;
487 m_flowSendRequest[i] = MPI_REQUEST_NULL;
488 m_flowRecvRequest[i] = MPI_REQUEST_NULL;
489 if(m_ellipsoids) {
490 m_slopesSendRequest[i] = MPI_REQUEST_NULL;
491 m_slopesRecvRequest[i] = MPI_REQUEST_NULL;
492 }
493 windowCellsCnt[i] = grid().noLeafWindowCells(i);
494 haloCellsCnt[i] = grid().noLeafHaloCells(i);
495 }
496
497 for(MInt d = 0; d < noDomains(); d++) {
498 m_checkAdaptationRecvRequest[d] = MPI_REQUEST_NULL;
499 m_checkAdaptationSendRequest[d] = MPI_REQUEST_NULL;
500 }
501
502 if(m_flowRecv != nullptr) {
503 mDeallocate(m_flowRecv);
504 }
505 mAlloc(m_flowRecv, noNeighborDomains(), &haloCellsCnt[0], PV.noVars() + m_evaporation, "m_flowRecv", AT_);
506 if(m_flowSend != nullptr) {
507 mDeallocate(m_flowSend);
508 }
509 mAlloc(m_flowSend, noNeighborDomains(), &windowCellsCnt[0], PV.noVars() + m_evaporation, "m_flowSend", AT_);
510
511
512 for(MInt i = 0; i < noNghbrDomains; i++) {
513 m_sourceSend[i] = make_unique<MFloat[]>(maxNoHaloCells * m_noSourceTerms);
514 m_sourceRecv[i] = make_unique<MFloat[]>(maxNoWindowCells * m_noSourceTerms);
515 }
516
517
518 for(MInt i = 0; i < noNghbrDomains; i++) {
519 for(MInt j = 0; j < grid().noLeafHaloCells(i) * m_noSourceTerms; j++) {
520 m_sourceSend[i][j] = 0.0;
521 }
522 for(MInt j = 0; j < grid().noLeafWindowCells(i) * m_noSourceTerms; j++) {
523 m_sourceRecv[i][j] = 0.0;
524 }
525 }
526 for(MInt i = 0; i < noDomains(); i++) {
527 m_checkAdaptationSend[i] = 0;
528 m_checkAdaptationRecv[i] = 0;
529 }
530
531 if(m_ellipsoids) {
532 if(m_slopesRecv != nullptr) {
533 mDeallocate(m_slopesRecv);
534 }
535 mAlloc(m_slopesRecv, noNeighborDomains(), &haloCellsCnt[0], nDim * nDim, "m_slopesRecv", AT_);
536 if(m_slopesSend != nullptr) {
537 mDeallocate(m_slopesSend);
538 }
539 mAlloc(m_slopesSend, noNeighborDomains(), &windowCellsCnt[0], nDim * nDim, "m_slopesSend", AT_);
540 }
541 }
542
543 if(m_respawn && fullReinit) {
544 struct {
545 MInt val;
546 MInt rank;
547 } local{}, global{};
548 local.val = (MInt)m_respawnCells.size();
549 local.rank = domainId();
550 global.val = 0;
551 global.rank = -1;
552 MPI_Allreduce(&local, &global, 1, MPI_2INT, MPI_MAXLOC, mpiComm(), AT_, "local", "global");
553 m_respawnDomain = global.rank;
554 MInt* particleRespawnCellsPerDomain = nullptr;
555 if(domainId() == m_respawnDomain) {
556 particleRespawnCellsPerDomain = new MInt[noDomains()];
557 }
558 MPI_Gather(&local.val, 1, MPI_INT, particleRespawnCellsPerDomain, 1, MPI_INT, m_respawnDomain, mpiComm(), AT_,
559 "local.val", "particleRespawnCellsPerDomain");
560
561
562 if(domainId() == m_respawnDomain) {
563 MInt globalNoRespawnCells = 0;
564 m_noRespawnDomains = 0;
565 for(MInt i = 0; i < noDomains(); ++i) {
566 globalNoRespawnCells += particleRespawnCellsPerDomain[i];
567 if(particleRespawnCellsPerDomain[i] > 0) {
568 m_noRespawnDomains++;
569 }
570 }
571 if(globalNoRespawnCells == 0) {
572 stringstream errorMessage;
573 errorMessage << "globally no RespawnCells found despite the set properties particleRespawn " << m_respawn
574 << " and general Respawn (particleRespawnType==0) or massiveParallel ";
575 mTerm(1, AT_, errorMessage.str());
576 }
577 m_respawnGlobalDomainOffsets = new MInt[m_noRespawnDomains + 1];
578 m_respawnGlobalDomainOffsets[0] = 0;
579 m_respawnDomainRanks = new MInt[m_noRespawnDomains];
580 MInt counter = 0;
581 for(MInt i = 0; i < noDomains(); ++i) {
582 if(particleRespawnCellsPerDomain[i] > 0) {
583 m_respawnDomainRanks[counter] = i;
584 m_respawnGlobalDomainOffsets[counter + 1] =
585 m_respawnGlobalDomainOffsets[counter] + particleRespawnCellsPerDomain[i];
586 ++counter;
587 }
588 }
589
590 delete[] particleRespawnCellsPerDomain;
591 } else {
592 m_noRespawnDomains = 1;
593 }
594 }
595}
596
602template <MInt nDim>
604 computeBoundingBox(m_globBbox.begin());
605 MInt periodic = 0;
606 for(MInt n = 0; n < nDim; n++) {
607 m_globDomainLength[n] = m_globBbox[n + nDim] - m_globBbox[n];
608 periodic += grid().raw().periodicCartesianDir(n);
609 }
610
611 if(periodic) {
612 m_periodicBC = true;
613 }
614}
615
623template <MInt nDim>
625 TRACE();
626
627 if(m_restart) {
628 const MInt noParticles = loadParticleRestartFile();
629 m_log << noParticles << " particles loaded from restart-file." << endl;
630
631 // particle
632 calculateTerminalVelocities();
633
634 checkParticles();
635
636 } else {
637 MInt noParticles = 0;
638
639 // particle initialization methods
640 // 0 = general init (default)
641 // 1 = centered in all inflow cells
642 // 2,3 = provide 'part.txt' file with diameter, density ratio and coordinates
643 // (the corresponding cellIds are determined, velocity is zero)
644 // 4 = provide 'part.txt' file with diameter, density ratio, coordinates
645 // and velocity (the corresponding cellIds are determined)
646 // 5 = no initialisation (only primary spray injection)
647
648
649 switch(m_initializationMethod) {
650 // general particle initialisation
651 case 0: {
652 MFloat* dia = nullptr;
653 MFloat* particleDiameters = nullptr;
654 MInt sumOfRates = 0;
655
656 // ellipsoids
657 MFloat particleEllipsoidInitNoPartperCell = F0;
658 vector<pair<MFloat, MFloat>> ellipsoidsInitVector;
659 MInt noEllipsoidCat = 0;
660
671 MFloat particleInitNoPartperCell = 0.0;
672 particleInitNoPartperCell = Context::getSolverProperty<MFloat>("particleInitNoPartperCell", solverId(), AT_,
673 &particleInitNoPartperCell);
674
675 if(particleInitNoPartperCell > 0) {
676 m_log << "LPT.cpp:initialCondition(): Initializing " << particleInitNoPartperCell << " particles per cell."
677 << endl;
678
686 array<MFloat, 10> particleDiametersDefaults = {0.00001, 0.00002, 0.00003, 0.00004, 0.00005,
687 0.00006, 0.00007, 0.00008, 0.00009, 0.00010};
688 MInt noDifferentPart = 10;
689 if(Context::propertyExists("particleDiameters", solverId())) {
690 noDifferentPart = Context::propertyLength("particleDiameters", solverId());
691 particleDiameters = new MFloat[noDifferentPart];
692 for(MInt i = 0; i < noDifferentPart; i++) {
693 particleDiameters[i] = Context::getSolverProperty<MFloat>("particleDiameters", solverId(), AT_, i);
694 }
695 } else {
696 particleDiameters = new MFloat[noDifferentPart];
697 copy(&particleDiametersDefaults[0], &particleDiametersDefaults[9], particleDiameters);
698 }
706 MIntScratchSpace particleInitQuotasOfDiameters(noDifferentPart, AT_, "particleInitQuotasOfDiameters");
707 MInt count = 0;
708 if(Context::propertyExists("particleInitRatesOfDiameters", solverId())) {
709 count = Context::propertyLength("particleInitRatesOfDiameters", solverId());
710 if(count == noDifferentPart) {
711 for(MInt i = 0; i < count; i++) {
712 particleInitQuotasOfDiameters[i] =
713 Context::getSolverProperty<MInt>("particleInitRatesOfDiameters", solverId(), AT_, i);
714 }
715 // const MInt* partQuotas =
716 // *(Context::getSolverProperty<MInt>("particleInitRatesOfDiameters",
717 // solverId(), AT_));
718 // const MInt* partQuotas = nullptr;
719 // copy(partQuotas, &partQuotas[noDifferentPart], &particleInitQuotasOfDiameters[0]);
720 } else {
721 stringstream errorMessage;
722 errorMessage << " LPT::initialize() with m_particleInitializationMethod"
723 << " = -1 : Try to initialize particles with " << noDifferentPart
724 << " different diameters (property particleDiameters), but found " << count
725 << " different rates (property particleInitRatesOfDiameters) " << endl;
726 mTerm(1, AT_, errorMessage.str());
727 }
728 } // otherwise define diameters to be equally likely
729 else {
730 for(MInt i = 0; i < noDifferentPart; ++i) {
731 particleInitQuotasOfDiameters[i] = 1;
732 }
733 }
734
735 // to randomly distribute the diameters among particles declare array
736 // dia with the number of diameters being dependent on the previously
737 // declared quotas
738 sumOfRates =
739 accumulate(&particleInitQuotasOfDiameters[0], &particleInitQuotasOfDiameters[0] + noDifferentPart, 0);
740 dia = new MFloat[sumOfRates];
741 MInt counter = 0;
742 for(MInt i = 0; i < noDifferentPart; ++i) {
743 for(MInt j = 0; j < particleInitQuotasOfDiameters[i]; ++j) {
744 dia[counter] = particleDiameters[i];
745 counter++;
746 }
747 }
748 shuffle(dia, &dia[sumOfRates], randomInit());
749
750 calculateTerminalVelocities();
751 }
752
753 if(m_ellipsoids) {
754 MFloat* ellipsoidSemiMinorAxis = nullptr;
755 MFloat* ellipsoidAspectRatios = nullptr;
756 const MInt noEllipsoidCatDefault = 6;
757 array<MFloat, noEllipsoidCatDefault> ellipsoidSemiMinorAxisDefaults = {
758 0.000031748, 0.000039850263, 0.00002773445, 0.000034668, 0.0000259842, 0.000031498026};
759 array<MFloat, noEllipsoidCatDefault> ellipsoidsAspectRatioDefaults = {2.0, 2.0, 3.0, 3.0, 4.0, 4.0};
760
761 particleEllipsoidInitNoPartperCell = F0;
762 particleEllipsoidInitNoPartperCell = Context::getSolverProperty<MFloat>(
763 "particleEllipsoidInitNoPartperCell", solverId(), AT_, &particleEllipsoidInitNoPartperCell);
764
765 if(particleEllipsoidInitNoPartperCell > 0) {
766 m_log << "LPT.cpp:initialCondition(): Initializing " << particleEllipsoidInitNoPartperCell
767 << " ellipsoids per cell." << endl;
768
769 if(Context::propertyExists("particleEllipsoidSemiMinorAxis", solverId())) {
770 noEllipsoidCat = Context::propertyLength("particleEllipsoidSemiMinorAxis", solverId());
771 if(noEllipsoidCat != Context::propertyLength("particleEllipsoidAspectRatio", solverId())) {
772 mTerm(1, AT_,
773 "Error the properties particleEllipsoidSemiMinorAxis and particleEllipsoidAspectRatio "
774 " do not have the same count!");
775 }
776 ellipsoidSemiMinorAxis = new MFloat[noEllipsoidCat];
777 ellipsoidAspectRatios = new MFloat[noEllipsoidCat];
778 for(MInt i = 0; i < noEllipsoidCat; i++) {
779 ellipsoidSemiMinorAxis[i] =
780 Context::getSolverProperty<MFloat>("particleEllipsoidSemiMinorAxis", solverId(), AT_, i);
781 ellipsoidAspectRatios[i] =
782 Context::getSolverProperty<MFloat>("particleEllipsoidAspectRatio", solverId(), AT_, i);
783 }
784 } else {
785 noEllipsoidCat = noEllipsoidCatDefault;
786 ellipsoidSemiMinorAxis = new MFloat[noEllipsoidCat];
787 ellipsoidAspectRatios = new MFloat[noEllipsoidCat];
788 copy(&ellipsoidSemiMinorAxisDefaults[0], &ellipsoidSemiMinorAxisDefaults[noEllipsoidCat - 1],
789 ellipsoidSemiMinorAxis);
790 copy(&ellipsoidsAspectRatioDefaults[0], &ellipsoidsAspectRatioDefaults[noEllipsoidCat - 1],
791 ellipsoidAspectRatios);
792 }
793
794 MIntScratchSpace ellipsoidInitQuotasPerCat(noEllipsoidCat, AT_, "particleInitQuotasOfDiameters");
795 MInt count = 0;
796 if(Context::propertyExists("particleEllipsoidInitRatesPerCat", solverId())) {
797 count = Context::propertyLength("particleEllipsoidInitRatesPerCat", solverId());
798 if(count == noEllipsoidCat) {
799 for(MInt i = 0; i < count; i++) {
800 ellipsoidInitQuotasPerCat[i] =
801 Context::getSolverProperty<MInt>("particleEllipsoidInitRatesPerCat", solverId(), AT_, i);
802 }
803 } else {
804 stringstream errorMessage;
805 errorMessage << " LPT::initialize() with m_particleInitializationMethod"
806 << " = -1 : Try to initialize particles with " << noEllipsoidCat
807 << " different ellipsoidal categories, but found " << count
808 << " different rates (property particleEllipsoidInitRatesPerCat) " << endl;
809 mTerm(1, AT_, errorMessage.str());
810 }
811 } else { // otherwise define cats to be equally likely
812 for(MInt i = 0; i < noEllipsoidCat; ++i) {
813 ellipsoidInitQuotasPerCat[i] = 1;
814 }
815 }
816
817 for(MInt i = 0; i < noEllipsoidCat; ++i) {
818 for(MInt j = 0; j < ellipsoidInitQuotasPerCat[i]; ++j) {
819 ellipsoidsInitVector.emplace_back(ellipsoidSemiMinorAxis[i], ellipsoidAspectRatios[i]);
820 }
821 }
822 shuffle(ellipsoidsInitVector.begin(), ellipsoidsInitVector.end(), randomInit());
823 }
824 delete[] ellipsoidSemiMinorAxis;
825 delete[] ellipsoidAspectRatios;
826 } // end of ellipsoids properties reading
827
828 if(particleEllipsoidInitNoPartperCell > 0 || particleInitNoPartperCell > 0) {
836 MFloatScratchSpace particleInitOffsets(nDim * 2, AT_, "particleInitOffsets");
837 m_geometry->getBoundingBox(&particleInitOffsets[0]);
838 for(MInt i = 0; i < nDim * 2; ++i) {
839 particleInitOffsets[i] =
840 Context::getSolverProperty<MFloat>("particleInitOffsets", solverId(), AT_, &particleInitOffsets[i], i);
841 }
842
843 MInt teller = 0;
844 MInt sayer = 0;
845 if(m_ellipsoids) sayer = domainId() % noEllipsoidCat;
846 MInt noParticleCells = 0;
847 MBool randomLocWthCell = true;
855 randomLocWthCell =
856 Context::getSolverProperty<MBool>("particleRandLocWthCell", solverId(), AT_, &randomLocWthCell);
857
865 array<MFloat, nDim> particleInitVelocity{};
866 for(MInt i = 0; i < nDim; ++i) {
867 particleInitVelocity.at(i) = Context::getSolverProperty<MFloat>("particleInitVelocity", solverId(), AT_,
868 &particleInitVelocity.at(i), i);
869 }
870
871 // Note: particleInitOffsets is multiplied by referenceLength
872 for(MInt cellId = 0; cellId < noInternalCells(); cellId++) {
873 if(!c_isLeafCell(cellId)) continue;
874 // don't use bndryCells!
875 MBool hasNoNeighbor = false;
876 for(MInt dir = 0; dir < m_noDirs; dir++) {
877 if(!c_hasNeighbor(cellId, m_revDir[dir])
878 && (c_parentId(cellId) == -1 || !c_hasNeighbor(c_parentId(cellId), m_revDir[dir]))) {
879 hasNoNeighbor = true;
880 break;
881 }
882 }
883 if(hasNoNeighbor) continue;
884 // check if particle is in bounding box
885 MBool inside = true;
886 for(MInt direction = 0; direction < nDim; ++direction) {
887 if(c_coordinate(cellId, direction) < particleInitOffsets[direction]
888 || c_coordinate(cellId, direction) > particleInitOffsets[nDim + direction]) {
889 inside = false;
890 }
891 }
892 if(!inside) continue;
893 if(inside) {
894 ++noParticleCells;
895 while((m_partList.size() / MFloat(noParticleCells)) < particleInitNoPartperCell) {
896 addParticle(cellId, dia[teller], m_material->densityRatio(), static_cast<MInt>(randomLocWthCell));
897
898 m_partList.back().m_velocity = particleInitVelocity;
899
900 // loops through the diameters
901 teller = (teller + 1) % sumOfRates;
902 if(teller == 0) {
903 shuffle(dia, &dia[sumOfRates], randomInit());
904 }
905 noParticles++;
906 }
907
908 if(m_ellipsoids) {
909 while((m_partListEllipsoid.size() / MFloat(noParticleCells)) < particleEllipsoidInitNoPartperCell) {
910 addEllipsoid(cellId, ellipsoidsInitVector[sayer].first, ellipsoidsInitVector[sayer].second,
911 m_material->densityRatio(), static_cast<MInt>(randomLocWthCell));
912
913 m_partListEllipsoid.back().m_velocity = particleInitVelocity;
914
915 sayer = (sayer + 1) % ellipsoidsInitVector.size(); // loops through the cats
916 if(sayer == 0) {
917 shuffle(ellipsoidsInitVector.begin(), ellipsoidsInitVector.end(), randomInit());
918 }
919 ++noParticles;
920 }
921 }
922 }
923 }
924 cerr << domainId() << ": Particles initialized: " << noParticles << endl;
925 exchangeOffset(noParticles);
926 }
927
928 delete[] particleDiameters;
929 break;
930 }
931 case 1: {
932 MFloat diam = 0.1;
933 MInt inflowDir = 1;
940 inflowDir = Context::getSolverProperty<MInt>("particleInflowDir", solverId(), AT_, &inflowDir);
941 MInt revDir = m_revDir[inflowDir];
942 for(MInt cellId = 0; cellId < noInternalCells(); cellId++) {
943 if(!c_isLeafCell(cellId)) continue;
944 if(!c_hasNeighbor(cellId, revDir)
945 && (c_parentId(cellId) == -1 || !c_hasNeighbor(c_parentId(cellId), revDir))) {
946 addParticle(cellId, diam, m_material->densityRatio(), 0);
947
948 // correct initial velocity!
949 // The position is already at the cartesian cell center!
950 for(MInt t = 0; t < nDim; t++) {
951 m_partList.back().m_oldVel.at(t) = 0;
952 m_partList.back().m_velocity.at(t) = 0;
953 }
954 noParticles++;
955 }
956 }
957 cerr << "domainId " << domainId() << " created " << noParticles << endl;
958
959 if(m_ellipsoids) mTerm(1, AT_, "Init mode 1 not yet implemented for ellipsoidal particles!");
960
961 exchangeOffset(noParticles);
962 break;
963 }
964
965 case 2:
966 case 3: // particles without defined velocity
967 case 4: {
968 ifstream readFile;
969 array<MFloat, nDim> thisCoord{};
970 array<MFloat, nDim> thisVel{};
971 MFloat thisDiameter = NAN;
972 MFloat density = NAN;
973
974 readFile.open("part.txt", ios_base::in);
975 if(!readFile) {
976 mTerm(1, AT_, "Error reading particle input file 'part.txt'.");
977 }
978
979 for(;;) { // read the particle parameters from file:
980 // 3D 2D
981 // (1) (1) diameter
982 // (2) (2) density
983 // (3-5) (3-4) x, y(, z) coordinates
984 // (6-8) (5-6) u, v(, w) velocities
985 readFile >> thisDiameter;
986 if((readFile.rdstate() & ifstream::eofbit) != 0) {
987 break;
988 }
989 readFile >> density;
990 for(MInt i = 0; i < nDim; i++) {
991 readFile >> thisCoord.at(i);
992 }
993
994 if(m_initializationMethod == 4) {
995 for(MInt i = 0; i < nDim; i++) {
996 readFile >> thisVel.at(i);
997 }
998 }
999
1000 // find cellId of new particle
1001 MInt thisCellId = grid().findContainingLeafCell(&thisCoord[0]);
1002
1003 // it is possible that there is more than one valid location to eliminate the possibility
1004 // of creating duplicate particles on multiple domains check via mpi
1005 if(noDomains() > 1) {
1006 MInt spawnDomain = domainId();
1007 MInt minDomain = -1;
1008 if(thisCellId == -1) {
1009 spawnDomain = std::numeric_limits<MInt>::max();
1010 }
1011
1012 // Only create particle on the domain with the lowest Id
1013 MPI_Allreduce(&spawnDomain, &minDomain, 1, MPI_INT, MPI_MIN, mpiComm(), AT_, "spawnDomain", "minDomain");
1014
1015 if(minDomain != domainId()) {
1016 continue;
1017 }
1018 }
1019
1020 // particle is not inside this domain
1021 if(thisCellId == -1) {
1022 if(noDomains() == 1) {
1023 m_log << "Particle initialisation file contains particles outside of the geometry." << endl;
1024 }
1025 continue;
1026 }
1027
1028 addParticle(thisCellId, thisDiameter, density);
1029 for(MInt i = 0; i < nDim; i++) {
1030 m_partList.back().m_position.at(i) = thisCoord.at(i);
1031 }
1032 for(MInt i = 0; i < nDim; i++) {
1033 m_partList.back().m_velocity.at(i) = thisVel.at(i);
1034 }
1035 for(MInt i = 0; i < nDim; i++) {
1036 m_partList.back().m_accel.at(i) = 0.0;
1037 }
1038 for(MInt i = 0; i < nDim; i++) {
1039 m_partList.back().m_oldPos.at(i) = thisCoord.at(i);
1040 }
1041 for(MInt i = 0; i < nDim; i++) {
1042 m_partList.back().m_oldVel.at(i) = thisVel.at(i);
1043 }
1044 noParticles++;
1045 }
1046 readFile.close();
1047
1048 m_log << noParticles << " particles from file introduced in this solver." << endl;
1049
1050 if(m_ellipsoids) {
1051 MInt noEllipsoids = 0;
1052 ifstream readFileE;
1053 array<MFloat, nDim> thisCoordE{};
1054 array<MFloat, nDim> thisVelE{};
1055 array<MFloat, nDim> thisOrientE{};
1056 MFloat thisAspectRatio = NAN;
1057 MFloat thisSemiMinorAxis = NAN;
1058 MFloat densityE = NAN;
1059
1060 readFile.open("ellipsoids.txt", ios_base::in);
1061 if(!readFile) {
1062 mTerm(1, AT_, "Error reading ellipsoidal particle input file 'ellipsoids.txt'.");
1063 }
1064
1065 for(;;) { // read the ellipsoidal particle parameters from file:
1066 // 3D 2D
1067 // (1) (1) aspect ratio
1068 // (2) (2) semi minor axis
1069 // (3) (3) density ratio
1070 // (4-6) (4-5) x, y (, z) coordinates
1071 // (7-9) (6-7) x, y (, z) orientation
1072 // (10-12) (8-9) u, v (, w) velocities
1073 readFile >> thisAspectRatio;
1074 readFile >> thisSemiMinorAxis;
1075 if((readFile.rdstate() & ifstream::eofbit) != 0) {
1076 break;
1077 }
1078 readFile >> densityE;
1079 for(MInt i = 0; i < nDim; i++) {
1080 readFile >> thisCoordE.at(i);
1081 }
1082
1083 if(m_initializationMethod >= 3) {
1084 for(MInt i = 0; i < nDim; i++) {
1085 readFile >> thisOrientE.at(i);
1086 thisOrientE.at(i) = thisOrientE.at(i) * PI / 180.0;
1087 }
1088 }
1089 if(m_initializationMethod == 4) {
1090 for(MInt i = 0; i < nDim; i++) {
1091 readFile >> thisVelE.at(i);
1092 }
1093 }
1094
1095 // find cellId of new particle
1096 MInt thisCellId = grid().findContainingLeafCell(&thisCoordE[0]);
1097
1098 // it is possible that there is more than one valid location to eliminate the possibility
1099 // of creating duplicate particles on multiple domains check via mpi
1100 if(noDomains() > 1) {
1101 MInt spawnDomain = domainId();
1102 MInt minDomain = -1;
1103 if(thisCellId == -1) {
1104 spawnDomain = std::numeric_limits<MInt>::max();
1105 }
1106
1107 // Only create particle on the domain with the lowest Id
1108 MPI_Allreduce(&spawnDomain, &minDomain, 1, MPI_INT, MPI_MIN, mpiComm(), AT_, "spawnDomain", "minDomain");
1109
1110 if(minDomain != domainId()) {
1111 continue;
1112 }
1113 }
1114
1115 // particle is not inside this domain
1116 if(thisCellId == -1) {
1117 if(noDomains() == 1) {
1118 m_log << "Ellipsoidal particle initialisation file contains particles outside of the geometry." << endl;
1119 }
1120 continue;
1121 }
1122
1123 addEllipsoid(thisCellId, thisSemiMinorAxis, thisAspectRatio, densityE);
1124 for(MInt i = 0; i < nDim; i++) {
1125 m_partListEllipsoid.back().m_position.at(i) = thisCoordE.at(i);
1126 m_partListEllipsoid.back().m_oldPos.at(i) = thisCoordE.at(i);
1127 m_partListEllipsoid.back().m_velocity.at(i) = thisVelE.at(i);
1128 m_partListEllipsoid.back().m_oldVel.at(i) = thisVelE.at(i);
1129 m_partListEllipsoid.back().m_accel.at(i) = F0;
1130 m_partListEllipsoid.back().m_oldAccel.at(i) = F0;
1131 m_partListEllipsoid.back().m_angularVel.at(i) = F0;
1132 m_partListEllipsoid.back().m_oldAngularVel.at(i) = F0;
1133 m_partListEllipsoid.back().m_angularAccel.at(i) = F0;
1134 m_partListEllipsoid.back().m_oldAngularAccel.at(i) = F0;
1135 }
1136 if(m_initializationMethod >= 3) {
1137 maia::math::rotation2quaternion(thisOrientE.begin(), m_partListEllipsoid.back().m_quaternion.begin());
1138 } else {
1139 // Rotation to identity rotation matrix
1140 m_partListEllipsoid.back().m_quaternion.at(0) = F1; // qw
1141 m_partListEllipsoid.back().m_quaternion.at(1) = F0; // qx
1142 m_partListEllipsoid.back().m_quaternion.at(2) = F0; // qy
1143 m_partListEllipsoid.back().m_quaternion.at(3) = F0; // qz
1144 }
1145 for(MInt i = 0; i < 4; i++) {
1146 m_partListEllipsoid.back().m_oldQuaternion.at(i) = m_partListEllipsoid.back().m_quaternion.at(i);
1147 }
1148
1149 noEllipsoids++;
1150 noParticles++;
1151 }
1152 readFile.close();
1153
1154 m_log << noEllipsoids << " ellipsoidal particles from file introduced in this solver." << endl;
1155 }
1156 exchangeOffset(noParticles);
1157 break;
1158 }
1159
1160 case 5: {
1161 ASSERT(m_activePrimaryBUp, "");
1162 break;
1163 }
1164 default:
1165 mTerm(1, AT_,
1166 "LPT::initialize() switch variable 'm_particleInitializationMethod' not "
1167 "matching any case");
1168 }
1169 }
1170}
1171
1172
1173/* \brief runified run loop function call
1174 *
1175 * \date October-2020
1176 * \author Sven Berger
1177 */
1178template <MInt nDim>
1180 TRACE();
1181
1182 if(m_spawnParticles && !m_restart) {
1183 m_particleResiduum = 1.0;
1184 }
1185
1186 if(!isActive()) return;
1187
1188 if(domainId() == 0) {
1189 cerr << "Finding cartesian neighbors for LPT-solver!" << endl;
1190 }
1191 grid().findCartesianNghbIds();
1192
1193
1194 if(domainId() == 0) {
1195 initSummary();
1196 }
1197
1198 if(!m_restart) {
1199 if(domainId() == 0) {
1200 cerr << "Writing initial particle file" << endl;
1201 }
1202 writePartData();
1203 }
1204
1205 setRegridTrigger();
1206}
1207
1208
1213template <MInt nDim>
1215 TRACE();
1216
1217 ASSERT(m_freeIndices.empty(), "");
1218 m_adaptationLevel = maxUniformRefinementLevel();
1219 m_forceAdaptation = false;
1220
1221 if(!isActive()) {
1222 return;
1223 }
1224
1225 receiveFlowField();
1226 receiveVelocitySlopes();
1227 waitForSendReqs();
1228
1229 if(globalTimeStep > 0) {
1230 countParticlesInCells();
1231 }
1232
1233 // add refinement around spawnCellId!
1234 // NOTE: a_noParticlesInCell will be corrected after the adaptation!
1235 if(m_spawnCellId > -1) {
1236 a_noParticlesInCell(m_spawnCellId) += 20;
1237 if(m_ellipsoids) a_noEllipsoidsInCell(m_spawnCellId) += 20;
1238 }
1239
1240 this->exchangeData(&a_noParticlesInCell(0), 1);
1241 if(m_ellipsoids) this->exchangeData(&a_noEllipsoidsInCell(0), 1);
1242
1243
1244 // add fluxes to window-cells
1245 if(!m_nonBlockingComm) {
1246 exchangeSourceTerms();
1247 } else {
1248 sendSourceTerms();
1249 receiveSourceTerms();
1250 waitForSendReqs();
1251 }
1252 // reset fluxes on halo-cells
1253 resetSourceTerms(noInternalCells());
1254
1255 // set bndry-Cell Id for adaptation on leaf-cells and all parants
1256 // and exchange information, to set this on halo-cells and
1257 // partition level-shift ancestors in a different rank
1258 // as required for interface sensor!!
1259 for(MInt cellId = 0; cellId < a_noCells(); cellId++) {
1260 a_bndryCellId(cellId) = -1;
1261 }
1262 for(MInt id = 0; id < m_bndryCells->size(); id++) {
1263 const MInt cellId = m_bndryCells->a[id].m_cellId;
1264 a_bndryCellId(cellId) = 1;
1265 MInt parentId = c_parentId(cellId);
1266 while(parentId > -1 && parentId < a_noCells()) {
1267 a_bndryCellId(parentId) = 1;
1268 parentId = c_parentId(parentId);
1269 }
1270 }
1271 this->exchangeData(&a_bndryCellId(0), 1);
1272
1273 // remove all bndryCells
1274 if(m_wallCollisions) {
1275 m_bndryCells->resetSize(0);
1276 m_noOuterBndryCells = 0;
1277 }
1278}
1279
1284template <MInt nDim>
1285void LPT<nDim>::setSensors(std::vector<std::vector<MFloat>>& sensors,
1286 std::vector<MFloat>& sensorWeight,
1287 std::vector<std::bitset<64>>& sensorCellFlag,
1288 std::vector<MInt>& sensorSolverId) {
1289 TRACE();
1290
1291 MInt noSensors = this->m_noInitialSensors;
1292 if(globalTimeStep > 0) noSensors = this->m_noSensors;
1293
1294 // If solver is inactive the sensor arrays still need to be set to optain the
1295 // correct offsets
1296 const MInt sensorOffset = (signed)sensors.size();
1297 ASSERT(sensorOffset == 0 || grid().raw().treeb().noSolvers() > 1, "");
1298 sensors.resize(sensorOffset + noSensors, vector<MFloat>(grid().raw().m_noInternalCells, F0));
1299 sensorWeight.resize(sensorOffset + noSensors, -1);
1300 sensorCellFlag.resize(grid().raw().m_noInternalCells, sensorOffset + noSensors);
1301 sensorSolverId.resize(sensorOffset + noSensors, solverId());
1302 ASSERT(sensorOffset + noSensors < CartesianGrid<nDim>::m_maxNoSensors, "Increase bitset size!");
1303
1304 if(domainId() == 0) {
1305 cerr << "Setting " << noSensors << " sensors for LPT-Solver adaptation." << endl;
1306 }
1307
1308 for(MInt sen = 0; sen < noSensors; sen++) {
1309 sensorWeight[sensorOffset + sen] = this->m_sensorWeight[sen];
1310 }
1311
1312 ASSERT(m_freeIndices.empty(), "");
1313 m_freeIndices.clear();
1314
1315 if(!isActive()) return;
1316
1317 for(MInt sen = 0; sen < noSensors; sen++) {
1318 (this->*(this->m_sensorFnPtr[sen]))(sensors, sensorCellFlag, sensorWeight, sensorOffset, sen);
1319 }
1320
1321 /*
1322 if(m_spawnCellId > -1) {
1323 const MInt gridCellId = grid().tree().solver2grid(m_spawnCellId);
1324 const MInt partent = c_parentId(m_spawnCellId);
1325 const MInt parentGridCellId = partent > -1 ? grid().tree().solver2grid(partent) : -1;
1326 if(parentGridCellId > -1 && sensors[sensorOffset][parentGridCellId] < 1) {
1327 if(sensors[sensorOffset][gridCellId] < 1) {
1328 cerr << "Injector is not refined!" << endl;
1329 }
1330 }
1331 }
1332 */
1333}
1334
1335
1348template <MInt nDim>
1349void LPT<nDim>::sensorParticle(std::vector<std::vector<MFloat>>& sensors, std::vector<std::bitset<64>>& sensorCellFlag,
1350 std::vector<MFloat>& sensorWeight, MInt sensorOffset, MInt sen) {
1351 static constexpr MFloat particleLimit = 1;
1352
1353 std::function<MFloat(const MInt)> noPart = [&](const MInt cellId) {
1354 return static_cast<MFloat>(a_noParticlesInCell(cellId)) + static_cast<MFloat>(a_noEllipsoidsInCell(cellId));
1355 };
1356
1357 this->sensorLimit(sensors, sensorCellFlag, sensorWeight, sensorOffset, sen, noPart, particleLimit, &m_bandWidth[0],
1358 true);
1359}
1360
1361
1367template <MInt nDim>
1368void LPT<nDim>::sensorInterface(std::vector<std::vector<MFloat>>& sensors, std::vector<std::bitset<64>>& sensorCellFlag,
1369 std::vector<MFloat>& sensorWeight, MInt sensorOffset, MInt sen) {
1370 MIntScratchSpace bandWidth(maxRefinementLevel() + 1, AT_, "bandWidth");
1371 MInt range1 = 2;
1372 MInt range2 = 1;
1373 bandWidth[maxRefinementLevel() - 1] = range1;
1374 for(MInt i = maxRefinementLevel() - 2; i >= 0; i--) {
1375 bandWidth[i] = (bandWidth[i + 1] / 2) + 1 + range2;
1376 }
1377
1378 static constexpr MFloat limit = 1;
1379 std::function<MFloat(const MInt)> isBndry = [&](const MInt cellId) {
1380 return static_cast<MFloat>(a_bndryCellId(cellId));
1381 };
1382
1383 this->sensorLimit(sensors, sensorCellFlag, sensorWeight, sensorOffset, sen, isBndry, limit, &bandWidth[0], true,
1384 false);
1385}
1386
1387
1388/* \brief post Adaptation call
1389 *
1390 * \date October-2020
1391 * \author Sven Berger, updates Tim Wegmann
1392 */
1393template <MInt nDim>
1395 TRACE();
1396
1397 this->compactCells();
1398
1399 m_freeIndices.clear();
1400
1401 grid().updateOther();
1402
1403 updateDomainInfo(grid().domainId(), grid().noDomains(), grid().mpiComm(), AT_);
1404 this->checkNoHaloLayers();
1405
1406 if(!isActive()) return;
1407
1408 grid().updateLeafCellExchange();
1409 initMPI(true);
1410
1411 if(globalTimeStep < 0) {
1412 findSpawnCellId();
1413 if(m_spawnCellId > -1) {
1414 a_noParticlesInCell(m_spawnCellId) += 1;
1415 if(m_ellipsoids) a_noEllipsoidsInCell(m_spawnCellId) += 1;
1416 }
1417 }
1418
1419 this->exchangeData(&a_noParticlesInCell(0), 1);
1420 this->exchangeData(&a_bndryCellId(0), 1);
1421 if(m_ellipsoids) this->exchangeData(&a_noEllipsoidsInCell(0), 1);
1422
1423 resetSourceTerms(noInternalCells());
1424
1425 m_adaptationLevel++;
1426}
1427
1428
1433template <MInt nDim>
1435 TRACE();
1436
1437 if(!isActive()) return;
1438
1439 if(domainId() == 0) {
1440 cerr << "Finding cartesian neighbors for LPT-solver!" << endl;
1441 }
1442 grid().findCartesianNghbIds();
1443
1444 updateExchangeCells();
1445
1446 findSpawnCellId();
1447
1448 m_cellToNghbrHood.clear();
1449 m_cellToNghbrHoodInterpolation.clear();
1450
1451 // update particle cellIds
1452 // this is probably still faster than having to swap all particle-cellIds during
1453 // multiple adaptation steps!
1454 for(MInt i = 0; i < a_noParticles(); i++) {
1455 m_partList[i].m_neighborList.clear();
1456 const MInt cellId = grid().findContainingLeafCell(&m_partList[i].m_position[0]);
1457 ASSERT(cellId > -1, "");
1458 m_partList[i].m_cellId = cellId;
1459 m_partList[i].m_oldCellId = cellId;
1460 ASSERT(m_partList[i].m_cellId > 0, "ERROR: Illegal cellId after adaptation!");
1461 ASSERT(c_isLeafCell(cellId) && c_level(cellId) == maxRefinementLevel(), "");
1462 }
1463 for(MInt i = 0; i < a_noEllipsoidalParticles(); i++) {
1464 m_partListEllipsoid[i].m_neighborList.clear();
1465 const MInt cellId = grid().findContainingLeafCell(&m_partListEllipsoid[i].m_position[0]);
1466 ASSERT(cellId > -1, "");
1467 m_partListEllipsoid[i].m_cellId = cellId;
1468 m_partListEllipsoid[i].m_oldCellId = cellId;
1469 ASSERT(m_partListEllipsoid[i].m_cellId > 0, "ERROR: Illegal cellId after adaptation!");
1470 ASSERT(c_isLeafCell(cellId) && c_level(cellId) == maxRefinementLevel(), "");
1471 }
1472
1473 countParticlesInCells();
1474 setRegridTrigger();
1475
1476 updateFluidFraction();
1477
1478 checkParticles();
1479 checkCells();
1480
1481 // unset bndry-Cell id after adaptation
1482 for(MInt cellId = 0; cellId < a_noCells(); cellId++) {
1483 a_bndryCellId(cellId) = -1;
1484 }
1485
1486 // LPT-statistics
1487 MInt noCells = a_noCells();
1488 MInt minCells = noCells;
1489 MInt maxCells = noCells;
1490 MInt noPart = a_noParticles();
1491 MInt minParts = noPart;
1492 MInt maxParts = noPart;
1493 MInt noEllips = a_noEllipsoidalParticles();
1494 MInt minEllips = noEllips;
1495 MInt maxEllips = noEllips;
1496
1497 MPI_Allreduce(MPI_IN_PLACE, &noCells, 1, MPI_INT, MPI_SUM, mpiComm(), AT_, "INPLACE", "noCells");
1498 MPI_Allreduce(MPI_IN_PLACE, &noPart, 1, MPI_INT, MPI_SUM, mpiComm(), AT_, "INPLACE", "noPart");
1499 MPI_Allreduce(MPI_IN_PLACE, &maxCells, 1, MPI_INT, MPI_MAX, mpiComm(), AT_, "INPLACE", "maxCells");
1500 MPI_Allreduce(MPI_IN_PLACE, &minCells, 1, MPI_INT, MPI_MIN, mpiComm(), AT_, "INPLACE", "minCells");
1501 MPI_Allreduce(MPI_IN_PLACE, &maxParts, 1, MPI_INT, MPI_MAX, mpiComm(), AT_, "INPLACE", "maxParts");
1502 MPI_Allreduce(MPI_IN_PLACE, &minParts, 1, MPI_INT, MPI_MIN, mpiComm(), AT_, "INPLACE", "minParts");
1503 if(m_ellipsoids) {
1504 MPI_Allreduce(MPI_IN_PLACE, &noEllips, 1, MPI_INT, MPI_SUM, mpiComm(), AT_, "INPLACE", "noEllips");
1505 MPI_Allreduce(MPI_IN_PLACE, &maxEllips, 1, MPI_INT, MPI_MAX, mpiComm(), AT_, "INPLACE", "maxEllips");
1506 MPI_Allreduce(MPI_IN_PLACE, &minEllips, 1, MPI_INT, MPI_MIN, mpiComm(), AT_, "INPLACE", "minEllips");
1507 }
1508
1509 if(domainId() == 0) {
1510 cerr << "LPT cell-statistics : " << minCells << " - " << maxCells << " avg " << noCells / noDomains() << endl;
1511 cerr << "LPT particle-statistics : " << minParts << " - " << maxParts << " avg " << noPart / noDomains() << endl;
1512 if(m_ellipsoids)
1513 cerr << "LPT ellipsoid-statistics : " << minEllips << " - " << maxEllips << " avg " << noEllips / noDomains()
1514 << endl;
1515 }
1516}
1517
1518template <MInt nDim>
1520 grid().resizeGridMap(m_cells.size());
1521}
1522
1527template <MInt nDim>
1528void LPT<nDim>::swapProxy(const MInt cellId0, const MInt cellId1) {
1529 grid().swapGridIds(cellId0, cellId1);
1530}
1531
1532
1537template <MInt nDim>
1538void LPT<nDim>::refineCell(const MInt gridCellId) {
1539 const MInt solverCellId = grid().tree().grid2solver(gridCellId);
1540
1541 ASSERT(solverCellId > -1 && solverCellId < m_cells.size(), "solverCellId is: " << solverCellId);
1542
1543 MInt noNewChildren = 0;
1544
1545 for(MInt child = 0; child < grid().m_maxNoChilds; child++) {
1546 const MInt gridChildId = grid().raw().treeb().child(gridCellId, child);
1547 if(gridChildId == -1) continue;
1548
1549 // Skip if cell is a partition level ancestor and its child was not newly created
1550 if(!grid().raw().a_hasProperty(gridChildId, Cell::WasNewlyCreated)
1551 && grid().raw().a_hasProperty(gridCellId, Cell::IsPartLvlAncestor)) {
1552 continue;
1553 }
1554
1555 // If solver is inactive all cells musst be halo cells!
1556 if(!isActive()) ASSERT(grid().raw().a_isHalo(gridChildId), "");
1557 // If child exists in grid but is not located inside solver geometry
1558 if(!grid().solverFlag(gridChildId, solverId())) continue;
1559
1560 const MInt solverChildId = this->createCellId(gridChildId);
1561 noNewChildren++;
1562
1563 for(MInt n = 0; n < nDim; n++) {
1564 a_fluidVelocity(solverChildId, n) = 0.0;
1565 }
1566 a_fluidDensity(solverChildId) = 0.0;
1567 a_fluidPressure(solverChildId) = 0.0;
1568 a_fluidTemperature(solverChildId) = 0.0;
1569
1570 if(m_evaporation) {
1571 a_fluidSpecies(solverChildId) = 0.0;
1572 }
1573 a_isValidCell(solverChildId) = true;
1574
1575 a_bndryCellId(solverChildId) = a_bndryCellId(solverCellId);
1576 if(m_ellipsoids) {
1577 for(MInt varId = 0; varId < nDim; varId++) {
1578 for(MInt dir = 0; dir < nDim; dir++) {
1579 a_velocitySlope(solverChildId, varId, dir) = F0;
1580 }
1581 }
1582 }
1583 }
1584
1585 if(noNewChildren == 0) return;
1586
1587 const MInt noParticles = a_noParticlesInCell(solverCellId);
1588 const MInt noEllipsoids = a_noEllipsoidsInCell(solverCellId);
1589
1590 //NOTE: setting an estimate of the number particles in the cell
1591 // is necessary as this is used to set the sensors for further refinement!
1592 // the true value will be updated in finalizeAdaptation!
1593 const MInt noParticlesEach = noParticles / noNewChildren;
1594 const MInt noEllipsoidsEach = noEllipsoids / noNewChildren;
1595
1596 for(MInt child = 0; child < c_noChildren(solverCellId) -1; child++) {
1597 const MInt childId = c_childId(solverCellId,child);
1598 if(childId < 0) continue;
1599 a_noParticlesInCell(childId) = noParticles > 0 ? noParticlesEach : 0;
1600 a_noEllipsoidsInCell(childId) = noEllipsoids > 0 ? noEllipsoidsEach : 0;
1601
1602 // average of solver parent
1603 if(m_momentumCoupling) {
1604 for(MInt i = 0; i < nDim; i++) {
1605 a_momentumFlux(childId, i) = a_momentumFlux(solverCellId, i) / noNewChildren;
1606 }
1607 a_workFlux(childId) = a_workFlux(solverCellId) / noNewChildren;
1608 }
1609 if(m_heatCoupling) {
1610 a_heatFlux(childId) = a_heatFlux(solverCellId) / noNewChildren;
1611 }
1612 if(m_massCoupling) {
1613 a_massFlux(childId) = a_massFlux(solverCellId) / noNewChildren;
1614 }
1615 }
1616 const MInt remainingParticles = noParticles - ((c_noChildren(solverCellId) - 1) * noParticlesEach);
1617 const MInt remainingEllipsoids = noEllipsoids - ((c_noChildren(solverCellId) - 1) * noEllipsoidsEach);
1618
1619 const MInt remainingChild = c_childId(solverCellId, c_noChildren(solverCellId) - 1);
1620 if(remainingChild > 0) {
1621 a_noParticlesInCell(remainingChild) = noParticles > 0 ? remainingParticles : 0;
1622 a_noEllipsoidsInCell(remainingChild) = noEllipsoids > 0 ? remainingEllipsoids : 0;
1623
1624 if(m_momentumCoupling) {
1625 for(MInt i = 0; i < nDim; i++) {
1626 a_momentumFlux(remainingChild, i) = a_momentumFlux(solverCellId, i) / noNewChildren;
1627 }
1628 a_workFlux(remainingChild) = a_workFlux(solverCellId) / noNewChildren;
1629 }
1630 if(m_heatCoupling) {
1631 a_heatFlux(remainingChild) = a_heatFlux(solverCellId) / noNewChildren;
1632 }
1633 if(m_massCoupling) {
1634 a_massFlux(remainingChild) = a_massFlux(solverCellId) / noNewChildren;
1635 }
1636 }
1637
1638 // reset values on new non-leaf cell
1639 if(noNewChildren > 0 ){
1640 a_noParticlesInCell(solverCellId) = 0;
1641 a_noEllipsoidsInCell(solverCellId) = 0;
1642
1643 if(m_momentumCoupling) {
1644 for(MInt i = 0; i < nDim; i++) {
1645 a_momentumFlux(solverCellId, i) = 0;
1646 }
1647 a_workFlux(solverCellId) = 0;
1648 }
1649 if(m_heatCoupling) {
1650 a_heatFlux(solverCellId) = 0;
1651 }
1652 if(m_massCoupling) {
1653 a_massFlux(solverCellId) = 0;
1654 }
1655 }
1656}
1657
1662template <MInt nDim>
1663void LPT<nDim>::removeChilds(const MInt gridCellId) {
1664 // If solver is inactive cell musst never be a internal cell
1665 if(!isActive()) {
1666 ASSERT(grid().raw().a_isHalo(gridCellId), "");
1667 }
1668
1669 const MInt solverCellId = grid().tree().grid2solver(gridCellId);
1670
1671 ASSERT(solverCellId > -1 && solverCellId < m_cells.size(), "solverCellId is: " << solverCellId);
1672
1673 // reset values of new leaf cell
1674 a_noParticlesInCell(solverCellId) = 0;
1675 a_noEllipsoidsInCell(solverCellId) = 0;
1676 if(m_momentumCoupling) {
1677 for(MInt i = 0; i < nDim; i++) {
1678 a_momentumFlux(solverCellId, i) = 0;
1679 }
1680 a_workFlux(solverCellId) = 0;
1681 }
1682 if(m_heatCoupling) {
1683 a_heatFlux(solverCellId) = 0;
1684 }
1685 if(m_massCoupling) {
1686 a_massFlux(solverCellId) = 0;
1687 }
1688
1689 for(MInt c = 0; c < grid().m_maxNoChilds; c++) {
1690 const MInt childId = c_childId(solverCellId, c);
1691 if(childId < 0) continue;
1692
1693 // sum values of childs which will be removed
1694 a_noParticlesInCell(solverCellId) += a_noParticlesInCell(childId);
1695 a_noEllipsoidsInCell(solverCellId) += a_noEllipsoidsInCell(childId);
1696 if(m_momentumCoupling) {
1697 for(MInt i = 0; i < nDim; i++) {
1698 a_momentumFlux(solverCellId, i) += a_momentumFlux(childId, i);
1699 }
1700 a_workFlux(solverCellId) += a_workFlux(childId);
1701 }
1702 if(m_heatCoupling) {
1703 a_heatFlux(solverCellId) += a_heatFlux(childId);
1704 }
1705 if(m_massCoupling) {
1706 a_massFlux(solverCellId) += a_massFlux(childId);
1707 }
1708
1709 this->removeCellId(childId);
1710 }
1711}
1712
1716template <MInt nDim>
1717void LPT<nDim>::removeCell(const MInt gridCellId) {
1718 // If solver is inactive cell musst never be a internal cell
1719 if(!isActive()) {
1720 ASSERT(grid().raw().a_isHalo(gridCellId), "");
1721 }
1722
1723 const MInt solverCellId = grid().tree().grid2solver(gridCellId);
1724
1725 ASSERT(gridCellId > -1 && gridCellId < grid().raw().treeb().size() && solverCellId > -1
1726 && solverCellId < m_cells.size() && grid().tree().solver2grid(solverCellId) == gridCellId,
1727 "");
1728 ASSERT(c_noChildren(solverCellId) == 0, "");
1729 this->removeCellId(solverCellId);
1730}
1731
1732
1739template <MInt nDim>
1740MInt LPT<nDim>::cellOutside(const MFloat* coords, const MInt level, const MInt gridCellId) {
1741 // currently all cells with the refineFlag are keep during adaptation!
1742 return -1;
1743
1744 std::ignore = gridCellId;
1745 std::ignore = coords[0];
1746 std::ignore = level;
1747
1748 /*
1749 static constexpr MInt cornerIndices[8][3] = {{-1, -1, -1}, {1, -1, -1}, {-1, 1, -1}, {1, 1, -1},
1750 {-1, -1, 1}, {1, -1, 1}, {-1, 1, 1}, {1, 1, 1}};
1751 static constexpr const MInt noCorners = (nDim == 2) ? 4 : 8;
1752
1753 MFloat corner[3] = {0, 0, 0};
1754 MBool outside = true;
1755 MFloat cellHalfLength = F1B2 * c_cellLengthAtLevel(level);
1756
1757
1758 for(MInt i = 0; i < noCorners; i++) {
1759 for(MInt dim = 0; dim < nDim; dim++) {
1760 corner[dim] = coords[dim] + cornerIndices[i][dim] * cellHalfLength;
1761 }
1762 IF_CONSTEXPR(nDim == 2) {
1763 if(!m_geometry->pointIsInside(corner)) outside = false;
1764 // pointIsInside == true if Point is outside fluid domain
1765 } else {
1766 if(!m_geometry->pointIsInside2(corner)) outside = false;
1767 // pointIsInside == true if Point is outside fluid domain
1768 }
1769 }
1770
1771 return outside;
1772 */
1773}
1774
1775
1781template <MInt nDim>
1782void LPT<nDim>::swapCells(const MInt cellId0, const MInt cellId1) {
1783 std::swap(m_cells.properties(cellId1), m_cells.properties(cellId0));
1784
1785 std::swap(m_cells.volumeFraction(cellId1), m_cells.volumeFraction(cellId0));
1786 std::swap(m_cells.noParticles(cellId1), m_cells.noParticles(cellId0));
1787 std::swap(m_cells.noEllipsoids(cellId1), m_cells.noEllipsoids(cellId0));
1788
1789 for(MInt n = 0; n < PV.noVars(); n++) {
1790 std::swap(m_cells.fluidVariable(cellId1, n), m_cells.fluidVariable(cellId0, n));
1791 }
1792
1793 if(m_evaporation) {
1794 std::swap(m_cells.fluidSpecies(cellId1), m_cells.fluidSpecies(cellId0));
1795 }
1796
1797 if(m_massCoupling) {
1798 std::swap(m_cells.massFlux(cellId1), m_cells.massFlux(cellId0));
1799 }
1800 if(m_heatCoupling) {
1801 std::swap(m_cells.heatFlux(cellId1), m_cells.heatFlux(cellId0));
1802 }
1803 if(m_momentumCoupling) {
1804 for(MInt i = 0; i < nDim; i++) {
1805 std::swap(m_cells.momentumFlux(cellId1, i), m_cells.momentumFlux(cellId0, i));
1806 }
1807 std::swap(m_cells.workFlux(cellId1), m_cells.workFlux(cellId0));
1808 }
1809 std::swap(m_cells.bndryCellId(cellId1), m_cells.bndryCellId(cellId0));
1810
1811 // only ellipsoids require velocity slopes
1812 if(m_ellipsoids) {
1813 for(MInt varId = 0; varId < nDim; varId++) {
1814 for(MInt dim = 0; dim < nDim; dim++) {
1815 std::swap(m_cells.velocitySlope(cellId1, varId, dim), m_cells.velocitySlope(cellId0, varId, dim));
1816 }
1817 }
1818 }
1819}
1820
1821
1826template <MInt nDim>
1827void LPT<nDim>::setCellWeights(MFloat* solverCellWeight) {
1828 TRACE();
1829 ASSERT(isActive(), "solver is not active");
1830
1831 countParticlesInCells();
1832
1833 const MInt noCellsGrid = grid().raw().treeb().size();
1834 const MInt offset = noCellsGrid * solverId();
1835
1836 for(MInt cellId = 0; cellId < noInternalCells(); cellId++) {
1837 const MInt gridCellId = grid().tree().solver2grid(cellId);
1838 const MInt id = gridCellId + offset;
1839 solverCellWeight[id] = m_weightBaseCell * m_weightMulitSolverFactor;
1840 if(!c_isLeafCell(cellId)) continue;
1841 solverCellWeight[id] = m_weightLeafCell * m_weightMulitSolverFactor;
1842 if(a_noParticlesInCell(cellId) == 0 && a_noEllipsoidsInCell(cellId) == 0) continue;
1843 solverCellWeight[id] =
1844 m_weightMulitSolverFactor
1845 * (m_weightParticleCell + (a_noParticlesInCell(cellId) + a_noEllipsoidsInCell(cellId)) * m_weightParticle);
1846 }
1847
1848 // additionally increase weight at spawncellId
1849 if(m_engineSetup && m_sprayModel != nullptr && m_spawnCellId > -1) {
1850 const MInt gridCellId = grid().tree().solver2grid(m_spawnCellId);
1851 const MInt id = gridCellId + offset;
1852 solverCellWeight[id] += m_weightSpawnCell * m_weightMulitSolverFactor;
1853 }
1854}
1855
1860template <MInt nDim>
1861void LPT<nDim>::getSolverTimings(std::vector<std::pair<MString, MFloat>>& solverTimings,
1862 const MBool NotUsed(allTimings)) {
1863 TRACE();
1864
1865 const MString namePrefix = "b" + std::to_string(solverId()) + "_";
1866
1867 const MFloat load = returnLoadRecord();
1868 const MFloat idle = returnIdleRecord();
1869
1870 solverTimings.emplace_back(namePrefix + "loadLptSolver", load);
1871 solverTimings.emplace_back(namePrefix + "idleLptSolver", idle);
1872
1873#ifdef MAIA_TIMER_FUNCTION
1874 solverTimings.emplace_back(namePrefix + "timeIntegration", RETURN_TIMER_TIME(m_timers[Timers::TimeInt]));
1875 solverTimings.emplace_back(namePrefix + "motion", RETURN_TIMER_TIME(m_timers[Timers::Motion]));
1876 solverTimings.emplace_back(namePrefix + "energy", RETURN_TIMER_TIME(m_timers[Timers::Energy]));
1877 solverTimings.emplace_back(namePrefix + "injection", RETURN_TIMER_TIME(m_timers[Timers::Injection]));
1878 solverTimings.emplace_back(namePrefix + "secBreakUp", RETURN_TIMER_TIME(m_timers[Timers::Breakup]));
1879 solverTimings.emplace_back(namePrefix + "wall", RETURN_TIMER_TIME(m_timers[Timers::Wall]));
1880 solverTimings.emplace_back(namePrefix + "source", RETURN_TIMER_TIME(m_timers[Timers::SourceTerms]));
1881 solverTimings.emplace_back(namePrefix + "regridExc", RETURN_TIMER_TIME(m_timers[Timers::Exchange2]));
1882 solverTimings.emplace_back(namePrefix + "exchange", RETURN_TIMER_TIME(m_timers[Timers::Exchange]));
1883#endif
1884}
1885
1887template <MInt nDim>
1888void LPT<nDim>::getDefaultWeights(MFloat* weights, std::vector<MString>& names) const {
1889 TRACE();
1890
1891 weights[0] = 0.1;
1892 names[0] = "lpt_cell";
1893 MInt count = 1;
1894
1895 weights[1] = 0.9;
1896 names[1] = "lpt_particleCell";
1897 count++;
1898
1899 weights[2] = 0.1;
1900 names[2] = "lpt_particle";
1901 count++;
1902
1903 if(m_weightSourceCells) {
1904 weights[3] = 0.01;
1905 names[3] = "lpt_source";
1906 count++;
1907 }
1908
1909 if(noLoadTypes() != count) {
1910 TERMM(1, "Count does not match noLoadTypes.");
1911 }
1912}
1913
1918template <MInt nDim>
1920 if(m_limitWeights) {
1921 weights[0] = mMax(weights[0], 0.01 * weights[1]);
1922 }
1923}
1924
1925
1933template <MInt nDim>
1934void LPT<nDim>::getLoadQuantities(MInt* const loadQuantities) const {
1935 TRACE();
1936
1937 // Nothing to do if solver is not active
1938 if(!isActive()) {
1939 return;
1940 }
1941
1942 // reset
1943 for(MInt type = 0; type < noLoadTypes(); type++) {
1944 loadQuantities[type] = 0;
1945 }
1946
1947 MInt noLeafCells = 0;
1948 for(MInt cellId = 0; cellId < grid().noInternalCells(); cellId++) {
1949 if(c_isLeafCell(cellId) && a_isValidCell(cellId)) {
1950 noLeafCells++;
1951 }
1952 }
1953
1954 loadQuantities[0] = noLeafCells;
1955
1956 MInt noCellsWithParticles = 0;
1957 MInt noCellsWithSources = 0;
1958
1959 for(MInt cellId = 0; cellId < grid().noInternalCells(); cellId++) {
1960 if(a_noParticlesInCell(cellId) == 0 && a_noEllipsoidsInCell(cellId) == 0) {
1961 if(m_massCoupling && a_massFlux(cellId) < 0.0) {
1962 noCellsWithSources++;
1963 }
1964 continue;
1965 }
1966 noCellsWithParticles++;
1967 }
1968 loadQuantities[1] = noCellsWithParticles;
1969 loadQuantities[2] = a_noSphericalParticles() + a_noEllipsoidalParticles();
1970
1971
1972 if(m_weightSourceCells) {
1973 loadQuantities[3] = noCellsWithSources;
1974 }
1975}
1976
1977
1987template <MInt nDim>
1988MFloat LPT<nDim>::getCellLoad(const MInt gridCellId, const MFloat* const weights) const {
1989 TRACE();
1990 ASSERT(isActive(), "solver is not active");
1991
1992 // Convert to solver cell id and check
1993 const MInt cellId = grid().tree().grid2solver(gridCellId);
1994 if(cellId < 0) {
1995 return 0;
1996 }
1997
1998 if(cellId < 0 || cellId >= grid().noInternalCells()) {
1999 TERMM(1, "The given cell id is invalid.");
2000 }
2001
2002 // Default cell load
2003 MFloat cellLoad = 0.0;
2004 if(c_isLeafCell(cellId) && a_isValidCell(cellId)) {
2005 cellLoad = weights[0];
2006 }
2007
2008 if(a_noParticlesInCell(cellId) > 0 || a_noEllipsoidsInCell(cellId) > 0) {
2009 cellLoad = weights[1];
2010 cellLoad += a_noParticlesInCell(cellId) * weights[2];
2011 } else if(m_weightSourceCells) {
2012 if(m_massCoupling && a_massFlux(cellId) < 0.0) {
2013 cellLoad = weights[3];
2014 }
2015 }
2016
2017 if(m_sprayModel != nullptr && cellId == m_spawnCellId) {
2018 cellLoad += 5 * weights[1];
2019 }
2020
2021
2022 return cellLoad;
2023}
2024
2025
2030template <MInt nDim>
2031void LPT<nDim>::getDomainDecompositionInformation(std::vector<std::pair<MString, MInt>>& domainInfo) {
2032 TRACE();
2033
2034 const MString namePrefix = "b" + std::to_string(solverId()) + "_";
2035
2036 const MInt noInternalCells = isActive() ? grid().noInternalCells() : 0;
2037
2038 domainInfo.emplace_back(namePrefix + "noLptCells", noInternalCells);
2039
2040 // Number of Ls-Band-Cells
2041 MInt noCellsWithParticles = 0;
2042 MInt noLeafCells = 0;
2043 MInt noParticles = 0;
2044
2045 if(isActive()) {
2046 noParticles = a_noParticles();
2047 for(MInt cellId = 0; cellId < grid().noInternalCells(); cellId++) {
2048 if(a_noParticlesInCell(cellId) > 0 || a_noEllipsoidsInCell(cellId) > 0) {
2049 noCellsWithParticles++;
2050 }
2051 if(c_isLeafCell(cellId) && a_isValidCell(cellId)) {
2052 noLeafCells++;
2053 }
2054 }
2055 }
2056
2057 domainInfo.emplace_back(namePrefix + "noLptLeafCells", noLeafCells);
2058 domainInfo.emplace_back(namePrefix + "noLptParticleCells", noCellsWithParticles);
2059 domainInfo.emplace_back(namePrefix + "noLptParticles", noParticles);
2060}
2061
2062
2068template <MInt nDim>
2070 TRACE();
2071
2072 RECORD_TIMER_START(m_timers[Timers::PreTime]);
2073
2074 // Exchange flow field wich has been set by one or more flow solver via the coupler(s)
2075 // exchangeData(&a_fluidVariable(0, 0), nDim+2);
2076
2077 // m_partCellMapCurrent = false;
2078
2079 // advanceTimeStep
2080 m_time += m_timeStep;
2081 ASSERT(m_timeStep > MFloatEps, "TS not set!");
2082
2083
2084 m_timeStepOld = m_timeStep;
2085 m_timeStepUpdated = false;
2086 m_sumEvapMass = 0;
2087 m_noSendParticles = 0;
2088
2089 if(m_skipLPT) {
2090 return;
2091 }
2092
2093 resetSourceTerms(0);
2094
2095 // reset creation time of all particles
2096 for(MInt i = 0; i < a_noParticles(); i++) {
2097 m_partList[i].m_creationTime = 0.0;
2098 }
2099 for(MInt i = 0; i < a_noEllipsoidalParticles(); i++) {
2100 m_partListEllipsoid[i].m_creationTime = 0.0;
2101 }
2102
2103 RECORD_TIMER_STOP(m_timers[Timers::PreTime]);
2104}
2105
2111template <MInt nDim>
2113 TRACE();
2114
2115 for(MInt cellId = offset; cellId < a_noCells(); cellId++) {
2116 if(m_momentumCoupling) {
2117 for(MInt i = 0; i < nDim; i++) {
2118 a_momentumFlux(cellId, i) = 0;
2119 }
2120 a_workFlux(cellId) = 0;
2121 }
2122 if(m_heatCoupling) {
2123 a_heatFlux(cellId) = 0;
2124 }
2125 if(m_massCoupling) {
2126 a_massFlux(cellId) = 0;
2127 }
2128 }
2129}
2130
2138template <MInt nDim>
2140 TRACE();
2141
2142 if((m_partList.empty() || m_partListEllipsoid.empty()) && !m_spawnParticles && !m_activePrimaryBUp
2143 && globalTimeStep == 0 && noDomains() == 1) {
2144 return true;
2145 }
2146
2147 // To set particle release Time for particles in isoTropic Turbulence
2148 // Release Timestep is determined by a Skewness of 0.44 of fluid velocity
2149 if(m_skewnessTimeStep != 0 && globalTimeStep < m_skewnessTimeStep) return true;
2150
2151 RECORD_TIMER_START(m_timers[Timers::TimeInt]);
2152
2153 // apply arbitrary LPT load to test&validate interleaved coupling
2154 // remaining for documentation and testing purposes
2155 if(m_sleepLPT > 0.0) {
2156 cerr0 << "Sleeping LPT-solver for " << m_sleepLPT << " milli-seconds!" << endl;
2157 std::this_thread::sleep_for(std::chrono::milliseconds(m_sleepLPT));
2158 }
2159
2160 if(m_skipLPT) {
2161 cerr0 << "Skipping LPT execution!" << endl;
2162 return true;
2163 }
2164
2165
2166 MBool completed = false;
2167 switch(m_noSolutionSteps) {
2168 case 1: {
2169 completed = oneStageSolutionStep();
2170 break;
2171 }
2172 case 5: {
2173 completed = fiveStageSolutionStep();
2174 break;
2175 }
2176 default: {
2177 mTerm(1, AT_, "SolutionStep method not implemented in LPT!");
2178 break;
2179 }
2180 }
2181
2182 RECORD_TIMER_STOP(m_timers[Timers::TimeInt]);
2183 return completed;
2184}
2185
2186
2193template <MInt nDim>
2195 NEW_TIMER_GROUP_STATIC(t_part, "particle timestep");
2196 NEW_TIMER_STATIC(partTS, "particle timestep", t_part);
2197 NEW_SUB_TIMER_STATIC(spawn, "spawn", partTS);
2198 NEW_SUB_TIMER_STATIC(motion, "motion", partTS);
2199 NEW_SUB_TIMER_STATIC(transfer, "transfer", partTS);
2200 NEW_SUB_TIMER_STATIC(evap, "evaporation", partTS);
2201 NEW_SUB_TIMER_STATIC(couplingTerms, "couplingTerms", partTS);
2202 NEW_SUB_TIMER_STATIC(collision1, "wall-collision", partTS);
2203 NEW_SUB_TIMER_STATIC(primBU, "primaryBreakUp", partTS);
2204 NEW_SUB_TIMER_STATIC(secBU, "secondaryBreakUp", partTS);
2205 NEW_SUB_TIMER_STATIC(respawn, "particle-respawn", partTS);
2206 NEW_SUB_TIMER_STATIC(deleteP, "Remove-particles", partTS);
2207
2208 RECORD_TIMER_START(partTS);
2209
2210 m_solutionStep++;
2211
2212 switch(m_solutionStep) {
2213 case 1: {
2214 // blocking injection step
2215 RECORD_TIMER_START(primBU);
2216 // some debug checks
2217 checkParticles();
2218 checkCells();
2219
2220 if(m_activePrimaryBUp) {
2221 sprayInjection();
2222 }
2223 RECORD_TIMER_STOP(primBU);
2224
2225 RECORD_TIMER_STOP(partTS);
2226 return false;
2227 break;
2228 }
2229 case 2: {
2230 // receive flow field data from previous TS
2231 receiveFlowField();
2232 // if ellipsoids are activated also receive velocity slopes from previous TS
2233 receiveVelocitySlopes();
2234
2235 if(m_nonBlockingComm && m_activePrimaryBUp) {
2236 RECORD_TIMER_START(transfer);
2237 receiveParticles<true>();
2238 m_primaryExchange = false;
2239 RECORD_TIMER_STOP(transfer);
2240 }
2241
2242 // fully non-blocking movement of particles
2243 RECORD_TIMER_START(motion);
2244 motionEquation(0);
2245 RECORD_TIMER_STOP(motion);
2246
2247 RECORD_TIMER_START(collision1);
2248 wallCollision();
2249 RECORD_TIMER_STOP(collision1);
2250
2251 if(m_nonBlockingComm) {
2252 RECORD_TIMER_START(transfer);
2253 exchangeParticles(false, 0);
2254 RECORD_TIMER_STOP(transfer);
2255 }
2256
2257 RECORD_TIMER_STOP(partTS);
2258 return false;
2259 break;
2260 }
2261 case 3: {
2262 if(!m_nonBlockingComm) {
2263 // blocking exchange step
2264 RECORD_TIMER_START(transfer);
2265 exchangeParticles(false, 0);
2266 RECORD_TIMER_STOP(transfer);
2267 } else {
2268 // non-blocking receive
2269 RECORD_TIMER_START(transfer);
2270 receiveParticles<false>();
2271 RECORD_TIMER_STOP(transfer);
2272 }
2273
2274 // non-blocking eveporation
2275 RECORD_TIMER_START(evap);
2276 evaporation(0);
2277 RECORD_TIMER_STOP(evap);
2278
2279 if(m_nonBlockingComm) {
2280 RECORD_TIMER_START(couplingTerms);
2281 coupling(0);
2282 RECORD_TIMER_STOP(couplingTerms);
2283 sendSourceTerms();
2284
2285 if(this->m_adaptation && this->m_noSensors > 0) {
2286 checkRegridTrigger();
2287 }
2288 }
2289
2290 RECORD_TIMER_STOP(partTS);
2291
2292 return false;
2293 break;
2294 }
2295 case 4: {
2296 if(!m_nonBlockingComm) {
2297 // non-blocking computation of source-terms
2298 RECORD_TIMER_START(couplingTerms);
2299 coupling(0);
2300 RECORD_TIMER_STOP(couplingTerms);
2301 } else {
2302 // wait and receive source terms
2303 RECORD_TIMER_START(transfer);
2304 receiveSourceTerms();
2305 RECORD_TIMER_STOP(transfer);
2306 }
2307
2308 RECORD_TIMER_STOP(partTS);
2309 return false;
2310 break;
2311 }
2312 case 5: {
2313 // non-blocking step unless using outdated particle spawn/respawn or particle-collision
2314 // only particle data-structure related
2315 RECORD_TIMER_START(secBU);
2316 RECORD_TIMER_START(m_timers[Timers::Breakup]);
2317 if(m_activeSecondaryBUp) {
2318 m_sprayModel->secondaryBreakUp(m_timeStep);
2319 }
2320 RECORD_TIMER_STOP(m_timers[Timers::Breakup]);
2321 RECORD_TIMER_STOP(secBU);
2322
2323 // check for collided particles to be copied to other ranks
2324 RECORD_TIMER_START(transfer);
2325 if(m_collisions < 5 && m_collisions > 0) {
2326 exchangeParticles(true, 0);
2327 }
2328 RECORD_TIMER_STOP(transfer);
2329
2330 RECORD_TIMER_START(spawn);
2331 if(m_spawnParticles) {
2332 spawnTimeStep();
2333 }
2334 RECORD_TIMER_STOP(spawn);
2335
2336 RECORD_TIMER_START(deleteP);
2337 removeInvalidParticles(false);
2338 RECORD_TIMER_STOP(deleteP);
2339
2340 RECORD_TIMER_START(respawn);
2341 if(m_respawn) {
2342 particleRespawn();
2343 }
2344 RECORD_TIMER_STOP(respawn);
2345
2346 RECORD_TIMER_START(deleteP);
2347 updateFluidFraction();
2348
2349 if(a_timeStepComputationInterval() > 0 && globalTimeStep % a_timeStepComputationInterval() == 0) {
2350 forceTimeStep(computeTimeStep());
2351 }
2352
2353 // gather post-processing data
2354 if(m_sprayModel != nullptr) {
2355 const MInt count = m_sprayModel->m_injDataSize;
2356 vector<MFloat> tmp(count + m_addInjDataCnt);
2357 for(MInt i = 0; i < count; i++) {
2358 tmp[i] = m_sprayModel->m_injData[i];
2359 }
2360 tmp[count] = m_sumEvapMass;
2361 tmp[count + 1] = m_sprayModel->m_noRTsecBreakUp;
2362 tmp[count + 2] = m_sprayModel->m_noKHsecBreakUp;
2363 tmp[count + 3] = m_noSendParticles;
2364
2365 m_injData.insert(make_pair(globalTimeStep, tmp));
2366 }
2367
2368 RECORD_TIMER_STOP(deleteP);
2369 RECORD_TIMER_STOP(partTS);
2370
2371 return true;
2372 break;
2373 }
2374 default: {
2375 mTerm(1, AT_, "Invalid solution Step!");
2376 break;
2377 }
2378 }
2379}
2380
2386template <MInt nDim>
2388 NEW_TIMER_GROUP_STATIC(t_part, "particle timestep");
2389 NEW_TIMER_STATIC(partTS, "particle timestep", t_part);
2390 NEW_SUB_TIMER_STATIC(spawn, "spawn", partTS);
2391 NEW_SUB_TIMER_STATIC(motion, "motion", partTS);
2392 NEW_SUB_TIMER_STATIC(transfer, "transfer", partTS);
2393 NEW_SUB_TIMER_STATIC(evap, "evaporation", partTS);
2394 NEW_SUB_TIMER_STATIC(couplingTerms, "couplingTerms", partTS);
2395 NEW_SUB_TIMER_STATIC(collision1, "wall-collision", partTS);
2396 NEW_SUB_TIMER_STATIC(primBU, "primaryBreakUp", partTS);
2397 NEW_SUB_TIMER_STATIC(secBU, "secondaryBreakUp", partTS);
2398 NEW_SUB_TIMER_STATIC(respawn, "particle-respawn", partTS);
2399 NEW_SUB_TIMER_STATIC(deleteP, "Remove-particles", partTS);
2400
2401 RECORD_TIMER_START(partTS);
2402
2403 ASSERT(!m_nonBlockingComm, "");
2404
2405 // some debug checks
2406 checkParticles();
2407 checkCells();
2408
2409 RECORD_TIMER_START(primBU);
2410 if(m_activePrimaryBUp) {
2411 sprayInjection();
2412 }
2413 RECORD_TIMER_STOP(primBU);
2414
2415 // receive flow field data from previous TS
2416 receiveFlowField();
2417 // if ellipsoids are activated also receive velocity slopes from previous TS
2418 receiveVelocitySlopes();
2419
2420 // move particles
2421 RECORD_TIMER_START(motion);
2422 motionEquation(0);
2423 RECORD_TIMER_STOP(motion);
2424
2425 RECORD_TIMER_START(collision1);
2426 wallCollision();
2427 RECORD_TIMER_STOP(collision1);
2428
2429 RECORD_TIMER_START(transfer);
2430 exchangeParticles(false, 0);
2431 RECORD_TIMER_STOP(transfer);
2432
2433 RECORD_TIMER_START(evap);
2434 evaporation(0);
2435 RECORD_TIMER_STOP(evap);
2436
2437 RECORD_TIMER_START(couplingTerms);
2438 coupling(0);
2439 RECORD_TIMER_STOP(couplingTerms);
2440
2441 RECORD_TIMER_START(secBU);
2442 RECORD_TIMER_START(m_timers[Timers::Breakup]);
2443 if(m_activeSecondaryBUp) {
2444 m_sprayModel->secondaryBreakUp(m_timeStep);
2445 }
2446 RECORD_TIMER_STOP(m_timers[Timers::Breakup]);
2447 RECORD_TIMER_STOP(secBU);
2448
2449 // check for collided particles to be copied to other ranks
2450 RECORD_TIMER_START(transfer);
2451 if(m_collisions < 5 && m_collisions > 0) {
2452 exchangeParticles(true, 0);
2453 }
2454 RECORD_TIMER_STOP(transfer);
2455
2456 RECORD_TIMER_START(spawn);
2457 if(m_spawnParticles) {
2458 spawnTimeStep();
2459 }
2460 RECORD_TIMER_STOP(spawn);
2461
2462 RECORD_TIMER_START(deleteP);
2463 removeInvalidParticles(false);
2464 RECORD_TIMER_STOP(deleteP);
2465
2466 // reduceParticles(); // TODO labels:LPT activate and make settable
2467
2468 // advanceParticles(0);
2469
2470 RECORD_TIMER_START(respawn);
2471 if(m_respawn) {
2472 particleRespawn();
2473 }
2474 RECORD_TIMER_STOP(respawn);
2475
2476 updateFluidFraction();
2477
2478 if(this->m_adaptation && this->m_noSensors > 0) {
2479 checkRegridTrigger();
2480 }
2481
2482 if(a_timeStepComputationInterval() > 0 && globalTimeStep % a_timeStepComputationInterval() == 0) {
2483 forceTimeStep(computeTimeStep());
2484 }
2485
2486 // gather post-processing data
2487 if(m_sprayModel != nullptr) {
2488 const MInt count = m_sprayModel->m_injDataSize;
2489 vector<MFloat> tmp(count + m_addInjDataCnt);
2490 for(MInt i = 0; i < count; i++) {
2491 tmp[i] = m_sprayModel->m_injData[i];
2492 }
2493 tmp[count] = m_sumEvapMass;
2494 tmp[count + 1] = m_sprayModel->m_noRTsecBreakUp;
2495 tmp[count + 2] = m_sprayModel->m_noKHsecBreakUp;
2496 tmp[count + 3] = m_noSendParticles;
2497
2498 m_injData.insert(make_pair(globalTimeStep, tmp));
2499 }
2500
2501 RECORD_TIMER_STOP(partTS);
2502 return true;
2503}
2504
2511template <MInt nDim>
2513 TRACE();
2514
2515 NEW_TIMER_GROUP_STATIC(t_post, "LPT postTimeStep");
2516 NEW_TIMER_STATIC(postTS, "postTimeStep", t_post);
2517
2518 RECORD_TIMER_START(postTS);
2519
2520 if(m_skipLPT) {
2521 return;
2522 }
2523 if(m_sleepLPT > 0.0) {
2524 std::this_thread::sleep_for(std::chrono::milliseconds(m_sleepLPT));
2525 }
2526
2527 // some debug checks
2528 checkParticles();
2529 checkCells();
2530
2531 m_solutionStep = 0;
2532
2533
2534 removeInvalidParticles(true);
2535 advanceParticles(0);
2536
2537 RECORD_TIMER_STOP(postTS);
2538}
2539
2546template <MInt nDim>
2547MBool LPT<nDim>::prepareRestart(MBool force, MBool& writeGridRestart) {
2548 MBool writeRestart = false;
2549
2550 if((((globalTimeStep % this->m_restartInterval) == 0 && (globalTimeStep > m_restartTimeStep)) || force)) {
2551 if(isActive()) {
2552 countParticlesInCells();
2553 if(!m_nonBlockingComm) {
2554 exchangeSourceTerms();
2555 } else {
2556 sendSourceTerms();
2557 receiveSourceTerms();
2558 receiveFlowField();
2559 receiveVelocitySlopes();
2560 waitForSendReqs();
2561 }
2562 }
2563 writeRestart = true;
2564 }
2565
2566 if(m_restart && !m_restartFile) writeGridRestart = true;
2567
2568 return (force || writeRestart);
2569}
2570
2577template <MInt nDim>
2578void LPT<nDim>::writeRestartFile(const MBool writeRestart, const MBool, const MString gridFileName,
2579 MInt* recalcIdTree) {
2580 if(writeRestart) {
2581 checkParticles();
2582
2583 writeCellSolutionFile(gridFileName, &recalcIdTree[0]);
2584
2585 // write the particle based restart-file
2586 writeParticleRestartFile();
2587 }
2588}
2589
2596template <MInt nDim>
2598 writeParticleRestartFile();
2599
2600 MBool writeCellFile = true;
2601 if(grid().hasInactiveRanks()) writeCellFile = false;
2602
2603 // write an additional cell based solution file (useful for debuging!)
2604 if(writeCellFile) {
2605 stringstream gridFile;
2606 stringstream gridFilePath;
2607 gridFile << "grid_LPTDebug_" << globalTimeStep << ".Netcdf";
2608 gridFilePath << outputDir() << "grid_LPTDebug_" << globalTimeStep << ".Netcdf";
2609 MIntScratchSpace recalcIds(grid().raw().treeb().size(), AT_, "recalcIds");
2610
2611 grid().raw().saveGrid((gridFilePath.str()).c_str(), recalcIds.begin());
2612
2613 writeCellSolutionFile((gridFile.str()).c_str(), &recalcIds[0]);
2614 }
2615}
2616
2623template <MInt nDim>
2625 // current number of particles
2626 const MInt oldNum = a_noParticles();
2627
2628 ASSERT(!m_nonBlockingComm, "");
2629
2630 spawnParticles();
2631
2632 exchangeParticles(false, oldNum, false);
2633
2634 motionEquation(oldNum);
2635
2636 exchangeParticles(false, oldNum);
2637
2638 evaporation(oldNum);
2639
2640 coupling(oldNum);
2641}
2642
2643template <MInt nDim>
2645 RECORD_TIMER_START(m_timers[Timers::Injection]);
2646 const MInt oldNum = a_noParticles();
2647
2648 m_sprayModel->injection(m_timeStep);
2649
2650 // check for particles to be copied to other solvers
2651 // this is necessary for particles during injection
2652 // for which a leaf-halo cell could be found
2653 // where the injector dimension lies within a 2 minLevel cells
2654 // meaning that at least lower level halo cells can be found and an exchange is possible
2655 // instead of a broadcast!
2656 MBool allowNonLeaf = !m_sprayModel->m_broadcastInjected;
2657 m_primaryExchange = true;
2658 exchangeParticles(false, oldNum, allowNonLeaf);
2659 if(!m_nonBlockingComm) {
2660 m_primaryExchange = false;
2661 }
2662 RECORD_TIMER_STOP(m_timers[Timers::Injection]);
2663}
2664
2665
2666template <MInt nDim>
2667void LPT<nDim>::removeInvalidParticles(const MBool alsoFullyEvaporated) {
2668 // check list for inactive particles and remove these
2669 // inactive particles are generally particles which:
2670 // a) have been communicated to a different rank
2671 // b) have been fully evaporated
2672
2673 if(!m_respawn) {
2674 for(MInt i = a_noParticles() - 1; i >= 0; i--) {
2675 if(!alsoFullyEvaporated && m_partList[i].fullyEvaporated()) continue;
2676 if(m_partList[i].isInvalid()) {
2677 if(!m_partList[i].fullyEvaporated() && !m_partList[i].wasSend()) {
2678 if(m_partList[i].m_cellId < 0 || m_partList[i].m_cellId >= a_noCells() || !a_isHalo(m_partList[i].m_cellId)) {
2679 cerr << "Removing at " << m_partList[i].m_position[0] << " " << m_partList[i].m_position[1] << " "
2680 << m_partList[i].m_position[nDim - 1] << " " << m_partList[i].m_cellId << " "
2681 << m_partList[i].m_oldCellId << " " << m_partList[i].hadWallColl() << " " << m_partList[i].m_oldPos[0]
2682 << " " << m_partList[i].m_oldPos[1] << " " << m_partList[i].m_oldPos[nDim - 1] << " "
2683 << m_partList[i].m_partId << endl;
2684 if(m_partList[i].m_cellId > -1 && m_partList[i].m_cellId < a_noCells()) {
2685 cerr << "Cell-stats " << a_isHalo(m_partList[i].m_cellId) << " " << a_isBndryCell(m_partList[i].m_cellId)
2686 << " " << a_isValidCell(m_partList[i].m_cellId) << endl;
2687 if(m_partList[i].m_oldCellId > -1 && m_partList[i].m_oldCellId < a_noCells()) {
2688 cerr << "Old-cell-stats " << a_isBndryCell(m_partList[i].m_oldCellId) << " "
2689 << a_isValidCell(m_partList[i].m_oldCellId) << endl;
2690 }
2691 m_partList[i].checkCellChange(nullptr, false);
2692 cerr << "After-update: " << m_partList[i].m_cellId << " " << m_partList[i].isInvalid() << " "
2693 << a_isValidCell(m_partList[i].m_cellId) << endl;
2694 }
2695 }
2696 }
2697 m_partList.erase(m_partList.begin() + i);
2698 }
2699 }
2700 if(m_ellipsoids) {
2701 for(MInt i = a_noEllipsoidalParticles() - 1; i >= 0; i--) {
2702 if(m_partListEllipsoid[i].isInvalid()) {
2703 if(!m_partListEllipsoid[i].fullyEvaporated() && !m_partListEllipsoid[i].wasSend()) {
2704 if(m_partListEllipsoid[i].m_cellId < 0 || m_partListEllipsoid[i].m_cellId >= a_noCells()
2705 || !a_isHalo(m_partListEllipsoid[i].m_cellId)) {
2706 cerr << "Removing at " << m_partListEllipsoid[i].m_position[0] << " "
2707 << m_partListEllipsoid[i].m_position[1] << " " << m_partListEllipsoid[i].m_position[nDim - 1] << " "
2708 << m_partListEllipsoid[i].m_cellId << " " << m_partListEllipsoid[i].m_oldCellId << " "
2709 << m_partListEllipsoid[i].hadWallColl() << " " << m_partListEllipsoid[i].m_oldPos[0] << " "
2710 << m_partListEllipsoid[i].m_oldPos[1] << " " << m_partListEllipsoid[i].m_oldPos[nDim - 1] << " "
2711 << m_partListEllipsoid[i].m_partId << endl;
2712 if(m_partListEllipsoid[i].m_cellId > -1 && m_partListEllipsoid[i].m_cellId < a_noCells()) {
2713 cerr << "Cell-stats " << a_isHalo(m_partListEllipsoid[i].m_cellId) << " "
2714 << a_isBndryCell(m_partListEllipsoid[i].m_cellId) << " "
2715 << a_isValidCell(m_partListEllipsoid[i].m_cellId) << endl;
2716 if(m_partListEllipsoid[i].m_oldCellId > -1 && m_partListEllipsoid[i].m_oldCellId < a_noCells()) {
2717 cerr << "Old-cell-stats " << a_isBndryCell(m_partListEllipsoid[i].m_oldCellId) << " "
2718 << a_isValidCell(m_partListEllipsoid[i].m_oldCellId) << endl;
2719 }
2720 m_partListEllipsoid[i].checkCellChange(nullptr, false);
2721 cerr << "After-update: " << m_partListEllipsoid[i].m_cellId << " " << m_partListEllipsoid[i].isInvalid()
2722 << " " << a_isValidCell(m_partListEllipsoid[i].m_cellId) << endl;
2723 }
2724 }
2725 }
2726 m_partListEllipsoid.erase(m_partListEllipsoid.begin() + i);
2727 }
2728 }
2729 }
2730 }
2731}
2732
2733
2740template <MInt nDim>
2742 TRACE();
2743
2744 // reset
2745 for(MInt cellId = 0; cellId < a_noCells(); cellId++) {
2746 a_noParticlesInCell(cellId) = 0;
2747 a_noEllipsoidsInCell(cellId) = 0;
2748 }
2749
2750 // fast, local count
2751 for(MInt i = 0; i < a_noParticles(); i++) {
2752 const MInt cellId = m_partList[i].m_cellId;
2753 if(m_partList[i].isInvalid()) {
2754 mTerm(1, AT_, "Counting invalid particle!");
2755 }
2756 ASSERT(cellId > -1, "");
2757 ASSERT(!a_isHalo(cellId), "Particle in halo-cell!");
2758 ASSERT(c_isLeafCell(cellId), "Particle in non-leaf cell!");
2759 a_noParticlesInCell(cellId)++;
2760 }
2761 // Ellipsoidal particles
2762 for(MInt i = 0; i < a_noEllipsoidalParticles(); i++) {
2763 const MInt cellId = m_partListEllipsoid[i].m_cellId;
2764 if(m_partListEllipsoid[i].isInvalid()) {
2765 mTerm(1, AT_, "Counting invalid ellipsoid!");
2766 }
2767 ASSERT(cellId > -1, "");
2768 ASSERT(!a_isHalo(cellId), "Ellipsoid in halo-cell!");
2769 if(globalTimeStep > 0) ASSERT(c_isLeafCell(cellId), "Ellipsoid in non-leaf cell!");
2770 a_noEllipsoidsInCell(cellId)++;
2771 }
2772}
2773
2779template <MInt nDim>
2781 TRACE();
2782
2783 // sort particles after id to make results consistent and faster searching!
2784 // own_sort(m_partList, sort_particleAfterPartIds<LPTSpherical<nDim>>::compare);
2785
2786 // update mapping from cellId to particles
2787 m_cellToPartMap.clear();
2788 // generate mapping cell to particles
2789 for(MInt i = 0; i < a_noParticles(); i++) {
2790 m_cellToPartMap.insert({m_partList[i].m_cellId, &m_partList[i]});
2791 ASSERT(!a_isHalo(m_partList[i].m_cellId), "Particle in halo-cell!");
2792 }
2793 if(m_ellipsoids) {
2794 m_cellToEllipsMap.clear();
2795 for(MInt i = 0; i < a_noEllipsoidalParticles(); i++) {
2796 m_cellToEllipsMap.insert({m_partListEllipsoid[i].m_cellId, &m_partListEllipsoid[i]});
2797 ASSERT(!a_isHalo(m_partListEllipsoid[i].m_cellId), "Ellipsoid in halo-cell!");
2798 }
2799 }
2800}
2801
2804template <MInt nDim>
2806 countParticlesInCells();
2807 perCellStats();
2808
2809 vector<MInt> cellIds;
2810
2811 // determine critical cells by checking conditions
2812 static constexpr MInt cellPartLimit = 150;
2813 for(MInt cellId = 0; cellId < noInternalCells(); cellId++) {
2814 if(a_noParticlesInCell(cellId) < cellPartLimit) {
2815 continue;
2816 }
2817 cellIds.emplace_back(cellId);
2818 }
2819
2820 // TODO labels:LPT make settable
2821 MInt totalMerged = 0;
2822 // 1. remove all particles with low relative velocity
2823 const MFloat dvLimit = 0.5;
2824 // 2. remove all particles with a small diameter
2825 const MFloat diamLimit = 1E-5;
2826 // 3. combine the rest by merging with the next valid parcel
2827 for(auto const& cellId : cellIds) {
2828 // iterator over all particles in this cell
2829 auto particlesInCell = m_cellToPartMap.equal_range(cellId);
2830 LPTSpherical<nDim>* validPartToMerge = nullptr;
2831
2832 MInt filterCount = 0;
2833 MInt mergedParticles = 0;
2834 for(auto i = particlesInCell.first; i != particlesInCell.second; i++) {
2835 auto& particle = i->second;
2836 const MFloat magDv =
2837 sqrt(POW2(particle->m_accel.at(0)) + POW2(particle->m_accel.at(1)) + POW2(particle->m_accel.at(2)))
2838 * m_timeStep;
2839 const MFloat partD = particle->m_diameter;
2840 if(magDv < dvLimit && partD < diamLimit) {
2841 if(validPartToMerge == nullptr) {
2842 validPartToMerge = particle;
2843 } else {
2844 mergeParticles(validPartToMerge, particle);
2845
2846 mergedParticles++;
2847 validPartToMerge = nullptr;
2848 }
2849 filterCount++;
2850 }
2851 }
2852
2853
2854 if(filterCount > 0) {
2855 totalMerged += mergedParticles;
2856 }
2857 }
2858
2859 // sort by diameter smallest last (otherwise deletion takes forever)
2860 own_sort(m_partList, sortDesc_particleAfterDiameter<LPTSpherical<nDim>>::compare);
2861
2862 // erase particles with 0 mass
2863 MInt noDeleted = 0;
2864 for(MInt i = (MInt)m_partList.size() - 1; i >= 0; i--) {
2865 if(smallParticle(m_partList[i])) {
2866#ifdef LPT_DEBUG
2867 cerr << "deleted small particle " << i << " at timestep " << m_timeStep << endl;
2868#endif
2869 ++noDeleted;
2870 m_partList.erase(m_partList.begin() + i);
2871 }
2872 // else {
2873 // break;
2874 // }
2875 }
2876
2877 // recover original sort
2878 own_sort(m_partList, sort_particleAfterPartIds<LPTSpherical<nDim>>::compare);
2879
2880 if(noDeleted > 0) {
2881 cerr << domainId() << ": total particles merged " << totalMerged << " deleted " << noDeleted << endl;
2882 }
2883}
2884
2889template <MInt nDim>
2891 if(smallParticle(*partA) || smallParticle(*partB)) {
2892 TERM(-1);
2893 }
2894
2895 // mass average value
2896 auto massAvg = [](const MFloat massA, MFloat* const valueA, const MFloat massB, const MFloat* const valueB) {
2897 for(MInt dim = 0; dim < nDim; dim++) {
2898 valueA[dim] = (massA * valueA[dim] + massB * valueB[dim]) / (massA + massB);
2899 }
2900 };
2901 // calculate new direction of parcel while keeping kinetic energy constant
2902 auto massAvg2 = [](const MFloat massA, MFloat* const valueA, const MFloat massB, const MFloat* const valueB) {
2903 // calculate unit direction vector
2904 array<MFloat, nDim> dir{};
2905 MFloat resultingV =
2906 pow((massA * POW2(math::norm(valueA, nDim)) + massB * POW2(math::norm(valueB, nDim))) / (massA + massB), 0.5);
2907 for(MInt dim = 0; dim < nDim; ++dim) {
2908 dir.at(dim) = massA * valueA[dim] + massB * valueB[dim];
2909 }
2910 math::normalize(dir);
2911
2912 for(MInt dim = 0; dim < nDim; dim++) {
2913 valueA[dim] = dir.at(dim) * resultingV;
2914 }
2915 };
2916
2917 const MInt noA = partA->m_noParticles;
2918 const MInt noB = partB->m_noParticles;
2919
2920 const MFloat massA = partA->sphericalMass() * noA;
2921 const MFloat massB = partB->sphericalMass() * noB;
2922
2923 massAvg(massA, &partA->m_oldPos[0], massB, &partB->m_oldPos[0]);
2924 massAvg(massA, &partA->m_position[0], massB, &partB->m_position[0]);
2925 massAvg(massA, &partA->m_accel[0], massB, &partB->m_accel[0]);
2926 massAvg(massA, &partA->m_oldAccel[0], massB, &partB->m_oldAccel[0]);
2927 massAvg2(massA, &partA->m_oldVel[0], massB, &partB->m_oldVel[0]);
2928
2929 // cerr << "EKIN before " << massA * POW2(math::norm(&partA->m_velocity[0], 3)) + massB * POW2(math::norm
2930 // (&partB->m_velocity[0], 3)) << endl;
2931
2932 // auto temp = partA->m_velocity;
2933 // cerr << "asddas " << temp[0] << endl;
2934 // massAvg(massA, &temp[0], massB, &partB->m_velocity[0]);
2935 // cerr << "before " << temp[0] << endl;
2936 massAvg2(massA, &partA->m_velocity[0], massB, &partB->m_velocity[0]);
2937 // cerr << "after " << partA->m_velocity[0] << endl;
2938 // cerr << "EKIN after " << (massA+massB) * POW2(math::norm(&partA->m_velocity[0], 3)) << endl;
2939 // cerr << "EKIN after2 " << (massA+massB) * POW2(math::norm(&temp[0], 3)) << endl;
2940 // TERM(-1);
2941 partA->m_creationTime = (massA * partA->m_creationTime + massB * partB->m_creationTime) / (massA + massB);
2942 partA->m_temperature = (massA * partA->m_temperature + massB * partB->m_temperature) / (massA + massB);
2943 partA->m_breakUpTime = (massA * partA->m_breakUpTime + massB * partB->m_breakUpTime) / (massA + massB);
2944 partA->m_shedDiam = (massA * partA->m_shedDiam + massB * partB->m_shedDiam) / (massA + massB);
2945 partA->m_dM = (massA * partA->m_dM + massB * partB->m_dM) / (massA + massB);
2946
2947 // calculate new diameter based on mass -> which does not keep SMD constant....
2948 // partA->m_diameter = pow((partA->m_noParticles * POW3(partA->m_diameter) + partB->m_noParticles * POW3
2949 // (partB->m_diameter))/(partA->m_noParticles + partB->m_noParticles), 1.0/3.0);
2950
2951 // to keep SMD constant merge to SMD value diameter
2952 partA->m_diameter = (noA * POW3(partA->m_diameter) + noB * POW3(partB->m_diameter))
2953 / (noA * POW2(partA->m_diameter) + noB * POW2(partB->m_diameter));
2954 partB->m_diameter = 0; // mark for deletion
2955
2956 const MFloat newMassPerDrop = partA->sphericalMass();
2957 partA->m_noParticles = (massA + massB) / newMassPerDrop;
2958}
2959
2960
2961template <MInt nDim>
2963 RECORD_TIMER_START(m_timers[Timers::Motion]);
2964 ASSERT(grid().checkNghbrIds(), "");
2965
2966#ifdef _OPENMP
2967#pragma omp parallel default(none) shared(offset)
2968#endif
2969 {
2970#ifdef _OPENMP
2971#pragma omp for nowait
2972#endif
2973 for(MInt i = offset; i < a_noParticles(); i++) {
2974 if(!m_partList[i].isInvalid()) {
2975 m_partList[i].motionEquation();
2976 }
2977 }
2978#ifdef _OPENMP
2979#pragma omp for nowait
2980#endif
2981 for(MInt i = offset; i < a_noEllipsoidalParticles(); i++) {
2982 if(!m_partListEllipsoid[i].isInvalid()) {
2983 m_partListEllipsoid[i].motionEquation();
2984 }
2985 }
2986 }
2987 RECORD_TIMER_STOP(m_timers[Timers::Motion]);
2988}
2989
2990template <MInt nDim>
2991void LPT<nDim>::evaporation(const MInt offset) {
2992 if(!m_heatCoupling && !m_evaporation) return;
2993
2994 RECORD_TIMER_START(m_timers[Timers::Energy]);
2995 ASSERT(grid().checkNghbrIds(), "");
2996
2997#ifdef _OPENMP
2998#pragma omp parallel default(none) shared(offset)
2999#endif
3000 {
3001#ifdef _OPENMP
3002#pragma omp for nowait
3003#endif
3004 for(MInt i = offset; i < a_noParticles(); i++) {
3005 if(!m_partList[i].isInvalid()) {
3006 m_partList[i].energyEquation();
3007 }
3008 }
3009 }
3010 RECORD_TIMER_STOP(m_timers[Timers::Energy]);
3011}
3012
3013template <MInt nDim>
3015 MBool afterBalance = false;
3016 if(offset > 0) {
3017 RECORD_TIMER_START(m_timers[Timers::SourceTerms]);
3018 } else {
3019 offset = 0;
3020 afterBalance = true;
3021 }
3022
3023 ASSERT(grid().checkNghbrIds(), "");
3024
3025 for(MInt i = offset; i < a_noParticles(); i++) {
3026 if(!m_partList[i].isInvalid() || m_partList[i].fullyEvaporated()) {
3027 m_partList[i].coupling();
3028 }
3029 }
3030 for(MInt i = offset; i < a_noEllipsoidalParticles(); i++) {
3031 if(!m_partListEllipsoid[i].isInvalid()) {
3032 m_partListEllipsoid[i].coupling();
3033 }
3034 }
3035 if(!afterBalance) {
3036 RECORD_TIMER_STOP(m_timers[Timers::SourceTerms]);
3037 }
3038}
3039
3040template <MInt nDim>
3042 ASSERT(grid().checkNghbrIds(), "");
3043#ifdef _OPENMP
3044#pragma omp parallel default(none) shared(offset)
3045#endif
3046 {
3047#ifdef _OPENMP
3048#pragma omp for nowait
3049#endif
3050 for(MInt i = offset; i < a_noParticles(); i++) {
3051 if(!m_partList[i].isInvalid()) {
3052 m_partList[i].advanceParticle();
3053 }
3054 }
3055#ifdef _OPENMP
3056#pragma omp for nowait
3057#endif
3058 for(MInt i = offset; i < a_noEllipsoidalParticles(); i++) {
3059 if(!m_partListEllipsoid[i].isInvalid()) {
3060 m_partListEllipsoid[i].advanceParticle();
3061 }
3062 }
3063 }
3064}
3065
3069template <MInt nDim>
3071 if(!m_wallCollisions) return;
3072
3073 RECORD_TIMER_START(m_timers[Timers::Wall]);
3074
3075 // loop over all particles and call particleCollision step if necessary
3076 for(MInt i = 0; i < a_noParticles(); i++) {
3077 const MInt cellId = m_partList[i].m_cellId;
3078 const MInt oldCellId = m_partList[i].m_oldCellId;
3079
3080 if(cellId < 0) continue;
3081
3082 // if is or was in a boundary cell
3083 // if the cell leaves the cartesin grid the current cellId may be -1,
3084 // but in this case the oldCellId should be a bndryCell!
3085 if(a_isBndryCell(cellId) || a_isBndryCell(oldCellId)) {
3086 m_partList[i].particleWallCollision();
3087 m_partList[i].wallParticleCollision();
3088 }
3089 }
3090
3091 // for ellipsoids
3092 for(MInt i = 0; i < a_noEllipsoidalParticles(); i++) {
3093 const MInt cellId = m_partListEllipsoid[i].m_cellId;
3094 const MInt oldCellId = m_partListEllipsoid[i].m_oldCellId;
3095 if(cellId < 0) continue;
3096 if(a_isBndryCell(cellId) || a_isBndryCell(oldCellId)) {
3097 m_partListEllipsoid[i].particleWallCollision();
3098 m_partListEllipsoid[i].wallParticleCollision();
3099 }
3100 }
3101
3102 RECORD_TIMER_STOP(m_timers[Timers::Wall]);
3103}
3104
3113template <MInt nDim>
3115 partType thisPart{};
3116 list<partType> partRespawnList; // queue<partType> partQueue;
3117
3118 {
3119 auto deleteOffset = partition(m_partList.begin(), m_partList.end(), activeParticle<nDim>);
3120 auto offset = deleteOffset;
3121 while(offset != m_partList.end()) {
3122 if(offset->toBeRespawn()) {
3123 thisPart.partId = offset->m_partId;
3124 thisPart.cellId = offset->m_cellId;
3125 thisPart.diam = offset->m_diameter;
3126 thisPart.densRatio = offset->m_densityRatio;
3127 partRespawnList.push_back(thisPart); // partQueue.push(thisPart);
3128 }
3129 ++offset;
3130 }
3131 m_partList.erase(deleteOffset, m_partList.end());
3132 }
3133
3134 if(noDomains() > 1) {
3135 MInt const respawnSize = 3;
3136 MInt const respawnSendSize = respawnSize + 3;
3137 if(domainId() == m_respawnDomain) {
3138 MIntScratchSpace arrayNoOfParts(noDomains(), AT_, "arrayNoOfParts");
3139 arrayNoOfParts.p[m_respawnDomain] = (MInt)partRespawnList.size();
3140 MPI_Gather(MPI_IN_PLACE, 1, MPI_INT, &arrayNoOfParts.p[0], 1, MPI_INT, m_respawnDomain, mpiComm(), AT_, "INPLACE",
3141 "arrayNoOfParts");
3142
3144
3145 MInt globalNoRespawnPart = 0;
3146 for(MInt i = 0; i < noDomains(); ++i) {
3147 globalNoRespawnPart += arrayNoOfParts.p[i];
3148 arrayNoOfParts.p[i] *= respawnSize;
3149 }
3150 MIntScratchSpace displs((noDomains() + 1), AT_, "displs");
3151 displs.p[0] = 0;
3152 for(MInt i = 1; i <= noDomains(); ++i) {
3153 displs.p[i] = displs.p[i - 1] + arrayNoOfParts.p[i - 1];
3154 }
3155
3156 MFloatScratchSpace recvbuf(max(displs.p[noDomains()], 1), AT_, "recvbuf");
3157 MInt counter = displs.p[m_respawnDomain];
3158 while(!partRespawnList.empty()) {
3159 thisPart = partRespawnList.front();
3160 recvbuf.p[counter++] = MFloat(thisPart.partId);
3161 recvbuf.p[counter++] = thisPart.diam;
3162 recvbuf.p[counter++] = thisPart.densRatio;
3163 partRespawnList.pop_front(); // partQueue.pop();
3164 }
3165 MPI_Gatherv(MPI_IN_PLACE, arrayNoOfParts.p[m_respawnDomain], MPI_DOUBLE, &recvbuf.p[0], &arrayNoOfParts.p[0],
3166 &displs.p[0], MPI_DOUBLE, m_respawnDomain, mpiComm(), AT_, "INPLACE", "m_respawnDomain");
3167
3168 // generate random things on top of the id, dia, densRation. 1 which cell, 2 random values for
3169 // coordinates
3170 MIntScratchSpace bufferSizes(m_noRespawnDomains, AT_, "bufferSizes");
3171 for(MInt domain = 0; domain < m_noRespawnDomains; ++domain) {
3172 bufferSizes.p[domain] = 0;
3173 }
3174 MIntScratchSpace cellIds(globalNoRespawnPart, AT_, "cellIds");
3175 MFloatScratchSpace randCoord1(globalNoRespawnPart, AT_, "randCoord1");
3176 MFloatScratchSpace randCoord2(globalNoRespawnPart, AT_, "randCoord2");
3177 uniform_int_distribution<MInt> dist_cells(0, m_respawnGlobalDomainOffsets[m_noRespawnDomains] - 1);
3178 uniform_real_distribution<MFloat> dist_coord(0, 1);
3179 for(MInt i = 0; i < globalNoRespawnPart; ++i) {
3180 cellIds.p[i] = dist_cells(randomRespawn());
3181 randCoord1.p[i] = dist_coord(randomRespawn()) - 0.5;
3182 randCoord2.p[i] = dist_coord(randomRespawn()) - 0.5;
3183
3184 for(MInt domain = 1; domain <= m_noRespawnDomains; ++domain) {
3185 if(cellIds.p[i] < m_respawnGlobalDomainOffsets[domain]) {
3186 bufferSizes.p[domain - 1] += 1;
3187 break;
3188 }
3189 }
3190 }
3191 MInt ownRankInRespawnDomains = -1;
3192 ScratchSpace<MPI_Request> receiveRequest(m_noRespawnDomains, AT_, "receiveRequest");
3193 for(MInt i = 0; i < m_noRespawnDomains; ++i) {
3194 receiveRequest[i] = MPI_REQUEST_NULL;
3195 if(m_respawnDomainRanks[i] == domainId()) {
3196 ownRankInRespawnDomains = i;
3197 continue;
3198 }
3199
3200 MPI_Isend(&bufferSizes.p[i], 1, MPI_INT, m_respawnDomainRanks[i], LPT_MPI_PARTICLE_RESPAWN_TAG, mpiComm(),
3201 &receiveRequest[i], AT_, "bufferSizes");
3202 }
3203 MIntScratchSpace countsPerDomain(m_noRespawnDomains, AT_, "countsPerDomain");
3204 countsPerDomain.p[0] = 0;
3205 for(MInt domain = 1; domain < m_noRespawnDomains; ++domain) {
3206 countsPerDomain.p[domain] = countsPerDomain.p[domain - 1];
3207 if((domain - 1) != ownRankInRespawnDomains) {
3208 countsPerDomain.p[domain] += bufferSizes.p[domain - 1] * respawnSendSize;
3209 }
3210 }
3211 MFloatScratchSpace sendbuf(respawnSendSize * (globalNoRespawnPart - bufferSizes.p[ownRankInRespawnDomains]), AT_,
3212 "sendbuf");
3213 for(MInt i = 0; i < globalNoRespawnPart; ++i) {
3214 MInt thisDomain = -1;
3215 for(MInt domain = 1; domain <= m_noRespawnDomains; ++domain) {
3216 if(cellIds.p[i] < m_respawnGlobalDomainOffsets[domain]) {
3217 thisDomain = domain - 1;
3218 break;
3219 }
3220 }
3221 if(thisDomain == ownRankInRespawnDomains) {
3222 continue;
3223 }
3224 MInt address = countsPerDomain.p[thisDomain];
3225 for(MInt inner = 0; inner < respawnSize; ++inner) {
3226 sendbuf.p[address + inner] = recvbuf.p[i * respawnSize + inner];
3227 }
3228 sendbuf.p[address + respawnSize + 0] = MFloat(cellIds.p[i] - m_respawnGlobalDomainOffsets[thisDomain]);
3229 sendbuf.p[address + respawnSize + 1] = randCoord1.p[i];
3230 sendbuf.p[address + respawnSize + 2] = randCoord2.p[i];
3231
3232 countsPerDomain.p[thisDomain] += respawnSendSize;
3233 }
3234 // reset countsPerDomain
3235 countsPerDomain.p[0] = 0;
3236 for(MInt domain = 1; domain < m_noRespawnDomains; ++domain) {
3237 countsPerDomain.p[domain] = countsPerDomain.p[domain - 1];
3238 if((domain - 1) != ownRankInRespawnDomains) {
3239 countsPerDomain.p[domain] += bufferSizes.p[domain - 1] * respawnSendSize;
3240 }
3241 }
3242 MPI_Waitall(m_noRespawnDomains, &receiveRequest[0], MPI_STATUSES_IGNORE, AT_);
3243 for(MInt domain = 0; domain < m_noRespawnDomains; ++domain) {
3244 if((domain == ownRankInRespawnDomains) || (bufferSizes.p[domain] == 0)) {
3245 receiveRequest[domain] = MPI_REQUEST_NULL;
3246 } else {
3247 MInt address = countsPerDomain.p[domain];
3248 MPI_Isend(&sendbuf.p[address], bufferSizes.p[domain] * respawnSendSize, MPI_DOUBLE,
3249 m_respawnDomainRanks[domain], LPT_MPI_PARTICLE_RESPAWN_TAG, mpiComm(), &receiveRequest[domain], AT_,
3250 "sendbuf");
3251 }
3252 }
3253
3254 // auspacken
3255 if(bufferSizes.p[ownRankInRespawnDomains] != 0) {
3256 for(MInt i = 0; i < globalNoRespawnPart; ++i) {
3257 if((cellIds.p[i] < m_respawnGlobalDomainOffsets[ownRankInRespawnDomains + 1])
3258 && (cellIds.p[i] >= m_respawnGlobalDomainOffsets[ownRankInRespawnDomains])) {
3259 LPTSpherical<nDim> thisParticle;
3260 MFloat x[3];
3261 MFloat v[3];
3262
3263 counter = i * respawnSize;
3264 MInt id = cellIds.p[i] - m_respawnGlobalDomainOffsets[ownRankInRespawnDomains];
3265 // obtain random positions within this cell
3266 MFloat cellLength = c_cellLengthAtCell(m_respawnCells.at((MUlong)id));
3267
3268 x[m_respawn - 1] = m_respawnPlane;
3269
3270 x[m_respawn % 3] =
3271 cellLength * randCoord1.p[i] + c_coordinate(m_respawnCells.at((MUlong)id), m_respawn % 3);
3272 x[(m_respawn + 1) % 3] =
3273 cellLength * randCoord2.p[i] + c_coordinate(m_respawnCells.at((MUlong)id), (m_respawn + 1) % 3);
3274
3275 thisParticle.m_cellId = m_respawnCells.at((MUlong)id);
3276 thisParticle.m_partId = MInt(recvbuf.p[counter++]);
3277 thisParticle.m_diameter = recvbuf.p[counter++];
3278 thisParticle.m_densityRatio = recvbuf.p[counter];
3279 thisParticle.updateProperties();
3280 thisParticle.firstStep() = true;
3281 interpolateVariablesLS<0, nDim>(m_respawnCells[id], x, v);
3282 for(MInt j = 0; j < nDim; j++) {
3283 thisParticle.m_position[j] = x[j];
3284 thisParticle.m_oldPos[j] = x[j];
3285 thisParticle.m_velocity[j] =
3286 v[j] + m_terminalVelocity[thisParticle.m_diameter] * m_partList[0].s_Frm[j] / m_FrMag;
3287 thisParticle.m_oldVel[j] =
3288 v[j] + m_terminalVelocity[thisParticle.m_diameter] * m_partList[0].s_Frm[j] / m_FrMag;
3289 thisParticle.m_accel[j] = F0;
3290 thisParticle.m_oldFluidVel[j] = a_fluidVelocity(thisParticle.m_cellId, j);
3291 }
3292
3293 thisParticle.m_oldFluidDensity = a_fluidDensity(thisParticle.m_cellId);
3294
3295 m_partList.push_back(thisParticle);
3296 }
3297 }
3298 }
3299
3300 MPI_Waitall(m_noRespawnDomains, &receiveRequest[0], MPI_STATUSES_IGNORE, AT_);
3301 } else {
3302 MInt noPartsToRespawn = 0;
3303 MPI_Request receiveRequest = MPI_REQUEST_NULL;
3304 if(!m_respawnCells.empty()) {
3305 MPI_Irecv(&noPartsToRespawn, 1, MPI_INT, m_respawnDomain, LPT_MPI_PARTICLE_RESPAWN_TAG, mpiComm(),
3306 &receiveRequest, AT_, "noPartsToRespawn");
3307 }
3308
3309 auto localNoOfParts = (MInt)partRespawnList.size();
3310 MPI_Gather(&localNoOfParts, 1, MPI_INT, nullptr, 1, MPI_INT, m_respawnDomain, mpiComm(), AT_, "localNoOfParts",
3311 "nullptr");
3312 MFloatScratchSpace sendbuf(max(localNoOfParts, 1) * respawnSize, AT_, "sendbuf");
3313 MInt counter = 0;
3314 while(!partRespawnList.empty()) {
3315 thisPart = partRespawnList.front();
3316 sendbuf.p[counter++] = MFloat(thisPart.partId);
3317 sendbuf.p[counter++] = thisPart.diam;
3318 sendbuf.p[counter++] = thisPart.densRatio;
3319 partRespawnList.pop_front(); // partQueue.pop();
3320 }
3321
3322 MPI_Gatherv(&sendbuf.p[0], localNoOfParts * respawnSize, MPI_DOUBLE, nullptr, nullptr, nullptr, MPI_DOUBLE,
3323 m_respawnDomain, mpiComm(), AT_, "sendbuf", "nullptr");
3324
3325 if(!m_respawnCells.empty()) {
3326 MPI_Wait(&receiveRequest, MPI_STATUS_IGNORE, AT_);
3327
3328 if(noPartsToRespawn != 0) {
3329 MFloatScratchSpace recvbuf(noPartsToRespawn * respawnSendSize, AT_, "recvbuf");
3330 MPI_Recv(&recvbuf.p[0], noPartsToRespawn * respawnSendSize, MPI_DOUBLE, m_respawnDomain,
3331 LPT_MPI_PARTICLE_RESPAWN_TAG, mpiComm(), MPI_STATUS_IGNORE, AT_, "recvbuf");
3332
3333 for(MInt part = 0; part < noPartsToRespawn; ++part) {
3334 LPTSpherical<nDim> thisParticle;
3335 MInt id;
3336 MInt address;
3337 MFloat x[3];
3338 MFloat v[3];
3339
3340 address = part * respawnSendSize;
3341 id = MInt(recvbuf.p[address + respawnSize + 0]);
3342 MFloat cellLength = c_cellLengthAtCell(m_respawnCells.at((MUlong)id));
3343
3344 x[m_respawn - 1] = m_respawnPlane;
3345
3346 x[m_respawn % 3] = cellLength * recvbuf.p[address + respawnSize + 1]
3347 + c_coordinate(m_respawnCells.at((MUlong)id), m_respawn % 3);
3348 x[(m_respawn + 1) % 3] = cellLength * recvbuf.p[address + respawnSize + 2]
3349 + c_coordinate(m_respawnCells.at((MUlong)id), (m_respawn + 1) % 3);
3350 thisParticle.m_cellId = m_respawnCells.at((MUlong)id);
3351 thisParticle.m_partId = MInt(recvbuf.p[address + 0]);
3352 thisParticle.m_diameter = recvbuf.p[address + 1];
3353 thisParticle.m_densityRatio = recvbuf.p[address + 2];
3354 thisParticle.updateProperties();
3355 thisParticle.firstStep() = true;
3356 interpolateVariablesLS<0, nDim>(m_respawnCells[id], x, v);
3357 for(MInt j = 0; j < nDim; j++) {
3358 thisParticle.m_position[j] = x[j];
3359 thisParticle.m_oldPos[j] = x[j];
3360 thisParticle.m_velocity[j] =
3361 v[j] + m_terminalVelocity[thisParticle.m_diameter] * m_partList[0].s_Frm[j] / m_FrMag;
3362 thisParticle.m_oldVel[j] =
3363 v[j] + m_terminalVelocity[thisParticle.m_diameter] * m_partList[0].s_Frm[j] / m_FrMag;
3364 thisParticle.m_accel[j] = F0;
3365 thisParticle.m_oldFluidVel[j] = a_fluidVelocity(thisParticle.m_cellId, j);
3366 }
3367 thisParticle.m_oldFluidDensity = a_fluidDensity(thisParticle.m_cellId);
3368 m_partList.push_back(thisParticle);
3369 }
3370 }
3371 }
3372 }
3373 }
3374 if(m_ellipsoids) {
3375 mTerm(-1, AT_, "particleRespawn not implemented for ellipsoidal particle");
3376 }
3377}
3378
3388template <MInt nDim>
3396 MFloat particleDiametersDefaults[10] = {0.00001, 0.00002, 0.00003, 0.00004, 0.00005,
3397 0.00006, 0.00007, 0.00008, 0.00009, 0.00010};
3398 MInt noDiffParticles = 10;
3399 // MFloat* helpPointer = nullptr;
3400 if(Context::propertyExists("particleDiameters", solverId())) {
3401 noDiffParticles = Context::propertyLength("particleDiameters", solverId());
3402 } // else {
3403 // helpPointer = &particleDiametersDefaults[0];
3404 //}
3405 MFloatScratchSpace particleDiameters(noDiffParticles, AT_, "particleDiameters");
3406 for(MInt i = 0; i < noDiffParticles; i++) {
3407 particleDiameters[i] =
3408 Context::getSolverProperty<MFloat>("particleDiameters", solverId(), AT_, &particleDiametersDefaults[i], i);
3409 }
3410
3411 m_terminalVelocity.clear();
3412
3413 if((m_FrMag > 1.0e-12) || (m_dragModelType == 0)) {
3414 for(MInt i = 0; i < noDiffParticles; ++i) {
3415 const MFloat tmp = particleDiameters[i];
3416 m_terminalVelocity.insert(make_pair(tmp, 0.0));
3417 }
3418 } else {
3419 MInt const maxIter = 1000;
3420 MFloat epsilon = 1.0e-8, min = F0, max = F1;
3421 MFloat value = F0;
3422 for(MInt i = 0; i < noDiffParticles; ++i) {
3423 MInt side = 0;
3424 MFloat fr = NAN;
3425 MFloat fs = 0;
3426 MFloat ft = 0;
3427
3428 MInt n = 0;
3429 for(n = 1; n <= maxIter; n++) {
3430 value = (fs * max - ft * min) / (fs - ft);
3431 fr = 0;
3432 if(fabs(fr) < epsilon * value) {
3433 break;
3434 }
3435
3436
3437 if(fr * ft > 0) {
3438 max = value;
3439 ft = fr;
3440 if(side == -1) {
3441 fs /= 2;
3442 }
3443 side = -1;
3444 } else {
3445 min = value;
3446 fs = fr;
3447 if(side == +1) {
3448 ft /= 2;
3449 }
3450 side = +1;
3451 }
3452 }
3453 m_terminalVelocity.insert(pair<MFloat, MFloat>(particleDiameters[i], value));
3454 }
3455 }
3456}
3457
3462template <MInt nDim>
3463void LPT<nDim>::exchangeParticles(const MBool collOnly, const MInt offset, const MBool allowNonLeaf) {
3464 if(m_ellipsoids) {
3465 exchangeParticles_<LPTEllipsoidal<nDim>>(collOnly, offset, allowNonLeaf);
3466 } else {
3467 exchangeParticles_<LPTSpherical<nDim>>(collOnly, offset, allowNonLeaf);
3468 }
3469}
3470
3476template <MInt nDim>
3477template <class LPTParticle>
3478void LPT<nDim>::exchangeParticles_(const MBool collOnly, const MInt offset, const MBool allowNonLeaf) {
3479 TRACE();
3480
3481 if(noDomains() == 1) return;
3482
3483 MBool foundCell = false;
3484 RECORD_TIMER_START(m_timers[Timers::Exchange]);
3485
3486 vector<LPTParticle>& partList = a_particleList<LPTParticle>();
3487 MInt noParticles = a_noParticles<LPTParticle>();
3488
3489 // check for transfer
3490 for(MInt id = offset; id < noParticles; id++) {
3491 auto& part = partList[id];
3492 if((collOnly && !part.hasCollided()) || // if coll only skip all non collided particles
3493 (part.wasSend())) { // exclude ghost particles
3494 continue;
3495 }
3496
3497 foundCell = true;
3498 if(!collOnly && m_collisions > 0 && part.isWindow()) {
3499 // particle must be copied to the halo cell(s) of neighboring solver(s)
3500 // this is only necessary when collisions are activated otherwise this just leads to
3501 // overhead...
3502 foundCell = pushToQueue<LPTParticle>(m_pointsToHalo, id);
3503 } else if(!m_periodicBC && part.reqSend()) {
3504 // particle must be copied to a window cell of a neighboring solver
3505 // reduce the search to only halo-cells!
3506
3507 if(a_isHalo(part.m_cellId)) {
3508 foundCell = pushToQueue<LPTParticle>(m_pointsToWindow, id);
3509 if(allowNonLeaf && !foundCell) {
3510 // if allowing for non-leaf cell transfer, check for all halo cells
3511 for(MInt d = 0; d < grid().noNeighborDomains(); d++) {
3512 for(MInt i = 0; i < noHaloCells(d); i++) {
3513 if(part.m_cellId == grid().haloCell(d, i)) {
3514 sendQueueType<nDim> thisSend{};
3515 thisSend.partPos = id;
3516 thisSend.toCellId = i;
3517 m_queueToSend[d].push(thisSend);
3518 foundCell = true;
3519 }
3520 }
3521 }
3522 }
3523 }
3524 } else if(m_periodicBC && part.reqSend()) {
3525 std::array<MFloat, nDim> tmpShift{F0};
3526 if(grid().isPeriodic(part.m_cellId)) {
3527 for(MInt n = 0; n < nDim; n++) {
3528 if(grid().raw().periodicCartesianDir(n)) {
3529 MFloat cellCoordinate = c_coordinate(part.m_cellId, n);
3530 MInt sign1 = maia::math::sgn(m_globBbox[n] - cellCoordinate);
3531 MInt sign2 = maia::math::sgn(m_globBbox[n + nDim] - cellCoordinate);
3532 MFloat sign = F0;
3533 if(sign1 == 1 && sign2 == 1) {
3534 sign = F1;
3535 } else if(sign1 == -1 && sign2 == -1) {
3536 sign = -F1;
3537 } else {
3538 sign = F0;
3539 }
3540 tmpShift[n] = m_globDomainLength[n] * sign;
3541 }
3542 }
3543 // new position of particle
3544 for(MInt n = 0; n < nDim; n++) {
3545 partList[id].m_position[n] += tmpShift[n];
3546 }
3547 }
3548 // find cell where particle is supposed to be send to
3549 if(a_isHalo(part.m_cellId)) {
3550 foundCell = pushToQueue<LPTParticle>(m_pointsToWindow, id);
3551 if(allowNonLeaf && !foundCell) {
3552 // if allowing for non-leaf cell transfer, check for all halo cells
3553 for(MInt d = 0; d < grid().noNeighborDomains(); d++) {
3554 for(MInt i = 0; i < noHaloCells(d); i++) {
3555 if(part.m_cellId == grid().haloCell(d, i)) {
3556 sendQueueType<nDim> thisSend{};
3557 thisSend.partPos = id;
3558 thisSend.toCellId = i;
3559 m_queueToSend[d].push(thisSend);
3560 foundCell = true;
3561 }
3562 }
3563 }
3564 }
3565 }
3566 }
3567
3568 if(!foundCell) {
3569 cerr << " globalTimeStep " << globalTimeStep << endl;
3570 cerr << part.m_cellId << endl;
3571 cerr << a_isHalo(part.m_cellId) << endl;
3572 m_log << "TRANSFER status &1 or &4: no corresponding cell found! Original cell " << part.m_cellId
3573 << " properties " << endl;
3574 TERMM(-1, "????");
3575 }
3576 }
3577
3578 // copy particles to the neighboring domains
3579 // blocking comminucation version
3580 if(!m_nonBlockingComm || collOnly) {
3581 sendAndReceiveParticles<LPTParticle>(allowNonLeaf);
3582 } else {
3583 if(allowNonLeaf) {
3584 sendParticles<true, LPTParticle>();
3585 m_nonBlockingStage = 2;
3586 } else {
3587 sendParticles<false, LPTParticle>();
3588 m_nonBlockingStage = 1;
3589 }
3590 }
3591 RECORD_TIMER_STOP(m_timers[Timers::Exchange]);
3592}
3593
3601template <MInt nDim>
3602template <class LPTParticle>
3604 TRACE();
3605
3606 MPI_Status status{};
3607
3608 // 1) exchange the number of particles to be sent:
3609 for(MInt d = 0; d < grid().noNeighborDomains(); d++) {
3610 m_sendSize[d] = m_queueToSend[d].size();
3611 if(m_sendSize[d] > m_exchangeBufferSize) {
3612 mTerm(1, AT_, "Increase particle exchange buffer size!");
3613 }
3614 MPI_Isend(&m_sendSize[d], 1, MPI_INT, grid().neighborDomain(d), mpiTag("PARTICLE_COUNT"), mpiComm(),
3615 &m_mpi_reqSendSize[d], AT_, "noToSend");
3616 }
3617
3618 for(MInt d = 0; d < grid().noNeighborDomains(); d++) {
3619 MPI_Recv(&m_recvSize[d], 1, MPI_INT, grid().neighborDomain(d), mpiTag("PARTICLE_COUNT"), mpiComm(), &status, AT_,
3620 "m_recvSize");
3621 }
3622
3623 for(MInt d = 0; d < grid().noNeighborDomains(); d++) {
3624 MPI_Wait(&m_mpi_reqSendSize[d], &status, AT_);
3625 }
3626
3627 // 2) prepare/write the two send buffers (MInt, MFloat)
3628 for(MInt d = 0; d < grid().noNeighborDomains(); d++) {
3629 if(m_queueToSend[d].empty()) {
3630 // skip nothing to send
3631 m_mpi_reqSendFloat[d] = MPI_REQUEST_NULL;
3632 m_mpi_reqSendInt[d] = MPI_REQUEST_NULL;
3633 continue;
3634 }
3635
3636 vector<LPTParticle*> particlesToSend;
3637 vector<MInt> cellIds;
3638 vector<LPTParticle>& partList = a_particleList<LPTParticle>();
3639
3640 while(!m_queueToSend[d].empty()) {
3641 sendQueueType<nDim> thisSend = m_queueToSend[d].front();
3642 m_queueToSend[d].pop();
3643 MInt partPos = thisSend.partPos;
3644
3645 particlesToSend.push_back(&partList[partPos]);
3646 cellIds.push_back(thisSend.toCellId);
3647 }
3648
3649 MInt noParticles = particlesToSend.size();
3650
3651 packParticles(particlesToSend, m_intSendBuffer[d].get(), m_sendBuffer[d].get(), cellIds);
3652
3653 for(auto* part : particlesToSend) {
3654 if(part->reqSend()) {
3655 part->wasSend() = true;
3656 part->reqSend() = false;
3657 part->isInvalid() = true;
3658 }
3659 }
3660
3661 // 3) send the buffers
3662 MPI_Isend(&m_sendBuffer[d][0], noParticles * elemPerP<LPTParticle>(), MPI_DOUBLE, grid().neighborDomain(d),
3663 mpiTag("PARTICLE_FLOAT"), mpiComm(), &m_mpi_reqSendFloat[d], AT_, "m_sendBuffer");
3664
3665 MPI_Isend(&m_intSendBuffer[d][0], noParticles * intElemPerP<LPTParticle>(), MPI_INT, grid().neighborDomain(d),
3666 mpiTag("PARTICLE_INT"), mpiComm(), &m_mpi_reqSendInt[d], AT_, "m_intSendBuffer");
3667 }
3668
3669 // 4) receive the buffers
3670 for(MInt d = 0; d < grid().noNeighborDomains(); d++) {
3671 if(m_recvSize[d] > 0) {
3672 MPI_Recv(&m_recvBuffer[d][0], mMin(bufSize<LPTParticle>(), m_recvSize[d] * elemPerP<LPTParticle>()), MPI_DOUBLE,
3673 grid().neighborDomain(d), mpiTag("PARTICLE_FLOAT"), mpiComm(), &status, AT_, "m_recvBuffer");
3674 MPI_Recv(&m_intRecvBuffer[d][0], mMin(intBufSize<LPTParticle>(), m_recvSize[d] * intElemPerP<LPTParticle>()),
3675 MPI_INT, grid().neighborDomain(d), mpiTag("PARTICLE_INT"), mpiComm(), &status, AT_, "m_intRecvBuffer");
3676 }
3677 }
3678
3679 // 5) wait for completion of communication
3680 for(MInt d = 0; d < grid().noNeighborDomains(); d++) {
3681 MPI_Wait(&m_mpi_reqSendFloat[d], &status, AT_);
3682 MPI_Wait(&m_mpi_reqSendInt[d], &status, AT_);
3683 }
3684
3685 // 6) interpret the receive buffers and add incomming particles
3686 for(MInt d = 0; d < grid().noNeighborDomains(); d++) {
3687 // nothing to receive skip
3688 if(m_recvSize[d] == 0) continue;
3689
3690 if(!m_periodicBC) {
3691 unpackParticles<LPTParticle, false>(m_recvSize[d], &m_intRecvBuffer[d][0], &m_recvBuffer[d][0], d, allowNonLeaf);
3692 } else {
3693 unpackParticles<LPTParticle, true>(m_recvSize[d], &m_intRecvBuffer[d][0], &m_recvBuffer[d][0], d, allowNonLeaf);
3694 }
3695 }
3696}
3697
3698
3703template <MInt nDim>
3704template <MBool allNeighbors, class LPTParticle>
3706 TRACE();
3707
3708 if(m_nonBlockingStage != 0) {
3709 mTerm(1, AT_, "Incorrect non-blocking stage!");
3710 }
3711
3712 RECORD_TIMER_START(m_timers[Timers::Exchange1]);
3713
3714 // wait on all previously send before send new
3715 if(m_openParticleInjSend) {
3716 MPI_Waitall(noNeighborDomains(), &m_mpi_reqSendFloat[0], MPI_STATUSES_IGNORE, AT_);
3717 MPI_Waitall(noNeighborDomains(), &m_mpi_reqSendInt[0], MPI_STATUSES_IGNORE, AT_);
3718 m_openParticleInjSend = false;
3719 }
3720
3721 if(m_openParticleSend) {
3722 MPI_Waitall(grid().noLeafSendNeighborDomains(), &m_mpi_reqSendFloat[0], MPI_STATUSES_IGNORE, AT_);
3723 MPI_Waitall(grid().noLeafSendNeighborDomains(), &m_mpi_reqSendInt[0], MPI_STATUSES_IGNORE, AT_);
3724 m_openParticleSend = false;
3725 }
3726
3727 const MInt noNeighborsSend = allNeighbors ? grid().noNeighborDomains() : grid().noLeafSendNeighborDomains();
3728 const MInt noNeighborsRecv = allNeighbors ? grid().noNeighborDomains() : grid().noLeafRecvNeighborDomains();
3729
3730 // 3) prepare/write the two send buffers (MInt, MFloat)
3731 for(MInt n = 0; n < noNeighborsSend; n++) {
3732 MInt d = n;
3733 if(!allNeighbors) {
3734 d = grid().leafSendNeighborDomain(n);
3735 }
3736 if(m_queueToSend[d].empty()) {
3737 // skip nothing to send
3738 m_mpi_reqSendFloat[n] = MPI_REQUEST_NULL;
3739 m_intSendBuffer[d][0] = -intElemPerP<LPTParticle>();
3740 MPI_Isend(&m_intSendBuffer[d][0], 0, MPI_INT, grid().neighborDomain(d), mpiTag("PARTICLE_INT"), mpiComm(),
3741 &m_mpi_reqSendInt[n], AT_, "m_intSendBuffer");
3742 continue;
3743 }
3744
3745 vector<LPTParticle*> particlesToSend;
3746 RECORD_TIMER_START(m_timers[Timers::Exchange3]);
3747 vector<MInt> cellIds;
3748 vector<LPTParticle>& partList = a_particleList<LPTParticle>();
3749
3750 while(!m_queueToSend[d].empty()) {
3751 sendQueueType<nDim> thisSend = m_queueToSend[d].front();
3752 m_queueToSend[d].pop();
3753 MInt partPos = thisSend.partPos;
3754
3755 particlesToSend.push_back(&partList[partPos]);
3756 cellIds.push_back(thisSend.toCellId);
3757 }
3758
3759 MInt noParticles = particlesToSend.size();
3760 packParticles(particlesToSend, &m_intSendBuffer[d][0], &m_sendBuffer[d][0], cellIds);
3761
3762 for(auto& part : particlesToSend) {
3763 if(part->reqSend()) {
3764 part->wasSend() = true;
3765 part->reqSend() = false;
3766 part->isInvalid() = true;
3767 }
3768 }
3769 RECORD_TIMER_STOP(m_timers[Timers::Exchange3]);
3770
3771 // 3) send the buffers
3772 MPI_Isend(&m_sendBuffer[d][0], noParticles * elemPerP<LPTParticle>(), MPI_DOUBLE, grid().neighborDomain(d),
3773 mpiTag("PARTICLE_FLOAT"), mpiComm(), &m_mpi_reqSendFloat[n], AT_, "m_sendBuffer");
3774
3775 MPI_Isend(&m_intSendBuffer[d][0], noParticles * intElemPerP<LPTParticle>(), MPI_INT, grid().neighborDomain(d),
3776 mpiTag("PARTICLE_INT"), mpiComm(), &m_mpi_reqSendInt[n], AT_, "m_intSendBuffer");
3777 }
3778
3779 if(allNeighbors) {
3780 m_openParticleInjSend = true;
3781 } else {
3782 m_openParticleSend = true;
3783 }
3784
3785 // 1) Probe and get count of the receiving int-buffer
3786 // to find the number received particles for each rank
3787 // from all ranks which have already called the send and receive those buffers
3788 const MBool loadWasRunning = this->isLoadTimerRunning();
3789 if(loadWasRunning) {
3790 this->stopLoadTimer(AT_);
3791 this->startIdleTimer(AT_);
3792 this->disableDlbTimers();
3793 }
3794
3795 for(MInt n = 0; n < noNeighborsRecv; n++) {
3796 MInt d = n;
3797 if(!allNeighbors) {
3798 d = grid().leafRecvNeighborDomain(n);
3799 }
3800 m_recvSize[d] = -99;
3801 MInt flag{};
3802 MPI_Iprobe(grid().neighborDomain(d), mpiTag("PARTICLE_INT"), mpiComm(), &flag, &m_mpi_statusProbe[n],
3803 "statusProbe");
3804 if(flag) {
3805 MPI_Get_count(&m_mpi_statusProbe[n], MPI_INT, &m_recvSize[d], "m_intRecvBuffer");
3806
3807 m_recvSize[d] = m_recvSize[d] / intElemPerP<LPTParticle>();
3808
3809 MPI_Irecv(&m_intRecvBuffer[d][0], m_recvSize[d] * intElemPerP<LPTParticle>(), MPI_INT, grid().neighborDomain(d),
3810 mpiTag("PARTICLE_INT"), mpiComm(), &m_mpi_reqRecvInt[n], AT_, "m_intRecvBuffer");
3811
3812 if(m_recvSize[d] > 0) {
3813 MPI_Irecv(&m_recvBuffer[d][0], m_recvSize[d] * elemPerP<LPTParticle>(), MPI_DOUBLE, grid().neighborDomain(d),
3814 mpiTag("PARTICLE_FLOAT"), mpiComm(), &m_mpi_reqRecvFloat[n], AT_, "m_recvBuffer");
3815 } else {
3816 m_mpi_reqRecvFloat[n] = MPI_REQUEST_NULL;
3817 }
3818 }
3819 }
3820
3821 if(loadWasRunning) {
3822 this->reEnableDlbTimers();
3823 this->stopIdleTimer(AT_);
3824 this->startLoadTimer(AT_);
3825 }
3826 RECORD_TIMER_STOP(m_timers[Timers::Exchange1]);
3827}
3828
3829
3834template <MInt nDim>
3835template <MBool allNeighbors>
3837 if(m_ellipsoids) {
3838 receiveParticles_<allNeighbors, LPTEllipsoidal<nDim>>();
3839 } else {
3840 receiveParticles_<allNeighbors, LPTSpherical<nDim>>();
3841 }
3842}
3843
3844
3849template <MInt nDim>
3850template <MBool allNeighbors, class LPTParticle>
3852 TRACE();
3853
3854 if(m_nonBlockingStage < 1) {
3855 mTerm(1, AT_, "Incorrect non-blocking stage!");
3856 }
3857 RECORD_TIMER_START(m_timers[Timers::Exchange]);
3858 RECORD_TIMER_START(m_timers[Timers::Exchange1]);
3859
3860 RECORD_TIMER_START(m_timers[Timers::Exchange4]);
3861
3862 // 1) Probe and get count of the receiving int-buffer
3863 // to find the number received particles for each rank
3864 // from all ranks, which could not be received at the send-stage
3865
3866 const MBool loadWasRunning = this->isLoadTimerRunning();
3867 if(loadWasRunning) {
3868 this->stopLoadTimer(AT_);
3869 this->startIdleTimer(AT_);
3870 this->disableDlbTimers();
3871 }
3872
3873 const MInt noNeighborsRecv = allNeighbors ? grid().noNeighborDomains() : grid().noLeafRecvNeighborDomains();
3874
3875 MBool allReceived = false;
3876 while(!allReceived) {
3877 // check if all have been received
3878 allReceived = true;
3879 for(MInt n = 0; n < noNeighborsRecv; n++) {
3880 MInt d = n;
3881 if(!allNeighbors) {
3882 d = grid().leafRecvNeighborDomain(n);
3883 }
3884 if(m_recvSize[d] == -99) {
3885 allReceived = false;
3886 break;
3887 }
3888 }
3889
3890 // loop aver all domains and check for receive again
3891 for(MInt n = 0; n < noNeighborsRecv; n++) {
3892 MInt d = n;
3893 if(!allNeighbors) {
3894 d = grid().leafRecvNeighborDomain(n);
3895 }
3896 MInt flag{};
3897 MPI_Iprobe(grid().neighborDomain(d), mpiTag("PARTICLE_INT"), mpiComm(), &flag, &m_mpi_statusProbe[n],
3898 "statusProbe");
3899 if(flag) {
3900 MPI_Get_count(&m_mpi_statusProbe[n], MPI_INT, &m_recvSize[d], "m_intRecvBuffer");
3901 m_recvSize[d] = m_recvSize[d] / intElemPerP<LPTParticle>();
3902 MPI_Irecv(&m_intRecvBuffer[d][0], m_recvSize[d] * intElemPerP<LPTParticle>(), MPI_INT, grid().neighborDomain(d),
3903 mpiTag("PARTICLE_INT"), mpiComm(), &m_mpi_reqRecvInt[n], AT_, "m_intRecvBuffer");
3904
3905 if(m_recvSize[d] > 0) {
3906 MPI_Irecv(&m_recvBuffer[d][0], m_recvSize[d] * elemPerP<LPTParticle>(), MPI_DOUBLE, grid().neighborDomain(d),
3907 mpiTag("PARTICLE_FLOAT"), mpiComm(), &m_mpi_reqRecvFloat[n], AT_, "m_recvBuffer");
3908 } else {
3909 m_mpi_reqRecvFloat[n] = MPI_REQUEST_NULL;
3910 }
3911 }
3912 }
3913 }
3914
3915 if(loadWasRunning) {
3916 this->reEnableDlbTimers();
3917 this->stopIdleTimer(AT_);
3918 this->startLoadTimer(AT_);
3919 }
3920
3921 RECORD_TIMER_STOP(m_timers[Timers::Exchange4]);
3922
3923 // 1) wait until I have received all
3924 MPI_Waitall(noNeighborsRecv, &m_mpi_reqRecvFloat[0], MPI_STATUSES_IGNORE, AT_);
3925 MPI_Waitall(noNeighborsRecv, &m_mpi_reqRecvInt[0], MPI_STATUSES_IGNORE, AT_);
3926
3927
3928 RECORD_TIMER_START(m_timers[Timers::Exchange5]);
3929
3930 MBool allowNonLeaf = false;
3931 if(m_nonBlockingStage == 2) allowNonLeaf = true;
3932
3933 // 2) interpret the receive buffers and add incomming particles
3934 for(MInt n = 0; n < noNeighborsRecv; n++) {
3935 MInt d = n;
3936 if(!allNeighbors) {
3937 d = grid().leafRecvNeighborDomain(n);
3938 }
3939 // nothing to receive skip
3940 if(m_recvSize[d] == 0) continue;
3941 if(!m_periodicBC) {
3942 unpackParticles<LPTParticle, false>(m_recvSize[d], &m_intRecvBuffer[d][0], &m_recvBuffer[d][0], d, allowNonLeaf);
3943 } else {
3944 unpackParticles<LPTParticle, true>(m_recvSize[d], &m_intRecvBuffer[d][0], &m_recvBuffer[d][0], d, allowNonLeaf);
3945 }
3946 }
3947
3948 RECORD_TIMER_STOP(m_timers[Timers::Exchange5]);
3949
3950 RECORD_TIMER_STOP(m_timers[Timers::Exchange1]);
3951 m_nonBlockingStage = 0;
3952 RECORD_TIMER_STOP(m_timers[Timers::Exchange]);
3953}
3954
3955
3962template <MInt nDim>
3964 if(noDomains() > 1) {
3965 MIntScratchSpace offsets(noDomains() + 1, AT_, "offsets");
3966 MPI_Allgather(&noParticles, 1, MPI_INT, &offsets[0], 1, MPI_INT, mpiComm(), AT_, "noParticles", "offsets");
3967
3968 // determine the particle offset for this solver
3969 const MInt totalOffset = accumulate(&offsets[0], &offsets[domainId()], 0);
3970
3971 // determine the maximal partId
3972 MLong maxPartId = accumulate(&offsets[0], &offsets[noDomains()], 0);
3973
3974 // output maxPartId
3975 if(domainId() == 0) {
3976 m_log << "Domain 0 has " << noParticles << " particles initialized, out of globally " << maxPartId << " particles"
3977 << endl;
3978 }
3979
3980 // now the particles can be consistently numbered throughout the
3981 // entire domain
3982 for(auto& part : m_partList) {
3983 part.m_partId += totalOffset;
3984 }
3985 for(auto& ellip : m_partListEllipsoid) {
3986 ellip.m_partId += totalOffset;
3987 }
3988 }
3989}
3990
3996template <MInt nDim>
3998 TRACE();
3999
4000 // sort particles after paricle-id!
4001 own_sort(m_partList, sort_particleAfterPartIds<LPTBase<nDim>>::compare);
4002
4003 queue<partListIteratorConst<nDim>> ncmpiPartQueue;
4004 MInt ncmpiPartQueueSize = 0;
4005
4006 auto i1 = m_partList.begin();
4007 while(i1 != m_partList.end()) {
4008 if(!(*i1).isInvalid() && ((*i1).m_position[0] > m_xCutOff)) {
4009 ++ncmpiPartQueueSize;
4010 ncmpiPartQueue.push(i1);
4011 }
4012 i1++;
4013 }
4014
4015 // Get Variable partCount[noDomains()]
4016 MIntScratchSpace ncmpiPartCount(noDomains(), AT_, "ncmpiPartCount");
4017 MPI_Allgather(&ncmpiPartQueueSize, 1, MPI_INT, &ncmpiPartCount[0], 1, MPI_INT, mpiComm(), AT_, "ncmpiPartQueueSize",
4018 "ncmpiPartCount");
4019
4020 // Calculate ncmpiPartCountMax
4021 ParallelIo::size_type ncmpiPartCountMax = 0;
4022 for(MInt i = 0; i < noDomains(); ++i) {
4023 ncmpiPartCountMax += ncmpiPartCount[i];
4024 }
4025
4026 // If there are 0 particle in the whole domain, don't write particle data.
4027 if(ncmpiPartCountMax != 0) {
4028 // stringstream ncmpistream;
4029 const MString ncmpiFileName =
4030 outputDir() + "partData_" + getIdentifier() + to_string(globalTimeStep) + ParallelIo::fileExt();
4031
4032 using namespace maia::parallel_io;
4033 ParallelIo parallelIo(ncmpiFileName, PIO_REPLACE, mpiComm());
4034
4035 // Define Attribute timestep (double).
4036 parallelIo.setAttribute(globalTimeStep, "globalTimestep");
4037 parallelIo.setAttribute(globalTimeStep, "particleTimestep");
4038 if(globalTimeStep == 0) {
4039 parallelIo.setAttribute(0.0, "time");
4040 } else {
4041 parallelIo.setAttribute(m_time, "time");
4042 }
4043 // Define Dimension partCount [noDomains()] & Define Variable partCount
4044 // (int) [partCount].
4045 parallelIo.defineArray(PIO_INT, "partCount", noDomains());
4046
4047 // Define Dimension partId [ncmpiPartCountMax] & Define Variable partId
4048 // (int) [partId].
4049 parallelIo.defineArray(PIO_LONG, "partId", ncmpiPartCountMax);
4050
4051 // Define Dimension partPos [3 * ncmpiPartCountMax] & Define Variable
4052 // partPos (double) [partPos].
4053 parallelIo.defineArray(PIO_FLOAT, "partPos", 3 * ncmpiPartCountMax);
4054
4055 // Define Dimension partVel [3 * ncmpiPartCountMax] & Define Variable
4056 // partVel (double) [partVel].
4057 parallelIo.defineArray(PIO_FLOAT, "partVel", 3 * ncmpiPartCountMax);
4058
4059 // Define Dimension partDia [ncmpiPartCountMax] & Define Variable partDia
4060 // (double) [partDia].
4061 parallelIo.defineArray(PIO_FLOAT, "partDia", ncmpiPartCountMax);
4062
4063 if(m_activePrimaryBUp || m_activeSecondaryBUp || m_wallCollisions) {
4064 parallelIo.defineArray(PIO_INT, "partParceledNo", ncmpiPartCountMax);
4065 }
4066
4067 if(m_heatCoupling || m_evaporation) {
4068 parallelIo.defineArray(PIO_FLOAT, "partTemp", ncmpiPartCountMax);
4069 }
4070
4071 if(m_domainIdOutput) {
4072 parallelIo.defineArray(PIO_INT, "domainId", ncmpiPartCountMax);
4073 }
4074
4075 parallelIo.setOffset(1, domainId());
4076 parallelIo.writeArray(&ncmpiPartQueueSize, "partCount");
4077
4078 // Create arrays to hold the particle datas.
4079 ParallelIo::size_type ncmpiStart = 0;
4080 ParallelIo::size_type ncmpiCount = ncmpiPartCount[domainId()];
4081
4082 if(ncmpiPartCount[domainId()] > 0) {
4083 for(MInt i = 0; i < domainId(); i++) {
4084 ncmpiStart += ncmpiPartCount[i];
4085 }
4086 }
4087
4088 ASSERT(ncmpiStart + ncmpiPartCount[domainId()] <= ncmpiPartCountMax,
4089 "ERROR: Invalid number of particles " + to_string(ncmpiStart + ncmpiPartCount[domainId()]) + " > "
4090 + to_string(ncmpiPartCountMax));
4091
4092 ASSERT(ncmpiStart + ncmpiPartCount[domainId()] <= ncmpiPartCountMax,
4093 "ERROR: Invalid number of particles " + to_string(ncmpiStart + ncmpiPartCount[domainId()]) + " > "
4094 + to_string(ncmpiPartCountMax));
4095
4096 MLongScratchSpace ncmpiPartId(ncmpiPartCount[domainId()], AT_, "ncmpiPartId");
4097 MIntScratchSpace ncmpiPartParceledNo(ncmpiPartCount[domainId()], AT_, "ncmpiPartParceledNo");
4098 MFloatScratchSpace ncmpiDiameter(ncmpiPartCount[domainId()], AT_, "ncmpiDiameter");
4099 MFloatScratchSpace ncmpiTemp(ncmpiPartCount[domainId()], AT_, "ncmpiTemp");
4100 MFloatScratchSpace ncmpiPartCoords(3 * ncmpiPartCount[domainId()], AT_, "ncmpiPartCoords");
4101 // MFloatScratchSpace ncmpiPartCoordsM(3 * ncmpiPartCount[domainId()], AT_,
4102 // "ncmpiPartCoordsM");
4103 MFloatScratchSpace ncmpiPartVel(3 * ncmpiPartCount[domainId()], AT_, "ncmpiPartVel");
4104 MInt ncmpiCountId = 0;
4105
4106 // Put all particle data in arrays.
4107 while(!ncmpiPartQueue.empty()) {
4108 ncmpiPartId[ncmpiCountId] = (*(ncmpiPartQueue.front())).m_partId;
4109 ncmpiPartParceledNo[ncmpiCountId] = (*(ncmpiPartQueue.front())).m_noParticles;
4110 ncmpiDiameter[ncmpiCountId] = (*(ncmpiPartQueue.front())).m_diameter;
4111 ncmpiTemp[ncmpiCountId] = (*(ncmpiPartQueue.front())).m_temperature;
4112 ncmpiPartCoords[(3 * ncmpiCountId)] = (*(ncmpiPartQueue.front())).m_position[0];
4113 ncmpiPartCoords[(3 * ncmpiCountId) + 1] = (*(ncmpiPartQueue.front())).m_position[1];
4114 ncmpiPartCoords[(3 * ncmpiCountId) + 2] = (*(ncmpiPartQueue.front())).m_position[2];
4115 ncmpiPartVel[(3 * ncmpiCountId)] = (*(ncmpiPartQueue.front())).m_velocity[0];
4116 ncmpiPartVel[(3 * ncmpiCountId) + 1] = (*(ncmpiPartQueue.front())).m_velocity[1];
4117 ncmpiPartVel[(3 * ncmpiCountId) + 2] = (*(ncmpiPartQueue.front())).m_velocity[2];
4118
4119 ncmpiPartQueue.pop();
4120 ++ncmpiCountId;
4121 }
4122
4123 // Put the arrays in the Netcdf file.
4124 parallelIo.setOffset(ncmpiCount, ncmpiStart);
4125 parallelIo.writeArray(ncmpiPartId.begin(), "partId");
4126 parallelIo.writeArray(ncmpiDiameter.begin(), "partDia");
4127
4128 if(m_activePrimaryBUp || m_activeSecondaryBUp || m_wallCollisions) {
4129 parallelIo.writeArray(ncmpiPartParceledNo.begin(), "partParceledNo");
4130 }
4131
4132 if(m_heatCoupling || m_evaporation) {
4133 parallelIo.writeArray(ncmpiTemp.begin(), "partTemp");
4134 }
4135 if(m_domainIdOutput) {
4136 MFloatScratchSpace ncmpiDomain(ncmpiPartCount[domainId()], AT_, "ncmpiDomain");
4137 ncmpiDomain.fill(domainId());
4138 parallelIo.writeArray(ncmpiDomain.begin(), "domainId");
4139 }
4140
4141 ncmpiCount *= 3;
4142 ParallelIo::size_type ncmpi3Start = 3 * ncmpiStart;
4143 parallelIo.setOffset(ncmpiCount, ncmpi3Start);
4144 parallelIo.writeArray(ncmpiPartCoords.begin(), "partPos");
4145 parallelIo.writeArray(ncmpiPartVel.begin(), "partVel");
4146
4147 if(domainId() == 0) {
4148 cerr << "Writing particle solution file at time step " << globalTimeStep << endl;
4149 }
4150
4151 } else {
4152 if(domainId() == 0) {
4153 m_log << "WARNING: Write called but no particles present!" << endl;
4154 cerr << "WARNING: Write called but no particles present!" << endl;
4155 }
4156 }
4157 if(m_ellipsoids) {
4158 // Make ncmpiPartQueue, Queue of "Real" Particle and ncmpiPartQueueSize (MInt),
4159 // size of the queue.
4160 queue<ellipsListIterator<nDim>> ncmpiEllipsoidQueue;
4161 ncmpiPartQueueSize = 0;
4162
4163 auto i2 = m_partListEllipsoid.begin();
4164 while(i2 != m_partListEllipsoid.end()) {
4165 if(!(*i2).isInvalid() && ((*i2).m_position[0] > m_xCutOff)) {
4166 ++ncmpiPartQueueSize;
4167 ncmpiEllipsoidQueue.push(i2);
4168 }
4169 i2++;
4170 }
4171
4172 // Get Variable partCount[noDomains()]
4173 MIntScratchSpace ncmpiEllipsoidCount(noDomains(), AT_, "ncmpiEllipsoidCount");
4174 MPI_Allgather(&ncmpiPartQueueSize, 1, MPI_INT, &ncmpiEllipsoidCount[0], 1, MPI_INT, mpiComm(), AT_,
4175 "ncmpiPartQueueSize", "ncmpiEllipsoidCount");
4176
4177 // Calculate ncmpiPartCountMax
4178 ncmpiPartCountMax = 0;
4179 for(MInt i = 0; i < noDomains(); ++i) {
4180 ncmpiPartCountMax += ncmpiEllipsoidCount[i];
4181 }
4182
4183 // If there are 0 particle in the whole domain, don't write particle data.
4184 if(ncmpiPartCountMax != 0) {
4185 const MString ncmpiFileName =
4186 outputDir() + "partEllipsoid_" + getIdentifier() + to_string(globalTimeStep) + ParallelIo::fileExt();
4187
4188 using namespace maia::parallel_io;
4189 ParallelIo parallelIo(ncmpiFileName, PIO_REPLACE, mpiComm());
4190
4191 // Define Attribute timestep (double).
4192 parallelIo.setAttribute(globalTimeStep + 1, "globalTimestep");
4193 parallelIo.setAttribute(globalTimeStep, "particleTimestep");
4194 parallelIo.setAttribute(m_time, "time");
4195
4196 parallelIo.defineArray(PIO_INT, "partCount", noDomains());
4197 parallelIo.defineArray(PIO_LONG, "partId", ncmpiPartCountMax);
4198 parallelIo.defineArray(PIO_FLOAT, "partSemiMinorAxis", ncmpiPartCountMax);
4199 parallelIo.defineArray(PIO_FLOAT, "partAspectRatio", ncmpiPartCountMax);
4200 parallelIo.defineArray(PIO_FLOAT, "partDia", ncmpiPartCountMax);
4201 parallelIo.defineArray(PIO_FLOAT, "partDensityRatio", ncmpiPartCountMax);
4202 parallelIo.defineArray(PIO_FLOAT, "partPos", 3 * ncmpiPartCountMax);
4203 parallelIo.defineArray(PIO_FLOAT, "partVel", 3 * ncmpiPartCountMax);
4204 parallelIo.defineArray(PIO_FLOAT, "partAngVel", 3 * ncmpiPartCountMax);
4205 parallelIo.defineArray(PIO_FLOAT, "partMajorAxis", 3 * ncmpiPartCountMax);
4206 parallelIo.defineArray(PIO_FLOAT, "partQuat", 4 * ncmpiPartCountMax);
4207 // EndDef (Go into data mode).
4208
4209 ParallelIo::size_type ncmpiStart = domainId();
4210 ParallelIo::size_type ncmpiCount = 1;
4211
4212 parallelIo.setOffset(ncmpiCount, ncmpiStart);
4213
4214 // Put Variable partCount = ncmpiPartQueueSize at Position MPI_Rank
4215 parallelIo.writeArray(&ncmpiPartQueueSize, "partCount");
4216
4217 // Create arrays to hold the particle data.
4218 ncmpiStart = 0;
4219
4220 for(MInt i = 0; i < domainId(); ++i) {
4221 ncmpiStart += ncmpiEllipsoidCount[i];
4222 }
4223 ncmpiCount = ncmpiEllipsoidCount[domainId()];
4224 if(ncmpiStart >= ncmpiPartCountMax) {
4225 if(ncmpiCount == 0) {
4226 ncmpiStart = 0;
4227 } else {
4228 mTerm(1, AT_, "Error in m_ellipsoids ncmpiStart >= ncmpiPartCountMax but ncmpiCount != 0");
4229 }
4230 }
4231
4232 MLongScratchSpace ncmpiPartId(ncmpiPartCountMax, AT_, "ncmpiEllipsId");
4233 MFloatScratchSpace ncmpiSemiMinorAxis(ncmpiPartCountMax, AT_, "ncmpiSemiMinorAxis");
4234 MFloatScratchSpace ncmpiAspectRatio(ncmpiPartCountMax, AT_, "ncmpiAspectRatio");
4235 MFloatScratchSpace ncmpiEqDia(ncmpiPartCountMax, AT_, "ncmpiEquivalentDiameter");
4236 MFloatScratchSpace ncmpiDensityRatio(ncmpiPartCountMax, AT_, "ncmpiDensityRatio");
4237 MFloatScratchSpace ncmpiPartCoords(3 * ncmpiPartCountMax, AT_, "ncmpiCoords");
4238 MFloatScratchSpace ncmpiPartVel(3 * ncmpiPartCountMax, AT_, "ncmpiEllipsVel");
4239 MFloatScratchSpace ncmpiPartAngVel(3 * ncmpiPartCountMax, AT_, "ncmpiEllipsAngVel");
4240 MFloatScratchSpace ncmpiPartMajorAxis(3 * ncmpiPartCountMax, AT_, "ncmpiEllipsMajorAxis");
4241 MFloatScratchSpace ncmpiPartQuat(4 * ncmpiPartCountMax, AT_, "ncmpiEllipsQuat");
4242 MInt ncmpiCountId = 0;
4243
4244 // Put all particle data in arrays.
4245 while(!ncmpiEllipsoidQueue.empty()) {
4246 LPTEllipsoidal<nDim>& particle = *ncmpiEllipsoidQueue.front();
4247 ncmpiPartId[ncmpiCountId] = particle.m_partId;
4248 ncmpiSemiMinorAxis[ncmpiCountId] = particle.m_semiMinorAxis;
4249 ncmpiAspectRatio[ncmpiCountId] = particle.m_aspectRatio;
4250 ncmpiEqDia[ncmpiCountId] = particle.equivalentDiameter();
4251 ncmpiDensityRatio[ncmpiCountId] = particle.m_densityRatio;
4252 std::array<MFloat, nDim> majorAxis{};
4253 particle.calculateMajorAxisOrientation(majorAxis.begin());
4254 for(MInt n = 0; n < nDim; n++) {
4255 ncmpiPartCoords[(3 * ncmpiCountId) + n] = particle.m_position[n];
4256 ncmpiPartVel[(3 * ncmpiCountId) + n] = particle.m_velocity[n];
4257 ncmpiPartAngVel[(3 * ncmpiCountId) + n] = particle.m_angularVel[n];
4258 ncmpiPartMajorAxis[(3 * ncmpiCountId) + n] = majorAxis[n];
4259 }
4260 for(MInt n = 0; n < 4; n++) {
4261 ncmpiPartQuat[(4 * ncmpiCountId) + n] = particle.m_quaternion[n];
4262 }
4263
4264 ncmpiEllipsoidQueue.pop();
4265 ++ncmpiCountId;
4266 }
4267
4268 // Put the arrays in the Netcdf file.
4269 parallelIo.setOffset(ncmpiCount, ncmpiStart);
4270 parallelIo.writeArray(&ncmpiPartId[0], "partId");
4271 parallelIo.writeArray(&ncmpiSemiMinorAxis[0], "partSemiMinorAxis");
4272 parallelIo.writeArray(&ncmpiAspectRatio[0], "partAspectRatio");
4273 parallelIo.writeArray(&ncmpiEqDia[0], "partDia");
4274 parallelIo.writeArray(&ncmpiDensityRatio[0], "partDensityRatio");
4275
4276 ncmpiCount *= 3;
4277 ParallelIo::size_type ncmpi3Start = 3 * ncmpiStart;
4278 parallelIo.setOffset(ncmpiCount, ncmpi3Start);
4279 parallelIo.writeArray(&ncmpiPartCoords[0], "partPos");
4280 parallelIo.writeArray(&ncmpiPartVel[0], "partVel");
4281 parallelIo.writeArray(&ncmpiPartAngVel[0], "partAngVel");
4282 parallelIo.writeArray(&ncmpiPartMajorAxis[0], "partMajorAxis");
4283
4284 ParallelIo::size_type ncmpi4Start = 4 * ncmpiStart;
4285 ncmpiCount /= 3;
4286 ncmpiCount *= 4;
4287 parallelIo.setOffset(ncmpiCount, ncmpi4Start);
4288 parallelIo.writeArray(&ncmpiPartQuat[0], "partQuat");
4289
4290 // Free Memory
4291 while(!ncmpiEllipsoidQueue.empty()) {
4292 ncmpiEllipsoidQueue.pop();
4293 }
4294 }
4295 }
4296}
4297
4303template <MInt nDim>
4305 TRACE();
4306
4307 // if(grid().hasInactiveRanks()) {
4308 grid().updatePartitionCellOffsets();
4309 //}
4310
4311 m_log << "Writing LPT restart file..." << endl;
4312
4313 // Write a collective Netcdf restart file.
4314 // First, create a new collective Netcdf file. (Leaves file open and in define mode).
4315 MInt noParticles = 0;
4316 for(MInt id = 0; id < a_noParticles(); id++) {
4317 if(m_partList[id].isInvalid()) {
4318 cerr << "Invalid particle when writing restart-file!" << endl;
4319 continue;
4320 }
4321 noParticles++;
4322 }
4323 MIntScratchSpace ncmpiPartCount(noDomains(), AT_, "ncmpiPartCount");
4324 const MInt noLocalPartitionCells =
4325 grid().localPartitionCellOffsetsRestart(1) - grid().localPartitionCellOffsetsRestart(0);
4326 const MInt noGlobalPartitionCells = grid().localPartitionCellOffsetsRestart(2);
4327
4328 MIntScratchSpace noParticlesPerLocalPartitionCell(noLocalPartitionCells, AT_, "noParticlesPerLocalPartitionCell");
4329 noParticlesPerLocalPartitionCell.fill(0);
4330
4331 MPI_Allgather(&noParticles, 1, MPI_INT, &ncmpiPartCount[0], 1, MPI_INT, mpiComm(), AT_, "noParticles",
4332 "ncmpiPartCount");
4333
4334 // Calculate global number of particles
4335 ParallelIo::size_type globalNoParticles = 0;
4336 for(MInt i = 0; i < noDomains(); ++i) {
4337 globalNoParticles += ncmpiPartCount[i];
4338 }
4339
4340 if(domainId() == 0) {
4341 cerr << "Write Particle Restart at Time step " << globalTimeStep << endl;
4342 cerr << "for number of particles: " << globalNoParticles << " and " << noGlobalPartitionCells << " partition cells!"
4343 << endl;
4344 }
4345
4346 m_log << "Time step " << globalTimeStep << " -- this proc. has " << noParticles << " particles of "
4347 << globalNoParticles << endl;
4348
4349
4350 ParallelIo::size_type ncmpiStart = grid().localPartitionCellOffsetsRestart(0);
4351 ParallelIo::size_type ncmpiCount = noLocalPartitionCells;
4352
4353 auto sortByGId = [&](const LPTBase<nDim>& i, const LPTBase<nDim>& j) {
4354 const MLong globalId1 = c_globalId(i.m_cellId);
4355 const MLong globalId2 = c_globalId(j.m_cellId);
4356 if(globalId1 != globalId2) {
4357 return (c_globalId(i.m_cellId) < c_globalId(j.m_cellId));
4358 } else {
4359 return (i.m_partId < j.m_partId);
4360 }
4361 };
4362
4363 if(a_noParticles() > 0) {
4364 own_sort(m_partList, sortByGId);
4365
4366 MInt localPartitionCellCounter = 0;
4367 for(auto i1 = m_partList.begin(); i1 != m_partList.end(); i1++) {
4368 MLong particleGlobalCellId = c_globalId(i1->m_cellId);
4369 if(i1->isInvalid()) continue;
4370 if(a_isHalo(i1->m_cellId)) {
4371 cerr << "Particle in halo-cell!" << endl;
4372 cerr << i1->hadWallColl() << " " << i1->isInvalid() << " " << i1->reqSend() << " " << i1->m_partId << endl;
4373 MInt cellId2 = grid().findContainingLeafCell(&i1->m_position[0]);
4374 MInt cellId3 = grid().findContainingLeafCell(&i1->m_position[0], i1->m_cellId, true);
4375 cerr << i1->m_cellId << " " << cellId2 << " " << cellId3 << endl;
4376 }
4377 if((localPartitionCellCounter + 1 < noLocalPartitionCells)) {
4378 ASSERT(grid().localPartitionCellGlobalIdsRestart(localPartitionCellCounter + 1) > -1, "");
4379 while(particleGlobalCellId >= grid().localPartitionCellGlobalIdsRestart(localPartitionCellCounter + 1)) {
4380 localPartitionCellCounter++;
4381 if(localPartitionCellCounter + 1 >= noLocalPartitionCells) {
4382 break;
4383 }
4384 }
4385 }
4386 noParticlesPerLocalPartitionCell[localPartitionCellCounter] += 1;
4387 }
4388 }
4389
4390 MString ncmpiFileName =
4391 outputDir() + "restartPart_" + getIdentifier() + to_string(globalTimeStep) + ParallelIo::fileExt();
4392
4393 using namespace maia::parallel_io;
4394 ParallelIo parallelIo(ncmpiFileName, PIO_REPLACE, mpiComm());
4395
4396 parallelIo.setAttribute(globalTimeStep, "timestep");
4397 parallelIo.setAttribute(globalTimeStep, "particleTimestep");
4398 if(m_activePrimaryBUp || m_activeSecondaryBUp) {
4399 MInt globalInjStep = m_sprayModel->m_injStep;
4400 MPI_Allreduce(MPI_IN_PLACE, &globalInjStep, 1, MPI_INT, MPI_MAX, mpiComm(), AT_, "INPLACE", "globalInjStep");
4401 parallelIo.setAttribute(globalInjStep, "injStep");
4402
4403 MFloat globalTimeSOI = m_sprayModel->timeSinceSOI();
4404 MPI_Allreduce(MPI_IN_PLACE, &globalTimeSOI, 1, MPI_DOUBLE, MPI_MAX, mpiComm(), AT_, "INPLACE", "globalTimeSOI");
4405 parallelIo.setAttribute(globalTimeSOI, "timeSinceSOI");
4406 }
4407 parallelIo.setAttribute(m_time, "time");
4408
4409 // count the number of times random numbers were drawn for predictable restart
4410 MInt count = m_PRNGSpawnCount;
4411 MFloat particleResiduum = 0;
4412
4413 if(m_activePrimaryBUp || m_activeSecondaryBUp) {
4414 count = m_sprayModel->m_PRNGPrimBreakUpCount;
4415 particleResiduum = std::numeric_limits<MFloat>::max();
4416 }
4417
4418 if(m_spawnParticles) {
4419 particleResiduum = std::numeric_limits<MFloat>::max();
4420 }
4421
4422 if(domainId() != m_spawnDomainId) {
4423 ASSERT(count == 0, "");
4424 } else {
4425#ifndef NDEBUG
4426 // re-compute the number of times the PRNG has been used!
4427 mt19937_64 PRNG;
4428 MInt j = 0;
4429 if(m_spawnParticles) {
4430 PRNG.seed(m_spawnSeed);
4431 } else if(m_sprayModel) {
4432 PRNG.seed(m_sprayModel->m_spraySeed);
4433 }
4434 const MInt maxCount = 1000000000;
4435 for(MInt i = 0; i < maxCount; i++) {
4436 if(!m_activePrimaryBUp && !m_activeSecondaryBUp && PRNG == m_PRNGSpawn) {
4437 break;
4438 } else if((m_activePrimaryBUp || m_activeSecondaryBUp) && PRNG == m_sprayModel->m_PRNGPrimBreakUp) {
4439 break;
4440 }
4441 j++;
4442 PRNG();
4443 }
4444 if(j < maxCount) {
4445 ASSERT(m_PRNGSpawnCount == j
4446 || ((m_activePrimaryBUp || m_activeSecondaryBUp) && j == m_sprayModel->m_PRNGPrimBreakUpCount),
4447 "");
4448 } else {
4449 cerr << "PRNG-state could not be checked" << endl;
4450 }
4451#endif
4452 }
4453 if(m_spawnCellId > -1) {
4454 particleResiduum = m_particleResiduum;
4455 }
4456
4457 MPI_Allreduce(MPI_IN_PLACE, &count, 1, MPI_INT, MPI_MAX, mpiComm(), AT_, "INPLACE", "count");
4458 MPI_Allreduce(MPI_IN_PLACE, &particleResiduum, 1, MPI_DOUBLE, MPI_MIN, mpiComm(), AT_, "INPLACE", "particleResiduum");
4459
4460 parallelIo.setAttribute(count, "spawnCount");
4461 parallelIo.setAttribute(particleResiduum, "particleResiduum");
4462
4463 parallelIo.defineArray(PIO_INT, "partCount", noGlobalPartitionCells);
4464
4465 if(globalNoParticles > 0) {
4466 parallelIo.defineArray(PIO_LONG, "partId", globalNoParticles);
4467 parallelIo.defineArray(PIO_FLOAT, "partDia", globalNoParticles);
4468 parallelIo.defineArray(PIO_INT, "partStatus", globalNoParticles);
4469 parallelIo.defineArray(PIO_FLOAT, "partPos", 3 * globalNoParticles);
4470 parallelIo.defineArray(PIO_FLOAT, "partVel", 3 * globalNoParticles);
4471 parallelIo.defineArray(PIO_FLOAT, "partAccel", 3 * globalNoParticles);
4472 parallelIo.defineArray(PIO_FLOAT, "oldPos", 3 * globalNoParticles);
4473 parallelIo.defineArray(PIO_FLOAT, "oldVel", 3 * globalNoParticles);
4474 parallelIo.defineArray(PIO_FLOAT, "oldAccel", 3 * globalNoParticles);
4475 parallelIo.defineArray(PIO_INT, "partParceledNo", globalNoParticles);
4476 parallelIo.defineArray(PIO_FLOAT, "creationTime", globalNoParticles);
4477 parallelIo.defineArray(PIO_FLOAT, "oldFluidVelocity", 3 * globalNoParticles);
4478 parallelIo.defineArray(PIO_FLOAT, "oldFluidDensity", globalNoParticles);
4479 if(m_activePrimaryBUp || m_activeSecondaryBUp) {
4480 parallelIo.defineArray(PIO_FLOAT, "breakUpTime", globalNoParticles);
4481 }
4482 if(m_heatCoupling || m_evaporation) {
4483 parallelIo.defineArray(PIO_FLOAT, "partTemp", globalNoParticles);
4484 parallelIo.defineArray(PIO_FLOAT, "partDM", globalNoParticles);
4485 }
4486 if(m_activeSecondaryBUp) {
4487 parallelIo.defineArray(PIO_FLOAT, "shedD", globalNoParticles);
4488 }
4489 }
4490
4491 // Put Variable partCount = ncmpiPartQueueSize at Position MPI_Rank
4492 parallelIo.setOffset(ncmpiCount, ncmpiStart);
4493 parallelIo.writeArray(&noParticlesPerLocalPartitionCell[0], "partCount");
4494
4495 // Write particle data.
4496 ncmpiStart = 0;
4497 for(MInt i = 0; i < domainId(); ++i) {
4498 ncmpiStart += ncmpiPartCount[i];
4499 }
4500 ncmpiCount = ncmpiPartCount[domainId()];
4501 if(ncmpiStart >= globalNoParticles) {
4502 if(ncmpiCount == 0) {
4503 ncmpiStart = 0;
4504 } else {
4505 mTerm(1, AT_, "Error ncmpiStart >= globalNoParticles but ncmpiCount != 0");
4506 }
4507 }
4508 ASSERT(ncmpiCount == a_noParticles(), "");
4509
4510 if(globalNoParticles > 0) {
4511 MLongScratchSpace ncmpiPartId(ncmpiCount, AT_, "ncmpiPartId");
4512 MFloatScratchSpace ncmpiDiameter(ncmpiCount, AT_, "ncmpiDiameter");
4513 MFloatScratchSpace ncmpiDensityRatio(ncmpiCount, AT_, "ncmpiDensityRatio");
4514 MIntScratchSpace ncmpiPartStatus(ncmpiCount, AT_, "ncmpiPartStatus");
4515 MFloatScratchSpace ncmpiPartCoords(3 * ncmpiCount, AT_, "PartCoords");
4516 MFloatScratchSpace ncmpiPartVel(3 * ncmpiCount, AT_, "ncmpiPartVel");
4517 MFloatScratchSpace ncmpiPartAccel(3 * ncmpiCount, AT_, "ncmpiPartAccel");
4518 MFloatScratchSpace ncmpiOldCoords(3 * ncmpiCount, AT_, "ncmpiOldCoords");
4519 MFloatScratchSpace ncmpiOldVel(3 * ncmpiCount, AT_, "ncmpiOldVel");
4520 MFloatScratchSpace ncmpiOldAccel(3 * ncmpiCount, AT_, "ncmpiOldAccel");
4521 MIntScratchSpace ncmpiPartParceledNo(ncmpiCount, AT_, "ncmpiPartParceledNo");
4522 MFloatScratchSpace ncmpiTemp(ncmpiCount, AT_, "ncmpiTemp");
4523 MFloatScratchSpace ncmpiDM(ncmpiCount, AT_, "ncmpiDM");
4524 MFloatScratchSpace ncmpiCT(ncmpiCount, AT_, "ncmpiCT");
4525 MFloatScratchSpace ncmpiBT(ncmpiCount, AT_, "ncmpiBT");
4526 MFloatScratchSpace ncmpiOldFluidDensity(ncmpiCount, AT_, "ncmpiOldFluidDensity");
4527 MFloatScratchSpace ncmpiOldFluidVelocity(3 * ncmpiCount, AT_, "ncmpiOldFluidVelocity");
4528 MFloatScratchSpace ncmpishedD(ncmpiCount, AT_, "ncmpishedD");
4529
4530 MInt id = 0;
4531
4532 for(MInt i = 0; i < a_noParticles(); i++) {
4533 if(m_partList[i].isInvalid()) continue;
4534 ncmpiPartId[id] = m_partList[i].m_partId;
4535 ncmpiDiameter[id] = m_partList[i].m_diameter;
4536 ncmpiDensityRatio[id] = m_partList[i].m_densityRatio;
4537 ncmpiPartStatus[id] = (m_partList[i].firstStep() ? 1 : 0);
4538 ncmpiPartCoords[(3 * id)] = m_partList[i].m_position[0];
4539 ncmpiPartCoords[(3 * id) + 1] = m_partList[i].m_position[1];
4540 ncmpiPartCoords[(3 * id) + 2] = m_partList[i].m_position[2];
4541 ncmpiPartVel[(3 * id)] = m_partList[i].m_velocity[0];
4542 ncmpiPartVel[(3 * id) + 1] = m_partList[i].m_velocity[1];
4543 ncmpiPartVel[(3 * id) + 2] = m_partList[i].m_velocity[2];
4544 ncmpiPartAccel[(3 * id)] = m_partList[i].m_accel[0];
4545 ncmpiPartAccel[(3 * id) + 1] = m_partList[i].m_accel[1];
4546 ncmpiPartAccel[(3 * id) + 2] = m_partList[i].m_accel[2];
4547 ncmpiOldCoords[(3 * id)] = m_partList[i].m_oldPos[0];
4548 ncmpiOldCoords[(3 * id) + 1] = m_partList[i].m_oldPos[1];
4549 ncmpiOldCoords[(3 * id) + 2] = m_partList[i].m_oldPos[2];
4550 ncmpiOldVel[(3 * id)] = m_partList[i].m_oldVel[0];
4551 ncmpiOldVel[(3 * id) + 1] = m_partList[i].m_oldVel[1];
4552 ncmpiOldVel[(3 * id) + 2] = m_partList[i].m_oldVel[2];
4553 ncmpiOldAccel[(3 * id)] = m_partList[i].m_oldAccel[0];
4554 ncmpiOldAccel[(3 * id) + 1] = m_partList[i].m_oldAccel[1];
4555 ncmpiOldAccel[(3 * id) + 2] = m_partList[i].m_oldAccel[2];
4556 ncmpiPartParceledNo[id] = m_partList[i].m_noParticles;
4557 ncmpiTemp[id] = m_partList[i].m_temperature;
4558 ncmpiDM[id] = m_partList[i].m_dM;
4559 ncmpiCT[id] = 0.0; // m_partList[i].m_creationTime;
4560 ncmpiBT[id] = m_partList[i].m_breakUpTime;
4561 if(m_activeSecondaryBUp) {
4562 ncmpishedD[id] = m_partList[i].m_shedDiam;
4563 }
4564
4565 ncmpiOldFluidDensity[id] = m_partList[i].m_oldFluidDensity;
4566 ncmpiOldFluidVelocity[(3 * id)] = m_partList[i].m_oldFluidVel[0];
4567 ncmpiOldFluidVelocity[(3 * id) + 1] = m_partList[i].m_oldFluidVel[1];
4568 ncmpiOldFluidVelocity[(3 * id) + 2] = m_partList[i].m_oldFluidVel[2];
4569
4570 id = id + 1;
4571 }
4572
4573 parallelIo.setOffset(ncmpiCount, ncmpiStart);
4574
4575 parallelIo.writeArray(ncmpiPartId.begin(), "partId");
4576 parallelIo.writeArray(ncmpiDiameter.begin(), "partDia");
4577 parallelIo.writeArray(ncmpiPartStatus.begin(), "partStatus");
4578 parallelIo.writeArray(ncmpiPartParceledNo.begin(), "partParceledNo");
4579 parallelIo.writeArray(ncmpiCT.begin(), "creationTime");
4580
4581 if(m_activePrimaryBUp || m_activeSecondaryBUp) {
4582 parallelIo.writeArray(ncmpiBT.begin(), "breakUpTime");
4583 }
4584
4585 if(m_heatCoupling || m_evaporation) {
4586 parallelIo.writeArray(ncmpiTemp.begin(), "partTemp");
4587 parallelIo.writeArray(ncmpiDM.begin(), "partDM");
4588 }
4589
4590 if(m_activeSecondaryBUp) {
4591 parallelIo.writeArray(ncmpishedD.begin(), "shedD");
4592 }
4593
4594 parallelIo.writeArray(ncmpiOldFluidDensity.begin(), "oldFluidDensity");
4595
4596 ncmpiCount *= 3;
4597 ParallelIo::size_type ncmpi3Start = 3 * ncmpiStart;
4598 parallelIo.setOffset(ncmpiCount, ncmpi3Start);
4599 parallelIo.writeArray(ncmpiPartCoords.begin(), "partPos");
4600 parallelIo.writeArray(ncmpiPartVel.begin(), "partVel");
4601 parallelIo.writeArray(ncmpiPartAccel.begin(), "partAccel");
4602 parallelIo.writeArray(ncmpiOldCoords.begin(), "oldPos");
4603 parallelIo.writeArray(ncmpiOldVel.begin(), "oldVel");
4604 parallelIo.writeArray(ncmpiOldAccel.begin(), "oldAccel");
4605 parallelIo.writeArray(ncmpiOldFluidVelocity.begin(), "oldFluidVelocity");
4606 }
4607 // recover original sorting
4608 own_sort(m_partList, sort_particleAfterPartIds<LPTBase<nDim>>::compare);
4609
4610 if(m_ellipsoids) {
4611 MInt noEllipsoids = 0;
4612 for(MInt id = 0; id < a_noEllipsoidalParticles(); id++) {
4613 if(m_partListEllipsoid[id].isInvalid()) {
4614 cerr << "Invalid particle when writing restart-file!" << endl;
4615 continue;
4616 }
4617 noEllipsoids++;
4618 }
4619
4620 MIntScratchSpace noEllipsoidsPerLocalPartitionCell(noLocalPartitionCells, AT_, "noEllipsoidsPerLocalPartitionCell");
4621 noEllipsoidsPerLocalPartitionCell.fill(0);
4622 MIntScratchSpace ncmpiEllipsoidCount(noDomains(), AT_, "ncmpiEllipsoidCount");
4623 MPI_Allgather(&noEllipsoids, 1, MPI_INT, &ncmpiEllipsoidCount[0], 1, MPI_INT, mpiComm(), AT_, "noEllipsoids",
4624 "ncmpiEllipsoidCount");
4625
4626 // Calculate global no ellipsoids
4627 ParallelIo::size_type globalNoEllipsoids = 0;
4628 for(MInt i = 0; i < noDomains(); ++i) {
4629 globalNoEllipsoids += ncmpiEllipsoidCount[i];
4630 }
4631
4632 m_log << "Time step " << globalTimeStep << " -- this proc. has " << noEllipsoids << " ellipsoids of "
4633 << globalNoEllipsoids << endl;
4634 if(domainId() == 0) {
4635 cerr << "Write Ellipsoid Restart at Time step " << globalTimeStep << endl;
4636 cerr << "for number of ellipsoids: " << globalNoEllipsoids << " and " << noGlobalPartitionCells
4637 << " partition cells!" << endl;
4638 }
4639
4640 ncmpiStart = grid().localPartitionCellOffsetsRestart(0);
4641 ncmpiCount = noLocalPartitionCells;
4642
4643 // If there are 0 particle in the whole domain, don't write particle data.
4644 if(a_noEllipsoidalParticles() > 0) {
4645 own_sort(m_partListEllipsoid, sortByGId);
4646
4647 MInt localPartitionCellCounter = 0;
4648 for(auto i1 = m_partListEllipsoid.begin(); i1 != m_partListEllipsoid.end(); i1++) {
4649 MLong particleGlobalCellId = c_globalId(i1->m_cellId);
4650 if(i1->isInvalid()) continue;
4651 if(a_isHalo(i1->m_cellId)) {
4652 cerr << "Particle in halo-cell!" << endl;
4653 cerr << i1->hadWallColl() << " " << i1->isInvalid() << " " << i1->reqSend() << " " << i1->m_partId << endl;
4654 MInt cellId2 = grid().findContainingLeafCell(&i1->m_position[0]);
4655 MInt cellId3 = grid().findContainingLeafCell(&i1->m_position[0], i1->m_cellId, true);
4656 cerr << i1->m_cellId << " " << cellId2 << " " << cellId3 << endl;
4657 }
4658 if((localPartitionCellCounter + 1 < noLocalPartitionCells)) {
4659 ASSERT(grid().localPartitionCellGlobalIdsRestart(localPartitionCellCounter + 1) > -1, "");
4660 while(particleGlobalCellId >= grid().localPartitionCellGlobalIdsRestart(localPartitionCellCounter + 1)) {
4661 localPartitionCellCounter++;
4662 if(localPartitionCellCounter + 1 >= noLocalPartitionCells) {
4663 break;
4664 }
4665 }
4666 }
4667 noEllipsoidsPerLocalPartitionCell[localPartitionCellCounter] += 1;
4668 }
4669 }
4670
4671 ncmpiFileName =
4672 outputDir() + "restartPartEllipsoid_" + getIdentifier() + to_string(globalTimeStep) + ParallelIo::fileExt();
4673
4674 ParallelIo parallelIoE(ncmpiFileName, PIO_REPLACE, mpiComm());
4675
4676 // Define Attribute timestep (double).
4677 parallelIoE.setAttribute(globalTimeStep, "timestep");
4678 parallelIoE.setAttribute(globalTimeStep, "particleTimestep");
4679 parallelIoE.defineArray(PIO_INT, "partCount", noGlobalPartitionCells);
4680
4681
4682 if(globalNoEllipsoids > 0) {
4683 parallelIoE.defineArray(PIO_LONG, "partId", globalNoEllipsoids);
4684 parallelIoE.defineArray(PIO_FLOAT, "partSemiMinorAxis", globalNoEllipsoids);
4685 parallelIoE.defineArray(PIO_FLOAT, "partDia", globalNoEllipsoids);
4686 parallelIoE.defineArray(PIO_FLOAT, "partAspectRatio", globalNoEllipsoids);
4687 parallelIoE.defineArray(PIO_FLOAT, "partDensityRatio", globalNoEllipsoids);
4688 parallelIoE.defineArray(PIO_INT, "partStatus", globalNoEllipsoids);
4689 parallelIoE.defineArray(PIO_FLOAT, "partPos", 3 * globalNoEllipsoids);
4690 parallelIoE.defineArray(PIO_FLOAT, "partVel", 3 * globalNoEllipsoids);
4691 parallelIoE.defineArray(PIO_FLOAT, "partAccel", 3 * globalNoEllipsoids);
4692 parallelIoE.defineArray(PIO_FLOAT, "partAngVel", 3 * globalNoEllipsoids);
4693 parallelIoE.defineArray(PIO_FLOAT, "partAngAccel", 3 * globalNoEllipsoids);
4694 parallelIoE.defineArray(PIO_FLOAT, "partOldPos", 3 * globalNoEllipsoids);
4695 parallelIoE.defineArray(PIO_FLOAT, "partOldVel", 3 * globalNoEllipsoids);
4696 parallelIoE.defineArray(PIO_FLOAT, "partOldAccel", 3 * globalNoEllipsoids);
4697 parallelIoE.defineArray(PIO_FLOAT, "partOldAngVel", 3 * globalNoEllipsoids);
4698 parallelIoE.defineArray(PIO_FLOAT, "partOldAngAccel", 3 * globalNoEllipsoids);
4699 parallelIoE.defineArray(PIO_FLOAT, "partQuat", 4 * globalNoEllipsoids);
4700 parallelIoE.defineArray(PIO_FLOAT, "partOldQuat", 4 * globalNoEllipsoids);
4701 parallelIoE.defineArray(PIO_FLOAT, "creationTime", globalNoEllipsoids);
4702 parallelIoE.defineArray(PIO_FLOAT, "oldFluidVelocity", 3 * globalNoEllipsoids);
4703 parallelIoE.defineArray(PIO_FLOAT, "oldFluidDensity", globalNoEllipsoids);
4704 if(m_heatCoupling) parallelIoE.defineArray(PIO_FLOAT, "partTemp", globalNoEllipsoids);
4705 // EndDef (Go into data mode).
4706
4707 // Put Variable partCount = ncmpiPartQueueSize at Position MPI_Rank
4708 parallelIoE.setOffset(ncmpiCount, ncmpiStart);
4709 parallelIoE.writeArray(&noEllipsoidsPerLocalPartitionCell[0], "partCount");
4710
4711 // Write particle data.
4712 ncmpiStart = 0;
4713 for(MInt i = 0; i < domainId(); ++i) {
4714 ncmpiStart += ncmpiEllipsoidCount[i];
4715 }
4716 ncmpiCount = ncmpiEllipsoidCount[domainId()];
4717 if(ncmpiStart >= globalNoEllipsoids) {
4718 if(ncmpiCount == 0) {
4719 ncmpiStart = 0;
4720 } else {
4721 mTerm(1, AT_, "m_ellipsoids ncmpiStart >= globalNoEllipsoids but ncmpiCount != 0");
4722 }
4723 }
4724 ASSERT(ncmpiCount == a_noEllipsoidalParticles(), "");
4725
4726 MLongScratchSpace ncmpiPartId(ncmpiCount, AT_, "ncmpiPartIdE");
4727 MFloatScratchSpace ncmpiSemiMinorAxis(ncmpiCount, AT_, "ncmpiSemiMinorAxis");
4728 MFloatScratchSpace ncmpiEquivalentDia(ncmpiCount, AT_, "ncmpiEquivalentDia");
4729 MFloatScratchSpace ncmpiAspectRatio(ncmpiCount, AT_, "ncmpiAspectRatio");
4730 MFloatScratchSpace ncmpiDensityRatio(ncmpiCount, AT_, "ncmpiDensityRatio");
4731 MIntScratchSpace ncmpiPartStatus(ncmpiCount, AT_, "ncmpiPartStatus");
4732 MFloatScratchSpace ncmpiPartCoords(3 * ncmpiCount, AT_, "ncmpiPartCoords");
4733 MFloatScratchSpace ncmpiPartVel(3 * ncmpiCount, AT_, "ncmpiPartVel");
4734 MFloatScratchSpace ncmpiPartAccel(3 * ncmpiCount, AT_, "ncmpiPartAccel");
4735 MFloatScratchSpace ncmpiPartAngVel(3 * ncmpiCount, AT_, "ncmpiPartAngVel");
4736 MFloatScratchSpace ncmpiPartAngAccel(3 * ncmpiCount, AT_, "ncmpiPartAngAccel");
4737 MFloatScratchSpace ncmpiPartOldCoords(3 * ncmpiCount, AT_, "ncmpiPartOldCoords");
4738 MFloatScratchSpace ncmpiPartOldVel(3 * ncmpiCount, AT_, "ncmpiPartOldVel");
4739 MFloatScratchSpace ncmpiPartOldAccel(3 * ncmpiCount, AT_, "ncmpiPartOldAccel");
4740 MFloatScratchSpace ncmpiPartOldAngVel(3 * ncmpiCount, AT_, "ncmpiPartOldAngVel");
4741 MFloatScratchSpace ncmpiPartOldAngAccel(3 * ncmpiCount, AT_, "ncmpiPartOldAngAccel");
4742 MFloatScratchSpace ncmpiPartQuat(4 * ncmpiCount, AT_, "ncmpiPartQuaternion");
4743 MFloatScratchSpace ncmpiPartOldQuat(4 * ncmpiCount, AT_, "ncmpiPartOldQuaternion");
4744 MFloatScratchSpace ncmpiTemp(ncmpiCount, AT_, "ncmpiTemp");
4745 MFloatScratchSpace ncmpiCT(ncmpiCount, AT_, "ncmpiCT");
4746 MFloatScratchSpace ncmpiOldFluidDensity(ncmpiCount, AT_, "ncmpiOldFluidDensity");
4747 MFloatScratchSpace ncmpiOldFluidVelocity(3 * ncmpiCount, AT_, "ncmpiOldFluidVelocity");
4748
4749 for(MInt i = 0; i < a_noEllipsoidalParticles(); i++) {
4750 if(m_partListEllipsoid[i].isInvalid()) continue;
4751 ncmpiPartId[i] = m_partListEllipsoid[i].m_partId;
4752 ncmpiSemiMinorAxis[i] = m_partListEllipsoid[i].m_semiMinorAxis;
4753 ncmpiEquivalentDia[i] = m_partListEllipsoid[i].equivalentDiameter();
4754 ncmpiAspectRatio[i] = m_partListEllipsoid[i].m_aspectRatio;
4755 ncmpiDensityRatio[i] = m_partListEllipsoid[i].m_densityRatio;
4756 ncmpiPartStatus[i] = (m_partListEllipsoid[i].firstStep() ? 1 : 0);
4757 for(MInt n = 0; n < nDim; n++) {
4758 ncmpiPartCoords[(3 * i) + n] = m_partListEllipsoid[i].m_position[n];
4759 ncmpiPartVel[(3 * i) + n] = m_partListEllipsoid[i].m_velocity[n];
4760 ncmpiPartAccel[(3 * i) + n] = m_partListEllipsoid[i].m_accel[n];
4761 ncmpiPartAngVel[(3 * i) + n] = m_partListEllipsoid[i].m_angularVel[n];
4762 ncmpiPartAngAccel[(3 * i) + n] = m_partListEllipsoid[i].m_angularAccel[n];
4763 ncmpiPartOldCoords[(3 * i) + n] = m_partListEllipsoid[i].m_oldPos[n];
4764 ncmpiPartOldVel[(3 * i) + n] = m_partListEllipsoid[i].m_oldVel[n];
4765 ncmpiPartOldAccel[(3 * i) + n] = m_partListEllipsoid[i].m_oldAccel[n];
4766 ncmpiPartOldAngVel[(3 * i) + n] = m_partListEllipsoid[i].m_oldAngularVel[n];
4767 ncmpiPartOldAngAccel[(3 * i) + n] = m_partListEllipsoid[i].m_oldAngularAccel[n];
4768 ncmpiOldFluidVelocity[(3 * i) + n] = m_partListEllipsoid[i].m_oldFluidVel[n];
4769 }
4770 for(MInt n = 0; n < 4; n++) {
4771 ncmpiPartQuat[(4 * i) + n] = m_partListEllipsoid[i].m_quaternion[n];
4772 ncmpiPartOldQuat[(4 * i) + n] = m_partListEllipsoid[i].m_oldQuaternion[n];
4773 }
4774 ncmpiOldFluidDensity[i] = m_partListEllipsoid[i].m_oldFluidDensity;
4775 ncmpiTemp[i] = m_partListEllipsoid[i].m_temperature;
4776 ncmpiCT[i] = m_partListEllipsoid[i].m_creationTime;
4777 }
4778
4779 parallelIoE.setOffset(ncmpiCount, ncmpiStart);
4780 parallelIoE.writeArray(ncmpiPartId.begin(), "partId");
4781 parallelIoE.writeArray(ncmpiSemiMinorAxis.begin(), "partSemiMinorAxis");
4782 parallelIoE.writeArray(ncmpiEquivalentDia.begin(), "partDia");
4783 parallelIoE.writeArray(ncmpiAspectRatio.begin(), "partAspectRatio");
4784 parallelIoE.writeArray(ncmpiDensityRatio.begin(), "partDensityRatio");
4785 parallelIoE.writeArray(ncmpiPartStatus.begin(), "partStatus");
4786 parallelIoE.writeArray(ncmpiCT.begin(), "creationTime");
4787 parallelIoE.writeArray(ncmpiOldFluidDensity.begin(), "oldFluidDensity");
4788 if(m_heatCoupling) parallelIoE.writeArray(ncmpiTemp.begin(), "partTemp");
4789
4790 ncmpiCount *= 3;
4791 ParallelIo::size_type ncmpi3Start = 3 * ncmpiStart;
4792 parallelIoE.setOffset(ncmpiCount, ncmpi3Start);
4793 parallelIoE.writeArray(ncmpiPartCoords.begin(), "partPos");
4794 parallelIoE.writeArray(ncmpiPartVel.begin(), "partVel");
4795 parallelIoE.writeArray(ncmpiPartAccel.begin(), "partAccel");
4796 parallelIoE.writeArray(ncmpiPartAngVel.begin(), "partAngVel");
4797 parallelIoE.writeArray(ncmpiPartAngAccel.begin(), "partAngAccel");
4798 parallelIoE.writeArray(ncmpiPartOldCoords.begin(), "partOldPos");
4799 parallelIoE.writeArray(ncmpiPartOldVel.begin(), "partOldVel");
4800 parallelIoE.writeArray(ncmpiPartOldAccel.begin(), "partOldAccel");
4801 parallelIoE.writeArray(ncmpiPartOldAngVel.begin(), "partOldAngVel");
4802 parallelIoE.writeArray(ncmpiPartOldAngAccel.begin(), "partOldAngAccel");
4803
4804 ParallelIo::size_type ncmpi4Start = 4 * ncmpiStart;
4805 ncmpiCount /= 3;
4806 ncmpiCount *= 4;
4807 parallelIoE.setOffset(ncmpiCount, ncmpi4Start);
4808 parallelIoE.writeArray(ncmpiPartQuat.begin(), "partQuat");
4809 parallelIoE.writeArray(ncmpiPartOldQuat.begin(), "partOldQuat");
4810 }
4811 own_sort(m_partListEllipsoid, sort_particleAfterPartIds<LPTBase<nDim>>::compare);
4812 }
4813}
4814
4821template <MInt nDim>
4823 TRACE();
4824
4825 // if(grid().hasInactiveRanks()) {
4826 grid().updatePartitionCellOffsets();
4827 //}
4828
4829 using namespace maia::parallel_io;
4830
4831 // Open collective Netcdf file.
4832 MString particleRestartFilename =
4833 restartDir() + "restartPart_" + getIdentifier() + to_string(globalTimeStep) + ParallelIo::fileExt();
4834
4835 // 1. Loading spherical particles
4836 {
4837 // check if file exists
4838 struct stat buffer {};
4839 if(stat(particleRestartFilename.c_str(), &buffer) != 0) {
4840 if(domainId() == 0) {
4841 cerr << "WARNING: starting with restart but no particle file present!" << endl;
4842 m_log << "WARNING: starting with restart but no particle file present!" << endl;
4843 }
4844 m_restartFile = false;
4845 return 0;
4846 }
4847
4848 // check that the timeStep in the restart-file matches the current globalTimeStep!
4849 MFloat loadedTimeStep = 0;
4850 ParallelIo parallelIo(particleRestartFilename, PIO_READ, mpiComm());
4851
4852 parallelIo.getAttribute(&loadedTimeStep, "timestep");
4853 if(MInt(loadedTimeStep) != globalTimeStep) {
4854 stringstream errorMessage;
4855 errorMessage << "Error! restartTimeStep = " << globalTimeStep << " differs from timestep in restartPart"
4856 << ParallelIo::fileExt() << ", which is " << loadedTimeStep << endl;
4857 mTerm(1, AT_, errorMessage.str());
4858 }
4859
4860 parallelIo.getAttribute(&m_time, "time");
4861
4862 if(m_activePrimaryBUp || m_activeSecondaryBUp) {
4863 parallelIo.getAttribute(&m_sprayModel->m_injStep, "injStep");
4864 parallelIo.getAttribute(&m_sprayModel->timeSinceSOI(), "timeSinceSOI");
4865 }
4866
4867
4868 // Get the number of particle each domain has.
4869 const MInt noGlobalPartitionCells = grid().localPartitionCellOffsetsRestart(2);
4870 const MInt localPartitionCellBeginning = grid().localPartitionCellOffsetsRestart(0);
4871 const MInt localPartitionCellEnd = grid().localPartitionCellOffsetsRestart(1);
4872
4873 MIntScratchSpace noParticlesPerPartitionCell(noGlobalPartitionCells, AT_, "noParticlesPerPartitionCell");
4874
4875 if(domainId() == 0) {
4876 cerr << "noGlobalPartitionCells " << noGlobalPartitionCells << endl;
4877 }
4878
4879 // load number of particle in partition cell for all partition cells!
4880 parallelIo.setOffset(noGlobalPartitionCells, 0);
4881 parallelIo.readArray(&noParticlesPerPartitionCell[0], "partCount");
4882
4883 // total number of particles
4884 MInt numberOfParts = 0;
4885#ifdef _OPENMP
4886#pragma omp parallel for reduction(+ : numberOfParts)
4887#endif
4888 for(MInt i = 0; i < noGlobalPartitionCells; ++i) {
4889 numberOfParts += noParticlesPerPartitionCell[i];
4890 }
4891 if(domainId() == 0) {
4892 cerr << "Global number of particles loaded from restart file " << numberOfParts << endl;
4893 }
4894 m_log << "Global number of particles loaded from restart file " << numberOfParts << endl;
4895
4896
4897 m_PRNGSpawn.seed(m_spawnSeed);
4898 m_particleResiduum = 0;
4899 if(domainId() == m_spawnDomainId) {
4900 parallelIo.getAttribute(&m_PRNGSpawnCount, "spawnCount");
4901 m_PRNGSpawn.discard(m_PRNGSpawnCount);
4902
4903 m_log << "PRNG state after restart " << randomSpawn(0) << endl;
4904
4905 parallelIo.getAttribute(&m_particleResiduum, "particleResiduum");
4906 }
4907
4908 ParallelIo::size_type beginPartId = 0;
4909 beginPartId = 0;
4910 for(MInt i = 0; i < localPartitionCellBeginning; ++i) {
4911 beginPartId += noParticlesPerPartitionCell[i];
4912 }
4913
4914 MInt localNoPart = noParticlesPerPartitionCell[localPartitionCellBeginning];
4915 for(MInt i = (localPartitionCellBeginning + 1); i < localPartitionCellEnd; ++i) {
4916 localNoPart += noParticlesPerPartitionCell[i];
4917 }
4918
4919 MInt checkPartCount = 0;
4920 MPI_Allreduce(&localNoPart, &checkPartCount, 1, MPI_INT, MPI_SUM, mpiComm(), AT_, "INPLACE", "localNoPart");
4921
4922 if(domainId() == 0 && checkPartCount != numberOfParts) {
4923 // numberOfParts is only set on the root-rank!
4924 TERM(-1);
4925 }
4926
4927 ASSERT(localNoPart >= 0, "ERROR: Invalid number of particles to be loaded during restart");
4928
4929 if(localNoPart == 0) {
4930 beginPartId = 0;
4931 }
4932 // if the last domain has nothing to load, beginPartId is
4933 // one index after the dimension of the array
4934
4935 // Preparing arrays and getting particle datas.
4936 if(numberOfParts > 0) {
4937 MLongScratchSpace ncmpiPartId(localNoPart, AT_, "ncmpiPartId");
4938 MFloatScratchSpace ncmpiDiameter(localNoPart, AT_, "ncmpiDiameter");
4939 MFloatScratchSpace ncmpiDensityRatio(localNoPart, AT_, "ncmpiDensityRatio");
4940 MIntScratchSpace ncmpiPartStatus(localNoPart, AT_, "ncmpiPartStatus");
4941 MFloatScratchSpace ncmpiPartCoords(3 * localNoPart, AT_, "PartCoords");
4942 MFloatScratchSpace ncmpiPartVel(3 * localNoPart, AT_, "ncmpiPartVel");
4943 MFloatScratchSpace ncmpiPartAccel(3 * localNoPart, AT_, "ncmpiPartAccel");
4944 MFloatScratchSpace ncmpiOldCoords(3 * localNoPart, AT_, "ncmpiOldCoords");
4945 MFloatScratchSpace ncmpiOldVel(3 * localNoPart, AT_, "ncmpiOldVel");
4946 MFloatScratchSpace ncmpiOldAccel(3 * localNoPart, AT_, "ncmpiOldAccel");
4947 MIntScratchSpace ncmpiPartParceledNo(localNoPart, AT_, "ncmpiPartParceledNo");
4948 MFloatScratchSpace ncmpiTemp(localNoPart, AT_, "ncmpiTemp");
4949 MFloatScratchSpace ncmpiDM(localNoPart, AT_, "ncmpiDM");
4950 MFloatScratchSpace ncmpiCT(localNoPart, AT_, "ncmpiCT");
4951 MFloatScratchSpace ncmpiBT(localNoPart, AT_, "ncmpiBT");
4952 MFloatScratchSpace ncmpiOldFluidDensity(localNoPart, AT_, "ncmpiOldFluidDensity");
4953 MFloatScratchSpace ncmpiOldFluidVelocity(3 * localNoPart, AT_, "ncmpiOldFluidVelocity");
4954 MFloatScratchSpace ncmpiShedD(localNoPart, AT_, "ncmpiShedD");
4955
4956 parallelIo.setOffset(localNoPart, beginPartId);
4957 parallelIo.readArray(ncmpiPartId.begin(), "partId");
4958 parallelIo.readArray(ncmpiDiameter.begin(), "partDia");
4959 // parallelIo.readArray(ncmpiDensityRatio.begin(), "partPpPf");
4960 parallelIo.readArray(ncmpiPartStatus.begin(), "partStatus");
4961 parallelIo.readArray(ncmpiPartParceledNo.begin(), "partParceledNo");
4962 parallelIo.readArray(ncmpiCT.begin(), "creationTime");
4963
4964 if(m_activePrimaryBUp || m_activeSecondaryBUp) {
4965 parallelIo.readArray(ncmpiBT.begin(), "breakUpTime");
4966 }
4967
4968
4969 if(m_activeSecondaryBUp) {
4970 parallelIo.readArray(ncmpiShedD.begin(), "shedD");
4971 }
4972
4973 if(m_heatCoupling || m_evaporation) {
4974 parallelIo.readArray(ncmpiTemp.begin(), "partTemp");
4975 parallelIo.readArray(ncmpiDM.begin(), "partDM");
4976 }
4977 parallelIo.readArray(ncmpiOldFluidDensity.begin(), "oldFluidDensity");
4978
4979 ParallelIo::size_type ncmpi3Start = 3 * beginPartId;
4980 ParallelIo::size_type ncmpi3Count = 3 * localNoPart;
4981 parallelIo.setOffset(ncmpi3Count, ncmpi3Start);
4982 parallelIo.readArray(ncmpiPartCoords.begin(), "partPos");
4983 parallelIo.readArray(ncmpiPartVel.begin(), "partVel");
4984 parallelIo.readArray(ncmpiPartAccel.begin(), "partAccel");
4985 parallelIo.readArray(ncmpiOldFluidVelocity.begin(), "oldFluidVelocity");
4986
4987 LPTSpherical<nDim> thisParticle;
4988
4989 for(MLong i = 0; i < localNoPart; ++i) {
4990 thisParticle.m_position[0] = ncmpiPartCoords[(3 * i)];
4991 thisParticle.m_position[1] = ncmpiPartCoords[(3 * i) + 1];
4992 thisParticle.m_position[2] = ncmpiPartCoords[(3 * i) + 2];
4993 // find matching cellId
4994 const MInt cellId = grid().findContainingLeafCell(&thisParticle.m_position[0]);
4995 if(cellId == -1) continue;
4996 if(a_isHalo(cellId)) {
4997 cerr << "Particle in halo-cell!" << endl;
4998 continue;
4999 }
5000
5001 thisParticle.m_cellId = cellId;
5002 ASSERT(thisParticle.m_cellId >= 0, "Invalid cellId! " + to_string(thisParticle.m_cellId));
5003 ASSERT(c_isLeafCell(thisParticle.m_cellId), "No leaf cell... " + std::to_string(thisParticle.m_cellId));
5004
5005 thisParticle.m_partId = ncmpiPartId[i];
5006 thisParticle.m_noParticles = ncmpiPartParceledNo[i];
5007 thisParticle.m_temperature = ncmpiTemp[i];
5008 thisParticle.m_dM = ncmpiDM[i];
5009 thisParticle.m_creationTime = ncmpiCT[i];
5010 thisParticle.m_breakUpTime = ncmpiBT[i];
5011 thisParticle.m_diameter = ncmpiDiameter[i];
5012 thisParticle.m_densityRatio = ncmpiDensityRatio[i];
5013 thisParticle.firstStep() = (ncmpiPartStatus[i] > 0);
5014 thisParticle.m_velocity[0] = ncmpiPartVel[(3 * i)];
5015 thisParticle.m_velocity[1] = ncmpiPartVel[(3 * i) + 1];
5016 thisParticle.m_velocity[2] = ncmpiPartVel[(3 * i) + 2];
5017 thisParticle.m_accel[0] = ncmpiPartAccel[(3 * i)];
5018 thisParticle.m_accel[1] = ncmpiPartAccel[(3 * i) + 1];
5019 thisParticle.m_accel[2] = ncmpiPartAccel[(3 * i) + 2];
5020
5021 thisParticle.m_oldCellId = cellId;
5022 for(MInt j = 0; j < nDim; j++) {
5023 thisParticle.m_oldPos[j] = thisParticle.m_position[j];
5024 thisParticle.m_oldVel[j] = thisParticle.m_velocity[j];
5025 thisParticle.m_oldAccel[j] = thisParticle.m_accel[j];
5026 }
5027
5028 thisParticle.m_oldFluidDensity = ncmpiOldFluidDensity[i];
5029 for(MInt j = 0; j < nDim; j++) {
5030 thisParticle.m_oldFluidVel[j] = ncmpiOldFluidVelocity[(3 * i) + j];
5031 }
5032
5033 thisParticle.updateProperties();
5034 if(m_activeSecondaryBUp) {
5035 thisParticle.m_shedDiam = ncmpiShedD[i];
5036 } else {
5037 thisParticle.m_shedDiam = thisParticle.m_diameter;
5038 }
5039
5040 m_partList.push_back(thisParticle);
5041 }
5042
5043 MLong sumPart = 0;
5044 MLong noPart = a_noParticles();
5045 MPI_Allreduce(&noPart, &sumPart, 1, type_traits<MLong>::mpiType(), MPI_SUM, mpiComm(), AT_, "INPLACE", "sumpart");
5046
5047 // check if everything is loaded
5048 if(domainId() == 0) {
5049 cerr << "Loaded " << numberOfParts << " particles of " << sumPart << endl;
5050
5051 if(numberOfParts != sumPart) {
5052 TERMM(-1, "invalid number of particles loaded");
5053 }
5054 }
5055 }
5056 }
5057
5058 if(m_ellipsoids) {
5059 particleRestartFilename =
5060 restartDir() + "restartPartEllipsoid_" + getIdentifier() + to_string(globalTimeStep) + ParallelIo::fileExt();
5061
5062 ParallelIo parallelIo2(particleRestartFilename, PIO_READ, mpiComm());
5063
5064 MInt loadedTimeStep2 = 0;
5065 parallelIo2.getAttribute(&loadedTimeStep2, "timestep");
5066 if(loadedTimeStep2 != globalTimeStep) {
5067 stringstream errorMessage;
5068 errorMessage << endl
5069 << "Error! restartTimeStep = " << globalTimeStep
5070 << " differs from timestep in "
5071 "restartPartEllipsoid"
5072 << ParallelIo::fileExt() << ", which is " << loadedTimeStep2 << endl;
5073 mTerm(1, AT_, errorMessage.str());
5074 }
5075
5076 // Get the number of particle each domain has.
5077 const MInt noGlobalPartitionCells = grid().localPartitionCellOffsetsRestart(2);
5078 const MInt localPartitionCellBeginning = grid().localPartitionCellOffsetsRestart(0);
5079 const MInt localPartitionCellEnd = grid().localPartitionCellOffsetsRestart(1);
5080
5081 MIntScratchSpace noEllipsoidsPerPartitionCell(noGlobalPartitionCells, AT_, "noParticlesPerPartitionCell");
5082
5083 if(domainId() == 0) {
5084 cerr << "noGlobalPartitionCells " << noGlobalPartitionCells << endl;
5085 }
5086
5087 // load number of particle in partition cell for all partition cells!
5088 parallelIo2.setOffset(noGlobalPartitionCells, 0);
5089 parallelIo2.readArray(&noEllipsoidsPerPartitionCell[0], "partCount");
5090
5091 // total number of ellipsoids
5092 MInt numberOfEllips = 0;
5093#ifdef _OPENMP
5094#pragma omp parallel for reduction(+ : numberOfEllips)
5095#endif
5096 for(MInt i = 0; i < noGlobalPartitionCells; ++i) {
5097 numberOfEllips += noEllipsoidsPerPartitionCell[i];
5098 }
5099
5100 if(domainId() == 0) {
5101 cerr << "Global number of ellipsoids loaded from restart file " << numberOfEllips << endl;
5102 }
5103 m_log << "Global number of ellipsoids loaded from restart file " << numberOfEllips << endl;
5104
5105 ParallelIo::size_type beginPartId = 0;
5106 beginPartId = 0;
5107 for(MInt i = 0; i < localPartitionCellBeginning; ++i) {
5108 beginPartId += noEllipsoidsPerPartitionCell[i];
5109 }
5110
5111 MInt localNoPart = noEllipsoidsPerPartitionCell[localPartitionCellBeginning];
5112 for(MInt i = (localPartitionCellBeginning + 1); i < localPartitionCellEnd; ++i) {
5113 localNoPart += noEllipsoidsPerPartitionCell[i];
5114 }
5115
5116 MInt checkPartCount = 0;
5117 MPI_Allreduce(&localNoPart, &checkPartCount, 1, MPI_INT, MPI_SUM, mpiComm(), AT_, "INPLACE", "localNoPart");
5118
5119 if(domainId() == 0 && checkPartCount != numberOfEllips) {
5120 // numberOfEllps is only set on the root-rank!
5121 TERM(-1);
5122 }
5123
5124 ASSERT(localNoPart >= 0, "ERROR: Invalid number of ellipsoids to be loaded during restart");
5125
5126 if(localNoPart == 0) {
5127 beginPartId = 0;
5128 }
5129
5130 // Preparing arrays and getting particle datas.
5131 if(numberOfEllips > 0) {
5132 MLongScratchSpace ncmpiPartId(localNoPart, AT_, "ncmpiPartId");
5133 MFloatScratchSpace ncmpiSemiMinorAxis(localNoPart, AT_, "ncmpiSemiMinorAxis");
5134 MFloatScratchSpace ncmpiAspectRatio(localNoPart, AT_, "ncmpiAspectRatio");
5135 MFloatScratchSpace ncmpiDensityRatio(localNoPart, AT_, "ncmpiDensityRatio");
5136 MIntScratchSpace ncmpiPartStatus(localNoPart, AT_, "ncmpiPartStatus");
5137 MFloatScratchSpace ncmpiPartCoords(3 * localNoPart, AT_, "ncmpiPartCoords");
5138 MFloatScratchSpace ncmpiPartVel(3 * localNoPart, AT_, "ncmpiPartVel");
5139 MFloatScratchSpace ncmpiPartAccel(3 * localNoPart, AT_, "ncmpiPartAccel");
5140 MFloatScratchSpace ncmpiPartAngVel(3 * localNoPart, AT_, "ncmpiPartVel");
5141 MFloatScratchSpace ncmpiPartAngAccel(3 * localNoPart, AT_, "ncmpiPartAngAccel");
5142 MFloatScratchSpace ncmpiPartOldCoords(3 * localNoPart, AT_, "ncmpiPartOldCoords");
5143 MFloatScratchSpace ncmpiPartOldVel(3 * localNoPart, AT_, "ncmpiPartOldVel");
5144 MFloatScratchSpace ncmpiPartOldAccel(3 * localNoPart, AT_, "ncmpiPartOldAccel");
5145 MFloatScratchSpace ncmpiPartOldAngVel(3 * localNoPart, AT_, "ncmpiPartOldAngVel");
5146 MFloatScratchSpace ncmpiPartOldAngAccel(3 * localNoPart, AT_, "ncmpiPartOldAngAccel");
5147 MFloatScratchSpace ncmpiPartQuat(4 * localNoPart, AT_, "ncmpiPartQuaternions");
5148 MFloatScratchSpace ncmpiPartOldQuat(4 * localNoPart, AT_, "ncmpiPartOldQuaternions");
5149 MFloatScratchSpace ncmpiTemp(localNoPart, AT_, "ncmpiTemp");
5150 MFloatScratchSpace ncmpiCT(localNoPart, AT_, "ncmpiCT");
5151 MFloatScratchSpace ncmpiOldFluidDensity(localNoPart, AT_, "ncmpiOldFluidDensity");
5152 MFloatScratchSpace ncmpiOldFluidVelocity(3 * localNoPart, AT_, "ncmpiOldFluidVelocity");
5153
5154 parallelIo2.setOffset(localNoPart, beginPartId);
5155 parallelIo2.readArray(ncmpiPartId.begin(), "partId");
5156 parallelIo2.readArray(ncmpiSemiMinorAxis.begin(), "partSemiMinorAxis");
5157 parallelIo2.readArray(ncmpiAspectRatio.begin(), "partAspectRatio");
5158 parallelIo2.readArray(ncmpiDensityRatio.begin(), "partDensityRatio");
5159 parallelIo2.readArray(ncmpiPartStatus.begin(), "partStatus");
5160 parallelIo2.readArray(ncmpiCT.begin(), "creationTime");
5161 if(m_heatCoupling) parallelIo2.readArray(ncmpiTemp.begin(), "partTemp");
5162 parallelIo2.readArray(ncmpiOldFluidDensity.begin(), "oldFluidDensity");
5163
5164 ParallelIo::size_type localNoPart3 = 3 * localNoPart;
5165 ParallelIo::size_type beginPartId3 = 3 * beginPartId;
5166 parallelIo2.setOffset(localNoPart3, beginPartId3);
5167 parallelIo2.readArray(ncmpiPartCoords.begin(), "partPos");
5168 parallelIo2.readArray(ncmpiPartVel.begin(), "partVel");
5169 parallelIo2.readArray(ncmpiPartAccel.begin(), "partAccel");
5170 parallelIo2.readArray(ncmpiPartAngVel.begin(), "partAngVel");
5171 parallelIo2.readArray(ncmpiPartAngAccel.begin(), "partAngAccel");
5172 parallelIo2.readArray(ncmpiPartOldCoords.begin(), "partOldPos");
5173 parallelIo2.readArray(ncmpiPartOldVel.begin(), "partOldVel");
5174 parallelIo2.readArray(ncmpiPartOldAccel.begin(), "partOldAccel");
5175 parallelIo2.readArray(ncmpiPartOldAngVel.begin(), "partOldAngVel");
5176 parallelIo2.readArray(ncmpiPartOldAngAccel.begin(), "partOldAngAccel");
5177
5178 ParallelIo::size_type localNoPart4 = 4 * localNoPart;
5179 ParallelIo::size_type beginPartId4 = 4 * beginPartId;
5180 parallelIo2.setOffset(localNoPart4, beginPartId4);
5181 parallelIo2.readArray(ncmpiPartQuat.begin(), "partQuat");
5182 parallelIo2.readArray(ncmpiPartOldQuat.begin(), "partOldQuat");
5183
5184 LPTEllipsoidal<nDim> thisParticleEllipsoid;
5185
5186 // If any particle, write particle to the particle list m_partList.
5187 for(MLong i = 0; i < localNoPart; ++i) {
5188 for(MInt n = 0; n < nDim; n++)
5189 thisParticleEllipsoid.m_position[n] = ncmpiPartCoords[(3 * i) + n];
5190 // find matching cellId
5191 const MInt cellId = grid().findContainingLeafCell(&thisParticleEllipsoid.m_position[0]);
5192 if(cellId == -1) continue;
5193 if(a_isHalo(cellId)) {
5194 cerr << "Particle in halo-cell!" << endl;
5195 continue;
5196 }
5197
5198 thisParticleEllipsoid.m_cellId = cellId;
5199 ASSERT(thisParticleEllipsoid.m_cellId >= 0, "Invalid cellId! " + to_string(thisParticleEllipsoid.m_cellId));
5200 ASSERT(c_isLeafCell(thisParticleEllipsoid.m_cellId),
5201 "No leaf cell... " + std::to_string(thisParticleEllipsoid.m_cellId));
5202 thisParticleEllipsoid.m_partId = ncmpiPartId[i];
5203 thisParticleEllipsoid.m_semiMinorAxis = ncmpiSemiMinorAxis[i];
5204 thisParticleEllipsoid.m_aspectRatio = ncmpiAspectRatio[i];
5205 thisParticleEllipsoid.initEllipsoialProperties();
5206 thisParticleEllipsoid.m_densityRatio = ncmpiDensityRatio[i];
5207 thisParticleEllipsoid.m_temperature = ncmpiTemp[i];
5208 thisParticleEllipsoid.m_creationTime = ncmpiCT[i];
5209 thisParticleEllipsoid.firstStep() = (ncmpiPartStatus[i] > 0);
5210 thisParticleEllipsoid.m_oldCellId = cellId;
5211 thisParticleEllipsoid.m_oldFluidDensity = ncmpiOldFluidDensity[i];
5212
5213 for(MInt n = 0; n < nDim; n++) {
5214 // velocites and accelerations
5215 thisParticleEllipsoid.m_velocity[n] = ncmpiPartVel[(3 * i) + n];
5216 thisParticleEllipsoid.m_accel[n] = ncmpiPartAccel[(3 * i) + n];
5217 thisParticleEllipsoid.m_angularVel[n] = ncmpiPartAngVel[(3 * i) + n];
5218 thisParticleEllipsoid.m_angularAccel[n] = ncmpiPartAngAccel[(3 * i) + n];
5219 // old position, velocities and accelerations
5220 thisParticleEllipsoid.m_oldPos[n] = ncmpiPartOldCoords[(3 * i) + n];
5221 thisParticleEllipsoid.m_oldVel[n] = ncmpiPartOldVel[(3 * i) + n];
5222 thisParticleEllipsoid.m_oldAccel[n] = ncmpiPartOldAccel[(3 * i) + n];
5223 thisParticleEllipsoid.m_oldAngularVel[n] = ncmpiPartOldAngVel[(3 * i) + n];
5224 thisParticleEllipsoid.m_oldAngularAccel[n] = ncmpiPartOldAngAccel[(3 * i) + n];
5225 // copy fluid velocity
5226 thisParticleEllipsoid.m_oldFluidVel[n] = ncmpiOldFluidVelocity[(3 * i) + n];
5227 }
5228 // store current and old quaternions
5229 for(MInt n = 0; n < 4; n++) {
5230 thisParticleEllipsoid.m_quaternion[n] = ncmpiPartQuat[(4 * i) + n];
5231 thisParticleEllipsoid.m_oldQuaternion[n] = ncmpiPartOldQuat[(4 * i) + n];
5232 }
5233 thisParticleEllipsoid.updateProperties();
5234
5235 m_partListEllipsoid.push_back(thisParticleEllipsoid);
5236 }
5237 MLong sumEllips = 0;
5238 MLong noEllips = a_noEllipsoidalParticles();
5239 MPI_Allreduce(&noEllips, &sumEllips, 1, type_traits<MLong>::mpiType(), MPI_SUM, mpiComm(), AT_, "INPLACE",
5240 "sumpart");
5241
5242 // check if everything is loaded
5243 if(domainId() == 0) {
5244 cerr << "Loaded " << numberOfEllips << " particles of " << sumEllips << endl;
5245
5246 if(numberOfEllips != sumEllips) {
5247 TERMM(-1, "invalid number of ellipsoids loaded");
5248 }
5249 }
5250 }
5251 }
5252
5253 MInt theSize = a_noParticles();
5254 own_sort(m_partList, sort_particleAfterPartIds<LPTBase<nDim>>::compare);
5255 if(m_ellipsoids) {
5256 theSize += a_noEllipsoidalParticles();
5257 own_sort(m_partListEllipsoid, sort_particleAfterPartIds<LPTBase<nDim>>::compare);
5258 }
5259
5260 m_addedParticle = 0;
5261 MLong noPart = a_noParticles();
5262 MPI_Allreduce(MPI_IN_PLACE, &noPart, 1, type_traits<MLong>::mpiType(), MPI_SUM, mpiComm(), AT_, "INPLACE", "noPart");
5263
5264 if(m_ellipsoids) {
5265 MLong noEllipsoids = a_noEllipsoidalParticles();
5266 MPI_Allreduce(MPI_IN_PLACE, &noEllipsoids, 1, type_traits<MLong>::mpiType(), MPI_SUM, mpiComm(), AT_, "INPLACE",
5267 "noPart");
5268 noPart += noEllipsoids;
5269 }
5270
5271 if(domainId() == m_spawnDomainId) {
5272 m_addedParticle = noPart;
5273 }
5274
5275 return theSize;
5276}
5277
5284template <MInt nDim>
5285void LPT<nDim>::writeCellSolutionFile(const MString& gridFileName, MInt* recalcIds) {
5286 TRACE();
5287
5288 MInt noCells;
5289 MInt noInternalCellIds;
5290 std::vector<MInt> recalcIdsSolver(0);
5291 std::vector<MInt> reOrderedCells(0);
5292 this->calcRecalcCellIdsSolver(recalcIds, noCells, noInternalCellIds, recalcIdsSolver, reOrderedCells);
5293 MInt* pointerRecalcIds = (recalcIds == nullptr) ? nullptr : recalcIdsSolver.data();
5294
5295 if(grid().newMinLevel() > 0) {
5296 cerr0 << "Skipping LPT solution file when min-level changes are applied!" << endl;
5297 return;
5298 }
5299
5300 MBool debugOutput = false;
5301#ifdef LPT_DEBUG
5302 debugOutput = true;
5303#endif
5304
5305 const MInt noIdParams = 0;
5306 const MInt noDbParams = 0;
5307 const MInt noIdVars = 1 + m_ellipsoids;
5308 // VolumeFraction + flowVariables + noSpecies + massCoupling + heatCoupling + momentumCoupling
5309 MInt noDbVars = 1 + PV.noVars() + m_evaporation + m_massCoupling + m_heatCoupling + (nDim + 1) * m_momentumCoupling;
5310 if(m_ellipsoids) {
5311 noDbVars += (nDim * nDim); // if ellipsoids are used also write out the velocity slopes
5312 }
5313 if(debugOutput) {
5314 if(this->m_adaptation && this->m_noSensors > 0) {
5315 noDbVars = noDbVars + 1;
5316 }
5317 if(m_wallCollisions) {
5318 noDbVars = noDbVars + 1;
5319 }
5320 }
5321
5322 MIntScratchSpace idVariables(noCells * noIdVars, AT_, "idVariables");
5323 MFloatScratchSpace dbVariables(noCells * noDbVars, AT_, "dbVariables");
5324 MIntScratchSpace idParameters(noIdParams, AT_, "idParameters");
5325 MFloatScratchSpace dbParameters(noDbParams, AT_, "dbParameters");
5326 vector<MString> dbVariablesName;
5327 vector<MString> idVariablesName;
5328 vector<MString> dbParametersName;
5329 vector<MString> idParametersName;
5330 vector<MString> name;
5331
5332 // TODO labels:LPT,IO @Julian, avoid using buffer
5333 MIntScratchSpace tmp(noCells, AT_, "tmp");
5334 MFloatScratchSpace tmpW(noCells, AT_, "tmpw");
5335
5336 // gather solver data:
5337 name.clear();
5338 name.push_back("noParticlesInCell");
5339 for(MInt cell = 0; cell < noCells; cell++) {
5340 tmp[cell] = a_noParticlesInCell(cell);
5341 }
5342 this->collectVariables(tmp.begin(), idVariables, name, idVariablesName, 1, noCells);
5343
5344 if(m_ellipsoids) {
5345 name.clear();
5346 name.push_back("noEllipsoidsInCell");
5347 for(MInt cell = 0; cell < noCells; cell++) {
5348 tmp[cell] = a_noEllipsoidsInCell(cell);
5349 }
5350 this->collectVariables(tmp.begin(), idVariables, name, idVariablesName, 1, noCells);
5351 }
5352
5353 if(m_massCoupling) {
5354 name.clear();
5355 name.push_back("massFlux");
5356 for(MInt cell = 0; cell < noCells; cell++) {
5357 tmpW[cell] = a_massFlux(cell);
5358 }
5359 this->collectVariables(tmpW.begin(), dbVariables, name, dbVariablesName, 1, noCells);
5360 }
5361
5362 if(m_momentumCoupling) {
5363 for(MInt i = 0; i < nDim; i++) {
5364 name.clear();
5365 string tmps = "momentumFlux" + to_string(i);
5366 name.push_back(tmps);
5367 for(MInt cell = 0; cell < noCells; cell++) {
5368 tmpW[cell] = a_momentumFlux(cell, i);
5369 }
5370 this->collectVariables(tmpW.begin(), dbVariables, name, dbVariablesName, 1, noCells);
5371 }
5372 name.clear();
5373 name.push_back("workFlux");
5374 for(MInt cell = 0; cell < noCells; cell++) {
5375 tmpW[cell] = a_workFlux(cell);
5376 }
5377 this->collectVariables(tmpW.begin(), dbVariables, name, dbVariablesName, 1, noCells);
5378 }
5379
5380 if(m_heatCoupling) {
5381 name.clear();
5382 name.push_back("heatFlux");
5383 for(MInt cell = 0; cell < noCells; cell++) {
5384 tmpW[cell] = a_heatFlux(cell);
5385 }
5386 this->collectVariables(tmpW.begin(), dbVariables, name, dbVariablesName, 1, noCells);
5387 }
5388
5389 name.clear();
5390 name.push_back("volumeFraction");
5391 for(MInt cell = 0; cell < noCells; cell++) {
5392 tmpW[cell] = a_volumeFraction(cell);
5393 }
5394 this->collectVariables(tmpW.begin(), dbVariables, name, dbVariablesName, 1, noCells);
5395
5396 for(MInt i = 0; i < nDim; i++) {
5397 name.clear();
5398 string tmps = "fluidVelocity_" + to_string(i);
5399 name.push_back(tmps);
5400 for(MInt cell = 0; cell < noCells; cell++) {
5401 tmpW[cell] = a_fluidVelocity(cell, i);
5402 }
5403 this->collectVariables(tmpW.begin(), dbVariables, name, dbVariablesName, 1, noCells);
5404 }
5405
5406 name.clear();
5407 name.push_back("fluidDensity");
5408 for(MInt cell = 0; cell < noCells; cell++) {
5409 tmpW[cell] = a_fluidDensity(cell);
5410 }
5411 this->collectVariables(tmpW.begin(), dbVariables, name, dbVariablesName, 1, noCells);
5412
5413 name.clear();
5414 name.push_back("fluidPressure");
5415 for(MInt cell = 0; cell < noCells; cell++) {
5416 tmpW[cell] = a_fluidPressure(cell);
5417 }
5418 this->collectVariables(tmpW.begin(), dbVariables, name, dbVariablesName, 1, noCells);
5419
5420 name.clear();
5421 name.push_back("fluidTemperture");
5422 for(MInt cell = 0; cell < noCells; cell++) {
5423 tmpW[cell] = a_fluidTemperature(cell);
5424 }
5425 this->collectVariables(tmpW.begin(), dbVariables, name, dbVariablesName, 1, noCells);
5426
5427 if(m_evaporation) {
5428 name.clear();
5429 name.push_back("fluidSpecies");
5430 for(MInt cell = 0; cell < noCells; cell++) {
5431 tmpW[cell] = a_fluidSpecies(cell);
5432 }
5433 this->collectVariables(tmpW.begin(), dbVariables, name, dbVariablesName, 1, noCells);
5434 }
5435
5436 if(m_ellipsoids) {
5437 for(MInt i = 0; i < nDim; i++) {
5438 for(MInt j = 0; j < nDim; j++) {
5439 name.clear();
5440 string tmps = "fluidVelocitySlopes_" + to_string(i) + "_" + to_string(j);
5441 name.push_back(tmps);
5442 for(MInt cell = 0; cell < noCells; cell++) {
5443 tmpW[cell] = a_velocitySlope(cell, i, j);
5444 }
5445 this->collectVariables(tmpW.begin(), dbVariables, name, dbVariablesName, 1, noCells);
5446 }
5447 }
5448 }
5449
5450 if(debugOutput && this->m_adaptation && this->m_noSensors > 0) {
5451 name.clear();
5452 name.push_back("regridTrigger");
5453 for(MInt cell = 0; cell < noCells; cell++) {
5454 tmpW[cell] = a_regridTrigger(cell);
5455 }
5456 this->collectVariables(tmpW.begin(), dbVariables, name, dbVariablesName, 1, noCells);
5457 }
5458
5459 if(debugOutput && m_wallCollisions) {
5460 name.clear();
5461 name.push_back("boundaryCellId");
5462 for(MInt cell = 0; cell < noCells; cell++) {
5463 tmpW[cell] = a_bndryCellId(cell);
5464 if(!a_isValidCell(cell)) {
5465 tmpW[cell] = -2;
5466 }
5467 }
5468 this->collectVariables(tmpW.begin(), dbVariables, name, dbVariablesName, 1, noCells);
5469 }
5470
5471 // build file name
5472 stringstream fileName;
5473 fileName.clear();
5474 fileName.str("");
5475 fileName << outputDir() << "solutionLPT_" << getIdentifier(true) << globalTimeStep << ParallelIo::fileExt();
5476
5477 this->saveGridFlowVars((fileName.str()).c_str(), gridFileName.c_str(), noCells, noInternalCellIds, dbVariables,
5478 dbVariablesName, 0, idVariables, idVariablesName, 0, dbParameters, dbParametersName,
5479 idParameters, idParametersName, pointerRecalcIds, -1);
5480}
5481
5494template <MInt nDim>
5495MInt LPT<nDim>::addParticle(const MInt cellId, const MFloat diameter, const MFloat particleDensityRatio,
5496 const MInt random, const MInt addMode, const MFloat* velocity, const MFloat* position,
5497 const MInt parcelSize) {
5498 LPTSpherical<nDim> thisParticle;
5499 const MFloat epsilon = MFloatEps;
5500 const MFloat cellLength = 0.5 * c_cellLengthAtCell(cellId) - epsilon;
5501
5502 thisParticle.m_cellId = cellId;
5503 thisParticle.m_oldCellId = cellId;
5504 thisParticle.m_diameter = diameter;
5505 thisParticle.m_shedDiam = diameter;
5506 thisParticle.m_temperature = m_material->T();
5507 thisParticle.m_noParticles = parcelSize;
5508 thisParticle.m_dM = 0;
5509
5510 if(addMode == 0) {
5511 for(MInt j = 0; j < nDim; j++) {
5512 // add particle around the cell-coordinate
5513 // include a small offset w.r.t.(with respect to) the cell coordinates, so that the
5514 // interpolation stencil is unambiguously defined later on
5515 thisParticle.m_position[j] = thisParticle.m_oldPos[j] =
5516 c_coordinate(cellId, j) + epsilon - random * (-cellLength + 2 * cellLength * rand() / (RAND_MAX + 1.0));
5517 thisParticle.m_velocity[j] = thisParticle.m_oldVel[j] = a_fluidVelocity(cellId, j);
5518
5519 thisParticle.m_accel[j] = 0.0;
5520 }
5521 } else if(addMode == 1 || addMode == 2) {
5522 // for spray-injection the position and velocity of new particles is specified
5523 for(MInt j = 0; j < nDim; j++) {
5524 thisParticle.m_position[j] = position[j];
5525 thisParticle.m_oldPos[j] = c_coordinate(cellId, j);
5526 thisParticle.m_velocity[j] = velocity[j];
5527 thisParticle.m_oldVel[j] = velocity[j];
5528 thisParticle.m_accel[j] = 0.0;
5529 }
5530 } else if(addMode == 3) {
5531 for(MInt j = 0; j < nDim; j++) {
5532 thisParticle.m_position[j] = position[j] + MFloatEps;
5533 thisParticle.m_oldPos[j] = c_coordinate(cellId, j);
5534 thisParticle.m_velocity[j] = velocity[j];
5535 thisParticle.m_oldVel[j] = velocity[j];
5536 thisParticle.m_accel[j] = 0.0;
5537 }
5538 } else {
5539 mTerm(1, AT_, "Unknown particle add-Mode!");
5540 }
5541
5542 thisParticle.m_oldFluidDensity = a_fluidDensity(thisParticle.m_cellId);
5543 for(MInt i = 0; i < nDim; i++) {
5544 thisParticle.m_oldFluidVel[i] = a_fluidVelocity(thisParticle.m_cellId, i);
5545 }
5546 thisParticle.m_densityRatio = particleDensityRatio;
5547
5548 thisParticle.updateProperties();
5549 thisParticle.firstStep() = true;
5550
5551 // update the cellId and compute createTime!
5552 MFloat time = 0;
5553 if(addMode == 1) {
5554 thisParticle.checkCellChange(m_spawnCoord);
5555 } else if(addMode == 2) {
5556 ASSERT(m_activePrimaryBUp, "");
5557 thisParticle.checkCellChange(m_spawnCoord, !m_sprayModel->m_broadcastInjected);
5558 uniform_real_distribution<MFloat> randMov(0, m_timeStep); // - MFloatEps);
5559 time = randMov(m_sprayModel->randomPrimBreakUp(1));
5560 if(time > m_timeStep) {
5561 time = m_timeStep;
5562 }
5563 } else if(addMode == 3) {
5564 ASSERT(m_activeSecondaryBUp, "");
5565
5566 thisParticle.m_fluidDensity = a_fluidDensity(thisParticle.m_cellId);
5567 for(MInt i = 0; i < nDim; i++) {
5568 thisParticle.m_fluidVel[i] = a_fluidVelocity(thisParticle.m_cellId, i);
5569 }
5570 }
5571
5572
5573 thisParticle.m_creationTime = time;
5574 thisParticle.m_breakUpTime = -time;
5575
5576 if(thisParticle.m_creationTime < 0 || thisParticle.m_creationTime > m_timeStep) {
5577 mTerm(1, AT_, "Invalid creation Time");
5578 }
5579
5580#ifdef _OPENMP
5581#pragma omp critical
5582#endif
5583 {
5584 // create a unique particle id
5585 thisParticle.m_partId = static_cast<MLong>(domainId() * 1E9 + m_addedParticle);
5586 m_addedParticle++;
5587 m_partList.push_back(thisParticle);
5588 }
5589
5590 return a_noParticles() - 1;
5591}
5592
5606template <MInt nDim>
5607MInt LPT<nDim>::addEllipsoid(const MInt cellId, const MFloat semiMinorAxis, const MFloat aspectRatio,
5608 const MFloat particleDensityRatio, const MInt random, const MInt addMode,
5609 const MFloat* velocity, const MFloat* position) {
5610 LPTEllipsoidal<nDim> thisParticle;
5611 const MFloat epsilon = MFloatEps;
5612 const MFloat cellLength = 0.5 * c_cellLengthAtCell(cellId) - epsilon;
5613
5614 thisParticle.m_cellId = cellId;
5615 thisParticle.m_oldCellId = cellId;
5616 thisParticle.m_temperature = m_material->T();
5617 thisParticle.m_semiMinorAxis = semiMinorAxis;
5618 thisParticle.m_aspectRatio = aspectRatio;
5619 thisParticle.initEllipsoialProperties();
5620
5621 if(addMode == 0) {
5622 for(MInt j = 0; j < nDim; j++) {
5623 // add particle around the cell-coordinate
5624 // include a small offset w.r.t.(with respect to) the cell coordinates, so that the
5625 // interpolation stencil is unambiguously defined later on
5626 thisParticle.m_position[j] = thisParticle.m_oldPos[j] =
5627 c_coordinate(cellId, j) + epsilon - random * (-cellLength + 2 * cellLength * rand() / (RAND_MAX + 1.0));
5628 thisParticle.m_velocity[j] = thisParticle.m_oldVel[j] = a_fluidVelocity(cellId, j);
5629 thisParticle.m_angularVel[j] = F0;
5630 thisParticle.m_accel[j] = F0;
5631 thisParticle.m_angularAccel[j] = F0;
5632 }
5633 } else if(addMode == 1 || addMode == 2) {
5634 // for spray-injection the position and velocity of new particles is specified
5635 for(MInt j = 0; j < nDim; j++) {
5636 thisParticle.m_position[j] = position[j];
5637 thisParticle.m_oldPos[j] = c_coordinate(cellId, j);
5638 thisParticle.m_velocity[j] = velocity[j];
5639 thisParticle.m_oldVel[j] = velocity[j];
5640 thisParticle.m_angularVel[j] = F0;
5641 thisParticle.m_accel[j] = F0;
5642 thisParticle.m_angularAccel[j] = F0;
5643 }
5644 } else if(addMode == 3) {
5645 for(MInt j = 0; j < nDim; j++) {
5646 thisParticle.m_position[j] = position[j] + MFloatEps;
5647 thisParticle.m_oldPos[j] = c_coordinate(cellId, j);
5648 thisParticle.m_velocity[j] = velocity[j];
5649 thisParticle.m_oldVel[j] = velocity[j];
5650 thisParticle.m_angularVel[j] = F0;
5651 thisParticle.m_accel[j] = F0;
5652 thisParticle.m_angularAccel[j] = F0;
5653 }
5654 } else {
5655 mTerm(1, AT_, "Unknown particle add-Mode!");
5656 }
5657
5658 if(m_ellipsoidRandomOrientation != 0) {
5659 mt19937_64 randomOrientationGen(m_ellipsoidRandomOrientationSeed);
5660 uniform_real_distribution<MFloat> dist(F0, F1);
5661 MFloat alpha = dist(randomOrientationGen);
5662 MFloat beta = dist(randomOrientationGen);
5663 MFloat gamma = dist(randomOrientationGen);
5664 thisParticle.m_quaternion[0] = sqrt(1 - alpha) * sin(2 * PI * beta);
5665 thisParticle.m_quaternion[1] = sqrt(1 - alpha) * cos(2 * PI * beta);
5666 thisParticle.m_quaternion[2] = sqrt(alpha) * sin(2 * PI * gamma);
5667 thisParticle.m_quaternion[3] = sqrt(alpha) * cos(2 * PI * gamma);
5668 MFloat normFactor = F0;
5669 for(MFloat k : thisParticle.m_quaternion) {
5670 normFactor += k * k;
5671 }
5672 normFactor = sqrt(normFactor);
5673 for(MInt i = 0; i < 4; i++) {
5674 thisParticle.m_quaternion[i] /= normFactor;
5675 thisParticle.m_oldQuaternion[i] = thisParticle.m_quaternion[i];
5676 }
5677 }
5678
5679 thisParticle.m_oldFluidDensity = a_fluidDensity(thisParticle.m_cellId);
5680 for(MInt i = 0; i < nDim; i++) {
5681 thisParticle.m_oldFluidVel[i] = a_fluidVelocity(thisParticle.m_cellId, i);
5682 }
5683 thisParticle.m_densityRatio = particleDensityRatio;
5684
5685 thisParticle.updateProperties();
5686 thisParticle.firstStep() = true;
5687
5688 // update the cellId and compute createTime!
5689 MFloat time = 0;
5690 if(addMode == 1) {
5691 thisParticle.checkCellChange(m_spawnCoord);
5692 } else if(addMode == 2) {
5693 ASSERT(m_activePrimaryBUp, "");
5694 thisParticle.checkCellChange(m_spawnCoord, !m_sprayModel->m_broadcastInjected);
5695 uniform_real_distribution<MFloat> randMov(0, m_timeStep); // - MFloatEps);
5696 time = randMov(m_sprayModel->randomPrimBreakUp(1));
5697 if(time > m_timeStep) {
5698 time = m_timeStep;
5699 }
5700 } else if(addMode == 3) {
5701 ASSERT(m_activeSecondaryBUp, "");
5702
5703 thisParticle.m_fluidDensity = a_fluidDensity(thisParticle.m_cellId);
5704 for(MInt i = 0; i < nDim; i++) {
5705 thisParticle.m_fluidVel[i] = a_fluidVelocity(thisParticle.m_cellId, i);
5706 }
5707 }
5708
5709 thisParticle.m_creationTime = time;
5710
5711 if(thisParticle.m_creationTime < 0 || thisParticle.m_creationTime > m_timeStep) {
5712 mTerm(1, AT_, "Invalid creation Time");
5713 }
5714
5715#ifdef _OPENMP
5716#pragma omp critical
5717#endif
5718 {
5719 // create a unique particle id
5720 thisParticle.m_partId = static_cast<MLong>(domainId() * 1E9 + m_addedParticle);
5721 m_addedParticle++;
5722 m_partListEllipsoid.push_back(thisParticle);
5723 }
5724
5725 return a_noParticles() + a_noEllipsoidalParticles() - 1;
5726}
5727
5728
5733template <MInt nDim>
5735 // particles are spawned on another domain
5736 if(m_spawnCellId < 0) {
5737 if(noDomains() > 1) {
5738 // receive injected particles instead
5739 recvInjected();
5740 }
5741 m_particleResiduum = 0.0;
5742 return;
5743 }
5744 ASSERT(domainId() == m_spawnDomainId, "");
5745
5746 const MInt prevNo = m_partList.empty() ? 0 : m_partList.size() - 1;
5747 const auto noNewParticles = (MInt)(m_spawnParticlesCount * m_timeStep + m_particleResiduum);
5748 m_particleResiduum += m_spawnParticlesCount * m_timeStep - noNewParticles;
5749
5750 if(noNewParticles >= 1) {
5751 MFloatScratchSpace spawnParticlesInitVelo(nDim, AT_, "spawnParticlesInitVelo");
5752
5753 for(MInt i = 0; i < noNewParticles; i++) {
5754 m_PRNGSpawnCount +=
5755 randomVectorInCone(&spawnParticlesInitVelo[0], &m_spawnDir[0], m_spawnVelocity, m_spawnParticlesConeAngle,
5756 m_spawnEmittDist, randomSpawn(0), m_spawnDistSigmaCoeff, 0);
5757
5758 addParticle(m_spawnCellId, m_spawnDiameter, m_material->densityRatio(), 0, 1, &spawnParticlesInitVelo[0],
5759 &m_spawnCoord[0], 1);
5760 }
5761
5762
5763 broadcastInjected(prevNo);
5764
5765#ifdef LPT_DEBUG
5766 cerr << "added " << noNewParticles << " new particles with diameter " << m_spawnDiameter << " to cell "
5767 << m_spawnCellId << endl;
5768#endif
5769 }
5770}
5771
5772
5777template <MInt nDim>
5779 cerr << "//////////////////////////////////////////////////////////////////////////////////////////////////" << endl;
5780 cerr << "/// Particle INITIALISATION INFORMATION ///" << endl;
5781 cerr << "///--------------------------------------------------------------------------------------------///" << endl;
5782 cerr << "spawning is active : " << boolalpha << m_spawnParticles << endl;
5783 cerr << "spray model is active : " << boolalpha << (MBool)(m_activePrimaryBUp || m_activeSecondaryBUp)
5784 << endl;
5785 cerr << "momentum coupling is active : " << boolalpha << m_momentumCoupling << endl;
5786 if(m_momentumCoupling || m_heatCoupling) {
5787 cerr << " * redistribution is active : " << boolalpha << m_couplingRedist << endl;
5788 cerr << " - redistribution area is set to : " << m_noRedistLayer << endl;
5789 }
5790 cerr << "drag modelling is active : " << boolalpha << (m_dragModelType > 0) << endl;
5791 if(m_dragModelType > 0) {
5792 cerr << " * drag model used is : " << m_dragModelType << endl;
5793 }
5794 if(m_ellipsoids) {
5795 cerr << "lift modelling is active : " << boolalpha << (m_liftModelType > 0) << endl;
5796 if(m_liftModelType > 0) {
5797 cerr << " * lift model used is : " << m_liftModelType << endl;
5798 }
5799 cerr << "torque modelling is active : " << boolalpha << (m_torqueModelType > 0) << endl;
5800 if(m_torqueModelType > 0) {
5801 cerr << " * torque model used is : " << m_torqueModelType << endl;
5802 }
5803 }
5804 cerr << "//////////////////////////////////////////////////////////////////////////////////////////////////" << endl;
5805}
5806
5808// if the injection spreads across non-neighbor domains on the min-Level!
5811template <MInt nDim>
5813 TRACE();
5814
5815 MInt partReceive = 0;
5816
5817 // wait for number
5818 MPI_Bcast(&partReceive, 1, MPI_INT, m_spawnDomainId, mpiComm(), AT_, "part");
5819
5820#ifndef NDEBUG
5821 MInt noParticlesB = -static_cast<MInt>(m_partList.size());
5822 // Note: cast to signed type first to prevent overflow
5823#endif
5824
5825 if(partReceive > 0) {
5826 // receive
5827 MPI_Bcast(&m_intRecvBuffer[0][0], partReceive * intElemPerP<LPTSpherical<nDim>>(), MPI_INT, m_spawnDomainId,
5828 mpiComm(), AT_, "buffer");
5829 MPI_Bcast(&m_recvBuffer[0][0], partReceive * elemPerP<LPTSpherical<nDim>>(), MPI_DOUBLE, m_spawnDomainId, mpiComm(),
5830 AT_, "buffer");
5831
5832 // unpack and check whether they are on this domain
5833 unpackParticles<LPTSpherical<nDim>, true>(partReceive, m_intRecvBuffer[0].get(), m_recvBuffer[0].get());
5834 }
5835
5836#ifndef NDEBUG
5837 noParticlesB += m_partList.size();
5838 MPI_Allreduce(MPI_IN_PLACE, &noParticlesB, 1, MPI_INT, MPI_SUM, mpiComm(), AT_, "INPLACE", "noParticlesB");
5839#endif
5840}
5841
5849template <MInt nDim>
5850void LPT<nDim>::packParticles(vector<LPTSpherical<nDim>*>& particlesToSend, MInt* intBuffer, MFloat* floatBuffer,
5851 vector<MInt> cellIds) {
5852 MInt z = 0;
5853 if(intBuffer != nullptr) {
5854 for(MInt id = 0; id < (MInt)particlesToSend.size(); id++) {
5855 // splitting long into two ints
5856 intBuffer[z++] = (MInt)(particlesToSend[id]->m_partId >> 32);
5857 intBuffer[z++] = MInt(particlesToSend[id]->m_partId);
5858 intBuffer[z++] = cellIds[id];
5859 intBuffer[z++] = particlesToSend[id]->m_noParticles;
5860 intBuffer[z++] = ((particlesToSend[id]->firstStep() ? 1 : 0) + (particlesToSend[id]->reqSend() ? 2 : 0));
5861 intBuffer[z++] = (MInt)(particlesToSend[id]->hadWallColl());
5862 if(id == 0 && z != intElemPerP<LPTSpherical<nDim>>()) {
5863 mTerm(1, AT_, "Buffersize missmatching!");
5864 }
5865 }
5866 }
5867
5868 if(floatBuffer != nullptr) {
5869 z = 0;
5870 for(MInt id = 0; id < (MInt)particlesToSend.size(); id++) {
5871 floatBuffer[z++] = particlesToSend[id]->m_diameter;
5872 floatBuffer[z++] = particlesToSend[id]->m_densityRatio;
5873 for(MInt j = 0; j < nDim; j++) {
5874 floatBuffer[z++] = particlesToSend[id]->m_position.at(j);
5875 }
5876 for(MInt j = 0; j < nDim; j++) {
5877 floatBuffer[z++] = particlesToSend[id]->m_velocity.at(j);
5878 }
5879 for(MInt j = 0; j < nDim; j++) {
5880 floatBuffer[z++] = particlesToSend[id]->m_accel.at(j);
5881 }
5882 for(MInt j = 0; j < nDim; j++) {
5883 floatBuffer[z++] = particlesToSend[id]->m_oldPos.at(j);
5884 }
5885 for(MInt j = 0; j < nDim; j++) {
5886 floatBuffer[z++] = particlesToSend[id]->m_oldVel.at(j);
5887 }
5888 for(MInt j = 0; j < nDim; j++) {
5889 floatBuffer[z++] = particlesToSend[id]->m_oldAccel.at(j);
5890 }
5891 floatBuffer[z++] = particlesToSend[id]->m_creationTime;
5892 floatBuffer[z++] = particlesToSend[id]->m_breakUpTime;
5893 floatBuffer[z++] = particlesToSend[id]->m_shedDiam;
5894 floatBuffer[z++] = particlesToSend[id]->m_temperature;
5895 floatBuffer[z++] = particlesToSend[id]->m_dM;
5896 floatBuffer[z++] = particlesToSend[id]->m_heatFlux;
5897 floatBuffer[z++] = particlesToSend[id]->m_fluidVelMag;
5898
5899 floatBuffer[z++] = particlesToSend[id]->m_oldFluidDensity;
5900 for(MInt j = 0; j < nDim; j++) {
5901 floatBuffer[z++] = particlesToSend[id]->m_oldFluidVel[j];
5902 }
5903 if(id == 0 && z != elemPerP<LPTSpherical<nDim>>()) {
5904 mTerm(1, AT_, "Buffersize missmatching!");
5905 }
5906 if(intBuffer == nullptr) {
5907 MInt partId1 = (MInt)(particlesToSend[id]->m_partId >> 32);
5908 MInt partId2 = MInt(particlesToSend[id]->m_partId);
5909 floatBuffer[z++] = (MFloat)partId1;
5910 floatBuffer[z++] = (MFloat)partId2;
5911 floatBuffer[z++] = particlesToSend[id]->m_noParticles;
5912 }
5913 }
5914 }
5915
5916 if(intBuffer != nullptr && floatBuffer != nullptr) {
5917 m_noSendParticles++;
5918 }
5919}
5920
5921
5927template <MInt nDim>
5928void LPT<nDim>::packParticles(vector<LPTEllipsoidal<nDim>*>& particlesToSend, MInt* intBuffer, MFloat* floatBuffer,
5929 vector<MInt> cellIds) {
5930 if(!m_ellipsoids) {
5931 mTerm(1, AT_, "Only allowed for ellipsoids!");
5932 }
5933
5934 MInt z = 0;
5935 if(intBuffer != nullptr) {
5936 for(MInt id = 0; id < (MInt)particlesToSend.size(); id++) {
5937 // splitting long into two ints
5938 intBuffer[z++] = (MInt)(particlesToSend[id]->m_partId >> 32);
5939 intBuffer[z++] = MInt(particlesToSend[id]->m_partId);
5940 intBuffer[z++] = cellIds[id];
5941 intBuffer[z++] = ((particlesToSend[id]->firstStep() ? 1 : 0) + (particlesToSend[id]->reqSend() ? 2 : 0));
5942 intBuffer[z++] = (MInt)(particlesToSend[id]->hadWallColl());
5943 if(id == 0 && z != intElemPerP<LPTEllipsoidal<nDim>>()) {
5944 mTerm(1, AT_, "Buffersize missmatching!");
5945 }
5946 }
5947 }
5948
5949 if(floatBuffer != nullptr) {
5950 z = 0;
5951 for(MInt id = 0; id < (MInt)particlesToSend.size(); id++) {
5952 floatBuffer[z++] = particlesToSend[id]->m_semiMinorAxis;
5953 floatBuffer[z++] = particlesToSend[id]->m_aspectRatio;
5954 floatBuffer[z++] = particlesToSend[id]->m_densityRatio;
5955 for(MInt j = 0; j < nDim; j++)
5956 floatBuffer[z++] = particlesToSend[id]->m_position.at(j);
5957 for(MInt j = 0; j < nDim; j++)
5958 floatBuffer[z++] = particlesToSend[id]->m_velocity.at(j);
5959 for(MInt j = 0; j < nDim; j++)
5960 floatBuffer[z++] = particlesToSend[id]->m_accel.at(j);
5961 for(MInt j = 0; j < nDim; j++)
5962 floatBuffer[z++] = particlesToSend[id]->m_angularVel.at(j);
5963 for(MInt j = 0; j < nDim; j++)
5964 floatBuffer[z++] = particlesToSend[id]->m_angularAccel.at(j);
5965 for(MInt j = 0; j < 4; j++)
5966 floatBuffer[z++] = particlesToSend[id]->m_quaternion.at(j);
5967 for(MInt j = 0; j < nDim; j++)
5968 floatBuffer[z++] = particlesToSend[id]->m_oldPos.at(j);
5969 for(MInt j = 0; j < nDim; j++)
5970 floatBuffer[z++] = particlesToSend[id]->m_oldVel.at(j);
5971 for(MInt j = 0; j < nDim; j++)
5972 floatBuffer[z++] = particlesToSend[id]->m_oldAccel.at(j);
5973 for(MInt j = 0; j < nDim; j++)
5974 floatBuffer[z++] = particlesToSend[id]->m_oldAngularVel.at(j);
5975 for(MInt j = 0; j < nDim; j++)
5976 floatBuffer[z++] = particlesToSend[id]->m_oldAngularAccel.at(j);
5977 for(MInt j = 0; j < 4; j++)
5978 floatBuffer[z++] = particlesToSend[id]->m_oldQuaternion.at(j);
5979 floatBuffer[z++] = particlesToSend[id]->m_creationTime;
5980 floatBuffer[z++] = particlesToSend[id]->m_temperature;
5981 floatBuffer[z++] = particlesToSend[id]->m_heatFlux;
5982 floatBuffer[z++] = particlesToSend[id]->m_fluidVelMag;
5983 floatBuffer[z++] = particlesToSend[id]->m_oldFluidDensity;
5984 for(MInt j = 0; j < nDim; j++)
5985 floatBuffer[z++] = particlesToSend[id]->m_oldFluidVel[j];
5986
5987 if(id == 0 && z != elemPerP<LPTEllipsoidal<nDim>>()) {
5988 mTerm(1, AT_, "Buffersize missmatching!");
5989 }
5990 if(intBuffer == nullptr) {
5991 MInt partId1 = (MInt)(particlesToSend[id]->m_partId >> 32);
5992 MInt partId2 = MInt(particlesToSend[id]->m_partId);
5993 floatBuffer[z++] = (MFloat)partId1;
5994 floatBuffer[z++] = (MFloat)partId2;
5995 }
5996 }
5997 }
5998}
5999
6000
6006template <MInt nDim>
6007template <class LPTParticle, MBool t_search>
6008void LPT<nDim>::unpackParticles(const MInt num, const MInt* intBuffer, const MFloat* floatBuffer, const MInt domain,
6009 const MBool allowNonLeaf) {
6010 MInt z = 0;
6011 MInt h = 0;
6012 MInt i = -1;
6013 for(MInt id = 0; id < num; id++) {
6014 LPTParticle thisParticle;
6015 unpackParticle(thisParticle, intBuffer, z, floatBuffer, h, i, id);
6016
6017 thisParticle.initProperties();
6018 thisParticle.firstStep() = ((i % 2) > 0);
6019
6020 // search for new cellId
6021 if(t_search) {
6022 const MInt cellId = grid().findContainingLeafCell(&thisParticle.m_position[0]);
6023
6024 if(cellId >= 0 && !a_isHalo(cellId)) {
6025 thisParticle.m_cellId = cellId;
6026 } else {
6027 continue;
6028 }
6029 } else { // use cellId from halo/window cell collector
6030 if(i < 2) {
6031 // before the particle-particle collision step,
6032 // when window particles are communicated back to the halo part for collision
6033 thisParticle.m_cellId = haloCellId(domain, thisParticle.m_cellId);
6034 thisParticle.wasSend() = true;
6035 thisParticle.toBeDeleted() = false;
6036 thisParticle.isInvalid() = true;
6037 ASSERT(m_collisions > 0, "");
6038 } else {
6039 // NOTE: at this point the cellId represents the entry to the matching
6040 // halo/windowcellId in the corresponding containers!
6041 if(thisParticle.m_cellId < 0) {
6042 mTerm(1, AT_, "ERROR: Particle has no valid cell!");
6043 }
6044 // regular exchange from halo to window => thus already knowing the matching cellId
6045 // and avoiding the search for the matching cellId!
6046 thisParticle.m_cellId = windowCellId(domain, thisParticle.m_cellId);
6047 // update window/halo cell properties only
6048 thisParticle.updateProperties(false);
6049
6050 // loop down and find leaf-windowcell for particles
6051 // which where on a non-leaf halo-cell
6052 if(allowNonLeaf && !c_isLeafCell(thisParticle.m_cellId)) {
6053 thisParticle.m_cellId = grid().findContainingLeafCell(&thisParticle.m_position[0], thisParticle.m_cellId);
6054 thisParticle.m_oldCellId = thisParticle.m_cellId;
6055
6056 if(!thisParticle.firstStep()) {
6057 mTerm(1, AT_, "Non-leaf comm. of not injected cell requested!");
6058 }
6059 }
6060 if(!c_isLeafCell(thisParticle.m_cellId)) {
6061 mTerm(1, AT_, "ERROR: Particle has no valid leaf-cell!");
6062 }
6063 }
6064 }
6065
6066 if(thisParticle.m_cellId < 0) {
6067 mTerm(1, AT_, "ERROR: Particle has no valid cell!");
6068 }
6069
6070 if(thisParticle.m_oldCellId < 0) {
6071 thisParticle.m_oldCellId = grid().findContainingLeafCell(&thisParticle.m_oldPos[0], thisParticle.m_cellId);
6072 }
6073
6074 std::vector<LPTParticle>& particleList = a_particleList<LPTParticle>();
6075 particleList.push_back(thisParticle);
6076 }
6077}
6078
6079
6083template <MInt nDim>
6084void LPT<nDim>::unpackParticle(LPTSpherical<nDim>& thisParticle, const MInt* intBuffer, MInt& z,
6085 const MFloat* floatBuffer, MInt& h, MInt& step, MInt id) {
6086 if(intBuffer != nullptr) {
6087 MInt a = intBuffer[z++];
6088 MInt b = intBuffer[z++];
6089 MLong partId = ((MLong)a << 32 | ((MLong)b & 0xFFFFFFFFL));
6090 thisParticle.m_partId = partId;
6091 thisParticle.m_cellId = intBuffer[z++];
6092 thisParticle.m_noParticles = intBuffer[z++];
6093 step = intBuffer[z++];
6094 thisParticle.hadWallColl() = (MBool)intBuffer[z++];
6095 if(id == 0 && z != intElemPerP<LPTSpherical<nDim>>()) {
6096 mTerm(1, AT_, "Buffersize missmatching!");
6097 }
6098 }
6099
6100 thisParticle.m_diameter = floatBuffer[h++];
6101 thisParticle.m_densityRatio = floatBuffer[h++];
6102 for(MInt l = 0; l < nDim; l++) {
6103 thisParticle.m_position.at(l) = floatBuffer[h++];
6104 }
6105 for(MInt l = 0; l < nDim; l++) {
6106 thisParticle.m_velocity.at(l) = floatBuffer[h++];
6107 }
6108 for(MInt l = 0; l < nDim; l++) {
6109 thisParticle.m_accel.at(l) = floatBuffer[h++];
6110 }
6111 for(MInt l = 0; l < nDim; l++) {
6112 thisParticle.m_oldPos.at(l) = floatBuffer[h++];
6113 }
6114 for(MInt l = 0; l < nDim; l++) {
6115 thisParticle.m_oldVel.at(l) = floatBuffer[h++];
6116 }
6117 for(MInt l = 0; l < nDim; l++) {
6118 thisParticle.m_oldAccel.at(l) = floatBuffer[h++];
6119 }
6120 thisParticle.m_creationTime = floatBuffer[h++];
6121 thisParticle.m_breakUpTime = floatBuffer[h++];
6122 thisParticle.m_shedDiam = floatBuffer[h++];
6123 thisParticle.m_temperature = floatBuffer[h++];
6124 thisParticle.m_dM = floatBuffer[h++];
6125 thisParticle.m_heatFlux = floatBuffer[h++];
6126 thisParticle.m_fluidVelMag = floatBuffer[h++];
6127
6128 thisParticle.m_oldFluidDensity = floatBuffer[h++];
6129 for(MInt l = 0; l < nDim; l++) {
6130 thisParticle.m_oldFluidVel[l] = floatBuffer[h++];
6131 }
6132 if(id == 0 && h != elemPerP<LPTSpherical<nDim>>()) {
6133 mTerm(1, AT_, "Buffersize missmatching!");
6134 }
6135
6136 if(intBuffer == nullptr) {
6137 MInt a = (MInt)floatBuffer[h++];
6138 MInt b = (MInt)floatBuffer[h++];
6139 MLong partId = ((MLong)a << 32 | ((MLong)b & 0xFFFFFFFFL));
6140 thisParticle.m_partId = partId;
6141 thisParticle.m_noParticles = (MInt)floatBuffer[h++];
6142 }
6143}
6144
6145
6151template <MInt nDim>
6152void LPT<nDim>::unpackParticle(LPTEllipsoidal<nDim>& thisParticle, const MInt* intBuffer, MInt& z,
6153 const MFloat* floatBuffer, MInt& h, MInt& step, MInt id) {
6154 if(intBuffer != nullptr) {
6155 MInt a = intBuffer[z++];
6156 MInt b = intBuffer[z++];
6157 MLong partId = ((MLong)a << 32 | ((MLong)b & 0xFFFFFFFFL));
6158 thisParticle.m_partId = partId;
6159 thisParticle.m_cellId = intBuffer[z++];
6160 step = intBuffer[z++];
6161 thisParticle.hadWallColl() = (MBool)intBuffer[z++];
6162 if(id == 0 && z != intElemPerP<LPTEllipsoidal<nDim>>()) {
6163 mTerm(1, AT_, "Buffersize missmatching!");
6164 }
6165 }
6166 thisParticle.m_semiMinorAxis = floatBuffer[h++];
6167 thisParticle.m_aspectRatio = floatBuffer[h++];
6168 thisParticle.initEllipsoialProperties();
6169 thisParticle.m_densityRatio = floatBuffer[h++];
6170 for(MInt l = 0; l < nDim; l++)
6171 thisParticle.m_position.at(l) = floatBuffer[h++];
6172 for(MInt l = 0; l < nDim; l++)
6173 thisParticle.m_velocity.at(l) = floatBuffer[h++];
6174 for(MInt l = 0; l < nDim; l++)
6175 thisParticle.m_accel.at(l) = floatBuffer[h++];
6176 for(MInt l = 0; l < nDim; l++)
6177 thisParticle.m_angularVel.at(l) = floatBuffer[h++];
6178 for(MInt l = 0; l < nDim; l++)
6179 thisParticle.m_angularAccel.at(l) = floatBuffer[h++];
6180 for(MInt l = 0; l < 4; l++)
6181 thisParticle.m_quaternion.at(l) = floatBuffer[h++];
6182 for(MInt l = 0; l < nDim; l++)
6183 thisParticle.m_oldPos.at(l) = floatBuffer[h++];
6184 for(MInt l = 0; l < nDim; l++)
6185 thisParticle.m_oldVel.at(l) = floatBuffer[h++];
6186 for(MInt l = 0; l < nDim; l++)
6187 thisParticle.m_oldAccel.at(l) = floatBuffer[h++];
6188 for(MInt l = 0; l < nDim; l++)
6189 thisParticle.m_oldAngularVel.at(l) = floatBuffer[h++];
6190 for(MInt l = 0; l < nDim; l++)
6191 thisParticle.m_oldAngularAccel.at(l) = floatBuffer[h++];
6192 for(MInt l = 0; l < 4; l++)
6193 thisParticle.m_oldQuaternion.at(l) = floatBuffer[h++];
6194 thisParticle.m_creationTime = floatBuffer[h++];
6195 thisParticle.m_temperature = floatBuffer[h++];
6196 thisParticle.m_heatFlux = floatBuffer[h++];
6197 thisParticle.m_fluidVelMag = floatBuffer[h++];
6198 thisParticle.m_oldFluidDensity = floatBuffer[h++];
6199 for(MInt l = 0; l < nDim; l++)
6200 thisParticle.m_oldFluidVel[l] = floatBuffer[h++];
6201
6202 if(id == 0 && h != elemPerP<LPTEllipsoidal<nDim>>()) {
6203 mTerm(1, AT_, "Buffersize missmatching!");
6204 }
6205
6206 if(intBuffer == nullptr) {
6207 MInt a = (MInt)floatBuffer[h++];
6208 MInt b = (MInt)floatBuffer[h++];
6209 MLong partId = ((MLong)a << 32 | ((MLong)b & 0xFFFFFFFFL));
6210 thisParticle.m_partId = partId;
6211 }
6212}
6213
6214
6220template <MInt nDim>
6221void LPT<nDim>::broadcastInjected(const MUint prevNumPart) {
6222 TRACE();
6223
6224 if(noDomains() > 1) {
6225 vector<LPTSpherical<nDim>*> particlesToSend;
6226 vector<MInt> cellIds;
6227 vector<LPTSpherical<nDim>>& partList = a_particleList<LPTSpherical<nDim>>();
6228 // collect particles which are marked as inactive
6229 for(MInt i = prevNumPart; i < a_noParticles(); i++) {
6230 if(partList[i].toBeDeleted() || partList[i].toBeRespawn()) {
6231 particlesToSend.push_back(&partList[i]);
6232 cellIds.push_back(-1);
6233#ifdef LPT_DEBUG
6234 cerr << domainId() << ": " << partList[i].m_partId << " is to be send to all domains during injection." << endl;
6235 << endl;
6236#endif
6237 }
6238 }
6239
6240 MInt numPartSend = static_cast<MInt>(particlesToSend.size());
6241
6242 // send number of particles
6243 MPI_Bcast(&numPartSend, 1, MPI_INT, m_spawnDomainId, mpiComm(), AT_, "intSendBuffer");
6244
6245#ifdef LPT_DEBUG
6246 cerr << domainId() << ": "
6247 << " is sending " << numPartSend << " particles" << endl;
6248#endif
6249
6250 ASSERT(m_exchangeBufferSize
6251 > max(numPartSend * intElemPerP<LPTSpherical<nDim>>(), numPartSend * elemPerP<LPTSpherical<nDim>>()),
6252 "ERROR: Exchange buffer to small");
6253
6254 if(numPartSend > 0) {
6255 // pack particles
6256 packParticles(particlesToSend, m_intSendBuffer[0].get(), m_sendBuffer[0].get(), cellIds);
6257
6258 // send particles
6259 MPI_Bcast(&m_intSendBuffer[0][0], numPartSend * intElemPerP<LPTSpherical<nDim>>(), type_traits<MInt>::mpiType(),
6260 m_spawnDomainId, mpiComm(), AT_, "intSendBuffer");
6261
6262 MPI_Bcast(&m_sendBuffer[0][0], numPartSend * elemPerP<LPTSpherical<nDim>>(), type_traits<MFloat>::mpiType(),
6263 m_spawnDomainId, mpiComm(), AT_, "intSendBuffer");
6264 }
6265
6266 for(auto part : particlesToSend) {
6267 part->wasSend() = true;
6268 part->reqSend() = false;
6269 part->isInvalid() = true;
6270 }
6271
6272
6273#ifndef NDEBUG
6274 MInt particlesReceived = 0;
6275 MPI_Allreduce(MPI_IN_PLACE, &particlesReceived, 1, MPI_INT, MPI_SUM, mpiComm(), AT_, "INPLACE",
6276 "particlesReceived");
6277 if(particlesReceived != numPartSend) {
6278 cerr << "total particles received: " << particlesReceived << endl;
6279 TERMM(-1, "ERROR: Inconsistent number of particles");
6280 }
6281#endif
6282 }
6283}
6284
6285template <MInt nDim>
6287
6288#ifdef _OPENMP
6289#pragma omp single
6290#endif
6291 for(MInt cellId = 0; cellId < a_noCells(); cellId++) {
6292 a_volumeFraction(cellId) = 0.0;
6293 }
6294
6295#ifdef _OPENMP
6296#pragma omp single
6297#endif
6298 for(MInt i = 0; i < a_noParticles(); i++) {
6299 const MInt cellId = m_partList[i].m_cellId;
6300 // particle Volume Fraction = particleVolume * parcelSize / volume of the cartesian cell!
6301 a_volumeFraction(cellId) +=
6302 4.0 / 3.0 * PI * POW3(0.5 * m_partList[i].m_diameter) * m_partList[i].m_noParticles / c_cellVolume(cellId);
6303 }
6304 if(m_ellipsoids) {
6305#ifdef _OPENMP
6306#pragma omp single
6307#endif
6308 for(MInt i = 0; i < a_noEllipsoidalParticles(); i++) {
6309 const MInt cellId = m_partListEllipsoid[i].m_cellId;
6310 // particle Volume Fraction = particleVolume / volume of the cartesian cell!
6311 a_volumeFraction(cellId) += m_partListEllipsoid[i].particleVolume() / c_cellVolume(cellId);
6312 }
6313 }
6314
6315 // correct cell-volume for bndrycells
6316 for(MInt bndryCellId = 0; bndryCellId < m_bndryCells->size(); bndryCellId++) {
6317 const MInt lptCellId = m_bndryCells->a[bndryCellId].m_cellId;
6318 a_volumeFraction(lptCellId) *= c_cellVolume(lptCellId) / m_bndryCells->a[bndryCellId].m_volume;
6319 }
6320
6321#if !defined NDEBUG
6322 /*
6323 for(MInt cellId = 0; cellId < a_noCells(); cellId++) {
6324 if(a_volumeFraction(cellId) > 0.9) {
6325 cerr << "Particle volume in cell is almost larger than the cartesian cell! " << a_volumeFraction(cellId)
6326 << " This is not possible, particles should have left the cell before! " << endl;
6327 }
6328 }
6329 */
6330#endif
6331}
6332
6338template <MInt nDim>
6340 TRACE();
6341
6342 const MInt noNghbrDomains = grid().noNeighborDomains();
6343
6344 // set lpt cell collector property
6345 for(MInt cellId = 0; cellId < a_noCells(); cellId++) {
6346 a_isHalo(cellId) = false;
6347 a_isWindow(cellId) = false;
6348 }
6349
6350 m_pointsToHalo.clear();
6351 m_pointsToHalo.resize(noNghbrDomains);
6352
6353 for(MInt i = 0; i < noNghbrDomains; i++) {
6354 for(MInt j = 0; j < noWindowCells(i); j++) {
6355 const MInt windowCell = windowCellId(i, j);
6356 a_isWindow(windowCell) = true;
6357 if(!c_isLeafCell(windowCell)) continue;
6358 // set pointsToHalo for leaf-Cell window-Cells.
6359 // this is used in the pushToQueue-function in particleTransfer!
6360 // Meaning that the particle is only transferred if the corresponding cellId
6361 // is found in this list!
6362 const MInt windowCellOffset = j;
6363 m_pointsToHalo[i].insert(make_pair(windowCell, windowCellOffset));
6364 }
6365 }
6366
6367 m_pointsToWindow.clear();
6368 m_pointsToWindow.resize(noNghbrDomains);
6369
6370 for(MInt i = 0; i < noNghbrDomains; i++) {
6371 for(MInt j = 0; j < noHaloCells(i); j++) {
6372 const MInt haloCell = haloCellId(i, j);
6373 a_isHalo(haloCell) = true;
6374 if(!c_isLeafCell(haloCell)) continue;
6375 const MInt HaloCellOffset = j;
6376 // set pointsToHalo for leaf-Cell halo-Cells.
6377 // this is used in the pushToQueue-function in particleTransfer!
6378 // Meaning that the particle is only transferred if the corresponding cellId
6379 // is found in this list!
6380 // NOTE: the search is now only done for matching cells(in this case halo-cells!)
6381 m_pointsToWindow[i].insert(make_pair(haloCell, HaloCellOffset));
6382 }
6383 }
6384}
6385
6390template <MInt nDim>
6391void LPT<nDim>::addBndryCell(const MInt cellId, MFloat* bndryData, MFloat* surfaceN, MFloatScratchSpace& surfaceC,
6392 MFloat* surfaceV) {
6393 TRACE();
6394
6395 ASSERT(a_bndryCellId(cellId) < 0, "ERROR, cell already has a bndryCell!");
6396
6397 // currently the LPT-bndryCell consist of the following data-structure:
6398 //- matching cellId
6399 //- coordinate of the cut-Cell
6400 // (note, that in the FV-solver, the coordinate is only the shift from the un-cut center!)
6401 //- volume of the cut-Cell
6402 //- a single bndry-Surface with the following data:
6403 // - surface normal
6404 // - surface velocity (only unqual zero for moving bndries!)
6405 // - surface centroid coordinate
6406 // - coordinate of 3 points on the cell-edges of the surface
6407
6408 const MInt bndryCellId = m_bndryCells->size();
6409 m_bndryCells->append();
6410 a_bndryCellId(cellId) = bndryCellId;
6411
6412 m_bndryCells->a[bndryCellId].m_cellId = cellId;
6413 m_bndryCells->a[bndryCellId].m_volume = bndryData[0];
6414 for(MInt n = 0; n < nDim; n++) {
6415 m_bndryCells->a[bndryCellId].m_coordinates[n] = c_coordinate(cellId, n) + bndryData[1 + n];
6416 }
6417 const MInt noSurf = 1;
6418 m_bndryCells->a[bndryCellId].m_noSrfcs = noSurf;
6419 for(MInt s = 0; s < noSurf; s++) {
6420 for(MInt n = 0; n < nDim; n++) {
6421 m_bndryCells->a[bndryCellId].m_srfcs[s]->m_normal[n] = surfaceN[noSurf * s + n];
6422 m_bndryCells->a[bndryCellId].m_srfcs[s]->m_velocity[n] = surfaceV[noSurf * s + n];
6423 m_bndryCells->a[bndryCellId].m_srfcs[s]->m_coordinates[n] = surfaceC(noSurf * s + n, 0);
6424 for(MInt i = 0; i < nDim; i++) {
6425 m_bndryCells->a[bndryCellId].m_srfcs[s]->m_planeCoordinates[i][n] = surfaceC(noSurf * s + n, i + 1);
6426 }
6427 }
6428 }
6429}
6430
6431
6432template <MInt nDim>
6434 m_collisionModel->writeCollData();
6435}
6436
6440template <MInt nDim>
6442 TRACE();
6443
6444 if(globalTimeStep < 0) return;
6445
6446 // check that particle starts on a valid location
6447#if defined LPT_DEBUG || !defined NDEBUG
6448
6449 // calculate weights of the redistribution
6450 static const MInt noOfRedistLayers = m_couplingRedist ? ((m_noRedistLayer > 0) ? m_noRedistLayer : 2) : 0;
6451
6452 if(!((noOfRedistLayers == 0 && !m_couplingRedist) || m_couplingRedist)) {
6453 mTerm(1, AT_, "Invalid configuration noOfRedistLayers > 0, but coupling redistribution is not active!");
6454 }
6455
6456 for(MInt part = 0; part < a_noParticles(); part++) {
6457 if(m_partList[part].isInvalid()) {
6458 if(!m_partList[part].fullyEvaporated()) continue;
6459 if(m_partList[part].m_noParticles < 1) {
6460 cerr << "Fully evap. has neg. parcel count! " << m_partList[part].m_noParticles << " " << m_partList[part].m_dM
6461 << endl;
6462 }
6463 if(m_partList[part].m_dM > 0.000001) {
6464 cerr << "Fully evap. has large mass-transfer " << m_partList[part].m_dM << " " << m_partList[part].m_partId
6465 << endl;
6466 }
6467 continue;
6468 }
6469 const MInt cellId = m_partList[part].m_cellId;
6470 if(cellId < 0 || cellId > noInternalCells()) {
6471 cerr << "Invalid cellId " << cellId << " " << m_partList[part].hasCollided() << " " << a_isHalo(cellId) << " "
6472 << c_isLeafCell(cellId) << " " << m_partList[part].isWindow() << " " << m_partList[part].reqSend() << " "
6473 << m_partList[part].wasSend() << " " << m_partList[part].hadWallColl() << " " << part << " "
6474 << m_partList[part].m_partId << endl;
6475 mTerm(1, AT_, "Invalid cellId!");
6476 }
6477 if(m_partList[part].isInvalid()) {
6478 mTerm(1, AT_, "Invalid particle at TS beginning!");
6479 }
6480 if(a_isHalo(cellId) || !c_isLeafCell(cellId) || c_noChildren(cellId) > 0) {
6481 mTerm(1, AT_, "Particle in halo or non-leaf cell!");
6482 }
6483 // check particle coordinate:
6484 const MFloat halfCellLength = c_cellLengthAtLevel(c_level(cellId) + 1);
6485 std::array<MFloat, 3> origCellC = {};
6486 for(MInt i = 0; i < nDim; i++) {
6487 origCellC[i] = c_coordinate(cellId, i);
6488 }
6489 // before particle motion, the particle should be located in the correct cell
6490 if(fabs(origCellC[0] - m_partList[part].m_position[0]) > halfCellLength
6491 || fabs(origCellC[1] - m_partList[part].m_position[1]) > halfCellLength
6492 || fabs(origCellC[2] - m_partList[part].m_position[2]) > halfCellLength) {
6493 cerr << "ERROR: particle " << m_partList[part].m_partId << " is in on incorrect cell "
6494 << sqrt(POW2(origCellC[0] - m_partList[part].m_position[0])
6495 + POW2(origCellC[1] - m_partList[part].m_position[1])
6496 + POW2(origCellC[2] - m_partList[part].m_position[2]))
6497 << " > " << halfCellLength << " and particle location: " << m_partList[part].m_position[0] << " "
6498 << m_partList[part].m_position[1] << " " << m_partList[part].m_position[2] << " "
6499 << m_partList[part].hadWallColl() << " " << a_level(cellId) << " " << origCellC[0] << " " << origCellC[1]
6500 << " " << origCellC[2] << endl;
6501 // mTerm(1, AT_, "Invalid cell for particle position!" );
6502 m_partList[part].isInvalid() = true;
6503 continue;
6504 }
6505
6506 if(m_partList[part].m_diameter < m_sizeLimit || isnan(m_partList[part].m_diameter)) {
6507 cerr << "Invalid particle diameter : " << m_partList[part].m_diameter << " " << m_sizeLimit << " "
6508 << m_partList[part].firstStep() << " " << m_partList[part].hadWallColl() << " "
6509 << m_partList[part].m_breakUpTime << " " << m_partList[part].m_partId << endl;
6510 // mTerm(1, AT_, "Invalid particle diameter!");
6511 }
6512
6513 if(m_partList[part].m_noParticles < 0) {
6514 cerr << m_partList[part].m_noParticles << endl;
6515 mTerm(1, AT_, "Invalid parcel count!");
6516 }
6517
6518 if(m_partList[part].m_oldCellId < 0 && !m_periodicBC) {
6519 mTerm(1, AT_, "Invalid old CellId!");
6520 }
6521
6522 if(m_partList[part].m_oldFluidDensity < MFloatEps) {
6523 cerr << a_fluidDensity(cellId) << " " << a_isHalo(cellId) << " " << a_level(cellId) << " " << part << " "
6524 << a_noParticles() << " " << m_partList[part].m_oldFluidDensity << endl;
6525 mTerm(1, AT_, "Invalid old fluid density!");
6526 }
6527
6528 for(MInt i = 0; i < nDim; i++) {
6529 if(isnan(m_partList[part].m_oldFluidVel[i])) {
6530 mTerm(1, AT_, "Invalid old fluid velocity!");
6531 }
6532 }
6533
6534 if(m_partList[part].m_temperature < 0) {
6535 mTerm(1, AT_, "Invalid particle temperature!");
6536 }
6537
6538 if(m_partList[part].m_shedDiam < 0) {
6539 cerr << "Negative shed-diameter " << m_partList[part].m_partId << endl;
6540 }
6541 if(m_partList[part].m_noParticles < 1) {
6542 cerr << "Negative parcel-count " << m_partList[part].m_partId << endl;
6543 }
6544 if(m_partList[part].m_dM > 0.000001) {
6545 cerr << "Large mass-transfer " << m_partList[part].m_dM << " " << m_partList[part].m_partId << endl;
6546 }
6547 }
6548
6549 // ellipsoids
6550 for(MInt part = 0; part < a_noEllipsoidalParticles(); part++) {
6551 const MInt cellId = m_partListEllipsoid[part].m_cellId;
6552
6553 if(cellId < 0 || cellId > noInternalCells()) {
6554 cerr << "Invalid cellId " << cellId << " " << m_partListEllipsoid[part].hasCollided() << " " << a_isHalo(cellId)
6555 << " " << c_isLeafCell(cellId) << " " << m_partListEllipsoid[part].isWindow() << " "
6556 << m_partListEllipsoid[part].reqSend() << " " << m_partListEllipsoid[part].wasSend() << " "
6557 << m_partListEllipsoid[part].hadWallColl() << " " << part << " " << m_partListEllipsoid[part].m_partId
6558 << endl;
6559 mTerm(1, AT_, "Invalid cellId!");
6560 }
6561 if(m_partListEllipsoid[part].isInvalid()) {
6562 mTerm(1, AT_, "Invalid ellipsoid at TS beginning!");
6563 }
6564
6565
6566 if(a_isHalo(cellId) || !c_isLeafCell(cellId) || c_noChildren(cellId) > 0) {
6567 mTerm(1, AT_, "Ellipsoid in halo or non-leaf cell!");
6568 }
6569
6570 // check particle coordinate:
6571 const MFloat halfCellLength = c_cellLengthAtLevel(c_level(cellId) + 1);
6572
6573 std::array<MFloat, 3> origCellC = {};
6574 for(MInt i = 0; i < nDim; i++) {
6575 origCellC[i] = c_coordinate(cellId, i);
6576 }
6577
6578 // before particle motion, the particle should be located in the correct cell
6579 if(fabs(origCellC[0] - m_partListEllipsoid[part].m_position[0]) > halfCellLength
6580 || fabs(origCellC[1] - m_partListEllipsoid[part].m_position[1]) > halfCellLength
6581 || fabs(origCellC[2] - m_partListEllipsoid[part].m_position[2]) > halfCellLength) {
6582 cerr << "ERROR: ellipsoid " << m_partListEllipsoid[part].m_partId << " is in on incorrect cell "
6583 << sqrt(POW2(origCellC[0] - m_partListEllipsoid[part].m_position[0])
6584 + POW2(origCellC[1] - m_partListEllipsoid[part].m_position[1])
6585 + POW2(origCellC[2] - m_partListEllipsoid[part].m_position[2]))
6586 << " > " << halfCellLength << " and ellipsoid location: " << m_partListEllipsoid[part].m_position[0] << " "
6587 << m_partListEllipsoid[part].m_position[1] << " " << m_partListEllipsoid[part].m_position[2] << " "
6588 << m_partListEllipsoid[part].hadWallColl() << " " << a_level(cellId) << " " << origCellC[0] << " "
6589 << origCellC[1] << " " << origCellC[2] << endl;
6590 // mTerm(1, AT_, "Invalid cell for particle position!" );
6591 m_partListEllipsoid[part].isInvalid() = true;
6592 continue;
6593 }
6594
6595 if(m_partListEllipsoid[part].equivalentDiameter() < m_sizeLimit
6596 || isnan(m_partListEllipsoid[part].equivalentDiameter())) {
6597 cerr << "Invalid ellipsoid diameter : " << m_partListEllipsoid[part].equivalentDiameter() << " " << m_sizeLimit
6598 << " " << m_partListEllipsoid[part].firstStep() << " " << m_partListEllipsoid[part].hadWallColl() << " "
6599 << m_partListEllipsoid[part].m_partId << endl;
6600 // mTerm(1, AT_, "Invalid particle diameter!");
6601 }
6602
6603 if(m_partListEllipsoid[part].m_oldCellId < 0 && !m_periodicBC) {
6604 mTerm(1, AT_, "Invalid old CellId!");
6605 }
6606
6607 if(m_partListEllipsoid[part].m_oldFluidDensity < 0) {
6608 cerr << a_fluidDensity(cellId) << " " << a_isHalo(cellId) << " " << a_level(cellId) << "" << part << " "
6609 << a_noEllipsoidalParticles() << endl;
6610 mTerm(1, AT_, "Invalid old fluid density!");
6611 }
6612
6613 for(MInt i = 0; i < nDim; i++) {
6614 if(isnan(m_partListEllipsoid[part].m_oldFluidVel[i])) {
6615 mTerm(1, AT_, "Invalid old fluid velocity!");
6616 }
6617 }
6618
6619 if(m_partListEllipsoid[part].m_temperature < 0) {
6620 mTerm(1, AT_, "Invalid particle temperature!");
6621 }
6622 }
6623#endif
6624}
6625
6626
6630template <MInt nDim>
6632 TRACE();
6633
6634// check that particle starts on a valid location
6635#ifdef LPT_DEBUG
6636 for(MInt cellId = 0; cellId < a_noCells(); cellId++) {
6637 if(!c_isLeafCell(cellId)) continue;
6638 if(a_fluidDensity(cellId) < MFloatEps) {
6639 mTerm(1, AT_, "Invalid density in cell!");
6640 }
6641
6642 if(isnan(a_massFlux(cellId))) {
6643 mTerm(1, AT_, "Invalid mass-flux in cell!");
6644 }
6645 }
6646
6647#endif
6648}
6649
6654template <MInt nDim>
6656 TRACE();
6657
6658 if(!isActive()) {
6659 m_injData.clear();
6660 } else {
6661 receiveFlowField();
6662 receiveVelocitySlopes();
6663 waitForSendReqs();
6664 }
6665
6666 // set correct size of m_injData on inactive ranks!
6667 MInt vectorSize = 0;
6668 if(m_activePrimaryBUp && grid().hasInactiveRanks()) {
6669 if(!m_injData.empty()) {
6670 vectorSize = m_injData.size();
6671 }
6672 MPI_Allreduce(MPI_IN_PLACE, &vectorSize, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD, AT_, "INPLACE", "vectorSize");
6673
6674 if(vectorSize > 0 && !isActive()) {
6675 m_injData.clear();
6676 const MInt count = m_sprayModel->m_injDataSize + m_addInjDataCnt;
6677 MInt dummyTimeStep = -1;
6678 for(MInt i = 0; i < vectorSize; i++) {
6679 vector<MFloat> tmp(count);
6680 for(MInt j = 0; j < count; j++) {
6681 tmp[j] = 0.0;
6682 }
6683 m_injData.insert(make_pair(dummyTimeStep, tmp));
6684 dummyTimeStep--;
6685 }
6686 } else if(isActive() && domainId() == m_spawnDomainId) {
6687 if(vectorSize != (signed)m_injData.size()) {
6688 cerr << "Injector size differs! " << vectorSize << " " << m_injData.size() << endl;
6689 }
6690 }
6691 }
6692
6693 if(!isActive()) {
6694 return;
6695 }
6696
6697 cerr0 << "LPT-time step is : " << m_timeStep << endl;
6698
6699 removeInvalidParticles(true);
6700
6701 checkParticles(); // check particles before balance!
6702
6703 countParticlesInCells(); // used to determine the data size necessary for sending
6704
6705 // remove all bndryCells
6706 if(m_wallCollisions) {
6707 m_bndryCells->resetSize(0);
6708 m_noOuterBndryCells = 0;
6709 }
6710
6711 // NOTE:latest point to write a debug file before the balance:
6712 /*
6713 MInt backup = globalTimeStep;
6714 globalTimeStep = -1;
6715 saveDebugRestartFile();
6716 globalTimeStep = backup;
6717 */
6718
6719 perCellStats(); // cell-particle mapping necessary to pack particles for a given cell
6720
6721 MPI_Allreduce(MPI_IN_PLACE, &m_particleResiduum, 1, MPI_DOUBLE, MPI_MAX, mpiComm(), AT_, "INPLACE",
6722 "m_particleResiduum");
6723
6724 MPI_Allreduce(MPI_IN_PLACE, &m_PRNGSpawnCount, 1, MPI_INT, MPI_MAX, mpiComm(), AT_, "INPLACE", "m_PRNGSpawnCount");
6725
6726 MPI_Allreduce(MPI_IN_PLACE, &m_spawnDomainId, 1, MPI_INT, MPI_MAX, mpiComm(), AT_, "INPLACE", "m_spawnDomainId");
6727
6728 if(m_activePrimaryBUp || m_activeSecondaryBUp) {
6729 MInt count = m_sprayModel->m_PRNGPrimBreakUpCount;
6730 MPI_Allreduce(MPI_IN_PLACE, &count, 1, MPI_INT, MPI_MAX, mpiComm(), AT_, "INPLACE", "m_PRNGPrimBreakUpCount");
6731 m_sprayModel->m_PRNGPrimBreakUpCount = count;
6732
6733 MFloat timeSinceSOI = 0;
6734 if(m_spawnCellId > -1) {
6735 timeSinceSOI = m_sprayModel->timeSinceSOI();
6736 }
6737 MPI_Allreduce(MPI_IN_PLACE, &timeSinceSOI, 1, MPI_DOUBLE, MPI_MAX, mpiComm(), AT_, "INPLACE", "timeSinceSOI");
6738 m_sprayModel->timeSinceSOI() = timeSinceSOI;
6739 }
6740
6741 // exchange the injection-data
6742 if(m_activePrimaryBUp) {
6743 MInt vectorSizeBU = vectorSize;
6744 vectorSize = m_injData.size();
6745 const MInt count = m_sprayModel->m_injDataSize + m_addInjDataCnt;
6746
6747 MPI_Allreduce(MPI_IN_PLACE, &vectorSize, 1, MPI_INT, MPI_MAX, mpiComm(), AT_, "INPLACE", "vectorSize");
6748 const MInt totalSize = vectorSize * (count + 1);
6749
6750 if(vectorSize > 0) {
6751 MFloatScratchSpace injData(vectorSize * (count + 1), AT_, "injData");
6752 injData.fill(0.0);
6753 if(vectorSize != vectorSizeBU && grid().hasInactiveRanks()) {
6754 cerr << domainId() << "Vector size differs " << vectorSizeBU << " " << vectorSize << endl;
6755 }
6756
6757 if(domainId() == m_spawnDomainId) {
6758 ASSERT((signed)m_injData.size() == vectorSize, "");
6759 MInt i = 0;
6760 for(auto it = m_injData.begin(); it != m_injData.end(); it++) {
6761 const MFloat timeStep = (MFloat)it->first;
6762 injData(i++) = timeStep;
6763 for(MInt j = 0; j < count; j++) {
6764 const MFloat data = (it->second)[j];
6765 injData(i++) = data;
6766 }
6767 }
6768 } else {
6769 MInt i = 0;
6770 for(auto it = m_injData.begin(); it != m_injData.end(); it++) {
6771 i = i + m_sprayModel->m_injDataSize + 1;
6772 const MFloat sumEvap = (it->second)[m_sprayModel->m_injDataSize];
6773 const MInt numRT = (it->second)[m_sprayModel->m_injDataSize + 1];
6774 const MInt numKH = (it->second)[m_sprayModel->m_injDataSize + 2];
6775 const MInt numSend = (it->second)[m_sprayModel->m_injDataSize + 3];
6776 injData(i++) = sumEvap;
6777 injData(i++) = numRT;
6778 injData(i++) = numKH;
6779 injData(i++) = numSend;
6780 }
6781 }
6782
6783 MPI_Allreduce(MPI_IN_PLACE, &injData[0], totalSize, MPI_DOUBLE, MPI_SUM, mpiComm(), AT_, "INPLACE", "injData");
6784
6785 // reset injection data with summed data
6786 m_injData.clear();
6787
6788 for(MInt i = 0; i < totalSize; i = i + (count + 1)) {
6789 const MInt timeStep = (MInt)injData(i);
6790 vector<MFloat> tmp(count);
6791 for(MInt j = 0; j < count; j++) {
6792 tmp[j] = injData[i + j + 1];
6793 }
6794 m_injData.insert(make_pair(timeStep, tmp));
6795 }
6796 }
6797 }
6798
6799#ifdef LPT_DEBUG
6800 MInt noParticles = a_noParticles();
6801
6802 MPI_Allreduce(MPI_IN_PLACE, &noParticles, 1, MPI_INT, MPI_SUM, mpiComm(), AT_, "INPLACE", "noParticles");
6803
6804 if(domainId() == 0) {
6805 cerr << noParticles << " #particles before balancing!" << endl;
6806 }
6807
6808 if(m_ellipsoids) {
6809 MInt noEllipsoids = a_noEllipsoidalParticles();
6810 MPI_Allreduce(MPI_IN_PLACE, &noEllipsoids, 1, MPI_INT, MPI_SUM, mpiComm(), AT_, "INPLACE", "noEllipsoids");
6811 cerr0 << noEllipsoids << " #ellipsoids before balancing!" << endl;
6812 }
6813
6814#endif
6815}
6816
6821template <MInt nDim>
6823 m_queueToSend.clear();
6824 m_sendBuffer.clear();
6825 m_recvBuffer.clear();
6826 m_intSendBuffer.clear();
6827 m_intRecvBuffer.clear();
6828 m_mpi_reqSendFloat.clear();
6829 m_mpi_reqSendInt.clear();
6830 m_mpi_reqSendSize.clear();
6831 m_mpi_statusProbe.clear();
6832
6833 m_partList.clear();
6834 m_partListEllipsoid.clear();
6835 m_cells.clear();
6836 if(m_wallCollisions) {
6837 m_bndryCells->resetSize(0);
6838 }
6839
6840 m_cellToNghbrHood.clear();
6841 m_cellToNghbrHoodInterpolation.clear();
6842
6843 m_pointsToHalo.clear();
6844 m_pointsToWindow.clear();
6845
6846 m_cellToPartMap.clear();
6847 m_cellToEllipsMap.clear();
6848}
6849
6854template <MInt nDim>
6856 if(isActive()) {
6857 countParticlesInCells();
6858 }
6859}
6860
6861
6866template <MInt nDim>
6868 // Set reinitialization stage
6869 m_loadBalancingReinitStage = 0;
6870
6871 // Update the grid proxy for this solver
6872 grid().update();
6873
6874 if(!grid().isActive()) {
6875 // Reset parallelization information if solver is not active
6876 updateDomainInfo(-1, -1, MPI_COMM_NULL, AT_);
6877 } else {
6878 // Set new domain info for solver
6879 updateDomainInfo(grid().domainId(), grid().noDomains(), grid().mpiComm(), AT_);
6880 }
6881
6882 // Reset cell, boundary cell data and deallocate halo/window cell arrays
6883 resetSolverFull();
6884
6885 // check for empry cell and particle collector
6886 ASSERT(m_cells.size() == 0, "");
6887 ASSERT(a_noParticles() == 0, "");
6888 ASSERT(a_noEllipsoidalParticles() == 0, "");
6889
6890 // Return if solver is not active
6891 if(!grid().isActive()) {
6892 m_cells.reset(grid().maxNoCells());
6893 this->setHaloCellsOnInactiveRanks();
6894 return;
6895 }
6896
6897 initCollector();
6898
6899 // Check that global ids are sorted
6900 for(MInt cellId = 0; cellId < grid().noInternalCells(); cellId++) {
6901 if(grid().domainOffset(domainId()) + (MLong)cellId != c_globalId(cellId)) {
6902 TERMM(1, "Global id mismatch.");
6903 }
6904 }
6905
6906 updateExchangeCells();
6907 grid().updateLeafCellExchange();
6908 initMPI();
6909
6910
6911 // this can only be done, after the halo property has been set above
6912 this->checkNoHaloLayers();
6913}
6914
6919template <MInt nDim>
6921 TRACE();
6922
6923 m_loadBalancingReinitStage = 1;
6924
6925 ASSERT(m_cells.size() == c_noCells() && c_noCells() == a_noCells(), "");
6926}
6927
6932template <MInt nDim>
6933MInt LPT<nDim>::cellDataSizeDlb(const MInt dataId, const MInt gridCellId) {
6934 // Inactive ranks do not have any data to communicate
6935 if(!isActive()) {
6936 return 0;
6937 }
6938
6939 // Convert to solver cell id and check
6940 const MInt cellId = grid().tree().grid2solver(gridCellId);
6941 if(cellId < 0 || cellId >= noInternalCells()) {
6942 return 0;
6943 }
6944
6945 // only noParticlesInCell and particle information is balanced
6946 if(dataId == 0) return 2; // 2 because we spherical and elllipsoidal particles
6947
6948 if(a_noParticlesInCell(cellId) == 0 && a_noEllipsoidsInCell(cellId) == 0) return 0;
6949
6950 MInt dataSize = 0;
6951
6952 switch(dataId) {
6953 case 1:
6954 dataSize = (elemPerP<LPTSpherical<nDim>>() + 3) * a_noParticlesInCell(cellId)
6955 + (elemPerP<LPTEllipsoidal<nDim>>() + 2) * a_noEllipsoidsInCell(cellId);
6956 break;
6957 case 2: // spheres particleId(2) and noParcels, ellipsoids only particleId(2)
6958 dataSize = 3 * a_noParticlesInCell(cellId) + 2 * a_noEllipsoidsInCell(cellId);
6959 break;
6960 default:
6961 TERMM(1, "Unknown data id.");
6962 break;
6963 }
6964
6965 return dataSize;
6966}
6967
6972template <MInt nDim>
6973void LPT<nDim>::getCellDataDlb(const MInt dataId, const MInt oldNoCells, const MInt* const bufferIdToCellId,
6974 MFloat* const data) {
6975 TRACE();
6976
6977 if(dataId != 1) mTerm(1, AT_, "Only Type 1 should be FLOAT");
6978
6979 MInt localBufferId = 0;
6980 for(MInt i = 0; i < oldNoCells; i++) {
6981 const MInt gridCellId = bufferIdToCellId[i];
6982
6983 if(gridCellId < 0) continue;
6984
6985 const MInt cellId = grid().tree().grid2solver(gridCellId);
6986 if(cellId < 0 || cellId >= noInternalCells()) {
6987 continue;
6988 }
6989
6990 if(a_noParticlesInCell(cellId) > 0) {
6991 auto particlesInCell = m_cellToPartMap.equal_range(cellId);
6992 // loop over all particles in the cell
6993 vector<LPTSpherical<nDim>*> particlesToSend;
6994 vector<MInt> cellIds;
6995 for(auto it = particlesInCell.first; it != particlesInCell.second; it++) {
6996 auto& particle = it->second;
6997 particlesToSend.push_back(particle);
6998 }
6999 if((MInt)particlesToSend.size() != a_noParticlesInCell(cellId)) {
7000 cerr << particlesToSend.size() << " " << a_noParticlesInCell(cellId) << " miss-count!" << endl;
7001 mTerm(1, AT_, "Particle count not matching!");
7002 }
7003 packParticles(particlesToSend, nullptr, &data[localBufferId], cellIds);
7004 localBufferId += ((elemPerP<LPTSpherical<nDim>>() + 3) * particlesToSend.size());
7005 }
7006
7007 if(a_noEllipsoidsInCell(cellId) > 0) {
7008 auto ellipsoidsInCell = m_cellToEllipsMap.equal_range(cellId);
7009 vector<LPTEllipsoidal<nDim>*> ellipsoidsToSend;
7010 vector<MInt> cellIds;
7011 for(auto it = ellipsoidsInCell.first; it != ellipsoidsInCell.second; it++) {
7012 auto& particle = it->second;
7013 ellipsoidsToSend.push_back(particle);
7014 }
7015 if((MInt)ellipsoidsToSend.size() != a_noEllipsoidsInCell(cellId)) {
7016 cerr << ellipsoidsToSend.size() << " " << a_noEllipsoidsInCell(cellId) << " miss-count!" << endl;
7017 mTerm(1, AT_, "Ellipsoids count not matching!");
7018 }
7019 packParticles(ellipsoidsToSend, nullptr, &data[localBufferId], cellIds);
7020 localBufferId += ((elemPerP<LPTEllipsoidal<nDim>>() + 2) * ellipsoidsToSend.size());
7021 }
7022 }
7023}
7024
7025template <MInt nDim>
7026void LPT<nDim>::getCellDataDlb(const MInt dataId, const MInt oldNoCells, const MInt* const bufferIdToCellId,
7027 MInt* const data) {
7028 TRACE();
7029
7030 if(dataId != 2 && dataId != 0) mTerm(1, AT_, "Type 0 or 2 should be INT");
7031
7032 MInt localBufferId = 0;
7033 for(MInt i = 0; i < oldNoCells; i++) {
7034 const MInt gridCellId = bufferIdToCellId[i];
7035
7036 if(gridCellId < 0) continue;
7037
7038 const MInt cellId = grid().tree().grid2solver(gridCellId);
7039 if(cellId < 0 || cellId >= noInternalCells()) {
7040 continue;
7041 }
7042
7043 if(dataId == 0) {
7044 data[localBufferId++] = a_noParticlesInCell(cellId);
7045 data[localBufferId++] = a_noEllipsoidsInCell(cellId);
7046 continue;
7047 }
7048
7049 if(a_noParticlesInCell(cellId) > 0) {
7050 auto particlesInCell = m_cellToPartMap.equal_range(cellId);
7051 // loop over all particles in the cell
7052 vector<LPTSpherical<nDim>*> particlesToSend;
7053 for(auto it = particlesInCell.first; it != particlesInCell.second; it++) {
7054 auto& particle = it->second;
7055 data[localBufferId] = (MInt)(particle->m_partId >> 32);
7056 data[localBufferId + 1] = MInt(particle->m_partId);
7057 data[localBufferId + 2] = particle->m_noParticles;
7058 // ASSERT(data[localBufferId + 2] > 0 , "");
7059 if(data[localBufferId + 2] < 0) {
7060 cerr << "Setting strange exchange buffer!" << endl;
7061 }
7062 localBufferId += 3;
7063 }
7064 }
7065
7066 if(a_noEllipsoidsInCell(cellId) > 0) {
7067 auto ellipsoidsInCell = m_cellToEllipsMap.equal_range(cellId);
7068 vector<LPTEllipsoidal<nDim>*> ellipsoidsToSend;
7069 for(auto it = ellipsoidsInCell.first; it != ellipsoidsInCell.second; it++) {
7070 auto& particle = it->second;
7071 data[localBufferId] = (MInt)(particle->m_partId >> 32);
7072 data[localBufferId + 1] = MInt(particle->m_partId);
7073 localBufferId += 2;
7074 }
7075 }
7076 }
7077}
7078
7083template <MInt nDim>
7084void LPT<nDim>::setCellDataDlb(const MInt dataId, const MFloat* const data) {
7085 TRACE();
7086
7087 // Nothing to do if solver is not active
7088 if(!isActive()) {
7089 return;
7090 }
7091
7092 if(dataId != 1) mTerm(1, AT_, "Only Type 1 should be FLOAT");
7093
7094 // Set the no-particles in cell if this is the correct reinitialization stage
7095 if(m_loadBalancingReinitStage == 0) {
7096 // gather the number of particles this rank will have after the balance
7097 MInt noParticlesOnRank = 0;
7098 MInt noEllipsoidsOnRank = 0;
7099 for(MInt cellId = 0; cellId < noInternalCells(); cellId++) {
7100 noParticlesOnRank += a_noParticlesInCell(cellId);
7101 noEllipsoidsOnRank += a_noEllipsoidsInCell(cellId);
7102 }
7103
7104 unpackParticles<LPTSpherical<nDim>, true>(noParticlesOnRank, nullptr, &data[0]);
7105 if(a_noParticles() != noParticlesOnRank) {
7106 cerr << domainId() << " has incorrect count " << a_noSphericalParticles() << " " << noParticlesOnRank << endl;
7107 mTerm(1, AT_, "Particle gone missing!");
7108 }
7109
7110 if(m_ellipsoids) {
7111 unpackParticles<LPTEllipsoidal<nDim>, true>(noEllipsoidsOnRank, nullptr,
7112 &data[(elemPerP<LPTSpherical<nDim>>() + 3) * noParticlesOnRank]);
7113 if(a_noEllipsoidalParticles() != noEllipsoidsOnRank) {
7114 cerr << domainId() << " has incorrect count " << a_noParticles<LPTEllipsoidal<nDim>>() << " "
7115 << noEllipsoidsOnRank << endl;
7116 mTerm(1, AT_, "Ellipsoid gone missing!");
7117 }
7118 }
7119 }
7120}
7121
7122template <MInt nDim>
7123void LPT<nDim>::setCellDataDlb(const MInt dataId, const MInt* const data) {
7124 TRACE();
7125
7126 // Nothing to do if solver is not active
7127 if(!isActive()) {
7128 return;
7129 }
7130
7131 if(dataId != 2 && dataId != 0) mTerm(1, AT_, "Type 0 or 2 should be INT");
7132
7133 if(m_loadBalancingReinitStage == 0) {
7134 if(dataId == 0) {
7135 MInt counter = 0;
7136 for(MInt cellId = 0; cellId < noInternalCells(); cellId++) {
7137 a_noParticlesInCell(cellId) = data[counter++];
7138 a_noEllipsoidsInCell(cellId) = data[counter++];
7139 }
7140 } else if(dataId == 2) {
7141 /*
7142 MInt i = 0;
7143 for(MInt part = 0; part < a_noParticles(); part++){
7144 MInt a = data[i];
7145 MInt b = data[i + 1 ];
7146 MLong partId = ((MLong)a << 32 | ((MLong)b & 0xFFFFFFFFL));
7147 //m_partList[part].m_partId = partId;
7148 //m_partList[part].m_noParticles = data[i + 2];
7149 //ASSERT(m_partList[part].m_noParticles > 0 , "");
7150 if(data[i + 2] < 0 || data[i + 2] > 10000) {
7151 cerr << "Strange parcel-count!" << data[i + 2] << " " << partId << endl;
7152 }
7153 i = i + 3;
7154 }
7155 */
7156 }
7157 }
7158}
7159
7160
7166template <MInt nDim>
7167void LPT<nDim>::getGlobalSolverVars(vector<MFloat>& globalFloatVars, vector<MInt>& globalIntVars) {
7168 TRACE();
7169
7170 globalIntVars.push_back(m_PRNGSpawnCount);
7171
7172 if(m_activePrimaryBUp || m_activeSecondaryBUp) {
7173 globalIntVars.push_back(m_sprayModel->m_PRNGPrimBreakUpCount);
7174 }
7175
7176 globalFloatVars.push_back(m_particleResiduum);
7177 globalFloatVars.push_back(m_time);
7178 globalFloatVars.push_back(m_timeStep);
7179
7180 if(m_activePrimaryBUp) {
7181 globalFloatVars.push_back(m_sprayModel->timeSinceSOI());
7182 }
7183
7184 if(m_activePrimaryBUp && !m_injData.empty()) {
7185 for(auto it = m_injData.begin(); it != m_injData.end(); it++) {
7186 const MFloat timeStep = (MFloat)it->first;
7187 globalFloatVars.push_back(timeStep);
7188 for(MInt i = 0; i < m_sprayModel->m_injDataSize + m_addInjDataCnt; i++) {
7189 const MFloat data = (it->second)[i];
7190 globalFloatVars.push_back(data);
7191 }
7192 }
7193 globalIntVars.push_back(m_injData.size());
7194 } else if(m_activePrimaryBUp) {
7195 globalIntVars.push_back(0);
7196 }
7197}
7198
7203template <MInt nDim>
7204void LPT<nDim>::setGlobalSolverVars(vector<MFloat>& globalFloatVars, vector<MInt>& globalIntVars) {
7205 TRACE();
7206
7207 m_PRNGSpawnCount = globalIntVars[0];
7208
7209 m_particleResiduum = globalFloatVars[0];
7210 m_time = globalFloatVars[1];
7211 m_timeStep = globalFloatVars[2];
7212
7213 if(m_activePrimaryBUp || m_activeSecondaryBUp) {
7214 m_sprayModel->m_PRNGPrimBreakUpCount = globalIntVars[1];
7215 }
7216 if(m_activePrimaryBUp) {
7217 m_sprayModel->timeSinceSOI() = globalFloatVars[3];
7218 }
7219
7220
7221 if(m_activePrimaryBUp) {
7222 const MInt vectorSize = globalIntVars[2];
7223 m_injData.clear();
7224
7225 const MInt count = m_sprayModel->m_injDataSize + m_addInjDataCnt;
7226 for(MInt i = 0; i < vectorSize * (count + 1); i = i + 1 + count) {
7227 const MInt timeStep = (MInt)globalFloatVars[i + m_addInjDataCnt];
7228 vector<MFloat> tmp(count);
7229 for(MInt j = 0; j < count; j++) {
7230 tmp[j] = globalFloatVars[i + j + 1 + m_addInjDataCnt];
7231 }
7232 m_injData.insert(make_pair(timeStep, tmp));
7233 }
7234 }
7235}
7236
7237
7242template <MInt nDim>
7244 TRACE();
7245
7246 if(!isActive()) return;
7247
7248 grid().findCartesianNghbIds();
7249
7250 findSpawnCellId();
7251
7252 // reset random-number generator
7253 if(m_activePrimaryBUp || m_activeSecondaryBUp) {
7254 m_sprayModel->m_PRNGPrimBreakUp.seed(m_sprayModel->m_spraySeed);
7255 }
7256
7257 if(domainId() != m_spawnDomainId) {
7258 m_particleResiduum = 0;
7259 if(m_activePrimaryBUp || m_activeSecondaryBUp) {
7260 m_sprayModel->m_PRNGPrimBreakUpCount = 0;
7261 }
7262 } else {
7263 m_sprayModel->m_PRNGPrimBreakUp.discard(m_sprayModel->m_PRNGPrimBreakUpCount);
7264 }
7265
7266 m_cellToNghbrHood.clear();
7267 m_cellToNghbrHoodInterpolation.clear();
7268
7269 // removeInvalidParticles(true);
7270
7271 checkParticles();
7272 checkCells();
7273
7274 updateFluidFraction();
7275
7276 setRegridTrigger();
7277
7278 // reset injection data on all ranks which do not have the injection-cell
7279 if(m_spawnCellId == -1 && m_sprayModel != nullptr) {
7280 const MInt vectorSize = m_injData.size();
7281 m_injData.clear();
7282 const MInt count = m_sprayModel->m_injDataSize + m_addInjDataCnt;
7283 vector<MFloat> tmp(count);
7284 for(MInt i = 0; i < count; i++) {
7285 tmp[i] = 0.0;
7286 }
7287 for(MInt i = 0; i < vectorSize; i++) {
7288 MInt stepId = globalTimeStep - vectorSize + i + 1;
7289 m_injData.insert(make_pair(stepId, tmp));
7290 }
7291 }
7292
7293#ifdef LPT_DEBUG
7294 MInt noParticles = a_noParticles();
7295
7296 MPI_Allreduce(MPI_IN_PLACE, &noParticles, 1, MPI_INT, MPI_SUM, mpiComm(), AT_, "INPLACE", "noParticles");
7297
7298 if(domainId() == 0) {
7299 cerr << noParticles << " #particles after balancing!" << endl;
7300 }
7301
7302 if(m_ellipsoids) {
7303 MInt noEllipsoids = a_noEllipsoidalParticles();
7304 MPI_Allreduce(MPI_IN_PLACE, &noEllipsoids, 1, MPI_INT, MPI_SUM, mpiComm(), AT_, "INPLACE", "noEllipsoids");
7305 cerr0 << noEllipsoids << " #ellipsoids after balancing!" << endl;
7306 }
7307#endif
7308
7309 /*
7310 MInt backup = globalTimeStep;
7311 globalTimeStep = -2;
7312 saveDebugRestartFile();
7313 globalTimeStep = backup;
7314 */
7315
7316 if(globalTimeStep < 0) {
7317 // add refinement around spawnCellId!
7318 // NOTE: a_noParticlesInCell will be corrected after the adaptation!
7319 if(m_spawnCellId > -1) {
7320 a_noParticlesInCell(m_spawnCellId) += 1;
7321 if(m_ellipsoids) a_noEllipsoidsInCell(m_spawnCellId) += 1;
7322 }
7323 } else {
7324 // get new neighbor list and distribution weights
7325 for(MInt i = 0; i < a_noParticles(); i++) {
7326 m_partList[i].resetWeights();
7327 }
7328 for(MInt i = 0; i < a_noEllipsoidalParticles(); i++) {
7329 m_partListEllipsoid[i].resetWeights();
7330 }
7331 // re-compute source terms
7332 resetSourceTerms(0);
7333 m_sumEvapMass = 0;
7334 if(!m_skipLPT) {
7335 coupling(-1);
7336 m_sumEvapMass = 0;
7337 if(m_nonBlockingComm) {
7338 sendSourceTerms();
7339 waitForSendReqs();
7340 receiveSourceTerms();
7341 } else {
7342 exchangeSourceTerms();
7343 }
7344 }
7345 }
7346
7347 // LPT-statistics
7348 MInt noCells = a_noCells();
7349 MInt minCells = noCells;
7350 MInt maxCells = noCells;
7351 MInt noPart = a_noParticles();
7352 MInt minParts = noPart;
7353 MInt maxParts = noPart;
7354
7355 MPI_Allreduce(MPI_IN_PLACE, &noCells, 1, MPI_INT, MPI_SUM, mpiComm(), AT_, "INPLACE", "noCells");
7356 MPI_Allreduce(MPI_IN_PLACE, &noPart, 1, MPI_INT, MPI_SUM, mpiComm(), AT_, "INPLACE", "noPart");
7357 MPI_Allreduce(MPI_IN_PLACE, &maxCells, 1, MPI_INT, MPI_MAX, mpiComm(), AT_, "INPLACE", "maxCells");
7358 MPI_Allreduce(MPI_IN_PLACE, &minCells, 1, MPI_INT, MPI_MIN, mpiComm(), AT_, "INPLACE", "minCells");
7359 MPI_Allreduce(MPI_IN_PLACE, &maxParts, 1, MPI_INT, MPI_MAX, mpiComm(), AT_, "INPLACE", "maxParts");
7360 MPI_Allreduce(MPI_IN_PLACE, &minParts, 1, MPI_INT, MPI_MIN, mpiComm(), AT_, "INPLACE", "minParts");
7361
7362 if(domainId() == 0) {
7363 cerr << "LPT cell-statistics : " << minCells << " - " << maxCells << " avg " << noCells / noDomains() << endl;
7364 cerr << "LPT particle-statistics : " << minParts << " - " << maxParts << " avg " << noPart / noDomains() << endl;
7365 }
7366}
7367
7372template <MInt nDim>
7374 TRACE();
7375
7376 if(!this->m_adaptation || this->m_noSensors == 0) return;
7377
7378 m_log << "Reset LPT-regrid trigger at timestep: " << globalTimeStep << endl;
7379
7380 MBoolScratchSpace inShadowLayer(a_noCells(), AT_, "inShadowLayer");
7381 MBoolScratchSpace tmp_data(a_noCells(), AT_, "tmp_data");
7382
7383 vector<MInt> lastLayer;
7384 vector<MInt> tmp;
7385
7386 // reset property and mark cells with particles as first layer
7387 for(MInt cellId = 0; cellId < a_noCells(); cellId++) {
7388 a_regridTrigger(cellId) = false;
7389 if(a_noParticlesInCell(cellId) > 0 || a_noEllipsoidsInCell(cellId) > 0 || cellId == m_spawnCellId) {
7390 inShadowLayer(cellId) = true;
7391 lastLayer.emplace_back(cellId);
7392 }
7393 }
7394
7395 // 4. cover shadow band locally in the domain
7396 MInt layerCount = 0;
7397 while(layerCount < m_bandWidth[maxRefinementLevel() - 1]) {
7398 // ii. build the next layer
7399 tmp.clear();
7400 for(MInt c = 0; c < (signed)lastLayer.size(); c++) {
7401 const MInt cellId = lastLayer[c];
7402 for(MInt d = 0; d < m_noDirs; d++) {
7403 if(c_hasNeighbor(cellId, d) == 0) {
7404 // additionally check for regrid when rebuilding
7405 if(c_parentId(cellId) > -1 && c_hasNeighbor(c_parentId(cellId), d) && !a_isHalo(cellId)) {
7406 if(a_isValidCell(cellId)) {
7407 m_forceAdaptation = true;
7408 }
7409 }
7410 continue;
7411 }
7412 const MInt nghbrId = c_neighborId(cellId, d);
7413 if(!inShadowLayer[nghbrId]) {
7414 tmp.emplace_back(nghbrId);
7415 if(layerCount > m_bandWidth[maxRefinementLevel() - 1] - m_innerBound) {
7416 a_regridTrigger(nghbrId) = true;
7417 }
7418 }
7419 inShadowLayer[nghbrId] = true;
7420 }
7421 }
7422
7423 // exchange next layer with neighboring domains
7424 for(MInt i = 0; i < grid().noNeighborDomains(); i++) {
7425 for(MInt j = 0; j < noWindowCells(i); j++) {
7426 tmp_data(windowCellId(i, j)) = inShadowLayer(windowCellId(i, j));
7427 }
7428 }
7429
7430 this->exchangeData(&tmp_data(0), 1);
7431
7432 for(MInt i = 0; i < grid().noNeighborDomains(); i++) {
7433 for(MInt j = 0; j < noHaloCells(i); j++) {
7434 const MInt haloCell = haloCellId(i, j);
7435 if(!inShadowLayer[haloCell]) {
7436 inShadowLayer[haloCell] = tmp_data(haloCell);
7437 if(inShadowLayer[haloCell]) {
7438 tmp.emplace_back(haloCell);
7439 if(layerCount > m_bandWidth[maxRefinementLevel() - 1] - m_innerBound) {
7440 a_regridTrigger(haloCell) = true;
7441 }
7442 }
7443 }
7444 }
7445 }
7446
7447 // iii. swap data
7448 // continue to the next layer
7449 lastLayer.clear();
7450 for(MInt c = 0; c < (signed)tmp.size(); c++) {
7451 lastLayer.emplace_back(tmp[c]);
7452 }
7453 layerCount++;
7454 }
7455
7456
7457 MPI_Allreduce(MPI_IN_PLACE, &m_forceAdaptation, 1, MPI_C_BOOL, MPI_LOR, mpiComm(), AT_, "MPI_IN_PLACE", "status");
7458
7459 if(domainId() == 0 && m_forceAdaptation) {
7460 cerr << "LPT-Solver is forcing a mesh-adaptation at time step " << globalTimeStep << endl;
7461 }
7462}
7463
7464
7469template <MInt nDim>
7471 if(!this->m_adaptation || this->m_noSensors < 1) return;
7472
7473 for(MInt id = 0; id < a_noParticles(); id++) {
7474 if(m_partList[id].isInvalid()) continue;
7475 const MInt cellId = m_partList[id].m_cellId;
7476 if(a_isHalo(cellId)) continue;
7477 if(a_regridTrigger(cellId)) {
7478 m_forceAdaptation = true;
7479 break;
7480 }
7481 }
7482 for(MInt id = 0; id < a_noEllipsoidalParticles(); id++) {
7483 if(m_partListEllipsoid[id].isInvalid()) continue;
7484 const MInt cellId = m_partListEllipsoid[id].m_cellId;
7485 if(a_isHalo(cellId)) continue;
7486 if(a_regridTrigger(cellId)) {
7487 m_forceAdaptation = true;
7488 break;
7489 }
7490 }
7491
7492 if(!m_nonBlockingComm) {
7493 // moved exchange for forceAdaptation() call!
7494 MPI_Allreduce(MPI_IN_PLACE, &m_forceAdaptation, 1, MPI_C_BOOL, MPI_LOR, mpiComm(), AT_, "MPI_IN_PLACE", "status");
7495
7496 if(domainId() == 0 && m_forceAdaptation) {
7497 cerr << "LPT-Solver is forcing a mesh-adaptation at time step " << globalTimeStep << endl;
7498 }
7499 } else {
7500 MPI_Iallreduce(MPI_IN_PLACE, &m_forceAdaptation, 1, MPI_C_BOOL, MPI_LOR, mpiComm(),
7501 &m_checkAdaptationRecvRequest[0], AT_, "MPI_IN_PLACE", "status");
7502 m_openRegridSend = true;
7503 }
7504}
7505
7506template <MInt nDim>
7508 if(!m_nonBlockingComm) {
7509 return;
7510 }
7511 if(!isActive()) {
7512 return;
7513 }
7514
7515 // wait for all send and receive
7516 if(m_openRegridSend) {
7517 RECORD_TIMER_START(m_timers[Timers::Exchange]);
7518 RECORD_TIMER_START(m_timers[Timers::Exchange2]);
7519 MPI_Wait(&m_checkAdaptationRecvRequest[0], MPI_STATUS_IGNORE, AT_);
7520 m_openRegridSend = false;
7521 RECORD_TIMER_STOP(m_timers[Timers::Exchange2]);
7522 RECORD_TIMER_STOP(m_timers[Timers::Exchange]);
7523 }
7524}
7525
7532template <MInt nDim>
7534 TRACE();
7535
7536 m_timeStepUpdated = true;
7537
7538 MFloat timeStep = std::numeric_limits<MFloat>::max();
7539
7540 for(MInt id = 0; id < a_noParticles(); id++) {
7541 if(m_partList[id].isInvalid()) continue;
7542 const MFloat dx = c_cellLengthAtCell(m_partList[id].m_cellId) / m_partList[id].s_lengthFactor;
7543 for(MInt d = 0; d < nDim; ++d) {
7544 const MFloat u_d = fabs(m_partList[id].m_velocity[d]);
7545 MFloat dt_dir = 0;
7546 if(abs(m_cfl + 1.0) < MFloatEps && abs(m_Ma + 1.0) > MFloatEps) {
7547 dt_dir = m_Ma / SQRT3 * dx / u_d;
7548 } else if(abs(m_cfl + 1.0) > MFloatEps)
7549 dt_dir = m_cfl * dx / u_d;
7550 timeStep = mMin(timeStep, dt_dir);
7551 }
7552 }
7553
7554 // Ellipsoids
7555 for(MInt id = 0; id < a_noEllipsoidalParticles(); id++) {
7556 if(m_partListEllipsoid[id].isInvalid()) continue;
7557 const MFloat dx = c_cellLengthAtCell(m_partListEllipsoid[id].m_cellId) / m_partListEllipsoid[id].s_lengthFactor;
7558 for(MInt d = 0; d < nDim; ++d) {
7559 const MFloat u_d = fabs(m_partListEllipsoid[id].m_velocity[d]);
7560 MFloat dt_dir = 0;
7561 if(abs(m_cfl + 1.0) < MFloatEps && abs(m_Ma + 1.0) > MFloatEps) {
7562 dt_dir = m_Ma / SQRT3 * dx / u_d;
7563 } else if(abs(m_cfl + 1.0) > MFloatEps)
7564 dt_dir = m_cfl * dx / u_d;
7565 timeStep = mMin(timeStep, dt_dir);
7566 }
7567 }
7568
7569 // consider injection speed
7570 if(m_activePrimaryBUp && m_spawnCellId > -1) {
7571 const MFloat dx = c_cellLengthAtCell(maxRefinementLevel()) / m_partList[0].s_lengthFactor;
7572 const MFloat u_d = fabs(m_sprayModel->injectionSpeed());
7573 MFloat dt_dir = m_cfl * dx / u_d;
7574 timeStep = mMin(timeStep, dt_dir);
7575 }
7576
7577 // consider spawn velocity
7578 if(m_spawnParticles && m_spawnCellId > -1) {
7579 const MFloat dx = c_cellLengthAtCell(m_spawnCellId) / m_partList[0].s_lengthFactor;
7580 const MFloat u_d = fabs(m_spawnVelocity);
7581 MFloat dt_dir = m_cfl * dx / u_d;
7582 timeStep = mMin(timeStep, dt_dir);
7583 }
7584
7585 // read timeStep from property file
7586 // i.e. used to resolve evaporation time-span
7587 MBool useFixed = false;
7588 if(a_noParticles() > 0) useFixed = true;
7589 if(a_noEllipsoidalParticles() > 0) useFixed = true;
7590 if(m_spawnCellId > -1 && m_sprayModel != nullptr && m_sprayModel->soonInjection(m_time + timeStep * 100)) {
7591 // check if injection will occure in the next time-steps
7592 useFixed = true;
7593 }
7594
7595 if(useFixed && Context::propertyExists("fixedTimeStep", solverId())) {
7596 timeStep = Context::getSolverProperty<MFloat>("fixedTimeStep", m_solverId, AT_);
7597 if(domainId() == m_spawnDomainId) {
7598 cerr << "Applying LPT fixed timeStep of " << timeStep << endl;
7599 }
7600 }
7601
7602 MPI_Allreduce(MPI_IN_PLACE, &timeStep, 1, MPI_DOUBLE, MPI_MIN, mpiComm(), AT_, "MPI_IN_PLACE", "timeStep");
7603
7604 if(domainId() == 0) {
7605 cerr << "LPT-timestep could be " << timeStep << " is " << m_timeStep << endl;
7606 }
7607
7608 return timeStep;
7609}
7610
7619template <MInt nDim>
7621 if(m_nonBlockingComm) return;
7622 if(noDomains() > 1) {
7623 if(m_massCoupling) {
7624 maia::mpi::reverseExchangeAddData(grid().neighborDomains(), grid().haloCells(), grid().windowCells(), mpiComm(),
7625 &a_massFlux(0), 1);
7626 }
7627 if(m_momentumCoupling) {
7628 maia::mpi::reverseExchangeAddData(grid().neighborDomains(), grid().haloCells(), grid().windowCells(), mpiComm(),
7629 &a_momentumFlux(0, 0), nDim);
7630 maia::mpi::reverseExchangeAddData(grid().neighborDomains(), grid().haloCells(), grid().windowCells(), mpiComm(),
7631 &a_workFlux(0), 1);
7632 }
7633 if(m_heatCoupling) {
7634 maia::mpi::reverseExchangeAddData(grid().neighborDomains(), grid().haloCells(), grid().windowCells(), mpiComm(),
7635 &a_heatFlux(0), 1);
7636 }
7637 }
7638 // after the values on halo-cells have been added to the matching window cells, the halo-cell values
7639 // must be resettet
7640 resetSourceTerms(noInternalCells());
7641}
7642
7647template <MInt nDim>
7649 if(!m_nonBlockingComm) {
7650 mTerm(1, AT_, "Calling non-Blocking exchange for blocking setup!");
7651 }
7652
7653 // start receiving source terms
7654 for(MInt n = 0; n < grid().noLeafSendNeighborDomains(); n++) {
7655 const MInt d = grid().leafSendNeighborDomain(n);
7656 MPI_Irecv(&m_sourceRecv[d][0], m_noSourceTerms * grid().noLeafWindowCells(d), MPI_DOUBLE, grid().neighborDomain(d),
7657 mpiTag("SOURCE_TERMS"), mpiComm(), &m_sourceRecvRequest[n], AT_, "m_sourceRecv");
7658 }
7659
7660 // wait until all sends have been received before sending new
7661 if(m_openSourceSend) {
7662 MPI_Waitall(grid().noLeafRecvNeighborDomains(), &m_sourceSendRequest[0], MPI_STATUSES_IGNORE, AT_);
7663 m_openSourceSend = false;
7664 }
7665
7666 // send source terms
7667 for(MInt n = 0; n < grid().noLeafRecvNeighborDomains(); n++) {
7668 const MInt d = grid().leafRecvNeighborDomain(n);
7669 MInt buffId = 0;
7670 for(MInt j = 0; j < grid().noLeafHaloCells(d); j++) {
7671 const MInt cellId = grid().leafHaloCell(d, j);
7672 if(m_massCoupling) {
7673 m_sourceSend[d][buffId++] = a_massFlux(cellId);
7674 }
7675 if(m_momentumCoupling) {
7676 for(MInt i = 0; i < nDim; i++) {
7677 m_sourceSend[d][buffId++] = a_momentumFlux(cellId, i);
7678 }
7679 m_sourceSend[d][buffId++] = a_workFlux(cellId);
7680 }
7681 if(m_heatCoupling) {
7682 m_sourceSend[d][buffId++] = a_heatFlux(cellId);
7683 }
7684 }
7685 MPI_Isend(&m_sourceSend[d][0], m_noSourceTerms * grid().noLeafHaloCells(d), MPI_DOUBLE, grid().neighborDomain(d),
7686 mpiTag("SOURCE_TERMS"), mpiComm(), &m_sourceSendRequest[n], AT_, "m_sourceSend");
7687 }
7688 m_openSourceSend = true;
7689
7690 // reset sources on halo cells to zero after send
7691 resetSourceTerms(noInternalCells());
7692}
7693
7698template <MInt nDim>
7700 if(!m_nonBlockingComm) return;
7701
7702 RECORD_TIMER_START(m_timers[Timers::Exchange]);
7703
7704 // wait for all receives before using buffers
7705 MPI_Waitall(grid().noLeafSendNeighborDomains(), &m_sourceRecvRequest[0], MPI_STATUSES_IGNORE, AT_);
7706
7707 // add received source terms
7708 RECORD_TIMER_START(m_timers[Timers::Exchange5]);
7709
7710 for(MInt n = 0; n < grid().noLeafSendNeighborDomains(); n++) {
7711 const MInt d = grid().leafSendNeighborDomain(n);
7712 MInt buffId = 0;
7713 for(MInt j = 0; j < grid().noLeafWindowCells(d); j++) {
7714 const MInt cellId = grid().leafWindowCell(d, j);
7715 if(m_massCoupling) {
7716 a_massFlux(cellId) += m_sourceRecv[d][buffId++];
7717 }
7718 if(m_momentumCoupling) {
7719 for(MInt i = 0; i < nDim; i++) {
7720 a_momentumFlux(cellId, i) += m_sourceRecv[d][buffId++];
7721 }
7722 a_workFlux(cellId) += m_sourceRecv[d][buffId++];
7723 }
7724 if(m_heatCoupling) {
7725 a_heatFlux(cellId) += m_sourceRecv[d][buffId++];
7726 }
7727 }
7728 }
7729
7730 RECORD_TIMER_STOP(m_timers[Timers::Exchange5]);
7731 RECORD_TIMER_STOP(m_timers[Timers::Exchange]);
7732}
7733
7738template <MInt nDim>
7740 if(!m_nonBlockingComm) {
7741 return;
7742 }
7743 m_receiveFlowField = true;
7744
7745 // start receiving flow field data
7746 for(MInt n = 0; n < grid().noLeafRecvNeighborDomains(); n++) {
7747 const MInt d = grid().leafRecvNeighborDomain(n);
7748 MPI_Irecv(&m_flowRecv[d][0], (PV.noVars() + m_evaporation) * grid().noLeafHaloCells(d), MPI_DOUBLE,
7749 grid().neighborDomain(d), mpiTag("FLOW_FIELD"), mpiComm(), &m_flowRecvRequest[n], AT_, "m_flowRecv");
7750 }
7751
7752 // wait until all sends have been processed before sending new
7753 if(m_openFlowSend) {
7754 MPI_Waitall(grid().noLeafSendNeighborDomains(), &m_flowSendRequest[0], MPI_STATUSES_IGNORE, AT_);
7755 m_openFlowSend = false;
7756 }
7757
7758
7759 // send flow field data
7760 for(MInt n = 0; n < grid().noLeafSendNeighborDomains(); n++) {
7761 const MInt d = grid().leafSendNeighborDomain(n);
7762 MInt buffId = 0;
7763 for(MInt j = 0; j < grid().noLeafWindowCells(d); j++) {
7764 const MInt cellId = grid().leafWindowCell(d, j);
7765 std::copy_n(&a_fluidVariable(cellId, 0), PV.noVars(), &m_flowSend[d][buffId]);
7766 buffId += PV.noVars();
7767 if(m_evaporation) {
7768 std::copy_n(&a_fluidSpecies(cellId), 1, &m_flowSend[d][buffId]);
7769 buffId++;
7770 }
7771 }
7772 MPI_Isend(&m_flowSend[d][0], (PV.noVars() + m_evaporation) * grid().noLeafWindowCells(d), MPI_DOUBLE,
7773 grid().neighborDomain(d), mpiTag("FLOW_FIELD"), mpiComm(), &m_flowSendRequest[n], AT_, "m_flowSend");
7774 }
7775
7776 m_openFlowSend = true;
7777}
7778
7783template <MInt nDim>
7785 if(!m_nonBlockingComm) return;
7786 if(!m_receiveFlowField) return;
7787
7788 RECORD_TIMER_START(m_timers[Timers::Exchange]);
7789
7790 MPI_Waitall(grid().noLeafRecvNeighborDomains(), &m_flowRecvRequest[0], MPI_STATUSES_IGNORE, AT_);
7791
7792 // set data on halo-cells
7793 RECORD_TIMER_START(m_timers[Timers::Exchange5]);
7794 for(MInt n = 0; n < grid().noLeafRecvNeighborDomains(); n++) {
7795 const MInt d = grid().leafRecvNeighborDomain(n);
7796 MInt buffId = 0;
7797 for(MInt j = 0; j < grid().noLeafHaloCells(d); j++) {
7798 const MInt cellId = grid().leafHaloCell(d, j);
7799 std::copy_n(&m_flowRecv[d][buffId], PV.noVars(), &a_fluidVariable(cellId, 0));
7800 buffId += PV.noVars();
7801 if(m_evaporation) {
7802 std::copy_n(&m_flowRecv[d][buffId], 1, &a_fluidSpecies(cellId));
7803 buffId++;
7804 }
7805 }
7806 }
7807
7808 RECORD_TIMER_STOP(m_timers[Timers::Exchange5]);
7809 RECORD_TIMER_STOP(m_timers[Timers::Exchange]);
7810 m_receiveFlowField = false;
7811}
7812
7817template <MInt nDim>
7819 if(!m_nonBlockingComm) {
7820 return;
7821 }
7822
7823 if(m_openParticleInjSend) {
7824 MPI_Waitall(noNeighborDomains(), &m_mpi_reqSendFloat[0], MPI_STATUSES_IGNORE, AT_);
7825 MPI_Waitall(noNeighborDomains(), &m_mpi_reqSendInt[0], MPI_STATUSES_IGNORE, AT_);
7826 m_openParticleInjSend = false;
7827 }
7828
7829 if(m_openParticleSend) {
7830 MPI_Waitall(grid().noLeafSendNeighborDomains(), &m_mpi_reqSendFloat[0], MPI_STATUSES_IGNORE, AT_);
7831 MPI_Waitall(grid().noLeafSendNeighborDomains(), &m_mpi_reqSendInt[0], MPI_STATUSES_IGNORE, AT_);
7832 m_openParticleSend = false;
7833 }
7834
7835 if(m_openSourceSend) {
7836 MPI_Waitall(grid().noLeafRecvNeighborDomains(), &m_sourceSendRequest[0], MPI_STATUSES_IGNORE, AT_);
7837 m_openSourceSend = false;
7838 }
7839
7840 if(m_openFlowSend) {
7841 MPI_Waitall(grid().noLeafSendNeighborDomains(), &m_flowSendRequest[0], MPI_STATUSES_IGNORE, AT_);
7842 m_openFlowSend = false;
7843 }
7844
7845 if(m_openRegridSend) {
7846 MPI_Wait(&m_checkAdaptationRecvRequest[0], MPI_STATUS_IGNORE, AT_);
7847 m_openRegridSend = false;
7848 }
7849
7850 if(m_openSlopesSend) {
7851 MPI_Waitall(grid().noLeafSendNeighborDomains(), &m_slopesSendRequest[0], MPI_STATUSES_IGNORE, AT_);
7852 m_openSlopesSend = false;
7853 }
7854}
7855
7860template <MInt nDim>
7862 if(!m_ellipsoids) return;
7863 if(!m_nonBlockingComm) return;
7864
7865 m_receiveVelocitySlopes = true;
7866
7867 // start receiving velocity slopes
7868 for(MInt n = 0; n < grid().noLeafRecvNeighborDomains(); n++) {
7869 const MInt d = grid().leafRecvNeighborDomain(n);
7870 MPI_Irecv(&m_slopesRecv[d][0], (nDim * nDim) * grid().noLeafHaloCells(d), MPI_DOUBLE, grid().neighborDomain(d),
7871 mpiTag("VELOCITY_SLOPES"), mpiComm(), &m_slopesRecvRequest[n], AT_, "m_slopesRecv");
7872 }
7873
7874 // wait until all sends have been processed before sending new
7875 if(m_openSlopesSend) {
7876 MPI_Waitall(grid().noLeafSendNeighborDomains(), &m_slopesSendRequest[0], MPI_STATUSES_IGNORE, AT_);
7877 m_openSlopesSend = false;
7878 }
7879
7880 // send velocity slopes
7881 for(MInt n = 0; n < grid().noLeafSendNeighborDomains(); n++) {
7882 const MInt d = grid().leafSendNeighborDomain(n);
7883 MInt buffId = 0;
7884 for(MInt j = 0; j < grid().noLeafWindowCells(d); j++) {
7885 const MInt cellId = grid().leafWindowCell(d, j);
7886 std::copy_n(&a_velocitySlope(cellId, 0, 0), nDim * nDim, &m_slopesSend[d][buffId]);
7887 buffId += nDim * nDim;
7888 }
7889 MPI_Isend(&m_slopesSend[d][0], (nDim * nDim) * grid().noLeafWindowCells(d), MPI_DOUBLE, grid().neighborDomain(d),
7890 mpiTag("VELOCITY_SLOPES"), mpiComm(), &m_slopesSendRequest[n], AT_, "m_slopesSend");
7891 }
7892
7893 m_openSlopesSend = true;
7894}
7895
7900template <MInt nDim>
7902 if(!m_ellipsoids) return; // only if ellipsoids are activated
7903 if(!m_nonBlockingComm) return;
7904 if(!m_receiveVelocitySlopes) return;
7905
7906 RECORD_TIMER_START(m_timers[Timers::Exchange]);
7907
7908 MPI_Waitall(grid().noLeafRecvNeighborDomains(), &m_slopesRecvRequest[0], MPI_STATUSES_IGNORE, AT_);
7909
7910 // set data on halo-cells
7911 RECORD_TIMER_START(m_timers[Timers::Exchange5]);
7912 for(MInt n = 0; n < grid().noLeafRecvNeighborDomains(); n++) {
7913 const MInt d = grid().leafRecvNeighborDomain(n);
7914 MInt buffId = 0;
7915 for(MInt j = 0; j < grid().noLeafHaloCells(d); j++) {
7916 const MInt cellId = grid().leafHaloCell(d, j);
7917 std::copy_n(&m_slopesRecv[d][buffId], nDim * nDim, &a_velocitySlope(cellId, 0, 0));
7918 buffId += nDim * nDim;
7919 }
7920 }
7921
7922 RECORD_TIMER_STOP(m_timers[Timers::Exchange5]);
7923 RECORD_TIMER_STOP(m_timers[Timers::Exchange]);
7924
7925 m_receiveVelocitySlopes = false;
7926}
7927
7934template <MInt nDim>
7936 TRACE();
7937
7938 if(m_sprayModel == nullptr) return;
7939
7940 static const MInt cadStart = m_sprayModel->m_injectionCrankAngle;
7941 static const MInt cadEnd = m_sprayModel->m_injectionCrankAngle + 90;
7942 static const MInt cadInterval = 1;
7943 const MInt cadMaxIter = (cadEnd - cadStart) / cadInterval;
7944
7945 const MFloat cad = maia::math::crankAngle(m_time, m_sprayModel->m_Strouhal, m_sprayModel->m_initialCad, 0);
7946 const MFloat cad_prev =
7947 maia::math::crankAngle(m_time - m_timeStep, m_sprayModel->m_Strouhal, m_sprayModel->m_initialCad, 0);
7948 const MFloat cad_next =
7949 maia::math::crankAngle(m_time + m_timeStep, m_sprayModel->m_Strouhal, m_sprayModel->m_initialCad, 0);
7950
7951 if(cad > cadEnd) return;
7952 if(cad < (cadStart - cadInterval)) return;
7953
7954 // iterate for full solution output
7955 for(MInt i = 0; i < cadMaxIter; i++) {
7956 const MFloat cadTarget = cadStart + i * cadInterval;
7957
7958 if(cad < cadTarget && cad_next > cadTarget) {
7959 if(fabs(cad - cadTarget) <= fabs(cad_next - cadTarget)) {
7960 if(domainId() == 0) {
7961 cerr << "Saving particle output at crankAngle " << cad << endl;
7962 }
7963 writePartData();
7964 break;
7965 }
7966 } else if(cad > cadTarget && cad_prev < cadTarget) {
7967 if(abs(cad - cadTarget) < fabs(cad_prev - cadTarget)) {
7968 if(domainId() == 0) {
7969 cerr << "Saving particle output at crankAngle " << cad << endl;
7970 }
7971 writePartData();
7972 break;
7973 }
7974 }
7975 }
7976}
7977
7983template <MInt nDim>
7985 TRACE();
7986
7987 std::fill(m_timers.begin(), m_timers.end(), -1);
7988
7989 // Create timer group & timer for solver, and start the timer
7990 NEW_TIMER_GROUP_NOCREATE(m_timerGroup, "LPT (solverId = " + to_string(m_solverId) + ")");
7991 NEW_TIMER_NOCREATE(m_timers[Timers::SolverType], "total object lifetime", m_timerGroup);
7992 RECORD_TIMER_START(m_timers[Timers::SolverType]);
7993
7994 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::TimeInt], "Time-integration", m_timers[Timers::SolverType]);
7995 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::PreTime], "PreTimeStep", m_timers[Timers::SolverType]);
7996
7997 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::Motion], "Motion", m_timers[Timers::TimeInt]);
7998 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::Energy], "Energy", m_timers[Timers::TimeInt]);
7999 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::Wall], "Wall", m_timers[Timers::TimeInt]);
8000 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::SourceTerms], "Sources", m_timers[Timers::TimeInt]);
8001
8002 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::Injection], "injection", m_timers[Timers::TimeInt]);
8003 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::Breakup], "breakup", m_timers[Timers::TimeInt]);
8004
8005 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::Exchange], "exchange", m_timers[Timers::SolverType]);
8006
8007 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::Exchange1], "mpi", m_timers[Timers::Exchange]);
8008 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::Exchange2], "prepare", m_timers[Timers::Exchange]);
8009 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::Exchange3], "pack", m_timers[Timers::Exchange]);
8010 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::Exchange4], "receive", m_timers[Timers::Exchange]);
8011 NEW_SUB_TIMER_NOCREATE(m_timers[Timers::Exchange5], "unpack", m_timers[Timers::Exchange]);
8012}
8013
8019template <MInt nDim>
8020MFloat LPT<nDim>::reduceData(const MInt cellId, MFloat* data, const MInt dataBlockSize, const MBool average) {
8021 MFloat vol = a_bndryCellId(cellId) < 0 ? c_cellVolume(cellId) : m_bndryCells->a[a_bndryCellId(cellId)].m_volume;
8022
8023 if(c_noChildren(cellId) > 0) {
8024 vol = F0;
8025 for(MInt d = 0; d < dataBlockSize; d++) {
8026 data[dataBlockSize * cellId + d] = F0;
8027 }
8028 for(MInt child = 0; child < c_noChildren(cellId); child++) {
8029 MInt childId = c_childId(cellId, child);
8030 if(childId < 0) {
8031 continue;
8032 }
8033 if(c_noChildren(childId) == 0 && !a_isValidCell(childId)) {
8034 continue;
8035 }
8036 MFloat volc = reduceData(childId, data, dataBlockSize);
8037 for(MInt d = 0; d < dataBlockSize; d++) {
8038 data[dataBlockSize * cellId + d] += volc * data[dataBlockSize * childId + d];
8039 }
8040 vol += volc;
8041 }
8042 if(average) {
8043 for(MInt d = 0; d < dataBlockSize; d++) {
8044 data[dataBlockSize * cellId + d] /= mMax(1e-14, vol);
8045 }
8046 }
8047 }
8048 return vol;
8049}
8050
8061template <MInt nDim>
8062template <MInt a, MInt b, MBool interpolateVelocitySlopes>
8063void LPT<nDim>::interpolateVariablesLS(const MInt cellId, const MFloat* const position, MFloat* const interpolatedVar) {
8064 ASSERT((b - a) > 0, "ERROR: Difference between b and a needs to be positive!");
8065 ASSERT(cellId >= 0, "ERROR: Invalid cellId!");
8066
8067 const MFloat originX = c_coordinate(cellId, 0);
8068 const MFloat originY = c_coordinate(cellId, 1);
8069 const MFloat originZ = c_coordinate(cellId, 2);
8070
8074 std::vector<MInt> nghbrList;
8075#ifdef _OPENMP
8076#pragma omp critical
8077#endif
8078 {
8079 if(m_cellToNghbrHoodInterpolation.count(cellId) > 0) {
8080 nghbrList = m_cellToNghbrHoodInterpolation[cellId];
8081 // std::cout << "New use existing hood with " << nghbrList.size() << " for " << cellId << std::endl;
8082 } else {
8083 grid().findDirectNghbrs(cellId, nghbrList);
8084 // std::cout << "New find hood with " << nghbrList.size() << std::endl;
8085 if(nghbrList.size() < 12) {
8086 // std::cout << "New find better hood with " << nghbrList.size() << std::endl;
8087 grid().findNeighborHood(cellId, 2, nghbrList);
8088#ifndef NDEBUG
8089 if(nghbrList.size() < 14) {
8090 std::cout << "WARNING: Only found " << nghbrList.size() << " neighbors during interpolation!" << std::endl;
8091 }
8092#endif
8093 }
8094 m_cellToNghbrHoodInterpolation.emplace(make_pair(cellId, nghbrList));
8095 }
8096 }
8097
8098 const MInt pseudoVarCount = (interpolateVelocitySlopes ? nDim * nDim + b - a : b - a);
8099 std::vector<std::array<MFloat, pseudoVarCount>> pseudoCellVar{};
8100 std::vector<std::array<MFloat, nDim>> pseudoCellPos{};
8101
8102 auto nghbrIt = nghbrList.begin();
8103 while(nghbrIt != nghbrList.end()) {
8104 const MInt nghbrId = *nghbrIt;
8105 const MInt bndryCellId = a_bndryCellId(nghbrId);
8106 if(bndryCellId > -1) {
8107 const auto& bndryCell = m_bndryCells->a[bndryCellId];
8108 for(MInt srfcId = 0; srfcId < bndryCell.m_noSrfcs; srfcId++) {
8109 auto& srfc = *bndryCell.m_srfcs[srfcId];
8110 pseudoCellPos.emplace_back();
8111 auto& pos = pseudoCellPos.back();
8112 for(MInt n = 0; n < nDim; n++) {
8113 pos[n] = srfc.m_coordinates[n];
8114 }
8115 // TODO labels:LPT Distinguish between different boundary condtions
8116 pseudoCellVar.emplace_back();
8117 auto& vars = pseudoCellVar.back();
8118 for(MInt i = a; i < b; i++) {
8119 vars[i] = a_fluidVariable(nghbrId, i);
8120 }
8121 if(interpolateVelocitySlopes) {
8122 for(MInt i = 0; i < nDim; i++) {
8123 for(MInt j = 0; j < nDim; j++) {
8124 vars[b + nDim * i + j] = a_velocitySlope(nghbrId, i, j);
8125 }
8126 }
8127 }
8128 }
8129 nghbrIt = nghbrList.erase(nghbrIt);
8130 } else {
8131 ++nghbrIt;
8132 }
8133 }
8134
8135 const MUint noInterpolationPoints = nghbrList.size() + pseudoCellPos.size();
8136
8140 if(noInterpolationPoints < 10) {
8141 std::vector<MFloat> dist(noInterpolationPoints, 0);
8142 // Internal cells
8143 for(MUint nId = 0; nId < nghbrList.size(); nId++) {
8144 const MInt nghbrId = nghbrList[nId];
8145 dist[nId] = 0;
8146 for(MInt i = 0; i < nDim; i++) {
8147 dist[nId] += POW2(c_coordinate(nghbrId, i) - position[i]);
8148 }
8149 }
8150 // Boundary surfaces
8151 for(MUint pId = 0; pId < pseudoCellPos.size(); pId++) {
8152 IF_CONSTEXPR(nDim == 2) {
8153 dist[pId + nghbrList.size()] =
8154 POW2(pseudoCellPos[pId][0] - position[0]) + POW2(pseudoCellPos[pId][1] - position[1]);
8155 }
8156 else {
8157 dist[pId + nghbrList.size()] = POW2(pseudoCellPos[pId][0] - position[0])
8158 + POW2(pseudoCellPos[pId][1] - position[1])
8159 + POW2(pseudoCellPos[pId][2] - position[2]);
8160 }
8161 }
8162
8163 MFloat sum = std::accumulate(dist.begin(), dist.end(), 0.0);
8164
8165 for(MInt k = a; k < b; k++) {
8166 MFloat var = 0;
8167 interpolatedVar[k] = 0.0;
8168
8169 // Internal cells
8170 for(MUint nId = 0; nId < nghbrList.size(); nId++) {
8171 const MInt nghbrId = nghbrList[nId];
8172 var = a_fluidVariable(nghbrId, k);
8173 interpolatedVar[k] += dist[nId] / sum * var;
8174 }
8175 // Boundary surfaces
8176 for(MUint pId = 0; pId < pseudoCellPos.size(); pId++) {
8177 var = pseudoCellVar[pId][k - a];
8178 interpolatedVar[k] += dist[pId + nghbrList.size()] / sum * var;
8179 }
8180 }
8181
8182 // interpolate velocity slopes
8183 if(interpolateVelocitySlopes) {
8184 for(MInt i = 0; i < nDim; i++) {
8185 for(MInt j = 0; j < nDim; j++) {
8186 MFloat var = 0;
8187 interpolatedVar[b + nDim * i + j - a] = 0.0;
8188 // Internal cells
8189 for(MUint nId = 0; nId < nghbrList.size(); nId++) {
8190 const MInt nghbrId = nghbrList[nId];
8191 var = a_velocitySlope(nghbrId, i, j);
8192 interpolatedVar[b + nDim * i + j - a] += dist[nId] / sum * var;
8193 }
8194 // Boundary surfaces
8195 for(MUint pId = 0; pId < pseudoCellPos.size(); pId++) {
8196 var = pseudoCellVar[pId][nDim * i + j + b];
8197 interpolatedVar[b + nDim * i + j - a] += dist[pId + nghbrList.size()] / sum * var;
8198 }
8199 }
8200 }
8201 }
8202
8203#ifndef NDEBUG
8204 // cout << "WARNING: Not enough neighbors found to use LS using weighted averaging instead!" << endl;
8205 m_log << "WARNING: Not enough neighbors found to use LS using weighted averaging instead!" << std::endl;
8206#endif
8207 } else {
8211 MFloatTensor LSMatrix(2 * nDim + pow(2, nDim - 1), 2 * nDim + pow(2, nDim - 1));
8212 MFloatTensor rhs(2 * nDim + pow(2, nDim - 1));
8213 // MFloatTensor weights(2 * nDim + pow(2, nDim - 1));
8214 MFloatTensor matInv(2 * nDim + pow(2, nDim - 1), 2 * nDim + pow(2, nDim - 1));
8215
8216 LSMatrix.set(0.0);
8217 // weights.set(1.0);
8218 matInv.set(0.0);
8219
8220 MFloat sumX = 0;
8221 MFloat sumY = 0;
8222 MFloat sumZ = 0;
8223 MFloat sumXY = 0;
8224 MFloat sumXZ = 0;
8225 MFloat sumYZ = 0;
8226 MFloat sumX2 = 0;
8227 MFloat sumY2 = 0;
8228 MFloat sumZ2 = 0;
8229 MFloat sumXYZ = 0;
8230 MFloat sumX2Y = 0;
8231 MFloat sumX2Z = 0;
8232 MFloat sumXY2 = 0;
8233 MFloat sumY2Z = 0;
8234 MFloat sumXZ2 = 0;
8235 MFloat sumYZ2 = 0;
8236 MFloat sumX3 = 0;
8237 MFloat sumY3 = 0;
8238 MFloat sumZ3 = 0;
8239 MFloat sumX3Y = 0;
8240 MFloat sumX3Z = 0;
8241 MFloat sumXY3 = 0;
8242 MFloat sumY3Z = 0;
8243 MFloat sumXZ3 = 0;
8244 MFloat sumYZ3 = 0;
8245 MFloat sumX2Y2 = 0;
8246 MFloat sumX2Z2 = 0;
8247 MFloat sumX2YZ = 0;
8248 MFloat sumY2Z2 = 0;
8249 MFloat sumXY2Z = 0;
8250 MFloat sumXYZ2 = 0;
8251 MFloat sumX4 = 0;
8252 MFloat sumY4 = 0;
8253 MFloat sumZ4 = 0;
8254
8255
8256 // 2.1 Calculate intermediate variables
8257 for(MUint nId = 1; nId < noInterpolationPoints; nId++) {
8258 MFloat x = 0;
8259 MFloat y = 0;
8260 MFloat z = 0;
8261 if(nId >= nghbrList.size()) {
8262 const MUint pId = nId - nghbrList.size();
8263 x = pseudoCellPos[pId][0] - originX;
8264 y = pseudoCellPos[pId][1] - originY;
8265 IF_CONSTEXPR(nDim == 3) { z = pseudoCellPos[pId][2] - originZ; }
8266 } else {
8267 const MInt nghbrId = nghbrList[nId];
8268 x = c_coordinate(nghbrId, 0) - originX;
8269 y = c_coordinate(nghbrId, 1) - originY;
8270 IF_CONSTEXPR(nDim == 3) { z = c_coordinate(nghbrId, 2) - originZ; }
8271 }
8272
8273 sumX += x;
8274 sumY += y;
8275 sumZ += z;
8276 sumXY += x * y;
8277 sumXZ += x * z;
8278 sumYZ += y * z;
8279 sumX2 += x * x;
8280 sumY2 += y * y;
8281 sumZ2 += z * z;
8282 sumXYZ += x * y * z;
8283 sumX2Y += x * x * y;
8284 sumX2Z += x * x * z;
8285 sumXY2 += x * y * y;
8286 sumY2Z += y * y * z;
8287 sumXZ2 += z * z * x;
8288 sumYZ2 += z * z * y;
8289 sumX3 += x * x * x;
8290 sumY3 += y * y * y;
8291 sumZ3 += z * z * z;
8292 sumX3Y += x * x * x * y;
8293 sumX3Z += x * x * x * z;
8294 sumXY3 += y * y * y * x;
8295 sumY3Z += y * y * y * z;
8296 sumXZ3 += z * z * z * x;
8297 sumYZ3 += z * z * z * y;
8298 sumX2Y2 += x * x * y * y;
8299 sumX2Z2 += x * x * z * z;
8300 sumX2YZ += x * x * y * z;
8301 sumY2Z2 += y * y * z * z;
8302 sumXY2Z += x * y * y * z;
8303 sumXYZ2 += x * y * z * z;
8304 sumX4 += x * x * x * x;
8305 sumY4 += y * y * y * y;
8306 sumZ4 += z * z * z * z;
8307 }
8308
8309 // 2.2 Use intermediate variables to build LSMatrix
8310 IF_CONSTEXPR(nDim == 2) {
8311 // diagonal
8312 LSMatrix(0, 0) = sumX4;
8313 LSMatrix(1, 1) = sumY4;
8314 LSMatrix(2, 2) = sumX2Y2;
8315 LSMatrix(3, 3) = sumX2;
8316 LSMatrix(4, 4) = sumY2;
8317 LSMatrix(5, 5) = noInterpolationPoints;
8318
8319 // first column/row
8320 LSMatrix(0, 1) = LSMatrix(1, 0) = sumX2Y2;
8321 LSMatrix(0, 2) = LSMatrix(2, 0) = sumX3Y;
8322 LSMatrix(0, 3) = LSMatrix(3, 0) = sumX3;
8323 LSMatrix(0, 4) = LSMatrix(4, 0) = sumX2Y;
8324 LSMatrix(0, 5) = LSMatrix(5, 0) = sumX2;
8325
8326 // second column/row
8327 LSMatrix(1, 2) = LSMatrix(2, 1) = sumXY3;
8328 LSMatrix(1, 3) = LSMatrix(3, 1) = sumXY2;
8329 LSMatrix(1, 4) = LSMatrix(4, 1) = sumY3;
8330 LSMatrix(1, 5) = LSMatrix(5, 1) = sumY2;
8331
8332 // third column/row
8333 LSMatrix(2, 3) = LSMatrix(3, 2) = sumX2Y;
8334 LSMatrix(2, 4) = LSMatrix(4, 2) = sumXY2;
8335 LSMatrix(2, 5) = LSMatrix(5, 2) = sumXY;
8336
8337 // fourth column/row
8338 LSMatrix(3, 4) = LSMatrix(4, 3) = sumXY;
8339 LSMatrix(3, 5) = LSMatrix(5, 3) = sumX;
8340
8341 // fifth coulmn/row
8342 LSMatrix(4, 5) = LSMatrix(5, 4) = sumY;
8343 }
8344 else {
8345 // diagonal
8346 LSMatrix(0, 0) = sumX4;
8347 LSMatrix(1, 1) = sumY4;
8348 LSMatrix(2, 2) = sumZ4;
8349 LSMatrix(3, 3) = sumX2Y2;
8350 LSMatrix(4, 4) = sumX2Z2;
8351 LSMatrix(5, 5) = sumY2Z2;
8352 LSMatrix(6, 6) = sumX2;
8353 LSMatrix(7, 7) = sumY2;
8354 LSMatrix(8, 8) = sumZ2;
8355 LSMatrix(9, 9) = noInterpolationPoints;
8356
8357 // first column/row
8358 LSMatrix(0, 1) = LSMatrix(1, 0) = sumX2Y2;
8359 LSMatrix(0, 2) = LSMatrix(2, 0) = sumX2Z2;
8360 LSMatrix(0, 3) = LSMatrix(3, 0) = sumX3Y;
8361 LSMatrix(0, 4) = LSMatrix(4, 0) = sumX3Z;
8362 LSMatrix(0, 5) = LSMatrix(5, 0) = sumX2YZ;
8363 LSMatrix(0, 6) = LSMatrix(6, 0) = sumX3;
8364 LSMatrix(0, 7) = LSMatrix(7, 0) = sumX2Y;
8365 LSMatrix(0, 8) = LSMatrix(8, 0) = sumX2Z;
8366 LSMatrix(0, 9) = LSMatrix(9, 0) = sumX2;
8367
8368 // second column/row
8369 LSMatrix(1, 2) = LSMatrix(2, 1) = sumY2Z2;
8370 LSMatrix(1, 3) = LSMatrix(3, 1) = sumXY3;
8371 LSMatrix(1, 4) = LSMatrix(4, 1) = sumXY2Z;
8372 LSMatrix(1, 5) = LSMatrix(5, 1) = sumY3Z;
8373 LSMatrix(1, 6) = LSMatrix(6, 1) = sumXY2;
8374 LSMatrix(1, 7) = LSMatrix(7, 1) = sumY3;
8375 LSMatrix(1, 8) = LSMatrix(8, 1) = sumY2Z;
8376 LSMatrix(1, 9) = LSMatrix(9, 1) = sumY2;
8377
8378 // third column/row
8379 LSMatrix(2, 3) = LSMatrix(3, 2) = sumXYZ2;
8380 LSMatrix(2, 4) = LSMatrix(4, 2) = sumXZ3;
8381 LSMatrix(2, 5) = LSMatrix(5, 2) = sumYZ3;
8382 LSMatrix(2, 6) = LSMatrix(6, 2) = sumXZ2;
8383 LSMatrix(2, 7) = LSMatrix(7, 2) = sumYZ2;
8384 LSMatrix(2, 8) = LSMatrix(8, 2) = sumZ3;
8385 LSMatrix(2, 9) = LSMatrix(9, 2) = sumZ2;
8386
8387 // fourth column, fourth line
8388 LSMatrix(3, 4) = LSMatrix(4, 3) = sumX2YZ;
8389 LSMatrix(3, 5) = LSMatrix(5, 3) = sumXY2Z;
8390 LSMatrix(3, 6) = LSMatrix(6, 3) = sumX2Y;
8391 LSMatrix(3, 7) = LSMatrix(7, 3) = sumXY2;
8392 LSMatrix(3, 8) = LSMatrix(8, 3) = sumXYZ;
8393 LSMatrix(3, 9) = LSMatrix(9, 3) = sumXY;
8394
8395 // fifth coulmn, fifth line
8396 LSMatrix(4, 5) = LSMatrix(5, 4) = sumXYZ2;
8397 LSMatrix(4, 6) = LSMatrix(6, 4) = sumX2Z;
8398 LSMatrix(4, 7) = LSMatrix(7, 4) = sumXYZ;
8399 LSMatrix(4, 8) = LSMatrix(8, 4) = sumXZ2;
8400 LSMatrix(4, 9) = LSMatrix(9, 4) = sumXZ;
8401
8402 // sixth column, sixth line
8403 LSMatrix(5, 6) = LSMatrix(6, 5) = sumXYZ;
8404 LSMatrix(5, 7) = LSMatrix(7, 5) = sumY2Z;
8405 LSMatrix(5, 8) = LSMatrix(8, 5) = sumYZ2;
8406 LSMatrix(5, 9) = LSMatrix(9, 5) = sumYZ;
8407
8408 // seventh column, seventh line
8409 LSMatrix(6, 7) = LSMatrix(7, 6) = sumXY;
8410 LSMatrix(6, 8) = LSMatrix(8, 6) = sumXZ;
8411 LSMatrix(6, 9) = LSMatrix(9, 6) = sumX;
8412
8413 // eigth column, eigth line
8414 LSMatrix(7, 8) = LSMatrix(8, 7) = sumYZ;
8415 LSMatrix(7, 9) = LSMatrix(9, 7) = sumY;
8416
8417 // nineth column, nineth line
8418 LSMatrix(8, 9) = LSMatrix(9, 8) = sumZ;
8419 }
8420
8421 // 2.3 scale solution since the condition number gets really bad for large discrepancies in cell size
8422 const MFloat normalizationFactor = FPOW2(2 * c_level(cellId)) / c_cellLengthAtLevel(0);
8423 for(MInt i = 0; i < 2 * nDim + pow(2, nDim - 1); i++) {
8424 for(MInt j = 0; j < 2 * nDim + pow(2, nDim - 1); j++) {
8425 LSMatrix(i, j) *= normalizationFactor;
8426 }
8427 }
8428
8429 // 2.4 determine the pseudoinverse using SVD
8430 maia::math::invert(LSMatrix, matInv, 2 * nDim + pow(2, nDim - 1), 2 * nDim + pow(2, nDim - 1));
8431
8435 const MInt totalNumberOfVars = interpolateVelocitySlopes ? b + nDim * nDim : b;
8436 for(MInt k = a; k < totalNumberOfVars; k++) {
8437 MFloat sumVar = 0;
8438 if(k < b) {
8439 sumVar = a_fluidVariable(cellId, k);
8440 } else {
8441 sumVar = a_velocitySlope(cellId, std::floor((k - b) / 3), (k - b) % 3);
8442 }
8443 MFloat sumVarX = 0;
8444 MFloat sumVarY = 0;
8445 MFloat sumVarZ = 0;
8446 MFloat sumVarXY = 0;
8447 MFloat sumVarXZ = 0;
8448 MFloat sumVarYZ = 0;
8449 MFloat sumVarX2 = 0;
8450 MFloat sumVarY2 = 0;
8451 MFloat sumVarZ2 = 0;
8452
8453 rhs.set(0.0);
8454
8455 // 3.1 Calculate intermediate variables
8456 for(MUint nId = 1; nId < noInterpolationPoints; nId++) {
8457 MFloat x = 0;
8458 MFloat y = 0;
8459 MFloat z = 0;
8460 MFloat var = 0;
8461 if(nId >= nghbrList.size()) {
8462 const MUint pId = nId - nghbrList.size();
8463 x = pseudoCellPos[pId][0] - originX;
8464 y = pseudoCellPos[pId][1] - originY;
8465 var = pseudoCellVar[pId][k - a];
8466 IF_CONSTEXPR(nDim == 3) { z = pseudoCellPos[pId][2] - originZ; }
8467 } else {
8468 const MInt nghbrId = nghbrList[nId];
8469 x = c_coordinate(nghbrId, 0) - originX;
8470 y = c_coordinate(nghbrId, 1) - originY;
8471 if(k < b) {
8472 var = a_fluidVariable(nghbrId, k);
8473 } else {
8474 var = a_velocitySlope(nghbrId, std::floor((k - b) / 3), (k - b) % 3);
8475 }
8476 IF_CONSTEXPR(nDim == 3) { z = c_coordinate(nghbrId, 2) - originZ; }
8477 }
8478
8479 sumVar += var;
8480 sumVarX += var * x;
8481 sumVarY += var * y;
8482 sumVarZ += var * z;
8483 sumVarXY += var * x * y;
8484 sumVarXZ += var * x * z;
8485 sumVarYZ += var * y * z;
8486 sumVarX2 += var * x * x;
8487 sumVarY2 += var * y * y;
8488 sumVarZ2 += var * z * z;
8489 }
8490
8491 // 3.2 assign intermediate variables to rhs
8492 IF_CONSTEXPR(nDim == 2) {
8493 rhs[0] = sumVarX2;
8494 rhs[1] = sumVarY2;
8495 rhs[2] = sumVarXY;
8496 rhs[3] = sumVarX;
8497 rhs[4] = sumVarY;
8498 rhs[5] = sumVar;
8499 }
8500 else {
8501 rhs(0) = sumVarX2;
8502 rhs(1) = sumVarY2;
8503 rhs(2) = sumVarZ2;
8504 rhs(3) = sumVarXY;
8505 rhs(4) = sumVarXZ;
8506 rhs(5) = sumVarYZ;
8507 rhs(6) = sumVarX;
8508 rhs(7) = sumVarY;
8509 rhs(8) = sumVarZ;
8510 rhs(9) = sumVar;
8511 }
8512
8513 // 3.3 scale rhs
8514 for(MInt i = 0; i < 2 * nDim + pow(2, nDim - 1); i++) {
8515 rhs(i) *= normalizationFactor;
8516 }
8517
8518 MFloatTensor coeff(2 * nDim + pow(2, nDim - 1));
8519 coeff.set(F0);
8520
8521 // 3.4 determine coefficients of the aim function polynomial
8522 for(MInt i = 0; i < 2 * nDim + pow(2, nDim - 1); i++) {
8523 for(MInt j = 0; j < 2 * nDim + pow(2, nDim - 1); j++) {
8524 coeff(i) += matInv(i, j) * rhs(j);
8525 }
8526 }
8527
8531 MFloat positionX = position[0] - originX;
8532 MFloat positionY = position[1] - originY;
8533 MFloat positionZ = 0;
8534 IF_CONSTEXPR(nDim == 3) { positionZ = position[2] - originZ; }
8535 MFloat result = coeff((MInt)(2 * nDim + pow(2, nDim - 1) - 1));
8536 IF_CONSTEXPR(nDim == 2) {
8537 result += coeff(0) * positionX * positionX;
8538 result += coeff(1) * positionY * positionY;
8539 result += coeff(2) * positionX * positionY;
8540 result += coeff(3) * positionX;
8541 result += coeff(4) * positionY;
8542 }
8543 else {
8544 result += coeff(0) * positionX * positionX;
8545 result += coeff(1) * positionY * positionY;
8546 result += coeff(2) * positionZ * positionZ;
8547 result += coeff(3) * positionX * positionY;
8548 result += coeff(4) * positionX * positionZ;
8549 result += coeff(5) * positionY * positionZ;
8550 result += coeff(6) * positionX;
8551 result += coeff(7) * positionY;
8552 result += coeff(8) * positionZ;
8553 }
8554
8555 interpolatedVar[k - a] = result;
8556 }
8557 }
8558
8559 // TRACE_OUT();
8560}
8561
8562template void LPT<2>::interpolateVariablesLS<0, 2, true>(const MInt, const MFloat* const, MFloat* const);
8563template void LPT<2>::interpolateVariablesLS<0, 2, false>(const MInt, const MFloat* const, MFloat* const);
8564template void LPT<2>::interpolateVariablesLS<0, 3, true>(const MInt, const MFloat* const, MFloat* const);
8565template void LPT<2>::interpolateVariablesLS<0, 3, false>(const MInt, const MFloat* const, MFloat* const);
8566template void LPT<2>::interpolateVariablesLS<0, 5, true>(const MInt, const MFloat* const, MFloat* const);
8567template void LPT<2>::interpolateVariablesLS<0, 5, false>(const MInt, const MFloat* const, MFloat* const);
8568template void LPT<2>::interpolateVariablesLS<0, 6, true>(const MInt, const MFloat* const, MFloat* const);
8569template void LPT<2>::interpolateVariablesLS<0, 6, false>(const MInt, const MFloat* const, MFloat* const);
8570
8571template void LPT<3>::interpolateVariablesLS<0, 3, true>(const MInt, const MFloat* const, MFloat* const);
8572template void LPT<3>::interpolateVariablesLS<0, 3, false>(const MInt, const MFloat* const, MFloat* const);
8573template void LPT<3>::interpolateVariablesLS<0, 4, true>(const MInt, const MFloat* const, MFloat* const);
8574template void LPT<3>::interpolateVariablesLS<0, 4, false>(const MInt, const MFloat* const, MFloat* const);
8575template void LPT<3>::interpolateVariablesLS<0, 6, true>(const MInt, const MFloat* const, MFloat* const);
8576template void LPT<3>::interpolateVariablesLS<0, 6, false>(const MInt, const MFloat* const, MFloat* const);
8577template void LPT<3>::interpolateVariablesLS<0, 7, true>(const MInt, const MFloat* const, MFloat* const);
8578template void LPT<3>::interpolateVariablesLS<0, 7, false>(const MInt, const MFloat* const, MFloat* const);
8579
8580
8581template class LPT<3>;
void mAlloc(T *&a, const MLong N, const MString &objectName, MString function)
allocates memory for one-dimensional array 'a' of size N
Definition: alloc.h:173
MBool mDeallocate(T *&a)
deallocates the memory previously allocated for element 'a'
Definition: alloc.h:544
static MInt propertyLength(const MString &name, MInt solverId=m_noSolvers)
Returns the number of elements of a property.
Definition: context.cpp:538
static MBool propertyExists(const MString &name, MInt solver=m_noSolvers)
This function checks if a property exists in general.
Definition: context.cpp:494
void 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
MFloat m_creationTime
creation time modifier
Definition: lptbase.h:50
std::array< MFloat, nDim > m_position
Definition: lptbase.h:34
MLong m_partId
Definition: lptbase.h:45
std::array< MFloat, nDim > m_velocity
Definition: lptbase.h:35
BitsetType::reference firstStep()
Definition: lptbase.h:179
std::array< MFloat, nDim > m_oldPos
particle position of the last time step
Definition: lptbase.h:39
BitsetType::reference hadWallColl()
Definition: lptbase.h:169
void updateProperties(const MBool init=true)
Update particle properties.
Definition: lptbase.cpp:299
MInt m_oldCellId
Definition: lptbase.h:47
std::array< MFloat, nDim > m_oldVel
particle velocity of the last time step
Definition: lptbase.h:41
MFloat m_densityRatio
Definition: lptbase.h:44
MInt m_cellId
Definition: lptbase.h:46
void checkCellChange(const MFloat *oldPosition, const MBool allowHaloNonLeaf=false)
Checks whether position is within cell with number cellId if not, cellId is updated.
Definition: lptbase.cpp:233
std::array< MFloat, nDim > m_accel
Definition: lptbase.h:36
std::array< MFloat, 4 > m_oldQuaternion
std::array< MFloat, nDim > m_oldAccel
particle acceleration of the last time step
void calculateMajorAxisOrientation(MFloat *majorAxis)
Calculate the orientation of the major axis in the world coordinate system.
MFloat m_fluidVelMag
fluid velocity magnitude
std::array< MFloat, nDim > m_oldAngularAccel
particle angular acceleration of the last time step
MFloat m_oldFluidDensity
old fluid density
MFloat equivalentDiameter() const
Returns the equivalent diameter.
std::array< MFloat, nDim > m_fluidVel
fluid velocity
std::array< MFloat, nDim > m_angularAccel
particle angular acceleration
MFloat m_temperature
temperature
std::array< MFloat, nDim > m_oldFluidVel
fluid velocity of the last time step
MFloat m_aspectRatio
aspect ratio a/c of ellipsoidal: 0 < oblate < 1, =1 spherical, >1 prolate
std::array< MFloat, nDim > m_oldAngularVel
particle angular velocity of the last time step
std::array< MFloat, nDim > m_angularVel
particle angular velocity
std::array< MFloat, 4 > m_quaternion
void initEllipsoialProperties()
MFloat m_fluidDensity
fluid density
MFloat m_heatFlux
heat flux to cell
MFloat m_semiMinorAxis
semi minor axis a
Definition: lpt.h:82
void sendSourceTerms()
exchanges the source terms on the LPT grid, non-Blocking version from above
Definition: lpt.cpp:7648
void cancelMpiRequests() override
actually doing same pre-partitioneing stuff during balcne
Definition: lpt.cpp:6855
void checkCells()
some debug check for cells
Definition: lpt.cpp:6631
void broadcastInjected(const MUint prevNumPart)
Send newly created particles.
Definition: lpt.cpp:6221
void perCellStats()
set cell-particle mapping which is used to merge particle in reduceParticles!
Definition: lpt.cpp:2780
void receiveParticles_()
receive particles that have been sent non-blocking before
Definition: lpt.cpp:3851
void sendParticles()
send particles non-blocking
Definition: lpt.cpp:3705
void spawnParticles()
Spawn new particles (used for testing)
Definition: lpt.cpp:5734
void interpolateVariablesLS(const MInt cellId, const MFloat *const position, MFloat *const interpolatedVar)
calculates interpolated variables (in the range a, b) for a given position in a given cell
Definition: lpt.cpp:8063
void getCellDataDlb(const MInt dataId, const MInt oldNoCells, const MInt *const bufferIdToCellId, MInt *const data) override
Return solver data for DLB.
Definition: lpt.cpp:7026
void setCellDataDlb(const MInt dataId, const MInt *const data) override
Set solver data for DLB.
Definition: lpt.cpp:7123
void sendAndReceiveParticles(const MBool allowNonLeaf)
Particle transfer between solvers The particles to be sent are in the queueToSend array MBool mayExis...
Definition: lpt.cpp:3603
void motionEquation(const MInt offset)
Definition: lpt.cpp:2962
void checkRegridTrigger()
check if any of the cells with the regrid Trigger also have particles inside
Definition: lpt.cpp:7470
void unpackParticle(LPTSpherical< nDim > &thisParticle, const MInt *intBuffer, MInt &z, const MFloat *floatBuffer, MInt &h, MInt &step, MInt id)
Unpack spherical particle from buffers.
Definition: lpt.cpp:6084
MFloat getCellLoad(const MInt cellId, const MFloat *const weights) const override
Return the load of a single cell (given computational weights).
Definition: lpt.cpp:1988
void getLoadQuantities(MInt *const loadQuantities) const override
Return the cumulative load quantities on this domain.
Definition: lpt.cpp:1934
void balancePre() override
reset the solver during balancing
Definition: lpt.cpp:6867
void checkParticles()
some debug check for partcles
Definition: lpt.cpp:6441
MBool prepareRestart(MBool force, MBool &) override
before writing a restart file
Definition: lpt.cpp:2547
void setCellWeights(MFloat *) override
set cell weight for partitioning
Definition: lpt.cpp:1827
void initCollector()
init LPT cell collector
Definition: lpt.cpp:153
void initGridProperties()
calculate Grid Properties for periodic boundaries, adapted from rigedbodies
Definition: lpt.cpp:603
void finalizeAdaptation() override
reinit the solver after the full adaptation loop!
Definition: lpt.cpp:1434
void resizeGridMap() override
Swap the given cells.
Definition: lpt.cpp:1519
void postAdaptation() override
post adaptation for split adaptation within the adaptation loop
Definition: lpt.cpp:1394
MInt cellOutside(const MFloat *, const MInt, const MInt) override
checks if a child lies outSide of the domain! necessary for refinement at the bndry!
Definition: lpt.cpp:1740
MInt loadParticleRestartFile()
Read particle restart file and create new particle instances accordingly.
Definition: lpt.cpp:4822
void refineCell(const MInt) override
refining a cartesian cell
Definition: lpt.cpp:1538
void writeCollData()
Definition: lpt.cpp:6433
void crankAngleSolutionOutput()
save a full solutionOutput at timeSteps closesed to a specified crankAngle Interval save a solution s...
Definition: lpt.cpp:7935
void recvRegridTrigger()
Definition: lpt.cpp:7507
void getDomainDecompositionInformation(std::vector< std::pair< MString, MInt > > &domainInfo) override
Return decomposition information, i.e. number of local elements,...
Definition: lpt.cpp:2031
void resetSourceTerms(const MInt)
reset all source terms with cellId larger than the offset
Definition: lpt.cpp:2112
void exchangeParticles(const MBool collOnly, const MInt offset, const MBool allowNonLeaf=false)
Calls exchange for the different particle types.
Definition: lpt.cpp:3463
void resetSolver() override
actually doing some pre-balance-stuff
Definition: lpt.cpp:6655
void evaporation(const MInt offset)
Definition: lpt.cpp:2991
MBool fiveStageSolutionStep()
Splitting the solutionStep into 5 separate parts to match the 5-stage Runge-Kutta time-stepping for i...
Definition: lpt.cpp:2194
void receiveSourceTerms()
exchanges the source terms on the LPT grid, non-Blocking version from above
Definition: lpt.cpp:7699
void removeCell(const MInt) override
Remove the given cell.
Definition: lpt.cpp:1717
void particleRespawn()
respawn of Particles
Definition: lpt.cpp:3114
MInt addEllipsoid(const MInt cellId, const MFloat semiMinorAxis, const MFloat aspectRatio, const MFloat particleDensityRatio, const MInt random=1, const MInt addMode=0, const MFloat *velocity=nullptr, const MFloat *position=nullptr)
Add new ellipsoidal particle.
Definition: lpt.cpp:5607
void resetSolverFull()
reset the solver during balancing
Definition: lpt.cpp:6822
void writePartData()
write particle snapshot to Netcdf file (using parallel output)
Definition: lpt.cpp:3997
void wallCollision()
collision step with LPT wall
Definition: lpt.cpp:3070
void removeInvalidParticles(const MBool)
Definition: lpt.cpp:2667
void balancePost() override
Reinitialize solver after setting solution data in DLB.
Definition: lpt.cpp:6920
void removeChilds(const MInt) override
coarseining a cartesian cell
Definition: lpt.cpp:1663
void packParticles(std::vector< LPTSpherical< nDim > * > &particlesToSend, MInt *intBuffer, MFloat *floatBuffer, std::vector< MInt >)
Pack particles for sending.
Definition: lpt.cpp:5850
LPT(const MInt solverId, GridProxy &gridProxy_, Geom &geometry_, const MPI_Comm comm)
Solver constructor.
void getGlobalSolverVars(std::vector< MFloat > &globalFloatVars, std::vector< MInt > &globalIdVars) override
Get/set global solver variables during DLB.
Definition: lpt.cpp:7167
void sendFlowField()
exchanges the flow Field data on the LPT grid, non-Blocking version
Definition: lpt.cpp:7739
void countParticlesInCells()
set accessor for a_noParticlesInCell which is used for the particle-sensor during adaptation
Definition: lpt.cpp:2741
void initializeTimers()
Initializes the communication timers.
Definition: lpt.cpp:7984
void spawnTimeStep()
spawn new particles and compute their timeStep NOTE: this is an older functionality which is only use...
Definition: lpt.cpp:2624
void setGlobalSolverVars(std::vector< MFloat > &globalFloatVars, std::vector< MInt > &globalIdVars) override
Set global solver variables for DLB (same on each domain)
Definition: lpt.cpp:7204
void receiveVelocitySlopes()
exchanges the velocity slopes on the LPT grid, non-Blocking version
Definition: lpt.cpp:7901
void initialCondition()
Initialize particles.
void coupling(MInt offset)
Definition: lpt.cpp:3014
void writeRestartFile(const MBool, const MBool, const MString, MInt *) override
writing a restart File as regularly called from the grid-controller!
Definition: lpt.cpp:2578
MBool oneStageSolutionStep()
single solution step for pure LPT computation or without interleafed coupling!
Definition: lpt.cpp:2387
void sensorInterface(std::vector< std::vector< MFloat > > &sensors, std::vector< std::bitset< 64 > > &sensorCellFlag, std::vector< MFloat > &sensorWeight, MInt sensorOffset, MInt sen) override
set the sensors around the boundary positions to get the markedCells
Definition: lpt.cpp:1368
void initMPI(const MBool=true)
Init MPI buffers and communication datastructures.
Definition: lpt.cpp:374
void saveDebugRestartFile()
writing a restart File NOTE: for debugging purposes only!
Definition: lpt.cpp:2597
void limitWeights(MFloat *) override
Limit weight of base cell.
Definition: lpt.cpp:1919
void mergeParticles(LPTSpherical< nDim > *partA, LPTSpherical< nDim > *partB)
Definition: lpt.cpp:2890
void writeParticleRestartFile()
Write particle restart file.
Definition: lpt.cpp:4304
void updateFluidFraction()
Definition: lpt.cpp:6286
void exchangeOffset(MInt noParticles)
In case of multisolver, communicate particle id offset to ensure consistent particle ids across the s...
Definition: lpt.cpp:3963
void finalizeBalance() override
re-inits the solver after the balance
Definition: lpt.cpp:7243
void unpackParticles(const MInt num, const MInt *intBuffer, const MFloat *floatBuffer, const MInt domainId=-1, const MBool allowNonLeaf=false)
Unpack particles.
Definition: lpt.cpp:6008
void setSensors(std::vector< std::vector< MFloat > > &, std::vector< MFloat > &, std::vector< std::bitset< 64 > > &, std::vector< MInt > &) override
set the sensors for a single adaptation step
Definition: lpt.cpp:1285
void initSolver() override
Definition: lpt.cpp:53
MBool solutionStep() override
Time stepping of particles NOTE: the solutionStep is split into sub-steps, to allow for an interleafe...
Definition: lpt.cpp:2139
void getDefaultWeights(MFloat *weights, std::vector< MString > &names) const override
Return the default weights for all load quantities.
Definition: lpt.cpp:1888
void exchangeSourceTerms()
exchanges the source terms on the LPT grid, this is necessary a) before writing the cell solution fil...
Definition: lpt.cpp:7620
void receiveFlowField()
exchanges the flow Field data on the LPT grid, non-Blocking version
Definition: lpt.cpp:7784
void waitForSendReqs()
wait on all mpi send calls, prior to balance, adaptation or IO
Definition: lpt.cpp:7818
void initBndryCells()
init LPT boundary-cells used for the wall-collision
Definition: lpt.cpp:352
void initParticleVector()
init LPT spherical and ellipsoids particle vectors (i.e. set length and read non-dimensionalisation P...
void prepareAdaptation() override
prepare solver adaptation
Definition: lpt.cpp:1214
void postTimeStep() override
after the LPT time-Step!
Definition: lpt.cpp:2512
void preTimeStep() override
prepare the LPT timeStep
Definition: lpt.cpp:2069
void getSolverTimings(std::vector< std::pair< std::string, MFloat > > &, const MBool allTimings) override
Get solver timings.
Definition: lpt.cpp:1861
void swapProxy(MInt, MInt) override
Definition: lpt.cpp:1528
MInt cellDataSizeDlb(const MInt dataId, const MInt cellId) override
Return data size to be communicated during DLB for a grid cell and given data id.
Definition: lpt.cpp:6933
void reduceParticles()
Definition: lpt.cpp:2805
void exchangeParticles_(const MBool collOnly, const MInt offset, const MBool allowNonLeaf=false)
exchange particles to neighboring domains, can be either non-blocking or blocking communication!
Definition: lpt.cpp:3478
void receiveParticles()
Calls particle receive function for different particle types.
Definition: lpt.cpp:3836
MFloat computeTimeStep()
computes the non-dimensional LPT time-step on the assumption that a particle may only travel for a co...
Definition: lpt.cpp:7533
void initSummary()
Print information at the start of simulation.
Definition: lpt.cpp:5778
void recvInjected()
receive particles generated during injection
Definition: lpt.cpp:5812
void finalizeInitSolver() override
Definition: lpt.cpp:1179
void sendVelocitySlopes()
exchanges the velocity slopes on the LPT grid, non-Blocking version
Definition: lpt.cpp:7861
void sensorParticle(std::vector< std::vector< MFloat > > &sensors, std::vector< std::bitset< 64 > > &sensorCellFlag, std::vector< MFloat > &sensorWeight, MInt sensorOffset, MInt sen) override
set the sensors around the particle positions to get the markedCells
Definition: lpt.cpp:1349
void advanceParticles(const MInt offset)
Definition: lpt.cpp:3041
void updateExchangeCells()
Set exchange properties in LPT cell collector (a_isHalo/a_isWindow) and set m_pointsToHalo and m_poin...
Definition: lpt.cpp:6339
void calculateTerminalVelocities()
calculates the terminal velocity of particles used for initialization
MFloat reduceData(const MInt cellId, MFloat *data, const MInt dataBlockSize=1, const MBool average=true)
reduce data to lower level
Definition: lpt.cpp:8020
void swapCells(const MInt, const MInt) override
swap Cell information when the grid collector is restructured during adaptation cleanup
Definition: lpt.cpp:1782
MInt addParticle(const MInt cellId, const MFloat diameter, const MFloat particleDensityRatio, const MInt random=1, const MInt addMode=0, const MFloat *velocity=nullptr, const MFloat *position=nullptr, const MInt parcelSize=1)
Add new particle.
Definition: lpt.cpp:5495
void writeCellSolutionFile(const MString &, MInt *)
Write lpt cell based solution file currently ony used for debugging!
Definition: lpt.cpp:5285
void setRegridTrigger()
set a_regridTriggerG around all particles to allow for an adaptation check
Definition: lpt.cpp:7373
void addBndryCell(const MInt, MFloat *, MFloat *, MFloatScratchSpace &, MFloat *)
add an LPT bndryCell to the collector and fill the data
Definition: lpt.cpp:6391
void sprayInjection()
Definition: lpt.cpp:2644
MFloat m_shedDiam
shedding diameter
Definition: lptspherical.h:82
std::array< MFloat, nDim > m_oldFluidVel
fluid velocity of the last time step
Definition: lptspherical.h:70
MFloat m_temperature
temperature
Definition: lptspherical.h:78
MFloat m_oldFluidDensity
old fluid density
Definition: lptspherical.h:73
MFloat m_dM
mass evaporation rate
Definition: lptspherical.h:86
MFloat sphericalMass() const
Calculate the current mass.
Definition: lptspherical.h:126
MInt m_noParticles
parceled particles
Definition: lptspherical.h:84
std::array< MFloat, nDim > m_oldAccel
particle acceleration of the last time step
Definition: lptspherical.h:68
MFloat m_diameter
particle diameter
Definition: lptspherical.h:76
MFloat m_heatFlux
heat flux to cell
Definition: lptspherical.h:90
MFloat m_breakUpTime
time since last breakUp
Definition: lptspherical.h:80
MFloat m_fluidVelMag
fluid velocity magnitude
Definition: lptspherical.h:88
MFloat m_fluidDensity
fluid density
Definition: lptspherical.h:65
std::array< MFloat, nDim > m_fluidVel
fluid velocity
Definition: lptspherical.h:63
This class is a ScratchSpace.
Definition: scratch.h:758
void fill(T val)
fill the scratch with a given value
Definition: scratch.h:311
pointer p
Deprecated: use [] instead!
Definition: scratch.h:315
iterator begin()
Definition: scratch.h:273
void set(const T &value)
Initializes tensor to constant value.
Definition: tensor.h:271
@ LPT_MPI_PARTICLE_RESPAWN_TAG
Definition: enums.h:20
void mTerm(const MInt errorCode, const MString &location, const MString &message)
Definition: functions.cpp:29
constexpr Real POW3(const Real x)
Definition: functions.h:123
constexpr Real POW2(const Real x)
Definition: functions.h:119
constexpr T mMin(const T &x, const T &y)
Definition: functions.h:90
constexpr T mMax(const T &x, const T &y)
Definition: functions.h:94
const MInt m_revDir[6]
MInt globalTimeStep
InfoOutFile m_log
std::ostream cerr0
constexpr MFloat FPOW2(MInt x)
int32_t MInt
Definition: maiatypes.h:62
uint32_t MUint
Definition: maiatypes.h:63
std::basic_string< char > MString
Definition: maiatypes.h:55
MInt noSolvers
Definition: maiatypes.h:73
double MFloat
Definition: maiatypes.h:52
int64_t MLong
Definition: maiatypes.h:64
bool MBool
Definition: maiatypes.h:58
MInt id
Definition: maiatypes.h:71
uint64_t MUlong
Definition: maiatypes.h:65
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_Isend(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_Isend
int MPI_Get_count(const MPI_Status *status, MPI_Datatype datatype, int *count, const MString &name)
same as MPI_Get_count
int MPI_Irecv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Request *request, const MString &name, const MString &varname)
same as MPI_Irecv
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_Iallreduce(const void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm, MPI_Request *request, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Iallreduce
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_Iprobe(int source, int tag, MPI_Comm comm, int *flag, MPI_Status *status, const MString &name)
Iprobe MPI to get status without actually receiving the message.
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
Definition: lpt.h:47
MInt randomVectorInCone(MFloat *vec, const MFloat *coneAxis, const MFloat length, const MFloat openingAngle, const MInt dist, std::mt19937_64 &PRNG, const MFloat distCoeff=0.0, const MFloat nozzleAngle=0.0)
Generate a random vector in a cone defined by its opening angle.
Definition: lptlib.h:252
void rotation2quaternion(MFloat *rotation, MFloat *quaternion)
Definition: maiamath.h:550
MInt sgn(T val)
Definition: maiamath.h:495
void invert(MFloat *A, const MInt m, const MInt n)
Definition: maiamath.cpp:171
void normalize(std::array< T, N > &u)
Definition: maiamath.h:191
MFloat crankAngle(const MFloat time, const MFloat Strouhal, const MFloat offset, const MInt mode)
help-function for engine calculations which returns the crank-angle for a given time,...
Definition: maiamath.h:506
MFloat norm(const std::array< T, N > &u)
Definition: maiamath.h:148
void reverseExchangeAddData(const std::vector< MInt > &nghbrDomains, const std::vector< std::vector< MInt > > &haloCellVec, const std::vector< std::vector< MInt > > &windowCellVec, const MPI_Comm comm, U **const data, const MInt noDat=1)
Generic exchange from halo to window cells, however in this case the value in the halo-cell is added ...
Definition: mpiexchange.h:1205
Namespace for auxiliary functions/classes.
PARALLELIO_DEFAULT_BACKEND ParallelIo
Definition: parallelio.h:292
MFloat dist(const Point< DIM > &p, const Point< DIM > &q)
Definition: pointbox.h:54
Definition: contexttypes.h:19
define array structures