1 /*
2  * NCWriteHelper.cpp
3  *
4  *  Created on: Mar 28, 2014
5  *      Author: iulian
6  */
7 
8 #include "NCWriteHelper.hpp"
9 #include "NCWriteEuler.hpp"
10 #include "NCWriteFV.hpp"
11 #include "NCWriteHOMME.hpp"
12 #include "NCWriteMPAS.hpp"
13 #include "NCWriteGCRM.hpp"
14 
15 #include "moab/WriteUtilIface.hpp"
16 #include "MBTagConventions.hpp"
17 
18 #include <sstream>
19 
20 #ifdef WIN32
21 #ifdef size_t
22 #undef size_t
23 #endif
24 #endif
25 
26 namespace moab {
27 
28 //! Get appropriate helper instance for WriteNC class; based on some info in the file set
get_nc_helper(WriteNC * writeNC,int fileId,const FileOptions & opts,EntityHandle fileSet)29 NCWriteHelper* NCWriteHelper::get_nc_helper(WriteNC* writeNC, int fileId, const FileOptions& opts, EntityHandle fileSet)
30 {
31   std::string& grid_type = writeNC->grid_type;
32   if (grid_type == "CAM_EUL")
33     return new (std::nothrow) NCWriteEuler(writeNC, fileId, opts, fileSet);
34   else if (grid_type == "CAM_FV")
35     return new (std::nothrow) NCWriteFV(writeNC, fileId, opts, fileSet);
36   else if (grid_type == "CAM_SE")
37     return new (std::nothrow) NCWriteHOMME(writeNC, fileId, opts, fileSet);
38   else if (grid_type == "MPAS")
39     return new (std::nothrow) NCWriteMPAS(writeNC, fileId, opts, fileSet);
40   else if (grid_type == "GCRM")
41     return new (std::nothrow) NCWriteGCRM(writeNC, fileId, opts, fileSet);
42 
43   // Unknown NetCDF grid
44   return NULL;
45 }
46 
collect_variable_data(std::vector<std::string> & var_names,std::vector<int> & tstep_nums)47 ErrorCode NCWriteHelper::collect_variable_data(std::vector<std::string>& var_names, std::vector<int>& tstep_nums)
48 {
49   Interface*& mbImpl = _writeNC->mbImpl;
50   std::vector<std::string>& dimNames = _writeNC->dimNames;
51   std::vector<int>& dimLens = _writeNC->dimLens;
52   std::set<std::string>& usedCoordinates = _writeNC->usedCoordinates;
53   std::set<std::string>& dummyVarNames = _writeNC->dummyVarNames;
54   std::map<std::string, WriteNC::VarData>& varInfo = _writeNC->varInfo;
55   DebugOutput& dbgOut = _writeNC->dbgOut;
56 
57   ErrorCode rval;
58 
59   usedCoordinates.clear();
60 
61   if (tstep_nums.empty() && nTimeSteps > 0) {
62     // No timesteps input, get them all
63     for (int i = 0; i < nTimeSteps; i++)
64       tstep_nums.push_back(i);
65   }
66 
67   for (size_t i = 0; i < var_names.size(); i++) {
68     std::string varname = var_names[i];
69     std::map<std::string, WriteNC::VarData>::iterator vit = varInfo.find(varname);
70     if (vit == varInfo.end())
71       MB_SET_ERR(MB_FAILURE, "Can't find variable " << varname);
72 
73     WriteNC::VarData& currentVarData = vit->second;
74 
75     dbgOut.tprintf(2, "    for variable %s varDims.size %d \n", varname.c_str(), (int)currentVarData.varDims.size());
76     for (size_t j = 0; j < currentVarData.varDims.size(); j++) {
77       std::string dimName = dimNames[currentVarData.varDims[j]];
78       vit = varInfo.find(dimName);
79       if (vit == varInfo.end())
80         MB_SET_ERR(MB_FAILURE, "Can't find coordinate variable " << dimName);
81 
82       usedCoordinates.insert(dimName); // Collect those used, we will need to write them to the file
83       dbgOut.tprintf(2, "    for variable %s need dimension %s with length %d\n", varname.c_str(), dimName.c_str(), dimLens[currentVarData.varDims[j]]);
84     }
85 
86     // Process coordinate variables later
87     if (usedCoordinates.find(varname) != usedCoordinates.end())
88       continue;
89 
90     // Default has_tsteps is false
91     if (std::find(currentVarData.varDims.begin(), currentVarData.varDims.end(), tDim) != currentVarData.varDims.end())
92       currentVarData.has_tsteps = true;
93 
94     // Default numLev is 0
95     if ((std::find(currentVarData.varDims.begin(), currentVarData.varDims.end(), levDim) != currentVarData.varDims.end()))
96       currentVarData.numLev = nLevels;
97 
98     // Process set variables
99     if (WriteNC::ENTLOCSET == currentVarData.entLoc) {
100       if (currentVarData.has_tsteps) {
101         // Set variables with timesteps, e.g. xtime(Time) or xtime(Time, StrLen)
102         // TBD
103         MB_SET_ERR(MB_NOT_IMPLEMENTED, "Writing set variables with timesteps is not implemented yet");
104       }
105       else {
106         // Get the tag with varname
107         Tag tag = 0;
108         rval = mbImpl->tag_get_handle(varname.c_str(), tag);MB_CHK_SET_ERR(rval, "Can't find tag " << varname);
109         currentVarData.varTags.push_back(tag); // Really, only one for these
110         const void* data;
111         int size;
112         rval = mbImpl->tag_get_by_ptr(tag, &_fileSet, 1, &data, &size);MB_CHK_SET_ERR(rval, "Can't get data of tag " << varname);
113 
114         // Find the type of tag, and use it
115         DataType type;
116         rval = mbImpl->tag_get_data_type(tag, type);MB_CHK_SET_ERR(rval, "Can't get data type of tag " << varname);
117 
118         currentVarData.varDataType = NC_DOUBLE;
119         if (MB_TYPE_INTEGER == type)
120           currentVarData.varDataType = NC_INT;
121 
122         assert(0 == currentVarData.memoryHogs.size()); // Nothing so far
123         currentVarData.memoryHogs.push_back((void*)data);
124 
125         if (currentVarData.varDims.empty()) {
126           // Scalar variable
127           currentVarData.writeStarts.push_back(0);
128           currentVarData.writeCounts.push_back(1);
129         }
130         else {
131           for (size_t j = 0; j < currentVarData.varDims.size(); j++) {
132             currentVarData.writeStarts.push_back(0);
133             currentVarData.writeCounts.push_back(dimLens[currentVarData.varDims[j]]);
134           }
135         }
136 
137         // Get variable size
138         currentVarData.sz = 1;
139         for (std::size_t idx = 0; idx != currentVarData.writeCounts.size(); idx++)
140           currentVarData.sz *= currentVarData.writeCounts[idx];
141       }
142     } // if (WriteNC::ENTLOCSET == currentVarData.entLoc)
143     // Process non-set variables
144     else {
145       Tag indexedTag = 0;
146 
147       if (currentVarData.has_tsteps) {
148         for (unsigned int t = 0; t < tstep_nums.size(); t++) {
149           std::stringstream ssTagNameWithIndex;
150           ssTagNameWithIndex << varname << tstep_nums[t];
151           rval = mbImpl->tag_get_handle(ssTagNameWithIndex.str().c_str(), indexedTag);MB_CHK_SET_ERR(rval, "Can't find tag " << ssTagNameWithIndex.str());
152           dbgOut.tprintf(2, "    found indexed tag %d with name %s\n", tstep_nums[t], ssTagNameWithIndex.str().c_str());
153           currentVarData.varTags.push_back(indexedTag);
154         }
155       }
156       else {
157         // This should be a user-created non-set variable without timesteps
158         // Treat it like having one, 0th, timestep
159         std::stringstream ssTagNameWithIndex;
160         ssTagNameWithIndex << varname << 0;
161         rval = mbImpl->tag_get_handle(ssTagNameWithIndex.str().c_str(), indexedTag);MB_CHK_SET_ERR(rval, "Can't find tag " << ssTagNameWithIndex.str() << " for a user-created variable");
162         dbgOut.tprintf(2, "    found indexed tag 0 with name %s\n", ssTagNameWithIndex.str().c_str());
163         currentVarData.varTags.push_back(indexedTag);
164       }
165 
166       // The type of the tag is fixed though
167       DataType type;
168       rval = mbImpl->tag_get_data_type(indexedTag, type);MB_CHK_SET_ERR(rval, "Can't get data type of tag " << varname);
169 
170       currentVarData.varDataType = NC_DOUBLE;
171       if (MB_TYPE_INTEGER == type)
172         currentVarData.varDataType = NC_INT;
173     }
174   } // for (size_t i = 0; i < var_names.size(); i++)
175 
176   // Process coordinate variables here
177   // Check that for used coordinates we have found the tags
178   for (std::set<std::string>::iterator setIt = usedCoordinates.begin();
179       setIt != usedCoordinates.end(); ++setIt) {
180     const std::string& coordName = *setIt;
181 
182     std::map<std::string, WriteNC::VarData>::iterator vit = varInfo.find(coordName);
183     if (vit == varInfo.end())
184       MB_SET_ERR(MB_FAILURE, "Can't find coordinate variable " << coordName);
185 
186     WriteNC::VarData& varCoordData = vit->second;
187     Tag coordTag = 0;
188     rval = mbImpl->tag_get_handle(coordName.c_str(), coordTag);MB_CHK_SET_ERR(rval, "Can't find tag " << coordName);
189     varCoordData.varTags.push_back(coordTag); // Really, only one for these
190 
191     const void* data;
192     int sizeCoordinate;
193     rval = mbImpl->tag_get_by_ptr(coordTag, &_fileSet, 1, &data, &sizeCoordinate);MB_CHK_SET_ERR(rval, "Can't get coordinate values of " << coordName);
194     dbgOut.tprintf(2, "    found coordinate tag with name %s and length %d\n", coordName.c_str(),
195         sizeCoordinate);
196 
197     // Find the type of tag, and use it
198     DataType type;
199     rval = mbImpl->tag_get_data_type(coordTag, type);MB_CHK_SET_ERR(rval, "Can't get data type of tag " << coordName);
200     varCoordData.varDataType = NC_DOUBLE;
201     if (MB_TYPE_INTEGER == type)
202       varCoordData.varDataType = NC_INT;
203 
204     // Get dimension length (the only dimension of this coordinate variable, with the same name)
205     assert(1 == varCoordData.varDims.size());
206     int coordDimLen = dimLens[varCoordData.varDims[0]];
207 
208     if (dummyVarNames.find(coordName) != dummyVarNames.end()) {
209       // For a dummy coordinate variable, the tag size is always 1
210       // The number of coordinates should be set to dimension length, instead of 1
211       assert(1 == sizeCoordinate);
212       sizeCoordinate = coordDimLen;
213 
214       // No variable data to write
215       data = NULL;
216     }
217     else {
218       // The number of coordinates should be exactly the same as dimension length
219       // However, if timesteps spread across files and time tag has been updated,
220       // sizeCoordinate will be larger
221       if (varCoordData.varDims[0] != tDim)
222         assert(sizeCoordinate == coordDimLen);
223     }
224 
225     // For time, the actual output size and values are determined by tstep_nums
226     if (varCoordData.varDims[0] == tDim) {
227       // Does not apply to dummy time tag (e.g. 'Time' tag of MPAS), when timesteps
228       // spread across files
229       if (NULL != data)
230         assert(tstep_nums.size() > 0 && tstep_nums.size() <= (size_t)sizeCoordinate);
231 
232       sizeCoordinate = tstep_nums.size();
233 
234       if (NULL != data) {
235         assert(NC_DOUBLE == varCoordData.varDataType);
236         timeStepVals.resize(sizeCoordinate);
237         for (unsigned int t = 0; t < tstep_nums.size(); t++)
238           timeStepVals[t] = ((double*)data)[tstep_nums[t]];
239 
240         data = &timeStepVals[0];
241       }
242     }
243 
244     // This is the length
245     varCoordData.sz = sizeCoordinate;
246     varCoordData.writeStarts.resize(1);
247     varCoordData.writeStarts[0] = 0;
248     varCoordData.writeCounts.resize(1);
249     varCoordData.writeCounts[0] = sizeCoordinate;
250 
251     assert(0 == varCoordData.memoryHogs.size()); // Nothing so far
252     varCoordData.memoryHogs.push_back((void*)data);
253   } // for (std::set<std::string>::iterator setIt ...
254 
255   return MB_SUCCESS;
256 }
257 
init_file(std::vector<std::string> & var_names,std::vector<std::string> & desired_names,bool append)258 ErrorCode NCWriteHelper::init_file(std::vector<std::string>& var_names, std::vector<std::string>& desired_names, bool append)
259 {
260   std::vector<std::string>& dimNames = _writeNC->dimNames;
261   std::set<std::string>& usedCoordinates = _writeNC->usedCoordinates;
262   std::set<std::string>& dummyVarNames = _writeNC->dummyVarNames;
263   std::map<std::string, WriteNC::VarData>& varInfo = _writeNC->varInfo;
264   std::map<std::string, WriteNC::AttData>& globalAtts = _writeNC->globalAtts;
265   DebugOutput& dbgOut = _writeNC->dbgOut;
266 
267   int tDim_in_dimNames = tDim;
268   int levDim_in_dimNames = levDim;
269 
270   // If append mode, make sure we are in define mode; a simple open will not allow creation of new variables
271   if (append) {
272     int errcode = NCFUNC(redef)(_fileId);
273     if (errcode != NC_NOERR)
274       MB_SET_ERR(MB_FAILURE, "Can't open file in redefine mode");
275   }
276 
277   // First initialize all coordinates, then fill VarData for actual variables (and dimensions)
278   // Check that for used coordinates we have found the tags
279   for (std::set<std::string>::iterator setIt = usedCoordinates.begin();
280       setIt != usedCoordinates.end(); ++setIt) {
281     const std::string& coordName = *setIt;
282 
283     std::map<std::string, WriteNC::VarData>::iterator vit = varInfo.find(coordName);
284     if (vit == varInfo.end())
285       MB_SET_ERR(MB_FAILURE, "Can't find coordinate variable " << coordName);
286 
287     WriteNC::VarData& varCoordData = vit->second;
288     varCoordData.varDims.resize(1);
289 
290     // If not append, create it for sure
291     // If append, we might already have it, including the tag / variable with the same name
292     /*
293      * int ncmpi_inq_dimid(int ncid, const char *name, int *idp);
294      */
295     if (append) {
296       int dimId;
297       if (NCFUNC(inq_dimid)(_fileId, coordName.c_str(), &dimId) == NC_NOERR) { // If not found, create it later
298         varCoordData.varDims[0] = dimId;
299         dbgOut.tprintf(2, "    file already has coordName %s dim id is %d \n", coordName.c_str(), (int)varCoordData.varDims[0]);
300 
301         // Update tDim and levDim to actual dimension id
302         if (coordName == dimNames[tDim_in_dimNames])
303           tDim = varCoordData.varDims[0];
304         else if (coordName == dimNames[levDim_in_dimNames])
305           levDim = varCoordData.varDims[0];
306 
307         // Skip dummy coordinate variables (e.g. ncol)
308         if (dummyVarNames.find(coordName) != dummyVarNames.end())
309           continue;
310 
311         // Check that the coordinate is a variable too
312         // Inquire for a variable with the same name
313         int varId;
314         if (NCFUNC(inq_varid)(_fileId, coordName.c_str(), &varId) != NC_NOERR)
315           MB_SET_ERR(MB_FAILURE, "We do not have a variable with the same name " << coordName);
316         // We should also check that this variable has one dimension, and it is dimId
317         varCoordData.varId = varId;
318         dbgOut.tprintf(2, "    file already has coordinate %s and varId is %d \n", coordName.c_str(), varId);
319 
320         continue; // Maybe more checks are needed here
321       }
322     }
323 
324     /* int nc_def_dim (int ncid, const char *name, size_t len, int *dimidp);
325        * example:  status = nc_def_dim(fileId, "lat", 18L, &latid);
326     */
327 
328     // Actually define a dimension
329     if (NCFUNC(def_dim)(_fileId, coordName.c_str(), (size_t)varCoordData.sz,
330         &varCoordData.varDims[0]) != NC_NOERR)
331       MB_SET_ERR(MB_FAILURE, "Failed to generate dimension " << coordName);
332     dbgOut.tprintf(2, "    for coordName %s dim id is %d \n", coordName.c_str(), (int)varCoordData.varDims[0]);
333 
334     // Update tDim and levDim to actual dimension id
335     if (coordName == dimNames[tDim_in_dimNames])
336       tDim = varCoordData.varDims[0];
337     else if (coordName == dimNames[levDim_in_dimNames])
338       levDim = varCoordData.varDims[0];
339 
340     // Create a variable with the same name, and its only dimension the one we just defined
341     /*
342      * int nc_def_var (int ncid, const char *name, nc_type xtype,
343                        int ndims, const int dimids[], int *varidp);
344        example: http://www.unidata.ucar.edu/software/netcdf/docs/netcdf-c/nc_005fdef_005fvar.html#nc_005fdef_005fvar
345      */
346 
347     // Skip dummy coordinate variables (e.g. ncol)
348     if (dummyVarNames.find(coordName) != dummyVarNames.end())
349       continue;
350 
351     // Define a coordinate variable
352     if (NCFUNC(def_var)(_fileId, coordName.c_str(), varCoordData.varDataType,
353         1, &(varCoordData.varDims[0]), &varCoordData.varId) != NC_NOERR)
354       MB_SET_ERR(MB_FAILURE, "Failed to create coordinate variable " << coordName);
355 
356     dbgOut.tprintf(2, "    for coordName %s variable id is %d \n", coordName.c_str(), varCoordData.varId);
357   }
358 
359   // Now look at requested variables, and update from the index in dimNames to the actual dimension id
360   for (size_t i = 0; i < var_names.size(); i++) {
361     std::map<std::string, WriteNC::VarData>::iterator vit = varInfo.find(var_names[i]);
362     if (vit == varInfo.end())
363       MB_SET_ERR(MB_FAILURE, "Can't find requested variable " << var_names[i]);
364 
365     // Skip coordinate variables
366     if (usedCoordinates.find(var_names[i]) != usedCoordinates.end())
367       continue;
368 
369     WriteNC::VarData& variableData = vit->second;
370 
371     // The index is for dimNames; we need to find out the actual dimension id (from above)
372     int numDims = (int)variableData.varDims.size();
373     for (int j = 0; j < numDims; j++) {
374       std::string dimName = dimNames[variableData.varDims[j]];
375       std::map<std::string, WriteNC::VarData>::iterator vit2 = varInfo.find(dimName);
376       if (vit2 == varInfo.end())
377         MB_SET_ERR(MB_FAILURE, "Can't find requested coordinate variable " << dimName);
378 
379       WriteNC::VarData& coordData = vit2->second;
380       // Index in dimNames to actual dimension id
381       variableData.varDims[j] = coordData.varDims[0]; // This one, being a coordinate, is the only one
382       dbgOut.tprintf(2, "          dimension with index %d name %s has ID %d \n",
383           j, dimName.c_str(), variableData.varDims[j]);
384     }
385 
386     // Define the variable now:
387     int errCode = NCFUNC(def_var)(_fileId, desired_names[i].c_str(), variableData.varDataType,
388         (int)variableData.varDims.size(), &(variableData.varDims[0]),
389         &variableData.varId);
390     if (errCode != NC_NOERR)
391       MB_SET_ERR(MB_FAILURE, "Failed to create requested variable " << desired_names[i]);
392 
393     dbgOut.tprintf(2, "    for variable %s with desired name %s variable id is %d \n", var_names[i].c_str(),
394         desired_names[i].c_str(), variableData.varId);
395     // Now define the variable, with all dimensions
396   }
397 
398   // Define global attributes (exactly copied from the original file for the time being)
399   // Should we modify some of them (e.g. revision_Id) later?
400   std::map<std::string, WriteNC::AttData>::iterator attIt;
401   for (attIt = globalAtts.begin(); attIt != globalAtts.end(); ++attIt) {
402     const std::string& attName = attIt->first;
403     WriteNC::AttData& attData = attIt->second;
404     NCDF_SIZE& attLen = attData.attLen;
405     nc_type& attDataType = attData.attDataType;
406     const std::string& attValue = attData.attValue;
407 
408     switch (attDataType) {
409       case NC_BYTE:
410       case NC_CHAR:
411         if (NC_NOERR != NCFUNC(put_att_text)(_fileId, NC_GLOBAL, attName.c_str(), attLen, attValue.c_str()))
412           MB_SET_ERR(MB_FAILURE, "Failed to define text type attribute");
413         break;
414       case NC_DOUBLE:
415         if (NC_NOERR != NCFUNC(put_att_double)(_fileId, NC_GLOBAL, attName.c_str(), NC_DOUBLE, 1, (double*)attValue.c_str()))
416           MB_SET_ERR(MB_FAILURE, "Failed to define double type attribute");
417         break;
418       case NC_FLOAT:
419         if (NC_NOERR != NCFUNC(put_att_float)(_fileId, NC_GLOBAL, attName.c_str(), NC_FLOAT, 1, (float*)attValue.c_str()))
420           MB_SET_ERR(MB_FAILURE, "Failed to define float type attribute");
421         break;
422       case NC_INT:
423         if (NC_NOERR != NCFUNC(put_att_int)(_fileId, NC_GLOBAL, attName.c_str(), NC_INT, 1, (int*)attValue.c_str()))
424           MB_SET_ERR(MB_FAILURE, "Failed to define int type attribute");
425         break;
426       case NC_SHORT:
427         if (NC_NOERR != NCFUNC(put_att_short)(_fileId, NC_GLOBAL, attName.c_str(), NC_SHORT, 1, (short*)attValue.c_str()))
428           MB_SET_ERR(MB_FAILURE, "Failed to define short type attribute");
429         break;
430       default:
431         MB_SET_ERR(MB_FAILURE, "Unknown attribute data type");
432     }
433   }
434 
435   // Take it out of define mode
436   if (NC_NOERR != NCFUNC(enddef)(_fileId))
437     MB_SET_ERR(MB_FAILURE, "Failed to close define mode");
438 
439   return MB_SUCCESS;
440 }
441 
write_values(std::vector<std::string> & var_names,std::vector<int> & tstep_nums)442 ErrorCode NCWriteHelper::write_values(std::vector<std::string>& var_names, std::vector<int>& tstep_nums)
443 {
444   std::set<std::string>& usedCoordinates = _writeNC->usedCoordinates;
445   std::set<std::string>& dummyVarNames = _writeNC->dummyVarNames;
446   std::map<std::string, WriteNC::VarData>& varInfo = _writeNC->varInfo;
447 
448   std::vector<WriteNC::VarData> vdatas;
449   std::vector<WriteNC::VarData> vsetdatas;
450 
451   // For set variables, include coordinates used by requested var_names
452   for (std::set<std::string>::iterator setIt = usedCoordinates.begin();
453       setIt != usedCoordinates.end(); ++setIt) {
454     const std::string& coordName = *setIt;
455 
456     // Skip dummy coordinate variables (if any)
457     if (dummyVarNames.find(coordName) != dummyVarNames.end())
458       continue;
459 
460     std::map<std::string, WriteNC::VarData>::iterator vit = varInfo.find(coordName);
461     if (vit == varInfo.end()) {
462       MB_SET_ERR(MB_FAILURE, "Can't find coordinate variable " << coordName);
463     }
464 
465      vsetdatas.push_back(vit->second);
466   }
467 
468   // Collect non-set and set variables from requested var_names
469   for (unsigned int i = 0; i < var_names.size(); i++) {
470     std::map<std::string, WriteNC::VarData>::iterator vit = varInfo.find(var_names[i]);
471     if (vit == varInfo.end()) {
472       MB_SET_ERR(MB_FAILURE, "Can't find requested variable " << var_names[i]);
473     }
474 
475     WriteNC::VarData& variableData = vit->second;
476     if (WriteNC::ENTLOCSET == variableData.entLoc) {
477       // Used coordinates has all ready been included
478       if (usedCoordinates.find(var_names[i]) != usedCoordinates.end())
479         continue;
480 
481       vsetdatas.push_back(variableData);
482     }
483     else
484       vdatas.push_back(variableData);
485   }
486 
487   // Assume that the data ranges do not overlap across processors
488   // While overlapped writing might still work, we should better not take that risk
489   write_nonset_variables(vdatas, tstep_nums);
490 
491   // Use independent I/O mode put, since this write is only for the root processor
492   write_set_variables(vsetdatas, tstep_nums);
493 
494   return MB_SUCCESS;
495 }
496 
write_set_variables(std::vector<WriteNC::VarData> & vsetdatas,std::vector<int> &)497 ErrorCode NCWriteHelper::write_set_variables(std::vector<WriteNC::VarData>& vsetdatas, std::vector<int>& /* tstep_nums */)
498 {
499   int success;
500 
501 // CAUTION: if the NetCDF ID is from a previous call to ncmpi_create rather than ncmpi_open,
502 // all processors need to call ncmpi_begin_indep_data(). If only the root processor does so,
503 // ncmpi_begin_indep_data() call will be blocked forever :(
504  #ifdef MOAB_HAVE_PNETCDF
505    // Enter independent I/O mode
506    success = NCFUNC(begin_indep_data)(_fileId);
507    if (success)
508      MB_SET_ERR(MB_FAILURE, "Failed to begin independent I/O mode");
509  #endif
510 
511    int rank = 0;
512  #ifdef MOAB_HAVE_MPI
513    bool& isParallel = _writeNC->isParallel;
514    if (isParallel) {
515      ParallelComm*& myPcomm = _writeNC->myPcomm;
516      rank = myPcomm->proc_config().proc_rank();
517    }
518  #endif
519    if (0 == rank) {
520      for (unsigned int i = 0; i < vsetdatas.size(); i++) {
521        WriteNC::VarData& variableData = vsetdatas[i];
522 
523        // Set variables with timesteps, e.g. xtime(Time) or xtime(Time, StrLen)
524        if (variableData.has_tsteps) {
525          MB_SET_ERR(MB_NOT_IMPLEMENTED, "Writing set variables with timesteps is not implemented yet");
526        }
527 
528        switch (variableData.varDataType) {
529          case NC_DOUBLE:
530            // Independent I/O mode put
531            success = NCFUNCP(_vara_double)(_fileId, variableData.varId, &variableData.writeStarts[0],
532                      &variableData.writeCounts[0], (double*)(variableData.memoryHogs[0]));
533            if (success)
534              MB_SET_ERR(MB_FAILURE, "Failed to write double data for variable " << variableData.varName);
535            break;
536          case NC_INT:
537            // Independent I/O mode put
538            success = NCFUNCP(_vara_int)(_fileId, variableData.varId, &variableData.writeStarts[0],
539                      &variableData.writeCounts[0], (int*)(variableData.memoryHogs[0]));
540            if (success)
541              MB_SET_ERR(MB_FAILURE, "Failed to write int data for variable " << variableData.varName);
542            break;
543          default:
544            MB_SET_ERR(MB_NOT_IMPLEMENTED, "Writing non-double or non-int data is not implemented yet");
545        }
546      }
547    }
548 
549  #ifdef MOAB_HAVE_PNETCDF
550    // End independent I/O mode
551    success = NCFUNC(end_indep_data)(_fileId);
552    if (success)
553      MB_SET_ERR(MB_FAILURE, "Failed to end independent I/O mode");
554  #endif
555 
556    return MB_SUCCESS;
557 }
558 
collect_mesh_info()559 ErrorCode ScdNCWriteHelper::collect_mesh_info()
560 {
561   Interface*& mbImpl = _writeNC->mbImpl;
562   std::vector<std::string>& dimNames = _writeNC->dimNames;
563   std::vector<int>& dimLens = _writeNC->dimLens;
564 
565   ErrorCode rval;
566 
567   // Look for time dimension
568   std::vector<std::string>::iterator vecIt;
569   if ((vecIt = std::find(dimNames.begin(), dimNames.end(), "time")) != dimNames.end())
570     tDim = vecIt - dimNames.begin();
571   else if ((vecIt = std::find(dimNames.begin(), dimNames.end(), "t")) != dimNames.end())
572     tDim = vecIt - dimNames.begin();
573   else {
574     MB_SET_ERR(MB_FAILURE, "Couldn't find 'time' or 't' dimension");
575   }
576   nTimeSteps = dimLens[tDim];
577 
578   // Get number of levels
579   if ((vecIt = std::find(dimNames.begin(), dimNames.end(), "lev")) != dimNames.end())
580     levDim = vecIt - dimNames.begin();
581   else if ((vecIt = std::find(dimNames.begin(), dimNames.end(), "ilev")) != dimNames.end())
582     levDim = vecIt - dimNames.begin();
583   else {
584     MB_SET_ERR(MB_FAILURE, "Couldn't find 'lev' or 'ilev' dimension");
585   }
586   nLevels = dimLens[levDim];
587 
588   // __<dim_name>_LOC_MINMAX (for slon, slat, lon and lat)
589   Tag convTag = 0;
590   rval = mbImpl->tag_get_handle("__slon_LOC_MINMAX", 0, MB_TYPE_INTEGER, convTag,
591                                 MB_TAG_ANY);MB_CHK_SET_ERR(rval, "Trouble getting conventional tag __slon_LOC_MINMAX");
592   int val[2];
593   rval = mbImpl->tag_get_data(convTag, &_fileSet, 1, val);MB_CHK_SET_ERR(rval, "Trouble getting data of conventional tag __slon_LOC_MINMAX");
594   lDims[0] = val[0];
595   lDims[3] = val[1];
596 
597   rval = mbImpl->tag_get_handle("__slat_LOC_MINMAX", 0, MB_TYPE_INTEGER, convTag,
598                                 MB_TAG_ANY);MB_CHK_SET_ERR(rval, "Trouble getting conventional tag __slat_LOC_MINMAX");
599   rval = mbImpl->tag_get_data(convTag, &_fileSet, 1, val);MB_CHK_SET_ERR(rval, "Trouble getting data of conventional tag __slat_LOC_MINMAX");
600   lDims[1] = val[0];
601   lDims[4] = val[1];
602 
603   rval = mbImpl->tag_get_handle("__lon_LOC_MINMAX", 0, MB_TYPE_INTEGER, convTag,
604                                 MB_TAG_ANY);MB_CHK_SET_ERR(rval, "Trouble getting conventional tag __lon_LOC_MINMAX");
605   rval = mbImpl->tag_get_data(convTag, &_fileSet, 1, val);MB_CHK_SET_ERR(rval, "Trouble getting data of conventional tag __lon_LOC_MINMAX");
606   lCDims[0] = val[0];
607   lCDims[3] = val[1];
608 
609   rval = mbImpl->tag_get_handle("__lat_LOC_MINMAX", 0, MB_TYPE_INTEGER, convTag,
610                                 MB_TAG_ANY);MB_CHK_SET_ERR(rval, "Trouble getting conventional tag __lat_LOC_MINMAX");
611   rval = mbImpl->tag_get_data(convTag, &_fileSet, 1, val);MB_CHK_SET_ERR(rval, "Trouble getting data of conventional tag __lat_LOC_MINMAX");
612   lCDims[1] = val[0];
613   lCDims[4] = val[1];
614 
615   // Get local faces
616   rval = mbImpl->get_entities_by_dimension(_fileSet, 2, localCellsOwned);MB_CHK_SET_ERR(rval, "Trouble getting local faces in current file set");
617   assert(!localCellsOwned.empty());
618 
619 #ifdef MOAB_HAVE_MPI
620   bool& isParallel = _writeNC->isParallel;
621   if (isParallel) {
622     ParallelComm*& myPcomm = _writeNC->myPcomm;
623     int procs = myPcomm->proc_config().proc_size();
624     if (procs > 1) {
625       rval = myPcomm->filter_pstatus(localCellsOwned, PSTATUS_NOT_OWNED, PSTATUS_NOT);MB_CHK_SET_ERR(rval, "Trouble getting owned faces in current file set");
626     }
627   }
628 #endif
629 
630   return MB_SUCCESS;
631 }
632 
collect_variable_data(std::vector<std::string> & var_names,std::vector<int> & tstep_nums)633 ErrorCode ScdNCWriteHelper::collect_variable_data(std::vector<std::string>& var_names, std::vector<int>& tstep_nums)
634 {
635   NCWriteHelper::collect_variable_data(var_names, tstep_nums);
636 
637   std::map<std::string, WriteNC::VarData>& varInfo = _writeNC->varInfo;
638 
639   for (size_t i = 0; i < var_names.size(); i++) {
640     std::string varname = var_names[i];
641     std::map<std::string, WriteNC::VarData>::iterator vit = varInfo.find(varname);
642     if (vit == varInfo.end())
643       MB_SET_ERR(MB_FAILURE, "Can't find variable " << varname);
644 
645     WriteNC::VarData& currentVarData = vit->second;
646 #ifndef NDEBUG
647     std::vector<int>& varDims = currentVarData.varDims;
648 #endif
649 
650     // Skip set variables, which were already processed in NCWriteHelper::collect_variable_data()
651     if (WriteNC::ENTLOCSET == currentVarData.entLoc)
652       continue;
653 
654     // Set up writeStarts and writeCounts (maximum number of dimensions is 4)
655     currentVarData.writeStarts.resize(4);
656     currentVarData.writeCounts.resize(4);
657     unsigned int dim_idx = 0;
658 
659     // First: time
660     if (currentVarData.has_tsteps) {
661       // Non-set variables with timesteps
662       // 4 dimensions like (time, lev, lat, lon)
663       // 3 dimensions like (time, lat, lon)
664       assert(4 == varDims.size() || 3 == varDims.size());
665 
666       // Time should be the first dimension
667       assert(tDim == varDims[0]);
668 
669       currentVarData.writeStarts[dim_idx] = 0; // This value is timestep dependent, will be set later
670       currentVarData.writeCounts[dim_idx] = 1;
671       dim_idx++;
672     }
673     else {
674       // Non-set variables without timesteps
675       // 3 dimensions like (lev, lat, lon)
676       // 2 dimensions like (lat, lon)
677       assert(3 == varDims.size() || 2 == varDims.size());
678     }
679 
680     // Next: lev
681     if (currentVarData.numLev > 0) {
682       // Non-set variables with levels
683       // 4 dimensions like (time, lev, lat, lon)
684       // 3 dimensions like (lev, lat, lon)
685       assert(4 == varDims.size() || 3 == varDims.size());
686 
687       currentVarData.writeStarts[dim_idx] = 0;
688       currentVarData.writeCounts[dim_idx] = currentVarData.numLev;
689       dim_idx++;
690     }
691     else {
692       // Non-set variables without levels
693       // 3 dimensions like (time, lat, lon)
694       // 2 dimensions like (lat, lon)
695       assert(3 == varDims.size() || 2 == varDims.size());
696     }
697 
698     // Finally: lat and lon
699     switch (currentVarData.entLoc) {
700       case WriteNC::ENTLOCFACE:
701         // Faces
702         currentVarData.writeStarts[dim_idx] = lCDims[1];
703         currentVarData.writeCounts[dim_idx] = lCDims[4] - lCDims[1] + 1;
704         currentVarData.writeStarts[dim_idx + 1] = lCDims[0];
705         currentVarData.writeCounts[dim_idx + 1] = lCDims[3] - lCDims[0] + 1;
706         break;
707       default:
708         MB_SET_ERR(MB_FAILURE, "Unexpected entity location type for variable " << varname);
709     }
710     dim_idx += 2;
711 
712     // Get variable size
713     currentVarData.sz = 1;
714     for (std::size_t idx = 0; idx < dim_idx; idx++)
715       currentVarData.sz *= currentVarData.writeCounts[idx];
716   } // for (size_t i = 0; i < var_names.size(); i++)
717 
718   return MB_SUCCESS;
719 }
720 
721 // Write CAM-EUL and CAM-FV non-set variables on non-shared quads (e.g. T)
722 // We assume that there are no variables on vertices and we do not support
723 // variables on edges (e.g. US in CAM-FV) for the time being
write_nonset_variables(std::vector<WriteNC::VarData> & vdatas,std::vector<int> & tstep_nums)724 ErrorCode ScdNCWriteHelper::write_nonset_variables(std::vector<WriteNC::VarData>& vdatas, std::vector<int>& tstep_nums)
725 {
726   Interface*& mbImpl = _writeNC->mbImpl;
727 
728   int success;
729 
730   // For each indexed variable tag, write a time step data
731   for (unsigned int i = 0; i < vdatas.size(); i++) {
732     WriteNC::VarData& variableData = vdatas[i];
733 
734     // Assume this variable is on faces for the time being
735     switch (variableData.entLoc) {
736       case WriteNC::ENTLOCFACE:
737         // Faces
738         break;
739       default:
740         MB_SET_ERR(MB_FAILURE, "Unexpected entity location type for variable " << variableData.varName);
741     }
742 
743     unsigned int num_timesteps;
744     unsigned int lat_idx = 0;
745     unsigned int lon_idx = 1;
746     if (variableData.has_tsteps) {
747       // Non-set variables with timesteps
748       // 4 dimensions like (time, lev, lat, lon)
749       // 3 dimensions like (time, lat, lon)
750       num_timesteps = tstep_nums.size();
751       lat_idx++;
752       lon_idx++;
753     }
754     else {
755       // Non-set variables without timesteps
756       // 3 dimensions like (lev, lat, lon)
757       // 2 dimensions like (lat, lon)
758       num_timesteps = 1;
759     }
760 
761     unsigned int num_lev;
762     if (variableData.numLev > 0) {
763       // Non-set variables with levels
764       // 4 dimensions like (time, lev, lat, lon)
765       // 3 dimensions like (lev, lat, lon)
766       num_lev = variableData.numLev;
767       lat_idx++;
768       lon_idx++;
769     }
770     else {
771       // Non-set variables without levels
772       // 3 dimensions like (time, lat, lon)
773       // 2 dimensions like (lat, lon)
774       num_lev = 1;
775     }
776 
777     size_t ni = variableData.writeCounts[lon_idx]; // lon
778     size_t nj = variableData.writeCounts[lat_idx]; // lat
779 
780     // At each timestep, we need to transpose tag format (lat, lon, lev) back
781     // to NC format (lev, lat, lon) for writing
782     for (unsigned int t = 0; t < num_timesteps; t++) {
783       // We will write one time step, and count will be one; start will be different
784       // Use tag_iterate to get tag data (assume that localCellsOwned is contiguous)
785       // We should also transpose for level so that means deep copy for transpose
786       if (tDim == variableData.varDims[0])
787         variableData.writeStarts[0] = t; // This is start for time
788       int count;
789       void* dataptr;
790       ErrorCode rval = mbImpl->tag_iterate(variableData.varTags[t], localCellsOwned.begin(), localCellsOwned.end(),
791                                            count, dataptr);MB_CHK_SET_ERR(rval, "Failed to iterate tag on owned faces");
792       assert(count == (int)localCellsOwned.size());
793 
794       // Now transpose and write tag data
795       // Use collective I/O mode put (synchronous write) for the time being, we can try
796       // nonblocking put (request aggregation) later
797       switch (variableData.varDataType) {
798         case NC_DOUBLE: {
799           std::vector<double> tmpdoubledata(ni*nj*num_lev);
800           if (num_lev > 1)
801             // Transpose (lat, lon, lev) back to (lev, lat, lon)
802             jik_to_kji(ni, nj, num_lev, &tmpdoubledata[0], (double*)(dataptr));
803           success = NCFUNCAP(_vara_double)(_fileId, variableData.varId,
804                     &variableData.writeStarts[0], &variableData.writeCounts[0],
805                     &tmpdoubledata[0]);
806           if (success)
807             MB_SET_ERR(MB_FAILURE, "Failed to write double data for variable " << variableData.varName);
808           break;
809         }
810         default:
811           MB_SET_ERR(MB_NOT_IMPLEMENTED, "Writing non-double data is not implemented yet");
812       }
813     }
814   }
815 
816   return MB_SUCCESS;
817 }
818 
819 } /* namespace moab */
820