preCICE
Loading...
Searching...
No Matches
ParticipantImpl.cpp
Go to the documentation of this file.
1#include <Eigen/Core>
2#include <algorithm>
3#include <array>
4#include <cmath>
5#include <deque>
6#include <filesystem>
7#include <functional>
8#include <iterator>
9#include <memory>
10#include <numeric>
11#include <optional>
12#include <ostream>
13#include <sstream>
14#include <string_view>
15#include <tuple>
16#include <utility>
17
18#include "ParticipantImpl.hpp"
20#include "com/Communication.hpp"
21#include "com/SharedPointer.hpp"
25#include "io/Export.hpp"
26#include "io/ExportContext.hpp"
28#include "logging/LogMacros.hpp"
29#include "m2n/BoundM2N.hpp"
30#include "m2n/M2N.hpp"
31#include "m2n/SharedPointer.hpp"
33#include "mapping/Mapping.hpp"
37#include "math/differences.hpp"
38#include "math/geometry.hpp"
39#include "mesh/Data.hpp"
40#include "mesh/Edge.hpp"
41#include "mesh/Mesh.hpp"
43#include "mesh/Utils.hpp"
44#include "mesh/Vertex.hpp"
65#include "precice/impl/versions.hpp"
66#include "profiling/Event.hpp"
70#include "utils/EigenIO.hpp"
71#include "utils/Helpers.hpp"
72#include "utils/IntraComm.hpp"
73#include "utils/Parallel.hpp"
74#include "utils/Petsc.hpp"
75#include "utils/algorithm.hpp"
76#include "utils/assertion.hpp"
77#include "xml/XMLTag.hpp"
78
80
81namespace precice::impl {
82
84 std::string_view participantName,
85 std::string_view configurationFileName,
86 int solverProcessIndex,
87 int solverProcessSize,
88 std::optional<void *> communicator)
89 : _accessorName(participantName),
90 _accessorProcessRank(solverProcessIndex),
91 _accessorCommunicatorSize(solverProcessSize)
92{
93
95 "This participant's name is an empty string. "
96 "When constructing a preCICE interface you need to pass the name of the "
97 "participant as first argument to the constructor.");
99
100 PRECICE_CHECK(!communicator || communicator.value() != nullptr,
101 "Passing \"nullptr\" as \"communicator\" to Participant constructor is not allowed. "
102 "Please use the Participant constructor without the \"communicator\" argument, if you don't want to pass an MPI communicator.");
104 "The solver process index needs to be a non-negative number, not: {}. "
105 "Please check the value given when constructing a preCICE interface.",
108 "The solver process size needs to be a positive number, not: {}. "
109 "Please check the value given when constructing a preCICE interface.",
112 "The solver process index, currently: {} needs to be smaller than the solver process size, currently: {}. "
113 "Please check the values given when constructing a preCICE interface.",
115
118 Event e("construction", profiling::Fundamental);
119
120 // Set the global communicator to the passed communicator.
121 // This is a noop if preCICE is not configured with MPI.
122#ifndef PRECICE_NO_MPI
123 Event e3("com.initializeMPI", profiling::Fundamental);
124 if (communicator.has_value()) {
125 auto commptr = static_cast<utils::Parallel::Communicator *>(communicator.value());
127 } else {
129 }
130
131 {
132 const auto currentRank = utils::Parallel::current()->rank();
133 PRECICE_CHECK(_accessorProcessRank == currentRank,
134 "The solver process index given in the preCICE interface constructor({}) does not match the rank of the passed MPI communicator ({}).",
135 _accessorProcessRank, currentRank);
136 const auto currentSize = utils::Parallel::current()->size();
138 "The solver process size given in the preCICE interface constructor({}) does not match the size of the passed MPI communicator ({}).",
139 _accessorCommunicatorSize, currentSize);
140 }
141 e3.stop();
142#else
143 PRECICE_WARN_IF(communicator.has_value(), "preCICE was configured without MPI but you passed an MPI communicator. preCICE ignores the communicator and continues.");
144#endif
145
146 Event e1("configure", profiling::Fundamental);
147 configure(configurationFileName);
148 e1.stop();
149
150 // Backend settings have been configured
151 Event e2("startProfilingBackend");
153 e2.stop();
154
155 PRECICE_DEBUG("Initialize intra-participant communication");
158 }
159
160 e.stop();
161 _solverInitEvent = std::make_unique<profiling::Event>("solver.initialize", profiling::Fundamental, profiling::Synchronize);
162}
163
165{
166 if (_state != State::Finalized) {
167 PRECICE_INFO("Implicitly finalizing in destructor");
168 finalize();
169 }
170}
171
173 std::string_view configurationFileName)
174{
175
182 _configHash = xml::configure(config.getXMLTag(), context, configurationFileName);
183 if (_accessorProcessRank == 0) {
184 PRECICE_INFO("This is preCICE version {}", PRECICE_VERSION);
185 PRECICE_INFO("Revision info: {}", precice::preciceRevision);
186 constexpr std::string_view buildTypeStr = "Build type: "
187#ifndef NDEBUG
188 "Debug"
189#else // NDEBUG
190 "Release"
191#ifndef PRECICE_NO_DEBUG_LOG
192 " + debug log"
193#else
194 " (without debug log)"
195#endif
196#ifndef PRECICE_NO_TRACE_LOG
197 " + trace log"
198#endif
199#ifndef PRECICE_NO_ASSERTIONS
200 " + assertions"
201#endif
202#endif // NDEBUG
203 ;
204 PRECICE_INFO(buildTypeStr);
205 try {
206 PRECICE_INFO("Working directory \"{}\"", std::filesystem::current_path().string());
207 } catch (std::filesystem::filesystem_error &fse) {
208 PRECICE_INFO("Working directory unknown due to error \"{}\"", fse.what());
209 }
210 PRECICE_INFO("Configuring preCICE with configuration \"{}\"", configurationFileName);
211 PRECICE_INFO("I am participant \"{}\"", _accessorName);
212 }
213
215
216 PRECICE_CHECK(config.getParticipantConfiguration()->nParticipants() > 1,
217 "In the preCICE configuration, only one participant is defined. "
218 "One participant makes no coupled simulation. "
219 "Please add at least another one.");
220
221 _allowsExperimental = config.allowsExperimental();
222 _allowsRemeshing = config.allowsRemeshing();
223 _waitInFinalize = config.waitInFinalize();
225 _participants = config.getParticipantConfiguration()->getParticipants();
226 _m2ns = config.getBoundM2NsFor(_accessorName);
227 config.configurePartitionsFor(_accessorName);
228 _couplingScheme = config.getCouplingSchemeConfiguration()->getCouplingScheme(_accessorName);
229
230 PRECICE_ASSERT(_accessorCommunicatorSize == 1 || _accessor->useIntraComm(),
231 "A parallel participant needs an intra-participant communication");
232 PRECICE_CHECK(not(_accessorCommunicatorSize == 1 && _accessor->useIntraComm()),
233 "You cannot use an intra-participant communication with a serial participant. "
234 "If you do not know exactly what an intra-participant communication is and why you want to use it "
235 "you probably just want to remove the intraComm tag from the preCICE configuration.");
236
238
239 // Register all MeshIds to the lock, but unlock them straight away as
240 // writing is allowed after configuration.
241 _meshLock.clear();
242 for (const auto &variant : _accessor->usedMeshContexts()) {
243 _meshLock.add(getMesh(variant).getName(), false);
244 }
245}
246
248{
250 PRECICE_CHECK(_state != State::Finalized, "initialize() cannot be called after finalize().");
251 PRECICE_CHECK(_state != State::Initialized, "initialize() may only be called once.");
252 PRECICE_ASSERT(not _couplingScheme->isInitialized());
253
255 PRECICE_CHECK(not failedToInitialize,
256 "Initial data has to be written to preCICE before calling initialize(). "
257 "After defining your mesh, call requiresInitialData() to check if the participant is required to write initial data using the writeData() function.");
258
259 // Enforce that all user-created events are stopped to prevent incorrect nesting.
260 PRECICE_CHECK(_userEvents.empty(), "There are unstopped user defined events. Please stop them using stopLastProfilingSection() before calling initialize().");
261
262 _solverInitEvent.reset();
264
265 for (const auto &context : _accessor->providedMeshContexts()) {
266 e.addData("meshSize" + context.mesh->getName(), context.mesh->nVertices());
267 }
268
270 setupWatcher();
271
272 _meshLock.lockAll();
273
274 for (auto &context : _accessor->writeDataContexts()) {
275 const double startTime = 0.0;
276 context.storeBufferedData(startTime);
277 }
278
281
282 PRECICE_DEBUG("Initialize coupling schemes");
283 Event e1("initalizeCouplingScheme", profiling::Fundamental);
284 _couplingScheme->initialize();
285 e1.stop();
286
289
291
293
294 e.stop();
295
297 PRECICE_INFO(_couplingScheme->printCouplingState());
298 _solverAdvanceEvent = std::make_unique<profiling::Event>("solver.advance", profiling::Fundamental, profiling::Synchronize);
299}
300
302{
305 PRECICE_INFO("Reinitializing Participant");
306 Event e("reinitialize", profiling::Fundamental);
308
309 for (const auto &context : _accessor->providedMeshContexts()) {
310 e.addData("meshSize" + context.mesh->getName(), context.mesh->nVertices());
311 }
312
314 setupWatcher();
315
316 PRECICE_DEBUG("Reinitialize coupling schemes");
317 _couplingScheme->reinitialize();
318}
319
321{
323
324 // TODO only preprocess changed meshes
325 PRECICE_DEBUG("Preprocessing provided meshes");
326 for (auto &context : _accessor->providedMeshContexts()) {
327 auto &mesh = *context.mesh;
328 Event e("preprocess." + mesh.getName());
329 mesh.preprocess();
330 }
331
332 // Setup communication
333
334 PRECICE_INFO("Setting up primary communication to coupling partner/s");
335 Event e2("connectPrimaries");
336 for (auto &m2nPair : _m2ns) {
337 auto &bm2n = m2nPair.second;
338 bool requesting = bm2n.isRequesting;
339 if (bm2n.m2n->isConnected()) {
340 PRECICE_DEBUG("Primary connection {} {} already connected.", (requesting ? "from" : "to"), bm2n.remoteName);
341 } else {
342 PRECICE_DEBUG((requesting ? "Awaiting primary connection from {}" : "Establishing primary connection to {}"), bm2n.remoteName);
343 bm2n.prepareEstablishment();
344 bm2n.connectPrimaryRanks(_configHash);
345 PRECICE_DEBUG("Established primary connection {} {}", (requesting ? "from " : "to "), bm2n.remoteName);
346 }
347 }
348 e2.stop();
349
350 PRECICE_INFO("Primary ranks are connected");
351
352 Event e3("repartitioning");
353 // clears the mappings as well (see clearMappings)
355
356 PRECICE_INFO("Setting up preliminary secondary communication to coupling partner/s");
357 for (auto &m2nPair : _m2ns) {
358 auto &bm2n = m2nPair.second;
359 bm2n.preConnectSecondaryRanks();
360 }
361
363 e3.stop();
364
365 PRECICE_INFO("Setting up secondary communication to coupling partner/s");
366 Event e4("connectSecondaries");
367 for (auto &m2nPair : _m2ns) {
368 auto &bm2n = m2nPair.second;
369 bm2n.connectSecondaryRanks();
370 PRECICE_DEBUG("Established secondary connection {} {}", (bm2n.isRequesting ? "from " : "to "), bm2n.remoteName);
371 }
372 PRECICE_INFO("Secondary ranks are connected");
373
374 for (auto &m2nPair : _m2ns) {
375 m2nPair.second.cleanupEstablishment();
376 }
377}
378
380{
382 PRECICE_DEBUG("Initialize watchpoints");
383 for (PtrWatchPoint &watchPoint : _accessor->watchPoints()) {
384 watchPoint->initialize();
385 }
386 for (PtrWatchIntegral &watchIntegral : _accessor->watchIntegrals()) {
387 watchIntegral->initialize();
388 }
389}
390
392 double computedTimeStepSize)
393{
394
395 PRECICE_TRACE(computedTimeStepSize);
396
397 // Enforce that all user-created events are stopped to prevent incorrect nesting.
398 PRECICE_CHECK(_userEvents.empty(), "There are unstopped user defined events. Please stop them using stopLastProfilingSection() before calling advance().");
399
400 // Events for the solver time, stopped when we enter, restarted when we leave advance
401 PRECICE_ASSERT(_solverAdvanceEvent, "The advance event is created in initialize");
402 _solverAdvanceEvent->stop();
403
405
406 PRECICE_CHECK(_state != State::Constructed, "initialize() has to be called before advance().");
407 PRECICE_CHECK(_state != State::Finalized, "advance() cannot be called after finalize().");
408 PRECICE_CHECK(_state == State::Initialized, "initialize() has to be called before advance().");
409 PRECICE_ASSERT(_couplingScheme->isInitialized());
410 PRECICE_CHECK(isCouplingOngoing(), "advance() cannot be called when isCouplingOngoing() returns false.");
411
412 // validating computed time step
413 PRECICE_CHECK(std::isfinite(computedTimeStepSize), "advance() cannot be called with an infinite time step size.");
414 PRECICE_CHECK(!math::equals(computedTimeStepSize, 0.0), "advance() cannot be called with a time step size of 0.");
415 PRECICE_CHECK(computedTimeStepSize > 0.0, "advance() cannot be called with a negative time step size {}.", computedTimeStepSize);
416
418
419#ifndef NDEBUG
420 PRECICE_DEBUG("Synchronize time step size");
422 syncTimestep(computedTimeStepSize);
423 }
424#endif
425
426 // Update the coupling scheme time state. Necessary to get correct remainder.
427 const bool isAtWindowEnd = _couplingScheme->addComputedTime(computedTimeStepSize);
428
429 if (_allowsRemeshing) {
430 if (isAtWindowEnd) {
431 auto totalMeshChanges = getTotalMeshChanges();
432 clearStamplesOfChangedMeshes(totalMeshChanges);
433
434 int sumOfChanges = std::accumulate(totalMeshChanges.begin(), totalMeshChanges.end(), 0);
435 if (reinitHandshake(sumOfChanges)) {
436 reinitialize();
437 }
438 } else {
439 PRECICE_CHECK(_meshLock.checkAll(), "The time window needs to end after remeshing.");
440 }
441 }
442
443 const double timeSteppedTo = _couplingScheme->getTime();
444 const auto dataToReceive = _couplingScheme->implicitDataToReceive();
445
446 handleDataBeforeAdvance(isAtWindowEnd, timeSteppedTo);
447
449
450 // In clase if an implicit scheme, this may be before timeSteppedTo
451 const double timeAfterAdvance = _couplingScheme->getTime();
452 const bool timeWindowComplete = _couplingScheme->isTimeWindowComplete();
453
454 handleDataAfterAdvance(isAtWindowEnd, timeWindowComplete, timeSteppedTo, timeAfterAdvance, dataToReceive);
455
456 PRECICE_INFO(_couplingScheme->printCouplingState());
457
458 PRECICE_DEBUG("Mapped {} samples in write mappings and {} samples in read mappings",
460
461 _meshLock.lockAll();
462
463 e.stop();
464 _solverAdvanceEvent->start();
465}
466
467void ParticipantImpl::handleDataBeforeAdvance(bool reachedTimeWindowEnd, double timeSteppedTo)
468{
469 // We only have to care about write data, in case substeps are enabled
470 // OR we are at the end of a timewindow, otherwise, we simply erase
471 // them as they have no relevance for the coupling (without time
472 // interpolation, only the time window end is relevant), the resetting
473 // happens regardless of the if-condition.
474 if (reachedTimeWindowEnd || _couplingScheme->requiresSubsteps()) {
475
476 // Here, we add the written data to the waveform storage. In the
477 // mapWrittenData, we then take samples from the storage and execute
478 // the mapping using waveform samples on the (for write mappings) "to"
479 // side.
480 samplizeWriteData(timeSteppedTo);
481 }
482
484
485 // Reset mapping counters here to cover subcycling
488
489 if (reachedTimeWindowEnd) {
490 mapWrittenData(_couplingScheme->getTimeWindowStart());
492 }
493}
494
495void ParticipantImpl::handleDataAfterAdvance(bool reachedTimeWindowEnd, bool isTimeWindowComplete, double timeSteppedTo, double timeAfterAdvance, const cplscheme::ImplicitData &receivedData)
496{
497 if (!reachedTimeWindowEnd) {
498 // We are subcycling
499 return;
500 }
501
503 // Move to next time window
504 PRECICE_ASSERT(math::greaterEquals(timeAfterAdvance, timeSteppedTo), "We must have stayed or moved forwards in time (min-time-step-size).", timeAfterAdvance, timeSteppedTo);
505
506 // As we move forward, there may now be old samples lying around
507 trimOldDataBefore(_couplingScheme->getTimeWindowStart());
508 } else {
509 // We are iterating
510 PRECICE_ASSERT(math::greater(timeSteppedTo, timeAfterAdvance), "We must have moved back in time!");
511
512 trimSendDataAfter(timeAfterAdvance);
513 }
514
515 if (reachedTimeWindowEnd) {
516 trimReadMappedData(timeAfterAdvance, isTimeWindowComplete, receivedData);
517 mapReadData();
519 }
520
521 // Required for implicit coupling
522 for (auto &context : _accessor->readDataContexts()) {
523 context.invalidateMappingCache();
524 }
525
526 // Strictly speaking, the write direction is not relevant here, but we will add it for the sake of completenss
527 for (auto &context : _accessor->writeDataContexts()) {
528 context.invalidateMappingCache();
529 }
530
532 // Reset initial guesses for iterative mappings
533 for (auto &context : _accessor->readDataContexts()) {
534 context.resetInitialGuesses();
535 }
536 for (auto &context : _accessor->writeDataContexts()) {
537 context.resetInitialGuesses();
538 }
539 }
540
542}
543
545{
546 // store buffered write data in sample storage and reset the buffer
547 for (auto &context : _accessor->writeDataContexts()) {
548
549 // Finalize conservative write mapping, later we reset
550 // the buffer in resetWrittenData
551
552 // Note that "samplizeWriteData" operates on _providedData of the
553 // DataContext, which is for just-in-time mappings the data we write
554 // on the received mesh.
555 // For just-in-time mappings, the _providedData should contain by now
556 // the "just-in-time" mapped data. However, it would be wasteful to
557 // execute expensive parts (in particular solving the RBF systems)
558 // for each writeAndMapData call. Thus, we create a DataCache during
559 // the writeAndMapData API calls, which contains pre-processed data
560 // values. Here, we now need to finalize the just-in-time mappings,
561 // before we can add it to the waveform buffer.
562 // For now, this only applies to just-in-time write mappings
563
564 context.completeJustInTimeMapping();
565 context.storeBufferedData(time);
566 }
567}
568
570{
571 for (auto &variant : _accessor->usedMeshContexts()) {
572 auto &mesh = getMesh(variant);
573 for (const auto &name : mesh.availableData()) {
574 mesh.data(name)->waveform().trimBefore(time);
575 }
576 }
577}
578
580{
581 for (auto &context : _accessor->writeDataContexts()) {
582 context.trimAfter(time);
583 }
584}
585
587{
589 PRECICE_CHECK(_state != State::Finalized, "finalize() may only be called once.");
590
591 // First we gracefully stop all existing user events and finally the last solver.advance event
592 while (!_userEvents.empty()) {
593 // Ensure reverse destruction order for correct nesting
594 _userEvents.pop_back();
595 }
596 _solverAdvanceEvent.reset();
597
598 Event e("finalize", profiling::Fundamental);
599
600 if (_state == State::Initialized) {
601
602 PRECICE_ASSERT(_couplingScheme->isInitialized());
603 PRECICE_DEBUG("Finalize coupling scheme");
604 _couplingScheme->finalize();
605
607 }
608
609 // Release ownership
610 _couplingScheme.reset();
611 _participants.clear();
612 _accessor.reset();
613
614 // Close Connections
615 PRECICE_DEBUG("Close intra-participant communication");
617 utils::IntraComm::getCommunication()->closeConnection();
619 }
620 _m2ns.clear();
621
622 // Stop and print Event logging
623 e.stop();
624
625 // Finalize PETSc and Events first
627// This will lead to issues if we call finalize afterwards again
628#if !defined(PRECICE_NO_GINKGO) || !defined(PRECICE_NO_KOKKOS_KERNELS)
630#endif
632
633 // Finally clear events and finalize MPI
636}
637
638int ParticipantImpl::getMeshDimensions(std::string_view meshName) const
639{
640 PRECICE_TRACE(meshName);
642 return _accessor->meshContext(meshName).mesh->getDimensions();
643}
644
645int ParticipantImpl::getDataDimensions(std::string_view meshName, std::string_view dataName) const
646{
647 PRECICE_TRACE(meshName, dataName);
649 PRECICE_VALIDATE_DATA_NAME(meshName, dataName);
650 return _accessor->meshContext(meshName).mesh->data(dataName)->getDimensions();
651}
652
654{
656 PRECICE_CHECK(_state != State::Finalized, "isCouplingOngoing() cannot be called after finalize().");
657 PRECICE_CHECK(_state == State::Initialized, "initialize() has to be called before isCouplingOngoing() can be evaluated.");
658 return _couplingScheme->isCouplingOngoing();
659}
660
662{
664 PRECICE_CHECK(_state != State::Constructed, "initialize() has to be called before isTimeWindowComplete().");
665 PRECICE_CHECK(_state != State::Finalized, "isTimeWindowComplete() cannot be called after finalize().");
666 return _couplingScheme->isTimeWindowComplete();
667}
668
670{
671 PRECICE_CHECK(_state != State::Finalized, "getMaxTimeStepSize() cannot be called after finalize().");
672 PRECICE_CHECK(_state == State::Initialized, "initialize() has to be called before getMaxTimeStepSize() can be evaluated.");
673 const double nextTimeStepSize = _couplingScheme->getNextTimeStepMaxSize();
674 // PRECICE_ASSERT(!math::equals(nextTimeStepSize, 0.0), nextTimeStepSize); // @todo requires https://github.com/precice/precice/issues/1904
675 // PRECICE_ASSERT(math::greater(nextTimeStepSize, 0.0), nextTimeStepSize); // @todo requires https://github.com/precice/precice/issues/1904
676
677 // safeguard needed because _couplingScheme->getNextTimeStepMaxSize() returns 0, if not isCouplingOngoing()
678 // actual case where we want to warn the user
680 isCouplingOngoing() && not math::greater(nextTimeStepSize, 0.0, 100 * math::NUMERICAL_ZERO_DIFFERENCE),
681 "preCICE just returned a maximum time step size of {}. Such a small value can happen if you use many substeps per time window over multiple time windows due to added-up differences of machine precision.",
682 nextTimeStepSize);
683 return nextTimeStepSize;
684}
685
687{
689 PRECICE_CHECK(_state == State::Constructed, "requiresInitialData() has to be called before initialize().");
691 if (required) {
693 }
694 return required;
695}
696
698{
700 PRECICE_CHECK(_state == State::Initialized, "initialize() has to be called before requiresWritingCheckpoint().");
702 if (required) {
704 }
705 return required;
706}
707
709{
711 PRECICE_CHECK(_state == State::Initialized, "initialize() has to be called before requiresReadingCheckpoint().");
713 if (required) {
715 }
716 return required;
717}
718
719bool ParticipantImpl::requiresMeshConnectivityFor(std::string_view meshName) const
720{
722 MeshContext &context = _accessor->meshContext(meshName);
724}
725
726bool ParticipantImpl::requiresGradientDataFor(std::string_view meshName,
727 std::string_view dataName) const
728{
729 PRECICE_VALIDATE_DATA_NAME(meshName, dataName);
730 // Read data never requires gradients
731 if (!_accessor->isDataWrite(meshName, dataName))
732 return false;
733
734 WriteDataContext &context = _accessor->writeDataContext(meshName, dataName);
735 return context.hasGradient();
736}
737
739 std::string_view meshName) const
740{
741 PRECICE_TRACE(meshName);
742 PRECICE_REQUIRE_MESH_USE(meshName);
743 // In case we access received mesh data: check, if the requested mesh data has already been received.
744 // Otherwise, the function call doesn't make any sense
745 PRECICE_CHECK((_state == State::Initialized) || _accessor->isMeshProvided(meshName),
746 "initialize() has to be called before accessing data of the received mesh \"{}\" on participant \"{}\".",
747 meshName, _accessor->getName());
748
749 // Returns true if we have api access configured and we run in parallel and have a received mesh
750 if (_accessor->isMeshReceived(meshName) && _accessor->isDirectAccessAllowed(meshName)) {
751 auto &receivedContext = _accessor->receivedMeshContext(meshName);
752 if (receivedContext.userDefinedAccessRegion || requiresUserDefinedAccessRegion(meshName)) {
753 // filter nVertices to the actual number of vertices queried by the user
754 PRECICE_CHECK(receivedContext.userDefinedAccessRegion, "The function getMeshVertexSize was called on the received mesh \"{0}\", "
755 "but no access region was defined although this is necessary for parallel runs. "
756 "Please define an access region using \"setMeshAccessRegion()\" before calling \"getMeshVertexSize()\".",
757 meshName);
758
759 auto result = mesh::countVerticesInBoundingBox(receivedContext.mesh, *receivedContext.userDefinedAccessRegion);
760
761 PRECICE_DEBUG("Filtered {} of {} vertices out on mesh {} due to the local access region. Mesh size in the access region: {}", receivedContext.mesh->nVertices() - result, receivedContext.mesh->nVertices(), meshName, result);
762 return result;
763 }
764 }
765 // For provided meshes and in case the api-access was not configured, we return here all vertices
766 PRECICE_WARN_IF(_accessor->isMeshReceived(meshName) && !_accessor->isDirectAccessAllowed(meshName),
767 "You are calling \"getMeshVertexSize()\" on a received mesh without api-access enabled (<receive-mesh name=\"{0}\" ... api-access=\"false\"/>). "
768 "Note that enabling api-access is required for this function to work properly with direct mesh access and just-in-time mappings.",
769 meshName);
770 return _accessor->meshContext(meshName).mesh->nVertices();
771}
772
775 std::string_view meshName)
776{
778 PRECICE_CHECK(_allowsRemeshing, "Cannot reset meshes. This feature needs to be enabled using <precice-configuration experimental=\"1\" allow-remeshing=\"1\">.");
779 PRECICE_CHECK(_state == State::Initialized, "initialize() has to be called before resetMesh().");
780 PRECICE_TRACE(meshName);
782 PRECICE_CHECK(_couplingScheme->isCouplingOngoing(), "Cannot remesh after the last time window has been completed.");
783 PRECICE_CHECK(_couplingScheme->isTimeWindowComplete(), "Cannot remesh while subcycling or iterating. Remeshing is only allowed when the time window is completed.");
784 impl::MeshContext &context = _accessor->meshContext(meshName);
785
786 PRECICE_DEBUG("Clear mesh positions for mesh \"{}\"", context.mesh->getName());
787 _meshLock.unlock(meshName);
788 context.mesh->clear();
789}
790
792 std::string_view meshName,
794{
795 PRECICE_TRACE(meshName);
797 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
798 auto &mesh = *context.mesh;
799 PRECICE_CHECK(position.size() == static_cast<unsigned long>(mesh.getDimensions()),
800 "Cannot set vertex for mesh \"{}\". Expected {} position components but found {}.", meshName, mesh.getDimensions(), position.size());
801 Event e{fmt::format("setMeshVertex.{}", meshName), profiling::API};
802 auto index = mesh.createVertex(Eigen::Map<const Eigen::VectorXd>{position.data(), mesh.getDimensions()}).getID();
803 mesh.allocateDataValues();
804
805 const auto newSize = mesh.nVertices();
806 for (auto &context : _accessor->writeDataContexts()) {
807 if (context.getMeshName() == mesh.getName()) {
808 context.resizeBufferTo(newSize);
809 }
810 }
811
812 return index;
813}
814
816 std::string_view meshName,
819{
820 PRECICE_TRACE(meshName, positions.size(), ids.size());
822 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
823 auto &mesh = *context.mesh;
824
825 const auto meshDims = mesh.getDimensions();
826 const auto expectedPositionSize = ids.size() * meshDims;
827 PRECICE_CHECK(positions.size() == expectedPositionSize,
828 "Input sizes are inconsistent attempting to set vertices on {}D mesh \"{}\". "
829 "You passed {} vertex indices and {} position components, but we expected {} position components ({} x {}).",
830 meshDims, meshName, ids.size(), positions.size(), expectedPositionSize, ids.size(), meshDims);
831
832 Event e{fmt::format("setMeshVertices.{}", meshName), profiling::API};
833 const Eigen::Map<const Eigen::MatrixXd> posMatrix{
834 positions.data(), mesh.getDimensions(), static_cast<EIGEN_DEFAULT_DENSE_INDEX_TYPE>(ids.size())};
835 for (unsigned long i = 0; i < ids.size(); ++i) {
836 ids[i] = mesh.createVertex(posMatrix.col(i)).getID();
837 }
838 mesh.allocateDataValues();
839
840 const auto newSize = mesh.nVertices();
841 for (auto &context : _accessor->writeDataContexts()) {
842 if (context.getMeshName() == mesh.getName()) {
843 context.resizeBufferTo(newSize);
844 }
845 }
846}
847
849 std::string_view meshName,
850 VertexID first,
851 VertexID second)
852{
853 PRECICE_TRACE(meshName, first, second);
855 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
857 return;
858 }
859
860 mesh::Mesh &mesh = *context.mesh;
862 PRECICE_CHECK(mesh.isValidVertexID(first), errorInvalidVertexID(first));
863 PRECICE_CHECK(mesh.isValidVertexID(second), errorInvalidVertexID(second));
864 Event e{fmt::format("setMeshEdge.{}", meshName), profiling::API};
865 mesh::Vertex &v0 = mesh.vertex(first);
866 mesh::Vertex &v1 = mesh.vertex(second);
867 mesh.createEdge(v0, v1);
868}
869
871 std::string_view meshName,
873{
874 PRECICE_TRACE(meshName, vertices.size());
876 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
878 return;
879 }
880
881 mesh::Mesh &mesh = *context.mesh;
882 PRECICE_CHECK(vertices.size() % 2 == 0,
883 "Cannot interpret passed vertex IDs attempting to set edges of mesh \"{}\" . "
884 "You passed {} vertex indices, but we expected an even number.",
885 meshName, vertices.size());
886 {
887 auto end = vertices.end();
888 auto [first, last] = utils::find_first_range(vertices.begin(), end, [&mesh](VertexID vid) {
889 return !mesh.isValidVertexID(vid);
890 });
891 PRECICE_CHECK(first == end,
893 std::distance(vertices.begin(), first),
894 std::distance(vertices.begin(), last));
895 }
896
897 Event e{fmt::format("setMeshEdges.{}", meshName), profiling::API};
898
899 for (unsigned long i = 0; i < vertices.size() / 2; ++i) {
900 auto aid = vertices[2 * i];
901 auto bid = vertices[2 * i + 1];
902 mesh.createEdge(mesh.vertex(aid), mesh.vertex(bid));
903 }
904}
905
907 std::string_view meshName,
908 VertexID first,
909 VertexID second,
910 VertexID third)
911{
912 PRECICE_TRACE(meshName, first, second, third);
914 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
916 return;
917 }
918
919 mesh::Mesh &mesh = *context.mesh;
921 PRECICE_CHECK(mesh.isValidVertexID(first), errorInvalidVertexID(first));
922 PRECICE_CHECK(mesh.isValidVertexID(second), errorInvalidVertexID(second));
923 PRECICE_CHECK(mesh.isValidVertexID(third), errorInvalidVertexID(third));
925 "setMeshTriangle() was called with repeated Vertex IDs ({}, {}, {}).",
926 first, second, third);
927
928 mesh::Vertex &A = mesh.vertex(first);
929 mesh::Vertex &B = mesh.vertex(second);
930 mesh::Vertex &C = mesh.vertex(third);
931
932 mesh.createTriangle(A, B, C);
933}
934
936 std::string_view meshName,
938{
939 PRECICE_TRACE(meshName, vertices.size());
941 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
943 return;
944 }
945
946 mesh::Mesh &mesh = *context.mesh;
947 PRECICE_CHECK(vertices.size() % 3 == 0,
948 "Cannot interpret passed vertex IDs attempting to set triangles of mesh \"{}\" . "
949 "You passed {} vertex indices, which isn't dividable by 3.",
950 meshName, vertices.size());
951 {
952 auto end = vertices.end();
953 auto [first, last] = utils::find_first_range(vertices.begin(), end, [&mesh](VertexID vid) {
954 return !mesh.isValidVertexID(vid);
955 });
956 PRECICE_CHECK(first == end,
958 std::distance(vertices.begin(), first),
959 std::distance(vertices.begin(), last));
960 }
961
962 Event e{fmt::format("setMeshTriangles.{}", meshName), profiling::API};
963
964 for (unsigned long i = 0; i < vertices.size() / 3; ++i) {
965 auto aid = vertices[3 * i];
966 auto bid = vertices[3 * i + 1];
967 auto cid = vertices[3 * i + 2];
968 mesh.createTriangle(mesh.vertex(aid),
969 mesh.vertex(bid),
970 mesh.vertex(cid));
971 }
972}
973
975 std::string_view meshName,
976 VertexID first,
977 VertexID second,
978 VertexID third,
979 VertexID fourth)
980{
981 PRECICE_TRACE(meshName, first,
982 second, third, fourth);
984 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
986 return;
987 }
988
989 PRECICE_ASSERT(context.mesh);
990 mesh::Mesh &mesh = *context.mesh;
992 PRECICE_CHECK(mesh.isValidVertexID(first), errorInvalidVertexID(first));
993 PRECICE_CHECK(mesh.isValidVertexID(second), errorInvalidVertexID(second));
994 PRECICE_CHECK(mesh.isValidVertexID(third), errorInvalidVertexID(third));
995 PRECICE_CHECK(mesh.isValidVertexID(fourth), errorInvalidVertexID(fourth));
996
997 auto vertexIDs = utils::make_array(first, second, third, fourth);
998 PRECICE_CHECK(utils::unique_elements(vertexIDs), "The four vertex ID's are not unique. Please check that the vertices that form the quad are correct.");
999
1000 auto coords = mesh::coordsFor(mesh, vertexIDs);
1002 "The four vertices that form the quad are not unique. The resulting shape may be a point, line or triangle. "
1003 "Please check that the adapter sends the four unique vertices that form the quad, or that the mesh on the interface is composed of quads.");
1004
1005 auto convexity = math::geometry::isConvexQuad(coords);
1006 PRECICE_CHECK(convexity.convex, "The given quad is not convex. "
1007 "Please check that the adapter send the four correct vertices or that the interface is composed of quads.");
1008 auto reordered = utils::reorder_array(convexity.vertexOrder, mesh::vertexPtrsFor(mesh, vertexIDs));
1009
1010 Event e{fmt::format("setMeshQuad.{}", meshName), profiling::API};
1011
1012 // Vertices are now in the order: V0-V1-V2-V3-V0.
1013 // Use the shortest diagonal to split the quad into 2 triangles.
1014 // Vertices are now in V0-V1-V2-V3-V0 order. The new edge, e[4] is either 0-2 or 1-3
1015 double distance02 = (reordered[0]->getCoords() - reordered[2]->getCoords()).norm();
1016 double distance13 = (reordered[1]->getCoords() - reordered[3]->getCoords()).norm();
1017
1018 // The new edge, e[4], is the shortest diagonal of the quad
1019 if (distance02 <= distance13) {
1020 mesh.createTriangle(*reordered[0], *reordered[2], *reordered[1]);
1021 mesh.createTriangle(*reordered[0], *reordered[2], *reordered[3]);
1022 } else {
1023 mesh.createTriangle(*reordered[1], *reordered[3], *reordered[0]);
1024 mesh.createTriangle(*reordered[1], *reordered[3], *reordered[2]);
1025 }
1026}
1027
1029 std::string_view meshName,
1031{
1032 PRECICE_TRACE(meshName, vertices.size());
1034 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
1036 return;
1037 }
1038
1039 mesh::Mesh &mesh = *context.mesh;
1040 PRECICE_CHECK(vertices.size() % 4 == 0,
1041 "Cannot interpret passed vertex IDs attempting to set quads of mesh \"{}\" . "
1042 "You passed {} vertex indices, which isn't dividable by 4.",
1043 meshName, vertices.size());
1044 {
1045 auto end = vertices.end();
1046 auto [first, last] = utils::find_first_range(vertices.begin(), end, [&mesh](VertexID vid) {
1047 return !mesh.isValidVertexID(vid);
1048 });
1049 PRECICE_CHECK(first == end,
1051 std::distance(vertices.begin(), first),
1052 std::distance(vertices.begin(), last));
1053 }
1054
1055 for (unsigned long i = 0; i < vertices.size() / 4; ++i) {
1056 auto aid = vertices[4 * i];
1057 auto bid = vertices[4 * i + 1];
1058 auto cid = vertices[4 * i + 2];
1059 auto did = vertices[4 * i + 3];
1060
1061 auto vertexIDs = utils::make_array(aid, bid, cid, did);
1062 PRECICE_CHECK(utils::unique_elements(vertexIDs), "The four vertex ID's of the quad nr {} are not unique. Please check that the vertices that form the quad are correct.", i);
1063
1064 auto coords = mesh::coordsFor(mesh, vertexIDs);
1066 "The four vertices that form the quad nr {} are not unique. The resulting shape may be a point, line or triangle. "
1067 "Please check that the adapter sends the four unique vertices that form the quad, or that the mesh on the interface is composed of quads.",
1068 i);
1069
1070 auto convexity = math::geometry::isConvexQuad(coords);
1071 PRECICE_CHECK(convexity.convex, "The given quad nr {} is not convex. "
1072 "Please check that the adapter send the four correct vertices or that the interface is composed of quads.",
1073 i);
1074 auto reordered = utils::reorder_array(convexity.vertexOrder, mesh::vertexPtrsFor(mesh, vertexIDs));
1075
1076 Event e{fmt::format("setMeshQuads.{}", meshName), profiling::API};
1077
1078 // Use the shortest diagonal to split the quad into 2 triangles.
1079 // Vertices are now in V0-V1-V2-V3-V0 order. The new edge, e[4] is either 0-2 or 1-3
1080 double distance02 = (reordered[0]->getCoords() - reordered[2]->getCoords()).norm();
1081 double distance13 = (reordered[1]->getCoords() - reordered[3]->getCoords()).norm();
1082
1083 if (distance02 <= distance13) {
1084 mesh.createTriangle(*reordered[0], *reordered[2], *reordered[1]);
1085 mesh.createTriangle(*reordered[0], *reordered[2], *reordered[3]);
1086 } else {
1087 mesh.createTriangle(*reordered[1], *reordered[3], *reordered[0]);
1088 mesh.createTriangle(*reordered[1], *reordered[3], *reordered[2]);
1089 }
1090 }
1091}
1092
1094 std::string_view meshName,
1095 VertexID first,
1096 VertexID second,
1097 VertexID third,
1098 VertexID fourth)
1099{
1100 PRECICE_TRACE(meshName, first, second, third, fourth);
1102 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
1103 PRECICE_CHECK(context.mesh->getDimensions() == 3, "setMeshTetrahedron is only possible for 3D meshes. "
1104 "Please set the mesh dimension to 3 in the preCICE configuration file.");
1106 return;
1107 }
1108
1109 Event e{fmt::format("setMeshTetrahedron.{}", meshName), profiling::API};
1110
1111 mesh::Mesh &mesh = *context.mesh;
1113 PRECICE_CHECK(mesh.isValidVertexID(first), errorInvalidVertexID(first));
1114 PRECICE_CHECK(mesh.isValidVertexID(second), errorInvalidVertexID(second));
1115 PRECICE_CHECK(mesh.isValidVertexID(third), errorInvalidVertexID(third));
1116 PRECICE_CHECK(mesh.isValidVertexID(fourth), errorInvalidVertexID(fourth));
1117 mesh::Vertex &A = mesh.vertex(first);
1118 mesh::Vertex &B = mesh.vertex(second);
1119 mesh::Vertex &C = mesh.vertex(third);
1120 mesh::Vertex &D = mesh.vertex(fourth);
1121
1122 mesh.createTetrahedron(A, B, C, D);
1123}
1124
1126 std::string_view meshName,
1128{
1129 PRECICE_TRACE(meshName, vertices.size());
1131 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
1132 PRECICE_CHECK(context.mesh->getDimensions() == 3, "setMeshTetrahedron is only possible for 3D meshes. "
1133 "Please set the mesh dimension to 3 in the preCICE configuration file.");
1135 return;
1136 }
1137
1138 mesh::Mesh &mesh = *context.mesh;
1139 PRECICE_CHECK(vertices.size() % 4 == 0,
1140 "Cannot interpret passed vertex IDs attempting to set quads of mesh \"{}\" . "
1141 "You passed {} vertex indices, which isn't dividable by 4.",
1142 meshName, vertices.size());
1143 {
1144 auto end = vertices.end();
1145 auto [first, last] = utils::find_first_range(vertices.begin(), end, [&mesh](VertexID vid) {
1146 return !mesh.isValidVertexID(vid);
1147 });
1148 PRECICE_CHECK(first == end,
1150 std::distance(vertices.begin(), first),
1151 std::distance(vertices.begin(), last));
1152 }
1153
1154 Event e{fmt::format("setMeshTetrahedra.{}", meshName), profiling::API};
1155
1156 for (unsigned long i = 0; i < vertices.size() / 4; ++i) {
1157 auto aid = vertices[4 * i];
1158 auto bid = vertices[4 * i + 1];
1159 auto cid = vertices[4 * i + 2];
1160 auto did = vertices[4 * i + 3];
1161 mesh.createTetrahedron(mesh.vertex(aid),
1162 mesh.vertex(bid),
1163 mesh.vertex(cid),
1164 mesh.vertex(did));
1165 }
1166}
1167
1169 std::string_view meshName,
1170 std::string_view dataName,
1173{
1174 PRECICE_TRACE(meshName, dataName, vertices.size());
1175 PRECICE_CHECK(_state != State::Finalized, "writeData(...) cannot be called after finalize().");
1176 PRECICE_CHECK(_state == State::Constructed || (_state == State::Initialized && isCouplingOngoing()), "Calling writeData(...) is forbidden if coupling is not ongoing, because the data you are trying to write will not be used anymore. You can fix this by always calling writeData(...) before the advance(...) call in your simulation loop or by using Participant::isCouplingOngoing() to implement a safeguard.");
1177 PRECICE_REQUIRE_DATA_WRITE(meshName, dataName);
1178 // Inconsistent sizes will be handled below
1179 if (vertices.empty() && values.empty()) {
1180 return;
1181 }
1182
1183 WriteDataContext &context = _accessor->writeDataContext(meshName, dataName);
1184
1185 const auto dataDims = context.getDataDimensions();
1186 const auto expectedDataSize = vertices.size() * dataDims;
1187 PRECICE_CHECK(expectedDataSize == values.size(),
1188 "Input sizes are inconsistent attempting to write {}D data \"{}\" to mesh \"{}\". "
1189 "You passed {} vertex indices and {} data components, but we expected {} data components ({} x {}).",
1190 dataDims, dataName, meshName,
1191 vertices.size(), values.size(), expectedDataSize, dataDims, vertices.size());
1192
1193 // Sizes are correct at this point
1194 PRECICE_VALIDATE_DATA(values.data(), values.size()); // TODO Only take span
1195
1196 if (auto index = context.locateInvalidVertexID(vertices); index) {
1197 PRECICE_ERROR("Cannot write data \"{}\" to mesh \"{}\" due to invalid Vertex ID at vertices[{}]. "
1198 "Please make sure you only use the results from calls to setMeshVertex/Vertices().",
1199 dataName, meshName, *index);
1200 }
1201 Event e{fmt::format("writeData.{}_{}", meshName, dataName), profiling::API};
1202 context.writeValuesIntoDataBuffer(vertices, values);
1203}
1204
1206 std::string_view meshName,
1207 std::string_view dataName,
1209 double relativeReadTime,
1210 ::precice::span<double> values) const
1211{
1212 PRECICE_TRACE(meshName, dataName, vertices.size(), relativeReadTime);
1213 PRECICE_CHECK(_state != State::Constructed, "readData(...) cannot be called before initialize().");
1214 PRECICE_CHECK(_state != State::Finalized, "readData(...) cannot be called after finalize().");
1215 PRECICE_CHECK(math::smallerEquals(relativeReadTime, _couplingScheme->getNextTimeStepMaxSize()), "readData(...) cannot sample data outside of current time window.");
1216 PRECICE_CHECK(relativeReadTime >= 0, "readData(...) cannot sample data before the current time.");
1217 PRECICE_CHECK(isCouplingOngoing() || math::equals(relativeReadTime, 0.0), "Calling readData(...) with relativeReadTime = {} is forbidden if coupling is not ongoing. If coupling finished, only data for relativeReadTime = 0 is available. Please always use precice.getMaxTimeStepSize() to obtain the maximum allowed relativeReadTime.", relativeReadTime);
1218
1219 PRECICE_REQUIRE_DATA_READ(meshName, dataName);
1220
1221 PRECICE_CHECK(_meshLock.check(meshName),
1222 "Cannot read from mesh \"{}\" after it has been reset. Please read data before calling resetMesh().",
1223 meshName);
1224
1225 // Inconsistent sizes will be handled below
1226 if (vertices.empty() && values.empty()) {
1227 return;
1228 }
1229
1230 ReadDataContext &context = _accessor->readDataContext(meshName, dataName);
1231 PRECICE_CHECK(context.hasSamples(), "Data \"{}\" cannot be read from mesh \"{}\" as it contains no samples. "
1232 "This is typically a configuration issue of the data flow. "
1233 "Check if the data is correctly exchanged to this participant \"{}\" and mapped to mesh \"{}\".",
1234 dataName, meshName, _accessorName, meshName);
1235
1236 const auto dataDims = context.getDataDimensions();
1237 const auto expectedDataSize = vertices.size() * dataDims;
1238 PRECICE_CHECK(expectedDataSize == values.size(),
1239 "Input/Output sizes are inconsistent attempting to read {}D data \"{}\" from mesh \"{}\". "
1240 "You passed {} vertex indices and {} data components, but we expected {} data components ({} x {}).",
1241 dataDims, dataName, meshName,
1242 vertices.size(), values.size(), expectedDataSize, dataDims, vertices.size());
1243
1244 if (auto index = context.locateInvalidVertexID(vertices); index) {
1245 PRECICE_ERROR("Cannot read data \"{}\" from mesh \"{}\" due to invalid Vertex ID at vertices[{}]. "
1246 "Please make sure you only use the results from calls to setMeshVertex/Vertices().",
1247 dataName, meshName, *index);
1248 }
1249
1250 Event e{fmt::format("readData.{}_{}", meshName, dataName), profiling::API};
1251
1252 double readTime = _couplingScheme->getTime() + relativeReadTime;
1253 context.readValues(vertices, readTime, values);
1254}
1255
1257 std::string_view meshName,
1258 std::string_view dataName,
1260 double relativeReadTime,
1261 ::precice::span<double> values) const
1262{
1264 PRECICE_TRACE(meshName, dataName, coordinates.size(), relativeReadTime);
1265 PRECICE_CHECK(_state != State::Constructed, "mapAndReadData(...) cannot be called before initialize().");
1266 PRECICE_CHECK(_state != State::Finalized, "mapAndReadData(...) cannot be called after finalize().");
1267 PRECICE_CHECK(math::smallerEquals(relativeReadTime, _couplingScheme->getNextTimeStepMaxSize()), "readData(...) cannot sample data outside of current time window.");
1268 PRECICE_CHECK(relativeReadTime >= 0, "mapAndReadData(...) cannot sample data before the current time.");
1269 PRECICE_CHECK(isCouplingOngoing() || math::equals(relativeReadTime, 0.0), "Calling mapAndReadData(...) with relativeReadTime = {} is forbidden if coupling is not ongoing. If coupling finished, only data for relativeReadTime = 0 is available. Please always use precice.getMaxTimeStepSize() to obtain the maximum allowed relativeReadTime.", relativeReadTime);
1270
1271 PRECICE_REQUIRE_DATA_READ(meshName, dataName);
1272 PRECICE_VALIDATE_DATA(coordinates.begin(), coordinates.size());
1273
1274 PRECICE_CHECK(_accessor->isMeshReceived(meshName) && _accessor->isDirectAccessAllowed(meshName),
1275 "This participant attempteded to map and read data (via \"mapAndReadData\") from mesh \"{0}\", "
1276 "but mesh \"{0}\" is either not a received mesh or its api access was not enabled in the configuration. "
1277 "mapAndReadData({0}, ...) is only valid for (<receive-mesh name=\"{0}\" ... api-access=\"true\"/>).",
1278 meshName);
1279 // If an access region is required, we have to check its existence
1280 bool requiresBB = requiresUserDefinedAccessRegion(meshName);
1281 PRECICE_CHECK(!requiresBB || (requiresBB && _accessor->receivedMeshContext(meshName).userDefinedAccessRegion),
1282 "The function \"mapAndReadData\" was called on mesh \"{0}\", "
1283 "but no access region was defined although this is necessary for parallel runs. "
1284 "Please define an access region using \"setMeshAccessRegion()\" before calling \"mapAndReadData()\".",
1285 meshName);
1286
1287 PRECICE_CHECK(!_accessor->receivedMeshContext(meshName).mesh->empty(), "This participant tries to mapAndRead data values for data \"{0}\" on mesh \"{1}\", but the mesh \"{1}\" is empty within the defined access region on this rank. "
1288 "How should the provided data values be read? Please make sure the mesh \"{1}\" is non-empty within the access region.",
1289 dataName, meshName);
1290
1291 ReadDataContext &dataContext = _accessor->readDataContext(meshName, dataName);
1292 PRECICE_CHECK(dataContext.hasJustInTimeMapping(),
1293 "The function \"mapAndReadData\" was called on mesh \"{0}\", but no matching just-in-time mapping was configured. "
1294 "Please define a mapping in read direction from the mesh \"{0}\" and omit the \"to\" attribute from the definition. "
1295 "Example \"<mapping:nearest-neighbor direction=\"read\" from=\"{0}\" constraint=\"consistent\" />",
1296 meshName);
1297
1298 // Inconsistent sizes will be handled below
1299 if (coordinates.empty() && values.empty()) {
1300 return;
1301 }
1302
1303 Event e{fmt::format("mapAndReadData.{}_{}", meshName, dataName), profiling::API};
1304
1305 // Note that meshName refers to a remote mesh
1306 const auto dataDims = dataContext.getDataDimensions();
1307 const auto dim = dataContext.getSpatialDimensions();
1308 const auto nVertices = (coordinates.size() / dim);
1309
1310 // Check that the vertex is actually within the defined access region
1311 _accessor->receivedMeshContext(meshName).checkVerticesInsideAccessRegion(coordinates, dim, "mapAndReadData");
1312
1313 // Make use of the read data context
1314 PRECICE_CHECK(nVertices * dataDims == values.size(),
1315 "Input sizes are inconsistent attempting to mapAndRead {}D data \"{}\" from mesh \"{}\". "
1316 "You passed {} vertex indices and {} data components, but we expected {} data components ({} x {}).",
1317 dataDims, dataName, meshName,
1318 nVertices, values.size(), nVertices * dataDims, dataDims, nVertices);
1319
1320 double readTime = _couplingScheme->getTime() + relativeReadTime;
1321 dataContext.mapAndReadValues(coordinates, readTime, values);
1322}
1323
1325 std::string_view meshName,
1326 std::string_view dataName,
1329{
1331 PRECICE_TRACE(meshName, dataName, coordinates.size());
1332 PRECICE_CHECK(_state != State::Finalized, "writeAndMapData(...) cannot be called after finalize().");
1333 PRECICE_CHECK(_state != State::Constructed, "writeAndMapData(...) cannot be called before initialize(), because the mesh to map onto hasn't been received yet.");
1334 PRECICE_CHECK(_state == State::Initialized && isCouplingOngoing(), "Calling writeAndMapData(...) is forbidden if coupling is not ongoing, because the data you are trying to write will not be used anymore. You can fix this by always calling writeAndMapData(...) before the advance(...) call in your simulation loop or by using Participant::isCouplingOngoing() to implement a safeguard.");
1335 PRECICE_REQUIRE_DATA_WRITE(meshName, dataName);
1336
1337 PRECICE_VALIDATE_DATA(coordinates.begin(), coordinates.size());
1338 PRECICE_VALIDATE_DATA(values.data(), values.size());
1339 PRECICE_CHECK(_accessor->isMeshReceived(meshName) && _accessor->isDirectAccessAllowed(meshName),
1340 "This participant attempteded to map and read data (via \"writeAndMapData\") from mesh \"{0}\", "
1341 "but mesh \"{0}\" is either not a received mesh or its api access was not enabled in the configuration. "
1342 "writeAndMapData({0}, ...) is only valid for (<receive-mesh name=\"{0}\" ... api-access=\"true\"/>).",
1343 meshName);
1344 // If an access region is required, we have to check its existence
1345 bool requiresBB = requiresUserDefinedAccessRegion(meshName);
1346 PRECICE_CHECK(!requiresBB || (requiresBB && _accessor->receivedMeshContext(meshName).userDefinedAccessRegion),
1347 "The function \"writeAndMapData\" was called on mesh \"{0}\", "
1348 "but no access region was defined although this is necessary for parallel runs. "
1349 "Please define an access region using \"setMeshAccessRegion()\" before calling \"writeAndMapData()\".",
1350 meshName);
1351
1352 WriteDataContext &dataContext = _accessor->writeDataContext(meshName, dataName);
1353 PRECICE_CHECK(dataContext.hasJustInTimeMapping(),
1354 "The function \"writeAndMapData\" was called on mesh \"{0}\", but no matching just-in-time mapping was configured. "
1355 "Please define a mapping in write direction to the mesh \"{0}\" and omit the \"from\" attribute from the definition. "
1356 "Example \"<mapping:nearest-neighbor direction=\"write\" to=\"{0}\" constraint=\"conservative\" />",
1357 meshName);
1358
1359 // Inconsistent sizes will be handled below
1360 if (coordinates.empty() && values.empty()) {
1361 return;
1362 }
1363
1364 Event e{fmt::format("writeAndMapData.{}_{}", meshName, dataName), profiling::API};
1365
1366 // Note that meshName refers here typically to a remote mesh
1367 const auto dataDims = dataContext.getDataDimensions();
1368 const auto dim = dataContext.getSpatialDimensions();
1369 const auto nVertices = (coordinates.size() / dim);
1370 ReceivedMeshContext &context = _accessor->receivedMeshContext(meshName);
1371
1372 // Check that the vertex is actually within the defined access region
1373 _accessor->receivedMeshContext(meshName).checkVerticesInsideAccessRegion(coordinates, dim, "writeAndMapData");
1374
1375 PRECICE_CHECK(nVertices * dataDims == values.size(),
1376 "Input sizes are inconsistent attempting to write {}D data \"{}\" to mesh \"{}\". "
1377 "You passed {} vertex indices and {} data components, but we expected {} data components ({} x {}).",
1378 dataDims, dataName, meshName,
1379 nVertices, values.size(), nVertices * dataDims, dataDims, nVertices);
1380
1381 PRECICE_CHECK(!context.mesh->empty(), "This participant tries to mapAndWrite data values for data \"{0}\" on mesh \"{1}\", but the mesh \"{1}\" is empty within the defined access region on this rank. "
1382 "Where should the provided data go? Please make sure the mesh \"{1}\" is non-empty within the access region.",
1383 dataName, meshName);
1384 dataContext.writeAndMapValues(coordinates, values);
1385}
1386
1388 std::string_view meshName,
1389 std::string_view dataName,
1392{
1394
1395 // Asserts and checks
1396 PRECICE_TRACE(meshName, dataName, vertices.size());
1397 PRECICE_CHECK(_state != State::Finalized, "writeGradientData(...) cannot be called after finalize().");
1398 PRECICE_REQUIRE_DATA_WRITE(meshName, dataName);
1399
1400 // Inconsistent sizes will be handled below
1401 if ((vertices.empty() && gradients.empty()) || !requiresGradientDataFor(meshName, dataName)) {
1402 return;
1403 }
1404
1405 // Get the data
1406 WriteDataContext &context = _accessor->writeDataContext(meshName, dataName);
1407
1408 // Check if the Data object of given mesh has been initialized with gradient data
1409 PRECICE_CHECK(context.hasGradient(), "Data \"{}\" has no gradient values available. Please set the gradient flag to true under the data attribute in the configuration file.", dataName);
1410
1411 if (auto index = context.locateInvalidVertexID(vertices); index) {
1412 PRECICE_ERROR("Cannot write gradient data \"{}\" to mesh \"{}\" due to invalid Vertex ID at vertices[{}]. "
1413 "Please make sure you only use the results from calls to setMeshVertex/Vertices().",
1414 dataName, meshName, *index);
1415 }
1416
1417 const auto dataDims = context.getDataDimensions();
1418 const auto meshDims = context.getSpatialDimensions();
1419 const auto gradientComponents = meshDims * dataDims;
1420 const auto expectedComponents = vertices.size() * gradientComponents;
1421 PRECICE_CHECK(expectedComponents == gradients.size(),
1422 "Input sizes are inconsistent attempting to write gradient for data \"{}\" to mesh \"{}\". "
1423 "A single gradient/Jacobian for {}D data on a {}D mesh has {} components. "
1424 "You passed {} vertex indices and {} gradient components, but we expected {} gradient components. ",
1425 dataName, meshName,
1426 dataDims, meshDims, gradientComponents,
1427 vertices.size(), gradients.size(), expectedComponents);
1428
1429 PRECICE_VALIDATE_DATA(gradients.data(), gradients.size());
1430
1431 Event e{fmt::format("writeGradientData.{}_{}", meshName, dataName), profiling::API};
1432
1433 context.writeGradientsIntoDataBuffer(vertices, gradients);
1434}
1435
1437 const std::string_view meshName,
1438 ::precice::span<const double> boundingBox) const
1439{
1440 PRECICE_TRACE(meshName, boundingBox.size());
1441 PRECICE_REQUIRE_MESH_USE(meshName);
1442 PRECICE_CHECK(_accessor->isMeshReceived(meshName) && _accessor->isDirectAccessAllowed(meshName),
1443 "This participant attempteded to set an access region (via \"setMeshAccessRegion\") on mesh \"{0}\", "
1444 "but mesh \"{0}\" is either not a received mesh or its api access was not enabled in the configuration. "
1445 "setMeshAccessRegion(...) is only valid for (<receive-mesh name=\"{0}\" ... api-access=\"true\"/>).",
1446 meshName);
1447 PRECICE_CHECK(_state != State::Finalized, "setMeshAccessRegion() cannot be called after finalize().");
1448 PRECICE_CHECK(_state != State::Initialized, "setMeshAccessRegion() needs to be called before initialize().");
1449
1450 // Get the related mesh - setMeshAccessRegion only works for received meshes
1451 ReceivedMeshContext &receivedContext = _accessor->receivedMeshContext(meshName);
1452
1453 PRECICE_CHECK(!receivedContext.userDefinedAccessRegion, "A mesh access region was already defined for mesh \"{}\". setMeshAccessRegion may only be called once per mesh.", receivedContext.mesh->getName());
1454 mesh::Mesh &mesh = *receivedContext.mesh;
1455 int dim = mesh.getDimensions();
1456 PRECICE_CHECK(boundingBox.size() == static_cast<unsigned long>(dim) * 2,
1457 "Incorrect amount of bounding box components attempting to set the bounding box of {}D mesh \"{}\" . "
1458 "You passed {} limits, but we expected {} ({}x2).",
1459 dim, meshName, boundingBox.size(), dim * 2, dim);
1460
1461 // Transform bounds into a suitable format
1462 PRECICE_DEBUG("Define bounding box");
1463 std::vector<double> bounds(dim * 2);
1464
1465 for (int d = 0; d < dim; ++d) {
1466 // Check that min is lower or equal to max
1467 PRECICE_CHECK(boundingBox[2 * d] <= boundingBox[2 * d + 1], "Your bounding box is ill defined, i.e. it has a negative volume. The required format is [x_min, x_max...]");
1468 bounds[2 * d] = boundingBox[2 * d];
1469 bounds[2 * d + 1] = boundingBox[2 * d + 1];
1470 }
1471 // Create a bounding box
1472 receivedContext.userDefinedAccessRegion = std::make_shared<mesh::BoundingBox>(bounds);
1473 // Expand the mesh associated bounding box
1474 mesh.expandBoundingBox(*receivedContext.userDefinedAccessRegion.get());
1475}
1476
1478 const std::string_view meshName,
1480 ::precice::span<double> coordinates) const
1481{
1482 PRECICE_TRACE(meshName, ids.size(), coordinates.size());
1483 PRECICE_REQUIRE_MESH_USE(meshName);
1484 PRECICE_CHECK(_accessor->isMeshReceived(meshName) && _accessor->isDirectAccessAllowed(meshName),
1485 "This participant attempteded to get mesh vertex IDs and coordinates (via \"getMeshVertexIDsAndCoordinates\") from mesh \"{0}\", "
1486 "but mesh \"{0}\" is either not a received mesh or its api access was not enabled in the configuration. "
1487 "getMeshVertexIDsAndCoordinates(...) is only valid for (<receive-mesh name=\"{0}\" ... api-access=\"true\"/>).",
1488 meshName);
1489 // If an access region is required, we have to check its existence
1490 bool requiresBB = requiresUserDefinedAccessRegion(meshName);
1491 PRECICE_CHECK(!requiresBB || (requiresBB && _accessor->receivedMeshContext(meshName).userDefinedAccessRegion),
1492 "The function \"getMeshVertexIDsAndCoordinates\" was called on mesh \"{0}\", "
1493 "but no access region was defined although this is necessary for parallel runs. "
1494 "Please define an access region using \"setMeshAccessRegion()\" before calling \"getMeshVertexIDsAndCoordinates()\".",
1495 meshName);
1496
1497 PRECICE_DEBUG("Get {} mesh vertices with IDs", ids.size());
1498
1499 // Check, if the requested mesh data has already been received. Otherwise, the function call doesn't make any sense
1500 PRECICE_CHECK((_state == State::Initialized) || _accessor->isMeshProvided(meshName),
1501 "initialize() has to be called before accessing data of the received mesh \"{}\" on participant \"{}\".",
1502 meshName, _accessor->getName());
1503
1504 if (ids.empty() && coordinates.empty()) {
1505 return;
1506 }
1507
1508 Event e{fmt::format("getMeshVertexIDsAndCoordinates.{}", meshName), profiling::API};
1509
1510 const MeshContext &context = _accessor->meshContext(meshName);
1511
1512 auto filteredVertices = _accessor->receivedMeshContext(meshName).filterVerticesToLocalAccessRegion(requiresBB);
1513 const auto meshSize = filteredVertices.size();
1514
1515 const mesh::Mesh &mesh = *(context.mesh);
1516 const auto meshDims = mesh.getDimensions();
1517 PRECICE_CHECK(ids.size() == meshSize,
1518 "Output size is incorrect attempting to get vertex ids of {}D mesh \"{}\". "
1519 "You passed {} vertex indices, but we expected {}. "
1520 "Use getMeshVertexSize(\"{}\") to receive the required amount of vertices.",
1521 meshDims, meshName, ids.size(), meshSize, meshName);
1522 const auto expectedCoordinatesSize = static_cast<unsigned long>(meshDims * meshSize);
1523 PRECICE_CHECK(coordinates.size() == expectedCoordinatesSize,
1524 "Output size is incorrect attempting to get vertex coordinates of {}D mesh \"{}\". "
1525 "You passed {} coordinate components, but we expected {} ({}x{}). "
1526 "Use getMeshVertexSize(\"{}\") and getMeshDimensions(\"{}\") to receive the required amount components",
1527 meshDims, meshName, coordinates.size(), expectedCoordinatesSize, meshSize, meshDims, meshName, meshName);
1528
1529 PRECICE_ASSERT(ids.size() <= mesh.nVertices(), "The queried size exceeds the number of available points.");
1530
1531 Eigen::Map<Eigen::MatrixXd> posMatrix{
1532 coordinates.data(), mesh.getDimensions(), static_cast<EIGEN_DEFAULT_DENSE_INDEX_TYPE>(ids.size())};
1533
1534 for (unsigned long i = 0; i < ids.size(); i++) {
1535 auto localID = filteredVertices[i].get().getID();
1536 PRECICE_ASSERT(mesh.isValidVertexID(localID), i, localID);
1537 ids[i] = localID;
1538 posMatrix.col(i) = filteredVertices[i].get().getCoords();
1539 }
1540}
1541
1543{
1544 // sort meshContexts by name, for communication in right order.
1545 std::sort(_accessor->usedMeshContexts().begin(), _accessor->usedMeshContexts().end(),
1546 [](const MeshContextVariant &lhs, const MeshContextVariant &rhs) -> bool {
1547 return getMesh(lhs).getName() < getMesh(rhs).getName();
1548 });
1549
1550 // Provided meshes need their bounding boxes already for the re-partitioning
1551 for (auto &context : _accessor->providedMeshContexts()) {
1552 context.mesh->computeBoundingBox();
1553 }
1554
1555 // Clear mappings for all meshes
1556 for (auto &variant : _accessor->usedMeshContexts()) {
1557 getMeshContext(variant)->clearMappings();
1558 }
1559
1560 // Compare bounding boxes for all meshes
1561 for (const auto &variant : _accessor->usedMeshContexts()) {
1563 }
1564}
1565
1567{
1568 // We need to do this in two loops: First, communicate the mesh and later compute the partition.
1569 // Originally, this was done in one loop. This however gave deadlock if two meshes needed to be communicated cross-wise.
1570 // Both loops need a different sorting
1571
1572 auto &contexts = _accessor->usedMeshContexts();
1573
1574 std::sort(contexts.begin(), contexts.end(),
1575 [](const MeshContextVariant &lhs, const MeshContextVariant &rhs) -> bool {
1576 return getMesh(lhs).getName() < getMesh(rhs).getName();
1577 });
1578
1579 for (const auto &variant : contexts) {
1580 getPartition(variant).communicate();
1581 }
1582
1583 // for two-level initialization, there is also still communication in partition::compute()
1584 // therefore, we cannot resort here.
1585 // @todo this hacky solution should be removed as part of #633
1586 bool resort = true;
1587 for (auto &m2nPair : _m2ns) {
1588 if (m2nPair.second.m2n->usesTwoLevelInitialization()) {
1589 resort = false;
1590 break;
1591 }
1592 }
1593
1594 if (resort) {
1595 // pull provided meshes up front, to have them ready for the decomposition of the received meshes (for the mappings)
1596 std::stable_partition(contexts.begin(), contexts.end(),
1597 [](const MeshContextVariant &variant) -> bool {
1598 return std::holds_alternative<ProvidedMeshContext *>(variant);
1599 });
1600 }
1601
1602 for (const auto &variant : contexts) {
1603 auto &mesh = getMesh(variant);
1604 getPartition(variant).compute();
1605
1606 // Received meshes can only compute their bounding boxes here
1607 if (std::holds_alternative<ReceivedMeshContext *>(variant)) {
1608 mesh.computeBoundingBox();
1609 }
1610
1611 mesh.allocateDataValues();
1612
1613 // Should be relevant for direct mesh access only
1614 const auto requiredSize = mesh.nVertices();
1615 for (auto &context : _accessor->writeDataContexts()) {
1616 if (context.getMeshName() == mesh.getName()) {
1617 context.resizeBufferTo(requiredSize, std::holds_alternative<ReceivedMeshContext *>(variant));
1618 }
1619 }
1620 }
1621}
1622
1623void ParticipantImpl::computeMappings(std::vector<MappingContext> &contexts, const std::string &mappingType)
1624{
1625 PRECICE_TRACE();
1626 bool anyMappingChanged = false;
1627 for (impl::MappingContext &context : contexts) {
1628 if (not context.mapping->hasComputedMapping()) {
1629 PRECICE_INFO_IF(context.configuredWithAliasTag,
1630 "Automatic RBF mapping alias from mesh \"{}\" to mesh \"{}\" in \"{}\" direction resolves to \"{}\" .",
1631 context.mapping->getInputMesh()->getName(), context.mapping->getOutputMesh()->getName(), mappingType, context.mapping->getName());
1632 PRECICE_INFO("Computing \"{}\" mapping from mesh \"{}\" to mesh \"{}\" in \"{}\" direction.",
1633 context.mapping->getName(), context.mapping->getInputMesh()->getName(), context.mapping->getOutputMesh()->getName(), mappingType);
1634 context.mapping->computeMapping();
1635 anyMappingChanged = true;
1636 }
1637 }
1638 if (anyMappingChanged) {
1639 _accessor->initializeMappingDataCache(mappingType);
1640 }
1641}
1642
1644{
1645 PRECICE_TRACE();
1646 if (!_accessor->hasWriteMappings()) {
1647 return;
1648 }
1649
1650 Event e("mapping", profiling::Fundamental);
1651 computeMappings(_accessor->writeMappingContexts(), "write");
1652 for (auto &context : _accessor->writeDataContexts()) {
1653 if (context.hasMapping()) {
1654 PRECICE_DEBUG("Map initial write data \"{}\" from mesh \"{}\"", context.getDataName(), context.getMeshName());
1655 _executedWriteMappings += context.mapData(std::nullopt, true);
1656 }
1657 }
1658}
1659
1660void ParticipantImpl::mapWrittenData(std::optional<double> after)
1661{
1662 PRECICE_TRACE();
1663 if (!_accessor->hasWriteMappings()) {
1664 return;
1665 }
1666
1667 Event e("mapping", profiling::Fundamental);
1668 computeMappings(_accessor->writeMappingContexts(), "write");
1669 for (auto &context : _accessor->writeDataContexts()) {
1670 if (context.hasMapping()) {
1671 PRECICE_DEBUG("Map write data \"{}\" from mesh \"{}\"", context.getDataName(), context.getMeshName());
1672 _executedWriteMappings += context.mapData(after);
1673 }
1674 }
1675}
1676
1677void ParticipantImpl::trimReadMappedData(double startOfTimeWindow, bool isTimeWindowComplete, const cplscheme::ImplicitData &fromData)
1678{
1679 PRECICE_TRACE();
1680 for (auto &context : _accessor->readDataContexts()) {
1681 if (context.hasMapping()) {
1683 // For serial implicit second, we need to discard everything before startOfTimeWindow to preserve the time window start
1684 // For serial implicit first, we need to discard everything as everything is new
1685 // For parallel implicit, we need to discard everything as everything is new
1686 context.clearToDataFor(fromData);
1687 } else {
1688 context.trimToDataAfterFor(fromData, startOfTimeWindow);
1689 }
1690 }
1691 }
1692}
1693
1695{
1696 PRECICE_TRACE();
1697 if (!_accessor->hasReadMappings()) {
1698 return;
1699 }
1700
1701 Event e("mapping", profiling::Fundamental);
1702 computeMappings(_accessor->readMappingContexts(), "read");
1703 for (auto &context : _accessor->readDataContexts()) {
1704 if (context.hasMapping()) {
1705 PRECICE_DEBUG("Map initial read data \"{}\" to mesh \"{}\"", context.getDataName(), context.getMeshName());
1706 // We always ensure that all read data was mapped
1707 _executedReadMappings += context.mapData(std::nullopt, true);
1708 }
1709 }
1710}
1711
1713{
1714 PRECICE_TRACE();
1715 if (!_accessor->hasReadMappings()) {
1716 return;
1717 }
1718
1719 Event e("mapping", profiling::Fundamental);
1720 computeMappings(_accessor->readMappingContexts(), "read");
1721 for (auto &context : _accessor->readDataContexts()) {
1722 if (context.hasMapping()) {
1723 PRECICE_DEBUG("Map read data \"{}\" to mesh \"{}\"", context.getDataName(), context.getMeshName());
1724 // We always ensure that all read data was mapped
1725 _executedReadMappings += context.mapData();
1726 }
1727 }
1728}
1729
1730void ParticipantImpl::performDataActions(const std::set<action::Action::Timing> &timings)
1731{
1732 PRECICE_TRACE();
1733 for (action::PtrAction &action : _accessor->actions()) {
1734 if (timings.find(action->getTiming()) != timings.end()) {
1735 action->performAction();
1736 }
1737 }
1738}
1739
1741{
1742 PRECICE_TRACE();
1743 if (!_accessor->hasExports()) {
1744 return;
1745 }
1746 PRECICE_DEBUG("Handle exports");
1747 profiling::Event e{"handleExports"};
1748
1749 if (timing == ExportTiming::Initial) {
1750 _accessor->exportInitial();
1751 return;
1752 }
1753
1755 exp.timewindow = _couplingScheme->getTimeWindows() - 1;
1757 exp.complete = _couplingScheme->isTimeWindowComplete();
1758 exp.final = !_couplingScheme->isCouplingOngoing();
1759 exp.time = _couplingScheme->getTime();
1760 _accessor->exportIntermediate(exp);
1761}
1762
1764{
1765 PRECICE_TRACE();
1766 for (auto &context : _accessor->writeDataContexts()) {
1767 // reset the buffered data here
1768 context.resetBufferedData();
1769 }
1770}
1771
1774{
1775 const auto &partConfig = *config.getParticipantConfiguration();
1776 PRECICE_CHECK(partConfig.hasParticipant(_accessorName),
1777 "This participant's name, which was specified in the constructor of the preCICE interface as \"{}\", "
1778 "is not defined in the preCICE configuration. "
1779 "Please double-check the correct spelling.",
1781 return partConfig.getParticipant(_accessorName);
1782}
1783
1794
1795void ParticipantImpl::syncTimestep(double computedTimeStepSize)
1796{
1798 Event e("syncTimestep", profiling::Fundamental);
1800 utils::IntraComm::getCommunication()->send(computedTimeStepSize, 0);
1801 } else {
1803 for (Rank secondaryRank : utils::IntraComm::allSecondaryRanks()) {
1804 double dt;
1805 utils::IntraComm::getCommunication()->receive(dt, secondaryRank);
1806 PRECICE_CHECK(math::equals(dt, computedTimeStepSize),
1807 "Found ambiguous values for the time step size passed to preCICE in \"advance\". On rank {}, the value is {}, while on rank 0, the value is {}.",
1808 secondaryRank, dt, computedTimeStepSize);
1809 }
1810 }
1811}
1812
1814{
1815 PRECICE_DEBUG("Advance coupling scheme");
1816 Event e("advanceCoupling", profiling::Fundamental);
1817 // Orchestrate local and remote mesh changes
1818 std::vector<MeshID> localChanges;
1819
1820 [[maybe_unused]] auto remoteChanges1 = _couplingScheme->firstSynchronization(localChanges);
1821 _couplingScheme->firstExchange();
1822 // Orchestrate remote mesh changes (local ones were handled in the first sync)
1823 [[maybe_unused]] auto remoteChanges2 = _couplingScheme->secondSynchronization();
1824 _couplingScheme->secondExchange();
1825}
1826
1828{
1829 // Optionally apply some final ping-pong to sync solver that run e.g. with a uni-directional coupling
1830 // afterwards close connections
1831 PRECICE_INFO("{} {}communication channels",
1832 (_waitInFinalize ? "Synchronize participants and close" : "Close"),
1833 (close == CloseChannels::Distributed ? "distributed " : ""));
1834 std::string ping = "ping";
1835 std::string pong = "pong";
1836 for (auto &iter : _m2ns) {
1837 auto bm2n = iter.second;
1838 if (!bm2n.m2n->isConnected()) {
1839 PRECICE_DEBUG("Skipping closure of defective connection with {}", bm2n.remoteName);
1840 continue;
1841 }
1843 auto comm = bm2n.m2n->getPrimaryRankCommunication();
1844 PRECICE_DEBUG("Synchronizing primary rank with {}", bm2n.remoteName);
1845 if (bm2n.isRequesting) {
1846 comm->send(ping, 0);
1847 std::string receive = "init";
1848 comm->receive(receive, 0);
1849 PRECICE_ASSERT(receive == pong);
1850 } else {
1851 std::string receive = "init";
1852 comm->receive(receive, 0);
1853 PRECICE_ASSERT(receive == ping);
1854 comm->send(pong, 0);
1855 }
1856 }
1857 if (close == CloseChannels::Distributed) {
1858 PRECICE_DEBUG("Closing distributed communication with {}", bm2n.remoteName);
1859 bm2n.m2n->closeDistributedConnections();
1860 } else {
1861 PRECICE_DEBUG("Closing communication with {}", bm2n.remoteName);
1862 bm2n.m2n->closeConnection();
1863 }
1864 }
1865}
1866
1867bool ParticipantImpl::requiresUserDefinedAccessRegion(std::string_view meshName) const
1868{
1869 return _accessor->isMeshReceived(meshName) && utils::IntraComm::isParallel();
1870}
1871
1872const mesh::Mesh &ParticipantImpl::mesh(const std::string &meshName) const
1873{
1874 PRECICE_TRACE(meshName);
1875 return *_accessor->meshContext(meshName).mesh;
1876}
1877
1885
1886// Reinitialization
1887
1889{
1890 PRECICE_TRACE();
1892 Event e("remesh.exchangeLocalMeshChanges", profiling::Synchronize);
1893
1894 // Gather local changes
1895 std::vector<double> localMeshChanges;
1896 for (const auto &variant : _accessor->usedMeshContexts()) {
1897 localMeshChanges.push_back(_meshLock.check(getMesh(variant).getName()) ? 0.0 : 1.0);
1898 }
1899 PRECICE_DEBUG("Mesh changes of rank: {}", localMeshChanges);
1900
1901 // TODO implement int version of allreduceSum
1902 std::vector<double> totalMeshChanges(localMeshChanges.size(), 0.0);
1903 utils::IntraComm::allreduceSum(localMeshChanges, totalMeshChanges);
1904
1905 // Convert the doubles to int
1906 MeshChanges totalMeshChangesInt(totalMeshChanges.begin(), totalMeshChanges.end());
1907 PRECICE_DEBUG("Mesh changes of participant: {}", totalMeshChangesInt);
1908 return totalMeshChangesInt;
1909}
1910
1912{
1913 // Clear stamples where changes were detected
1914 std::size_t i = 0;
1915 for (auto &variant : _accessor->usedMeshContexts()) {
1916 if (totalMeshChanges[i] > 0.0) {
1917 getMesh(variant).clearDataStamples();
1918 }
1919 ++i;
1920 }
1921}
1922
1923bool ParticipantImpl::reinitHandshake(bool requestReinit) const
1924{
1925 PRECICE_TRACE();
1927 Event e("remesh.exchangeRemoteMeshChanges", profiling::Synchronize);
1928
1930 PRECICE_DEBUG("Remeshing is{} required by this participant.", (requestReinit ? "" : " not"));
1931
1932 bool swarmReinitRequired = requestReinit;
1933 for (auto &iter : _m2ns) {
1934 PRECICE_DEBUG("Coordinating remeshing with {}", iter.first);
1935 bool received = false;
1936 auto &comm = *iter.second.m2n->getPrimaryRankCommunication();
1937 if (iter.second.isRequesting) {
1938 comm.send(requestReinit, 0);
1939 comm.receive(received, 0);
1940 } else {
1941 comm.receive(received, 0);
1942 comm.send(requestReinit, 0);
1943 }
1944 swarmReinitRequired |= received;
1945 }
1946 PRECICE_DEBUG("Coordinated that overall{} remeshing is required.", (swarmReinitRequired ? "" : " no"));
1947
1948 utils::IntraComm::broadcast(swarmReinitRequired);
1949 return swarmReinitRequired;
1950 } else {
1951 bool swarmReinitRequired = false;
1952 utils::IntraComm::broadcast(swarmReinitRequired);
1953 return swarmReinitRequired;
1954 }
1955}
1956
1957void ParticipantImpl::startProfilingSection(std::string_view sectionName)
1958{
1959 PRECICE_CHECK(std::find(sectionName.begin(), sectionName.end(), '/') == sectionName.end(),
1960 "The provided section name \"{}\" may not contain a forward-slash \"/\"",
1961 sectionName);
1962 _userEvents.emplace_back(sectionName, profiling::Fundamental);
1963}
1964
1966{
1967 PRECICE_CHECK(!_userEvents.empty(), "There is no user-started event to stop.");
1968 _userEvents.pop_back();
1969}
1970
1971} // namespace precice::impl
#define PRECICE_ERROR(...)
Definition LogMacros.hpp:16
#define PRECICE_WARN_IF(condition,...)
Definition LogMacros.hpp:18
#define PRECICE_DEBUG(...)
Definition LogMacros.hpp:61
#define PRECICE_TRACE(...)
Definition LogMacros.hpp:92
#define PRECICE_INFO_IF(condition,...)
Definition LogMacros.hpp:25
#define PRECICE_INFO(...)
Definition LogMacros.hpp:14
#define PRECICE_CHECK(check,...)
Definition LogMacros.hpp:32
#define PRECICE_VALIDATE_DATA_NAME(mesh, data)
#define PRECICE_REQUIRE_DATA_WRITE(mesh, data)
#define PRECICE_REQUIRE_MESH_USE(name)
#define PRECICE_REQUIRE_DATA_READ(mesh, data)
#define PRECICE_REQUIRE_MESH_MODIFY(name)
#define PRECICE_VALIDATE_DATA(data, size)
#define PRECICE_EXPERIMENTAL_API()
#define PRECICE_VALIDATE_MESH_NAME(name)
#define PRECICE_ASSERT(...)
Definition assertion.hpp:85
int getDimensions() const
Definition Mesh.cpp:100
void clear()
Removes all mesh elements and data values (does not remove data or the bounding boxes).
Definition Mesh.cpp:281
const std::string & getName() const
Returns the name of the mesh, as set in the config file.
Definition Mesh.cpp:243
bool empty() const
Does the mesh contain any vertices?
Definition Mesh.hpp:88
Main class for preCICE XML configuration tree.
@ WriteCheckpoint
Is the participant required to write a checkpoint?
@ ReadCheckpoint
Is the participant required to read a previously written checkpoint?
@ InitializeData
Is the initialization of coupling data required?
static void finalize()
Definition Device.cpp:35
bool hasGradient() const
Returns whether _providedData has gradient.
int getDataDimensions() const
Get the dimensions of _providedData.
int getSpatialDimensions() const
Get the spatial dimensions of _providedData.
std::optional< std::size_t > locateInvalidVertexID(const Container &c)
CloseChannels
Which channels to close in closeCommunicationChannels().
void writeGradientData(std::string_view meshName, std::string_view dataName, ::precice::span< const VertexID > vertices, ::precice::span< const double > gradients)
Writes vector gradient data to a mesh.
int getMeshVertexSize(std::string_view meshName) const
Returns the number of vertices of a mesh.
utils::MultiLock< std::string > _meshLock
std::deque< profiling::Event > _userEvents
void setMeshQuad(std::string_view meshName, VertexID first, VertexID second, VertexID third, VertexID fourth)
Sets a planar surface mesh quadrangle from vertex IDs.
void setMeshTetrahedra(std::string_view meshName, ::precice::span< const VertexID > vertices)
Sets multiple mesh tetrahedra from vertex IDs.
bool requiresGradientDataFor(std::string_view meshName, std::string_view dataName) const
Checks if the given data set requires gradient data. We check if the data object has been initialized...
int getDataDimensions(std::string_view meshName, std::string_view dataName) const
Returns the spatial dimensionality of the given data on the given mesh.
impl::PtrParticipant determineAccessingParticipant(const config::Configuration &config)
Determines participant accessing this interface from the configuration.
MeshChanges getTotalMeshChanges() const
void advanceCouplingScheme()
Advances the coupling schemes.
void computePartitions()
Communicate meshes and create partitions.
void setMeshEdge(std::string_view meshName, VertexID first, VertexID second)
Sets a mesh edge from vertex IDs.
std::vector< impl::PtrParticipant > _participants
Holds information about solvers participating in the coupled simulation.
bool requiresMeshConnectivityFor(std::string_view meshName) const
Checks if the given mesh requires connectivity.
void setMeshTriangles(std::string_view meshName, ::precice::span< const VertexID > vertices)
Sets multiple mesh triangles from vertex IDs.
int _executedReadMappings
Counts the amount of samples mapped in read mappings executed in the latest advance.
void clearStamplesOfChangedMeshes(MeshChanges totalMeshChanges)
Clears stample of changed meshes to make them consistent after the reinitialization.
void performDataActions(const std::set< action::Action::Timing > &timings)
Performs all data actions with given timing.
void setMeshTriangle(std::string_view meshName, VertexID first, VertexID second, VertexID third)
Sets mesh triangle from vertex IDs.
double getMaxTimeStepSize() const
Get the maximum allowed time step size of the current window.
cplscheme::PtrCouplingScheme _couplingScheme
long int _numberAdvanceCalls
Counts calls to advance for plotting.
bool _allowsExperimental
Are experimental API calls allowed?
void handleDataBeforeAdvance(bool reachedTimeWindowEnd, double timeSteppedTo)
Completes everything data-related between adding time to and advancing the coupling scheme.
void setMeshTetrahedron(std::string_view meshName, VertexID first, VertexID second, VertexID third, VertexID fourth)
Set tetrahedron in 3D mesh from vertex ID.
void handleDataAfterAdvance(bool reachedTimeWindowEnd, bool isTimeWindowComplete, double timeSteppedTo, double timeAfterAdvance, const cplscheme::ImplicitData &receivedData)
Completes everything data-related after advancing the coupling scheme.
void samplizeWriteData(double time)
Creates a Stample at the given time for each write Data and zeros the buffers.
void setMeshQuads(std::string_view meshName, ::precice::span< const VertexID > vertices)
Sets multiple mesh quads from vertex IDs.
void getMeshVertexIDsAndCoordinates(std::string_view meshName, ::precice::span< VertexID > ids, ::precice::span< double > coordinates) const
getMeshVertexIDsAndCoordinates Iterates over the region of interest defined by bounding boxes and rea...
void closeCommunicationChannels(CloseChannels cc)
Syncs the primary ranks of all connected participants.
bool reinitHandshake(bool requestReinit) const
void trimSendDataAfter(double time)
Discards send (currently write) data of a participant after a given time when another iteration is re...
std::unique_ptr< profiling::Event > _solverInitEvent
ParticipantImpl(std::string_view participantName, std::string_view configurationFileName, int solverProcessIndex, int solverProcessSize, std::optional< void * > communicator)
Generic constructor for ParticipantImpl.
void advance(double computedTimeStepSize)
Advances preCICE after the solver has computed one time step.
void setMeshAccessRegion(std::string_view meshName, ::precice::span< const double > boundingBox) const
setMeshAccessRegion Define a region of interest on a received mesh (<receive-mesh ....
void writeData(std::string_view meshName, std::string_view dataName, ::precice::span< const VertexID > vertices, ::precice::span< const double > values)
Writes data to a mesh.
void handleExports(ExportTiming timing)
bool isCouplingOngoing() const
Checks if the coupled simulation is still ongoing.
State _state
The current State of the Participant.
void mapInitialWrittenData()
Computes, and performs write mappings of the initial data in initialize.
void setMeshVertices(std::string_view meshName, ::precice::span< const double > positions, ::precice::span< VertexID > ids)
Creates multiple mesh vertices.
void resetMesh(std::string_view meshName)
std::vector< int > MeshChanges
How many ranks have changed each used mesh.
MappedSamples mappedSamples() const
Returns the amount of mapped read and write samples in the last call to advance.
void finalize()
Finalizes preCICE.
int _executedWriteMappings
Counts the amount of samples mapped in write mappings executed in the latest advance.
void trimReadMappedData(double timeAfterAdvance, bool isTimeWindowComplete, const cplscheme::ImplicitData &fromData)
Removes samples in mapped to data connected to received data via a mapping.
std::map< std::string, m2n::BoundM2N > _m2ns
void reinitialize()
Reinitializes preCICE.
void setupWatcher()
Setup mesh watcher such as WatchPoints.
bool isTimeWindowComplete() const
Checks if the current coupling window is completed.
void setMeshEdges(std::string_view meshName, ::precice::span< const VertexID > vertices)
Sets multiple mesh edges from vertex IDs.
std::string _configHash
The hash of the configuration file used to configure this participant.
void setupCommunication()
Connect participants including repartitioning.
bool _allowsRemeshing
Are experimental remeshing API calls allowed?
VertexID setMeshVertex(std::string_view meshName, ::precice::span< const double > position)
Creates a mesh vertex.
void syncTimestep(double computedTimeStepSize)
Syncs the time step size between all ranks (all time steps sizes should be the same!...
void readData(std::string_view meshName, std::string_view dataName, ::precice::span< const VertexID > vertices, double relativeReadTime, ::precice::span< double > values) const
Reads data values from a mesh. Values correspond to a given point in time relative to the beginning o...
const mesh::Mesh & mesh(const std::string &meshName) const
Allows to access a registered mesh.
void compareBoundingBoxes()
Communicate bounding boxes and look for overlaps.
bool _waitInFinalize
Are participants waiting for each other in finalize?
void startProfilingSection(std::string_view eventName)
void initializeIntraCommunication()
Initializes intra-participant communication.
void resetWrittenData()
Resets written data.
void trimOldDataBefore(double time)
Discards data before the given time for all meshes and data known by this participant.
void mapAndReadData(std::string_view meshName, std::string_view dataName, ::precice::span< const double > coordinates, double relativeReadTime, ::precice::span< double > values) const
Reads data values from a mesh using a just-in-time data mapping. Values correspond to a given point i...
std::unique_ptr< profiling::Event > _solverAdvanceEvent
bool requiresUserDefinedAccessRegion(std::string_view meshName) const
void configure(std::string_view configurationFileName)
Configures the coupling interface from the given xml file.
void mapWrittenData(std::optional< double > after=std::nullopt)
Computes, and performs suitable write mappings either entirely or after given time.
void initialize()
Fully initializes preCICE and coupling data.
void writeAndMapData(std::string_view meshName, std::string_view dataName, ::precice::span< const double > coordinates, ::precice::span< const double > values)
Writes data values to a mesh using a just-in-time mapping (experimental).
void computeMappings(std::vector< MappingContext > &contexts, const std::string &mappingType)
Helper for mapWrittenData and mapReadData.
int getMeshDimensions(std::string_view meshName) const
Returns the spatial dimensionality of the given mesh.
Stores one Data object with related mesh. Context stores data to be read from and potentially provide...
bool hasSamples() const
Are there samples to read from?
void readValues(::precice::span< const VertexID > vertices, double time, ::precice::span< double > values) const
Samples data at a given point in time within the current time window for given indices.
void mapAndReadValues(::precice::span< const double > coordinates, double readTime, ::precice::span< double > values)
Forwards the just-in-time mapping API call for reading data to the data context.
Stores one Data object with related mesh. Context stores data to be written to and potentially provid...
void writeGradientsIntoDataBuffer(::precice::span< const VertexID > vertices, ::precice::span< const double > gradients)
Store gradients in _writeDataBuffer.
void writeAndMapValues(::precice::span< const double > coordinates, ::precice::span< const double > values)
Forwards the just-in-time mapping API call for writing data to the data context.
void writeValuesIntoDataBuffer(::precice::span< const VertexID > vertices, ::precice::span< const double > values)
Store values in _writeDataBuffer.
Container and creator for meshes.
Definition Mesh.hpp:38
void clearDataStamples()
Clears all data stamples.
Definition Mesh.cpp:305
Vertex of a mesh.
Definition Vertex.hpp:16
virtual void compareBoundingBoxes()=0
Intersections between bounding boxes around each rank are computed.
virtual void compute()=0
The partition is computed, i.e. the mesh re-partitioned if required and all data structures are set u...
virtual void communicate()=0
The mesh is communicated between both primary ranks (if required).
static EventRegistry & instance()
Returns the only instance (singleton) of the EventRegistry class.
void startBackend()
Create the file and starts the filestream if profiling is turned on.
void initialize(std::string_view applicationName, int rank=0, int size=1)
Sets the global start time.
void finalize()
Sets the global end time and flushes buffers.
void stop()
Stops a running event.
Definition Event.cpp:51
void addData(std::string_view key, int value)
Adds named integer data, associated to an event.
Definition Event.cpp:63
A C++ 11 implementation of the non-owning C++20 std::span type.
Definition span.hpp:284
constexpr pointer data() const noexcept
Definition span.hpp:500
PRECICE_SPAN_NODISCARD constexpr bool empty() const noexcept
Definition span.hpp:476
constexpr iterator begin() const noexcept
Definition span.hpp:503
constexpr iterator end() const noexcept
Definition span.hpp:505
constexpr size_type size() const noexcept
Definition span.hpp:469
static void barrier()
Synchronizes all ranks.
static void allreduceSum(precice::span< const double > sendData, precice::span< double > rcvData)
static bool isPrimary()
True if this process is running the primary rank.
Definition IntraComm.cpp:52
static void broadcast(bool &value)
static auto allSecondaryRanks()
Returns an iterable range over salve ranks [1, _size).
Definition IntraComm.hpp:37
static bool isParallel()
True if this process is running in parallel.
Definition IntraComm.cpp:62
static bool isSecondary()
True if this process is running a secondary rank.
Definition IntraComm.cpp:57
static com::PtrCommunication & getCommunication()
Intra-participant communication.
Definition IntraComm.hpp:31
static void configure(Rank rank, int size)
Configures the intra-participant communication.
Definition IntraComm.cpp:31
static void finalizeOrCleanupMPI()
Finalized a managed MPI environment or cleans up after an non-managed session.
Definition Parallel.cpp:230
static CommStatePtr current()
Returns an owning pointer to the current CommState.
Definition Parallel.cpp:147
static void initializeOrDetectMPI(std::optional< Communicator > userProvided=std::nullopt)
Definition Parallel.cpp:190
static void finalize()
Finalizes Petsc environment.
Definition Petsc.cpp:59
contains actions to modify exchanged data.
Definition Action.hpp:6
std::unique_ptr< Action > PtrAction
std::shared_ptr< WatchPoint > PtrWatchPoint
std::string errorInvalidVertexID(int vid)
std::variant< ProvidedMeshContext *, ReceivedMeshContext * > MeshContextVariant
Type alias for variant holding either provided or received mesh context pointers.
static constexpr auto errorInvalidVertexIDRange
std::shared_ptr< ParticipantState > PtrParticipant
MeshContext * getMeshContext(const MeshContextVariant &variant)
Helper to extract base MeshContext pointer from variant.
partition::Partition & getPartition(const MeshContextVariant &variant)
Helper to extract partition from variant.
mesh::Mesh & getMesh(const MeshContextVariant &variant)
Helper to extract mesh from variant.
std::shared_ptr< WatchIntegral > PtrWatchIntegral
void setMPIRank(int const rank)
void setParticipant(std::string const &participant)
ConvexityResult isConvexQuad(std::array< Eigen::VectorXd, 4 > coords)
Definition geometry.cpp:143
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.
std::enable_if< std::is_arithmetic< Scalar >::value, bool >::type smallerEquals(Scalar A, Scalar B, Scalar tolerance=NUMERICAL_ZERO_DIFFERENCE)
constexpr double NUMERICAL_ZERO_DIFFERENCE
std::enable_if< std::is_arithmetic< Scalar >::value, bool >::type greaterEquals(Scalar A, Scalar B, Scalar tolerance=NUMERICAL_ZERO_DIFFERENCE)
std::enable_if< std::is_arithmetic< Scalar >::value, bool >::type greater(Scalar A, Scalar B, Scalar tolerance=NUMERICAL_ZERO_DIFFERENCE)
std::array< Eigen::VectorXd, n > coordsFor(const Mesh &mesh, const std::array< int, n > &vertexIDs)
Given a mesh and an array of vertexIDS, this function returns an array of coordinates of the vertices...
Definition Utils.hpp:122
std::array< Vertex *, n > vertexPtrsFor(Mesh &mesh, const std::array< int, n > &vertexIDs)
Given a mesh and an array of vertexIDS, this function returns an array of pointers to vertices.
Definition Utils.hpp:111
std::size_t countVerticesInBoundingBox(mesh::PtrMesh mesh, const mesh::BoundingBox &bb)
Given a Mesh and a bounding box, counts all vertices within the bounding box.
Definition Utils.cpp:72
static constexpr Group API
Convenience instance of the Cat::API.
Definition Event.hpp:25
static constexpr SynchronizeTag Synchronize
Convenience instance of the SynchronizeTag.
Definition Event.hpp:28
static constexpr Group Fundamental
Convenience instance of the Cat::Fundamental.
Definition Event.hpp:22
contains the time interpolation logic.
Definition Sample.hpp:8
auto reorder_array(const std::array< Index, n > &order, const std::array< T, n > &elements) -> std::array< T, n >
Reorders an array given an array of unique indices.
std::pair< InputIt, InputIt > find_first_range(InputIt first, InputIt last, Predicate p)
Finds the first range in [first, last[ that fulfills a predicate.
bool unique_elements(const Container &c, BinaryPredicate p={})
Definition algorithm.hpp:63
auto make_array(Elements &&...elements) -> std::array< typename std::common_type< Elements... >::type, sizeof...(Elements)>
Function that generates an array from given elements.
Definition algorithm.hpp:50
std::string configure(XMLTag &tag, const precice::xml::ConfigurationContext &context, std::string_view configurationFilename)
Configures the given configuration from file configurationFilename.
Definition XMLTag.cpp:284
int VertexID
Definition Types.hpp:13
int Rank
Definition Types.hpp:37
Holds a data mapping and related information.
mesh::PtrMesh mesh
Mesh holding the geometry data structure.
mapping::Mapping::MeshRequirement meshRequirement
Determines which mesh type has to be provided by the accessor.
Context for a mesh provided by this participant.
Context for a mesh received from another participant.
std::shared_ptr< mesh::BoundingBox > userDefinedAccessRegion
Tightly coupled to the parameters of Participant().
Definition XMLTag.hpp:21