diff --git a/IO/Geometry/vtkOpenFOAMReader.cxx b/IO/Geometry/vtkOpenFOAMReader.cxx index e5e925d45aaa88d58461822afd72392898967ec8..67adf239fc12b2e9e65c705365e71e4237d5338a 100644 --- a/IO/Geometry/vtkOpenFOAMReader.cxx +++ b/IO/Geometry/vtkOpenFOAMReader.cxx @@ -137,6 +137,9 @@ // This could also be made part of the GUI properties #define VTK_FOAMFILE_IGNORE_FIELD_RESTART 0 +// Support for finiteArea +#define VTK_FOAMFILE_FINITE_AREA 0 + // Support extra decomposition of polyhedral cells #define VTK_FOAMFILE_DECOMPOSE_POLYHEDRA 1 @@ -237,8 +240,10 @@ uLong ZEXPORT crc32(uLong, const Bytef*, uInt) vtkStandardNewMacro(vtkOpenFOAMReader); +#if VTK_FOAMFILE_FINITE_AREA // The name for finiteArea mesh -// pending: static constexpr const char* const NAME_AREAMESH = "areaMesh"; +static constexpr const char* const NAME_AREAMESH = "areaMesh"; +#endif // The name for finiteVolume internal mesh (unzoned) static constexpr const char* const NAME_INTERNALMESH = "internalMesh"; @@ -1040,6 +1045,11 @@ private: vtkFoamLabelArrayVector* AdditionalCellPoints; #endif +#if VTK_FOAMFILE_FINITE_AREA + vtkFoamZones areaMeshMap; + vtkPolyData* AreaMesh; +#endif + // Constructor and destructor are kept private vtkOpenFOAMReaderPrivate(); ~vtkOpenFOAMReaderPrivate() override; @@ -1051,6 +1061,7 @@ private: void ClearInternalMeshes(); void ClearBoundaryMeshes(); void ClearZoneMeshes(); + void ClearAreaMeshes(); void ClearMeshes(); // The subdirectory for a region. Eg, "/solid". Empty for default region. @@ -1184,9 +1195,12 @@ private: vtkSmartPointer<vtkFloatArray> FillField(vtkFoamEntry& entry, vtkIdType nElements, const vtkFoamIOobject& io, vtkFoamTypes::dataType fieldDataType); void GetVolFieldAtTimeStep(const std::string& varName, bool isInternalField = false); - // void GetAreaFieldAtTimeStep(const std::string& varName); void GetPointFieldAtTimeStep(const std::string& varName); +#if VTK_FOAMFILE_FINITE_AREA + void GetAreaFieldAtTimeStep(const std::string& varName); +#endif + // Create lagrangian mesh/fields vtkMultiBlockDataSet* MakeLagrangianMesh(); @@ -1200,6 +1214,11 @@ private: bool GetFaceZoneMesh( vtkMultiBlockDataSet* zoneMesh, const vtkFoamLabelListList& meshFaces, vtkPoints*); bool GetPointZoneMesh(vtkMultiBlockDataSet* zoneMesh, vtkPoints*); + +#if VTK_FOAMFILE_FINITE_AREA + // Mechanism for finiteArea mesh is similar to faceZone + bool GetAreaMesh(vtkPolyData* areaMesh, const vtkFoamLabelListList& meshFaces, vtkPoints*); +#endif }; vtkStandardNewMacro(vtkOpenFOAMReaderPrivate); @@ -5207,6 +5226,11 @@ vtkOpenFOAMReaderPrivate::vtkOpenFOAMReaderPrivate() this->AdditionalCellPoints = nullptr; #endif +#if VTK_FOAMFILE_FINITE_AREA + this->areaMeshMap.reset(vtkFoamZones::FACE); + this->AreaMesh = nullptr; +#endif + this->Parent = nullptr; } @@ -5284,6 +5308,18 @@ void vtkOpenFOAMReaderPrivate::ClearZoneMeshes() } } +void vtkOpenFOAMReaderPrivate::ClearAreaMeshes() +{ +#if VTK_FOAMFILE_FINITE_AREA + this->areaMeshMap.clearAll(); + if (this->AreaMesh != nullptr) + { + this->AreaMesh->Delete(); + this->AreaMesh = nullptr; + } +#endif +} + void vtkOpenFOAMReaderPrivate::ClearBoundaryMeshes() { if (this->BoundaryMesh != nullptr) @@ -5317,6 +5353,7 @@ void vtkOpenFOAMReaderPrivate::ClearMeshes() this->ClearInternalMeshes(); this->ClearBoundaryMeshes(); this->ClearZoneMeshes(); + this->ClearAreaMeshes(); } void vtkOpenFOAMReaderPrivate::SetTimeValue(double requestedTime) @@ -6002,7 +6039,9 @@ int vtkOpenFOAMReaderPrivate::MakeMetaDataAtTimeStep(vtkStringArray* cellSelecti // sort array names. volFields first, followed by internal fields this->SortFieldFiles(cellSelectionNames, this->VolFieldFiles); this->SortFieldFiles(cellSelectionNames, this->DimFieldFiles); - // pending: this->SortFieldFiles(cellSelectionNames, this->AreaFieldFiles); +#if VTK_FOAMFILE_FINITE_AREA + this->SortFieldFiles(cellSelectionNames, this->AreaFieldFiles); +#endif this->SortFieldFiles(pointSelectionNames, this->PointFieldFiles); this->SortFieldFiles(lagrangianSelectionNames, this->LagrangianFieldFiles); @@ -9302,6 +9341,85 @@ void vtkOpenFOAMReaderPrivate::GetPointFieldAtTimeStep(const std::string& varNam // ... } +//------------------------------------------------------------------------------ +// Read area field at a timestep +#if VTK_FOAMFILE_FINITE_AREA +void vtkOpenFOAMReaderPrivate::GetAreaFieldAtTimeStep(const std::string& varName) +{ + // Where to map data + vtkPolyData* areaMesh = this->AreaMesh; + + // Really simple, skip if not selected, or no areaMesh + const vtkIdType nFaces = (areaMesh ? areaMesh->GetNumberOfCells() : 0); + + if (!nFaces) + { + return; + } + + vtkFoamIOobject io(this->CasePath, this->Parent); + vtkFoamDict dict; + if (!this->ReadFieldFile(io, dict, varName, this->Parent->CellDataArraySelection)) + { + return; + } + + if (io.GetClassName().compare(0, 4, "area") != 0) + { + vtkErrorMacro(<< io.GetFileName() << " is not a areaField"); + return; + } + + // Eg, from "areaScalarField" -> SCALAR_TYPE + const auto fieldDataType(vtkFoamTypes::FieldToEnum(io.GetClassName(), 4)); + + // ------------------------- + // Handle dictionary lookups first + + // The "dimensions" entry - stringify + const std::string dimString(this->ConstructDimensions(dict)); + + // The "internalField" entry + vtkFoamEntry* ifieldEntry = nullptr; + { + ifieldEntry = dict.Lookup("internalField"); + if (ifieldEntry == nullptr) + { + vtkErrorMacro(<< "internalField not found in " << io.GetFileName()); + return; + } + else if (ifieldEntry->FirstValue().GetType() == vtkFoamToken::EMPTYLIST) + { + if (this->NumFaces) + { + vtkErrorMacro(<< "internalField of " << io.GetFileName() << " is empty"); + } + return; + } + } + + // Forget "boundaryField" entry + // - don't really handle edge constraints, + + // ------------------------- + + // Generate internal field data + vtkSmartPointer<vtkFloatArray> iData = this->FillField(*ifieldEntry, nFaces, io, fieldDataType); + if (iData == nullptr) + { + return; + } + else if (iData->GetSize() == 0) + { + // Determined that there are no faces. Ignore the field + return; + } + + // Set data to area mesh + ::AddArrayToFieldData(areaMesh->GetCellData(), iData, io.GetObjectName(), dimString); +} +#endif + //------------------------------------------------------------------------------ vtkMultiBlockDataSet* vtkOpenFOAMReaderPrivate::MakeLagrangianMesh() { @@ -9907,6 +10025,108 @@ bool vtkOpenFOAMReaderPrivate::GetPointZoneMesh(vtkMultiBlockDataSet* zoneMesh, return true; } +//------------------------------------------------------------------------------ +// Populate face zone(s) mesh + +#if VTK_FOAMFILE_FINITE_AREA +bool vtkOpenFOAMReaderPrivate::GetAreaMesh( + vtkPolyData* areaMesh, const vtkFoamLabelListList& meshFaces, vtkPoints* points) +{ + // Tie to faces instance (like zones etc) + const std::string timeRegionDir(this->CurrentTimeRegionPath(*this->PolyMeshTimeIndexFaces)); + + auto& zoneMap = this->areaMeshMap; + zoneMap.clearAll(); // Remove all old ids and errors + + vtkFoamIOobject io(this->CasePath, this->Parent); + + // Read faMesh/faceLabels + if (!io.OpenOrGzip(timeRegionDir + "/faMesh/faceLabels")) + { + // Not an error if missing + return true; + } + const bool use64BitLabels = io.IsLabel64(); + + vtkSmartPointer<vtkDataArray> labelArray; + + { + vtkFoamEntryValue dict(nullptr); + dict.SetStreamOption(io); + try + { + if (use64BitLabels) + { + dict.ReadNonUniformList<vtkFoamToken::LABELLIST, + vtkFoamEntryValue::listTraits<vtkTypeInt64Array, vtkTypeInt64>>(io); + } + else + { + dict.ReadNonUniformList<vtkFoamToken::LABELLIST, + vtkFoamEntryValue::listTraits<vtkTypeInt32Array, vtkTypeInt32>>(io); + } + + // Capture content as smart pointer + labelArray.TakeReference(dict.ReleasePtr<vtkDataArray>()); + } + catch (const vtkFoamError& err) + { + vtkErrorMacro(<< "Error reading line " << io.GetLineNumber() << " of " << io.GetFileName() + << ": " << err); + return false; + } + io.Close(); + } + + if (labelArray) + { + vtkDataArray& labels = *labelArray; + const vtkIdType nLabels = labels.GetNumberOfTuples(); + + // Transcribe into vtkIdList. Don't check for questionable entries just yet + auto elemIds = vtkSmartPointer<vtkIdList>::New(); + elemIds->SetNumberOfIds(nLabels); + + // Size for per-face scratch array + vtkIdType maxNFacePoints = 16; + + for (vtkIdType idx = 0; idx < nLabels; ++idx) + { + const vtkTypeInt64 elemId = GetLabelValue(labelArray, idx, use64BitLabels); + + const vtkIdType nFacePoints = meshFaces.GetSize(elemId); + if (maxNFacePoints < nFacePoints) + { + maxNFacePoints = nFacePoints; + } + + elemIds->SetId(idx, elemId); + } + + zoneMap.zones_[NAME_AREAMESH] = elemIds; + + // Per-face scratch array + auto facePointsVtkId = vtkSmartPointer<vtkIdList>::New(); + facePointsVtkId->SetNumberOfIds(maxNFacePoints); + + // Allocate new grid: we do not use resize() beforehand since it + // could lead to undefined pointer if we return by error + + areaMesh->AllocateEstimate(nLabels, 1); + + // Insert faces + this->InsertFacesToGrid( + areaMesh, meshFaces, 0, nLabels, nullptr, facePointsVtkId, elemIds, false); + + // Set points for zone + areaMesh->SetPoints(points); + + return true; + } + return false; +} +#endif + //------------------------------------------------------------------------------ // return 0 if there's any error, 1 if success int vtkOpenFOAMReaderPrivate::RequestData(vtkMultiBlockDataSet* output) @@ -10117,6 +10337,17 @@ int vtkOpenFOAMReaderPrivate::RequestData(vtkMultiBlockDataSet* output) this->PointZoneMesh->Delete(); this->PointZoneMesh = nullptr; } + +#if VTK_FOAMFILE_FINITE_AREA + // Needs a proper selection mechanism + this->AreaMesh = vtkPolyData::New(); + if (!this->GetAreaMesh(this->AreaMesh, *meshFaces, points)) + { + this->areaMeshMap.clearAll(); + this->AreaMesh->Delete(); + this->AreaMesh = nullptr; + } +#endif } // Don't need meshCells beyond here @@ -10276,9 +10507,13 @@ int vtkOpenFOAMReaderPrivate::RequestData(vtkMultiBlockDataSet* output) // read field data variables into Internal/Boundary meshes vtkIdType nFieldsRead = 0; - const vtkIdType nFieldsToRead = (this->VolFieldFiles->GetNumberOfValues() + + vtkIdType nFieldsToRead = (this->VolFieldFiles->GetNumberOfValues() + this->DimFieldFiles->GetNumberOfValues() + this->PointFieldFiles->GetNumberOfValues()); +#if VTK_FOAMFILE_FINITE_AREA + nFieldsToRead += this->AreaFieldFiles->GetNumberOfValues(); +#endif + for (vtkIdType i = 0; i < this->VolFieldFiles->GetNumberOfValues(); ++i) { this->GetVolFieldAtTimeStep(this->VolFieldFiles->GetValue(i)); @@ -10294,6 +10529,13 @@ int vtkOpenFOAMReaderPrivate::RequestData(vtkMultiBlockDataSet* output) this->GetPointFieldAtTimeStep(this->PointFieldFiles->GetValue(i)); this->Parent->UpdateProgress(0.5 + (0.5 * ++nFieldsRead) / nFieldsToRead); } +#if VTK_FOAMFILE_FINITE_AREA + for (vtkIdType i = 0; i < this->AreaFieldFiles->GetNumberOfValues(); ++i) + { + this->GetAreaFieldAtTimeStep(this->AreaFieldFiles->GetValue(i)); + this->Parent->UpdateProgress(0.5 + (0.5 * ++nFieldsRead) / nFieldsToRead); + } +#endif } // Read lagrangian mesh and fields @@ -10315,6 +10557,14 @@ int vtkOpenFOAMReaderPrivate::RequestData(vtkMultiBlockDataSet* output) ::AppendBlock(output, this->BoundaryMesh, "boundary"); } +#if VTK_FOAMFILE_FINITE_AREA + // Set finiteArea mesh/data as output + if (this->AreaMesh != nullptr) + { + ::AppendBlock(output, this->AreaMesh, NAME_AREAMESH); + } +#endif + // Set lagrangian mesh as output if (lagrangianMesh != nullptr) {