Skip to content

Commit

Permalink
v3: Update documentation w.r.t getMaxTimeStep(). (#258)
Browse files Browse the repository at this point in the history
* Update documentation w.r.t getMaxTimeStep().
* Implements changes from precice/precice#1623

---------

Co-authored-by: Benjamin Uekermann <benjamin.uekermann@gmail.com>
  • Loading branch information
BenjaminRodenberg and uekerman authored May 28, 2023
1 parent 792208b commit 03b0cbb
Show file tree
Hide file tree
Showing 9 changed files with 43 additions and 27 deletions.
6 changes: 3 additions & 3 deletions pages/docs/configuration/configuration-coupling.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Now, the `first` and the `second` participant are executed at the same time. Act
With `max-time-windows`, you say how many coupling time windows you want to simulate. Alternatively, you can use:

```xml
<max-time value="1.0"/>
<max-time value="1.0"/>
```

Afterwards,
Expand Down Expand Up @@ -68,7 +68,7 @@ For implicit coupling, you need to specify several additional options:
<coupling-scheme:parallel-implicit>
<participants first="MySolver1" second="MySolver2"/>
...
<exchange data="Temperature" mesh="MyMesh2" from="MySolver2" to="MySolver1"/>
<exchange data="Temperature" mesh="MyMesh2" from="MySolver2" to="MySolver1"/>
<max-iterations value="100"/>
<relative-convergence-measure limit="1e-4" data="Displacements" mesh="MyMesh2"/>
<relative-convergence-measure limit="1e-4" data="Forces" mesh="MyMesh2"/>
Expand All @@ -82,7 +82,7 @@ To control the number of sub-iterations within an implicit coupling loop, you ca

* `relative-convergence-measure` for a relative criterion
* `absolute-convergence-measure` for an absolute criterion
* `min-iteration-convergence-measure` to require a minimum of iterations
* `min-iteration-convergence-measure` to require a minimum of iterations
If multiple convergence measure are combined they all need to be fulfilled to go to the next time window. Alternatively, you can specify `suffices="yes"` within any convergence measure.
The data used for a convergence measure needs to be exchanged within the coupling-scheme (tag `exchange`).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ precice.configure("precice-config.xml");

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

double preciceDt = precice.initialize();
precice.initialize();

[...] // solving and coupling

Expand Down
4 changes: 2 additions & 2 deletions pages/docs/couple-your-code/couple-your-code-gradient-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ double* stress = new double[vertexSize * dim];
// create gradient data
double* stressGradient = new double[vertexSize * dim * dim]
[...]
preciceDt = precice.initialize();
precice.initialize();

while (not simulationDone()){ // time loop
precice.readBlockVectorData(displID, vertexSize, vertexIDs, displacements);
Expand All @@ -89,7 +89,7 @@ while (not simulationDone()){ // time loop
precice.writeBlockVectorGradientData(stressID, vertexSize, vertexIDs, stressGradient);
}

preciceDt = precice.advance(dt);
precice.advance(dt);
}
[...]
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,21 @@ double dt; // actual time step size
```
<!-- Long code blocks need to be split. See https://github.com/precice/precice.github.io/commit/74e377cece4a221e00b5c56b1db3942ec70a6272 -->
```cpp
preciceDt = precice.initialize();
precice.initialize();
while (precice.isCouplingOngoing()){
if(precice.isActionRequired(cowic)){
saveOldState(); // save checkpoint
precice.markActionFulfilled(cowic);
}
preciceDt = precice.getMaxTimeStepSize();
solverDt = beginTimeStep(); // e.g. compute adaptive dt
dt = min(preciceDt, solverDt);
precice.readBlockVectorData(displID, vertexSize, vertexIDs, displacements);
setDisplacements(displacements);
solveTimeStep(dt);
computeForces(forces);
precice.writeBlockVectorData(forceID, vertexSize, vertexIDs, forces);
preciceDt = precice.advance(dt);
precice.advance(dt);
if(precice.isActionRequired(coric)){ // time step not converged
reloadOldState(); // set variables back to checkpoint
precice.markActionFulfilled(coric);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,13 @@ double* displacements = new double[vertexSize*dim];

[...]

preciceDt = precice.initialize();

if(precice.isActionRequired(cowid)){
precice.writeBlockVectorData(forceID, vertexSize, vertexIDs, forces);
precice.markActionFulfilled(cowid);
}

precice.initializeData();
precice.initialize();

while (precice.isCouplingOngoing()){
[...]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,17 @@ double solverDt; // solver time step size
double preciceDt; // maximum precice time step size
double dt; // actual time step size

preciceDt = precice.initialize();
precice.initialize();
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);
setDisplacements(displacements);
solveTimeStep(dt);
computeForces(forces);
precice.writeBlockVectorData(forceID, vertexSize, vertexIDs, forces);
preciceDt = precice.advance(dt);
precice.advance(dt);
endTimeStep(); // e.g. update variables, increment time
}
precice.finalize(); // frees data structures and closes communication channels
Expand Down
21 changes: 14 additions & 7 deletions pages/docs/couple-your-code/couple-your-code-steering-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,24 @@ The handle to the preCICE API is the class `precice::SolverInterface`. Its const

```cpp
SolverInterface( String participantName, String configurationFileName, int rank, int size );
double initialize();
double advance ( double computedTimeStepLength );
void initialize();
void advance ( double computedTimeStepSize );
void finalize();
```
What do they do?
* `initialize` establishes communication channels, sets up data structures of preCICE, and returns the maximum time step size the solver should use next. But let's ignore time step sizes for the moment. This will be the topic of [Step 5](couple-your-code-time-step-sizes.html).
* `advance` needs to be called after the computation of every time step to _advance_ the coupling. As an argument, you have to pass the solver's last time step size (`dt`). Again, the function returns the next maximum time step size you can use (`preciceDt`). More importantly, it maps coupling data between the coupling meshes, it communicates coupling data between the coupled participants, and it accelerates coupling data. One could say the complete coupling happens within this single function.
* `initialize` establishes communication channels and sets up data structures of preCICE.
* `advance` needs to be called after the computation of every time step to _advance_ the coupling. As an argument, you have to pass the solver's last time step size (`dt`). Additionally, it maps coupling data between the coupling meshes, it communicates coupling data between the coupled participants, and it accelerates coupling data. One could say the complete coupling happens within this single function.
* `finalize` frees the preCICE data structures and closes communication channels.
So, let's extend the code of our fluid solver:
The following function allows us to query the maximum allowed time step size from preCICE:
```cpp
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"
Expand All @@ -35,12 +41,13 @@ double solverDt; // solver time step size
double preciceDt; // maximum precice time step size
double dt; // actual time step size

preciceDt = precice.initialize();
precice.initialize();
while (not simulationDone()){ // time loop
preciceDt = getMaxTimeStepSize();
solverDt = beginTimeStep(); // e.g. compute adaptive dt
dt = min(preciceDt, solverDt); // more about this in Step 5
solveTimeStep(dt);
preciceDt = precice.advance(dt);
precice.advance(dt);
endTimeStep(); // e.g. update variables, increment time
}
precice.finalize(); // frees data structures and closes communication channels
Expand Down
17 changes: 12 additions & 5 deletions pages/docs/couple-your-code/couple-your-code-time-step-sizes.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ double solverDt; // solver time step size
double preciceDt; // maximum precice time step size
double dt; // actual time step size

preciceDt = precice.initialize();
precice.initialize();
while (not simulationDone()){ // time loop
preciceDt = precice.getMaxTimeStepSize();
solverDt = beginTimeStep(); // e.g. compute adaptive dt
dt = min(preciceDt, solverDt);
solveTimeStep(dt);
Expand Down Expand Up @@ -47,10 +48,10 @@ The figure below illustrates this procedure (k is the subcycling index, the dash

![Timestepping with a fixed time window](images/docs/couple-your-code-timestepping-fixed.png)

* After each time step, both participants tell preCICE which time step size `dt` they just used. This way, preCICE can keep track of the total time. preCICE returns the remainder time to the next window.
* After each time step, both participants tell preCICE which time step size `dt` they just used by calling `precice.advance(dt)`. This way, preCICE can keep track of the total time. `preciceDt` is the remainder time to the next window:

```c++
preciceDt = precice.advance(dt);
preciceDt = precice.getMaxTimeStepSize();
```

* Both participants compute their next (adaptive) time step size. It can be larger or smaller than the remainder `preciceDt`.
Expand Down Expand Up @@ -85,6 +86,7 @@ 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()){
Expand All @@ -96,7 +98,7 @@ while (not simulationDone()){ // time loop
computeForces(forces);
precice.writeBlockVectorData(forceID, vertexSize, vertexIDs, forces);
}
preciceDt = precice.advance(dt);
precice.advance(dt);
endTimeStep(); // e.g. update variables, increment time
}
```
Expand All @@ -111,10 +113,15 @@ The `first` participant sets the time step size. This requires that the `second`
* In `advance`, this time step size is given to preCICE (Step 2).

```c++
preciceDt = precice.advance(dt);
precice.advance(dt);
```

* preCICE has tracked the time level of the orange participant A and returns the remainder to reach B's time step size.

```c++
preciceDt = precice.getMaxTimeStepSize();
```

* A computes its next (adaptive) time step size. It can now be larger or smaller than the remainder.

```c++
Expand Down
7 changes: 4 additions & 3 deletions pages/docs/couple-your-code/couple-your-code-waveform.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ void readBlockVectorData(int dataID, int size, const int* valueIndices, double*
void readBlockVectorData(int dataID, int size, const int* valueIndices, double relativeReadTime, double* 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.advance(dt)` as `preciceDt`. So `relativeReadTime = preciceDt` corresponds to sampling data at the end of the current time window.
`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.
If we choose to use a smaller time step size `dt < preciceDt`, we apply subcycling and therefore `relativeReadTime = dt` corresponds to sampling data at the end of the time step. But we can also use smaller values for `relativeReadTime`, as shown in the usage example below. When using subcycling, it is important to note that `relativeReadTime = preciceDt` is the default behavior, if no `relativeReadTime` is provided, because preCICE cannot know the `dt` our solver wants to use. This also means that if subcycling is applied one must use the experimental API and provide `relativeReadTime` to benefit from the higher accuracy waveforms.
Expand All @@ -74,10 +74,11 @@ We are now ready to extend the example from ["Step 6 - Implicit coupling"](coupl

```cpp
...
preciceDt = precice.initialize();
precice.initialize();
while (not simulationDone()){ // time loop
// write checkpoint
...
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
Expand All @@ -90,7 +91,7 @@ while (not simulationDone()){ // time loop
computeForces(forces);
precice.writeBlockVectorData(forceID, vertexSize, vertexIDs, forces);
}
preciceDt = precice.advance(dt);
precice.advance(dt);
// read checkpoint & end time step
...
}
Expand Down

0 comments on commit 03b0cbb

Please sign in to comment.