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  *
37  * exgsnl - ex_get_side_set_node_list_len
38  *
39  * entry conditions -
40  *   input parameters:
41  *       int     exoid                   exodus file id
42  *       int     side_set_id             side set id
43  *
44  * exit conditions -
45  *       int     *side_set_node_list_len length of node list
46  *
47  * revision history -
48  *
49  *
50  *****************************************************************************/
51 
52 #include "exodusII.h"     // for ex_err, etc
53 #include "exodusII_int.h" // for elem_blk_parm, EX_FATAL, etc
54 #include "vtk_netcdf.h"       // for NC_NOERR
55 #include <ctype.h>        // for toupper
56 #include <inttypes.h>     // for PRId64
57 #include <stddef.h>       // for size_t
58 #include <stdio.h>
59 #include <stdlib.h>    // for malloc, NULL, free
60 #include <string.h>    // for strncmp, strlen
61 #include <sys/types.h> // for int64_t
62 
63 /*!
64  * This routine is designed to read the Exodus II V 2.0 side set side
65  * definition  and return the length of a ExodusI style side set node list.
66  * \param           exoid                   exodus file id
67  * \param           side_set_id             side set id
68  * \param[out]     *side_set_node_list_len length of node list
69  */
70 
ex_get_side_set_node_list_len(int exoid,ex_entity_id side_set_id,void_int * side_set_node_list_len)71 int ex_get_side_set_node_list_len(int exoid, ex_entity_id side_set_id,
72                                   void_int *side_set_node_list_len)
73 {
74   size_t    ii, i, j;
75   int64_t   num_side_sets, num_elem_blks, num_df, ndim;
76   size_t    list_len     = 0;
77   int64_t   tot_num_elem = 0, tot_num_ss_elem = 0;
78   void_int *elem_blk_ids   = NULL;
79   int *     ss_elem_ndx    = NULL;
80   int64_t * ss_elem_ndx_64 = NULL;
81 
82   void_int *side_set_elem_list = NULL;
83   void_int *side_set_side_list = NULL;
84   size_t    elem_ctr;
85 
86   int err_stat = EX_NOERR;
87   int status;
88 
89   struct elem_blk_parm *elem_blk_parms = NULL;
90 
91   char errmsg[MAX_ERR_LENGTH];
92 
93   EX_FUNC_ENTER();
94   ex_check_valid_file_id(exoid, __func__);
95 
96   if (ex_int64_status(exoid) & EX_BULK_INT64_API) {
97     *(int64_t *)side_set_node_list_len = 0; /* default value */
98   }
99   else {
100     *(int *)side_set_node_list_len = 0; /* default value */
101   }
102 
103   /* first check if any side sets are specified */
104   /* inquire how many side sets have been stored */
105 
106   /* get the dimensionality of the coordinates;  this is necessary to
107      distinguish between 2d TRIs and 3d TRIs */
108 
109   ndim = ex_inquire_int(exoid, EX_INQ_DIM);
110   if (ndim < 0) {
111     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to get dimensionality in file id %d", exoid);
112     ex_err(__func__, errmsg, EX_LASTERR);
113     EX_FUNC_LEAVE(EX_FATAL);
114   }
115 
116   tot_num_elem = ex_inquire_int(exoid, EX_INQ_ELEM);
117   if (tot_num_elem < 0) {
118     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to get total number of elements in file id %d",
119              exoid);
120     ex_err(__func__, errmsg, EX_LASTERR);
121     EX_FUNC_LEAVE(EX_FATAL);
122   }
123 
124   num_elem_blks = ex_inquire_int(exoid, EX_INQ_ELEM_BLK);
125   if (num_elem_blks < 0) {
126     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to get number of element blocks in file id %d",
127              exoid);
128     ex_err(__func__, errmsg, EX_LASTERR);
129     EX_FUNC_LEAVE(EX_FATAL);
130   }
131 
132   num_side_sets = ex_inquire_int(exoid, EX_INQ_SIDE_SETS);
133   if (num_side_sets < 0) {
134     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to get number of side sets in file id %d",
135              exoid);
136     ex_err(__func__, errmsg, EX_LASTERR);
137     EX_FUNC_LEAVE(EX_FATAL);
138   }
139 
140   if (num_side_sets == 0) {
141     snprintf(errmsg, MAX_ERR_LENGTH, "Warning: no side sets defined in file id %d", exoid);
142     ex_err(__func__, errmsg, EX_WARN);
143     EX_FUNC_LEAVE(EX_WARN);
144   }
145 
146   /* First determine the  # of elements in the side set*/
147   if (ex_int64_status(exoid) & EX_BULK_INT64_API) {
148     status = ex_get_set_param(exoid, EX_SIDE_SET, side_set_id, &tot_num_ss_elem, &num_df);
149   }
150   else {
151     int tot;
152     int df;
153     status          = ex_get_set_param(exoid, EX_SIDE_SET, side_set_id, &tot, &df);
154     tot_num_ss_elem = tot;
155     num_df          = df;
156   }
157 
158   if (status != NC_NOERR) {
159     snprintf(errmsg, MAX_ERR_LENGTH,
160              "ERROR: failed to get number of elements in side set %" PRId64 " in file id %d",
161              side_set_id, exoid);
162     ex_err(__func__, errmsg, EX_LASTERR);
163     EX_FUNC_LEAVE(EX_FATAL);
164   }
165 
166   if (tot_num_ss_elem == 0) { /* NULL side set? */
167     EX_FUNC_LEAVE(EX_NOERR);
168   }
169 
170   /* Allocate space for the side set element list */
171   {
172     int int_size = sizeof(int);
173     if (ex_int64_status(exoid) & EX_BULK_INT64_API) {
174       int_size = sizeof(int64_t);
175     }
176     if (!(side_set_elem_list = malloc(tot_num_ss_elem * int_size))) {
177       snprintf(errmsg, MAX_ERR_LENGTH,
178                "ERROR: failed to allocate space for side set element "
179                "list for file id %d",
180                exoid);
181       ex_err(__func__, errmsg, EX_MEMFAIL);
182       EX_FUNC_LEAVE(EX_FATAL);
183     }
184 
185     /* Allocate space for the side set side list */
186     if (!(side_set_side_list = malloc(tot_num_ss_elem * int_size))) {
187       snprintf(errmsg, MAX_ERR_LENGTH,
188                "ERROR: failed to allocate space for side set side list "
189                "for file id %d",
190                exoid);
191       ex_err(__func__, errmsg, EX_MEMFAIL);
192       err_stat = EX_FATAL;
193       goto cleanup;
194     }
195 
196     if (ex_get_set(exoid, EX_SIDE_SET, side_set_id, side_set_elem_list, side_set_side_list) !=
197         NC_NOERR) {
198       snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to get side set %" PRId64 " in file id %d",
199                side_set_id, exoid);
200       ex_err(__func__, errmsg, EX_LASTERR);
201       err_stat = EX_FATAL;
202       goto cleanup;
203     }
204 
205     /* Allocate space for the ss element index array */
206     if (int_size == sizeof(int64_t)) {
207       ss_elem_ndx_64 = malloc(tot_num_ss_elem * int_size);
208     }
209     else {
210       ss_elem_ndx = malloc(tot_num_ss_elem * int_size);
211     }
212 
213     if (ss_elem_ndx_64 == NULL && ss_elem_ndx == NULL) {
214       snprintf(errmsg, MAX_ERR_LENGTH,
215                "ERROR: failed to allocate space for side set elem sort "
216                "array for file id %d",
217                exoid);
218       ex_err(__func__, errmsg, EX_MEMFAIL);
219       err_stat = EX_FATAL;
220       goto cleanup;
221     }
222   }
223 
224   /* Sort side set element list into index array  - non-destructive */
225   if (ex_int64_status(exoid) & EX_BULK_INT64_API) {
226     for (i = 0; i < tot_num_ss_elem; i++) {
227       ss_elem_ndx_64[i] = i; /* init index array to current position */
228     }
229     ex_iqsort64(side_set_elem_list, ss_elem_ndx_64, tot_num_ss_elem);
230   }
231   else {
232     for (i = 0; i < tot_num_ss_elem; i++) {
233       ss_elem_ndx[i] = i; /* init index array to current position */
234     }
235     ex_iqsort(side_set_elem_list, ss_elem_ndx, tot_num_ss_elem);
236   }
237 
238   /* Allocate space for the element block ids */
239   {
240     int int_size = sizeof(int);
241     if (ex_int64_status(exoid) & EX_IDS_INT64_API) {
242       int_size = sizeof(int64_t);
243     }
244 
245     if (!(elem_blk_ids = malloc(num_elem_blks * int_size))) {
246       snprintf(errmsg, MAX_ERR_LENGTH,
247                "ERROR: failed to allocate space for element block ids "
248                "for file id %d",
249                exoid);
250       ex_err(__func__, errmsg, EX_MEMFAIL);
251       err_stat = EX_FATAL;
252       goto cleanup;
253     }
254   }
255 
256   if (ex_get_ids(exoid, EX_ELEM_BLOCK, elem_blk_ids)) {
257     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to get element block ids in file id %d", exoid);
258     ex_err(__func__, errmsg, EX_MSG);
259     err_stat = EX_FATAL;
260     goto cleanup;
261   }
262 
263   /* Allocate space for the element block params */
264   if (!(elem_blk_parms = malloc(num_elem_blks * sizeof(struct elem_blk_parm)))) {
265     snprintf(errmsg, MAX_ERR_LENGTH,
266              "ERROR: failed to allocate space for element block params "
267              "for file id %d",
268              exoid);
269     ex_err(__func__, errmsg, EX_MEMFAIL);
270     err_stat = EX_FATAL;
271     goto cleanup;
272   }
273 
274   elem_ctr = 0;
275   for (i = 0; i < num_elem_blks; i++) {
276     ex_entity_id id;
277     if (ex_int64_status(exoid) & EX_IDS_INT64_API) {
278       id = ((int64_t *)elem_blk_ids)[i];
279     }
280     else {
281       id = ((int *)elem_blk_ids)[i];
282     }
283 
284     err_stat = ex_int_get_block_param(exoid, id, ndim, &elem_blk_parms[i]);
285     if (err_stat != EX_NOERR) {
286       goto cleanup;
287     }
288 
289     elem_ctr += elem_blk_parms[i].num_elem_in_blk;
290     elem_blk_parms[i].elem_ctr = elem_ctr; /* save elem number max */
291   }
292 
293   /* Walk through element list and keep a running count of the node length */
294 
295   list_len = 0;
296   j        = 0; /* The current element block... */
297   for (ii = 0; ii < tot_num_ss_elem; ii++) {
298     size_t elem;
299     size_t side;
300     if (ex_int64_status(exoid) & EX_BULK_INT64_API) {
301       i    = ss_elem_ndx_64[ii];
302       elem = ((int64_t *)side_set_elem_list)[i];
303       side = ((int64_t *)side_set_side_list)[i];
304     }
305     else {
306       i    = ss_elem_ndx[ii];
307       elem = ((int *)side_set_elem_list)[i];
308       side = ((int *)side_set_side_list)[i];
309     }
310 
311     /*
312      * Since the elements are being accessed in sorted, order, the
313      * block that contains the elements must progress sequentially
314      * from block 0 to block[num_elem_blks-1]. Once we find an element
315      * not in this block, find a following block that contains it...
316      */
317     for (; j < num_elem_blks; j++) {
318       if (elem_blk_parms[j].elem_type_val != EX_EL_NULL_ELEMENT) {
319         if (elem <= elem_blk_parms[j].elem_ctr) {
320           break;
321         }
322       }
323     }
324 
325     if (j >= num_elem_blks) {
326       snprintf(errmsg, MAX_ERR_LENGTH,
327                "ERROR: Invalid element number %" ST_ZU " found in side set %" PRId64 " in file %d",
328                elem, side_set_id, exoid);
329       ex_err(__func__, errmsg, EX_BADPARAM);
330       err_stat = EX_FATAL;
331       goto cleanup;
332     }
333     list_len += elem_blk_parms[j].num_nodes_per_side[side - 1];
334   }
335 
336   if (ex_int64_status(exoid) & EX_BULK_INT64_API) {
337     *(int64_t *)side_set_node_list_len = list_len;
338   }
339   else {
340     *(int *)side_set_node_list_len = list_len;
341   }
342 
343   if (num_df > 0 && num_df != tot_num_ss_elem) {
344     if (list_len != num_df) {
345       snprintf(errmsg, MAX_ERR_LENGTH,
346                "Warning: In side set %" PRId64 " the distribution factor count (%" PRId64
347                ") does not match the side set node list length (%" ST_ZU
348                "). These should match and this may indicate a corrupt database in file %d",
349                side_set_id, num_df, list_len, exoid);
350       ex_err(__func__, errmsg, EX_MSG);
351       err_stat = EX_WARN;
352       goto cleanup;
353     }
354   }
355 
356 /* All done: release element block ids array,
357    element block parameters array, and side set element index array */
358 cleanup:
359   free(elem_blk_ids);
360   free(elem_blk_parms);
361   free(ss_elem_ndx);
362   free(ss_elem_ndx_64);
363   free(side_set_side_list);
364   free(side_set_elem_list);
365 
366   EX_FUNC_LEAVE(err_stat);
367 }
368