preCICE v3.3.0
Loading...
Searching...
No Matches
ParticipantConfiguration.cpp
Go to the documentation of this file.
2#include <algorithm>
3#include <boost/range/adaptor/transformed.hpp>
4#include <list>
5#include <memory>
6#include <stdexcept>
7#include <utility>
8
9#include "action/Action.hpp"
11#include "com/SharedPointer.hpp"
13#include "io/ExportCSV.hpp"
14#include "io/ExportContext.hpp"
15#include "io/ExportVTK.hpp"
16#include "io/ExportVTP.hpp"
17#include "io/ExportVTU.hpp"
18#include "io/SharedPointer.hpp"
20#include "logging/LogMacros.hpp"
21#include "mapping/Mapping.hpp"
22#include "mesh/Data.hpp"
23#include "mesh/Mesh.hpp"
31#include "utils/IntraComm.hpp"
32#include "utils/assertion.hpp"
33#include "utils/networking.hpp"
34#include "xml/ConfigParser.hpp"
35#include "xml/XMLAttribute.hpp"
36
37#ifdef PRECICE_NO_MPI
39#else
41#endif
42
43namespace precice::config {
44
46 xml::XMLTag &parent,
47 mesh::PtrMeshConfiguration meshConfiguration)
48 : _meshConfig(std::move(meshConfiguration))
49{
51 using namespace xml;
52 std::string doc;
53 XMLTag tag(*this, TAG, XMLTag::OCCUR_ONCE_OR_MORE);
54 doc = "Represents one solver using preCICE. At least two ";
55 doc += "participants have to be defined.";
56 tag.setDocumentation(doc);
57
58 auto attrName = XMLAttribute<std::string>(ATTR_NAME)
59 .setDocumentation(
60 "Name of the participant. Has to match the name given on construction "
61 "of the precice::Participant object used by the participant.");
62 tag.addAttribute(attrName);
63
64 XMLTag tagWriteData(*this, TAG_WRITE, XMLTag::OCCUR_ARBITRARY);
65 doc = "Sets data to be written by the participant to preCICE. ";
66 doc += "Data is defined by using the <data> tag.";
67 tagWriteData.setDocumentation(doc);
68 XMLTag tagReadData(*this, TAG_READ, XMLTag::OCCUR_ARBITRARY);
69 doc = "Sets data to be read by the participant from preCICE. ";
70 doc += "Data is defined by using the <data> tag.";
71 tagReadData.setDocumentation(doc);
72 auto attrDataName = XMLAttribute<std::string>(ATTR_NAME)
73 .setDocumentation("Name of the data.");
74 tagWriteData.addAttribute(attrDataName);
75 tagReadData.addAttribute(attrDataName);
76 auto attrMesh = XMLAttribute<std::string>(ATTR_MESH)
77 .setDocumentation(
78 "Mesh the data belongs to. If data should be read/written to several "
79 "meshes, this has to be specified separately for each mesh.");
80 tagWriteData.addAttribute(attrMesh);
81 tagReadData.addAttribute(attrMesh);
82
83 tag.addSubtag(tagWriteData);
84 tag.addSubtag(tagReadData);
85
87 tag, _meshConfig);
88
90 tag, _meshConfig);
91
93
94 XMLTag tagWatchPoint(*this, TAG_WATCH_POINT, XMLTag::OCCUR_ARBITRARY);
95 doc = "A watch point can be used to follow the transient changes of data ";
96 doc += "and mesh vertex coordinates at a given point";
97 tagWatchPoint.setDocumentation(doc);
98 doc = "Name of the watch point. Is taken in combination with the participant ";
99 doc += "name to construct the filename the watch point data is written to.";
100 attrName.setDocumentation(doc);
101 tagWatchPoint.addAttribute(attrName);
102 doc = "Mesh to be watched.";
103 attrMesh.setDocumentation(doc);
104 tagWatchPoint.addAttribute(attrMesh);
105 auto attrCoordinate = XMLAttribute<Eigen::VectorXd>(ATTR_COORDINATE)
106 .setDocumentation(
107 "The coordinates of the watch point. If the watch point is not put exactly "
108 "on the mesh to observe, the closest projection of the point onto the "
109 "mesh is considered instead, and values/coordinates are interpolated "
110 "linearly to that point.");
111 tagWatchPoint.addAttribute(attrCoordinate);
112 tag.addSubtag(tagWatchPoint);
113
114 auto attrScaleWitConn = XMLAttribute<bool>(ATTR_SCALE_WITH_CONN)
115 .setDocumentation("Whether the vertex data is scaled with the element area before "
116 "summing up or not. In 2D, vertex data is scaled with the average length of "
117 "neighboring edges. In 3D, vertex data is scaled with the average surface of "
118 "neighboring triangles. If false, vertex data is directly summed up.");
119 XMLTag tagWatchIntegral(*this, TAG_WATCH_INTEGRAL, XMLTag::OCCUR_ARBITRARY);
120 doc = "A watch integral can be used to follow the transient change of integral data ";
121 doc += "and surface area for a given coupling mesh.";
122 tagWatchIntegral.setDocumentation(doc);
123 doc = "Name of the watch integral. Is taken in combination with the participant ";
124 doc += "name to construct the filename the watch integral data is written to.";
125 attrName.setDocumentation(doc);
126 tagWatchIntegral.addAttribute(attrName);
127 doc = "Mesh to be watched.";
128 attrMesh.setDocumentation(doc);
129 tagWatchIntegral.addAttribute(attrMesh);
130 tagWatchIntegral.addAttribute(attrScaleWitConn);
131 tag.addSubtag(tagWatchIntegral);
132
133 XMLTag tagProvideMesh(*this, TAG_PROVIDE_MESH, XMLTag::OCCUR_ARBITRARY);
134 doc = "Provide a mesh (see tag `<mesh>`) to other participants.";
135 tagProvideMesh.setDocumentation(doc);
136 attrName.setDocumentation("Name of the mesh to provide.");
137 tagProvideMesh.addAttribute(attrName);
138 tag.addSubtag(tagProvideMesh);
139
140 XMLTag tagReceiveMesh(*this, TAG_RECEIVE_MESH, XMLTag::OCCUR_ARBITRARY);
141 doc = "Makes a remote mesh (see tag `<mesh>`) available to this participant.";
142 tagReceiveMesh.setDocumentation(doc);
143 attrName.setDocumentation("Name of the mesh to receive.");
144 tagReceiveMesh.addAttribute(attrName);
145 auto attrFrom = XMLAttribute<std::string>(ATTR_FROM)
146 .setDocumentation("The name of the participant to receive the mesh from. "
147 "This participant needs to provide the mesh using `<provide-mesh />`.");
148
149 auto attrEnableAccess = makeXMLAttribute(ATTR_API_ACCESS, false)
150 .setDocumentation(
151 "Enables access to the data on this received mesh via the preCICE API functions without having to map it to a provided mesh. "
152 "This is required for direct access or just-in-time mappings. "
153 "A received mesh needs to be decomposed in preCICE using a region of interest, which cannot be inferred, if there are no mappings to or from a provided mesh. "
154 "In such cases the API function `setMeshAccessRegion()` must be used to define the region of interest. "
155 "See the user documentation for more information.");
156 tagReceiveMesh.addAttribute(attrEnableAccess);
157 // @todo: remove with the next breaking release
158 auto attrDirectAccess = makeXMLAttribute(ATTR_DIRECT_ACCESS, false)
159 .setDocumentation(
160 "Deprecated: use \"api-access\" instead.");
161 tagReceiveMesh.addAttribute(attrDirectAccess);
162
163 auto attrGeoFilter = XMLAttribute<std::string>(ATTR_GEOMETRIC_FILTER)
164 .setDocumentation(
165 "For parallel execution, a received mesh needs to be decomposed. "
166 "A geometric filter based on bounding-boxes around the local mesh can speed up this process. "
167 "This setting controls if and where this filter is applied. "
168 "`on-primary-rank` is beneficial for a huge mesh and a low number of processors, but is incompatible with two-level initialization. "
169 "`on-secondary-ranks` performs better for a very high number of processors. "
170 "Both result in the same distribution if the safety-factor is sufficiently large. "
171 "`no-filter` may be useful for very asymmetric cases and for debugging. "
172 "If a mapping based on RBFs (rbf-pum,global-rbf) is used, the filter has no influence and is always `no-filter`.")
174 .setDefaultValue(VALUE_FILTER_ON_SECONDARY_RANKS);
175 tagReceiveMesh.addAttribute(attrGeoFilter);
176
177 tagReceiveMesh.addAttribute(attrFrom);
178 auto attrSafetyFactor = makeXMLAttribute(ATTR_SAFETY_FACTOR, 0.5)
179 .setDocumentation(
180 "The safety factor of the geometric filter uniformly scales the rank-local bounding box by the given factor. "
181 "A safety-factor of `0.5` means that the bounding box is 150% of its original size.");
182 tagReceiveMesh.addAttribute(attrSafetyFactor);
183
184 tag.addSubtag(tagReceiveMesh);
185
186 std::list<XMLTag> intraCommTags;
187 XMLTag::Occurrence intraCommOcc = XMLTag::OCCUR_NOT_OR_ONCE;
188 {
189 XMLTag tagIntraComm(*this, "sockets", intraCommOcc, TAG_INTRA_COMM);
190 doc = "A solver in parallel needs a communication between its ranks. ";
191 doc += "By default, the participant's MPI_COM_WORLD is reused.";
192 doc += "Use this tag to use TCP/IP sockets instead.";
193 tagIntraComm.setDocumentation(doc);
194
195 auto attrPort = makeXMLAttribute("port", 0)
196 .setDocumentation(
197 "Port number (16-bit unsigned integer) to be used for socket "
198 "communication. The default is \"0\", what means that OS will "
199 "dynamically search for a free port (if at least one exists) and "
200 "bind it automatically.");
201 tagIntraComm.addAttribute(attrPort);
202
203 auto attrNetwork = makeXMLAttribute(ATTR_NETWORK, utils::networking::loopbackInterfaceName())
204 .setDocumentation(
205 "Interface name to be used for socket communication. "
206 "Default is the canonical name of the loopback interface of your platform. "
207 "Might be different on supercomputing systems, e.g. \"ib0\" "
208 "for the InfiniBand on SuperMUC. ");
209 tagIntraComm.addAttribute(attrNetwork);
210
211 auto attrExchangeDirectory = makeXMLAttribute(ATTR_EXCHANGE_DIRECTORY, ".")
212 .setDocumentation(
213 "Directory where connection information is exchanged. By default, the "
214 "directory of startup is chosen.");
215 tagIntraComm.addAttribute(attrExchangeDirectory);
216
217 intraCommTags.push_back(tagIntraComm);
218 }
219 {
220 XMLTag tagIntraComm(*this, "mpi", intraCommOcc, TAG_INTRA_COMM);
221 doc = "A solver in parallel needs a communication between its ranks. ";
222 doc += "By default, the participant's MPI_COM_WORLD is reused.";
223 doc += "Use this tag to use MPI with separated communication spaces instead instead.";
224 tagIntraComm.setDocumentation(doc);
225
226 auto attrExchangeDirectory = makeXMLAttribute(ATTR_EXCHANGE_DIRECTORY, ".")
227 .setDocumentation(
228 "Directory where connection information is exchanged. By default, the "
229 "directory of startup is chosen.");
230 tagIntraComm.addAttribute(attrExchangeDirectory);
231
232 intraCommTags.push_back(tagIntraComm);
233 }
234
235 for (XMLTag &tagIntraComm : intraCommTags) {
236 tag.addSubtag(tagIntraComm);
237 }
238 parent.addSubtag(tag);
239}
240
242 bool experimental)
243{
244 _experimental = experimental;
245 _mappingConfig->setExperimental(_experimental);
246}
247
249 bool allowed)
250{
251 _remeshing = allowed;
252}
253
255 const xml::ConfigurationContext &context,
256 xml::XMLTag &tag)
257{
258 PRECICE_TRACE(tag.getName());
259 if (tag.getName() == TAG) {
262 _participants.push_back(p);
263 } else if (tag.getName() == TAG_PROVIDE_MESH) {
265
266 mesh::PtrMesh mesh = _meshConfig->getMesh(name);
268 R"(Participant "{}" attempts to provide an unknown mesh "{}". <mesh name="{}"> needs to be defined first.)",
269 _participants.back()->getName(), name, name);
270 _participants.back()->provideMesh(mesh);
271 } else if (tag.getName() == TAG_RECEIVE_MESH) {
274 double safetyFactor = tag.getDoubleAttributeValue(ATTR_SAFETY_FACTOR);
277 PRECICE_WARN_IF(tag.getBooleanAttributeValue(ATTR_DIRECT_ACCESS), "The 'direct-access' flag (<receive-mesh direct-access=\"...\" />) is deprecated and will be removed in preCICE v4. Use 'api-access' instead (<receive-mesh api-access=\"...\" />).");
278
279 // Start with defining the mesh
280 mesh::PtrMesh mesh = _meshConfig->getMesh(name);
282 R"(Participant "{}" attempts to receive an unknown mesh "{}". <mesh name="{}"> needs to be defined first.)",
283 _participants.back()->getName(), name, name);
284
285 // Then check the attributes
286 PRECICE_CHECK(!from.empty(),
287 R"(Participant "{}" receives mesh "{}", but doesn't specify where from. )"
288 "Please add the name of the other participant to the receive-mesh tag: <receive-mesh name=\"{}\" from=\"(other participant)\" ... />",
289 context.name, name, name);
290
291 PRECICE_CHECK(_participants.back()->getName() != from,
292 "Participant \"{}\" cannot receive mesh \"{}\" from itself. "
293 "To provide a mesh, use <provide-mesh name=\"{}\" /> instead.",
294 context.name, name, name);
295
296 PRECICE_CHECK(safetyFactor >= 0,
297 "Participant \"{}\" receives mesh \"{}\" with safety-factor=\"{}\". "
298 "Please use a positive or zero safety-factor instead.",
299 context.name, name, safetyFactor);
300
301 _participants.back()->receiveMesh(mesh, from, safetyFactor, geoFilter, allowDirectAccess);
302 } else if (tag.getName() == TAG_WRITE) {
303 const std::string &dataName = tag.getStringAttributeValue(ATTR_NAME);
305 mesh::PtrMesh mesh = _meshConfig->getMesh(meshName);
307 R"(Participant "{}" attempts to write data "{}" from an unknown mesh "{}". <mesh name="{}"> needs to be defined first.)",
308 _participants.back()->getName(), dataName, meshName, meshName);
309 mesh::PtrData data = getData(mesh, dataName);
310 _participants.back()->addWriteData(data, mesh);
311 } else if (tag.getName() == TAG_READ) {
312 const std::string &dataName = tag.getStringAttributeValue(ATTR_NAME);
314 mesh::PtrMesh mesh = _meshConfig->getMesh(meshName);
316 R"(Participant "{}" attempts to read data "{}" to an unknown mesh "{}". <mesh name="{}"> needs to be defined first.)",
317 _participants.back()->getName(), dataName, meshName, meshName);
318 mesh::PtrData data = getData(mesh, dataName);
319 _participants.back()->addReadData(data, mesh);
320 } else if (tag.getName() == TAG_WATCH_POINT) {
325 _watchPointConfigs.push_back(config);
326 } else if (tag.getName() == TAG_WATCH_INTEGRAL) {
331 _watchIntegralConfigs.push_back(config);
332 } else if (tag.getNamespace() == TAG_INTRA_COMM) {
333 if (auto participant = _participants.back()->getName();
334 context.size == 1 && participant == context.name) {
335 PRECICE_INFO("Ignoring user-defined intra-comm for participant {} as it is running in serial.", participant);
336 return;
337 }
340 _isIntraCommDefined = true;
341 _participants.back()->setUsePrimaryRank(true);
342 }
343}
344
346 const xml::ConfigurationContext &context,
347 xml::XMLTag &tag)
348{
349 if (tag.getName() == TAG) {
351 }
352}
353
358
364
366{
367 auto participant = std::find_if(_participants.begin(), _participants.end(), [&participantName](const auto &p) { return p->getName() == participantName; });
368 PRECICE_ASSERT(participant != _participants.end(), "Did not find participant \"{}\"", participantName);
369
370 return *participant;
371}
372
374{
375 auto range = _participants | boost::adaptors::transformed([](auto &p) { return p->getName(); });
376 return {range.begin(), range.end()};
377}
378
380{
381 return std::any_of(_participants.begin(), _participants.end(), [name](auto &p) { return p->getName() == name; });
382}
383
385{
386 PRECICE_ASSERT(!hasParticipant(wrongName));
387
388 const auto names = knownParticipants();
389 const auto matches = utils::computeMatches(wrongName, names);
390
391 // Typo detection
392 if (matches.front().distance < 3) {
393 return fmt::format("Did you mean: \"{}\"?", matches.front().name);
394 }
395
396 return fmt::format("Available participants are: {}.", fmt::join(names, ", "));
397}
398
410
412 const mesh::PtrMesh &mesh,
413 const std::string &nameData) const
414{
415 PRECICE_CHECK(mesh->hasDataName(nameData),
416 "Participant \"{}\" asks for data \"{}\" from mesh \"{}\", but this mesh does not use such data. "
417 "Please add a use-data tag with name=\"{}\" to this mesh.",
418 _participants.back()->getName(), nameData, mesh->getName(), nameData);
419 return mesh->data(nameData);
420}
421
423 const xml::ConfigurationContext &context,
424 const impl::PtrParticipant &participant)
425{
426 PRECICE_TRACE(participant->getName());
427
428 // Set input/output meshes for data mappings and mesh requirements
429 // This for loop transforms the MappingConfiguration::ConfiguredMappings
430 // into a MappingContext
432 for (const ConfMapping &confMapping : _mappingConfig->mappings()) {
433
434 checkIllDefinedMappings(confMapping, participant);
435
436 auto fromMesh = confMapping.fromMesh->getName();
437 auto toMesh = confMapping.toMesh->getName();
438
439 // sanity checks
440 if (confMapping.direction == mapping::MappingConfiguration::Direction::READ) {
441 // A read mapping maps from received to provided
442 PRECICE_CHECK(participant->isMeshReceived(fromMesh) || confMapping.toMesh->isJustInTime() || participant->isMeshProvided(toMesh),
443 "A read mapping of participant \"{}\" needs to map from a received to a provided mesh, but in this case they are swapped. "
444 "Did you intent to map from mesh \"{}\" to mesh \"{}\", or use a write mapping instead?",
445 participant->getName(), confMapping.toMesh->getName(), confMapping.fromMesh->getName());
446 PRECICE_CHECK(participant->isMeshReceived(fromMesh),
447 "Participant \"{}\" has a read mapping from mesh \"{}\", without receiving it. "
448 "Please add a receive-mesh tag with name=\"{}\"",
449 participant->getName(), fromMesh, fromMesh);
450 // The just-in-time mesh cannot be on the "from" mesh, as only the combinations read-consistent and write-conservative are allowed
451 PRECICE_CHECK(confMapping.toMesh->isJustInTime() || participant->isMeshProvided(toMesh),
452 "Participant \"{}\" has a read mapping to mesh \"{}\", without providing it. "
453 "Please add a provide-mesh tag with name=\"{}\"",
454 participant->getName(), toMesh, toMesh);
455 } else {
456 // A write mapping maps from provided to received
457 PRECICE_CHECK(confMapping.fromMesh->isJustInTime() || participant->isMeshProvided(fromMesh) || participant->isMeshReceived(toMesh),
458 "A write mapping of participant \"{}\" needs to map from a provided to a received mesh, but in this case they are swapped. "
459 "Did you intent to map from mesh \"{}\" to mesh \"{}\", or use a read mapping instead?",
460 participant->getName(), confMapping.toMesh->getName(), confMapping.fromMesh->getName());
461 // The just-in-time mesh cannot be on the "to" mesh, as only the combinations read-consistent and write-conservative are allowed
462 PRECICE_CHECK(confMapping.fromMesh->isJustInTime() || participant->isMeshProvided(fromMesh),
463 "Participant \"{}\" has a write mapping from mesh \"{}\", without providing it. "
464 "Please add a provided-mesh tag with name=\"{}\"",
465 participant->getName(), fromMesh, fromMesh);
466 PRECICE_CHECK(participant->isMeshReceived(toMesh),
467 "Participant \"{}\" has a write mapping to mesh \"{}\", without receiving it. "
468 "Please add a receive-mesh tag with name=\"{}\"",
469 participant->getName(), toMesh, toMesh);
470 }
471
472 if (context.size > 1 && context.name == participant->getName()) {
473 if ((confMapping.direction == mapping::MappingConfiguration::WRITE &&
474 confMapping.mapping->getConstraint() == mapping::Mapping::CONSISTENT) ||
475 (confMapping.direction == mapping::MappingConfiguration::READ &&
476 confMapping.mapping->getConstraint() == mapping::Mapping::CONSERVATIVE)) {
477 PRECICE_ERROR("For a parallel participant, only the mapping combinations read-consistent and write-conservative are allowed");
478 } else if (confMapping.mapping->isScaledConsistent()) {
479 PRECICE_ERROR("Scaled consistent mapping is not yet supported for a parallel participant. "
480 "You could run in serial or use a plain (read-)consistent mapping instead.");
481 }
482 }
483
484 PRECICE_CHECK(!confMapping.mapping->isScaledConsistent() || !(confMapping.fromMesh->isJustInTime() || confMapping.toMesh->isJustInTime()),
485 "The just-in-time mapping from mesh \"{}\" to mesh \"{}\" was configured with a scaled-consistent constraint. A scaled-consistent constraint is not implemented for just-in-time mappings in preCICE.", confMapping.fromMesh->getName(), confMapping.toMesh->getName());
486
487 // We disable the geometric filter for any kernel method, as the default safety factor is not reliable enough to provide a robust
488 // safety margin such that the mapping is still correct.
489 if (confMapping.requiresBasisFunction) {
490 if (!confMapping.fromMesh->isJustInTime()) {
491 participant->meshContext(fromMesh).geoFilter = partition::ReceivedPartition::GeometricFilter::NO_FILTER;
492 }
493 if (!confMapping.toMesh->isJustInTime()) {
494 participant->meshContext(toMesh).geoFilter = partition::ReceivedPartition::GeometricFilter::NO_FILTER;
495 }
496 }
497
498 // Now we create the mappingContext, which will be stored permanently
499 precice::impl::MappingContext mappingContext;
500 // Copy over data from MappingConfiguration
501 // 1. the mesh data
502 mappingContext.fromMeshID = confMapping.fromMesh->getID();
503 mappingContext.toMeshID = confMapping.toMesh->getID();
504
505 // Upon creation, the mapping should be empty
506 mapping::PtrMapping &map = mappingContext.mapping;
507 PRECICE_ASSERT(map.get() == nullptr);
508 // 2. ... and the mappings
509 map = confMapping.mapping;
510 mappingContext.configuredWithAliasTag = confMapping.configuredWithAliasTag;
511
512 // Set input and output meshes in the Mapping from the mesh contexts
513 const mesh::PtrMesh &input = confMapping.fromMesh->isJustInTime() ? confMapping.fromMesh : participant->meshContext(fromMesh).mesh;
514 const mesh::PtrMesh &output = confMapping.toMesh->isJustInTime() ? confMapping.toMesh : participant->meshContext(toMesh).mesh;
515 PRECICE_DEBUG("Configure mapping for input={}, output={}", input->getName(), output->getName());
516 map->setMeshes(input, output);
517
518 // just-in-time mappings go for now into the participant's mapping context
519 // Add the mapping context to the participant, separated by direction
520 if (confMapping.direction == mapping::MappingConfiguration::WRITE) {
521 participant->addWriteMappingContext(mappingContext);
522 } else {
523 PRECICE_ASSERT(confMapping.direction == mapping::MappingConfiguration::READ);
524 participant->addReadMappingContext(mappingContext);
525 }
526
527 // configure the involved mesh context with connectivity requirements stemming from the mapping
528 // Add the mapping context to the mesh context, only required to later on forward them to the Partition
529 if (!input->isJustInTime()) {
530 participant->configureInputMeshContext(fromMesh, mappingContext, map->getInputRequirement());
531 }
532 if (!output->isJustInTime()) {
533 participant->configureOutputMeshContext(toMesh, mappingContext, map->getOutputRequirement());
534 }
535 }
536 // clear the data structure we just transformed and don't need anymore
537 _mappingConfig->resetMappings();
538
539 // Now we have the MappingContexts and need to add information on the associated Data we want to map
540 //
541 // First in write direction:
542 // for all writeMappingContexts ...
543 for (impl::MappingContext &mappingContext : participant->writeMappingContexts()) {
544 // Check, whether we can find a corresponding write data context
545 bool dataFound = false;
546 for (auto &dataContext : participant->writeDataContexts()) {
547 // First we look for the "from" mesh ID from the "data perspective"
548 const int fromMeshID = dataContext.getMeshID();
549 // and compare it against the "from" mesh ID from the "mapping perspective"
550 if (mappingContext.fromMeshID == fromMeshID) {
551 // If these two are the same, we have a match of data and mapping contexts on the 'from' side
552 //
553 // the data context carries now the information about the associated name of the data itself
554 // Hence, we look if the "to" mesh (ID) stored in the mappingContext exists on the participant...
555 impl::MeshContext &meshContext = participant->meshContext(mappingContext.mapping->getOutputMesh()->getName());
556 // .. and if the mesh 'uses' the data to be mapped
557 // If this is true, we actually found a proper configuration and add the mapping
558 // If it is false, we look for another "from" mesh ID in the data context, because we might have multiple read and write mappings from the same 'from' mesh
559 if (meshContext.mesh->hasDataName(dataContext.getDataName())) {
560 // Check, if the fromMesh is a provided mesh
561 PRECICE_CHECK(participant->isMeshProvided(dataContext.getMeshName()),
562 "Participant \"{}\" has to provide mesh \"{}\" to be able to write data to it. "
563 "Please add a provide-mesh node with name=\"{}\".",
564 participant->getName(), dataContext.getMeshName(), dataContext.getMeshName());
565 // here, the mappingContext receives its to and from data pointer
566 // we append the mappingContext into the dataContext by copying it over, which is fine, since the context
567 // structures operate only on shared object pointers
568 dataContext.appendMappingConfiguration(mappingContext, meshContext);
569 // Enable gradient data if required
570 if (mappingContext.mapping->requiresGradientData() == true) {
571 mappingContext.requireGradientData(dataContext.getDataName());
572 }
573 dataFound = true;
574 }
575 } else if (mappingContext.mapping->getInputMesh()->isJustInTime()) {
576 const int toMeshID = dataContext.getMeshID();
577 // We compare here the to mesh instead of the from mesh
578 if (mappingContext.toMeshID == toMeshID) {
579 impl::MeshContext &meshContext = participant->meshContext(mappingContext.mapping->getOutputMesh()->getName());
580 dataContext.addJustInTimeMapping(mappingContext, meshContext);
581 if (mappingContext.mapping->requiresGradientData() == true) {
582 mappingContext.requireGradientData(dataContext.getDataName());
583 }
584 }
585 dataFound = true;
586 }
587 }
588 PRECICE_CHECK(dataFound,
589 "Participant \"{}\" defines a write mapping from mesh \"{}\" to mesh \"{}\", "
590 "but there is either no corresponding write-data tag or the meshes used "
591 "by this participant lack the necessary use-data tags.",
592 participant->getName(), mappingContext.mapping->getInputMesh()->getName(), mappingContext.mapping->getOutputMesh()->getName());
593 }
594
595 // Iterate over all read mappings
596 for (impl::MappingContext &mappingContext : participant->readMappingContexts()) {
597 // Check, weather we can find a corresponding read data context
598 bool dataFound = false;
599 for (auto &dataContext : participant->readDataContexts()) {
600 // First we look for the "to" mesh ID
601 const int toMeshID = dataContext.getMeshID();
602 if (mappingContext.toMeshID == toMeshID) {
603 // Second we look for the "from" mesh ID
604 impl::MeshContext &meshContext = participant->meshContext(mappingContext.mapping->getInputMesh()->getName());
605 // If this is true, we actually found a proper configuration
606 // If it is false, we look for another "from" mesh ID, because we might have multiple read and write mappings
607 if (meshContext.mesh->hasDataName(dataContext.getDataName())) {
608 // Check, if the toMesh is a provided mesh
609 PRECICE_CHECK(participant->isMeshProvided(dataContext.getMeshName()),
610 "Participant \"{}\" has to provide mesh \"{}\" in order to read data from it. "
611 "Please add a provide-mesh node with name=\"{}\".",
612 participant->getName(), dataContext.getMeshName(), dataContext.getMeshName());
613 dataContext.appendMappingConfiguration(mappingContext, meshContext);
614 // Enable gradient data if required
615 if (mappingContext.mapping->requiresGradientData() == true) {
616 mappingContext.requireGradientData(dataContext.getDataName());
617 }
618 dataFound = true;
619 }
620 } else if (mappingContext.mapping->getOutputMesh()->isJustInTime()) {
621 const int fromMeshID = dataContext.getMeshID();
622 // We compare here the from mesh instead of the to mesh
623 if (mappingContext.fromMeshID == fromMeshID) {
624 impl::MeshContext &meshContext = participant->meshContext(mappingContext.mapping->getInputMesh()->getName());
625
626 dataContext.addJustInTimeMapping(mappingContext, meshContext);
627 if (mappingContext.mapping->requiresGradientData() == true) {
628 mappingContext.requireGradientData(dataContext.getDataName());
629 }
630 }
631 dataFound = true;
632 }
633 }
634 PRECICE_CHECK(dataFound,
635 "Participant \"{}\" defines a read mapping from mesh \"{}\" to mesh \"{}\", "
636 "but there is either no corresponding read-data tag or the meshes used "
637 "by this participant lack the necessary use-data tags.",
638 participant->getName(), mappingContext.mapping->getInputMesh()->getName(), mappingContext.mapping->getOutputMesh()->getName());
639 }
640
641 // Add actions
642 for (const action::PtrAction &action : _actionConfig->actions()) {
643 bool used = _participants.back()->isMeshUsed(action->getMesh()->getName());
644 PRECICE_CHECK(used,
645 "Data action of participant \"{}\" uses mesh \"{}\", which is not used by the participant. "
646 "Please add a provide-mesh or receive-mesh node with name=\"{}\".",
647 _participants.back()->getName(), action->getMesh()->getName(), action->getMesh()->getName());
648 }
649 for (action::PtrAction &action : _actionConfig->extractActions()) {
650 _participants.back()->addAction(std::move(action));
651 }
652
653 // Check for unsupported remeshing options
654 for (auto &context : participant->writeDataContexts()) {
655 PRECICE_CHECK(participant->meshContext(context.getMeshName()).provideMesh || !(participant->isDirectAccessAllowed(context.getMeshName()) && _remeshing), "Writing data via API access (configuration <write-data ... mesh=\"{}\") is not (yet) supported with remeshing", context.getMeshName());
656 }
657
658 // Add export contexts
659 for (io::ExportContext &exportContext : _exportConfig->exportContexts()) {
660 auto kind = exportContext.everyIteration ? io::Export::ExportKind::Iterations : io::Export::ExportKind::TimeWindows;
661 // Create one exporter per mesh
662 for (const auto &meshContext : participant->usedMeshContexts()) {
663
664 exportContext.meshName = meshContext->mesh->getName();
665
666 io::PtrExport exporter;
667 if (exportContext.type == VALUE_VTK) {
668 // This is handled with respect to the current configuration context.
669 // Hence, this is potentially wrong for every participant other than context.name.
670 if (context.size > 1) {
671 // Only display the warning message if this participant configuration is the current one.
672 if (context.name == participant->getName()) {
673 PRECICE_ERROR("You attempted to use the legacy VTK exporter with the parallel participant {}, which isn't supported. "
674 "Migrate to another exporter, such as the VTU exporter by specifying \"<export:vtu ... />\" instead of \"<export:vtk ... />\".",
675 participant->getName());
676 }
677 } else {
678 exporter = io::PtrExport(new io::ExportVTK(
679 participant->getName(),
680 exportContext.location,
681 *meshContext->mesh,
682 kind,
683 exportContext.everyNTimeWindows,
684 context.rank,
685 context.size));
686 }
687 } else if (exportContext.type == VALUE_VTU) {
688 exporter = io::PtrExport(new io::ExportVTU(
689 participant->getName(),
690 exportContext.location,
691 *meshContext->mesh,
692 kind,
693 exportContext.everyNTimeWindows,
694 context.rank,
695 context.size));
696 } else if (exportContext.type == VALUE_VTP) {
697 exporter = io::PtrExport(new io::ExportVTP(
698 participant->getName(),
699 exportContext.location,
700 *meshContext->mesh,
701 kind,
702 exportContext.everyNTimeWindows,
703 context.rank,
704 context.size));
705 } else if (exportContext.type == VALUE_CSV) {
706 exporter = io::PtrExport(new io::ExportCSV(
707 participant->getName(),
708 exportContext.location,
709 *meshContext->mesh,
710 kind,
711 exportContext.everyNTimeWindows,
712 context.rank,
713 context.size));
714 } else {
715 PRECICE_ERROR("Participant {} defines an <export/> tag of unknown type \"{}\".",
716 _participants.back()->getName(), exportContext.type);
717 }
718 exportContext.exporter = std::move(exporter);
719
720 _participants.back()->addExportContext(exportContext);
721 }
722 PRECICE_WARN_IF(exportContext.everyNTimeWindows > 1 && exportContext.everyIteration,
723 "Participant {} defines an exporter of type {} which exports every iteration. "
724 "This overrides the every-n-time-window value you provided.",
725 _participants.back()->getName(), exportContext.type);
726 }
727 _exportConfig->resetExports();
728
729 // Create watch points
730 if (context.name == participant->getName()) {
732 PRECICE_CHECK(participant->isMeshUsed(config.nameMesh),
733 "Participant \"{}\" defines watchpoint \"{}\" for mesh \"{}\" which is not provided by the participant. "
734 "Please add <provide-mesh name=\"{}\" /> to the participant.",
735 participant->getName(), config.name, config.nameMesh, config.nameMesh);
736 const auto &meshContext = participant->usedMeshContext(config.nameMesh);
737 PRECICE_CHECK(meshContext.provideMesh,
738 "Participant \"{}\" defines watchpoint \"{}\" for the received mesh \"{}\", which is not allowed. "
739 "Please move the watchpoint definition to the participant providing mesh \"{}\".",
740 participant->getName(), config.name, config.nameMesh, config.nameMesh);
741 PRECICE_CHECK(config.coordinates.size() == meshContext.mesh->getDimensions(),
742 "Provided coordinate to watch is {}D, which does not match the dimension of the {}D mesh \"{}\".",
743 config.coordinates.size(), meshContext.mesh->getDimensions(), meshContext.mesh->getName());
744 std::string filename = "precice-" + participant->getName() + "-watchpoint-" + config.name + ".log";
745 participant->addWatchPoint(std::make_shared<impl::WatchPoint>(config.coordinates, meshContext.mesh, std::move(filename)));
746 }
747 }
748 _watchPointConfigs.clear();
749
750 // Create watch integrals
751 if (context.name == participant->getName()) {
753 PRECICE_CHECK(participant->isMeshUsed(config.nameMesh),
754 "Participant \"{}\" defines watch integral \"{}\" for mesh \"{}\" which is not used by the participant. "
755 "Please add a provide-mesh node with name=\"{}\".",
756 participant->getName(), config.name, config.nameMesh, config.nameMesh);
757 const auto &meshContext = participant->usedMeshContext(config.nameMesh);
758 PRECICE_CHECK(meshContext.provideMesh,
759 "Participant \"{}\" defines watch integral \"{}\" for the received mesh \"{}\", which is not allowed. "
760 "Please move the watchpoint definition to the participant providing mesh \"{}\".",
761 participant->getName(), config.name, config.nameMesh, config.nameMesh);
762
763 std::string filename = "precice-" + participant->getName() + "-watchintegral-" + config.name + ".log";
764 participant->addWatchIntegral(std::make_shared<impl::WatchIntegral>(meshContext.mesh, std::move(filename), config.isScalingOn));
765 }
766 }
767 _watchIntegralConfigs.clear();
768
769 // create default primary communication if needed
770 if (context.size > 1 && not _isIntraCommDefined && participant->getName() == context.name) {
771#ifdef PRECICE_NO_MPI
773 PRECICE_INFO("Implicit intra-participant communications for parallel participants using preCICE without MPI defaults to a sockets intracomm using the default loopback device {}. "
774 "Define your own <intra-comm:sockets ... /> to modify these defaults.",
775 lo);
777#else
779#endif
780 participant->setUsePrimaryRank(true);
781 }
782 _isIntraCommDefined = false; // to not mess up with previous participant
783}
784
787 const impl::PtrParticipant &participant)
788{
791
792 for (const ConfMapping &configuredMapping : _mappingConfig->mappings()) {
793 bool sameToMesh = (mapping.toMesh->getName() == configuredMapping.toMesh->getName()) && !mapping.toMesh->isJustInTime();
794 bool sameFromMesh = (mapping.fromMesh->getName() == configuredMapping.fromMesh->getName()) && !mapping.fromMesh->isJustInTime();
795 if (sameToMesh && sameFromMesh) {
796 // It's really the same mapping, not a duplicated one. Those are already checked for in MappingConfiguration.
797 return;
798 }
799
800 if (sameToMesh) {
801 for (const mesh::PtrData &data : mapping.fromMesh->data()) {
802 for (const mesh::PtrData &configuredData : configuredMapping.fromMesh->data()) {
803 bool sameFromData = data->getName() == configuredData->getName();
804
805 if (not sameFromData) {
806 continue;
807 }
808
809 bool sameDirection = false;
810
812 for (const auto &dataContext : participant->writeDataContexts()) {
813 sameDirection |= data->getName() == dataContext.getDataName();
814 }
815 }
817 for (const auto &dataContext : participant->readDataContexts()) {
818 sameDirection |= data->getName() == dataContext.getDataName();
819 }
820 }
821 PRECICE_CHECK(!sameDirection,
822 "There cannot be two mappings to mesh \"{}\" if the meshes from which is mapped contain "
823 "duplicated data fields that are also actually mapped on this participant. "
824 "Here, both from meshes contain data \"{}\". "
825 "The mapping is not well defined. "
826 "Which data \"{}\" should be mapped to mesh \"{}\"?",
827 mapping.toMesh->getName(), data->getName(), data->getName(), mapping.toMesh->getName());
828 }
829 }
830 }
831 }
832}
833
834} // namespace precice::config
#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(...)
Definition LogMacros.hpp:14
#define PRECICE_CHECK(check,...)
Definition LogMacros.hpp:32
T any_of(T... args)
#define PRECICE_ASSERT(...)
Definition assertion.hpp:85
const std::string & getName() const
Returns the name of the mesh, as set in the config file.
Definition Mesh.cpp:220
bool isJustInTime() const
Definition Mesh.hpp:332
bool hasDataName(std::string_view dataName) const
Returns whether Mesh has Data with the dataName.
Definition Mesh.cpp:194
Configuration for communication channels between a primary and its secondary ranks....
PtrCommunication createCommunication(const xml::XMLTag &tag) const
Returns a communication object of given type.
const std::vector< impl::PtrParticipant > & getParticipants() const
Returns all configured participants.
std::vector< impl::PtrParticipant > _participants
ParticipantConfiguration(xml::XMLTag &parent, mesh::PtrMeshConfiguration meshConfiguration)
partition::ReceivedPartition::GeometricFilter getGeoFilter(const std::string &geoFilter) const
void xmlTagCallback(const xml::ConfigurationContext &context, xml::XMLTag &callingTag) override
Callback function required for use of automatic configuration.
const mesh::PtrData & getData(const mesh::PtrMesh &mesh, const std::string &nameData) const
void finishParticipantConfiguration(const xml::ConfigurationContext &context, const impl::PtrParticipant &participant)
std::string hintFor(std::string_view wrongName) const
void checkIllDefinedMappings(const mapping::MappingConfiguration::ConfiguredMapping &mapping, const impl::PtrParticipant &participant)
Check whether a mapping to the same mesh and with similar data fields already exists.
void xmlEndTagCallback(const xml::ConfigurationContext &context, xml::XMLTag &callingTag) override
Callback function required for use of automatic configuration.
std::vector< WatchIntegralConfig > _watchIntegralConfigs
const impl::PtrParticipant getParticipant(std::string_view participantName) const
Returns a participant with the given name.
Holds coupling state of one participating solver in coupled simulation.
Writes polygonal, or triangle meshes to vtk files.
Definition ExportVTK.hpp:16
GeometricFilter
Defines the type of geometric filter used.
@ ON_PRIMARY_RANK
Filter at primary rank and communicate only filtered mesh.
@ ON_SECONDARY_RANKS
Filter after communication on all secondary ranks.
@ NO_FILTER
No geometric filter used (e.g. for RBF mappings)
static com::PtrCommunication & getCommunication()
Intra-participant communication.
Definition IntraComm.hpp:31
Represents an XML tag to be configured automatically.
Definition XMLTag.hpp:28
Eigen::VectorXd getEigenVectorXdAttributeValue(const std::string &name) const
Definition XMLTag.cpp:173
const std::string & getNamespace() const
Returns xml namespace.
Definition XMLTag.hpp:159
std::string getStringAttributeValue(const std::string &name, std::optional< std::string > default_value=std::nullopt) const
Definition XMLTag.cpp:145
bool getBooleanAttributeValue(const std::string &name, std::optional< bool > default_value=std::nullopt) const
Definition XMLTag.cpp:159
const std::string & getName() const
Returns name (without namespace).
Definition XMLTag.hpp:153
double getDoubleAttributeValue(const std::string &name, std::optional< double > default_value=std::nullopt) const
Definition XMLTag.cpp:117
XMLTag & addSubtag(const XMLTag &tag)
Adds an XML tag as subtag by making a copy of the given tag.
Definition XMLTag.cpp:41
T empty(T... args)
T find_if(T... args)
T get(T... args)
vector< double > getData()
Definition mainA.cpp:19
T make_shared(T... args)
contains actions to modify exchanged data.
Definition Action.hpp:6
std::unique_ptr< Action > PtrAction
std::shared_ptr< ParticipantState > PtrParticipant
std::shared_ptr< Export > PtrExport
contains data mapping from points to meshes.
std::shared_ptr< Mapping > PtrMapping
provides Mesh, Data and primitives.
std::shared_ptr< Data > PtrData
std::shared_ptr< Mesh > PtrMesh
std::shared_ptr< MeshConfiguration > PtrMeshConfiguration
std::string loopbackInterfaceName()
Returns the name of the canonical loopback interface on this system.
Definition networking.cpp:5
std::vector< StringMatch > computeMatches(std::string_view given, const Container &expected)
Definition String.hpp:95
contains the XML configuration parser.
STL namespace.
T push_back(T... args)
Holds a data mapping and related information.
mapping::PtrMapping mapping
Data mapping.
MeshID fromMeshID
id of mesh from which is mapped
bool configuredWithAliasTag
used the automatic rbf alias tag in order to set the mapping
MeshID toMeshID
id of mesh to which is mapped
Stores a mesh and related objects and data.
mesh::PtrMesh mesh
Mesh holding the geometry data structure.
Tightly coupled to the parameters of Participant()
Definition XMLTag.hpp:21