//============================================================================ // 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 #include #include #include #include #include #include #include namespace example { namespace internal { static vtkmdiy::ContinuousBounds convert(const vtkm::Bounds& bds) { vtkmdiy::ContinuousBounds result(3); result.min[0] = static_cast(bds.X.Min); result.min[1] = static_cast(bds.Y.Min); result.min[2] = static_cast(bds.Z.Min); result.max[0] = static_cast(bds.X.Max); result.max[1] = static_cast(bds.Y.Max); result.max[2] = static_cast(bds.Z.Max); return result; } template class Redistributor { const vtkmdiy::RegularDecomposer& 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( Appender{}, this->Field, this->CurrentIdx); this->CurrentIdx += field.GetNumberOfValues(); } const vtkm::cont::Field& GetResult() const { return this->Field; } private: struct Appender { template void operator()(const vtkm::cont::ArrayHandle& data, vtkm::cont::Field& field, vtkm::Id currentIdx) const { vtkm::cont::ArrayHandle farray = field.GetData().template AsArrayHandle>(); 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& 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 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 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(ptr); }); decomposer.decompose(comm.rank(), assigner, master); assert(static_cast(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 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