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 *
10 * exutils - utility routines
11 *
12 *****************************************************************************/
13
14 #if defined(DEBUG_QSORT)
15 #endif
16
17 #include <errno.h>
18 #include <stdbool.h>
19
20 #include "exodusII.h"
21 #include "exodusII_int.h"
22
23 struct ex__obj_stats *exoII_eb = 0;
24 struct ex__obj_stats *exoII_ed = 0;
25 struct ex__obj_stats *exoII_fa = 0;
26 struct ex__obj_stats *exoII_ns = 0;
27 struct ex__obj_stats *exoII_es = 0;
28 struct ex__obj_stats *exoII_fs = 0;
29 struct ex__obj_stats *exoII_ss = 0;
30 struct ex__obj_stats *exoII_els = 0;
31 struct ex__obj_stats *exoII_em = 0;
32 struct ex__obj_stats *exoII_edm = 0;
33 struct ex__obj_stats *exoII_fam = 0;
34 struct ex__obj_stats *exoII_nm = 0;
35 struct ex__obj_stats *exoII_ass = 0;
36
37 /*****************************************************************************
38 *
39 * utility routines for string conversions
40 * ex__catstr - concatenate string/number (where number is converted to ASCII)
41 * ex__catstr2 - concatenate string1/number1/string2/number2 "
42 *
43 * NOTE: these routines reuse the same storage over and over to build
44 * concatenated strings, because the strings are just passed to netCDF
45 * routines as names used to look up variables. if the strings returned
46 * by these routines are needed for any other purpose, they should
47 * immediately be copied into other storage.
48 *****************************************************************************/
49
50 static char ret_string[10 * (MAX_VAR_NAME_LENGTH + 1)];
51 static char *cur_string = &ret_string[0];
52
53 #ifndef _MSC_VER
54 #if NC_HAS_HDF5
55 extern int H5get_libversion(unsigned *, unsigned *, unsigned *);
56 #endif
57 #endif
58
59 #if NC_HAS_PNETCDF
60 extern char *ncmpi_inq_libvers();
61 #endif
62
63 /*!
64 \ingroup Utilities
65 \undoc
66 */
ex_config(void)67 const char *ex_config(void)
68 {
69 static char buffer[2048];
70 int j =
71 sprintf(buffer, "\tExodus Version %s, Released %s\n", EXODUS_VERSION, EXODUS_RELEASE_DATE);
72 #if defined(PARALLEL_AWARE_EXODUS)
73 j += sprintf(buffer + j, "\t\tParallel enabled\n");
74 #else
75 j += sprintf(buffer + j, "\t\tParallel NOT enabled\n");
76 #endif
77 #if defined(EXODUS_THREADSAFE)
78 j += sprintf(buffer + j, "\t\tThread Safe enabled\n");
79 #else
80 j += sprintf(buffer + j, "\t\tThread Safe NOT enabled\n");
81 #endif
82 #if defined(SEACAS_HIDE_DEPRECATED_CODE)
83 j += sprintf(buffer + j, "\t\tDeprecated Functions NOT built\n\n");
84 #else
85 j += sprintf(buffer + j, "\t\tDeprecated Functions available\n\n");
86 #endif
87 #if defined(NC_VERSION)
88 j += sprintf(buffer + j, "\tNetCDF Version %s\n", NC_VERSION);
89 #else
90 j += sprintf(buffer + j, "\tNetCDF Version < 4.3.3\n");
91 #endif
92 #if NC_HAS_CDF5
93 j += sprintf(buffer + j, "\t\tCDF5 enabled\n");
94 #endif
95 #ifndef _MSC_VER
96 #if NC_HAS_HDF5
97 {
98 unsigned major, minor, release;
99 H5get_libversion(&major, &minor, &release);
100 j += sprintf(buffer + j, "\t\tHDF5 enabled (%u.%u.%u)\n", major, minor, release);
101 }
102 j += sprintf(buffer + j, "\t\tZlib Compression (read/write) enabled\n");
103 #if NC_HAS_SZIP_WRITE == 1
104 j += sprintf(buffer + j, "\t\tSZip Compression (read/write) enabled\n");
105 #else
106 j += sprintf(buffer + j, "\t\tSZip Compression (read/write) NOT enabled\n");
107 #endif
108 #endif
109 #endif
110 #if defined(PARALLEL_AWARE_EXODUS)
111 #if NC_HAS_PARALLEL
112 j += sprintf(buffer + j, "\t\tParallel IO enabled via HDF5 and/or PnetCDF\n");
113 #else
114 j += sprintf(buffer + j,
115 "\t\tParallel IO *NOT* enabled via HDF5 and/or PnetCDF (PROBABLY A BUILD ERROR!)\n");
116 #endif
117 #if NC_HAS_PARALLEL4
118 j += sprintf(buffer + j, "\t\tParallel IO enabled via HDF5\n");
119 #else
120 j += sprintf(buffer + j, "\t\tParallel IO *NOT* enabled via HDF5\n");
121 #endif
122 #if NC_HAS_PAR_FILTERS
123 j += sprintf(buffer + j, "\t\tParallel IO supports filters\n");
124 #endif
125 #if NC_HAS_PNETCDF
126 {
127 char *libver = ncmpi_inq_libvers();
128 j += sprintf(buffer + j, "\t\tParallel IO enabled via PnetCDF (%s)\n", libver);
129 }
130 #else
131 j += sprintf(buffer + j, "\t\tParallel IO *NOT* enabled via PnetCDF\n");
132 #endif
133 #endif /* PARALLEL_AWARE_EXODUS */
134
135 #if NC_HAS_ERANGE_FILL
136 j += sprintf(buffer + j, "\t\tERANGE_FILL support\n");
137 #endif
138 #if NC_RELAX_COORD_BOUND
139 j += sprintf(buffer + j, "\t\tRELAX_COORD_BOUND defined\n");
140 #endif
141 #if defined(NC_COMPACT)
142 j += sprintf(buffer + j, "\t\tNC_COMPACT defined\n");
143 #endif
144 #if defined(NC_HAVE_META_H)
145 j += sprintf(buffer + j, "\t\tNC_HAVE_META_H defined\n");
146 #endif
147 #if NC_HAS_NC2
148 j += sprintf(buffer + j, "\t\tAPI Version 2 support enabled\n");
149 #else
150 j += sprintf(buffer + j, "\t\tAPI Version 2 support NOT enabled\n");
151 #endif
152 j += sprintf(buffer + j, "\n");
153
154 assert(j < 2048);
155 return buffer;
156 }
ex_print_config(void)157 void ex_print_config(void) { fprintf(stderr, "%s\n", ex_config()); }
158
159 /*!
160 \ingroup Utilities
161 \undoc
162 */
ex__check_file_type(const char * path,int * type)163 int ex__check_file_type(const char *path, int *type)
164 {
165 /* Based on (stolen from?) NC_check_file_type from netcdf sources.
166
167 Type is set to:
168 1 if this is a netcdf classic file,
169 2 if this is a netcdf 64-bit offset file,
170 4 pnetcdf cdf5 file.
171 5 if this is an hdf5 file
172 */
173
174 #define MAGIC_NUMBER_LEN 4
175
176 char magic[MAGIC_NUMBER_LEN+1];
177 EX_FUNC_ENTER();
178
179 *type = 0;
180
181 /* Get the 4-byte magic from the beginning of the file. */
182 {
183 FILE *fp;
184 int i;
185
186 if (!(fp = fopen(path, "r"))) {
187 char errmsg[MAX_ERR_LENGTH];
188 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: Could not open file '%s', error = %s.", path,
189 strerror(errno));
190 ex_err(__func__, errmsg, EX_WRONGFILETYPE);
191 EX_FUNC_LEAVE(EX_FATAL);
192 }
193 i = fread(magic, MAGIC_NUMBER_LEN, 1, fp);
194 magic[MAGIC_NUMBER_LEN] = '\0';
195 fclose(fp);
196 if (i != 1) {
197 char errmsg[MAX_ERR_LENGTH];
198 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: Could not read magic data from file '%s', err = %s.",
199 path, strerror(errno));
200 ex_err(__func__, errmsg, EX_WRONGFILETYPE);
201 EX_FUNC_LEAVE(EX_FATAL);
202 }
203 }
204
205 /* Ignore the first byte for HDF */
206 if (magic[1] == 'H' && magic[2] == 'D' && magic[3] == 'F') {
207 *type = 5;
208 }
209 else if (magic[0] == 'C' && magic[1] == 'D' && magic[2] == 'F') {
210 if (magic[3] == '\001') {
211 *type = 1;
212 }
213 else if (magic[3] == '\002') {
214 *type = 2;
215 }
216 else if (magic[3] == '\005') {
217 *type = 4; /* cdf5 (including pnetcdf) file */
218 }
219 }
220 if (*type == 0) {
221 char errmsg[MAX_ERR_LENGTH];
222 snprintf(
223 errmsg, MAX_ERR_LENGTH,
224 "ERROR: Could not recognize %s as a valid Exodus/NetCDF file variant. Magic value is '%s'",
225 path, magic);
226 ex_err(__func__, errmsg, EX_WRONGFILETYPE);
227 EX_FUNC_LEAVE(EX_FATAL);
228 }
229 EX_FUNC_LEAVE(EX_NOERR);
230 }
231
232 /*!
233 \ingroup Utilities
234 \undoc
235 */
ex_set_max_name_length(int exoid,int length)236 int ex_set_max_name_length(int exoid, int length)
237 {
238 char errmsg[MAX_ERR_LENGTH];
239
240 EX_FUNC_ENTER();
241 if (ex__check_valid_file_id(exoid, __func__) == EX_FATAL) {
242 EX_FUNC_LEAVE(EX_FATAL);
243 }
244 if (length <= 0) {
245 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: Max name length must be positive.");
246 ex_err_fn(exoid, __func__, errmsg, NC_EMAXNAME);
247 EX_FUNC_LEAVE(EX_FATAL);
248 }
249 if (length > NC_MAX_NAME) {
250 snprintf(errmsg, MAX_ERR_LENGTH,
251 "ERROR: Max name length (%d) exceeds netcdf max name size (%d).", length, NC_MAX_NAME);
252 ex_err_fn(exoid, __func__, errmsg, NC_EMAXNAME);
253 EX_FUNC_LEAVE(EX_FATAL);
254 }
255
256 ex_set_option(exoid, EX_OPT_MAX_NAME_LENGTH, length);
257
258 EX_FUNC_LEAVE(EX_NOERR);
259 }
260
261 /*!
262 \ingroup Utilities
263 \undoc
264 */
ex__update_max_name_length(int exoid,int length)265 void ex__update_max_name_length(int exoid, int length)
266 {
267 int status;
268 int db_length = 0;
269 int rootid = exoid & EX_FILE_ID_MASK;
270
271 EX_FUNC_ENTER();
272 if (ex__check_valid_file_id(exoid, __func__) == EX_FATAL) {
273 EX_FUNC_VOID();
274 }
275
276 /* Get current value of the maximum_name_length attribute... */
277 if ((status = nc_get_att_int(rootid, NC_GLOBAL, ATT_MAX_NAME_LENGTH, &db_length)) != NC_NOERR) {
278 char errmsg[MAX_ERR_LENGTH];
279 snprintf(errmsg, MAX_ERR_LENGTH,
280 "ERROR: failed to update 'max_name_length' attribute in file id %d", exoid);
281 ex_err_fn(exoid, __func__, errmsg, status);
282 }
283
284 if (length > db_length) {
285 /* Update with new value... */
286 ex_set_max_name_length(exoid, length);
287 if ((status = nc_put_att_int(rootid, NC_GLOBAL, ATT_MAX_NAME_LENGTH, NC_INT, 1, &length)) !=
288 NC_NOERR) {
289 char errmsg[MAX_ERR_LENGTH];
290 snprintf(errmsg, MAX_ERR_LENGTH,
291 "ERROR: failed to update 'max_name_length' attribute with new value in file id %d",
292 exoid);
293 ex_err_fn(exoid, __func__, errmsg, status);
294 }
295 nc_sync(rootid);
296 }
297 EX_FUNC_VOID();
298 }
299
300 /*!
301 \internal
302 \undoc
303 */
ex__put_names(int exoid,int varid,size_t num_names,char ** names,ex_entity_type obj_type,const char * subtype,const char * routine)304 int ex__put_names(int exoid, int varid, size_t num_names, char **names, ex_entity_type obj_type,
305 const char *subtype, const char *routine)
306 {
307 size_t i;
308 int status;
309 char errmsg[MAX_ERR_LENGTH];
310 int max_name_len = 0;
311 size_t name_length;
312 size_t length;
313 char * int_names = NULL;
314 size_t idx = 0;
315 int found_name = 0;
316
317 EX_FUNC_ENTER();
318 if (ex__check_valid_file_id(exoid, __func__) == EX_FATAL) {
319 EX_FUNC_LEAVE(EX_FATAL);
320 }
321 /* inquire previously defined dimensions */
322 name_length = ex_inquire_int(exoid, EX_INQ_DB_MAX_ALLOWED_NAME_LENGTH) + 1;
323
324 if (!(int_names = calloc(num_names * name_length, 1))) {
325 snprintf(errmsg, MAX_ERR_LENGTH,
326 "ERROR: failed to allocate memory for internal int_names "
327 "array in file id %d",
328 exoid);
329 ex_err_fn(exoid, __func__, errmsg, EX_MEMFAIL);
330 EX_FUNC_LEAVE(EX_FATAL);
331 }
332
333 for (i = 0; i < num_names; i++) {
334 if (names != NULL && *names != NULL && *names[i] != '\0') {
335 found_name = 1;
336 ex_copy_string(&int_names[idx], names[i], name_length);
337 length = strlen(names[i]) + 1;
338 if (length > name_length) {
339 fprintf(stderr,
340 "Warning: The %s %s name '%s' is too long.\n\tIt will "
341 "be truncated from %d to %d characters. [Called from %s]\n",
342 ex_name_of_object(obj_type), subtype, names[i], (int)length - 1,
343 (int)name_length - 1, routine);
344 length = name_length;
345 }
346
347 if (length > max_name_len) {
348 max_name_len = length;
349 }
350 }
351 idx += name_length;
352 }
353
354 if ((status = nc_put_var_text(exoid, varid, int_names)) != NC_NOERR) {
355 free(int_names);
356 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to store %s names in file id %d",
357 ex_name_of_object(obj_type), exoid);
358 ex_err_fn(exoid, __func__, errmsg, status);
359 EX_FUNC_LEAVE(EX_FATAL);
360 }
361
362 if (found_name) {
363
364 /* Update the maximum_name_length attribute on the file. */
365 ex__update_max_name_length(exoid, max_name_len - 1);
366 }
367 free(int_names);
368
369 EX_FUNC_LEAVE(EX_NOERR);
370 }
371
372 /*!
373 \internal
374 \undoc
375 */
ex__put_name(int exoid,int varid,size_t index,const char * name,ex_entity_type obj_type,const char * subtype,const char * routine)376 int ex__put_name(int exoid, int varid, size_t index, const char *name, ex_entity_type obj_type,
377 const char *subtype, const char *routine)
378 {
379 int status;
380 size_t start[2], count[2];
381 char errmsg[MAX_ERR_LENGTH];
382 size_t name_length;
383
384 if (ex__check_valid_file_id(exoid, __func__) == EX_FATAL) {
385 EX_FUNC_LEAVE(EX_FATAL);
386 }
387
388 /* inquire previously defined dimensions */
389 name_length = ex_inquire_int(exoid, EX_INQ_DB_MAX_ALLOWED_NAME_LENGTH) + 1;
390
391 if (name != NULL && *name != '\0') {
392 int too_long = 0;
393 start[0] = index;
394 start[1] = 0;
395
396 count[0] = 1;
397 count[1] = strlen(name) + 1;
398
399 if (count[1] > name_length) {
400 fprintf(stderr,
401 "Warning: The %s %s name '%s' is too long.\n\tIt will be "
402 "truncated from %d to %d characters. [Called from %s]\n",
403 ex_name_of_object(obj_type), subtype, name, (int)strlen(name), (int)name_length - 1,
404 routine);
405 count[1] = name_length;
406 too_long = 1;
407 }
408
409 if ((status = nc_put_vara_text(exoid, varid, start, count, name)) != NC_NOERR) {
410 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to store %s name in file id %d",
411 ex_name_of_object(obj_type), exoid);
412 ex_err_fn(exoid, __func__, errmsg, status);
413 return (EX_FATAL);
414 }
415
416 /* Add the trailing null if the variable name was too long */
417 if (too_long) {
418 start[1] = name_length - 1;
419 nc_put_var1_text(exoid, varid, start, "\0");
420 }
421
422 /* Update the maximum_name_length attribute on the file. */
423 ex__update_max_name_length(exoid, count[1] - 1);
424 }
425 return (EX_NOERR);
426 }
427
428 /*!
429 \internal
430 \undoc
431 */
ex__get_names(int exoid,int varid,size_t num_names,char ** names,ex_entity_type obj_type,const char * routine)432 int ex__get_names(int exoid, int varid, size_t num_names, char **names, ex_entity_type obj_type,
433 const char *routine)
434 {
435 size_t i;
436 int status;
437
438 /* Query size of names on file
439 * Use the smaller of the size on file or user-specified length
440 */
441 int db_name_size = ex_inquire_int(exoid, EX_INQ_DB_MAX_ALLOWED_NAME_LENGTH);
442 int api_name_size = ex_inquire_int(exoid, EX_INQ_MAX_READ_NAME_LENGTH);
443 int name_size = db_name_size < api_name_size ? db_name_size : api_name_size;
444
445 for (i = 0; i < num_names; i++) {
446 status = ex__get_name(exoid, varid, i, names[i], name_size, obj_type, routine);
447 if (status != NC_NOERR) {
448 return (status);
449 }
450 }
451 return (EX_NOERR);
452 }
453
454 /*!
455 \internal
456 \undoc
457 */
ex__get_name(int exoid,int varid,size_t index,char * name,int name_size,ex_entity_type obj_type,const char * routine)458 int ex__get_name(int exoid, int varid, size_t index, char *name, int name_size,
459 ex_entity_type obj_type, const char *routine)
460 {
461 size_t start[2], count[2];
462 int status;
463 char errmsg[MAX_ERR_LENGTH];
464 int api_name_size = 0;
465
466 api_name_size = ex_inquire_int(exoid, EX_INQ_MAX_READ_NAME_LENGTH);
467
468 /* read the name */
469 start[0] = index;
470 count[0] = 1;
471 start[1] = 0;
472 count[1] = name_size + 1;
473
474 status = nc_get_vara_text(exoid, varid, start, count, name);
475 if (status != NC_NOERR) {
476 snprintf(errmsg, MAX_ERR_LENGTH,
477 "ERROR: failed to get %s name at index %d from file id %d [Called from %s]",
478 ex_name_of_object(obj_type), (int)index, exoid, routine);
479 ex_err_fn(exoid, __func__, errmsg, status);
480 return (EX_FATAL);
481 }
482
483 name[api_name_size] = '\0';
484
485 ex__trim(name);
486 return (EX_NOERR);
487 }
488
489 /*!
490 \internal
491 \undoc
492 */
ex__trim(char * name)493 void ex__trim(char *name)
494 {
495 /* Thread-safe, reentrant */
496 /* Trim trailing spaces... */
497 size_t size;
498 char * end;
499
500 if (name == NULL) {
501 return;
502 }
503
504 size = strlen(name);
505 if (size == 0) {
506 return;
507 }
508
509 end = name + size - 1;
510 while (end >= name && isspace(*end)) {
511 end--;
512 }
513
514 *(end + 1) = '\0';
515 }
516
517 /** ex__catstr - concatenate string/number (where number is converted to ASCII)
518 */
519 /*!
520 \internal
521 \undoc
522 */
ex__catstr(const char * string,int num)523 char *ex__catstr(const char *string, int num)
524 {
525 /* Only called from an already locked function */
526 char *tmp_string = cur_string;
527 cur_string += sprintf(cur_string, "%s%d", string, num) + 1;
528 if (cur_string - ret_string > 9 * (MAX_VAR_NAME_LENGTH + 1)) {
529 cur_string = ret_string;
530 }
531 return (tmp_string);
532 }
533
534 /** ex__catstr2 - concatenate string1num1string2num2 */
535 /*!
536 \internal
537 \undoc
538 */
ex__catstr2(const char * string1,int num1,const char * string2,int num2)539 char *ex__catstr2(const char *string1, int num1, const char *string2, int num2)
540 {
541 /* Only called from an already locked function */
542 char *tmp_string = cur_string;
543 cur_string += sprintf(cur_string, "%s%d%s%d", string1, num1, string2, num2) + 1;
544 if (cur_string - ret_string > 9 * (MAX_VAR_NAME_LENGTH + 1)) {
545 cur_string = ret_string;
546 }
547 return (tmp_string);
548 }
549
550 /*!
551 \internal
552 \undoc
553 */
ex_name_of_object(ex_entity_type obj_type)554 char *ex_name_of_object(ex_entity_type obj_type)
555 {
556 /* Thread-safe and reentrant */
557 switch (obj_type) {
558 case EX_ASSEMBLY: return "assembly";
559 case EX_BLOB: return "blob";
560 case EX_COORDINATE: /* kluge so some wrapper functions work */ return "coordinate";
561 case EX_NODAL: return "nodal";
562 case EX_EDGE_BLOCK: return "edge block";
563 case EX_FACE_BLOCK: return "face block";
564 case EX_ELEM_BLOCK: return "element block";
565 case EX_NODE_SET: return "node set";
566 case EX_EDGE_SET: return "edge set";
567 case EX_FACE_SET: return "face set";
568 case EX_SIDE_SET: return "side set";
569 case EX_ELEM_SET: return "element set";
570 case EX_ELEM_MAP: return "element map";
571 case EX_NODE_MAP: return "node map";
572 case EX_EDGE_MAP: return "edge map";
573 case EX_FACE_MAP: return "face map";
574 case EX_GLOBAL: return "global";
575 default: return "invalid type";
576 }
577 }
578
579 /*!
580 \internal
581 \undoc
582 */
ex_var_type_to_ex_entity_type(char var_type)583 ex_entity_type ex_var_type_to_ex_entity_type(char var_type)
584 {
585 /* Thread-safe and reentrant */
586 char var_lower = tolower(var_type);
587 if (var_lower == 'n') {
588 return EX_NODAL;
589 }
590 if (var_lower == 'l') {
591 return EX_EDGE_BLOCK;
592 }
593 if (var_lower == 'f') {
594 return EX_FACE_BLOCK;
595 }
596 if (var_lower == 'e') {
597 return EX_ELEM_BLOCK;
598 }
599 if (var_lower == 'm') {
600 return EX_NODE_SET;
601 }
602 else if (var_lower == 'd') {
603 return EX_EDGE_SET;
604 }
605 else if (var_lower == 'a') {
606 return EX_FACE_SET;
607 }
608 else if (var_lower == 's') {
609 return EX_SIDE_SET;
610 }
611 else if (var_lower == 't') {
612 return EX_ELEM_SET;
613 }
614 else if (var_lower == 'g') {
615 return EX_GLOBAL;
616 }
617 else {
618 return EX_INVALID;
619 }
620 }
621
622 /*!
623 \internal
624 \undoc
625 */
ex__dim_num_objects(ex_entity_type obj_type)626 char *ex__dim_num_objects(ex_entity_type obj_type)
627 {
628 switch (obj_type) {
629 case EX_NODAL: return DIM_NUM_NODES;
630 case EX_ASSEMBLY: return DIM_NUM_ASSEMBLY;
631 case EX_BLOB: return DIM_NUM_BLOB;
632 case EX_ELEM_BLOCK: return DIM_NUM_EL_BLK;
633 case EX_EDGE_BLOCK: return DIM_NUM_ED_BLK;
634 case EX_FACE_BLOCK: return DIM_NUM_FA_BLK;
635 case EX_NODE_SET: return DIM_NUM_NS;
636 case EX_EDGE_SET: return DIM_NUM_ES;
637 case EX_FACE_SET: return DIM_NUM_FS;
638 case EX_ELEM_SET: return DIM_NUM_ELS;
639 case EX_SIDE_SET: return DIM_NUM_SS;
640 case EX_ELEM_MAP: return DIM_NUM_EM;
641 case EX_FACE_MAP: return DIM_NUM_FAM;
642 case EX_EDGE_MAP: return DIM_NUM_EDM;
643 case EX_NODE_MAP: return DIM_NUM_NM;
644 default: {
645 char errmsg[MAX_ERR_LENGTH];
646 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: object type %d not supported in call to %s", obj_type,
647 __func__);
648 ex_err(__func__, errmsg, EX_BADPARAM);
649 return (NULL);
650 }
651 }
652 }
653
654 /*!
655 \internal
656 \undoc
657 */
ex__dim_num_entries_in_object(ex_entity_type obj_type,int idx)658 char *ex__dim_num_entries_in_object(ex_entity_type obj_type, int idx)
659 {
660 switch (obj_type) {
661 case EX_NODAL: return DIM_NUM_NODES;
662 case EX_ASSEMBLY: return DIM_NUM_ENTITY_ASSEMBLY(idx);
663 case EX_BLOB: return DIM_NUM_VALUES_BLOB(idx);
664 case EX_EDGE_BLOCK: return DIM_NUM_ED_IN_EBLK(idx);
665 case EX_FACE_BLOCK: return DIM_NUM_FA_IN_FBLK(idx);
666 case EX_ELEM_BLOCK: return DIM_NUM_EL_IN_BLK(idx);
667 case EX_NODE_SET: return DIM_NUM_NOD_NS(idx);
668 case EX_EDGE_SET: return DIM_NUM_EDGE_ES(idx);
669 case EX_FACE_SET: return DIM_NUM_FACE_FS(idx);
670 case EX_SIDE_SET: return DIM_NUM_SIDE_SS(idx);
671 case EX_ELEM_SET: return DIM_NUM_ELE_ELS(idx);
672 default: {
673 char errmsg[MAX_ERR_LENGTH];
674 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: object type %d not supported in call to %s", obj_type,
675 __func__);
676 ex_err(__func__, errmsg, EX_BADPARAM);
677 return NULL;
678 }
679 }
680 }
681
682 /*!
683 \internal
684 \undoc
685 */
ex__name_var_of_object(ex_entity_type obj_type,int i,int j)686 char *ex__name_var_of_object(ex_entity_type obj_type, int i, int j)
687 {
688 switch (obj_type) {
689 case EX_ASSEMBLY: return VAR_ASSEMBLY_VAR(i, j);
690 case EX_BLOB: return VAR_BLOB_VAR(i, j);
691 case EX_EDGE_BLOCK: return VAR_EDGE_VAR(i, j);
692 case EX_FACE_BLOCK: return VAR_FACE_VAR(i, j);
693 case EX_ELEM_BLOCK: return VAR_ELEM_VAR(i, j);
694 case EX_NODE_SET: return VAR_NS_VAR(i, j);
695 case EX_EDGE_SET: return VAR_ES_VAR(i, j);
696 case EX_FACE_SET: return VAR_FS_VAR(i, j);
697 case EX_SIDE_SET: return VAR_SS_VAR(i, j);
698 case EX_ELEM_SET: return VAR_ELS_VAR(i, j);
699 default: {
700 char errmsg[MAX_ERR_LENGTH];
701 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: object type %d not supported in call to %s", obj_type,
702 __func__);
703 ex_err(__func__, errmsg, EX_BADPARAM);
704 return (NULL);
705 }
706 }
707 }
708
709 /*!
710 \internal
711 \undoc
712 */
ex__name_red_var_of_object(ex_entity_type obj_type,int id)713 char *ex__name_red_var_of_object(ex_entity_type obj_type, int id)
714 {
715 switch (obj_type) {
716 case EX_ASSEMBLY: return VAR_ASSEMBLY_RED_VAR(id);
717 case EX_BLOB: return VAR_BLOB_RED_VAR(id);
718 case EX_EDGE_BLOCK: return VAR_EDGE_RED_VAR(id);
719 case EX_FACE_BLOCK: return VAR_FACE_RED_VAR(id);
720 case EX_ELEM_BLOCK: return VAR_ELEM_RED_VAR(id);
721 case EX_NODE_SET: return VAR_NS_RED_VAR(id);
722 case EX_EDGE_SET: return VAR_ES_RED_VAR(id);
723 case EX_FACE_SET: return VAR_FS_RED_VAR(id);
724 case EX_SIDE_SET: return VAR_SS_RED_VAR(id);
725 case EX_ELEM_SET: return VAR_ELS_RED_VAR(id);
726 default: {
727 char errmsg[MAX_ERR_LENGTH];
728 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: object type %d not supported in call to %s", obj_type,
729 __func__);
730 ex_err(__func__, errmsg, EX_BADPARAM);
731 return (NULL);
732 }
733 }
734 }
735
736 /*!
737 \internal
738 \undoc
739 */
ex__name_of_map(ex_entity_type map_type,int map_index)740 char *ex__name_of_map(ex_entity_type map_type, int map_index)
741 {
742 switch (map_type) {
743 case EX_NODE_MAP: return VAR_NODE_MAP(map_index);
744 case EX_EDGE_MAP: return VAR_EDGE_MAP(map_index);
745 case EX_FACE_MAP: return VAR_FACE_MAP(map_index);
746 case EX_ELEM_MAP: return VAR_ELEM_MAP(map_index);
747 default: return NULL;
748 }
749 }
750
751 /*****************************************************************************
752 *
753 * ex__id_lkup - look up id
754 *
755 * entry conditions -
756 * input parameters:
757 * int exoid exodus file id
758 * ex_entity_type id_type id type name:
759 * elem_ss
760 * node_ns
761 2* side_ss
762 * int num id value
763 *
764 * exit conditions -
765 * int return index into table (1-based)
766 *
767 *****************************************************************************/
768
769 /*!
770 \internal
771 \undoc
772 */
ex__id_lkup(int exoid,ex_entity_type id_type,ex_entity_id num)773 int ex__id_lkup(int exoid, ex_entity_type id_type, ex_entity_id num)
774 {
775 char * id_table;
776 char * id_dim;
777 char * stat_table;
778 int varid, dimid;
779 size_t dim_len, j;
780 int64_t i;
781 int64_t *id_vals = NULL;
782 int * stat_vals = NULL;
783
784 static bool filled = false;
785 static bool sequential = false;
786 struct ex__obj_stats *tmp_stats;
787 int status;
788 char errmsg[MAX_ERR_LENGTH];
789
790 switch (id_type) {
791 case EX_NODAL: return (0);
792 case EX_GLOBAL: return (0);
793 case EX_ASSEMBLY: return num;
794 case EX_BLOB: return num;
795 case EX_ELEM_BLOCK:
796 id_table = VAR_ID_EL_BLK; /* id array name */
797 id_dim = DIM_NUM_EL_BLK; /* id array dimension name*/
798 stat_table = VAR_STAT_EL_BLK; /* id status array name */
799 tmp_stats = ex__get_stat_ptr(exoid, &exoII_eb);
800 break;
801 case EX_NODE_SET:
802 id_table = VAR_NS_IDS;
803 id_dim = DIM_NUM_NS;
804 stat_table = VAR_NS_STAT;
805 tmp_stats = ex__get_stat_ptr(exoid, &exoII_ns);
806 break;
807 case EX_SIDE_SET:
808 id_table = VAR_SS_IDS;
809 id_dim = DIM_NUM_SS;
810 stat_table = VAR_SS_STAT;
811 tmp_stats = ex__get_stat_ptr(exoid, &exoII_ss);
812 break;
813 case EX_EDGE_BLOCK:
814 id_table = VAR_ID_ED_BLK;
815 id_dim = DIM_NUM_ED_BLK;
816 stat_table = VAR_STAT_ED_BLK;
817 tmp_stats = ex__get_stat_ptr(exoid, &exoII_ed);
818 break;
819 case EX_FACE_BLOCK:
820 id_table = VAR_ID_FA_BLK;
821 id_dim = DIM_NUM_FA_BLK;
822 stat_table = VAR_STAT_FA_BLK;
823 tmp_stats = ex__get_stat_ptr(exoid, &exoII_fa);
824 break;
825 case EX_EDGE_SET:
826 id_table = VAR_ES_IDS;
827 id_dim = DIM_NUM_ES;
828 stat_table = VAR_ES_STAT;
829 tmp_stats = ex__get_stat_ptr(exoid, &exoII_es);
830 break;
831 case EX_FACE_SET:
832 id_table = VAR_FS_IDS;
833 id_dim = DIM_NUM_FS;
834 stat_table = VAR_FS_STAT;
835 tmp_stats = ex__get_stat_ptr(exoid, &exoII_fs);
836 break;
837 case EX_ELEM_SET:
838 id_table = VAR_ELS_IDS;
839 id_dim = DIM_NUM_ELS;
840 stat_table = VAR_ELS_STAT;
841 tmp_stats = ex__get_stat_ptr(exoid, &exoII_els);
842 break;
843 case EX_NODE_MAP:
844 id_table = VAR_NM_PROP(1);
845 id_dim = DIM_NUM_NM;
846 stat_table = "";
847 tmp_stats = ex__get_stat_ptr(exoid, &exoII_nm);
848 break;
849 case EX_EDGE_MAP:
850 id_table = VAR_EDM_PROP(1);
851 id_dim = DIM_NUM_EDM;
852 stat_table = "";
853 tmp_stats = ex__get_stat_ptr(exoid, &exoII_edm);
854 break;
855 case EX_FACE_MAP:
856 id_table = VAR_FAM_PROP(1);
857 id_dim = DIM_NUM_FAM;
858 stat_table = "";
859 tmp_stats = ex__get_stat_ptr(exoid, &exoII_fam);
860 break;
861 case EX_ELEM_MAP:
862 id_table = VAR_EM_PROP(1);
863 id_dim = DIM_NUM_EM;
864 stat_table = "";
865 tmp_stats = ex__get_stat_ptr(exoid, &exoII_em);
866 break;
867 default:
868 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: unsupported id array type %d for file id %d", id_type,
869 exoid);
870 ex_err_fn(exoid, __func__, errmsg, EX_BADPARAM);
871 return (EX_FATAL);
872 }
873
874 if ((tmp_stats->id_vals == NULL) || (!(tmp_stats->valid_ids))) {
875
876 /* first time through or id arrays haven't been completely filled yet */
877
878 /* get size of id array */
879
880 /* First get dimension id of id array */
881 if ((status = nc_inq_dimid(exoid, id_dim, &dimid)) != NC_NOERR) {
882 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to locate id array dimension in file id %d",
883 exoid);
884 ex_err_fn(exoid, __func__, errmsg, status);
885 return (EX_FATAL);
886 }
887
888 /* Next get value of dimension */
889 if ((status = nc_inq_dimlen(exoid, dimid, &dim_len)) != NC_NOERR) {
890 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to locate %s array length in file id %d",
891 id_table, exoid);
892 ex_err_fn(exoid, __func__, errmsg, status);
893 return (EX_FATAL);
894 }
895
896 /* get variable id of id array */
897 if ((status = nc_inq_varid(exoid, id_table, &varid)) != NC_NOERR) {
898 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to locate %s array in file id %d", id_table,
899 exoid);
900 ex_err_fn(exoid, __func__, errmsg, status);
901 return (EX_FATAL);
902 }
903
904 /* allocate space for id array and initialize to zero to ensure
905 that the higher bits don't contain garbage while copy from ints */
906 if (!(id_vals = calloc(dim_len, sizeof(int64_t)))) {
907 snprintf(errmsg, MAX_ERR_LENGTH,
908 "ERROR: failed to allocate memory for %s array for file id %d", id_table, exoid);
909 ex_err_fn(exoid, __func__, errmsg, EX_MEMFAIL);
910 return (EX_FATAL);
911 }
912
913 if (ex_int64_status(exoid) & EX_IDS_INT64_API) {
914 status = nc_get_var_longlong(exoid, varid, (long long *)id_vals);
915 }
916 else {
917 int *id_vals_int;
918 if (!(id_vals_int = malloc(dim_len * sizeof(int)))) {
919 snprintf(errmsg, MAX_ERR_LENGTH,
920 "ERROR: failed to allocate memory for temporary array "
921 "id_vals_int for file id %d",
922 exoid);
923 ex_err_fn(exoid, __func__, errmsg, EX_MEMFAIL);
924 free(id_vals);
925 return (EX_FATAL);
926 }
927 status = nc_get_var_int(exoid, varid, id_vals_int);
928 if (status == NC_NOERR) {
929 for (i = 0; i < dim_len; i++) {
930 id_vals[i] = (int64_t)id_vals_int[i];
931 }
932 }
933 free(id_vals_int);
934 }
935
936 if (status != NC_NOERR) {
937 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to get %s array from file id %d", id_table,
938 exoid);
939 ex_err_fn(exoid, __func__, errmsg, status);
940 free(id_vals);
941 return (EX_FATAL);
942 }
943
944 /* check if values in stored arrays are filled with non-zeroes */
945 filled = true;
946 sequential = true;
947 for (i = 0; i < dim_len; i++) {
948 if (id_vals[i] != i + 1) {
949 sequential = false;
950 }
951 if (id_vals[i] == EX_INVALID_ID || id_vals[i] == NC_FILL_INT) {
952 filled = false;
953 sequential = false;
954 break; /* id array hasn't been completely filled with valid ids yet */
955 }
956 }
957
958 if (filled) {
959 tmp_stats->valid_ids = true;
960 tmp_stats->sequential = sequential;
961 tmp_stats->num = dim_len;
962 tmp_stats->id_vals = id_vals;
963 }
964 }
965 else {
966 id_vals = tmp_stats->id_vals;
967 dim_len = tmp_stats->num;
968 sequential = tmp_stats->sequential;
969 }
970
971 if (sequential && num < dim_len) {
972 i = num - 1;
973 }
974 else {
975 /* Do a linear search through the id array to find the array value
976 corresponding to the passed index number */
977 for (i = 0; i < dim_len; i++) {
978 if (id_vals[i] == num) {
979 break; /* found the id requested */
980 }
981 }
982 }
983 if (i >= dim_len) /* failed to find id number */
984 {
985 if (!(tmp_stats->valid_ids)) {
986 free(id_vals);
987 }
988 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to locate id %" PRId64 " for file id %d", num,
989 exoid);
990 ex_set_err(__func__, errmsg, EX_LOOKUPFAIL);
991 return (-EX_LOOKUPFAIL); /*if we got here, the id array value doesn't exist */
992 }
993
994 /* Now check status array to see if object is null */
995 if ((tmp_stats->stat_vals == NULL) || (!(tmp_stats->valid_stat))) {
996
997 /* allocate space for new status array */
998 if (!(stat_vals = malloc(dim_len * sizeof(int)))) {
999 free(id_vals);
1000 snprintf(errmsg, MAX_ERR_LENGTH,
1001 "ERROR: failed to allocate memory for %s array for file id %d", id_table, exoid);
1002 ex_err_fn(exoid, __func__, errmsg, EX_MEMFAIL);
1003 return (EX_FATAL);
1004 }
1005
1006 /* first time through or status arrays haven't been filled yet */
1007 if (nc_inq_varid(exoid, stat_table, &varid) == NC_NOERR) {
1008 /* get variable id of status array */
1009 /* if status array exists, use it, otherwise assume object exists
1010 to be backward compatible */
1011
1012 if ((status = nc_get_var_int(exoid, varid, stat_vals)) != NC_NOERR) {
1013 free(id_vals);
1014 free(stat_vals);
1015 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to get %s array from file id %d",
1016 stat_table, exoid);
1017 ex_err_fn(exoid, __func__, errmsg, status);
1018 return (EX_FATAL);
1019 }
1020 }
1021 else {
1022 for (j = 0; j < dim_len; j++) {
1023 stat_vals[j] = 1;
1024 }
1025 }
1026
1027 if (tmp_stats->valid_ids) {
1028 /* status array is valid only if ids are valid */
1029 tmp_stats->valid_stat = true;
1030 tmp_stats->stat_vals = stat_vals;
1031 }
1032 }
1033 else {
1034 stat_vals = tmp_stats->stat_vals;
1035 }
1036
1037 if (!(tmp_stats->valid_ids)) {
1038 free(id_vals);
1039 }
1040
1041 if (stat_vals[i] == 0) /* is this object null? */ {
1042 ex_err_fn(exoid, __func__, "", EX_NULLENTITY);
1043 if (!(tmp_stats->valid_stat)) {
1044 free(stat_vals);
1045 }
1046 return (-((int)i + 1)); /* return index into id array (1-based) */
1047 }
1048 if (!(tmp_stats->valid_stat)) {
1049 free(stat_vals);
1050 }
1051 return (i + 1); /* return index into id array (1-based) */
1052 }
1053
1054 /******************************************************************************
1055 *
1056 * ex__get_stat_ptr - returns a pointer to a structure of object ids
1057 *
1058 *****************************************************************************/
1059
1060 /*! this routine returns a pointer to a structure containing the ids of
1061 * element blocks, node sets, or side sets according to exoid; if there
1062 * is not a structure that matches the exoid, one is created
1063 * \internal
1064 */
1065
ex__get_stat_ptr(int exoid,struct ex__obj_stats ** obj_ptr)1066 struct ex__obj_stats *ex__get_stat_ptr(int exoid, struct ex__obj_stats **obj_ptr)
1067 {
1068 struct ex__obj_stats *tmp_ptr;
1069
1070 tmp_ptr = *obj_ptr;
1071
1072 while (tmp_ptr) {
1073 if ((tmp_ptr)->exoid == exoid) {
1074 break;
1075 }
1076 tmp_ptr = (tmp_ptr)->next;
1077 }
1078
1079 if (!tmp_ptr) { /* exoid not found */
1080 tmp_ptr = (struct ex__obj_stats *)calloc(1, sizeof(struct ex__obj_stats));
1081 tmp_ptr->exoid = exoid;
1082 tmp_ptr->next = *obj_ptr;
1083 tmp_ptr->id_vals = 0;
1084 tmp_ptr->stat_vals = 0;
1085 tmp_ptr->num = 0;
1086 tmp_ptr->valid_ids = 0;
1087 tmp_ptr->valid_stat = 0;
1088 *obj_ptr = tmp_ptr;
1089 }
1090 return tmp_ptr;
1091 }
1092
1093 /******************************************************************************
1094 *
1095 * ex__rm_stat_ptr - removes a pointer to a structure of object ids
1096 *
1097 *****************************************************************************/
1098
1099 /*! this routine removes a pointer to a structure containing the ids of
1100 * element blocks, node sets, or side sets according to exoid; this
1101 * is necessary to clean up because netCDF reuses file ids; should be
1102 * called from ex_close
1103 * \internal
1104 */
1105
ex__rm_stat_ptr(int exoid,struct ex__obj_stats ** obj_ptr)1106 void ex__rm_stat_ptr(int exoid, struct ex__obj_stats **obj_ptr)
1107 {
1108 struct ex__obj_stats *last_head_list_ptr, *tmp_ptr;
1109
1110 tmp_ptr = *obj_ptr;
1111 last_head_list_ptr = *obj_ptr; /* save last head pointer */
1112
1113 while (tmp_ptr) /* Walk linked list of file ids/vals */
1114 {
1115 if (exoid == tmp_ptr->exoid) /* linear search for exodus file id */
1116 {
1117 if (tmp_ptr == *obj_ptr) { /* Are we at the head of the list? */
1118 *obj_ptr = (*obj_ptr)->next; /* yes, reset ptr to head of list */
1119 }
1120 else { /* no, remove this record from chain*/
1121 last_head_list_ptr->next = tmp_ptr->next;
1122 }
1123 free(tmp_ptr->id_vals); /* free up memory */
1124 free(tmp_ptr->stat_vals);
1125 free(tmp_ptr);
1126 break; /* Quit if found */
1127 }
1128 last_head_list_ptr = tmp_ptr; /* save last head pointer */
1129 tmp_ptr = tmp_ptr->next; /* Loop back if not */
1130 }
1131 }
1132
1133 /* structures to hold number of blocks of that type for each file id */
1134 static struct ex__list_item *ed_ctr_list = 0; /* edge blocks */
1135 static struct ex__list_item *fa_ctr_list = 0; /* face blocks */
1136 static struct ex__list_item *eb_ctr_list = 0; /* element blocks */
1137
1138 /* structures to hold number of sets of that type for each file id */
1139 static struct ex__list_item *ns_ctr_list = 0; /* node sets */
1140 static struct ex__list_item *es_ctr_list = 0; /* edge sets */
1141 static struct ex__list_item *fs_ctr_list = 0; /* face sets */
1142 static struct ex__list_item *ss_ctr_list = 0; /* side sets */
1143 static struct ex__list_item *els_ctr_list = 0; /* element sets */
1144
1145 /* structures to hold number of blobs/assemblies for each file id */
1146 static struct ex__list_item *assm_ctr_list = 0; /* assemblies */
1147 static struct ex__list_item *blob_ctr_list = 0; /* blobs */
1148
1149 /* structures to hold number of maps of that type for each file id */
1150 static struct ex__list_item *nm_ctr_list = 0; /* node maps */
1151 static struct ex__list_item *edm_ctr_list = 0; /* edge maps */
1152 static struct ex__list_item *fam_ctr_list = 0; /* face maps */
1153 static struct ex__list_item *em_ctr_list = 0; /* element maps */
1154
1155 /*!
1156 \internal
1157 \undoc
1158 */
ex__get_counter_list(ex_entity_type obj_type)1159 struct ex__list_item **ex__get_counter_list(ex_entity_type obj_type)
1160 {
1161 /* Thread-safe, but is dealing with globals */
1162 /* Only called from a routine which will be using locks */
1163 switch (obj_type) {
1164 case EX_ASSEMBLY: return &assm_ctr_list;
1165 case EX_BLOB: return &blob_ctr_list;
1166 case EX_ELEM_BLOCK: return &eb_ctr_list;
1167 case EX_NODE_SET: return &ns_ctr_list;
1168 case EX_SIDE_SET: return &ss_ctr_list;
1169 case EX_ELEM_MAP: return &em_ctr_list;
1170 case EX_NODE_MAP: return &nm_ctr_list;
1171 case EX_EDGE_BLOCK: return &ed_ctr_list;
1172 case EX_FACE_BLOCK: return &fa_ctr_list;
1173 case EX_EDGE_SET: return &es_ctr_list;
1174 case EX_FACE_SET: return &fs_ctr_list;
1175 case EX_ELEM_SET: return &els_ctr_list;
1176 case EX_EDGE_MAP: return &edm_ctr_list;
1177 case EX_FACE_MAP: return &fam_ctr_list;
1178 default: return (NULL);
1179 }
1180 }
1181
1182 /******************************************************************************
1183 *
1184 * ex__inc_file_item - increment file item
1185 *
1186 *****************************************************************************/
1187
1188 /*! this routine sets up a structure to track and increment a counter for
1189 * each open exodus file. it is designed to be used by the routines
1190 * ex_put_elem_block() and ex_put_set_param(),
1191 * to keep track of the number of element blocks, and each type of set,
1192 * respectively, for each open exodus II file.
1193 *
1194 * The list structure is used as follows:
1195 *
1196 * ptr -----------> list item structure
1197 * -------------------
1198 * exodus file id
1199 * item value (int)
1200 * ptr to next (NULL if last)
1201 *
1202 *
1203 * NOTE: since netCDF reuses its file ids, and a user may open and close any
1204 * number of files in one application, items must be taken out of the
1205 * linked lists in each of the above routines. these should be called
1206 * after ncclose().
1207 * \internal
1208 */
1209
ex__inc_file_item(int exoid,struct ex__list_item ** list_ptr)1210 int ex__inc_file_item(int exoid, /* file id */
1211 struct ex__list_item **list_ptr) /* ptr to ptr to list_item */
1212 {
1213 struct ex__list_item *tlist_ptr = *list_ptr; /* use temp list ptr to walk linked list */
1214 while (tlist_ptr) { /* Walk linked list of file ids/vals */
1215 if (exoid == tlist_ptr->exo_id) { /* linear search for exodus file id */
1216 break; /* Quit if found */
1217 }
1218 tlist_ptr = tlist_ptr->next; /* Loop back if not */
1219 }
1220
1221 if (!tlist_ptr) { /* ptr NULL? */
1222 /* allocate space for new structure record */
1223 tlist_ptr = (struct ex__list_item *)calloc(1, sizeof(struct ex__list_item));
1224 tlist_ptr->exo_id = exoid; /* insert file id */
1225 tlist_ptr->next = *list_ptr; /* insert into head of list */
1226 *list_ptr = tlist_ptr; /* fix up new head of list */
1227 }
1228 return (tlist_ptr->value++);
1229 }
1230
1231 /*****************************************************************************
1232 *
1233 * ex__get_file_item - return count
1234 *
1235 *****************************************************************************/
1236
1237 /*! this routine accesses a structure to track and increment a counter for
1238 * each open exodus file. it is designed to be used by the routines
1239 * ex_put_elem_block(), and ex_put_set_param(),
1240 * to get the number of element blocks, or a type of set,
1241 * respectively, for an open exodus II file.
1242 *
1243 * The list structure is used as follows:
1244 *
1245 * ptr -----------> list item structure
1246 * -------------------
1247 * exodus file id
1248 * item value (int)
1249 * ptr to next (NULL if last)
1250 *
1251 *
1252 * NOTE: since netCDF reuses its file ids, and a user may open and close any
1253 * number of files in one application, items must be taken out of the
1254 * linked lists in each of the above routines. these should be called
1255 * after nc_close().
1256 * \internal
1257 */
1258
ex__get_file_item(int exoid,struct ex__list_item ** list_ptr)1259 int ex__get_file_item(int exoid, /* file id */
1260 struct ex__list_item **list_ptr) /* ptr to ptr to list_item */
1261 {
1262 /* Not thread-safe: list_ptr passed in is a global
1263 * Would probably work ok with multiple threads since read-only,
1264 * but possible that list_ptr will be modified while being used
1265 */
1266 struct ex__list_item *tlist_ptr = *list_ptr; /* use temp list ptr to walk linked list */
1267 while (tlist_ptr) { /* Walk linked list of file ids/vals */
1268 if (exoid == tlist_ptr->exo_id) { /* linear search for exodus file id */
1269 break; /* Quit if found */
1270 }
1271 tlist_ptr = tlist_ptr->next; /* Loop back if not */
1272 }
1273
1274 if (!tlist_ptr) { /* ptr NULL? */
1275 return (-1);
1276 }
1277
1278 return (tlist_ptr->value);
1279 }
1280
1281 /*****************************************************************************
1282 *
1283 * ex__rm_file_item - remove file item
1284 *
1285 *****************************************************************************/
1286
1287 /*! this routine removes a structure to track and increment a counter for
1288 * each open exodus file.
1289 *
1290 * The list structure is used as follows:
1291 *
1292 * ptr -----------> list item structure
1293 * -------------------
1294 * exodus file id
1295 * item value (int)
1296 * ptr to next (NULL if last)
1297 *
1298 *
1299 * NOTE: since netCDF reuses its file ids, and a user may open and close any
1300 * number of files in one application, items must be taken out of the
1301 * linked lists in each of the above routines. these should be called
1302 * after ncclose().
1303 * \internal
1304 */
1305
ex__rm_file_item(int exoid,struct ex__list_item ** list_ptr)1306 void ex__rm_file_item(int exoid, /* file id */
1307 struct ex__list_item **list_ptr) /* ptr to ptr to list_item */
1308
1309 {
1310 struct ex__list_item *last_head_list_ptr = *list_ptr; /* save last head pointer */
1311
1312 struct ex__list_item *tlist_ptr = *list_ptr;
1313 while (tlist_ptr) { /* Walk linked list of file ids/vals */
1314 if (exoid == tlist_ptr->exo_id) { /* linear search for exodus file id */
1315 if (tlist_ptr == *list_ptr) { /* Are we at the head of the list? */
1316 *list_ptr = (*list_ptr)->next; /* yes, reset ptr to head of list */
1317 }
1318 else { /* no, remove this record from chain*/
1319 last_head_list_ptr->next = tlist_ptr->next;
1320 }
1321 free(tlist_ptr); /* free up memory */
1322 break; /* Quit if found */
1323 }
1324 last_head_list_ptr = tlist_ptr; /* save last head pointer */
1325 tlist_ptr = tlist_ptr->next; /* Loop back if not */
1326 }
1327 }
1328
1329 /*!
1330 \ingroup Utilities
1331 \undoc
1332 */
ex_get_num_props(int exoid,ex_entity_type obj_type)1333 int ex_get_num_props(int exoid, ex_entity_type obj_type)
1334 {
1335 int cntr, varid;
1336 char *var_name;
1337 char errmsg[MAX_ERR_LENGTH];
1338
1339 EX_FUNC_ENTER();
1340 cntr = 0;
1341
1342 /* loop until there is not a property variable defined; the name of */
1343 /* the variables begin with an increment of 1 ("xx_prop1") so use cntr+1 */
1344 while (true) {
1345 switch (obj_type) {
1346 case EX_ELEM_BLOCK: var_name = VAR_EB_PROP(cntr + 1); break;
1347 case EX_EDGE_BLOCK: var_name = VAR_ED_PROP(cntr + 1); break;
1348 case EX_FACE_BLOCK: var_name = VAR_FA_PROP(cntr + 1); break;
1349 case EX_NODE_SET: var_name = VAR_NS_PROP(cntr + 1); break;
1350 case EX_EDGE_SET: var_name = VAR_ES_PROP(cntr + 1); break;
1351 case EX_FACE_SET: var_name = VAR_FS_PROP(cntr + 1); break;
1352 case EX_SIDE_SET: var_name = VAR_SS_PROP(cntr + 1); break;
1353 case EX_ELEM_SET: var_name = VAR_ELS_PROP(cntr + 1); break;
1354 case EX_ASSEMBLY: var_name = VAR_ASSEMBLY_PROP(cntr + 1); break;
1355 case EX_BLOB: var_name = VAR_BLOB_PROP(cntr + 1); break;
1356 case EX_ELEM_MAP: var_name = VAR_EM_PROP(cntr + 1); break;
1357 case EX_FACE_MAP: var_name = VAR_FAM_PROP(cntr + 1); break;
1358 case EX_EDGE_MAP: var_name = VAR_EDM_PROP(cntr + 1); break;
1359 case EX_NODE_MAP: var_name = VAR_NM_PROP(cntr + 1); break;
1360 default:
1361 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: object type %d not supported; file id %d", obj_type,
1362 exoid);
1363 ex_err_fn(exoid, __func__, errmsg, EX_BADPARAM);
1364 EX_FUNC_LEAVE(EX_FATAL);
1365 }
1366
1367 if (nc_inq_varid(exoid, var_name, &varid) != NC_NOERR) {
1368 /* no variable with this name; return cntr which is now the number of */
1369 /* properties for this type of entity */
1370 EX_FUNC_LEAVE(cntr);
1371 }
1372 cntr++;
1373 }
1374 EX_FUNC_LEAVE(EX_FATAL);
1375 }
1376
1377 /*!
1378 \ingroup Utilities
1379 \undoc
1380 */
ex__get_cpu_ws(void)1381 int ex__get_cpu_ws(void) { return (sizeof(float)); }
1382
1383 /* swap - interchange v[i] and v[j] */
1384 /*!
1385 \internal
1386 \undoc
1387 */
ex_swap(int v[],int64_t i,int64_t j)1388 static void ex_swap(int v[], int64_t i, int64_t j)
1389 {
1390 /* Thread-safe, reentrant */
1391 int temp;
1392
1393 temp = v[i];
1394 v[i] = v[j];
1395 v[j] = temp;
1396 }
1397
1398 /*!
1399 \internal
1400 \undoc
1401 */
ex_swap64(int64_t v[],int64_t i,int64_t j)1402 static void ex_swap64(int64_t v[], int64_t i, int64_t j)
1403 {
1404 /* Thread-safe, reentrant */
1405 int64_t temp;
1406
1407 temp = v[i];
1408 v[i] = v[j];
1409 v[j] = temp;
1410 }
1411
1412 #define EX_QSORT_CUTOFF 12
1413
1414 /*!
1415 \internal
1416 \undoc
1417 */
ex_int_median3(int v[],int iv[],int64_t left,int64_t right)1418 static int ex_int_median3(int v[], int iv[], int64_t left, int64_t right)
1419 {
1420 /* Thread-safe, reentrant */
1421 int64_t center;
1422 center = (left + right) / 2;
1423
1424 if (v[iv[left]] > v[iv[center]]) {
1425 ex_swap(iv, left, center);
1426 }
1427 if (v[iv[left]] > v[iv[right]]) {
1428 ex_swap(iv, left, right);
1429 }
1430 if (v[iv[center]] > v[iv[right]]) {
1431 ex_swap(iv, center, right);
1432 }
1433
1434 ex_swap(iv, center, right - 1);
1435 return iv[right - 1];
1436 }
1437
1438 /*!
1439 \internal
1440 \undoc
1441 */
ex_int_median3_64(int64_t v[],int64_t iv[],int64_t left,int64_t right)1442 static int64_t ex_int_median3_64(int64_t v[], int64_t iv[], int64_t left, int64_t right)
1443 {
1444 /* Thread-safe, reentrant */
1445 int64_t center;
1446 center = (left + right) / 2;
1447
1448 if (v[iv[left]] > v[iv[center]]) {
1449 ex_swap64(iv, left, center);
1450 }
1451 if (v[iv[left]] > v[iv[right]]) {
1452 ex_swap64(iv, left, right);
1453 }
1454 if (v[iv[center]] > v[iv[right]]) {
1455 ex_swap64(iv, center, right);
1456 }
1457
1458 ex_swap64(iv, center, right - 1);
1459 return iv[right - 1];
1460 }
1461
1462 /*!
1463 \internal
1464 \undoc
1465 */
ex_int_iqsort(int v[],int iv[],int left,int right)1466 static void ex_int_iqsort(int v[], int iv[], int left, int right)
1467 {
1468 /* Thread-safe, reentrant */
1469 int pivot;
1470 int i, j;
1471
1472 if (left + EX_QSORT_CUTOFF <= right) {
1473 pivot = ex_int_median3(v, iv, left, right);
1474 i = left;
1475 j = right - 1;
1476
1477 for (;;) {
1478 while (v[iv[++i]] < v[pivot]) {
1479 ;
1480 }
1481 while (v[iv[--j]] > v[pivot]) {
1482 ;
1483 }
1484 if (i < j) {
1485 ex_swap(iv, i, j);
1486 }
1487 else {
1488 break;
1489 }
1490 }
1491
1492 ex_swap(iv, i, right - 1);
1493 ex_int_iqsort(v, iv, left, i - 1);
1494 ex_int_iqsort(v, iv, i + 1, right);
1495 }
1496 }
1497
1498 /*!
1499 \internal
1500 \undoc
1501 */
ex_int_iqsort64(int64_t v[],int64_t iv[],int64_t left,int64_t right)1502 static void ex_int_iqsort64(int64_t v[], int64_t iv[], int64_t left, int64_t right)
1503 {
1504 /* Thread-safe, reentrant */
1505 int64_t pivot;
1506 int64_t i, j;
1507
1508 if (left + EX_QSORT_CUTOFF <= right) {
1509 pivot = ex_int_median3_64(v, iv, left, right);
1510 i = left;
1511 j = right - 1;
1512
1513 for (;;) {
1514 while (v[iv[++i]] < v[pivot]) {
1515 ;
1516 }
1517 while (v[iv[--j]] > v[pivot]) {
1518 ;
1519 }
1520 if (i < j) {
1521 ex_swap64(iv, i, j);
1522 }
1523 else {
1524 break;
1525 }
1526 }
1527
1528 ex_swap64(iv, i, right - 1);
1529 ex_int_iqsort64(v, iv, left, i - 1);
1530 ex_int_iqsort64(v, iv, i + 1, right);
1531 }
1532 }
1533
1534 /*!
1535 \internal
1536 \undoc
1537 */
ex_int_iisort(int v[],int iv[],int N)1538 static void ex_int_iisort(int v[], int iv[], int N)
1539 {
1540 /* Thread-safe, reentrant */
1541 int i, j;
1542 int ndx = 0;
1543 int small;
1544 int tmp;
1545
1546 small = v[iv[0]];
1547 for (i = 1; i < N; i++) {
1548 if (v[iv[i]] < small) {
1549 small = v[iv[i]];
1550 ndx = i;
1551 }
1552 }
1553 /* Put smallest value in slot 0 */
1554 ex_swap(iv, 0, ndx);
1555
1556 for (i = 1; i < N; i++) {
1557 tmp = iv[i];
1558 for (j = i; v[tmp] < v[iv[j - 1]]; j--) {
1559 iv[j] = iv[j - 1];
1560 }
1561 iv[j] = tmp;
1562 }
1563 }
1564
1565 /*!
1566 \internal
1567 \undoc
1568 */
ex_int_iisort64(int64_t v[],int64_t iv[],int64_t N)1569 static void ex_int_iisort64(int64_t v[], int64_t iv[], int64_t N)
1570 {
1571 /* Thread-safe, reentrant */
1572 int64_t i, j;
1573 int64_t ndx = 0;
1574 int64_t small;
1575 int64_t tmp;
1576
1577 small = v[iv[0]];
1578 for (i = 1; i < N; i++) {
1579 if (v[iv[i]] < small) {
1580 small = v[iv[i]];
1581 ndx = i;
1582 }
1583 }
1584 /* Put smallest value in slot 0 */
1585 ex_swap64(iv, 0, ndx);
1586
1587 for (i = 1; i < N; i++) {
1588 tmp = iv[i];
1589 for (j = i; v[tmp] < v[iv[j - 1]]; j--) {
1590 iv[j] = iv[j - 1];
1591 }
1592 iv[j] = tmp;
1593 }
1594 }
1595
1596 /*!
1597 * \ingroup Utilities
1598 * \internal
1599 * The following 'indexed qsort' routine is modified from Sedgewicks
1600 * algorithm It selects the pivot based on the median of the left,
1601 * right, and center values to try to avoid degenerate cases ocurring
1602 * when a single value is chosen. It performs a quicksort on
1603 * intervals down to the #EX_QSORT_CUTOFF size and then performs a final
1604 * insertion sort on the almost sorted final array. Based on data in
1605 * Sedgewick, the #EX_QSORT_CUTOFF value should be between 5 and 20.
1606 *
1607 * See Sedgewick for further details
1608 * Define `DEBUG_QSORT` at the top of this file and recompile to compile
1609 * in code that verifies that the array is sorted.
1610 *
1611 * NOTE: The 'int' implementation below assumes that *both* the items
1612 * being sorted and the *number* of items being sorted are both
1613 * representable as 'int'.
1614 * \internal
1615 */
ex__iqsort(int v[],int iv[],int N)1616 void ex__iqsort(int v[], int iv[], int N)
1617 {
1618 /* Thread-safe, reentrant */
1619 ex_int_iqsort(v, iv, 0, N - 1);
1620 ex_int_iisort(v, iv, N);
1621
1622 #if defined(DEBUG_QSORT)
1623 fprintf(stderr, "Checking sort of %d values\n", N + 1);
1624 int i;
1625 for (i = 1; i < N; i++) {
1626 assert(v[iv[i - 1]] <= v[iv[i]]);
1627 }
1628 #endif
1629 }
1630
1631 /*! \sa ex__iqsort() */
ex__iqsort64(int64_t v[],int64_t iv[],int64_t N)1632 void ex__iqsort64(int64_t v[], int64_t iv[], int64_t N)
1633 {
1634 /* Thread-safe, reentrant */
1635 ex_int_iqsort64(v, iv, 0, N - 1);
1636 ex_int_iisort64(v, iv, N);
1637
1638 #if defined(DEBUG_QSORT)
1639 fprintf(stderr, "Checking sort of %" PRId64 " values\n", N + 1);
1640 int i;
1641 for (i = 1; i < N; i++) {
1642 assert(v[iv[i - 1]] <= v[iv[i]]);
1643 }
1644 #endif
1645 }
1646
1647 /*!
1648 * Determine whether the new large model storage is being used in this
1649 * file, or old method. Basically, the difference is whether the
1650 * coordinates and nodal variables are stored in a blob (xyz
1651 * components together) or as a variable per component per
1652 * nodal_variable.
1653 *
1654 * \ingroup Utilities
1655 */
ex_large_model(int exoid)1656 int ex_large_model(int exoid)
1657 {
1658 if (exoid < 0) {
1659 return (EXODUS_DEFAULT_SIZE); /* Specified in exodusII_int.h */
1660 }
1661
1662 /* See if the ATT_FILESIZE attribute is defined in the file */
1663 EX_FUNC_ENTER();
1664 int file_size = 0;
1665 int rootid = exoid & EX_FILE_ID_MASK;
1666 if (nc_get_att_int(rootid, NC_GLOBAL, ATT_FILESIZE, &file_size) != NC_NOERR) {
1667 /* Variable not found; default is 0 */
1668 file_size = 0;
1669 }
1670 EX_FUNC_LEAVE(file_size);
1671 }
1672
1673 /*!
1674 \internal
1675 \undoc
1676 */
ex__get_dimension(int exoid,const char * DIMENSION,const char * label,size_t * count,int * dimid,const char * routine)1677 int ex__get_dimension(int exoid, const char *DIMENSION, const char *label, size_t *count,
1678 int *dimid, const char *routine)
1679 {
1680 char errmsg[MAX_ERR_LENGTH];
1681 int status;
1682
1683 *count = 0;
1684 *dimid = -1;
1685
1686 if ((status = nc_inq_dimid(exoid, DIMENSION, dimid)) != NC_NOERR) {
1687 if (routine != NULL) {
1688 if (status == NC_EBADDIM) {
1689 snprintf(errmsg, MAX_ERR_LENGTH, "Warning: no dimension defining '%s' found in file id %d",
1690 label, exoid);
1691 ex_err_fn(exoid, __func__, errmsg, status);
1692 }
1693 else {
1694 snprintf(errmsg, MAX_ERR_LENGTH,
1695 "ERROR: failed to locate dimension defining number of '%s' in file id %d", label,
1696 exoid);
1697 ex_err_fn(exoid, __func__, errmsg, status);
1698 }
1699 }
1700 return status;
1701 }
1702
1703 if ((status = nc_inq_dimlen(exoid, *dimid, count)) != NC_NOERR) {
1704 if (routine != NULL) {
1705 snprintf(errmsg, MAX_ERR_LENGTH,
1706 "ERROR: failed to get length of dimension defining number of '%s' in file id %d",
1707 label, exoid);
1708 ex_err_fn(exoid, __func__, errmsg, status);
1709 }
1710 return status;
1711 }
1712 return status;
1713 }
1714
1715 /*!
1716 \deprecated
1717 */
ex_header_size(int exoid)1718 size_t ex_header_size(int exoid)
1719 {
1720 EX_UNUSED(exoid);
1721 return 0;
1722 }
1723
ex__set_compact_storage(int exoid,int varid)1724 void ex__set_compact_storage(int exoid, int varid)
1725 {
1726 /* Capability was released in version 4.7.4
1727 Only applicable to netcdf-4 files, but will
1728 succeed on other files; just won't do anything
1729 */
1730 #if defined(NC_COMPACT)
1731 nc_def_var_chunking(exoid, varid, NC_COMPACT, NULL);
1732 #else
1733 EX_UNUSED(exoid);
1734 EX_UNUSED(varid);
1735 #endif
1736 }
1737
1738 /* type = 1 for integer, 2 for real, 3 for character */
1739 /*!
1740 \internal
1741 \undoc
1742 */
ex__compress_variable(int exoid,int varid,int type)1743 void ex__compress_variable(int exoid, int varid, int type)
1744 {
1745 #if NC_HAS_HDF5
1746 struct ex__file_item *file = ex__find_file_item(exoid);
1747
1748 if (!file) {
1749 char errmsg[MAX_ERR_LENGTH];
1750 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: unknown file id %d for ex__compress_variable().",
1751 exoid);
1752 ex_err_fn(exoid, __func__, errmsg, EX_BADFILEID);
1753 }
1754 else {
1755 /* Compression only supported on HDF5 (NetCDF-4) files; Do not try to compress character data */
1756 if (type != 3 && file->is_hdf5) {
1757 if (file->compression_algorithm == EX_COMPRESS_GZIP) {
1758 int deflate_level = file->compression_level;
1759 int compress = 1;
1760 int shuffle = file->shuffle;
1761 if (deflate_level > 0) {
1762 nc_def_var_deflate(exoid, varid, shuffle, compress, deflate_level);
1763 }
1764 }
1765 else if (file->compression_algorithm == EX_COMPRESS_SZIP) {
1766 #if NC_HAS_SZIP_WRITE == 1
1767 /* See: https://support.hdfgroup.org/doc_resource/SZIP/ and
1768 https://support.hdfgroup.org/HDF5/doc/RM/RM_H5P.html#Property-SetSzip
1769 for details on SZIP library and parameters.
1770 */
1771
1772 /* const int NC_SZIP_EC = 4; */ /* Selects entropy coding method for szip. */
1773 const int NC_SZIP_NN = 32; /* Selects nearest neighbor coding method for szip. */
1774 /* Even and between 4 and 32; typical values are 8, 10, 16, 32 */
1775 const int SZIP_PIXELS_PER_BLOCK =
1776 file->compression_level == 0 ? 32 : file->compression_level;
1777 nc_def_var_szip(exoid, varid, NC_SZIP_NN, SZIP_PIXELS_PER_BLOCK);
1778 #else
1779 char errmsg[MAX_ERR_LENGTH];
1780 snprintf(errmsg, MAX_ERR_LENGTH,
1781 "ERROR: Compression algorithm SZIP is not supported yet (EXPERIMENTAL).");
1782 ex_err_fn(exoid, __func__, errmsg, EX_BADPARAM);
1783 #endif
1784 }
1785
1786 #if defined(PARALLEL_AWARE_EXODUS)
1787 if (file->is_parallel && file->is_hdf5) {
1788 nc_var_par_access(exoid, varid, NC_COLLECTIVE);
1789 }
1790 #endif
1791 }
1792 }
1793 #endif
1794 }
1795
1796 /*!
1797 \internal
1798 \undoc
1799 */
ex__leavedef(int exoid,const char * call_rout)1800 int ex__leavedef(int exoid, const char *call_rout)
1801 {
1802 char errmsg[MAX_ERR_LENGTH];
1803 int status;
1804
1805 if ((status = nc_enddef(exoid)) != NC_NOERR) {
1806 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to complete definition for file id %d", exoid);
1807 ex_err_fn(exoid, call_rout, errmsg, status);
1808
1809 return (EX_FATAL);
1810 }
1811 return (EX_NOERR);
1812 }
1813
1814 static int warning_output = 0;
1815
ex__check_version(int run_version)1816 int ex__check_version(int run_version)
1817 {
1818 if (run_version != EX_API_VERS_NODOT && warning_output == 0) {
1819 int run_version_major = run_version / 100;
1820 int run_version_minor = run_version % 100;
1821 int lib_version_major = EXODUS_VERSION_MAJOR;
1822 int lib_version_minor = EXODUS_VERSION_MINOR;
1823 fprintf(stderr,
1824 "EXODUS: Warning: This code was compiled with exodus "
1825 "version %d.%02d,\n but was linked with exodus "
1826 "library version %d.%02d\n This is probably an "
1827 "error in the build process of this code.\n",
1828 run_version_major, run_version_minor, lib_version_major, lib_version_minor);
1829 warning_output = 1;
1830 }
1831 return warning_output;
1832 }
1833
1834 /*!
1835 \internal
1836 \undoc
1837 */
ex__handle_mode(unsigned int my_mode,int is_parallel,int run_version)1838 int ex__handle_mode(unsigned int my_mode, int is_parallel, int run_version)
1839 {
1840 char errmsg[MAX_ERR_LENGTH];
1841 int nc_mode = 0;
1842 static int netcdf4_mode = -1;
1843 #if NC_HAS_CDF5
1844 static int netcdf5_mode = -1;
1845 #endif
1846
1847 int filesiz = 1;
1848 int int64_status;
1849 int pariomode = 0;
1850
1851 /* Contains a 1 in all bits corresponding to file modes */
1852 /* Do not include EX_64BIT_DATA in this list */
1853 static unsigned int all_modes = EX_NORMAL_MODEL | EX_64BIT_OFFSET | EX_NETCDF4 | EX_PNETCDF;
1854
1855 ex__check_version(run_version);
1856
1857 /*
1858 * See if specified mode is supported in the version of netcdf we
1859 * are using
1860 */
1861 #if !NC_HAS_HDF5
1862 if (my_mode & EX_NETCDF4) {
1863 snprintf(errmsg, MAX_ERR_LENGTH,
1864 "EXODUS: ERROR: File format specified as netcdf-4, but the "
1865 "NetCDF library being used was not configured to enable "
1866 "this format\n");
1867 ex_err(__func__, errmsg, EX_BADPARAM);
1868 EX_FUNC_LEAVE(EX_FATAL);
1869 }
1870 #endif
1871
1872 #if !NC_HAS_CDF5
1873 if (my_mode & EX_64BIT_DATA) {
1874 snprintf(errmsg, MAX_ERR_LENGTH,
1875 "EXODUS: ERROR: File format specified as 64bit_data, but "
1876 "the NetCDF library being used does not support this "
1877 "format\n");
1878 ex_err(__func__, errmsg, EX_BADPARAM);
1879 EX_FUNC_LEAVE(EX_FATAL);
1880 }
1881 #endif
1882
1883 /* EX_64_BIT_DATA is 64-bit integer version of EX_PNETCDF. If
1884 EX_64_BIT_DATA and EX_PNETCDF is not set, then set EX_PNETCDF... */
1885 if (my_mode & EX_64BIT_DATA) {
1886 my_mode |= EX_PNETCDF;
1887 }
1888
1889 /* Check that one and only one format mode is specified... */
1890 {
1891 unsigned int set_modes = all_modes & my_mode;
1892
1893 if (set_modes == 0) {
1894 my_mode |= EX_64BIT_OFFSET; /* Default if nothing specified */
1895 }
1896 else {
1897 /* Checks that only a single bit is set */
1898 set_modes = !(set_modes & (set_modes - 1));
1899 if (!set_modes) {
1900 snprintf(errmsg, MAX_ERR_LENGTH,
1901 "EXODUS: ERROR: More than 1 file format "
1902 "(EX_NORMAL_MODEL, EX_LARGE_MODEL, EX_64BIT_OFFSET, "
1903 "or EX_NETCDF4)\nwas specified in the "
1904 "mode argument of the ex_create call. Only a single "
1905 "format can be specified.\n");
1906 ex_err(__func__, errmsg, EX_BADPARAM);
1907 EX_FUNC_LEAVE(EX_FATAL);
1908 }
1909 }
1910 }
1911
1912 /*
1913 * See if any integer data is to be stored as int64 (long long). If
1914 * so, then need to set NC_NETCDF4 and unset NC_CLASSIC_MODEL (or
1915 * set EX_NOCLASSIC. Output meaningful error message if the library
1916 * is not NetCDF-4 enabled...
1917 *
1918 * As of netcdf-4.4.0, can also use NC_64BIT_DATA (CDF5) mode for this...
1919 */
1920 int64_status = my_mode & (EX_ALL_INT64_DB | EX_ALL_INT64_API);
1921
1922 if ((int64_status & EX_ALL_INT64_DB) != 0) {
1923 #if NC_HAS_HDF5 || NC_HAS_CDF5
1924 /* Library DOES support netcdf4 and/or cdf5 ... See if user
1925 * specified either of these and use that one; if not, pick
1926 * netcdf4, non-classic as default.
1927 */
1928 if (my_mode & EX_NETCDF4) {
1929 my_mode |= EX_NOCLASSIC;
1930 }
1931 #if NC_HAS_CDF5
1932 else if (my_mode & EX_64BIT_DATA) {
1933 ; /* Do nothing, already set */
1934 }
1935 else if (my_mode & EX_PNETCDF) {
1936 my_mode |= EX_64BIT_DATA;
1937 }
1938 #endif
1939 else {
1940 /* Unset the current mode so we don't have multiples specified */
1941 /* ~all_modes sets to 1 all bits not associated with file format */
1942 my_mode &= ~all_modes;
1943 #if NC_HAS_HDF5
1944 /* Pick netcdf4 as default mode for 64-bit integers */
1945 my_mode |= EX_NOCLASSIC;
1946 my_mode |= EX_NETCDF4;
1947 #else
1948 /* Pick 64bit_data as default mode for 64-bit integers */
1949 my_mode |= EX_64BIT_DATA;
1950 #endif
1951 }
1952 #else
1953 /* Library does NOT support netcdf4 or cdf5 */
1954 snprintf(errmsg, MAX_ERR_LENGTH,
1955 "EXODUS: ERROR: 64-bit integer storage requested, but the "
1956 "netcdf library does not support the required netcdf-4 or "
1957 "64BIT_DATA extensions.\n");
1958 ex_err(__func__, errmsg, EX_BADPARAM);
1959 EX_FUNC_LEAVE(EX_FATAL);
1960 #endif
1961 }
1962
1963 #if defined(PARALLEL_AWARE_EXODUS)
1964 /* Check parallel io mode. Valid is NC_MPIPOSIX or NC_MPIIO or NC_PNETCDF
1965 * Exodus uses different flag values; map to netcdf values
1966 *
1967 * NOTE: In current versions of NetCDF, MPIPOSIX and MPIIO are ignored and the
1968 * underlying format is either NC_PNETCDF or NC_NETCDF4 (hdf5-based)
1969 * They map NC_MPIIO to NC_PNETCDF, but in the past, exodus mapped EX_MPIIO
1970 * to EX_NETCDF4.
1971 */
1972 if (is_parallel) {
1973 int tmp_mode = 0;
1974 if (my_mode & EX_MPIPOSIX) {
1975 pariomode = NC_MPIIO;
1976 tmp_mode = EX_NETCDF4;
1977 #if !NC_HAS_HDF5
1978 snprintf(errmsg, MAX_ERR_LENGTH,
1979 "EXODUS: ERROR: EX_MPIPOSIX parallel output requested "
1980 "which requires NetCDF-4 support, but the library does "
1981 "not have that option enabled.\n");
1982 ex_err(__func__, errmsg, EX_BADPARAM);
1983 EX_FUNC_LEAVE(EX_FATAL);
1984 #endif
1985 }
1986 else if (my_mode & EX_MPIIO) {
1987 pariomode = NC_MPIIO;
1988 tmp_mode = EX_NETCDF4;
1989 #if !NC_HAS_HDF5
1990 snprintf(errmsg, MAX_ERR_LENGTH,
1991 "EXODUS: ERROR: EX_MPIIO parallel output requested which "
1992 "requires NetCDF-4 support, but the library does not "
1993 "have that option enabled.\n");
1994 ex_err(__func__, errmsg, EX_BADPARAM);
1995 EX_FUNC_LEAVE(EX_FATAL);
1996 #endif
1997 }
1998 else if (my_mode & EX_NETCDF4) {
1999 pariomode = NC_MPIIO;
2000 tmp_mode = EX_NETCDF4;
2001 #if !NC_HAS_HDF5
2002 snprintf(errmsg, MAX_ERR_LENGTH,
2003 "EXODUS: ERROR: EX_NETCDF4 parallel output requested which "
2004 "requires NetCDF-4 support, but the library does not "
2005 "have that option enabled.\n");
2006 ex_err(__func__, errmsg, EX_BADPARAM);
2007 EX_FUNC_LEAVE(EX_FATAL);
2008 #endif
2009 }
2010 else if (my_mode & EX_PNETCDF) {
2011 pariomode = NC_PNETCDF;
2012 /* See if client specified 64-bit or not... */
2013 if ((my_mode & EX_64BIT_DATA) || (int64_status & EX_ALL_INT64_DB)) {
2014 tmp_mode = EX_64BIT_DATA;
2015 }
2016 else {
2017 if (my_mode & EX_64BIT_DATA) {
2018 tmp_mode = EX_64BIT_DATA;
2019 }
2020 else {
2021 tmp_mode = EX_64BIT_OFFSET;
2022 }
2023 }
2024 #if !NC_HAS_PNETCDF
2025 snprintf(errmsg, MAX_ERR_LENGTH,
2026 "EXODUS: ERROR: EX_PNETCDF parallel output requested "
2027 "which requires PNetCDF support, but the library does "
2028 "not have that option enabled.\n");
2029 ex_err(__func__, errmsg, EX_BADPARAM);
2030 EX_FUNC_LEAVE(EX_FATAL);
2031 #endif
2032 }
2033
2034 /* If tmp_mode was set here, then need to clear any other mode that
2035 was potentially already set in my_mode... */
2036 my_mode &= ~all_modes;
2037 my_mode |= tmp_mode;
2038 }
2039 #else
2040 EX_UNUSED(is_parallel);
2041 #endif /* PARALLEL_AWARE_EXODUS */
2042
2043 if (my_mode & EX_NETCDF4) {
2044 nc_mode |= NC_NETCDF4;
2045 }
2046 else {
2047 if (netcdf4_mode == -1) {
2048 char *option = getenv("EXODUS_NETCDF4");
2049 if (option != NULL) {
2050 netcdf4_mode = NC_NETCDF4;
2051 if (option[0] != 'q') {
2052 fprintf(stderr, "EXODUS: Using netcdf version 4 selected via "
2053 "EXODUS_NETCDF4 environment variable\n");
2054 }
2055 }
2056 else {
2057 netcdf4_mode = 0;
2058 }
2059 }
2060 nc_mode |= netcdf4_mode;
2061 }
2062
2063 if (!(my_mode & EX_NOCLASSIC)) {
2064 nc_mode |= NC_CLASSIC_MODEL;
2065 }
2066
2067 #if NC_HAS_CDF5
2068 if (my_mode & EX_64BIT_DATA) {
2069 nc_mode |= (NC_64BIT_DATA);
2070 }
2071 else {
2072 if (netcdf5_mode == -1) {
2073 char *option = getenv("EXODUS_NETCDF5");
2074 if (option != NULL) {
2075 netcdf5_mode = NC_64BIT_DATA;
2076 if (option[0] != 'q') {
2077 fprintf(stderr, "EXODUS: Using netcdf version 5 (CDF5) selected via "
2078 "EXODUS_NETCDF5 environment variable\n");
2079 }
2080 }
2081 else {
2082 netcdf5_mode = 0;
2083 }
2084 }
2085 nc_mode |= netcdf5_mode;
2086 }
2087 #endif
2088
2089 /*
2090 * Hardwire filesiz to 1 for all created files. Reduce complexity in nodal output routines.
2091 * has been default for a decade or so, but still support it on read...
2092 */
2093 if (
2094 #if NC_HAS_HDF5
2095 !(nc_mode & NC_NETCDF4) &&
2096 #endif
2097 #if NC_HAS_CDF5
2098 !(nc_mode & NC_64BIT_DATA) &&
2099 #endif
2100 filesiz == 1) {
2101 nc_mode |= NC_64BIT_OFFSET;
2102 }
2103
2104 if (my_mode & EX_SHARE) {
2105 nc_mode |= NC_SHARE;
2106 }
2107
2108 /*
2109 * set error handling mode to no messages, non-fatal errors
2110 * unless specified differently via environment.
2111 */
2112 {
2113 char *option = getenv("EXODUS_VERBOSE");
2114 if (option != NULL) {
2115 exoptval = EX_VERBOSE;
2116 }
2117 ex_opts(exoptval); /* call required to set ncopts first time through */
2118 }
2119
2120 if (my_mode & EX_CLOBBER) {
2121 nc_mode |= NC_CLOBBER;
2122 }
2123 else {
2124 nc_mode |= NC_NOCLOBBER;
2125 }
2126
2127 #if NC_HAS_DISKLESS
2128 /* Use of diskless (in-memory) and parallel is not tested... */
2129 if (my_mode & EX_DISKLESS) {
2130 nc_mode |= NC_DISKLESS;
2131 nc_mode |= NC_WRITE;
2132 #if defined NC_PERSIST
2133 nc_mode |= NC_PERSIST;
2134 #endif
2135 }
2136 #endif
2137 return nc_mode | pariomode;
2138 }
2139
2140 /*!
2141 \internal
2142 \undoc
2143 */
ex__populate_header(int exoid,const char * path,int my_mode,int is_parallel,int * comp_ws,int * io_ws)2144 int ex__populate_header(int exoid, const char *path, int my_mode, int is_parallel, int *comp_ws,
2145 int *io_ws)
2146 {
2147 int status;
2148 int old_fill;
2149 int lio_ws;
2150 int filesiz = 1;
2151 bool is_hdf5 = false;
2152 bool is_pnetcdf = false;
2153
2154 float version;
2155 char errmsg[MAX_ERR_LENGTH];
2156 int int64_status = my_mode & (EX_ALL_INT64_DB | EX_ALL_INT64_API);
2157
2158 int format = 0;
2159 int mode;
2160
2161 /* turn off automatic filling of netCDF variables */
2162 if ((status = nc_set_fill(exoid, NC_NOFILL, &old_fill)) != NC_NOERR) {
2163 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to set nofill mode in file id %d", exoid);
2164 ex_err_fn(exoid, __func__, errmsg, status);
2165 return (EX_FATAL);
2166 }
2167
2168 /* Verify that there is not an existing file_item struct for this
2169 exoid This could happen (and has) when application calls
2170 ex_open(), but then closes file using nc_close() and then reopens
2171 file. NetCDF will possibly reuse the exoid which results in
2172 internal corruption in exodus data structures since exodus does
2173 not know that file was closed and possibly new file opened for
2174 this exoid
2175 */
2176 if (ex__find_file_item(exoid) != NULL) {
2177 snprintf(errmsg, MAX_ERR_LENGTH,
2178 "ERROR: There is an existing file already using the file "
2179 "id %d which was also assigned to file %s.\n\tWas "
2180 "nc_close() called instead of ex_close() on an open Exodus "
2181 "file?\n",
2182 exoid, path);
2183 ex_err_fn(exoid, __func__, errmsg, EX_BADFILEID);
2184 nc_close(exoid);
2185 return (EX_FATAL);
2186 }
2187
2188 /* initialize floating point size conversion. since creating new file,
2189 * i/o wordsize attribute from file is zero.
2190 */
2191 /* Determine format being used for underlying NetCDF file */
2192 nc_inq_format_extended(exoid, &format, &mode);
2193
2194 if (format & NC_FORMAT_PNETCDF) {
2195 is_pnetcdf = true;
2196 ;
2197 }
2198
2199 if (format & NC_FORMAT_NC_HDF5) {
2200 is_hdf5 = true;
2201 }
2202
2203 if (ex__conv_init(exoid, comp_ws, io_ws, 0, int64_status, is_parallel, is_hdf5, is_pnetcdf,
2204 my_mode & EX_WRITE) != EX_NOERR) {
2205 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to init conversion routines in file id %d",
2206 exoid);
2207 ex_err_fn(exoid, __func__, errmsg, EX_LASTERR);
2208 return (EX_FATAL);
2209 }
2210
2211 /* put the EXODUS version number, and i/o floating point word size as
2212 * netcdf global attributes
2213 */
2214
2215 /* store Exodus API version # as an attribute */
2216 {
2217 float version_major = EXODUS_VERSION_MAJOR;
2218 float version_minor = EXODUS_VERSION_MINOR;
2219 version = version_major + version_minor / 100.0;
2220 if ((status = nc_put_att_float(exoid, NC_GLOBAL, ATT_API_VERSION, NC_FLOAT, 1, &version)) !=
2221 NC_NOERR) {
2222 snprintf(errmsg, MAX_ERR_LENGTH,
2223 "ERROR: failed to store Exodus II API version attribute in file id %d", exoid);
2224 ex_err_fn(exoid, __func__, errmsg, status);
2225 return (EX_FATAL);
2226 }
2227 }
2228
2229 /* store Exodus file version # as an attribute */
2230 {
2231 float version_major = EXODUS_VERSION_MAJOR;
2232 float version_minor = EXODUS_VERSION_MINOR;
2233 version = version_major + version_minor / 100.0;
2234 if ((status = nc_put_att_float(exoid, NC_GLOBAL, ATT_VERSION, NC_FLOAT, 1, &version)) !=
2235 NC_NOERR) {
2236 snprintf(errmsg, MAX_ERR_LENGTH,
2237 "ERROR: failed to store Exodus II file version attribute in file id %d", exoid);
2238 ex_err_fn(exoid, __func__, errmsg, status);
2239 return (EX_FATAL);
2240 }
2241 }
2242
2243 /* store Exodus file float word size as an attribute */
2244 lio_ws = (*io_ws);
2245 if ((status = nc_put_att_int(exoid, NC_GLOBAL, ATT_FLT_WORDSIZE, NC_INT, 1, &lio_ws)) !=
2246 NC_NOERR) {
2247 snprintf(errmsg, MAX_ERR_LENGTH,
2248 "ERROR: failed to store Exodus II file float word size "
2249 "attribute in file id %d",
2250 exoid);
2251 ex_err_fn(exoid, __func__, errmsg, status);
2252 return (EX_FATAL);
2253 }
2254
2255 /* store Exodus file size (1=large, 0=normal) as an attribute */
2256 if ((status = nc_put_att_int(exoid, NC_GLOBAL, ATT_FILESIZE, NC_INT, 1, &filesiz)) != NC_NOERR) {
2257 snprintf(errmsg, MAX_ERR_LENGTH,
2258 "ERROR: failed to store Exodus II file size attribute in file id %d", exoid);
2259 ex_err_fn(exoid, __func__, errmsg, status);
2260 return (EX_FATAL);
2261 }
2262
2263 {
2264 int max_so_far = 32;
2265 if ((status = nc_put_att_int(exoid, NC_GLOBAL, ATT_MAX_NAME_LENGTH, NC_INT, 1, &max_so_far)) !=
2266 NC_NOERR) {
2267 snprintf(errmsg, MAX_ERR_LENGTH,
2268 "ERROR: failed to add maximum_name_length attribute in file id %d", exoid);
2269 ex_err_fn(exoid, __func__, errmsg, status);
2270 return (EX_FATAL);
2271 }
2272 }
2273
2274 {
2275 int int64_db_status = int64_status & EX_ALL_INT64_DB;
2276 if ((status = nc_put_att_int(exoid, NC_GLOBAL, ATT_INT64_STATUS, NC_INT, 1,
2277 &int64_db_status)) != NC_NOERR) {
2278 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to add int64_status attribute in file id %d",
2279 exoid);
2280 ex_err_fn(exoid, __func__, errmsg, status);
2281 return (EX_FATAL);
2282 }
2283 }
2284
2285 #if 0
2286 /* Testing to see if can eliminate some nc_enddef movement of vars/recs */
2287 if ((status = nc__enddef(exoid, 10000, 4, 10000, 4)) != NC_NOERR) {
2288 #else
2289 if ((status = nc_enddef(exoid)) != NC_NOERR) {
2290 #endif
2291 snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to complete definition for file id %d", exoid);
2292 ex_err_fn(exoid, __func__, errmsg, status);
2293 return (EX_FATAL);
2294 }
2295 return EX_NOERR;
2296 }
2297
2298 /*!
2299 \internal
2300 \undoc
2301 Safer than strncpy -- guarantees null termination
2302 */
2303 char *ex_copy_string(char *dest, char const *source, size_t elements)
2304 {
2305 char *d;
2306 for (d = dest; d + 1 < dest + elements && *source; d++, source++) {
2307 *d = *source;
2308 }
2309 *d = '\0';
2310 return d;
2311 }
2312