11#if not defined(MAIA_DISABLE_DG)
45#ifndef MAIA_DISABLE_STRUCTURED
78struct FvApeSolverXD {};
80struct FvApeSolverXD<2> {
84struct FvApeSolverXD<3> {
89#ifndef MAIA_DISABLE_STRUCTURED
91struct FvStructuredSolverXD {};
93struct FvStructuredSolverXD<2> {
97struct FvStructuredSolverXD<3> {
104struct LsCartesianSolverXD {};
106struct LsCartesianSolverXD<2> {
110struct LsCartesianSolverXD<3> {
114template <MInt nDim,
class SysEqn>
115struct LsFvCombustionXD {};
133template <MInt nDim,
class SysEqn>
152template <MInt nDim,
class SysEqn>
153struct CouplingLsFvXD {};
216 const MBool flowSolverDefault =
false;
217 const auto flowSolver = Context::getBasicProperty<MBool>(
"flowSolver", AT_, &flowSolverDefault);
247 const MBool gridGenDefault =
false;
248 const auto gridGeneration = Context::getBasicProperty<MBool>(
"gridGenerator", AT_, &gridGenDefault);
251 if(gridGeneration && flowSolver) {
252 TERMM(1,
"Error: grid generation and flow solver activated.");
254 if(!gridGeneration && !flowSolver) {
255 TERMM(1,
"Error: grid generation and flow solver both deactivated.");
288 m_log <<
"Split MPI communication activated." << std::endl;
330 TERMM(1,
"solverTimingsSampleInterval <= 0");
351 const MString message =
"NOTE: skipping solution and restart IO for performance testing!";
358 const MInt nDim = read_nDim();
388 DEBUG(
"Application::() noSolvers " <<
m_noSolvers, MAIA_DEBUG_LEVEL1);
404 mTerm(1, AT_,
"Enabling multi-solver without having more than one solver does not make sense");
407 MBool addSolverToGrid =
false;
408 addSolverToGrid = Context::getBasicProperty<MBool>(
"addSolverToGrid", AT_, &addSolverToGrid);
412 TERMM(1,
"Number of solvers > 1, but multiSolverGrid not enabled!");
415 g_timeSteps = Context::getBasicProperty<MInt>(
"timeSteps", AT_);
416 DEBUG(
"Application::() g_timeSteps " <<
g_timeSteps, MAIA_DEBUG_LEVEL1);
469#ifndef DISABLE_OUTPUT
473 TERMM(1,
"postProcessing is true, but the number of postprocessing solvers is 0!");
537 for(
MInt i = 0; i < noSolversAndCouplers; i++) {
543 MInt maxNoGlobalSolverTimers = -1;
545 MPI_COMM_WORLD, AT_,
"m_noGlobalSolverTimers",
"maxNoGlobalSolverTimers");
547 TERMM(1,
"Error: number of global solver timings does not match on all domains.");
567 for(
MInt i = 0; i < noSolversAndCouplers; i++) {
571 std::vector<std::pair<MString, MFloat>> timings{};
574 ASSERT((
MInt)timings.size() == noTimers,
"number of timings mismatch #" + std::to_string(i) +
": "
575 + std::to_string(timings.size()) +
" != " + std::to_string(noTimers));
577 for(
MInt j = 0; j < noTimers; j++) {
595 cerr0 << endl <<
"=== MAIA RUN LOOP ===" << endl << endl;
596 const MPI_Comm comm = MPI_COMM_WORLD;
599 auto logDuration = [&](
const MFloat timeStart,
const MString comment) {
604 NEW_TIMER_GROUP(tg_totalMainLoop,
"total main loop");
605 NEW_TIMER(t_totalMainLoop,
"total main loop", tg_totalMainLoop);
606 NEW_SUB_TIMER(t_adaptation,
"adaptation", t_totalMainLoop);
607 NEW_SUB_TIMER(t_preTimeStep,
"preTimeStep", t_totalMainLoop);
608 NEW_SUB_TIMER(t_preCouple,
"preCouple", t_totalMainLoop);
609 NEW_SUB_TIMER(t_timeStep,
"timeStep", t_totalMainLoop);
610 NEW_SUB_TIMER(t_postTimeStep,
"postTimeStep", t_totalMainLoop);
611 NEW_SUB_TIMER(t_postCouple,
"postCouple", t_totalMainLoop);
612 NEW_SUB_TIMER(t_implicitTimeStep,
"Implicit time-step", t_totalMainLoop);
614#ifndef DISABLE_OUTPUT
615 NEW_SUB_TIMER(t_solutionOutput,
"solutionOutput", t_totalMainLoop);
618 NEW_SUB_TIMER(t_balance,
"balance", t_totalMainLoop);
620 RECORD_TIMER_START(t_totalMainLoop);
625 const MString outputDir = Context::getSolverProperty<MString>(
"outputDir", i, AT_);
632 vector<typename GeometryXD<nDim>::type*> geometries;
634 const MString solverType = Context::getSolverProperty<MString>(
"solvertype", solverId, AT_);
638 geometries.push_back(
nullptr);
641 logDuration(geometryTimeStart,
"Create geometries");
644 MInt noMinutesEndAutoSave = 5;
645 noMinutesEndAutoSave = Context::getBasicProperty<MInt>(
"noMinutesEndAutoSave", AT_, &noMinutesEndAutoSave);
647 MInt endAutoSaveTime = -1;
648 const char* envJobEndTime = getenv(
"MAIA_JOB_END_TIME");
650 const MString jobEndTime(envJobEndTime);
651 MBool onlyDigits =
true;
652 for(
auto&& character : jobEndTime) {
653 if(!isdigit(character)) {
654 m_log <<
"Warning: the environment variable MAIA_JOB_END_TIME includes "
655 <<
"non-digit characters.\n"
656 <<
"Saving a restart file before the compute job ends is NOT active." << endl;
662 endAutoSaveTime = stoi(jobEndTime);
663 endAutoSaveTime -= noMinutesEndAutoSave * 60;
664 time_t time_point = endAutoSaveTime;
665 m_log <<
"Activated automatic restart file writing at " << ctime(&time_point)
666 <<
" (in Unix time: " << endAutoSaveTime <<
")." << endl;
682 const MBool seedRNGWithTime =
false;
683 if(Context::getBasicProperty<MBool>(
"seedRNGWithTime", AT_, &seedRNGWithTime)) {
685 srand(time(
nullptr));
698 seed = Context::getBasicProperty<MInt>(
"RNGSeed", AT_, &seed);
699 if(seed < 0)
mTerm(1,
"RNGSeed has to be a positive integer.");
700 srand(
static_cast<MUint>(seed));
704 cerr0 <<
"=== Create grid..." << endl;
708#ifndef MAIA_DISABLE_STRUCTURED
711 MBool structuredGridExist =
false;
712 MBool cartGridExist =
false;
715 const MString solverType = Context::getSolverProperty<MString>(
"solvertype", i, AT_);
719 if(!structuredGridExist) {
721 cerr0 <<
"=== Create structured grid..." << endl;
722#ifndef MAIA_DISABLE_STRUCTURED
725 cerr0 <<
"=== done." << endl;
726 structuredGridExist =
true;
731 cerr0 <<
"=== Create Cartesian grid..." << endl;
733 MInt maxNoCells = Context::getBasicProperty<MInt>(
"maxNoCells", AT_);
735 const MInt testNoDomains = Context::getBasicProperty<MInt>(
"noDomains", AT_, 0);
742 cerr0 <<
"noDomain > number of used mpi ranks! Therefore, increasing maxNoCells: " <<
maxNoCells
747 cerr0 <<
"=== done." << endl;
748 cartGridExist =
true;
753 TERMM(1,
"Invalid grid type: " + std::to_string(solverGridType));
756 logDuration(gridTimeStart,
"Create grid");
764 cerr0 <<
"=== Create solvers..." << endl;
768 MBool active =
false;
769 const MString solverType = Context::getSolverProperty<MString>(
"solvertype", i, AT_);
773 ASSERT(structuredGridExist,
"");
774 cerr0 <<
"=== Create structured solver..." << endl;
775#ifndef MAIA_DISABLE_STRUCTURED
776 createSolver<nDim>(gridStructured, i, propertiesGroups, comm);
778 cerr0 <<
"=== done." << endl;
780 ASSERT(cartGridExist,
"");
781 cerr0 <<
"=== Create " << solverType <<
"..." << endl;
782 createSolver<nDim>(grid, i, geometries[i], propertiesGroups, active);
783 cerr0 <<
"=== done." << endl;
785 cerr0 <<
"=== Create gridless solver " << solverType <<
"..." << endl;
786 createSolver<nDim>(i, comm);
787 cerr0 <<
"=== done." << endl;
789 TERMM(1,
"Invalid grid type: " + std::to_string(solverGridType));
792 isActive[i] = active;
793 logDuration(createSolverTimeStart,
"Create solver #" + std::to_string(i));
795 logDuration(createSolversTimeStart,
"Create solvers");
798 delete[] propertiesGroups;
802 cerr0 <<
"=== Create couplers..." << endl;
804 createCoupler<nDim>(i);
806 cerr0 <<
"=== done." << endl;
807 logDuration(couplersTimeStart,
"Create couplers");
810 MInt restartTimeStep = -1;
812 MBool restartFile =
false;
813 restartFile = Context::getBasicProperty<MBool>(
"restartFile", AT_, &restartFile);
822 cerr0 <<
"=== Create grid controller..." << endl;
824 cerr0 <<
"=== done." << endl;
825 logDuration(controllerTimeStart,
"Create grid controller");
829 std::vector<PostProcessingInterface*> pp;
833 cerr0 <<
"=== Init solvers..." << endl;
837 logDuration(initSolverTimeStart,
"Init solver #" + std::to_string(i));
843 logDuration(initSolversTimeStart,
"Init solvers");
844 cerr0 <<
"=== done." << endl;
849 cerr0 <<
"=== Init couplers..." << endl;
853 logDuration(initCouplersTimeStart,
"Init couplers");
859 MBool ignoreDlbTimers =
false;
860 ignoreDlbTimers = Context::getBasicProperty<MBool>(
"ignoreDlbTimers", AT_, &ignoreDlbTimers);
862 if(ignoreDlbTimers) {
863 const MBool performanceOutput = Context::getBasicProperty<MBool>(
"performanceOutput", AT_);
864 TERMM_IF_COND(performanceOutput,
865 "ERROR: ignoreDlbTimers=true should only be used together with performanceOutput=false");
880 cerr0 <<
"=== done." << endl;
882 MBool forceInitialAdaptation =
false;
884 if(
m_solvers[solverId]->forceAdaptation()) {
885 forceInitialAdaptation =
true;
886 cerr0 <<
"=== Forcing Initial adaptation: " << endl;
892 if(!restartFile || forceInitialAdaptation) {
893 cerr0 <<
"=== Initial adaptation..." << endl;
896 RECORD_TIMER_START(t_adaptation);
898 RECORD_TIMER_STOP(t_adaptation);
901 MBool outputInitialAdaptation =
false;
902 outputInitialAdaptation =
903 Context::getBasicProperty<MBool>(
"outputInitialAdaptation", AT_, &outputInitialAdaptation);
904 if(!restartFile && outputInitialAdaptation) {
905 controller.writeRestartFile(
true,
false);
907 cerr0 <<
"=== done." << endl;
912 cerr0 <<
"=== Finalize initialization of solvers and couplers..." << endl;
917 if(geometries[i] !=
nullptr) {
918 geometries[i]->logStatistics();
924 coupler->finalizeSubCoupleInit(i);
926 logDuration(finalizeInitSolverTimeStart,
"Finalize initialization solver #" + std::to_string(i));
928 logDuration(finalizeInitTimeStart,
"Finalize initialization");
933 coupler->finalizeCouplerInit();
935 cerr0 <<
"=== done." << endl;
938 logDuration(finalizeCouplerInitTimeStart,
"Finalize coupler init");
942 cerr0 <<
"=== Init postprocessing..." << endl;
944 pp.push_back(createPostProcessing<nDim>(ppId));
949 ppController->
init();
952 cerr0 <<
"=== done." << endl;
955 MBool outputInitialCondition =
false;
956 outputInitialCondition = Context::getBasicProperty<MBool>(
"outputInitialCondition", AT_, &outputInitialCondition);
957 MBool writePostprocessingPre =
false;
958 writePostprocessingPre = Context::getBasicProperty<MBool>(
"writePostprocessingPre", AT_, &writePostprocessingPre);
960 if((outputInitialCondition && (!restartFile || forceInitialAdaptation)) || writePostprocessingPre) {
961 controller.writeRestartFile(
true, initialBackup);
965 if(controller.updateGridPartitionWorkloads()) {
994 MInt aliveInterval = 0;
995 aliveInterval = Context::getBasicProperty<MInt>(
"aliveInterval", AT_, &aliveInterval);
996 if(aliveInterval < 0) {
997 TERMM(1,
"Alive interval must be >= 0 (is: " + to_string(aliveInterval) +
").");
1000 MFloat lastRunTime = 0.0;
1006 MBool debugOutputAfterBalance =
false;
1007 debugOutputAfterBalance = Context::getBasicProperty<MBool>(
"debugOutputAfterBalance", AT_, &debugOutputAfterBalance);
1010 if(endAutoSaveTime != -1
1012 <= chrono::duration_cast<chrono::seconds>(chrono::system_clock::now().time_since_epoch()).count()) {
1013 finalTimeStep =
true;
1016 logDuration(runTimeStart,
"Full initialization");
1026 MBool advanceTimeStep =
false;
1027 while(!advanceTimeStep) {
1031 MBool force =
false;
1033 if(
m_solvers[solverId]->forceAdaptation()) {
1035 m_log <<
"Solver " << solverId <<
" is forcing a mesh-adaptation at time-step " <<
globalTimeStep << endl;
1042 if(force || controller.isAdaptationTimeStep()) {
1046 RECORD_TIMER_START(t_adaptation);
1047 const MBool wasAdapted = controller.adaptation(force);
1048 RECORD_TIMER_STOP(t_adaptation);
1058 RECORD_TIMER_START(t_preTimeStep);
1060 RECORD_TIMER_STOP(t_preTimeStep);
1063 RECORD_TIMER_START(t_preCouple);
1065 RECORD_TIMER_STOP(t_preCouple);
1068 RECORD_TIMER_START(t_timeStep);
1070 RECORD_TIMER_STOP(t_timeStep);
1075 ppController->
inSolve(finalTimeStep);
1079 RECORD_TIMER_START(t_postTimeStep);
1081 RECORD_TIMER_STOP(t_postTimeStep);
1084 RECORD_TIMER_START(t_postCouple);
1086 RECORD_TIMER_STOP(t_postCouple);
1091 ppController->
inSolve(finalTimeStep);
1101 RECORD_TIMER_START(t_implicitTimeStep);
1105 RECORD_TIMER_STOP(t_implicitTimeStep);
1110 MBool completed =
true;
1117 completed &=
m_solvers[i]->solverConverged();
1126 std::cerr <<
"Warning: one or multiple solvers in a multisolver computation returned a "
1127 "'completed' status, however this information might not be consistent among "
1128 "ranks if there are inactive ranks!"
1136 finalTimeStep = finalTimeStep || completed;
1139 if(endAutoSaveTime != -1
1141 <= chrono::duration_cast<chrono::seconds>(chrono::system_clock::now().time_since_epoch()).count()) {
1142 finalTimeStep =
true;
1145 std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
1147 m_log <<
"Finished end auto save at " << std::ctime(&now) <<
" (in Unix time: " << now <<
")." << endl;
1148 time_t remaining_time = endAutoSaveTime - now;
1149 m_log <<
"Job will end at" << std::ctime(&remaining_time) <<
" (in Unix time: " << remaining_time <<
")." << endl;
1152#ifndef DISABLE_OUTPUT
1153 const MBool forceOutput =
false;
1154 RECORD_TIMER_START(t_solutionOutput);
1158 controller.writeRestartFile(finalTimeStep, backup);
1167 m_solvers[i]->saveSolverSolution(forceOutput, finalTimeStep);
1177 RECORD_TIMER_STOP(t_solutionOutput);
1204 RECORD_TIMER_START(t_balance);
1205 if(controller.isDlbTimeStep()) {
1209 const MBool wasBalanced = controller.balance(
false, finalTimeStep,
false);
1210 RECORD_TIMER_STOP(t_balance);
1222 const MFloat runTimePerStep =
1224 lastRunTime = runTimeTotal;
1228 printf(
"#t/s: %8d | Run time: %.4e s | Time/step: %.4e s\n",
globalTimeStep, runTimeTotal, runTimePerStep);
1231 if(debugOutputAfterBalance && wasBalanced) {
1233 printf(
"------------Write debug restart after balance------------");
1235 controller.writeRestartFile(
true,
false);
1239 if(completed || finalTimeStep) {
1250 controller.savePartitionFile();
1261 RECORD_TIMER_STOP(t_totalMainLoop);
1271 MBool useNonSpecifiedRestartFile =
false;
1272 useNonSpecifiedRestartFile =
1273 Context::getBasicProperty<MBool>(
"useNonSpecifiedRestartFile", AT_, &useNonSpecifiedRestartFile);
1275 if(!useNonSpecifiedRestartFile) {
1277 return Context::getBasicProperty<MInt>(
"restartTimeStep", AT_);
1286 if(
m_solvers[solver]->hasRestartTimeStep()) {
1287 timeStep =
m_solvers[solver]->determineRestartTimeStep();
1293 m_log <<
"Determined global time step from restart file: " << timeStep << std::endl;
1307 MBool tmpFalse =
false;
1313 propertiesGroups[
LEVELSETMB] = Context::getBasicProperty<MBool>(
"levelSetMb", AT_, &tmpFalse);
1314 propertiesGroups[
LEVELSET] = Context::getBasicProperty<MBool>(
"levelSet", AT_, &tmpFalse);
1315 propertiesGroups[
LB] = Context::getBasicProperty<MBool>(
"lb", AT_, &tmpFalse);
1316 propertiesGroups[
LS_SOLVER] = Context::getBasicProperty<MBool>(
"lsSolver", AT_, &tmpFalse);
1317 propertiesGroups[
COMBUSTION] = Context::getBasicProperty<MBool>(
"combustion", AT_, &tmpFalse);
1318 propertiesGroups[
DG] = Context::getBasicProperty<MBool>(
"DG", AT_, &tmpFalse);
1319 propertiesGroups[
LS_RANS] = Context::getBasicProperty<MBool>(
"levelSetRans", AT_, &tmpFalse);
1321 MString couplerTypeDefault =
"";
1322 MString couplerType = Context::getBasicProperty<MString>(
"couplerType_0", AT_, &couplerTypeDefault);
1323 propertiesGroups[
LEVELSET_LB] = couplerType ==
"COUPLER_LS_LB";
1326 cerr <<
"FV-MB is " << ((propertiesGroups[
LEVELSETMB]) ?
"on" :
"off") << endl;
1327 cerr <<
"FV-LS is " << ((propertiesGroups[
LEVELSET]) ?
"on" :
"off") << endl;
1328 cerr <<
"LB is " << ((propertiesGroups[
LB]) ?
"on" :
"off") << endl;
1329 cerr <<
"LB LS is " << ((propertiesGroups[
LEVELSET_LB]) ?
"on" :
"off") << endl;
1330 cerr <<
"LS SOLVER is " << ((propertiesGroups[
LS_SOLVER]) ?
"on" :
"off") << endl;
1331 cerr <<
"COMBUSTION is " << ((propertiesGroups[
COMBUSTION]) ?
"on" :
"off") << endl;
1332 cerr <<
"DG is " << ((propertiesGroups[
DG]) ?
"on" :
"off") << endl;
1333 cerr <<
"LS-RANS is " << ((propertiesGroups[
LS_RANS]) ?
"on" :
"off") << endl;
1336 return propertiesGroups;
1367 const MString solverType = Context::getSolverProperty<MString>(
"solvertype", solverId, AT_);
1382 noSpecies = Context::getSolverProperty<MInt>(
"noSpecies", solverId, AT_, &noSpecies);
1385 fvSystemEquations =
string2enum(Context::getSolverProperty<MString>(
"fvSystemEquations", solverId, AT_));
1389 ransMethod = Context::getSolverProperty<MString>(
"ransMethod", solverId, AT_);
1392 switch(fvSystemEquations) {
1398 make_unique<FvCartesianSolverXD<nDim, FvSysEqnRANS<nDim, RANSModelConstants<RANS_SA_DV>>>>(
1399 solverId, noSpecies, propertiesGroups, *gridProxy, *geometry, gridProxy->
mpiComm());
1404 make_unique<FvCartesianSolverXD<nDim, FvSysEqnRANS<nDim, RANSModelConstants<RANS_FS>>>>(
1405 solverId, noSpecies, propertiesGroups, *gridProxy, *geometry, gridProxy->
mpiComm());
1410 make_unique<FvCartesianSolverXD<nDim, FvSysEqnRANS<nDim, RANSModelConstants<RANS_KOMEGA>>>>(
1411 solverId, noSpecies, propertiesGroups, *gridProxy, *geometry, gridProxy->
mpiComm());
1415 TERMM(1,
"Unknown RANS model for solver " + to_string(solverId));
1421 IF_CONSTEXPR(nDim == 2)
1422 mTerm(1, AT_,
"FVSysEqnEEGas is not tested for 2D at all and probably does not make much sense!");
1423 else m_solvers[solverId] = make_unique<FvCartesianSolverXD<nDim, FvSysEqnEEGas<nDim>>>(
1424 solverId, noSpecies, propertiesGroups, *gridProxy, *geometry, gridProxy->
mpiComm());
1428 m_solvers[solverId] = make_unique<FvCartesianSolverXD<nDim, FvSysEqnNS<nDim>>>(
1429 solverId, noSpecies, propertiesGroups, *gridProxy, *geometry, gridProxy->
mpiComm());
1433 m_solvers[solverId] = make_unique<FvCartesianSolverXD<nDim, FvSysEqnDetChem<nDim>>>(
1434 solverId, noSpecies, propertiesGroups, *gridProxy, *geometry, gridProxy->
mpiComm());
1438 TERMM(1,
"Unsupported system of equations.");
1444 m_solvers[solverId] = make_unique<typename FvApeSolverXD<nDim>::type>(solverId, 0, propertiesGroups, *gridProxy,
1445 *geometry, gridProxy->
mpiComm());
1450 noSpecies = Context::getSolverProperty<MInt>(
"noSpecies", solverId, AT_, &noSpecies);
1453 fvSystemEquations =
string2enum(Context::getSolverProperty<MString>(
"fvSystemEquations", solverId, AT_));
1455 switch(fvSystemEquations) {
1458 make_unique<FvMbCartesianSolverXD<nDim, FvSysEqnRANS<nDim, RANSModelConstants<RANS_SA_DV>>>>(
1459 solverId, noSpecies, propertiesGroups, *gridProxy, *geometry, gridProxy->
mpiComm());
1463 m_solvers[solverId] = make_unique<FvMbCartesianSolverXD<nDim, FvSysEqnNS<nDim>>>(
1464 solverId, noSpecies, propertiesGroups, *gridProxy, *geometry, gridProxy->
mpiComm());
1468 TERMM(1,
"Unsupported system of equations.");
1473 const MInt dgSystemEquations =
1474 string2enum(Context::getSolverProperty<MString>(
"dgSystemEquations", solverId, AT_));
1475 switch(dgSystemEquations) {
1477 m_solvers[solverId] = make_unique<DgCartesianSolver<nDim, DgSysEqnAcousticPerturb<nDim>>>(
1478 solverId, *gridProxy, *geometry, gridProxy->
mpiComm());
1482 m_solvers[solverId] = make_unique<DgCartesianSolver<nDim, DgSysEqnLinearScalarAdv<nDim>>>(
1483 solverId, *gridProxy, *geometry, gridProxy->
mpiComm());
1487 TERMM(1,
"Unsupported system of equations.");
1497 m_solvers[solverId] = make_unique<FcSolver<nDim>>(solverId, *gridProxy, *geometry, gridProxy->
mpiComm());
1501 IF_CONSTEXPR(nDim == 3) {
1502 m_solvers[solverId] = make_unique<LPT<nDim>>(solverId, *gridProxy, *geometry, gridProxy->
mpiComm());
1505 TERMM(-1,
"LPT not currently supported in 2D");
1510 m_solvers[solverId] = make_unique<RigidBodies<nDim>>(solverId, *gridProxy, *geometry, gridProxy->
mpiComm());
1514 m_solvers[solverId] = make_unique<PostData<nDim>>(solverId, *gridProxy, *geometry, gridProxy->
mpiComm());
1515 if(
m_postDataSolverId != -1) TERMM(1,
"Currently not more than 1 PostData possible!");
1520 TERMM(1,
"Unknown solver type '" + solverType +
"', exiting ... ");
1524 m_log <<
"Created solver #" << std::to_string(solverId) <<
": " << solverType
1525 <<
m_solvers[solverId]->getIdentifier(
false,
" with alias: ",
"") << std::endl;
1532#ifndef MAIA_DISABLE_STRUCTURED
1535 const MPI_Comm comm) {
1540 const MString solverType = Context::getSolverProperty<MString>(
"solvertype", solverId, AT_);
1546 make_unique<typename FvStructuredSolverXD<nDim>::type>(solverId, grid, propertiesGroups, comm);
1550 TERMM(1,
"Unknown solver type '" + solverType +
"', exiting ... ");
1554 m_log <<
"Created solver #" << std::to_string(solverId) <<
": " << solverType
1555 <<
m_solvers[solverId]->getIdentifier(
false,
" with alias: ",
"") << std::endl;
1566 const MString solverType = Context::getSolverProperty<MString>(
"solvertype", solverId, AT_);
1570 m_solvers[solverId] = make_unique<AcaSolver<nDim>>(solverId, comm);
1574 TERMM(1,
"Unknown solver type '" + solverType +
"', exiting ... ");
1578 m_log <<
"Created solver #" << std::to_string(solverId) <<
": " << solverType
1579 <<
m_solvers[solverId]->getIdentifier(
false,
" with alias: ",
"") << std::endl;
1591 const MString couplerType = Context::getBasicProperty<MString>(
"couplerType_" + std::to_string(couplerId), AT_);
1593 cerr0 <<
"=== Create coupler #" << couplerId <<
" " << couplerType << std::endl;
1598 std::vector<MInt> solversToCouple;
1599 for(
MInt solver = 0; solver < solverToCoupleLength; solver++) {
1600 solversToCouple.push_back(-1);
1602 solversToCouple[solver] =
1603 Context::getBasicProperty<MInt>(
"solversToCouple_" + std::to_string(couplerId), AT_,
nullptr, solver);
1604 if(solversToCouple[solver] < 0 || solversToCouple[solver] >=
m_noSolvers) {
1605 TERMM(1,
"Invalid solver id: " + std::to_string(solversToCouple[solver]));
1608 TERMM(1,
"solversToCouple_" + std::to_string(couplerId) +
" has to be specified!");
1616 const MInt nDist = Context::getSolverProperty<MInt>(
"noDistributions", solversToCouple[0], AT_);
1621 const auto dgSolver =
1623 m_couplers[couplerId] = make_unique<LbDgApe<3, 19, SysEqnLb>>(couplerId, lbSolver, dgSolver);
1629 const auto dgSolver =
1631 m_couplers[couplerId] = make_unique<LbDgApe<3, 27, SysEqnLb>>(couplerId, lbSolver, dgSolver);
1635 mTerm(1,
"Unknown number of distributions! Only working for q19 and q27, yet.");
1644 string2enum(Context::getSolverProperty<MString>(
"fvSystemEquations", solversToCouple[1], AT_));
1646 switch(fvSystemEquations) {
1649 make_unique<typename LsFvMbXD<nDim, FvSysEqnRANS<nDim, RANSModelConstants<RANS_SA_DV>>>::type>(
1651 static_cast<typename LsCartesianSolverXD<nDim>::type*
>(
m_solvers[solversToCouple[0]].get()),
1658 m_couplers[couplerId] = make_unique<typename LsFvMbXD<nDim, FvSysEqnNS<nDim>>::type>(
1660 static_cast<typename LsCartesianSolverXD<nDim>::type*
>(
m_solvers[solversToCouple[0]].get()),
1671 ransMethod = Context::getSolverProperty<MString>(
"ransMethod", solversToCouple[0], AT_);
1676 m_couplers[couplerId] = make_unique<FvZonalRTV<nDim, FvSysEqnRANS<nDim, RANSModelConstants<RANS_SA_DV>>>>(
1684 m_couplers[couplerId] = make_unique<FvZonalRTV<nDim, FvSysEqnRANS<nDim, RANSModelConstants<RANS_FS>>>>(
1692 TERMM(1,
"Unknown RANS model for solver " + to_string(solversToCouple[0]));
1700 ransMethod = Context::getSolverProperty<MString>(
"ransMethod", solversToCouple[0], AT_);
1705 m_couplers[couplerId] = make_unique<FvZonalSTG<nDim, FvSysEqnRANS<nDim, RANSModelConstants<RANS_SA_DV>>>>(
1713 m_couplers[couplerId] = make_unique<FvZonalSTG<nDim, FvSysEqnRANS<nDim, RANSModelConstants<RANS_FS>>>>(
1721 TERMM(1,
"Unknown RANS model for solver " + to_string(solversToCouple[0]));
1730 string2enum(Context::getSolverProperty<MString>(
"fvSystemEquations", solversToCouple[1], AT_));
1732 switch(fvSystemEquations) {
1735 make_unique<CouplerFvMbZonal<nDim, FvSysEqnRANS<nDim, RANSModelConstants<RANS_SA_DV>>>>(
1745 m_couplers[couplerId] = make_unique<CouplerFvMbZonal<nDim, FvSysEqnNS<nDim>>>(
1759 string2enum(Context::getSolverProperty<MString>(
"fvSystemEquations", solversToCouple[1], AT_));
1761 switch(fvSystemEquations) {
1764 make_unique<FvCartesianInterpolation<nDim, FvSysEqnRANS<nDim, RANSModelConstants<RANS_SA_DV>>,
1789 auto fvSolvers = std::vector<FvCartesianSolverXD<nDim, FvSysEqnNS<nDim>>*>{};
1790 for(
MInt s = 0; s < (
MInt)solversToCouple.size(); s++) {
1791 fvSolvers.push_back(
1794 m_couplers[couplerId] = make_unique<CouplerFvMultilevel<nDim, FvSysEqnNS<nDim>>>(couplerId, fvSolvers);
1801 auto fvSolvers = std::vector<FvCartesianSolverXD<nDim, FvSysEqnNS<nDim>>*>{};
1802 for(
MInt s = 0; s < (
MInt)solversToCouple.size(); s++) {
1803 fvSolvers.push_back(
1807 make_unique<CouplerFvMultilevelInterpolation<nDim, FvSysEqnNS<nDim>>>(couplerId, fvSolvers);
1812 m_couplers[couplerId] = make_unique<DgCcAcousticPerturb<nDim, FvSysEqnNS<nDim>>>(
1820 nDist = Context::getSolverProperty<MInt>(
"noDistributions", solversToCouple[1], AT_, &nDist);
1824 auto lsSolver =
static_cast<typename LsCartesianSolverXD<2>::type*
>(
m_solvers[solversToCouple[0]].get());
1827 m_couplers[couplerId] = make_unique<LsLb<2, 9, SysEqnLb>>(couplerId, lsSolver, lbSolver);
1832 auto lsSolver =
static_cast<typename LsCartesianSolverXD<3>::type*
>(
m_solvers[solversToCouple[0]].get());
1835 m_couplers[couplerId] = make_unique<LsLb<3, 19, SysEqnLb>>(couplerId, lsSolver, lbSolver);
1840 auto lsSolver =
static_cast<typename LsCartesianSolverXD<3>::type*
>(
m_solvers[solversToCouple[0]].get());
1843 m_couplers[couplerId] = make_unique<LsLb<3, 27, SysEqnLb>>(couplerId, lsSolver, lbSolver);
1848 mTerm(1,
"Unknown number of distributions!");
1857 nDist = Context::getSolverProperty<MInt>(
"noDistributions", solversToCouple[0], AT_, &nDist);
1864 m_couplers[couplerId] = make_unique<LbRb<2, 9, SysEqnLb>>(couplerId, lbSolver, rigidBodies);
1872 m_couplers[couplerId] = make_unique<LbRb<3, 19, SysEqnLb>>(couplerId, lbSolver, rigidBodies);
1880 m_couplers[couplerId] = make_unique<LbRb<3, 27, SysEqnLb>>(couplerId, lbSolver, rigidBodies);
1885 mTerm(1,
"Unknown number of distributions!");
1893 nDist = Context::getSolverProperty<MInt>(
"noDistributions", solversToCouple[1], AT_, &nDist);
1897 IF_CONSTEXPR(nDim == 3) {
mTerm(1,
"LPT not supported in 2D!"); }
1903 auto particleSolver =
static_cast<LPT<3>*
>(
m_solvers[solversToCouple[1]].get());
1904 m_couplers[couplerId] = make_unique<LbLpt<3, 19, SysEqnLb>>(couplerId, particleSolver, lbSolver);
1911 auto particleSolver =
static_cast<LPT<3>*
>(
m_solvers[solversToCouple[1]].get());
1912 m_couplers[couplerId] = make_unique<LbLpt<3, 27, SysEqnLb>>(couplerId, particleSolver, lbSolver);
1917 mTerm(1,
"Unknown number of distributions!");
1925 nDist = Context::getSolverProperty<MInt>(
"noDistributions", solversToCouple[1], AT_, &nDist);
1929 auto lsSolver =
static_cast<typename LsCartesianSolverXD<2>::type*
>(
m_solvers[solversToCouple[0]].get());
1932 m_couplers[couplerId] = make_unique<LsLbSurface<2, 9, SysEqnLb>>(couplerId, lsSolver, lbSolver);
1937 auto lsSolver =
static_cast<typename LsCartesianSolverXD<3>::type*
>(
m_solvers[solversToCouple[0]].get());
1940 m_couplers[couplerId] = make_unique<LsLbSurface<3, 19, SysEqnLb>>(couplerId, lsSolver, lbSolver);
1945 auto lsSolver =
static_cast<typename LsCartesianSolverXD<3>::type*
>(
m_solvers[solversToCouple[0]].get());
1948 m_couplers[couplerId] = make_unique<LsLbSurface<3, 27, SysEqnLb>>(couplerId, lsSolver, lbSolver);
1953 mTerm(1,
"Unknown number of distributions!");
1961 auto lsSolver =
static_cast<typename LsCartesianSolverXD<nDim>::type*
>(
m_solvers[solversToCouple[0]].get());
1965 string2enum(Context::getSolverProperty<MString>(
"fvSystemEquations", solversToCouple[1], AT_));
1967 switch(fvSystemEquations) {
1973 make_unique<typename CouplingLsFvXD<nDim, FvSysEqnRANS<nDim, RANSModelConstants<RANS_SA_DV>>>::type>(
1974 couplerId, lsSolver, fvSolvers);
1980 make_unique<typename CouplingLsFvXD<nDim, FvSysEqnNS<nDim>>::type>(couplerId, lsSolver, fvSolvers);
1988 auto lsSolverId = solversToCouple[0];
1989 auto fvSolverId = solversToCouple[1];
1991 m_couplers[couplerId] = make_unique<typename LsFvCombustionXD<nDim, FvSysEqnNS<nDim>>::type>(
1993 static_cast<typename LsCartesianSolverXD<nDim>::type*
>(
m_solvers[lsSolverId].get()),
2002 string2enum(Context::getSolverProperty<MString>(
"fvSystemEquations", solversToCouple[1], AT_));
2004 if(nDim != 3)
mTerm(1, AT_,
"LB-FV-Euler-Euler-Multiphase only implemented for nDim = 3");
2006 switch(fvSystemEquations) {
2008 mTerm(1, AT_,
"RANS not supported in combination with LB-FV-EE-Multiphase!");
2013 nDist = Context::getSolverProperty<MInt>(
"noDistributions", solversToCouple[0], AT_, &nDist);
2017 mTerm(1,
"nDist = 9 not supported with EE Multiphase!");
2027 m_couplers[couplerId] = make_unique<CouplerLbFvEEMultiphase<3, 19, SysEqnLb, FvSysEqnEEGas<3>>>(
2028 couplerId, lbSolver, fvSolver);
2038 m_couplers[couplerId] = make_unique<CouplerLbFvEEMultiphase<3, 27, SysEqnLb, FvSysEqnEEGas<3>>>(
2039 couplerId, lbSolver, fvSolver);
2044 mTerm(1,
"Unknown number of distributions!");
2051 mTerm(1, AT_,
"This SysEqn is not supported in combination with LB-FV-EE-Multiphase!");
2060 nDist = Context::getSolverProperty<MInt>(
"noDistributions", solversToCouple[0], AT_, &nDist);
2062 const MInt tempNDist = Context::getSolverProperty<MInt>(
"noDistributions", solversToCouple[1], AT_, &nDist);
2063 if(nDist != tempNDist)
mTerm(1, AT_,
"Can't couple LB solvers with different noDistributions!");
2068 std::vector<LbSolverDxQy<2, 9, SysEqnLb>*> lbSolvers;
2071 m_couplers[couplerId] = make_unique<CouplerLbLb<2, 9, SysEqnLb>>(couplerId, lbSolvers);
2076 std::vector<LbSolverDxQy<3, 19, SysEqnLb>*> lbSolvers;
2079 m_couplers[couplerId] = make_unique<CouplerLbLb<3, 19, SysEqnLb>>(couplerId, lbSolvers);
2084 std::vector<LbSolverDxQy<3, 27, SysEqnLb>*> lbSolvers;
2087 m_couplers[couplerId] = make_unique<CouplerLbLb<3, 27, SysEqnLb>>(couplerId, lbSolvers);
2091 mTerm(1,
"Unknown number of distributions!");
2102 string2enum(Context::getSolverProperty<MString>(
"fvSystemEquations", solversToCouple[1], AT_));
2104 switch(fvSystemEquations) {
2110 make_unique<CouplerFvParticle<nDim, FvSysEqnRANS<nDim, RANSModelConstants<RANS_SA_DV>>>>(
2111 couplerId, particleSolver, fvSolver);
2113 mTerm(1, AT_,
"FV Particle not supported in 2D!");
2123 make_unique<CouplerFvParticle<nDim, FvSysEqnNS<nDim>>>(couplerId, particleSolver, fvSolver);
2125 mTerm(1, AT_,
"FV Particle not supported in 2D!");
2135 TERMM(1,
"Unknown coupler type '" + couplerType +
"', exiting ... ");
2139 m_log <<
"Created coupler #" << std::to_string(couplerId) <<
": " << couplerType <<
" for solvers";
2140 for(
MInt solver = 0; solver < 2; solver++) {
2141 m_log <<
" " << solversToCouple[solver];
2156 const MString recipeType = Context::getBasicProperty<MString>(
"executionRecipe", AT_);
2172 TERMM(1,
"Unknown execution recipe '" + recipeType +
"', exiting ... ");
2176 m_log <<
"Created execution recipe: " << recipeType <<
" for " <<
m_noSolvers <<
" solvers" << std::endl;
2191 const MString postprocessingType =
2192 Context::getBasicProperty<MString>(
"postProcessingType_" + std::to_string(postprocessingId), AT_);
2196 const MInt postprocessingSolverId =
2197 Context::getBasicProperty<MInt>(
"postProcessingSolverIds", AT_,
nullptr, postprocessingId);
2203 cerr <<
"\033[0;31m#### WARNING:\033[0m You are using a postprocessing routine without a PostData Solver!"
2217 string2enum(Context::getSolverProperty<MString>(
"fvSystemEquations", postprocessingSolverId, AT_));
2219 switch(fvSystemEquations) {
2225 m_solvers[postprocessingSolverId].get()));
2229 IF_CONSTEXPR(nDim == 2)
2230 mTerm(1, AT_,
"FVSysEqnEEGas is not tested for 2D at all and probably does not make much sense!");
2252 TERMM(1,
"Unsupported system of equations.");
2257 const MInt dgSystemEquations =
2258 string2enum(Context::getSolverProperty<MString>(
"dgSystemEquations", postprocessingSolverId, AT_));
2259 switch(dgSystemEquations) {
2265 m_solvers[postprocessingSolverId].get()));
2273 m_solvers[postprocessingSolverId].get()));
2277 TERMM(1,
"Unsupported system of equations.");
2290 string2enum(Context::getSolverProperty<MString>(
"fvSystemEquations", postprocessingSolverId, AT_));
2292 switch(fvSystemEquations) {
2299 m_solvers[postprocessingSolverId].get()),
2302 mTerm(1, AT_,
"LPT not supported in 2D!");
2314 mTerm(1, AT_,
"LPT not supported in 2D!");
2319 TERMM(1,
"Unsupported system of equations.");
2330 mTerm(1, AT_,
"LPT not supported in 2D!");
2335 TERMM(1,
"Unknown postprocessing type '" + postprocessingType +
"', exiting ... ");
2339 m_log <<
"Created postprocessing #" << std::to_string(postprocessingId) <<
": " << postprocessingType <<
" for solver"
2340 <<
" " << postprocessingSolverId << std::endl;
2356 if(isSamplingStep) {
2374 MInt timerIndex = 0;
2375 for(
MInt j = 0; j < noSolversAndCouplers; j++) {
2377 const MInt noTimers = (isSolver) ?
m_solvers[j]->noSolverTimers(allTimings)
2381 std::vector<std::pair<MString, MFloat>> timings{};
2382 (isSolver) ?
m_solvers[j]->getSolverTimings(timings, allTimings)
2385 if((
MInt)timings.size() != noTimers) {
2386 TERMM(1,
"Wrong number of solver timings returned by getSolverTimings(): is " + std::to_string(timings.size())
2387 +
", should be " + std::to_string(noTimers));
2390 for(
MInt i = 0; i < noTimers; i++) {
2391 const MFloat timing = timings[i].second;
2392 const MString name = timings[i].first;
2397 TERMM(1,
"Timer name does not match.");
2406 if(isSamplingStep) {
2443 if(noTimings == 0) {
2448 stringstream fileName;
2454 readFile.open(fileName.str());
2456 stringstream fileNameNew;
2458 std::rename(fileName.str().c_str(), fileNameNew.str().c_str());
2462 ParallelIo file(fileName.str(), PIO_REPLACE, MPI_COMM_WORLD);
2463 file.defineArray(PIO_INT,
"timeStep", noTimings);
2470 file.defineArray(PIO_INT,
"domainInfo", 2, &dimSizesInfo[0]);
2471 file.setAttribute(
"domain index",
"dim_0",
"domainInfo");
2472 file.setAttribute(
"information index",
"dim_1",
"domainInfo");
2474 for(
MInt i = 0; i < noInfo; i++) {
2475 std::stringstream var;
2477 file.setAttribute(
m_domainInfo[i].first, var.str(),
"domainInfo");
2481 ParallelIo::size_type dimSizesTimings[] = {
globalNoDomains(), noTimings, noValues};
2483 file.defineArray(PIO_FLOAT,
"timings", 3, &dimSizesTimings[0]);
2485 file.setAttribute(
"domain index",
"dim_0",
"timings");
2486 file.setAttribute(
"time step index",
"dim_1",
"timings");
2487 file.setAttribute(
"timings index",
"dim_2",
"timings");
2489 for(
MInt i = 0; i < noValues; i++) {
2490 std::stringstream var;
2497 for(
MInt i = 0; i < noInfo; i++) {
2503 for(
MInt i = 0; i < noTimings; i++) {
2504 for(
MInt j = 0; j < noValues; j++) {
2511 file.setOffset(noTimings, 0);
2513 file.setOffset(0, 0);
2519 file.writeArray(&info[0],
"domainInfo");
2523 file.writeArray(&data[0],
"timings");
2546template void Application::run<2>();
2547template void Application::run<3>();
std::vector< MInt > m_solverTimingsTimeStep
MInt m_noGlobalSolverTimers
MInt gridType(const MString solverType)
Return the type of grid for a given solver type.
std::vector< MString > m_solverTimingsNames
MInt m_restartBackupInterval
The number of timesteps before executing a restart-backup.
MBool m_writeAllSolverTimings
Switch timings mode between ALL timings (default) and a reduced/essential timings mode.
std::vector< std::unique_ptr< Solver > > m_solvers
The list of solvers.
void cleanUp()
call cleanup functions in all solvers and couplers before returning from the application
ExecutionRecipe * createRecipe()
This function handels the creation of the execution recipe.
static MBool * readPropertiesGroups()
Reads in the properties groups.
void collectTimingsAndSolverInformation(const MBool finalTimeStep)
Collect the timings of all solvers and domain decomposition information.
MInt m_solverTimingsWriteInterval
Write interval for timings.
MBool m_ppAfterTS
post-processing in-solve position
std::vector< std::unique_ptr< Coupling > > m_couplers
The list of couplers.
std::vector< MFloat > m_solverTimingsPrevTime
MInt m_solverTimingsSampleInterval
Sampling interval for timings.
MInt m_noPostProcessing
The number of postprocessing solvers.
MInt m_maxIterations
Maximum number of iterations.
MBool m_initialAdaptation
Initial adaptation.
void createCoupler(MInt)
This function handels the creation of new couplers.
std::vector< std::vector< MFloat > > m_solverTimings
MBool m_displayMemoryStatistics
Memory statistics controller.
MInt m_noCouplers
The number of couplers.
MInt getInitialTimeStep(const MBool restartFile, const MPI_Comm comm)
Return the initial global time step.
void storeTimingsAndSolverInformation(const MBool finalTimeStep)
Store timings for all solvers and domain decomposition information on all domains.
MInt m_noSolvers
The number of solvers.
std::vector< std::pair< MString, MInt > > m_domainInfo
MBool m_writeSolverTimings
Solver/coupler timings for performance evaluations.
void initTimings()
Initialize the collection of solver/coupler timings for performance evaluations.
const MInt m_maxNoSolverTimings
void createSolver(CartesianGrid< nDim > *grid, const MInt solverId, Geometry< nDim > *geometry, MBool *propertiesGroup, MBool &isActive)
This function handels the creation of new solvers.
PostProcessingInterface * createPostProcessing(MInt)
This function handels the creation of the Postprocessing classes.
Application()
Gets initial data from the property file and creates the solvers and methods.
static MInt propertyLength(const MString &name, MInt solverId=m_noSolvers)
Returns the number of elements of a property.
static MBool propertyExists(const MString &name, MInt solver=m_noSolvers)
This function checks if a property exists in general.
void createDlbTimers(const MInt noTimers, const MBool ignore=false)
Create the given number of DLB timers.
void enableAllDlbTimers(const MBool *const wasEnabled=nullptr)
Enable all DLB timers (or those given by the array wasEnabled)
void disableAllDlbTimers(MBool *const wasEnabled=nullptr)
Disable all (enabled) DLB timers.
Base recipe provides public interface to Application.
virtual void postTimeStep() final
: Calls each solvers postTimeStep - might be empty
virtual void postCouple() final
: Calls each couplers postCouple - might be empty
MBool callAdaptation() const
virtual void preCouple() final
: Calls each couplers preCouple - might be empty
virtual MBool updateCallOrder()
virtual void preTimeStep() final
: Calls each solvers preTimeStep - might be empty
virtual void timeStep()
: Single solver time step function. Calls solutionStep() of the specific solver
2D structured solver class
3D structured solver class
This class represents all LB models.
static std::unique_ptr< LsCartesianSolver< nDim > > create(MInt solverId_, const MBool *propertiesGroups, GridProxy &gridProxy_, Geometry< nDim > &geometry_, const MPI_Comm comm)
Factory method for LsCartesianSolver.
void setStep(const MInt step)
void inSolve(MBool finalTimeStep)
This class is a ScratchSpace.
MBool isActive() const
Return whether the solver is active on the current domain.
static std::unique_ptr< Solver > create(const MInt solverId, maia::grid::Proxy< nDim > &gridProxy, Geometry< nDim > &geometry, const MPI_Comm comm)
MInt string2enum(MString theString)
This global function translates strings in their corresponding enum values (integer values)....
@ COUPLER_FV_MULTILEVEL_INTERPOLATION
@ COUPLER_LS_FV_COMBUSTION
@ COUPLER_LB_FV_EE_MULTIPHASE
@ COUPLER_CARTESIAN_INTERPOLATION
@ MAIA_DISCONTINUOUS_GALERKIN
@ DG_SYSEQN_ACOUSTICPERTURB
@ DG_SYSEQN_LINEARSCALARADV
void mTerm(const MInt errorCode, const MString &location, const MString &message)
void writeMemoryStatistics(const MPI_Comm comm, const MInt noDomains, const MInt domainId, const MString at, const MString comment)
Write memory statistics.
const MString const MString & message
void createDir(const std::string &dir)
MInt globalNoDomains()
Return global number of domains.
MInt globalDomainId()
Return global domain id.
MBool g_dynamicLoadBalancing
std::basic_string< char > MString
int MPI_Allreduce(const void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Allreduce
int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm, const MString &name, const MString &varname)
same as MPI_Bcast
void const MInt const MInt const MInt const MInt maxNoCells
DlbTimerController g_dlbTimerController
Namespace for auxiliary functions/classes.
PARALLELIO_DEFAULT_BACKEND ParallelIo
void logDuration_(const MFloat timeStart, const MString module, const MString comment, const MPI_Comm comm, const MInt domainId, const MInt noDomains)
Output the min/max/average duration of a code section over the ranks in a communicator Note: only use...