1 /*
2 * Copyright(C) 1999-2020 National Technology & Engineering Solutions
3 * of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with
4 * NTESS, the U.S. Government retains certain rights in this software.
5 *
6 * See packages/seacas/LICENSE for details
7 */
8
9 #include "exodusII.h" // for ex_err, etc
10 #include "exodusII_int.h" // for ex__file_item, EX_FATAL, etc
11 #include "stdbool.h"
12
13 /*! \file
14 * this file contains code needed to support the various floating point word
15 * size combinations for computation and i/o that applications might want to
16 * use. See the netcdf documentation for more details on the floating point
17 * conversion capabilities.
18 *
19 * netCDF supports two floating point word sizes for its files:
20 * - NC_FLOAT - 32 bit IEEE
21 * - NC_DOUBLE - 64 bit IEEE
22 *
23 */
24
25 #define NC_FLOAT_WORDSIZE 4
26
27 static struct ex__file_item *file_list = NULL;
28
ex__find_file_item(int exoid)29 struct ex__file_item *ex__find_file_item(int exoid)
30 {
31 /* Find base filename in case exoid refers to a group */
32 int base_exoid = (unsigned)exoid & EX_FILE_ID_MASK;
33 struct ex__file_item *ptr = file_list;
34 while (ptr) {
35 if (ptr->file_id == base_exoid) {
36 break;
37 }
38 ptr = ptr->next;
39 }
40 return (ptr);
41 }
42
43 #define EX__MAX_PATHLEN 8192
ex__check_multiple_open(const char * path,int mode,const char * func)44 int ex__check_multiple_open(const char *path, int mode, const char *func)
45 {
46 EX_FUNC_ENTER();
47 bool is_write = mode & EX_WRITE;
48 char tmp[EX__MAX_PATHLEN];
49 size_t pathlen;
50 struct ex__file_item *ptr = file_list;
51 while (ptr) {
52 nc_inq_path(ptr->file_id, &pathlen, tmp);
53 /* If path is too long, assume it is ok... */
54 if (pathlen < EX__MAX_PATHLEN && strncmp(path, tmp, EX__MAX_PATHLEN) == 0) {
55 /* Found matching file. See if any open for write */
56 if (ptr->is_write || is_write) {
57 char errmsg[MAX_ERR_LENGTH];
58 snprintf(errmsg, MAX_ERR_LENGTH,
59 "ERROR: The file '%s' is open for both read and write."
60 " File corruption or incorrect behavior can occur.\n",
61 path);
62 ex_err(func, errmsg, EX_BADFILEID);
63 #if defined BUILT_IN_SIERRA
64 EX_FUNC_LEAVE(EX_NOERR);
65 #else
66 EX_FUNC_LEAVE(EX_FATAL);
67 #endif
68 }
69 }
70 ptr = ptr->next;
71 }
72 EX_FUNC_LEAVE(EX_NOERR);
73 }
74
ex__check_valid_file_id(int exoid,const char * func)75 int ex__check_valid_file_id(int exoid, const char *func)
76 {
77 bool error = false;
78 if (exoid <= 0) {
79 error = true;
80 }
81 #if !defined BUILT_IN_SIERRA
82 else {
83 struct ex__file_item *file = ex__find_file_item(exoid);
84
85 if (!file) {
86 error = true;
87 }
88 }
89 #endif
90
91 if (error) {
92 int old_opt = ex_opts(EX_VERBOSE);
93 if (old_opt & EX_ABORT) {
94 ex_opts(EX_VERBOSE | EX_ABORT);
95 }
96 char errmsg[MAX_ERR_LENGTH];
97 snprintf(errmsg, MAX_ERR_LENGTH,
98 "ERROR: In \"%s\", the file id %d was not obtained via a call "
99 "to \"ex_open\" or \"ex_create\".\n\t\tIt does not refer to a "
100 "valid open exodus file.\n\t\tAborting to avoid file "
101 "corruption or data loss or other potential problems.",
102 func, exoid);
103 ex_err(__func__, errmsg, EX_BADFILEID);
104 ex_opts(old_opt);
105 return EX_FATAL;
106 }
107 return EX_NOERR;
108 }
109
ex__conv_init(int exoid,int * comp_wordsize,int * io_wordsize,int file_wordsize,int int64_status,bool is_parallel,bool is_hdf5,bool is_pnetcdf,bool is_write)110 int ex__conv_init(int exoid, int *comp_wordsize, int *io_wordsize, int file_wordsize,
111 int int64_status, bool is_parallel, bool is_hdf5, bool is_pnetcdf, bool is_write)
112 {
113 char errmsg[MAX_ERR_LENGTH];
114 struct ex__file_item *new_file;
115 int filetype = 0;
116
117 /*! ex__conv_init() initializes the floating point conversion process.
118 *
119 * \param exoid an integer uniquely identifying the file of interest.
120 *
121 * \param comp_wordsize compute floating point word size in the user's code.
122 * a zero value indicates that the user is requesting the
123 * default float size for the machine. The appropriate
124 * value is chosen and returned in comp_wordsize, and
125 * used in subsequent conversions. a valid but
126 * inappropriate for this parameter cannot be detected.
127 *
128 * \param io_wordsize the desired floating point word size for a netCDF
129 * file. For an existing file, if this parameter doesn't
130 * match the word size of data already stored in the file,
131 * a fatal error is generated. a value of 0 for an
132 * existing file indicates that the word size of the file
133 * was not known a priori, so use whatever is in the file.
134 * a value of 0 for a new file means to use the default
135 * size, an NC_FLOAT (4 bytes). when a value of 0 is
136 * specified the actual value used is returned in
137 * io_wordsize.
138 *
139 * \param file_wordsize floating point word size in an existing netCDF file.
140 * a value of 0 should be passed in for a new netCDF
141 * file.
142 *
143 * \param int64_status the flags specifying how integer values should be
144 * stored on the database and how they should be
145 * passes through the api functions.
146 *
147 * \param is_parallel 1 if parallel file; 0 if serial
148 *
149 * \param is_hdf5 1 if parallel netcdf-4 mode; 0 if not.
150 *
151 * \param is_pnetcdf 1 if parallel PNetCDF file; 0 if not.
152 *
153 * \param is_write 1 if output file; 0 if readonly
154 *
155 * word size parameters are specified in bytes. valid values are 0, 4, and 8:
156 */
157
158 EX_FUNC_ENTER();
159
160 /* check to make sure machine word sizes are sane */
161 /* If the following line causes a compile-time error, then there is a problem
162 * which will cause exodus to not work correctly on this platform.
163 *
164 * Contact Greg Sjaardema, gdsjaar@sandia.gov for asisstance.
165 */
166 #define CT_ASSERT(e) extern char(*ct_assert(void))[sizeof(char[1 - 2 * !(e)])]
167 CT_ASSERT((sizeof(float) == 4 || sizeof(float) == 8) &&
168 (sizeof(double) == 4 || sizeof(double) == 8));
169
170 /* check to see if requested word sizes are valid */
171 if (!*io_wordsize) {
172 if (!file_wordsize) {
173 *io_wordsize = NC_FLOAT_WORDSIZE;
174 }
175 else {
176 *io_wordsize = file_wordsize;
177 }
178 }
179
180 else if (*io_wordsize != 4 && *io_wordsize != 8) {
181 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: unsupported I/O word size for file id: %d", exoid);
182 ex_err_fn(exoid, __func__, errmsg, EX_BADPARAM);
183 EX_FUNC_LEAVE(EX_FATAL);
184 }
185
186 else if (file_wordsize && *io_wordsize != file_wordsize) {
187 *io_wordsize = file_wordsize;
188 snprintf(errmsg, MAX_ERR_LENGTH,
189 "ERROR: invalid I/O word size specified for existing file id: "
190 "%d, Requested I/O word size overridden.",
191 exoid);
192 ex_err_fn(exoid, __func__, errmsg, EX_BADPARAM);
193 }
194
195 if (!*comp_wordsize) {
196 *comp_wordsize = sizeof(float);
197 }
198 else if (*comp_wordsize != 4 && *comp_wordsize != 8) {
199 ex_err_fn(exoid, __func__, "ERROR: invalid compute wordsize specified", EX_BADPARAM);
200 EX_FUNC_LEAVE(EX_FATAL);
201 }
202
203 /* Check that the int64_status contains only valid bits... */
204 {
205 int valid_int64 = EX_ALL_INT64_API | EX_ALL_INT64_DB;
206 if ((int64_status & valid_int64) != int64_status) {
207 snprintf(errmsg, MAX_ERR_LENGTH,
208 "Warning: invalid int64_status flag (%d) specified for "
209 "existing file id: %d. Ignoring invalids",
210 int64_status, exoid);
211 ex_err_fn(exoid, __func__, errmsg, -EX_BADPARAM);
212 }
213 int64_status &= valid_int64;
214 }
215
216 /* Verify filetype
217 * 0 -- classic format (NC_FORMAT_CLASSIC -1)
218 * 1 -- 64 bit classic (NC_FORMAT_64BIT -1)
219 * 2 -- netcdf4 (NC_FORMAT_NETCDF4 -1)
220 * 3 -- netcdf4 classic (NC_FORMAT_NETCDF4_CLASSIC -1)
221 */
222
223 nc_inq_format(exoid, &filetype);
224
225 if (!(new_file = malloc(sizeof(struct ex__file_item)))) {
226 snprintf(errmsg, MAX_ERR_LENGTH,
227 "ERROR: failed to allocate memory for internal file "
228 "structure storage file id %d",
229 exoid);
230 ex_err_fn(exoid, __func__, errmsg, EX_MEMFAIL);
231 EX_FUNC_LEAVE(EX_FATAL);
232 }
233
234 new_file->file_id = exoid;
235 new_file->user_compute_wordsize = *comp_wordsize == 4 ? 0 : 1;
236 new_file->int64_status = int64_status;
237 new_file->maximum_name_length = ex__default_max_name_length;
238 new_file->time_varid = -1;
239 new_file->compression_algorithm = EX_COMPRESS_GZIP;
240 new_file->assembly_count = 0;
241 new_file->blob_count = 0;
242 new_file->compression_level = 0;
243 new_file->shuffle = 0;
244 new_file->file_type = filetype - 1;
245 new_file->is_parallel = is_parallel;
246 new_file->is_hdf5 = is_hdf5;
247 new_file->is_pnetcdf = is_pnetcdf;
248 new_file->has_nodes = 1; /* default to yes in case not set */
249 new_file->has_edges = 1;
250 new_file->has_faces = 1;
251 new_file->has_elems = 1;
252 new_file->is_write = is_write;
253
254 new_file->next = file_list;
255 file_list = new_file;
256
257 if (*io_wordsize == NC_FLOAT_WORDSIZE) {
258 new_file->netcdf_type_code = NC_FLOAT;
259 }
260 else {
261 new_file->netcdf_type_code = NC_DOUBLE;
262 }
263
264 EX_FUNC_LEAVE(EX_NOERR);
265 }
266
267 /*............................................................................*/
268 /*............................................................................*/
269
270 /*! ex__conv_exit() takes the structure identified by "exoid" out of the linked
271 * list which describes the files that ex_conv_array() knows how to convert.
272 *
273 * \note it is absolutely necessary for ex__conv_exit() to be called after
274 * ncclose(), if the parameter used as "exoid" is the id returned from
275 * an ncopen() or nccreate() call, as netCDF reuses file ids!
276 * the best place to do this is ex_close(), which is where I did it.
277 *
278 * \param exoid integer which uniquely identifies the file of interest.
279 */
ex__conv_exit(int exoid)280 void ex__conv_exit(int exoid)
281 {
282
283 char errmsg[MAX_ERR_LENGTH];
284 struct ex__file_item *file = file_list;
285 struct ex__file_item *prev = NULL;
286
287 EX_FUNC_ENTER();
288 while (file) {
289 if (file->file_id == exoid) {
290 break;
291 }
292
293 prev = file;
294 file = file->next;
295 }
296
297 if (!file) {
298 snprintf(errmsg, MAX_ERR_LENGTH, "Warning: failure to clear file id %d - not in list.", exoid);
299 ex_err(__func__, errmsg, -EX_BADFILEID);
300 EX_FUNC_VOID();
301 }
302
303 if (prev) {
304 prev->next = file->next;
305 }
306 else {
307 file_list = file->next;
308 }
309
310 free(file);
311 EX_FUNC_VOID();
312 }
313
314 /*............................................................................*/
315 /*............................................................................*/
316
nc_flt_code(int exoid)317 nc_type nc_flt_code(int exoid)
318 {
319 /*!
320 * \ingroup Utilities
321 * nc_flt_code() returns either NC_FLOAT or NC_DOUBLE, based on the parameters
322 * with which ex__conv_init() was called. nc_flt_code() is used as the nc_type
323 * parameter on ncvardef() calls that define floating point variables.
324 *
325 * "exoid" is some integer which uniquely identifies the file of interest.
326 */
327 EX_FUNC_ENTER();
328 struct ex__file_item *file = ex__find_file_item(exoid);
329
330 if (!file) {
331 char errmsg[MAX_ERR_LENGTH];
332 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: unknown file id %d for nc_flt_code().", exoid);
333 ex_err(__func__, errmsg, EX_BADFILEID);
334 EX_FUNC_LEAVE((nc_type)-1);
335 }
336 EX_FUNC_LEAVE(file->netcdf_type_code);
337 }
338
ex_int64_status(int exoid)339 int ex_int64_status(int exoid)
340 {
341 /*!
342 * \ingroup Utilities
343 ex_int64_status() returns an int that can be tested
344 against the defines listed below to determine which, if any,
345 'types' in the database are to be stored as int64 types and which, if any,
346 types are passed/returned as int64 types in the API
347
348 | Defines: | |
349 |----------|-|
350 | #EX_MAPS_INT64_DB | All maps (id, order, ...) store int64_t values |
351 | #EX_IDS_INT64_DB | All entity ids (sets, blocks, maps) are int64_t values |
352 | #EX_BULK_INT64_DB | All integer bulk data (local indices, counts, maps); not ids |
353 | #EX_ALL_INT64_DB | (#EX_MAPS_INT64_DB \| #EX_IDS_INT64_DB \| #EX_BULK_INT64_DB) |
354 | #EX_MAPS_INT64_API| All maps (id, order, ...) passed as int64_t values |
355 | #EX_IDS_INT64_API | All entity ids (sets, blocks, maps) are passed as int64_t values |
356 | #EX_BULK_INT64_API| All integer bulk data (local indices, counts, maps); not ids|
357 | #EX_INQ_INT64_API | Integers passed to/from ex_inquire() are int64_t |
358 | #EX_ALL_INT64_API | (#EX_MAPS_INT64_API \| #EX_IDS_INT64_API \| #EX_BULK_INT64_API \|
359 #EX_INQ_INT64_API) |
360 */
361 EX_FUNC_ENTER();
362 struct ex__file_item *file = ex__find_file_item(exoid);
363
364 if (!file) {
365 char errmsg[MAX_ERR_LENGTH];
366 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: unknown file id %d for ex_int64_status().", exoid);
367 ex_err(__func__, errmsg, EX_BADFILEID);
368 EX_FUNC_LEAVE(0);
369 }
370 EX_FUNC_LEAVE(file->int64_status);
371 }
372
ex_set_int64_status(int exoid,int mode)373 int ex_set_int64_status(int exoid, int mode)
374 {
375 /*!
376 \ingroup Utilities
377
378 ex_set_int64_status() sets the value of the INT64_API flags which
379 specify how integer types are passed/returned as int64 types in
380 the API
381
382 | Mode can be one of: | |
383 |----------|-|
384 | 0 | All integers are passed as int32_t values. |
385 | #EX_MAPS_INT64_API| All maps (id, order, ...) passed as int64_t values |
386 | #EX_IDS_INT64_API | All entity ids (sets, blocks, maps) are passed as int64_t values |
387 | #EX_BULK_INT64_API| All integer bulk data (local indices, counts, maps); not ids|
388 | #EX_INQ_INT64_API | Integers passed to/from ex_inquire() are int64_t |
389 | #EX_ALL_INT64_API | (#EX_MAPS_INT64_API \| #EX_IDS_INT64_API \| #EX_BULK_INT64_API \|
390 #EX_INQ_INT64_API) |
391 */
392
393 int api_mode = 0;
394 int db_mode = 0;
395
396 EX_FUNC_ENTER();
397 struct ex__file_item *file = ex__find_file_item(exoid);
398
399 if (!file) {
400 char errmsg[MAX_ERR_LENGTH];
401 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: unknown file id %d for ex_int64_status().", exoid);
402 ex_err(__func__, errmsg, EX_BADFILEID);
403 EX_FUNC_LEAVE(0);
404 }
405
406 /* Strip of all non-INT64_API values */
407 api_mode = mode & EX_ALL_INT64_API;
408 db_mode = file->int64_status & EX_ALL_INT64_DB;
409
410 file->int64_status = api_mode | db_mode;
411 EX_FUNC_LEAVE(file->int64_status);
412 }
413
414 /*!
415 \ingroup Utilities
416 \undoc
417 */
ex_set_option(int exoid,ex_option_type option,int option_value)418 int ex_set_option(int exoid, ex_option_type option, int option_value)
419 {
420 EX_FUNC_ENTER();
421 struct ex__file_item *file = ex__find_file_item(exoid);
422 if (!file) {
423 char errmsg[MAX_ERR_LENGTH];
424 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: unknown file id %d for ex_set_option().", exoid);
425 ex_err(__func__, errmsg, EX_BADFILEID);
426 EX_FUNC_LEAVE(EX_FATAL);
427 }
428
429 switch (option) {
430 case EX_OPT_MAX_NAME_LENGTH: file->maximum_name_length = option_value; break;
431 case EX_OPT_COMPRESSION_TYPE: file->compression_algorithm = option_value; break;
432 case EX_OPT_COMPRESSION_LEVEL: /* 0 (disabled/fastest) ... 9 (best/slowest) */
433 /* Check whether file type supports compression... */
434 if (file->is_hdf5) {
435 int value = option_value;
436 if (file->compression_algorithm == EX_COMPRESS_ZLIB) {
437 if (value > 9) {
438 value = 9;
439 }
440 if (value < 0) {
441 value = 0;
442 }
443 }
444 else if (file->compression_algorithm == EX_COMPRESS_SZIP) {
445 if (value % 2 != 0 || value < 4 || value > 32) {
446 char errmsg[MAX_ERR_LENGTH];
447 snprintf(errmsg, MAX_ERR_LENGTH,
448 "ERROR: invalid value %d for SZIP Compression. Must be even and 4 <= value <= "
449 "32. Ignoring.",
450 value);
451 ex_err_fn(exoid, __func__, errmsg, EX_BADPARAM);
452 EX_FUNC_LEAVE(EX_FATAL);
453 }
454 }
455 file->compression_level = value;
456 assert(value == file->compression_level);
457 }
458 else {
459 file->compression_level = 0;
460 }
461 break;
462 case EX_OPT_COMPRESSION_SHUFFLE: /* 0 (disabled); 1 (enabled) */
463 file->shuffle = option_value != 0 ? 1 : 0;
464 break;
465 case EX_OPT_INTEGER_SIZE_API: /* See *_INT64_* values above */
466 ex_set_int64_status(exoid, option_value);
467 break;
468 case EX_OPT_INTEGER_SIZE_DB: /* (query only) */ break;
469 default: {
470 char errmsg[MAX_ERR_LENGTH];
471 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: invalid option %d for ex_set_option().", (int)option);
472 ex_err_fn(exoid, __func__, errmsg, EX_BADPARAM);
473 EX_FUNC_LEAVE(EX_FATAL);
474 }
475 }
476 EX_FUNC_LEAVE(EX_NOERR);
477 }
478
479 /*!
480 * \ingroup Utilities
481 * ex__comp_ws() returns 4 (i.e. sizeof(float)) or 8 (i.e. sizeof(double)),
482 * depending on the value of floating point word size used to initialize
483 * the conversion facility for this file id (exoid).
484 * \param exoid integer which uniquely identifies the file of interest.
485 */
ex__comp_ws(int exoid)486 int ex__comp_ws(int exoid)
487 {
488 struct ex__file_item *file = ex__find_file_item(exoid);
489
490 if (!file) {
491 char errmsg[MAX_ERR_LENGTH];
492 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: unknown file id %d", exoid);
493 ex_err(__func__, errmsg, EX_BADFILEID);
494 return (EX_FATAL);
495 }
496 /* Stored as 0 for 4-byte; 1 for 8-byte */
497 return ((file->user_compute_wordsize + 1) * 4);
498 }
499
500 /*!
501 * \ingroup Utilities
502 * ex__is_parallel() returns 1 (true) or 0 (false) depending on whether
503 * the file was opened in parallel or serial/file-per-processor mode.
504 * Note that in this case parallel assumes the output of a single file,
505 * not a parallel run using file-per-processor.
506 * \param exoid integer which uniquely identifies the file of interest.
507 */
ex__is_parallel(int exoid)508 int ex__is_parallel(int exoid)
509 {
510 EX_FUNC_ENTER();
511 struct ex__file_item *file = ex__find_file_item(exoid);
512
513 if (!file) {
514 char errmsg[MAX_ERR_LENGTH];
515 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: unknown file id %d", exoid);
516 ex_err(__func__, errmsg, EX_BADFILEID);
517 EX_FUNC_LEAVE(EX_FATAL);
518 }
519 /* Stored as 1 for parallel, 0 for serial or file-per-processor */
520 EX_FUNC_LEAVE(file->is_parallel);
521 }
522
523 /*!
524 * \ingroup Utilities
525 * \note
526 * Do not use this unless you know what you are doing and why you
527 * are doing it. One use is if calling ex_get_partial_set() in a
528 * serial mode (proc 0 only) on a file opened in parallel.
529 * Make sure to reset the value to original value after done with
530 * special case...
531 *
532 * ex_set_parallel() sets the parallel setting for a file.
533 * returns 1 (true) or 0 (false) depending on the current setting.
534 * \param exoid integer which uniquely identifies the file of interest.
535 * \param is_parallel 1 if parallel, 0 if serial.
536 */
ex_set_parallel(int exoid,int is_parallel)537 int ex_set_parallel(int exoid, int is_parallel)
538 {
539 EX_FUNC_ENTER();
540 int old_value = 0;
541 struct ex__file_item *file = ex__find_file_item(exoid);
542
543 if (!file) {
544 char errmsg[MAX_ERR_LENGTH];
545 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: unknown file id %d", exoid);
546 ex_err(__func__, errmsg, EX_BADFILEID);
547 EX_FUNC_LEAVE(EX_FATAL);
548 }
549
550 old_value = file->is_parallel;
551 file->is_parallel = is_parallel;
552 /* Stored as 1 for parallel, 0 for serial or file-per-processor */
553 EX_FUNC_LEAVE(old_value);
554 }
555