diff --git a/IO/Geometry/vtkOpenFOAMReader.cxx b/IO/Geometry/vtkOpenFOAMReader.cxx index 26b2324484164ddb413bed6cc218af7688c32067..16944b1557a6b43a4c5acfd3e535369428a24bed 100644 --- a/IO/Geometry/vtkOpenFOAMReader.cxx +++ b/IO/Geometry/vtkOpenFOAMReader.cxx @@ -1121,22 +1121,21 @@ private: bool ListTimeDirectoriesByInstances(); // Read polyMesh/points (vectorField) - vtkSmartPointer<vtkFloatArray> ReadPointsFile(); + vtkSmartPointer<vtkFloatArray> ReadPointsFile(const std::string& meshDir); // Read polyMesh/faces (faceCompactList or faceList) std::unique_ptr<vtkFoamLabelListList> ReadFacesFile(const std::string& meshDir); - // Read polyMesh/{owner,neighbour}, check overall number of faces. Return meshCells - std::unique_ptr<vtkFoamLabelListList> ReadOwnerNeighbourFiles(const std::string& meshDir); + // Read polyMesh/{owner,neighbour}, check overall number of faces. + bool ReadOwnerNeighbourFiles(const std::string& meshDir); // Create meshCells from owner/neighbour information - std::unique_ptr<vtkFoamLabelListList> CreateCellFaces( - const vtkDataArray& faceOwner, const vtkDataArray& faceNeigh); + std::unique_ptr<vtkFoamLabelListList> CreateCellFaces(); bool CheckFaceList(const vtkFoamLabelListList& faces); // Create volume mesh - void InsertCellsToGrid(vtkUnstructuredGrid*, const vtkFoamLabelListList& meshCells, + void InsertCellsToGrid(vtkUnstructuredGrid*, std::unique_ptr<vtkFoamLabelListList>& meshCellsPtr, const vtkFoamLabelListList& meshFaces, vtkIdList* cellLabels = nullptr #if VTK_FOAMFILE_DECOMPOSE_POLYHEDRA , @@ -1144,7 +1143,7 @@ private: #endif ); - vtkUnstructuredGrid* MakeInternalMesh(const vtkFoamLabelListList& meshCells, + vtkUnstructuredGrid* MakeInternalMesh(std::unique_ptr<vtkFoamLabelListList>& meshCellsPtr, const vtkFoamLabelListList& meshFaces, vtkFloatArray* pointArray); void InsertFacesToGrid(vtkPolyData*, const vtkFoamLabelListList& meshFaces, vtkIdType startFace, @@ -1153,8 +1152,6 @@ private: vtkMultiBlockDataSet* MakeBoundaryMesh( const vtkFoamLabelListList& meshFaces, vtkFloatArray* pointArray); - void TruncateFaceOwner(); - // Move additional points for decomposed cells bool MoveInternalMesh(vtkUnstructuredGrid*, vtkFloatArray*); bool MoveBoundaryMesh(vtkMultiBlockDataSet*, vtkFloatArray*); @@ -1182,8 +1179,9 @@ private: std::unique_ptr<vtkFoamDict> GatherBlocks(const char* typeName, bool mandatory); // Create (cell|face|point) zones - bool GetCellZoneMesh(vtkMultiBlockDataSet* zoneMesh, const vtkFoamLabelListList& meshCells, - const vtkFoamLabelListList& meshFaces, vtkPoints*); + bool GetCellZoneMesh(vtkMultiBlockDataSet* zoneMesh, + std::unique_ptr<vtkFoamLabelListList>& meshCellsPtr, const vtkFoamLabelListList& meshFaces, + vtkPoints*); bool GetFaceZoneMesh( vtkMultiBlockDataSet* zoneMesh, const vtkFoamLabelListList& meshFaces, vtkPoints*); bool GetPointZoneMesh(vtkMultiBlockDataSet* zoneMesh, vtkPoints*); @@ -6332,7 +6330,7 @@ void vtkOpenFOAMReaderPrivate::PopulatePolyMeshDirArrays() // // - sets NumPoints -vtkSmartPointer<vtkFloatArray> vtkOpenFOAMReaderPrivate::ReadPointsFile() +vtkSmartPointer<vtkFloatArray> vtkOpenFOAMReaderPrivate::ReadPointsFile(const std::string& meshDir) { // Assume failure this->NumPoints = 0; @@ -6341,10 +6339,8 @@ vtkSmartPointer<vtkFloatArray> vtkOpenFOAMReaderPrivate::ReadPointsFile() // Read polyMesh/points { - const std::string pointPath = - this->CurrentTimeRegionMeshPath(this->PolyMeshPointsDir) + "points"; - - if (!io.OpenOrGzip(pointPath)) + const std::string pointsPath(meshDir + "points"); + if (!io.OpenOrGzip(pointsPath)) { vtkErrorMacro(<< "Error opening " << io.GetFileName() << ": " << io.GetError()); return nullptr; @@ -6450,14 +6446,10 @@ std::unique_ptr<vtkFoamLabelListList> vtkOpenFOAMReaderPrivate::ReadFacesFile( } //------------------------------------------------------------------------------ -// Read owner, neighbour files and create meshCells -// +// Read owner, neighbour files // - sets NumFaces and NumInternalFaces, and NumCells -// -// Return meshCells -std::unique_ptr<vtkFoamLabelListList> vtkOpenFOAMReaderPrivate::ReadOwnerNeighbourFiles( - const std::string& meshDir) +bool vtkOpenFOAMReaderPrivate::ReadOwnerNeighbourFiles(const std::string& meshDir) { // Assume failure this->NumCells = 0; @@ -6470,7 +6462,7 @@ std::unique_ptr<vtkFoamLabelListList> vtkOpenFOAMReaderPrivate::ReadOwnerNeighbo if (!io.OpenOrGzip(ownPath)) { vtkErrorMacro(<< "Error opening " << io.GetFileName() << ": " << io.GetError()); - return nullptr; + return false; } } const bool use64BitLabels = io.IsLabel64(); @@ -6495,12 +6487,31 @@ std::unique_ptr<vtkFoamLabelListList> vtkOpenFOAMReaderPrivate::ReadOwnerNeighbo { vtkErrorMacro(<< "Error reading line " << io.GetLineNumber() << " of " << io.GetFileName() << ": " << err); - return nullptr; + return false; } io.Close(); // Store owner faces this->FaceOwner = ownerDict.ReleasePtr<vtkDataArray>(); + const vtkIdType nFaces = this->FaceOwner->GetNumberOfTuples(); + + // Count cells, check validity + vtkTypeInt64 nCells = -1; + for (vtkIdType facei = 0; facei < nFaces; ++facei) + { + const vtkTypeInt64 celli = GetLabelValue(this->FaceOwner, facei, use64BitLabels); + if (celli < 0) + { + vtkErrorMacro(<< "Illegal cell label in owner addressing. Face " << facei); + return false; + } + if (nCells < celli) + { + nCells = celli; // <- max(nCells, celli) + } + } + + this->NumCells = static_cast<vtkIdType>(++nCells); } // Read polyMesh/neighbour @@ -6509,7 +6520,7 @@ std::unique_ptr<vtkFoamLabelListList> vtkOpenFOAMReaderPrivate::ReadOwnerNeighbo if (!io.OpenOrGzip(neiPath)) { vtkErrorMacro(<< "Error opening " << io.GetFileName() << ": " << io.GetError()); - return nullptr; + return false; } } @@ -6517,7 +6528,7 @@ std::unique_ptr<vtkFoamLabelListList> vtkOpenFOAMReaderPrivate::ReadOwnerNeighbo { vtkErrorMacro(<< "owner/neighbour with different label-size: should not happen" << io.GetCasePath()); - return nullptr; + return false; } { @@ -6540,26 +6551,62 @@ std::unique_ptr<vtkFoamLabelListList> vtkOpenFOAMReaderPrivate::ReadOwnerNeighbo { vtkErrorMacro(<< "Error reading line " << io.GetLineNumber() << " of " << io.GetFileName() << ": " << err); - return nullptr; + return false; } io.Close(); // Store neighbour faces this->FaceNeigh = neighDict.ReleasePtr<vtkDataArray>(); + const vtkIdType nFaces = this->FaceOwner->GetNumberOfTuples(); + + if (nFaces == this->FaceNeigh->GetNumberOfTuples()) + { + // Extremely old meshes had identical size for owner/neighbour and -1 padding + vtkIdType nInternalFaces = 0; + for (vtkIdType facei = 0; facei < nFaces; ++facei) + { + if (GetLabelValue(this->FaceNeigh, facei, use64BitLabels) < 0) + { + break; + } + else + { + ++nInternalFaces; + } + } + this->FaceNeigh->Resize(nInternalFaces); + } + else + { + // Check validity + const vtkIdType nInternalFaces = this->FaceNeigh->GetNumberOfTuples(); + + for (vtkIdType facei = 0; facei < nInternalFaces; ++facei) + { + const vtkTypeInt64 celli = GetLabelValue(this->FaceNeigh, facei, use64BitLabels); + if (celli < 0) + { + vtkErrorMacro(<< "Illegal cell label in neighbour addressing. Face " << facei); + return false; + } + } + } } - // Basic checks - const vtkDataArray& faceOwner = *this->FaceOwner; - const vtkDataArray& faceNeigh = *this->FaceNeigh; + // Size checks + if (this->NumCells == 0) + { + vtkWarningMacro(<< "The mesh contains no cells"); + } - const vtkIdType nFaces = faceOwner.GetNumberOfTuples(); - const vtkIdType nInternalFaces = faceNeigh.GetNumberOfTuples(); + const vtkIdType nFaces = this->FaceOwner->GetNumberOfTuples(); + const vtkIdType nInternalFaces = this->FaceNeigh->GetNumberOfTuples(); if (nFaces < nInternalFaces) { vtkErrorMacro(<< "Number of owner faces " << nFaces << " not equal or greater than number of neighbour faces " << nInternalFaces); - return nullptr; + return false; } // Set or check number of mesh faces @@ -6571,83 +6618,65 @@ std::unique_ptr<vtkFoamLabelListList> vtkOpenFOAMReaderPrivate::ReadOwnerNeighbo { vtkErrorMacro(<< "Expected " << this->NumFaces << " faces, but owner had " << nFaces << " faces"); - return nullptr; + return false; } this->NumInternalFaces = nInternalFaces; - // The cell-faces - return this->CreateCellFaces(faceOwner, faceNeigh); + return true; } //------------------------------------------------------------------------------ // Create meshCells from owner/neighbour information // -// - sets NumFaces and NumInternalFaces (again), and NumCells -// -// Return meshCells -std::unique_ptr<vtkFoamLabelListList> vtkOpenFOAMReaderPrivate::CreateCellFaces( - const vtkDataArray& faceOwner, const vtkDataArray& faceNeigh) +// - sets NumFaces and NumInternalFaces (again), optionally NumCells + +std::unique_ptr<vtkFoamLabelListList> vtkOpenFOAMReaderPrivate::CreateCellFaces() { - // Reset - this->NumCells = 0; + if (!this->FaceOwner) + { + vtkErrorMacro(<< "Cannot create cell faces without face owner information"); + return nullptr; + } + if (!this->FaceNeigh) + { + vtkErrorMacro(<< "Cannot create cell faces without face neighbour information"); + return nullptr; + } + const bool use64BitLabels = ::Is64BitArray(this->FaceOwner); - const bool use64BitLabels = ::Is64BitArray(&faceOwner); + const vtkDataArray& faceOwner = *this->FaceOwner; + const vtkDataArray& faceNeigh = *this->FaceNeigh; const vtkIdType nFaces = faceOwner.GetNumberOfTuples(); const vtkIdType nInternalFaces = faceNeigh.GetNumberOfTuples(); - // Extra safety + // Extra safety (consistency) this->NumFaces = nFaces; this->NumInternalFaces = nInternalFaces; - // Determine the number of cells and number of cell faces (total) - vtkTypeInt64 nCells = -1; - vtkTypeInt64 nTotalCellFaces = 0; - - for (vtkIdType facei = 0; facei < nInternalFaces; ++facei) + // Recalculate number of cells if needed + if (this->NumCells == 0) { - const vtkTypeInt64 own = GetLabelValue(&faceOwner, facei, use64BitLabels); - if (own >= 0) + vtkTypeInt64 nCells = -1; + for (vtkIdType facei = 0; facei < nFaces; ++facei) { - ++nTotalCellFaces; - if (nCells < own) + const vtkTypeInt64 celli = GetLabelValue(&faceOwner, facei, use64BitLabels); + if (nCells < celli) { - nCells = own; // <- max(nCells, own) + nCells = celli; // <- max(nCells, celli) } } - const vtkTypeInt64 nei = GetLabelValue(&faceNeigh, facei, use64BitLabels); - if (nei >= 0) - { - ++nTotalCellFaces; - if (nCells < nei) - { - nCells = nei; // <- max(nCells, nei) - } - } + // Set the number of cells + this->NumCells = static_cast<vtkIdType>(++nCells); } - for (vtkIdType facei = nInternalFaces; facei < nFaces; ++facei) - { - const vtkTypeInt64 own = GetLabelValue(&faceOwner, facei, use64BitLabels); - if (own >= 0) - { - ++nTotalCellFaces; - if (nCells < own) - { - nCells = own; // <- max(nCells, own) - } - } - } - ++nCells; - - if (nCells == 0) - { - vtkWarningMacro(<< "The mesh contains no cells"); - } + // The number of cells + const vtkTypeInt64 nCells = this->NumCells; - // Set the number of cells - this->NumCells = static_cast<vtkIdType>(nCells); + // Total number of cell faces + const vtkTypeInt64 nTotalCellFaces = + static_cast<vtkTypeInt64>(nFaces) + static_cast<vtkTypeInt64>(nInternalFaces); // Create meshCells. Avoid 32bit overflow for nTotalCellFaces std::unique_ptr<vtkFoamLabelListList> meshCells; @@ -6670,28 +6699,15 @@ std::unique_ptr<vtkFoamLabelListList> vtkOpenFOAMReaderPrivate::CreateCellFaces( // Accumulate offsets into slot *above* constexpr vtkIdType cellIndexOffset = 1; - for (vtkIdType facei = 0; facei < nInternalFaces; ++facei) + for (vtkIdType facei = 0; facei < nFaces; ++facei) { - const vtkTypeInt64 own = GetLabelValue(&faceOwner, facei, use64BitLabels); - if (own >= 0) - { - cells.IncrementOffset(cellIndexOffset + own); - } - - const vtkTypeInt64 nei = GetLabelValue(&faceNeigh, facei, use64BitLabels); - if (nei >= 0) - { - cells.IncrementOffset(cellIndexOffset + nei); - } + const vtkTypeInt64 celli = GetLabelValue(&faceOwner, facei, use64BitLabels); + cells.IncrementOffset(cellIndexOffset + celli); } - - for (vtkIdType facei = nInternalFaces; facei < nFaces; ++facei) + for (vtkIdType facei = 0; facei < nInternalFaces; ++facei) { - const vtkTypeInt64 own = GetLabelValue(&faceOwner, facei, use64BitLabels); - if (own >= 0) - { - cells.IncrementOffset(cellIndexOffset + own); - } + const vtkTypeInt64 celli = GetLabelValue(&faceNeigh, facei, use64BitLabels); + cells.IncrementOffset(cellIndexOffset + celli); } // Reduce per-cell face count -> start offsets @@ -6719,30 +6735,29 @@ std::unique_ptr<vtkFoamLabelListList> vtkOpenFOAMReaderPrivate::CreateCellFaces( // Add face numbers to cell-faces list, using tmpAddr offsets to manage the locations for (vtkIdType facei = 0; facei < nInternalFaces; ++facei) { - const vtkTypeInt64 own = GetLabelValue(&faceOwner, facei, use64BitLabels); - if (own >= 0) + // owner { - const vtkTypeInt64 next = tmpAddr->GetBeginOffset(own); - tmpAddr->IncrementOffset(own); + const vtkTypeInt64 celli = GetLabelValue(&faceOwner, facei, use64BitLabels); + const vtkTypeInt64 next = tmpAddr->GetBeginOffset(celli); + tmpAddr->IncrementOffset(celli); cells.SetValue(next, facei); } - - const vtkTypeInt64 nei = GetLabelValue(&faceNeigh, facei, use64BitLabels); - if (nei >= 0) + // neighbour { - const vtkTypeInt64 next = tmpAddr->GetBeginOffset(nei); - tmpAddr->IncrementOffset(nei); + const vtkTypeInt64 celli = GetLabelValue(&faceNeigh, facei, use64BitLabels); + const vtkTypeInt64 next = tmpAddr->GetBeginOffset(celli); + tmpAddr->IncrementOffset(celli); cells.SetValue(next, facei); } } for (vtkIdType facei = nInternalFaces; facei < nFaces; ++facei) { - const vtkTypeInt64 own = GetLabelValue(&faceOwner, facei, use64BitLabels); - if (own >= 0) + // owner { - const vtkTypeInt64 next = tmpAddr->GetBeginOffset(own); - tmpAddr->IncrementOffset(own); + const vtkTypeInt64 celli = GetLabelValue(&faceOwner, facei, use64BitLabels); + const vtkTypeInt64 next = tmpAddr->GetBeginOffset(celli); + tmpAddr->IncrementOffset(celli); cells.SetValue(next, facei); } } @@ -6785,7 +6800,7 @@ bool vtkOpenFOAMReaderPrivate::CheckFaceList(const vtkFoamLabelListList& faces) // determine cell shape and insert the cell into the mesh // hexahedron, prism, pyramid, tetrahedron and decompose polyhedron void vtkOpenFOAMReaderPrivate::InsertCellsToGrid( - vtkUnstructuredGrid* internalMesh, const vtkFoamLabelListList& meshCells, + vtkUnstructuredGrid* internalMesh, std::unique_ptr<vtkFoamLabelListList>& meshCellsPtr, const vtkFoamLabelListList& meshFaces, vtkIdList* cellLabels #if VTK_FOAMFILE_DECOMPOSE_POLYHEDRA , @@ -6821,6 +6836,15 @@ void vtkOpenFOAMReaderPrivate::InsertCellsToGrid( return; } #endif + if (!nCells) + { + return; + } + if (!meshCellsPtr) + { + meshCellsPtr = this->CreateCellFaces(); + } + const auto& meshCells = *meshCellsPtr; for (vtkIdType celli = 0; celli < nCells; ++celli) { @@ -7610,7 +7634,7 @@ void vtkOpenFOAMReaderPrivate::InsertCellsToGrid( //------------------------------------------------------------------------------ // derive cell types and create the internal mesh vtkUnstructuredGrid* vtkOpenFOAMReaderPrivate::MakeInternalMesh( - const vtkFoamLabelListList& meshCells, const vtkFoamLabelListList& meshFaces, + std::unique_ptr<vtkFoamLabelListList>& meshCellsPtr, const vtkFoamLabelListList& meshFaces, vtkFloatArray* pointArray) { // Create Mesh @@ -7630,7 +7654,7 @@ vtkUnstructuredGrid* vtkOpenFOAMReaderPrivate::MakeInternalMesh( additionalCells->SetNumberOfComponents(5); // Accommodate tetra or pyramid this->InsertCellsToGrid( - internalMesh, meshCells, meshFaces, nullptr, additionalCells, pointArray); + internalMesh, meshCellsPtr, meshFaces, nullptr, additionalCells, pointArray); // For polyhedral decomposition pointArray->Squeeze(); @@ -7657,7 +7681,7 @@ vtkUnstructuredGrid* vtkOpenFOAMReaderPrivate::MakeInternalMesh( else #endif // VTK_FOAMFILE_DECOMPOSE_POLYHEDRA { - this->InsertCellsToGrid(internalMesh, meshCells, meshFaces); + this->InsertCellsToGrid(internalMesh, meshCellsPtr, meshFaces); } // Set points for internalMesh @@ -8057,28 +8081,6 @@ vtkMultiBlockDataSet* vtkOpenFOAMReaderPrivate::MakeBoundaryMesh( return boundaryMesh; } -//------------------------------------------------------------------------------ -// truncate face owner to have only boundary face info -void vtkOpenFOAMReaderPrivate::TruncateFaceOwner() -{ - const vtkIdType boundaryStartFace = - (!this->BoundaryDict.empty() ? this->BoundaryDict.startFace() - : this->FaceOwner->GetNumberOfTuples()); - - // All boundary faces - const vtkIdType nBoundaryFaces = this->FaceOwner->GetNumberOfTuples() - boundaryStartFace; - memmove(this->FaceOwner->GetVoidPointer(0), this->FaceOwner->GetVoidPointer(boundaryStartFace), - static_cast<size_t>(this->FaceOwner->GetDataTypeSize() * nBoundaryFaces)); - this->FaceOwner->Resize(nBoundaryFaces); - - // Has side effect on neighbour too - if (this->FaceNeigh != nullptr) - { - this->FaceNeigh->Delete(); - this->FaceNeigh = nullptr; - } -} - //------------------------------------------------------------------------------ // Move mesh points, including the cell centroids for any decomposed polyhedra bool vtkOpenFOAMReaderPrivate::MoveInternalMesh( @@ -9430,7 +9432,8 @@ std::unique_ptr<vtkFoamDict> vtkOpenFOAMReaderPrivate::GatherBlocks( // Populate cell zone(s) mesh bool vtkOpenFOAMReaderPrivate::GetCellZoneMesh(vtkMultiBlockDataSet* zoneMesh, - const vtkFoamLabelListList& meshCells, const vtkFoamLabelListList& meshFaces, vtkPoints* points) + std::unique_ptr<vtkFoamLabelListList>& meshCellsPtr, const vtkFoamLabelListList& meshFaces, + vtkPoints* points) { const bool supportFields = this->Parent->CopyDataToCellZones; @@ -9536,7 +9539,7 @@ bool vtkOpenFOAMReaderPrivate::GetCellZoneMesh(vtkMultiBlockDataSet* zoneMesh, zm->Allocate(nUsed); // Insert cells - this->InsertCellsToGrid(zm, meshCells, meshFaces, elemIds); + this->InsertCellsToGrid(zm, meshCellsPtr, meshFaces, elemIds); // Set points for zone zm->SetPoints(points); @@ -9951,17 +9954,16 @@ int vtkOpenFOAMReaderPrivate::RequestData(vtkMultiBlockDataSet* output) } // Mesh primitives + vtkSmartPointer<vtkFloatArray> pointArray; std::unique_ptr<vtkFoamLabelListList> meshCells; std::unique_ptr<vtkFoamLabelListList> meshFaces; - std::string meshDir; if (createEulerians && (recreateInternalMesh || recreateBoundaryMesh)) { - // Path to polyMesh/ files - meshDir = this->CurrentTimeRegionMeshPath(this->PolyMeshFacesDir); + const std::string facesInstance = this->CurrentTimeRegionMeshPath(this->PolyMeshFacesDir); // Read polyMesh/faces, create the list of faces, set the number of faces - meshFaces = this->ReadFacesFile(meshDir); + meshFaces = this->ReadFacesFile(facesInstance); if (!meshFaces) { return 0; @@ -9971,23 +9973,25 @@ int vtkOpenFOAMReaderPrivate::RequestData(vtkMultiBlockDataSet* output) if (createEulerians && recreateInternalMesh) { - // Read polyMesh/{owner,neighbour}, create FaceOwner/FaceNeigh and meshCells vectors - meshCells = this->ReadOwnerNeighbourFiles(meshDir); - if (!meshCells) + const std::string facesInstance = this->CurrentTimeRegionMeshPath(this->PolyMeshFacesDir); + + // Read polyMesh/{owner,neighbour}, create FaceOwner/FaceNeigh + if (!this->ReadOwnerNeighbourFiles(facesInstance)) { return 0; } this->Parent->UpdateProgress(0.3); } - vtkSmartPointer<vtkFloatArray> pointArray; if (createEulerians && (recreateInternalMesh || (recreateBoundaryMesh && !recreateInternalMesh && this->InternalMesh == nullptr) || moveInternalPoints || moveBoundaryPoints)) { + const std::string pointsInstance = this->CurrentTimeRegionMeshPath(this->PolyMeshPointsDir); + // Read polyMesh/points, set the number of faces - pointArray = this->ReadPointsFile(); + pointArray = this->ReadPointsFile(pointsInstance); if ((recreateInternalMesh && pointArray.Get() == nullptr) || (meshFaces && !this->CheckFaceList(*meshFaces))) { @@ -10003,7 +10007,7 @@ int vtkOpenFOAMReaderPrivate::RequestData(vtkMultiBlockDataSet* output) if (this->Parent->PatchDataArraySelection->ArrayExists(displayName.c_str()) && this->Parent->GetPatchArrayStatus(displayName.c_str())) { - this->InternalMesh = this->MakeInternalMesh(*meshCells, *meshFaces, pointArray); + this->InternalMesh = this->MakeInternalMesh(meshCells, *meshFaces, pointArray); } } @@ -10025,7 +10029,7 @@ int vtkOpenFOAMReaderPrivate::RequestData(vtkMultiBlockDataSet* output) } this->CellZoneMesh = vtkMultiBlockDataSet::New(); - if (!this->GetCellZoneMesh(this->CellZoneMesh, *meshCells, *meshFaces, points) || + if (!this->GetCellZoneMesh(this->CellZoneMesh, meshCells, *meshFaces, points) || this->CellZoneMesh->GetNumberOfBlocks() == 0) { this->cellZoneMap.clearAll(); @@ -10055,12 +10059,7 @@ int vtkOpenFOAMReaderPrivate::RequestData(vtkMultiBlockDataSet* output) // Don't need meshCells beyond here meshCells.reset(nullptr); - // Only need boundary face owners beyond here, - // except if we have faceZones, then we still need them - if (createEulerians && recreateInternalMesh && !this->FaceZoneMesh) - { - this->TruncateFaceOwner(); - } + // Note: preserve face owner/neighbour information for reconstruction, face zones etc. // Create boundary mesh if (createEulerians && recreateBoundaryMesh)