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::isZero(computedTimeStepSize), "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) {
432 auto totalMeshChanges = getTotalMeshChanges();
433 clearStamplesOfChangedMeshes(totalMeshChanges);
434
435 int sumOfChanges = std::accumulate(totalMeshChanges.begin(), totalMeshChanges.end(), 0);
436 if (reinitHandshake(sumOfChanges)) {
437 e.stop();
438 reinitialize();
439 }
440 } else {
441 PRECICE_CHECK(_meshLock.checkAll(), "The time window needs to end after remeshing.");
442 }
443 }
444
445 const double timeSteppedTo = _couplingScheme->getTime();
446 const auto dataToReceive = _couplingScheme->implicitDataToReceive();
447
448 handleDataBeforeAdvance(isAtWindowEnd, timeSteppedTo);
449
451
452 // In clase if an implicit scheme, this may be before timeSteppedTo
453 const double timeAfterAdvance = _couplingScheme->getTime();
454 const bool timeWindowComplete = _couplingScheme->isTimeWindowComplete();
455
456 handleDataAfterAdvance(isAtWindowEnd, timeWindowComplete, timeSteppedTo, timeAfterAdvance, dataToReceive);
457
458 PRECICE_INFO(_couplingScheme->printCouplingState());
459
460 PRECICE_DEBUG("Mapped {} samples in write mappings and {} samples in read mappings",
462
463 _meshLock.lockAll();
464
465 e.stop();
466 _solverAdvanceEvent->start();
467}
468
469void ParticipantImpl::handleDataBeforeAdvance(bool reachedTimeWindowEnd, double timeSteppedTo)
470{
471 // We only have to care about write data, in case substeps are enabled
472 // OR we are at the end of a timewindow, otherwise, we simply erase
473 // them as they have no relevance for the coupling (without time
474 // interpolation, only the time window end is relevant), the resetting
475 // happens regardless of the if-condition.
476 if (reachedTimeWindowEnd || _couplingScheme->requiresSubsteps()) {
477
478 // Here, we add the written data to the waveform storage. In the
479 // mapWrittenData, we then take samples from the storage and execute
480 // the mapping using waveform samples on the (for write mappings) "to"
481 // side.
482 samplizeWriteData(timeSteppedTo);
483 }
484
486
487 // Reset mapping counters here to cover subcycling
490
491 if (reachedTimeWindowEnd) {
492 mapWrittenData(_couplingScheme->getTimeWindowStart());
494 }
495}
496
497void ParticipantImpl::handleDataAfterAdvance(bool reachedTimeWindowEnd, bool isTimeWindowComplete, double timeSteppedTo, double timeAfterAdvance, const cplscheme::ImplicitData &receivedData)
498{
499 if (!reachedTimeWindowEnd) {
500 // We are subcycling
501 return;
502 }
503
505 // Move to next time window
506 PRECICE_ASSERT(math::greaterEquals(timeAfterAdvance, timeSteppedTo), "We must have stayed or moved forwards in time (min-time-step-size).", timeAfterAdvance, timeSteppedTo);
507
508 // As we move forward, there may now be old samples lying around
509 trimOldDataBefore(_couplingScheme->getTimeWindowStart());
510 } else {
511 // We are iterating
512 PRECICE_ASSERT(math::greater(timeSteppedTo, timeAfterAdvance), "We must have moved back in time!");
513
514 trimSendDataAfter(timeAfterAdvance);
515 }
516
517 if (reachedTimeWindowEnd) {
518 trimReadMappedData(timeAfterAdvance, isTimeWindowComplete, receivedData);
519 mapReadData();
521 }
522
523 // Required for implicit coupling
524 for (auto &context : _accessor->readDataContexts()) {
525 context.invalidateMappingCache();
526 }
527
528 // Strictly speaking, the write direction is not relevant here, but we will add it for the sake of completenss
529 for (auto &context : _accessor->writeDataContexts()) {
530 context.invalidateMappingCache();
531 }
532
534 // Reset initial guesses for iterative mappings
535 for (auto &context : _accessor->readDataContexts()) {
536 context.resetInitialGuesses();
537 }
538 for (auto &context : _accessor->writeDataContexts()) {
539 context.resetInitialGuesses();
540 }
541 }
542
544}
545
547{
548 // store buffered write data in sample storage and reset the buffer
549 for (auto &context : _accessor->writeDataContexts()) {
550
551 // Finalize conservative write mapping, later we reset
552 // the buffer in resetWrittenData
553
554 // Note that "samplizeWriteData" operates on _providedData of the
555 // DataContext, which is for just-in-time mappings the data we write
556 // on the received mesh.
557 // For just-in-time mappings, the _providedData should contain by now
558 // the "just-in-time" mapped data. However, it would be wasteful to
559 // execute expensive parts (in particular solving the RBF systems)
560 // for each writeAndMapData call. Thus, we create a DataCache during
561 // the writeAndMapData API calls, which contains pre-processed data
562 // values. Here, we now need to finalize the just-in-time mappings,
563 // before we can add it to the waveform buffer.
564 // For now, this only applies to just-in-time write mappings
565
566 context.completeJustInTimeMapping();
567 context.storeBufferedData(time);
568 }
569}
570
572{
573 for (auto &variant : _accessor->usedMeshContexts()) {
574 auto &mesh = getMesh(variant);
575 for (const auto &name : mesh.availableData()) {
576 mesh.data(name)->waveform().trimBefore(time);
577 }
578 }
579}
580
582{
583 for (auto &context : _accessor->writeDataContexts()) {
584 context.trimAfter(time);
585 }
586}
587
589{
591 PRECICE_CHECK(_state != State::Finalized, "finalize() may only be called once.");
592
593 // First we gracefully stop all existing user events and finally the last solver.advance event
594 while (!_userEvents.empty()) {
595 // Ensure reverse destruction order for correct nesting
596 _userEvents.pop_back();
597 }
598 _solverAdvanceEvent.reset();
599
600 Event e("finalize", profiling::Fundamental);
601
602 if (_state == State::Initialized) {
603
604 PRECICE_ASSERT(_couplingScheme->isInitialized());
605 PRECICE_DEBUG("Finalize coupling scheme");
606 _couplingScheme->finalize();
607
609 }
610
611 // Release ownership
612 _couplingScheme.reset();
613 _participants.clear();
614 _accessor.reset();
615
616 // Close Connections
617 PRECICE_DEBUG("Close intra-participant communication");
619 utils::IntraComm::getCommunication()->closeConnection();
621 }
622 _m2ns.clear();
623
624 // Stop and print Event logging
625 e.stop();
626
627 // Finalize PETSc and Events first
629// This will lead to issues if we call finalize afterwards again
630#if !defined(PRECICE_NO_GINKGO) || !defined(PRECICE_NO_KOKKOS_KERNELS)
632#endif
634
635 // Finally clear events and finalize MPI
638}
639
640int ParticipantImpl::getMeshDimensions(std::string_view meshName) const
641{
642 PRECICE_TRACE(meshName);
644 return _accessor->meshContext(meshName).mesh->getDimensions();
645}
646
647int ParticipantImpl::getDataDimensions(std::string_view meshName, std::string_view dataName) const
648{
649 PRECICE_TRACE(meshName, dataName);
651 PRECICE_VALIDATE_DATA_NAME(meshName, dataName);
652 return _accessor->meshContext(meshName).mesh->data(dataName)->getDimensions();
653}
654
656{
658 PRECICE_CHECK(_state != State::Finalized, "isCouplingOngoing() cannot be called after finalize().");
659 PRECICE_CHECK(_state == State::Initialized, "initialize() has to be called before isCouplingOngoing() can be evaluated.");
660 return _couplingScheme->isCouplingOngoing();
661}
662
664{
666 PRECICE_CHECK(_state != State::Constructed, "initialize() has to be called before isTimeWindowComplete().");
667 PRECICE_CHECK(_state != State::Finalized, "isTimeWindowComplete() cannot be called after finalize().");
668 return _couplingScheme->isTimeWindowComplete();
669}
670
672{
673 PRECICE_CHECK(_state != State::Finalized, "getMaxTimeStepSize() cannot be called after finalize().");
674 PRECICE_CHECK(_state == State::Initialized, "initialize() has to be called before getMaxTimeStepSize() can be evaluated.");
675 const double nextTimeStepSize = _couplingScheme->getNextTimeStepMaxSize();
676 // PRECICE_ASSERT(!math::isZero(nextTimeStepSize), nextTimeStepSize); // @todo requires https://github.com/precice/precice/issues/1904
677 // PRECICE_ASSERT(math::greater(nextTimeStepSize, 0.0), nextTimeStepSize); // @todo requires https://github.com/precice/precice/issues/1904
678
679 // safeguard needed because _couplingScheme->getNextTimeStepMaxSize() returns 0, if not isCouplingOngoing()
680 // actual case where we want to warn the user
682 isCouplingOngoing() && not math::greater(nextTimeStepSize, 0.0, 100 * math::NUMERICAL_ZERO_DIFFERENCE),
683 "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.",
684 nextTimeStepSize);
685 return nextTimeStepSize;
686}
687
689{
691 PRECICE_CHECK(_state == State::Constructed, "requiresInitialData() has to be called before initialize().");
693 if (required) {
695 }
696 return required;
697}
698
700{
702 PRECICE_CHECK(_state == State::Initialized, "initialize() has to be called before requiresWritingCheckpoint().");
704 if (required) {
706 }
707 return required;
708}
709
711{
713 PRECICE_CHECK(_state == State::Initialized, "initialize() has to be called before requiresReadingCheckpoint().");
715 if (required) {
717 }
718 return required;
719}
720
721bool ParticipantImpl::requiresMeshConnectivityFor(std::string_view meshName) const
722{
724 MeshContext &context = _accessor->meshContext(meshName);
726}
727
728bool ParticipantImpl::requiresGradientDataFor(std::string_view meshName,
729 std::string_view dataName) const
730{
731 PRECICE_VALIDATE_DATA_NAME(meshName, dataName);
732 // Read data never requires gradients
733 if (!_accessor->isDataWrite(meshName, dataName))
734 return false;
735
736 WriteDataContext &context = _accessor->writeDataContext(meshName, dataName);
737 return context.hasGradient();
738}
739
741 std::string_view meshName) const
742{
743 PRECICE_TRACE(meshName);
744 PRECICE_REQUIRE_MESH_USE(meshName);
745 // In case we access received mesh data: check, if the requested mesh data has already been received.
746 // Otherwise, the function call doesn't make any sense
747 PRECICE_CHECK((_state == State::Initialized) || _accessor->isMeshProvided(meshName),
748 "initialize() has to be called before accessing data of the received mesh \"{}\" on participant \"{}\".",
749 meshName, _accessor->getName());
750
751 // Returns true if we have api access configured and we run in parallel and have a received mesh
752 if (_accessor->isMeshReceived(meshName) && _accessor->isDirectAccessAllowed(meshName)) {
753 auto &receivedContext = _accessor->receivedMeshContext(meshName);
754 if (receivedContext.userDefinedAccessRegion || requiresUserDefinedAccessRegion(meshName)) {
755 // filter nVertices to the actual number of vertices queried by the user
756 PRECICE_CHECK(receivedContext.userDefinedAccessRegion, "The function getMeshVertexSize was called on the received mesh \"{0}\", "
757 "but no access region was defined although this is necessary for parallel runs. "
758 "Please define an access region using \"setMeshAccessRegion()\" before calling \"getMeshVertexSize()\".",
759 meshName);
760
761 auto result = mesh::countVerticesInBoundingBox(receivedContext.mesh, *receivedContext.userDefinedAccessRegion);
762
763 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);
764 return result;
765 }
766 }
767 // For provided meshes and in case the api-access was not configured, we return here all vertices
768 PRECICE_WARN_IF(_accessor->isMeshReceived(meshName) && !_accessor->isDirectAccessAllowed(meshName),
769 "You are calling \"getMeshVertexSize()\" on a received mesh without api-access enabled (<receive-mesh name=\"{0}\" ... api-access=\"false\"/>). "
770 "Note that enabling api-access is required for this function to work properly with direct mesh access and just-in-time mappings.",
771 meshName);
772 return _accessor->meshContext(meshName).mesh->nVertices();
773}
774
777 std::string_view meshName)
778{
780 PRECICE_CHECK(_allowsRemeshing, "Cannot reset meshes. This feature needs to be enabled using <precice-configuration experimental=\"1\" allow-remeshing=\"1\">.");
781 PRECICE_CHECK(_state == State::Initialized, "initialize() has to be called before resetMesh().");
782 PRECICE_TRACE(meshName);
784 PRECICE_CHECK(_couplingScheme->isCouplingOngoing(), "Cannot remesh after the last time window has been completed.");
785 PRECICE_CHECK(_couplingScheme->isTimeWindowComplete(), "Cannot remesh while subcycling or iterating. Remeshing is only allowed when the time window is completed.");
786 impl::MeshContext &context = _accessor->meshContext(meshName);
787
788 PRECICE_DEBUG("Clear mesh positions for mesh \"{}\"", context.mesh->getName());
789 _meshLock.unlock(meshName);
790 context.mesh->clear();
791}
792
794 std::string_view meshName,
796{
797 PRECICE_TRACE(meshName);
799 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
800 auto &mesh = *context.mesh;
801 PRECICE_CHECK(position.size() == static_cast<unsigned long>(mesh.getDimensions()),
802 "Cannot set vertex for mesh \"{}\". Expected {} position components but found {}.", meshName, mesh.getDimensions(), position.size());
803 Event e{fmt::format("setMeshVertex.{}", meshName), profiling::API};
804 auto index = mesh.createVertex(Eigen::Map<const Eigen::VectorXd>{position.data(), mesh.getDimensions()}).getID();
805 mesh.allocateDataValues();
806
807 const auto newSize = mesh.nVertices();
808 for (auto &context : _accessor->writeDataContexts()) {
809 if (context.getMeshName() == mesh.getName()) {
810 context.resizeBufferTo(newSize);
811 }
812 }
813
814 return index;
815}
816
818 std::string_view meshName,
821{
822 PRECICE_TRACE(meshName, positions.size(), ids.size());
824 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
825 auto &mesh = *context.mesh;
826
827 const auto meshDims = mesh.getDimensions();
828 const auto expectedPositionSize = ids.size() * meshDims;
829 PRECICE_CHECK(positions.size() == expectedPositionSize,
830 "Input sizes are inconsistent attempting to set vertices on {}D mesh \"{}\". "
831 "You passed {} vertex indices and {} position components, but we expected {} position components ({} x {}).",
832 meshDims, meshName, ids.size(), positions.size(), expectedPositionSize, ids.size(), meshDims);
833
834 Event e{fmt::format("setMeshVertices.{}", meshName), profiling::API};
835 const Eigen::Map<const Eigen::MatrixXd> posMatrix{
836 positions.data(), mesh.getDimensions(), static_cast<EIGEN_DEFAULT_DENSE_INDEX_TYPE>(ids.size())};
837 for (unsigned long i = 0; i < ids.size(); ++i) {
838 ids[i] = mesh.createVertex(posMatrix.col(i)).getID();
839 }
840 mesh.allocateDataValues();
841
842 const auto newSize = mesh.nVertices();
843 for (auto &context : _accessor->writeDataContexts()) {
844 if (context.getMeshName() == mesh.getName()) {
845 context.resizeBufferTo(newSize);
846 }
847 }
848}
849
851 std::string_view meshName,
852 VertexID first,
853 VertexID second)
854{
855 PRECICE_TRACE(meshName, first, second);
857 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
859 return;
860 }
861
862 mesh::Mesh &mesh = *context.mesh;
864 PRECICE_CHECK(mesh.isValidVertexID(first), errorInvalidVertexID(first));
865 PRECICE_CHECK(mesh.isValidVertexID(second), errorInvalidVertexID(second));
866 Event e{fmt::format("setMeshEdge.{}", meshName), profiling::API};
867 mesh::Vertex &v0 = mesh.vertex(first);
868 mesh::Vertex &v1 = mesh.vertex(second);
869 mesh.createEdge(v0, v1);
870}
871
873 std::string_view meshName,
875{
876 PRECICE_TRACE(meshName, vertices.size());
878 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
880 return;
881 }
882
883 mesh::Mesh &mesh = *context.mesh;
884 PRECICE_CHECK(vertices.size() % 2 == 0,
885 "Cannot interpret passed vertex IDs attempting to set edges of mesh \"{}\" . "
886 "You passed {} vertex indices, but we expected an even number.",
887 meshName, vertices.size());
888 {
889 auto end = vertices.end();
890 auto [first, last] = utils::find_first_range(vertices.begin(), end, [&mesh](VertexID vid) {
891 return !mesh.isValidVertexID(vid);
892 });
893 PRECICE_CHECK(first == end,
895 std::distance(vertices.begin(), first),
896 std::distance(vertices.begin(), last));
897 }
898
899 Event e{fmt::format("setMeshEdges.{}", meshName), profiling::API};
900
901 for (unsigned long i = 0; i < vertices.size() / 2; ++i) {
902 auto aid = vertices[2 * i];
903 auto bid = vertices[2 * i + 1];
904 mesh.createEdge(mesh.vertex(aid), mesh.vertex(bid));
905 }
906}
907
909 std::string_view meshName,
910 VertexID first,
911 VertexID second,
912 VertexID third)
913{
914 PRECICE_TRACE(meshName, first, second, third);
916 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
918 return;
919 }
920
921 mesh::Mesh &mesh = *context.mesh;
923 PRECICE_CHECK(mesh.isValidVertexID(first), errorInvalidVertexID(first));
924 PRECICE_CHECK(mesh.isValidVertexID(second), errorInvalidVertexID(second));
925 PRECICE_CHECK(mesh.isValidVertexID(third), errorInvalidVertexID(third));
927 "setMeshTriangle() was called with repeated Vertex IDs ({}, {}, {}).",
928 first, second, third);
929
930 mesh::Vertex &A = mesh.vertex(first);
931 mesh::Vertex &B = mesh.vertex(second);
932 mesh::Vertex &C = mesh.vertex(third);
933
934 mesh.createTriangle(A, B, C);
935}
936
938 std::string_view meshName,
940{
941 PRECICE_TRACE(meshName, vertices.size());
943 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
945 return;
946 }
947
948 mesh::Mesh &mesh = *context.mesh;
949 PRECICE_CHECK(vertices.size() % 3 == 0,
950 "Cannot interpret passed vertex IDs attempting to set triangles of mesh \"{}\" . "
951 "You passed {} vertex indices, which isn't dividable by 3.",
952 meshName, vertices.size());
953 {
954 auto end = vertices.end();
955 auto [first, last] = utils::find_first_range(vertices.begin(), end, [&mesh](VertexID vid) {
956 return !mesh.isValidVertexID(vid);
957 });
958 PRECICE_CHECK(first == end,
960 std::distance(vertices.begin(), first),
961 std::distance(vertices.begin(), last));
962 }
963
964 Event e{fmt::format("setMeshTriangles.{}", meshName), profiling::API};
965
966 for (unsigned long i = 0; i < vertices.size() / 3; ++i) {
967 auto aid = vertices[3 * i];
968 auto bid = vertices[3 * i + 1];
969 auto cid = vertices[3 * i + 2];
970 mesh.createTriangle(mesh.vertex(aid),
971 mesh.vertex(bid),
972 mesh.vertex(cid));
973 }
974}
975
977 std::string_view meshName,
978 VertexID first,
979 VertexID second,
980 VertexID third,
981 VertexID fourth)
982{
983 PRECICE_TRACE(meshName, first,
984 second, third, fourth);
986 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
988 return;
989 }
990
991 PRECICE_ASSERT(context.mesh);
992 mesh::Mesh &mesh = *context.mesh;
994 PRECICE_CHECK(mesh.isValidVertexID(first), errorInvalidVertexID(first));
995 PRECICE_CHECK(mesh.isValidVertexID(second), errorInvalidVertexID(second));
996 PRECICE_CHECK(mesh.isValidVertexID(third), errorInvalidVertexID(third));
997 PRECICE_CHECK(mesh.isValidVertexID(fourth), errorInvalidVertexID(fourth));
998
999 auto vertexIDs = utils::make_array(first, second, third, fourth);
1000 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.");
1001
1002 auto coords = mesh::coordsFor(mesh, vertexIDs);
1004 "The four vertices that form the quad are not unique. The resulting shape may be a point, line or triangle. "
1005 "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.");
1006
1007 auto convexity = math::geometry::isConvexQuad(coords);
1008 PRECICE_CHECK(convexity.convex, "The given quad is not convex. "
1009 "Please check that the adapter send the four correct vertices or that the interface is composed of quads.");
1010 auto reordered = utils::reorder_array(convexity.vertexOrder, mesh::vertexPtrsFor(mesh, vertexIDs));
1011
1012 Event e{fmt::format("setMeshQuad.{}", meshName), profiling::API};
1013
1014 // Vertices are now in the order: V0-V1-V2-V3-V0.
1015 // Use the shortest diagonal to split the quad into 2 triangles.
1016 // Vertices are now in V0-V1-V2-V3-V0 order. The new edge, e[4] is either 0-2 or 1-3
1017 double distance02 = (reordered[0]->getCoords() - reordered[2]->getCoords()).norm();
1018 double distance13 = (reordered[1]->getCoords() - reordered[3]->getCoords()).norm();
1019
1020 // The new edge, e[4], is the shortest diagonal of the quad
1021 if (distance02 <= distance13) {
1022 mesh.createTriangle(*reordered[0], *reordered[2], *reordered[1]);
1023 mesh.createTriangle(*reordered[0], *reordered[2], *reordered[3]);
1024 } else {
1025 mesh.createTriangle(*reordered[1], *reordered[3], *reordered[0]);
1026 mesh.createTriangle(*reordered[1], *reordered[3], *reordered[2]);
1027 }
1028}
1029
1031 std::string_view meshName,
1033{
1034 PRECICE_TRACE(meshName, vertices.size());
1036 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
1038 return;
1039 }
1040
1041 mesh::Mesh &mesh = *context.mesh;
1042 PRECICE_CHECK(vertices.size() % 4 == 0,
1043 "Cannot interpret passed vertex IDs attempting to set quads of mesh \"{}\" . "
1044 "You passed {} vertex indices, which isn't dividable by 4.",
1045 meshName, vertices.size());
1046 {
1047 auto end = vertices.end();
1048 auto [first, last] = utils::find_first_range(vertices.begin(), end, [&mesh](VertexID vid) {
1049 return !mesh.isValidVertexID(vid);
1050 });
1051 PRECICE_CHECK(first == end,
1053 std::distance(vertices.begin(), first),
1054 std::distance(vertices.begin(), last));
1055 }
1056
1057 for (unsigned long i = 0; i < vertices.size() / 4; ++i) {
1058 auto aid = vertices[4 * i];
1059 auto bid = vertices[4 * i + 1];
1060 auto cid = vertices[4 * i + 2];
1061 auto did = vertices[4 * i + 3];
1062
1063 auto vertexIDs = utils::make_array(aid, bid, cid, did);
1064 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);
1065
1066 auto coords = mesh::coordsFor(mesh, vertexIDs);
1068 "The four vertices that form the quad nr {} are not unique. The resulting shape may be a point, line or triangle. "
1069 "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.",
1070 i);
1071
1072 auto convexity = math::geometry::isConvexQuad(coords);
1073 PRECICE_CHECK(convexity.convex, "The given quad nr {} is not convex. "
1074 "Please check that the adapter send the four correct vertices or that the interface is composed of quads.",
1075 i);
1076 auto reordered = utils::reorder_array(convexity.vertexOrder, mesh::vertexPtrsFor(mesh, vertexIDs));
1077
1078 Event e{fmt::format("setMeshQuads.{}", meshName), profiling::API};
1079
1080 // Use the shortest diagonal to split the quad into 2 triangles.
1081 // Vertices are now in V0-V1-V2-V3-V0 order. The new edge, e[4] is either 0-2 or 1-3
1082 double distance02 = (reordered[0]->getCoords() - reordered[2]->getCoords()).norm();
1083 double distance13 = (reordered[1]->getCoords() - reordered[3]->getCoords()).norm();
1084
1085 if (distance02 <= distance13) {
1086 mesh.createTriangle(*reordered[0], *reordered[2], *reordered[1]);
1087 mesh.createTriangle(*reordered[0], *reordered[2], *reordered[3]);
1088 } else {
1089 mesh.createTriangle(*reordered[1], *reordered[3], *reordered[0]);
1090 mesh.createTriangle(*reordered[1], *reordered[3], *reordered[2]);
1091 }
1092 }
1093}
1094
1096 std::string_view meshName,
1097 VertexID first,
1098 VertexID second,
1099 VertexID third,
1100 VertexID fourth)
1101{
1102 PRECICE_TRACE(meshName, first, second, third, fourth);
1104 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
1105 PRECICE_CHECK(context.mesh->getDimensions() == 3, "setMeshTetrahedron is only possible for 3D meshes. "
1106 "Please set the mesh dimension to 3 in the preCICE configuration file.");
1108 return;
1109 }
1110
1111 Event e{fmt::format("setMeshTetrahedron.{}", meshName), profiling::API};
1112
1113 mesh::Mesh &mesh = *context.mesh;
1115 PRECICE_CHECK(mesh.isValidVertexID(first), errorInvalidVertexID(first));
1116 PRECICE_CHECK(mesh.isValidVertexID(second), errorInvalidVertexID(second));
1117 PRECICE_CHECK(mesh.isValidVertexID(third), errorInvalidVertexID(third));
1118 PRECICE_CHECK(mesh.isValidVertexID(fourth), errorInvalidVertexID(fourth));
1119 mesh::Vertex &A = mesh.vertex(first);
1120 mesh::Vertex &B = mesh.vertex(second);
1121 mesh::Vertex &C = mesh.vertex(third);
1122 mesh::Vertex &D = mesh.vertex(fourth);
1123
1124 mesh.createTetrahedron(A, B, C, D);
1125}
1126
1128 std::string_view meshName,
1130{
1131 PRECICE_TRACE(meshName, vertices.size());
1133 ProvidedMeshContext &context = _accessor->providedMeshContext(meshName);
1134 PRECICE_CHECK(context.mesh->getDimensions() == 3, "setMeshTetrahedron is only possible for 3D meshes. "
1135 "Please set the mesh dimension to 3 in the preCICE configuration file.");
1137 return;
1138 }
1139
1140 mesh::Mesh &mesh = *context.mesh;
1141 PRECICE_CHECK(vertices.size() % 4 == 0,
1142 "Cannot interpret passed vertex IDs attempting to set quads of mesh \"{}\" . "
1143 "You passed {} vertex indices, which isn't dividable by 4.",
1144 meshName, vertices.size());
1145 {
1146 auto end = vertices.end();
1147 auto [first, last] = utils::find_first_range(vertices.begin(), end, [&mesh](VertexID vid) {
1148 return !mesh.isValidVertexID(vid);
1149 });
1150 PRECICE_CHECK(first == end,
1152 std::distance(vertices.begin(), first),
1153 std::distance(vertices.begin(), last));
1154 }
1155
1156 Event e{fmt::format("setMeshTetrahedra.{}", meshName), profiling::API};
1157
1158 for (unsigned long i = 0; i < vertices.size() / 4; ++i) {
1159 auto aid = vertices[4 * i];
1160 auto bid = vertices[4 * i + 1];
1161 auto cid = vertices[4 * i + 2];
1162 auto did = vertices[4 * i + 3];
1163 mesh.createTetrahedron(mesh.vertex(aid),
1164 mesh.vertex(bid),
1165 mesh.vertex(cid),
1166 mesh.vertex(did));
1167 }
1168}
1169
1171 std::string_view meshName,
1172 std::string_view dataName,
1175{
1176 PRECICE_TRACE(meshName, dataName, vertices.size());
1177 PRECICE_CHECK(_state != State::Finalized, "writeData(...) cannot be called after finalize().");
1178 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.");
1179 PRECICE_REQUIRE_DATA_WRITE(meshName, dataName);
1180 // Inconsistent sizes will be handled below
1181 if (vertices.empty() && values.empty()) {
1182 return;
1183 }
1184
1185 WriteDataContext &context = _accessor->writeDataContext(meshName, dataName);
1186
1187 const auto dataDims = context.getDataDimensions();
1188 const auto expectedDataSize = vertices.size() * dataDims;
1189 PRECICE_CHECK(expectedDataSize == values.size(),
1190 "Input sizes are inconsistent attempting to write {}D data \"{}\" to mesh \"{}\". "
1191 "You passed {} vertex indices and {} data components, but we expected {} data components ({} x {}).",
1192 dataDims, dataName, meshName,
1193 vertices.size(), values.size(), expectedDataSize, dataDims, vertices.size());
1194
1195 // Sizes are correct at this point
1196 PRECICE_VALIDATE_DATA(values.data(), values.size()); // TODO Only take span
1197
1198 if (auto index = context.locateInvalidVertexID(vertices); index) {
1199 PRECICE_ERROR("Cannot write data \"{}\" to mesh \"{}\" due to invalid Vertex ID at vertices[{}]. "
1200 "Please make sure you only use the results from calls to setMeshVertex/Vertices().",
1201 dataName, meshName, *index);
1202 }
1203 Event e{fmt::format("writeData.{}_{}", meshName, dataName), profiling::API};
1204 context.writeValuesIntoDataBuffer(vertices, values);
1205}
1206
1208 std::string_view meshName,
1209 std::string_view dataName,
1211 double relativeReadTime,
1212 ::precice::span<double> values) const
1213{
1214 PRECICE_TRACE(meshName, dataName, vertices.size(), relativeReadTime);
1215 PRECICE_CHECK(_state != State::Constructed, "readData(...) cannot be called before initialize().");
1216 PRECICE_CHECK(_state != State::Finalized, "readData(...) cannot be called after finalize().");
1217 PRECICE_CHECK(math::smallerEquals(relativeReadTime, _couplingScheme->getNextTimeStepMaxSize()), "readData(...) cannot sample data outside of current time window.");
1218 PRECICE_CHECK(relativeReadTime >= 0, "readData(...) cannot sample data before the current time.");
1219 PRECICE_CHECK(isCouplingOngoing() || math::isZero(relativeReadTime), "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);
1220
1221 PRECICE_REQUIRE_DATA_READ(meshName, dataName);
1222
1223 PRECICE_CHECK(_meshLock.check(meshName),
1224 "Cannot read from mesh \"{}\" after it has been reset. Please read data before calling resetMesh().",
1225 meshName);
1226
1227 // Inconsistent sizes will be handled below
1228 if (vertices.empty() && values.empty()) {
1229 return;
1230 }
1231
1232 ReadDataContext &context = _accessor->readDataContext(meshName, dataName);
1233 PRECICE_CHECK(context.hasSamples(), "Data \"{}\" cannot be read from mesh \"{}\" as it contains no samples. "
1234 "This is typically a configuration issue of the data flow. "
1235 "Check if the data is correctly exchanged to this participant \"{}\" and mapped to mesh \"{}\".",
1236 dataName, meshName, _accessorName, meshName);
1237
1238 const auto dataDims = context.getDataDimensions();
1239 const auto expectedDataSize = vertices.size() * dataDims;
1240 PRECICE_CHECK(expectedDataSize == values.size(),
1241 "Input/Output sizes are inconsistent attempting to read {}D data \"{}\" from mesh \"{}\". "
1242 "You passed {} vertex indices and {} data components, but we expected {} data components ({} x {}).",
1243 dataDims, dataName, meshName,
1244 vertices.size(), values.size(), expectedDataSize, dataDims, vertices.size());
1245
1246 if (auto index = context.locateInvalidVertexID(vertices); index) {
1247 PRECICE_ERROR("Cannot read data \"{}\" from mesh \"{}\" due to invalid Vertex ID at vertices[{}]. "
1248 "Please make sure you only use the results from calls to setMeshVertex/Vertices().",
1249 dataName, meshName, *index);
1250 }
1251
1252 Event e{fmt::format("readData.{}_{}", meshName, dataName), profiling::API};
1253
1254 double readTime = _couplingScheme->getTime() + relativeReadTime;
1255 context.readValues(vertices, readTime, values);
1256}
1257
1259 std::string_view meshName,
1260 std::string_view dataName,
1262 double relativeReadTime,
1263 ::precice::span<double> values) const
1264{
1266 PRECICE_TRACE(meshName, dataName, coordinates.size(), relativeReadTime);
1267 PRECICE_CHECK(_state != State::Constructed, "mapAndReadData(...) cannot be called before initialize().");
1268 PRECICE_CHECK(_state != State::Finalized, "mapAndReadData(...) cannot be called after finalize().");
1269 PRECICE_CHECK(math::smallerEquals(relativeReadTime, _couplingScheme->getNextTimeStepMaxSize()), "readData(...) cannot sample data outside of current time window.");
1270 PRECICE_CHECK(relativeReadTime >= 0, "mapAndReadData(...) cannot sample data before the current time.");
1271 PRECICE_CHECK(isCouplingOngoing() || math::isZero(relativeReadTime), "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);
1272
1273 PRECICE_REQUIRE_DATA_READ(meshName, dataName);
1274 PRECICE_VALIDATE_DATA(coordinates.begin(), coordinates.size());
1275
1276 PRECICE_CHECK(_accessor->isMeshReceived(meshName) && _accessor->isDirectAccessAllowed(meshName),
1277 "This participant attempteded to map and read data (via \"mapAndReadData\") from mesh \"{0}\", "
1278 "but mesh \"{0}\" is either not a received mesh or its api access was not enabled in the configuration. "
1279 "mapAndReadData({0}, ...) is only valid for (<receive-mesh name=\"{0}\" ... api-access=\"true\"/>).",
1280 meshName);
1281 // If an access region is required, we have to check its existence
1282 bool requiresBB = requiresUserDefinedAccessRegion(meshName);
1283 PRECICE_CHECK(!requiresBB || (requiresBB && _accessor->receivedMeshContext(meshName).userDefinedAccessRegion),
1284 "The function \"mapAndReadData\" was called on mesh \"{0}\", "
1285 "but no access region was defined although this is necessary for parallel runs. "
1286 "Please define an access region using \"setMeshAccessRegion()\" before calling \"mapAndReadData()\".",
1287 meshName);
1288
1289 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. "
1290 "How should the provided data values be read? Please make sure the mesh \"{1}\" is non-empty within the access region.",
1291 dataName, meshName);
1292
1293 ReadDataContext &dataContext = _accessor->readDataContext(meshName, dataName);
1294 PRECICE_CHECK(dataContext.hasJustInTimeMapping(),
1295 "The function \"mapAndReadData\" was called on mesh \"{0}\", but no matching just-in-time mapping was configured. "
1296 "Please define a mapping in read direction from the mesh \"{0}\" and omit the \"to\" attribute from the definition. "
1297 "Example \"<mapping:nearest-neighbor direction=\"read\" from=\"{0}\" constraint=\"consistent\" />",
1298 meshName);
1299
1300 // Inconsistent sizes will be handled below
1301 if (coordinates.empty() && values.empty()) {
1302 return;
1303 }
1304
1305 Event e{fmt::format("mapAndReadData.{}_{}", meshName, dataName), profiling::API};
1306
1307 // Note that meshName refers to a remote mesh
1308 const auto dataDims = dataContext.getDataDimensions();
1309 const auto dim = dataContext.getSpatialDimensions();
1310 const auto nVertices = (coordinates.size() / dim);
1311
1312 // Check that the vertex is actually within the defined access region
1313 _accessor->receivedMeshContext(meshName).checkVerticesInsideAccessRegion(coordinates, dim, "mapAndReadData");
1314
1315 // Make use of the read data context
1316 PRECICE_CHECK(nVertices * dataDims == values.size(),
1317 "Input sizes are inconsistent attempting to mapAndRead {}D data \"{}\" from mesh \"{}\". "
1318 "You passed {} vertex indices and {} data components, but we expected {} data components ({} x {}).",
1319 dataDims, dataName, meshName,
1320 nVertices, values.size(), nVertices * dataDims, dataDims, nVertices);
1321
1322 double readTime = _couplingScheme->getTime() + relativeReadTime;
1323 dataContext.mapAndReadValues(coordinates, readTime, values);
1324}
1325
1327 std::string_view meshName,
1328 std::string_view dataName,
1331{
1333 PRECICE_TRACE(meshName, dataName, coordinates.size());
1334 PRECICE_CHECK(_state != State::Finalized, "writeAndMapData(...) cannot be called after finalize().");
1335 PRECICE_CHECK(_state != State::Constructed, "writeAndMapData(...) cannot be called before initialize(), because the mesh to map onto hasn't been received yet.");
1336 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.");
1337 PRECICE_REQUIRE_DATA_WRITE(meshName, dataName);
1338
1339 PRECICE_VALIDATE_DATA(coordinates.begin(), coordinates.size());
1340 PRECICE_VALIDATE_DATA(values.data(), values.size());
1341 PRECICE_CHECK(_accessor->isMeshReceived(meshName) && _accessor->isDirectAccessAllowed(meshName),
1342 "This participant attempteded to map and read data (via \"writeAndMapData\") from mesh \"{0}\", "
1343 "but mesh \"{0}\" is either not a received mesh or its api access was not enabled in the configuration. "
1344 "writeAndMapData({0}, ...) is only valid for (<receive-mesh name=\"{0}\" ... api-access=\"true\"/>).",
1345 meshName);
1346 // If an access region is required, we have to check its existence
1347 bool requiresBB = requiresUserDefinedAccessRegion(meshName);
1348 PRECICE_CHECK(!requiresBB || (requiresBB && _accessor->receivedMeshContext(meshName).userDefinedAccessRegion),
1349 "The function \"writeAndMapData\" was called on mesh \"{0}\", "
1350 "but no access region was defined although this is necessary for parallel runs. "
1351 "Please define an access region using \"setMeshAccessRegion()\" before calling \"writeAndMapData()\".",
1352 meshName);
1353
1354 WriteDataContext &dataContext = _accessor->writeDataContext(meshName, dataName);
1355 PRECICE_CHECK(dataContext.hasJustInTimeMapping(),
1356 "The function \"writeAndMapData\" was called on mesh \"{0}\", but no matching just-in-time mapping was configured. "
1357 "Please define a mapping in write direction to the mesh \"{0}\" and omit the \"from\" attribute from the definition. "
1358 "Example \"<mapping:nearest-neighbor direction=\"write\" to=\"{0}\" constraint=\"conservative\" />",
1359 meshName);
1360
1361 // Inconsistent sizes will be handled below
1362 if (coordinates.empty() && values.empty()) {
1363 return;
1364 }
1365
1366 Event e{fmt::format("writeAndMapData.{}_{}", meshName, dataName), profiling::API};
1367
1368 // Note that meshName refers here typically to a remote mesh
1369 const auto dataDims = dataContext.getDataDimensions();
1370 const auto dim = dataContext.getSpatialDimensions();
1371 const auto nVertices = (coordinates.size() / dim);
1372 ReceivedMeshContext &context = _accessor->receivedMeshContext(meshName);
1373
1374 // Check that the vertex is actually within the defined access region
1375 _accessor->receivedMeshContext(meshName).checkVerticesInsideAccessRegion(coordinates, dim, "writeAndMapData");
1376
1377 PRECICE_CHECK(nVertices * dataDims == values.size(),
1378 "Input sizes are inconsistent attempting to write {}D data \"{}\" to mesh \"{}\". "
1379 "You passed {} vertex indices and {} data components, but we expected {} data components ({} x {}).",
1380 dataDims, dataName, meshName,
1381 nVertices, values.size(), nVertices * dataDims, dataDims, nVertices);
1382
1383 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. "
1384 "Where should the provided data go? Please make sure the mesh \"{1}\" is non-empty within the access region.",
1385 dataName, meshName);
1386 dataContext.writeAndMapValues(coordinates, values);
1387}
1388
1390 std::string_view meshName,
1391 std::string_view dataName,
1394{
1396
1397 // Asserts and checks
1398 PRECICE_TRACE(meshName, dataName, vertices.size());
1399 PRECICE_CHECK(_state != State::Finalized, "writeGradientData(...) cannot be called after finalize().");
1400 PRECICE_REQUIRE_DATA_WRITE(meshName, dataName);
1401
1402 // Inconsistent sizes will be handled below
1403 if ((vertices.empty() && gradients.empty()) || !requiresGradientDataFor(meshName, dataName)) {
1404 return;
1405 }
1406
1407 // Get the data
1408 WriteDataContext &context = _accessor->writeDataContext(meshName, dataName);
1409
1410 // Check if the Data object of given mesh has been initialized with gradient data
1411 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);
1412
1413 if (auto index = context.locateInvalidVertexID(vertices); index) {
1414 PRECICE_ERROR("Cannot write gradient data \"{}\" to mesh \"{}\" due to invalid Vertex ID at vertices[{}]. "
1415 "Please make sure you only use the results from calls to setMeshVertex/Vertices().",
1416 dataName, meshName, *index);
1417 }
1418
1419 const auto dataDims = context.getDataDimensions();
1420 const auto meshDims = context.getSpatialDimensions();
1421 const auto gradientComponents = meshDims * dataDims;
1422 const auto expectedComponents = vertices.size() * gradientComponents;
1423 PRECICE_CHECK(expectedComponents == gradients.size(),
1424 "Input sizes are inconsistent attempting to write gradient for data \"{}\" to mesh \"{}\". "
1425 "A single gradient/Jacobian for {}D data on a {}D mesh has {} components. "
1426 "You passed {} vertex indices and {} gradient components, but we expected {} gradient components. ",
1427 dataName, meshName,
1428 dataDims, meshDims, gradientComponents,
1429 vertices.size(), gradients.size(), expectedComponents);
1430
1431 PRECICE_VALIDATE_DATA(gradients.data(), gradients.size());
1432
1433 Event e{fmt::format("writeGradientData.{}_{}", meshName, dataName), profiling::API};
1434
1435 context.writeGradientsIntoDataBuffer(vertices, gradients);
1436}
1437
1439 const std::string_view meshName,
1440 ::precice::span<const double> boundingBox) const
1441{
1442 PRECICE_TRACE(meshName, boundingBox.size());
1443 PRECICE_REQUIRE_MESH_USE(meshName);
1444 PRECICE_CHECK(_accessor->isMeshReceived(meshName) && _accessor->isDirectAccessAllowed(meshName),
1445 "This participant attempteded to set an access region (via \"setMeshAccessRegion\") on mesh \"{0}\", "
1446 "but mesh \"{0}\" is either not a received mesh or its api access was not enabled in the configuration. "
1447 "setMeshAccessRegion(...) is only valid for (<receive-mesh name=\"{0}\" ... api-access=\"true\"/>).",
1448 meshName);
1449 PRECICE_CHECK(_state != State::Finalized, "setMeshAccessRegion() cannot be called after finalize().");
1450 PRECICE_CHECK(_state != State::Initialized, "setMeshAccessRegion() needs to be called before initialize().");
1451
1452 // Get the related mesh - setMeshAccessRegion only works for received meshes
1453 ReceivedMeshContext &receivedContext = _accessor->receivedMeshContext(meshName);
1454
1455 PRECICE_CHECK(!receivedContext.userDefinedAccessRegion, "A mesh access region was already defined for mesh \"{}\". setMeshAccessRegion may only be called once per mesh.", receivedContext.mesh->getName());
1456 mesh::Mesh &mesh = *receivedContext.mesh;
1457 int dim = mesh.getDimensions();
1458 PRECICE_CHECK(boundingBox.size() == static_cast<unsigned long>(dim) * 2,
1459 "Incorrect amount of bounding box components attempting to set the bounding box of {}D mesh \"{}\" . "
1460 "You passed {} limits, but we expected {} ({}x2).",
1461 dim, meshName, boundingBox.size(), dim * 2, dim);
1462
1463 // Transform bounds into a suitable format
1464 PRECICE_DEBUG("Define bounding box");
1465 std::vector<double> bounds(dim * 2);
1466
1467 for (int d = 0; d < dim; ++d) {
1468 // Check that min is lower or equal to max
1469 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...]");
1470 bounds[2 * d] = boundingBox[2 * d];
1471 bounds[2 * d + 1] = boundingBox[2 * d + 1];
1472 }
1473 // Create a bounding box
1474 receivedContext.userDefinedAccessRegion = std::make_shared<mesh::BoundingBox>(bounds);
1475 // Expand the mesh associated bounding box
1476 mesh.expandBoundingBox(*receivedContext.userDefinedAccessRegion.get());
1477}
1478
1480 const std::string_view meshName,
1482 ::precice::span<double> coordinates) const
1483{
1484 PRECICE_TRACE(meshName, ids.size(), coordinates.size());
1485 PRECICE_REQUIRE_MESH_USE(meshName);
1486 PRECICE_CHECK(_accessor->isMeshReceived(meshName) && _accessor->isDirectAccessAllowed(meshName),
1487 "This participant attempteded to get mesh vertex IDs and coordinates (via \"getMeshVertexIDsAndCoordinates\") from mesh \"{0}\", "
1488 "but mesh \"{0}\" is either not a received mesh or its api access was not enabled in the configuration. "
1489 "getMeshVertexIDsAndCoordinates(...) is only valid for (<receive-mesh name=\"{0}\" ... api-access=\"true\"/>).",
1490 meshName);
1491 // If an access region is required, we have to check its existence
1492 bool requiresBB = requiresUserDefinedAccessRegion(meshName);
1493 PRECICE_CHECK(!requiresBB || (requiresBB && _accessor->receivedMeshContext(meshName).userDefinedAccessRegion),
1494 "The function \"getMeshVertexIDsAndCoordinates\" was called on mesh \"{0}\", "
1495 "but no access region was defined although this is necessary for parallel runs. "
1496 "Please define an access region using \"setMeshAccessRegion()\" before calling \"getMeshVertexIDsAndCoordinates()\".",
1497 meshName);
1498
1499 PRECICE_DEBUG("Get {} mesh vertices with IDs", ids.size());
1500
1501 // Check, if the requested mesh data has already been received. Otherwise, the function call doesn't make any sense
1502 PRECICE_CHECK((_state == State::Initialized) || _accessor->isMeshProvided(meshName),
1503 "initialize() has to be called before accessing data of the received mesh \"{}\" on participant \"{}\".",
1504 meshName, _accessor->getName());
1505
1506 if (ids.empty() && coordinates.empty()) {
1507 return;
1508 }
1509
1510 Event e{fmt::format("getMeshVertexIDsAndCoordinates.{}", meshName), profiling::API};
1511
1512 const MeshContext &context = _accessor->meshContext(meshName);
1513
1514 auto filteredVertices = _accessor->receivedMeshContext(meshName).filterVerticesToLocalAccessRegion(requiresBB);
1515 const auto meshSize = filteredVertices.size();
1516
1517 const mesh::Mesh &mesh = *(context.mesh);
1518 const auto meshDims = mesh.getDimensions();
1519 PRECICE_CHECK(ids.size() == meshSize,
1520 "Output size is incorrect attempting to get vertex ids of {}D mesh \"{}\". "
1521 "You passed {} vertex indices, but we expected {}. "
1522 "Use getMeshVertexSize(\"{}\") to receive the required amount of vertices.",
1523 meshDims, meshName, ids.size(), meshSize, meshName);
1524 const auto expectedCoordinatesSize = static_cast<unsigned long>(meshDims * meshSize);
1525 PRECICE_CHECK(coordinates.size() == expectedCoordinatesSize,
1526 "Output size is incorrect attempting to get vertex coordinates of {}D mesh \"{}\". "
1527 "You passed {} coordinate components, but we expected {} ({}x{}). "
1528 "Use getMeshVertexSize(\"{}\") and getMeshDimensions(\"{}\") to receive the required amount components",
1529 meshDims, meshName, coordinates.size(), expectedCoordinatesSize, meshSize, meshDims, meshName, meshName);
1530
1531 PRECICE_ASSERT(ids.size() <= mesh.nVertices(), "The queried size exceeds the number of available points.");
1532
1533 Eigen::Map<Eigen::MatrixXd> posMatrix{
1534 coordinates.data(), mesh.getDimensions(), static_cast<EIGEN_DEFAULT_DENSE_INDEX_TYPE>(ids.size())};
1535
1536 for (unsigned long i = 0; i < ids.size(); i++) {
1537 auto localID = filteredVertices[i].get().getID();
1538 PRECICE_ASSERT(mesh.isValidVertexID(localID), i, localID);
1539 ids[i] = localID;
1540 posMatrix.col(i) = filteredVertices[i].get().getCoords();
1541 }
1542}
1543
1545{
1546 // sort meshContexts by name, for communication in right order.
1547 std::sort(_accessor->usedMeshContexts().begin(), _accessor->usedMeshContexts().end(),
1548 [](const MeshContextVariant &lhs, const MeshContextVariant &rhs) -> bool {
1549 return getMesh(lhs).getName() < getMesh(rhs).getName();
1550 });
1551
1552 // Provided meshes need their bounding boxes already for the re-partitioning
1553 for (auto &context : _accessor->providedMeshContexts()) {
1554 context.mesh->computeBoundingBox();
1555 }
1556
1557 // Clear mappings for all meshes
1558 for (auto &variant : _accessor->usedMeshContexts()) {
1559 getMeshContext(variant)->clearMappings();
1560 }
1561
1562 // Compare bounding boxes for all meshes
1563 for (const auto &variant : _accessor->usedMeshContexts()) {
1565 }
1566}
1567
1569{
1570 // We need to do this in two loops: First, communicate the mesh and later compute the partition.
1571 // Originally, this was done in one loop. This however gave deadlock if two meshes needed to be communicated cross-wise.
1572 // Both loops need a different sorting
1573
1574 auto &contexts = _accessor->usedMeshContexts();
1575
1576 std::sort(contexts.begin(), contexts.end(),
1577 [](const MeshContextVariant &lhs, const MeshContextVariant &rhs) -> bool {
1578 return getMesh(lhs).getName() < getMesh(rhs).getName();
1579 });
1580
1581 for (const auto &variant : contexts) {
1582 getPartition(variant).communicate();
1583 }
1584
1585 // for two-level initialization, there is also still communication in partition::compute()
1586 // therefore, we cannot resort here.
1587 // @todo this hacky solution should be removed as part of #633
1588 bool resort = true;
1589 for (auto &m2nPair : _m2ns) {
1590 if (m2nPair.second.m2n->usesTwoLevelInitialization()) {
1591 resort = false;
1592 break;
1593 }
1594 }
1595
1596 if (resort) {
1597 // pull provided meshes up front, to have them ready for the decomposition of the received meshes (for the mappings)
1598 std::stable_partition(contexts.begin(), contexts.end(),
1599 [](const MeshContextVariant &variant) -> bool {
1600 return std::holds_alternative<ProvidedMeshContext *>(variant);
1601 });
1602 }
1603
1604 for (const auto &variant : contexts) {
1605 auto &mesh = getMesh(variant);
1606 getPartition(variant).compute();
1607
1608 // Received meshes can only compute their bounding boxes here
1609 if (std::holds_alternative<ReceivedMeshContext *>(variant)) {
1610 mesh.computeBoundingBox();
1611 }
1612
1613 mesh.allocateDataValues();
1614
1615 // Should be relevant for direct mesh access only
1616 const auto requiredSize = mesh.nVertices();
1617 for (auto &context : _accessor->writeDataContexts()) {
1618 if (context.getMeshName() == mesh.getName()) {
1619 context.resizeBufferTo(requiredSize, std::holds_alternative<ReceivedMeshContext *>(variant));
1620 }
1621 }
1622 }
1623}
1624
1625void ParticipantImpl::computeMappings(std::vector<MappingContext> &contexts, const std::string &mappingType)
1626{
1627 PRECICE_TRACE();
1628 bool anyMappingChanged = false;
1629 for (impl::MappingContext &context : contexts) {
1630 if (not context.mapping->hasComputedMapping()) {
1631 PRECICE_INFO_IF(context.configuredWithAliasTag,
1632 "Automatic RBF mapping alias from mesh \"{}\" to mesh \"{}\" in \"{}\" direction resolves to \"{}\" .",
1633 context.mapping->getInputMesh()->getName(), context.mapping->getOutputMesh()->getName(), mappingType, context.mapping->getName());
1634 PRECICE_INFO("Computing \"{}\" mapping from mesh \"{}\" to mesh \"{}\" in \"{}\" direction.",
1635 context.mapping->getName(), context.mapping->getInputMesh()->getName(), context.mapping->getOutputMesh()->getName(), mappingType);
1636 context.mapping->computeMapping();
1637 anyMappingChanged = true;
1638 }
1639 }
1640 if (anyMappingChanged) {
1641 _accessor->initializeMappingDataCache(mappingType);
1642 }
1643}
1644
1646{
1647 PRECICE_TRACE();
1648 if (!_accessor->hasWriteMappings()) {
1649 return;
1650 }
1651
1652 Event e("mapping", profiling::Fundamental);
1653 computeMappings(_accessor->writeMappingContexts(), "write");
1654 for (auto &context : _accessor->writeDataContexts()) {
1655 if (context.hasMapping()) {
1656 PRECICE_DEBUG("Map initial write data \"{}\" from mesh \"{}\"", context.getDataName(), context.getMeshName());
1657 _executedWriteMappings += context.mapData(std::nullopt, true);
1658 }
1659 }
1660}
1661
1662void ParticipantImpl::mapWrittenData(std::optional<double> after)
1663{
1664 PRECICE_TRACE();
1665 if (!_accessor->hasWriteMappings()) {
1666 return;
1667 }
1668
1669 Event e("mapping", profiling::Fundamental);
1670 computeMappings(_accessor->writeMappingContexts(), "write");
1671 for (auto &context : _accessor->writeDataContexts()) {
1672 if (context.hasMapping()) {
1673 PRECICE_DEBUG("Map write data \"{}\" from mesh \"{}\"", context.getDataName(), context.getMeshName());
1674 _executedWriteMappings += context.mapData(after);
1675 }
1676 }
1677}
1678
1679void ParticipantImpl::trimReadMappedData(double startOfTimeWindow, bool isTimeWindowComplete, const cplscheme::ImplicitData &fromData)
1680{
1681 PRECICE_TRACE();
1682 for (auto &context : _accessor->readDataContexts()) {
1683 if (context.hasMapping()) {
1685 // For serial implicit second, we need to discard everything before startOfTimeWindow to preserve the time window start
1686 // For serial implicit first, we need to discard everything as everything is new
1687 // For parallel implicit, we need to discard everything as everything is new
1688 context.clearToDataFor(fromData);
1689 } else {
1690 context.trimToDataAfterFor(fromData, startOfTimeWindow);
1691 }
1692 }
1693 }
1694}
1695
1697{
1698 PRECICE_TRACE();
1699 if (!_accessor->hasReadMappings()) {
1700 return;
1701 }
1702
1703 Event e("mapping", profiling::Fundamental);
1704 computeMappings(_accessor->readMappingContexts(), "read");
1705 for (auto &context : _accessor->readDataContexts()) {
1706 if (context.hasMapping()) {
1707 PRECICE_DEBUG("Map initial read data \"{}\" to mesh \"{}\"", context.getDataName(), context.getMeshName());
1708 // We always ensure that all read data was mapped
1709 _executedReadMappings += context.mapData(std::nullopt, true);
1710 }
1711 }
1712}
1713
1715{
1716 PRECICE_TRACE();
1717 if (!_accessor->hasReadMappings()) {
1718 return;
1719 }
1720
1721 Event e("mapping", profiling::Fundamental);
1722 computeMappings(_accessor->readMappingContexts(), "read");
1723 for (auto &context : _accessor->readDataContexts()) {
1724 if (context.hasMapping()) {
1725 PRECICE_DEBUG("Map read data \"{}\" to mesh \"{}\"", context.getDataName(), context.getMeshName());
1726 // We always ensure that all read data was mapped
1727 _executedReadMappings += context.mapData();
1728 }
1729 }
1730}
1731
1732void ParticipantImpl::performDataActions(const std::set<action::Action::Timing> &timings)
1733{
1734 PRECICE_TRACE();
1735 for (action::PtrAction &action : _accessor->actions()) {
1736 if (timings.find(action->getTiming()) != timings.end()) {
1737 action->performAction();
1738 }
1739 }
1740}
1741
1743{
1744 PRECICE_TRACE();
1745 if (!_accessor->hasExports()) {
1746 return;
1747 }
1748 PRECICE_DEBUG("Handle exports");
1749 profiling::Event e{"handleExports"};
1750
1751 if (timing == ExportTiming::Initial) {
1752 _accessor->exportInitial();
1753 return;
1754 }
1755
1757 exp.timewindow = _couplingScheme->getTimeWindows() - 1;
1759 exp.complete = _couplingScheme->isTimeWindowComplete();
1760 exp.final = !_couplingScheme->isCouplingOngoing();
1761 exp.time = _couplingScheme->getTime();
1762 _accessor->exportIntermediate(exp);
1763}
1764
1766{
1767 PRECICE_TRACE();
1768 for (auto &context : _accessor->writeDataContexts()) {
1769 // reset the buffered data here
1770 context.resetBufferedData();
1771 }
1772}
1773
1776{
1777 const auto &partConfig = *config.getParticipantConfiguration();
1778 PRECICE_CHECK(partConfig.hasParticipant(_accessorName),
1779 "This participant's name, which was specified in the constructor of the preCICE interface as \"{}\", "
1780 "is not defined in the preCICE configuration. "
1781 "Please double-check the correct spelling.",
1783 return partConfig.getParticipant(_accessorName);
1784}
1785
1796
1797void ParticipantImpl::syncTimestep(double computedTimeStepSize)
1798{
1800 Event e("syncTimestep", profiling::Fundamental);
1802 utils::IntraComm::getCommunication()->send(computedTimeStepSize, 0);
1803 } else {
1805 for (Rank secondaryRank : utils::IntraComm::allSecondaryRanks()) {
1806 double dt;
1807 utils::IntraComm::getCommunication()->receive(dt, secondaryRank);
1808 PRECICE_CHECK(math::equals(dt, computedTimeStepSize),
1809 "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 {}.",
1810 secondaryRank, dt, computedTimeStepSize);
1811 }
1812 }
1813}
1814
1816{
1817 PRECICE_DEBUG("Advance coupling scheme");
1818 Event e("advanceCoupling", profiling::Fundamental);
1819 // Orchestrate local and remote mesh changes
1820 std::vector<MeshID> localChanges;
1821
1822 [[maybe_unused]] auto remoteChanges1 = _couplingScheme->firstSynchronization(localChanges);
1823 _couplingScheme->firstExchange();
1824 // Orchestrate remote mesh changes (local ones were handled in the first sync)
1825 [[maybe_unused]] auto remoteChanges2 = _couplingScheme->secondSynchronization();
1826 _couplingScheme->secondExchange();
1827}
1828
1830{
1831 // Optionally apply some final ping-pong to sync solver that run e.g. with a uni-directional coupling
1832 // afterwards close connections
1833 PRECICE_INFO("{} {}communication channels",
1834 (_waitInFinalize ? "Synchronize participants and close" : "Close"),
1835 (close == CloseChannels::Distributed ? "distributed " : ""));
1836 std::string ping = "ping";
1837 std::string pong = "pong";
1838 for (auto &iter : _m2ns) {
1839 auto bm2n = iter.second;
1840 if (!bm2n.m2n->isConnected()) {
1841 PRECICE_DEBUG("Skipping closure of defective connection with {}", bm2n.remoteName);
1842 continue;
1843 }
1845 auto comm = bm2n.m2n->getPrimaryRankCommunication();
1846 PRECICE_DEBUG("Synchronizing primary rank with {}", bm2n.remoteName);
1847 if (bm2n.isRequesting) {
1848 comm->send(ping, 0);
1849 std::string receive = "init";
1850 comm->receive(receive, 0);
1851 PRECICE_ASSERT(receive == pong);
1852 } else {
1853 std::string receive = "init";
1854 comm->receive(receive, 0);
1855 PRECICE_ASSERT(receive == ping);
1856 comm->send(pong, 0);
1857 }
1858 }
1859 if (close == CloseChannels::Distributed) {
1860 PRECICE_DEBUG("Closing distributed communication with {}", bm2n.remoteName);
1861 bm2n.m2n->closeDistributedConnections();
1862 } else {
1863 PRECICE_DEBUG("Closing communication with {}", bm2n.remoteName);
1864 bm2n.m2n->closeConnection();
1865 }
1866 }
1867}
1868
1869bool ParticipantImpl::requiresUserDefinedAccessRegion(std::string_view meshName) const
1870{
1871 return _accessor->isMeshReceived(meshName) && utils::IntraComm::isParallel();
1872}
1873
1874const mesh::Mesh &ParticipantImpl::mesh(const std::string &meshName) const
1875{
1876 PRECICE_TRACE(meshName);
1877 return *_accessor->meshContext(meshName).mesh;
1878}
1879
1887
1888// Reinitialization
1889
1891{
1892 PRECICE_TRACE();
1894 Event e("gatherLocalMeshChanges", profiling::Synchronize);
1895
1896 // Gather local changes
1897 std::vector<double> localMeshChanges;
1898 for (const auto &variant : _accessor->usedMeshContexts()) {
1899 localMeshChanges.push_back(_meshLock.check(getMesh(variant).getName()) ? 0.0 : 1.0);
1900 }
1901 PRECICE_DEBUG("Mesh changes of rank: {}", localMeshChanges);
1902
1903 // TODO implement int version of allreduceSum
1904 std::vector<double> totalMeshChanges(localMeshChanges.size(), 0.0);
1905 utils::IntraComm::allreduceSum(localMeshChanges, totalMeshChanges);
1906
1907 // Convert the doubles to int
1908 MeshChanges totalMeshChangesInt(totalMeshChanges.begin(), totalMeshChanges.end());
1909 PRECICE_DEBUG("Mesh changes of participant: {}", totalMeshChangesInt);
1910 return totalMeshChangesInt;
1911}
1912
1914{
1915 // Clear stamples where changes were detected
1916 std::size_t i = 0;
1917 for (auto &variant : _accessor->usedMeshContexts()) {
1918 if (totalMeshChanges[i] > 0.0) {
1919 getMesh(variant).clearDataStamples();
1920 }
1921 ++i;
1922 }
1923}
1924
1925bool ParticipantImpl::reinitHandshake(bool requestReinit) const
1926{
1927 PRECICE_TRACE();
1929
1930 bool swarmReinitRequired = false;
1932 Event e("exchangeRemoteMeshChanges", profiling::Synchronize);
1933 PRECICE_DEBUG("Remeshing is{} required by this participant.", (requestReinit ? "" : " not"));
1934
1935 swarmReinitRequired = requestReinit;
1936 for (auto &iter : _m2ns) {
1937 PRECICE_DEBUG("Coordinating remeshing with {}", iter.first);
1938 bool received = false;
1939 auto &comm = *iter.second.m2n->getPrimaryRankCommunication();
1940 if (iter.second.isRequesting) {
1941 comm.send(requestReinit, 0);
1942 comm.receive(received, 0);
1943 } else {
1944 comm.receive(received, 0);
1945 comm.send(requestReinit, 0);
1946 }
1947 swarmReinitRequired |= received;
1948 }
1949 PRECICE_DEBUG("Coordinated that overall{} remeshing is required.", (swarmReinitRequired ? "" : " no"));
1950 }
1951
1952 Event e("broadcastLocalMeshChanges", profiling::Synchronize);
1953 utils::IntraComm::broadcast(swarmReinitRequired);
1954 return swarmReinitRequired;
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
bool smallerEquals(double a, double b, double tolerance=NUMERICAL_ZERO_DIFFERENCE) noexcept
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.
bool greaterEquals(double a, double b, double tolerance=NUMERICAL_ZERO_DIFFERENCE) noexcept
bool greater(double a, double b, double tolerance=NUMERICAL_ZERO_DIFFERENCE) noexcept
constexpr double NUMERICAL_ZERO_DIFFERENCE
bool isZero(double a, double tolerance=NUMERICAL_ZERO_DIFFERENCE) noexcept
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