Skip to content

Commit 093c8fb

Browse files
committed
Update couple your code
1 parent e729744 commit 093c8fb

6 files changed

+109
-94
lines changed

pages/docs/couple-your-code/couple-your-code-defining-mesh-connectivity.md

Lines changed: 50 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,41 @@ For volume coupling in 2D, mesh connectivity boils down to defining triangles an
1515

1616
All kind of connectivity can be built up directly from vertices. Triangles and quads also allow us to define them using edge IDs.
1717

18-
<!-- TODO: What about setMeshEdges, setMeshTriangle, setMeshQuad, setMeshTetrahedron? -->
1918
```cpp
20-
int setMeshEdge (precice::string_view meshName, int firstVertexID, int secondVertexID);
21-
void setMeshTriangle (precice::string_view meshName, int firstVertexID, int secondVertexID, int thirdVertexID);
22-
void setMeshQuad(precice::string_view meshName, int firstVertexID, int secondVertexID, int thirdVertexID, int fourthVertexID);
23-
void setMeshTetrahedron(precice::string_view meshName, int firstVertexID, int secondVertexID, int thirdVertexID, int fourthVertexID);
19+
void setMeshEdge(precice::string_view meshName, VertexID firstVertexID, VertexID secondVertexID);
20+
void setMeshTriangle(precice::string_view meshName, VertexID firstVertexID, VertexID secondVertexID, VertexID thirdVertexID);
21+
void setMeshQuad(precice::string_view meshName, VertexID firstVertexID, VertexID secondVertexID, VertexID thirdVertexID, VertexID fourthVertexID);
22+
void setMeshTetrahedron(precice::string_view meshName, VertexID firstVertexID, VertexID secondVertexID, VertexID thirdVertexID, VertexID fourthVertexID);
2423
```
2524
2625
* `setMeshEdge` defines a mesh edge between two vertices and returns an edge ID.
2726
* `setMeshTriangle` defines a mesh triangle by three edges.
28-
* `setMeshQuad` defines a mesh quad by four edges.
27+
* `setMeshQuad` defines a planar mesh quad by four edges, which is internally triangulated. Prefer to define triangles if you can.
2928
* `setMeshTetrahredron` defines a mesh tetrahedron by four vertices.
3029
30+
There are also versions of these methods, which can be easier to handle in some cases:
31+
32+
```cpp
33+
void setMeshEdges(precice::string_view meshName, precice::span<const VertexID> vertices);
34+
void setMeshTriangles(precice::string_view meshName, precice::span<const VertexID> vertices);
35+
void setMeshQuads(precice::string_view meshName, precice::span<const VertexID> vertices);
36+
void setMeshTetrahedra(precice::string_view meshName, precice::span<const VertexID> vertices);
37+
```
38+
3139
If you do not configure any features in the preCICE configuration that require mesh connectivity, all these API functions are [no-ops](https://en.wikipedia.org/wiki/NOP_(code)). Thus, don't worry about performance. If you need a significant workload to already create this connectivity information in your adapter in the first place, you can also explicitly ask preCICE whether it is required:
3240

33-
<!-- TODO: needs update -->
3441
```cpp
35-
bool isMeshConnectivityRequired(int meshID);
42+
bool requiresMeshConnectivityFor(precice::string_view meshName);
3643
```
3744
3845
{% warning %}
3946
The API function `isMeshConnectivityRequired` is only supported since v2.3.
4047
{% endwarning %}
4148
49+
{% warning %}
50+
The bulk API functions are only supported since v3.
51+
{% endwarning %}
52+
4253
Maybe interesting to know: preCICE actually does internally not compute with quads, but creates two triangles. [Read more](https://precice.discourse.group/t/highlights-of-the-new-precice-release-v2-1/274#2-1-using-quads-for-projection).
4354
4455
{% warning %}
@@ -48,52 +59,45 @@ Quads are only supported since v2.1. For older version, the methods only exist a
4859
The following code shows how mesh connectivity can be defined in our example. For sake of simplification, let's only define one triangle and let's assume that it consists of the first three vertices.
4960
5061
```cpp
51-
52-
[...]
53-
54-
std::vector<int> vertexIDs(vertexSize);
62+
/* ... */
63+
64+
// We define the unit square in 2D
65+
std::vector<double> coords{
66+
0, 0, // A
67+
0, 1, // B
68+
1, 0, // C
69+
1, 1 // D
70+
};
71+
std::vector<VertexID> vertexIDs(4);
5572
precice.setMeshVertices(meshName, coords, vertexIDs);
5673
57-
int edgeIDs[3];
58-
edgeIDs[0] = precice.setMeshEdge(meshName, vertexIDs[0], vertexIDs[1]);
59-
edgeIDs[1] = precice.setMeshEdge(meshName, vertexIDs[1], vertexIDs[2]);
60-
edgeIDs[2] = precice.setMeshEdge(meshName, vertexIDs[2], vertexIDs[0]);
61-
62-
if(dim==3)
63-
precice.setMeshTriangle(meshName, edgeIDs[0], edgeIDs[1], edgeIDs[2]);
64-
65-
[...]
74+
if (precice.requiresMeshConnectivityFor(meshName)) {
75+
76+
// defines triangles ABC and BCD separately
77+
precice.setMeshTriangle(meshName, vertexIDs[0], vertexIDs[1], vertexIDs[2]);
78+
precice.setMeshTriangle(meshName, vertexIDs[1], vertexIDs[2], vertexIDs[3]);
79+
80+
// defines triangles ABC and BCD in one go
81+
std::vector<VertexID> triangles{
82+
vertexIDs[0], vertexIDs[1], vertexIDs[2],
83+
vertexIDs[1], vertexIDs[2], vertexIDs[3]
84+
};
85+
precice.setMeshTriangles(meshName, triangles)
86+
}
6687
88+
/* ... */
6789
```
6890

69-
<!-- TODO: Section below should be the default on branch precice-v3 -->
70-
## Changes in v3
71-
72-
Version 3 overhauls the definition of meshes.
73-
74-
Connectivity now consists of explicitly defined elements (elements created via calls to the API) and implicitly defined elements (elements additionally created by preCICE).
75-
As an example, explicitly defining a triangle ABC via the API guarantees the existence of the implicit edges AB, AC, and BC.
76-
77-
Furthermore, all connectivity is defined only via vertex IDs. There are no more edge IDs to worry about.
78-
The order of vertices also does not matter. Triangles BAC and CAB are considered duplicates and preCICE removes one of them during the deduplication step.
91+
## Mesh pre-processing
7992

80-
The API for defining individual connectivity elements looks as follows:
93+
preCICE pre-processes all provided meshes during initialization, removing duplicates and adding missing hierarchical elements.
8194

82-
```cpp
83-
void setMeshEdge(precice::string_view meshName, int firstVertexID, int secondVertexID);
84-
void setMeshTriangle(precice::string_view meshName, int firstVertexID, int secondVertexID, int thirdVertexID);
85-
void setMeshQuad(precice::string_view meshName, int firstVertexID, int secondVertexID, int thirdVertexID, int fourthVertexID);
86-
void setMeshTetrahredron(precice::string_view meshName, int firstVertexID, int secondVertexID, int thirdVertexID, int fourthVertexID);
87-
```
88-
89-
Each of the above functions is accompanied by a bulk version, which allows to set multiple elements in a single call.
95+
Some projection-based features require all hierarchical elements to be present.
96+
Meaning, a triangle ABC requires edges AB, BC and AC to exist.
97+
Manually defining such elements is not a pleasant experience, especially when dealing with tetrahedra whilst avoiding duplicates.
9098

91-
```cpp
92-
void setMeshEdges(precice::string_view meshName, ::precice::span<const VertexID> vertices);
93-
void setMeshTriangles(precice::string_view meshName, ::precice::span<const VertexID> vertices);
94-
void setMeshQuads(precice::string_view meshName, ::precice::span<const VertexID> vertices);
95-
void setMeshTetrahedra(precice::string_view meshName, ::precice::span<const VertexID> vertices);
96-
```
99+
This is why preCICE steps in and handles this internally.
100+
In practise, you only need to define the connectivity your solver exposes.
97101

98102
## Putting it all together
99103

pages/docs/couple-your-code/couple-your-code-direct-access.md

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,41 +14,48 @@ These API functions are work in progress, experimental, and are not yet released
1414
This concept is required if you want to access received meshes directly. It might be relevant in case you don't want to use the mapping schemes in preCICE, but rather want to use your own solver for data mapping. As opposed to the usual preCICE mapping, only a single mesh (from the other participant) is now involved in this situation since an 'own' mesh defined by the participant itself is not required any more. In order to re-partition the received mesh, the participant needs to define the mesh region it wants read data from and write data to. The complete concept on the receiving participant looks as follows:
1515

1616
```cpp
17-
// Allocate a bounding-box vector containing lower and upper bounds per
18-
// space dimension
19-
std::vector<double> boundingBox(dim * 2);
20-
21-
// fill the allocated 'boundingBox' according to the interested region
22-
// with the desired bounds...
23-
// Get relevant IDs. Note that "ReceivedMeshname" is not a name of a
17+
// Note that "ReceivedMeshname" is not a name of a
2418
// provided mesh, but a mesh defined by another participant. Accessing
2519
// a received mesh directly is disabled in a usual preCICE configuration.
26-
const int otherMeshID = precice.getMeshID("ReceivedMeshName");
27-
const int writeDataID = precice.getDataID("WriteDataName", otherMeshID);
20+
const std::string otherMesh = "ReceivedMeshName";
21+
22+
// Get the spacial dimensionality of the mesh
23+
const int dims = precice.getMeshDimensions(otherMesh);
24+
25+
// Allocate and fill the 'boundingBox' according to the interested region
26+
// with the desired bounds, in our example we use the unit cube.
27+
// Assuming dim == 3, means that the bounding box has dim * 2 == 6 elements.
28+
std::vector<double> boundingBox {
29+
0, 0, 0, // minimum corner
30+
1, 1, 1, // maximum corner
31+
};
2832

2933
// Define region of interest, where we want to obtain the direct access.
3034
// See also the API documentation of this function for further notes.
31-
precice.setMeshAccessRegion(otherMeshID, boundingBox.data());
35+
precice.setMeshAccessRegion(otherMesh, boundingBox);
3236

3337
// initialize preCICE as usual
34-
double dt = precice.initialize();
38+
precice.initialize();
3539

3640
// Get the size of the received mesh partition, which lies within the
3741
// defined bounding (provided by the coupling participant)
38-
const int otherMeshSize = precice.getMeshVertexSize(otherMeshID);
42+
const int otherMeshSize = precice.getMeshVertexSize(otherMesh);
3943

40-
// Now finally get the data. First allocate memory for the IDs and the
41-
// vertices
42-
std::vector<double> otherSolverVertices(otherMeshSize * dim);
43-
std::vector<int> ids(otherMeshSize);
44+
// Now finally get information about the mesh vertices.
45+
// First allocate memory for the IDs and coordinates
46+
std::vector<double> otherCoordinates(otherMeshSize * dim);
47+
std::vector<VertexID> otherVertexIDs(otherMeshSize);
4448
// ... and afterwards ask preCICE to fill the vectors
45-
precice.getMeshVerticesAndIDs(otherMeshID,
46-
otherMeshSize,
47-
ids.data(),
48-
otherSolverVertices.data());
49+
precice.getMeshVertexIDsAndCoordinates(otherMesh,
50+
otherVertexIDs,
51+
otherCoordinates);
4952

50-
// continue with time loop and write data directly using writeDataID and
53+
// continue with time loop and write data directly to the mesh using
5154
// the received ids, which correspond to the vertices
55+
const int dataDims = precice.getDataDimensions(otherMesh, "OtherData");
56+
std::vector<double> data(dataDims * otherMeshSize);
57+
precice.writeData(otherMesh, "OtherData", otherVertexIDs, data);
58+
5259
```
5360
5461
## Concept and API

pages/docs/couple-your-code/couple-your-code-existing-mpi-environment.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ keywords: api, adapter, parallelization, mpi, initialization
55
summary: "preCICE uses MPI for communication between different participants (and also for communication between ranks of the same participant). So are there any problems if the solver that you intend to couple also already uses MPI (e.g. for parallelization)? Who should initialize MPI? Who should finalize MPI? This is what we discuss here."
66
---
77

8-
It is not complicated. There are just three rules that preCICE follows:
8+
It is not complicated. There are three rules that preCICE follows:
99

1010
* preCICE only initializes MPI if it is not yet initialized (by e.g. the solver you want to couple).
1111
* preCICE finalizes MPI if and only if it was also initialized by preCICE.
1212
* preCICE only initializes MPI if it needs MPI.
13+
* preCICE uses `MPI_COMM_WORLD` if no custom communicator is provided.
1314

1415
So what does this mean for your adapter code:
1516

@@ -26,8 +27,7 @@ MPI_Comm_size(MPI_COMM_WORLD, &world_size);
2627

2728
[...] // maybe more initialization
2829

29-
precice::Participant precice("SolverName", world_rank, world_size);
30-
precice.configure("precice-config.xml");
30+
precice::Participant precice("SolverName", "precice-config.xml", world_rank, world_size);
3131

3232
[...] // declare meshes vertices etc.
3333

@@ -42,3 +42,17 @@ precice.finalize();
4242
MPI_Finalize();
4343

4444
```
45+
46+
If you need to provide a custom communicator to preCICE, you can do so as follows:
47+
48+
```cpp
49+
50+
[...] // Initialize MPI and start your solver
51+
52+
MPI_Comm my_comm = /* ... */;
53+
54+
precice::Participant precice("SolverName", "precice-config.xml", world_rank, world_size, &my_comm);
55+
56+
[...]
57+
58+
```

pages/docs/couple-your-code/couple-your-code-gradient-data.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,14 @@ Let's add gradient data to our example code:
3636
precice::Participant precice("FluidSolver", "precice-config.xml", rank, size); // constructor
3737

3838
int dim = precice.getMeshDimensions("FluidMesh");
39-
[...]
39+
/* ... */
4040
precice.setMeshVertices("FluidMesh", vertexSize, coords, vertexIDs);
4141

4242
std::vector<double> stress(vertexSize * dim);
4343

4444
// create gradient data
4545
std::vector<double> stressGradient(vertexSize * dim * dim)
46-
[...]
46+
/* ... */
4747
precice.initialize();
4848

4949
while (not simulationDone()){ // time loop
@@ -58,14 +58,14 @@ while (not simulationDone()){ // time loop
5858
precice.writeData("FluidMesh", "Stress", vertexIDs, stress);
5959

6060
// write gradient data
61-
if (isGradientDataRequired(dataID)){
61+
if (requiresGradientDataFor("FluidMesh")){
6262
computeStressGradient(stressGradient)
6363
precice.writeGradientData("FluidMesh", "Stress", vertexIDs, stressGradient);
6464
}
6565

6666
precice.advance(dt);
6767
}
68-
[...]
68+
/* ... */
6969
```
7070
7171
{% experimental %}

pages/docs/couple-your-code/couple-your-code-implicit-coupling.md

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,17 @@ keywords: api, adapter, coupling schemes, checkpoint, fixed-point
55
summary: "In previous steps, we only considered explicit coupling. We now move onto implicit coupling, so sub-iterating each time step multiple times until a convergence threshold is reached. This stabilzes strongly-coupled problems."
66
---
77

8-
The main ingredient needed for implicit coupling is move backwards in time. For that, we need a [flux capacitor](https://www.youtube.com/watch?v=VcZe8_RZO8c). Just kidding :wink:. What we really need is that your solver can write and read iteration checkpoints. An iteration checkpoint should contain all the information necessary to reload a previous state of your solver. What exactly is needed depends solely on your solver. preCICE tells you when you need to write and read checkpoints. To this end, preCICE uses the following action interface:
8+
The main ingredient needed for implicit coupling is move backwards in time. For that, we need a [flux capacitor](https://www.youtube.com/watch?v=VcZe8_RZO8c). Just kidding :wink:. What we really need is that your solver can write and read iteration checkpoints. An iteration checkpoint should contain all the information necessary to reload a previous state of your solver. What exactly is needed depends solely on your solver. preCICE tells you when you need to write and read checkpoints. To this end, preCICE uses the following interface:
99

1010
```cpp
11-
bool isActionRequired(const std::string& action)
12-
void markActionFulfilled(const std::string& action)
13-
const std::string& constants::actionReadIterationCheckpoint()
14-
const std::string& constants::actionWriteIterationCheckpoint()
11+
bool requiresWritingCheckpoint()
12+
bool requiresReadingCheckpoint()
1513
```
1614

17-
* `isActionRequired` inquires the necessity of a certain action. It takes a string argument to reference the action.
18-
* `markActionFulfilled` tells preCICE that the action is fulfilled. This is a simple safeguard. If a certain action is required and you did not mark it as fulfilled preCICE will complain.
19-
* The Methods in the `precice::constants` namespace return strings to reference specific actions. For implicit coupling, we need `actionReadIterationCheckpoint` and `actionWriteIterationCheckpoint`.
15+
These functions perform double duty:
16+
17+
1. They inform the adapter that writing or reading a checkpoint is required by the solver.
18+
2. They let preCICE know that your adapter is capable of implicit coupling. preCICE will show an error if you configure implicit coupling without calling these functions.
2019

2120
Let's extend our example code to also handle implicit coupling.
2221

@@ -25,17 +24,13 @@ turnOnSolver(); //e.g. setup and partition mesh
2524

2625
precice::Participant precice("FluidSolver","precice-config.xml",rank,size); // constructor
2726

28-
const std::string& coric = precice::constants::actionReadIterationCheckpoint();
29-
const std::string& cowic = precice::constants::actionWriteIterationCheckpoint();
30-
3127
int dim = precice.getMeshDimensions("FluidMesh");
3228
int vertexSize; // number of vertices at wet surface
3329
// determine vertexSize
3430
std::vector<double> coords(vertexSize*dim); // coords of vertices at wet surface
3531
// determine coordinates
3632
std::vector<int> vertexIDs(vertexSize);
3733
precice.setMeshVertices("FluidMesh", coords, vertexIDs);
38-
delete[] coords;
3934

4035
std::vector<double> forces(vertexSize*dim);
4136
std::vector<double> displacements(vertexSize*dim);
@@ -48,9 +43,8 @@ double dt; // actual time step size
4843
```cpp
4944
precice.initialize();
5045
while (precice.isCouplingOngoing()){
51-
if(precice.isActionRequired(cowic)){
46+
if(precice.requiresWritingCheckpoint()){ // new time window
5247
saveOldState(); // save checkpoint
53-
precice.markActionFulfilled(cowic);
5448
}
5549
preciceDt = precice.getMaxTimeStepSize();
5650
solverDt = beginTimeStep(); // e.g. compute adaptive dt
@@ -61,16 +55,14 @@ while (precice.isCouplingOngoing()){
6155
computeForces(forces);
6256
precice.writeData("FluidMesh", "Forces", vertexIDs, forces);
6357
precice.advance(dt);
64-
if(precice.isActionRequired(coric)){ // time step not converged
58+
if(precice.requiresReadingCheckpoint()){ // iteration did not converge
6559
reloadOldState(); // set variables back to checkpoint
66-
precice.markActionFulfilled(coric);
6760
}
68-
else{ // time step converged
61+
else{ // iteration converged
6962
endTimeStep(); // e.g. update variables, increment time
7063
}
7164
}
7265
precice.finalize(); // frees data structures and closes communication channels
73-
delete[] vertexIDs, forces, displacements;
7466
turnOffSolver();
7567
```
7668

pages/docs/couple-your-code/couple-your-code-mesh-and-data-access.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ std::vector<double> coords(vertexSize*dim); // coords of vertices at wet surface
5050
// determine coordinates
5151
std::vector<int> vertexIDs(vertexSize);
5252
precice.setMeshVertices("FluidMesh", coords, vertexIDs);
53-
delete[] coords;
5453

5554
std::vector<double> forces(vertexSize*dim);
5655
std::vector<double> displacements(vertexSize*dim);
@@ -73,7 +72,6 @@ while (not simulationDone()){ // time loop
7372
endTimeStep(); // e.g. update variables, increment time
7473
}
7574
precice.finalize(); // frees data structures and closes communication channels
76-
delete[] vertexIDs, forces, displacements;
7775
turnOffSolver();
7876
```
7977

0 commit comments

Comments
 (0)