Skip to content

Commit 80aabc0

Browse files
committed
WIP: Add Python code for couple your code section
1 parent 9ad48b5 commit 80aabc0

File tree

3 files changed

+200
-7
lines changed

3 files changed

+200
-7
lines changed

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

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ For coupling, we need coupling meshes. Let's see how we can tell preCICE about o
99

1010
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:
1111

12+
<ul id="apiTabs" class="nav nav-tabs">
13+
<li class="active"><a href="#cpp-1" data-toggle="tab">C++</a></li>
14+
<li><a href="#python-1" data-toggle="tab">Python</a></li>
15+
</ul>
16+
<div class="tab-content">
17+
<div role="tabpanel" class="tab-pane active" id="cpp-1" markdown="1">
18+
1219
```cpp
1320
VertexID setMeshVertex(
1421
::precice::string_view meshName,
@@ -88,6 +95,128 @@ turnOffSolver();
8895
```
8996

9097
Did you see that your fluid solver now also needs to provide the functions `computeForces` and `setDisplacements`? As you are an expert in your fluid code, these functions should be easy to implement. Most probably, you already have such functionality anyway. If you are not an expert in your code try to find an expert :smirk:.
98+
</div>
99+
<div role="tabpanel" class="tab-pane active" id="python-1" markdown="1">
100+
101+
```python
102+
"""
103+
Parameters
104+
----------
105+
mesh_name : str
106+
Name of the mesh to add the vertex to.
107+
position : array_like
108+
The coordinates of the vertex.
109+
110+
Returns
111+
-------
112+
vertex_id : int
113+
ID of the vertex which is set.
114+
"""
115+
set_mesh_vertex(mesh_name, position)
116+
117+
"""
118+
Parameters
119+
----------
120+
mesh_name : str
121+
Name of the mesh to add the vertices to.
122+
positions : array_like
123+
The coordinates of the vertices in a numpy array [N x D] where
124+
N = number of vertices and D = dimensions of geometry.
125+
126+
Returns
127+
-------
128+
vertex_ids : numpy.ndarray
129+
IDs of the created vertices.
130+
"""
131+
set_mesh_vertices(mesh_name, positions)
132+
```
133+
134+
* `set_mesh_vertex` defines the coordinates of a single mesh vertex and returns a vertex ID, which you can use to refer to this vertex.
135+
* `set_mesh_vertices` defines multiple vertices at once. So, you can use this function instead of calling `set_mesh_vertex` multiple times. This is also good practice for performance reasons.
136+
137+
To write data to the coupling data structure the following API function is needed:
138+
139+
```python
140+
"""
141+
Parameters
142+
----------
143+
mesh_name : str
144+
name of the mesh to write to.
145+
data_name : str
146+
Data name to write to.
147+
vertex_ids : array_like
148+
Indices of the vertices.
149+
values : array_like
150+
Values of data
151+
"""
152+
write_data(self, mesh_name, data_name, vertex_ids, values)
153+
```
154+
155+
Similarly, there is a `read_data` API function for reading coupling data:
156+
157+
```python
158+
"""
159+
Parameters
160+
----------
161+
mesh_name : str
162+
Name of the mesh to write to.
163+
data_name : str
164+
ID to read from.
165+
vertex_ids : array_like
166+
Indices of the vertices.
167+
relative_read_time : double
168+
Point in time where data is read relative to the beginning of the current time step
169+
170+
Returns
171+
-------
172+
values : numpy.ndarray
173+
Contains the read data.
174+
"""
175+
read_data(mesh_name, data_name, vertex_ids, relative_read_time)
176+
```
177+
178+
The relative read time can be anything from the current point in time (`0`) to the end of the time window (`get_max_time_step_size()`). We will talk about the additional argument `relative_read_time` in detail in [the section on time interpolation](couple-your-code-waveform.html).
179+
180+
Let's define coupling meshes and access coupling data in our example code:
181+
182+
```python
183+
turn_on_solver() # e.g. setup and partition mesh
184+
num_vertices = 3
185+
precice = precice.Participant("FluidSolver", "precice-config.xml", rank, size) # Initialize participant
186+
187+
mesh_dim = precice.get_mesh_dimensions("FluidMesh")
188+
189+
vertices = np.zeros((num_vertices, participant.get_mesh_dimensions(mesh_name))) # coords of vertices at wet surface
190+
vertex_ids = precice.set_mesh_vertices("FluidMesh", vertices)
191+
192+
forces_dim = precice.get_data_dimensions("FluidMesh", "Forces")
193+
forces = np.zeros((num_vertices, forces_dim))
194+
195+
displacements_dim = precice.get_data_dimensions("FluidMesh", "Displacements")
196+
displacements = np.zeros((num_vertices, displacements_dim))
197+
198+
199+
precice.initialize()
200+
while participant.is_coupling_ongoing(): # time loop
201+
precice_dt = precice.get_max_time_step_size()
202+
solver_dt = begin_time_step() # e.g. compute adaptive dt
203+
dt = min(precice_dt, solver_dt)
204+
precice.read_data("FluidMesh", "Displacements", vertex_ids, dt, displacements)
205+
set_displacements(displacements)
206+
solve_time_step(dt)
207+
compute_forces(forces)
208+
precice.write_data("FluidMesh", "Forces", vertex_ids, forces)
209+
precice.advance(dt)
210+
end_time_step() # e.g. update variables, increment time
211+
212+
precice.finalize() # frees data structures and closes communication channels
213+
turn_off_solver()
214+
```
215+
216+
Did you see that your fluid solver now also needs to provide the functions `compute_forces` and `set_displacements`? As you are an expert in your fluid code, these functions should be easy to implement. Most probably, you already have such functionality anyway. If you are not an expert in your code try to find an expert :smirk:.
217+
218+
</div>
219+
</div>
91220

92221
Once your adapter reaches this point, it is a good idea to test your adapter against one of the [solverdummies](couple-your-code-api#minimal-reference-implementation), which then plays the role of the `SolidSolver`.
93222

pages/docs/couple-your-code/couple-your-code-preparing-your-solver.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ summary: "If you want to couple your own code you need to properly understand it
77

88
Let's say you want to prepare a fluid solver for fluid-structure interaction and that your code looks like this:
99

10+
<ul id="apiTabs" class="nav nav-tabs">
11+
<li class="active"><a href="#cpp" data-toggle="tab">C++</a></li>
12+
<li><a href="#python" data-toggle="tab">Python</a></li>
13+
</ul>
14+
<div class="tab-content">
15+
<div role="tabpanel" class="tab-pane active" id="cpp" markdown="1">
16+
1017
```cpp
1118
turnOnSolver(); //e.g. setup and partition mesh
1219

@@ -18,8 +25,25 @@ while (not simulationDone()){ // time loop
1825
endTimeStep(); // e.g. update variables, increment time
1926
}
2027
turnOffSolver();
28+
29+
```
30+
31+
</div>
32+
<div role="tabpanel" class="tab-pane" id="python" markdown="1">
33+
34+
```python
35+
turn_on_solver() #e.g. setup and partition mesh
36+
37+
while not simulation_done(): # time loop
38+
dt = begin_time_step() #e.g compute adaptive dt
39+
solve_time_step(dt)
40+
end_time_step() #e.g. update variables, increment time
41+
42+
turn_off_solver()
2143
```
2244

45+
</div>
46+
</div>
2347
Probably most solvers have such a structures: something in the beginning (reading input, domain decomposition), a big time loop, and something in the end. Each time step also falls into three parts: some pre-computations (e.g. computing an adaptive time step size), the actual computation (solving a linear or non-linear equation system), and something in the end (updating variables, incrementing time). Try to identify these parts in the code you want to couple.
2448

2549
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`, `Julia`, and `Rust` (see [Application Programming Interface](couple-your-code-api)).

pages/docs/couple-your-code/couple-your-code-steering-methods.md

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,65 @@ summary: "In this step, you get to know the most important API functions of preC
99
As a first preparation step, you need to include the preCICE library headers. In C++, you need to include the file [precice.hpp](https://github.com/precice/precice/blob/develop/src/precice/precice.hpp).
1010
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:
1111

12+
<ul id="apiTabs" class="nav nav-tabs">
13+
<li class="active"><a href="#cpp-1" data-toggle="tab">C++</a></li>
14+
<li><a href="#python-1" data-toggle="tab">Python</a></li>
15+
</ul>
16+
<div class="tab-content">
17+
<div role="tabpanel" class="tab-pane active" id="cpp-1" markdown="1">
18+
1219
```cpp
1320
Participant( String participantName, String configurationFileName, int rank, int size );
1421
void initialize();
1522
void advance ( double computedTimeStepSize );
1623
void finalize();
1724
```
1825
26+
</div>
27+
<div role="tabpanel" class="tab-pane" id="python-1" markdown="1">
28+
29+
```python
30+
"""
31+
Parameters:
32+
participant_name: string
33+
Name of the solver
34+
configuration_file_name: string
35+
Name of preCICE config file
36+
rank: int
37+
Rank of the process
38+
size: int
39+
Size of the process
40+
"""
41+
participant = Participant(participant_name, configuration_file_name, rank, size)
42+
43+
participant.initialize()
44+
45+
"""
46+
Parameters:
47+
computed_timestep_size: double
48+
Length of timestep used by solver
49+
"""
50+
participant.advance(computed_timestep_size)
51+
52+
participant.finalize()
53+
54+
```
55+
56+
</div>
57+
</div>
1958
What do they do?
2059

2160
* `initialize` establishes communication channels and sets up data structures of preCICE.
2261
* `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.
2362
* `finalize` frees the preCICE data structures and closes communication channels.
2463

2564
The following function allows us to query the maximum allowed time step size from preCICE:
26-
27-
2865
<ul id="apiTabs" class="nav nav-tabs">
29-
<li class="active"><a href="#cpp" data-toggle="tab">C++</a></li>
30-
<li><a href="#python" data-toggle="tab">Python</a></li>
66+
<li class="active"><a href="#cpp-2" data-toggle="tab">C++</a></li>
67+
<li><a href="#python-2" data-toggle="tab">Python</a></li>
3168
</ul>
3269
<div class="tab-content">
33-
<div role="tabpanel" class="tab-pane active" id="cpp" markdown="1">
70+
<div role="tabpanel" class="tab-pane active" id="cpp-2" markdown="1">
3471
```cpp
3572
double getMaxTimeStepSize();
3673
```
@@ -60,8 +97,10 @@ while (not simulationDone()){ // time loop
6097
precice.finalize(); // frees data structures and closes communication channels
6198
turnOffSolver();
6299
```
100+
63101
</div>
64-
<div role="tabpanel" class="tab-pane" id="python" markdown="1">
102+
<div role="tabpanel" class="tab-pane" id="python-2" markdown="1">
103+
65104
```python
66105
import precice
67106
@@ -82,7 +121,8 @@ while t < t_end: # time loop
82121
t = t + dt
83122
84123
precice.finalize() # frees data structures and closes communication channels
124+
85125
```
126+
86127
</div>
87128
</div>
88-

0 commit comments

Comments
 (0)