Skip to content
Merged
4 changes: 3 additions & 1 deletion pages/docs/configuration/configuration-basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ You need to define which data values the coupled solvers want to exchange, e.g.

Once you have defined these fields, you can use the preCICE API to access them:

<!-- TODO: needs update -->
```c++
int temperatureID = precice.getDataID("Temperature", meshID);
```
Expand All @@ -63,6 +64,7 @@ Next, you can define the interface coupling meshes.

With the preCICE API, you get an ID for each mesh:

<!-- TODO: needs update -->
```c++
int meshID = precice.getMeshID("MyMesh1");
```
Expand All @@ -85,7 +87,7 @@ Each solver that participates in the coupled simulation needs a participant defi
The name of the participant has to coincide with the name you give when creating the preCICE interface object in the adapter:

```c++
precice::SolverInterface precice("MySolver1",rank,size);
precice::Participant precice("MySolver1",rank,size);
```

The participant `provides` the mesh. This means that you have to define the coordinates:
Expand Down
2 changes: 1 addition & 1 deletion pages/docs/configuration/configuration-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ For a complete overview of all basis function, refer to [this paper](https://www
The interpolation problem might not be well-defined if you map along an axis-symmetric surface. This means, preCICE tries to compute, for example, a 3D interpolant out of 2D information. If so, preCICE throws an error `RBF linear system has not converged` or `Interpolation matrix C is not invertible`. In this case, you can restrict the interpolation problem by ignoring certain coordinates, e.g. `x-dead="true"` to ignore the x coordinate.

{% note %}
All data mappings are executed during `advance` and not in `readBlockVectorData` etc., cf. the section on [how to couple your own code](couple-your-code-overview.html).
All data mappings are executed during `advance` and not in `readData`, cf. the section on [how to couple your own code](couple-your-code-overview.html).
{% endnote %}

## Restrictions for parallel participants
Expand Down
6 changes: 3 additions & 3 deletions pages/docs/couple-your-code/couple-your-code-api.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
---
title: Application programming interface
permalink: couple-your-code-api.html
keywords: api, adapter, library, bindings, SolverInterface
keywords: api, adapter, library, bindings, Participant
summary: "This page gives an overview on available preCICE APIs and minimal reference implementations."
---

preCICE is written in C++. Thus, the native API language of preCICE is C++ as well. If you are new to the preCICE API, we recommended that you first follow the [step-by-step guide](couple-your-code-preparing-your-solver.html).

## Native API

The definite documentation of the C++ API is available on [the preCICE doxygen pages](https://precice.org/doxygen/main/classprecice_1_1SolverInterface.html).
The definite documentation of the C++ API is available on [the preCICE doxygen pages](https://precice.org/doxygen/main/classprecice_1_1Participant.html).

| Language | Location | Installation |
|----------------|---------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------|
| C++ | [`precice/precice/tree/main/src/precice/SolverInterface.hpp`](https://github.com/precice/precice/tree/main/src/precice/SolverInterface.hpp) | Automatically included |
| C++ | [`precice/precice/tree/main/src/precice/Participant.hpp`](https://github.com/precice/precice/tree/main/src/precice/Participant.hpp) | Automatically included |

## Bindings

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,22 @@ For volume coupling in 2D, mesh connectivity boils down to defining triangles an

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

<!-- TODO: What about setMeshEdges, setMeshTriangle, setMeshQuad, setMeshTetrahedron? -->
```cpp
int setMeshEdge (int meshID, int firstVertexID, int secondVertexID);
void setMeshTriangle (int meshID, int firstEdgeID, int secondEdgeID, int thirdEdgeID);
void setMeshTriangleWithEdges (int meshID, int firstVertexID, int secondVertexID, int thirdVertexID);
void setMeshQuad(int meshID, int firstEdgeID, int secondEdgeID, int thirdEdgeID, int fourthEdgeID);
void setMeshQuadWithEdges(int meshID, int firstVertexID, int secondVertexID, int thirdVertexID, int fourthVertexID);
void setMeshTetrahredron(int meshID, int firstVertexID, int secondVertexID, int thirdVertexID, int fourthVertexID);
int setMeshEdge (precice::string_view meshName, int firstVertexID, int secondVertexID);
void setMeshTriangle (precice::string_view meshName, int firstVertexID, int secondVertexID, int thirdVertexID);
void setMeshQuad(precice::string_view meshName, int firstVertexID, int secondVertexID, int thirdVertexID, int fourthVertexID);
void setMeshTetrahedron(precice::string_view meshName, int firstVertexID, int secondVertexID, int thirdVertexID, int fourthVertexID);
```

* `setMeshEdge` defines a mesh edge between two vertices and returns an edge ID.
* `setMeshTriangle` defines a mesh triangle by three edges.
* `setMeshTriangleWithEdges` defines a mesh triangle by three vertices and also creates the edges in preCICE on the fly. Of course, preCICE takes care that no edge is defined twice. Please note that this function is computationally more expensive than `setMeshTriangle`.
* `setMeshQuad` defines a mesh quad by four edges.
* `setMeshQuadWithEdges` defines a mesh quad by four vertices and also creates the edges in preCICE on the fly. Again, preCICE takes care that no edge is defined twice. This function is computationally more expensive than `setMeshQuad`.
* `setMeshTetrahredron` defines a mesh tetrahedron by four vertices.

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:

<!-- TODO: needs update -->
```cpp
bool isMeshConnectivityRequired(int meshID);
```
Expand All @@ -53,22 +51,22 @@ The following code shows how mesh connectivity can be defined in our example. Fo

[...]

int* vertexIDs = new int[vertexSize];
precice.setMeshVertices(meshID, vertexSize, coords, vertexIDs);
delete[] coords;
std::vector<int> vertexIDs(vertexSize);
precice.setMeshVertices(meshName, coords, vertexIDs);

int edgeIDs[3];
edgeIDs[0] = precice.setMeshEdge(meshID, vertexIDs[0], vertexIDs[1]);
edgeIDs[1] = precice.setMeshEdge(meshID, vertexIDs[1], vertexIDs[2]);
edgeIDs[2] = precice.setMeshEdge(meshID, vertexIDs[2], vertexIDs[0]);
edgeIDs[0] = precice.setMeshEdge(meshName, vertexIDs[0], vertexIDs[1]);
edgeIDs[1] = precice.setMeshEdge(meshName, vertexIDs[1], vertexIDs[2]);
edgeIDs[2] = precice.setMeshEdge(meshName, vertexIDs[2], vertexIDs[0]);

if(dim==3)
precice.setMeshTriangle(meshID, edgeIDs[0], edgeIDs[1], edgeIDs[2]);
precice.setMeshTriangle(meshName, edgeIDs[0], edgeIDs[1], edgeIDs[2]);

[...]

```

<!-- TODO: Section below should be the default on branch precice-v3 -->
## Changes in v3

Version 3 overhauls the definition of meshes.
Expand All @@ -82,19 +80,19 @@ The order of vertices also does not matter. Triangles BAC and CAB are considered
The API for defining individual connectivity elements looks as follows:

```cpp
void setMeshEdge(int meshID, int firstVertexID, int secondVertexID);
void setMeshTriangle(int meshID, int firstVertexID, int secondVertexID, int thirdVertexID);
void setMeshQuad(int meshID, int firstVertexID, int secondVertexID, int thirdVertexID, int fourthVertexID);
void setMeshTetrahredron(int meshID, int firstVertexID, int secondVertexID, int thirdVertexID, int fourthVertexID);
void setMeshEdge(precice::string_view meshName, int firstVertexID, int secondVertexID);
void setMeshTriangle(precice::string_view meshName, int firstVertexID, int secondVertexID, int thirdVertexID);
void setMeshQuad(precice::string_view meshName, int firstVertexID, int secondVertexID, int thirdVertexID, int fourthVertexID);
void setMeshTetrahredron(precice::string_view meshName, int firstVertexID, int secondVertexID, int thirdVertexID, int fourthVertexID);
```

Each of the above functions is accompanied by a bulk version, which allows to set multiple elements in a single call.

```cpp
void setMeshEdges(int meshID, int size, int* vertices);
void setMeshTriangles(int meshID, int size, int* vertices);
void setMeshQuads(int meshID, int size, int* vertices);
void setMeshTetrahedra(int meshID, int size, int* vertices);
void setMeshEdges(precice::string_view meshName, ::precice::span<const VertexID> vertices);
void setMeshTriangles(precice::string_view meshName, ::precice::span<const VertexID> vertices);
void setMeshQuads(precice::string_view meshName, ::precice::span<const VertexID> vertices);
void setMeshTetrahedra(precice::string_view meshName, ::precice::span<const VertexID> vertices);
```

## Putting it all together
Expand All @@ -114,13 +112,12 @@ For triangular faces, these would be the 3 corner points.
Then map these Solver IDs to preCICE IDs, and use those to define your connectivity.

```cpp
SolverInterface participant(...);
auto meshID = participant.getMesh(...);
Participant participant(...);

// Define the map from the solver to the preCICE vertex ID
std::map<Solver::VertexID, precice::VertexID> idMap;
for (auto& vertex: solver.vertices) {
auto vertexID = participant.setMeshVertex(meshID, vertex.coords);
auto vertexID = participant.setMeshVertex("MyMesh", vertex.coords);
// set the vertexID as label
idMap.emplace(vertex.id, vertexID);
}
Expand All @@ -131,7 +128,7 @@ for (auto& tri: solver.triangularFaces) {
auto b = idMap.at(tri.b.id);
auto c = idMap.at(tri.c.id);
// Then define the connectivity
participant.setMeshTriangle(meshID, a, b, c);
participant.setMeshTriangle("MyMesh", a, b, c);
}
```

Expand All @@ -151,11 +148,10 @@ Define the vertices using the preCICE API, then iterate over them and apply the
When iterating over faces, get the preCICE vertex IDs from the point labels, and use those to define your connectivity.

```cpp
SolverInterface participant(...);
auto meshID = participant.getMesh(...);
Participant participant(...);

for (auto& vertex: solver.vertices) {
auto vertexID = participant.setMeshVertex(meshID, vertex.coords);
auto vertexID = participant.setMeshVertex("MyMesh", vertex.coords);
vertex.label = vertexID; // set the vertexID as label
}

Expand All @@ -165,7 +161,7 @@ for (auto& tri: solver.triangularFaces) {
auto b = tri.b.label;
auto c = tri.c.label;
// Then define the connectivity
participant.setMeshTriangle(meshID, a, b, c);
participant.setMeshTriangle("MyMesh", a, b, c);
}
```

Expand All @@ -180,20 +176,19 @@ Hence, a C++ `std::map` without custom comparator, or python `dict` may not be s
An alternative would be to use a spatial index as a data structure to store this information.

```cpp
SolverInterface participant(...);
auto meshID = participant.getMesh(...);
Participant participant(...);

IDLookup lookup;
for (auto& vertex: solver.vertices) {
auto vid = participant.setMeshVertex(meshID, vertex.coords);
auto vid = participant.setMeshVertex("MyMesh", vertex.coords);
lookup.insert(vertex.coords, vid);
}

for (auto& tri: solver.triangularFaces) {
auto a = lookup.lookup(tri.a.coords);
auto b = lookup.lookup(tri.b.coords);
auto c = lookup.lookup(tri.c.coords);
participant.setMeshTriangle(meshID, a, b, c);
participant.setMeshTriangle("MyMesh", a, b, c);
}
```

Expand Down Expand Up @@ -235,17 +230,16 @@ In python, you could use the rtree package:
```py
import rtree

participant = precice.Interface(...)
meshID = participant.get_mesh(...)
participant = precice.Participant(...)

index = rtree.index.Index()
for vertex in solver.vertices:
vid = participant.set_mesh_vertex(meshID, vertex.coords)
vid = participant.set_mesh_vertex("MyMesh", vertex.coords)
index.insert(vid, vertex.coords)

for tri in solver.triangularFaces:
a = index.nearest(tri.a.coords)
b = index.nearest(tri.b.coords)
c = index.nearest(tri.c.coords)
participant.set_mesh_triangle(a, b, c)
participant.set_mesh_triangle("MyMesh", a, b, c)
```
2 changes: 2 additions & 0 deletions pages/docs/couple-your-code/couple-your-code-direct-access.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ keywords: api, adapter, mapping, meshes
summary: "You can access received meshes and their data directly by using specific optional API functions."
---

<!-- TODO: needs update -->

{% warning %}
These API functions are work in progress, experimental, and are not yet released. The API might change during the ongoing development process. Use with care.
{% endwarning %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ MPI_Comm_size(MPI_COMM_WORLD, &world_size);

[...] // maybe more initialization

precice::SolverInterface precice("SolverName", world_rank, world_size);
precice::Participant precice("SolverName", world_rank, world_size);
precice.configure("precice-config.xml");

[...] // declare meshes vertices etc.
Expand Down
60 changes: 17 additions & 43 deletions pages/docs/couple-your-code/couple-your-code-gradient-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,16 @@ This feature is available since version 2.4.0.
{% endversion %}

When using `nearest-neighbor-gradient` mapping, we require coupling data and additional gradient data. We have seen in [Step 3](couple-your-code-mesh-and-data-access.html) how to write data to the mesh.
Now, we will learn how to write gradient data to the mesh.

For this purpose, we use the following API methods:
Now, we will learn how to write gradient data to the mesh. For this purpose, we use the following API method:

```cpp
bool isGradientDataRequired(int dataID);

void writeScalarGradientData(
int dataID,
int valueIndex,
const double* gradientValues);

void writeBlockScalarGradientData(
int dataID,
int size,
const int* valueIndices,
const double* gradientValues);

void writeVectorGradientData(
int dataID,
int valueIndex,
const double* gradientValues);

void writeBlockVectorGradientData(
int dataID,
int size,
const int* valueIndices,
const double* gradientValues);
void writeGradientData(
precice::string_view meshName,
precice::string_view dataName,
precice::span<const VertexID> vertices,
precice::span<const double> gradients);
```

* `isGradientDataRequired` returns a boolean, indicates if the data corresponding to the ID `dataID` requires additional gradient data.
* `writeScalarGradientData` writes gradient data corresponding to scalar data values, i.e., a vector containing the spatial derivative of a scalar quantity in each space dimension.
* `ẁriteBlockScalarGradintData` writes multiple scalar gradient data at once (analogue to `writeBlockScalarData`).
* `writeVectorGradientData` writes gradient data corresponding to vector-valued data, i.e, a Gradient matrix. The matrix is passed as a 1D-array.
* `writeBlockVectorGradintData` writes multiple vector gradient data at once (analogue to `writeBlockVectorData`)

Let's consider an example for writing block vector gradient data corresponding to the vector data `v0 = (v0x, v0y) , v1 = (v1x, v1y), ... , vn = (vnx, vny)` differentiated in spatial directions x and y.
The values are passed as following:

Expand All @@ -59,34 +33,34 @@ The values are passed as following:
Let's add gradient data to our example code:

```cpp
precice::SolverInterface precice("FluidSolver", "precice-config.xml", rank, size); // constructor
precice::Participant precice("FluidSolver", "precice-config.xml", rank, size); // constructor

int dim = precice.getDimensions();
int meshID = precice.getMeshID("FluidMesh");
int dim = precice.getMeshDimensions("FluidMesh");
[...]
precice.setMeshVertices(meshID, vertexSize, coords, vertexIDs);
precice.setMeshVertices("FluidMesh", vertexSize, coords, vertexIDs);

int stressID = precice.getDataID("Stress", meshID);
double* stress = new double[vertexSize * dim];
std::vector<double> stress(vertexSize * dim);

// create gradient data
double* stressGradient = new double[vertexSize * dim * dim]
std::vector<double> stressGradient(vertexSize * dim * dim)
[...]
precice.initialize();

while (not simulationDone()){ // time loop
precice.readBlockVectorData(displID, vertexSize, vertexIDs, displacements);
preciceDt = precice.getMaxTimeStepSize();
solverDt = beginTimeStep(); // e.g. compute adaptive dt
dt = min(preciceDt, solverDt);
precice.readData("FluidMesh", "Displacements", vertexIDs, dt, displacements);
setDisplacements(displacements);
[...]
solveTimeStep(dt);
computeStress(stress);

precice.writeBlockVectorData(stressID, vertexSize, vertexIDs, stress);
precice.writeData("FluidMesh", "Stress", vertexIDs, stress);

// write gradient data
if (isGradientDataRequired(dataID)){
computeStressGradient(stressGradient)
precice.writeBlockVectorGradientData(stressID, vertexSize, vertexIDs, stressGradient);
precice.writeGradientData("FluidMesh", "Stress", vertexIDs, stressGradient);
}

precice.advance(dt);
Expand Down
21 changes: 9 additions & 12 deletions pages/docs/couple-your-code/couple-your-code-implicit-coupling.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,22 @@ Let's extend our example code to also handle implicit coupling.
```cpp
turnOnSolver(); //e.g. setup and partition mesh

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

const std::string& coric = precice::constants::actionReadIterationCheckpoint();
const std::string& cowic = precice::constants::actionWriteIterationCheckpoint();

int dim = precice.getDimension();
int meshID = precice.getMeshID("FluidMesh");
int dim = precice.getMeshDimensions("FluidMesh");
int vertexSize; // number of vertices at wet surface
// determine vertexSize
double* coords = new double[vertexSize*dim]; // coords of vertices at wet surface
std::vector<double> coords(vertexSize*dim); // coords of vertices at wet surface
// determine coordinates
int* vertexIDs = new int[vertexSize];
precice.setMeshVertices(meshID, vertexSize, coords, vertexIDs);
std::vector<int> vertexIDs(vertexSize);
precice.setMeshVertices("FluidMesh", coords, vertexIDs);
delete[] coords;

int displID = precice.getDataID("Displacements", meshID);
int forceID = precice.getDataID("Forces", meshID);
double* forces = new double[vertexSize*dim];
double* displacements = new double[vertexSize*dim];
std::vector<double> forces(vertexSize*dim);
std::vector<double> displacements(vertexSize*dim);

double solverDt; // solver time step size
double preciceDt; // maximum precice time step size
Expand All @@ -58,11 +55,11 @@ while (precice.isCouplingOngoing()){
preciceDt = precice.getMaxTimeStepSize();
solverDt = beginTimeStep(); // e.g. compute adaptive dt
dt = min(preciceDt, solverDt);
precice.readBlockVectorData(displID, vertexSize, vertexIDs, displacements);
precice.readData("FluidMesh", "Displacements", vertexIDs, dt, displacements);
setDisplacements(displacements);
solveTimeStep(dt);
computeForces(forces);
precice.writeBlockVectorData(forceID, vertexSize, vertexIDs, forces);
precice.writeData("FluidMesh", "Forces", vertexIDs, forces);
precice.advance(dt);
if(precice.isActionRequired(coric)){ // time step not converged
reloadOldState(); // set variables back to checkpoint
Expand Down
Loading