1 // -*- c++ -*-
2 /*=========================================================================
3 
4   Program:   Visualization Toolkit
5   Module:    vtkPSLACReader.cxx
6 
7   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
8   All rights reserved.
9   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
10 
11      This software is distributed WITHOUT ANY WARRANTY; without even
12      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13      PURPOSE.  See the above copyright notice for more information.
14 
15 =========================================================================*/
16 
17 /*-------------------------------------------------------------------------
18   Copyright 2008 Sandia Corporation.
19   Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
20   the U.S. Government retains certain rights in this software.
21 -------------------------------------------------------------------------*/
22 
23 #include "vtkPSLACReader.h"
24 
25 #include "vtkCellArray.h"
26 #include "vtkCellArrayIterator.h"
27 #include "vtkCompositeDataIterator.h"
28 #include "vtkDoubleArray.h"
29 #include "vtkDummyController.h"
30 #include "vtkIdTypeArray.h"
31 #include "vtkInformation.h"
32 #include "vtkInformationVector.h"
33 #include "vtkMath.h"
34 #include "vtkMultiBlockDataSet.h"
35 #include "vtkMultiProcessController.h"
36 #include "vtkMultiProcessStream.h"
37 #include "vtkObjectFactory.h"
38 #include "vtkPointData.h"
39 #include "vtkSortDataArray.h"
40 #include "vtkStreamingDemandDrivenPipeline.h"
41 #include "vtkUnstructuredGrid.h"
42 
43 #include "vtkSmartPointer.h"
44 #define VTK_CREATE(type, name) vtkSmartPointer<type> name = vtkSmartPointer<type>::New()
45 
46 #include "vtk_netcdf.h"
47 
48 #include <unordered_map>
49 
50 //=============================================================================
51 #define CALL_NETCDF(call)                                                                          \
52   {                                                                                                \
53     int errorcode = call;                                                                          \
54     if (errorcode != NC_NOERR)                                                                     \
55     {                                                                                              \
56       vtkErrorMacro(<< "netCDF Error: " << nc_strerror(errorcode));                                \
57       return 0;                                                                                    \
58     }                                                                                              \
59   }
60 
61 #define WRAP_NETCDF(call)                                                                          \
62   {                                                                                                \
63     int errorcode = call;                                                                          \
64     if (errorcode != NC_NOERR)                                                                     \
65       return errorcode;                                                                            \
66   }
67 
68 #ifdef VTK_USE_64BIT_IDS
69 //#ifdef NC_INT64
70 //// This may or may not work with the netCDF 4 library reading in netCDF 3 files.
71 //#define nc_get_vars_vtkIdType nc_get_vars_longlong
72 //#else // NC_INT64
nc_get_vars_vtkIdType(int ncid,int varid,const size_t start[],const size_t count[],const ptrdiff_t stride[],vtkIdType * ip)73 static int nc_get_vars_vtkIdType(int ncid, int varid, const size_t start[], const size_t count[],
74   const ptrdiff_t stride[], vtkIdType* ip)
75 {
76   // Step 1, figure out how many entries in the given variable.
77   int numdims;
78   WRAP_NETCDF(nc_inq_varndims(ncid, varid, &numdims));
79   vtkIdType numValues = 1;
80   for (int dim = 0; dim < numdims; dim++)
81   {
82     numValues *= count[dim];
83   }
84 
85   // Step 2, read the data in as 32 bit integers.  Recast the input buffer
86   // so we do not have to create a new one.
87   long* smallIp = reinterpret_cast<long*>(ip);
88   WRAP_NETCDF(nc_get_vars_long(ncid, varid, start, count, stride, smallIp));
89 
90   // Step 3, recast the data from 32 bit integers to 64 bit integers.  Since we
91   // are storing both in the same buffer, we need to be careful to not overwrite
92   // uncopied 32 bit numbers with 64 bit numbers.  We can do that by copying
93   // backwards.
94   for (vtkIdType i = numValues - 1; i >= 0; i--)
95   {
96     ip[i] = static_cast<vtkIdType>(smallIp[i]);
97   }
98 
99   return NC_NOERR;
100 }
101 //#endif // NC_INT64
102 #else // VTK_USE_64_BIT_IDS
103 #define nc_get_vars_vtkIdType nc_get_vars_int
104 #endif // VTK_USE_64BIT_IDS
105 
106 //=============================================================================
NetCDFTypeToVTKType(nc_type type)107 static int NetCDFTypeToVTKType(nc_type type)
108 {
109   switch (type)
110   {
111     case NC_BYTE:
112       return VTK_UNSIGNED_CHAR;
113     case NC_CHAR:
114       return VTK_CHAR;
115     case NC_SHORT:
116       return VTK_SHORT;
117     case NC_INT:
118       return VTK_INT;
119     case NC_FLOAT:
120       return VTK_FLOAT;
121     case NC_DOUBLE:
122       return VTK_DOUBLE;
123     default:
124       vtkGenericWarningMacro(<< "Unknown netCDF variable type " << type);
125       return -1;
126   }
127 }
128 
129 //=============================================================================
130 // In this version, indexMap points from outArray to inArray.  All the values
131 // of outArray get filled.
132 template <class T>
vtkPSLACReaderMapValues1(const T * inArray,T * outArray,int numComponents,vtkIdTypeArray * indexMap,vtkIdType offset=0)133 void vtkPSLACReaderMapValues1(
134   const T* inArray, T* outArray, int numComponents, vtkIdTypeArray* indexMap, vtkIdType offset = 0)
135 {
136   vtkIdType numVals = indexMap->GetNumberOfTuples();
137   for (vtkIdType i = 0; i < numVals; i++)
138   {
139     vtkIdType j = indexMap->GetValue(i) - offset;
140     for (int c = 0; c < numComponents; c++)
141     {
142       outArray[numComponents * i + c] = inArray[numComponents * j + c];
143     }
144   }
145 }
146 
147 // // In this version, indexMap points from inArray to outArray.  All the values
148 // // of inArray get copied.
149 // template<class T>
150 // void vtkPSLACReaderMapValues2(const T *inArray, T *outArray, int numComponents,
151 //                               vtkIdTypeArray *indexMap)
152 // {
153 //   vtkIdType numVals = indexMap->GetNumberOfTuples();
154 //   for (vtkIdType i = 0; i < numVals; i++)
155 //     {
156 //     vtkIdType j = indexMap->GetValue(i);
157 //     for (int c = 0; c < numComponents; c++)
158 //       {
159 //       outArray[numComponents*j+c] = inArray[numComponents*i+c];
160 //       }
161 //     }
162 // }
163 
164 //=============================================================================
165 // Make sure that each process has the same number of blocks in the same
166 // position.  Assumes that all blocks are unstructured grids.
SynchronizeBlocks(vtkMultiBlockDataSet * blocks,vtkMultiProcessController * controller,vtkInformationIntegerKey * typeKey)167 static void SynchronizeBlocks(vtkMultiBlockDataSet* blocks, vtkMultiProcessController* controller,
168   vtkInformationIntegerKey* typeKey)
169 {
170   unsigned long localNumBlocks = blocks->GetNumberOfBlocks();
171   unsigned long numBlocks;
172   controller->AllReduce(&localNumBlocks, &numBlocks, 1, vtkCommunicator::MAX_OP);
173   if (blocks->GetNumberOfBlocks() < numBlocks)
174   {
175     blocks->SetNumberOfBlocks(numBlocks);
176   }
177 
178   for (unsigned int blockId = 0; blockId < numBlocks; blockId++)
179   {
180     vtkDataObject* object = blocks->GetBlock(blockId);
181     if (object && !object->IsA("vtkUnstructuredGrid"))
182     {
183       vtkGenericWarningMacro(<< "Sanity error: found a block that is not an unstructured grid.");
184     }
185     int localBlockExists = (object != nullptr);
186     int globalBlockExists = 0;
187     controller->AllReduce(&localBlockExists, &globalBlockExists, 1, vtkCommunicator::LOGICAL_OR_OP);
188     if (!localBlockExists && globalBlockExists)
189     {
190       VTK_CREATE(vtkUnstructuredGrid, grid);
191       blocks->SetBlock(blockId, grid);
192       blocks->GetMetaData(blockId)->Set(typeKey, 1);
193     }
194   }
195 }
196 
197 //=============================================================================
198 // Structures used by ReadMidpointCoordinates to store and transfer midpoint
199 // information.
200 namespace vtkPSLACReaderTypes
201 {
202 struct EdgeEndpointsHash
203 {
204 public:
operator ()vtkPSLACReaderTypes::EdgeEndpointsHash205   size_t operator()(const vtkSLACReader::EdgeEndpoints& edge) const
206   {
207     return static_cast<size_t>(edge.GetMinEndPoint() + edge.GetMaxEndPoint());
208   }
209 };
210 
211 struct midpointPositionType_t
212 {
213   double coord[3];
214 };
215 using midpointPositionType = struct midpointPositionType_t;
216 const vtkIdType midpointPositionSize = sizeof(midpointPositionType) / sizeof(double);
217 
218 struct midpointTopologyType_t
219 {
220   vtkIdType minEdgePoint;
221   vtkIdType maxEdgePoint;
222   vtkIdType globalId;
223 };
224 using midpointTopologyType = struct midpointTopologyType_t;
225 const vtkIdType midpointTopologySize = sizeof(midpointTopologyType) / sizeof(vtkIdType);
226 
227 struct midpointListsType_t
228 {
229   std::vector<midpointPositionType> position;
230   std::vector<midpointTopologyType> topology;
231 };
232 using midpointListsType = struct midpointListsType_t;
233 
234 struct midpointPointersType_t
235 {
236   midpointPositionType* position;
237   midpointTopologyType* topology;
238 };
239 using midpointPointersType = struct midpointPointersType_t;
240 typedef std::unordered_map<vtkSLACReader::EdgeEndpoints, midpointPointersType, EdgeEndpointsHash>
241   MidpointsAvailableType;
242 
243 //------------------------------------------------------------------------------
244 // Convenience function for gathering midpoint information to a process.
GatherMidpoints(vtkMultiProcessController * controller,const midpointListsType & sendMidpoints,midpointListsType & recvMidpoints,int process)245 static void GatherMidpoints(vtkMultiProcessController* controller,
246   const midpointListsType& sendMidpoints, midpointListsType& recvMidpoints, int process)
247 {
248   vtkIdType sendLength = static_cast<vtkIdType>(sendMidpoints.position.size());
249   if (sendLength != static_cast<vtkIdType>(sendMidpoints.topology.size()))
250   {
251     vtkGenericWarningMacro(<< "Bad midpoint array structure.");
252     return;
253   }
254 
255   vtkIdType numProcesses = controller->GetNumberOfProcesses();
256 
257   // Gather the amount of data each process is going to send.
258   std::vector<vtkIdType> receiveCounts(numProcesses);
259   controller->Gather(&sendLength, &receiveCounts.at(0), 1, process);
260 
261   // Get ready the arrays for the receiver that determine how much data
262   // to get and where to put it.
263   std::vector<vtkIdType> positionLengths(numProcesses);
264   std::vector<vtkIdType> positionOffsets(numProcesses);
265   std::vector<vtkIdType> topologyLengths(numProcesses);
266   std::vector<vtkIdType> topologyOffsets(numProcesses);
267 
268   const double* sendPositionBuffer =
269     ((sendLength > 0) ? reinterpret_cast<const double*>(&sendMidpoints.position.at(0)) : nullptr);
270   const vtkIdType* sendTopologyBuffer =
271     ((sendLength > 0) ? reinterpret_cast<const vtkIdType*>(&sendMidpoints.topology.at(0))
272                       : nullptr);
273   double* recvPositionBuffer;
274   vtkIdType* recvTopologyBuffer;
275 
276   if (process == controller->GetLocalProcessId())
277   {
278     vtkIdType numEntries = 0;
279     for (int i = 0; i < numProcesses; i++)
280     {
281       positionLengths[i] = midpointPositionSize * receiveCounts[i];
282       positionOffsets[i] = midpointPositionSize * numEntries;
283       topologyLengths[i] = midpointTopologySize * receiveCounts[i];
284       topologyOffsets[i] = midpointTopologySize * numEntries;
285       numEntries += receiveCounts[i];
286     }
287     recvMidpoints.position.resize(numEntries);
288     recvMidpoints.topology.resize(numEntries);
289 
290     recvPositionBuffer =
291       ((numEntries > 0) ? reinterpret_cast<double*>(&recvMidpoints.position.at(0)) : nullptr);
292     recvTopologyBuffer =
293       ((numEntries > 0) ? reinterpret_cast<vtkIdType*>(&recvMidpoints.topology.at(0)) : nullptr);
294   }
295   else
296   {
297     recvPositionBuffer = nullptr;
298     recvTopologyBuffer = nullptr;
299   }
300 
301   // Gather the actual data.
302   controller->GatherV(sendPositionBuffer, recvPositionBuffer, midpointPositionSize * sendLength,
303     &positionLengths.at(0), &positionOffsets.at(0), process);
304   controller->GatherV(sendTopologyBuffer, recvTopologyBuffer, midpointTopologySize * sendLength,
305     &topologyLengths.at(0), &topologyOffsets.at(0), process);
306 }
307 };
308 using namespace vtkPSLACReaderTypes;
309 
310 //------------------------------------------------------------------------------
311 // Simple hash function for vtkIdType.
312 struct vtkPSLACReaderIdTypeHash
313 {
operator ()vtkPSLACReaderIdTypeHash314   size_t operator()(vtkIdType val) const { return static_cast<size_t>(val); }
315 };
316 
317 //=============================================================================
318 vtkObjectFactoryNewMacro(vtkPSLACReader);
319 
320 vtkCxxSetObjectMacro(vtkPSLACReader, Controller, vtkMultiProcessController);
321 
322 //------------------------------------------------------------------------------
323 class vtkPSLACReader::vtkInternal
324 {
325 public:
326   typedef std::unordered_map<vtkIdType, vtkIdType, vtkPSLACReaderIdTypeHash> GlobalToLocalIdType;
327   GlobalToLocalIdType GlobalToLocalIds;
328 
329   // Description:
330   // A map from local point ids to global ids.  Can also be used as the
331   // global point ids.
332   vtkSmartPointer<vtkIdTypeArray> LocalToGlobalIds;
333 
334   // Description:
335   // The point data we expect to receive from each process.
336   vtkSmartPointer<vtkIdTypeArray> PointsExpectedFromProcessesLengths;
337   vtkSmartPointer<vtkIdTypeArray> PointsExpectedFromProcessesOffsets;
338 
339   // Description:
340   // The point data we have to send to each process.  Stored as global ids.
341   vtkSmartPointer<vtkIdTypeArray> PointsToSendToProcesses;
342   vtkSmartPointer<vtkIdTypeArray> PointsToSendToProcessesLengths;
343   vtkSmartPointer<vtkIdTypeArray> PointsToSendToProcessesOffsets;
344 
345   // Description:
346   // The edge data we expect to receive from each process.
347   vtkSmartPointer<vtkIdTypeArray> EdgesExpectedFromProcessesCounts;
348 
349   // Description:
350   // The edge data we have to send to each process.  Stored as global ids.
351   vtkSmartPointer<vtkIdTypeArray> EdgesToSendToProcesses;
352   vtkSmartPointer<vtkIdTypeArray> EdgesToSendToProcessesLengths;
353   vtkSmartPointer<vtkIdTypeArray> EdgesToSendToProcessesOffsets;
354 };
355 
356 //------------------------------------------------------------------------------
vtkPSLACReader()357 vtkPSLACReader::vtkPSLACReader()
358 {
359   this->Controller = nullptr;
360   this->SetController(vtkMultiProcessController::GetGlobalController());
361   if (!this->Controller)
362   {
363     this->SetController(vtkSmartPointer<vtkDummyController>::New());
364   }
365   this->NumberOfPiecesCache = 0;
366   this->RequestedPieceCache = -1;
367 
368   this->PInternal = new vtkPSLACReader::vtkInternal;
369 }
370 
~vtkPSLACReader()371 vtkPSLACReader::~vtkPSLACReader()
372 {
373   this->SetController(nullptr);
374 
375   delete this->PInternal;
376 }
377 
PrintSelf(ostream & os,vtkIndent indent)378 void vtkPSLACReader::PrintSelf(ostream& os, vtkIndent indent)
379 {
380   this->Superclass::PrintSelf(os, indent);
381 
382   if (this->Controller)
383   {
384     os << indent << "Controller: " << this->Controller << endl;
385   }
386   else
387   {
388     os << indent << "Controller: (null)\n";
389   }
390 }
391 
392 //------------------------------------------------------------------------------
RequestInformation(vtkInformation * request,vtkInformationVector ** inputVector,vtkInformationVector * outputVector)393 int vtkPSLACReader::RequestInformation(
394   vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector)
395 {
396   // It would be more efficient to read the meta data on just process 0 and
397   // propgate to the rest.  However, this will probably have a profound effect
398   // only on big jobs accessing parallel file systems.  Until we need that,
399   // I'm not going to bother.
400   if (!this->Superclass::RequestInformation(request, inputVector, outputVector))
401   {
402     return 0;
403   }
404 
405   if (!this->Controller)
406   {
407     vtkErrorMacro(<< "I need a Controller to read the data.");
408     return 0;
409   }
410 
411   for (int i = 0; i < vtkPSLACReader::NUM_OUTPUTS; i++)
412   {
413     vtkInformation* outInfo = outputVector->GetInformationObject(i);
414     outInfo->Set(CAN_HANDLE_PIECE_REQUEST(), 1);
415   }
416 
417   return 1;
418 }
419 
420 //------------------------------------------------------------------------------
RequestData(vtkInformation * request,vtkInformationVector ** inputVector,vtkInformationVector * outputVector)421 int vtkPSLACReader::RequestData(
422   vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector)
423 {
424   // Check to make sure the pieces match the processes.
425   this->RequestedPiece = 0;
426   this->NumberOfPieces = 1;
427   for (int i = 0; i < vtkSLACReader::NUM_OUTPUTS; i++)
428   {
429     vtkInformation* outInfo = outputVector->GetInformationObject(i);
430     if (outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER()) &&
431       outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES()))
432     {
433       this->RequestedPiece = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER());
434       this->NumberOfPieces =
435         outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES());
436       if ((this->RequestedPiece == this->Controller->GetLocalProcessId()) &&
437         (this->NumberOfPieces == this->Controller->GetNumberOfProcesses()))
438       {
439         break;
440       }
441     }
442   }
443 
444   if ((this->RequestedPiece != this->Controller->GetLocalProcessId()) ||
445     (this->NumberOfPieces != this->Controller->GetNumberOfProcesses()))
446   {
447     vtkErrorMacro(<< "Process numbers do not match piece numbers.");
448     return 0;
449   }
450 
451   // RequestData will call other methods that we have overloaded to read
452   // partitioned pieces.
453   int retval = this->Superclass::RequestData(request, inputVector, outputVector);
454 
455   return retval;
456 }
457 
458 //------------------------------------------------------------------------------
ReadTetrahedronInteriorArray(int meshFD,vtkIdTypeArray * connectivity)459 int vtkPSLACReader::ReadTetrahedronInteriorArray(int meshFD, vtkIdTypeArray* connectivity)
460 {
461   int tetInteriorVarId;
462   CALL_NETCDF(nc_inq_varid(meshFD, "tetrahedron_interior", &tetInteriorVarId));
463   vtkIdType numTets = this->GetNumTuplesInVariable(meshFD, tetInteriorVarId, NumPerTetInt);
464 
465   vtkIdType numTetsPerPiece = numTets / this->NumberOfPieces + 1;
466   vtkIdType startTet = this->RequestedPiece * numTetsPerPiece;
467   vtkIdType endTet = startTet + numTetsPerPiece;
468   if (endTet > numTets)
469     endTet = numTets;
470 
471   size_t start[2];
472   size_t count[2];
473 
474   start[0] = startTet;
475   count[0] = endTet - startTet;
476   start[1] = 0;
477   count[1] = NumPerTetInt;
478 
479   connectivity->Initialize();
480   connectivity->SetNumberOfComponents(static_cast<int>(count[1]));
481   connectivity->SetNumberOfTuples(static_cast<vtkIdType>(count[0]));
482   CALL_NETCDF(nc_get_vars_vtkIdType(
483     meshFD, tetInteriorVarId, start, count, nullptr, connectivity->GetPointer(0)));
484 
485   return 1;
486 }
487 
488 //------------------------------------------------------------------------------
ReadTetrahedronExteriorArray(int meshFD,vtkIdTypeArray * connectivity)489 int vtkPSLACReader::ReadTetrahedronExteriorArray(int meshFD, vtkIdTypeArray* connectivity)
490 {
491   int tetExteriorVarId;
492   CALL_NETCDF(nc_inq_varid(meshFD, "tetrahedron_exterior", &tetExteriorVarId));
493   vtkIdType numTets = this->GetNumTuplesInVariable(meshFD, tetExteriorVarId, NumPerTetExt);
494 
495   vtkIdType numTetsPerPiece = numTets / this->NumberOfPieces + 1;
496   vtkIdType startTet = this->RequestedPiece * numTetsPerPiece;
497   vtkIdType endTet = startTet + numTetsPerPiece;
498   if (endTet > numTets)
499     endTet = numTets;
500 
501   size_t start[2];
502   size_t count[2];
503 
504   start[0] = startTet;
505   count[0] = endTet - startTet;
506   start[1] = 0;
507   count[1] = NumPerTetExt;
508 
509   connectivity->Initialize();
510   connectivity->SetNumberOfComponents(static_cast<int>(count[1]));
511   connectivity->SetNumberOfTuples(static_cast<vtkIdType>(count[0]));
512   CALL_NETCDF(nc_get_vars_vtkIdType(
513     meshFD, tetExteriorVarId, start, count, nullptr, connectivity->GetPointer(0)));
514 
515   return 1;
516 }
517 
518 //------------------------------------------------------------------------------
CheckTetrahedraWinding(int meshFD)519 int vtkPSLACReader::CheckTetrahedraWinding(int meshFD)
520 {
521   // Check the file only on the first process and broadcast the result.
522   int winding;
523   if (this->Controller->GetLocalProcessId() == 0)
524   {
525     winding = this->Superclass::CheckTetrahedraWinding(meshFD);
526   }
527   this->Controller->Broadcast(&winding, 1, 0);
528   return winding;
529 }
530 
531 //------------------------------------------------------------------------------
ReadConnectivity(int meshFD,vtkMultiBlockDataSet * surfaceOutput,vtkMultiBlockDataSet * volumeOutput)532 int vtkPSLACReader::ReadConnectivity(
533   int meshFD, vtkMultiBlockDataSet* surfaceOutput, vtkMultiBlockDataSet* volumeOutput)
534 {
535   //---------------------------------
536   // Call the superclass to read the arrays from disk and assemble the
537   // primitives.  The superclass will call the ReadTetrahedron*Array methods,
538   // which we have overridden to read only a partition of the cells.
539   if (!this->Superclass::ReadConnectivity(meshFD, surfaceOutput, volumeOutput))
540   {
541     return 0;
542   }
543 
544   //---------------------------------
545   // Right now, the output only has blocks that are defined by the local piece.
546   // However, downstream components will expect the multiblock structure to be
547   // uniform amongst all processes.  Thus, we correct that problem here by
548   // adding empty blocks for those not in our local piece.
549   SynchronizeBlocks(surfaceOutput, this->Controller, IS_EXTERNAL_SURFACE());
550   SynchronizeBlocks(volumeOutput, this->Controller, IS_INTERNAL_VOLUME());
551 
552   //---------------------------------
553   // This multiblock that contains both outputs provides an easy way to iterate
554   // over all cells in both output.
555   VTK_CREATE(vtkMultiBlockDataSet, compositeOutput);
556   compositeOutput->SetNumberOfBlocks(2);
557   compositeOutput->SetBlock(SURFACE_OUTPUT, surfaceOutput);
558   compositeOutput->SetBlock(VOLUME_OUTPUT, volumeOutput);
559 
560   // ---------------------------------
561   // All the cells have "global" ids.  That is, an index into a global list of
562   // all possible points.  We don't want to have to read in all points in all
563   // processes, so here we are going to figure out what points we need to load
564   // locally, make maps between local and global ids, and convert the ids in the
565   // connectivity arrays from global ids to local ids.
566 
567   this->PInternal->LocalToGlobalIds = vtkSmartPointer<vtkIdTypeArray>::New();
568   this->PInternal->LocalToGlobalIds->SetName("GlobalIds");
569 
570   // Iterate over all points of all cells and mark what points we encounter
571   // in GlobalToLocalIds.
572   this->PInternal->GlobalToLocalIds.clear();
573   vtkSmartPointer<vtkCompositeDataIterator> outputIter;
574   for (outputIter.TakeReference(compositeOutput->NewIterator()); !outputIter->IsDoneWithTraversal();
575        outputIter->GoToNextItem())
576   {
577     vtkUnstructuredGrid* ugrid =
578       vtkUnstructuredGrid::SafeDownCast(compositeOutput->GetDataSet(outputIter));
579     vtkCellArray* cells = ugrid->GetCells();
580 
581     vtkIdType npts;
582     const vtkIdType* pts;
583     for (cells->InitTraversal(); cells->GetNextCell(npts, pts);)
584     {
585       for (vtkIdType i = 0; i < npts; i++)
586       {
587         // The following inserts an entry into the map if one does not exist.
588         // We will assign actual local ids later.
589         this->PInternal->GlobalToLocalIds[pts[i]] = -1;
590       }
591     }
592   }
593 
594   // If we are reading midpoints, record any edges that might require endpoints.
595   std::vector<vtkSLACReader::EdgeEndpoints> edgesNeeded;
596 
597   if (this->ReadMidpoints)
598   {
599     for (outputIter.TakeReference(surfaceOutput->NewIterator()); !outputIter->IsDoneWithTraversal();
600          outputIter->GoToNextItem())
601     {
602       vtkUnstructuredGrid* ugrid =
603         vtkUnstructuredGrid::SafeDownCast(surfaceOutput->GetDataSet(outputIter));
604       vtkCellArray* cells = ugrid->GetCells();
605 
606       vtkIdType npts;
607       const vtkIdType* pts;
608       for (cells->InitTraversal(); cells->GetNextCell(npts, pts);)
609       {
610         for (vtkIdType i = 0; i < npts; i++)
611         {
612           edgesNeeded.emplace_back(pts[i], pts[(i + 1) % npts]);
613         }
614       }
615     }
616   }
617 
618   // ---------------------------------
619   // Now that we know all the global ids we have, create a map from local
620   // to global ids.  First we'll just copy the global ids into the array and
621   // then sort them.  Sorting them will make the global ids monotonically
622   // increasing, which means that when we get data from another process we
623   // can just copy it into a block of memory.  We are only calculating the
624   // local to global id map for now.  We will fill the global to local id
625   // later when we iterate over the local ids.
626   this->PInternal->LocalToGlobalIds->Allocate(
627     static_cast<vtkIdType>(this->PInternal->GlobalToLocalIds.size()));
628   vtkInternal::GlobalToLocalIdType::iterator itr;
629   for (itr = this->PInternal->GlobalToLocalIds.begin();
630        itr != this->PInternal->GlobalToLocalIds.end(); ++itr)
631   {
632     this->PInternal->LocalToGlobalIds->InsertNextValue(itr->first);
633   }
634   vtkSortDataArray::Sort(this->PInternal->LocalToGlobalIds);
635 
636   // ---------------------------------
637   // Now that we have the local to global id maps, we can determine which
638   // process will send what point data where.  This is also where we assign
639   // local ids to global ids (i.e. determine locally where we store each point).
640   this->PInternal->PointsExpectedFromProcessesLengths = vtkSmartPointer<vtkIdTypeArray>::New();
641   this->PInternal->PointsExpectedFromProcessesLengths->SetNumberOfTuples(this->NumberOfPieces);
642   this->PInternal->PointsExpectedFromProcessesOffsets = vtkSmartPointer<vtkIdTypeArray>::New();
643   this->PInternal->PointsExpectedFromProcessesOffsets->SetNumberOfTuples(this->NumberOfPieces);
644   this->PInternal->PointsToSendToProcesses = vtkSmartPointer<vtkIdTypeArray>::New();
645   this->PInternal->PointsToSendToProcessesLengths = vtkSmartPointer<vtkIdTypeArray>::New();
646   this->PInternal->PointsToSendToProcessesLengths->SetNumberOfTuples(this->NumberOfPieces);
647   this->PInternal->PointsToSendToProcessesOffsets = vtkSmartPointer<vtkIdTypeArray>::New();
648   this->PInternal->PointsToSendToProcessesOffsets->SetNumberOfTuples(this->NumberOfPieces);
649 
650   // Record how many global points there are.
651   int coordsVarId;
652   CALL_NETCDF(nc_inq_varid(meshFD, "coords", &coordsVarId));
653   this->NumberOfGlobalPoints = this->GetNumTuplesInVariable(meshFD, coordsVarId, 3);
654 
655   // Iterate over our LocalToGlobalIds map and determine which process reads
656   // which points.  We also fill out GlobalToLocalIds.  Until this point we
657   // only have keys and we need to set the values.
658   vtkIdType localId = 0;
659   vtkIdType numLocalIds = this->PInternal->LocalToGlobalIds->GetNumberOfTuples();
660   for (int process = 0; process < this->NumberOfPieces; process++)
661   {
662     VTK_CREATE(vtkIdTypeArray, pointList);
663     pointList->Allocate(this->NumberOfGlobalPoints / this->NumberOfPieces,
664       this->NumberOfGlobalPoints / this->NumberOfPieces);
665     vtkIdType lastId = this->EndPointRead(process);
666     for (; (localId < numLocalIds); localId++)
667     {
668       vtkIdType globalId = this->PInternal->LocalToGlobalIds->GetValue(localId);
669       if (globalId >= lastId)
670         break;
671       this->PInternal->GlobalToLocalIds[globalId] = localId;
672       pointList->InsertNextValue(globalId);
673     }
674 
675     // pointList now has all the global ids for points that will be loaded by
676     // process.  Send those ids to process so that it knows what data to send
677     // back when reading in point data.
678     vtkIdType numPoints = pointList->GetNumberOfTuples();
679     this->PInternal->PointsExpectedFromProcessesLengths->SetValue(process, numPoints);
680     this->Controller->Gather(&numPoints,
681       this->PInternal->PointsToSendToProcessesLengths->WritePointer(0, this->NumberOfPieces), 1,
682       process);
683     vtkIdType offset = 0;
684     if (process == this->RequestedPiece)
685     {
686       for (int i = 0; i < this->NumberOfPieces; i++)
687       {
688         this->PInternal->PointsToSendToProcessesOffsets->SetValue(i, offset);
689         offset += this->PInternal->PointsToSendToProcessesLengths->GetValue(i);
690       }
691       this->PInternal->PointsToSendToProcesses->SetNumberOfTuples(offset);
692     }
693     this->Controller->GatherV(pointList->GetPointer(0),
694       this->PInternal->PointsToSendToProcesses->WritePointer(0, offset), numPoints,
695       this->PInternal->PointsToSendToProcessesLengths->GetPointer(0),
696       this->PInternal->PointsToSendToProcessesOffsets->GetPointer(0), process);
697   }
698 
699   // Calculate the offsets for the incoming point data into the local array.
700   vtkIdType offset = 0;
701   for (int process = 0; process < this->NumberOfPieces; process++)
702   {
703     this->PInternal->PointsExpectedFromProcessesOffsets->SetValue(process, offset);
704     offset += this->PInternal->PointsExpectedFromProcessesLengths->GetValue(process);
705   }
706 
707   // Now that we have a complete map from global to local ids, modify the
708   // connectivity arrays to use local ids instead of global ids.
709   for (outputIter.TakeReference(compositeOutput->NewIterator()); !outputIter->IsDoneWithTraversal();
710        outputIter->GoToNextItem())
711   {
712     vtkUnstructuredGrid* ugrid =
713       vtkUnstructuredGrid::SafeDownCast(compositeOutput->GetDataSet(outputIter));
714     vtkCellArray* cells = ugrid->GetCells();
715 
716     vtkNew<vtkIdList> cell;
717     auto cellIter = vtk::TakeSmartPointer(cells->NewIterator());
718     for (cellIter->GoToFirstCell(); !cellIter->IsDoneWithTraversal(); cellIter->GoToNextCell())
719     {
720       cellIter->GetCurrentCell(cell);
721       for (vtkIdType i = 0; i < cell->GetNumberOfIds(); i++)
722       {
723         const vtkIdType id = cell->GetId(i);
724         cell->SetId(i, this->PInternal->GlobalToLocalIds[id]);
725       }
726       cellIter->ReplaceCurrentCell(cell);
727     }
728   }
729 
730   if (this->ReadMidpoints)
731   {
732     // Setup the Edge transfers
733     this->PInternal->EdgesExpectedFromProcessesCounts = vtkSmartPointer<vtkIdTypeArray>::New();
734     this->PInternal->EdgesExpectedFromProcessesCounts->SetNumberOfTuples(this->NumberOfPieces);
735     this->PInternal->EdgesToSendToProcesses = vtkSmartPointer<vtkIdTypeArray>::New();
736     this->PInternal->EdgesToSendToProcessesLengths = vtkSmartPointer<vtkIdTypeArray>::New();
737     this->PInternal->EdgesToSendToProcessesLengths->SetNumberOfTuples(this->NumberOfPieces);
738     this->PInternal->EdgesToSendToProcessesOffsets = vtkSmartPointer<vtkIdTypeArray>::New();
739     this->PInternal->EdgesToSendToProcessesOffsets->SetNumberOfTuples(this->NumberOfPieces);
740 
741     std::vector<vtkSmartPointer<vtkIdTypeArray>> edgeLists(this->NumberOfPieces);
742     for (int process = 0; process < this->NumberOfPieces; process++)
743     {
744       edgeLists[process] = vtkSmartPointer<vtkIdTypeArray>::New();
745       edgeLists[process]->SetNumberOfComponents(2);
746     }
747     int pointsPerProcess = this->NumberOfGlobalPoints / this->NumberOfPieces + 1;
748     for (size_t i = 0; i < edgesNeeded.size(); i++)
749     {
750       int process = edgesNeeded[i].GetMinEndPoint() / pointsPerProcess;
751       vtkIdType ids[2];
752       ids[0] = edgesNeeded[i].GetMinEndPoint();
753       ids[1] = edgesNeeded[i].GetMaxEndPoint();
754       edgeLists[process]->InsertNextTypedTuple(static_cast<vtkIdType*>(ids));
755     }
756     for (int process = 0; process < this->NumberOfPieces; process++)
757     {
758       vtkIdType numEdges = edgeLists[process]->GetNumberOfTuples();
759       this->PInternal->EdgesExpectedFromProcessesCounts->SetValue(process, numEdges);
760       this->Controller->Gather(&numEdges,
761         this->PInternal->EdgesToSendToProcessesLengths->WritePointer(0, this->NumberOfPieces), 1,
762         process);
763       offset = 0;
764       if (process == this->RequestedPiece)
765       {
766         for (int i = 0; i < this->NumberOfPieces; i++)
767         {
768           this->PInternal->EdgesToSendToProcessesOffsets->SetValue(i, offset);
769           int len = this->PInternal->EdgesToSendToProcessesLengths->GetValue(i) * 2;
770           this->PInternal->EdgesToSendToProcessesLengths->SetValue(i, len);
771           offset += len;
772         }
773       }
774       this->PInternal->EdgesToSendToProcesses->SetNumberOfComponents(2);
775       this->PInternal->EdgesToSendToProcesses->SetNumberOfTuples(offset / 2);
776       this->Controller->GatherV(edgeLists[process]->GetPointer(0),
777         this->PInternal->EdgesToSendToProcesses->WritePointer(0, offset), numEdges * 2,
778         this->PInternal->EdgesToSendToProcessesLengths->GetPointer(0),
779         this->PInternal->EdgesToSendToProcessesOffsets->GetPointer(0), process);
780     }
781   }
782   return 1;
783 }
784 
785 //------------------------------------------------------------------------------
RestoreMeshCache(vtkMultiBlockDataSet * surfaceOutput,vtkMultiBlockDataSet * volumeOutput,vtkMultiBlockDataSet * compositeOutput)786 int vtkPSLACReader::RestoreMeshCache(vtkMultiBlockDataSet* surfaceOutput,
787   vtkMultiBlockDataSet* volumeOutput, vtkMultiBlockDataSet* compositeOutput)
788 {
789   if (!this->Superclass::RestoreMeshCache(surfaceOutput, volumeOutput, compositeOutput))
790     return 0;
791 
792   // Record the global ids in the point data.
793   vtkPointData* pd =
794     vtkPointData::SafeDownCast(compositeOutput->GetInformation()->Get(vtkSLACReader::POINT_DATA()));
795   pd->SetGlobalIds(this->PInternal->LocalToGlobalIds);
796   pd->SetPedigreeIds(this->PInternal->LocalToGlobalIds);
797 
798   return 1;
799 }
800 
801 //------------------------------------------------------------------------------
ReadPointDataArray(int ncFD,int varId)802 vtkSmartPointer<vtkDataArray> vtkPSLACReader::ReadPointDataArray(int ncFD, int varId)
803 {
804   // Get the dimension info.  We should only need to worry about 1 or 2D arrays.
805   int numDims;
806   CALL_NETCDF(nc_inq_varndims(ncFD, varId, &numDims));
807   if (numDims > 2)
808   {
809     vtkErrorMacro(<< "Sanity check failed.  "
810                   << "Encountered array with too many dimensions.");
811     return nullptr;
812   }
813   if (numDims < 1)
814   {
815     vtkErrorMacro(<< "Sanity check failed.  "
816                   << "Encountered array with *no* dimensions.");
817     return nullptr;
818   }
819   int dimIds[2];
820   CALL_NETCDF(nc_inq_vardimid(ncFD, varId, dimIds));
821   size_t numCoords;
822   CALL_NETCDF(nc_inq_dimlen(ncFD, dimIds[0], &numCoords));
823   if (numCoords != static_cast<size_t>(this->NumberOfGlobalPoints))
824   {
825     vtkErrorMacro(<< "Encountered inconsistent number of coordinates.");
826     return nullptr;
827   }
828   size_t numComponents = 1;
829   if (numDims > 1)
830   {
831     CALL_NETCDF(nc_inq_dimlen(ncFD, dimIds[1], &numComponents));
832   }
833 
834   // Allocate an array of the right type.
835   nc_type ncType;
836   CALL_NETCDF(nc_inq_vartype(ncFD, varId, &ncType));
837   int vtkType = NetCDFTypeToVTKType(ncType);
838   if (vtkType < 1)
839     return nullptr;
840   vtkSmartPointer<vtkDataArray> dataArray;
841   dataArray.TakeReference(vtkDataArray::CreateDataArray(vtkType));
842 
843   // Read the data from the file.
844   size_t start[2], count[2];
845   start[0] = this->StartPointRead(this->RequestedPiece);
846   count[0] = this->EndPointRead(this->RequestedPiece) - start[0];
847   start[1] = 0;
848   count[1] = numComponents;
849   dataArray->SetNumberOfComponents(static_cast<int>(count[1]));
850   dataArray->SetNumberOfTuples(static_cast<vtkIdType>(count[0]));
851   CALL_NETCDF(nc_get_vars(ncFD, varId, start, count, nullptr, dataArray->GetVoidPointer(0)));
852 
853   // We now need to redistribute the data.  Allocate an array to store the final
854   // point data and a buffer to send data to the rest of the processes.
855   vtkSmartPointer<vtkDataArray> finalDataArray;
856   finalDataArray.TakeReference(vtkDataArray::CreateDataArray(vtkType));
857   finalDataArray->SetNumberOfComponents(static_cast<int>(numComponents));
858   finalDataArray->SetNumberOfTuples(this->PInternal->LocalToGlobalIds->GetNumberOfTuples());
859 
860   vtkSmartPointer<vtkDataArray> sendBuffer;
861   sendBuffer.TakeReference(vtkDataArray::CreateDataArray(vtkType));
862   sendBuffer->SetNumberOfComponents(static_cast<int>(numComponents));
863   sendBuffer->SetNumberOfTuples(this->PInternal->PointsToSendToProcesses->GetNumberOfTuples());
864   switch (vtkType)
865   {
866     vtkTemplateMacro(vtkPSLACReaderMapValues1((VTK_TT*)dataArray->GetVoidPointer(0),
867       (VTK_TT*)sendBuffer->GetVoidPointer(0), static_cast<int>(numComponents),
868       this->PInternal->PointsToSendToProcesses, this->StartPointRead(this->RequestedPiece)));
869   }
870 
871   // Scatter expects identifiers per value, not per tuple.  Thus, we (may)
872   // need to adjust the lengths and offsets of what we send.
873   VTK_CREATE(vtkIdTypeArray, sendLengths);
874   sendLengths->SetNumberOfTuples(this->NumberOfPieces);
875   VTK_CREATE(vtkIdTypeArray, sendOffsets);
876   sendOffsets->SetNumberOfTuples(this->NumberOfPieces);
877   for (int i = 0; i < this->NumberOfPieces; i++)
878   {
879     sendLengths->SetValue(i,
880       static_cast<int>(
881         this->PInternal->PointsToSendToProcessesLengths->GetValue(i) * numComponents));
882     sendOffsets->SetValue(i,
883       static_cast<int>(
884         this->PInternal->PointsToSendToProcessesOffsets->GetValue(i) * numComponents));
885   }
886 
887   // Let each process have a turn sending data to the other processes.
888   // Upon receiving
889   for (int proc = 0; proc < this->NumberOfPieces; proc++)
890   {
891     // Scatter data from source.  Note that lengths and offsets are only valid
892     // on the source process.  All others are ignored.
893     vtkIdType destLength = static_cast<vtkIdType>(
894       numComponents * this->PInternal->PointsExpectedFromProcessesLengths->GetValue(proc));
895     vtkIdType destOffset = static_cast<vtkIdType>(
896       numComponents * this->PInternal->PointsExpectedFromProcessesOffsets->GetValue(proc));
897     this->Controller->GetCommunicator()->ScatterVVoidArray(sendBuffer->GetVoidPointer(0),
898       finalDataArray->GetVoidPointer(destOffset), sendLengths->GetPointer(0),
899       sendOffsets->GetPointer(0), destLength, vtkType, proc);
900   }
901 
902   return finalDataArray;
903 }
904 
905 //------------------------------------------------------------------------------
ReadCoordinates(int meshFD,vtkMultiBlockDataSet * output)906 int vtkPSLACReader::ReadCoordinates(int meshFD, vtkMultiBlockDataSet* output)
907 {
908   // The superclass reads everything correctly because it will call our
909   // ReadPointDataArray method, which will properly redistribute points.
910   if (!this->Superclass::ReadCoordinates(meshFD, output))
911     return 0;
912 
913   // This is a convenient place to set the global ids.  Doing this in
914   // ReadFieldData is not a good idea as it might not be called if no mode
915   // file is specified.
916   vtkPointData* pd =
917     vtkPointData::SafeDownCast(output->GetInformation()->Get(vtkSLACReader::POINT_DATA()));
918   pd->SetGlobalIds(this->PInternal->LocalToGlobalIds);
919   pd->SetPedigreeIds(this->PInternal->LocalToGlobalIds);
920 
921   return 1;
922 }
923 
924 //------------------------------------------------------------------------------
ReadFieldData(const int * modeFDArray,int numModeFDs,vtkMultiBlockDataSet * output)925 int vtkPSLACReader::ReadFieldData(
926   const int* modeFDArray, int numModeFDs, vtkMultiBlockDataSet* output)
927 {
928   // The superclass reads everything correctly because it will call our
929   // ReadPointDataArray method, which will properly redistribute points.
930   return this->Superclass::ReadFieldData(modeFDArray, numModeFDs, output);
931 }
932 
933 //------------------------------------------------------------------------------
ReadMidpointCoordinates(int meshFD,vtkMultiBlockDataSet * vtkNotUsed (output),vtkSLACReader::MidpointCoordinateMap & map)934 int vtkPSLACReader::ReadMidpointCoordinates(
935   int meshFD, vtkMultiBlockDataSet* vtkNotUsed(output), vtkSLACReader::MidpointCoordinateMap& map)
936 {
937   // Get the number of midpoints.
938   int midpointsVar;
939   CALL_NETCDF(nc_inq_varid(meshFD, "surface_midpoint", &midpointsVar));
940   this->NumberOfGlobalMidpoints = this->GetNumTuplesInVariable(meshFD, midpointsVar, 5);
941   if (this->NumberOfGlobalMidpoints < 1)
942     return 0;
943 
944   vtkIdType numMidpointsPerPiece = this->NumberOfGlobalMidpoints / this->NumberOfPieces + 1;
945   vtkIdType startMidpoint = this->RequestedPiece * numMidpointsPerPiece;
946   vtkIdType endMidpoint = startMidpoint + numMidpointsPerPiece;
947   if (endMidpoint > this->NumberOfGlobalMidpoints)
948   {
949     endMidpoint = this->NumberOfGlobalMidpoints;
950   }
951 
952   size_t starts[2];
953   size_t counts[2];
954 
955   starts[0] = startMidpoint;
956   counts[0] = endMidpoint - startMidpoint;
957   starts[1] = 0;
958   counts[1] = 5;
959 
960   VTK_CREATE(vtkDoubleArray, midpointData);
961   midpointData->SetNumberOfComponents(static_cast<int>(counts[1]));
962   midpointData->SetNumberOfTuples(static_cast<vtkIdType>(counts[0]));
963   CALL_NETCDF(
964     nc_get_vars_double(meshFD, midpointsVar, starts, counts, nullptr, midpointData->GetPointer(0)));
965 
966   // Collect the midpoints we've read on the processes that originally read the
967   // corresponding main points (the edge the midpoint is on).  These original
968   // processes are aware of who requested hose original points.  Thus they can
969   // redistribute the midpoints that correspond to those processes that
970   // requested the original points.
971   std::vector<midpointListsType> midpointsToDistribute(this->NumberOfPieces);
972 
973   int pointsPerProcess = this->NumberOfGlobalPoints / this->NumberOfPieces + 1;
974   for (vtkIdType i = 0; i < midpointData->GetNumberOfTuples(); i++)
975   {
976     double* mp = midpointData->GetPointer(i * 5);
977 
978     midpointPositionType position;
979     position.coord[0] = mp[2];
980     position.coord[1] = mp[3];
981     position.coord[2] = mp[4];
982 
983     midpointTopologyType topology;
984     topology.minEdgePoint = static_cast<vtkIdType>(vtkMath::Min(mp[0], mp[1]));
985     topology.maxEdgePoint = static_cast<vtkIdType>(vtkMath::Max(mp[0], mp[1]));
986     topology.globalId = i + startMidpoint + this->NumberOfGlobalPoints;
987 
988     // find the processor the minimum edge point belongs to (by global id)
989     vtkIdType process = topology.minEdgePoint / pointsPerProcess;
990 
991     // insert the midpoint's global point id into the data
992     midpointsToDistribute[process].position.push_back(position);
993     midpointsToDistribute[process].topology.push_back(topology);
994   }
995 
996   midpointListsType midpointsToRedistribute;
997   for (int process = 0; process < this->NumberOfPieces; process++)
998   {
999     GatherMidpoints(
1000       this->Controller, midpointsToDistribute[process], midpointsToRedistribute, process);
1001   }
1002 
1003   // Build a map of midpoints so that as processes request midpoints we can
1004   // quickly find them.
1005   MidpointsAvailableType MidpointsAvailable;
1006 
1007   std::vector<midpointPositionType>::iterator posIter;
1008   std::vector<midpointTopologyType>::iterator topIter;
1009   for (posIter = midpointsToRedistribute.position.begin(),
1010       topIter = midpointsToRedistribute.topology.begin();
1011        posIter != midpointsToRedistribute.position.end(); ++posIter, ++topIter)
1012   {
1013     midpointPointersType mp;
1014     mp.position = &(*posIter);
1015     mp.topology = &(*topIter);
1016 #ifdef _RWSTD_NO_MEMBER_TEMPLATES
1017     // Deal with Sun Studio old libCstd.
1018     // http://sahajtechstyle.blogspot.com/2007/11/whats-wrong-with-sun-studio-c.html
1019     MidpointsAvailable.insert(std::pair<const EdgeEndpoints, midpointPointersType>(
1020       EdgeEndpoints(topIter->minEdgePoint, topIter->maxEdgePoint), mp));
1021 #else
1022     MidpointsAvailable.insert(
1023       std::make_pair(EdgeEndpoints(topIter->minEdgePoint, topIter->maxEdgePoint), mp));
1024 #endif
1025   }
1026 
1027   // For each process, find the midpoints we need to send there and then
1028   // send them with a gather operation.
1029   midpointListsType midpointsToReceive;
1030   for (int process = 0; process < this->NumberOfPieces; process++)
1031   {
1032     vtkIdType start = this->PInternal->EdgesToSendToProcessesOffsets->GetValue(process);
1033     vtkIdType end = start + this->PInternal->EdgesToSendToProcessesLengths->GetValue(process);
1034 
1035     start /= this->PInternal->EdgesToSendToProcesses->GetNumberOfComponents();
1036     end /= this->PInternal->EdgesToSendToProcesses->GetNumberOfComponents();
1037 
1038     // FIXME: There seems to be a bug somewhere that results in the
1039     // EdgesToSendToProcesses array to be empty, while the corresponding
1040     // Offsets and Lengths arrays are not. This only happens on some processes,
1041     // and the PSLAC unit tests still pass. The bit below prevents invalid
1042     // memory accesses when this occurs.
1043     if (this->PInternal->EdgesToSendToProcesses->GetNumberOfTuples() == 0 &&
1044       this->PInternal->EdgesToSendToProcessesOffsets->GetNumberOfTuples() != 0)
1045     {
1046       vtkWarningMacro("Inconsistent reader state detected. Skipping midpoint "
1047                       "sync.");
1048       end = start = 0;
1049     }
1050 
1051     midpointListsType midpointsToSend;
1052     for (vtkIdType i = start; i < end; i++)
1053     {
1054       MidpointsAvailableType::const_iterator iter;
1055       vtkIdType e[2];
1056       this->PInternal->EdgesToSendToProcesses->GetTypedTuple(i, e);
1057       iter = MidpointsAvailable.find(EdgeEndpoints(e[0], e[1]));
1058       if (iter != MidpointsAvailable.end())
1059       {
1060         midpointsToSend.position.push_back(*iter->second.position);
1061         midpointsToSend.topology.push_back(*iter->second.topology);
1062       }
1063       else // in order to have the proper length we must insert empty.
1064       {
1065         midpointPositionType position;
1066         position.coord[0] = -1;
1067         position.coord[1] = -1;
1068         position.coord[2] = -1;
1069         midpointTopologyType topology;
1070         topology.minEdgePoint = -1;
1071         topology.maxEdgePoint = -1;
1072         topology.globalId = -1;
1073         midpointsToSend.position.push_back(position);
1074         midpointsToSend.topology.push_back(topology);
1075       }
1076     }
1077 
1078     GatherMidpoints(this->Controller, midpointsToSend, midpointsToReceive, process);
1079   }
1080 
1081   // finally, we have all midpoints that correspond to edges we know about
1082   // convert their edge points to localId and insert into the map and return.
1083   typedef std::unordered_map<vtkIdType, vtkIdType, vtkPSLACReaderIdTypeHash> localMapType;
1084   localMapType localMap;
1085   for (posIter = midpointsToReceive.position.begin(), topIter = midpointsToReceive.topology.begin();
1086        posIter != midpointsToReceive.position.end(); ++posIter, ++topIter)
1087   {
1088     if (topIter->globalId < 0)
1089       continue;
1090 
1091     vtkIdType local0 = this->PInternal->GlobalToLocalIds[topIter->minEdgePoint];
1092     vtkIdType local1 = this->PInternal->GlobalToLocalIds[topIter->maxEdgePoint];
1093     localMapType::const_iterator iter;
1094     iter = localMap.find(topIter->globalId);
1095     vtkIdType index;
1096     if (iter == localMap.end())
1097     {
1098       index = this->PInternal->LocalToGlobalIds->InsertNextTypedTuple(&topIter->globalId);
1099       localMap[topIter->globalId] = index;
1100     }
1101     else
1102     {
1103       index = iter->second;
1104     }
1105     map.AddMidpoint(vtkSLACReader::EdgeEndpoints(local0, local1),
1106       vtkSLACReader::MidpointCoordinates(posIter->coord, index));
1107   }
1108   return 1;
1109 }
1110 
1111 //------------------------------------------------------------------------------
ReadMidpointData(int meshFD,vtkMultiBlockDataSet * output,vtkSLACReader::MidpointIdMap & map)1112 int vtkPSLACReader::ReadMidpointData(
1113   int meshFD, vtkMultiBlockDataSet* output, vtkSLACReader::MidpointIdMap& map)
1114 {
1115   int result = this->Superclass::ReadMidpointData(meshFD, output, map);
1116   if (result != 1)
1117   {
1118     return result;
1119   }
1120   // add global IDs for midpoints added that weren't in the file
1121   vtkPoints* points =
1122     vtkPoints::SafeDownCast(output->GetInformation()->Get(vtkSLACReader::POINTS()));
1123   vtkIdType pointsAdded =
1124     points->GetNumberOfPoints() - this->PInternal->LocalToGlobalIds->GetNumberOfTuples();
1125   // Use the maximum number of points added so that the offsets don't overlap
1126   // There will be gaps and shared edges between two processes will get different ids
1127   // TODO: Will this cause problems?
1128   vtkIdType maxPointsAdded;
1129   this->Controller->AllReduce(&pointsAdded, &maxPointsAdded, 1, vtkCommunicator::MAX_OP);
1130 
1131   vtkIdType start = this->NumberOfGlobalPoints + this->NumberOfGlobalMidpoints +
1132     this->RequestedPiece * maxPointsAdded;
1133   vtkIdType end = start + pointsAdded;
1134   for (vtkIdType i = start; i < end; i++)
1135   {
1136     this->PInternal->LocalToGlobalIds->InsertNextTypedTuple(&i);
1137   }
1138 
1139   return 1;
1140 }
1141 
1142 //------------------------------------------------------------------------------
MeshUpToDate()1143 int vtkPSLACReader::MeshUpToDate()
1144 {
1145   int localflag = this->Superclass::MeshUpToDate();
1146   localflag &= (this->NumberOfPieces != this->NumberOfPiecesCache);
1147   localflag &= (this->RequestedPieceCache != this->RequestedPiece);
1148 
1149   int globalflag;
1150   this->Controller->AllReduce(&localflag, &globalflag, 1, vtkCommunicator::LOGICAL_AND_OP);
1151   return globalflag;
1152 }
1153