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