1 /* Copyright 2018, UCAR/Unidata See COPYRIGHT file for copying and
2  * redistribution conditions.
3  *
4  * This program tests netcdf-4 parallel I/O.
5  *
6  * Ed Hartnett
7  */
8 
9 #include <nc_tests.h>
10 #include "err_macros.h"
11 #include <mpi.h>
12 
13 #define FILE "tst_parallel5.nc"
14 #define VAR_NAME "TheIrishRover"
15 #define DIM_NAME "number_of_masts"
16 #define MASTS 27
17 #define NDIMS1 1
18 #define DIMSIZE 4
19 #define NUM_PROC 4
20 #define NUM_SLABS 10
21 #define NUM_ACCESS_TESTS 2
22 
23 int
main(int argc,char ** argv)24 main(int argc, char **argv)
25 {
26     int mpi_size, mpi_rank;
27     MPI_Comm comm = MPI_COMM_WORLD;
28     MPI_Info info = MPI_INFO_NULL;
29     int ncid, v1id, dimid;
30     size_t start[NDIMS1] = {0}, count[NDIMS1] = {0};
31     int data = MASTS;
32     int data_in = TEST_VAL_42;
33     int acc;
34 
35     /* Initialize MPI. */
36     MPI_Init(&argc, &argv);
37     MPI_Comm_size(MPI_COMM_WORLD, &mpi_size);
38     MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
39 
40     /* Require exactly 4 tasks. */
41     if (mpi_size != NUM_PROC) ERR;
42 
43     if (!mpi_rank)
44         printf("\n*** Testing parallel I/O.\n");
45 
46     if (!mpi_rank)
47         printf("*** testing whether we can write 0 elements from some tasks...");
48     {
49         for (acc = 0; acc < NUM_ACCESS_TESTS; acc++)
50         {
51             /* Create a parallel netcdf-4 file. */
52             /*nc_set_log_level(3);*/
53             if (nc_create_par(FILE, NC_NETCDF4, comm, info, &ncid)) ERR;
54 
55             /* Create a dimension. */
56             if (nc_def_dim(ncid, DIM_NAME, DIMSIZE, &dimid)) ERR;
57 
58             /* Create one var. */
59             if (nc_def_var(ncid, VAR_NAME, NC_INT, NDIMS1, &dimid, &v1id)) ERR;
60 
61             /* Write metadata to file. */
62             if (nc_enddef(ncid)) ERR;
63 
64             /* Set up slab for this process. */
65             if (!mpi_rank)
66                 count[0] = 1;
67 
68             if (nc_var_par_access(ncid, v1id, acc ? NC_COLLECTIVE : NC_INDEPENDENT)) ERR;
69 
70             /* Write phoney data. */
71             if (nc_put_vara_int(ncid, v1id, start, count, &data)) ERR;
72 
73             if (nc_sync(ncid)) ERR;
74 
75             /* Read phoney data. */
76             if (nc_get_vara_int(ncid, v1id, start, count, &data_in)) ERR;
77 
78             /* Task 0 has MASTS, the others have data_in remaining, as
79              * initialized, at TEST_VAL_42. */
80             if (data_in != (mpi_rank ? TEST_VAL_42 : MASTS)) ERR;
81 
82             /* Close the netcdf file. */
83             if (nc_close(ncid)) ERR;
84         }
85     }
86     if (!mpi_rank)
87         SUMMARIZE_ERR;
88 
89     if (!mpi_rank)
90         printf("*** testing enum type and parallel I/O...");
91     {
92         for (acc = 0; acc < NUM_ACCESS_TESTS; acc++)
93         {
94 #define ENUM_NAME "cargo"
95 #define ENUM_VAR_NAME "in_the_hold_of_the_Irish_Rover"
96 #define NUM_ENUM_FIELDS 8
97             int typeid;
98             int f;
99             char field_name[NUM_ENUM_FIELDS][NC_MAX_NAME + 1] = {"bags of the best Sligo rags", "barrels of bones",
100                                                                  "bails of old nanny goats' tails", "barrels of stones",
101                                                                  "dogs", "hogs", "barrels of porter",
102                                                                  "sides of old blind horses hides"};
103             unsigned long long field_value[NUM_ENUM_FIELDS] = {1000000, 2000000, 3000000, 4000000,
104                                                                5000000, 6000000, 7000000, 8000000};
105             unsigned long long data = 1000000, data_in = TEST_VAL_42;
106 
107             /* Create a parallel netcdf-4 file. */
108             /*nc_set_log_level(3);*/
109             if (nc_create_par(FILE, NC_NETCDF4, comm, info, &ncid)) ERR;
110 
111             /* Create a dimension. */
112             if (nc_def_dim(ncid, DIM_NAME, DIMSIZE, &dimid)) ERR;
113 
114             /* Create an enum type. */
115             if (nc_def_enum(ncid, NC_UINT64, ENUM_NAME, &typeid)) ERR;
116             for (f = 0; f < NUM_ENUM_FIELDS; f++)
117                 if (nc_insert_enum(ncid, typeid, field_name[f], &field_value[f])) ERR;
118 
119             /* Create one var. */
120             if (nc_def_var(ncid, ENUM_VAR_NAME, typeid, NDIMS1, &dimid, &v1id)) ERR;
121 
122             /* Write metadata to file. */
123             if (nc_enddef(ncid)) ERR;
124 
125             /* Set up slab for this process. */
126             if (!mpi_rank)
127                 count[0] = 1;
128 
129             if (nc_var_par_access(ncid, v1id, acc ? NC_COLLECTIVE : NC_INDEPENDENT)) ERR;
130 
131             /* Write phoney data. */
132             if (nc_put_vara(ncid, v1id, start, count, &data)) ERR;
133 
134             if (nc_sync(ncid)) ERR;
135 
136             /* Read phoney data. */
137             if (nc_get_vara(ncid, v1id, start, count, &data_in)) ERR;
138 
139             /* Task 0 has 1000000, the others have data_in remaining, as
140              * initialized, at TEST_VAL_42. */
141             if (data_in != (mpi_rank ? TEST_VAL_42 : 1000000)) ERR;
142 
143             /* Close the netcdf file. */
144             if (nc_close(ncid)) ERR;
145         }
146     }
147     if (!mpi_rank)
148         SUMMARIZE_ERR;
149     if (!mpi_rank)
150         printf("*** testing compound type and parallel I/O...");
151     {
152         for (acc = 0; acc < NUM_ACCESS_TESTS; acc++)
153         {
154 #define COMPOUND_NAME "crew_info"
155 #define COMPOUND_VAR_NAME "whale_of_a_crew"
156 #define NUM_CREW 5
157 #define CREW_DIM_NAME "number_of_crew"
158             int typeid;
159             struct crew
160             {
161                 char name[NC_MAX_NAME + 1];
162                 char description[NC_MAX_NAME + 1];
163                 char origin[NC_MAX_NAME + 1];
164                 int age;
165             };
166             struct crew data = {"Mick McCann", "the skipper of the Irish Rover",
167                                 "from the banks of the Bann", 42};
168             struct crew data_in = {"", "", "", -42};
169             int dim_size = NC_MAX_NAME + 1;
170 
171             /* Create a parallel netcdf-4 file. */
172             /*nc_set_log_level(3);*/
173             if (nc_create_par(FILE, NC_NETCDF4, comm, info, &ncid)) ERR;
174 
175             /* Create a dimension. */
176             if (nc_def_dim(ncid, CREW_DIM_NAME, NUM_CREW, &dimid)) ERR;
177 
178             /* Create a compound type. */
179             if (nc_def_compound(ncid, sizeof(struct crew), COMPOUND_NAME, &typeid)) ERR;
180             if (nc_insert_array_compound(ncid, typeid, "name", NC_COMPOUND_OFFSET(struct crew, name), NC_CHAR, 1, &dim_size)) ERR;
181             if (nc_insert_array_compound(ncid, typeid, "description", NC_COMPOUND_OFFSET(struct crew, description), NC_CHAR, 1, &dim_size)) ERR;
182             if (nc_insert_array_compound(ncid, typeid, "origin", NC_COMPOUND_OFFSET(struct crew, origin), NC_CHAR, 1, &dim_size)) ERR;
183             if (nc_insert_compound(ncid, typeid, "age", NC_COMPOUND_OFFSET(struct crew, age), NC_INT)) ERR;
184 
185             /* Create one var. */
186             if (nc_def_var(ncid, COMPOUND_VAR_NAME, typeid, NDIMS1, &dimid, &v1id)) ERR;
187 
188             /* Write metadata to file. */
189             if (nc_enddef(ncid)) ERR;
190 
191             /* Set up slab for this process. */
192             if (!mpi_rank)
193                 count[0] = 1;
194 
195             if (nc_var_par_access(ncid, v1id, acc ? NC_COLLECTIVE : NC_INDEPENDENT)) ERR;
196 
197             /* Write phoney data. */
198             if (nc_put_vara(ncid, v1id, start, count, &data)) ERR;
199 
200             if (nc_sync(ncid)) ERR;
201 
202             /* Read phoney data. */
203             if (nc_get_vara(ncid, v1id, start, count, &data_in)) ERR;
204 
205             /* Task 0 has data, the others have nothing. */
206             if (!mpi_rank)
207             {
208                 if (strcmp(data_in.name, data.name) || strcmp(data_in.description, data.description) ||
209                     strcmp(data_in.origin, data.origin) || data_in.age != data.age) ERR;
210             }
211             else
212             {
213                 if (strcmp(data_in.name, "") || strcmp(data_in.description, "") ||
214                     strcmp(data_in.origin, "") || data_in.age != -42) ERR;
215             }
216 
217             /* Close the netcdf file. */
218             if (nc_close(ncid)) ERR;
219         }
220     }
221     if (!mpi_rank)
222         SUMMARIZE_ERR;
223     if (!mpi_rank)
224         printf("*** testing string type and parallel I/O...");
225     {
226         for (acc = 0; acc < NUM_ACCESS_TESTS; acc++)
227         {
228 #define STORY_VAR_NAME "fate_of_the_Irish_Rover"
229 #define STORY_DIM_NAME "number_of_lines"
230 #define STORY_LEN 8
231             char *story[STORY_LEN] = {"We had sailed seven years when the measles broke out",
232                                       "And the ship lost it's way in the fog",
233                                       "And that whale of the crew was reduced down to two",
234                                       "Just myself and the captain's old dog",
235                                       "Then the ship struck a rock, oh Lord what a shock",
236                                       "The bulkhead was turned right over",
237                                       "Turned nine times around, and the poor dog was drowned",
238                                       "I'm the last of the Irish Rover"};
239             char *story_in[STORY_LEN];
240             int s;
241 
242             /* Create a netcdf-4 file. Turns out that HDF5 does not
243              * support VLEN writes with parallel I/O. Strings are
244              * VLENS. So here I write a file with task 0 and then read it
245              * with all tasks. */
246             if (!mpi_rank)
247             {
248                 if (nc_create(FILE, NC_NETCDF4, &ncid)) ERR;
249 
250                 /* Create a dimension. */
251                 if (nc_def_dim(ncid, STORY_DIM_NAME, STORY_LEN, &dimid)) ERR;
252 
253                 /* Create one var. */
254                 if (nc_def_var(ncid, STORY_VAR_NAME, NC_STRING, NDIMS1, &dimid, &v1id)) ERR;
255 
256                 /* Write metadata to file. */
257                 if (nc_enddef(ncid)) ERR;
258 
259                 /* Set up slab for this process. */
260                 count[0] = STORY_LEN;
261 
262                 /* Write phoney data. */
263                 if (nc_put_vara(ncid, v1id, start, count, story)) ERR;
264 
265                 /* Close the netcdf file. */
266                 if (nc_close(ncid)) ERR;
267             }
268 
269             /* Now try parallel read. */
270             if (nc_open_par(FILE, 0, comm, info, &ncid)) ERR;
271 
272             /* Task 0 reads all 8 lines, other tasks read 0. */
273             if (nc_get_vara(ncid, v1id, start, count, story_in)) ERR;
274 
275             if (!mpi_rank)
276             {
277                 for (s = 0; s < STORY_LEN; s++)
278                     if (strcmp(story_in[s], story[s])) ERR;
279                 if (nc_free_string(STORY_LEN, (char **)story_in)) ERR;
280             }
281 
282             /* Close the netcdf file. */
283             if (nc_close(ncid)) ERR;
284         }
285     }
286     if (!mpi_rank)
287         SUMMARIZE_ERR;
288 
289     if (!mpi_rank)
290         printf("*** testing NC_BYTE type and parallel I/O...");
291     {
292         /* This test is related to
293          * https://github.com/Unidata/netcdf-c/issues/1462. */
294         int ncid, varid;
295         signed char test_data_in, test_data = 42;
296 
297         /* Crate a file with a scalar NC_BYTE value. */
298         if (nc_create_par(FILE, NC_NETCDF4, MPI_COMM_WORLD, MPI_INFO_NULL,
299                           &ncid)) ERR;
300         if (nc_def_var(ncid, "fred", NC_BYTE, 0, NULL, &varid)) ERR;
301         if (nc_enddef(ncid)) ERR;
302         if (nc_put_var_schar(ncid, varid, &test_data)) ERR;
303         if (nc_close(ncid)) ERR;
304 
305         /* Reopen the file and check. */
306         if (nc_open_par(FILE, 0, comm, info, &ncid)) ERR;
307         if (nc_get_var_schar(ncid, varid, &test_data_in)) ERR;
308         if (test_data_in != test_data) ERR;
309         if (nc_close(ncid)) ERR;
310     }
311     if (!mpi_rank)
312         SUMMARIZE_ERR;
313 #ifdef USE_SZIP
314 #ifdef HDF5_SUPPORTS_PAR_FILTERS
315 #define SZIP_DIM_LEN 256
316 #define SZIP_DIM_NAME "Barrels"
317 #define SZIP_VAR_NAME "Best_Sligo_Rags"
318 #define SZIP_PIXELS_PER_BLOCK 32
319     if (!mpi_rank)
320         printf("*** testing szip compression with parallel I/O...");
321     {
322         int ncid, dimid, varid;
323         float *data;
324         float *data_in;
325         int elements_per_pe = SZIP_DIM_LEN/mpi_size;
326         size_t start[NDIMS1], count[NDIMS1];
327         int i;
328 
329         /* Create test data. */
330         if (!(data = malloc(elements_per_pe * sizeof(float)))) ERR;
331         for (i = 0; i < elements_per_pe; i++)
332             data[i] = mpi_rank + i * 0.1;
333 
334         /* Crate a file with a scalar NC_BYTE value. */
335         if (nc_create_par(FILE, NC_NETCDF4, MPI_COMM_WORLD, MPI_INFO_NULL,
336                           &ncid)) ERR;
337         if (nc_def_dim(ncid, SZIP_DIM_NAME, SZIP_DIM_LEN, &dimid)) ERR;
338         if (nc_def_var(ncid, SZIP_VAR_NAME, NC_FLOAT, NDIMS1, &dimid, &varid)) ERR;
339         if (nc_def_var_szip(ncid, varid, H5_SZIP_NN_OPTION_MASK,
340                             SZIP_PIXELS_PER_BLOCK)) ERR;
341         if (nc_enddef(ncid)) ERR;
342         start[0] = mpi_rank * elements_per_pe;
343         count[0] = elements_per_pe;
344         if (nc_put_vara_float(ncid, varid, start, count, data)) ERR;
345         if (nc_close(ncid)) ERR;
346 
347         /* Reopen the file and check. */
348         if (nc_open_par(FILE, 0, comm, info, &ncid)) ERR;
349         if (!(data_in = malloc(elements_per_pe * sizeof(float)))) ERR;
350         if (nc_get_vara_float(ncid, varid, start, count, data_in)) ERR;
351         if (nc_close(ncid)) ERR;
352         for (i = 0; i < elements_per_pe; i++)
353             if (data_in[i] != data[i]) ERR;
354 
355         /* Release resources. */
356         free(data_in);
357         free(data);
358     }
359     if (!mpi_rank)
360         SUMMARIZE_ERR;
361 #endif /* HDF5_SUPPORTS_PAR_FILTERS */
362 #endif /* USE_SZIP */
363 
364     /* Shut down MPI. */
365     MPI_Finalize();
366 
367     if (!mpi_rank)
368         FINAL_RESULTS;
369 
370     return 0;
371 }
372