1 /*=========================================================================
2 
3   Program:   ParaView
4   Module:    H5RageAdaptor.cxx
5 
6   Copyright (c) Kitware, Inc.
7   All rights reserved.
8   See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 
16 #include "H5RageAdaptor.h"
17 
18 #include "vtkCellArray.h"
19 #include "vtkDataArraySelection.h"
20 #include "vtkDirectory.h"
21 #include "vtkDoubleArray.h"
22 #include "vtkErrorCode.h"
23 #include "vtkFloatArray.h"
24 #include "vtkIdList.h"
25 #include "vtkImageData.h"
26 #include "vtkInformation.h"
27 #include "vtkMultiPieceDataSet.h"
28 #include "vtkMultiProcessController.h"
29 #include "vtkNew.h"
30 #include "vtkPointData.h"
31 #include "vtkPoints.h"
32 #include "vtkStdString.h"
33 #include "vtkStringArray.h"
34 
35 #include <float.h>
36 #include <fstream>
37 #include <iostream>
38 #include <map>
39 #include <set>
40 #include <sstream>
41 #include <vtk_hdf5.h>
42 
43 #if defined(_WIN32) && !defined(__CYGWIN__)
44 const static char* Slash = "\\";
45 #else
46 const static char* Slash = "/";
47 #endif
48 
49 namespace
50 {
51 // mpi tag
52 const int mpiTag = 1758978;
53 
BroadcastString(vtkMultiProcessController * controller,std::string & str,int rank)54 void BroadcastString(vtkMultiProcessController* controller, std::string& str, int rank)
55 {
56   unsigned long len = static_cast<unsigned long>(str.size()) + 1;
57   controller->Broadcast(&len, 1, 0);
58   if (len)
59   {
60     if (rank)
61     {
62       std::vector<char> tmp;
63       tmp.resize(len);
64       controller->Broadcast(&(tmp[0]), len, 0);
65       str = &tmp[0];
66     }
67     else
68     {
69       const char* start = str.c_str();
70       std::vector<char> tmp(start, start + len);
71       controller->Broadcast(&tmp[0], len, 0);
72     }
73   }
74 }
75 
BroadcastStringVector(vtkMultiProcessController * controller,std::vector<std::string> & svec,int rank)76 void BroadcastStringVector(
77   vtkMultiProcessController* controller, std::vector<std::string>& svec, int rank)
78 {
79   unsigned long len = static_cast<unsigned long>(svec.size());
80   controller->Broadcast(&len, 1, 0);
81   if (rank)
82   {
83     svec.resize(len);
84   }
85   std::vector<std::string>::iterator it;
86   for (it = svec.begin(); it != svec.end(); ++it)
87   {
88     BroadcastString(controller, *it, rank);
89   }
90 }
91 };
92 
93 ///////////////////////////////////////////////////////////////////////////////
94 //
95 // Constructor and destructor
96 //
97 ///////////////////////////////////////////////////////////////////////////////
98 
H5RageAdaptor(vtkMultiProcessController * ctrl)99 H5RageAdaptor::H5RageAdaptor(vtkMultiProcessController* ctrl)
100 {
101   this->Controller = ctrl;
102   if (this->Controller)
103   {
104     this->Rank = this->Controller->GetLocalProcessId();
105     this->TotalRank = this->Controller->GetNumberOfProcesses();
106   }
107   else
108   {
109     this->Rank = 0;
110     this->TotalRank = 1;
111   }
112 
113   this->NumberOfVariables = 0;
114   this->NumberOfTimeSteps = 0;
115   this->NumberOfDimensions = 3;
116   this->TotalTuples = 0;
117   this->UseFloat64 = false;
118 
119   for (int dim = 0; dim < this->NumberOfDimensions; dim++)
120   {
121     this->Dimension[dim] = 1;
122     this->Origin[dim] = 0.0;
123     this->Spacing[dim] = 1.0;
124     this->WholeExtent[dim * 2] = 0;
125     this->WholeExtent[dim * 2 + 1] = 0;
126   }
127 
128   // Schedule for sending partitioned variables to processors
129   // Only rank 0 reads the data, ghost overlap is 1
130   this->NumberOfTuples = new int[this->TotalRank];
131   this->ExtentSchedule = new int*[this->TotalRank];
132   for (int rank = 0; rank < this->TotalRank; rank++)
133   {
134     this->ExtentSchedule[rank] = new int[6];
135   }
136 }
137 
~H5RageAdaptor()138 H5RageAdaptor::~H5RageAdaptor()
139 {
140   for (int rank = 0; rank < this->TotalRank; rank++)
141   {
142     delete[] this->ExtentSchedule[rank];
143   }
144   delete[] this->ExtentSchedule;
145   delete[] this->NumberOfTuples;
146 
147   this->Controller = nullptr;
148 }
149 
150 ///////////////////////////////////////////////////////////////////////////////
151 //
152 // Remove whitespace from the beginning and end of a string
153 //
154 ///////////////////////////////////////////////////////////////////////////////
155 
TrimString(const std::string & str)156 std::string H5RageAdaptor::TrimString(const std::string& str)
157 {
158   std::string whitespace = " \n\r\t\f\v";
159   size_t start = str.find_first_not_of(whitespace);
160   size_t end = str.find_last_not_of(whitespace);
161   if (start == std::string::npos || end == std::string::npos)
162   {
163     // the whole line is whitespace
164     return std::string("");
165   }
166   else
167   {
168     return str.substr(start, end - start + 1);
169   }
170 }
171 
172 ///////////////////////////////////////////////////////////////////////////////
173 //
174 // Read descriptor file, collect meta data on proc 0, share with other procs
175 //
176 ///////////////////////////////////////////////////////////////////////////////
177 
InitializeGlobal(const char * H5RageFileName)178 int H5RageAdaptor::InitializeGlobal(const char* H5RageFileName)
179 {
180   if (this->Rank == 0)
181   {
182     if (!CollectMetaData(H5RageFileName))
183     {
184       return 0;
185     }
186   }
187 
188   // Share with all processors sizes, timesteps, variables
189   this->Controller->Broadcast(this->Dimension, this->NumberOfDimensions, 0);
190   this->Controller->Broadcast(this->Origin, this->NumberOfDimensions, 0);
191   this->Controller->Broadcast(this->Spacing, this->NumberOfDimensions, 0);
192   this->Controller->Broadcast(&this->NumberOfVariables, 1, 0);
193   this->Controller->Broadcast(&this->NumberOfTimeSteps, 1, 0);
194   if (this->Rank > 0)
195   {
196     this->TimeSteps = new double[this->NumberOfTimeSteps];
197   }
198   this->Controller->Broadcast(this->TimeSteps, this->NumberOfTimeSteps, 0);
199   BroadcastStringVector(this->Controller, this->VariableName, this->Rank);
200 
201   // Calculate WholeExtent
202   for (int dim = 0; dim < this->NumberOfDimensions; dim++)
203   {
204     this->WholeExtent[dim * 2] = 0;
205     this->WholeExtent[dim * 2 + 1] = this->Dimension[dim] - 1;
206   }
207 
208   // Calculate SubExtent for each processor for schedule to send variables
209   if (this->Rank == 0)
210   {
211     // Initialize schedule to whole extent
212     for (int rank = 0; rank < this->TotalRank; rank++)
213     {
214       for (int ext = 0; ext < 6; ext++)
215       {
216         this->ExtentSchedule[rank][ext] = this->WholeExtent[ext];
217       }
218     }
219 
220     // Calculate which dimension to partition on in slabs
221     int maxDim = 0;
222     int useDim = 0;
223     for (int dim = 0; dim < this->NumberOfDimensions; dim++)
224     {
225       if (this->Dimension[dim] > maxDim)
226       {
227         maxDim = this->Dimension[dim];
228         useDim = dim;
229       }
230     }
231     int perSlab = maxDim / this->TotalRank;
232     int indx0 = useDim * 2;
233     int indx1 = indx0 + 1;
234     for (int rank = 0; rank < this->TotalRank; rank++)
235     {
236       this->ExtentSchedule[rank][indx0] = rank * perSlab;
237       this->ExtentSchedule[rank][indx1] = this->ExtentSchedule[rank][indx0] + perSlab - 1;
238     }
239     this->ExtentSchedule[(this->TotalRank - 1)][indx1] = this->WholeExtent[indx1];
240 
241     // Enlarge the extents to allow for ghost levels
242     for (int rank = 0; rank < this->TotalRank; rank++)
243     {
244       if (this->ExtentSchedule[rank][indx1] != this->WholeExtent[indx1])
245       {
246         this->ExtentSchedule[rank][indx1] += 1;
247       }
248     }
249 
250     // Share the extent schedule and number of tuples belonging to each processor
251     for (int rank = 0; rank < this->TotalRank; rank++)
252     {
253       this->NumberOfTuples[rank] = 1;
254       for (int dim = 0; dim < this->NumberOfDimensions; dim++)
255       {
256         int subDim =
257           this->ExtentSchedule[rank][dim * 2 + 1] - this->ExtentSchedule[rank][dim * 2] + 1;
258         if (subDim > 0)
259         {
260           this->NumberOfTuples[rank] *= subDim;
261         }
262       }
263     }
264 
265     // Send to each processor
266     for (int rank = 1; rank < this->TotalRank; rank++)
267     {
268       this->Controller->Send(this->NumberOfTuples, this->TotalRank, rank, mpiTag);
269       this->Controller->Send(this->ExtentSchedule[rank], 6, rank, mpiTag);
270     }
271 
272     // Set for rank 0
273     for (int ext = 0; ext < 6; ext++)
274     {
275       this->SubExtent[ext] = this->ExtentSchedule[0][ext];
276     }
277   }
278   else
279   {
280     // Receive on each processor
281     this->Controller->Receive(this->NumberOfTuples, this->TotalRank, 0, mpiTag);
282     this->Controller->Receive(this->SubExtent, 6, 0, mpiTag);
283   }
284   return 1;
285 }
286 
287 ///////////////////////////////////////////////////////////////////////////////
288 //
289 // Read the global descriptor file (name.h5rage) collecting hdf directory info,
290 // filenames, cycle numbers and variable names
291 // Read hdf file to collect sizes, origin and spacing
292 //
293 ///////////////////////////////////////////////////////////////////////////////
294 
CollectMetaData(const char * H5RageFileName)295 int H5RageAdaptor::CollectMetaData(const char* H5RageFileName)
296 {
297   // Parse descriptor file collecting hdf directory, base name, create list of files
298   // One hdf file per variable and per cycle with dataset named "data"
299   if (ParseH5RageFile(H5RageFileName) == 0)
300   {
301     return 0;
302   }
303 
304   // Read the first file to get sizes and type
305   std::string firstFileName = this->HdfFileName[0];
306   hid_t file_id = H5Fopen(firstFileName.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
307   if (file_id < 0)
308   {
309     vtkGenericWarningMacro(
310       "Error loading file: " << firstFileName << ". Please ensure files are HDF5 and not HDF4.");
311     return 0;
312   }
313 
314   // Data is stored column major order so dimensions must be reversed
315   hid_t dataset_id = H5Dopen(file_id, "data", hid_t(0));
316   hid_t datatype_id = H5Dget_type(dataset_id);
317   hid_t dataspace_id = H5Dget_space(dataset_id);
318   size_t size = H5Tget_size(datatype_id);
319   if (size == 8)
320   {
321     this->UseFloat64 = true;
322   }
323 
324   // Read coordinates for physical extents and record which plane
325   // Dimension of each coordinate array gives dimensions for render
326   std::string coordName[3] = { "x", "y", "z" };
327   hid_t memspace_id = hid_t(0);
328   for (int dim = 0; dim < 3; dim++)
329   {
330     if (H5Lexists(file_id, coordName[dim].c_str(), H5P_DEFAULT))
331     {
332       dataset_id = H5Dopen(file_id, coordName[dim].c_str(), hid_t(0));
333       dataspace_id = H5Dget_space(dataset_id);
334       hsize_t ndims = H5Sget_simple_extent_ndims(dataspace_id);
335       hsize_t* coordSize = new hsize_t[ndims];
336       H5Sget_simple_extent_dims(dataspace_id, coordSize, nullptr);
337       memspace_id = H5Screate_simple(ndims, coordSize, nullptr);
338       float* coordinates = new float[coordSize[0]];
339       H5Dread(dataset_id, H5T_NATIVE_FLOAT, memspace_id, dataspace_id, H5P_DEFAULT, coordinates);
340       this->Origin[dim] = coordinates[0];
341       this->Spacing[dim] = coordinates[1] - coordinates[0];
342       this->Dimension[dim] = coordSize[0];
343       delete[] coordinates;
344       delete[] coordSize;
345     }
346   }
347 
348   this->TotalTuples = 1;
349   for (int dim = 0; dim < 3; dim++)
350   {
351     this->TotalTuples *= this->Dimension[dim];
352   }
353 
354   H5Tclose(datatype_id);
355   H5Dclose(dataset_id);
356   H5Sclose(dataspace_id);
357   H5Sclose(memspace_id);
358   H5Fclose(file_id);
359 
360   return 1;
361 }
362 
363 ///////////////////////////////////////////////////////////////////////////////
364 //
365 // Read the global descriptor file (name.h5rage)
366 //
367 // HDF_BASE_NAME base        (Required)
368 // HDF_DIRECTORY hdf0        (Defaults to "." if missing)
369 // HDF_DIRECTORY hdf1
370 // HDF_CYCLE_DIGITS number
371 //
372 ///////////////////////////////////////////////////////////////////////////////
373 
ParseH5RageFile(const char * H5RageFileName)374 int H5RageAdaptor::ParseH5RageFile(const char* H5RageFileName)
375 {
376   // Read the global descriptor file (name.hrage)
377   std::string hdfRageFileName = H5RageFileName;
378   std::ifstream ifStr(hdfRageFileName);
379   if (!ifStr)
380   {
381     vtkGenericWarningMacro(
382       "Could not open the global description .h5rage file: " << H5RageFileName);
383     return 0;
384   }
385 
386   // Get the directory name from the full path in GUI or use current directory
387   std::string::size_type dirPos = hdfRageFileName.find_last_of(Slash);
388   std::string dirName;
389   if (dirPos == std::string::npos)
390   {
391     std::ostringstream tempStr;
392     tempStr << "." << Slash;
393     dirName = tempStr.str();
394   }
395   else
396   {
397     dirName = hdfRageFileName.substr(0, dirPos);
398   }
399 
400   // Parse the h5rage input file
401   std::string hdfBaseName;               // base name to use for data files
402   std::vector<std::string> hdfDirectory; // directories holding data files
403   int numCycleDigits = 6;                // number of digits used for cycle number, default is 6
404   std::string::size_type pos = hdfRageFileName.rfind('.');
405   std::string suffix = hdfRageFileName.substr(pos + 1);
406   char inBuf[256];
407   std::string rest;
408   std::string keyword;
409 
410   while (ifStr.getline(inBuf, 256))
411   {
412     std::string localline(inBuf);
413     localline = TrimString(localline);
414     if (localline.length() > 0)
415     {
416       if (localline[0] != '#' && localline[0] != '!')
417       {
418         // Remove quotes from input
419         localline.erase(std::remove(localline.begin(), localline.end(), '\"'), localline.end());
420         localline.erase(std::remove(localline.begin(), localline.end(), '\''), localline.end());
421 
422         // check for comments in the middle of the line
423         std::string::size_type poundPos = localline.find('#');
424         if (poundPos != std::string::npos)
425         {
426           localline = localline.substr(0, poundPos);
427         }
428 
429         std::string::size_type bangPos = localline.find('!');
430         if (bangPos != std::string::npos)
431         {
432           localline = localline.substr(0, bangPos);
433         }
434 
435         std::string::size_type keyPos = localline.find(' ');
436         keyword = TrimString(localline.substr(0, keyPos));
437         rest = TrimString(localline.substr(keyPos + 1));
438         std::istringstream line(rest);
439 
440         if (keyword == "HDF_DIRECTORY")
441         {
442           line >> rest;
443           if (rest[0] == '/')
444           {
445             // If a full path is given use it
446             hdfDirectory.push_back(rest);
447           }
448           else
449           {
450             // If partial path append to the dir of the .h5rage file
451             std::ostringstream tempStr;
452             tempStr << dirName << Slash << rest;
453             hdfDirectory.push_back(tempStr.str());
454           }
455         }
456 
457         if (keyword == "HDF_BASE_NAME")
458         {
459           line >> rest;
460           std::ostringstream tempStr;
461           tempStr << rest;
462           hdfBaseName = tempStr.str();
463         }
464 
465         if (keyword == "HDF_CYCLE_DIGITS")
466         {
467           line >> rest;
468           std::ostringstream tempStr;
469           tempStr << rest;
470 
471           // check that all characters are digits
472           bool good = true;
473           std::string cycleDigits = tempStr.str();
474           for (std::string::const_iterator it = cycleDigits.begin(); it != cycleDigits.end(); it++)
475           {
476             if (!std::isdigit(*it))
477             {
478               vtkGenericWarningMacro(
479                 "Argument for HDF_CYCLE_DIGITS is not a number: \'" << cycleDigits << "\'");
480               good = false;
481               break;
482             }
483           }
484           if (good)
485           {
486             numCycleDigits = std::stoi(cycleDigits);
487           }
488         }
489       }
490     }
491   }
492   if (hdfDirectory.empty())
493   {
494     hdfDirectory.push_back(dirName);
495   }
496 
497   // Find all files starting with base name with -h appended
498   auto directory = vtkSmartPointer<vtkDirectory>::New();
499   uint64_t numFiles = 0;
500   std::ostringstream baseNameStr;
501   baseNameStr << hdfBaseName << "-h";
502 
503   std::map<std::string, std::vector<std::string>> varToFileMap;
504   std::set<std::string> cycleSet;
505 
506   // Each hdf file is specific to a variable and a cycle
507   int numTotalHDFFiles = 0;
508   for (size_t dir = 0; dir < hdfDirectory.size(); dir++)
509   {
510     if (directory->Open(hdfDirectory[dir].c_str()) == false)
511     {
512       vtkGenericWarningMacro("HDF directory does not exist: " << hdfDirectory[dir]);
513     }
514     else
515     {
516       int numFound = 0;
517       numFiles = directory->GetNumberOfFiles();
518       for (unsigned int i = 0; i < numFiles; i++)
519       {
520         // Check for legal hdf name
521         // check that the beginning of the filename is correct
522         std::string fileStr = directory->GetFile(i);
523         std::size_t found = fileStr.find(baseNameStr.str());
524         if (found == std::string::npos || found > 0)
525         {
526           continue;
527         }
528 
529         // check that there is no '.' in the filename
530         // filename should not have file extension or be a hidden file
531         std::size_t dotPos = fileStr.find('.');
532         if (dotPos != std::string::npos)
533         {
534           continue;
535         }
536 
537         // check if there is only one "-" in the filename
538         std::size_t dashPos = fileStr.find('-');
539         if (dashPos != std::string::npos)
540         {
541           std::string postDash = fileStr.substr(dashPos + 1);
542           dashPos = postDash.find('-');
543           if (dashPos != std::string::npos)
544           {
545             // more than one dash was found
546             continue;
547           }
548         }
549 
550         // filename is basename + four digits, then variable name, then cycle number
551         size_t base_length = baseNameStr.str().length();
552         std::string varStr =
553           fileStr.substr(base_length + 4, fileStr.length() - base_length - 4 - numCycleDigits);
554 
555         // check that the cycle is all digits
556         std::string cycleStr = fileStr.substr(fileStr.length() - numCycleDigits, numCycleDigits);
557         for (std::string::const_iterator it = cycleStr.begin(); it != cycleStr.end(); it++)
558         {
559           if (!std::isdigit(*it))
560           {
561             vtkGenericWarningMacro("Expected last six characters of filename to be digits, but "
562                                    "found a non-digit chacter. Filename: \'"
563               << fileStr << "\' cycleStr: \'" << cycleStr << "\'");
564             return 0;
565           }
566         }
567 
568         cycleSet.insert(cycleStr);
569         std::ostringstream tempStr2;
570         tempStr2 << hdfDirectory[dir] << Slash << fileStr;
571         varToFileMap[varStr].push_back(tempStr2.str());
572         numFound++;
573       }
574       if (numFound == 0)
575       {
576         vtkGenericWarningMacro("HDF directory contains no valid HDF5 files: " << hdfDirectory[dir]);
577       }
578       numTotalHDFFiles += numFound;
579     }
580   }
581 
582   if (numTotalHDFFiles == 0)
583   {
584     vtkGenericWarningMacro("No valid HDF5 files were found over all HDF directories");
585     return 0;
586   }
587 
588   this->NumberOfVariables = static_cast<int>(varToFileMap.size());
589   this->NumberOfTimeSteps = static_cast<int>(cycleSet.size());
590 
591   std::map<std::string, std::vector<std::string>>::iterator miter;
592   std::vector<std::string>::iterator viter;
593   for (miter = varToFileMap.begin(); miter != varToFileMap.end(); ++miter)
594   {
595     this->VariableName.push_back(miter->first);
596     sort(miter->second.begin(), miter->second.end());
597     if ((int)miter->second.size() == this->NumberOfTimeSteps)
598     {
599       for (viter = miter->second.begin(); viter != miter->second.end(); ++viter)
600       {
601         this->HdfFileName.push_back(*viter);
602       }
603     }
604     else
605     {
606       vtkGenericWarningMacro("Missing cycle for var " + miter->first);
607     }
608   }
609 
610   // Move cycle set to double array of steps
611   this->TimeSteps = nullptr;
612   std::set<std::string>::iterator siter;
613   if (this->NumberOfTimeSteps > 0)
614   {
615     this->TimeSteps = new double[this->NumberOfTimeSteps];
616     int step = 0;
617     for (siter = cycleSet.begin(); siter != cycleSet.end(); ++siter)
618     {
619       char* p;
620       long cycle = std::strtol(siter->c_str(), &p, 10);
621       this->TimeSteps[step] = (double)cycle;
622       step++;
623     }
624   }
625   return 1;
626 }
627 
628 //------------------------------------------------------------------------------
629 // Load variable data using files associated with cycle and point selection
630 //------------------------------------------------------------------------------
LoadVariableData(vtkImageData * output,int timeStep,vtkDataArraySelection * pointDataArraySelection)631 void H5RageAdaptor::LoadVariableData(
632   vtkImageData* output, int timeStep, vtkDataArraySelection* pointDataArraySelection)
633 {
634   // Add FieldData array for cycle number
635   vtkNew<vtkDoubleArray> cycleArray;
636   cycleArray->SetName("CycleIndex");
637   cycleArray->SetNumberOfComponents(1);
638   cycleArray->SetNumberOfTuples(1);
639   cycleArray->SetTuple1(0, this->TimeSteps[timeStep]);
640   output->GetFieldData()->AddArray(cycleArray);
641 
642   bool firstScalar = true;
643   for (int var = 0; var < this->NumberOfVariables; var++)
644   {
645     if (pointDataArraySelection->ArrayIsEnabled(this->VariableName[var].c_str()))
646     {
647       float* fData = nullptr;
648       double* dData = nullptr;
649 
650       // Proc 0 reads the HDF file verifying and getting data
651       // HDF data was written as column major and must be reversed
652       if (this->Rank == 0)
653       {
654         int offset = var * this->NumberOfTimeSteps + timeStep;
655         hid_t file_id = H5Fopen(this->HdfFileName[offset].c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
656         hid_t dataset_id = H5Dopen(file_id, "data", hid_t(0));
657         hid_t dataspace_id = H5Dget_space(dataset_id);
658         hid_t datatype_id = H5Dget_type(dataset_id);
659         hid_t ndims = H5Sget_simple_extent_ndims(dataspace_id);
660         hsize_t* dims_out = new hsize_t[ndims];
661         H5Sget_simple_extent_dims(dataspace_id, dims_out, nullptr);
662         int* dimensions = new int[ndims];
663         for (int dim = 0; dim < ndims; dim++)
664         {
665           dimensions[dim] = dims_out[dim];
666         }
667 
668         hid_t memspace_id;
669         if (this->UseFloat64)
670         {
671           dData = new double[this->TotalTuples];
672           memspace_id = H5Screate_simple(ndims, dims_out, nullptr);
673           H5Dread(dataset_id, H5T_NATIVE_DOUBLE, memspace_id, dataspace_id, H5P_DEFAULT, dData);
674           ConvertHDFData(ndims, dimensions, dData);
675         }
676         else
677         {
678           fData = new float[this->TotalTuples];
679           memspace_id = H5Screate_simple(ndims, dims_out, nullptr);
680           H5Dread(dataset_id, H5T_NATIVE_FLOAT, memspace_id, dataspace_id, H5P_DEFAULT, fData);
681           ConvertHDFData(ndims, dimensions, fData);
682         }
683 
684         H5Tclose(datatype_id);
685         H5Dclose(dataset_id);
686         H5Sclose(memspace_id);
687         H5Sclose(dataspace_id);
688         H5Fclose(file_id);
689         delete[] dimensions;
690         delete[] dims_out;
691       }
692 
693       // Share the hdf data read among processors according to schedule
694       if (this->UseFloat64)
695       {
696         // First variable is stored in Scalars to eliminate error on Contour
697         if (firstScalar)
698         {
699           firstScalar = false;
700           output->GetPointData()->GetScalars()->SetName(pointDataArraySelection->GetArrayName(var));
701         }
702         vtkNew<vtkDoubleArray> data;
703         data->SetName(pointDataArraySelection->GetArrayName(var));
704         data->SetNumberOfComponents(1);
705         data->SetNumberOfTuples(this->NumberOfTuples[this->Rank]);
706         output->GetPointData()->AddArray(data);
707         double* varData = data->GetPointer(0);
708 
709         if (this->Rank == 0)
710         {
711           // Store the rank 0 portion of the data
712           int pos = 0;
713           for (int k = this->ExtentSchedule[0][4]; k <= this->ExtentSchedule[0][5]; k++)
714           {
715             for (int j = this->ExtentSchedule[0][2]; j <= this->ExtentSchedule[0][3]; j++)
716             {
717               for (int i = this->ExtentSchedule[0][0]; i <= this->ExtentSchedule[0][1]; i++)
718               {
719                 int indx =
720                   (k * this->Dimension[0] * this->Dimension[1]) + (j * this->Dimension[0]) + i;
721                 varData[pos++] = dData[indx];
722               }
723             }
724           }
725 
726           // Buffer for sending to other processors
727           for (int rank = 1; rank < this->TotalRank; rank++)
728           {
729             // Allocate buffer to send and fill with correct range per processor
730             double* buffer = new double[this->NumberOfTuples[this->Rank]];
731             int pos2 = 0;
732             for (int k = this->ExtentSchedule[rank][4]; k <= this->ExtentSchedule[rank][5]; k++)
733             {
734               for (int j = this->ExtentSchedule[rank][2]; j <= this->ExtentSchedule[rank][3]; j++)
735               {
736                 for (int i = this->ExtentSchedule[rank][0]; i <= this->ExtentSchedule[rank][1]; i++)
737                 {
738                   int indx =
739                     (k * this->Dimension[0] * this->Dimension[1]) + (j * this->Dimension[0]) + i;
740                   buffer[pos2++] = dData[indx];
741                 }
742               }
743             }
744             this->Controller->Send(buffer, this->NumberOfTuples[rank], rank, mpiTag);
745             delete[] buffer;
746           }
747         }
748         else
749         {
750           this->Controller->Receive(varData, this->NumberOfTuples[this->Rank], 0, mpiTag);
751         }
752       }
753       else
754       {
755         // First variable is stored in Scalars to eliminate error on Contour
756         if (firstScalar)
757         {
758           firstScalar = false;
759           output->GetPointData()->GetScalars()->SetName(pointDataArraySelection->GetArrayName(var));
760         }
761         vtkNew<vtkFloatArray> data;
762         data->SetName(pointDataArraySelection->GetArrayName(var));
763         data->SetNumberOfComponents(1);
764         data->SetNumberOfTuples(this->NumberOfTuples[this->Rank]);
765         output->GetPointData()->AddArray(data);
766         float* varData = data->GetPointer(0);
767 
768         if (this->Rank == 0)
769         {
770           // Store the rank 0 portion of the data
771           int pos2 = 0;
772           for (int k = this->ExtentSchedule[0][4]; k <= this->ExtentSchedule[0][5]; k++)
773           {
774             for (int j = this->ExtentSchedule[0][2]; j <= this->ExtentSchedule[0][3]; j++)
775             {
776               for (int i = this->ExtentSchedule[0][0]; i <= this->ExtentSchedule[0][1]; i++)
777               {
778                 int indx =
779                   (k * this->Dimension[0] * this->Dimension[1]) + (j * this->Dimension[0]) + i;
780                 varData[pos2++] = fData[indx];
781               }
782             }
783           }
784 
785           // Buffer for sending to other processors
786           for (int rank = 1; rank < this->TotalRank; rank++)
787           {
788             // Allocate buffer to send and fill with correct range per processor
789             float* buffer = new float[this->NumberOfTuples[rank]];
790             int pos3 = 0;
791             for (int k = this->ExtentSchedule[rank][4]; k <= this->ExtentSchedule[rank][5]; k++)
792             {
793               for (int j = this->ExtentSchedule[rank][2]; j <= this->ExtentSchedule[rank][3]; j++)
794               {
795                 for (int i = this->ExtentSchedule[rank][0]; i <= this->ExtentSchedule[rank][1]; i++)
796                 {
797                   int indx =
798                     (k * this->Dimension[0] * this->Dimension[1]) + (j * this->Dimension[0]) + i;
799                   buffer[pos3++] = fData[indx];
800                 }
801               }
802             }
803             this->Controller->Send(buffer, this->NumberOfTuples[rank], rank, mpiTag);
804             delete[] buffer;
805           }
806         }
807         else
808         {
809           this->Controller->Receive(varData, this->NumberOfTuples[this->Rank], 0, mpiTag);
810         }
811       }
812     }
813   }
814 }
815 
816 //------------------------------------------------------------------------------
817 // Convert HDF data into standard row major ordering.
818 // Depending on whether the data is 3D or 2D, and which 2D slice, data along
819 // one axis needs to be flipped. This is a bug in Rage that did not get fixed
820 // for backward compatibility issues.
821 // Rage data 3D must be flipped on the second dim
822 // Rage data 2D must be flipped on first dim (will be y in y-z/y-x or z in z-x)
823 //------------------------------------------------------------------------------
824 template <class T>
ConvertHDFData(int ndims,int * dimensions,T * hdfData)825 void H5RageAdaptor::ConvertHDFData(int ndims, int* dimensions, T* hdfData)
826 {
827   T* convertedData = new T[this->TotalTuples];
828 
829   if (ndims == 3)
830   {
831     int pos = 0;
832     int planeSize = dimensions[1] * dimensions[2];
833     int rowSize = dimensions[2];
834     for (int k = 0; k < dimensions[0]; k++)
835     {
836       for (int j = (dimensions[1] - 1); j >= 0; j--)
837       {
838         for (int i = 0; i < dimensions[2]; i++)
839         {
840           int indx = (k * planeSize) + (j * rowSize) + i;
841           convertedData[pos++] = hdfData[indx];
842         }
843       }
844     }
845   }
846   else
847   {
848     int pos = 0;
849     int rowSize = dimensions[1];
850     for (int j = (dimensions[0] - 1); j >= 0; j--)
851     {
852       for (int i = 0; i < dimensions[1]; i++)
853       {
854         int indx = (j * rowSize) + i;
855         convertedData[pos++] = hdfData[indx];
856       }
857     }
858   }
859 
860   // Copy back to original
861   for (int indx = 0; indx < this->TotalTuples; indx++)
862   {
863     hdfData[indx] = convertedData[indx];
864   }
865   delete[] convertedData;
866 }
867