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_err, etc
37 #include "exodusII_int.h" // for EX_FATAL, ex_id_lkup, etc
38 #include "vtk_netcdf.h"       // for NC_NOERR, nc_set_fill, etc
39 #include <inttypes.h>     // for PRId64
40 #include <stddef.h>       // for size_t
41 #include <stdio.h>
42 #include <string.h> // for memset, strcmp, etc
43 
44 /*!
45 
46 The function ex_put_prop() stores an integer object property value to
47 a single element block, node set, or side set. Although it is not
48 necessary to invoke ex_put_prop_names(), since ex_put_prop() will
49 allocate space within the data file if it hasn't been previously
50 allocated, it is more efficient to use ex_put_prop_names() if there is
51 more than one property to store. \see Efficiency for a discussion of
52 efficiency issues.
53 
54 It should be noted that the interpretation of the values of the
55 integers stored as properties is left to the application code. In
56 general, a zero (0) means the object does not have the specified
57 property (or is not in the specified group); a nonzero value means the
58 object does have the specified property. When space is allocated for
59 the properties using ex_put_prop_names() or ex_put_prop(), the
60 properties are initialized to zero (0).
61 
62 Because the ID of an element block, node set, or side set is just
63 another property (named \b ID), this routine can be used to change
64 the value of an ID. This feature must be used with caution, though,
65 because changing the ID of an object to the ID of another object of
66 the same type (element block, node set, or side set) would cause two
67 objects to have the same ID, and thus only the first would be
68 accessible. Therefore, ex_put_prop() issues a warning if a user
69 attempts to give two objects the same ID.
70 
71 \return In case of an error, ex_put_prop() returns a negative number;
72 a warning will return a positive number.  Possible causes of errors
73 include:
74   -  data file not properly opened with call to ex_create() or ex_open()
75   -  data file opened for read only.
76   -  data file not initialized properly with call to ex_put_init().
77   -  invalid object type specified.
78   -  a warning is issued if a user attempts to change the ID of an
79      object to the ID of an existing object of the same type.
80 
81 \param[in] exoid      exodus file ID returned from a previous call to
82 ex_create() or ex_open().
83 \param[in] obj_type   Type of object; use one of the options in the table below.
84 \param[in] obj_id     The element block, node set, or side set ID.
85 \param[in]  prop_name The name of the property for which the value will be
86 stored.
87                       Maximum length of this string is \p MAX_STR_LENGTH .
88 \param[in] value      The value of the property.
89 
90 | ex_entity_type | description               |
91 | -------------- | ------------------------- |
92 |  EX_NODE_SET   |  Node Set entity type     |
93 |  EX_EDGE_BLOCK |  Edge Block entity type   |
94 |  EX_EDGE_SET   |  Edge Set entity type     |
95 |  EX_FACE_BLOCK |  Face Block entity type   |
96 |  EX_FACE_SET   |  Face Set entity type     |
97 |  EX_ELEM_BLOCK |  Element Block entity type|
98 |  EX_ELEM_SET   |  Element Set entity type  |
99 |  EX_SIDE_SET   |  Side Set entity type     |
100 |  EX_ELEM_MAP   |  Element Map entity type  |
101 |  EX_NODE_MAP   |  Node Map entity type     |
102 |  EX_EDGE_MAP   |  Edge Map entity type     |
103 |  EX_FACE_MAP   |  Face Map entity type     |
104 
105 For an example of code to write out an object property, refer to the
106 description for ex_put_prop_names().
107 */
108 
ex_put_prop(int exoid,ex_entity_type obj_type,ex_entity_id obj_id,const char * prop_name,ex_entity_id value)109 int ex_put_prop(int exoid, ex_entity_type obj_type, ex_entity_id obj_id, const char *prop_name,
110                 ex_entity_id value)
111 {
112   int       status;
113   int       oldfill = 0;
114   int       temp;
115   int       found = EX_FALSE;
116   int       num_props, i, dimid, propid, dims[1];
117   int       int_type;
118   size_t    start[1];
119   size_t    prop_name_len, name_length;
120   char *    name;
121   char      tmpstr[MAX_STR_LENGTH + 1];
122   char *    dim_name;
123   long long vals[1];
124 
125   char errmsg[MAX_ERR_LENGTH];
126 
127   EX_FUNC_ENTER();
128   ex_check_valid_file_id(exoid, __func__);
129 
130   /* check if property has already been created */
131 
132   num_props = ex_get_num_props(exoid, obj_type);
133 
134   if (num_props > 1) { /* any properties other than the default 1? */
135 
136     for (i = 1; i <= num_props; i++) {
137       switch (obj_type) {
138       case EX_ELEM_BLOCK: name = VAR_EB_PROP(i); break;
139       case EX_EDGE_BLOCK: name = VAR_ED_PROP(i); break;
140       case EX_FACE_BLOCK: name = VAR_FA_PROP(i); break;
141       case EX_NODE_SET: name = VAR_NS_PROP(i); break;
142       case EX_EDGE_SET: name = VAR_ES_PROP(i); break;
143       case EX_FACE_SET: name = VAR_FS_PROP(i); break;
144       case EX_ELEM_SET: name = VAR_ELS_PROP(i); break;
145       case EX_SIDE_SET: name = VAR_SS_PROP(i); break;
146       case EX_ELEM_MAP: name = VAR_EM_PROP(i); break;
147       case EX_FACE_MAP: name = VAR_FAM_PROP(i); break;
148       case EX_EDGE_MAP: name = VAR_EDM_PROP(i); break;
149       case EX_NODE_MAP: name = VAR_NM_PROP(i); break;
150       default:
151         snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: object type %d not supported; file id %d",
152                  obj_type, exoid);
153         ex_err(__func__, errmsg, EX_BADPARAM);
154         EX_FUNC_LEAVE(EX_FATAL);
155       }
156 
157       if ((status = nc_inq_varid(exoid, name, &propid)) != NC_NOERR) {
158         snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to get property array id in file id %d",
159                  exoid);
160         ex_err(__func__, errmsg, status);
161         EX_FUNC_LEAVE(EX_FATAL);
162       }
163 
164       /*   compare stored attribute name with passed property name   */
165       memset(tmpstr, 0, MAX_STR_LENGTH + 1);
166       if ((status = nc_get_att_text(exoid, propid, ATT_PROP_NAME, tmpstr)) != NC_NOERR) {
167         snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to get property name in file id %d", exoid);
168         ex_err(__func__, errmsg, status);
169         EX_FUNC_LEAVE(EX_FATAL);
170       }
171 
172       if (strcmp(tmpstr, prop_name) == 0) {
173         found = EX_TRUE;
174         break;
175       }
176     }
177   }
178 
179   /* if property array has not been created, create it */
180   if (!found) {
181 
182     name_length = ex_inquire_int(exoid, EX_INQ_DB_MAX_ALLOWED_NAME_LENGTH) + 1;
183 
184     /* put netcdf file into define mode  */
185     if ((status = nc_redef(exoid)) != NC_NOERR) {
186       snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to place file id %d into define mode", exoid);
187       ex_err(__func__, errmsg, status);
188       EX_FUNC_LEAVE(EX_FATAL);
189     }
190 
191     /*   create a variable with a name xx_prop#, where # is the new number   */
192     /*   of the property                                                     */
193 
194     switch (obj_type) {
195     case EX_ELEM_BLOCK:
196       name     = VAR_EB_PROP(num_props + 1);
197       dim_name = DIM_NUM_EL_BLK;
198       break;
199     case EX_FACE_BLOCK:
200       name     = VAR_FA_PROP(num_props + 1);
201       dim_name = DIM_NUM_FA_BLK;
202       break;
203     case EX_EDGE_BLOCK:
204       name     = VAR_ED_PROP(num_props + 1);
205       dim_name = DIM_NUM_ED_BLK;
206       break;
207     case EX_NODE_SET:
208       name     = VAR_NS_PROP(num_props + 1);
209       dim_name = DIM_NUM_NS;
210       break;
211     case EX_EDGE_SET:
212       name     = VAR_ES_PROP(num_props + 1);
213       dim_name = DIM_NUM_ES;
214       break;
215     case EX_FACE_SET:
216       name     = VAR_FS_PROP(num_props + 1);
217       dim_name = DIM_NUM_FS;
218       break;
219     case EX_ELEM_SET:
220       name     = VAR_ELS_PROP(num_props + 1);
221       dim_name = DIM_NUM_ELS;
222       break;
223     case EX_SIDE_SET:
224       name     = VAR_SS_PROP(num_props + 1);
225       dim_name = DIM_NUM_SS;
226       break;
227     case EX_ELEM_MAP:
228       name     = VAR_EM_PROP(num_props + 1);
229       dim_name = DIM_NUM_EM;
230       break;
231     case EX_FACE_MAP:
232       name     = VAR_FAM_PROP(num_props + 1);
233       dim_name = DIM_NUM_FAM;
234       break;
235     case EX_EDGE_MAP:
236       name     = VAR_EDM_PROP(num_props + 1);
237       dim_name = DIM_NUM_EDM;
238       break;
239     case EX_NODE_MAP:
240       name     = VAR_NM_PROP(num_props + 1);
241       dim_name = DIM_NUM_NM;
242       break;
243     default:
244       snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: object type %d not supported; file id %d", obj_type,
245                exoid);
246       ex_err(__func__, errmsg, EX_BADPARAM);
247       goto error_ret; /* Exit define mode and return */
248     }
249 
250     /*   inquire id of previously defined dimension (number of objects) */
251     if ((status = nc_inq_dimid(exoid, dim_name, &dimid)) != NC_NOERR) {
252       snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to locate number of objects in file id %d",
253                exoid);
254       ex_err(__func__, errmsg, status);
255       goto error_ret; /* Exit define mode and return */
256     }
257 
258     dims[0] = dimid;
259     nc_set_fill(exoid, NC_FILL, &oldfill); /* fill with zeros per routine spec */
260 
261     int_type = NC_INT;
262     if (ex_int64_status(exoid) & EX_IDS_INT64_DB) {
263       int_type = NC_INT64;
264     }
265 
266     if ((status = nc_def_var(exoid, name, int_type, 1, dims, &propid)) != NC_NOERR) {
267       snprintf(errmsg, MAX_ERR_LENGTH,
268                "ERROR: failed to create property array variable in file id %d", exoid);
269       ex_err(__func__, errmsg, status);
270       goto error_ret; /* Exit define mode and return */
271     }
272 
273     vals[0] = 0; /* fill value */
274     /*   create attribute to cause variable to fill with zeros per routine spec
275      */
276     if ((status = nc_put_att_longlong(exoid, propid, _FillValue, int_type, 1, vals)) != NC_NOERR) {
277       snprintf(errmsg, MAX_ERR_LENGTH,
278                "ERROR: failed to create property name fill attribute in file id %d", exoid);
279       ex_err(__func__, errmsg, status);
280       goto error_ret; /* Exit define mode and return */
281     }
282 
283     /*   Check that the property name length is less than MAX_NAME_LENGTH */
284     prop_name_len = strlen(prop_name) + 1;
285     if (prop_name_len > name_length) {
286       fprintf(stderr,
287               "Warning: The property name '%s' is too long.\n\tIt will "
288               "be truncated from %d to %d characters\n",
289               prop_name, (int)prop_name_len - 1, (int)name_length - 1);
290       prop_name_len = name_length;
291     }
292 
293     /*   store property name as attribute of property array variable */
294     if ((status = nc_put_att_text(exoid, propid, ATT_PROP_NAME, prop_name_len,
295                                   (void *)prop_name)) != NC_NOERR) {
296       snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to store property name %s in file id %d",
297                prop_name, exoid);
298       ex_err(__func__, errmsg, status);
299       goto error_ret; /* Exit define mode and return */
300     }
301 
302     ex_update_max_name_length(exoid, prop_name_len - 1);
303 
304     /* leave define mode  */
305     if ((status = nc_enddef(exoid)) != NC_NOERR) {
306       snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to leave define mode in file id %d", exoid);
307       ex_err(__func__, errmsg, status);
308       EX_FUNC_LEAVE(EX_FATAL);
309     }
310 
311     nc_set_fill(exoid, oldfill, &temp); /* default: nofill */
312   }
313 
314   /* find index into property array using obj_id; put value in property */
315   /* array at proper index; ex_id_lkup returns an index that is 1-based,*/
316   /* but netcdf expects 0-based arrays so subtract 1                    */
317 
318   /* special case: property name ID - check for duplicate ID assignment */
319   if (strcmp("ID", prop_name) == 0) {
320     int indx = ex_id_lkup(exoid, obj_type, value);
321     if (indx != -EX_LOOKUPFAIL) { /* found the id */
322       snprintf(errmsg, MAX_ERR_LENGTH,
323                "Warning: attempt to assign duplicate %s ID %" PRId64 " in file id %d",
324                ex_name_of_object(obj_type), value, exoid);
325       ex_err(__func__, errmsg, EX_DUPLICATEID);
326       EX_FUNC_LEAVE(EX_WARN);
327     }
328   }
329 
330   status = ex_id_lkup(exoid, obj_type, obj_id);
331   if (status > 0) {
332     start[0] = status - 1;
333   }
334   else {
335     ex_get_err(NULL, NULL, &status);
336 
337     if (status != 0) {
338       if (status == EX_NULLENTITY) {
339         snprintf(errmsg, MAX_ERR_LENGTH,
340                  "Warning: no properties allowed for NULL %s id %" PRId64 " in file id %d",
341                  ex_name_of_object(obj_type), obj_id, exoid);
342         ex_err(__func__, errmsg, EX_NULLENTITY);
343         EX_FUNC_LEAVE(EX_WARN);
344       }
345       snprintf(errmsg, MAX_ERR_LENGTH,
346                "ERROR: failed to find value %" PRId64 " in %s property array in file id %d", obj_id,
347                ex_name_of_object(obj_type), exoid);
348       ex_err(__func__, errmsg, status);
349       EX_FUNC_LEAVE(EX_FATAL);
350     }
351   }
352 
353   /* value is of type 'ex_entity_id' which is a typedef to int64_t or long long
354    */
355   status = nc_put_var1_longlong(exoid, propid, start, (long long *)&value);
356 
357   if (status != NC_NOERR) {
358     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to store property value in file id %d", exoid);
359     ex_err(__func__, errmsg, status);
360     EX_FUNC_LEAVE(EX_FATAL);
361   }
362 
363   EX_FUNC_LEAVE(EX_NOERR);
364 
365 /* Fatal error: exit definition mode and return */
366 error_ret:
367   nc_set_fill(exoid, oldfill, &temp); /* default: nofill */
368 
369   if ((status = nc_enddef(exoid)) != NC_NOERR) { /* exit define mode */
370     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to complete definition for file id %d", exoid);
371     ex_err(__func__, errmsg, status);
372   }
373   EX_FUNC_LEAVE(EX_FATAL);
374 }
375