1 #include "NCHelperHOMME.hpp"
2 #include "moab/ReadUtilIface.hpp"
3 #include "moab/FileOptions.hpp"
4 #include "moab/SpectralMeshTool.hpp"
5 
6 #include <cmath>
7 
8 namespace moab {
9 
NCHelperHOMME(ReadNC * readNC,int fileId,const FileOptions & opts,EntityHandle fileSet)10 NCHelperHOMME::NCHelperHOMME(ReadNC* readNC, int fileId, const FileOptions& opts, EntityHandle fileSet)
11 : UcdNCHelper(readNC, fileId, opts, fileSet),
12 _spectralOrder(-1), connectId(-1), isConnFile(false)
13 {
14   // Calculate spectral order
15   std::map<std::string, ReadNC::AttData>::iterator attIt = readNC->globalAtts.find("np");
16   if (attIt != readNC->globalAtts.end()) {
17     int success = NCFUNC(get_att_int)(readNC->fileId, attIt->second.attVarId, attIt->second.attName.c_str(), &_spectralOrder);
18     if (0 == success)
19       _spectralOrder--; // Spectral order is one less than np
20   }
21   else {
22     // As can_read_file() returns true and there is no global attribute "np", it should be a connectivity file
23     isConnFile = true;
24     _spectralOrder = 3; // Assume np is 4
25   }
26 }
27 
can_read_file(ReadNC * readNC,int fileId)28 bool NCHelperHOMME::can_read_file(ReadNC* readNC, int fileId)
29 {
30   // If global attribute "np" exists then it should be the HOMME grid
31   if (readNC->globalAtts.find("np") != readNC->globalAtts.end()) {
32     // Make sure it is CAM grid
33     std::map<std::string, ReadNC::AttData>::iterator attIt = readNC->globalAtts.find("source");
34     if (attIt == readNC->globalAtts.end())
35       return false;
36     unsigned int sz = attIt->second.attLen;
37     std::string att_data;
38     att_data.resize(sz + 1);
39     att_data[sz] = '\000';
40     int success = NCFUNC(get_att_text)(fileId, attIt->second.attVarId, attIt->second.attName.c_str(), &att_data[0]);
41     if (success)
42       return false;
43     if (att_data.find("CAM") == std::string::npos)
44       return false;
45 
46     return true;
47   }
48   else {
49     // If dimension names "ncol" AND "ncorners" AND "ncells" exist, then it should be the HOMME connectivity file
50     // In this case, the mesh can still be created
51     std::vector<std::string>& dimNames = readNC->dimNames;
52     if ((std::find(dimNames.begin(), dimNames.end(), std::string("ncol")) != dimNames.end()) &&
53         (std::find(dimNames.begin(), dimNames.end(), std::string("ncorners")) != dimNames.end()) &&
54         (std::find(dimNames.begin(), dimNames.end(), std::string("ncells")) != dimNames.end()))
55       return true;
56   }
57 
58   return false;
59 }
60 
init_mesh_vals()61 ErrorCode NCHelperHOMME::init_mesh_vals()
62 {
63   std::vector<std::string>& dimNames = _readNC->dimNames;
64   std::vector<int>& dimLens = _readNC->dimLens;
65   std::map<std::string, ReadNC::VarData>& varInfo = _readNC->varInfo;
66 
67   ErrorCode rval;
68   unsigned int idx;
69   std::vector<std::string>::iterator vit;
70 
71   // Look for time dimension
72   if (isConnFile) {
73     // Connectivity file might not have time dimension
74   }
75   else {
76     if ((vit = std::find(dimNames.begin(), dimNames.end(), "time")) != dimNames.end())
77       idx = vit - dimNames.begin();
78     else if ((vit = std::find(dimNames.begin(), dimNames.end(), "t")) != dimNames.end())
79       idx = vit - dimNames.begin();
80     else {
81       MB_SET_ERR(MB_FAILURE, "Couldn't find 'time' or 't' dimension");
82     }
83     tDim = idx;
84     nTimeSteps = dimLens[idx];
85   }
86 
87   // Get number of vertices (labeled as number of columns)
88   if ((vit = std::find(dimNames.begin(), dimNames.end(), "ncol")) != dimNames.end())
89     idx = vit - dimNames.begin();
90   else {
91     MB_SET_ERR(MB_FAILURE, "Couldn't find 'ncol' dimension");
92   }
93   vDim = idx;
94   nVertices = dimLens[idx];
95 
96   // Set number of cells
97   nCells = nVertices - 2;
98 
99   // Get number of levels
100   if (isConnFile) {
101     // Connectivity file might not have level dimension
102   }
103   else {
104     if ((vit = std::find(dimNames.begin(), dimNames.end(), "lev")) != dimNames.end())
105       idx = vit - dimNames.begin();
106     else if ((vit = std::find(dimNames.begin(), dimNames.end(), "ilev")) != dimNames.end())
107       idx = vit - dimNames.begin();
108     else {
109       MB_SET_ERR(MB_FAILURE, "Couldn't find 'lev' or 'ilev' dimension");
110     }
111     levDim = idx;
112     nLevels = dimLens[idx];
113   }
114 
115   // Store lon values in xVertVals
116   std::map<std::string, ReadNC::VarData>::iterator vmit;
117   if ((vmit = varInfo.find("lon")) != varInfo.end() && (*vmit).second.varDims.size() == 1) {
118     rval = read_coordinate("lon", 0, nVertices - 1, xVertVals);MB_CHK_SET_ERR(rval, "Trouble reading 'lon' variable");
119   }
120   else {
121     MB_SET_ERR(MB_FAILURE, "Couldn't find 'lon' variable");
122   }
123 
124   // Store lat values in yVertVals
125   if ((vmit = varInfo.find("lat")) != varInfo.end() && (*vmit).second.varDims.size() == 1) {
126     rval = read_coordinate("lat", 0, nVertices - 1, yVertVals);MB_CHK_SET_ERR(rval, "Trouble reading 'lat' variable");
127   }
128   else {
129     MB_SET_ERR(MB_FAILURE, "Couldn't find 'lat' variable");
130   }
131 
132   // Store lev values in levVals
133   if (isConnFile) {
134     // Connectivity file might not have level variable
135   }
136   else {
137     if ((vmit = varInfo.find("lev")) != varInfo.end() && (*vmit).second.varDims.size() == 1) {
138       rval = read_coordinate("lev", 0, nLevels - 1, levVals);MB_CHK_SET_ERR(rval, "Trouble reading 'lev' variable");
139 
140       // Decide whether down is positive
141       char posval[10] = {0};
142       int success = NCFUNC(get_att_text)(_fileId, (*vmit).second.varId, "positive", posval);
143       if (0 == success && !strcmp(posval, "down")) {
144         for (std::vector<double>::iterator dvit = levVals.begin(); dvit != levVals.end(); ++dvit)
145           (*dvit) *= -1.0;
146       }
147     }
148     else {
149       MB_SET_ERR(MB_FAILURE, "Couldn't find 'lev' variable");
150     }
151   }
152 
153   // Store time coordinate values in tVals
154   if (isConnFile) {
155     // Connectivity file might not have time variable
156   }
157   else {
158     if ((vmit = varInfo.find("time")) != varInfo.end() && (*vmit).second.varDims.size() == 1) {
159       rval = read_coordinate("time", 0, nTimeSteps - 1, tVals);MB_CHK_SET_ERR(rval, "Trouble reading 'time' variable");
160     }
161     else if ((vmit = varInfo.find("t")) != varInfo.end() && (*vmit).second.varDims.size() == 1) {
162       rval = read_coordinate("t", 0, nTimeSteps - 1, tVals);MB_CHK_SET_ERR(rval, "Trouble reading 't' variable");
163     }
164     else {
165       // If expected time variable does not exist, set dummy values to tVals
166       for (int t = 0; t < nTimeSteps; t++)
167         tVals.push_back((double)t);
168     }
169   }
170 
171   // For each variable, determine the entity location type and number of levels
172   std::map<std::string, ReadNC::VarData>::iterator mit;
173   for (mit = varInfo.begin(); mit != varInfo.end(); ++mit) {
174     ReadNC::VarData& vd = (*mit).second;
175 
176     // Default entLoc is ENTLOCSET
177     if (std::find(vd.varDims.begin(), vd.varDims.end(), tDim) != vd.varDims.end()) {
178       if (std::find(vd.varDims.begin(), vd.varDims.end(), vDim) != vd.varDims.end())
179         vd.entLoc = ReadNC::ENTLOCVERT;
180     }
181 
182     // Default numLev is 0
183     if (std::find(vd.varDims.begin(), vd.varDims.end(), levDim) != vd.varDims.end())
184       vd.numLev = nLevels;
185   }
186 
187   // Hack: create dummy variables for dimensions (like ncol) with no corresponding coordinate variables
188   rval = create_dummy_variables();MB_CHK_SET_ERR(rval, "Failed to create dummy variables");
189 
190   return MB_SUCCESS;
191 }
192 
193 // When noMesh option is used on this read, the old ReadNC class instance for last read can get out
194 // of scope (and deleted). The old instance initialized localGidVerts properly when the mesh was
195 // created, but it is now lost. The new instance (will not create the mesh with noMesh option) has
196 // to restore it based on the existing mesh from last read
check_existing_mesh()197 ErrorCode NCHelperHOMME::check_existing_mesh()
198 {
199   Interface*& mbImpl = _readNC->mbImpl;
200   Tag& mGlobalIdTag = _readNC->mGlobalIdTag;
201   bool& noMesh = _readNC->noMesh;
202 
203   if (noMesh && localGidVerts.empty()) {
204     // We need to populate localGidVerts range with the gids of vertices from current file set
205     // localGidVerts is important in reading the variable data into the nodes
206     // Also, for our purposes, localGidVerts is truly the GLOBAL_ID tag data, not other
207     // file_id tags that could get passed around in other scenarios for parallel reading
208 
209     // Get all vertices from current file set (it is the input set in no_mesh scenario)
210     Range local_verts;
211     ErrorCode rval = mbImpl->get_entities_by_dimension(_fileSet, 0, local_verts);MB_CHK_SET_ERR(rval, "Trouble getting local vertices in current file set");
212 
213     if (!local_verts.empty()) {
214       std::vector<int> gids(local_verts.size());
215 
216       // !IMPORTANT : this has to be the GLOBAL_ID tag
217       rval = mbImpl->tag_get_data(mGlobalIdTag, local_verts, &gids[0]);MB_CHK_SET_ERR(rval, "Trouble getting local gid values of vertices");
218 
219       // Restore localGidVerts
220       std::copy(gids.rbegin(), gids.rend(), range_inserter(localGidVerts));
221       nLocalVertices = localGidVerts.size();
222     }
223   }
224 
225   return MB_SUCCESS;
226 }
227 
create_mesh(Range & faces)228 ErrorCode NCHelperHOMME::create_mesh(Range& faces)
229 {
230   Interface*& mbImpl = _readNC->mbImpl;
231   std::string& fileName = _readNC->fileName;
232   Tag& mGlobalIdTag = _readNC->mGlobalIdTag;
233   const Tag*& mpFileIdTag = _readNC->mpFileIdTag;
234   DebugOutput& dbgOut = _readNC->dbgOut;
235   bool& spectralMesh = _readNC->spectralMesh;
236   int& gatherSetRank = _readNC->gatherSetRank;
237   int& trivialPartitionShift = _readNC->trivialPartitionShift;
238 
239   int rank = 0;
240   int procs = 1;
241 #ifdef MOAB_HAVE_MPI
242   bool& isParallel = _readNC->isParallel;
243   if (isParallel) {
244     ParallelComm*& myPcomm = _readNC->myPcomm;
245     rank = myPcomm->proc_config().proc_rank();
246     procs = myPcomm->proc_config().proc_size();
247   }
248 #endif
249 
250   ErrorCode rval;
251   int success = 0;
252 
253   // Need to get/read connectivity data before creating elements
254   std::string conn_fname;
255 
256   if (isConnFile) {
257     // Connectivity file has already been read
258     connectId = _readNC->fileId;
259   }
260   else {
261     // Try to open the connectivity file through CONN option, if used
262     rval = _opts.get_str_option("CONN", conn_fname);
263     if (MB_SUCCESS != rval) {
264       // Default convention for reading HOMME is a file HommeMapping.nc in same dir as data file
265       conn_fname = std::string(fileName);
266       size_t idx = conn_fname.find_last_of("/");
267       if (idx != std::string::npos)
268         conn_fname = conn_fname.substr(0, idx).append("/HommeMapping.nc");
269       else
270         conn_fname = "HommeMapping.nc";
271     }
272 #ifdef MOAB_HAVE_PNETCDF
273 #ifdef MOAB_HAVE_MPI
274     if (isParallel) {
275       ParallelComm*& myPcomm = _readNC->myPcomm;
276       success = NCFUNC(open)(myPcomm->proc_config().proc_comm(), conn_fname.c_str(), 0, MPI_INFO_NULL, &connectId);
277     }
278     else
279       success = NCFUNC(open)(MPI_COMM_SELF, conn_fname.c_str(), 0, MPI_INFO_NULL, &connectId);
280 #endif
281 #else
282     success = NCFUNC(open)(conn_fname.c_str(), 0, &connectId);
283 #endif
284     if (success)
285       MB_SET_ERR(MB_FAILURE, "Failed on open");
286   }
287 
288   std::vector<std::string> conn_names;
289   std::vector<int> conn_vals;
290   rval = _readNC->get_dimensions(connectId, conn_names, conn_vals);MB_CHK_SET_ERR(rval, "Failed to get dimensions for connectivity");
291 
292   // Read connectivity into temporary variable
293   int num_fine_quads = 0;
294   int num_coarse_quads = 0;
295   int start_idx = 0;
296   std::vector<std::string>::iterator vit;
297   int idx = 0;
298   if ((vit = std::find(conn_names.begin(), conn_names.end(), "ncells")) != conn_names.end())
299     idx = vit - conn_names.begin();
300   else if ((vit = std::find(conn_names.begin(), conn_names.end(), "ncenters")) != conn_names.end())
301     idx = vit - conn_names.begin();
302   else {
303     MB_SET_ERR(MB_FAILURE, "Failed to get number of quads");
304   }
305   int num_quads = conn_vals[idx];
306   if (!isConnFile && num_quads != nCells) {
307     dbgOut.tprintf(1, "Warning: number of quads from %s and cells from %s are inconsistent; num_quads = %d, nCells = %d.\n",
308         conn_fname.c_str(), fileName.c_str(), num_quads, nCells);
309   }
310 
311   // Get the connectivity into tmp_conn2 and permute into tmp_conn
312   int cornerVarId;
313   success = NCFUNC(inq_varid)(connectId, "element_corners", &cornerVarId);
314   if (success)
315     MB_SET_ERR(MB_FAILURE, "Failed to get variable id of 'element_corners'");
316   NCDF_SIZE tmp_starts[2] = {0, 0};
317   NCDF_SIZE tmp_counts[2] = {4, static_cast<NCDF_SIZE>(num_quads)};
318   std::vector<int> tmp_conn(4 * num_quads), tmp_conn2(4 * num_quads);
319   success = NCFUNCAG(_vara_int)(connectId, cornerVarId, tmp_starts, tmp_counts, &tmp_conn2[0]);
320   if (success)
321     MB_SET_ERR(MB_FAILURE, "Failed to get temporary connectivity");
322   if (isConnFile) {
323     // This data/connectivity file will be closed later in ReadNC::load_file()
324   }
325   else {
326     success = NCFUNC(close)(connectId);
327     if (success)
328       MB_SET_ERR(MB_FAILURE, "Failed on close");
329   }
330   // Permute the connectivity
331   for (int i = 0; i < num_quads; i++) {
332     tmp_conn[4 * i] = tmp_conn2[i];
333     tmp_conn[4 * i + 1] = tmp_conn2[i + 1 * num_quads];
334     tmp_conn[4 * i + 2] = tmp_conn2[i + 2 * num_quads];
335     tmp_conn[4 * i + 3] = tmp_conn2[i + 3 * num_quads];
336   }
337 
338   // Need to know whether we'll be creating gather mesh later, to make sure
339   // we allocate enough space in one shot
340   bool create_gathers = false;
341   if (rank == gatherSetRank)
342     create_gathers = true;
343 
344   // Shift rank to obtain a rotated trivial partition
345   int shifted_rank = rank;
346   if (procs >= 2 && trivialPartitionShift > 0)
347     shifted_rank = (rank + trivialPartitionShift) % procs;
348 
349   // Compute the number of local quads, accounting for coarse or fine representation
350   // spectral_unit is the # fine quads per coarse quad, or spectralOrder^2
351   int spectral_unit = (spectralMesh ? _spectralOrder * _spectralOrder : 1);
352   // num_coarse_quads is the number of quads instantiated in MOAB; if !spectralMesh, num_coarse_quads = num_fine_quads
353   num_coarse_quads = int(std::floor(1.0 * num_quads / (spectral_unit * procs)));
354   // start_idx is the starting index in the HommeMapping connectivity list for this proc, before converting to coarse quad representation
355   start_idx = 4 * shifted_rank * num_coarse_quads * spectral_unit;
356   // iextra = # coarse quads extra after equal split over procs
357   int iextra = num_quads % (procs * spectral_unit);
358   if (shifted_rank < iextra)
359     num_coarse_quads++;
360   start_idx += 4 * spectral_unit * std::min(shifted_rank, iextra);
361   // num_fine_quads is the number of quads in the connectivity list in HommeMapping file assigned to this proc
362   num_fine_quads = spectral_unit * num_coarse_quads;
363 
364   // Now create num_coarse_quads
365   EntityHandle* conn_arr;
366   EntityHandle start_vertex;
367   Range tmp_range;
368 
369   // Read connectivity into that space
370   EntityHandle* sv_ptr = NULL;
371   EntityHandle start_quad;
372   SpectralMeshTool smt(mbImpl, _spectralOrder);
373   if (!spectralMesh) {
374     rval = _readNC->readMeshIface->get_element_connect(num_coarse_quads, 4,
375                                                       MBQUAD, 0, start_quad, conn_arr,
376                                                       // Might have to create gather mesh later
377                                                       (create_gathers ? num_coarse_quads + num_quads : num_coarse_quads));MB_CHK_SET_ERR(rval, "Failed to create local quads");
378     tmp_range.insert(start_quad, start_quad + num_coarse_quads - 1);
379     int* tmp_conn_end = (&tmp_conn[start_idx + 4 * num_fine_quads-1])+1;
380     std::copy(&tmp_conn[start_idx], tmp_conn_end, conn_arr);
381     std::copy(conn_arr, conn_arr + 4 * num_fine_quads, range_inserter(localGidVerts));
382   }
383   else {
384     rval = smt.create_spectral_elems(&tmp_conn[0], num_fine_quads, 2, tmp_range, start_idx, &localGidVerts);MB_CHK_SET_ERR(rval, "Failed to create spectral elements");
385     int count, v_per_e;
386     rval = mbImpl->connect_iterate(tmp_range.begin(), tmp_range.end(), conn_arr, v_per_e, count);MB_CHK_SET_ERR(rval, "Failed to get connectivity of spectral elements");
387     rval = mbImpl->tag_iterate(smt.spectral_vertices_tag(true), tmp_range.begin(), tmp_range.end(),
388                                count, (void*&)sv_ptr);MB_CHK_SET_ERR(rval, "Failed to get fine connectivity of spectral elements");
389   }
390 
391   // Create vertices
392   nLocalVertices = localGidVerts.size();
393   std::vector<double*> arrays;
394   rval = _readNC->readMeshIface->get_node_coords(3, nLocalVertices, 0, start_vertex, arrays,
395                                                 // Might have to create gather mesh later
396                                                 (create_gathers ? nLocalVertices + nVertices : nLocalVertices));MB_CHK_SET_ERR(rval, "Failed to create local vertices");
397 
398   // Set vertex coordinates
399   Range::iterator rit;
400   double* xptr = arrays[0];
401   double* yptr = arrays[1];
402   double* zptr = arrays[2];
403   int i;
404   for (i = 0, rit = localGidVerts.begin(); i < nLocalVertices; i++, ++rit) {
405     assert(*rit < xVertVals.size() + 1);
406     xptr[i] = xVertVals[(*rit) - 1]; // lon
407     yptr[i] = yVertVals[(*rit) - 1]; // lat
408   }
409 
410   // Convert lon/lat/rad to x/y/z
411   const double pideg = acos(-1.0) / 180.0;
412   double rad = (isConnFile) ? 8000.0 : 8000.0 + levVals[0];
413   for (i = 0; i < nLocalVertices; i++) {
414     double cosphi = cos(pideg * yptr[i]);
415     double zmult = sin(pideg * yptr[i]);
416     double xmult = cosphi * cos(xptr[i] * pideg);
417     double ymult = cosphi * sin(xptr[i] * pideg);
418     xptr[i] = rad * xmult;
419     yptr[i] = rad * ymult;
420     zptr[i] = rad * zmult;
421   }
422 
423   // Get ptr to gid memory for vertices
424   Range vert_range(start_vertex, start_vertex + nLocalVertices - 1);
425   void* data;
426   int count;
427   rval = mbImpl->tag_iterate(mGlobalIdTag, vert_range.begin(), vert_range.end(),
428                              count, data);MB_CHK_SET_ERR(rval, "Failed to iterate global id tag on local vertices");
429   assert(count == nLocalVertices);
430   int* gid_data = (int*) data;
431   std::copy(localGidVerts.begin(), localGidVerts.end(), gid_data);
432 
433   // Duplicate global id data, which will be used to resolve sharing
434   if (mpFileIdTag) {
435     rval = mbImpl->tag_iterate(*mpFileIdTag, vert_range.begin(), vert_range.end(),
436                                count, data);MB_CHK_SET_ERR(rval, "Failed to iterate file id tag on local vertices");
437     assert(count == nLocalVertices);
438     int bytes_per_tag = 4;
439     rval = mbImpl->tag_get_bytes(*mpFileIdTag, bytes_per_tag);MB_CHK_SET_ERR(rval, "Can't get number of bytes for file id tag");
440     if (4 == bytes_per_tag) {
441       gid_data = (int*) data;
442       std::copy(localGidVerts.begin(), localGidVerts.end(), gid_data);
443     }
444     else if (8 == bytes_per_tag) { // Should be a handle tag on 64 bit machine?
445       long* handle_tag_data = (long*)data;
446       std::copy(localGidVerts.begin(), localGidVerts.end(), handle_tag_data);
447     }
448   }
449 
450   // Create map from file ids to vertex handles, used later to set connectivity
451   std::map<EntityHandle, EntityHandle> vert_handles;
452   for (rit = localGidVerts.begin(), i = 0; rit != localGidVerts.end(); ++rit, i++)
453     vert_handles[*rit] = start_vertex + i;
454 
455   // Compute proper handles in connectivity using offset
456   for (int q = 0; q < 4 * num_coarse_quads; q++) {
457     conn_arr[q] = vert_handles[conn_arr[q]];
458     assert(conn_arr[q]);
459   }
460   if (spectralMesh) {
461     int verts_per_quad = (_spectralOrder + 1) * (_spectralOrder + 1);
462     for (int q = 0; q < verts_per_quad * num_coarse_quads; q++) {
463       sv_ptr[q] = vert_handles[sv_ptr[q]];
464       assert(sv_ptr[q]);
465     }
466   }
467 
468   // Add new vertices and quads to current file set
469   faces.merge(tmp_range);
470   tmp_range.insert(start_vertex, start_vertex + nLocalVertices - 1);
471   rval = mbImpl->add_entities(_fileSet, tmp_range);MB_CHK_SET_ERR(rval, "Failed to add new vertices and quads to current file set");
472 
473   // Mark the set with the spectral order
474   Tag sporder;
475   rval = mbImpl->tag_get_handle("SPECTRAL_ORDER", 1, MB_TYPE_INTEGER, sporder,
476                                 MB_TAG_SPARSE | MB_TAG_CREAT);MB_CHK_SET_ERR(rval, "Trouble creating SPECTRAL_ORDER tag");
477   rval = mbImpl->tag_set_data(sporder, &_fileSet, 1, &_spectralOrder);MB_CHK_SET_ERR(rval, "Trouble setting data to SPECTRAL_ORDER tag");
478 
479   if (create_gathers) {
480     EntityHandle gather_set;
481     rval = _readNC->readMeshIface->create_gather_set(gather_set);MB_CHK_SET_ERR(rval, "Failed to create gather set");
482 
483     // Create vertices
484     arrays.clear();
485     // Don't need to specify allocation number here, because we know enough verts were created before
486     rval = _readNC->readMeshIface->get_node_coords(3, nVertices, 0, start_vertex, arrays);MB_CHK_SET_ERR(rval, "Failed to create gather set vertices");
487 
488     xptr = arrays[0];
489     yptr = arrays[1];
490     zptr = arrays[2];
491     for (i = 0; i < nVertices; i++) {
492       double cosphi = cos(pideg * yVertVals[i]);
493       double zmult = sin(pideg * yVertVals[i]);
494       double xmult = cosphi * cos(xVertVals[i] * pideg);
495       double ymult = cosphi * sin(xVertVals[i] * pideg);
496       xptr[i] = rad * xmult;
497       yptr[i] = rad * ymult;
498       zptr[i] = rad * zmult;
499     }
500 
501     // Get ptr to gid memory for vertices
502     Range gather_set_verts_range(start_vertex, start_vertex + nVertices - 1);
503     rval = mbImpl->tag_iterate(mGlobalIdTag, gather_set_verts_range.begin(), gather_set_verts_range.end(),
504                                count, data);MB_CHK_SET_ERR(rval, "Failed to iterate global id tag on gather set vertices");
505     assert(count == nVertices);
506     gid_data = (int*) data;
507     for (int j = 1; j <= nVertices; j++)
508       gid_data[j - 1] = j;
509     // Set the file id tag too, it should be bigger something not interfering with global id
510     if (mpFileIdTag) {
511       rval = mbImpl->tag_iterate(*mpFileIdTag, gather_set_verts_range.begin(), gather_set_verts_range.end(),
512                                  count, data);MB_CHK_SET_ERR(rval, "Failed to iterate file id tag on gather set vertices");
513       assert(count == nVertices);
514       int bytes_per_tag = 4;
515       rval = mbImpl->tag_get_bytes(*mpFileIdTag, bytes_per_tag);MB_CHK_SET_ERR(rval, "Can't get number of bytes for file id tag");
516       if (4 == bytes_per_tag) {
517         gid_data = (int*)data;
518         for (int j = 1; j <= nVertices; j++)
519           gid_data[j - 1] = nVertices + j; // Bigger than global id tag
520       }
521       else if (8 == bytes_per_tag) { // Should be a handle tag on 64 bit machine?
522         long* handle_tag_data = (long*)data;
523         for (int j = 1; j <= nVertices; j++)
524           handle_tag_data[j - 1] = nVertices + j; // Bigger than global id tag
525       }
526     }
527 
528     rval = mbImpl->add_entities(gather_set, gather_set_verts_range);MB_CHK_SET_ERR(rval, "Failed to add vertices to the gather set");
529 
530     // Create quads
531     Range gather_set_quads_range;
532     // Don't need to specify allocation number here, because we know enough quads were created before
533     rval = _readNC->readMeshIface->get_element_connect(num_quads, 4, MBQUAD, 0,
534                                                        start_quad, conn_arr);MB_CHK_SET_ERR(rval, "Failed to create gather set quads");
535     gather_set_quads_range.insert(start_quad, start_quad + num_quads - 1);
536     int* tmp_conn_end = (&tmp_conn[4 * num_quads-1]) + 1;
537     std::copy(&tmp_conn[0], tmp_conn_end, conn_arr);
538     for (i = 0; i != 4 * num_quads; i++)
539       conn_arr[i] += start_vertex - 1; // Connectivity array is shifted by where the gather verts start
540     rval = mbImpl->add_entities(gather_set, gather_set_quads_range);MB_CHK_SET_ERR(rval, "Failed to add quads to the gather set");
541   }
542 
543   return MB_SUCCESS;
544 }
545 
read_ucd_variables_to_nonset_allocate(std::vector<ReadNC::VarData> & vdatas,std::vector<int> & tstep_nums)546 ErrorCode NCHelperHOMME::read_ucd_variables_to_nonset_allocate(std::vector<ReadNC::VarData>& vdatas, std::vector<int>& tstep_nums)
547 {
548   Interface*& mbImpl = _readNC->mbImpl;
549   std::vector<int>& dimLens = _readNC->dimLens;
550   DebugOutput& dbgOut = _readNC->dbgOut;
551 
552   Range* range = NULL;
553 
554   // Get vertices
555   Range verts;
556   ErrorCode rval = mbImpl->get_entities_by_dimension(_fileSet, 0, verts);MB_CHK_SET_ERR(rval, "Trouble getting vertices in current file set");
557   assert("Should only have a single vertex subrange, since they were read in one shot" && verts.psize() == 1);
558 
559   for (unsigned int i = 0; i < vdatas.size(); i++) {
560     // Support non-set variables with 3 dimensions like (time, lev, ncol)
561     assert(3 == vdatas[i].varDims.size());
562 
563     // For a non-set variable, time should be the first dimension
564     assert(tDim == vdatas[i].varDims[0]);
565 
566     // Set up readStarts and readCounts
567     vdatas[i].readStarts.resize(3);
568     vdatas[i].readCounts.resize(3);
569 
570     // First: time
571     vdatas[i].readStarts[0] = 0; // This value is timestep dependent, will be set later
572     vdatas[i].readCounts[0] = 1;
573 
574     // Next: lev
575     vdatas[i].readStarts[1] = 0;
576     vdatas[i].readCounts[1] = vdatas[i].numLev;
577 
578     // Finally: ncol
579     switch (vdatas[i].entLoc) {
580       case ReadNC::ENTLOCVERT:
581         // Vertices
582         // Start from the first localGidVerts
583         // Actually, this will be reset later on in a loop
584         vdatas[i].readStarts[2] = localGidVerts[0] - 1;
585         vdatas[i].readCounts[2] = nLocalVertices;
586         range = &verts;
587         break;
588       default:
589         MB_SET_ERR(MB_FAILURE, "Unexpected entity location type for variable " << vdatas[i].varName);
590     }
591 
592     // Get variable size
593     vdatas[i].sz = 1;
594     for (std::size_t idx = 0; idx != 3; idx++)
595       vdatas[i].sz *= vdatas[i].readCounts[idx];
596 
597     for (unsigned int t = 0; t < tstep_nums.size(); t++) {
598       dbgOut.tprintf(2, "Reading variable %s, time step %d\n", vdatas[i].varName.c_str(), tstep_nums[t]);
599 
600       if (tstep_nums[t] >= dimLens[tDim]) {
601         MB_SET_ERR(MB_INDEX_OUT_OF_RANGE, "Wrong value for timestep number " << tstep_nums[t]);
602       }
603 
604       // Get the tag to read into
605       if (!vdatas[i].varTags[t]) {
606         rval = get_tag_to_nonset(vdatas[i], tstep_nums[t], vdatas[i].varTags[t], vdatas[i].numLev);MB_CHK_SET_ERR(rval, "Trouble getting tag for variable " << vdatas[i].varName);
607       }
608 
609       // Get ptr to tag space
610       void* data;
611       int count;
612       rval = mbImpl->tag_iterate(vdatas[i].varTags[t], range->begin(), range->end(), count, data);MB_CHK_SET_ERR(rval, "Failed to iterate tag for variable " << vdatas[i].varName);
613       assert((unsigned)count == range->size());
614       vdatas[i].varDatas[t] = data;
615     }
616   }
617 
618   return rval;
619 }
620 
621 #ifdef MOAB_HAVE_PNETCDF
read_ucd_variables_to_nonset_async(std::vector<ReadNC::VarData> & vdatas,std::vector<int> & tstep_nums)622 ErrorCode NCHelperHOMME::read_ucd_variables_to_nonset_async(std::vector<ReadNC::VarData>& vdatas, std::vector<int>& tstep_nums)
623 {
624   DebugOutput& dbgOut = _readNC->dbgOut;
625 
626   ErrorCode rval = read_ucd_variables_to_nonset_allocate(vdatas, tstep_nums);MB_CHK_SET_ERR(rval, "Trouble allocating space to read non-set variables");
627 
628   // Finally, read into that space
629   int success;
630 
631   for (unsigned int i = 0; i < vdatas.size(); i++) {
632     std::size_t sz = vdatas[i].sz;
633 
634     // A typical supported variable: float T(time, lev, ncol)
635     // For tag values, need transpose (lev, ncol) to (ncol, lev)
636     size_t ni = vdatas[i].readCounts[2]; // ncol
637     size_t nj = 1; // Here we should just set nj to 1
638     size_t nk = vdatas[i].readCounts[1]; // lev
639 
640     for (unsigned int t = 0; t < tstep_nums.size(); t++) {
641       // We will synchronize all these reads with the other processors,
642       // so the wait will be inside this double loop; is it too much?
643       size_t nb_reads = localGidVerts.psize();
644       std::vector<int> requests(nb_reads), statuss(nb_reads);
645       size_t idxReq = 0;
646 
647       // Tag data for this timestep
648       void* data = vdatas[i].varDatas[t];
649 
650       // Set readStart for each timestep along time dimension
651       vdatas[i].readStarts[0] = tstep_nums[t];
652 
653       switch (vdatas[i].varDataType) {
654         case NC_FLOAT:
655         case NC_DOUBLE: {
656           // Read float as double
657           std::vector<double> tmpdoubledata(sz);
658 
659           // In the case of ucd mesh, and on multiple proc,
660           // we need to read as many times as subranges we have in the
661           // localGidVerts range;
662           // basically, we have to give a different point
663           // for data to start, for every subrange :(
664           size_t indexInDoubleArray = 0;
665           size_t ic = 0;
666           for (Range::pair_iterator pair_iter = localGidVerts.pair_begin();
667               pair_iter != localGidVerts.pair_end();
668               ++pair_iter, ic++) {
669             EntityHandle starth = pair_iter->first;
670             EntityHandle endh = pair_iter->second; // Inclusive
671             vdatas[i].readStarts[2] = (NCDF_SIZE) (starth - 1);
672             vdatas[i].readCounts[2] = (NCDF_SIZE) (endh - starth + 1);
673 
674             // Do a partial read, in each subrange
675             // Wait outside this loop
676             success = NCFUNCREQG(_vara_double)(_fileId, vdatas[i].varId,
677                             &(vdatas[i].readStarts[0]), &(vdatas[i].readCounts[0]),
678                             &(tmpdoubledata[indexInDoubleArray]), &requests[idxReq++]);
679             if (success)
680               MB_SET_ERR(MB_FAILURE, "Failed to read double data in a loop for variable " << vdatas[i].varName);
681             // We need to increment the index in double array for the
682             // next subrange
683             indexInDoubleArray += (endh - starth + 1) * 1 * vdatas[i].numLev;
684           }
685           assert(ic == localGidVerts.psize());
686 
687           success = ncmpi_wait_all(_fileId, requests.size(), &requests[0], &statuss[0]);
688           if (success)
689             MB_SET_ERR(MB_FAILURE, "Failed on wait_all");
690 
691           if (vdatas[i].numLev > 1)
692             // Transpose (lev, ncol) to (ncol, lev)
693             kji_to_jik_stride(ni, nj, nk, data, &tmpdoubledata[0], localGidVerts);
694           else {
695             for (std::size_t idx = 0; idx != tmpdoubledata.size(); idx++)
696               ((double*) data)[idx] = tmpdoubledata[idx];
697           }
698 
699           break;
700         }
701         default:
702           MB_SET_ERR(MB_FAILURE, "Unexpected data type for variable " << vdatas[i].varName);
703       }
704     }
705   }
706 
707   // Debug output, if requested
708   if (1 == dbgOut.get_verbosity()) {
709     dbgOut.printf(1, "Read variables: %s", vdatas.begin()->varName.c_str());
710     for (unsigned int i = 1; i < vdatas.size(); i++)
711       dbgOut.printf(1, ", %s ", vdatas[i].varName.c_str());
712     dbgOut.tprintf(1, "\n");
713   }
714 
715   return rval;
716 }
717 #else
read_ucd_variables_to_nonset(std::vector<ReadNC::VarData> & vdatas,std::vector<int> & tstep_nums)718 ErrorCode NCHelperHOMME::read_ucd_variables_to_nonset(std::vector<ReadNC::VarData>& vdatas, std::vector<int>& tstep_nums)
719 {
720   DebugOutput& dbgOut = _readNC->dbgOut;
721 
722   ErrorCode rval = read_ucd_variables_to_nonset_allocate(vdatas, tstep_nums);MB_CHK_SET_ERR(rval, "Trouble allocating space to read non-set variables");
723 
724   // Finally, read into that space
725   int success;
726   for (unsigned int i = 0; i < vdatas.size(); i++) {
727     std::size_t sz = vdatas[i].sz;
728 
729     // A typical supported variable: float T(time, lev, ncol)
730     // For tag values, need transpose (lev, ncol) to (ncol, lev)
731     size_t ni = vdatas[i].readCounts[2]; // ncol
732     size_t nj = 1; // Here we should just set nj to 1
733     size_t nk = vdatas[i].readCounts[1]; // lev
734 
735     for (unsigned int t = 0; t < tstep_nums.size(); t++) {
736       // Tag data for this timestep
737       void* data = vdatas[i].varDatas[t];
738 
739       // Set readStart for each timestep along time dimension
740       vdatas[i].readStarts[0] = tstep_nums[t];
741 
742       switch (vdatas[i].varDataType) {
743         case NC_FLOAT:
744         case NC_DOUBLE: {
745           // Read float as double
746           std::vector<double> tmpdoubledata(sz);
747 
748           // In the case of ucd mesh, and on multiple proc,
749           // we need to read as many times as subranges we have in the
750           // localGidVerts range;
751           // basically, we have to give a different point
752           // for data to start, for every subrange :(
753           size_t indexInDoubleArray = 0;
754           size_t ic = 0;
755           for (Range::pair_iterator pair_iter = localGidVerts.pair_begin();
756               pair_iter != localGidVerts.pair_end();
757               ++pair_iter, ic++) {
758             EntityHandle starth = pair_iter->first;
759             EntityHandle endh = pair_iter->second; // Inclusive
760             vdatas[i].readStarts[2] = (NCDF_SIZE) (starth - 1);
761             vdatas[i].readCounts[2] = (NCDF_SIZE) (endh - starth + 1);
762 
763             success = NCFUNCAG(_vara_double)(_fileId, vdatas[i].varId,
764                             &(vdatas[i].readStarts[0]), &(vdatas[i].readCounts[0]),
765                             &(tmpdoubledata[indexInDoubleArray]));
766             if (success)
767               MB_SET_ERR(MB_FAILURE, "Failed to read double data in a loop for variable " << vdatas[i].varName);
768             // We need to increment the index in double array for the
769             // next subrange
770             indexInDoubleArray += (endh - starth + 1) * 1 * vdatas[i].numLev;
771           }
772           assert(ic == localGidVerts.psize());
773 
774           if (vdatas[i].numLev > 1)
775             // Transpose (lev, ncol) to (ncol, lev)
776             kji_to_jik_stride(ni, nj, nk, data, &tmpdoubledata[0], localGidVerts);
777           else {
778             for (std::size_t idx = 0; idx != tmpdoubledata.size(); idx++)
779               ((double*) data)[idx] = tmpdoubledata[idx];
780           }
781 
782           break;
783         }
784         default:
785           MB_SET_ERR(MB_FAILURE, "Unexpected data type for variable " << vdatas[i].varName);
786       }
787     }
788   }
789 
790   // Debug output, if requested
791   if (1 == dbgOut.get_verbosity()) {
792     dbgOut.printf(1, "Read variables: %s", vdatas.begin()->varName.c_str());
793     for (unsigned int i = 1; i < vdatas.size(); i++)
794       dbgOut.printf(1, ", %s ", vdatas[i].varName.c_str());
795     dbgOut.tprintf(1, "\n");
796   }
797 
798   return rval;
799 }
800 #endif
801 
802 } // namespace moab
803