1 #include "WriteNC.hpp"
2 #include "moab/CN.hpp"
3 #include "MBTagConventions.hpp"
4 #include "MBParallelConventions.h"
5 #include "moab/Interface.hpp"
6 #include "moab/Range.hpp"
7 #include "moab/WriteUtilIface.hpp"
8 #include "moab/FileOptions.hpp"
9 #include "NCWriteHelper.hpp"
10 
11 #include <fstream>
12 #include <map>
13 #include <set>
14 
15 #include <iostream>
16 #include <sstream>
17 
18 #ifdef WIN32
19 #ifdef size_t
20 #undef size_t
21 #endif
22 #endif
23 
24 namespace moab {
25 
factory(Interface * iface)26 WriterIface *WriteNC::factory(Interface* iface)
27 {
28   return new WriteNC(iface);
29 }
30 
WriteNC(Interface * impl)31 WriteNC::WriteNC(Interface* impl) :
32   mbImpl(impl), dbgOut(stderr),
33 #ifdef MOAB_HAVE_MPI
34   myPcomm(NULL),
35 #endif
36   noMesh(false), noVars(false), append(false),
37   mGlobalIdTag(0), isParallel(false),
38   myHelper(NULL)
39 {
40   assert(impl != NULL);
41   impl->query_interface(mWriteIface);
42 }
43 
~WriteNC()44 WriteNC::~WriteNC()
45 {
46   mbImpl->release_interface(mWriteIface);
47   if (myHelper != NULL)
48     delete myHelper;
49 }
50 
51 //! Writes out a file
write_file(const char * file_name,const bool overwrite,const FileOptions & options,const EntityHandle * file_set,const int num_set,const std::vector<std::string> &,const Tag *,int,int)52 ErrorCode WriteNC::write_file(const char* file_name,
53                               const bool overwrite,
54                               const FileOptions& options,
55                               const EntityHandle* file_set,
56                               const int num_set,
57                               const std::vector<std::string>&,
58                               const Tag*,
59                               int,
60                               int)
61 {
62   ErrorCode rval;
63   // See if opts has variable(s) specified
64   std::vector<std::string> var_names;
65   std::vector<std::string> desired_names;
66   std::vector<int> tstep_nums;
67   std::vector<double> tstep_vals;
68 
69   // Get and cache predefined tag handles
70   int dum_val = 0;
71   rval = mbImpl->tag_get_handle(GLOBAL_ID_TAG_NAME, 1, MB_TYPE_INTEGER, mGlobalIdTag, MB_TAG_DENSE,
72                                 &dum_val);MB_CHK_SET_ERR(rval, "Trouble getting global ID tag");
73 
74   // num set has to be 1, we will write only one set, the original file set used to load
75   if (num_set != 1)
76     MB_SET_ERR(MB_FAILURE, "We should write only one set (the file set used to read data into)");
77 
78   rval = parse_options(options, var_names, desired_names, tstep_nums, tstep_vals);MB_CHK_SET_ERR(rval, "Trouble parsing option string");
79 
80   // Important to create some data that will be used to write the file; dimensions, variables, etc
81   // new variables still need to have some way of defining their dimensions
82   // maybe it will be passed as write options
83   rval = process_conventional_tags(*file_set);MB_CHK_SET_ERR(rval, "Trouble processing conventional tags");
84 
85   // Create or append the file
86   if (append)
87     dbgOut.tprintf(1, "opening file %s for appending \n", file_name);
88   else
89     dbgOut.tprintf(1, "creating file %s\n", file_name);
90   fileName = file_name;
91   int success;
92 
93   if (append) {
94     int omode = NC_WRITE;
95 #ifdef MOAB_HAVE_PNETCDF
96     if (isParallel)
97       success = NCFUNC(open)(myPcomm->proc_config().proc_comm(), file_name, omode, MPI_INFO_NULL, &fileId);
98     else
99       success = NCFUNC(open)(MPI_COMM_SELF, file_name, omode, MPI_INFO_NULL, &fileId);
100 #else
101     // This is a regular netcdf file, open in write mode
102     success = NCFUNC(open)(file_name, omode, &fileId);
103 #endif
104     if (success)
105       MB_SET_ERR(MB_FAILURE, "Trouble opening file " << file_name << " for appending");
106   }
107   else { // Case when the file is new, will be overwritten, most likely
108     int cmode = overwrite ? NC_CLOBBER : NC_NOCLOBBER;
109 #ifdef MOAB_HAVE_PNETCDF
110     if (isParallel)
111       success = NCFUNC(create)(myPcomm->proc_config().proc_comm(), file_name, cmode, MPI_INFO_NULL, &fileId);
112     else
113       success = NCFUNC(create)(MPI_COMM_SELF, file_name, cmode, MPI_INFO_NULL, &fileId);
114 #else
115     // This is a regular netcdf file
116     success = NCFUNC(create)(file_name, cmode, &fileId);
117 #endif
118     if (success)
119       MB_SET_ERR(MB_FAILURE, "Trouble creating file " << file_name << " for writing");
120   }
121 
122   if (NULL != myHelper)
123     delete myHelper;
124 
125   // Get appropriate helper instance for WriteNC class based on some info in the file set
126   myHelper = NCWriteHelper::get_nc_helper(this, fileId, options, *file_set);
127   if (NULL == myHelper) {
128     MB_SET_ERR(MB_FAILURE, "Failed to get NCWriteHelper class instance");
129   }
130 
131   rval = myHelper->collect_mesh_info();MB_CHK_SET_ERR(rval, "Trouble collecting mesh information");
132 
133   rval = myHelper->collect_variable_data(var_names, tstep_nums);MB_CHK_SET_ERR(rval, "Trouble collecting variable data");
134 
135   rval = myHelper->init_file(var_names, desired_names, append);MB_CHK_SET_ERR(rval, "Trouble initializing file");
136 
137   rval = myHelper->write_values(var_names, tstep_nums);MB_CHK_SET_ERR(rval, "Trouble writing values to file");
138 
139   success = NCFUNC(close)(fileId);
140   if (success)
141     MB_SET_ERR(MB_FAILURE, "Trouble closing file");
142 
143   return MB_SUCCESS;
144 }
145 
parse_options(const FileOptions & opts,std::vector<std::string> & var_names,std::vector<std::string> & desired_names,std::vector<int> & tstep_nums,std::vector<double> & tstep_vals)146 ErrorCode WriteNC::parse_options(const FileOptions& opts, std::vector<std::string>& var_names,
147                                 std::vector<std::string>& desired_names, std::vector<int>& tstep_nums,
148                                 std::vector<double>& tstep_vals)
149 {
150   int tmpval;
151   if (MB_SUCCESS == opts.get_int_option("DEBUG_IO", 1, tmpval)) {
152     dbgOut.set_verbosity(tmpval);
153     dbgOut.set_prefix("NCWrite");
154   }
155 
156   ErrorCode rval = opts.get_strs_option("VARIABLE", var_names);
157   if (MB_TYPE_OUT_OF_RANGE == rval)
158     noVars = true;
159   else
160     noVars = false;
161 
162   rval = opts.get_strs_option("RENAME", desired_names);
163   if (MB_ENTITY_NOT_FOUND == rval) {
164     if (!noVars) {
165       desired_names.resize(var_names.size());
166       std::copy(var_names.begin(), var_names.end(), desired_names.begin());
167     }
168   }
169   // Either way
170   assert(desired_names.size() == var_names.size());
171 
172   opts.get_ints_option("TIMESTEP", tstep_nums);
173   opts.get_reals_option("TIMEVAL", tstep_vals);
174   rval = opts.get_null_option("NOMESH");
175   if (MB_SUCCESS == rval)
176     noMesh = true;
177 
178   rval = opts.get_null_option("APPEND");
179   if (MB_SUCCESS == rval)
180     append = true;
181 
182   if (2 <= dbgOut.get_verbosity()) {
183     if (!var_names.empty()) {
184       std::cerr << "Variables requested: ";
185       for (unsigned int i = 0; i < var_names.size(); i++)
186         std::cerr << var_names[i];
187       std::cerr << std::endl;
188     }
189     if (!tstep_nums.empty()) {
190       std::cerr << "Timesteps requested: ";
191       for (unsigned int i = 0; i < tstep_nums.size(); i++)
192         std::cerr << tstep_nums[i];
193       std::cerr << std::endl;
194     }
195     if (!tstep_vals.empty()) {
196       std::cerr << "Time vals requested: ";
197       for (unsigned int i = 0; i < tstep_vals.size(); i++)
198         std::cerr << tstep_vals[i];
199       std::cerr << std::endl;
200     }
201   }
202 
203 // FIXME: copied from ReadNC, may need revise
204 #ifdef MOAB_HAVE_MPI
205   isParallel = (opts.match_option("PARALLEL", "WRITE_PART") != MB_ENTITY_NOT_FOUND);
206 
207   if (!isParallel)
208   // Return success here, since rval still has _NOT_FOUND from not finding option
209   // in this case, myPcomm will be NULL, so it can never be used; always check for isParallel
210   // before any use for myPcomm
211     return MB_SUCCESS;
212 
213   int pcomm_no = 0;
214   rval = opts.get_int_option("PARALLEL_COMM", pcomm_no);
215   if (MB_TYPE_OUT_OF_RANGE == rval) {
216     MB_SET_ERR(rval, "Invalid value for PARALLEL_COMM option");
217   }
218 
219   myPcomm = ParallelComm::get_pcomm(mbImpl, pcomm_no);
220   if (0 == myPcomm) {
221     myPcomm = new ParallelComm(mbImpl, MPI_COMM_WORLD);
222   }
223 
224 #ifndef MOAB_HAVE_PNETCDF
225   const int procs = myPcomm->proc_config().proc_size();
226   if (procs > 1) {
227     MB_SET_ERR(MB_UNSUPPORTED_OPERATION, "Attempt to launch NC writer in parallel without pnetcdf support");
228   }
229 #endif
230 
231   const int rank = myPcomm->proc_config().proc_rank();
232   dbgOut.set_rank(rank);
233 #endif
234 
235   return MB_SUCCESS;
236 }
237 
238 // This is the inverse process to create conventional tags
239 // Will look at <pargal_source>/src/core/fileinfo.cpp, init dim, vars, atts
process_conventional_tags(EntityHandle fileSet)240 ErrorCode WriteNC::process_conventional_tags(EntityHandle fileSet)
241 {
242   ErrorCode rval;
243 
244   // Start copy
245   Tag dimNamesTag = 0;
246   std::string tag_name = "__DIM_NAMES";
247   const void* data = NULL;
248   int dimNamesSz = 0;
249   rval = mbImpl->tag_get_handle(tag_name.c_str(), 0, MB_TYPE_OPAQUE, dimNamesTag,
250                                 MB_TAG_ANY);MB_CHK_SET_ERR(rval, "Trouble getting conventional tag " << tag_name);
251   rval = mbImpl->tag_get_by_ptr(dimNamesTag, &fileSet, 1, &data, &dimNamesSz);MB_CHK_SET_ERR(rval, "Trouble getting data of conventional tag " << tag_name);
252   const char* p = static_cast<const char*>(data);
253   dbgOut.tprintf(1, "__DIM_NAMES tag has string length %d\n", dimNamesSz);
254 
255   std::size_t start = 0;
256 
257   Tag dimLensTag = 0;
258   tag_name = "__DIM_LENS";
259   data = NULL;
260   int dimLensSz = 0;
261   rval = mbImpl->tag_get_handle(tag_name.c_str(), 0, MB_TYPE_INTEGER, dimLensTag,
262                                 MB_TAG_ANY);MB_CHK_SET_ERR(rval, "Trouble getting conventional tag " << tag_name);
263   rval = mbImpl->tag_get_by_ptr(dimLensTag, &fileSet, 1, &data, &dimLensSz);MB_CHK_SET_ERR(rval, "Trouble getting data of conventional tag " << tag_name);
264   const int* int_p = static_cast<const int*>(data);
265   dbgOut.tprintf(1, "__DIM_LENS tag has %d values\n", dimLensSz);
266 
267   int idxDim = 0;
268   // Dim names are separated by '\0' in the string of __DIM_NAMES tag
269   for (std::size_t i = 0; i != static_cast<std::size_t>(dimNamesSz); i++) {
270     if (p[i] == '\0') {
271       std::string dim_name(&p[start], i - start);
272       int len = int_p[idxDim];
273       dimNames.push_back(dim_name);
274       dimLens.push_back(len);
275       dbgOut.tprintf(2, "Dimension %s has length %d\n", dim_name.c_str(), len);
276       // FIXME: Need info from moab to set unlimited dimension
277       // Currently assume each file has the same number of time dimensions
278       /*if ((dim_name == "time") || (dim_name == "Time"))
279         insert(dim_name, *(new pcdim(dim_name, len * m_file_names.size(), true)));
280       else
281         insert(dim_name, *(new pcdim(dim_name, len)));*/
282       start = i + 1;
283       idxDim++;
284     }
285   }
286 
287   Tag meshTypeTag = 0;
288   tag_name = "__MESH_TYPE";
289   data = NULL;
290   int meshTypeSz = 0;
291   rval = mbImpl->tag_get_handle(tag_name.c_str(), 0, MB_TYPE_OPAQUE, meshTypeTag,
292                                 MB_TAG_ANY);MB_CHK_SET_ERR(rval, "Trouble getting conventional tag " << tag_name);
293   rval = mbImpl->tag_get_by_ptr(meshTypeTag, &fileSet, 1, &data, &meshTypeSz);MB_CHK_SET_ERR(rval, "Trouble getting data of conventional tag " << tag_name);
294   p = static_cast<const char*>(data);
295   grid_type = std::string(&p[0], meshTypeSz);
296   dbgOut.tprintf(2, "Mesh type: %s\n", grid_type.c_str());
297 
298   // Read <__VAR_NAMES_LOCATIONS> tag
299   Tag varNamesLocsTag = 0;
300   tag_name = "__VAR_NAMES_LOCATIONS";
301   data = NULL;
302   int varNamesLocsSz = 0;
303   rval = mbImpl->tag_get_handle(tag_name.c_str(), 0, MB_TYPE_INTEGER, varNamesLocsTag,
304                                 MB_TAG_ANY);MB_CHK_SET_ERR(rval, "Trouble getting conventional tag " << tag_name);
305   rval = mbImpl->tag_get_by_ptr(varNamesLocsTag, &fileSet, 1, &data, &varNamesLocsSz);MB_CHK_SET_ERR(rval, "Trouble getting data of conventional tag " << tag_name);
306   int_p = static_cast<const int*>(data);
307   std::vector<int> varNamesLocs(varNamesLocsSz);
308   std::copy(int_p, int_p + varNamesLocsSz, varNamesLocs.begin());
309 
310   Tag varNamesTag = 0;
311   tag_name = "__VAR_NAMES";
312   rval = mbImpl->tag_get_handle(tag_name.c_str(), 0, MB_TYPE_OPAQUE, varNamesTag,
313                                 MB_TAG_ANY);MB_CHK_SET_ERR(rval, "Trouble getting conventional tag " << tag_name);
314   data = NULL;
315   int varNamesSz = 0;
316   rval = mbImpl->tag_get_by_ptr(varNamesTag, &fileSet, 1, &data, &varNamesSz);MB_CHK_SET_ERR(rval, "Trouble getting data of conventional tag " << tag_name);
317   dbgOut.tprintf(2, "__VAR_NAMES tag has string length %d\n", varNamesSz);
318   p = static_cast<const char*>(data);
319 
320   start = 0;
321   int idxVar = 0;
322   int sz;
323   // Var names are separated by '\0' in the string of __VAR_NAMES tag
324   for (std::size_t i = 0; i != static_cast<std::size_t>(varNamesSz); i++) {
325     if (p[i] == '\0') {
326       std::string var_name(&p[start], i - start);
327 
328       dbgOut.tprintf(2, "var name: %s index %d \n", var_name.c_str(), idxVar);
329       // Process var name:
330       // This will create/initiate map; we will populate variableDataStruct with info about dims, tags, etc
331       // reference & is important; otherwise variableDataStruct will go out of scope, and deleted :(
332       VarData& variableDataStruct = varInfo[var_name];
333       variableDataStruct.varName = var_name;
334       variableDataStruct.entLoc = varNamesLocs[idxVar];
335 
336       dbgOut.tprintf(2, "at var name %s varInfo size %d \n", var_name.c_str(), (int)varInfo.size());
337 
338       sz = 0;
339       Tag dims_tag = 0;
340       std::string dim_names = "__" + var_name + "_DIMS";
341       rval = mbImpl->tag_get_handle(dim_names.c_str(), 0, MB_TYPE_OPAQUE, dims_tag, MB_TAG_ANY);
342       if (MB_SUCCESS != rval) {
343         if (MB_TAG_NOT_FOUND == rval) {
344           dbgOut.tprintf(2, "tag : %s not found, continue \n", dim_names.c_str());
345           start = i + 1;
346           idxVar++;
347           continue;
348         }
349         MB_SET_ERR(rval, "Trouble getting conventional tag " << dim_names);
350       }
351       rval = mbImpl->tag_get_length(dims_tag, sz);MB_CHK_SET_ERR(rval, "Trouble getting size of dimensions for variable " << var_name);
352       sz /= sizeof(Tag); // The type is MB_TYPE_OPAQUE, but it is a list of tags, so we need to divide by the size of Tag
353       // sz is used for number of dimension tags in this list
354       dbgOut.tprintf(2, "var name: %s has %d dimensions \n", var_name.c_str(), sz);
355 
356       variableDataStruct.varDims.resize(sz);
357       const void* ptr = NULL;
358       rval = mbImpl->tag_get_by_ptr(dims_tag, &fileSet, 1, &ptr);
359 
360       const Tag* ptags = static_cast<const moab::Tag*>(ptr);
361       for (std::size_t j = 0; j != static_cast<std::size_t>(sz); j++) {
362         std::string dim_name;
363         rval = mbImpl->tag_get_name(ptags[j], dim_name);MB_CHK_SET_ERR(rval, "Trouble getting dimension of variable " << var_name);
364         dbgOut.tprintf(2, "var name: %s has %s as dimension \n", var_name.c_str(), dim_name.c_str());
365         std::vector<std::string>::iterator vit = std::find(dimNames.begin(), dimNames.end(), dim_name);
366         if (vit == dimNames.end())
367           MB_SET_ERR(MB_FAILURE, "Dimension " << dim_name << " not found for variable " << var_name);
368         variableDataStruct.varDims[j] = (int)(vit - dimNames.begin()); // Will be used for writing
369         // This will have to change to actual file dimension, for writing
370       }
371 
372       // Attributes for this variable
373       std::stringstream ssTagName;
374       ssTagName << "__" << var_name << "_ATTRIBS";
375       tag_name = ssTagName.str();
376       Tag varAttTag = 0;
377       rval = mbImpl->tag_get_handle(tag_name.c_str(), 0, MB_TYPE_OPAQUE, varAttTag,
378                                     MB_TAG_SPARSE | MB_TAG_VARLEN);MB_CHK_SET_ERR(rval, "Trouble getting conventional tag " << tag_name);
379       const void* varAttPtr = NULL;
380       int varAttSz = 0;
381       rval = mbImpl->tag_get_by_ptr(varAttTag, &fileSet, 1, &varAttPtr, &varAttSz);MB_CHK_SET_ERR(rval, "Trouble getting data of conventional tag " << tag_name);
382       if (MB_SUCCESS == rval)
383         dbgOut.tprintf(2, "Tag retrieved for variable %s\n", tag_name.c_str());
384 
385       std::string attribString((char*)varAttPtr, (char*)varAttPtr + varAttSz);
386       if (attribString == "NO_ATTRIBS") {
387         // This variable has no attributes
388         variableDataStruct.numAtts = 0;
389       }
390       else if (attribString == "DUMMY_VAR") {
391         // This variable is a dummy coordinate variable
392         variableDataStruct.numAtts = 0;
393         dummyVarNames.insert(variableDataStruct.varName);
394       }
395       else {
396         ssTagName << "_LEN";
397         tag_name = ssTagName.str();
398         Tag varAttLenTag = 0;
399         rval = mbImpl->tag_get_handle(tag_name.c_str(), 0, MB_TYPE_INTEGER, varAttLenTag,
400                                       MB_TAG_ANY);MB_CHK_SET_ERR(rval, "Trouble getting conventional tag " << tag_name);
401         int varAttLenSz = 0;
402         rval = mbImpl->tag_get_length(varAttLenTag, varAttLenSz);MB_CHK_SET_ERR(rval, "Trouble getting length of conventional tag " << tag_name);
403         std::vector<int> varAttLen(varAttLenSz);
404         rval = mbImpl->tag_get_data(varAttLenTag, &fileSet, 1, &varAttLen[0]);MB_CHK_SET_ERR(rval, "Trouble getting data of conventional tag " << tag_name);
405 
406         rval = process_concatenated_attribute(varAttPtr, varAttSz, varAttLen, variableDataStruct.varAtts);MB_CHK_SET_ERR(rval, "Trouble processing attributes of variable " << var_name);
407 
408         if (MB_SUCCESS == rval)
409           dbgOut.tprintf(2, "Tag metadata for variable %s\n", tag_name.c_str());
410       }
411       // End attribute
412 
413       start = i + 1;
414       idxVar++;
415     } // if (p[i] == '\0')
416   }
417 
418   // Global attributes
419   tag_name = "__GLOBAL_ATTRIBS";
420   Tag globalAttTag = 0;
421   rval = mbImpl->tag_get_handle(tag_name.c_str(), 0, MB_TYPE_OPAQUE, globalAttTag,
422                                 MB_TAG_SPARSE | MB_TAG_VARLEN);MB_CHK_SET_ERR(rval, "Trouble getting conventional tag " << tag_name);
423   std::vector<int> gattLen;
424 
425   const void* gattptr = NULL;
426   int globalAttSz = 0;
427   rval = mbImpl->tag_get_by_ptr(globalAttTag, &fileSet, 1, &gattptr, &globalAttSz);MB_CHK_SET_ERR(rval, "Trouble getting data of conventional tag " << tag_name);
428 
429   if (MB_SUCCESS == rval)
430     dbgOut.tprintf(2, "Tag value retrieved for %s size %d\n", tag_name.c_str(), globalAttSz);
431 
432   // <__GLOBAL_ATTRIBS_LEN>
433   tag_name = "__GLOBAL_ATTRIBS_LEN";
434   Tag globalAttLenTag = 0;
435 
436   rval = mbImpl->tag_get_handle(tag_name.c_str(), 0, MB_TYPE_INTEGER, globalAttLenTag,
437                                 MB_TAG_ANY);MB_CHK_SET_ERR(rval, "Trouble getting conventional tag " << tag_name);
438   int sizeGAtt = 0;
439   rval = mbImpl->tag_get_length(globalAttLenTag, sizeGAtt);MB_CHK_SET_ERR(rval, "Trouble getting length of conventional tag " << tag_name);
440   gattLen.resize(sizeGAtt);
441   rval = mbImpl->tag_get_data(globalAttLenTag, &fileSet, 1, &gattLen[0]);MB_CHK_SET_ERR(rval, "Trouble getting data of conventional tag " << tag_name);
442   if (MB_SUCCESS == rval)
443     dbgOut.tprintf(2, "Tag retrieved for variable %s\n", tag_name.c_str());
444 
445   rval = process_concatenated_attribute(gattptr, globalAttSz, gattLen, globalAtts);MB_CHK_SET_ERR(rval, "Trouble processing global attributes");
446 
447   return MB_SUCCESS;
448 }
449 
450 // Reverse process from create_attrib_string
process_concatenated_attribute(const void * attPtr,int attSz,std::vector<int> & attLen,std::map<std::string,AttData> & attributes)451 ErrorCode WriteNC::process_concatenated_attribute(const void* attPtr, int attSz,
452                                                   std::vector<int>& attLen,
453                                                   std::map<std::string, AttData>& attributes)
454 {
455   std::size_t start = 0;
456   std::size_t att_counter = 0;
457   std::string concatString((char*)attPtr, (char*)attPtr + attSz);
458 
459   for (std::size_t i = 0; i != (size_t)attSz; i++) {
460     if (concatString[i] == '\0') {
461       std::string att_name(&concatString[start], i - start);
462       start = i + 1;
463       while (concatString[i] != ';')
464         ++i;
465       std::string data_type(&concatString[start], i - start);
466       ++i;
467       start = i;
468       i = attLen[att_counter];
469       if (concatString[i] != ';')
470         MB_SET_ERR(MB_FAILURE, "Error parsing attributes");
471 
472       std::string data_val(&concatString[start], i - start);
473       start = i + 1;
474 
475       AttData& attrib = attributes[att_name];
476       attrib.attValue = data_val;
477       attrib.attLen = data_val.size();
478 
479       if (data_type == "char")
480         attrib.attDataType = NC_CHAR;
481       else if (data_type == "double")
482         attrib.attDataType = NC_DOUBLE;
483       else if (data_type == "float")
484         attrib.attDataType = NC_FLOAT;
485       else if (data_type == "int")
486         attrib.attDataType = NC_INT;
487       else if (data_type == "short")
488         attrib.attDataType = NC_SHORT;
489 
490       ++att_counter;
491       dbgOut.tprintf(2, "       Process attribute %s with value %s \n", att_name.c_str(), data_val.c_str());
492     }
493   }
494 
495   return MB_SUCCESS;
496 }
497 
498 } // namespace moab
499