--- /dev/null
+/*=========================================================================
+
+ Program: Visualization Toolkit
+ Module: vtkPUnstructuredGridGhostCellsGenerator.cxx
+
+ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+ All rights reserved.
+ See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+
+#include "vtkPUnstructuredGridGhostCellsGenerator.h"
+
+#include "vtkCellArray.h"
+#include "vtkCharArray.h"
+#include "vtkDataSetSurfaceFilter.h"
+#include "vtkExtractCells.h"
+#include "vtkIdList.h"
+#include "vtkIdTypeArray.h"
+#include "vtkInformation.h"
+#include "vtkInformationVector.h"
+#include "vtkMergeCells.h"
+#include "vtkMergePoints.h"
+#include "vtkMPICommunicator.h"
+#include "vtkMultiProcessController.h"
+#include "vtkNew.h"
+#include "vtkObjectFactory.h"
+#include "vtkPointData.h"
+#include "vtkPoints.h"
+#include "vtkStreamingDemandDrivenPipeline.h"
+#include "vtkUnstructuredGrid.h"
+
+#include <map>
+#include <set>
+#include <vector>
+
+//----------------------------------------------------------------------------
+// Helpers
+namespace
+{
+template<class T>
+bool AllGatherV(vtkMultiProcessController* controller,
+ const T* localV,
+ vtkIdType localSize,
+ std::vector<T>& globalV,
+ std::vector<vtkIdType>& sizes,
+ std::vector<vtkIdType>& offsets)
+{
+ int nbOfRanks = controller->GetNumberOfProcesses();
+ sizes.resize(nbOfRanks);
+ int ret = controller->AllGather(&localSize, &sizes[0], 1);
+ if (ret == 0)
+ {
+ vtkErrorWithObjectMacro(controller, << "Communication error!");
+ return false;
+ }
+ vtkIdType count = 0;
+ offsets.resize(nbOfRanks);
+ for (int i = 0; i < nbOfRanks; i++)
+ {
+ offsets[i] = count;
+ count += sizes[i];
+ }
+ globalV.resize(count);
+ if (count > 0)
+ {
+ controller->AllGatherV(localSize > 0 ? localV : 0,
+ &globalV[0], localSize, &sizes[0], &offsets[0]);
+ }
+ return true;
+}
+}
+
+//----------------------------------------------------------------------------
+// Internal data structures
+
+// Class to hold asynchronous communication information
+class CommDataInfo
+{
+public:
+ CommDataInfo() : SendLen(-1), RecvLen(-1), CommStep(0)
+ {
+ this->SendBuffer = vtkCharArray::New();
+ this->RecvBuffer = vtkCharArray::New();
+ }
+
+ CommDataInfo(const CommDataInfo& c)
+ {
+ *this = c;
+ if (this->SendBuffer) { this->SendBuffer->Register(0); }
+ if (this->RecvBuffer) { this->RecvBuffer->Register(0); }
+ }
+
+ ~CommDataInfo()
+ {
+ if (this->SendBuffer) { this->SendBuffer->Delete(); }
+ if (this->RecvBuffer) { this->RecvBuffer->Delete(); }
+ }
+
+ vtkMPICommunicator::Request SendReqs[2];
+ vtkMPICommunicator::Request RecvReqs[2];
+ vtkCharArray *SendBuffer;
+ vtkCharArray *RecvBuffer;
+ vtkIdType SendLen;
+ vtkIdType RecvLen;
+ int CommStep;
+};
+
+// Communication arrays
+struct vtkPUnstructuredGridGhostCellsGenerator::vtkInternals
+{
+ // For global ids
+ std::map<vtkIdType, vtkIdType> GlobalToLocalPointIdMap;
+ std::vector<vtkIdType> AllGlobalIdsOfSurfacePoints;
+
+ // For point coordinates
+ vtkNew<vtkMergePoints> LocalPoints;
+ std::vector<vtkIdType> LocalPointsMap;
+ std::vector<double> AllPointsOfSurfacePoints;
+
+ std::vector<vtkIdType> AllSizes;
+ std::vector<vtkIdType> AllOffsets;
+ std::map<int, std::set<vtkIdType> > NeighborRanksCells;
+ std::map<int, CommDataInfo> CommData;
+ vtkUnstructuredGridBase* Input;
+
+ vtkDataArray* InputGlobalPointIds;
+ bool UseGlobalPointIds;
+};
+
+static const int UGGCG_SIZE_EXCHANGE_TAG = 9000;
+static const int UGGCG_DATA_EXCHANGE_TAG = 9001;
+static const char* UGGCG_GLOBAL_POINT_IDS = "GlobalNodeIds";
+
+//----------------------------------------------------------------------------
+
+vtkStandardNewMacro(vtkPUnstructuredGridGhostCellsGenerator)
+vtkSetObjectImplementationMacro(
+ vtkPUnstructuredGridGhostCellsGenerator, Controller, vtkMultiProcessController);
+
+//----------------------------------------------------------------------------
+vtkPUnstructuredGridGhostCellsGenerator::vtkPUnstructuredGridGhostCellsGenerator()
+{
+ this->Controller = NULL;
+ this->SetController(vtkMultiProcessController::GetGlobalController());
+
+ this->Internals = NULL;
+ this->GlobalPointIdsArrayName = NULL;
+ this->UseGlobalPointIds = true;
+ this->SetGlobalPointIdsArrayName(UGGCG_GLOBAL_POINT_IDS);
+}
+
+//----------------------------------------------------------------------------
+vtkPUnstructuredGridGhostCellsGenerator::~vtkPUnstructuredGridGhostCellsGenerator()
+{
+ this->SetController(NULL);
+ this->SetGlobalPointIdsArrayName(NULL);
+
+ delete this->Internals;
+ this->Internals = 0;
+}
+
+//-----------------------------------------------------------------------------
+void vtkPUnstructuredGridGhostCellsGenerator::PrintSelf(ostream& os, vtkIndent indent)
+{
+ Superclass::PrintSelf(os, indent);
+}
+
+//-----------------------------------------------------------------------------
+int vtkPUnstructuredGridGhostCellsGenerator::RequestData(
+ vtkInformation *vtkNotUsed(request),
+ vtkInformationVector **inputVector,
+ vtkInformationVector *outputVector)
+{
+ // get the info objects
+ vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
+ vtkInformation *outInfo = outputVector->GetInformationObject(0);
+
+ // get the input and output. Input may just have the UnstructuredGridBase
+ // interface, but output should be an unstructured grid.
+ vtkUnstructuredGridBase *input = vtkUnstructuredGridBase::SafeDownCast(
+ inInfo->Get(vtkDataObject::DATA_OBJECT()));
+ vtkUnstructuredGrid *output = vtkUnstructuredGrid::SafeDownCast(
+ outInfo->Get(vtkDataObject::DATA_OBJECT()));
+
+ int ghostLevels = 1;
+ //ghostLevels = outInfo->Get(
+ // vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS());
+
+ if (!this->Controller)
+ {
+ this->Controller = vtkMultiProcessController::GetGlobalController();
+ }
+ output->Reset();
+
+ this->NumRanks = this->Controller ? this->Controller->GetNumberOfProcesses() : 1;
+ this->RankId = this->Controller ? this->Controller->GetLocalProcessId() : 0;
+ if (ghostLevels == 0 || !this->Controller || this->NumRanks == 1)
+ {
+ // Ghost levels are not requested. Nothing to do but pass the dataset.
+ if (this->RankId == 0)
+ {
+ vtkWarningMacro(<< "Ghost cells not requested or not needed.");
+ }
+ output->ShallowCopy(input);
+ return 1;
+ }
+
+ delete this->Internals;
+ this->Internals = new vtkPUnstructuredGridGhostCellsGenerator::vtkInternals();
+ this->Internals->Input = input;
+
+ vtkPointData *inputPD = input->GetPointData();
+ this->Internals->InputGlobalPointIds = inputPD->GetGlobalIds();
+
+ if (!this->Internals->InputGlobalPointIds)
+ {
+ this->Internals->InputGlobalPointIds =
+ inputPD->GetArray(this->GlobalPointIdsArrayName);
+ inputPD->SetGlobalIds(this->Internals->InputGlobalPointIds);
+ }
+
+ if (!this->UseGlobalPointIds)
+ {
+ this->Internals->InputGlobalPointIds = NULL;
+ }
+
+ this->ExtractAndReduceSurfacePoints();
+ this->UpdateProgress(0.3);
+
+ this->ComputeSharedPoints();
+ this->UpdateProgress(0.6);
+
+ this->ExtractAndSendGhostCells();
+ this->UpdateProgress(0.8);
+
+ this->ReceiveAndMergeGhostCells(output);
+ this->UpdateProgress(1.0);
+
+ output->GetInformation()->Set(vtkDataObject::DATA_NUMBER_OF_GHOST_LEVELS(), 1);
+
+ this->Controller->Barrier();
+
+ delete this->Internals;
+ this->Internals = 0;
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Step 1: Extract surface geometry and all reduce global ids of surface points
+void vtkPUnstructuredGridGhostCellsGenerator::ExtractAndReduceSurfacePoints()
+{
+ // Extract boundary cells and points with the surface filter
+ vtkNew<vtkDataSetSurfaceFilter> surfaceFilter;
+ surfaceFilter->SetInputData(this->Internals->Input);
+ surfaceFilter->PassThroughPointIdsOn();
+ surfaceFilter->Update();
+
+ vtkPolyData* surface = surfaceFilter->GetOutput();
+ vtkIdType nbSurfacePoints = surface->GetNumberOfPoints();
+ vtkCellArray* surfaceCells = surface->GetPolys();
+ surfaceCells->InitTraversal();
+ vtkIdType npts, *pts;
+
+ vtkIdTypeArray* surfaceOriginalPointIds = vtkIdTypeArray::SafeDownCast(
+ surface->GetPointData()->GetArray(surfaceFilter->GetOriginalPointIdsName()));
+
+ if (this->Internals->InputGlobalPointIds)
+ {
+ std::vector<vtkIdType> globalIdsOfSurfacePoints;
+ globalIdsOfSurfacePoints.reserve(nbSurfacePoints);
+
+ // Browse surface cells and save global and local ids of cell points
+ while (surfaceCells->GetNextCell(npts, pts))
+ {
+ for (vtkIdType i = 0; i < npts; i++)
+ {
+ vtkIdType origPtId = surfaceOriginalPointIds->GetValue(pts[i]);
+ vtkIdType globalPtId = static_cast<vtkIdType>(
+ this->Internals->InputGlobalPointIds->GetTuple1(origPtId));
+
+ if (this->Internals->GlobalToLocalPointIdMap.find(globalPtId) ==
+ this->Internals->GlobalToLocalPointIdMap.end())
+ {
+ this->Internals->GlobalToLocalPointIdMap[globalPtId] = origPtId;
+ globalIdsOfSurfacePoints.push_back(globalPtId);
+ }
+ }
+ }
+
+ // Now reduce surface point global ids on ALL ranks
+ ::AllGatherV(this->Controller, &globalIdsOfSurfacePoints[0],
+ globalIdsOfSurfacePoints.size(),
+ this->Internals->AllGlobalIdsOfSurfacePoints,
+ this->Internals->AllSizes, this->Internals->AllOffsets);
+ }
+ else
+ {
+ // We can't use global ids, so we will process point coordinates instead
+ vtkPoints* inputPoints = this->Internals->Input->GetPoints();
+ vtkNew<vtkPoints> surfacePoints;
+ surfacePoints->SetDataTypeToDouble();
+ surfacePoints->Allocate(nbSurfacePoints);
+ this->Internals->LocalPoints->InitPointInsertion(
+ surfacePoints.Get(), surface->GetPoints()->GetBounds());
+ this->Internals->LocalPointsMap.reserve(nbSurfacePoints);
+
+ // Browse surface cells and push point coordinates to the locator
+ while (surfaceCells->GetNextCell(npts, pts))
+ {
+ for (vtkIdType i = 0; i < npts; i++)
+ {
+ vtkIdType origPtId = surfaceOriginalPointIds->GetValue(pts[i]);
+ double p[3];
+ inputPoints->GetPoint(origPtId, p);
+ vtkIdType sid;
+ if (this->Internals->LocalPoints->InsertUniquePoint(p, sid))
+ {
+ // New point, save the id of the original grid point id associated
+ // to this surface point
+ if (static_cast<vtkIdType>(this->Internals->LocalPointsMap.size()) <= sid)
+ {
+ this->Internals->LocalPointsMap.resize(sid + 1);
+ }
+ this->Internals->LocalPointsMap[sid] = origPtId;
+ }
+ }
+ }
+
+ // Now reduce surface point coordinates on ALL ranks
+ ::AllGatherV(this->Controller,
+ (double*)surfacePoints->GetVoidPointer(0),
+ surfacePoints->GetNumberOfPoints() * 3,
+ this->Internals->AllPointsOfSurfacePoints,
+ this->Internals->AllSizes, this->Internals->AllOffsets);
+ }
+}
+
+//---------------------------------------------------------------------------
+// Step 2: browse global ids/point coordinates of other ranks and check if some
+// are duplicated locally.
+// For each neighbor rank, save the ids of the cells adjacent to the surface
+// points shared, those cells are the ghost cells we will send them.
+void vtkPUnstructuredGridGhostCellsGenerator::ComputeSharedPoints()
+{
+ vtkNew<vtkIdList> cellIdsList;
+ for (int i = 0; i < this->NumRanks; i++)
+ {
+ if (i == this->RankId)
+ {
+ continue;
+ }
+ for (vtkIdType j = 0, idx = this->Internals->AllOffsets[i];
+ j < this->Internals->AllSizes[i]; j++, idx++)
+ {
+ vtkIdType localPointId = -1;
+ if (this->Internals->InputGlobalPointIds)
+ {
+ // Check if this point exists locally from its global ids, if so
+ // get its local id.
+ vtkIdType gid = this->Internals->AllGlobalIdsOfSurfacePoints[idx];
+ std::map<vtkIdType, vtkIdType>::iterator iter =
+ this->Internals->GlobalToLocalPointIdMap.find(gid);
+ if (iter != this->Internals->GlobalToLocalPointIdMap.end())
+ {
+ localPointId = iter->second;
+ }
+ }
+ else
+ {
+ // Check if this point exists locally from its coordinates, if so
+ // get its local id.
+ double *p = &this->Internals->AllPointsOfSurfacePoints[idx];
+ localPointId = this->Internals->LocalPoints->IsInsertedPoint(p);
+
+ if (localPointId != -1)
+ {
+ localPointId = this->Internals->LocalPointsMap[localPointId];
+ }
+ idx += 2; // jump to next coordinates
+ j += 2;
+ }
+
+ if (localPointId != -1)
+ {
+ // Current rank also has a copy of this global point
+ cellIdsList->Reset();
+ // Get the cells connected to this point
+ this->Internals->Input->GetPointCells(localPointId, cellIdsList.Get());
+ vtkIdType nbIds = cellIdsList->GetNumberOfIds();
+ // Add those cells to the list of cells to send to this rank
+ for (vtkIdType k = 0; k < nbIds; k++)
+ {
+ this->Internals->NeighborRanksCells[i].insert(cellIdsList->GetId(k));
+ }
+ }
+ }
+ }
+
+ // Release memory of all reduced arrays
+ this->Internals->AllGlobalIdsOfSurfacePoints.resize(0);
+ this->Internals->AllPointsOfSurfacePoints.resize(0);
+ this->Internals->AllSizes.resize(0);
+ this->Internals->AllOffsets.resize(0);
+ // Now we know our neighbors and which points we have in common and the
+ // ghost cells to share.
+}
+
+//-----------------------------------------------------------------------------
+// Step 3: extract and send the ghost cells to the neighbor ranks
+void vtkPUnstructuredGridGhostCellsGenerator::ExtractAndSendGhostCells()
+{
+ vtkNew<vtkIdList> cellIdsList;
+ vtkNew<vtkExtractCells> extractCells;
+ extractCells->SetInputData(this->Internals->Input);
+ std::map<int, std::set<vtkIdType> >::iterator iter =
+ this->Internals->NeighborRanksCells.begin();
+
+ vtkMPICommunicator* com =
+ vtkMPICommunicator::SafeDownCast(this->Controller->GetCommunicator());
+
+ // Browse all neighbor ranks and extract the cells connected to the points we share
+ for (; iter != this->Internals->NeighborRanksCells.end(); ++iter)
+ {
+ int toRank = iter->first;
+ std::set<vtkIdType>& cellsToShare = iter->second;
+ cellIdsList->SetNumberOfIds(cellsToShare.size());
+ std::set<vtkIdType>::iterator sIter = cellsToShare.begin();
+ for (vtkIdType i = 0; sIter != cellsToShare.end(); ++sIter, i++)
+ {
+ cellIdsList->SetId(i, *sIter);
+ }
+ extractCells->SetCellList(cellIdsList.Get());
+ extractCells->Update();
+ vtkUnstructuredGrid* extractGrid = extractCells->GetOutput();
+
+ // Send the extracted grid to the neighbor rank asynchronously
+ CommDataInfo& c = this->Internals->CommData[toRank];
+ if (vtkCommunicator::MarshalDataObject(extractGrid, c.SendBuffer))
+ {
+ c.SendLen = c.SendBuffer->GetNumberOfTuples();
+ // Send data length
+ com->NoBlockSend(&c.SendLen, 1, toRank, UGGCG_SIZE_EXCHANGE_TAG, c.SendReqs[0]);
+ // Send raw data
+ com->NoBlockSend((char*)c.SendBuffer->GetVoidPointer(0), c.SendLen, toRank,
+ UGGCG_DATA_EXCHANGE_TAG, c.SendReqs[1]);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Step 4: receive the ghost cells from the neighbor ranks and merge them
+// to the local grid.
+void vtkPUnstructuredGridGhostCellsGenerator::ReceiveAndMergeGhostCells(
+ vtkUnstructuredGrid *output)
+{
+ // We need to compute a rough estimation of the total number of cells and
+ // points for vtkMergeCells
+ vtkIdType totalNbCells = this->Internals->Input->GetNumberOfCells();
+ vtkIdType totalNbPoints = this->Internals->Input->GetNumberOfPoints();
+
+ vtkMPICommunicator* com =
+ vtkMPICommunicator::SafeDownCast(this->Controller->GetCommunicator());
+
+ // Browse all neighbor ranks and receive the mesh that contains cells
+ int nbNeighbors = static_cast<int>(this->Internals->NeighborRanksCells.size());
+ std::vector<vtkUnstructuredGridBase*> neighborGrids;
+ neighborGrids.reserve(nbNeighbors);
+
+ // First create requests to receive the size of the mesh to receive
+ std::map<int, std::set<vtkIdType> >::iterator iter;
+ for (iter = this->Internals->NeighborRanksCells.begin();
+ iter != this->Internals->NeighborRanksCells.end(); ++iter)
+ {
+ vtkIdType fromRank = iter->first;
+ CommDataInfo& c = this->Internals->CommData[fromRank];
+ com->NoBlockReceive(
+ &c.RecvLen, 1, fromRank, UGGCG_SIZE_EXCHANGE_TAG, c.RecvReqs[0]);
+ }
+
+ // Then, once the data length is received, create requests to receive the mesh data
+ int counter = 0;
+ while (counter != nbNeighbors)
+ {
+ for (iter = this->Internals->NeighborRanksCells.begin();
+ iter != this->Internals->NeighborRanksCells.end(); ++iter)
+ {
+ vtkIdType fromRank = iter->first;
+ CommDataInfo& c = this->Internals->CommData[fromRank];
+ if (!c.RecvReqs[0].Test() || c.CommStep != 0)
+ {
+ continue;
+ }
+ c.CommStep = 1;
+ c.RecvBuffer->SetNumberOfValues(c.RecvLen);
+ com->NoBlockReceive(
+ (char*)c.RecvBuffer->GetVoidPointer(0), c.RecvLen, fromRank,
+ UGGCG_DATA_EXCHANGE_TAG, c.RecvReqs[1]);
+ counter++;
+ }
+ }
+
+ // Browse all neighbor ranks and receive the mesh that contains cells
+ // that are ghost cells for current rank.
+ counter = 0;
+ while (counter != nbNeighbors)
+ {
+ for (iter = this->Internals->NeighborRanksCells.begin();
+ iter != this->Internals->NeighborRanksCells.end(); ++iter)
+ {
+ vtkIdType fromRank = iter->first;
+ CommDataInfo& c = this->Internals->CommData[fromRank];
+
+ if (!c.RecvReqs[1].Test() || c.CommStep != 1)
+ {
+ continue;
+ }
+
+ c.CommStep = 2;
+ vtkUnstructuredGrid* grid = vtkUnstructuredGrid::New();
+ vtkCommunicator::UnMarshalDataObject(c.RecvBuffer, grid);
+ c.RecvBuffer->Delete();
+ c.RecvBuffer = NULL;
+
+ // Flag the received grid elements as ghosts
+ grid->AllocatePointGhostArray();
+ grid->AllocateCellGhostArray();
+ grid->GetPointGhostArray()->FillComponent(0, 1);
+ grid->GetCellGhostArray()->FillComponent(0, 1);
+
+ // Make sure the global point ids array is tagged accordingly
+ if (this->Internals->InputGlobalPointIds &&
+ !grid->GetPointData()->GetGlobalIds())
+ {
+ grid->GetPointData()->SetGlobalIds(grid->GetPointData()->GetArray(
+ this->Internals->InputGlobalPointIds->GetName()));
+ }
+
+ totalNbCells += grid->GetNumberOfCells();
+ totalNbPoints += grid->GetNumberOfPoints();
+
+ neighborGrids.push_back(grid);
+
+ counter++;
+ }
+ }
+
+ // Shallow copy the input grid and initialize the ghost arrays
+ vtkNew<vtkUnstructuredGrid> inputCopy;
+ inputCopy->ShallowCopy(this->Internals->Input);
+ inputCopy->AllocatePointGhostArray();
+ inputCopy->AllocateCellGhostArray();
+
+ // MergeCells merge input + grids that contains ghost cells to the output grid
+ vtkNew<vtkMergeCells> mergeCells;
+ mergeCells->SetUnstructuredGrid(output);
+ mergeCells->SetTotalNumberOfCells(totalNbCells);
+ mergeCells->SetTotalNumberOfPoints(totalNbPoints);
+ mergeCells->SetTotalNumberOfDataSets(
+ 1 + static_cast<int>(this->Internals->NeighborRanksCells.size()));
+ mergeCells->SetUseGlobalIds(this->Internals->InputGlobalPointIds != 0 ? 1 : 0);
+ mergeCells->SetPointMergeTolerance(0.0);
+
+ // Merge input grid first
+ mergeCells->MergeDataSet(inputCopy.Get());
+
+ // Then merge ghost grid from neighbor rank
+ for (std::size_t i = 0; i < neighborGrids.size(); i++)
+ {
+ mergeCells->MergeDataSet(neighborGrids[i]);
+ neighborGrids[i]->Delete();
+ }
+
+ // Finalize the merged output
+ mergeCells->Finish();
+}