preCICE
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"
33#include "utils/IntraComm.hpp"
34#include "utils/assertion.hpp"
35#include "utils/networking.hpp"
36#include "xml/ConfigParser.hpp"
37#include "xml/XMLAttribute.hpp"
38
39#ifdef PRECICE_NO_MPI
41#else
43#endif
44
45namespace precice::config {
46
48 xml::XMLTag &parent,
49 mesh::PtrMeshConfiguration meshConfiguration)
50 : _meshConfig(std::move(meshConfiguration))
51{
53 using namespace xml;
54 std::string doc;
55 XMLTag tag(*this, TAG, XMLTag::OCCUR_ONCE_OR_MORE);
56 doc = "Represents one solver using preCICE. At least two ";
57 doc += "participants have to be defined.";
58 tag.setDocumentation(doc);
59
60 auto attrName = XMLAttribute<std::string>(ATTR_NAME)
61 .setDocumentation(
62 "Name of the participant. Has to match the name given on construction "
63 "of the precice::Participant object used by the participant.");
64 tag.addAttribute(attrName);
65
66 XMLTag tagWriteData(*this, TAG_WRITE, XMLTag::OCCUR_ARBITRARY);
67 doc = "Sets data to be written by the participant to preCICE. ";
68 doc += "Data is defined by using the <data> tag.";
69 tagWriteData.setDocumentation(doc);
70 XMLTag tagReadData(*this, TAG_READ, XMLTag::OCCUR_ARBITRARY);
71 doc = "Sets data to be read by the participant from preCICE. ";
72 doc += "Data is defined by using the <data> tag.";
73 tagReadData.setDocumentation(doc);
74 auto attrDataName = XMLAttribute<std::string>(ATTR_NAME)
75 .setDocumentation("Name of the data.");
76 tagWriteData.addAttribute(attrDataName);
77 tagReadData.addAttribute(attrDataName);
78 auto attrMesh = XMLAttribute<std::string>(ATTR_MESH)
79 .setDocumentation(
80 "Mesh the data belongs to. If data should be read/written to several "
81 "meshes, this has to be specified separately for each mesh.");
82 tagWriteData.addAttribute(attrMesh);
83 tagReadData.addAttribute(attrMesh);
84
85 tag.addSubtag(tagWriteData);
86 tag.addSubtag(tagReadData);
87
88 _mappingConfig = std::make_shared<mapping::MappingConfiguration>(
89 tag, _meshConfig);
90
91 _actionConfig = std::make_shared<action::ActionConfiguration>(
92 tag, _meshConfig);
93
94 _exportConfig = std::make_shared<io::ExportConfiguration>(tag);
95
96 XMLTag tagWatchPoint(*this, TAG_WATCH_POINT, XMLTag::OCCUR_ARBITRARY);
97 doc = "A watch point can be used to follow the transient changes of data ";
98 doc += "and mesh vertex coordinates at a given point";
99 tagWatchPoint.setDocumentation(doc);
100 doc = "Name of the watch point. Is taken in combination with the participant ";
101 doc += "name to construct the filename the watch point data is written to.";
102 attrName.setDocumentation(doc);
103 tagWatchPoint.addAttribute(attrName);
104 doc = "Mesh to be watched.";
105 attrMesh.setDocumentation(doc);
106 tagWatchPoint.addAttribute(attrMesh);
107 auto attrCoordinate = XMLAttribute<Eigen::VectorXd>(ATTR_COORDINATE)
108 .setDocumentation(
109 "The coordinates of the watch point. If the watch point is not put exactly "
110 "on the mesh to observe, the closest projection of the point onto the "
111 "mesh is considered instead, and values/coordinates are interpolated "
112 "linearly to that point.");
113 tagWatchPoint.addAttribute(attrCoordinate);
114 tag.addSubtag(tagWatchPoint);
115
116 auto attrScaleWitConn = XMLAttribute<bool>(ATTR_SCALE_WITH_CONN)
117 .setDocumentation("Whether the vertex data is scaled with the element area before "
118 "summing up or not. In 2D, vertex data is scaled with the average length of "
119 "neighboring edges. In 3D, vertex data is scaled with the average surface of "
120 "neighboring triangles. If false, vertex data is directly summed up.");
121 XMLTag tagWatchIntegral(*this, TAG_WATCH_INTEGRAL, XMLTag::OCCUR_ARBITRARY);
122 doc = "A watch integral can be used to follow the transient change of integral data ";
123 doc += "and surface area for a given coupling mesh.";
124 tagWatchIntegral.setDocumentation(doc);
125 doc = "Name of the watch integral. Is taken in combination with the participant ";
126 doc += "name to construct the filename the watch integral data is written to.";
127 attrName.setDocumentation(doc);
128 tagWatchIntegral.addAttribute(attrName);
129 doc = "Mesh to be watched.";
130 attrMesh.setDocumentation(doc);
131 tagWatchIntegral.addAttribute(attrMesh);
132 tagWatchIntegral.addAttribute(attrScaleWitConn);
133 tag.addSubtag(tagWatchIntegral);
134
135 XMLTag tagProvideMesh(*this, TAG_PROVIDE_MESH, XMLTag::OCCUR_ARBITRARY);
136 doc = "Provide a mesh (see tag `<mesh>`) to other participants.";
137 tagProvideMesh.setDocumentation(doc);
138 attrName.setDocumentation("Name of the mesh to provide.");
139 tagProvideMesh.addAttribute(attrName);
140 tag.addSubtag(tagProvideMesh);
141
142 XMLTag tagReceiveMesh(*this, TAG_RECEIVE_MESH, XMLTag::OCCUR_ARBITRARY);
143 doc = "Makes a remote mesh (see tag `<mesh>`) available to this participant.";
144 tagReceiveMesh.setDocumentation(doc);
145 attrName.setDocumentation("Name of the mesh to receive.");
146 tagReceiveMesh.addAttribute(attrName);
147 auto attrFrom = XMLAttribute<std::string>(ATTR_FROM)
148 .setDocumentation("The name of the participant to receive the mesh from. "
149 "This participant needs to provide the mesh using `<provide-mesh />`.");
150
151 auto attrEnableAccess = makeXMLAttribute(ATTR_API_ACCESS, false)
152 .setDocumentation(
153 "Enables access to the data on this received mesh via the preCICE API functions without having to map it to a provided mesh. "
154 "This is required for direct access or just-in-time mappings. "
155 "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. "
156 "In such cases the API function `setMeshAccessRegion()` must be used to define the region of interest. "
157 "See the user documentation for more information.");
158 tagReceiveMesh.addAttribute(attrEnableAccess);
159 // @todo: remove with the next breaking release
160 auto attrDirectAccess = makeXMLAttribute(ATTR_DIRECT_ACCESS, false)
161 .setDocumentation(
162 "Deprecated: use \"api-access\" instead.");
163 tagReceiveMesh.addAttribute(attrDirectAccess);
164
165 auto attrGeoFilter = XMLAttribute<std::string>(ATTR_GEOMETRIC_FILTER)
166 .setDocumentation(
167 "For parallel execution, a received mesh needs to be decomposed. "
168 "A geometric filter based on bounding-boxes around the local mesh can speed up this process. "
169 "This setting controls if and where this filter is applied. "
170 "`on-primary-rank` is beneficial for a huge mesh and a low number of processors, but is incompatible with two-level initialization. "
171 "`on-secondary-ranks` performs better for a very high number of processors. "
172 "Both result in the same distribution if the safety-factor is sufficiently large. "
173 "`no-filter` may be useful for very asymmetric cases and for debugging. "
174 "If a mapping based on RBFs (rbf-pum,global-rbf) is used, the filter has no influence and is always `no-filter`.")
176 .setDefaultValue(VALUE_FILTER_ON_SECONDARY_RANKS);
177 tagReceiveMesh.addAttribute(attrGeoFilter);
178
179 tagReceiveMesh.addAttribute(attrFrom);
180 auto attrSafetyFactor = makeXMLAttribute(ATTR_SAFETY_FACTOR, 0.5)
181 .setDocumentation(
182 "The safety factor of the geometric filter uniformly scales the rank-local bounding box by the given factor. "
183 "A safety-factor of `0.5` means that the bounding box is 150% of its original size.");
184 tagReceiveMesh.addAttribute(attrSafetyFactor);
185
186 tag.addSubtag(tagReceiveMesh);
187
188 std::list<XMLTag> intraCommTags;
189 XMLTag::Occurrence intraCommOcc = XMLTag::OCCUR_NOT_OR_ONCE;
190 {
191 XMLTag tagIntraComm(*this, "sockets", intraCommOcc, TAG_INTRA_COMM);
192 doc = "A solver in parallel needs a communication between its ranks. ";
193 doc += "By default, the participant's MPI_COM_WORLD is reused.";
194 doc += "Use this tag to use TCP/IP sockets instead.";
195 tagIntraComm.setDocumentation(doc);
196
197 auto attrPort = makeXMLAttribute("port", 0)
198 .setDocumentation(
199 "Port number (16-bit unsigned integer) to be used for socket "
200 "communication. The default is \"0\", what means that OS will "
201 "dynamically search for a free port (if at least one exists) and "
202 "bind it automatically.");
203 tagIntraComm.addAttribute(attrPort);
204
205 auto attrNetwork = makeXMLAttribute(ATTR_NETWORK, utils::networking::loopbackInterfaceName())
206 .setDocumentation(
207 "Interface name to be used for socket communication. "
208 "Default is the canonical name of the loopback interface of your platform. "
209 "Might be different on supercomputing systems, e.g. \"ib0\" "
210 "for the InfiniBand on SuperMUC. ");
211 tagIntraComm.addAttribute(attrNetwork);
212
213 auto attrExchangeDirectory = makeXMLAttribute(ATTR_EXCHANGE_DIRECTORY, ".")
214 .setDocumentation(
215 "Directory where connection information is exchanged. By default, the "
216 "directory of startup is chosen.");
217 tagIntraComm.addAttribute(attrExchangeDirectory);
218
219 intraCommTags.push_back(tagIntraComm);
220 }
221 {
222 XMLTag tagIntraComm(*this, "mpi", intraCommOcc, TAG_INTRA_COMM);
223 doc = "A solver in parallel needs a communication between its ranks. ";
224 doc += "By default, the participant's MPI_COM_WORLD is reused.";
225 doc += "Use this tag to use MPI with separated communication spaces instead instead.";
226 tagIntraComm.setDocumentation(doc);
227
228 auto attrExchangeDirectory = makeXMLAttribute(ATTR_EXCHANGE_DIRECTORY, ".")
229 .setDocumentation(
230 "Directory where connection information is exchanged. By default, the "
231 "directory of startup is chosen.");
232 tagIntraComm.addAttribute(attrExchangeDirectory);
233
234 intraCommTags.push_back(tagIntraComm);
235 }
236
237 for (XMLTag &tagIntraComm : intraCommTags) {
238 tag.addSubtag(tagIntraComm);
239 }
240 parent.addSubtag(tag);
241}
242
244 bool experimental)
245{
246 _experimental = experimental;
247 _mappingConfig->setExperimental(_experimental);
248}
249
251 bool allowed)
252{
253 _remeshing = allowed;
254}
255
257 const xml::ConfigurationContext &context,
258 xml::XMLTag &tag)
259{
260 PRECICE_TRACE(tag.getName());
261 if (tag.getName() == TAG) {
262 const std::string &name = tag.getStringAttributeValue(ATTR_NAME);
264 _participants.push_back(p);
265 } else if (tag.getName() == TAG_PROVIDE_MESH) {
266 std::string name = tag.getStringAttributeValue(ATTR_NAME);
267
268 mesh::PtrMesh mesh = _meshConfig->getMesh(name);
270 R"(Participant "{}" attempts to provide an unknown mesh "{}". <mesh name="{}"> needs to be defined first.)",
271 _participants.back()->getName(), name, name);
272 _participants.back()->provideMesh(mesh);
273 } else if (tag.getName() == TAG_RECEIVE_MESH) {
274 std::string name = tag.getStringAttributeValue(ATTR_NAME);
275 std::string from = tag.getStringAttributeValue(ATTR_FROM);
276 double safetyFactor = tag.getDoubleAttributeValue(ATTR_SAFETY_FACTOR);
279 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=\"...\" />).");
280
281 // Start with defining the mesh
282 mesh::PtrMesh mesh = _meshConfig->getMesh(name);
284 R"(Participant "{}" attempts to receive an unknown mesh "{}". <mesh name="{}"> needs to be defined first.)",
285 _participants.back()->getName(), name, name);
286
287 // Then check the attributes
288 PRECICE_CHECK(!from.empty(),
289 R"(Participant "{}" receives mesh "{}", but doesn't specify where from. )"
290 "Please add the name of the other participant to the receive-mesh tag: <receive-mesh name=\"{}\" from=\"(other participant)\" ... />",
291 context.name, name, name);
292
293 PRECICE_CHECK(_participants.back()->getName() != from,
294 "Participant \"{}\" cannot receive mesh \"{}\" from itself. "
295 "To provide a mesh, use <provide-mesh name=\"{}\" /> instead.",
296 context.name, name, name);
297
298 PRECICE_CHECK(safetyFactor >= 0,
299 "Participant \"{}\" receives mesh \"{}\" with safety-factor=\"{}\". "
300 "Please use a positive or zero safety-factor instead.",
301 context.name, name, safetyFactor);
302
303 _participants.back()->receiveMesh(mesh, from, safetyFactor, geoFilter, allowDirectAccess);
304 } else if (tag.getName() == TAG_WRITE) {
305 const std::string &dataName = tag.getStringAttributeValue(ATTR_NAME);
306 std::string meshName = tag.getStringAttributeValue(ATTR_MESH);
307 mesh::PtrMesh mesh = _meshConfig->getMesh(meshName);
309 R"(Participant "{}" attempts to write data "{}" from an unknown mesh "{}". <mesh name="{}"> needs to be defined first.)",
310 _participants.back()->getName(), dataName, meshName, meshName);
311 mesh::PtrData data = getData(mesh, dataName);
312 _participants.back()->addWriteData(data, mesh);
313 } else if (tag.getName() == TAG_READ) {
314 const std::string &dataName = tag.getStringAttributeValue(ATTR_NAME);
315 std::string meshName = tag.getStringAttributeValue(ATTR_MESH);
316 mesh::PtrMesh mesh = _meshConfig->getMesh(meshName);
318 R"(Participant "{}" attempts to read data "{}" to an unknown mesh "{}". <mesh name="{}"> needs to be defined first.)",
319 _participants.back()->getName(), dataName, meshName, meshName);
320 mesh::PtrData data = getData(mesh, dataName);
321 _participants.back()->addReadData(data, mesh);
322 } else if (tag.getName() == TAG_WATCH_POINT) {
327 _watchPointConfigs.push_back(config);
328 } else if (tag.getName() == TAG_WATCH_INTEGRAL) {
333 _watchIntegralConfigs.push_back(config);
334 } else if (tag.getNamespace() == TAG_INTRA_COMM) {
335 if (auto participant = _participants.back()->getName();
336 context.size == 1 && participant == context.name) {
337 PRECICE_INFO("Ignoring user-defined intra-comm for participant {} as it is running in serial.", participant);
338 return;
339 }
342 _isIntraCommDefined = true;
343 _participants.back()->setUsePrimaryRank(true);
344 }
345}
346
348 const xml::ConfigurationContext &context,
349 xml::XMLTag &tag)
350{
351 if (tag.getName() == TAG) {
353 }
354}
355
357{
358 return _participants.size();
359}
360
361const std::vector<impl::PtrParticipant> &
366
367const impl::PtrParticipant ParticipantConfiguration::getParticipant(std::string_view participantName) const
368{
369 auto participant = std::find_if(_participants.begin(), _participants.end(), [&participantName](const auto &p) { return p->getName() == participantName; });
370 PRECICE_ASSERT(participant != _participants.end(), "Did not find participant \"{}\"", participantName);
371
372 return *participant;
373}
374
376{
377 auto range = _participants | boost::adaptors::transformed([](auto &p) { return p->getName(); });
378 return {range.begin(), range.end()};
379}
380
381bool ParticipantConfiguration::hasParticipant(std::string_view name) const
382{
383 return std::any_of(_participants.begin(), _participants.end(), [name](auto &p) { return p->getName() == name; });
384}
385
386std::string ParticipantConfiguration::hintFor(std::string_view wrongName) const
387{
388 PRECICE_ASSERT(!hasParticipant(wrongName));
389
390 const auto names = knownParticipants();
391 const auto matches = utils::computeMatches(wrongName, names);
392
393 // Typo detection
394 if (matches.front().distance < 3) {
395 return fmt::format("Did you mean: \"{}\"?", matches.front().name);
396 }
397
398 return fmt::format("Available participants are: {}.", fmt::join(names, ", "));
399}
400
412
414 const mesh::PtrMesh &mesh,
415 const std::string &nameData) const
416{
417 PRECICE_CHECK(mesh->hasDataName(nameData),
418 "Participant \"{}\" asks for data \"{}\" from mesh \"{}\", but this mesh does not use such data. "
419 "Please add a use-data tag with name=\"{}\" to this mesh.",
420 _participants.back()->getName(), nameData, mesh->getName(), nameData);
421 return mesh->data(nameData);
422}
423
425 const xml::ConfigurationContext &context,
426 const impl::PtrParticipant &participant)
427{
428 PRECICE_TRACE(participant->getName());
429
430 // Set input/output meshes for data mappings and mesh requirements
431 // This for loop transforms the MappingConfiguration::ConfiguredMappings
432 // into a MappingContext
434 for (const ConfMapping &confMapping : _mappingConfig->mappings()) {
435
436 checkIllDefinedMappings(confMapping, participant);
437
438 auto fromMesh = confMapping.fromMesh->getName();
439 auto toMesh = confMapping.toMesh->getName();
440
441 // sanity checks
442 if (confMapping.direction == mapping::MappingConfiguration::Direction::READ) {
443 // A read mapping maps from received to provided
444 PRECICE_CHECK(participant->isMeshReceived(fromMesh),
445 "Participant \"{}\" has a read mapping from mesh \"{}\", without receiving it. "
446 "Please add a receive-mesh tag with name=\"{}\"",
447 participant->getName(), fromMesh, fromMesh);
448 // The just-in-time mesh cannot be on the "from" mesh, as only the combinations read-consistent and write-conservative are allowed
449 PRECICE_CHECK(confMapping.toMesh->isJustInTime() || participant->isMeshProvided(toMesh),
450 "Participant \"{}\" has a read mapping to mesh \"{}\", without providing it. "
451 "Please add a provide-mesh tag with name=\"{}\"",
452 participant->getName(), toMesh, toMesh);
453 } else {
454 // A write mapping maps from provided to received
455 // The just-in-time mesh cannot be on the "to" mesh, as only the combinations read-consistent and write-conservative are allowed
456 PRECICE_CHECK(confMapping.fromMesh->isJustInTime() || participant->isMeshProvided(fromMesh),
457 "Participant \"{}\" has a write mapping from mesh \"{}\", without providing it. "
458 "Please add a provided-mesh tag with name=\"{}\"",
459 participant->getName(), fromMesh, fromMesh);
460 PRECICE_CHECK(participant->isMeshReceived(toMesh),
461 "Participant \"{}\" has a write mapping to mesh \"{}\", without receiving it. "
462 "Please add a receive-mesh tag with name=\"{}\"",
463 participant->getName(), toMesh, toMesh);
464 }
465
466 if (context.size > 1 && context.name == participant->getName()) {
467 if ((confMapping.direction == mapping::MappingConfiguration::WRITE &&
468 confMapping.mapping->getConstraint() == mapping::Mapping::CONSISTENT) ||
469 (confMapping.direction == mapping::MappingConfiguration::READ &&
470 confMapping.mapping->getConstraint() == mapping::Mapping::CONSERVATIVE)) {
471 PRECICE_ERROR("For a parallel participant, only the mapping combinations read-consistent and write-conservative are allowed");
472 } else if (confMapping.mapping->isScaledConsistent()) {
473 PRECICE_ERROR("Scaled consistent mapping is not yet supported for a parallel participant. "
474 "You could run in serial or use a plain (read-)consistent mapping instead.");
475 }
476 }
477
478 PRECICE_CHECK(!confMapping.mapping->isScaledConsistent() || !(confMapping.fromMesh->isJustInTime() || confMapping.toMesh->isJustInTime()),
479 "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());
480
481 // We disable the geometric filter for any kernel method, as the default safety factor is not reliable enough to provide a robust
482 // safety margin such that the mapping is still correct.
483 if (confMapping.requiresBasisFunction) {
484 if (!confMapping.fromMesh->isJustInTime()) {
485 // Only set geoFilter if this is a ReceivedMeshContext
486 if (participant->isMeshReceived(fromMesh)) {
487 participant->receivedMeshContext(fromMesh).geoFilter = partition::ReceivedPartition::GeometricFilter::NO_FILTER;
488 }
489 }
490 if (!confMapping.toMesh->isJustInTime()) {
491 // Only set geoFilter if this is a ReceivedMeshContext
492 if (participant->isMeshReceived(toMesh)) {
493 participant->receivedMeshContext(toMesh).geoFilter = partition::ReceivedPartition::GeometricFilter::NO_FILTER;
494 }
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 bool isProvided = participant->isMeshProvided(context.getMeshName());
656 PRECICE_CHECK(isProvided || !(participant->isDirectAccessAllowed(context.getMeshName()) && _remeshing), "Writing data via API access (configuration <write-data ... mesh=\"{}\") is not (yet) supported with remeshing", context.getMeshName());
657 }
658
659 // Add export contexts
660 for (io::ExportContext &exportContext : _exportConfig->exportContexts()) {
661 auto kind = exportContext.everyIteration ? io::Export::ExportKind::Iterations : io::Export::ExportKind::TimeWindows;
662
663 // Lambda to create exporter for any mesh context (avoids code duplication)
664 auto createExporter = [&](const impl::MeshContext &meshContext) {
665 exportContext.meshName = meshContext.mesh->getName();
666
667 io::PtrExport exporter;
668 if (exportContext.type == VALUE_VTK) {
669 // This is handled with respect to the current configuration context.
670 // Hence, this is potentially wrong for every participant other than context.name.
671 if (context.size > 1) {
672 // Only display the warning message if this participant configuration is the current one.
673 if (context.name == participant->getName()) {
674 PRECICE_ERROR("You attempted to use the legacy VTK exporter with the parallel participant {}, which isn't supported. "
675 "Migrate to another exporter, such as the VTU exporter by specifying \"<export:vtu ... />\" instead of \"<export:vtk ... />\".",
676 participant->getName());
677 }
678 } else {
679 exporter = io::PtrExport(new io::ExportVTK(
680 participant->getName(),
681 exportContext.location,
682 *meshContext.mesh,
683 kind,
684 exportContext.everyNTimeWindows,
685 context.rank,
686 context.size));
687 }
688 } else if (exportContext.type == VALUE_VTU) {
689 exporter = io::PtrExport(new io::ExportVTU(
690 participant->getName(),
691 exportContext.location,
692 *meshContext.mesh,
693 kind,
694 exportContext.everyNTimeWindows,
695 context.rank,
696 context.size));
697 } else if (exportContext.type == VALUE_VTP) {
698 exporter = io::PtrExport(new io::ExportVTP(
699 participant->getName(),
700 exportContext.location,
701 *meshContext.mesh,
702 kind,
703 exportContext.everyNTimeWindows,
704 context.rank,
705 context.size));
706 } else if (exportContext.type == VALUE_CSV) {
707 exporter = io::PtrExport(new io::ExportCSV(
708 participant->getName(),
709 exportContext.location,
710 *meshContext.mesh,
711 kind,
712 exportContext.everyNTimeWindows,
713 context.rank,
714 context.size));
715 } else {
716 PRECICE_ERROR("Participant {} defines an <export/> tag of unknown type \"{}\".",
717 _participants.back()->getName(), exportContext.type);
718 }
719 exportContext.exporter = std::move(exporter);
720 _participants.back()->addExportContext(exportContext);
721 };
722
723 // Create one exporter per provided mesh
724 for (const auto &meshContext : participant->providedMeshContexts()) {
725 createExporter(meshContext);
726 }
727
728 // Create one exporter per received mesh
729 for (const auto &meshContext : participant->receivedMeshContexts()) {
730 createExporter(meshContext);
731 }
732
733 PRECICE_WARN_IF(exportContext.everyNTimeWindows > 1 && exportContext.everyIteration,
734 "Participant {} defines an exporter of type {} which exports every iteration. "
735 "This overrides the every-n-time-window value you provided.",
736 _participants.back()->getName(), exportContext.type);
737 }
738 _exportConfig->resetExports();
739
740 // Create watch points
741 if (context.name == participant->getName()) {
743 PRECICE_CHECK(participant->isMeshUsed(config.nameMesh),
744 "Participant \"{}\" defines watchpoint \"{}\" for mesh \"{}\" which is not provided by the participant. "
745 "Please add <provide-mesh name=\"{}\" /> to the participant.",
746 participant->getName(), config.name, config.nameMesh, config.nameMesh);
747
748 PRECICE_CHECK(!participant->isMeshReceived(config.nameMesh),
749 "Participant \"{}\" defines watchpoint \"{}\" for the received mesh \"{}\", which is not allowed. "
750 "Please move the watchpoint definition to the participant providing mesh \"{}\".",
751 participant->getName(), config.name, config.nameMesh, config.nameMesh);
752 const auto &meshContext = participant->providedMeshContext(config.nameMesh);
753 PRECICE_CHECK(config.coordinates.size() == meshContext.mesh->getDimensions(),
754 "Provided coordinate to watch is {}D, which does not match the dimension of the {}D mesh \"{}\".",
755 config.coordinates.size(), meshContext.mesh->getDimensions(), meshContext.mesh->getName());
756 std::string filename = "precice-" + participant->getName() + "-watchpoint-" + config.name + ".log";
757 participant->addWatchPoint(std::make_shared<impl::WatchPoint>(config.coordinates, meshContext.mesh, std::move(filename)));
758 }
759 }
760 _watchPointConfigs.clear();
761
762 // Create watch integrals
763 if (context.name == participant->getName()) {
765 PRECICE_CHECK(participant->isMeshUsed(config.nameMesh),
766 "Participant \"{}\" defines watch integral \"{}\" for mesh \"{}\" which is not used by the participant. "
767 "Please add a provide-mesh node with name=\"{}\".",
768 participant->getName(), config.name, config.nameMesh, config.nameMesh);
769 const auto &meshContext = participant->meshContext(config.nameMesh);
770 PRECICE_CHECK(participant->isMeshProvided(config.nameMesh),
771 "Participant \"{}\" defines watch integral \"{}\" for the received mesh \"{}\", which is not allowed. "
772 "Please move the watchpoint definition to the participant providing mesh \"{}\".",
773 participant->getName(), config.name, config.nameMesh, config.nameMesh);
774
775 std::string filename = "precice-" + participant->getName() + "-watchintegral-" + config.name + ".log";
776 participant->addWatchIntegral(std::make_shared<impl::WatchIntegral>(meshContext.mesh, std::move(filename), config.isScalingOn));
777 }
778 }
779 _watchIntegralConfigs.clear();
780
781 // create default primary communication if needed
782 if (context.size > 1 && not _isIntraCommDefined && participant->getName() == context.name) {
783#ifdef PRECICE_NO_MPI
785 PRECICE_INFO("Implicit intra-participant communications for parallel participants using preCICE without MPI defaults to a sockets intracomm using the default loopback device {}. "
786 "Define your own <intra-comm:sockets ... /> to modify these defaults.",
787 lo);
788 utils::IntraComm::getCommunication() = std::make_shared<com::SocketCommunication>(0, false, lo, ".");
789#else
790 utils::IntraComm::getCommunication() = std::make_shared<com::MPIDirectCommunication>();
791#endif
792 participant->setUsePrimaryRank(true);
793 }
794 _isIntraCommDefined = false; // to not mess up with previous participant
795}
796
799 const impl::PtrParticipant &participant)
800{
803
804 for (const ConfMapping &configuredMapping : _mappingConfig->mappings()) {
805 bool sameToMesh = (mapping.toMesh->getName() == configuredMapping.toMesh->getName()) && !mapping.toMesh->isJustInTime();
806 bool sameFromMesh = (mapping.fromMesh->getName() == configuredMapping.fromMesh->getName()) && !mapping.fromMesh->isJustInTime();
807 if (sameToMesh && sameFromMesh) {
808 // It's really the same mapping, not a duplicated one. Those are already checked for in MappingConfiguration.
809 return;
810 }
811
812 if (sameToMesh) {
813 for (const mesh::PtrData &data : mapping.fromMesh->data()) {
814 for (const mesh::PtrData &configuredData : configuredMapping.fromMesh->data()) {
815 bool sameFromData = data->getName() == configuredData->getName();
816
817 if (not sameFromData) {
818 continue;
819 }
820
821 bool sameDirection = false;
822
824 for (const auto &dataContext : participant->writeDataContexts()) {
825 sameDirection |= data->getName() == dataContext.getDataName();
826 }
827 }
829 for (const auto &dataContext : participant->readDataContexts()) {
830 sameDirection |= data->getName() == dataContext.getDataName();
831 }
832 }
833 PRECICE_CHECK(!sameDirection,
834 "There cannot be two mappings to mesh \"{}\" if the meshes from which is mapped contain "
835 "duplicated data fields that are also actually mapped on this participant. "
836 "Here, both from meshes contain data \"{}\". "
837 "The mapping is not well defined. "
838 "Which data \"{}\" should be mapped to mesh \"{}\"?",
839 mapping.toMesh->getName(), data->getName(), data->getName(), mapping.toMesh->getName());
840 }
841 }
842 }
843 }
844}
845
846} // 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
#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:242
bool isJustInTime() const
Definition Mesh.hpp:340
bool hasDataName(std::string_view dataName) const
Returns whether Mesh has Data with the dataName.
Definition Mesh.cpp:216
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
vector< double > getData()
Definition mainA.cpp:19
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.
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
mesh::PtrMesh mesh
Mesh holding the geometry data structure.
Tightly coupled to the parameters of Participant()
Definition XMLTag.hpp:21