YetAnotherCoupler 3.5.2
Loading...
Searching...
No Matches
io_utils.c
Go to the documentation of this file.
1// Copyright (c) 2024 The YAC Authors
2//
3// SPDX-License-Identifier: BSD-3-Clause
4
5#include <string.h>
6#include "yac_mpi_internal.h"
7#include "io_utils.h"
8#include "math.h"
9
10#define IO_RANK_LIST_STR "YAC_IO_RANK_LIST"
11#define IO_MAX_NUM_RANKS_STR "YAC_IO_MAX_NUM_RANKS"
12#define IO_RANK_EXCLUDE_LIST_STR "YAC_IO_RANK_EXCLUDE_LIST"
13#define IO_MAX_NUM_RANKS_PER_NODE "YAC_IO_MAX_NUM_RANKS_PER_NODE"
14#define DEFAULT_MAX_NUM_IO_RANK_PER_NODE (1);
15
16static inline int compare_int(const void * a, const void * b) {
17
18 int const * a_ = a, * b_ = b;
19
20 return (*a_ > *b_) - (*b_ > *a_);
21}
22
23static void read_rank_list(
24 char const * env_name, MPI_Comm comm, size_t * num_ranks_, int ** ranks_) {
25
26 int rank;
27 yac_mpi_call(MPI_Comm_rank(comm, &rank), comm);
28
29 size_t num_ranks = 0;
30 int * ranks = NULL;
31
32 // environment is only checked on rank 0, results are broadcasted
33 // to other processes
34 if (rank == 0) {
35
36 // check whether the user provided a list of ranks for IO
37 char * rank_list_str = getenv(env_name);
38 if ((rank_list_str != NULL) && (rank_list_str[0] != '\0')) {
39
40 int temp_num_ranks = 1;
41 for (char * curr_char = rank_list_str + 1; *curr_char != '\0'; ++curr_char)
42 if (*curr_char == ',') ++temp_num_ranks;
43
44 char * rank_list_copy = strdup(rank_list_str);
45 num_ranks = 0;
46 int * world_ranks = xmalloc(temp_num_ranks * sizeof(*world_ranks));
47 int world_size;
48 yac_mpi_call(MPI_Comm_size(MPI_COMM_WORLD, &world_size), MPI_COMM_WORLD);
49
50 // parse rank list
51 char * rank_str = strtok(rank_list_copy, ",");
52 while (rank_str != NULL) {
53 int curr_rank = atoi(rank_str);
55 curr_rank >= 0, "ERROR(read_rank_list): \"%s\" is not a valid rank",
56 rank_str);
58 curr_rank < world_size,
59 "ERROR(read_rank_list): rank %d exceeds size of MPI_COMM_WORLD (%d)",
60 curr_rank, world_size);
61 world_ranks[num_ranks++] = curr_rank;
62 rank_str = strtok(NULL, ",");
63 }
64 free(rank_list_copy);
65
66 // sort ranks
67 qsort(world_ranks, num_ranks, sizeof(*world_ranks), compare_int);
68
69 // remove duplicated ranks
70 yac_remove_duplicates_int(world_ranks, &num_ranks);
71
72 // translate rank list from MPI_COMM_WORLD to comm
73 ranks = xmalloc(num_ranks * sizeof(ranks));
74 MPI_Group world_group, comm_group;
76 MPI_Comm_group(MPI_COMM_WORLD, &world_group), MPI_COMM_WORLD);
77 yac_mpi_call(MPI_Comm_group(comm, &comm_group), comm);
79 MPI_Group_translate_ranks(
80 world_group, (int)num_ranks, world_ranks,
81 comm_group, ranks), MPI_COMM_WORLD);
82 yac_mpi_call(MPI_Group_free(&comm_group), comm);
83 yac_mpi_call(MPI_Group_free(&world_group), MPI_COMM_WORLD);
84 free(world_ranks);
85
86 // remove ranks, which are not avaible in comm
87 size_t new_num_ranks = 0;
88 for (size_t i = 0; i < num_ranks; ++i) {
89 if (ranks[i] != MPI_UNDEFINED) {
90 if (i != new_num_ranks)
91 ranks[new_num_ranks] = ranks[i];
92 ++new_num_ranks;
93 }
94 }
95 num_ranks = new_num_ranks;
96
97 // sort ranks
98 qsort(ranks, num_ranks, sizeof(*ranks), compare_int);
99 }
100
101 // broadcast ranks
103 MPI_Bcast(&num_ranks, 1, YAC_MPI_SIZE_T, 0, comm), comm);
104 if (num_ranks > 0) {
105 ranks = xrealloc(ranks, num_ranks * sizeof(*ranks));
107 MPI_Bcast(ranks, (int)num_ranks, MPI_INT, 0, comm), comm);
108 } else {
109 free(ranks);
110 }
111 } else {
112
113 // receive ranks from root
115 MPI_Bcast(&num_ranks, 1, YAC_MPI_SIZE_T, 0, comm), comm);
116 if (num_ranks > 0) {
117 ranks = xmalloc(num_ranks * sizeof(*ranks));
119 MPI_Bcast(ranks, (int)num_ranks, MPI_INT, 0, comm), comm);
120 }
121 }
122 *num_ranks_ = num_ranks;
123 *ranks_ = ranks;
124}
125
127 MPI_Comm comm, size_t * num_io_ranks, int ** io_ranks) {
128
129 read_rank_list(IO_RANK_LIST_STR, comm, num_io_ranks, io_ranks);
130}
131
133 MPI_Comm comm, size_t * num_io_ranks_, int ** io_ranks_) {
134
135 int rank, size;
136 yac_mpi_call(MPI_Comm_rank(comm, &rank), comm);
137 yac_mpi_call(MPI_Comm_size(comm, &size), comm);
138
139 int max_num_io_rank_per_node;
140
141 // environment is only checked on rank 0, results are broadcasted
142 // to other processes
143 if (rank == 0) {
144
145 // check whether the user provided a maximum number of ranks per node
146 char * max_num_io_rank_per_node_str = getenv(IO_MAX_NUM_RANKS_PER_NODE);
147 if ((max_num_io_rank_per_node_str != NULL) &&
148 (max_num_io_rank_per_node_str[0] != '\0')) {
149
150 max_num_io_rank_per_node = atoi(max_num_io_rank_per_node_str);
152 (max_num_io_rank_per_node > 0) || (max_num_io_rank_per_node == -1),
153 "ERROR(check_io_max_num_ranks_per_node): "
154 "\"%s\" is not a valid value for the maximum number of io ranks "
155 "per node", max_num_io_rank_per_node_str);
156 } else {
157 max_num_io_rank_per_node = DEFAULT_MAX_NUM_IO_RANK_PER_NODE;
158 }
160 MPI_Bcast(&max_num_io_rank_per_node, 1, MPI_INT, 0, comm), comm);
161 } else {
163 MPI_Bcast(&max_num_io_rank_per_node, 1, MPI_INT, 0, comm), comm);
164 }
165
166 // if there is no limit on the number of io ranks per node
167 if (max_num_io_rank_per_node == -1) return;
168
169 // create one communicator per node
170 MPI_Comm node_comm;
172 MPI_Comm_split_type(
173 comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &node_comm), comm);
174
175 // determine rank within node comm
176 int node_rank;
177 yac_mpi_call(MPI_Comm_rank(node_comm, &node_rank), node_comm);
178
179 // translate io ranks to node ranks
180 int * node_ranks = xmalloc(*num_io_ranks_ * sizeof(*node_ranks));
181 MPI_Group io_group, node_group;
182 yac_mpi_call(MPI_Comm_group(comm, &io_group), comm);
183 yac_mpi_call(MPI_Comm_group(node_comm, &node_group), node_comm);
185 MPI_Group_translate_ranks(
186 io_group, (int)*num_io_ranks_, *io_ranks_,
187 node_group, node_ranks), comm);
188 yac_mpi_call(MPI_Group_free(&node_group), node_comm);
189 yac_mpi_call(MPI_Group_free(&io_group), comm);
190 yac_mpi_call(MPI_Comm_free(&node_comm), comm);
191
192 // remove MPI_UNDEFINED entries
193 size_t num_node_io_ranks = 0;
194 for (size_t i = 0; i < *num_io_ranks_; ++i) {
195 if (node_ranks[i] != MPI_UNDEFINED) {
196 if (i != num_node_io_ranks)
197 node_ranks[num_node_io_ranks] = node_ranks[i];
198 ++num_node_io_ranks;
199 }
200 }
201
202 // sort ranks
203 qsort(node_ranks, num_node_io_ranks, sizeof(*node_ranks), compare_int);
204
205 // check whether local process is within the first n io ranks on its node
206 int local_is_io_rank = 0;
207 for (size_t i = 0;
208 (i < num_node_io_ranks) &&
209 (i < (size_t)max_num_io_rank_per_node) && !local_is_io_rank; ++i)
210 if (node_ranks[i] == node_rank) local_is_io_rank = 1;
211 free(node_ranks);
212
213 // determine io ranks on all nodes
214 int * is_io_rank = xmalloc((size_t)size * sizeof(*is_io_rank));
216 MPI_Allgather(
217 &local_is_io_rank, 1, MPI_INT, is_io_rank, 1, MPI_INT, comm), comm);
218
219 // compress is_io_rank into io_ranks
220 size_t num_io_ranks = 0;
221 for (int i = 0; i < size; ++i)
222 if (is_io_rank[i]) is_io_rank[num_io_ranks++] = i;
223
224 free(*io_ranks_);
225 *num_io_ranks_ = num_io_ranks;
226 *io_ranks_ = xrealloc(is_io_rank, num_io_ranks * sizeof(**io_ranks_));
227}
228
230 MPI_Comm comm, size_t * num_io_ranks, int ** io_ranks) {
231
232 int rank;
233 yac_mpi_call(MPI_Comm_rank(comm, &rank), comm);
234
235 int max_num_ranks = -1;
236
237 // environment is only checked on rank 0, results are broadcasted
238 // to other processes
239 if (rank == 0) {
240
241 // check whether the user provided a maximum number of io ranks
242 char * max_num_ranks_str = getenv(IO_MAX_NUM_RANKS_STR);
243 if ((max_num_ranks_str != NULL) && (max_num_ranks_str[0] != '\0')) {
244 max_num_ranks = atoi(max_num_ranks_str);
246 (max_num_ranks > 0) || (max_num_ranks == -1),
247 "ERROR(check_io_max_num_ranks): "
248 "\"%s\" is not a valid value for the maximum number of io ranks",
249 max_num_ranks_str);
250 }
251
253 MPI_Bcast(&max_num_ranks, 1, MPI_INT, 0, comm), comm);
254 } else {
256 MPI_Bcast(&max_num_ranks, 1, MPI_INT, 0, comm), comm);
257 }
258
259 // if there is no limit on the number of io ranks
260 if (max_num_ranks == -1) return;
261
262 if ((max_num_ranks > 0) && ((size_t)max_num_ranks < *num_io_ranks)) {
263 *io_ranks =
264 xrealloc(*io_ranks, (size_t)max_num_ranks * sizeof(**io_ranks));
265 *num_io_ranks = (size_t)max_num_ranks;
266 }
267}
268
270 MPI_Comm comm, size_t * num_io_ranks, int ** io_ranks) {
271
272 // read in the list of ranks which are not to be used for io
273 size_t num_io_ranks_excluded;
274 int * io_ranks_excluded;
276 IO_RANK_EXCLUDE_LIST_STR, comm, &num_io_ranks_excluded, &io_ranks_excluded);
277
278 // if there are ranks to be excluded
279 if (num_io_ranks_excluded > 0) {
280
281 // sort ranks
282 qsort(*io_ranks, *num_io_ranks, sizeof(**io_ranks), compare_int);
283
284 // match exclude list with io rank list
285 size_t new_num_io_ranks = 0;
286 for (size_t i = 0, j = 0; i < *num_io_ranks; ++i) {
287
288 while ((j < num_io_ranks_excluded) &&
289 (io_ranks_excluded[j] < (*io_ranks)[i])) ++j;
290
291 if ((j >= num_io_ranks_excluded) ||
292 ((*io_ranks)[i] != io_ranks_excluded[j])) {
293
294 if (i != new_num_io_ranks)
295 (*io_ranks)[new_num_io_ranks] = (*io_ranks)[i];
296 ++new_num_io_ranks;
297 }
298 }
299
300 if (new_num_io_ranks != *num_io_ranks) {
301 *io_ranks = xrealloc(*io_ranks, new_num_io_ranks * sizeof(**io_ranks));
302 *num_io_ranks = new_num_io_ranks;
303 }
304 }
305
306 free(io_ranks_excluded);
307}
308
310 MPI_Comm comm, int * local_is_io_, int ** io_ranks_, int * num_io_ranks_) {
311
312 int rank, size;
313 yac_mpi_call(MPI_Comm_rank(comm, &rank), comm);
314 yac_mpi_call(MPI_Comm_size(comm, &size), comm);
315
316 size_t num_io_ranks = 0;
317 int * io_ranks = NULL;
318
319 // check environment for io rank list
320 read_io_rank_list(comm, &num_io_ranks, &io_ranks);
321
322 // if no rank list was provided -> generate default rank list (all processes)
323 if (num_io_ranks == 0) {
324 num_io_ranks = (size_t)size;
325 io_ranks = xmalloc(num_io_ranks * sizeof(*io_ranks));
326 for (int i = 0; i < size; ++i) io_ranks[i] = i;
327 }
328
329 // check whether we have to exclude some ranks
330 check_io_rank_exclude_list(comm, &num_io_ranks, &io_ranks);
331
332 // check for the maximum number of io ranks per node
333 check_io_max_num_ranks_per_node(comm, &num_io_ranks, &io_ranks);
334
335 // check maximum number of io ranks
336 check_io_max_num_ranks(comm, &num_io_ranks, &io_ranks);
337
339 num_io_ranks > 0, "ERROR(yac_get_io_ranks): could not determine io ranks");
340
341 int local_is_io = 0;
342 for (size_t i = 0; (i < num_io_ranks) && !local_is_io; ++i)
343 if (io_ranks[i] == rank) local_is_io = 1;
344
345 *local_is_io_ = local_is_io;
346 *io_ranks_ = io_ranks;
347 *num_io_ranks_ = (int)num_io_ranks;
348}
349
350void yac_nc_open(const char * path, int omode, int * ncidp) {
351
352#ifndef YAC_NETCDF_ENABLED
353
354 UNUSED(path);
355 UNUSED(omode);
356 UNUSED(ncidp);
357 die("ERROR(yac_nc_open): YAC is built without the NetCDF support");
358#else
359
361 yac_file_exists(path),
362 "ERROR(yac_nc_open): file \"%s\" does not exist", path);
363 YAC_HANDLE_ERROR(nc_open(path, omode, ncidp));
364#endif
365}
366
367void yac_nc_create(const char * path, int cmode, int * ncidp) {
368
369#ifndef YAC_NETCDF_ENABLED
370
371 UNUSED(path);
372 UNUSED(cmode);
373 UNUSED(ncidp);
374 die("ERROR(yac_nc_create): YAC is built without the NetCDF support");
375#else
376
377 int status = nc_create(path, cmode, ncidp);
379 status == NC_NOERR,
380 "ERROR(yac_nc_create): failed to create file \"%s\" "
381 "(NetCDF error message: \"%s\")", path, nc_strerror(status));
382#endif
383}
384
385void yac_nc_inq_dimid(int ncid, char const * name, int * dimidp) {
386
387#ifndef YAC_NETCDF_ENABLED
388
389 UNUSED(ncid);
390 UNUSED(name);
391 UNUSED(dimidp);
392 die("ERROR(yac_nc_inq_dimid): YAC is built without the NetCDF support");
393#else
394
395 int status = nc_inq_dimid(ncid, name, dimidp);
396
397 if (status == NC_EBADDIM) {
398 // GCOVR_EXCL_START
399 size_t pathlen;
400 YAC_HANDLE_ERROR(nc_inq_path(ncid, &pathlen, NULL));
401 char * path = xmalloc(pathlen * sizeof(*path));
402 YAC_HANDLE_ERROR(nc_inq_path(ncid, NULL, path));
404 0, "ERROR(yac_nc_inq_dimid): "
405 "dimension \"%s\" could not be found in file \"%s\"", name, path);
406 // GCOVR_EXCL_STOP
407 } else YAC_HANDLE_ERROR(status);
408#endif
409}
410
411void yac_nc_inq_varid(int ncid, char const * name, int * varidp) {
412
413#ifndef YAC_NETCDF_ENABLED
414
415 UNUSED(ncid);
416 UNUSED(name);
417 UNUSED(varidp);
418 die("ERROR(yac_nc_inq_varid): YAC is built without the NetCDF support");
419#else
420
421 int status = nc_inq_varid(ncid, name, varidp);
422
423 if (status == NC_ENOTVAR) {
424 // GCOVR_EXCL_START
425 size_t pathlen;
426 YAC_HANDLE_ERROR(nc_inq_path(ncid, &pathlen, NULL));
427 char * path = xmalloc(pathlen * sizeof(*path));
428 YAC_HANDLE_ERROR(nc_inq_path(ncid, NULL, path));
430 0, "ERROR(yac_nc_inq_varid): "
431 "variable \"%s\" could not be found in file \"%s\"", name, path);
432 // GCOVR_EXCL_STOP
433 } else YAC_HANDLE_ERROR(status);
434#endif
435}
#define UNUSED(x)
Definition core.h:73
#define IO_MAX_NUM_RANKS_PER_NODE
Definition io_utils.c:13
#define IO_RANK_LIST_STR
Definition io_utils.c:10
static void read_io_rank_list(MPI_Comm comm, size_t *num_io_ranks, int **io_ranks)
Definition io_utils.c:126
#define DEFAULT_MAX_NUM_IO_RANK_PER_NODE
Definition io_utils.c:14
void yac_get_io_ranks(MPI_Comm comm, int *local_is_io_, int **io_ranks_, int *num_io_ranks_)
Definition io_utils.c:309
static void read_rank_list(char const *env_name, MPI_Comm comm, size_t *num_ranks_, int **ranks_)
Definition io_utils.c:23
void yac_nc_create(const char *path, int cmode, int *ncidp)
Definition io_utils.c:367
void yac_nc_inq_varid(int ncid, char const *name, int *varidp)
Definition io_utils.c:411
#define IO_RANK_EXCLUDE_LIST_STR
Definition io_utils.c:12
static void check_io_max_num_ranks(MPI_Comm comm, size_t *num_io_ranks, int **io_ranks)
Definition io_utils.c:229
static void check_io_max_num_ranks_per_node(MPI_Comm comm, size_t *num_io_ranks_, int **io_ranks_)
Definition io_utils.c:132
#define IO_MAX_NUM_RANKS_STR
Definition io_utils.c:11
static int compare_int(const void *a, const void *b)
Definition io_utils.c:16
void yac_nc_open(const char *path, int omode, int *ncidp)
Definition io_utils.c:350
void yac_nc_inq_dimid(int ncid, char const *name, int *dimidp)
Definition io_utils.c:385
static void check_io_rank_exclude_list(MPI_Comm comm, size_t *num_io_ranks, int **io_ranks)
Definition io_utils.c:269
int yac_file_exists(const char *filename)
Definition utils_core.c:12
#define YAC_HANDLE_ERROR(exp)
Definition io_utils.h:30
#define xrealloc(ptr, size)
Definition ppm_xfuncs.h:67
#define xmalloc(size)
Definition ppm_xfuncs.h:66
static void yac_remove_duplicates_int(int *array, size_t *n)
#define YAC_ASSERT_F(exp, format,...)
Definition yac_assert.h:19
#define die(msg)
Definition yac_assert.h:12
#define YAC_ASSERT(exp, msg)
Definition yac_assert.h:16
#define yac_mpi_call(call, comm)
#define YAC_MPI_SIZE_T