1 /*
2  * Copyright (c) 2005-2017 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  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *
13  *     * Redistributions in binary form must reproduce the above
14  *       copyright notice, this list of conditions and the following
15  *       disclaimer in the documentation and/or other materials provided
16  *       with the distribution.
17  *
18  *     * Neither the name of NTESS nor the names of its
19  *       contributors may be used to endorse or promote products derived
20  *       from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  */
35 
36 #include "exodusII.h"     // for ex_set, ex_err, etc
37 #include "exodusII_int.h" // for EX_FATAL, etc
38 #include "vtk_netcdf.h"       // for NC_NOERR, nc_def_var, etc
39 #include <inttypes.h>     // for PRId64
40 #include <stddef.h>       // for size_t
41 #include <stdio.h>
42 #include <stdlib.h>    // for NULL, free, malloc
43 #include <sys/types.h> // for int64_t
44 
45 /*!
46  * writes the set parameters and optionally set data for 1 or more sets
47  * \param   exoid                   exodus file id
48  * \param   set_count               number of sets to write
49  * \param  *sets                    array of ex_set structures
50  */
51 
ex_put_sets(int exoid,size_t set_count,const struct ex_set * sets)52 int ex_put_sets(int exoid, size_t set_count, const struct ex_set *sets)
53 {
54   size_t i;
55   int    needs_define = 0;
56   int    set_stat;
57   int    dimid, varid, status, dims[1];
58   int    set_id_ndx;
59   size_t start[1];
60   int    cur_num_sets;
61   char   errmsg[MAX_ERR_LENGTH];
62   int *  sets_to_define = NULL;
63   char * numentryptr    = NULL;
64   char * entryptr       = NULL;
65   char * extraptr       = NULL;
66   char * idsptr         = NULL;
67   char * statptr        = NULL;
68   char * numdfptr       = NULL;
69   char * factptr        = NULL;
70 
71   int int_type;
72 
73   EX_FUNC_ENTER();
74 
75   ex_check_valid_file_id(exoid, __func__);
76 
77   sets_to_define = malloc(set_count * sizeof(int));
78 
79   /* Note that this routine can be called:
80      1) just define the sets
81      2) just output the set data (after a previous call to define)
82      3) define and output the set data in one call.
83   */
84   for (i = 0; i < set_count; i++) {
85     /* first check if any sets are specified */
86     if ((status = nc_inq_dimid(exoid, ex_dim_num_objects(sets[i].type), &dimid)) != NC_NOERR) {
87       if (status == NC_EBADDIM) {
88         snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: no %ss defined for file id %d",
89                  ex_name_of_object(sets[i].type), exoid);
90         ex_err(__func__, errmsg, status);
91       }
92       else {
93         snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to locate %ss defined in file id %d",
94                  ex_name_of_object(sets[i].type), exoid);
95         ex_err(__func__, errmsg, status);
96       }
97       free(sets_to_define);
98       EX_FUNC_LEAVE(EX_FATAL);
99     }
100 
101     if (sets[i].id < 0) {
102       /* We are adding a set with id = -sets[i].id. We want to define
103        * everything, but we don't
104        * want to increment the number of sets...  Major kluge / proof of concept
105        */
106       needs_define++;
107       sets_to_define[i] = -1;
108     }
109     else {
110       status = ex_id_lkup(exoid, sets[i].type, sets[i].id);
111       if (status != -EX_LOOKUPFAIL) { /* found the side set id, so set is already defined... */
112         sets_to_define[i] = 0;
113       }
114       else {
115         needs_define++;
116         sets_to_define[i] = 1;
117       }
118     }
119   }
120 
121   if (needs_define > 0) {
122     /* put netcdf file into define mode  */
123     if ((status = nc_redef(exoid)) != NC_NOERR) {
124       snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid);
125       ex_err(__func__, errmsg, status);
126       free(sets_to_define);
127       EX_FUNC_LEAVE(EX_FATAL);
128     }
129 
130     for (i = 0; i < set_count; i++) {
131       if (sets_to_define[i] == 0) {
132         continue;
133       }
134 
135       if (sets_to_define[i] > 0) {
136         /*   NOTE: ex_inc_file_item finds the current number of sets defined
137              for a specific file and returns that value incremented. */
138         cur_num_sets      = ex_inc_file_item(exoid, ex_get_counter_list(sets[i].type));
139         set_id_ndx        = cur_num_sets + 1;
140         sets_to_define[i] = set_id_ndx;
141       }
142       else {
143         cur_num_sets      = ex_get_file_item(exoid, ex_get_counter_list(sets[i].type));
144         set_id_ndx        = cur_num_sets - set_count + i + 1;
145         sets_to_define[i] = set_id_ndx;
146       }
147 
148       if (sets[i].num_entry == 0) {
149         continue;
150       }
151 
152       /* setup pointers based on set_type */
153       if (sets[i].type == EX_NODE_SET) {
154         numentryptr = DIM_NUM_NOD_NS(set_id_ndx);
155         entryptr    = VAR_NODE_NS(set_id_ndx);
156         extraptr    = NULL;
157         /* note we are using DIM_NUM_NODE_NS instead of DIM_NUM_DF_NS */
158         numdfptr = DIM_NUM_NOD_NS(set_id_ndx);
159         factptr  = VAR_FACT_NS(set_id_ndx);
160       }
161       else if (sets[i].type == EX_EDGE_SET) {
162         numentryptr = DIM_NUM_EDGE_ES(set_id_ndx);
163         entryptr    = VAR_EDGE_ES(set_id_ndx);
164         extraptr    = VAR_ORNT_ES(set_id_ndx);
165         numdfptr    = DIM_NUM_DF_ES(set_id_ndx);
166         factptr     = VAR_FACT_ES(set_id_ndx);
167       }
168       else if (sets[i].type == EX_FACE_SET) {
169         numentryptr = DIM_NUM_FACE_FS(set_id_ndx);
170         entryptr    = VAR_FACE_FS(set_id_ndx);
171         extraptr    = VAR_ORNT_FS(set_id_ndx);
172         numdfptr    = DIM_NUM_DF_FS(set_id_ndx);
173         factptr     = VAR_FACT_FS(set_id_ndx);
174       }
175       else if (sets[i].type == EX_SIDE_SET) {
176         numentryptr = DIM_NUM_SIDE_SS(set_id_ndx);
177         entryptr    = VAR_ELEM_SS(set_id_ndx);
178         extraptr    = VAR_SIDE_SS(set_id_ndx);
179         numdfptr    = DIM_NUM_DF_SS(set_id_ndx);
180         factptr     = VAR_FACT_SS(set_id_ndx);
181       }
182       else if (sets[i].type == EX_ELEM_SET) {
183         numentryptr = DIM_NUM_ELE_ELS(set_id_ndx);
184         entryptr    = VAR_ELEM_ELS(set_id_ndx);
185         extraptr    = NULL;
186         numdfptr    = DIM_NUM_DF_ELS(set_id_ndx);
187         factptr     = VAR_FACT_ELS(set_id_ndx);
188       }
189 
190       /* define dimensions and variables */
191       if ((status = nc_def_dim(exoid, numentryptr, sets[i].num_entry, &dimid)) != NC_NOERR) {
192         if (status == NC_ENAMEINUSE) {
193           snprintf(errmsg, MAX_ERR_LENGTH,
194                    "ERROR: %s %" PRId64 " -- size already defined in file id %d",
195                    ex_name_of_object(sets[i].type), sets[i].id, exoid);
196           ex_err(__func__, errmsg, status);
197         }
198         else {
199           snprintf(errmsg, MAX_ERR_LENGTH,
200                    "ERROR: failed to define number of entries in %s %" PRId64 " in file id %d",
201                    ex_name_of_object(sets[i].type), sets[i].id, exoid);
202           ex_err(__func__, errmsg, status);
203         }
204         goto error_ret;
205       }
206 
207       int_type = NC_INT;
208       if (ex_int64_status(exoid) & EX_BULK_INT64_DB) {
209         int_type = NC_INT64;
210       }
211 
212       /* create variable array in which to store the entry lists */
213       dims[0] = dimid;
214       if ((status = nc_def_var(exoid, entryptr, int_type, 1, dims, &varid)) != NC_NOERR) {
215         if (status == NC_ENAMEINUSE) {
216           snprintf(errmsg, MAX_ERR_LENGTH,
217                    "ERROR: entry list already exists for %s %" PRId64 " in file id %d",
218                    ex_name_of_object(sets[i].type), sets[i].id, exoid);
219           ex_err(__func__, errmsg, status);
220         }
221         else {
222           snprintf(errmsg, MAX_ERR_LENGTH,
223                    "ERROR: failed to create entry list for %s %" PRId64 " in file id %d",
224                    ex_name_of_object(sets[i].type), sets[i].id, exoid);
225           ex_err(__func__, errmsg, status);
226         }
227         goto error_ret; /* exit define mode and return */
228       }
229       ex_compress_variable(exoid, varid, 1);
230 
231       if (extraptr) {
232         if ((status = nc_def_var(exoid, extraptr, int_type, 1, dims, &varid)) != NC_NOERR) {
233           if (status == NC_ENAMEINUSE) {
234             snprintf(errmsg, MAX_ERR_LENGTH,
235                      "ERROR: extra list already exists for %s %" PRId64 " in file id %d",
236                      ex_name_of_object(sets[i].type), sets[i].id, exoid);
237             ex_err(__func__, errmsg, status);
238           }
239           else {
240             snprintf(errmsg, MAX_ERR_LENGTH,
241                      "ERROR: failed to create extra list for %s %" PRId64 " in file id %d",
242                      ex_name_of_object(sets[i].type), sets[i].id, exoid);
243             ex_err(__func__, errmsg, status);
244           }
245           goto error_ret; /* exit define mode and return */
246         }
247         ex_compress_variable(exoid, varid, 1);
248       }
249 
250       /* Create distribution factors variable if required */
251       if (sets[i].num_distribution_factor > 0) {
252         if (sets[i].type != EX_SIDE_SET) {
253           /* but sets[i].num_distribution_factor must equal number of nodes */
254           if (sets[i].num_distribution_factor != sets[i].num_entry) {
255             snprintf(errmsg, MAX_ERR_LENGTH,
256                      "ERROR: # dist fact (%" PRId64 ") not equal to # nodes (%" PRId64
257                      ") in node  set %" PRId64 " file id %d",
258                      sets[i].num_distribution_factor, sets[i].num_entry, sets[i].id, exoid);
259             ex_err(__func__, errmsg, EX_BADPARAM);
260             goto error_ret; /* exit define mode and return */
261           }
262         }
263         else {
264           /* resuse dimid from entry lists */
265           if ((status = nc_def_dim(exoid, numdfptr, sets[i].num_distribution_factor, &dimid)) !=
266               NC_NOERR) {
267             snprintf(errmsg, MAX_ERR_LENGTH,
268                      "ERROR: failed to define number of dist factors in %s %" PRId64
269                      " in file id %d",
270                      ex_name_of_object(sets[i].type), sets[i].id, exoid);
271             ex_err(__func__, errmsg, status);
272             goto error_ret; /* exit define mode and return */
273           }
274         }
275 
276         /* create variable array in which to store the set distribution factors
277          */
278         dims[0] = dimid;
279         if ((status = nc_def_var(exoid, factptr, nc_flt_code(exoid), 1, dims, &varid)) !=
280             NC_NOERR) {
281           if (status == NC_ENAMEINUSE) {
282             snprintf(errmsg, MAX_ERR_LENGTH,
283                      "ERROR: dist factors list already exists for %s %" PRId64 " in file id %d",
284                      ex_name_of_object(sets[i].type), sets[i].id, exoid);
285             ex_err(__func__, errmsg, status);
286           }
287           else {
288             snprintf(errmsg, MAX_ERR_LENGTH,
289                      "ERROR: failed to create dist factors list for %s %" PRId64 " in file id %d",
290                      ex_name_of_object(sets[i].type), sets[i].id, exoid);
291             ex_err(__func__, errmsg, status);
292           }
293           goto error_ret; /* exit define mode and return */
294         }
295         ex_compress_variable(exoid, varid, 2);
296       }
297     }
298 
299     /* leave define mode  */
300     if ((status = nc_enddef(exoid)) != NC_NOERR) {
301       snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to complete definition in file id %d", exoid);
302       ex_err(__func__, errmsg, status);
303       free(sets_to_define);
304       EX_FUNC_LEAVE(EX_FATAL);
305     }
306 
307     /* Output the set ids and status... */
308     for (i = 0; i < set_count; i++) {
309       /* setup pointers based on sets[i].type */
310       if (sets[i].type == EX_NODE_SET) {
311         idsptr  = VAR_NS_IDS;
312         statptr = VAR_NS_STAT;
313       }
314       else if (sets[i].type == EX_EDGE_SET) {
315         idsptr  = VAR_ES_IDS;
316         statptr = VAR_ES_STAT;
317       }
318       else if (sets[i].type == EX_FACE_SET) {
319         idsptr  = VAR_FS_IDS;
320         statptr = VAR_FS_STAT;
321       }
322       else if (sets[i].type == EX_SIDE_SET) {
323         idsptr  = VAR_SS_IDS;
324         statptr = VAR_SS_STAT;
325       }
326       else if (sets[i].type == EX_ELEM_SET) {
327         idsptr  = VAR_ELS_IDS;
328         statptr = VAR_ELS_STAT;
329       }
330 
331       /* first: get id of set id variable */
332       if ((status = nc_inq_varid(exoid, idsptr, &varid)) != NC_NOERR) {
333         snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to locate %s %" PRId64 " in file id %d",
334                  ex_name_of_object(sets[i].type), sets[i].id, exoid);
335         ex_err(__func__, errmsg, status);
336         free(sets_to_define);
337         EX_FUNC_LEAVE(EX_FATAL);
338       }
339 
340       /* write out set id */
341       start[0]     = sets_to_define[i] - 1;
342       long long id = sets[i].id;
343       if (id < 0) {
344         id = -id;
345       }
346       status = nc_put_var1_longlong(exoid, varid, start, &id);
347 
348       if (status != NC_NOERR) {
349         snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to store %s id %" PRId64 " in file id %d",
350                  ex_name_of_object(sets[i].type), sets[i].id, exoid);
351         ex_err(__func__, errmsg, status);
352         free(sets_to_define);
353         EX_FUNC_LEAVE(EX_FATAL);
354       }
355 
356       set_stat = (sets[i].num_entry == 0) ? 0 : 1;
357 
358       if ((status = nc_inq_varid(exoid, statptr, &varid)) != NC_NOERR) {
359         snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to locate %s status in file id %d",
360                  ex_name_of_object(sets[i].type), exoid);
361         ex_err(__func__, errmsg, status);
362         free(sets_to_define);
363         EX_FUNC_LEAVE(EX_FATAL);
364       }
365 
366       if ((status = nc_put_var1_int(exoid, varid, start, &set_stat)) != NC_NOERR) {
367         snprintf(errmsg, MAX_ERR_LENGTH,
368                  "ERROR: failed to store %s %" PRId64 " status to file id %d",
369                  ex_name_of_object(sets[i].type), sets[i].id, exoid);
370         ex_err(__func__, errmsg, status);
371         free(sets_to_define);
372         EX_FUNC_LEAVE(EX_FATAL);
373       }
374     }
375   }
376 
377   free(sets_to_define);
378 
379   /* Sets are now all defined; see if any set data needs to be output... */
380   status = EX_NOERR;
381   for (i = 0; i < set_count; i++) {
382     int       stat;
383     long long id = sets[i].id;
384     if (id < 0) {
385       id = -id;
386     }
387     if (sets[i].entry_list != NULL || sets[i].extra_list != NULL) {
388       /* NOTE: ex_put_set will write the warning/error message... */
389       stat = ex_put_set(exoid, sets[i].type, id, sets[i].entry_list, sets[i].extra_list);
390       if (stat != EX_NOERR) {
391         status = EX_FATAL;
392       }
393     }
394     if (sets[i].num_distribution_factor > 0 && sets[i].distribution_factor_list != NULL) {
395       /* NOTE: ex_put_set_dist_fact will write the warning/error message... */
396       stat = ex_put_set_dist_fact(exoid, sets[i].type, id, sets[i].distribution_factor_list);
397       if (stat != EX_NOERR) {
398         status = EX_FATAL;
399       }
400     }
401   }
402   EX_FUNC_LEAVE(status);
403 
404 /* Fatal error: exit definition mode and return */
405 error_ret:
406   free(sets_to_define);
407 
408   if ((status = nc_enddef(exoid)) != NC_NOERR) { /* exit define mode */
409     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to complete definition for file id %d", exoid);
410     ex_err(__func__, errmsg, status);
411   }
412   EX_FUNC_LEAVE(EX_FATAL);
413 }
414