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