2af555f6c9
`vtkm::cont::DataSet` is a dynamic object that can hold cell sets and fields of many different types, none of which are known until runtime. This causes a problem with serialization, which has to know what type to compile the serialization for, particularly when unserializing the type at the receiving end. The original implementation "solved" the problem by creating a secondary wrapper object that was templated on types of field arrays and cell sets that might be serialized. This is not a great solution as it punts the problem to algorithm developers. This problem has been completely solved for fields, as it is possible to serialize most types of arrays without knowing their type now. You still need to iterate over every possible `CellSet` type, but there are not that many `CellSet`s that are practically encountered. Thus, there is now a direct implementation of `Serialization` for `DataSet` that covers all the data types you are likely to encounter. The old `SerializableDataSet` has been deprecated. In the unlikely event an algorithm needs to transfer a non-standard type of `CellSet` (such as a permuted cell set), it can use the replacement `DataSetWithCellSetTypes`, which just specifies the cell set types.
234 lines
7.2 KiB
C++
234 lines
7.2 KiB
C++
//============================================================================
|
|
// Copyright (c) Kitware, Inc.
|
|
// All rights reserved.
|
|
// See LICENSE.txt 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.
|
|
//============================================================================
|
|
|
|
#include "RedistributePoints.h"
|
|
|
|
#include <vtkm/ImplicitFunction.h>
|
|
#include <vtkm/cont/Algorithm.h>
|
|
#include <vtkm/cont/AssignerPartitionedDataSet.h>
|
|
#include <vtkm/cont/BoundsGlobalCompute.h>
|
|
#include <vtkm/cont/EnvironmentTracker.h>
|
|
#include <vtkm/cont/Serialization.h>
|
|
#include <vtkm/filter/entity_extraction/ExtractPoints.h>
|
|
|
|
#include <vtkm/thirdparty/diy/diy.h>
|
|
|
|
namespace example
|
|
{
|
|
|
|
namespace internal
|
|
{
|
|
|
|
static vtkmdiy::ContinuousBounds convert(const vtkm::Bounds& bds)
|
|
{
|
|
vtkmdiy::ContinuousBounds result(3);
|
|
result.min[0] = static_cast<float>(bds.X.Min);
|
|
result.min[1] = static_cast<float>(bds.Y.Min);
|
|
result.min[2] = static_cast<float>(bds.Z.Min);
|
|
result.max[0] = static_cast<float>(bds.X.Max);
|
|
result.max[1] = static_cast<float>(bds.Y.Max);
|
|
result.max[2] = static_cast<float>(bds.Z.Max);
|
|
return result;
|
|
}
|
|
|
|
|
|
template <typename FilterType>
|
|
class Redistributor
|
|
{
|
|
const vtkmdiy::RegularDecomposer<vtkmdiy::ContinuousBounds>& Decomposer;
|
|
const FilterType& Filter;
|
|
|
|
vtkm::cont::DataSet Extract(const vtkm::cont::DataSet& input,
|
|
const vtkmdiy::ContinuousBounds& bds) const
|
|
{
|
|
// extract points
|
|
vtkm::Box box(bds.min[0], bds.max[0], bds.min[1], bds.max[1], bds.min[2], bds.max[2]);
|
|
|
|
vtkm::filter::entity_extraction::ExtractPoints extractor;
|
|
extractor.SetCompactPoints(true);
|
|
extractor.SetImplicitFunction(box);
|
|
return extractor.Execute(input);
|
|
}
|
|
|
|
class ConcatenateFields
|
|
{
|
|
public:
|
|
explicit ConcatenateFields(vtkm::Id totalSize)
|
|
: TotalSize(totalSize)
|
|
, CurrentIdx(0)
|
|
{
|
|
}
|
|
|
|
void Append(const vtkm::cont::Field& field)
|
|
{
|
|
VTKM_ASSERT(this->CurrentIdx + field.GetNumberOfValues() <= this->TotalSize);
|
|
|
|
if (this->Field.GetNumberOfValues() == 0)
|
|
{
|
|
// Copy metadata
|
|
this->Field = field;
|
|
// Reset array
|
|
this->Field.SetData(field.GetData().NewInstanceBasic());
|
|
// Preallocate array
|
|
this->Field.GetData().Allocate(this->TotalSize);
|
|
}
|
|
else
|
|
{
|
|
VTKM_ASSERT(this->Field.GetName() == field.GetName() &&
|
|
this->Field.GetAssociation() == field.GetAssociation());
|
|
}
|
|
|
|
field.GetData().CastAndCallForTypes<VTKM_DEFAULT_TYPE_LIST, VTKM_DEFAULT_STORAGE_LIST>(
|
|
Appender{}, this->Field, this->CurrentIdx);
|
|
this->CurrentIdx += field.GetNumberOfValues();
|
|
}
|
|
|
|
const vtkm::cont::Field& GetResult() const { return this->Field; }
|
|
|
|
private:
|
|
struct Appender
|
|
{
|
|
template <typename T, typename S>
|
|
void operator()(const vtkm::cont::ArrayHandle<T, S>& data,
|
|
vtkm::cont::Field& field,
|
|
vtkm::Id currentIdx) const
|
|
{
|
|
vtkm::cont::ArrayHandle<T> farray =
|
|
field.GetData().template AsArrayHandle<vtkm::cont::ArrayHandle<T>>();
|
|
vtkm::cont::Algorithm::CopySubRange(data, 0, data.GetNumberOfValues(), farray, currentIdx);
|
|
}
|
|
};
|
|
|
|
vtkm::Id TotalSize;
|
|
vtkm::Id CurrentIdx;
|
|
vtkm::cont::Field Field;
|
|
};
|
|
|
|
public:
|
|
Redistributor(const vtkmdiy::RegularDecomposer<vtkmdiy::ContinuousBounds>& decomposer,
|
|
const FilterType& filter)
|
|
: Decomposer(decomposer)
|
|
, Filter(filter)
|
|
{
|
|
}
|
|
|
|
void operator()(vtkm::cont::DataSet* block, const vtkmdiy::ReduceProxy& rp) const
|
|
{
|
|
if (rp.in_link().size() == 0)
|
|
{
|
|
if (block->GetNumberOfCoordinateSystems() > 0)
|
|
{
|
|
for (int cc = 0; cc < rp.out_link().size(); ++cc)
|
|
{
|
|
auto target = rp.out_link().target(cc);
|
|
// let's get the bounding box for the target block.
|
|
vtkmdiy::ContinuousBounds bds(3);
|
|
this->Decomposer.fill_bounds(bds, target.gid);
|
|
|
|
auto extractedDS = this->Extract(*block, bds);
|
|
rp.enqueue(target, extractedDS);
|
|
}
|
|
// clear our dataset.
|
|
*block = vtkm::cont::DataSet();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vtkm::Id numValues = 0;
|
|
std::vector<vtkm::cont::DataSet> receives;
|
|
for (int cc = 0; cc < rp.in_link().size(); ++cc)
|
|
{
|
|
auto target = rp.in_link().target(cc);
|
|
if (rp.incoming(target.gid).size() > 0)
|
|
{
|
|
vtkm::cont::DataSet incomingDS;
|
|
rp.dequeue(target.gid, incomingDS);
|
|
receives.push_back(incomingDS);
|
|
numValues += receives.back().GetCoordinateSystem(0).GetNumberOfPoints();
|
|
}
|
|
}
|
|
|
|
*block = vtkm::cont::DataSet();
|
|
if (receives.size() == 1)
|
|
{
|
|
*block = receives[0];
|
|
}
|
|
else if (receives.size() > 1)
|
|
{
|
|
ConcatenateFields concatCoords(numValues);
|
|
for (const auto& ds : receives)
|
|
{
|
|
concatCoords.Append(ds.GetCoordinateSystem(0));
|
|
}
|
|
block->AddCoordinateSystem(vtkm::cont::CoordinateSystem(
|
|
concatCoords.GetResult().GetName(), concatCoords.GetResult().GetData()));
|
|
|
|
for (vtkm::IdComponent i = 0; i < receives[0].GetNumberOfFields(); ++i)
|
|
{
|
|
ConcatenateFields concatField(numValues);
|
|
for (const auto& ds : receives)
|
|
{
|
|
concatField.Append(ds.GetField(i));
|
|
}
|
|
block->AddField(concatField.GetResult());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
} // namespace example::internal
|
|
|
|
|
|
VTKM_CONT vtkm::cont::PartitionedDataSet RedistributePoints::DoExecutePartitions(
|
|
const vtkm::cont::PartitionedDataSet& input)
|
|
{
|
|
auto comm = vtkm::cont::EnvironmentTracker::GetCommunicator();
|
|
|
|
// let's first get the global bounds of the domain
|
|
vtkm::Bounds gbounds = vtkm::cont::BoundsGlobalCompute(input);
|
|
|
|
vtkm::cont::AssignerPartitionedDataSet assigner(input.GetNumberOfPartitions());
|
|
vtkmdiy::RegularDecomposer<vtkmdiy::ContinuousBounds> decomposer(
|
|
/*dim*/ 3, internal::convert(gbounds), assigner.nblocks());
|
|
|
|
vtkmdiy::Master master(
|
|
comm,
|
|
/*threads*/ 1,
|
|
/*limit*/ -1,
|
|
[]() -> void* { return new vtkm::cont::DataSet(); },
|
|
[](void* ptr) { delete static_cast<vtkm::cont::DataSet*>(ptr); });
|
|
decomposer.decompose(comm.rank(), assigner, master);
|
|
|
|
assert(static_cast<vtkm::Id>(master.size()) == input.GetNumberOfPartitions());
|
|
// let's populate local blocks
|
|
master.foreach ([&input](vtkm::cont::DataSet* ds, const vtkmdiy::Master::ProxyWithLink& proxy) {
|
|
auto lid = proxy.master()->lid(proxy.gid());
|
|
*ds = input.GetPartition(lid);
|
|
});
|
|
|
|
internal::Redistributor<RedistributePoints> redistributor(decomposer, *this);
|
|
vtkmdiy::all_to_all(master, assigner, redistributor, /*k=*/2);
|
|
|
|
vtkm::cont::PartitionedDataSet result;
|
|
master.foreach ([&result](vtkm::cont::DataSet* ds, const vtkmdiy::Master::ProxyWithLink&) {
|
|
result.AppendPartition(*ds);
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
vtkm::cont::DataSet RedistributePoints::DoExecute(const vtkm::cont::DataSet&)
|
|
{
|
|
throw vtkm::cont::ErrorBadType("RedistributePoints requires PartitionedDataSet.");
|
|
}
|
|
|
|
} // namespace example
|