preCICE v3.2.0
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 precice::cplscheme {
18
19namespace {
20bool compatibleTimeWindowSizes(const CouplingScheme &impl, const CouplingScheme &expl)
21{
22 if (!impl.hasTimeWindowSize() || !expl.hasTimeWindowSize()) {
23 return true;
24 }
25 double idt = impl.getTimeWindowSize();
26 double edt = expl.getTimeWindowSize();
27 // edt needs to be an integer multiple of idt
28 return math::equals(edt, idt) || math::equals(std::remainder(edt, idt), 0.0);
29}
30} // namespace
31
33{
34 if (compatibleTimeWindowSizes(impl, expl)) {
35 return;
36 }
37 auto local = expl.localParticipant();
38 auto explPartners = expl.getCouplingPartners();
39 auto implPartners = impl.getCouplingPartners();
40 double edt = expl.getTimeWindowSize();
41 double idt = impl.getTimeWindowSize();
42
44 "The participant {} is implicitly coupled to {} with time-window-size {} and explicitly coupled to {} with time-window-size {}, which isn't well-defined. "
45 "Explicit time windows should be aligned with implicit time windows. "
46 "Choose time window sizes so that the explicit (currently {}) is an integer multiple of the implicit (currently {}).",
47 local, implPartners.front(), idt, explPartners.front(), edt,
48 edt, idt);
49}
50
52 const PtrCouplingScheme &couplingScheme)
53{
55 if (!couplingScheme->isImplicitCouplingScheme()) {
56 _explicitSchemes.emplace_back(couplingScheme);
57
58 if (_implicitScheme) {
60 }
61 } else {
63 _implicitScheme = couplingScheme;
64
65 for (const auto &scheme : _explicitSchemes) {
67 }
68 }
69}
70
72{
75 for (const auto scheme : allSchemes()) {
76 scheme->initialize();
77 _activeSchemes.push_back(scheme);
78 }
79}
80
82{
84 PRECICE_UNREACHABLE("Not implemented and not allowed");
85}
86
95
97{
99 auto schemes = allSchemes();
100 bool isInitialized = std::any_of(schemes.begin(), schemes.end(), std::mem_fn(&CouplingScheme::isInitialized));
101 return isInitialized;
102}
103
105{
106 PRECICE_TRACE(timeToAdd);
107
109 auto explicitAtWindowEnd = false;
110 for (auto &scheme : _explicitSchemes) {
111 explicitAtWindowEnd |= scheme->addComputedTime(timeToAdd);
112 }
113 return explicitAtWindowEnd;
114 }
115
116 // Reaching the timewindow end of the implicit scheme requires explicit coupling schemes to freeze
117 auto implicitAtWindowEnd = _implicitScheme->addComputedTime(timeToAdd);
118
119 // We are done if the implicit scheme is iterating
120 if (_explicitOnHold) {
121 // No explicit schemes run in later iterations
122 PRECICE_DEBUG("Explicit schemes are still on hold");
123 return implicitAtWindowEnd;
124 }
125
126 // We are in the first iteration so explicit schemes need to step forward
127 auto explicitAtWindowEnd = false;
128 for (auto &scheme : _explicitSchemes) {
129 explicitAtWindowEnd |= scheme->addComputedTime(timeToAdd);
130 }
131
132 if (implicitAtWindowEnd) {
133 PRECICE_DEBUG("Implicit scheme reached the end of the first iteration at t={}. "
134 "Explicit schemes are on hold until convergence achieved.",
135 _implicitScheme->getTime());
136 // explicit schemes are on hold until converged
137 _explicitOnHold = true;
139 }
140
141 return implicitAtWindowEnd || explicitAtWindowEnd;
142}
143
145{
147 PRECICE_ASSERT(changes.empty());
148 PRECICE_ASSERT(!_activeSchemes.empty(), "Call initialize first");
150 for (const auto scheme : _activeSchemes) {
151 auto remoteChanges = scheme->firstSynchronization(changes);
152 totalChanges.insert(totalChanges.end(), remoteChanges.begin(), remoteChanges.end());
153 }
154 return totalChanges;
155}
156
158{
160
162 for (auto &scheme : _explicitSchemes) {
163 scheme->firstExchange();
164 }
165 return;
166 }
167
168 // The implicit scheme either just reached the end of the first time window, or is already iterating.
169 _implicitScheme->firstExchange();
170 if (_explicitOnHold) {
171 return;
172 }
173
174 // The implicit scheme hasn't reached the end of the first time window yet.
175 for (auto &scheme : _explicitSchemes) {
176 scheme->firstExchange();
177 }
178}
179
181{
183 PRECICE_ASSERT(!_activeSchemes.empty(), "Call initialize first");
185 for (const auto scheme : _activeSchemes) {
186 auto remoteChanges = scheme->secondSynchronization();
187 totalChanges.insert(totalChanges.end(), remoteChanges.begin(), remoteChanges.end());
188 }
189 return totalChanges;
190}
191
193{
195
197 for (auto &scheme : _explicitSchemes) {
198 scheme->secondExchange();
199 }
200 return;
201 }
202
203 // First complete the time window of the implicit scheme to determine if the scheme has converged
204 _implicitScheme->secondExchange();
205
206 double implicitTime = _implicitScheme->getTime();
207 double explicitTime = _explicitSchemes.front()->getTime();
208 bool iterating = implicitTime < explicitTime; // TODO numeric check?
209
210 if (iterating) {
211 PRECICE_DEBUG("Implicit scheme hasn't converged. Explicit schemes remain on hold.");
212 PRECICE_ASSERT(_explicitOnHold, "Iterative scheme hasn't converged yet");
213 return;
214 }
215 PRECICE_DEBUG("Implicit scheme converged. Running explicit schemes.");
216
217 for (auto &scheme : _explicitSchemes) {
218 scheme->firstExchange();
219 }
220 for (auto &scheme : _explicitSchemes) {
221 scheme->secondSynchronization();
222 }
223 for (auto &scheme : _explicitSchemes) {
224 scheme->secondExchange();
225 }
226 PRECICE_DEBUG("Explicit schemes caught up. All schemes are in sync.");
227
228 _explicitOnHold = false;
230}
231
233{
235 for (const auto scheme : allSchemes()) {
236 scheme->finalize();
237 }
238}
239
241{
244 for (const auto scheme : allSchemes()) {
245 auto subpartners = scheme->getCouplingPartners();
246 partners.insert(partners.end(), subpartners.begin(), subpartners.end());
247 }
248 return partners;
249}
250
252{
255 return _explicitSchemes.front()->localParticipant();
256}
257
258bool CompositionalCouplingScheme::willDataBeExchanged(double lastSolverTimeStepSize) const
259{
260 PRECICE_TRACE(lastSolverTimeStepSize);
261 // @TODO
262 auto schemes = allSchemes();
263 bool willBeExchanged = std::any_of(schemes.begin(), schemes.end(),
264 [lastSolverTimeStepSize](const auto &cpl) { return cpl->willDataBeExchanged(lastSolverTimeStepSize); });
265 PRECICE_DEBUG("return {}", willBeExchanged);
266 return willBeExchanged;
267}
268
270{
272 // @TODO
273 auto schemes = allSchemes();
274 bool hasBeenReceived = std::any_of(schemes.begin(), schemes.end(), std::mem_fn(&CouplingScheme::hasDataBeenReceived));
275 PRECICE_DEBUG("return {}", hasBeenReceived);
276 return hasBeenReceived;
277}
278
280{
282 if (_implicitScheme) {
283 // Either the implicit scheme is behind while iterating or all schemes are in sync
284 return _implicitScheme->getTime();
285 } else {
286 // Explicit schemes are always in sync
287 return _explicitSchemes.front()->getTime();
288 }
289}
290
292{
294 // In case of various time window sizes, use the scheme with the earliest start.
295 auto schemes = allSchemes();
297 schemes.begin(), schemes.end(),
299 [](double a, double b) { return std::min(a, b); },
301}
302
304{
306 // In case of various time window sizes, use the scheme with the most timeWindows.
307 auto schemes = allSchemes();
308 auto timeWindows = std::transform_reduce(
309 schemes.begin(), schemes.end(),
311 [](int a, int b) { return std::min(a, b); },
313 PRECICE_DEBUG("return {}", timeWindows);
314 return timeWindows;
315}
316
318{
319 // TODO Move to BaseCouplingScheme
321 PRECICE_UNREACHABLE("This may not be called for a CompositionalCouplingScheme");
322 return true;
323}
324
326{
327 // TODO Move to BaseCouplingScheme
329 PRECICE_UNREACHABLE("This may not be called for a CompositionalCouplingScheme");
330 return -1.0;
331}
332
334{
336 PRECICE_ASSERT(!_activeSchemes.empty(), "Call initialize first");
337
338 /* For compositional schemes with mixed time window sizes, this is the max time step size to the next end of a time window.
339 *
340 * 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.
341 *
342 * Then the synchronization points for A-B are (2, 4, 6, 8, ...) and for B-C are (3, 6, 9, ...) .
343 * The compositional scheme has to synchronize at the union of all these points (2, 3, 4, 6, 8, 9, ...)
344 *
345 * Hence the getNextTimeStepMaxSize() is the difference between these points.
346 * Starting at t=0 and always advancing the full getNextTimeStepMaxSize() will lead to time steps of sizes (2,1,1,2,2,1,...)
347 *
348 * 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.
349 */
350
351 double maxLength = std::transform_reduce(
353 [](double a, double b) { return std::min(a, b); },
355 PRECICE_DEBUG("return {}", maxLength);
356 return maxLength;
357}
358
360{
362 if (_implicitScheme) {
363 // The implicit scheme is either the one that has to catch up, or all schemes are in sync
364 return _implicitScheme->isCouplingOngoing();
365 }
366 // All explicit schemes are always in sync
367 return _explicitSchemes.front()->isCouplingOngoing();
368}
369
371{
373 PRECICE_ASSERT(!_activeSchemes.empty(), "Call initialize first");
374 // Same situation as described in getNextTimeStepMaxSize(), but we are interested in reaching these time windows
376 PRECICE_DEBUG("return {}", isOneCompleted);
377 return isOneCompleted;
378}
379
381 Action action) const
382{
384 bool isRequired = false;
385
386 for (auto scheme : activeOrAllSchemes()) {
387 if (scheme->isActionRequired(action)) {
388 isRequired = true;
389 break;
390 }
391 }
392 PRECICE_DEBUG("return {}", isRequired);
393 return isRequired;
394}
395
397 Action action) const
398{
400 bool isFulfilled = false;
401 for (auto scheme : activeOrAllSchemes()) {
402 if (scheme->isActionFulfilled(action)) {
403 isFulfilled = true;
404 break;
405 }
406 }
407 PRECICE_DEBUG("return {}", isFulfilled);
408 return isFulfilled;
409}
410
413{
415 for (auto scheme : activeOrAllSchemes()) {
416 if (scheme->isActionRequired(action)) {
417 scheme->markActionFulfilled(action);
418 }
419 }
420}
421
424{
426 PRECICE_UNREACHABLE("This may not be called for a CompositionalCouplingScheme");
427}
428
430{
431 // TODO is a redesign necessary?
432 std::string state;
434 for (const auto scheme : allSchemes()) {
435 if (not state.empty()) {
436 state += "\n";
437 }
438 partners = scheme->getCouplingPartners();
439 state += partners[0];
440 state += ": ";
441 state += scheme->printCouplingState();
442 }
443 return state;
444}
445
447{
448 if (_implicitScheme == nullptr) {
449 // We only have explicit schemes, which are always in sync and thus always run together
450 // Active schemes never change, so there is nothing to do
451 PRECICE_ASSERT(_activeSchemes.size() == _explicitSchemes.size(), "Active scheme haven't been initialized correctly!");
452 return;
453 }
454
455 // There is an implicit scheme, which may be iterating
456
457 _activeSchemes.clear();
458
459 // The implicit scheme is either in sync or has to catch up
460 _activeSchemes.push_back(_implicitScheme.get());
461
462 if (_explicitOnHold) {
463 return;
464 }
465
466 // If the implicit scheme isn't currently iterating, then all schemes are in sync
467 for (auto &scheme : _explicitSchemes) {
468 _activeSchemes.push_back(scheme.get());
469 }
470}
471
481
486
488{
489 return _implicitScheme != nullptr;
490}
491
493{
495 return true;
496 }
497
498 return _implicitScheme->hasConverged();
499}
500
502{
503 for (auto scheme : allSchemes()) {
504 if (scheme->requiresSubsteps()) {
505 return true;
506 }
507 }
508 return false;
509}
510
512{
513 if (_implicitScheme) {
514 return _implicitScheme->implicitDataToReceive();
515 }
516 return {};
517}
518
519} // namespace precice::cplscheme
#define PRECICE_WARN(...)
Definition LogMacros.hpp:12
#define PRECICE_DEBUG(...)
Definition LogMacros.hpp:61
#define PRECICE_TRACE(...)
Definition LogMacros.hpp:92
T any_of(T... args)
#define PRECICE_ASSERT(...)
Definition assertion.hpp:85
#define PRECICE_UNREACHABLE(...)
Definition assertion.hpp:93
T begin(T... args)
PtrCouplingScheme _implicitScheme
The optional implicit scheme to be handled last.
bool sendsInitializedData() const final override
Returns true, if any of the composed coupling schemes sendsInitializedData for this participant.
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.
std::vector< CouplingScheme * > allSchemes() const
Returns all schemes in execution order, explicit as well as implicit.
std::string localParticipant() const final override
Returns the name of the local participant.
void finalize() final override
Finalizes the coupling and disconnects communication.
ImplicitData implicitDataToReceive() const final override
Returns a vector of implicit data to receive in the next advance.
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.
void initialize() final override
Initializes the coupling scheme and establishes a communication connection to the coupling partner.
Schemes _explicitSchemes
Explicit coupling schemes to be executed.
bool isImplicitCouplingScheme() const final override
True if one cplscheme is an implicit scheme.
double getTimeWindowSize() const final override
Returns the time window size, if one is given by the coupling scheme.
bool requiresSubsteps() const final override
Returns true if any send data of the scheme requires substeps.
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.
bool hasDataBeenReceived() const final override
checks all coupling schemes this coupling scheme is composed of.
bool hasConverged() const final override
True if the implicit scheme has converged or no implicit scheme is defined.
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
void reinitialize() final override
Reinitializes the coupling scheme, coupling data, and acceleration schemes.
std::string printCouplingState() const final override
Returns a string representation of the current coupling state.
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 empty(T... args)
T end(T... args)
CouplingScheme get(CouplingScheme... args)
T insert(T... args)
T max(T... args)
T mem_fn(T... args)
contains actions to modify exchanged data.
Definition Action.hpp:6
contains implementations of coupling schemes for coupled simulations.
std::shared_ptr< CouplingScheme > PtrCouplingScheme
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 transform(T... args)
T transform_reduce(T... args)