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