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