From b9129f44533af57227bd7b0f6ca8b159e94feed2 Mon Sep 17 00:00:00 2001 From: marioalexis Date: Sun, 14 Sep 2025 00:34:06 -0300 Subject: [PATCH] Fem: Add upstream vtkCleanUnstructuredGrid class source files --- src/Mod/Fem/App/CMakeLists.txt | 8 + src/Mod/Fem/App/PreCompiled.h | 4 + src/Mod/Fem/App/VTKExtensions/CMakeLists.txt | 6 - .../vtkCleanUnstructuredGrid.cxx | 680 ++++++++++++++++++ .../VTKExtensions/vtkCleanUnstructuredGrid.h | 153 ++++ src/Mod/Fem/App/VTKExtensions/vtkSMPTools.h | 604 ++++++++++++++++ 6 files changed, 1449 insertions(+), 6 deletions(-) delete mode 100644 src/Mod/Fem/App/VTKExtensions/CMakeLists.txt create mode 100644 src/Mod/Fem/App/VTKExtensions/vtkCleanUnstructuredGrid.cxx create mode 100644 src/Mod/Fem/App/VTKExtensions/vtkCleanUnstructuredGrid.h create mode 100644 src/Mod/Fem/App/VTKExtensions/vtkSMPTools.h diff --git a/src/Mod/Fem/App/CMakeLists.txt b/src/Mod/Fem/App/CMakeLists.txt index 6a8f02fb2e..2ddc53b4df 100644 --- a/src/Mod/Fem/App/CMakeLists.txt +++ b/src/Mod/Fem/App/CMakeLists.txt @@ -87,6 +87,14 @@ if(BUILD_FEM_VTK) VTKExtensions/vtkFemFrameSourceAlgorithm.h VTKExtensions/vtkFemFrameSourceAlgorithm.cpp ) + SET(VTK_SRCS_0903 + VTKExtensions/vtkCleanUnstructuredGrid.h + VTKExtensions/vtkCleanUnstructuredGrid.cxx + VTKExtensions/vtkSMPTools.h + ) + if (${VTK_MAJOR_VERSION} EQUAL 9 AND ${VTK_MINOR_VERSION} LESS 3) + list(APPEND FemVTK_SRCS ${VTK_SRCS_0903}) + endif() SOURCE_GROUP("VTKExtensions" FILES ${FemVTK_SRCS}) endif(BUILD_FEM_VTK) diff --git a/src/Mod/Fem/App/PreCompiled.h b/src/Mod/Fem/App/PreCompiled.h index 4df4b93e63..e336b2c98d 100644 --- a/src/Mod/Fem/App/PreCompiled.h +++ b/src/Mod/Fem/App/PreCompiled.h @@ -148,10 +148,14 @@ #include // VTK +#include #include #include #include #include +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 0) +#include +#endif #include #include #include diff --git a/src/Mod/Fem/App/VTKExtensions/CMakeLists.txt b/src/Mod/Fem/App/VTKExtensions/CMakeLists.txt deleted file mode 100644 index 8e9fbd85f7..0000000000 --- a/src/Mod/Fem/App/VTKExtensions/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -SET(FemVTK_SRCS - vtkFemFrameSourceAlgorithm.h - vtkFemFrameSourceAlgorithm.cpp - ) - -target_sources(Fem PRIVATE ${FemVTK_SRCS}) diff --git a/src/Mod/Fem/App/VTKExtensions/vtkCleanUnstructuredGrid.cxx b/src/Mod/Fem/App/VTKExtensions/vtkCleanUnstructuredGrid.cxx new file mode 100644 index 0000000000..94118758d7 --- /dev/null +++ b/src/Mod/Fem/App/VTKExtensions/vtkCleanUnstructuredGrid.cxx @@ -0,0 +1,680 @@ +// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen +// SPDX-FileCopyrightText: Copyright (c) Kitware, Inc. +// SPDX-License-Identifier: BSD-3-Clause + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#endif + +#include "vtkCleanUnstructuredGrid.h" +#include "vtkSMPTools.h" + +namespace +{ + +int GetDimension(unsigned char type) +{ + // For the most common cell types, this is a fast call. If the cell type is + // more exotic, then the cell must be grabbed and queried directly, which is + // slow. + switch (type) { + case VTK_EMPTY_CELL: + case VTK_VERTEX: + case VTK_POLY_VERTEX: + return 0; + case VTK_LINE: + case VTK_POLY_LINE: + case VTK_QUADRATIC_EDGE: + case VTK_CUBIC_LINE: + case VTK_LAGRANGE_CURVE: + case VTK_BEZIER_CURVE: + return 1; + case VTK_TRIANGLE: + case VTK_QUAD: + case VTK_PIXEL: + case VTK_POLYGON: + case VTK_TRIANGLE_STRIP: + case VTK_QUADRATIC_TRIANGLE: + case VTK_QUADRATIC_QUAD: + case VTK_QUADRATIC_POLYGON: + case VTK_BIQUADRATIC_QUAD: + case VTK_BIQUADRATIC_TRIANGLE: + case VTK_LAGRANGE_TRIANGLE: + case VTK_LAGRANGE_QUADRILATERAL: + case VTK_BEZIER_TRIANGLE: + case VTK_BEZIER_QUADRILATERAL: + return 2; + case VTK_TETRA: + case VTK_VOXEL: + case VTK_HEXAHEDRON: + case VTK_WEDGE: + case VTK_PYRAMID: + case VTK_PENTAGONAL_PRISM: + case VTK_HEXAGONAL_PRISM: + case VTK_QUADRATIC_TETRA: + case VTK_QUADRATIC_HEXAHEDRON: + case VTK_QUADRATIC_WEDGE: + case VTK_QUADRATIC_PYRAMID: + case VTK_BIQUADRATIC_QUADRATIC_HEXAHEDRON: + case VTK_BIQUADRATIC_QUADRATIC_WEDGE: + case VTK_TRIQUADRATIC_HEXAHEDRON: + case VTK_TRIQUADRATIC_PYRAMID: + case VTK_LAGRANGE_TETRAHEDRON: + case VTK_LAGRANGE_HEXAHEDRON: + case VTK_LAGRANGE_WEDGE: + case VTK_BEZIER_TETRAHEDRON: + case VTK_BEZIER_HEXAHEDRON: + case VTK_BEZIER_WEDGE: + return 3; + default: + vtkNew cell; + cell->SetCellType(type); + return cell->GetCellDimension(); + } +} + +constexpr unsigned char MAX_CELL_DIM = 3; + +void AllocatePointAttributes(vtkPointData* inPD, vtkPointData* outPD, vtkIdType nPoints) +{ + for (vtkIdType iArr = 0; iArr < inPD->GetNumberOfArrays(); ++iArr) { + auto inArr = inPD->GetAbstractArray(iArr); + auto outArr = outPD->GetAbstractArray(inArr->GetName()); + if (!outArr) { + vtkGenericWarningMacro(<< inArr->GetName() << " output array is nullptr"); + continue; + } + outArr->SetNumberOfComponents(inArr->GetNumberOfComponents()); + outArr->SetNumberOfTuples(nPoints); + } +} + +unsigned char GetTopologicalDimension(vtkDataSet* ds) +{ + vtkNew cTypes; + ds->GetCellTypes(cTypes); + unsigned char topoDim = 0; + for (vtkIdType iC = 0; iC < cTypes->GetNumberOfTypes(); ++iC) { + unsigned char dimC = static_cast(GetDimension(cTypes->GetCellType(iC))); + topoDim = std::max(topoDim, dimC); + if (topoDim >= MAX_CELL_DIM) { + break; + } + } + if (topoDim > MAX_CELL_DIM) { + vtkErrorWithObjectMacro( + nullptr, + "Topological dimension of data set is larger than the maximal cell dimension"); + return MAX_CELL_DIM; + } + return topoDim; +} + +struct WeighingStrategy +{ + virtual ~WeighingStrategy() = default; + virtual vtkSmartPointer ComputeWeights(vtkDataSet* ds, + const std::vector& ptMap) = 0; +}; + +struct FirstPointStrategy: public WeighingStrategy +{ + vtkSmartPointer ComputeWeights(vtkDataSet* ds, + const std::vector& ptMap) override + { + if (ds->GetNumberOfPoints() != static_cast(ptMap.size())) { + vtkGenericWarningMacro( + "Number of points in dataset and number of entries in point map don't line up."); + return nullptr; + } + vtkNew weights; + weights->SetNumberOfComponents(1); + weights->SetNumberOfTuples(ds->GetNumberOfPoints()); + weights->Fill(0.0); + + std::unordered_set firstPoints; + for (vtkIdType iP = 0; iP < ds->GetNumberOfPoints(); ++iP) { + if (firstPoints.find(ptMap[iP]) != firstPoints.end()) { + continue; + } + firstPoints.insert(ptMap[iP]); + weights->SetValue(iP, 1.0); + } + return weights; + } +}; + +struct AveragingStrategy: public WeighingStrategy +{ + vtkSmartPointer ComputeWeights(vtkDataSet* ds, + const std::vector& ptMap) override + { + if (ds->GetNumberOfPoints() != static_cast(ptMap.size())) { + vtkGenericWarningMacro( + "Number of points in dataset and number of entries in point map don't line up."); + return nullptr; + } + std::vector counts(ds->GetNumberOfPoints(), 0.0); + for (vtkIdType iP = 0; iP < ds->GetNumberOfPoints(); ++iP) { + if (ptMap[iP] < 0) { + continue; + } + counts[ptMap[iP]] += 1.0; + } + + vtkNew weights; + weights->SetNumberOfComponents(1); + weights->SetNumberOfTuples(ds->GetNumberOfPoints()); + weights->Fill(0.0); + + auto wRange = vtk::DataArrayValueRange<1>(weights); + for (vtkIdType iP = 0; iP < ds->GetNumberOfPoints(); ++iP) { + if (ptMap[iP] < 0) { + continue; + } + wRange[iP] = (counts[ptMap[iP]] ? 1.0 / counts[ptMap[iP]] : 0.0); + } + + return weights; + } +}; + +struct SpatialDensityStrategy: public WeighingStrategy +{ + vtkSmartPointer ComputeWeights(vtkDataSet* ds, + const std::vector& ptMap) override + { + if (ds->GetNumberOfPoints() != static_cast(ptMap.size())) { + vtkGenericWarningMacro( + "Number of points in dataset and number of entries in point map don't line up."); + return nullptr; + } + // Get topological dimension of data set + auto topoDim = GetTopologicalDimension(ds); + // Calculate cell measures + vtkSmartPointer measures; + { + // this scope is so that any extra memory related to the cell size filter gets released + // when no longer needed + vtkNew cellSizeFilter; + cellSizeFilter->SetInputData(ds); + cellSizeFilter->Update(); + auto output = vtkDataSet::SafeDownCast(cellSizeFilter->GetOutputDataObject(0)); + auto cData = output->GetCellData(); + if (!cData || !cData->HasArray("VertexCount") || !cData->HasArray("Length") + || !cData->HasArray("Area") || !cData->HasArray("Volume")) { + vtkErrorWithObjectMacro( + nullptr, + "Could not find correct cell data in output of cell size filter"); + return nullptr; + } + switch (topoDim) { + case 0: + measures = cData->GetArray("VertexCount"); + break; + case 1: + measures = cData->GetArray("Length"); + break; + case 2: + measures = cData->GetArray("Area"); + break; + case 3: + measures = cData->GetArray("Volume"); + break; + default: + vtkErrorWithObjectMacro(nullptr, + "Topological dimension of data set is higher than 3. " + "Cannot deal with that."); + return nullptr; + } + } + // Distribute spatial density to points + vtkNew density; + density->SetNumberOfComponents(1); + density->SetNumberOfTuples(ds->GetNumberOfPoints()); + density->Fill(0.0); + auto dRange = vtk::DataArrayValueRange<1>(density); + auto mRange = vtk::DataArrayValueRange<1>(measures); + if (ds->GetNumberOfCells() > 0) { + vtkNew pointIdList; + ds->GetCellPoints(0, pointIdList); + } + vtkSMPThreadLocalObject localPointIds; + auto distribute = [&](vtkIdType begin, vtkIdType end) { + for (vtkIdType iC = begin; iC < end; ++iC) { + ds->GetCellPoints(iC, localPointIds.Local()); + double participation = mRange[iC] / localPointIds.Local()->GetNumberOfIds(); + for (vtkIdType iP = 0; iP < localPointIds.Local()->GetNumberOfIds(); ++iP) { + dRange[localPointIds.Local()->GetId(iP)] += participation; + } + } + }; + // For thread safety + if (ds->GetNumberOfCells() > 0) { + vtkNew buffer; + ds->GetCellPoints(0, buffer); + } + distribute(0, ds->GetNumberOfCells()); + // Merits a dedicated struct with a reduce operation + // collisions occuring in the += operation + // vtkSMPTools::For(0, ds->GetNumberOfCells(), distribute); + // Normalize spatial densities with respect to point map + { + std::vector masses(*std::max_element(ptMap.begin(), ptMap.end()) + 1, 0); + auto computeMasses = [&dRange, &masses, &ptMap](vtkIdType begin, vtkIdType end) { + for (vtkIdType iP = begin; iP < end; ++iP) { + if (ptMap[iP] < 0) { + dRange[iP] = 0.0; + continue; + } + masses[ptMap[iP]] += dRange[iP]; + } + }; + computeMasses(0, ds->GetNumberOfPoints()); + // Merits a dedicated struct with a reduce operation + // collisions occuring in the += operation + // vtkSMPTools::For(0, ds->GetNumberOfPoints(), computeMasses); + vtkSMPTools::For(0, + ds->GetNumberOfPoints(), + [&dRange, &masses, &ptMap](vtkIdType begin, vtkIdType end) { + for (vtkIdType iP = begin; iP < end; ++iP) { + if (ptMap[iP] < 0) { + continue; + } + dRange[iP] = + (masses[ptMap[iP]] != 0 ? dRange[iP] / masses[ptMap[iP]] + : 0.0); + } + }); + } + return density; + } +}; + +struct WeighingStrategyFactory +{ + std::shared_ptr operator()(vtkCleanUnstructuredGrid::DataWeighingType dwt) + { + switch (dwt) { + case vtkCleanUnstructuredGrid::FIRST_POINT: + return std::make_shared(); + case vtkCleanUnstructuredGrid::AVERAGING: + return std::make_shared(); + case vtkCleanUnstructuredGrid::SPATIAL_DENSITY: + return std::make_shared(); + default: + vtkGenericWarningMacro("Incorrect weighing strategy type passed to factory. " + "defaulting to FIRST_POINT."); + return std::make_shared(); + } + } +}; + +struct WeighingWorklet +{ + template + void operator()(ArrayTypeIn* inArray, + ArrayTypeOut* outArray, + vtkDoubleArray* weights, + const std::vector& ptMap) + { + outArray->Fill(0); + auto inRange = vtk::DataArrayTupleRange(inArray); + auto outRange = vtk::DataArrayTupleRange(outArray); + auto wRange = vtk::DataArrayValueRange<1>(weights); + auto weighing = [&](vtkIdType begin, vtkIdType end) { + for (vtkIdType iP = begin; iP < end; ++iP) { + if (ptMap[iP] < 0) { + continue; + } + auto inT = inRange[iP]; + auto outT = outRange[ptMap[iP]]; + for (vtkIdType iT = 0; iT < inArray->GetNumberOfComponents(); ++iT) { + outT[iT] += wRange[iP] * inT[iT]; + } + } + }; + weighing(0, inArray->GetNumberOfTuples()); + // Merits a dedicated struct with a reduce operation + // collisions occuring in the += operation + // vtkSMPTools::For(0, inArray->GetNumberOfTuples(), weighing); + } +}; + +template<> +void WeighingWorklet::operator()(vtkBitArray* inArray, + vtkBitArray* outArray, + vtkDoubleArray* vtkNotUsed(weights), + const std::vector& ptMap) +{ + outArray->Fill(0); + for (vtkIdType iP = 0; iP < inArray->GetNumberOfValues(); ++iP) { + if (ptMap[iP] < 0) { + continue; + } + outArray->SetValue(ptMap[iP], inArray->GetValue(iP)); + } +} + +template<> +void WeighingWorklet::operator()(vtkStringArray* inArray, + vtkStringArray* outArray, + vtkDoubleArray* vtkNotUsed(weights), + const std::vector& ptMap) +{ + for (vtkIdType iP = 0; iP < inArray->GetNumberOfValues(); ++iP) { + if (ptMap[iP] < 0) { + continue; + } + outArray->SetValue(ptMap[iP], inArray->GetValue(iP)); + } +} + +template<> +void WeighingWorklet::operator()(vtkAbstractArray* inArray, + vtkAbstractArray* outArray, + vtkDoubleArray* vtkNotUsed(weights), + const std::vector& ptMap) +{ + for (vtkIdType iP = 0; iP < inArray->GetNumberOfTuples(); ++iP) { + if (ptMap[iP] < 0) { + continue; + } + outArray->InsertTuple(ptMap[iP], iP, inArray); + } +} + +void WeightAttributes(vtkPointData* inPD, + vtkPointData* outPD, + vtkDoubleArray* weights, + const std::vector& ptMap) +{ + // better here to use a Dispatch2BySameArrayType, but that doesn't exist + using Dispatcher = vtkArrayDispatch::Dispatch2BySameValueType; + WeighingWorklet worker; + for (vtkIdType iArr = 0; iArr < inPD->GetNumberOfArrays(); ++iArr) { + auto inArr = inPD->GetArray(iArr); + // if not data array check for abstract + if (!inArr) { + auto inAbsArr = inPD->GetAbstractArray(iArr); + if (!inAbsArr) { + vtkGenericWarningMacro("One of the arrays in the point data is nullptr."); + continue; + } + auto outAbsArr = outPD->GetAbstractArray(inAbsArr->GetName()); + if (!outAbsArr) { + vtkGenericWarningMacro("Output array " << inAbsArr->GetName() << " is nullptr."); + continue; + } + // if string array go to dedicated path + auto inStrArr = vtkStringArray::SafeDownCast(inAbsArr); + if (inStrArr) { + auto outStrArr = vtkStringArray::SafeDownCast(outAbsArr); + if (!outStrArr) { + vtkGenericWarningMacro("Output array " + << inStrArr->GetName() + << " is not the same type as input string array."); + continue; + } + worker(inStrArr, outStrArr, weights, ptMap); + continue; + } + worker(inAbsArr, outAbsArr, weights, ptMap); + continue; + } + auto outArr = outPD->GetArray(inArr->GetName()); + if (!outArr) { + vtkGenericWarningMacro("Output array " << inArr->GetName() + << " is nullptr or not a vtkDataArray."); + continue; + } + if (!Dispatcher::Execute(inArr, outArr, worker, weights, ptMap)) { + auto inBitArr = vtkBitArray::SafeDownCast(inArr); + auto outBitArr = vtkBitArray::SafeDownCast(outArr); + if (inBitArr && outBitArr) { + worker(inBitArr, outBitArr, weights, ptMap); + } + worker(inArr, outArr, weights, ptMap); + } + } +} + +} // namespace + +vtkStandardNewMacro(vtkCleanUnstructuredGrid); +vtkCxxSetSmartPointerMacro(vtkCleanUnstructuredGrid, Locator, vtkIncrementalPointLocator); + +//---------------------------------------------------------------------------- +vtkCleanUnstructuredGrid::vtkCleanUnstructuredGrid() = default; + +//---------------------------------------------------------------------------- +vtkCleanUnstructuredGrid::~vtkCleanUnstructuredGrid() = default; + +//---------------------------------------------------------------------------- +void vtkCleanUnstructuredGrid::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); + + if (this->Locator) { + os << indent << "Locator: "; + this->Locator->PrintSelf(os, indent.GetNextIndent()); + } + else { + os << indent << "Locator: none\n"; + } + os << indent << "ToleranceIsAbsolute: " << (this->ToleranceIsAbsolute ? "On\n" : "Off\n"); + os << indent << "Tolerance: " << this->Tolerance << std::endl; + os << indent << "AbsoluteTolerance: " << this->AbsoluteTolerance << std::endl; + os << indent + << "RemovePointsWithoutCells: " << (this->RemovePointsWithoutCells ? "On\n" : "Off\n"); + os << indent << "OutputPointsPrecision: " << this->OutputPointsPrecision << std::endl; +} + +//---------------------------------------------------------------------------- +int vtkCleanUnstructuredGrid::RequestData(vtkInformation* vtkNotUsed(request), + vtkInformationVector** inputVector, + vtkInformationVector* outputVector) +{ + vtkInformation* inInfo = inputVector[0]->GetInformationObject(0); + vtkInformation* outInfo = outputVector->GetInformationObject(0); + + vtkDataSet* input = vtkDataSet::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT())); + vtkUnstructuredGrid* output = + vtkUnstructuredGrid::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); + + if (input->GetNumberOfCells() == 0) { + // set up a ugrid with same data arrays as input, but + // no points, cells or data. + output->Allocate(1); + output->GetPointData()->CopyAllocate(input->GetPointData(), VTK_CELL_SIZE); + output->GetCellData()->CopyAllocate(input->GetCellData(), 1); + vtkNew pts; + output->SetPoints(pts); + return 1; + } + + output->GetPointData()->CopyAllocate(input->GetPointData()); + output->GetCellData()->PassData(input->GetCellData()); + + // First, create a new points array that eliminate duplicate points. + // Also create a mapping from the old point id to the new. + vtkNew newPts; + + // Set the desired precision for the points in the output. + if (this->OutputPointsPrecision == vtkAlgorithm::DEFAULT_PRECISION) { + // The logical behaviour would be to use the data type from the input. + // However, input is a vtkDataSet, which has no point data type; only the + // derived class vtkPointSet has a vtkPoints attribute, so only for that + // the logical practice can be applied, while for others (currently + // vtkImageData and vtkRectilinearGrid) the data type is the default + // for vtkPoints - which is VTK_FLOAT. + vtkPointSet* ps = vtkPointSet::SafeDownCast(input); + if (ps) { + newPts->SetDataType(ps->GetPoints()->GetDataType()); + } + } + else if (this->OutputPointsPrecision == vtkAlgorithm::SINGLE_PRECISION) { + newPts->SetDataType(VTK_FLOAT); + } + else if (this->OutputPointsPrecision == vtkAlgorithm::DOUBLE_PRECISION) { + newPts->SetDataType(VTK_DOUBLE); + } + + vtkIdType num = input->GetNumberOfPoints(); + vtkIdType id; + vtkIdType newId; + std::vector ptMap(num); + double pt[3]; + + this->CreateDefaultLocator(input); + if (this->ToleranceIsAbsolute) { + this->Locator->SetTolerance(this->AbsoluteTolerance); + } + else { + this->Locator->SetTolerance(this->Tolerance * input->GetLength()); + } + double bounds[6]; + input->GetBounds(bounds); + this->Locator->InitPointInsertion(newPts, bounds); + + vtkIdType progressStep = num / 100; + if (progressStep == 0) { + progressStep = 1; + } + + vtkNew pointCells; + for (id = 0; id < num; ++id) { + if (id % progressStep == 0) { + this->UpdateProgress(0.8 * ((float)id / num)); + } + + bool insert = true; + if (this->RemovePointsWithoutCells) { + input->GetPointCells(id, pointCells); + if (pointCells->GetNumberOfIds() == 0) { + insert = false; + } + } + + if (insert) { + input->GetPoint(id, pt); + this->Locator->InsertUniquePoint(pt, newId); + ptMap[id] = newId; + } + else { + // Strictly speaking, this is not needed + // as this is never accessed, but better not let + // an id undefined. + ptMap[id] = -1; + } + } + output->SetPoints(newPts); + + ::WeighingStrategyFactory factory; + auto strategy = factory(static_cast(this->PointDataWeighingStrategy)); + auto weights = strategy->ComputeWeights(input, ptMap); + auto inPD = input->GetPointData(); + auto outPD = output->GetPointData(); + ::AllocatePointAttributes(inPD, outPD, output->GetNumberOfPoints()); + ::WeightAttributes(inPD, outPD, weights, ptMap); + + // Now copy the cells. + vtkNew cellPoints; + num = input->GetNumberOfCells(); + output->Allocate(num); + for (id = 0; id < num; ++id) { + if (id % progressStep == 0) { + this->UpdateProgress(0.8 + 0.2 * (static_cast(id) / num)); + } + // special handling for polyhedron cells + if (vtkUnstructuredGrid::SafeDownCast(input) && input->GetCellType(id) == VTK_POLYHEDRON) { + vtkUnstructuredGrid::SafeDownCast(input)->GetFaceStream(id, cellPoints); + vtkUnstructuredGrid::ConvertFaceStreamPointIds(cellPoints, ptMap.data()); + } + else { + input->GetCellPoints(id, cellPoints); + for (int i = 0; i < cellPoints->GetNumberOfIds(); i++) { + int cellPtId = cellPoints->GetId(i); + newId = ptMap[cellPtId]; + cellPoints->SetId(i, newId); + } + } + output->InsertNextCell(input->GetCellType(id), cellPoints); + } + + output->Squeeze(); + return 1; +} + +//---------------------------------------------------------------------------- +int vtkCleanUnstructuredGrid::FillInputPortInformation(int vtkNotUsed(port), vtkInformation* info) +{ + info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet"); + return 1; +} + +//------------------------------------------------------------------------------ +vtkIncrementalPointLocator* vtkCleanUnstructuredGrid::GetLocator() +{ + return this->Locator; +} + +//---------------------------------------------------------------------------- +void vtkCleanUnstructuredGrid::CreateDefaultLocator(vtkDataSet* input) +{ + double tol; + if (this->ToleranceIsAbsolute) { + tol = this->AbsoluteTolerance; + } + else { + if (input) { + tol = this->Tolerance * input->GetLength(); + } + else { + tol = this->Tolerance; + } + } + + if (this->Locator.Get() == nullptr) { + if (tol == 0.0) { + this->Locator = vtkSmartPointer::New(); + } + else { + this->Locator = vtkSmartPointer::New(); + } + } + else { + // check that the tolerance wasn't changed from zero to non-zero + if ((tol > 0.0) && (this->GetLocator()->GetTolerance() == 0.0)) { + this->Locator = vtkSmartPointer::New(); + } + } +} diff --git a/src/Mod/Fem/App/VTKExtensions/vtkCleanUnstructuredGrid.h b/src/Mod/Fem/App/VTKExtensions/vtkCleanUnstructuredGrid.h new file mode 100644 index 0000000000..f3db74ce4d --- /dev/null +++ b/src/Mod/Fem/App/VTKExtensions/vtkCleanUnstructuredGrid.h @@ -0,0 +1,153 @@ +// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen +// SPDX-FileCopyrightText: Copyright (c) Kitware, Inc. +// SPDX-License-Identifier: BSD-3-Clause +/** + * @class vtkCleanUnstructuredGrid + * @brief merge duplicate points + * + * + * vtkCleanUnstructuredGrid is a filter that takes unstructured grid data as + * input and generates unstructured grid data as output. vtkCleanUnstructuredGrid can + * merge duplicate points (with coincident coordinates) using the vtkMergePoints object + * to merge points. + * + * @sa + * vtkCleanPolyData + */ + + +#ifndef vtkCleanUnstructuredGrid_h +#define vtkCleanUnstructuredGrid_h + +#include "vtkFiltersGeneralModule.h" // For export macro +#include "vtkSmartPointer.h" +#include "vtkUnstructuredGridAlgorithm.h" + +/*VTK_ABI_NAMESPACE_BEGIN*/ + +class vtkIncrementalPointLocator; +class vtkDataSet; + +class /*VTKFILTERSGENERAL_EXPORT*/ vtkCleanUnstructuredGrid: public vtkUnstructuredGridAlgorithm +{ +public: + static vtkCleanUnstructuredGrid* New(); + vtkTypeMacro(vtkCleanUnstructuredGrid, vtkUnstructuredGridAlgorithm); + void PrintSelf(ostream& os, vtkIndent indent) override; + + ///@{ + /** + * By default ToleranceIsAbsolute is false and Tolerance is + * a fraction of Bounding box diagonal, if true, AbsoluteTolerance is + * used when adding points to locator (merging) + */ + vtkSetMacro(ToleranceIsAbsolute, bool); + vtkBooleanMacro(ToleranceIsAbsolute, bool); + vtkGetMacro(ToleranceIsAbsolute, bool); + ///@} + + ///@{ + /** + * Specify tolerance in terms of fraction of bounding box length. + * Default is 0.0. + */ + vtkSetClampMacro(Tolerance, double, 0.0, 1.0); + vtkGetMacro(Tolerance, double); + ///@} + + ///@{ + /** + * Specify tolerance in absolute terms. Default is 1.0. + */ + vtkSetClampMacro(AbsoluteTolerance, double, 0.0, VTK_DOUBLE_MAX); + vtkGetMacro(AbsoluteTolerance, double); + ///@} + + ///@{ + /** + * Set/Get a spatial locator for speeding the search process. By + * default an instance of vtkMergePoints is used. + */ + virtual void SetLocator(vtkIncrementalPointLocator* locator); + virtual vtkIncrementalPointLocator* GetLocator(); + ///@} + + /** + * Create default locator. Used to create one when none is specified. + */ + void CreateDefaultLocator(vtkDataSet* input = nullptr); + + /** + * Release locator + */ + void ReleaseLocator() + { + this->SetLocator(nullptr); + } + + ///@{ + /** + * Set/get the desired precision for the output types. See the documentation + * for the vtkAlgorithm::DesiredOutputPrecision enum for an explanation of + * the available precision settings. + */ + vtkSetMacro(OutputPointsPrecision, int); + vtkGetMacro(OutputPointsPrecision, int); + ///@} + + ///@{ + /** + * Set/Get whether to remove points that do not + * have any cells associated with it. + * Default is false + */ + vtkSetMacro(RemovePointsWithoutCells, bool); + vtkGetMacro(RemovePointsWithoutCells, bool); + vtkBooleanMacro(RemovePointsWithoutCells, bool); + ///@} + + ///@{ + /** + * Set/Get the strategy used to weigh point data on merging points + * + * Possibilities: + * - FIRST_POINT (int(0), default): the point with the lowest index imposes its data on to the + * merged point + * - AVERAGING (int(1)): a number average is performed on all the duplicate points + * - SPATIAL_DENSITY (int(2)): an average by attached cell volume (i.e. for every cell the point + * is connected to sum cell_volume/number_cell_points) is performed on the point data + */ + vtkGetMacro(PointDataWeighingStrategy, int); + vtkSetClampMacro(PointDataWeighingStrategy, int, FIRST_POINT, NUMBER_OF_WEIGHING_TYPES - 1); + ///@} + + enum DataWeighingType + { + FIRST_POINT = 0, + AVERAGING, + SPATIAL_DENSITY, + NUMBER_OF_WEIGHING_TYPES + }; + +protected: + vtkCleanUnstructuredGrid(); + ~vtkCleanUnstructuredGrid() override; + + bool ToleranceIsAbsolute = false; + double Tolerance = 0.0; + double AbsoluteTolerance = 1.0; + bool RemovePointsWithoutCells = false; + vtkSmartPointer Locator; + int OutputPointsPrecision = vtkAlgorithm::DEFAULT_PRECISION; + int PointDataWeighingStrategy = FIRST_POINT; + + int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override; + int FillInputPortInformation(int port, vtkInformation* info) override; + +private: + vtkCleanUnstructuredGrid(const vtkCleanUnstructuredGrid&) = delete; + void operator=(const vtkCleanUnstructuredGrid&) = delete; +}; +/*VTK_ABI_NAMESPACE_END*/ +#endif +// VTK-HeaderTest-Exclude: vtkCleanUnstructuredGrid.h diff --git a/src/Mod/Fem/App/VTKExtensions/vtkSMPTools.h b/src/Mod/Fem/App/VTKExtensions/vtkSMPTools.h new file mode 100644 index 0000000000..611860721b --- /dev/null +++ b/src/Mod/Fem/App/VTKExtensions/vtkSMPTools.h @@ -0,0 +1,604 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkSMPTools.h + + 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. + +=========================================================================*/ +/** + * @class vtkSMPTools + * @brief A set of parallel (multi-threaded) utility functions. + * + * vtkSMPTools provides a set of utility functions that can + * be used to parallelize parts of VTK code using multiple threads. + * There are several back-end implementations of parallel functionality + * (currently Sequential, TBB, OpenMP and STDThread) that actual execution is + * delegated to. + * + * @sa + * vtkSMPThreadLocal + * vtkSMPThreadLocalObject + */ + +#ifndef vtkSMPTools_h +#define vtkSMPTools_h + +#include "vtkCommonCoreModule.h" // For export macro +#include "vtkObject.h" + +#include "SMP/Common/vtkSMPToolsAPI.h" +#include "vtkSMPThreadLocal.h" // For Initialized + +#include // For std::function +#include // For std::iterator +#include // For std:::enable_if + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +namespace vtk +{ +namespace detail +{ +namespace smp +{ +template +class vtkSMPTools_Has_Initialize +{ + typedef char (&no_type)[1]; + typedef char (&yes_type)[2]; + template + struct V + { + }; + template + static yes_type check(V*); + template + static no_type check(...); + +public: + static bool const value = sizeof(check(nullptr)) == sizeof(yes_type); +}; + +template +class vtkSMPTools_Has_Initialize_const +{ + typedef char (&no_type)[1]; + typedef char (&yes_type)[2]; + template + struct V + { + }; + template + static yes_type check(V*); + template + static no_type check(...); + +public: + static bool const value = sizeof(check(0)) == sizeof(yes_type); +}; + +template +struct vtkSMPTools_FunctorInternal; + +template +struct vtkSMPTools_FunctorInternal +{ + Functor& F; + vtkSMPTools_FunctorInternal(Functor& f) + : F(f) + {} + void Execute(vtkIdType first, vtkIdType last) + { + this->F(first, last); + } + void For(vtkIdType first, vtkIdType last, vtkIdType grain) + { + auto& SMPToolsAPI = vtkSMPToolsAPI::GetInstance(); + SMPToolsAPI.For(first, last, grain, *this); + } + vtkSMPTools_FunctorInternal& + operator=(const vtkSMPTools_FunctorInternal&); + vtkSMPTools_FunctorInternal(const vtkSMPTools_FunctorInternal&); +}; + +template +struct vtkSMPTools_FunctorInternal +{ + Functor& F; + vtkSMPThreadLocal Initialized; + vtkSMPTools_FunctorInternal(Functor& f) + : F(f) + , Initialized(0) + {} + void Execute(vtkIdType first, vtkIdType last) + { + unsigned char& inited = this->Initialized.Local(); + if (!inited) { + this->F.Initialize(); + inited = 1; + } + this->F(first, last); + } + void For(vtkIdType first, vtkIdType last, vtkIdType grain) + { + auto& SMPToolsAPI = vtkSMPToolsAPI::GetInstance(); + SMPToolsAPI.For(first, last, grain, *this); + this->F.Reduce(); + } + vtkSMPTools_FunctorInternal& + operator=(const vtkSMPTools_FunctorInternal&); + vtkSMPTools_FunctorInternal(const vtkSMPTools_FunctorInternal&); +}; + +template +class vtkSMPTools_Lookup_For +{ + static bool const init = vtkSMPTools_Has_Initialize::value; + +public: + typedef vtkSMPTools_FunctorInternal type; +}; + +template +class vtkSMPTools_Lookup_For +{ + static bool const init = vtkSMPTools_Has_Initialize_const::value; + +public: + typedef vtkSMPTools_FunctorInternal type; +}; + +template +struct vtkSMPTools_RangeFunctor; + +template +struct vtkSMPTools_RangeFunctor +{ + Functor& F; + Iterator& Begin; + vtkSMPTools_RangeFunctor(Iterator& begin, Functor& f) + : F(f) + , Begin(begin) + {} + void operator()(vtkIdType first, vtkIdType last) + { + Iterator itFirst(Begin); + std::advance(itFirst, first); + Iterator itLast(itFirst); + std::advance(itLast, last - first); + this->F(itFirst, itLast); + } +}; + +template +struct vtkSMPTools_RangeFunctor +{ + Functor& F; + Iterator& Begin; + vtkSMPTools_RangeFunctor(Iterator& begin, Functor& f) + : F(f) + , Begin(begin) + {} + void Initialize() + { + this->F.Initialize(); + } + void operator()(vtkIdType first, vtkIdType last) + { + Iterator itFirst(Begin); + std::advance(itFirst, first); + Iterator itLast(itFirst); + std::advance(itLast, last - first); + this->F(itFirst, itLast); + } + void Reduce() + { + this->F.Reduce(); + } +}; + +template +class vtkSMPTools_Lookup_RangeFor +{ + static bool const init = vtkSMPTools_Has_Initialize::value; + +public: + typedef vtkSMPTools_RangeFunctor type; +}; + +template +class vtkSMPTools_Lookup_RangeFor +{ + static bool const init = vtkSMPTools_Has_Initialize_const::value; + +public: + typedef vtkSMPTools_RangeFunctor type; +}; + +template +using resolvedNotInt = typename std::enable_if::value, void>::type; +} // namespace smp +} // namespace detail +} // namespace vtk +#endif // DOXYGEN_SHOULD_SKIP_THIS + +class VTKCOMMONCORE_EXPORT vtkSMPTools +{ +public: + ///@{ + /** + * Execute a for operation in parallel. First and last + * define the range over which to operate (which is defined + * by the operator). The operation executed is defined by + * operator() of the functor object. The grain gives the parallel + * engine a hint about the coarseness over which to parallelize + * the function (as defined by last-first of each execution of + * operator() ). + */ + template + static void For(vtkIdType first, vtkIdType last, vtkIdType grain, Functor& f) + { + typename vtk::detail::smp::vtkSMPTools_Lookup_For::type fi(f); + fi.For(first, last, grain); + } + + template + static void For(vtkIdType first, vtkIdType last, vtkIdType grain, Functor const& f) + { + typename vtk::detail::smp::vtkSMPTools_Lookup_For::type fi(f); + fi.For(first, last, grain); + } + ///@} + + ///@{ + /** + * Execute a for operation in parallel. First and last + * define the range over which to operate (which is defined + * by the operator). The operation executed is defined by + * operator() of the functor object. The grain gives the parallel + * engine a hint about the coarseness over which to parallelize + * the function (as defined by last-first of each execution of + * operator() ). Uses a default value for the grain. + */ + template + static void For(vtkIdType first, vtkIdType last, Functor& f) + { + vtkSMPTools::For(first, last, 0, f); + } + + template + static void For(vtkIdType first, vtkIdType last, Functor const& f) + { + vtkSMPTools::For(first, last, 0, f); + } + ///@} + + ///@{ + /** + * Execute a for operation in parallel. Begin and end iterators + * define the range over which to operate (which is defined + * by the operator). The operation executed is defined by + * operator() of the functor object. The grain gives the parallel + * engine a hint about the coarseness over which to parallelize + * the function (as defined by last-first of each execution of + * operator() ). + * + * Usage example: + * \code + * template + * class ExampleFunctor + * { + * void operator()(IteratorT begin, IteratorT end) + * { + * for (IteratorT it = begin; it != end; ++it) + * { + * // Do stuff + * } + * } + * }; + * ExampleFunctor::iterator> worker; + * vtkSMPTools::For(container.begin(), container.end(), 5, worker); + * \endcode + * + * Lambda are also supported through Functor const& + * function overload: + * \code + * vtkSMPTools::For(container.begin(), container.end(), 5, + * [](std::set::iterator begin, std::set::iterator end) { + * // Do stuff + * }); + * \endcode + */ + template + static vtk::detail::smp::resolvedNotInt + For(Iter begin, Iter end, vtkIdType grain, Functor& f) + { + vtkIdType size = std::distance(begin, end); + typename vtk::detail::smp::vtkSMPTools_Lookup_RangeFor::type fi(begin, f); + vtkSMPTools::For(0, size, grain, fi); + } + + template + static vtk::detail::smp::resolvedNotInt + For(Iter begin, Iter end, vtkIdType grain, Functor const& f) + { + vtkIdType size = std::distance(begin, end); + typename vtk::detail::smp::vtkSMPTools_Lookup_RangeFor::type fi(begin, + f); + vtkSMPTools::For(0, size, grain, fi); + } + ///@} + + ///@{ + /** + * Execute a for operation in parallel. Begin and end iterators + * define the range over which to operate (which is defined + * by the operator). The operation executed is defined by + * operator() of the functor object. Uses a default value + * for the grain. + * + * Usage example: + * \code + * template + * class ExampleFunctor + * { + * void operator()(IteratorT begin, IteratorT end) + * { + * for (IteratorT it = begin; it != end; ++it) + * { + * // Do stuff + * } + * } + * }; + * ExampleFunctor::iterator> worker; + * vtkSMPTools::For(container.begin(), container.end(), worker); + * \endcode + * + * Lambda are also supported through Functor const& + * function overload: + * \code + * vtkSMPTools::For(container.begin(), container.end(), + * [](std::set::iterator begin, std::set::iterator end) { + * // Do stuff + * }); + * \endcode + */ + template + static vtk::detail::smp::resolvedNotInt For(Iter begin, Iter end, Functor& f) + { + vtkSMPTools::For(begin, end, 0, f); + } + + template + static vtk::detail::smp::resolvedNotInt For(Iter begin, Iter end, Functor const& f) + { + vtkSMPTools::For(begin, end, 0, f); + } + ///@} + + /** + * Get the backend in use. + */ + static const char* GetBackend(); + + /** + * /!\ This method is not thread safe. + * Change the backend in use. + * The options can be: "Sequential", "STDThread", "TBB" or "OpenMP" + * + * VTK_SMP_BACKEND_IN_USE env variable can also be used to set the default SMPTools + * backend, in that case SetBackend() doesn't need to be called. + * The backend selected with SetBackend() have the priority over VTK_SMP_BACKEND_IN_USE. + * + * SetBackend() will return true if the backend was found and available. + */ + static bool SetBackend(const char* backend); + + /** + * /!\ This method is not thread safe. + * Initialize the underlying libraries for execution. This is + * not required as it is automatically defined by the libaries. + * However, it can be used to control the maximum number of thread used. + * Make sure to call it before the parallel operation. + * + * If Initialize is called without argument it will reset + * to the maximum number of threads or use the VTK_SMP_MAX_THREADS + * env variable if it is defined. + * + * Note: If VTK_SMP_MAX_THREADS env variable is defined the SMPTools will try + * to use it to set the maximum number of threads. Initialize() doesn't + * need to be called. + */ + static void Initialize(int numThreads = 0); + + /** + * Get the estimated number of threads being used by the backend. + * This should be used as just an estimate since the number of threads may + * vary dynamically and a particular task may not be executed on all the + * available threads. + */ + static int GetEstimatedNumberOfThreads(); + + /** + * /!\ This method is not thread safe. + * If true enable nested parallelism for underlying backends. + * When enabled the comportement is different for each backend: + * - TBB support nested parallelism by default. + * - For OpenMP, we set `omp_set_nested` to true so that it is supported. + * - STDThread support nested parallelism by creating new threads pools. + * - For Sequential nothing changes. + * + * Default to true + */ + static void SetNestedParallelism(bool isNested); + + /** + * Get true if the nested parallelism is enabled. + */ + static bool GetNestedParallelism(); + + /** + * Return true if it is called from a parallel scope. + */ + static bool IsParallelScope(); + + /** + * Structure used to specify configuration for LocalScope() method. + * Several parameters can be configured: + * - MaxNumberOfThreads set the maximum number of threads. + * - Backend set a specific SMPTools backend. + * - NestedParallelism, if true enable nested parallelism. + */ + struct Config + { + int MaxNumberOfThreads = 0; + std::string Backend = vtk::detail::smp::vtkSMPToolsAPI::GetInstance().GetBackend(); + bool NestedParallelism = true; + + Config() + {} + Config(int maxNumberOfThreads) + : MaxNumberOfThreads(maxNumberOfThreads) + {} + Config(std::string backend) + : Backend(backend) + {} + Config(bool nestedParallelism) + : NestedParallelism(nestedParallelism) + {} + Config(int maxNumberOfThreads, std::string backend, bool nestedParallelism) + : MaxNumberOfThreads(maxNumberOfThreads) + , Backend(backend) + , NestedParallelism(nestedParallelism) + {} +#ifndef DOXYGEN_SHOULD_SKIP_THIS + Config(vtk::detail::smp::vtkSMPToolsAPI& API) + : MaxNumberOfThreads(API.GetInternalDesiredNumberOfThread()) + , Backend(API.GetBackend()) + , NestedParallelism(API.GetNestedParallelism()) + {} +#endif // DOXYGEN_SHOULD_SKIP_THIS + }; + + /** + * /!\ This method is not thread safe. + * Change the number of threads locally within this scope and call a functor which + * should contains a vtkSMPTools method. + * + * Usage example: + * \code + * vtkSMPTools::LocalScope( + * vtkSMPTools::Config{ 4, "OpenMP", false }, [&]() { vtkSMPTools::For(0, size, worker); }); + * \endcode + */ + template + static void LocalScope(Config const& config, T&& lambda) + { + auto& SMPToolsAPI = vtk::detail::smp::vtkSMPToolsAPI::GetInstance(); + SMPToolsAPI.LocalScope(config, lambda); + } + + /** + * A convenience method for transforming data. It is a drop in replacement for + * std::transform(), it does a unary operation on the input ranges. The data array must have the + * same length. The performed transformation is defined by operator() of the functor object. + * + * Usage example with vtkDataArray: + * \code + * const auto range0 = vtk::DataArrayValueRange<1>(array0); + * auto range1 = vtk::DataArrayValueRange<1>(array1); + * vtkSMPTools::Transform( + * range0.cbegin(), range0.cend(), range1.begin(), [](double x) { return x - 1; }); + * \endcode + * + * Please visit vtkDataArrayRange.h documentation for more information and optimisation. + */ + template + static void Transform(InputIt inBegin, InputIt inEnd, OutputIt outBegin, Functor transform) + { + auto& SMPToolsAPI = vtk::detail::smp::vtkSMPToolsAPI::GetInstance(); + SMPToolsAPI.Transform(inBegin, inEnd, outBegin, transform); + } + + /** + * A convenience method for transforming data. It is a drop in replacement for + * std::transform(), it does a binary operation on the input ranges. The data array must have + * the same length. The performed transformation is defined by operator() of the functor object. + * + * Usage example with vtkDataArray: + * \code + * const auto range0 = vtk::DataArrayValueRange<1>(array0); + * auto range1 = vtk::DataArrayValueRange<1>(array1); + * vtkSMPTools::Transform( + * range0.cbegin(), range0.cend(), range1.cbegin(), range1.begin(), + * [](double x, double y) { return x * y; }); + * \endcode + * + * Please visit vtkDataArrayRange.h documentation for more information and optimisation. + */ + template + static void Transform(InputIt1 inBegin1, + InputIt1 inEnd, + InputIt2 inBegin2, + OutputIt outBegin, + Functor transform) + { + auto& SMPToolsAPI = vtk::detail::smp::vtkSMPToolsAPI::GetInstance(); + SMPToolsAPI.Transform(inBegin1, inEnd, inBegin2, outBegin, transform); + } + + /** + * A convenience method for filling data. It is a drop in replacement for std::fill(), + * it assign the given value to the element in ranges. + * + * Usage example with vtkDataArray: + * \code + * // Fill range with its first tuple value + * auto range = vtk::DataArrayTupleRange<1>(array); + * const auto value = *range.begin(); + * vtkSMPTools::Fill(range.begin(), range.end(), value); + * \endcode + * + * Please visit vtkDataArrayRange.h documentation for more information and optimisation. + */ + template + static void Fill(Iterator begin, Iterator end, const T& value) + { + auto& SMPToolsAPI = vtk::detail::smp::vtkSMPToolsAPI::GetInstance(); + SMPToolsAPI.Fill(begin, end, value); + } + + /** + * A convenience method for sorting data. It is a drop in replacement for + * std::sort(). Under the hood different methods are used. For example, + * tbb::parallel_sort is used in TBB. + */ + template + static void Sort(RandomAccessIterator begin, RandomAccessIterator end) + { + auto& SMPToolsAPI = vtk::detail::smp::vtkSMPToolsAPI::GetInstance(); + SMPToolsAPI.Sort(begin, end); + } + + /** + * A convenience method for sorting data. It is a drop in replacement for + * std::sort(). Under the hood different methods are used. For example, + * tbb::parallel_sort is used in TBB. This version of Sort() takes a + * comparison class. + */ + template + static void Sort(RandomAccessIterator begin, RandomAccessIterator end, Compare comp) + { + auto& SMPToolsAPI = vtk::detail::smp::vtkSMPToolsAPI::GetInstance(); + SMPToolsAPI.Sort(begin, end, comp); + } +}; + +#endif +// VTK-HeaderTest-Exclude: vtkSMPTools.h