1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkMPIMultiBlockPLOT3DReader.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm 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 #include "vtkMPIMultiBlockPLOT3DReader.h"
16 
17 #include "vtkByteSwap.h"
18 #include "vtkDoubleArray.h"
19 #include "vtkErrorCode.h"
20 #include "vtkFloatArray.h"
21 #include "vtkIntArray.h"
22 #include "vtkMPICommunicator.h"
23 #include "vtkMPIController.h"
24 #include "vtkMPI.h"
25 #include "vtkMultiBlockPLOT3DReaderInternals.h"
26 #include "vtkObjectFactory.h"
27 #include "vtkStructuredData.h"
28 #include <exception>
29 #include <cassert>
30 
31 
32 
33 #define DEFINE_MPI_TYPE(ctype, mpitype) \
34   template <> struct mpi_type<ctype> { static MPI_Datatype type() { return mpitype; }  };
35 
36 namespace
37 {
38   template <class T> struct mpi_type {};
39   DEFINE_MPI_TYPE(char, MPI_CHAR);
40   DEFINE_MPI_TYPE(signed char, MPI_SIGNED_CHAR);
41   DEFINE_MPI_TYPE(unsigned char, MPI_UNSIGNED_CHAR);
42   DEFINE_MPI_TYPE(short, MPI_SHORT);
43   DEFINE_MPI_TYPE(unsigned short, MPI_UNSIGNED_SHORT);
44   DEFINE_MPI_TYPE(int, MPI_INT);
45   DEFINE_MPI_TYPE(unsigned int, MPI_UNSIGNED);
46   DEFINE_MPI_TYPE(long, MPI_LONG);
47   DEFINE_MPI_TYPE(unsigned long, MPI_UNSIGNED_LONG);
48   DEFINE_MPI_TYPE(float, MPI_FLOAT);
49   DEFINE_MPI_TYPE(double, MPI_DOUBLE);
50   DEFINE_MPI_TYPE(long long, MPI_LONG_LONG);
51   DEFINE_MPI_TYPE(unsigned long long, MPI_UNSIGNED_LONG_LONG);
52 
53   class MPIPlot3DException : public std::exception
54   {
55   };
56 
57   template <class DataType>
58   class vtkMPIPLOT3DArrayReader
59   {
60   public:
vtkMPIPLOT3DArrayReader()61     vtkMPIPLOT3DArrayReader() : ByteOrder(
62       vtkMultiBlockPLOT3DReader::FILE_BIG_ENDIAN)
63     {
64     }
65 
ReadScalar(void * vfp,vtkTypeUInt64 offset,vtkIdType preskip,vtkIdType n,vtkIdType vtkNotUsed (postskip),DataType * scalar,const vtkMultiBlockPLOT3DReaderRecord & record=vtkMultiBlockPLOT3DReaderRecord ())66     vtkIdType ReadScalar(void* vfp,
67       vtkTypeUInt64 offset,
68       vtkIdType preskip,
69       vtkIdType n,
70       vtkIdType vtkNotUsed(postskip),
71       DataType* scalar,
72       const vtkMultiBlockPLOT3DReaderRecord& record = vtkMultiBlockPLOT3DReaderRecord())
73     {
74       vtkMPIOpaqueFileHandle* fp = reinterpret_cast<vtkMPIOpaqueFileHandle*>(vfp);
75       assert(fp);
76 
77       // skip preskip if we're setting over subrecord separators.
78       offset += record.GetLengthWithSeparators(offset, preskip*sizeof(DataType));
79 
80       // Let's see if we encounter markers while reading the data from current
81       // position.
82       std::vector<std::pair<vtkTypeUInt64, vtkTypeUInt64> > chunks =
83         record.GetChunksToRead(offset, sizeof(DataType) * n);
84 
85       const int dummy_INT_MAX = 2e9; /// XXX: arbitrary limit that seems
86                                      /// to work when reading large files.
87       vtkIdType bytesread = 0;
88       for (size_t cc=0; cc < chunks.size(); cc++)
89       {
90         vtkTypeUInt64 start = chunks[cc].first;
91         vtkTypeUInt64 length = chunks[cc].second;
92         while (length > 0)
93         {
94           int segment = length > static_cast<vtkTypeUInt64>(dummy_INT_MAX)?
95             (length - dummy_INT_MAX) : static_cast<int>(length);
96 
97           MPI_Status status;
98           if (MPI_File_read_at(fp->Handle, start,
99             reinterpret_cast<char*>(scalar) + bytesread,
100             segment, MPI_UNSIGNED_CHAR, &status) != MPI_SUCCESS)
101           {
102             return 0; // let's assume nothing was read.
103           }
104           start += segment;
105           length -= segment;
106           bytesread += segment;
107         }
108       }
109 
110       if (this->ByteOrder == vtkMultiBlockPLOT3DReader::FILE_LITTLE_ENDIAN)
111       {
112         if (sizeof(DataType) == 4)
113         {
114           vtkByteSwap::Swap4LERange(scalar, n);
115         }
116         else
117         {
118           vtkByteSwap::Swap8LERange(scalar, n);
119         }
120       }
121       else
122       {
123         if (sizeof(DataType) == 4)
124         {
125           vtkByteSwap::Swap4BERange(scalar, n);
126         }
127         else
128         {
129           vtkByteSwap::Swap8BERange(scalar, n);
130         }
131       }
132       return bytesread / sizeof(DataType);
133     }
134 
ReadVector(void * vfp,vtkTypeUInt64 offset,int extent[6],int wextent[6],int numDims,DataType * vector,const vtkMultiBlockPLOT3DReaderRecord & record)135     vtkIdType ReadVector(void* vfp,
136       vtkTypeUInt64 offset,
137       int extent[6], int wextent[6],
138       int numDims, DataType* vector,
139       const vtkMultiBlockPLOT3DReaderRecord &record)
140     {
141       vtkIdType n = vtkStructuredData::GetNumberOfPoints(extent);
142       vtkIdType totalN = vtkStructuredData::GetNumberOfPoints(wextent);
143 
144       // Setting to 0 in case numDims == 0. We still need to
145       // populate an array with 3 components but the code below
146       // does not read the 3rd component (it doesn't exist
147       // in the file)
148       memset(vector, 0, n*3*sizeof(DataType));
149 
150       vtkIdType retVal = 0;
151       DataType* buffer = new DataType[n];
152       for (int component = 0; component < numDims; component++)
153       {
154         vtkIdType preskip, postskip;
155         vtkMultiBlockPLOT3DReaderInternals::CalculateSkips(extent, wextent, preskip, postskip);
156         vtkIdType valread = this->ReadScalar(vfp, offset, preskip, n, postskip, buffer, record);
157         if (valread != n)
158         {
159           return 0; // failed.
160         }
161         retVal += valread;
162         for (vtkIdType i=0; i<n; i++)
163         {
164           vector[3*i+component] = buffer[i];
165         }
166         offset += record.GetLengthWithSeparators(offset, totalN * sizeof(DataType));
167       }
168       delete[] buffer;
169       return retVal;
170     }
171     int ByteOrder;
172   };
173 }
174 
175 vtkStandardNewMacro(vtkMPIMultiBlockPLOT3DReader);
176 //----------------------------------------------------------------------------
vtkMPIMultiBlockPLOT3DReader()177 vtkMPIMultiBlockPLOT3DReader::vtkMPIMultiBlockPLOT3DReader()
178 {
179   this->UseMPIIO = true;
180 }
181 
182 //----------------------------------------------------------------------------
~vtkMPIMultiBlockPLOT3DReader()183 vtkMPIMultiBlockPLOT3DReader::~vtkMPIMultiBlockPLOT3DReader()
184 {
185 }
186 
187 //----------------------------------------------------------------------------
CanUseMPIIO()188 bool vtkMPIMultiBlockPLOT3DReader::CanUseMPIIO()
189 {
190   return (this->UseMPIIO && this->BinaryFile &&
191     this->Internal->Settings.NumberOfDimensions == 3 &&
192     vtkMPIController::SafeDownCast(this->Controller) != nullptr);
193 }
194 
195 //----------------------------------------------------------------------------
OpenFileForDataRead(void * & vfp,const char * fname)196 int vtkMPIMultiBlockPLOT3DReader::OpenFileForDataRead(void*& vfp, const char* fname)
197 {
198   if (!this->CanUseMPIIO())
199   {
200     return this->Superclass::OpenFileForDataRead(vfp, fname);
201   }
202 
203   vtkMPICommunicator* mpiComm = vtkMPICommunicator::SafeDownCast(
204     this->Controller->GetCommunicator());
205   assert(mpiComm);
206 
207   vtkMPIOpaqueFileHandle* handle = new vtkMPIOpaqueFileHandle();
208   try
209   {
210     if (MPI_File_open(*mpiComm->GetMPIComm()->GetHandle(),
211                       const_cast<char*>(fname), MPI_MODE_RDONLY,
212                       MPI_INFO_NULL, &handle->Handle) != MPI_SUCCESS)
213     {
214       this->SetErrorCode(vtkErrorCode::FileNotFoundError);
215       vtkErrorMacro("File: " << fname << " not found.");
216       throw MPIPlot3DException();
217     }
218   }
219   catch (const MPIPlot3DException &)
220   {
221     delete handle;
222     vfp = nullptr;
223     return VTK_ERROR;
224   }
225   vfp = handle;
226   return VTK_OK;
227 }
228 
229 //----------------------------------------------------------------------------
CloseFile(void * vfp)230 void vtkMPIMultiBlockPLOT3DReader::CloseFile(void* vfp)
231 {
232   if (!this->CanUseMPIIO())
233   {
234     this->Superclass::CloseFile(vfp);
235     return;
236   }
237 
238   vtkMPIOpaqueFileHandle* handle = reinterpret_cast<vtkMPIOpaqueFileHandle*>(vfp);
239   assert(handle);
240   if (MPI_File_close(&handle->Handle) != MPI_SUCCESS)
241   {
242     vtkErrorMacro("Failed to close file!");
243   }
244 }
245 
246 //----------------------------------------------------------------------------
ReadIntScalar(void * vfp,int extent[6],int wextent[6],vtkDataArray * scalar,vtkTypeUInt64 offset,const vtkMultiBlockPLOT3DReaderRecord & record)247 int vtkMPIMultiBlockPLOT3DReader::ReadIntScalar(
248   void* vfp, int extent[6], int wextent[6],
249   vtkDataArray* scalar, vtkTypeUInt64 offset,
250   const vtkMultiBlockPLOT3DReaderRecord& record)
251 
252 {
253   if (!this->CanUseMPIIO())
254   {
255     return this->Superclass::ReadIntScalar(vfp, extent, wextent, scalar, offset, record);
256   }
257 
258   vtkIdType n = vtkStructuredData::GetNumberOfPoints(extent);
259   vtkMPIPLOT3DArrayReader<int> arrayReader;
260   arrayReader.ByteOrder = this->Internal->Settings.ByteOrder;
261   vtkIdType preskip, postskip;
262   vtkMultiBlockPLOT3DReaderInternals::CalculateSkips(extent, wextent, preskip, postskip);
263   vtkIntArray* intArray = static_cast<vtkIntArray*>(scalar);
264   return arrayReader.ReadScalar(
265     vfp, offset, preskip, n, postskip, intArray->GetPointer(0), record) == n;
266 }
267 
268 //----------------------------------------------------------------------------
ReadScalar(void * vfp,int extent[6],int wextent[6],vtkDataArray * scalar,vtkTypeUInt64 offset,const vtkMultiBlockPLOT3DReaderRecord & record)269 int vtkMPIMultiBlockPLOT3DReader::ReadScalar(
270   void* vfp,
271   int extent[6], int wextent[6],
272   vtkDataArray* scalar, vtkTypeUInt64 offset,
273   const vtkMultiBlockPLOT3DReaderRecord& record)
274 {
275   if (!this->CanUseMPIIO())
276   {
277     return this->Superclass::ReadScalar(vfp, extent, wextent, scalar, offset, record);
278   }
279 
280   vtkIdType n = vtkStructuredData::GetNumberOfPoints(extent);
281   if (this->Internal->Settings.Precision == 4)
282   {
283     vtkMPIPLOT3DArrayReader<float> arrayReader;
284     arrayReader.ByteOrder = this->Internal->Settings.ByteOrder;
285     vtkIdType preskip, postskip;
286     vtkMultiBlockPLOT3DReaderInternals::CalculateSkips(extent, wextent, preskip, postskip);
287     vtkFloatArray* floatArray = static_cast<vtkFloatArray*>(scalar);
288     return arrayReader.ReadScalar(
289       vfp, offset, preskip, n, postskip, floatArray->GetPointer(0),
290       record) == n;
291   }
292   else
293   {
294     vtkMPIPLOT3DArrayReader<double> arrayReader;
295     arrayReader.ByteOrder = this->Internal->Settings.ByteOrder;
296     vtkIdType preskip, postskip;
297     vtkMultiBlockPLOT3DReaderInternals::CalculateSkips(extent, wextent, preskip, postskip);
298     vtkDoubleArray* doubleArray = static_cast<vtkDoubleArray*>(scalar);
299     return arrayReader.ReadScalar(
300       vfp, offset, preskip, n, postskip, doubleArray->GetPointer(0),
301       record) == n;
302   }
303 }
304 
305 //----------------------------------------------------------------------------
ReadVector(void * vfp,int extent[6],int wextent[6],int numDims,vtkDataArray * vector,vtkTypeUInt64 offset,const vtkMultiBlockPLOT3DReaderRecord & record)306 int vtkMPIMultiBlockPLOT3DReader::ReadVector(
307   void* vfp,
308   int extent[6], int wextent[6],
309   int numDims, vtkDataArray* vector, vtkTypeUInt64 offset,
310   const vtkMultiBlockPLOT3DReaderRecord& record)
311 {
312   if (!this->CanUseMPIIO())
313   {
314     return this->Superclass::ReadVector(vfp, extent, wextent, numDims, vector, offset, record);
315   }
316 
317   vtkIdType n = vtkStructuredData::GetNumberOfPoints(extent);
318   vtkIdType nValues = n*numDims;
319   if (this->Internal->Settings.Precision == 4)
320   {
321     vtkMPIPLOT3DArrayReader<float> arrayReader;
322     arrayReader.ByteOrder = this->Internal->Settings.ByteOrder;
323     vtkFloatArray* floatArray = static_cast<vtkFloatArray*>(vector);
324     return arrayReader.ReadVector(
325       vfp, offset, extent, wextent, numDims, floatArray->GetPointer(0), record) == nValues;
326   }
327   else
328   {
329     vtkMPIPLOT3DArrayReader<double> arrayReader;
330     arrayReader.ByteOrder = this->Internal->Settings.ByteOrder;
331     vtkDoubleArray* doubleArray = static_cast<vtkDoubleArray*>(vector);
332     return arrayReader.ReadVector(
333       vfp, offset, extent, wextent, numDims, doubleArray->GetPointer(0), record) == nValues;
334   }
335 }
336 
337 //----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)338 void vtkMPIMultiBlockPLOT3DReader::PrintSelf(ostream& os, vtkIndent indent)
339 {
340   this->Superclass::PrintSelf(os, indent);
341   os << indent << "UseMPIIO: " << this->UseMPIIO << endl;
342 }
343