preCICE v3.1.1
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
29 const PtrCouplingScheme &couplingScheme)
30{
32
33 if (!couplingScheme->isImplicitCouplingScheme()) {
34 _explicitSchemes.emplace_back(couplingScheme);
35 return;
36 }
37
39 _implicitScheme = couplingScheme;
40}
41
43 double startTime,
44 int startTimeWindow)
45{
46 PRECICE_TRACE(startTime, startTimeWindow);
48 for (const auto scheme : allSchemes()) {
49 scheme->initialize(startTime, startTimeWindow);
50 _activeSchemes.push_back(scheme);
51 }
52}
53
62
64{
66 auto schemes = allSchemes();
67 bool isInitialized = std::any_of(schemes.begin(), schemes.end(), std::mem_fn(&CouplingScheme::isInitialized));
68 return isInitialized;
69}
70
72{
73 PRECICE_TRACE(timeToAdd);
74 PRECICE_ASSERT(!_activeSchemes.empty(), "Call initialize first");
75
76 bool atWindowEnd = false;
77 for (auto &scheme : _activeSchemes) {
78 atWindowEnd |= scheme->addComputedTime(timeToAdd);
79 }
80 return atWindowEnd;
81}
82
84{
86 PRECICE_ASSERT(changes.empty());
87 PRECICE_ASSERT(!_activeSchemes.empty(), "Call initialize first");
89 for (const auto scheme : _activeSchemes) {
90 auto remoteChanges = scheme->firstSynchronization(changes);
91 totalChanges.insert(totalChanges.end(), remoteChanges.begin(), remoteChanges.end());
92 }
93 return totalChanges;
94}
95
97{
99 PRECICE_ASSERT(!_activeSchemes.empty(), "Call initialize first");
100 for (const auto scheme : _activeSchemes) {
101 scheme->firstExchange();
102 }
103}
104
106{
108 PRECICE_ASSERT(!_activeSchemes.empty(), "Call initialize first");
110 for (const auto scheme : _activeSchemes) {
111 auto remoteChanges = scheme->secondSynchronization();
112 totalChanges.insert(totalChanges.end(), remoteChanges.begin(), remoteChanges.end());
113 }
114 return totalChanges;
115}
116
118{
120 PRECICE_ASSERT(!_activeSchemes.empty(), "Call initialize first");
121 for (const auto scheme : _activeSchemes) {
122 scheme->secondExchange();
123 }
124
126}
127
129{
131 for (const auto scheme : allSchemes()) {
132 scheme->finalize();
133 }
134}
135
137{
140 for (const auto scheme : allSchemes()) {
141 auto subpartners = scheme->getCouplingPartners();
142 partners.insert(partners.end(), subpartners.begin(), subpartners.end());
143 }
144 return partners;
145}
146
147bool CompositionalCouplingScheme::willDataBeExchanged(double lastSolverTimeStepSize) const
148{
149 PRECICE_TRACE(lastSolverTimeStepSize);
150 // @TODO
151 auto schemes = allSchemes();
152 bool willBeExchanged = std::any_of(schemes.begin(), schemes.end(),
153 [lastSolverTimeStepSize](const auto &cpl) { return cpl->willDataBeExchanged(lastSolverTimeStepSize); });
154 PRECICE_DEBUG("return {}", willBeExchanged);
155 return willBeExchanged;
156}
157
159{
161 // @TODO
162 auto schemes = allSchemes();
163 bool hasBeenReceived = std::any_of(schemes.begin(), schemes.end(), std::mem_fn(&CouplingScheme::hasDataBeenReceived));
164 PRECICE_DEBUG("return {}", hasBeenReceived);
165 return hasBeenReceived;
166}
167
169{
171 if (_implicitScheme) {
172 // Either the implicit scheme is behind while iterating or all schemes are in sync
173 return _implicitScheme->getTime();
174 } else {
175 // Explicit schemes are always in sync
176 return _explicitSchemes.front()->getTime();
177 }
178}
179
181{
183 // In case of various time window sizes, use the scheme with the earliest start.
184 auto schemes = allSchemes();
186 schemes.begin(), schemes.end(),
188 ::min<double>,
190}
191
193{
195 // In case of various time window sizes, use the scheme with the most timeWindows.
196 auto schemes = allSchemes();
197 auto timeWindows = std::transform_reduce(
198 schemes.begin(), schemes.end(),
200 ::min<int>,
202 PRECICE_DEBUG("return {}", timeWindows);
203 return timeWindows;
204}
205
207{
208 // TODO Move to BaseCouplingScheme
210 PRECICE_UNREACHABLE("This may not be called for a CompositionalCouplingScheme");
211 return true;
212}
213
215{
216 // TODO Move to BaseCouplingScheme
218 PRECICE_UNREACHABLE("This may not be called for a CompositionalCouplingScheme");
219 return -1.0;
220}
221
223{
225 PRECICE_ASSERT(!_activeSchemes.empty(), "Call initialize first");
226
227 /* For compositional schemes with mixed time window sizes, this is the max time step size to the next end of a time window.
228 *
229 * 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.
230 *
231 * Then the synchronization points for A-B are (2, 4, 6, 8, ...) and for B-C are (3, 6, 9, ...) .
232 * The compositional scheme has to synchronize at the union of all these points (2, 3, 4, 6, 8, 9, ...)
233 *
234 * Hence the getNextTimeStepMaxSize() is the difference between these points.
235 * Starting at t=0 and always advancing the full getNextTimeStepMaxSize() will lead to time steps of sizes (2,1,1,2,2,1,...)
236 *
237 * 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.
238 */
239
240 double maxLength = std::transform_reduce(
242 ::min<double>,
244 PRECICE_DEBUG("return {}", maxLength);
245 return maxLength;
246}
247
249{
251 if (_implicitScheme) {
252 // The implicit scheme is either the one that has to catch up, or all schemes are in sync
253 return _implicitScheme->isCouplingOngoing();
254 }
255 // All explicit schemes are always in sync
256 return _explicitSchemes.front()->isCouplingOngoing();
257}
258
260{
262 PRECICE_ASSERT(!_activeSchemes.empty(), "Call initialize first");
263 // Same situation as described in getNextTimeStepMaxSize(), but we are interested in reaching these time windows
265 PRECICE_DEBUG("return {}", isOneCompleted);
266 return isOneCompleted;
267}
268
270 Action action) const
271{
273 bool isRequired = false;
274 for (auto scheme : activeOrAllSchemes()) {
275 if (scheme->isActionRequired(action)) {
276 isRequired = true;
277 break;
278 }
279 }
280 PRECICE_DEBUG("return {}", isRequired);
281 return isRequired;
282}
283
285 Action action) const
286{
288 bool isFulfilled = false;
289 for (auto scheme : activeOrAllSchemes()) {
290 if (scheme->isActionFulfilled(action)) {
291 isFulfilled = true;
292 break;
293 }
294 }
295 PRECICE_DEBUG("return {}", isFulfilled);
296 return isFulfilled;
297}
298
300 Action action)
301{
303 for (auto scheme : activeOrAllSchemes()) {
304 if (scheme->isActionRequired(action)) {
305 scheme->markActionFulfilled(action);
306 }
307 }
308}
309
311 Action action)
312{
314 PRECICE_ASSERT(!_activeSchemes.empty(), "Call initialize first");
315 for (auto scheme : _activeSchemes) {
316 scheme->requireAction(action);
317 }
318}
319
321{
322 // TODO is a redesign necessary?
323 std::string state;
325 for (const auto scheme : allSchemes()) {
326 if (not state.empty()) {
327 state += "\n";
328 }
329 partners = scheme->getCouplingPartners();
330 state += partners[0];
331 state += ": ";
332 state += scheme->printCouplingState();
333 }
334 return state;
335}
336
338{
339 if (_implicitScheme == nullptr) {
340 // We only have explicit schemes, which are always in sync and thus always run together
341 // Active schemes never change, so there is nothing to do
342 PRECICE_ASSERT(_activeSchemes.size() == _explicitSchemes.size(), "Active scheme haven't been initialized correctly!");
343 return;
344 }
345
346 // There is an implicit scheme, which may be iterating
347
348 _activeSchemes.clear();
349 PRECICE_DEBUG("Updating active schemes of a mixed compositional coupling scheme");
350
351 double implicitTime = _implicitScheme->getTime();
352 double explicitTime = _explicitSchemes.front()->getTime();
353 bool iterating = implicitTime < explicitTime; // TODO numeric check?
354
355 if (!iterating) {
356 // If the implicit scheme isn't currently iterating, then all schemes are in sync
357 for (auto &scheme : _explicitSchemes) {
358 _activeSchemes.push_back(scheme.get());
359 }
360 } else {
361 PRECICE_INFO("Explicit coupling schemes are on hold until the implicit scheme has caught up.");
362 }
363
364 // The implicit scheme is either in sync or has to catch up
366}
367
377
382
384{
385 return _implicitScheme != nullptr;
386}
387
389{
390 if (!_implicitScheme) {
391 return true;
392 }
393
394 return _implicitScheme->hasConverged();
395}
396
398{
399 for (auto scheme : allSchemes()) {
400 if (scheme->requiresSubsteps()) {
401 return true;
402 }
403 }
404 return false;
405}
406
408{
409 if (_implicitScheme) {
410 return _implicitScheme->implicitDataToReceive();
411 }
412 return {};
413}
414
415} // namespace precice::cplscheme
#define PRECICE_DEBUG(...)
Definition LogMacros.hpp:64
#define PRECICE_TRACE(...)
Definition LogMacros.hpp:95
#define PRECICE_INFO(...)
Definition LogMacros.hpp:13
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.
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().
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.
Action
Actions that are required by CouplingSchemes.
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 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().
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.
T push_back(T... args)
T size(T... args)
T transform(T... args)
T transform_reduce(T... args)