Skip to content

Added documentation for nearest-neighbor-gradient [cont] #169

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jun 24, 2022
4 changes: 4 additions & 0 deletions _data/sidebars/docs_sidebar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,10 @@ entries:
- title: Step 8 – Mesh connectivity
url: /couple-your-code-defining-mesh-connectivity.html
output: web, pdf

- title: Step 9 – Gradient Data
url: /couple-your-code-gradient-data.html
output: web, pdf

- title: Advanced topics
output: web, pdf
Expand Down
23 changes: 12 additions & 11 deletions pages/docs/configuration/configuration-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ Therefore, preCICE provides data mapping methods to map coupling data from one m
A participant needs to `use` at least two meshes to define a mapping between both:

```xml
<participant name="MySolver1">
<use-mesh name="MyMesh1" provide="yes"/>
<participant name="MySolver1">
<use-mesh name="MyMesh1" provide="yes"/>
<use-mesh name="MyMesh2" from="MySolver2"/>
<write-data name="Forces" mesh="MyMesh1"/>
<read-data name="Temperature" mesh="MyMesh1"/>
<write-data name="Forces" mesh="MyMesh1"/>
<read-data name="Temperature" mesh="MyMesh1"/>
<mapping:nearest-neighbor direction="read" from="MyMesh2" to="MyMesh1" constraint="consistent"/>
<mapping:nearest-neighbor direction="write" from="MyMesh1" to="MyMesh2" constraint="conservative"/>
</participant>
Expand All @@ -35,19 +35,19 @@ Furthermore, mappings come int two types of `constraint`: `consistent` and `cons
```text
f=2 f=1 f=2 f=1 f=1
------+------+------+------+------+------
\ | / | /
\ | / | /
-------------+-------------+-------------
f=(2+1+2) f=(1+1)
f=(2+1+2) f=(1+1)
```

* `consistent` mapping: For quantities that are normalized (intensive quantities; `Temperature` in our example, or pressure, which is force _per unit area_), we need a consistent mapping. This means that the value at coarse nodes is the same as the value at the corresponding fine node. Again, an example for a nearest-neighbor mapping could look like this:

```text
T=2 T=1 T=2 T=1 T=1
------+------+------+------+------+------
| |
| |
-------------+-------------+-------------
T=1 T=1
T=1 T=1
```

For a sequential participant, any combination of `read`/`write`-`consistent/conservative` is valid. For a parallel participant (i.e. a `master` tag is defined), only `read`-`consistent` and `write`-`conservative` is possible. More details are given [further below](configuration-mapping.html#restrictions-for-parallel-participants).
Expand All @@ -58,10 +58,11 @@ Furthermore, mappings have an optional parameter `timing`, it can be:
* `onadvance`: The mapping is newly computed for every mapping of coupling data. This can be expensive and is only recommend if you know exactly why you want to do this.
* `ondemand`: Data is not mapped in `initialize`, `initializeData`, or `advance`, but only if steered manually through `mapReadDataTo` resp. `mapWriteDataFrom`. Only use this if you are sure that your adapter uses theses methods.

Concerning mapping methods, preCICE offers three variants:
Concerning mapping methods, preCICE offers four variants:

* `nearest-neighbor`: A first-order method, which is fast, easy to use, but, of course, has numerical deficiencies.
* `nearest-projection`: A (mostly) second-order method, which first projects onto mesh elements and, then, uses linear interpolation within each element (compare the figure below). The method is still relatively fast and numerically clearly superior to `nearest-neighbor`. The downside is that mesh connectivity information needs to be defined, i.e. in the adapter, the participant needs to tell preCICE about edges in 2D and edges, triangles, or quads (see [issue](https://github.com/precice/precice/issues/153)) in 3D. On the [mesh connectivity page](couple-your-code-defining-mesh-connectivity.html), we explain how to do that. If no connectivity information is provided, `nearest-projection` falls back to an (expensive) `nearest-neighbor` mapping.
* `nearest-neighbor-gradient`: A second-order method, which uses the same algorithm as nearest-neighbor with an additional linear approximation using gradient data. This method requires additional gradient data information. On the [gradient data page](couple-your-code-gradient-data.html), we explain how to add gradient data to the mesh. This method is only applicable with the `consistent` constraint.
* Radial-basis function mapping. Here, the configuration is more involved, so keep reading.

![different mapping variants visualised](images/docs/configuration-mapping-variants.png)
Expand All @@ -77,7 +78,7 @@ To compute the interpolant, a linear equation system needs to be solved in every

For small/medium size problems, the QR decomposition is enough and you don't need to install anything else. However, this follows a gather-scatter approach, which limits the scalability. For large problems, the GMRES solver performs better than the QR decomposition. For this, you need to [build preCICE with PETSc](https://github.com/precice/precice/wiki/Dependencies#petsc-optional). If you built with PETSc, the default is always GMRES. If you still want to use the QR decomposition, you can use the option `use-qr-decomposition`.

Radial basis function mapping also behaves as a second-order method just as `nearest-projection`, but without the need to define connectivity information. The downside is that it is normally more expensive to compute and that it shows numerical problems for large or highly irregular meshes.
Radial basis function mapping also behaves as a second-order method just as `nearest-projection`, but without the need to define connectivity information. The downside is that it is normally more expensive to compute and that it shows numerical problems for large or highly irregular meshes.

The configuration might look like this:

Expand All @@ -87,7 +88,7 @@ The configuration might look like this:

`thin-plate-splines` is the type of basis function used. preCICE offers basis function with global and local support:

* Basis function with global support (such as `thin-plate-splines`) are easier to configure as no further parameter needs to be set. For larger meshes, however, such functions lead to performance issues in terms of algorithmic complexity, numerical condition, and scalability.
* Basis function with global support (such as `thin-plate-splines`) are easier to configure as no further parameter needs to be set. For larger meshes, however, such functions lead to performance issues in terms of algorithmic complexity, numerical condition, and scalability.
* Basis functions with local support need either the definition of a `support-radius` (such as for `rbf-compact-tps-c2`) or a `shape-parameter` (such as for `gaussian`). To have a good trade-off between accuracy and efficiency, the support of each basis function should cover three to five vertices in every direction. You can use the tool [rbfShape.py](https://github.com/precice/precice/tree/develop/extras/rbfShape) to get a good estimate of `shape-parameter`.

For a complete overview of all basis function, refer to [this paper](https://www.sciencedirect.com/science/article/pii/S0045793016300974), page 5.
Expand Down
137 changes: 137 additions & 0 deletions pages/docs/couple-your-code/couple-your-code-gradient-data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
---
title: Step 9 – Gradient data
permalink: couple-your-code-gradient-data.html
keywords: api, adapter, mapping, gradient, nearest-neighbor-gradient
summary: "So far, our mesh contains only data. This is sufficient for most of the numerical methods that preCICE offers. For nearest-neighbor-gradient mapping, however, preCICE also requires additional gradient data information. In this step, you learn how to add gradient data to the mesh."
---

{% version 2.4.0 %}
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:

```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);
```

* `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:

```cpp
( v0x_dx, v0y_dx, v0x_dy, v0y_dy,
v1x_dx, v1y_dx, v1x_dy, v1y_dy,
... ,
vnx_dx, vny_dx, vnx_dy, vny_dy )
```

Let's add gradient data to our example code:

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

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

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

// create gradient data
double* stressGradient = new double[vertexSize * dim * dim]
[...]
precice_dt = precice.initialize();

while (not simulationDone()){ // time loop
precice.readBlockVectorData(displID, vertexSize, vertexIDs, displacements);
setDisplacements(displacements);
[...]
solveTimeStep(dt);
computeStress(stress);

precice.writeBlockVectorData(stressID, vertexSize, vertexIDs, stress);

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

precice_dt = precice.advance(dt);
}
[...]

{% experimental %}
This is an experimental feature.
{% endexperimental %}

As a last step, you need to set the flag `gradient="on"` in the configuration file, whenever you require to write gradient data.

For the example, you can use the following `precice-config.xml`:

```xml
<?xml version="1.0"?>

<precice-configuration>

<solver-interface dimensions="3" experimental="on">

<data:vector name="Stress" gradient="on"/>
<data:vector name="Displacements" />

<mesh name="FluidMesh">
<use-data name="Stress"/>
<use-data name="Displacements"/>
</mesh>

<mesh name="StructureMesh">
<use-data name="Stress"/>
<use-data name="Displacements"/>
</mesh>

<participant name="FluidSolver">
<use-mesh name="FluidMesh" provide="yes"/>
<use-mesh name="StructureMesh" from="SolidSolver"/>
<write-data name="Stress" mesh="FluidMesh"/>
<read-data name="Displacements" mesh="FluidMesh"/>
<mapping:nearest-neighbor-gradient direction="write" from="FluidMesh"
to="StructureMesh" constraint="consistent"/>
<mapping:nearest-neighbor direction="read" from="StructureMesh"
to="FluidMesh" constraint="consistent"/>
</participant>
[...]
</solver-interface>
</precice-configuration>
```