Skip to content
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

fix #24, update ex3 to ex5 (hdf5) #27

Merged
merged 9 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 125 additions & 68 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,123 +136,180 @@ Here, the %PDI \ref trace_plugin "Trace plugin" is used to trace %PDI calls.
Notice that some share/reclaim pairs come one after the other while others are
interlaced.
Is one better than the other?
If you do not know the answer to this question, just wait until Ex5. :)
If you do not know the answer to this question, just wait until ex5. :)


## Decl'HDF5 plugin

From exercise 3 to exercise 9 included, we present the \ref Decl_HDF5_plugin
"Decl'HDF5 plugin" (`decl_hdf5`). We will introduce some keywords (`when`,
`datasets`, ...) in the sub-tree of `decl_hdf5` in configuration YAML file.

All keywords are defined in the last section **full configuration example** of
\ref Decl_HDF5_plugin "Decl'HDF5 plugin".

### Ex3. HDF5 through PDI

In this exercise, the code is the same as in ex2.
No need to touch the C code here, modification of the YAML file (`ex3.yml`)
should be enough.
In this exercise, the C code is the same as in ex2. You only need to modify the
YAML file (`ex3.yml`).

* Examine the YAML file, compile the code and run it.

The \ref trace_plugin "Trace plugin" (`trace`) was replaced by the
\ref Decl_HDF5_plugin "Decl'HDF5 plugin" (`decl_hdf5`) in the specification
tree.
In its configuration, the `dsize` variable is written.
The \ref Decl_HDF5_plugin "Decl'HDF5 plugin" (`decl_hdf5`) is added in the
specification tree.
In its configuration, the `dsize` variable is defined as a metadata for %PDI.

* Write the `psize` and `pcoord` variables in addition to `dsize` to match the
content expected as described in the `ex3.h5dump` text file (use the `h5dump`
command to see the content of your HDF5 output file in the same format as the
`.h5dump` file). You can easily check if the files are the same by running the
command:
```bash
diff ex3.h5dump <(h5dump ex3*.h5)
```
* Write the `psize` and `pcoord` variables in addition to `dsize` in a file `ex3.h5`
with one MPI process.
To achieve this result, you will need to fill 2 sections in the YAML file:

To achieve this result, you will need to fill 2 sections in the YAML file.
1. The `data` section to indicate to %PDI the \ref datatype_node "type" of the
fields that are exposed.

1. The `data` section to indicate to %PDI the \ref datatype_node type of the
fields that are exposed.
2. The `decl_hdf5` section for the configuration of the \ref Decl_HDF5_plugin
"Decl'HDF5 plugin".

2. The `decl_hdf5` section for the configuration of the
\ref Decl_HDF5_plugin "Decl'HDF5 plugin".
* Use the `h5dump` command to see the content of your HDF5 output file in the
same format as the h5dump file `ex3.h5dump`. You can easily check if the files
are the same by running:
```bash
diff ex3.h5dump <(h5dump ex3*.h5)
```
To see your `h5` file in readable file format, you can check the section
[Comparison with the `h5dump` command](#h5comparison).

\warning
If you relaunch the executable, remember to delete your old `ex3.h5` file
before, otherwise the data will not be changed.
If you relaunch the executable, remember to delete your old `ex3.h5` file before,
otherwise the data will not be changed.

\warning
Since we write to the same location independently of the MPI rank, this exercise
will fail if more than one MPI rank is used.

When more than one MPI rank is used, we write to the same location in the file
independently of the MPI rank. For this reason, this exercise will fail. The next
exercise solves this issue.

### Ex4. Writing some real data

In this exercise each MPI process will write its local 2D array block contained
in the `main_field` variable to a separate HDF5 file.
Once again, this can be done by modifying the YAML file only, no nee to touch
Once again, this can be done by modifying the YAML file only, no need to touch
the C file.

* Examine the YAML file, compile the code and run it.

Look at the number of blocks, you will have to use the correct number of MPI
ranks to run the example.

Notice that in the YAML file, a list was used in the `decl_hdf5` section with
multiple write blocks instead of a single one as before in order to write to
multiple files.
\note Notice that in the YAML file `ex4.yml`, a list was used in the `decl_hdf5`
section with multiple write blocks instead of a single one as before in order
to write to multiple files.

\note Notice that we have moved fields (`dsize`, `psize` and `pcoord`) in the
`metadata` section.
```yaml
pdi:
metadata: # small values for which PDI keeps a copy
#*** add ii as metadata
#...
dsize: { type: array, subtype: int, size: 2 }
```
You can reference them from dynamic "$-expressions" in the configuration file.

Also notice that this example now runs in parallel with two processes.
Therefore it uses "$-expressions" to specify the file names and ensure we
do not write to the same file from distinct ranks.
\remark Also notice that this example now runs in parallel with 4 processes.
To ensure we do not write to the same file, we need to specify the file name
using "$-expressions" for the different process rank.

Unlike the other fields manipulated until now, the type of `main_field` is not
fully known, its size is dynamic.
By moving other fields in the `metadata` section, you can reference them from
"$-expressions" in the configuration file.
fully known, its size is dynamic. Therefore, we need to define the size in YAML
file for %PDI using "$-expressions".

* Use a $-expression to specify the size of `main_field`.
* Describe the temperature data on the current iteration by using a $-expression
to specify the size of `main_field` in `data` section.

Unlike the other fields manipulated until now, `main_field` is exposed multiple
Unlike the other fields manipulated until now, `main_field` is exposed multiple
times along execution.
In order not to overwrite it every time it is exposed, you need to add a `when`
condition to restrict its output.
In order not to overwrite it every time it is exposed, you propose to write one
file per rank only at the first iteration (`ii=1`) with the directive `when`.

* Add the iteration loop `ii` as a metadata.
* Write the curent temperature field in one file per process at first iteration.

* Only write `main_field` at the second iteration (when `ii=1`) and match the
expected content as described in `ex4.h5dump`.
You should be able to match the expected output described in `ex4.h5dump`.
You can easily check if the files are the same by running:
```bash
diff ex4.h5dump <(h5dump ex4*.h5)
diff ex4.h5dump <(h5dump ex4-data-*.h5)
```
To see your `h5` file in readable file format, you can check the section
[Comparison with the `h5dump` command](#h5comparison).

### Ex5. Introducing events
\note A definition of `metadata` and `data` can be:

In ex4., two variables were written to `ex4-data*.h5`, but the file was opened
- `metadata`: small values for which PDI keeps a copy. These value can be referenced
by using "$-expressions" in the configuration YAML file.

- `data` : values for which PDI does not keep a copy.

### Ex5. Introducing events and group of dataset

This exercise is done sequentially to facilitate the comparison between logs.

#### Ex 5.1 PDI event and on_event

In ex4, two variables were written to `ex4-data-*.h5`, but the files were opened
and closed for each and every write.

Since Decl'HDF5 only sees the data appear one after the other, it does not keep
the file open.
Since `ii` and `main_field` are shared in an interlaced way, they are both
available to %PDI at the same time and could be written without opening the file
twice.
You have to use events for that, you will modify both the C and YAML file.
the file open. Since `ii` and `main_field` are shared in an interlaced way, they
are both available to %PDI at the same time and could be written without opening
the file twice.
You have to use events for that, you will modify both the C and YAML file in this
exercise.

* Examine the YAML file and source code.

* In the C file, trigger a %PDI event named `loop` when both `ii` and
* In the C file, add a %PDI event named `loop` when both `ii` and
`main_field` are shared.
With the \ref trace_plugin "Trace plugin", check that the event is indeed
triggered at the expected time as described in `ex5.log` (only the lines
matching `[Trace-plugin]` have been kept). You can check if the files are the
same by running:

With the \ref trace_plugin "Trace plugin", check that the event is indeed
triggered at the expected time as described in `ex5.log` (only the lines
matching `[Trace-plugin]` have been kept).
Using the previous section [Execution with storage of the log](#execution-with-storage-of-the-log),
run this exercise in saving the output log in the `ex5.result.log`.
After that you can easily check if the files are the same by running:
```bash
diff ex5.log <(grep Trace-plugin ex5.result.log)
```
* In the YAML file, use the `on_event` mechanism to trigger the write of `ii` and
`main_field` for event `loop` only. This mechanism can be combined with a `when`
directive, in that case the write is only executed when both mechanisms agree.
* Add the `when` directive to write only at iteration 1 and 2. Use the symbol `&`
which corresponds to the logical operation `and`.

#### Ex 5.2 group of dataset
In ex4, the name of the datasets of `.h5` file are `ii` and `main_field`(see ex4.h5dump).
Using the keyword `dataset`, it is possible to have a different name from the
%PDI variable name.

The name of the dataset is given after the definition of the data
```yaml
write:
ii: # name of the PDI data to write
dataset: 'new_name'
```
Using this mechanism, it is possible to define a group object of hdf5 see
https://support.hdfgroup.org/documentation/hdf5/latest/_h5_g__u_g.html.
If you want to add a dataset `my_data` in the sub-group `groupA` of the group
`my_group`, the name of the dataset will be:
```yaml
dataset: 'my_group/groupA/my_data'.
```
where the symbol "/" is used to separate groups in path.

* Use the `on_event` mechanism to trigger the write of `ii` and `main_field`.
This mechanism can be combined with a `when` directive, in that case the
write is only executed when both mechanisms agree.
* Change the YAML file to write `main_field` and `ii` at iterations 1 and 2,
in two distinct groups `iter1` and `iter2`.

* Also notice the extended syntax that make it possible to write data to a
dataset whose name differs from the %PDI variable name.
Use this mechanism to write `main_field` at iterations 1 and 2, in two
distinct groups `iter1` and `iter2`.
Your output should match the content described in `ex5.h5dump`.
To match the expected output described in `ex5.h5dump`. You can easily check
if the files are the same by running:
```bash
diff ex5.h5dump <(h5dump ex5*.h5)
diff ex5.h5dump <(h5dump ex5-data-*.h5)
```
To see your `h5` file in readable file format, you can check the section
[Comparison with the `h5dump` command](#h5comparison).


### Ex6. Simplifying the code
Expand Down
60 changes: 34 additions & 26 deletions ex3.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,32 @@ int pcoord[2];
/// the alpha coefficient used in the computation
double alpha;

double L=1.0;
double source1[4]={0.4, 0.4, 0.2, 100};
double source2[4]={0.7, 0.8, 0.1, 200};

/** Initialize the data all to 0 except for the left border (XX==0) initialized to 1 million
* \param[out] dat the local data to initialize
*/
void init(double dat[dsize[0]][dsize[1]])
{
for (int yy=0; yy<dsize[0]; ++yy) for (int xx=0; xx<dsize[1]; ++xx) dat[yy][xx] = 0;
if ( pcoord[1] == 0 ) for (int yy=0; yy<dsize[0]; ++yy) dat[yy][0] = 1000000;
double dy = L / ((dsize[0]-2) *psize[0]) ;
double dx = L / ((dsize[1]-2) *psize[1]) ;

double cpos_x,cpos_y;
for(int yy=0; yy<dsize[0];++yy) {
cpos_y=(yy+pcoord[0]*(dsize[0]-2))*dy-0.5*dy;
for(int xx=0; xx<dsize[1];++xx) {
cpos_x=(xx+pcoord[1]*(dsize[1]-2))*dx-0.5*dx;
if((cpos_y-source1[0])*(cpos_y-source1[0]) + (cpos_x-source1[1])*(cpos_x-source1[1]) <= source1[2]*source1[2]) {
dat[yy][xx] = source1[3];
}
if((cpos_y-source2[0])*(cpos_y-source2[0]) + (cpos_x-source2[1])*(cpos_x-source2[1]) <= source2[2]*source2[2]) {
dat[yy][xx] = source2[3];
}
}
}
}

/** Compute the values at the next time-step based on the values at the current time-step
Expand All @@ -60,21 +79,15 @@ void init(double dat[dsize[0]][dsize[1]])
void iter(double cur[dsize[0]][dsize[1]], double next[dsize[0]][dsize[1]])
{
int xx, yy;
for (xx=0; xx<dsize[1]; ++xx) next[0][xx] = cur[0][xx];
for (yy=1; yy<dsize[0]-1; ++yy) {
next[yy][0] = cur[yy][0];
for (xx=1; xx<dsize[1]-1; ++xx) {
next[yy][xx] =
(1.-4.*alpha) * cur[yy][xx]
+ alpha * ( cur[yy][xx-1]
+ cur[yy][xx+1]
+ cur[yy-1][xx]
+ cur[yy+1][xx]
);
next[yy][xx] = (1.-4.*alpha) * cur[yy][xx]
+alpha * ( cur[yy][xx-1]
+ cur[yy][xx+1]
+ cur[yy-1][xx]
+ cur[yy+1][xx]);
}
next[yy][dsize[1]-1] = cur[yy][dsize[1]-1];
}
for (xx=0; xx<dsize[1]; ++xx) next[dsize[0]-1][xx] = cur[dsize[0]-1][xx];
}

/** Exchanges ghost values with neighbours
Expand Down Expand Up @@ -104,8 +117,8 @@ void exchange(MPI_Comm cart_comm, double cur[dsize[0]][dsize[1]])

// send up
MPI_Cart_shift(cart_comm, 0, -1, &rank_source, &rank_dest);
MPI_Sendrecv(&cur[1][1], 1, row, rank_dest, 100, // send column after ghost
&cur[dsize[0]-1][1], 1, row, rank_source, 100, // receive last column (ghost)
MPI_Sendrecv(&cur[1][1], 1, row, rank_dest, 100, // send row after ghost
&cur[dsize[0]-1][1], 1, row, rank_source, 100, // receive last row (ghost)
cart_comm, &status);

// send to the right
Expand All @@ -116,7 +129,7 @@ void exchange(MPI_Comm cart_comm, double cur[dsize[0]][dsize[1]])

// send to the left
MPI_Cart_shift(cart_comm, 1, -1, &rank_source, &rank_dest);
MPI_Sendrecv(&cur[1][1], 1, column, rank_dest, 100, // send column after ghost
MPI_Sendrecv(&cur[1][1], 1, column, rank_dest, 100, // send column after ghost
&cur[1][dsize[1]-1], 1, column, rank_source, 100, // receive last column (ghost)
cart_comm, &status);
}
Expand Down Expand Up @@ -162,7 +175,7 @@ int main( int argc, char* argv[] )
dsize[1] = global_size[1]/psize[1] + 2;

// create a 2D Cartesian MPI communicator & get our coordinate (rank) in it
int cart_period[2] = { 0, 0 };
int cart_period[2] = { 1, 1 };
MPI_Comm cart_comm; MPI_Cart_create(main_comm, 2, psize, cart_period, 1, &cart_comm);
MPI_Cart_coords(cart_comm, pcoord_1d, 2, pcoord);

Expand All @@ -177,24 +190,20 @@ int main( int argc, char* argv[] )
int ii=0;

// share useful configuration bits with PDI
PDI_share("ii", &ii, PDI_OUT);
PDI_reclaim("ii");
PDI_share("pcoord", pcoord, PDI_OUT);
PDI_reclaim("pcoord");
PDI_share("dsize", dsize, PDI_OUT);
PDI_reclaim("dsize");
PDI_share("psize", psize, PDI_OUT);
PDI_reclaim("psize");
PDI_share("main_field", cur, PDI_OUT);
PDI_reclaim("main_field");

// the main loop
for (; ii<10; ++ii) {
// share the loop counter & main field at each iteration
PDI_share("ii", &ii, PDI_OUT);
PDI_share("ii", &ii, PDI_OUT);
PDI_reclaim("ii");
PDI_share("main_field", cur, PDI_OUT);
PDI_reclaim("main_field");
PDI_reclaim("ii");

// compute the values for the next iteration
iter(cur, next);
Expand All @@ -205,11 +214,10 @@ int main( int argc, char* argv[] )
// swap the current and next values
double (*tmp)[dsize[1]] = cur; cur = next; next = tmp;
}
// finally share the main field after the main loop body
PDI_share("main_field", cur, PDI_OUT);
// as well as the loop counter
PDI_share("ii", &ii, PDI_OUT);
// finally share the loop counter and main field after the main loop body
PDI_share("ii", &ii, PDI_OUT);
PDI_reclaim("ii");
PDI_share("main_field", cur, PDI_OUT);
PDI_reclaim("main_field");

// finalize PDI
Expand Down
Loading