MAIA bb96820c
Multiphysics at AIA
Loading...
Searching...
No Matches
fvparticle.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 "fvparticle.h"
8#include "LPT/lptlib.h"
9
10using namespace std;
11using namespace maia::lpt;
12
13template <MInt nDim, class SysEqn>
15 : Coupling(couplingId), CouplingLpt<nDim, CouplingFv<nDim, SysEqn>>(couplingId, particle, fv), m_sysEqn(1, 0) {
16 TRACE();
17
18 m_particle = particle;
19 m_fvSolver = fv;
20
21 // get LPT-solver order
23 m_noSolverSteps = Context::getBasicProperty<MInt>("recipeMaxNoSteps", AT_, &m_noSolverSteps);
24
25 const MString propNameLpt = "solverOrder_" + std::to_string(lpt().solverId());
26 const MString propNameFv = "solverOrder_" + std::to_string(fvSolver().solverId());
27
28 for(MInt step = 0; step < m_noSolverSteps; step++) {
29 if(Context::getBasicProperty<MInt>(propNameLpt, AT_, step) > 0) {
30 m_lptSolverOrder = step;
31 }
32 if(Context::getBasicProperty<MInt>(propNameFv, AT_, step) > 0) {
33 m_fvSolverOrder = step;
34 }
35 }
36
37 m_interLeafed = false;
39
41 m_interLeafed = true;
43 lpt().m_noSolutionSteps = 5;
44 if(lpt().domainId() == 0) {
45 cerr << "Interleafed Fv - LPT execution at step " << m_lptSolverOrder << endl;
46 }
47 }
48
49 initData();
50}
51
55template <MInt nDim, class SysEqn>
57 TRACE();
58
59 checkProperties();
60
61 initConversion();
62
63 // check for any in-active ranks
64 MInt noInactiveFv = 0;
65 MInt noInactiveLPT = 0;
66 if(!fvSolver().isActive()) {
67 noInactiveFv++;
68 }
69 if(!lpt().isActive()) {
70 noInactiveLPT++;
71 }
72
73 MPI_Allreduce(MPI_IN_PLACE, &noInactiveFv, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD, AT_, "MPI_IN_PLACE", "noInactiveFv");
74 MPI_Allreduce(MPI_IN_PLACE, &noInactiveLPT, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD, AT_, "MPI_IN_PLACE",
75 "noInactiveLPT");
76 if(noInactiveFv > 0) {
77 cerr0 << "Fv solver has " << noInactiveFv << " inactive ranks!" << endl;
78 }
79 if(noInactiveLPT > 0) {
80 cerr0 << "LPT solver has " << noInactiveLPT << " inactive ranks!" << endl;
81 }
82}
83
84
91template <MInt nDim, class SysEqn>
93 // 1) read the following conversion factors as properties
94 MFloat lengthFactor = 1;
103 lengthFactor = Context::getSolverProperty<MFloat>("lengthFactor", lpt().solverId(), AT_, &lengthFactor);
104
105 MFloat velocityFactor = 1;
113 velocityFactor = Context::getSolverProperty<MFloat>("velocityFactor", lpt().solverId(), AT_, &velocityFactor);
114
115 MFloat viscosityFactor = 1.0;
125 viscosityFactor = Context::getSolverProperty<MFloat>("viscosityFactor", lpt().solverId(), AT_, &viscosityFactor);
126
127 const MFloat particleRe = Context::getSolverProperty<MFloat>("Re", lpt().solverId(), AT_);
128
129 // 2) compute the missing conversion factors
130 const MFloat ReFactor = fvSolver().sysEqn().m_Re0 / particleRe;
131 /*const */ MFloat densityFactor = ReFactor * lengthFactor / (velocityFactor * viscosityFactor);
132 /*const */ MFloat pressureFactor = POW2(velocityFactor) * densityFactor;
133 /*const */ MFloat temperatureFactor = sqrt(velocityFactor);
134
135
136 //-----------------------------------------------------------------------------------
137 // Fixes for dimensional LPT
138 if(!lpt().m_nonDimensional) {
139 MFloat specificGasConstant = 287.00283051433; // changes all particle testcases
140 static constexpr MFloat defaultAir = 0.02896;
141 MFloat molarMass = Context::getSolverProperty<MFloat>("ambientMolarWeight", lpt().solverId(), AT_, &defaultAir);
142 MFloat T0 = 293.15;
143 T0 = Context::getSolverProperty<MFloat>("ambientTemperature", lpt().solverId(), AT_, &T0);
144
145 if(Context::propertyExists("gasConstant", lpt().solverId())) {
146 specificGasConstant =
147 Context::getSolverProperty<MFloat>("gasConstant", lpt().solverId(), AT_, &specificGasConstant);
148 specificGasConstant = specificGasConstant / molarMass;
149 }
150
151 MFloat Tinfty = T0;
152 if(Context::getSolverProperty<MInt>("initialCondition", lpt().solverId(), AT_) != 465) {
153 Tinfty = T0 * fvSolver().sysEqn().temperature_IR(fvSolver().m_Ma);
154 }
155
156 MFloat density0 = -1;
157 density0 = Context::getSolverProperty<MFloat>("ambientDensity", lpt().solverId(), AT_, &density0);
158 MFloat dynamicViscosity0 = 0.00001716 * pow(T0 / 273.15, 1.5) * (273.15 + 110.4) / (T0 + 110.4);
159 dynamicViscosity0 =
160 Context::getSolverProperty<MFloat>("ambientDynViscosity", lpt().solverId(), AT_, &dynamicViscosity0);
161
162 if(density0 < 0) {
163 density0 = fvSolver().sysEqn().m_Re0 * dynamicViscosity0
164 / sqrt(fvSolver().sysEqn().gamma_Ref() * specificGasConstant * T0);
165 }
166
167 const MFloat pressure = density0 * specificGasConstant * Tinfty;
168
169 velocityFactor = sqrt(fvSolver().sysEqn().gamma_Ref() * specificGasConstant * T0);
170 densityFactor = density0;
171 pressureFactor = pressure * fvSolver().sysEqn().gamma_Ref();
172 temperatureFactor = T0;
173 viscosityFactor = dynamicViscosity0;
174
175 lpt().m_material->m_temperatureFactor = temperatureFactor;
176 lpt().m_material->m_viscosityFactor = viscosityFactor;
177 }
178
179 //---------------------------------------------------------------------------------------
180
181 conversionFvLpt.velocity = velocityFactor;
182 conversionLptFv.velocity = 1.0 / conversionFvLpt.velocity;
183 conversionFvLpt.density = densityFactor;
184 conversionLptFv.density = 1.0 / conversionFvLpt.density;
185 conversionFvLpt.pressure = pressureFactor;
186 conversionLptFv.pressure = 1.0 / conversionFvLpt.pressure;
187 conversionLptFv.length = lengthFactor;
188 conversionFvLpt.length = 1.0 / conversionLptFv.length;
189 conversionFvLpt.temperature = temperatureFactor;
190 conversionLptFv.temperature = 1 / conversionFvLpt.temperature;
191 conversionFvLpt.viscosity = viscosityFactor;
192 conversionLptFv.viscosity = 1 / conversionFvLpt.viscosity;
193
194 ASSERT(fabs(lpt().m_sutherlandConstant - fvSolver().m_sutherlandConstant) < MFloatEps, "");
195 ASSERT(fabs(lpt().m_sutherlandPlusOne - fvSolver().m_sutherlandPlusOne) < MFloatEps, "");
196
197 if(lpt().domainId() == 0) {
198 cerr << "Fv-Lpt conversion factors are: " << endl;
199 cerr << "length : " << conversionFvLpt.length << endl;
200 cerr << "velocity : " << conversionFvLpt.velocity << endl;
201 cerr << "density : " << conversionFvLpt.density << endl;
202 cerr << "pressure : " << conversionFvLpt.pressure << endl;
203 cerr << "temperature : " << conversionFvLpt.temperature << endl;
204 cerr << "viscosity : " << conversionFvLpt.viscosity << endl;
205 }
206
207 conversionFvLpt.mass = conversionFvLpt.velocity * conversionFvLpt.density * POW2(conversionFvLpt.length);
208 conversionLptFv.mass = 1 / conversionFvLpt.mass;
209 conversionFvLpt.momentum = conversionFvLpt.density * POW2(conversionFvLpt.velocity) * POW2(conversionFvLpt.length);
210 conversionLptFv.momentum = 1 / conversionFvLpt.momentum;
211 conversionFvLpt.energy = conversionFvLpt.density * conversionFvLpt.velocity * POW2(conversionFvLpt.velocity)
212 * POW2(conversionFvLpt.length);
213 conversionLptFv.energy = 1 / conversionFvLpt.energy;
214
215 if(lpt().m_ellipsoids) {
216 conversionFvLpt.velocitySlope = conversionFvLpt.velocity / conversionFvLpt.length;
217 conversionLptFv.velocitySlope = 1 / conversionFvLpt.velocitySlope;
218 }
219}
220
226template <MInt nDim, class SysEqn>
228 if(!lpt().m_restart) {
229 // init fluid density and velocity, after the values have been transferred to the LPT solver!
230 for(MInt i = 0; i < lpt().a_noParticles(); i++) {
231 lpt().m_partList[i].initVelocityAndDensity();
232 }
233 // init fluid density and velocity for ellipsoidal particles
234 for(MInt i = 0; i < lpt().a_noEllipsoidalParticles(); i++) {
235 lpt().m_partListEllipsoid[i].initVelocityAndDensity();
236 }
237 }
238}
239
243template <MInt nDim, class SysEqn>
245 TRACE();
246
247 m_fvSolverId = fvSolver().m_solverId;
248 m_lptSolverId = lpt().m_solverId;
249
250 readProperties();
251}
252
253
254template <MInt nDim, class SysEqn>
256 TRACE();
257
258 // Transfer the flow field after the intial condition for FV has been applied
259 transferCellStatus();
260 transferFlowField();
261 transferVelocitySlopes();
262 if(lpt().isActive()) {
263 lpt().receiveFlowField();
264 lpt().receiveVelocitySlopes();
265 lpt().waitForSendReqs();
266 }
267
268 initParticleVelocity();
269
270 if(!lpt().m_restartFile && fvSolver().m_restartFile) {
271 if(lpt().domainId() == 0) {
272 cerr << "Restart without LPT restartFile -> setting time from Fv-time!" << endl;
273 }
274 const MFloat conversionTime = conversionFvLpt.length / conversionFvLpt.velocity;
275 const MFloat fvTime = fvSolver().m_levelSetMb ? fvSolver().m_physicalTime : fvSolver().m_time;
276 lpt().m_time = fvTime * conversionTime;
277 }
278
279 // set the timeStep in the LPT solver
280 if(m_forceFvTimeStep) {
281 transferTimeStep();
282 } else {
283 unifyTimeStep();
284 }
285
286 MBool writeParticleStats = false;
287 if(writeParticleStats) {
288 writeParticleCellStats();
289 }
290}
291
295template <MInt nDim, class SysEqn>
297 TRACE();
298
299 m_lptFvInterpolation =
300 Context::getSolverProperty<MBool>("allowLPTFvInterpolation", m_fvSolverId, AT_, &m_lptFvInterpolation);
301
312 m_fvLPTSpeciesId = 0;
313 m_fvLPTSpeciesId = Context::getSolverProperty<MInt>("fvLptSpeciesId", m_lptSolverId, AT_, &m_fvLPTSpeciesId);
314
327 m_forceFvTimeStep = true;
328 m_forceFvTimeStep = Context::getSolverProperty<MBool>("forceFvTimeStep", m_lptSolverId, AT_, &m_forceFvTimeStep);
329 if(!m_forceFvTimeStep && !m_interLeafed) {
330 mTerm(1, AT_, "Coupled timesteps only working with interleafed setup!");
331 }
332}
333
337template <MInt nDim, class SysEqn>
339 TRACE();
340
341 ASSERT(fabs(fvSolver().m_sutherlandConstant - lpt().m_sutherlandConstant) < MFloatEps, "");
342 ASSERT(fabs(lpt().m_sutherlandPlusOne - fvSolver().m_sutherlandPlusOne) < MFloatEps, "");
343 ASSERT(lpt().a_timeStepComputationInterval() == fvSolver().a_timeStepComputationInterval(), "");
344}
345
349template <MInt nDim, class SysEqn>
351 TRACE();
352
353 // PRE LPT
354 if(recipeStep == m_lptSolverOrder) {
355 m_solutionStep = 0;
356
357 // TODO-timw labels:COUPLER,FV,toenhance,totest check if this can be avoided when transfering variables at some
358 // other stage!
359 // when removed, testcase results are changed slightly!
360 if(!m_interLeafed) fvSolver().computeConservativeVariables();
361
362 // update LPT bndryCells for wall-collision
363 updateLPTBndry();
364 transferCellStatus();
365
366
367 if(m_interLeafed) {
368 const MFloat conversionTime = conversionFvLpt.length / conversionFvLpt.velocity;
369 const MFloat fvTime = fvSolver().m_levelSetMb ? fvSolver().m_physicalTime : fvSolver().m_time;
370 if(lpt().isActive() && fabs(fvTime * conversionTime - lpt().m_time) > MFloatEps) {
371 cerr << "LPT-time " << lpt().m_time << " Fv-time " << fvTime << endl;
372 mTerm(1, AT_, "Miss-matching time!");
373 }
374 }
375 }
376}
377
381template <MInt nDim, class SysEqn>
383 // when using inter-leafed application, external sources and flow field are transfered in the subCouple!
384 if(m_interLeafed) {
385 if(recipeStep == m_fvSolverOrder) {
386 transferVelocitySlopes(); // transfer only after postTimestep
387 }
388 return;
389 }
390
391 // Post LPT (as the LPT-solver is called before the Fv-solver!)
392 if(recipeStep == m_lptSolverOrder) {
393 // reset and transfer external Sources to FV-solver
394 if(fvSolver().m_hasExternalSource) {
395 fvSolver().applyExternalOldSource();
396 fvSolver().resetExternalSources();
397 transferExternalSources();
398 }
399 }
400
401 // POST FV
402 if(recipeStep == m_fvSolverOrder) {
403 transferFlowField();
404 transferVelocitySlopes();
405 if(lpt().isActive()) {
406 lpt().receiveFlowField();
407 lpt().receiveVelocitySlopes();
408 lpt().waitForSendReqs();
409 }
410 transferTimeStep();
411 }
412}
413
417template <MInt nDim, class SysEqn>
419 // post LPT solver
420 if(id == lpt().solverId()) {
421 if(fvSolver().m_hasExternalSource) {
422 fvSolver().resetExternalSources();
423 }
424 if(globalTimeStep < 0) {
425 prepareAdaptation();
426 } else {
427 transferExternalSources();
428 fvSolver().advanceExternalSource();
429 }
430
431 } else if(id == fvSolver().solverId()) {
432 transferCellStatus();
433 transferFlowField();
434 transferVelocitySlopes();
435 if(lpt().isActive()) {
436 lpt().receiveFlowField();
437 lpt().receiveVelocitySlopes();
438 lpt().waitForSendReqs();
439 }
440
441 updateLPTBndry();
442 }
443}
444
451template <MInt nDim, class SysEqn>
453 TRACE();
454
455#ifndef NDEBUG
456 MBool diverged = false;
457#endif
458
459 for(MInt lptCellId = 0; lptCellId < lpt().noInternalCells(); lptCellId++) {
460 if(!lpt().c_isLeafCell(lptCellId)) continue;
461 MInt fvCellId = lpt2fvId(lptCellId);
462 if(fvCellId > -1 && fvSolver().c_isLeafCell(fvCellId)) {
463 // simple transfer if both cells are leaf-cells
464 for(MInt n = 0; n < nDim; n++) {
465 lpt().a_fluidVelocity(lptCellId, n) =
466 fvSolver().a_pvariable(fvCellId, fvSolver().m_sysEqn.PV->VV[n]) * conversionFvLpt.velocity;
467 }
468 lpt().a_fluidDensity(lptCellId) =
469 fvSolver().a_pvariable(fvCellId, fvSolver().m_sysEqn.PV->RHO) * conversionFvLpt.density;
470 lpt().a_fluidPressure(lptCellId) =
471 fvSolver().a_pvariable(fvCellId, fvSolver().m_sysEqn.PV->P) * conversionFvLpt.pressure;
472#ifndef NDEBUG
473 if(std::isnan(lpt().a_fluidPressure(lptCellId)) && lpt().a_isValidCell(lptCellId)) {
474 diverged = true;
475 cerr << "Nan pressure " << lpt().a_fluidPressure(lptCellId) << " " << lpt().a_coordinate(lptCellId, 0) << " "
476 << lpt().a_coordinate(lptCellId, 1) << " " << lpt().a_coordinate(lptCellId, nDim - 1) << endl;
477 // mTerm(1, AT_, "Nan pressure detected!");
478 }
479#endif
480 lpt().a_fluidTemperature(lptCellId) =
481 fvSolver().sysEqn().temperature_ES(fvSolver().a_pvariable(fvCellId, fvSolver().m_sysEqn.PV->RHO),
482 fvSolver().a_pvariable(fvCellId, fvSolver().m_sysEqn.PV->P))
483 * conversionFvLpt.temperature;
484 if(lpt().m_evaporation && fvSolver().m_noSpecies > 0) {
485 lpt().a_fluidSpecies(lptCellId) =
486 mMax(0.0, mMin(1.0, fvSolver().a_pvariable(fvCellId, fvSolver().m_sysEqn.PV->Y[m_fvLPTSpeciesId])));
487 }
488
489 } else if(fvCellId > -1 && !fvSolver().c_isLeafCell(fvCellId)) {
490 // interpolate from FV-leaf childs if the matching fv-Cell is not a leaf cell
491 if(lpt().a_isHalo(lptCellId)) continue;
492 // gather all fv-leaf-cell childs for internal cells
493 vector<MInt> leafChildIds;
494 fvSolver().grid().getAllLeafChilds(fvCellId, leafChildIds);
495 ASSERT(!leafChildIds.empty(), "");
496
497 // reset variables
498 for(MInt n = 0; n < nDim; n++) {
499 lpt().a_fluidVelocity(lptCellId, n) = 0;
500 }
501 lpt().a_fluidDensity(lptCellId) = 0;
502 lpt().a_fluidPressure(lptCellId) = 0;
503 lpt().a_fluidTemperature(lptCellId) = 0;
504 if(lpt().m_evaporation) {
505 lpt().a_fluidSpecies(lptCellId) = 0;
506 }
507 // interpolate variables by volume
508 MFloat volume = 0;
509 for(MUint i = 0; i < leafChildIds.size(); i++) {
510 const MInt cellId = leafChildIds[i];
511 if(fvSolver().a_isInactive(cellId)) continue;
512 const MFloat cellVolume = fvSolver().a_cellVolume(cellId);
513 ASSERT(fvSolver().c_isLeafCell(cellId), "");
514 // ASSERT(cellVolume > 0, "");
515 if(cellVolume < 1e-16) {
516 mTerm(1, AT_, "Invalid cell-volume!");
517 }
518 volume += cellVolume;
519 for(MInt n = 0; n < nDim; n++) {
520 lpt().a_fluidVelocity(lptCellId, n) +=
521 cellVolume * fvSolver().a_pvariable(cellId, fvSolver().m_sysEqn.PV->VV[n]);
522 }
523 lpt().a_fluidDensity(lptCellId) += cellVolume * fvSolver().a_pvariable(cellId, fvSolver().m_sysEqn.PV->RHO);
524 lpt().a_fluidPressure(lptCellId) += cellVolume * fvSolver().a_pvariable(cellId, fvSolver().m_sysEqn.PV->P);
525 lpt().a_fluidTemperature(lptCellId) +=
526 cellVolume
527 * fvSolver().sysEqn().temperature_ES(fvSolver().a_pvariable(fvCellId, fvSolver().m_sysEqn.PV->RHO),
528 fvSolver().a_pvariable(fvCellId, fvSolver().m_sysEqn.PV->P));
529 if(lpt().m_evaporation && fvSolver().m_noSpecies > 0) {
530 lpt().a_fluidSpecies(lptCellId) += mMax(
531 0.0,
532 mMin(1.0, cellVolume * fvSolver().a_pvariable(fvCellId, fvSolver().m_sysEqn.PV->Y[m_fvLPTSpeciesId])));
533 }
534 }
535
536 if(volume > 1e-16) {
537 // meaning at least one child is active
538 for(MInt n = 0; n < nDim; n++) {
539 lpt().a_fluidVelocity(lptCellId, n) *= conversionFvLpt.velocity / volume;
540 }
541 lpt().a_fluidDensity(lptCellId) *= conversionFvLpt.density / volume;
542 lpt().a_fluidPressure(lptCellId) *= conversionFvLpt.pressure / volume;
543 lpt().a_fluidTemperature(lptCellId) *= conversionFvLpt.temperature / volume;
544 if(lpt().m_evaporation && fvSolver().m_noSpecies > 0) {
545 lpt().a_fluidSpecies(lptCellId) *= 1 / volume;
546 }
547 } else {
548 // meaning completely outside
549 for(MInt n = 0; n < nDim; n++) {
550 lpt().a_fluidVelocity(lptCellId, n) = conversionFvLpt.velocity;
551 }
552 lpt().a_fluidDensity(lptCellId) = conversionFvLpt.density;
553 lpt().a_fluidPressure(lptCellId) = conversionFvLpt.pressure;
554 lpt().a_fluidTemperature(lptCellId) = conversionFvLpt.temperature;
555 if(lpt().m_evaporation && fvSolver().m_noSpecies > 0) {
556 lpt().a_fluidSpecies(lptCellId) = 1;
557 }
558 }
559 } else if(fvCellId < 0) {
560 if(lpt().a_isHalo(lptCellId)) continue;
561 // interpolate linear from the FV parents to the LPT-child
562 fvCellId = lpt2fvIdParent(lptCellId);
563 // ASSERT(fvCellId > -1, "LPT-cell has no matching FV-cell!");
564 if(fvCellId < 0) {
565 cerr << "LPT-cell is missing fv-cell parent" << lptCellId << " " << fvCellId << " " << lpt().a_level(lptCellId)
566 << endl;
567 }
568 interpolateFVLPT(fvCellId, lptCellId);
569 }
570 }
571
572 // NOTE: exchange is necessary as particles on window-cells interpolate the flow variables to their
573 // position and will need values on halo-cells to do so!
574 if(!lpt().m_nonBlockingComm) {
575 lpt().exchangeData(&lpt().a_fluidVariable(0, 0), lpt().PV.noVars());
576 if(lpt().m_evaporation) {
577 lpt().exchangeData(&lpt().a_fluidSpecies(0), 1);
578 }
579#ifndef NDEBUG
580 if(lpt().isActive()) {
581 MPI_Allreduce(MPI_IN_PLACE, &diverged, 1, MPI_C_BOOL, MPI_LOR, lpt().mpiComm(), AT_, "MPI_IN_PLACE", "divCheck");
582 if(diverged) {
583 lpt().saveDebugRestartFile();
584 }
585 }
586 if(fvSolver().isActive()) {
587 MPI_Allreduce(MPI_IN_PLACE, &diverged, 1, MPI_C_BOOL, MPI_LOR, fvSolver().mpiComm(), AT_, "MPI_IN_PLACE",
588 "divCheck");
589 if(diverged) {
590 fvSolver().writeVtkXmlFiles("QOUT", "GEOM", false, true);
591 }
592 }
593#endif
594 } else {
595 if(lpt().isActive()) {
596 lpt().sendFlowField();
597 // the flow field data is received in the time-step
598 }
599 }
600}
601
602
606template <MInt nDim, class SysEqn>
608 TRACE();
609
610 if(!lpt().m_wallCollisions) return;
611
612 if(!lpt().isActive()) return;
613
614 // reset all moving bndry-Cells
615 for(MInt lptCell = 0; lptCell < lpt().a_noCells(); lptCell++) {
616 if(lpt().a_bndryCellId(lptCell) >= lpt().m_noOuterBndryCells) {
617 lpt().a_bndryCellId(lptCell) = -1;
618 }
619 }
620
621 // reset size to stationary outer bndryCells
622 lpt().m_bndryCells->resetSize(lpt().m_noOuterBndryCells);
623
624 MFloatScratchSpace surfaceC(nDim, nDim + 1, AT_, "surfaceC");
625 MFloatScratchSpace surfaceN(nDim, AT_, "surfaceN");
626 MFloatScratchSpace surfaceV(nDim, AT_, "surfaceV");
627 MFloatScratchSpace bndryData(nDim + 1, AT_, "surfaceV");
628
629 // update moving bndryCells from FV
630 for(MInt bndryId = fvSolver().m_noOuterBndryCells; bndryId < fvSolver().m_fvBndryCnd->m_bndryCells->size();
631 bndryId++) {
632 const MInt fvCellId = fvSolver().m_bndryCells->a[bndryId].m_cellId;
633 // TODO: use splitChildToSplitcell for bndry-cells of sliptchilds!
634 if(fvCellId < 0 || fvCellId >= fvSolver().c_noCells()) continue;
635 if(!fvSolver().c_isLeafCell(fvCellId)) continue;
636 MInt lptCellId = fv2lptId(fvCellId);
637 if(lptCellId < 0) {
638 lptCellId = fv2lptIdParent(fvCellId);
639 if(lptCellId < 0) continue;
640 if(lpt().c_level(lptCellId) < lpt().maxRefinementLevel()) continue;
641 ASSERT(m_lptFvInterpolation, "");
642 }
643
644 if(!lpt().c_isLeafCell(lptCellId)) {
645 ASSERT(m_lptFvInterpolation, "");
646 }
647
648 MInt noSurf = fvSolver().m_bndryCells->a[bndryId].m_noSrfcs;
649
650 // bndryCell without a bndrySurface, how is that even possible?
651 // if(noSurf < 1 ) continue;
652
653 // ASSERT(noSurf == 1, "Only for single bndry-surfaces!");
654 if(noSurf > 1) noSurf = 1;
655
656 bndryData[0] = fvSolver().m_fvBndryCnd->m_bndryCells->a[bndryId].m_volume;
657 for(MInt i = 0; i < nDim; i++) {
658 bndryData[1 + i] = fvSolver().m_fvBndryCnd->m_bndryCells->a[bndryId].m_coordinates[i];
659 }
660
661 for(MInt s = 0; s < noSurf; s++) {
662 MInt bodyId = fvSolver().m_levelSetMb ? fvSolver().m_bndryCells->a[bndryId].m_srfcs[s]->m_bodyId[0] : -1;
663 for(MInt n = 0; n < nDim; n++) {
664 surfaceC(noSurf * s + n, 0) = fvSolver().m_fvBndryCnd->m_bndryCells->a[bndryId].m_srfcs[s]->m_coordinates[n];
665 for(MInt i = 0; i < nDim; i++) {
666 surfaceC(noSurf * s + n, i + 1) =
667 fvSolver().m_fvBndryCnd->m_bndryCells->a[bndryId].m_srfcs[s]->m_cutCoordinates[i][n];
668 }
669 // surfaceV[noSurf * s + n] = fvSolver()
670 // .m_fvBndryCnd->m_bndryCells->a[bndryId]
671 // .m_srfcVariables[s]
672 // ->m_primVars[fvSolver().m_sysEqn.PV->VV[n]];
673 if(bodyId > 0) {
674 surfaceV[noSurf * s + n] = fvSolver().m_bodyVelocity[bodyId * nDim + n];
675 } else {
676 surfaceV[noSurf * s + n] = 0.0;
677 }
678 surfaceN[noSurf * s + n] = fvSolver().m_fvBndryCnd->m_bndryCells->a[bndryId].m_srfcs[s]->m_normalVector[n];
679 }
680 }
681
682 // append new LPT bndryCell based on FV data
683 lpt().addBndryCell(lptCellId, &bndryData[0], &surfaceN[0], surfaceC, &surfaceV[0]);
684 }
685}
686
691template <MInt nDim, class SysEqn>
693 TRACE();
694
695 if(!fvSolver().m_sensorParticle) return;
696
697 // reset
698 for(MInt fvCell = 0; fvCell < fvSolver().c_noCells(); fvCell++) {
699 fvSolver().a_noPart(fvCell) = 0;
700 }
701 for(MInt fvCell = fvSolver().c_noCells(); fvCell < fvSolver().a_noCells(); fvCell++) {
702#ifndef NDEBUG
703 if(fvSolver().a_noPart(fvCell) > 0) {
704 cerr << " Particles in " << fvCell << " " << fvSolver().a_noCells() << " " << fvSolver().a_isHalo(fvCell)
705 << fvSolver().a_isBndryGhostCell(fvCell) << endl;
706 }
707#endif
708 fvSolver().a_noPart(fvCell) = 0;
709 }
710
711 // transfer
712 for(MInt lptCell = 0; lptCell < lpt().noInternalCells(); lptCell++) {
713 if(lpt().a_noParticlesInCell(lptCell) < 1 && lpt().a_noEllipsoidsInCell(lptCell) < 1) continue;
714 MInt fvCellId = lpt2fvId(lptCell);
715 if(fvCellId < 0) {
716 ASSERT(m_lptFvInterpolation, "");
717 fvCellId = lpt2fvIdParent(lptCell);
718 if(fvCellId < 0) continue;
719 }
720
721 if(!fvSolver().c_isLeafCell(fvCellId)) {
722 ASSERT(m_lptFvInterpolation, "");
723 }
724
725 fvSolver().a_noPart(fvCellId) = lpt().a_noParticlesInCell(lptCell) + lpt().a_noEllipsoidsInCell(lptCell);
726 }
727
728 fvSolver().exchangeData(&fvSolver().a_noPart(0));
729}
730
731
735template <MInt nDim, class SysEqn>
737 TRACE();
738
739 if(!fvSolver().isActive()) return;
740
741 if(!lpt().m_momentumCoupling && !lpt().m_heatCoupling && !lpt().m_massCoupling) return;
742
743 if(lpt().isActive()) lpt().exchangeSourceTerms();
744
745#ifndef NDEBUG
746 // check that externalSource is set to zero!
747 for(MInt cellId = 0; cellId < fvSolver().a_noCells(); cellId++) {
748 for(MInt var = 0; var < fvSolver().CV->noVariables; var++) {
749 // NOTE: only valid for single particle coupling!
750 // ASSERT(abs(fvSolver().a_externalSource(cellId, var)) < MFloatEps, "");
751 if(abs(fvSolver().a_externalSource(cellId, var)) > MFloatEps) {
752 mTerm(1, AT_, "External Source not reset correctly!!");
753 }
754 }
755 }
756#endif
757
758 for(MInt lptCell = 0; lptCell < lpt().noInternalCells(); lptCell++) {
759 // the source term is only set on LPT leaf-cells
760 if(!lpt().c_isLeafCell(lptCell)) continue;
761 MInt fvCellId = lpt2fvId(lptCell);
762 if(fvCellId > -1 && fvSolver().c_isLeafCell(fvCellId)) {
763 // simple transfer
764 setExternalSourceInCell(fvCellId, lptCell, -1);
765
766 } else if(fvCellId > -1 && !fvSolver().c_isLeafCell(fvCellId)) {
767 // fv-cell is refined further: volume average from LPT to FV
768 // gather all fv-leaf-cell children for internal cells
769 vector<MInt> leafChildIds;
770 fvSolver().grid().getAllLeafChilds(fvCellId, leafChildIds);
771 MFloat totalVol = 0;
772 for(MUint i = 0; i < leafChildIds.size(); i++) {
773 const MInt cellId = leafChildIds[i];
774 totalVol += fvSolver().a_cellVolume(cellId);
775 }
776 for(MUint i = 0; i < leafChildIds.size(); i++) {
777 const MInt cellId = leafChildIds[i];
778 const MFloat vFrac = fvSolver().a_cellVolume(cellId) / totalVol;
779 setExternalSourceInCell(cellId, lptCell, vFrac);
780 }
781 } else {
782 // lpt-Cell is refined further
783 fvCellId = lpt2fvIdParent(lptCell);
784 if(fvCellId < 0) continue;
785 setExternalSourceInCell(fvCellId, lptCell, -1);
786 }
787 }
788
789 // count source-terms in the fv-solver:
790 MInt numVars = 2 + nDim;
791 MFloatScratchSpace conservativeSums(numVars, AT_, "conservativeSums");
792 conservativeSums.fill(0.0);
793 const MFloat dt = fvSolver().timeStep();
794 for(MInt cellId = 0; cellId < fvSolver().noInternalCells(); cellId++) {
795 if(!fvSolver().c_isLeafCell(cellId)) continue;
796 if(fvSolver().a_isInactive(cellId)) continue;
797
798 if(fvSolver().m_noSpecies > 0) {
799 conservativeSums[0] -= dt * fvSolver().a_externalSource(cellId, fvSolver().CV->RHO_Y[m_fvLPTSpeciesId]);
800 }
801 conservativeSums[1] -= dt * fvSolver().a_externalSource(cellId, fvSolver().CV->RHO_E);
802 for(MInt i = 0; i < nDim; i++) {
803 conservativeSums[2 + i] -= dt * fvSolver().a_externalSource(cellId, fvSolver().CV->RHO_VV[i]);
804 }
805 }
806
807 if(fvSolver().m_vapourData.size() > 0) {
808 auto it = fvSolver().m_vapourData.find(globalTimeStep - 1);
809 if(it != fvSolver().m_vapourData.end()) {
810 for(MInt i = 0; i < numVars; i++) {
811 MFloat lastSource = (it->second)[i];
812 conservativeSums[i] += lastSource;
813 }
814 }
815 }
816
817 vector<MFloat> tmp(numVars);
818 for(MInt j = 0; j < numVars; j++) {
819 tmp[j] = conservativeSums[j];
820 }
821
822 fvSolver().m_vapourData.insert(make_pair(globalTimeStep, tmp));
823}
824
825
829template <MInt nDim, class SysEqn>
831 TRACE();
832
833 ASSERT(m_forceFvTimeStep, "");
834
835 // set the timeStep in the LPT solver
836 const MFloat conversionTime = conversionFvLpt.length / conversionFvLpt.velocity;
837 lpt().forceTimeStep(fvSolver().timeStep(true) * conversionTime);
838 const MFloat fvTime = fvSolver().m_levelSetMb ? fvSolver().m_physicalTime : fvSolver().m_time;
839 if(lpt().isActive()) {
840 if(fabs(fvTime * conversionTime - lpt().m_time) > MFloatEps) {
841 cerr << "LPT-time " << lpt().m_time << " Fv-time " << fvTime << " " << conversionTime << " " << globalTimeStep
842 << " " << lpt().isActive() << endl;
843 mTerm(1, AT_, "Miss-matching time!");
844 }
845 } else {
846 lpt().forceTimeStep(fvSolver().timeStep(true) * fvSolver().m_timeRef);
847
848 cerr0 << "Fv-LPT time step set as " << fvSolver().timeStep(true) * fvSolver().m_timeRef << endl;
849 }
850
851 // increasing CFL number after the injection!
852 if(lpt().m_sprayModel != nullptr && lpt().m_sprayModel->m_injectionCrankAngle > 0) {
853 const MFloat cad = fvSolver().crankAngle(fvTime, 0);
854 MFloat injDuration = 10;
855 injDuration = Context::getSolverProperty<MFloat>("injectorInjectionTime", lpt().solverId(), AT_, &injDuration);
856 MInt injCAD = 40;
857 if(injDuration > 15) {
858 injCAD = 55;
859 }
860 if(cad > lpt().m_sprayModel->m_injectionCrankAngle + injCAD && fvSolver().m_cfl < 0.7) {
861 cerr0 << "Increasing FV CFL for next computation from " << fvSolver().m_cfl << " to 0.95!" << endl;
862 fvSolver().m_cfl = 0.95;
863 }
864 }
865}
866
870template <MInt nDim, class SysEqn>
872 TRACE();
873
874 // transfer the number of particles for the particle limit sensor!
875 transferNoParticlesInCell();
876}
877
881template <MInt nDim, class SysEqn>
883 const MInt lptCell,
884 const MFloat factor) {
885 TRACE();
886
887 if(factor < 0) {
888 if(lpt().m_momentumCoupling) {
889 for(MInt i = 0; i < nDim; i++) {
890 fvSolver().a_externalSource(fvCellId, fvSolver().CV->RHO_VV[i]) +=
891 lpt().a_momentumFlux(lptCell, i) / conversionFvLpt.momentum;
892 }
893 fvSolver().a_externalSource(fvCellId, fvSolver().CV->RHO_E) += lpt().a_workFlux(lptCell) / conversionFvLpt.energy;
894 }
895 if(lpt().m_heatCoupling) {
896 fvSolver().a_externalSource(fvCellId, fvSolver().CV->RHO_E) += lpt().a_heatFlux(lptCell) / conversionFvLpt.energy;
897 }
898 if(lpt().m_massCoupling) {
899 fvSolver().a_externalSource(fvCellId, fvSolver().CV->RHO) += lpt().a_massFlux(lptCell) / conversionFvLpt.mass;
900 fvSolver().a_externalSource(fvCellId, fvSolver().CV->RHO_Y[m_fvLPTSpeciesId]) +=
901 lpt().a_massFlux(lptCell) / conversionFvLpt.mass;
902 }
903 } else {
904 if(lpt().m_momentumCoupling) {
905 for(MInt i = 0; i < nDim; i++) {
906 fvSolver().a_externalSource(fvCellId, fvSolver().CV->RHO_VV[i]) +=
907 lpt().a_momentumFlux(lptCell, i) * factor / conversionFvLpt.momentum;
908 }
909 fvSolver().a_externalSource(fvCellId, fvSolver().CV->RHO_E) +=
910 lpt().a_workFlux(lptCell) * factor / conversionFvLpt.energy;
911 }
912 if(lpt().m_heatCoupling) {
913 fvSolver().a_externalSource(fvCellId, fvSolver().CV->RHO_E) +=
914 lpt().a_heatFlux(lptCell) * factor / conversionFvLpt.energy;
915 }
916 if(lpt().m_massCoupling) {
917 fvSolver().a_externalSource(fvCellId, fvSolver().CV->RHO) +=
918 lpt().a_massFlux(lptCell) * factor / conversionFvLpt.mass;
919
920 fvSolver().a_externalSource(fvCellId, fvSolver().CV->RHO_Y[m_fvLPTSpeciesId]) +=
921 lpt().a_massFlux(lptCell) * factor / conversionFvLpt.mass;
922 }
923 }
924}
925
929template <MInt nDim, class SysEqn>
931 TRACE();
932
933 // Post FV solver
934 if(solverId == fvSolver().solverId()) {
935 transferCellStatus();
936 transferFlowField();
937 transferVelocitySlopes();
938 if(lpt().isActive()) {
939 lpt().receiveFlowField();
940 lpt().receiveVelocitySlopes();
941 lpt().waitForSendReqs();
942 }
943
944 updateLPTBndry();
945
946 } else if(solverId == lpt().solverId()) {
947 // set external sources in the FV-solver
948 // as they are reset there in finalizeAdaptation
949 if(fvSolver().m_hasExternalSource) {
950 fvSolver().resetExternalSources();
951 transferExternalSources();
952 }
953 }
954}
955
961template <MInt nDim, class SysEqn>
963 TRACE();
964
965 MInt interpolationCells[8] = {0, 0, 0, 0, 0, 0, 0, 0};
966 MFloat point[3] = {lpt().c_coordinate(to, 0), lpt().c_coordinate(to, 1), lpt().c_coordinate(to, 2)};
967
968 std::function<MBool(const MInt, const MInt)> neighborCheck = [&](const MInt cellId, const MInt id) {
969 return static_cast<MBool>(fvSolver().checkNeighborActive(cellId, id));
970 };
971
972 const MInt position = fvSolver().setUpInterpolationStencil(from, interpolationCells, point, neighborCheck, true);
973
974 if(position < 0) {
975 for(MInt n = 0; n < nDim; n++) {
976 lpt().a_fluidVelocity(to, n) =
977 fvSolver().a_pvariable(from, fvSolver().m_sysEqn.PV->VV[n]) * conversionFvLpt.velocity;
978 }
979 lpt().a_fluidDensity(to) = fvSolver().a_pvariable(from, fvSolver().m_sysEqn.PV->RHO) * conversionFvLpt.density;
980 lpt().a_fluidPressure(to) = fvSolver().a_pvariable(from, fvSolver().m_sysEqn.PV->P) * conversionFvLpt.pressure;
981 lpt().a_fluidTemperature(to) =
982 fvSolver().sysEqn().temperature_ES(fvSolver().a_pvariable(from, fvSolver().m_sysEqn.PV->RHO),
983 fvSolver().a_pvariable(from, fvSolver().m_sysEqn.PV->P))
984 * conversionFvLpt.temperature;
985 if(lpt().m_evaporation && fvSolver().m_noSpecies > 0) {
986 lpt().a_fluidSpecies(to) = fvSolver().a_pvariable(from, fvSolver().m_sysEqn.PV->Y[m_fvLPTSpeciesId]);
987 }
988
989 } else {
990 for(MInt n = 0; n < nDim; n++) {
991 lpt().a_fluidVelocity(to, n) =
992 interpolateVariable(interpolationCells, point, fvSolver().m_sysEqn.PV->VV[n]) * conversionFvLpt.velocity;
993 }
994 const MFloat rho = interpolateVariable(interpolationCells, point, fvSolver().m_sysEqn.PV->RHO);
995 const MFloat p = interpolateVariable(interpolationCells, point, fvSolver().m_sysEqn.PV->P);
996 lpt().a_fluidDensity(to) = rho * conversionFvLpt.density;
997 lpt().a_fluidPressure(to) = p * conversionFvLpt.pressure;
998 lpt().a_fluidTemperature(to) = fvSolver().sysEqn().temperature_ES(rho, p) * conversionFvLpt.temperature;
999 if(lpt().m_evaporation && fvSolver().m_noSpecies > 0) {
1000 lpt().a_fluidSpecies(to) =
1001 interpolateVariable(interpolationCells, point, fvSolver().m_sysEqn.PV->Y[m_fvLPTSpeciesId]);
1002 }
1003 }
1004}
1005
1011template <MInt nDim, class SysEqn>
1013 TRACE();
1014
1015 MInt interpolationCells[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1016 MFloat point[3] = {lpt().c_coordinate(to, 0), lpt().c_coordinate(to, 1), lpt().c_coordinate(to, 2)};
1017
1018 std::function<MBool(const MInt, const MInt)> neighborCheck = [&](const MInt cellId, const MInt id) {
1019 return static_cast<MBool>(fvSolver().checkNeighborActive(cellId, id));
1020 };
1021
1022 const MInt position = fvSolver().setUpInterpolationStencil(from, interpolationCells, point, neighborCheck, true);
1023
1024 if(position < 0) {
1025 for(MInt varId = 0; varId < nDim; varId++) {
1026 for(MInt dir = 0; dir < nDim; dir++) {
1027 lpt().a_velocitySlope(to, varId, dir) =
1028 fvSolver().a_slope(from, fvSolver().m_sysEqn.PV->VV[varId], dir) * conversionFvLpt.velocitySlope;
1029 }
1030 }
1031 } else {
1032 for(MInt varId = 0; varId < nDim; varId++) {
1033 for(MInt dir = 0; dir < nDim; dir++) {
1034 lpt().a_velocitySlope(to, varId, dir) =
1035 interpolateSlope(interpolationCells, point, fvSolver().m_sysEqn.PV->VV[varId], dir)
1036 * conversionFvLpt.velocitySlope;
1037 }
1038 }
1039 }
1040}
1041
1045
1046template <MInt nDim, class SysEqn>
1048 TRACE();
1049
1050 std::function<MFloat(const MInt, const MInt)> scalarField = [&](const MInt cellId, const MInt varId) {
1051 return static_cast<MFloat>(fvSolver().a_pvariable(cellId, varId));
1052 };
1053
1054 std::function<MFloat(const MInt, const MInt)> coordinate = [&](const MInt cellId, const MInt id) {
1055 return static_cast<MFloat>(fvSolver().a_coordinate(cellId, id));
1056 };
1057
1058 return fvSolver().template interpolateFieldData<false>(&interpolationCells[0], &point[0], v, scalarField, coordinate);
1059}
1060
1066template <MInt nDim, class SysEqn>
1068 TRACE();
1069
1070 std::function<MFloat(const MInt, const MInt)> scalarField = [&](const MInt cellId, const MInt varId) {
1071 return static_cast<MFloat>(*(&fvSolver().a_slope(cellId, 0, 0) + varId));
1072 };
1073
1074 std::function<MFloat(const MInt, const MInt)> coordinate = [&](const MInt cellId, const MInt id) {
1075 return static_cast<MFloat>(fvSolver().a_coordinate(cellId, id));
1076 };
1077
1078 MInt valIndex = v * nDim + dir;
1079 return fvSolver().template interpolateFieldData<false>(&interpolationCells[0], &point[0], valIndex, scalarField,
1080 coordinate);
1081}
1082
1086template <MInt nDim, class SysEqn>
1088 for(MInt lptCellId = 0; lptCellId < lpt().a_noCells(); lptCellId++) {
1089 MInt fvCellId = lpt2fvIdParent(lptCellId);
1090 if(fvCellId < 0 || fvCellId > fvSolver().a_noCells()) {
1091 lpt().a_isValidCell(lptCellId) = false;
1092 } else if(fvSolver().a_hasProperty(fvCellId, FvCell::IsCutOff) && !lpt().grid().isPeriodic(lptCellId)) {
1093 lpt().a_isValidCell(lptCellId) = false;
1094 } else if(fvSolver().a_hasProperty(fvCellId, FvCell::IsInSpongeLayer)) {
1095 lpt().a_isValidCell(lptCellId) = false;
1096 } else if(fvSolver().a_isInactive(fvCellId)) {
1097 lpt().a_isValidCell(lptCellId) = false;
1098 } else {
1099 lpt().a_isValidCell(lptCellId) = true;
1100 }
1101 }
1102}
1103
1111template <MInt nDim, class SysEqn>
1113 if(!lpt().m_ellipsoids) return;
1114
1115#ifndef NDEBUG
1116 MBool diverged = false;
1117#endif
1118
1119 for(MInt lptCellId = 0; lptCellId < lpt().noInternalCells(); lptCellId++) {
1120 if(!lpt().c_isLeafCell(lptCellId)) continue;
1121 MInt fvCellId = lpt2fvId(lptCellId);
1122 if(fvCellId > -1 && fvSolver().c_isLeafCell(fvCellId)) {
1123 // simple transfer if both cells are leaf-cells
1124 for(MInt varId = 0; varId < nDim; varId++) {
1125 for(MInt dir = 0; dir < nDim; dir++) {
1126 lpt().a_velocitySlope(lptCellId, varId, dir) =
1127 fvSolver().a_slope(fvCellId, fvSolver().m_sysEqn.PV->VV[varId], dir) * conversionFvLpt.velocitySlope;
1128#ifndef NDEBUG
1129 if(std::isnan(lpt().a_velocitySlope(lptCellId, varId, dir)) && lpt().a_isValidCell(lptCellId)
1130 && globalTimeStep > 0) {
1131 diverged = true;
1132 cerr << "Nan velocity slope: " << lpt().a_velocitySlope(lptCellId, varId, dir) << " "
1133 << lpt().a_coordinate(lptCellId, 0) << " " << lpt().a_coordinate(lptCellId, 1) << " "
1134 << lpt().a_coordinate(lptCellId, nDim - 1) << endl;
1135 mTerm(1, AT_, "FVParticle: Transfer of slope that is NAN");
1136 }
1137#endif
1138 }
1139 }
1140 } else if(fvCellId > -1 && !fvSolver().c_isLeafCell(fvCellId)) {
1141 // interpolate from FV-leaf childs if the matching fv-Cell is not a leaf cell
1142 if(lpt().a_isHalo(lptCellId)) continue;
1143 // gather all fv-leaf-cell childs for internal cells
1144 vector<MInt> leafChildIds;
1145 fvSolver().grid().getAllLeafChilds(fvCellId, leafChildIds);
1146 ASSERT(!leafChildIds.empty(), "");
1147
1148 // reset variables
1149 for(MInt varId = 0; varId < nDim; varId++) {
1150 for(MInt dir = 0; dir < nDim; dir++) {
1151 lpt().a_velocitySlope(lptCellId, varId, dir) = F0;
1152 }
1153 }
1154 // interpolate variables by volume
1155 MFloat volume = 0;
1156 for(MUint i = 0; i < leafChildIds.size(); i++) {
1157 const MInt cellId = leafChildIds[i];
1158 if(fvSolver().a_isInactive(cellId)) continue;
1159 const MFloat cellVolume = fvSolver().a_cellVolume(cellId);
1160 ASSERT(fvSolver().c_isLeafCell(cellId), "");
1161 if(cellVolume < 1e-16) mTerm(1, AT_, "Invalid cell-volume!");
1162 volume += cellVolume;
1163 for(MInt varId = 0; varId < nDim; varId++) {
1164 for(MInt dir = 0; dir < nDim; dir++) {
1165 lpt().a_velocitySlope(lptCellId, varId, dir) +=
1166 cellVolume * fvSolver().a_slope(fvCellId, fvSolver().m_sysEqn.PV->VV[varId], dir);
1167 }
1168 }
1169 }
1170 if(volume > 1e-16) {
1171 // meaning at least one child is active
1172 for(MInt varId = 0; varId < nDim; varId++) {
1173 for(MInt dir = 0; dir < nDim; dir++) {
1174 lpt().a_velocitySlope(lptCellId, varId, dir) *= conversionFvLpt.velocitySlope / volume;
1175 }
1176 }
1177 } else {
1178 // meaning completely outside
1179 for(MInt varId = 0; varId < nDim; varId++) {
1180 for(MInt dir = 0; dir < nDim; dir++) {
1181 lpt().a_velocitySlope(lptCellId, varId, dir) = conversionFvLpt.velocitySlope;
1182 }
1183 }
1184 }
1185 } else if(fvCellId < 0) {
1186 if(lpt().a_isHalo(lptCellId)) continue;
1187 // interpolate linear from the FV parents to the LPT-child
1188 fvCellId = lpt2fvIdParent(lptCellId);
1189 ASSERT(fvCellId > -1, "LPT-cell has no matching FV-cell!");
1190 interpolateVelocitySlopesFVLPT(fvCellId, lptCellId);
1191 }
1192 }
1193
1194 // NOTE: exchange is necessary as particles on window-cells interpolate the flow variables to their
1195 // position and will need values on halo-cells to do so!
1196 if(!lpt().m_nonBlockingComm) {
1197 lpt().exchangeData(&lpt().a_velocitySlope(0, 0, 0), nDim * nDim);
1198#ifndef NDEBUG
1199 if(lpt().isActive()) {
1200 MPI_Allreduce(MPI_IN_PLACE, &diverged, 1, MPI_C_BOOL, MPI_LOR, lpt().mpiComm(), AT_, "MPI_IN_PLACE", "divCheck");
1201 if(diverged) {
1202 lpt().saveDebugRestartFile();
1203 }
1204 }
1205 if(fvSolver().isActive()) {
1206 MPI_Allreduce(MPI_IN_PLACE, &diverged, 1, MPI_C_BOOL, MPI_LOR, fvSolver().mpiComm(), AT_, "MPI_IN_PLACE",
1207 "divCheck");
1208 if(diverged) {
1209 fvSolver().writeVtkXmlFiles("QOUT", "GEOM", false, true);
1210 }
1211 }
1212#endif
1213 } else {
1214 if(lpt().isActive()) {
1215 lpt().sendVelocitySlopes(); // the velocity slopes are received in the time-step
1216 }
1217 }
1218}
1219
1225template <MInt nDim, class SysEqn>
1227 TRACE();
1228
1229 ASSERT(m_interLeafed, "");
1230
1231 cerr0 << "Unifying time-step LPT: " << lpt().m_timeStep << " FV: " << fvSolver().timeStep() << endl;
1232
1233 const MFloat invalidTimeStep = std::numeric_limits<MFloat>::max();
1234 const MFloat conversionTime = conversionFvLpt.length / conversionFvLpt.velocity;
1235 MFloat timeFv = invalidTimeStep;
1236 MFloat timeLpt = invalidTimeStep;
1237 MInt maxLevelFv = -1;
1238 MInt maxLevelLpt = -1;
1239 MFloat newTimeFv = invalidTimeStep;
1240 MFloat newTimeLpt = invalidTimeStep;
1241
1242 // determine the new/combined timeStep on ranks which have both Fv and Lpt solvers!
1243 if(fvSolver().isActive() && lpt().isActive()) {
1244 timeFv = fvSolver().timeStep(true);
1245 maxLevelFv = fvSolver().maxLevel();
1246 timeLpt = lpt().timeStep() / conversionTime;
1247 maxLevelLpt = lpt().maxLevel();
1248
1249 MInt levelDiff = maxLevelLpt - maxLevelFv;
1250 if(levelDiff == 0) {
1251 newTimeFv = mMin(timeFv, timeLpt);
1252 newTimeLpt = newTimeFv * conversionTime;
1253 } else {
1254 if(levelDiff == 1) {
1255 // LPT has higher level => lower timeStep!
1256 newTimeLpt = mMin(newTimeLpt, newTimeFv / 2.0);
1257 newTimeFv = 2.0 * newTimeLpt;
1258 } else if(levelDiff == -1) {
1259 // Fv has the higher level => lower timeStep!
1260 newTimeFv = mMin(newTimeFv, newTimeLpt / 2.0);
1261 newTimeLpt = 2.0 * newTimeFv;
1262 } else {
1263 mTerm(1, AT_, "TimeStepping currently only implemented for 1 level difference!");
1264 }
1265 }
1266 }
1267
1268 if(lpt().isActive()) {
1269 MPI_Allreduce(MPI_IN_PLACE, &newTimeLpt, 1, MPI_DOUBLE, MPI_MIN, lpt().mpiComm(), AT_, "MPI_IN_PLACE",
1270 "newTimeLpt");
1271 }
1272 if(fvSolver().isActive()) {
1273 MPI_Allreduce(MPI_IN_PLACE, &newTimeFv, 1, MPI_DOUBLE, MPI_MIN, fvSolver().mpiComm(), AT_, "MPI_IN_PLACE",
1274 "newTimeFv");
1275 }
1276
1277 if(fvSolver().domainId() == 0) {
1278 cerr << "Fv timestep was " << timeFv << " combined TS is " << newTimeFv << endl;
1279 }
1280
1281 if(lpt().domainId() == 0) {
1282 cerr << "LPT timestep was " << timeLpt << " combined TS is " << newTimeLpt << endl;
1283 }
1284
1285
1286 if(fvSolver().isActive()) {
1287 fvSolver().forceTimeStep(newTimeFv);
1288 }
1289 if(lpt().isActive()) {
1290 lpt().forceTimeStep(newTimeLpt);
1291 }
1292}
1293
1297template <MInt nDim, class SysEqn>
1299 std::vector<MBool>& /*solverCompleted*/) {
1300 if(!m_interLeafed) return;
1301
1302 if(fvSolver().isActive() && lpt().isActive()) {
1303 if(globalTimeStep % lpt().a_timeStepComputationInterval() != 0
1304 && fabs(lpt().m_timeStep - fvSolver().timeStep()) > MFloatEps) {
1305 mTerm(1, AT_, "TimeSteps of FV and LPT differ at TS " + to_string(globalTimeStep));
1306 }
1307 }
1308
1309 // only during FV-Lpt computation
1310 if(recipeStep == m_lptSolverOrder && (solverId == m_fvSolverId || solverId == m_lptSolverId)) {
1311 m_solutionStep++;
1312
1313 // meaning after the 4th LPT solution step!
1314 if(m_solutionStep == 2 * m_noSolutionSteps - 3) {
1315 if(solverId == m_lptSolverId) {
1316 // set the source-terms before the last FV-Solution(=RK) Step
1317 // and before the 4th RK step for FvMb
1318
1319 if(fvSolver().m_hasExternalSource && !lpt().m_skipLPT) {
1320 fvSolver().resetExternalSources();
1321 transferExternalSources();
1322 } else if(lpt().m_skipLPT) {
1323 vector<MFloat> tmp(2 + nDim);
1324 for(MInt j = 0; j < (2 + nDim); j++) {
1325 tmp[j] = 0.0;
1326 }
1327 fvSolver().m_vapourData.insert(make_pair(globalTimeStep, tmp));
1328 }
1329 }
1330 }
1331
1332 if(m_solutionStep == 2 * m_noSolutionSteps - 2 && !lpt().m_skipLPT) {
1333 if(fvSolver().m_hasExternalSource) {
1334 fvSolver().applyExternalOldSource();
1335 }
1336 }
1337
1338 // meaning after the last RK-step!
1339 if(m_solutionStep == 2 * m_noSolutionSteps) {
1340 if(solverId == m_fvSolverId && !lpt().m_skipLPT) {
1341 // after the last FV-solution step!
1342 transferFlowField();
1343 }
1344 }
1345
1346 if(m_solutionStep == 2 * m_noSolutionSteps && (fvSolver().m_timeStepUpdated || lpt().m_timeStepUpdated)) {
1347 if(!m_forceFvTimeStep) {
1348 unifyTimeStep();
1349 } else {
1350 transferTimeStep();
1351 }
1352 }
1353 }
1354}
1355
1359template <MInt nDim, class SysEqn>
1361 TRACE();
1362
1363 // count particles in cells
1364 lpt().countParticlesInCells();
1365
1366 if(!fvSolver().isActive()) {
1367 return;
1368 }
1369
1370 // transfer to FV-grid
1371 transferNoParticlesInCell();
1372
1373 // determine max. No Particles in a FV-cell
1374 MInt maxNoParticles = 0;
1375 MInt maxParticlesSum = 0;
1376 for(MInt cellId = 0; cellId < fvSolver().noInternalCells(); cellId++) {
1377 if(fvSolver().a_noPart(cellId) > maxNoParticles) {
1378 maxNoParticles = fvSolver().a_noPart(cellId);
1379 }
1380 maxParticlesSum += fvSolver().a_noPart(cellId);
1381 }
1382 // create binning ranges
1383 MPI_Allreduce(MPI_IN_PLACE, &maxNoParticles, 1, MPI_INT, MPI_MAX, fvSolver().mpiComm(), AT_, "MPI_IN_PLACE",
1384 "maxParticles");
1385
1386 MPI_Allreduce(MPI_IN_PLACE, &maxParticlesSum, 1, MPI_INT, MPI_MAX, fvSolver().mpiComm(), AT_, "MPI_IN_PLACE",
1387 "maxParticlesSum");
1388
1389 {
1390 MFloatScratchSpace noCells(maxNoParticles + 1, AT_, "noCells");
1391 noCells.fill(0);
1392
1393 // perform cell-binning
1394 for(MInt cellId = 0; cellId < fvSolver().noInternalCells(); cellId++) {
1395 if(!fvSolver().c_isLeafCell(cellId)) {
1396 continue;
1397 }
1398 if(!fvSolver().a_isActive(cellId)) {
1399 continue;
1400 }
1401
1402 const MInt pos = fvSolver().a_noPart(cellId);
1403 noCells(pos)++;
1404 }
1405
1406 // communicate results
1407 MPI_Allreduce(MPI_IN_PLACE, &noCells(0), maxNoParticles + 1, MPI_DOUBLE, MPI_SUM, fvSolver().mpiComm(), AT_,
1408 "MPI_IN_PLACE", "noCells");
1409
1410 // write to file
1411 if(fvSolver().domainId() == 0) {
1412 struct stat buffer {};
1413 MString name = "noParticlesInCells_" + to_string(fvSolver().solverId()) + "_" + to_string(globalTimeStep);
1414 const char* cstr = name.c_str();
1415 if(stat(cstr, &buffer) == 0) {
1416 rename(cstr, "noParticlesInCells_BAK");
1417 }
1418 ofstream ofl;
1419 ofl.open(name, ios_base::out | ios_base::app);
1420 ofl << "# 1:noParticles 2:noCells " << endl;
1421 for(MInt i = 0; i < maxNoParticles + 1; i++) {
1422 ofl << i << " " << noCells(i) << endl;
1423 }
1424 ofl.close();
1425 }
1426 }
1427
1428 // perform min-cell binning
1429 MIntScratchSpace noMinCells(maxParticlesSum + 1, AT_, "noMinCells");
1430 noMinCells.fill(0);
1431
1432 // reset particles to zero on halo-cells
1433 for(MInt cellId = fvSolver().noInternalCells(); cellId < fvSolver().a_noCells(); cellId++) {
1434 fvSolver().a_noPart(cellId) = 0;
1435 }
1436
1437 MFloat minCellVolume = -1;
1438 for(MInt id = 0; id < fvSolver().grid().noMinCells(); id++) {
1439 const MInt cellId = fvSolver().grid().minCell(id);
1440 if(cellId < 0) {
1441 continue;
1442 }
1443 minCellVolume = fvSolver().c_cellVolumeAtLevel(fvSolver().a_level(cellId));
1444
1445 // loop over all children and sum particles
1446 MInt curCount = childLoop(cellId);
1447
1448 if(curCount > maxParticlesSum || curCount < 0) {
1449 cerr << "Strange particle count!" << curCount << endl;
1450 }
1451
1452 noMinCells(curCount)++;
1453 }
1454
1455 // communicate results
1456 MPI_Allreduce(MPI_IN_PLACE, &noMinCells(0), maxParticlesSum + 1, MPI_INT, MPI_SUM, fvSolver().mpiComm(), AT_,
1457 "MPI_IN_PLACE", "noMinCells");
1458
1459 // write to file
1460 if(fvSolver().domainId() == 0) {
1461 struct stat buffer {};
1462 MString name = "noParticlesInMinCells_" + to_string(fvSolver().solverId()) + "_" + to_string(globalTimeStep);
1463 const char* cstr = name.c_str();
1464 if(stat(cstr, &buffer) == 0) {
1465 rename(cstr, "noParticlesInMinCells_BAK");
1466 }
1467
1468 MString header = "# Min-cell volume : " + to_string(minCellVolume) + " \n";
1469 ofstream ofl;
1470 ofl.open(name, ios_base::out | ios_base::trunc);
1471 ofl << header << endl;
1472
1473 for(MInt i = 0; i < maxParticlesSum + 1; i++) {
1474 ofl << i << " " << noMinCells(i) << endl;
1475 }
1476 ofl.close();
1477 }
1478}
1479
1483template <MInt nDim, class SysEqn>
1485 MInt sum = 0;
1486 if(fvSolver().c_noChildren(cellId) > 0) {
1487 for(MInt child = 0; child < fvSolver().c_noChildren(cellId); child++) {
1488 MInt childId = fvSolver().c_childId(cellId, child);
1489 if(childId < 0) {
1490 continue;
1491 }
1492 sum += childLoop(childId);
1493 }
1494 } else {
1495 sum += fvSolver().a_noPart(cellId);
1496 }
1497 return sum;
1498}
1499
static MBool propertyExists(const MString &name, MInt solver=m_noSolvers)
This function checks if a property exists in general.
Definition: context.cpp:494
FvCartesianSolverXD< nDim, SysEqn > & fvSolver() const
Definition: fvparticle.h:48
LPT< nDim > * m_particle
Definition: fvparticle.h:116
void finalizeAdaptation(const MInt) override
prepate adaptation
Definition: fvparticle.cpp:930
LPT< nDim > & lpt() const override
Definition: fvparticle.h:47
void initConversion()
Calculates the conversion factor for different non-dimensionalisations in the FV and LPT solvers.
void finalizeBalance(const MInt unused) override
postCouple: exchange source terms after the LPT timeStep
Definition: fvparticle.cpp:418
void transferVelocitySlopes()
transfer the FV velocity slopes to the LPT solver
MInt childLoop(MInt cellId)
compute particle-cell binning and write to file
void initParticleVelocity()
Sets the initial particle velocity based on the flow field velocity in that cell. NOTE: This can not ...
Definition: fvparticle.cpp:227
MFloat interpolateVariable(MInt *, MFloat *, MInt)
interpolates the fv-variable /
void transferCellStatus()
set the isValid status for LPT cells based on the FV-solver cell properties
void transferNoParticlesInCell()
set a_noPart in Fv based on a_noParticlesInCell and a_noEllipsoidsInCell in LPT solver NOTE: this is ...
Definition: fvparticle.cpp:692
void unifyTimeStep()
Find combinded maximum timeStep for Fv and LPT solvers only possible for interleafed execution assumi...
void updateLPTBndry()
transfer all relevant bndryCell-data from FV to LPT solver before the LPT timeStep!
Definition: fvparticle.cpp:607
void interpolateFVLPT(const MInt from, const MInt to)
interpolate flow variables from the fv-grid to the LPT-cell position
Definition: fvparticle.cpp:962
FvCartesianSolverXD< nDim, SysEqn > * m_fvSolver
Definition: fvparticle.h:117
void writeParticleCellStats()
compute particle-cell binning and write to file
void transferFlowField()
transfer flow data from FV to LPT
Definition: fvparticle.cpp:452
void preCouple(MInt) override
preCoupler: reset external source terms before the LPT timestep
Definition: fvparticle.cpp:350
void checkProperties() override
Checks property-data which is read in by both lpt-and Fv-Solver.
Definition: fvparticle.cpp:338
MFloat interpolateSlope(MInt *, MFloat *, MInt, MInt)
interpolate fv slope
void initData()
Initialize coupling-class-specific Data.
Definition: fvparticle.cpp:244
void transferTimeStep()
transfer/enforce Fv timeStep onto LPT solver
Definition: fvparticle.cpp:830
void subCouple(MInt, MInt, std::vector< MBool > &) override
transfer the FV velocity slopes to the LPT solver
void postCouple(MInt) override
postCouple: exchange source terms after the LPT timeStep
Definition: fvparticle.cpp:382
void transferExternalSources()
set external sources in Fv solver based on values in the LPT solver
Definition: fvparticle.cpp:736
void prepareAdaptation() override
prepate adaptation
Definition: fvparticle.cpp:871
void interpolateVelocitySlopesFVLPT(const MInt from, const MInt to)
Interpolates the velocity slopes from the fv-grid to the LPT-cell.
void init() override
performs the coupling after solver initialization
Definition: fvparticle.cpp:56
void setExternalSourceInCell(const MInt, const MInt, const MFloat)
set external source in FV-solver from LPT fluxes
Definition: fvparticle.cpp:882
void finalizeCouplerInit() override
Definition: fvparticle.cpp:255
CouplerFvParticle(const MInt couplingId, LPT< nDim > *particle, FvCartesianSolver *fv)
Definition: fvparticle.cpp:14
Definition: lpt.h:82
This class is a ScratchSpace.
Definition: scratch.h:758
void fill(T val)
fill the scratch with a given value
Definition: scratch.h:311
void mTerm(const MInt errorCode, const MString &location, const MString &message)
Definition: functions.cpp:29
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
@ IsInSpongeLayer
MInt globalTimeStep
std::ostream cerr0
int32_t MInt
Definition: maiatypes.h:62
uint32_t MUint
Definition: maiatypes.h:63
std::basic_string< char > MString
Definition: maiatypes.h:55
double MFloat
Definition: maiatypes.h:52
bool MBool
Definition: maiatypes.h:58
MInt id
Definition: maiatypes.h:71
int MPI_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
Definition: lpt.h:47