From f71bbf85844cc6c1680052f195c09f52e49b75ce Mon Sep 17 00:00:00 2001 From: Benjamin Rodenberg Date: Wed, 15 Nov 2023 09:44:09 +0100 Subject: [PATCH] Update breaking API changes (#299) --------- Co-authored-by: Gerasimos Chourdakis --- .../configuration/configuration-basics.md | 4 +- .../configuration/configuration-mapping.md | 2 +- .../couple-your-code/couple-your-code-api.md | 6 +- ...le-your-code-defining-mesh-connectivity.md | 72 +++++++++---------- .../couple-your-code-direct-access.md | 2 + ...uple-your-code-existing-mpi-environment.md | 2 +- .../couple-your-code-gradient-data.md | 60 +++++----------- .../couple-your-code-implicit-coupling.md | 21 +++--- ...le-your-code-initializing-coupling-data.md | 2 +- .../couple-your-code-mesh-and-data-access.md | 53 +++++++------- .../couple-your-code-preparing-your-solver.md | 2 +- .../couple-your-code-steering-methods.md | 10 +-- .../couple-your-code-time-step-sizes.md | 28 -------- .../couple-your-code-waveform.md | 30 ++++---- pages/docs/dev-docs/dev-sourcedocs.md | 2 +- .../docs/fundamentals/fundamentals-roadmap.md | 2 +- .../installation-bindings-python.md | 6 +- .../installation-bindings-rust.md | 4 +- .../docs/installation/installation-linking.md | 4 +- 19 files changed, 123 insertions(+), 189 deletions(-) diff --git a/pages/docs/configuration/configuration-basics.md b/pages/docs/configuration/configuration-basics.md index 044bfce8ee..62ac84fc8a 100644 --- a/pages/docs/configuration/configuration-basics.md +++ b/pages/docs/configuration/configuration-basics.md @@ -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: + ```c++ int temperatureID = precice.getDataID("Temperature", meshID); ``` @@ -63,6 +64,7 @@ Next, you can define the interface coupling meshes. With the preCICE API, you get an ID for each mesh: + ```c++ int meshID = precice.getMeshID("MyMesh1"); ``` @@ -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: diff --git a/pages/docs/configuration/configuration-mapping.md b/pages/docs/configuration/configuration-mapping.md index 9bea562c16..2822504da4 100644 --- a/pages/docs/configuration/configuration-mapping.md +++ b/pages/docs/configuration/configuration-mapping.md @@ -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 diff --git a/pages/docs/couple-your-code/couple-your-code-api.md b/pages/docs/couple-your-code/couple-your-code-api.md index 7e74abbe27..91c96e752c 100644 --- a/pages/docs/couple-your-code/couple-your-code-api.md +++ b/pages/docs/couple-your-code/couple-your-code-api.md @@ -1,7 +1,7 @@ --- 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." --- @@ -9,11 +9,11 @@ preCICE is written in C++. Thus, the native API language of preCICE is C++ as we ## 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 diff --git a/pages/docs/couple-your-code/couple-your-code-defining-mesh-connectivity.md b/pages/docs/couple-your-code/couple-your-code-defining-mesh-connectivity.md index c992c85180..465255ee42 100644 --- a/pages/docs/couple-your-code/couple-your-code-defining-mesh-connectivity.md +++ b/pages/docs/couple-your-code/couple-your-code-defining-mesh-connectivity.md @@ -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. + ```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: + ```cpp bool isMeshConnectivityRequired(int meshID); ``` @@ -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 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]); [...] ``` + ## Changes in v3 Version 3 overhauls the definition of meshes. @@ -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 vertices); +void setMeshTriangles(precice::string_view meshName, ::precice::span vertices); +void setMeshQuads(precice::string_view meshName, ::precice::span vertices); +void setMeshTetrahedra(precice::string_view meshName, ::precice::span vertices); ``` ## Putting it all together @@ -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 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); } @@ -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); } ``` @@ -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 } @@ -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); } ``` @@ -180,12 +176,11 @@ 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); } @@ -193,7 +188,7 @@ 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); } ``` @@ -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) ``` diff --git a/pages/docs/couple-your-code/couple-your-code-direct-access.md b/pages/docs/couple-your-code/couple-your-code-direct-access.md index 4180b7d164..dd7ffd9c44 100644 --- a/pages/docs/couple-your-code/couple-your-code-direct-access.md +++ b/pages/docs/couple-your-code/couple-your-code-direct-access.md @@ -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." --- + + {% 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 %} diff --git a/pages/docs/couple-your-code/couple-your-code-existing-mpi-environment.md b/pages/docs/couple-your-code/couple-your-code-existing-mpi-environment.md index dcfcf41e73..5a2589a3ba 100644 --- a/pages/docs/couple-your-code/couple-your-code-existing-mpi-environment.md +++ b/pages/docs/couple-your-code/couple-your-code-existing-mpi-environment.md @@ -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. diff --git a/pages/docs/couple-your-code/couple-your-code-gradient-data.md b/pages/docs/couple-your-code/couple-your-code-gradient-data.md index e24e35f364..568a0ceba1 100644 --- a/pages/docs/couple-your-code/couple-your-code-gradient-data.md +++ b/pages/docs/couple-your-code/couple-your-code-gradient-data.md @@ -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 vertices, + precice::span 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: @@ -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 stress(vertexSize * dim); // create gradient data -double* stressGradient = new double[vertexSize * dim * dim] +std::vector 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); diff --git a/pages/docs/couple-your-code/couple-your-code-implicit-coupling.md b/pages/docs/couple-your-code/couple-your-code-implicit-coupling.md index a4a34537bd..dfb41c477a 100644 --- a/pages/docs/couple-your-code/couple-your-code-implicit-coupling.md +++ b/pages/docs/couple-your-code/couple-your-code-implicit-coupling.md @@ -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 coords(vertexSize*dim); // coords of vertices at wet surface // determine coordinates -int* vertexIDs = new int[vertexSize]; -precice.setMeshVertices(meshID, vertexSize, coords, vertexIDs); +std::vector 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 forces(vertexSize*dim); +std::vector displacements(vertexSize*dim); double solverDt; // solver time step size double preciceDt; // maximum precice time step size @@ -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 diff --git a/pages/docs/couple-your-code/couple-your-code-initializing-coupling-data.md b/pages/docs/couple-your-code/couple-your-code-initializing-coupling-data.md index 1a4fd8ffc7..5f211e26c7 100644 --- a/pages/docs/couple-your-code/couple-your-code-initializing-coupling-data.md +++ b/pages/docs/couple-your-code/couple-your-code-initializing-coupling-data.md @@ -18,7 +18,7 @@ To support data initialization, we extend our example as follows: [...] if(precice.requiresInitialData()){ - precice.writeBlockVectorData("FluidMesh", "Forces", numberOfVertices, vertexIDs, forces); + precice.writeData("FluidMesh", "Forces", vertexIDs, forces); } precice.initialize(); diff --git a/pages/docs/couple-your-code/couple-your-code-mesh-and-data-access.md b/pages/docs/couple-your-code/couple-your-code-mesh-and-data-access.md index 8e26cb5ab3..7a4c0a65a7 100644 --- a/pages/docs/couple-your-code/couple-your-code-mesh-and-data-access.md +++ b/pages/docs/couple-your-code/couple-your-code-mesh-and-data-access.md @@ -10,55 +10,50 @@ For coupling, we need coupling meshes. Let's see how we can tell preCICE about o Coupling meshes and associated data fields are defined in the preCICE configuration file, which you probably already know from the tutorials. The concrete values, however, you can access with the API: ```cpp -int getMeshID (const std::string& meshName); -int setMeshVertex (int meshID, const double* position); -void setMeshVertices (int meshID, int size, double* positions, int* ids); +int setMeshVertex( + ::precice::string_view meshName, + ::precice::span position); + +void setMeshVertices( + precice::string_view meshName, + precice::span positions, + precice::span ids); ``` -* `getMeshID` returns the ID of the coupling mesh. You need the ID of the mesh whenever you want to something with the mesh. * `setMeshVertex` defines the coordinates of a single mesh vertex and returns a vertex ID, which you can use to refer to this vertex. * `setMeshVertices` defines multiple vertices at once. So, you can use this function instead of calling `setMeshVertex` multiple times. This is also good practice for performance reasons. -To access coupling data, the following API functions are needed: +To write data to the coupling data structure the following API function is needed: ```cpp -int getDataID (const std::string& dataName, int meshID); -void writeVectorData (int dataID, int vertexID, const double* value); -void writeBlockVectorData (int dataID, int size, int* vertexIDs, double* values); +void Participant::writeData( + precice::string_view meshName, + precice::string_view dataName, + precice::span vertices, + precice::span values) ``` -* `getDataID` returns the data ID for a coupling data field (e.g. "Displacements", "Forces", etc). -* `writeVectorData` writes vector-valued data to the coupling data structure. -* `writeBlockVectorData` writes multiple vector data at once, again for performance reasons. - -Similarly, there are methods for reading coupling data: `readVectorData` and `readBlockVectorData`. Furthermore, -preCICE distinguishes between scalar-valued and vector-valued data. For scalar data, similar methods exist, for example `writeScalarData`. - -{% note %} -The IDs that preCICE uses (for data fields, meshes, or vertices) have arbitrary integer values. Actually, you should never need to look at the values. The only purpose of the IDs is to talk to preCICE. You also do not look at the value of a C pointer, it is just a non-readable address. In particular, you should not assume that vertex IDs are ordered in any certain way (say from 0 to N-1) or, for example, that 'Forces' always have the same ID '2' on all meshes. -{% endnote %} + +Similarly, there is a `readData` API function for reading coupling data. Let's define coupling meshes and access coupling data in our example code: ```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 int dim = precice.getDimensions(); -int meshID = precice.getMeshID("FluidMesh"); int vertexSize; // number of vertices at wet surface // determine vertexSize -double* coords = new double[vertexSize*dim]; // coords of coupling vertices +std::vector coords(vertexSize*dim); // coords of vertices at wet surface // determine coordinates -int* vertexIDs = new int[vertexSize]; -precice.setMeshVertices(meshID, vertexSize, coords, vertexIDs); +std::vector 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 forces(vertexSize*dim); +std::vector displacements(vertexSize*dim); double solverDt; // solver time step size double preciceDt; // maximum precice time step size @@ -69,11 +64,11 @@ while (not simulationDone()){ // time loop 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); endTimeStep(); // e.g. update variables, increment time } diff --git a/pages/docs/couple-your-code/couple-your-code-preparing-your-solver.md b/pages/docs/couple-your-code/couple-your-code-preparing-your-solver.md index cee55b97c8..6cf540f938 100644 --- a/pages/docs/couple-your-code/couple-your-code-preparing-your-solver.md +++ b/pages/docs/couple-your-code/couple-your-code-preparing-your-solver.md @@ -25,7 +25,7 @@ Probably most solvers have such a structures: something in the beginning (readin In the following steps, we will slowly add more and more calls to the preCICE API in this code snippet. Some part of the preCICE API is briefly described in each step. More precisely (no pun intended :grin:), we use the native `C++` API of preCICE. The API is, however, also available in other scientific programming languages: plain `C`, `Fortran`, `Python`, `Matlab`, and `Julia` (see [Application Programming Interface](couple-your-code-prerequisites#application-programming-interface)). {% tip %} -Also have a look at the [definite C++ API documentation](https://precice.org/doxygen/main/classprecice_1_1SolverInterface.html). +Also have a look at the [definite C++ API documentation](https://precice.org/doxygen/main/classprecice_1_1Participant.html). {% endtip %} {% note %} diff --git a/pages/docs/couple-your-code/couple-your-code-steering-methods.md b/pages/docs/couple-your-code/couple-your-code-steering-methods.md index 76a1669744..542ee9368e 100644 --- a/pages/docs/couple-your-code/couple-your-code-steering-methods.md +++ b/pages/docs/couple-your-code/couple-your-code-steering-methods.md @@ -6,11 +6,11 @@ summary: "In this step, you get to know the most important API functions of preC --- -As a first preparation step, you need to include the preCICE library headers. In C++, you need to include the file [SolverInterface.hpp](https://github.com/precice/precice/blob/develop/src/precice/SolverInterface.hpp). -The handle to the preCICE API is the class `precice::SolverInterface`. Its constructor requires the participant's name, the preCICE configuration file's name and the `rank` and `size` of the current thread. Once the basic preCICE interface is set up, we can _steer_ the behaviour of preCICE. For that we need the following functions: +As a first preparation step, you need to include the preCICE library headers. In C++, you need to include the file [Participant.hpp](https://github.com/precice/precice/blob/develop/src/precice/Participant.hpp). +The handle to the preCICE API is the class `precice::Participant`. Its constructor requires the participant's name, the preCICE configuration file's name and the `rank` and `size` of the current thread. Once the basic preCICE interface is set up, we can _steer_ the behaviour of preCICE. For that we need the following functions: ```cpp -SolverInterface( String participantName, String configurationFileName, int rank, int size ); +Participant( String participantName, String configurationFileName, int rank, int size ); void initialize(); void advance ( double computedTimeStepSize ); void finalize(); @@ -31,11 +31,11 @@ double getMaxTimeStepSize(); But let's ignore the details of time step sizes for the moment. This will be the topic of [Step 5](couple-your-code-time-step-sizes.html). We can now extend the code of our fluid solver: ```cpp -#include "precice/SolverInterface.hpp" +#include "precice/Participant.hpp" 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 double solverDt; // solver time step size double preciceDt; // maximum precice time step size diff --git a/pages/docs/couple-your-code/couple-your-code-time-step-sizes.md b/pages/docs/couple-your-code/couple-your-code-time-step-sizes.md index f6ecbb77e4..6b3b8e3022 100644 --- a/pages/docs/couple-your-code/couple-your-code-time-step-sizes.md +++ b/pages/docs/couple-your-code/couple-your-code-time-step-sizes.md @@ -75,34 +75,6 @@ This procedure is independent of whether a serial or a parallel coupling scheme For parallel coupling, both solvers run together and everything happens simultaneously in both participants, while for serial coupling, the first participant needs reach the end of the window before the second one can start. {% endnote %} -If a participant subcycles it is actually not necessary to write data to or read data from preCICE. To avoid unnecessary calls, preCICE offers two optional helper functions: - -```c++ -bool isReadDataAvailable () const; -bool isWriteDataRequired (double computedTimeStepLength) const; -``` - -You can use them as follows: - -```c++ -while (not simulationDone()){ // time loop - preciceDt = precice.getMaxTimeStepSize(); - solverDt = beginTimeStep(); // e.g. compute adaptive dt - dt = min(preciceDt, solverDt); - if (precice.isReadDataAvailable()){ - precice.readBlockVectorData(displID, vertexSize, vertexIDs, displacements); - setDisplacements(displacements); - } - solveTimeStep(dt); - if (precice.isWriteDataRequired(dt)){ - computeForces(forces); - precice.writeBlockVectorData(forceID, vertexSize, vertexIDs, forces); - } - precice.advance(dt); - endTimeStep(); // e.g. update variables, increment time -} -``` - ## First participant prescribes time step size The `first` participant sets the time step size. This requires that the `second` participant runs after the `first` one. Thus, as stated above, this option is only applicable for serial coupling. diff --git a/pages/docs/couple-your-code/couple-your-code-waveform.md b/pages/docs/couple-your-code/couple-your-code-waveform.md index ca54821499..794170af38 100644 --- a/pages/docs/couple-your-code/couple-your-code-waveform.md +++ b/pages/docs/couple-your-code/couple-your-code-waveform.md @@ -33,7 +33,7 @@ Linear interpolation between coupling boundary conditions of the previous and th ![Coupling data exchange with linear interpolation](images/docs/couple-your-code/couple-your-code-waveform/WaveformLinear.png) -If the Dirichlet participant $$\mathcal{D}$$ calls `readBlockVectorData`, it samples the data from a time-dependent function $$c_\mathcal{D}(t)$$. This function is created from linear interpolation of the first and the last sample $$c_{\mathcal{D}0}$$ and $$c_{\mathcal{D}5}$$ created by the Neumann participant $$\mathcal{N}$$ in the current time window. This allows $$\mathcal{D}$$ to sample the coupling condition at arbitrary times $$t$$ inside the current time window. +If the Dirichlet participant $$\mathcal{D}$$ calls `readData`, it samples the data from a time-dependent function $$c_\mathcal{D}(t)$$. This function is created from linear interpolation of the first and the last sample $$c_{\mathcal{D}0}$$ and $$c_{\mathcal{D}5}$$ created by the Neumann participant $$\mathcal{N}$$ in the current time window. This allows $$\mathcal{D}$$ to sample the coupling condition at arbitrary times $$t$$ inside the current time window. {% note %} As soon as time interpolation is used to obtain data for a `relativeReadTime` that does not refer to the end of the current window, the data from the end of the previous window might be used to compute the interpolated value. For the first time window, this means one should provide appropriate initial data, since otherwise preCICE will use zero-valued initial data. There are, however, situations where initial data is not necessarily needed. For example, if one uses zeroth order time interpolation without accessing the data value at the beginning of the time window. It is still recommended to always provide initial data, if available. See ["Step 7 - Data initialization"](couple-your-code-initializing-coupling-data.md) for details on data initialization. @@ -41,14 +41,16 @@ As soon as time interpolation is used to obtain data for a `relativeReadTime` th ## Experimental API for waveform iteration -If we want to improve the accuracy by using waveforms, this requires an extension of the existing API, because we need a way to tell preCICE where we want to sample the waveform. For this purpose, preCICE offers an experimental API. Here, `readBlockVectorData` accepts an additional argument `relativeReadTime`. This allows us to choose the time where the waveform should be sampled: + +If we want to improve the accuracy by using waveforms, this requires an extension of the existing API, because we need a way to tell preCICE where we want to sample the waveform. For this purpose, preCICE offers an experimental API. Here, `readData` accepts an additional argument `relativeReadTime`. This allows us to choose the time where the waveform should be sampled: ```cpp -// stable API with constant data in time window -void readBlockVectorData(int dataID, int size, const int* valueIndices, double* values) const; - -// experimental API for waveform iteration -void readBlockVectorData(int dataID, int size, const int* valueIndices, double relativeReadTime, double* values) const; +void Participant::readData( + precice::string_view meshName, + precice::string_view dataName, + precice::span vertices, + double relativeReadTime, + precice::span values) const ``` `relativeReadTime` describes the time relatively to the beginning of the current time step. This means that `relativeReadTime = 0` gives us access to data at the beginning of the time step. By choosing `relativeReadTime > 0` we can sample data at later points. The maximum allowed `relativeReadTime` corresponds to the remaining time until the end of the current time window. Remember that the remaining time until the end of the time window is always returned when calling `preciceDt = precice.getMaxTimeStepSize()` as `preciceDt`. So `relativeReadTime = preciceDt` corresponds to sampling data at the end of the current time window. @@ -82,16 +84,12 @@ while (not simulationDone()){ // time loop preciceDt = precice.getMaxTimeStepSize(); solverDt = beginTimeStep(); // e.g. compute adaptive dt dt = min(preciceDt, solverDt); - if (precice.isReadDataAvailable()){ // if waveform order >= 1 always true, because we can sample at arbitrary points - // sampling in the middle of the time step - precice.readBlockVectorData(displID, vertexSize, vertexIDs, 0.5 * dt, displacements); - setDisplacements(displacements); // displacement at the middle of the time step - } + // sampling in the middle of the time step + precice.readData("FluidMesh", "Displacements", vertexIDs, 0.5 * dt, displacements); + setDisplacements(displacements); // displacement at the middle of the time step solveTimeStep(dt); // might be using midpoint rule for time-stepping - if (precice.isWriteDataRequired(dt)){ // only true at the end of the time window - computeForces(forces); - precice.writeBlockVectorData(forceID, vertexSize, vertexIDs, forces); - } + computeForces(forces); + precice.writeData("FluidMesh", "Forces", vertexIDs, forces); precice.advance(dt); // read checkpoint & end time step ... diff --git a/pages/docs/dev-docs/dev-sourcedocs.md b/pages/docs/dev-docs/dev-sourcedocs.md index 44e26b611d..5270d243dd 100644 --- a/pages/docs/dev-docs/dev-sourcedocs.md +++ b/pages/docs/dev-docs/dev-sourcedocs.md @@ -17,7 +17,7 @@ It includes the source code documentation including the API documentation of bot Generated from the current `main` branch of preCICE. Use this version if you are developing your own adapter for preCICE. -API +API Docs ### Development Version diff --git a/pages/docs/fundamentals/fundamentals-roadmap.md b/pages/docs/fundamentals/fundamentals-roadmap.md index 100bc90d87..1f07cc302f 100644 --- a/pages/docs/fundamentals/fundamentals-roadmap.md +++ b/pages/docs/fundamentals/fundamentals-roadmap.md @@ -37,7 +37,7 @@ We are currently working towards [preCICE v3](https://github.com/precice/precice ## On our list - Tutorial testcase on electromagnetics -- [Support multiple `SolverInterface` instances simultaneously](https://github.com/precice/precice/projects/8) +- [Support multiple `Participant` instances simultaneously](https://github.com/precice/precice/projects/8) - [Windows support](https://github.com/precice/precice/issues/200) - [Two-level initialization enabled by default](https://github.com/precice/precice/issues/633). This feature was introduced in preCICE v2.0, but is currently switched off by default as not all use cases are supported yet. - Tutorial testcases for CFD-DEM coupling diff --git a/pages/docs/installation/installation-bindings-python.md b/pages/docs/installation/installation-bindings-python.md index 74fb05b69f..94e9f82afb 100644 --- a/pages/docs/installation/installation-bindings-python.md +++ b/pages/docs/installation/installation-bindings-python.md @@ -27,13 +27,13 @@ The python bindings for preCICE are [published on PyPI](https://pypi.org/project The usage of the python language bindings for preCICE is very similar to the C++ API. Therefore, please refer to our section on [coupling your code](https://precice.org/couple-your-code-overview.html) for getting started. Some important differences: * Call `import precice` at the beginning of your script. -* The object `precice.Interface` is the main access point to the preCICE API. +* The object `precice.Participant` is the main access point to the preCICE API. * We try to follow [PEP8](https://pep8.org/) with respect to function and class names. Meaning: `write_block_scalar_data`, not `writeBlockScalarData`, since this is a function call. -* Please use `numpy` arrays, if this seems appropriate. For scalar data a 1D-`numpy` with `size` entries should be used; for vector data a 2D-`numpy` array with `size x dimensions` entries should be used. This allows us to drop the `size` argument some functions calls. Meaning: not `writeBlockScalarData (int dataID, int size, int* vertexIDs, double* values)`, but `write_block_scalar_data(dataID, vertexIDs, values)`. +* Please use `numpy` arrays, if this seems appropriate. For scalar data a 1D-`numpy` with `size` entries should be used; for vector data a 2D-`numpy` array with `size x dimensions` entries should be used. * Refer to [the python version of the tutorial for the elastic tube in 1D](tutorials-elastic-tube-1d.html#python) and the corresponding [source code](https://github.com/precice/tutorials/tree/master/elastic-tube-1d) for a complete example of how pyprecice can be used to couple your code. {% tip %} -You can use Python's `help()` function for getting detailed usage information. Example: Open a python3 shell, `import precice`, `help(precice.Interface)` or `help(precice.Interface.write_block_scalar_data)` +You can use Python's `help()` function for getting detailed usage information. Example: Open a python3 shell, `import precice`, `help(precice.Participant)` or `help(precice.Participant.write_data)` {% endtip %} ## More details & troubleshooting diff --git a/pages/docs/installation/installation-bindings-rust.md b/pages/docs/installation/installation-bindings-rust.md index 6ef556d0e9..2f7ef07a96 100644 --- a/pages/docs/installation/installation-bindings-rust.md +++ b/pages/docs/installation/installation-bindings-rust.md @@ -39,8 +39,8 @@ cargo add --git https://github.com/precice/rust-bindings.git --rev v2.5.0 precic The usage of the rust language bindings for preCICE is very similar to the C++ API. Therefore, please refer to our section on [coupling your code](https://precice.org/couple-your-code-overview.html) for getting started. Some important differences: -* Use `precice::new()` to create a new `SolverInterface`. -* All calls that require mutable access to the `SolverInterface` need to pin it in memory first using [`pin_mut()`](https://docs.rs/cxx/1.0.91/cxx/struct.UniquePtr.html#method.pin_mut). +* Use `precice::new()` to create a new `Participant`. +* All calls that require mutable access to the `Participant` need to pin it in memory first using [`pin_mut()`](https://docs.rs/cxx/1.0.91/cxx/struct.UniquePtr.html#method.pin_mut). * Action constants are directly exposed via the `precice` module. * Sizes are inferred from passed slices diff --git a/pages/docs/installation/installation-linking.md b/pages/docs/installation/installation-linking.md index 324843fb63..ad21024eba 100644 --- a/pages/docs/installation/installation-linking.md +++ b/pages/docs/installation/installation-linking.md @@ -32,7 +32,7 @@ This directory can also point to the build directory of preCICE. This allows to An alternative is to tell CMake to consider an additional install prefix by passing the following to CMake `-DCMAKE_PREFIX_PATH=`. {% note %} -__Static linking is not recommended nor supported by the preCICE developers!__ +__Static linking is not recommended nor supported by the preCICE developers!__ Static linking in CMake requires you to provide all transitive dependencies of the preCICE, which includes private dependencies! Meaning that you have to find and provide the requested targets in your `CMakeLists.txt`. @@ -121,7 +121,7 @@ Depending on the configuration of `ld` it might look by default into `/usr/local ### `precice/Version.h` cannot be found -Version 2.5 introduces the `precice/Version.h` header and includes it by default in `SolverInterface.hpp` and `SolverInterfaceC.h`. +Version 2.5 introduces the `precice/Version.h` header and includes it by default in `Participant.hpp` and `SolverInterfaceC.h`. This file is generated during the preCICE build and not part of the sources. If you are using preCICE directly from the build directory without the help of pkg-config nor CMake, then you are likely missing an include-directory.