// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkDGTranscribeCellGridCells.h" #include "vtkBoundingBox.h" #include "vtkCellArray.h" #include "vtkCellAttribute.h" #include "vtkCellGrid.h" #include "vtkCellGridElevationQuery.h" #include "vtkDGInterpolateCalculator.h" #include "vtkDGVert.h" #include "vtkDataSetAttributes.h" #include "vtkDoubleArray.h" #include "vtkFloatArray.h" #include "vtkIdTypeArray.h" #include "vtkIncrementalOctreePointLocator.h" #include "vtkIntArray.h" #include "vtkObjectFactory.h" #include "vtkSMPTools.h" #include "vtkStringToken.h" #include "vtkTypeInt64Array.h" #include "vtkUnsignedCharArray.h" #include "vtkUnstructuredGrid.h" #include "vtkVector.h" #include #include VTK_ABI_NAMESPACE_BEGIN using namespace vtk::literals; namespace { int vtkCellTypeForDGShape(vtkDGCell::Shape shape) { switch (shape) { case vtkDGCell::Shape::Vertex: return VTK_VERTEX; case vtkDGCell::Shape::Edge: return VTK_LINE; case vtkDGCell::Shape::Triangle: return VTK_TRIANGLE; case vtkDGCell::Shape::Quadrilateral: return VTK_QUAD; case vtkDGCell::Shape::Tetrahedron: return VTK_TETRA; case vtkDGCell::Shape::Hexahedron: return VTK_HEXAHEDRON; case vtkDGCell::Shape::Wedge: return VTK_WEDGE; case vtkDGCell::Shape::Pyramid: return VTK_PYRAMID; default: break; } return VTK_EMPTY_CELL; } void vtkCellInfoFromDGType( vtkCellGridToUnstructuredGrid::Query::OutputAllocation& alloc, vtkDGCell* dgCell) { auto shape = dgCell->GetShape(); alloc.CellType = vtkCellTypeForDGShape(shape); alloc.NumberOfCells = 0; alloc.NumberOfConnectivityEntries = 0; for (int ii = -1; ii < static_cast(dgCell->GetNumberOfCellSources()); ++ii) { const auto& source(dgCell->GetCellSource(ii)); if (source.Blanked) { continue; } // Fetch the range of side indices that have the shape corresponding to source.SideType: auto sideRange = dgCell->GetSideRangeForType(source.SideType); shape = dgCell->GetSideShape(sideRange.first); vtkIdType pointsPerSide = vtkDGCell::GetShapeCornerCount(shape); vtkIdType numCells = source.Connectivity->GetNumberOfTuples(); alloc.NumberOfCells += numCells; alloc.NumberOfConnectivityEntries += (pointsPerSide + 1) * numCells; } } // The contributions of cell-grid corner points to // corner points in the output unstructured grid. // Attributes are interpolated using the cell IDs // and parametric coordinates, then summed to the // output points. struct Contributions { Contributions() { this->ParametricCoords->SetNumberOfComponents(3); } vtkIdType AddContribution( vtkIdType outputPointId, vtkIdType inputCellId, const std::array& pcoord) { vtkIdType nn = this->OutputPointIds->InsertNextValue(outputPointId); this->InputCellIds->InsertNextValue(inputCellId); this->ParametricCoords->InsertNextTuple(pcoord.data()); return nn; } vtkNew OutputPointIds; vtkNew InputCellIds; vtkNew ParametricCoords; }; using ContributionMap = std::unordered_map; class TranscribeCellGridPointCache : public vtkObject { public: vtkTypeMacro(TranscribeCellGridPointCache, vtkObject); void PrintSelf(std::ostream& os, vtkIndent indent) override { this->Superclass::PrintSelf(os, indent); os << indent << "ContributionsByType: " << this->ContributionsByType.size() << " entries\n"; } static TranscribeCellGridPointCache* New(); ContributionMap ContributionsByType; }; Contributions& FetchPointContributionCache( vtkCellGridToUnstructuredGrid::Query* request, vtkDGCell* cellType, vtkCellGridResponders* caches) { std::ostringstream cacheName; cacheName << "TranscribeCellGridPointCache_" << request; vtkStringToken cacheKey(cacheName.str()); vtkStringToken cellTypeToken(cellType->GetClassName()); auto data = caches->GetCacheDataAs(cacheKey.GetId(), /*createIfAbsent*/ true); return data->ContributionsByType[cellTypeToken]; }; void FreePointContributionCache( vtkCellGridToUnstructuredGrid::Query* request, vtkDGCell* cellType, vtkCellGridResponders* caches) { std::ostringstream cacheName; cacheName << "TranscribeCellGridPointCache_" << request; vtkStringToken cacheKey(cacheName.str()); vtkStringToken cellTypeToken(cellType->GetClassName()); auto data = caches->GetCacheDataAs( cacheKey.GetId(), /*createIfAbsent*/ false); if (data) { data->ContributionsByType.erase(cellTypeToken); if (data->ContributionsByType.empty()) { vtkSmartPointer blank; caches->SetCacheData(cacheKey.GetId(), blank, /*overwrite*/ true); } } }; } // anonymous namespace vtkStandardNewMacro(vtkDGTranscribeCellGridCells); vtkStandardNewMacro(TranscribeCellGridPointCache); void vtkDGTranscribeCellGridCells::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); } bool vtkDGTranscribeCellGridCells::Query( TranscribeQuery* request, vtkCellMetadata* cellType, vtkCellGridResponders* caches) { (void)caches; auto* dgCell = vtkDGCell::SafeDownCast(cellType); if (!dgCell) { return false; } auto* grid = dgCell->GetCellGrid(); if (!grid) { return false; } switch (request->GetPass()) { case TranscribeQuery::PassType::CountOutputs: { auto& alloc(request->GetOutputAllocations()); vtkCellInfoFromDGType(alloc[dgCell->GetClassName()], dgCell); } break; case TranscribeQuery::PassType::GenerateConnectivity: this->GenerateConnectivity(request, dgCell, caches); break; case TranscribeQuery::PassType::GeneratePointData: this->GeneratePointData(request, dgCell, caches); break; default: vtkErrorMacro("Unknown pass " << request->GetPass()); } return true; } void vtkDGTranscribeCellGridCells::GenerateConnectivity( TranscribeQuery* request, vtkDGCell* cellType, vtkCellGridResponders* caches) { vtkStringToken cellTypeToken = cellType->GetClassName(); auto& alloc = request->GetOutputAllocations(); auto ait = alloc.find(cellTypeToken); if (ait == alloc.end()) { return; } auto& contribs = FetchPointContributionCache(request, cellType, caches); auto* cellArray = request->GetOutput()->GetCells(); auto* cellTypes = vtkUnsignedCharArray::FastDownCast(request->GetOutput()->GetCellTypes()); auto* locator = request->GetLocator(); auto& pointMap = request->GetConnectivityTransform(cellTypeToken); auto& pointCounts = request->GetConnectivityCount(); auto shapeAtt = request->GetInput()->GetShapeAttribute(); auto shapeInfo = shapeAtt->GetCellTypeInfo(cellTypeToken); auto shapePoints = shapeInfo.GetArrayForRoleAs("values"_token); auto shapeConn = shapeInfo.GetArrayForRoleAs("connectivity"_token); // Insert points, add to map, and write output-cell connectivity // NB: We currently assume the shape attribute uses a constant (vertices) or HGRAD // function space. If not, we would need to interpolate values here instead of // copying from the shape attribute. for (int ii = -1; ii < static_cast(cellType->GetSideSpecs().size()); ++ii) { auto& source(cellType->GetCellSource(ii)); if (source.Blanked) { continue; } // source.Connectivity is either the connectivity of the cells (when // source.SideType < 0) or (cellId, sideIndex) 2-tuples (when // source.SideType >= 0). Either way, the number of tuples is the // number of cells corresponding to \a source: vtkIdType numSideTuples = source.Connectivity->GetNumberOfTuples(); std::vector inConn; std::vector outConn; inConn.resize(shapeConn->GetNumberOfComponents()); outConn.reserve(shapeConn->GetNumberOfComponents()); std::array xx; std::array sideTuple; // (cellId, sideIndex) if (source.SideType < 0) { for (vtkIdType cc = 0; cc < numSideTuples; ++cc) { outConn.clear(); // source is the CellSpec. source.Connectivity->GetUnsignedTuple(cc, inConn.data()); int pp = 0; for (const auto& inPointId : inConn) { vtkIdType outPointId; shapePoints->GetTuple(inPointId, xx.data()); if (locator->InsertUniquePoint(xx.data(), outPointId) != 0) { pointMap[static_cast(inPointId)] = outPointId; } ++pointCounts[outPointId]; outConn.push_back(outPointId); contribs.AddContribution( outPointId, cc + source.Offset, cellType->GetCornerParameter(pp)); ++pp; } cellArray->InsertNextCell(outConn.size(), outConn.data()); cellTypes->InsertNextValue(ait->second.CellType); } } else { // source is a SideSpec; fetch the side 2-tuple, then fetch // the cell's connectivity, then fetch a subset of the values // using the connectivity and side-connectivity. auto sideRange = cellType->GetSideRangeForType(source.SideType); auto shape = cellType->GetSideShape(sideRange.first); unsigned char sideShapeVTK = vtkCellTypeForDGShape(shape); for (vtkIdType cc = 0; cc < numSideTuples; ++cc) { outConn.clear(); source.Connectivity->GetUnsignedTuple(cc, sideTuple.data()); shapeConn->GetUnsignedTuple(sideTuple[0], inConn.data()); const auto& sideConn = cellType->GetSideConnectivity(sideTuple[1]); for (const auto& sidePointId : sideConn) { vtkIdType inPointId = inConn[sidePointId]; vtkIdType outPointId; shapePoints->GetTuple(inPointId, xx.data()); if (locator->InsertUniquePoint(xx.data(), outPointId) != 0) { pointMap[inPointId] = outPointId; } ++pointCounts[outPointId]; outConn.push_back(outPointId); contribs.AddContribution( outPointId, cc + source.Offset, cellType->GetCornerParameter(sidePointId)); } cellArray->InsertNextCell(outConn.size(), outConn.data()); cellTypes->InsertNextValue(sideShapeVTK); } } } } void vtkDGTranscribeCellGridCells::GeneratePointData( TranscribeQuery* request, vtkDGCell* cellType, vtkCellGridResponders* caches) { auto& alloc = request->GetOutputAllocations(); auto ait = alloc.find(cellType->GetClassName()); if (ait == alloc.end()) { return; } auto& contribs = FetchPointContributionCache(request, cellType, caches); vtkIdType nn = contribs.InputCellIds->GetNumberOfTuples(); auto& pointWeights = request->GetConnectivityWeights(); vtkNew interpolateProto; for (const auto& inCellAtt : request->GetInput()->GetCellAttributeList()) { if (inCellAtt == request->GetInput()->GetShapeAttribute()) { continue; } // TODO: We could handle the "constant"_token function-space differently // (by creating cell-data, not point-data, arrays). auto* outputArray = request->GetOutputArray(inCellAtt); auto rawCalc = interpolateProto->PrepareForGrid(cellType, inCellAtt); auto dgCalc = vtkDGInterpolateCalculator::SafeDownCast(rawCalc); vtkNew interpResult; int nc = inCellAtt->GetNumberOfComponents(); interpResult->SetNumberOfComponents(nc); interpResult->SetNumberOfTuples(nn); dgCalc->Evaluate(contribs.InputCellIds, contribs.ParametricCoords, interpResult); vtkSMPTools::For(0, nn, [&](vtkIdType begin, vtkIdType end) { std::vector outTuple(nc, 0.); std::vector inTuple(nc, 0.); for (vtkIdType ii = begin; ii < end; ++ii) { interpResult->GetTuple(ii, inTuple.data()); vtkIdType outputPointId = contribs.OutputPointIds->GetValue(ii); outputArray->GetTuple(outputPointId, outTuple.data()); double pw = pointWeights[outputPointId]; for (int jj = 0; jj < nc; ++jj) { outTuple[jj] += pw * inTuple[jj]; } outputArray->SetTuple(outputPointId, outTuple.data()); } }); } FreePointContributionCache(request, cellType, caches); } VTK_ABI_NAMESPACE_END