preCICE v3.1.2
Loading...
Searching...
No Matches
CompositionalCouplingScheme.cpp
Go to the documentation of this file.
1#include <algorithm>
2#include <functional>
3#include <limits>
4#include <memory>
5#include <numeric>
6#include <ostream>
7
9#include "Constants.hpp"
14#include "logging/LogMacros.hpp"
15#include "utils/assertion.hpp"
16
17namespace {
19template <typename T>
20T min(T a, T b)
21{
22 return std::min(a, b);
23}
24} // namespace
25
26namespace precice::cplscheme {
27
28namespace {
29bool compatibleTimeWindowSizes(const CouplingScheme &impl, const CouplingScheme &expl)
30{
31 if (!impl.hasTimeWindowSize() || !expl.hasTimeWindowSize()) {
32 return true;
33 }
34 double idt = impl.getTimeWindowSize();
35 double edt = expl.getTimeWindowSize();
36 // edt needs to be an integer multiple of idt
37 return math::equals(edt, idt) || math::equals(std::remainder(edt, idt), 0.0);
38}
39} // namespace
40
42{
43 if (compatibleTimeWindowSizes(impl, expl)) {
44 return;
45 }
46 auto local = expl.localParticipant();
47 auto explPartners = expl.getCouplingPartners();
48 auto implPartners = impl.getCouplingPartners();
49 double edt = expl.getTimeWindowSize();
50 double idt = impl.getTimeWindowSize();
51
53 "The participant {} is implicitly coupled to {} with time-window-size {} and explicitly coupled to {} with time-window-size {}, which isn't well-defined. "
54 "Explicit time windows should be aligned with implicit time windows. "
55 "Choose time window sizes so that the explicit (currently {}) is an integer multiple of the implicit (currently {}).",
56 local, implPartners.front(), idt, explPartners.front(), edt,
57 edt, idt);
58}
59
61 const PtrCouplingScheme &couplingScheme)
62{
64 if (!couplingScheme->isImplicitCouplingScheme()) {
65 _explicitSchemes.emplace_back(couplingScheme);
66
67 if (_implicitScheme) {
69 }
70 } else {
72 _implicitScheme = couplingScheme;
73
74 for (const auto &scheme : _explicitSchemes) {
76 }
77 }
78}
79
81 double startTime,
82 int startTimeWindow)
83{
84 PRECICE_TRACE(startTime, startTimeWindow);
86 for (const auto scheme : allSchemes()) {
87 scheme->initialize(startTime, startTimeWindow);
88 _activeSchemes.push_back(scheme);
89 }
90}
91
100
102{
104 auto schemes = allSchemes();
105 bool isInitialized = std::any_of(schemes.begin(), schemes.end(), std::mem_fn(&CouplingScheme::isInitialized));
106 return isInitialized;
107}
108
110{
111 PRECICE_TRACE(timeToAdd);
112
114 auto explicitAtWindowEnd = false;
115 for (auto &scheme : _explicitSchemes) {
116 explicitAtWindowEnd |= scheme->addComputedTime(timeToAdd);
117 }
118 return explicitAtWindowEnd;
119 }
120
121 // Reaching the timewindow end of the implicit scheme requires explicit coupling schemes to freeze
122 auto implicitAtWindowEnd = _implicitScheme->addComputedTime(timeToAdd);
123
124 // We are done if the implicit scheme is iterating
125 if (_explicitOnHold) {
126 // No explicit schemes run in later iterations
127 PRECICE_DEBUG("Explicit schemes are still on hold");
128 return implicitAtWindowEnd;
129 }
130
131 // We are in the first iteration so explicit schemes need to step forward
132 auto explicitAtWindowEnd = false;
133 for (auto &scheme : _explicitSchemes) {
134 explicitAtWindowEnd |= scheme->addComputedTime(timeToAdd);
135 }
136
137 if (implicitAtWindowEnd) {
138 PRECICE_DEBUG("Implicit scheme reached the end of the first iteration at t={}. "
139 "Explicit schemes are on hold until convergence achieved.",
140 _implicitScheme->getTime());
141 // explicit schemes are on hold until converged
142 _explicitOnHold = true;
144 }
145
146 return implicitAtWindowEnd || explicitAtWindowEnd;
147}
148
150{
152 PRECICE_ASSERT(changes.empty());
153 PRECICE_ASSERT(!_activeSchemes.empty(), "Call initialize first");
155 for (const auto scheme : _activeSchemes) {
156 auto remoteChanges = scheme->firstSynchronization(changes);
157 totalChanges.insert(totalChanges.end(), remoteChanges.begin(), remoteChanges.end());
158 }
159 return totalChanges;
160}
161
163{
165
167 for (auto &scheme : _explicitSchemes) {
168 scheme->firstExchange();
169 }
170 return;
171 }
172
173 // The implicit scheme either just reached the end of the first time window, or is already iterating.
174 _implicitScheme->firstExchange();
175 if (_explicitOnHold) {
176 return;
177 }
178
179 // The implicit scheme hasn't reached the end of the first time window yet.
180 for (auto &scheme : _explicitSchemes) {
181 scheme->firstExchange();
182 }
183}
184
186{
188 PRECICE_ASSERT(!_activeSchemes.empty(), "Call initialize first");
190 for (const auto scheme : _activeSchemes) {
191 auto remoteChanges = scheme->secondSynchronization();
192 totalChanges.insert(totalChanges.end(), remoteChanges.begin(), remoteChanges.end());
193 }
194 return totalChanges;
195}
196
198{
200
202 for (auto &scheme : _explicitSchemes) {
203 scheme->secondExchange();
204 }
205 return;
206 }
207
208 // First complete the time window of the implicit scheme to determine if the scheme has converged
209 _implicitScheme->secondExchange();
210
211 double implicitTime = _implicitScheme->getTime();
212 double explicitTime = _explicitSchemes.front()->getTime();
213 bool iterating = implicitTime < explicitTime; // TODO numeric check?
214
215 if (iterating) {
216 PRECICE_DEBUG("Implicit scheme hasn't converged. Explicit schemes remain on hold.");
217 PRECICE_ASSERT(_explicitOnHold, "Iterative scheme hasn't converged yet");
218 return;
219 }
220 PRECICE_DEBUG("Implicit scheme converged. Running explicit schemes.");
221
222 for (auto &scheme : _explicitSchemes) {
223 scheme->firstExchange();
224 }
225 for (auto &scheme : _explicitSchemes) {
226 scheme->secondSynchronization();
227 }
228 for (auto &scheme : _explicitSchemes) {
229 scheme->secondExchange();
230 }
231 PRECICE_DEBUG("Explicit schemes caught up. All schemes are in sync.");
232
233 _explicitOnHold = false;
235}
236
238{
240 for (const auto scheme : allSchemes()) {
241 scheme->finalize();
242 }
243}
244
246{
249 for (const auto scheme : allSchemes()) {
250 auto subpartners = scheme->getCouplingPartners();
251 partners.insert(partners.end(), subpartners.begin(), subpartners.end());
252 }
253 return partners;
254}
255
262
263bool CompositionalCouplingScheme::willDataBeExchanged(double lastSolverTimeStepSize) const
264{
265 PRECICE_TRACE(lastSolverTimeStepSize);
266 // @TODO
267 auto schemes = allSchemes();
268 bool willBeExchanged = std::any_of(schemes.begin(), schemes.end(),
269 [lastSolverTimeStepSize](const auto &cpl) { return cpl->willDataBeExchanged(lastSolverTimeStepSize); });
270 PRECICE_DEBUG("return {}", willBeExchanged);
271 return willBeExchanged;
272}
273
275{
277 // @TODO
278 auto schemes = allSchemes();
279 bool hasBeenReceived = std::any_of(schemes.begin(), schemes.end(), std::mem_fn(&CouplingScheme::hasDataBeenReceived));
280 PRECICE_DEBUG("return {}", hasBeenReceived);
281 return hasBeenReceived;
282}
283
285{
287 if (_implicitScheme) {
288 // Either the implicit scheme is behind while iterating or all schemes are in sync
289 return _implicitScheme->getTime();
290 } else {
291 // Explicit schemes are always in sync
292 return _explicitSchemes.front()->getTime();
293 }
294}
295
297{
299 // In case of various time window sizes, use the scheme with the earliest start.
300 auto schemes = allSchemes();
302 schemes.begin(), schemes.end(),
304 ::min<double>,
306}
307
309{
311 // In case of various time window sizes, use the scheme with the most timeWindows.
312 auto schemes = allSchemes();
313 auto timeWindows = std::transform_reduce(
314 schemes.begin(), schemes.end(),
316 ::min<int>,
318 PRECICE_DEBUG("return {}", timeWindows);
319 return timeWindows;
320}
321
323{
324 // TODO Move to BaseCouplingScheme
326 PRECICE_UNREACHABLE("This may not be called for a CompositionalCouplingScheme");
327 return true;
328}
329
331{
332 // TODO Move to BaseCouplingScheme
334 PRECICE_UNREACHABLE("This may not be called for a CompositionalCouplingScheme");
335 return -1.0;
336}
337
339{
341 PRECICE_ASSERT(!_activeSchemes.empty(), "Call initialize first");
342
343 /* For compositional schemes with mixed time window sizes, this is the max time step size to the next end of a time window.
344 *
345 * As a concrete example we have 3 solvers A,B,C which are coupled A-B with Dt=2 and B-C with Dt=3.
346 *
347 * Then the synchronization points for A-B are (2, 4, 6, 8, ...) and for B-C are (3, 6, 9, ...) .
348 * The compositional scheme has to synchronize at the union of all these points (2, 3, 4, 6, 8, 9, ...)
349 *
350 * Hence the getNextTimeStepMaxSize() is the difference between these points.
351 * Starting at t=0 and always advancing the full getNextTimeStepMaxSize() will lead to time steps of sizes (2,1,1,2,2,1,...)
352 *
353 * Exception is when the implicit scheme is iterating. In that moment the explicit schemes are on hold and the implicit scheme may advance its full max time step size.
354 */
355
356 double maxLength = std::transform_reduce(
358 ::min<double>,
360 PRECICE_DEBUG("return {}", maxLength);
361 return maxLength;
362}
363
365{
367 if (_implicitScheme) {
368 // The implicit scheme is either the one that has to catch up, or all schemes are in sync
369 return _implicitScheme->isCouplingOngoing();
370 }
371 // All explicit schemes are always in sync
372 return _explicitSchemes.front()->isCouplingOngoing();
373}
374
376{
378 PRECICE_ASSERT(!_activeSchemes.empty(), "Call initialize first");
379 // Same situation as described in getNextTimeStepMaxSize(), but we are interested in reaching these time windows
381 PRECICE_DEBUG("return {}", isOneCompleted);
382 return isOneCompleted;
383}
384
386 Action action) const
387{
389 bool isRequired = false;
390
391 for (auto scheme : activeOrAllSchemes()) {
392 if (scheme->isActionRequired(action)) {
393 isRequired = true;
394 break;
395 }
396 }
397 PRECICE_DEBUG("return {}", isRequired);
398 return isRequired;
399}
400
402 Action action) const
403{
405 bool isFulfilled = false;
406 for (auto scheme : activeOrAllSchemes()) {
407 if (scheme->isActionFulfilled(action)) {
408 isFulfilled = true;
409 break;
410 }
411 }
412 PRECICE_DEBUG("return {}", isFulfilled);
413 return isFulfilled;
414}
415
417 Action action)
418{
420 for (auto scheme : activeOrAllSchemes()) {
421 if (scheme->isActionRequired(action)) {
422 scheme->markActionFulfilled(action);
423 }
424 }
425}
426
428 Action action)
429{
431 PRECICE_UNREACHABLE("This may not be called for a CompositionalCouplingScheme");
432}
433
435{
436 // TODO is a redesign necessary?
437 std::string state;
439 for (const auto scheme : allSchemes()) {
440 if (not state.empty()) {
441 state += "\n";
442 }
443 partners = scheme->getCouplingPartners();
444 state += partners[0];
445 state += ": ";
446 state += scheme->printCouplingState();
447 }
448 return state;
449}
450
452{
453 if (_implicitScheme == nullptr) {
454 // We only have explicit schemes, which are always in sync and thus always run together
455 // Active schemes never change, so there is nothing to do
456 PRECICE_ASSERT(_activeSchemes.size() == _explicitSchemes.size(), "Active scheme haven't been initialized correctly!");
457 return;
458 }
459
460 // There is an implicit scheme, which may be iterating
461
462 _activeSchemes.clear();
463
464 // The implicit scheme is either in sync or has to catch up
466
467 if (_explicitOnHold) {
468 return;
469 }
470
471 // If the implicit scheme isn't currently iterating, then all schemes are in sync
472 for (auto &scheme : _explicitSchemes) {
473 _activeSchemes.push_back(scheme.get());
474 }
475}
476
486
491
493{
494 return _implicitScheme != nullptr;
495}
496
498{
500 return true;
501 }
502
503 return _implicitScheme->hasConverged();
504}
505
507{
508 for (auto scheme : allSchemes()) {
509 if (scheme->requiresSubsteps()) {
510 return true;
511 }
512 }
513 return false;
514}
515
517{
518 if (_implicitScheme) {
519 return _implicitScheme->implicitDataToReceive();
520 }
521 return {};
522}
523
524} // namespace precice::cplscheme
#define PRECICE_WARN(...)
Definition LogMacros.hpp:11
#define PRECICE_DEBUG(...)
Definition LogMacros.hpp:64
#define PRECICE_TRACE(...)
Definition LogMacros.hpp:95
T any_of(T... args)
#define PRECICE_ASSERT(...)
Definition assertion.hpp:87
#define PRECICE_UNREACHABLE(...)
Definition assertion.hpp:95
T begin(T... args)
bool requiresSubsteps() const override final
Returns true if any send data of the scheme requires substeps.
PtrCouplingScheme _implicitScheme
The optional implicit scheme to be handled last.
void initialize(double startTime, int startTimeWindow) final override
Initializes the coupling scheme and establishes a communication connection to the coupling partner.
std::vector< CouplingScheme * > activeOrAllSchemes() const
Actions also work before initialize is called.
bool isTimeWindowComplete() const final override
Returns true, when the accessor can advance to the next time window.
bool hasConverged() const final
True if the implicit scheme has converged or no implicit scheme is defined.
std::vector< CouplingScheme * > allSchemes() const
Returns all schemes in execution order, explicit as well as implicit.
void finalize() final override
Finalizes the coupling and disconnects communication.
bool isImplicitCouplingScheme() const final
True if one cplscheme is an implicit scheme.
bool isCouplingOngoing() const final override
Returns true, when the coupled simulation is still ongoing.
double getTime() const final override
Returns the currently computed time of the coupling scheme.
std::vector< std::string > getCouplingPartners() const final override
Returns list of all coupling partners.
Schemes _explicitSchemes
Explicit coupling schemes to be executed.
double getTimeWindowSize() const final override
Returns the time window size, if one is given by the coupling scheme.
bool isActionRequired(Action action) const final override
Returns true, if the given action has to be performed by the accessor.
void markActionFulfilled(Action action) final override
Tells the coupling scheme that the accessor has performed the given action.
bool addComputedTime(double timeToAdd) final override
Adds newly computed time. Has to be called before every advance.
std::string localParticipant() const override final
Returns the name of the local participant.
bool hasDataBeenReceived() const final override
checks all coupling schemes this coupling scheme is composed of.
int getTimeWindows() const final override
Returns the currently computed time windows of the coupling scheme.
void requireAction(Action action) final override
Sets an action required to be performed by the accessor.
bool isInitialized() const final override
Returns true, if initialize has been called.
double getNextTimeStepMaxSize() const final override
Returns the maximal size of the next time step to be computed.
bool hasTimeWindowSize() const final override
Returns true, if time window size is given by any of the coupling schemes in this compositional coupl...
void addCouplingScheme(const PtrCouplingScheme &scheme)
Adds another coupling scheme in parallel to this scheme.
ChangedMeshes firstSynchronization(const ChangedMeshes &changes) override
Exchanges data and updates the state of the coupling scheme.
bool willDataBeExchanged(double lastSolverTimeStepSize) const final override
Returns true, if data will be exchanged when calling advance().
void checkCompatibleTimeWindowSizes(const CouplingScheme &impl, const CouplingScheme &expl) const
check if time windows are compatible
std::string printCouplingState() const final override
Returns a string representation of the current coupling state.
bool sendsInitializedData() const override final
Returns true, if any of the composed coupling schemes sendsInitializedData for this participant.
ImplicitData implicitDataToReceive() const override final
Returns a vector of implicit data to receive in the next advance.
bool isActionFulfilled(Action action) const final override
Returns true, if the given action has been performed by the accessor.
Interface for all coupling schemes.
Action
Actions that are required by CouplingSchemes.
virtual std::vector< std::string > getCouplingPartners() const =0
Returns list of all coupling partners.
virtual double getTimeWindowStart() const =0
virtual bool isInitialized() const =0
Returns true, if initialize has been called.
virtual double getNextTimeStepMaxSize() const =0
Returns the maximal size of the next time step to be computed.
virtual bool isTimeWindowComplete() const =0
Returns true, when the accessor can advance to the next time window.
virtual double getTimeWindowSize() const =0
Returns the time window size, if one is given by the coupling scheme.
virtual bool sendsInitializedData() const =0
Returns whether this participant of the coupling scheme sends initialized data.
virtual int getTimeWindows() const =0
Returns the currently computed time windows of the coupling scheme.
virtual bool hasDataBeenReceived() const =0
Returns true, if data has been exchanged in last call of advance().
virtual std::string localParticipant() const =0
Returns the name of the local participant.
T emplace_back(T... args)
T empty(T... args)
T end(T... args)
T front(T... args)
T get(T... args)
T insert(T... args)
T mem_fn(T... args)
T min(T... args)
contains implementations of coupling schemes for coupled simulations.
constexpr bool equals(const Eigen::MatrixBase< DerivedA > &A, const Eigen::MatrixBase< DerivedB > &B, double tolerance=NUMERICAL_ZERO_DIFFERENCE)
Compares two Eigen::MatrixBase for equality up to tolerance.
T push_back(T... args)
T remainder(T... args)
T size(T... args)
T transform(T... args)
T transform_reduce(T... args)