MAIA bb96820c
Multiphysics at AIA
Loading...
Searching...
No Matches
executionrecipe.h
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
8#ifndef EXECUTIONRECIPE_H_
9#define EXECUTIONRECIPE_H_
10
11#include <algorithm>
12#include <vector>
13
14#include "IO/context.h"
15#include "UTIL/functions.h"
16
17#include "COUPLER/coupling.h"
18#include "solver.h"
19
22 public:
23 ExecutionRecipe(std::vector<std::unique_ptr<Solver>>* const solvers,
24 std::vector<std::unique_ptr<Coupling>>* const couplers)
25 : m_solvers(solvers), m_couplers(couplers) {
26 // This function initializes function pointers to the preTimeStep, solutionStep
27 // and postTimeStep functions of each solver
29 }
30
31 // public functions calle from the run-loop:
32
33 virtual void preTimeStep() final;
34 virtual void timeStep();
35 virtual void postTimeStep() final;
36
37 virtual void preCouple() final;
38 virtual void postCouple() final;
39
40 virtual MBool updateCallOrder() { return true; };
41
42 MInt a_step() const { return m_step; }
43 MInt& a_step() { return m_step; }
44
46
47 protected:
48 void readCallOrder();
50
52 void setCouplerStatus(const MInt couplerId, const MBool active);
53
54 MInt noSolvers() const { return m_solvers->size(); }
55 MInt noCouplers() const { return m_couplers->size(); }
56
57 void nextStep() { ++m_step; }
58
59 MBool solverOrder(const MInt solverId) const { return m_solverOrder[m_step][solverId]; }
60
61 MBool couplerOrder(const MInt couplerId) const { return m_couplerOrder[m_step][couplerId]; }
62
64
65 MInt maxNoSteps() const { return m_maxNoSteps; }
66
67 MBool solutionStep(const MInt solverId) { return m_solutionStep[solverId](); }
68
69 void preSolutionStep(const MInt solverId, const MInt mode) { return m_preSolutionStep[solverId](mode); }
70
71 MBool postSolutionStep(const MInt solverId) { return m_postSolutionStep[solverId](); }
72
73 void subCouple(const MInt couplerId, const MInt step, const MInt solverId, std::vector<MBool>& solverCompleted) {
74 m_subCouple[couplerId](step, solverId, solverCompleted);
75 }
76
77 const std::vector<std::unique_ptr<Solver>>* a_solvers() const { return m_solvers; }
78
79 const std::vector<std::unique_ptr<Coupling>>* a_couplers() const { return m_couplers; }
80
82
83 std::map<MInt, MInt> m_swapSolverIds{};
84
85 MInt swapedSolverId(const MInt oldSolverId) {
86 // switch solver order during execution, this becomes necessary during intraStep-coupling,
87 // when however one solver depends on a certain solver initialisation, thus prescrining the
88 // solverId-oder, but during the time-step the order needs to be the other way around!
89
90 ASSERT(!m_swapSolverIds.empty(), "");
91
92 MInt solverId = oldSolverId;
93 auto it = m_swapSolverIds.find(solverId);
94 if(it != m_swapSolverIds.end()) {
95 solverId = it->second;
96 }
97
98 ASSERT(solverOrder(oldSolverId) == solverOrder(solverId), "");
99
100 return solverId;
101 }
102
103 void startLoadTimer(const MInt solverId) { m_solvers->at(solverId)->startLoadTimer(AT_); }
104
105 void stopLoadTimer(const MInt solverId) { m_solvers->at(solverId)->stopLoadTimer(AT_); }
106
107 MBool solverIsActive(const MInt solverId) { return m_solvers->at(solverId)->isActive(); }
108
109 private:
110 const std::vector<std::unique_ptr<Solver>>* const m_solvers;
111 const std::vector<std::unique_ptr<Coupling>>* const m_couplers;
112
113 std::vector<std::function<void()>> m_preStep;
114 std::vector<std::function<void(const MInt)>> m_preSolutionStep;
115 std::vector<std::function<MBool()>> m_solutionStep;
116 std::vector<std::function<MBool()>> m_postSolutionStep;
117 std::vector<std::function<void()>> m_postStep;
118
119 std::vector<std::function<void(const MInt)>> m_preCouple;
120 // std::vector< std::function< void(const MInt) > > m_preSolutionCouple;
121 std::vector<std::function<void(const MInt, const MInt, std::vector<MBool>&)>> m_subCouple;
122 // std::vector< std::function< void(const MInt) > > m_postSolutionCouple;
123 std::vector<std::function<void(const MInt)>> m_postCouple;
124
125
127
130 std::vector<std::vector<MBool>> m_solverOrder{};
131 std::vector<std::vector<MBool>> m_couplerOrder{};
132 std::vector<MBool> m_adaptationOrder{};
133};
134
135
154 TRACE();
155
156 a_step() = 0;
157
158 // TODO labels:COUPLER,DOC,toenhance allow a default call order to be defined by the couplers (when there is only one
159 // coupler)
160 // @ansgar this would make sense e.g. for the multilevel interpolation coupler!
161
162 m_maxNoSteps = 1;
163 m_maxNoSteps = Context::getBasicProperty<MInt>("recipeMaxNoSteps", AT_, &m_maxNoSteps);
164
165
167 m_maxSolutionIteration = Context::getBasicProperty<MInt>("maxIterations", AT_, &m_maxSolutionIteration);
168
169 m_solverOrder.resize(m_maxNoSteps, std::vector<MBool>(noSolvers()));
170 m_couplerOrder.resize(m_maxNoSteps, std::vector<MBool>(noCouplers()));
172
173 // Read solverOrder_x properties defining when the solvers should be executed, e.g. solverOrder_1 =
174 // [1, 0] to execute the solver #1 in the first step but not in the second step
175 for(MInt solver = 0; solver < noSolvers(); solver++) {
176 const MString propName = "solverOrder_" + std::to_string(solver);
178
179 for(MInt step = 0; step < m_maxNoSteps; step++) {
180 m_solverOrder[step][solver] = (MBool)Context::getBasicProperty<MInt>(propName, AT_, step);
181 }
182 }
183
184 // Read couplerOrder_x properties defining when the coupler should be executed, e.g. [0, 1] to
185 // execute the coupler only in the second step
186 for(MInt coupler = 0; coupler < noCouplers(); coupler++) {
187 const MString propName = "couplerOrder_" + std::to_string(coupler);
189
190 for(MInt step = 0; step < m_maxNoSteps; step++) {
191 m_couplerOrder[step][coupler] = (MBool)Context::getBasicProperty<MInt>(propName, AT_, step);
192 }
193 }
194
195 for(MInt step = 0; step < m_maxNoSteps; step++) {
196 // Property adaptationOrder is an array of length m_maxNoSteps - e.g. [0,1]
198 m_adaptationOrder[step] = (MBool)Context::getBasicProperty<MInt>("adaptationOrder", AT_, step);
199 }
200
201 // Set solver and coupler statuses for recipe step 0
202 for(MInt blck = 0; blck < noSolvers(); blck++) {
203 setSolverStatus(blck, m_solverOrder[a_step()][blck]);
204 }
205
206 for(MInt cpl = 0; cpl < noCouplers(); cpl++) {
208 }
209
211
212 // Print execution recipe call order to m_log
213 m_log << " === Execution recipe: call order with " << m_maxNoSteps << " steps" << std::endl;
214 m_log << "step: ";
215 for(MInt step = 0; step < m_maxNoSteps; step++) {
216 m_log << " " << step;
217 }
218 m_log << std::endl;
219
220 for(MInt solver = 0; solver < noSolvers(); solver++) {
221 m_log << "solver #" << solver << ": ";
222 for(MInt step = 0; step < m_maxNoSteps; step++) {
223 m_log << " " << m_solverOrder[step][solver];
224 }
225 m_log << std::endl;
226 }
227 for(MInt cpl = 0; cpl < noCouplers(); cpl++) {
228 m_log << "coupler #" << cpl << ":";
229 for(MInt step = 0; step < m_maxNoSteps; step++) {
230 m_log << " " << m_couplerOrder[step][cpl];
231 }
232 m_log << std::endl;
233 }
234
235 for(MInt blck = 0; blck < noSolvers(); blck++) {
236 setSolverStatus(blck, solverOrder(blck));
237 }
238
239 for(MInt cpl = 0; cpl < noCouplers(); cpl++) {
241 }
242
244
245 m_swapSolverIds.clear();
246 if(Context::propertyExists("swapSolverSolutionStep")) {
247 MInt size = Context::propertyLength("swapSolverSolutionStep") / 2;
248 for(MInt i = 0; i < size; i++) {
249 const MInt oldId = Context::getBasicProperty<MInt>("swapSolverSolutionStep", AT_, i);
250 const MInt newId = Context::getBasicProperty<MInt>("swapSolverSolutionStep", AT_, i + 1);
251 m_swapSolverIds.insert(std::make_pair(oldId, newId));
252 m_swapSolverIds.insert(std::make_pair(newId, oldId));
253 }
254 }
255}
256
262 TRACE();
263
264 // Tell the solver its current status such that a coupler can ask the solver if its currently active
265 // inside of the execution recipe
266 m_solvers->at(solverId)->setSolverStatus(active);
267
268
269 if(active) {
270 m_preStep[solverId] = [=]() { m_solvers->at(solverId)->preTimeStep(); };
271 m_preSolutionStep[solverId] = [=](MInt mode) { m_solvers->at(solverId)->preSolutionStep(mode); };
272 m_solutionStep[solverId] = [=]() { return m_solvers->at(solverId)->solutionStep(); };
273 m_postSolutionStep[solverId] = [=]() { return m_solvers->at(solverId)->postSolutionStep(); };
274 m_postStep[solverId] = [=]() { m_solvers->at(solverId)->postTimeStep(); };
275 } else {
276 // set to empty lambda
277 m_preStep[solverId] = []() {};
278 m_preSolutionStep[solverId] = [](MInt) {};
279 m_solutionStep[solverId] = []() { return true; };
280 m_postSolutionStep[solverId] = []() { return true; };
281 m_postStep[solverId] = []() {};
282 }
283}
284
289void ExecutionRecipe::setCouplerStatus(const MInt couplerId, const MBool active) {
290 TRACE();
291
292 if(active) {
293 m_preCouple[couplerId] = [=](MInt i) { m_couplers->at(couplerId)->preCouple(i); };
294 m_subCouple[couplerId] = [=](const MInt i, const MInt s, std::vector<MBool>& sc) {
295 return m_couplers->at(couplerId)->subCouple(i, s, sc);
296 };
297 m_postCouple[couplerId] = [=](MInt i) { m_couplers->at(couplerId)->postCouple(i); };
298 } else {
299 // set to empty lambda
300 m_preCouple[couplerId] = [](const MInt) {};
301 m_subCouple[couplerId] = [](const MInt, const MInt, std::vector<MBool>&) { return true; };
302 m_postCouple[couplerId] = [](const MInt) {};
303 }
304}
305
313 TRACE();
314
315 for(MInt i = 0; i < noSolvers(); i++) {
316 m_preStep.emplace_back([=]() { m_solvers->at(i)->preTimeStep(); });
317 m_preSolutionStep.emplace_back([=](MInt b) { m_solvers->at(i)->preSolutionStep(b); });
318 m_solutionStep.emplace_back([=]() { return m_solvers->at(i)->solutionStep(); });
319 m_postSolutionStep.emplace_back([=]() { return m_solvers->at(i)->postSolutionStep(); });
320 m_postStep.emplace_back([=]() { m_solvers->at(i)->postTimeStep(); });
321 }
322
323 for(MInt i = 0; i < noCouplers(); i++) {
324 m_preCouple.emplace_back([=](MInt v) { m_couplers->at(i)->preCouple(v); });
325 m_subCouple.emplace_back(
326 [=](const MInt v, const MInt s, std::vector<MBool>& sc) { return m_couplers->at(i)->subCouple(v, s, sc); });
327 m_postCouple.emplace_back([=](MInt v) { m_couplers->at(i)->postCouple(v); });
328 }
329}
330
336 TRACE();
337 for(auto&& solver : *m_solvers) {
338 if(!solver->isActive()) {
339 continue; // skip inactive solvers
340 }
341
342 solver->startLoadTimer(AT_);
343 m_preStep[solver->solverId()]();
344 solver->stopLoadTimer(AT_);
345 }
346}
347
354 TRACE();
355
356 // For a single solver run, there is obviously only one solver!
357 for(auto&& solver : *m_solvers) {
358 if(!solver->isActive()) {
359 continue; // skip inactive solvers
360 }
361
362 // Outer loop over substep iterations
363 MBool completed = false;
364 while(!completed) {
365 // Solution step
366 solver->startLoadTimer(AT_);
367 completed = m_solutionStep[solver->solverId()]();
368 solver->stopLoadTimer(AT_);
369 }
370 }
371}
372
378 TRACE();
379 for(auto&& solver : *m_solvers) {
380 const MInt solverId = m_swapSolverIds.empty() ? solver->solverId() : swapedSolverId(solver->solverId());
381 if(!solverIsActive(solverId)) {
382 continue; // skip inactive solvers
383 }
384 startLoadTimer(solverId);
385 m_postStep[solverId]();
386 stopLoadTimer(solverId);
387 }
388}
389
395 TRACE();
396
397 for(auto&& coupler : *m_couplers) {
398 coupler->startLoadTimer(AT_);
399 m_preCouple[coupler->couplerId()](a_step());
400 coupler->stopLoadTimer(AT_);
401 }
402}
403
409 TRACE();
410
411 for(auto&& coupler : *m_couplers) {
412 coupler->startLoadTimer(AT_);
413 m_postCouple[coupler->couplerId()](a_step());
414 coupler->stopLoadTimer(AT_);
415 }
416}
417
420
425 public:
426 ExecutionRecipeIntraStepCoupling(std::vector<std::unique_ptr<Solver>>* const solvers,
427 std::vector<std::unique_ptr<Coupling>>* const couplers)
428 : ExecutionRecipe(solvers, couplers) {
429 // This function initializes function pointers to the preTimeStep, solutionStep
430 // and postTimeStep functions of each solver
432
433 // Read the callOrder from the propertiesFile
435 }
436
437 // Set solvers and couplers active or idle according to the callOrder read in
438 // readCallOrder(). Also sets adaptation active or inactive.
439 // The function is called in the advance time step iteration of the time step loop.
440 // If the maximum number of iteration steps is reached (all solvers have been
441 // executed) advanceTimeStep is set to true.
443 MBool advanceTimeStep = false;
444
445 nextStep();
446
447 if(a_step() == maxNoSteps()) {
448 a_step() = 0;
449 advanceTimeStep = true;
450 }
451
452
453 for(MInt solver = 0; solver < noSolvers(); solver++) {
454 setSolverStatus(solver, solverOrder(solver));
455 }
456
457 for(MInt cpl = 0; cpl < noCouplers(); cpl++) {
459 }
460
462
463 return advanceTimeStep;
464 }
465
466 // The time step function.
467 void timeStep() override {
468 TRACE();
469
470 MBool completed = false;
471 // Store solver completed status to allow solvers to have different number of RK steps, i.e.,
472 // skip solutionStep of a solver that is already finished
473 std::vector<MBool> solverCompleted(noSolvers(), false);
474
475 while(!completed) {
476 // Exit once all solvers are completed.
477 completed = true;
478
479 // Intermediate loop over all solvers
480 for(auto&& solver : *a_solvers()) {
481 const MInt solverId = m_swapSolverIds.empty() ? solver->solverId() : swapedSolverId(solver->solverId());
482
483 // Solver is active on this domain (has cells) and has not already completed its current
484 // solution step, i.e., all RK stages
485 if(solverIsActive(solverId) && !solverCompleted[solverId]) {
486 // Call solver specific solutionStep() - solvers might be idle (in this step)
487 startLoadTimer(solverId);
488 solverCompleted[solverId] = solutionStep(solverId);
489 stopLoadTimer(solverId);
490 }
491
492 // Inner-most loop over all couplers
493 // Each couplers subCouple() is executed after each solvers solutionStep()
494 // Couplers can, however, be disabled via the callOrder
495 for(auto&& coupler : *a_couplers()) {
496 coupler->startLoadTimer(AT_);
497 subCouple(coupler->couplerId(), a_step(), solverId, solverCompleted);
498 coupler->stopLoadTimer(AT_);
499 }
500 if(solverIsActive(solverId) && !solverCompleted[solverId]) {
501 completed &= solverCompleted[solverId];
502 }
503 }
504 }
505 };
506};
507
510
511// new recepie class to handle multiple solution loops
512
514 public:
515 ExecutionRecipeSolutionIteration(std::vector<std::unique_ptr<Solver>>* const solvers,
516 std::vector<std::unique_ptr<Coupling>>* const couplers)
517 : ExecutionRecipe(solvers, couplers) {
518 // This function initializes function pointers to the preTimeStep, solutionStep
519 // and postTimeStep functions of each solver
521
522 // Read the callOrder from the propertiesFile
524 }
525
526 // Set solvers and couplers active or idle according to the callOrder read in
527 // readCallOrder(). Also sets adaptation active or inactive.
528 // The function is called in the advance time step iteration of the time step loop.
529 // If the maximum number of iteration steps is reached (all solvers have been
530 // executed) advanceTimeStep is set to true.
532 MBool advanceTimeStep = false;
533
534 nextStep();
535
536 if(a_step() == maxNoSteps()) {
537 a_step() = 0;
538 advanceTimeStep = true;
539 }
540
541
542 for(MInt solver = 0; solver < noSolvers(); solver++) {
543 setSolverStatus(solver, solverOrder(solver));
544 }
545
546 for(MInt cpl = 0; cpl < noCouplers(); cpl++) {
548 }
549
551
552 return advanceTimeStep;
553 }
554
555 // NOTE: this timeStep allows for a iteration of multiple solution Steps
556 // (i.e. multiple full RungeKutta-Steps during a single time-step!
557 // however a sub-couple is not implemented yet!
558 void timeStep() override {
559 TRACE();
560
561 MInt iteration = 0;
562 while(iteration < m_maxSolutionIteration) {
563 // Intermediate loop over all solvers
564 for(auto&& solver : *a_solvers()) {
565 const MInt solverId = solver->solverId();
566
567 if(iteration > 0) {
568 solver->startLoadTimer(AT_);
569 preSolutionStep(solverId, -1);
570 solver->stopLoadTimer(AT_);
571 }
572
573 // Outer loop over substep iterations
574 MBool timeStepCompleted = false;
575 // Store solver completed status to allow solvers to have different number of RK steps, i.e.,
576 // skip solutionStep of a solver that is already finished
577 std::vector<MBool> solverTimeStepCompleted(noSolvers(), false);
578
579 while(!timeStepCompleted) {
580 timeStepCompleted = true;
581
582 // Solver is active on this domain (has cells) and has not already completed its current
583 // solution step, i.e., all RK stages
584 if(solver->isActive() && !solverTimeStepCompleted[solverId]) {
585 // Call solver specific solutionStep() - solvers might be idle (in this step)
586 solver->startLoadTimer(AT_);
587 solverTimeStepCompleted[solverId] = solutionStep(solver->solverId());
588 timeStepCompleted &= solverTimeStepCompleted[solverId];
589 solver->stopLoadTimer(AT_);
590 }
591 }
592
594 solver->startLoadTimer(AT_);
595 MBool iterationConverged = postSolutionStep(solverId);
596 if(iterationConverged) iteration = m_maxSolutionIteration;
597 solver->stopLoadTimer(AT_);
598 }
599 iteration++;
600 }
601 }
602 };
603};
604
605
606#endif // ifndef EXECUTIONRECIPE_H_
static MInt propertyLength(const MString &name, MInt solverId=m_noSolvers)
Returns the number of elements of a property.
Definition: context.cpp:538
static void assertPropertyLength(const MString &name, const MInt length, const MInt solverId=m_noSolvers)
Assert that the length of a property matches the given length.
Definition: context.cpp:559
static MBool propertyExists(const MString &name, MInt solver=m_noSolvers)
This function checks if a property exists in general.
Definition: context.cpp:494
Base recipe provides public interface to Application.
void setSolverStatus(MInt, MBool)
: Wrapper function to set solvers active or idle
void stopLoadTimer(const MInt solverId)
MBool couplerOrder(const MInt couplerId) const
MBool solutionStep(const MInt solverId)
MBool solverIsActive(const MInt solverId)
void startLoadTimer(const MInt solverId)
virtual void postTimeStep() final
: Calls each solvers postTimeStep - might be empty
virtual void postCouple() final
: Calls each couplers postCouple - might be empty
std::vector< std::function< void(const MInt)> > m_postCouple
std::map< MInt, MInt > m_swapSolverIds
MBool callAdaptation() const
MInt swapedSolverId(const MInt oldSolverId)
std::vector< std::function< MBool()> > m_solutionStep
std::vector< std::function< void()> > m_postStep
void readCallOrder()
: Reads the call order of solvers, couplers and adaptation
virtual void preCouple() final
: Calls each couplers preCouple - might be empty
const std::vector< std::unique_ptr< Coupling > > *const m_couplers
std::vector< std::function< void(const MInt, const MInt, std::vector< MBool > &)> > m_subCouple
std::vector< std::function< void(const MInt)> > m_preCouple
std::vector< std::function< void(const MInt)> > m_preSolutionStep
MInt noCouplers() const
MBool solverOrder(const MInt solverId) const
std::vector< std::function< void()> > m_preStep
virtual MBool updateCallOrder()
ExecutionRecipe(std::vector< std::unique_ptr< Solver > > *const solvers, std::vector< std::unique_ptr< Coupling > > *const couplers)
const std::vector< std::unique_ptr< Solver > > *const m_solvers
void initFunctionPointers()
: Initialize the vector containing function pointers to preTimeStep, solutionStep and postTimestep as...
MInt noSolvers() const
std::vector< std::vector< MBool > > m_couplerOrder
std::vector< MBool > m_adaptationOrder
const std::vector< std::unique_ptr< Solver > > * a_solvers() const
void subCouple(const MInt couplerId, const MInt step, const MInt solverId, std::vector< MBool > &solverCompleted)
MInt maxNoSteps() const
const std::vector< std::unique_ptr< Coupling > > * a_couplers() const
void preSolutionStep(const MInt solverId, const MInt mode)
virtual void preTimeStep() final
: Calls each solvers preTimeStep - might be empty
MBool postSolutionStep(const MInt solverId)
MInt a_step() const
std::vector< std::vector< MBool > > m_solverOrder
virtual void timeStep()
: Single solver time step function. Calls solutionStep() of the specific solver
void setCouplerStatus(const MInt couplerId, const MBool active)
: Wrapper function to set couplers active or empty
std::vector< std::function< MBool()> > m_postSolutionStep
ExecutionRecipeIntraStepCoupling(std::vector< std::unique_ptr< Solver > > *const solvers, std::vector< std::unique_ptr< Coupling > > *const couplers)
void timeStep() override
: Single solver time step function. Calls solutionStep() of the specific solver
ExecutionRecipeSolutionIteration(std::vector< std::unique_ptr< Solver > > *const solvers, std::vector< std::unique_ptr< Coupling > > *const couplers)
void timeStep() override
: Single solver time step function. Calls solutionStep() of the specific solver
InfoOutFile m_log
int32_t MInt
Definition: maiatypes.h:62
std::basic_string< char > MString
Definition: maiatypes.h:55
MInt * solvers
Definition: maiatypes.h:72
bool MBool
Definition: maiatypes.h:58