Merge pull request #23823 from marioalexis84/fem-remove_elmer_filter_parameter
Fem: Remove dependency of the ViewProviderFemPostObject class on user parameters
This commit is contained in:
@@ -82,6 +82,20 @@ if(BUILD_FEM_VTK)
|
||||
FemVTKTools.cpp
|
||||
)
|
||||
SOURCE_GROUP("PostObjects" FILES ${FemPost_SRCS})
|
||||
|
||||
SET(FemVTK_SRCS
|
||||
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)
|
||||
|
||||
|
||||
@@ -177,6 +191,7 @@ SET(Fem_SRCS
|
||||
${FemConstraints_SRCS}
|
||||
${FemPost_SRCS}
|
||||
${FemSet_SRCS}
|
||||
${FemVTK_SRCS}
|
||||
${Mod_SRCS}
|
||||
${Python_SRCS}
|
||||
)
|
||||
@@ -196,6 +211,7 @@ target_include_directories(
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_BINARY_DIR}/src
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include <vtkXMLUnstructuredGridReader.h>
|
||||
#include <vtkXMLMultiBlockDataReader.h>
|
||||
#include <vtkMultiBlockDataSet.h>
|
||||
#include <vtkStreamingDemandDrivenPipeline.h>
|
||||
#include <vtkFloatArray.h>
|
||||
#include <vtkStringArray.h>
|
||||
#include <vtkInformation.h>
|
||||
@@ -58,145 +57,6 @@ using namespace Fem;
|
||||
using namespace App;
|
||||
|
||||
|
||||
vtkStandardNewMacro(FemFrameSourceAlgorithm);
|
||||
|
||||
FemFrameSourceAlgorithm::FemFrameSourceAlgorithm::FemFrameSourceAlgorithm()
|
||||
{
|
||||
// we are a source
|
||||
SetNumberOfInputPorts(0);
|
||||
SetNumberOfOutputPorts(1);
|
||||
}
|
||||
|
||||
|
||||
FemFrameSourceAlgorithm::FemFrameSourceAlgorithm::~FemFrameSourceAlgorithm()
|
||||
{}
|
||||
|
||||
void FemFrameSourceAlgorithm::setDataObject(vtkSmartPointer<vtkDataObject> data)
|
||||
{
|
||||
m_data = data;
|
||||
Modified();
|
||||
Update();
|
||||
}
|
||||
|
||||
bool FemFrameSourceAlgorithm::isValid()
|
||||
{
|
||||
return m_data.GetPointer() != nullptr;
|
||||
}
|
||||
|
||||
std::vector<double> FemFrameSourceAlgorithm::getFrameValues()
|
||||
{
|
||||
|
||||
// check if we have frame data
|
||||
if (!m_data || !m_data->IsA("vtkMultiBlockDataSet")) {
|
||||
return std::vector<double>();
|
||||
}
|
||||
|
||||
// we have multiple frames! let's check the amount and times
|
||||
vtkSmartPointer<vtkMultiBlockDataSet> multiblock = vtkMultiBlockDataSet::SafeDownCast(m_data);
|
||||
|
||||
unsigned long nblocks = multiblock->GetNumberOfBlocks();
|
||||
std::vector<double> tFrames(nblocks);
|
||||
|
||||
for (unsigned long i = 0; i < nblocks; i++) {
|
||||
|
||||
vtkDataObject* block = multiblock->GetBlock(i);
|
||||
// check if the TimeValue field is available
|
||||
if (!block->GetFieldData()->HasArray("TimeValue")) {
|
||||
// a frame with no valid value is a undefined state
|
||||
return std::vector<double>();
|
||||
}
|
||||
|
||||
// store the time value!
|
||||
vtkDataArray* TimeValue = block->GetFieldData()->GetArray("TimeValue");
|
||||
if (!TimeValue->IsA("vtkFloatArray") || TimeValue->GetNumberOfTuples() < 1) {
|
||||
// a frame with no valid value is a undefined state
|
||||
return std::vector<double>();
|
||||
}
|
||||
|
||||
tFrames[i] = vtkFloatArray::SafeDownCast(TimeValue)->GetValue(0);
|
||||
}
|
||||
|
||||
return tFrames;
|
||||
}
|
||||
|
||||
int FemFrameSourceAlgorithm::RequestInformation(vtkInformation* reqInfo,
|
||||
vtkInformationVector** inVector,
|
||||
vtkInformationVector* outVector)
|
||||
{
|
||||
|
||||
// setup default information
|
||||
if (!this->Superclass::RequestInformation(reqInfo, inVector, outVector)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!m_data) {
|
||||
// for the no data case we would return a empty data set in RequestData.
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<double> frames = getFrameValues();
|
||||
|
||||
if (frames.empty()) {
|
||||
// no frames, default info is sufficient
|
||||
return 1;
|
||||
}
|
||||
|
||||
double tRange[2] = {frames.front(), frames.back()};
|
||||
|
||||
// finally set the time info!
|
||||
vtkInformation* info = outVector->GetInformationObject(0);
|
||||
info->Set(vtkStreamingDemandDrivenPipeline::TIME_RANGE(), tRange, 2);
|
||||
info->Set(vtkStreamingDemandDrivenPipeline::TIME_STEPS(), &frames[0], frames.size());
|
||||
info->Set(CAN_HANDLE_PIECE_REQUEST(), 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int FemFrameSourceAlgorithm::RequestData(vtkInformation*,
|
||||
vtkInformationVector**,
|
||||
vtkInformationVector* outVector)
|
||||
{
|
||||
vtkInformation* outInfo = outVector->GetInformationObject(0);
|
||||
vtkUnstructuredGrid* output =
|
||||
vtkUnstructuredGrid::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));
|
||||
|
||||
if (!output) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!m_data) {
|
||||
outInfo->Set(vtkDataObject::DATA_OBJECT(), vtkUnstructuredGrid::New());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!m_data->IsA("vtkMultiBlockDataSet")) {
|
||||
// no multi frame data, return directly
|
||||
outInfo->Set(vtkDataObject::DATA_OBJECT(), m_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vtkSmartPointer<vtkMultiBlockDataSet> multiblock = vtkMultiBlockDataSet::SafeDownCast(m_data);
|
||||
// find the block asked for (lazy implementation)
|
||||
unsigned long idx = 0;
|
||||
if (outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP())) {
|
||||
auto time = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP());
|
||||
auto frames = getFrameValues();
|
||||
|
||||
// we have float values, so be aware of rounding errors. lets subtract the searched time and
|
||||
// then use the smallest value
|
||||
for (auto& frame : frames) {
|
||||
frame = std::abs(frame - time);
|
||||
}
|
||||
|
||||
auto it = std::ranges::min_element(frames);
|
||||
idx = std::distance(frames.begin(), it);
|
||||
}
|
||||
|
||||
auto block = multiblock->GetBlock(idx);
|
||||
output->ShallowCopy(block);
|
||||
return 1;
|
||||
}
|
||||
|
||||
PROPERTY_SOURCE_WITH_EXTENSIONS(Fem::FemPostPipeline, Fem::FemPostObject)
|
||||
|
||||
FemPostPipeline::FemPostPipeline()
|
||||
@@ -210,9 +70,17 @@ FemPostPipeline::FemPostPipeline()
|
||||
App::Prop_None,
|
||||
"The frame used to calculate the data in the pipeline processing (read only, "
|
||||
"set via pipeline object).");
|
||||
ADD_PROPERTY_TYPE(MergeDuplicate,
|
||||
(false),
|
||||
"Pipeline",
|
||||
App::Prop_None,
|
||||
"Remove coindent elements.");
|
||||
|
||||
// create our source algorithm
|
||||
m_source_algorithm = vtkSmartPointer<FemFrameSourceAlgorithm>::New();
|
||||
m_source_algorithm = vtkSmartPointer<vtkFemFrameSourceAlgorithm>::New();
|
||||
m_clean_filter = vtkSmartPointer<vtkCleanUnstructuredGrid>::New();
|
||||
|
||||
m_clean_filter->SetPointDataWeighingStrategy(vtkCleanUnstructuredGrid::AVERAGING);
|
||||
m_transform_filter->SetInputConnection(m_source_algorithm->GetOutputPort(0));
|
||||
}
|
||||
|
||||
@@ -412,6 +280,19 @@ void FemPostPipeline::onChanged(const Property* prop)
|
||||
recomputeChildren();
|
||||
}
|
||||
|
||||
if (prop == &MergeDuplicate) {
|
||||
if (MergeDuplicate.getValue()) {
|
||||
m_clean_filter->SetInputConnection(m_source_algorithm->GetOutputPort(0));
|
||||
m_transform_filter->SetInputConnection(m_clean_filter->GetOutputPort(0));
|
||||
}
|
||||
else {
|
||||
m_transform_filter->SetInputConnection(m_source_algorithm->GetOutputPort(0));
|
||||
}
|
||||
m_transform_filter->Update();
|
||||
updateData();
|
||||
recomputeChildren();
|
||||
}
|
||||
|
||||
// use the correct data as source
|
||||
if (prop == &Data && !m_block_property) {
|
||||
m_source_algorithm->setDataObject(Data.getValue());
|
||||
|
||||
@@ -30,44 +30,19 @@
|
||||
#include "FemPostFunction.h"
|
||||
#include "FemPostObject.h"
|
||||
#include "FemResultObject.h"
|
||||
#include "VTKExtensions/vtkFemFrameSourceAlgorithm.h"
|
||||
|
||||
#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 3, 0)
|
||||
#include "VTKExtensions/vtkCleanUnstructuredGrid.h"
|
||||
#else
|
||||
#include <vtkCleanUnstructuredGrid.h>
|
||||
#endif
|
||||
#include <vtkSmartPointer.h>
|
||||
#include <vtkUnstructuredGridAlgorithm.h>
|
||||
|
||||
class vtkInformation;
|
||||
class vtkInformationVector;
|
||||
|
||||
|
||||
namespace Fem
|
||||
{
|
||||
|
||||
// algorithm that allows multi frame handling: if data is stored in MultiBlock dataset
|
||||
// this source enables the downstream filters to query the blocks as different time frames
|
||||
class FemFrameSourceAlgorithm: public vtkUnstructuredGridAlgorithm
|
||||
{
|
||||
public:
|
||||
static FemFrameSourceAlgorithm* New();
|
||||
vtkTypeMacro(FemFrameSourceAlgorithm, vtkUnstructuredGridAlgorithm);
|
||||
|
||||
bool isValid();
|
||||
void setDataObject(vtkSmartPointer<vtkDataObject> data);
|
||||
std::vector<double> getFrameValues();
|
||||
|
||||
protected:
|
||||
FemFrameSourceAlgorithm();
|
||||
~FemFrameSourceAlgorithm() override;
|
||||
|
||||
vtkSmartPointer<vtkDataObject> m_data;
|
||||
|
||||
int RequestInformation(vtkInformation* reqInfo,
|
||||
vtkInformationVector** inVector,
|
||||
vtkInformationVector* outVector) override;
|
||||
int RequestData(vtkInformation* reqInfo,
|
||||
vtkInformationVector** inVector,
|
||||
vtkInformationVector* outVector) override;
|
||||
};
|
||||
|
||||
|
||||
class FemExport FemPostPipeline: public Fem::FemPostObject, public Fem::FemPostGroupExtension
|
||||
{
|
||||
PROPERTY_HEADER_WITH_EXTENSIONS(Fem::FemPostPipeline);
|
||||
@@ -77,7 +52,7 @@ public:
|
||||
FemPostPipeline();
|
||||
|
||||
App::PropertyEnumeration Frame;
|
||||
|
||||
App::PropertyBool MergeDuplicate;
|
||||
|
||||
virtual vtkDataSet* getDataSet() override;
|
||||
Fem::FemPostFunctionProvider* getFunctionProvider();
|
||||
@@ -136,7 +111,9 @@ protected:
|
||||
|
||||
private:
|
||||
App::Enumeration m_frameEnum;
|
||||
vtkSmartPointer<FemFrameSourceAlgorithm> m_source_algorithm;
|
||||
|
||||
vtkSmartPointer<vtkFemFrameSourceAlgorithm> m_source_algorithm;
|
||||
vtkSmartPointer<vtkCleanUnstructuredGrid> m_clean_filter;
|
||||
|
||||
bool m_block_property = false;
|
||||
bool m_data_updated = false;
|
||||
|
||||
@@ -146,10 +146,14 @@
|
||||
#include <gp_Vec.hxx>
|
||||
|
||||
// VTK
|
||||
#include <vtkVersionMacros.h>
|
||||
#include <vtkAlgorithmOutput.h>
|
||||
#include <vtkAppendFilter.h>
|
||||
#include <vtkArrayCalculator.h>
|
||||
#include <vtkCellArray.h>
|
||||
#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 0)
|
||||
#include <vtkCleanUnstructuredGrid.h>
|
||||
#endif
|
||||
#include <vtkCompositeDataSet.h>
|
||||
#include <vtkDataArray.h>
|
||||
#include <vtkDataSetReader.h>
|
||||
|
||||
680
src/Mod/Fem/App/VTKExtensions/vtkCleanUnstructuredGrid.cxx
Normal file
680
src/Mod/Fem/App/VTKExtensions/vtkCleanUnstructuredGrid.cxx
Normal file
@@ -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 <vtkArrayDispatch.h>
|
||||
#include <vtkArrayDispatchArrayList.h>
|
||||
#include <vtkBitArray.h>
|
||||
#include <vtkCell.h>
|
||||
#include <vtkCellData.h>
|
||||
#include <vtkCellSizeFilter.h>
|
||||
#include <vtkCellTypes.h>
|
||||
#include <vtkCollection.h>
|
||||
#include <vtkDataArrayRange.h>
|
||||
#include <vtkDataSet.h>
|
||||
#include <vtkDoubleArray.h>
|
||||
#include <vtkIncrementalPointLocator.h>
|
||||
#include <vtkInformation.h>
|
||||
#include <vtkInformationVector.h>
|
||||
#include <vtkIntArray.h>
|
||||
#include <vtkMergePoints.h>
|
||||
#include <vtkObjectFactory.h>
|
||||
#include <vtkPointData.h>
|
||||
#include <vtkPointSet.h>
|
||||
#include <vtkPoints.h>
|
||||
#include <vtkRectilinearGrid.h>
|
||||
#include <vtkSMPThreadLocalObject.h>
|
||||
#include <vtkStringArray.h>
|
||||
#include <vtkUnstructuredGrid.h>
|
||||
|
||||
#include <unordered_set>
|
||||
#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<vtkGenericCell> 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<vtkCellTypes> cTypes;
|
||||
ds->GetCellTypes(cTypes);
|
||||
unsigned char topoDim = 0;
|
||||
for (vtkIdType iC = 0; iC < cTypes->GetNumberOfTypes(); ++iC) {
|
||||
unsigned char dimC = static_cast<unsigned char>(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<vtkDoubleArray> ComputeWeights(vtkDataSet* ds,
|
||||
const std::vector<vtkIdType>& ptMap) = 0;
|
||||
};
|
||||
|
||||
struct FirstPointStrategy: public WeighingStrategy
|
||||
{
|
||||
vtkSmartPointer<vtkDoubleArray> ComputeWeights(vtkDataSet* ds,
|
||||
const std::vector<vtkIdType>& ptMap) override
|
||||
{
|
||||
if (ds->GetNumberOfPoints() != static_cast<vtkIdType>(ptMap.size())) {
|
||||
vtkGenericWarningMacro(
|
||||
"Number of points in dataset and number of entries in point map don't line up.");
|
||||
return nullptr;
|
||||
}
|
||||
vtkNew<vtkDoubleArray> weights;
|
||||
weights->SetNumberOfComponents(1);
|
||||
weights->SetNumberOfTuples(ds->GetNumberOfPoints());
|
||||
weights->Fill(0.0);
|
||||
|
||||
std::unordered_set<vtkIdType> 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<vtkDoubleArray> ComputeWeights(vtkDataSet* ds,
|
||||
const std::vector<vtkIdType>& ptMap) override
|
||||
{
|
||||
if (ds->GetNumberOfPoints() != static_cast<vtkIdType>(ptMap.size())) {
|
||||
vtkGenericWarningMacro(
|
||||
"Number of points in dataset and number of entries in point map don't line up.");
|
||||
return nullptr;
|
||||
}
|
||||
std::vector<double> counts(ds->GetNumberOfPoints(), 0.0);
|
||||
for (vtkIdType iP = 0; iP < ds->GetNumberOfPoints(); ++iP) {
|
||||
if (ptMap[iP] < 0) {
|
||||
continue;
|
||||
}
|
||||
counts[ptMap[iP]] += 1.0;
|
||||
}
|
||||
|
||||
vtkNew<vtkDoubleArray> 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<vtkDoubleArray> ComputeWeights(vtkDataSet* ds,
|
||||
const std::vector<vtkIdType>& ptMap) override
|
||||
{
|
||||
if (ds->GetNumberOfPoints() != static_cast<vtkIdType>(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<vtkDataArray> measures;
|
||||
{
|
||||
// this scope is so that any extra memory related to the cell size filter gets released
|
||||
// when no longer needed
|
||||
vtkNew<vtkCellSizeFilter> 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<vtkDoubleArray> 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<vtkIdList> pointIdList;
|
||||
ds->GetCellPoints(0, pointIdList);
|
||||
}
|
||||
vtkSMPThreadLocalObject<vtkIdList> 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<vtkIdList> 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<double> 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<WeighingStrategy> operator()(vtkCleanUnstructuredGrid::DataWeighingType dwt)
|
||||
{
|
||||
switch (dwt) {
|
||||
case vtkCleanUnstructuredGrid::FIRST_POINT:
|
||||
return std::make_shared<FirstPointStrategy>();
|
||||
case vtkCleanUnstructuredGrid::AVERAGING:
|
||||
return std::make_shared<AveragingStrategy>();
|
||||
case vtkCleanUnstructuredGrid::SPATIAL_DENSITY:
|
||||
return std::make_shared<SpatialDensityStrategy>();
|
||||
default:
|
||||
vtkGenericWarningMacro("Incorrect weighing strategy type passed to factory. "
|
||||
"defaulting to FIRST_POINT.");
|
||||
return std::make_shared<FirstPointStrategy>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct WeighingWorklet
|
||||
{
|
||||
template<typename ArrayTypeIn, typename ArrayTypeOut>
|
||||
void operator()(ArrayTypeIn* inArray,
|
||||
ArrayTypeOut* outArray,
|
||||
vtkDoubleArray* weights,
|
||||
const std::vector<vtkIdType>& 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<vtkIdType>& 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<vtkIdType>& 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<vtkIdType>& 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<vtkIdType>& ptMap)
|
||||
{
|
||||
// better here to use a Dispatch2BySameArrayType, but that doesn't exist
|
||||
using Dispatcher = vtkArrayDispatch::Dispatch2BySameValueType<vtkArrayDispatch::AllTypes>;
|
||||
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<vtkPoints> 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<vtkPoints> 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<vtkIdType> 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<vtkIdList> 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<DataWeighingType>(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<vtkIdList> 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<float>(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<vtkMergePoints>::New();
|
||||
}
|
||||
else {
|
||||
this->Locator = vtkSmartPointer<vtkPointLocator>::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<vtkPointLocator>::New();
|
||||
}
|
||||
}
|
||||
}
|
||||
153
src/Mod/Fem/App/VTKExtensions/vtkCleanUnstructuredGrid.h
Normal file
153
src/Mod/Fem/App/VTKExtensions/vtkCleanUnstructuredGrid.h
Normal file
@@ -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<vtkIncrementalPointLocator> 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
|
||||
180
src/Mod/Fem/App/VTKExtensions/vtkFemFrameSourceAlgorithm.cpp
Normal file
180
src/Mod/Fem/App/VTKExtensions/vtkFemFrameSourceAlgorithm.cpp
Normal file
@@ -0,0 +1,180 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
#include <vtkUnstructuredGrid.h>
|
||||
#include <vtkMultiBlockDataSet.h>
|
||||
#include <vtkFieldData.h>
|
||||
#include <vtkStreamingDemandDrivenPipeline.h>
|
||||
#include <vtkFloatArray.h>
|
||||
#include <vtkInformation.h>
|
||||
#include <vtkInformationVector.h>
|
||||
#endif
|
||||
|
||||
#include "vtkFemFrameSourceAlgorithm.h"
|
||||
|
||||
using namespace Fem;
|
||||
|
||||
|
||||
vtkStandardNewMacro(vtkFemFrameSourceAlgorithm);
|
||||
|
||||
vtkFemFrameSourceAlgorithm::vtkFemFrameSourceAlgorithm()
|
||||
{
|
||||
// we are a source
|
||||
SetNumberOfInputPorts(0);
|
||||
SetNumberOfOutputPorts(1);
|
||||
}
|
||||
|
||||
vtkFemFrameSourceAlgorithm::~vtkFemFrameSourceAlgorithm() = default;
|
||||
|
||||
void vtkFemFrameSourceAlgorithm::setDataObject(vtkSmartPointer<vtkDataObject> data)
|
||||
{
|
||||
m_data = data;
|
||||
Modified();
|
||||
Update();
|
||||
}
|
||||
|
||||
bool vtkFemFrameSourceAlgorithm::isValid()
|
||||
{
|
||||
return m_data.GetPointer() ? true : false;
|
||||
}
|
||||
|
||||
std::vector<double> vtkFemFrameSourceAlgorithm::getFrameValues()
|
||||
{
|
||||
|
||||
// check if we have frame data
|
||||
if (!m_data || !m_data->IsA("vtkMultiBlockDataSet")) {
|
||||
return std::vector<double>();
|
||||
}
|
||||
|
||||
// we have multiple frames! let's check the amount and times
|
||||
vtkSmartPointer<vtkMultiBlockDataSet> multiblock = vtkMultiBlockDataSet::SafeDownCast(m_data);
|
||||
|
||||
unsigned long nblocks = multiblock->GetNumberOfBlocks();
|
||||
std::vector<double> tFrames(nblocks);
|
||||
|
||||
for (unsigned long i = 0; i < nblocks; i++) {
|
||||
|
||||
vtkDataObject* block = multiblock->GetBlock(i);
|
||||
// check if the TimeValue field is available
|
||||
if (!block->GetFieldData()->HasArray("TimeValue")) {
|
||||
// a frame with no valid value is a undefined state
|
||||
return std::vector<double>();
|
||||
}
|
||||
|
||||
// store the time value!
|
||||
vtkDataArray* TimeValue = block->GetFieldData()->GetArray("TimeValue");
|
||||
if (!TimeValue->IsA("vtkFloatArray") || TimeValue->GetNumberOfTuples() < 1) {
|
||||
// a frame with no valid value is a undefined state
|
||||
return std::vector<double>();
|
||||
}
|
||||
|
||||
tFrames[i] = vtkFloatArray::SafeDownCast(TimeValue)->GetValue(0);
|
||||
}
|
||||
|
||||
return tFrames;
|
||||
}
|
||||
|
||||
int vtkFemFrameSourceAlgorithm::RequestInformation(vtkInformation* reqInfo,
|
||||
vtkInformationVector** inVector,
|
||||
vtkInformationVector* outVector)
|
||||
{
|
||||
|
||||
// setup default information
|
||||
if (!this->Superclass::RequestInformation(reqInfo, inVector, outVector)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!m_data) {
|
||||
// for the no data case we would return a empty data set in RequestData.
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<double> frames = getFrameValues();
|
||||
|
||||
if (frames.empty()) {
|
||||
// no frames, default info is sufficient
|
||||
return 1;
|
||||
}
|
||||
|
||||
double tRange[2] = {frames.front(), frames.back()};
|
||||
|
||||
// finally set the time info!
|
||||
vtkInformation* info = outVector->GetInformationObject(0);
|
||||
info->Set(vtkStreamingDemandDrivenPipeline::TIME_RANGE(), tRange, 2);
|
||||
info->Set(vtkStreamingDemandDrivenPipeline::TIME_STEPS(), &frames[0], frames.size());
|
||||
info->Set(CAN_HANDLE_PIECE_REQUEST(), 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vtkFemFrameSourceAlgorithm::RequestData(vtkInformation*,
|
||||
vtkInformationVector**,
|
||||
vtkInformationVector* outVector)
|
||||
{
|
||||
vtkInformation* outInfo = outVector->GetInformationObject(0);
|
||||
vtkUnstructuredGrid* output =
|
||||
vtkUnstructuredGrid::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));
|
||||
|
||||
if (!output) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!m_data) {
|
||||
outInfo->Set(vtkDataObject::DATA_OBJECT(), vtkUnstructuredGrid::New());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!m_data->IsA("vtkMultiBlockDataSet")) {
|
||||
// no multi frame data, return directly
|
||||
outInfo->Set(vtkDataObject::DATA_OBJECT(), m_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vtkSmartPointer<vtkMultiBlockDataSet> multiblock = vtkMultiBlockDataSet::SafeDownCast(m_data);
|
||||
// find the block asked for (lazy implementation)
|
||||
unsigned long idx = 0;
|
||||
if (outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP())) {
|
||||
auto time = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP());
|
||||
auto frames = getFrameValues();
|
||||
|
||||
// we have float values, so be aware of rounding errors. lets subtract the searched time and
|
||||
// then use the smallest value
|
||||
for (auto& frame : frames) {
|
||||
frame = std::abs(frame - time);
|
||||
}
|
||||
|
||||
auto it = std::ranges::min_element(frames);
|
||||
idx = std::distance(frames.begin(), it);
|
||||
}
|
||||
|
||||
auto block = multiblock->GetBlock(idx);
|
||||
output->ShallowCopy(block);
|
||||
return 1;
|
||||
}
|
||||
66
src/Mod/Fem/App/VTKExtensions/vtkFemFrameSourceAlgorithm.h
Normal file
66
src/Mod/Fem/App/VTKExtensions/vtkFemFrameSourceAlgorithm.h
Normal file
@@ -0,0 +1,66 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef Fem_VTK_vtkFemFrameSourceAlgorithm_H
|
||||
#define Fem_VTK_vtkFemFrameSourceAlgorithm_H
|
||||
|
||||
#include <vtkSmartPointer.h>
|
||||
#include <vtkUnstructuredGridAlgorithm.h>
|
||||
|
||||
class vtkInformation;
|
||||
class vtkInformationVector;
|
||||
|
||||
|
||||
namespace Fem
|
||||
{
|
||||
|
||||
// algorithm that allows multi frame handling: if data is stored in MultiBlock dataset
|
||||
// this source enables the downstream filters to query the blocks as different time frames
|
||||
class vtkFemFrameSourceAlgorithm: public vtkUnstructuredGridAlgorithm
|
||||
{
|
||||
public:
|
||||
static vtkFemFrameSourceAlgorithm* New();
|
||||
vtkTypeMacro(vtkFemFrameSourceAlgorithm, vtkUnstructuredGridAlgorithm);
|
||||
|
||||
bool isValid();
|
||||
void setDataObject(vtkSmartPointer<vtkDataObject> data);
|
||||
std::vector<double> getFrameValues();
|
||||
|
||||
protected:
|
||||
vtkFemFrameSourceAlgorithm();
|
||||
~vtkFemFrameSourceAlgorithm() override;
|
||||
|
||||
vtkSmartPointer<vtkDataObject> m_data;
|
||||
|
||||
int RequestInformation(vtkInformation* reqInfo,
|
||||
vtkInformationVector** inVector,
|
||||
vtkInformationVector* outVector) override;
|
||||
int RequestData(vtkInformation* reqInfo,
|
||||
vtkInformationVector** inVector,
|
||||
vtkInformationVector* outVector) override;
|
||||
};
|
||||
|
||||
} // namespace Fem
|
||||
|
||||
|
||||
#endif // Fem_VTK_vtkFemFrameSourceAlgorithm_H
|
||||
604
src/Mod/Fem/App/VTKExtensions/vtkSMPTools.h
Normal file
604
src/Mod/Fem/App/VTKExtensions/vtkSMPTools.h
Normal file
@@ -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 <functional> // For std::function
|
||||
#include <iterator> // For std::iterator
|
||||
#include <type_traits> // For std:::enable_if
|
||||
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
||||
namespace vtk
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
namespace smp
|
||||
{
|
||||
template<typename T>
|
||||
class vtkSMPTools_Has_Initialize
|
||||
{
|
||||
typedef char (&no_type)[1];
|
||||
typedef char (&yes_type)[2];
|
||||
template<typename U, void (U::*)()>
|
||||
struct V
|
||||
{
|
||||
};
|
||||
template<typename U>
|
||||
static yes_type check(V<U, &U::Initialize>*);
|
||||
template<typename U>
|
||||
static no_type check(...);
|
||||
|
||||
public:
|
||||
static bool const value = sizeof(check<T>(nullptr)) == sizeof(yes_type);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class vtkSMPTools_Has_Initialize_const
|
||||
{
|
||||
typedef char (&no_type)[1];
|
||||
typedef char (&yes_type)[2];
|
||||
template<typename U, void (U::*)() const>
|
||||
struct V
|
||||
{
|
||||
};
|
||||
template<typename U>
|
||||
static yes_type check(V<U, &U::Initialize>*);
|
||||
template<typename U>
|
||||
static no_type check(...);
|
||||
|
||||
public:
|
||||
static bool const value = sizeof(check<T>(0)) == sizeof(yes_type);
|
||||
};
|
||||
|
||||
template<typename Functor, bool Init>
|
||||
struct vtkSMPTools_FunctorInternal;
|
||||
|
||||
template<typename Functor>
|
||||
struct vtkSMPTools_FunctorInternal<Functor, false>
|
||||
{
|
||||
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<Functor, false>&
|
||||
operator=(const vtkSMPTools_FunctorInternal<Functor, false>&);
|
||||
vtkSMPTools_FunctorInternal(const vtkSMPTools_FunctorInternal<Functor, false>&);
|
||||
};
|
||||
|
||||
template<typename Functor>
|
||||
struct vtkSMPTools_FunctorInternal<Functor, true>
|
||||
{
|
||||
Functor& F;
|
||||
vtkSMPThreadLocal<unsigned char> 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<Functor, true>&
|
||||
operator=(const vtkSMPTools_FunctorInternal<Functor, true>&);
|
||||
vtkSMPTools_FunctorInternal(const vtkSMPTools_FunctorInternal<Functor, true>&);
|
||||
};
|
||||
|
||||
template<typename Functor>
|
||||
class vtkSMPTools_Lookup_For
|
||||
{
|
||||
static bool const init = vtkSMPTools_Has_Initialize<Functor>::value;
|
||||
|
||||
public:
|
||||
typedef vtkSMPTools_FunctorInternal<Functor, init> type;
|
||||
};
|
||||
|
||||
template<typename Functor>
|
||||
class vtkSMPTools_Lookup_For<Functor const>
|
||||
{
|
||||
static bool const init = vtkSMPTools_Has_Initialize_const<Functor>::value;
|
||||
|
||||
public:
|
||||
typedef vtkSMPTools_FunctorInternal<Functor const, init> type;
|
||||
};
|
||||
|
||||
template<typename Iterator, typename Functor, bool Init>
|
||||
struct vtkSMPTools_RangeFunctor;
|
||||
|
||||
template<typename Iterator, typename Functor>
|
||||
struct vtkSMPTools_RangeFunctor<Iterator, Functor, false>
|
||||
{
|
||||
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<typename Iterator, typename Functor>
|
||||
struct vtkSMPTools_RangeFunctor<Iterator, Functor, true>
|
||||
{
|
||||
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<typename Iterator, typename Functor>
|
||||
class vtkSMPTools_Lookup_RangeFor
|
||||
{
|
||||
static bool const init = vtkSMPTools_Has_Initialize<Functor>::value;
|
||||
|
||||
public:
|
||||
typedef vtkSMPTools_RangeFunctor<Iterator, Functor, init> type;
|
||||
};
|
||||
|
||||
template<typename Iterator, typename Functor>
|
||||
class vtkSMPTools_Lookup_RangeFor<Iterator, Functor const>
|
||||
{
|
||||
static bool const init = vtkSMPTools_Has_Initialize_const<Functor>::value;
|
||||
|
||||
public:
|
||||
typedef vtkSMPTools_RangeFunctor<Iterator, Functor const, init> type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using resolvedNotInt = typename std::enable_if<!std::is_integral<T>::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<typename Functor>
|
||||
static void For(vtkIdType first, vtkIdType last, vtkIdType grain, Functor& f)
|
||||
{
|
||||
typename vtk::detail::smp::vtkSMPTools_Lookup_For<Functor>::type fi(f);
|
||||
fi.For(first, last, grain);
|
||||
}
|
||||
|
||||
template<typename Functor>
|
||||
static void For(vtkIdType first, vtkIdType last, vtkIdType grain, Functor const& f)
|
||||
{
|
||||
typename vtk::detail::smp::vtkSMPTools_Lookup_For<Functor const>::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<typename Functor>
|
||||
static void For(vtkIdType first, vtkIdType last, Functor& f)
|
||||
{
|
||||
vtkSMPTools::For(first, last, 0, f);
|
||||
}
|
||||
|
||||
template<typename Functor>
|
||||
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 IteratorT>
|
||||
* class ExampleFunctor
|
||||
* {
|
||||
* void operator()(IteratorT begin, IteratorT end)
|
||||
* {
|
||||
* for (IteratorT it = begin; it != end; ++it)
|
||||
* {
|
||||
* // Do stuff
|
||||
* }
|
||||
* }
|
||||
* };
|
||||
* ExampleFunctor<std::set<int>::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<int>::iterator begin, std::set<int>::iterator end) {
|
||||
* // Do stuff
|
||||
* });
|
||||
* \endcode
|
||||
*/
|
||||
template<typename Iter, typename Functor>
|
||||
static vtk::detail::smp::resolvedNotInt<Iter>
|
||||
For(Iter begin, Iter end, vtkIdType grain, Functor& f)
|
||||
{
|
||||
vtkIdType size = std::distance(begin, end);
|
||||
typename vtk::detail::smp::vtkSMPTools_Lookup_RangeFor<Iter, Functor>::type fi(begin, f);
|
||||
vtkSMPTools::For(0, size, grain, fi);
|
||||
}
|
||||
|
||||
template<typename Iter, typename Functor>
|
||||
static vtk::detail::smp::resolvedNotInt<Iter>
|
||||
For(Iter begin, Iter end, vtkIdType grain, Functor const& f)
|
||||
{
|
||||
vtkIdType size = std::distance(begin, end);
|
||||
typename vtk::detail::smp::vtkSMPTools_Lookup_RangeFor<Iter, Functor const>::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 IteratorT>
|
||||
* class ExampleFunctor
|
||||
* {
|
||||
* void operator()(IteratorT begin, IteratorT end)
|
||||
* {
|
||||
* for (IteratorT it = begin; it != end; ++it)
|
||||
* {
|
||||
* // Do stuff
|
||||
* }
|
||||
* }
|
||||
* };
|
||||
* ExampleFunctor<std::set<int>::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<int>::iterator begin, std::set<int>::iterator end) {
|
||||
* // Do stuff
|
||||
* });
|
||||
* \endcode
|
||||
*/
|
||||
template<typename Iter, typename Functor>
|
||||
static vtk::detail::smp::resolvedNotInt<Iter> For(Iter begin, Iter end, Functor& f)
|
||||
{
|
||||
vtkSMPTools::For(begin, end, 0, f);
|
||||
}
|
||||
|
||||
template<typename Iter, typename Functor>
|
||||
static vtk::detail::smp::resolvedNotInt<Iter> 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<typename T>
|
||||
static void LocalScope(Config const& config, T&& lambda)
|
||||
{
|
||||
auto& SMPToolsAPI = vtk::detail::smp::vtkSMPToolsAPI::GetInstance();
|
||||
SMPToolsAPI.LocalScope<vtkSMPTools::Config>(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<typename InputIt, typename OutputIt, typename Functor>
|
||||
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<typename InputIt1, typename InputIt2, typename OutputIt, typename Functor>
|
||||
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<typename Iterator, typename T>
|
||||
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<typename RandomAccessIterator>
|
||||
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<typename RandomAccessIterator, typename Compare>
|
||||
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
|
||||
@@ -15,7 +15,7 @@
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="gb_gmsh_param">
|
||||
<widget class="QGroupBox" name="gb_elmer_param">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
@@ -169,25 +169,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="Gui::PrefCheckBox" name="cb_filtering">
|
||||
<property name="toolTip">
|
||||
<string>Merge mesh volume regions processed by each CPU core to make boundaries invisible.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Filter results</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>FilterMultiCPUResults</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Mod/Fem/Elmer</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -56,7 +56,6 @@ void DlgSettingsFemElmerImp::saveSettings()
|
||||
|
||||
ui->sb_num_processes->onSave();
|
||||
|
||||
ui->cb_filtering->onSave();
|
||||
ui->ckb_binary_format->onSave();
|
||||
ui->ckb_geom_id->onSave();
|
||||
}
|
||||
@@ -68,7 +67,6 @@ void DlgSettingsFemElmerImp::loadSettings()
|
||||
|
||||
ui->sb_num_processes->onRestore();
|
||||
|
||||
ui->cb_filtering->onRestore();
|
||||
ui->ckb_binary_format->onRestore();
|
||||
ui->ckb_geom_id->onRestore();
|
||||
}
|
||||
|
||||
@@ -511,7 +511,6 @@ void ViewProviderFemPostObject::updateProperties()
|
||||
|
||||
void ViewProviderFemPostObject::update3D()
|
||||
{
|
||||
|
||||
vtkPolyData* pd = m_currentAlgorithm->GetOutput();
|
||||
|
||||
vtkPointData* pntData;
|
||||
@@ -798,79 +797,6 @@ void ViewProviderFemPostObject::updateData(const App::Property* p)
|
||||
}
|
||||
}
|
||||
|
||||
void ViewProviderFemPostObject::filterArtifacts(vtkDataSet* dset)
|
||||
{
|
||||
// The problem is that in the surface view the boundary regions of the volumes
|
||||
// calculated by the different CPU cores is always visible, independent of the
|
||||
// transparency setting. Elmer is not to blame because this is a property of the
|
||||
// partial VTK file reader. So this can happen with various inputs
|
||||
// since FreeCAD can also be used to view VTK files without the need to perform
|
||||
// an analysis. Therefore it is impossible to know in advance when a filter
|
||||
// is necessary or not.
|
||||
// Only for pure CCX analyses we know that no filtering is necessary. However,
|
||||
// the effort to catch this case is not worth it since the filtering is
|
||||
// only as time-consuming as enabling the surface filter. In fact, it is like
|
||||
// performing the surface filter twice.
|
||||
|
||||
// We need to set the filter clipping plane below the z-minimum of the data.
|
||||
// We can either do this by checking the VTK data or by getting the info from
|
||||
// the 3D view. We use here the latter because this is much faster.
|
||||
|
||||
// since we will set the filter according to the visible bounding box
|
||||
// assure the object is visible
|
||||
bool visibility = this->Visibility.getValue();
|
||||
if (!visibility) {
|
||||
this->Visibility.setValue(true);
|
||||
}
|
||||
m_blockPropertyChanges = true;
|
||||
|
||||
Gui::Document* doc = this->getDocument();
|
||||
Gui::View3DInventor* view =
|
||||
qobject_cast<Gui::View3DInventor*>(doc->getViewOfViewProvider(this));
|
||||
|
||||
if (view) {
|
||||
Gui::View3DInventorViewer* viewer = view->getViewer();
|
||||
SbBox3f boundingBox;
|
||||
boundingBox = viewer->getBoundingBox();
|
||||
if (boundingBox.hasVolume()) {
|
||||
// setup
|
||||
vtkSmartPointer<vtkImplicitFunction> m_implicit;
|
||||
auto m_plane = vtkSmartPointer<vtkPlane>::New();
|
||||
m_implicit = m_plane;
|
||||
m_plane->SetNormal(0., 0., 1.);
|
||||
auto extractor = vtkSmartPointer<vtkTableBasedClipDataSet>::New();
|
||||
float dx, dy, dz;
|
||||
boundingBox.getSize(dx, dy, dz);
|
||||
// Set plane below the minimum to assure there are
|
||||
// no boundary cells (touching the function) and for Warp filters
|
||||
// the user might change the warp factor a lot. Thus set
|
||||
// 10 times dz to be safe even for unrealistic warp deformations
|
||||
m_plane->SetOrigin(0., 0., -10 * dz);
|
||||
extractor->SetClipFunction(m_implicit);
|
||||
extractor->SetInputData(dset);
|
||||
extractor->Update();
|
||||
auto extractorResult = extractor->GetOutputDataObject(0);
|
||||
if (extractorResult) {
|
||||
m_surface->SetInputData(extractorResult);
|
||||
}
|
||||
else {
|
||||
m_surface->SetInputData(dset);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// for the case that there are only 2D objects
|
||||
m_surface->SetInputData(dset);
|
||||
}
|
||||
}
|
||||
|
||||
m_blockPropertyChanges = false;
|
||||
|
||||
// restore initial vsibility
|
||||
if (!visibility) {
|
||||
this->Visibility.setValue(visibility);
|
||||
}
|
||||
}
|
||||
|
||||
bool ViewProviderFemPostObject::setupPipeline()
|
||||
{
|
||||
if (m_blockPropertyChanges) {
|
||||
@@ -878,9 +804,6 @@ bool ViewProviderFemPostObject::setupPipeline()
|
||||
}
|
||||
|
||||
auto postObject = getObject<Fem::FemPostObject>();
|
||||
|
||||
// check all fields if there is a real/imaginary one and if so
|
||||
// add a field with an absolute value
|
||||
vtkDataSet* dset = postObject->getDataSet();
|
||||
if (!dset) {
|
||||
return false;
|
||||
@@ -889,26 +812,7 @@ bool ViewProviderFemPostObject::setupPipeline()
|
||||
m_outline->SetInputData(dset);
|
||||
m_points->SetInputData(dset);
|
||||
m_wireframe->SetInputData(dset);
|
||||
|
||||
// Filtering artifacts is necessary for partial VTU files (*.pvtu) independent of the
|
||||
// current Elmer CPU core settings because the user might load an external file.
|
||||
// It is only necessary for the surface filter.
|
||||
// The problem is that when opening an existing FreeCAD file, we get no information how the
|
||||
// Data of the postObject was once created. The vtkDataObject type does not provide this info.
|
||||
// Therefore the only way is the hack to filter only if the used Elmer CPU cores are > 1.
|
||||
auto hGrp = App::GetApplication().GetParameterGroupByPath(
|
||||
"User parameter:BaseApp/Preferences/Mod/Fem/Elmer");
|
||||
bool FilterMultiCPUResults = hGrp->GetBool("FilterMultiCPUResults", true);
|
||||
int UseNumberOfCores = hGrp->GetInt("UseNumberOfCores", 1);
|
||||
// filtering is only necessary for pipelines and warp filters
|
||||
if (FilterMultiCPUResults && (UseNumberOfCores > 1)
|
||||
&& ((postObject->getTypeId() == Base::Type::fromName("Fem::FemPostPipeline"))
|
||||
|| (postObject->getTypeId() == Base::Type::fromName("Fem::FemPostWarpVectorFilter")))) {
|
||||
filterArtifacts(dset);
|
||||
}
|
||||
else {
|
||||
m_surface->SetInputData(dset);
|
||||
}
|
||||
m_surface->SetInputData(dset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -155,7 +155,6 @@ protected:
|
||||
vtkSmartPointer<vtkVertexGlyphFilter> m_points, m_pointsSurface;
|
||||
|
||||
private:
|
||||
void filterArtifacts(vtkDataSet* data);
|
||||
void updateProperties();
|
||||
void update3D();
|
||||
void WritePointData(vtkPoints* points, vtkDataArray* normals, vtkDataArray* tcoords);
|
||||
|
||||
@@ -61,6 +61,9 @@ void ViewProviderFemPostPipeline::updateData(const App::Property* prop)
|
||||
updateFunctionSize();
|
||||
updateColorBars();
|
||||
}
|
||||
else if (prop == &pipeline->MergeDuplicate) {
|
||||
updateVtk();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewProviderFemPostPipeline::updateFunctionSize()
|
||||
|
||||
Reference in New Issue
Block a user