Skip to content

Commit 91bfa24

Browse files
jmorice91JAuriac
andauthored
fix #17, update set_value plugin (ex12) (#22)
* Update ex12 --------- Co-authored-by: JAuriac <56091659+JAuriac@users.noreply.github.com>
1 parent ca74dcb commit 91bfa24

7 files changed

Lines changed: 1365 additions & 0 deletions

File tree

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,7 @@ target_link_libraries(ex9 m MPI::MPI_C paraconf::paraconf PDI::pdi)
6060
add_executable(ex10 ex10.c)
6161
target_link_libraries(ex10 m MPI::MPI_C paraconf::paraconf PDI::pdi)
6262

63+
add_executable(ex12 ex12.c)
64+
target_link_libraries(ex12 m MPI::MPI_C paraconf::paraconf PDI::pdi)
65+
6366
add_subdirectory(ex11/)

README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,64 @@ In a more realistic setup, one would typically not write much code in the YAML
503503
file directly, but would instead call functions specified in a `.py` file on
504504
the side.
505505

506+
## set value of data and metadata
507+
508+
### Ex12. set_value plugin
509+
510+
The \ref set_value_plugin "set_value" plugin allows setting values to data and
511+
metadata descriptors from the YAML file.
512+
In the \ref set_value_plugin "set_value", the user can trigger action upon:
513+
`on_init`, `on_finalize`, `on_data`, `on_event`.
514+
In this plugin, we have five different types of action:
515+
- Share data (`share`) - plugin will share new allocated data with given values.
516+
- Release data (`release`) - plugin will release shared data.
517+
- Expose data (`expose`) - plugin will expose new allocated data
518+
with given values.
519+
- Set data (`set`) - plugin will set given values to the already shared data.
520+
- Calling an event (`event`) - plugin will call an event.
521+
522+
\note examples with keywords `share`, `release`, `expose`, `set` and `event`
523+
are given at the end of section "set_value" plugin in the website.
524+
525+
In this exercise, we expose the integer `switch` to %PDI at each iteration.
526+
This interger is used to enable or to disable the writing of `main_field`.
527+
We want to start writing once this integer passes 50 and stop when it's below 25.
528+
For this purpose, we introduce a new logical variable `should_output`
529+
in `ex12.yml`.
530+
The value of `should_output` is defined by:
531+
```c
532+
if(switch > 50) should_output = true
533+
if(switch < 25) should_output = false
534+
//otherwise the value of should_output doesn't change.
535+
```
536+
537+
* At initialization of %PDI, define the `should_output` to 0 (false).
538+
* At finalization, release the variable `should_output`.
539+
* When `switch` is shared with %PDI, set the value of `should_output`
540+
according to its definition.
541+
\attention
542+
%PDI doesn't known directive `if` in YAML file. Therefore, you need to redefine
543+
the value of `should_output` according to the previous value of `should_output`
544+
and the value of `switch`.
545+
546+
You should be able to match the expected output described in
547+
`should_output_solution.dat`.
548+
You can easily check if the files are the same by running:
549+
```bash
550+
diff should_output_solution.dat should_output.dat
551+
```
552+
553+
* Enable the writing of `main_field` according to the value of `should_output`.
554+
555+
You should be able to match the expected output described in `ex12.h5dump`.
556+
You can easily check if the files are the same by running:
557+
```bash
558+
diff ex12.h5dump <(h5dump ex12*.h5)
559+
```
560+
To see your `h5` file in readable file format,
561+
you can check the section [Comparison with the `h5dump` command](#h5comparison).
562+
563+
506564
## What next ?
507565

508566
In this tutorial, you used the C API of %PDI and from YAML, you used the

ex12.c

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
/*******************************************************************************
2+
* Copyright (C) 2015-2019 Commissariat a l'energie atomique et aux energies alternatives (CEA)
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
******************************************************************************/
22+
23+
#include <mpi.h>
24+
25+
#include <assert.h>
26+
#include <math.h>
27+
#include <stdio.h>
28+
#include <stdlib.h>
29+
#include <time.h>
30+
#include <stdbool.h>
31+
32+
#include <paraconf.h>
33+
// load the PDI header
34+
#include <pdi.h>
35+
36+
/// size of the local data as [HEIGHT, WIDTH] including ghosts & boundary constants
37+
int dsize[2];
38+
39+
/// 2D size of the process grid as [HEIGHT, WIDTH]
40+
int psize[2];
41+
42+
/// 2D rank of the local process in the process grid as [YY, XX]
43+
int pcoord[2];
44+
45+
/// the alpha coefficient used in the computation
46+
double alpha;
47+
48+
double L=1.0;
49+
double source1[4]={0.4, 0.4, 0.2, 100};
50+
double source2[4]={0.7, 0.8, 0.1, 200};
51+
52+
FILE *pFile2=NULL;
53+
54+
void open_file(void)
55+
{
56+
int rank;
57+
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
58+
59+
if(rank>0) return;
60+
printf("Call open_file.\n");
61+
pFile2 = fopen("should_output.dat", "w");
62+
fprintf(pFile2, "iter switch should_output\n");
63+
}
64+
65+
void close_file(void)
66+
{
67+
int rank;
68+
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
69+
70+
if(rank>0) return;
71+
printf("Call close_file.\n");
72+
73+
if(pFile2 == NULL){
74+
fprintf(stderr,"\n error: The file should_output is not open. Call open_file before\n \n");
75+
exit(1);
76+
}
77+
fclose(pFile2);
78+
}
79+
80+
/** Initialize the data all to 0 except for the left border (XX==0) initialized to 1 million
81+
* \param[out] dat the local data to initialize
82+
*/
83+
void init(double dat[dsize[0]][dsize[1]])
84+
{
85+
for (int yy=0; yy<dsize[0]; ++yy) for (int xx=0; xx<dsize[1]; ++xx) dat[yy][xx] = 0;
86+
double dy = L / ((dsize[0]-2) *psize[0]) ;
87+
double dx = L / ((dsize[1]-2) *psize[1]) ;
88+
89+
double cpos_x,cpos_y;
90+
for(int yy=0; yy<dsize[0];++yy) {
91+
cpos_y=(yy+pcoord[0]*(dsize[0]-2))*dy-0.5*dy;
92+
for(int xx=0; xx<dsize[1];++xx) {
93+
cpos_x=(xx+pcoord[1]*(dsize[1]-2))*dx-0.5*dx;
94+
if((cpos_y-source1[0])*(cpos_y-source1[0]) + (cpos_x-source1[1])*(cpos_x-source1[1]) <= source1[2]*source1[2]) {
95+
dat[yy][xx] = source1[3];
96+
}
97+
if((cpos_y-source2[0])*(cpos_y-source2[0]) + (cpos_x-source2[1])*(cpos_x-source2[1]) <= source2[2]*source2[2]) {
98+
dat[yy][xx] = source2[3];
99+
}
100+
}
101+
}
102+
}
103+
104+
/** Compute the values at the next time-step based on the values at the current time-step
105+
* \param[in] cur the local data at the current time-step
106+
* \param[out] next the local data at the next time-step
107+
*/
108+
void iter(double cur[dsize[0]][dsize[1]], double next[dsize[0]][dsize[1]])
109+
{
110+
int xx, yy;
111+
for (yy=1; yy<dsize[0]-1; ++yy) {
112+
for (xx=1; xx<dsize[1]-1; ++xx) {
113+
next[yy][xx] = (1.-4.*alpha) * cur[yy][xx]
114+
+alpha * ( cur[yy][xx-1]
115+
+ cur[yy][xx+1]
116+
+ cur[yy-1][xx]
117+
+ cur[yy+1][xx]);
118+
}
119+
}
120+
}
121+
122+
/** Exchanges ghost values with neighbours
123+
* \param[in] cart_comm the MPI communicator with all processes organized in a 2D Cartesian grid
124+
* \param[in] cur the local data at the current time-step whose ghosts need exchanging
125+
*/
126+
void exchange(MPI_Comm cart_comm, double cur[dsize[0]][dsize[1]])
127+
{
128+
MPI_Status status;
129+
int rank_source, rank_dest;
130+
static MPI_Datatype column, row;
131+
static int initialized = 0;
132+
133+
if ( !initialized ) {
134+
MPI_Type_vector(dsize[0]-2, 1, dsize[1], MPI_DOUBLE, &column);
135+
MPI_Type_commit(&column);
136+
MPI_Type_contiguous(dsize[1]-2, MPI_DOUBLE, &row);
137+
MPI_Type_commit(&row);
138+
initialized = 1;
139+
}
140+
141+
// send down
142+
MPI_Cart_shift(cart_comm, 0, 1, &rank_source, &rank_dest);
143+
MPI_Sendrecv(&cur[dsize[0]-2][1], 1, row, rank_dest, 100, // send row before ghost
144+
&cur[0][1], 1, row, rank_source, 100, // receive 1st row (ghost)
145+
cart_comm, &status);
146+
147+
// send up
148+
MPI_Cart_shift(cart_comm, 0, -1, &rank_source, &rank_dest);
149+
MPI_Sendrecv(&cur[1][1], 1, row, rank_dest, 100, // send column after ghost
150+
&cur[dsize[0]-1][1], 1, row, rank_source, 100, // receive last column (ghost)
151+
cart_comm, &status);
152+
153+
// send to the right
154+
MPI_Cart_shift(cart_comm, 1, 1, &rank_source, &rank_dest);
155+
MPI_Sendrecv(&cur[1][dsize[1]-2], 1, column, rank_dest, 100, // send column before ghost
156+
&cur[1][0], 1, column, rank_source, 100, // receive 1st column (ghost)
157+
cart_comm, &status);
158+
159+
// send to the left
160+
MPI_Cart_shift(cart_comm, 1, -1, &rank_source, &rank_dest);
161+
MPI_Sendrecv(&cur[1][1], 1, column, rank_dest, 100, // send column after ghost
162+
&cur[1][dsize[1]-1], 1, column, rank_source, 100, // receive last column (ghost)
163+
cart_comm, &status);
164+
}
165+
166+
int main( int argc, char* argv[] )
167+
{
168+
MPI_Init(&argc, &argv);
169+
srand( time( NULL ) );
170+
171+
int switch_iter_value[10] = {20, 35, 50, 55, 60, 35, 25, 20, 15, 60 };
172+
173+
// load the configuration tree
174+
PC_tree_t conf = PC_parse_path("ex12.yml");
175+
176+
// NEVER USE MPI_COMM_WORLD IN THE CODE, use our own communicator main_comm instead
177+
MPI_Comm main_comm = MPI_COMM_WORLD;
178+
179+
// initialize PDI, it can replace our main communicator by its own
180+
PDI_init(PC_get(conf, ".pdi"));
181+
182+
// load the MPI rank & size
183+
int psize_1d; MPI_Comm_size(main_comm, &psize_1d);
184+
int pcoord_1d; MPI_Comm_rank(main_comm, &pcoord_1d);
185+
186+
long longval;
187+
188+
// load the alpha parameter
189+
PC_double(PC_get(conf, ".alpha"), &alpha);
190+
191+
// load the global data-size
192+
int global_size[2];
193+
PC_int(PC_get(conf, ".global_size.height"), &longval); global_size[0] = longval;
194+
PC_int(PC_get(conf, ".global_size.width"), &longval); global_size[1] = longval;
195+
196+
// load the parallelism configuration
197+
PC_int(PC_get(conf, ".parallelism.height"), &longval); psize[0] = longval;
198+
PC_int(PC_get(conf, ".parallelism.width" ), &longval); psize[1] = longval;
199+
200+
// check the configuration is coherent
201+
assert(global_size[0]%psize[0]==0);
202+
assert(global_size[1]%psize[1]==0);
203+
assert(psize[1]*psize[0] == psize_1d);
204+
205+
// compute the local data-size with space for ghosts and boundary constants
206+
dsize[0] = global_size[0]/psize[0] + 2;
207+
dsize[1] = global_size[1]/psize[1] + 2;
208+
209+
// create a 2D Cartesian MPI communicator & get our coordinate (rank) in it
210+
int cart_period[2] = { 1, 1 };
211+
MPI_Comm cart_comm; MPI_Cart_create(main_comm, 2, psize, cart_period, 1, &cart_comm);
212+
MPI_Cart_coords(cart_comm, pcoord_1d, 2, pcoord);
213+
214+
// allocate memory for the double buffered data
215+
double(*cur)[dsize[1]] = malloc(sizeof(double)*dsize[1]*dsize[0]);
216+
double(*next)[dsize[1]] = malloc(sizeof(double)*dsize[1]*dsize[0]);
217+
218+
// initialize the value of switch for each iterations
219+
int switch_iter_values[10] = {20, 35, 50, 55, 60, 35, 25, 20, 15, 60 };
220+
221+
// open the file should_output.dat
222+
open_file();
223+
224+
// initialize the data content
225+
PDI_event("initialization");
226+
init(cur);
227+
228+
// our loop counter so as to be able to use it outside the loop
229+
int ii=0;
230+
// share useful configuration bits with PDI
231+
PDI_expose("pcoord", pcoord, PDI_OUT);
232+
PDI_expose("dsize", dsize, PDI_OUT);
233+
PDI_expose("psize", psize, PDI_OUT);
234+
235+
// value of switch inside the iteration loop
236+
int tmp_switch;
237+
// pointer to the value of should_output
238+
bool *should_output;
239+
240+
// get the initial value of should_output
241+
PDI_access("should_output", (void **)&should_output, PDI_IN);
242+
PDI_release("should_output");
243+
if(pcoord_1d == 0) printf("initial value: should_output = %d \n", *should_output);
244+
245+
// the main loop
246+
for (; ii<10; ++ii) {
247+
// set value of switch inside the iteration loop
248+
tmp_switch = switch_iter_value[ii];
249+
if(pcoord_1d == 0) printf("iter = %d, switch = %d\n", ii, tmp_switch);
250+
PDI_expose("switch", &tmp_switch, PDI_OUT);
251+
252+
// get should_output value and write to the file should_output.dat
253+
PDI_access("should_output", (void **)&should_output, PDI_IN);
254+
PDI_release("should_output");
255+
if(pcoord_1d == 0) printf("iter = %d, should_output = %d \n", ii, *should_output);
256+
if(pcoord_1d == 0) fprintf(pFile2, "%d\t%d\t%d\n", ii, tmp_switch, *should_output);
257+
258+
// share the loop counter & main field at each iteration
259+
PDI_multi_expose("loop",
260+
"ii", &ii, PDI_OUT,
261+
"main_field", cur, PDI_OUT,
262+
NULL);
263+
264+
// compute the values for the next iteration
265+
iter(cur, next);
266+
267+
// exchange data with the neighbours
268+
exchange(cart_comm, next);
269+
270+
// swap the current and next values
271+
double (*tmp)[dsize[1]] = cur; cur = next; next = tmp;
272+
}
273+
// finally share the main field as well as the loop counter after the loop
274+
PDI_multi_expose("finalization",
275+
"ii", &ii, PDI_OUT,
276+
"main_field", cur, PDI_OUT,
277+
NULL);
278+
279+
// close the file should_output.dat
280+
close_file();
281+
282+
// finalize PDI
283+
PDI_finalize();
284+
285+
// destroy the paraconf configuration tree
286+
PC_tree_destroy(&conf);
287+
288+
// free the allocated memory
289+
free(cur);
290+
free(next);
291+
292+
// finalize MPI
293+
MPI_Finalize();
294+
295+
fprintf(stderr, "[%d] SUCCESS\n", pcoord_1d);
296+
return EXIT_SUCCESS;
297+
}

0 commit comments

Comments
 (0)