VTK-m  2.3
AdvectAlgorithm.h
Go to the documentation of this file.
1 //============================================================================
2 // Copyright (c) Kitware, Inc.
3 // All rights reserved.
4 // See LICENSE.txt for details.
5 //
6 // This software is distributed WITHOUT ANY WARRANTY; without even
7 // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8 // PURPOSE. See the above copyright notice for more information.
9 //============================================================================
10 
11 #ifndef vtk_m_filter_flow_internal_AdvectAlgorithm_h
12 #define vtk_m_filter_flow_internal_AdvectAlgorithm_h
13 
14 
18 #ifdef VTKM_ENABLE_MPI
21 #include <vtkm/thirdparty/diy/diy.h>
22 #include <vtkm/thirdparty/diy/mpi-cast.h>
23 #endif
24 
25 namespace vtkm
26 {
27 namespace filter
28 {
29 namespace flow
30 {
31 namespace internal
32 {
33 
34 template <typename DSIType>
35 class AdvectAlgorithm
36 {
37 public:
38  using ParticleType = typename DSIType::PType;
39 
40  AdvectAlgorithm(const vtkm::filter::flow::internal::BoundsMap& bm, std::vector<DSIType>& blocks)
41  : Blocks(blocks)
42  , BoundsMap(bm)
43 #ifdef VTKM_ENABLE_MPI
44  , Exchanger(this->Comm)
45  , Terminator(this->Comm)
46 #endif
47  , NumRanks(this->Comm.size())
48  , Rank(this->Comm.rank())
49  {
50  }
51 
52  void Execute(const vtkm::cont::ArrayHandle<ParticleType>& seeds, vtkm::FloatDefault stepSize)
53  {
54  this->SetStepSize(stepSize);
55  this->SetSeeds(seeds);
56 
57  this->Go();
58  }
59 
60  vtkm::cont::PartitionedDataSet GetOutput() const
61  {
63  for (const auto& b : this->Blocks)
64  {
66  if (b.GetOutput(ds))
67  output.AppendPartition(ds);
68  }
69  return output;
70  }
71 
72  void SetStepSize(vtkm::FloatDefault stepSize) { this->StepSize = stepSize; }
73 
74  void SetSeeds(const vtkm::cont::ArrayHandle<ParticleType>& seeds)
75  {
76  this->ClearParticles();
77 
78  vtkm::Id n = seeds.GetNumberOfValues();
79  auto portal = seeds.ReadPortal();
80 
81  std::vector<std::vector<vtkm::Id>> blockIDs;
82  std::vector<ParticleType> particles;
83  for (vtkm::Id i = 0; i < n; i++)
84  {
85  const ParticleType p = portal.Get(i);
86  std::vector<vtkm::Id> ids = this->BoundsMap.FindBlocks(p.GetPosition());
87 
88  //Note: For duplicate blocks, this will give the seeds to the rank that are first in the list.
89  if (!ids.empty())
90  {
91  auto ranks = this->BoundsMap.FindRank(ids[0]);
92  if (!ranks.empty() && this->Rank == ranks[0])
93  {
94  particles.emplace_back(p);
95  blockIDs.emplace_back(ids);
96  }
97  }
98  }
99  this->SetSeedArray(particles, blockIDs);
100  }
101 
102  virtual bool HaveWork()
103  {
104  const bool haveParticles = !this->Active.empty() || !this->Inactive.empty();
105 #ifndef VTKM_ENABLE_MPI
106  return haveParticles;
107 #else
108  return haveParticles || this->Exchanger.HaveWork();
109 #endif
110  }
111 
112  virtual bool GetDone()
113  {
114 #ifndef VTKM_ENABLE_MPI
115  return !this->HaveWork();
116 #else
117  return this->Terminator.Done();
118 #endif
119  }
120 
121  //Advect all the particles.
122  virtual void Go()
123  {
124  while (!this->GetDone())
125  {
126  std::vector<ParticleType> v;
127  vtkm::Id blockId = -1;
128 
129  if (this->GetActiveParticles(v, blockId))
130  {
131  //make this a pointer to avoid the copy?
132  auto& block = this->GetDataSet(blockId);
133  DSIHelperInfo<ParticleType> bb(v, this->BoundsMap, this->ParticleBlockIDsMap);
134  block.Advect(bb, this->StepSize);
135  this->UpdateResult(bb);
136  }
137 
138  this->ExchangeParticles();
139  }
140  }
141 
142  virtual void ClearParticles()
143  {
144  this->Active.clear();
145  this->Inactive.clear();
146  this->ParticleBlockIDsMap.clear();
147  }
148 
149  DataSetIntegrator<DSIType, ParticleType>& GetDataSet(vtkm::Id id)
150  {
151  for (auto& it : this->Blocks)
152  if (it.GetID() == id)
153  return it;
154 
155  throw vtkm::cont::ErrorFilterExecution("Bad block");
156  }
157 
158  virtual void SetSeedArray(const std::vector<ParticleType>& particles,
159  const std::vector<std::vector<vtkm::Id>>& blockIds)
160  {
161  VTKM_ASSERT(particles.size() == blockIds.size());
162 
163  auto pit = particles.begin();
164  auto bit = blockIds.begin();
165  while (pit != particles.end() && bit != blockIds.end())
166  {
167  vtkm::Id blockId0 = (*bit)[0];
168  this->ParticleBlockIDsMap[pit->GetID()] = *bit;
169  if (this->Active.find(blockId0) == this->Active.end())
170  this->Active[blockId0] = { *pit };
171  else
172  this->Active[blockId0].emplace_back(*pit);
173  pit++;
174  bit++;
175  }
176  }
177 
178  virtual bool GetActiveParticles(std::vector<ParticleType>& particles, vtkm::Id& blockId)
179  {
180  particles.clear();
181  blockId = -1;
182  if (this->Active.empty())
183  return false;
184 
185  //If only one, return it.
186  if (this->Active.size() == 1)
187  {
188  blockId = this->Active.begin()->first;
189  particles = std::move(this->Active.begin()->second);
190  this->Active.clear();
191  }
192  else
193  {
194  //Find the blockId with the most particles.
195  std::size_t maxNum = 0;
196  auto maxIt = this->Active.end();
197  for (auto it = this->Active.begin(); it != this->Active.end(); it++)
198  {
199  auto sz = it->second.size();
200  if (sz > maxNum)
201  {
202  maxNum = sz;
203  maxIt = it;
204  }
205  }
206 
207  if (maxNum == 0)
208  {
209  this->Active.clear();
210  return false;
211  }
212 
213  blockId = maxIt->first;
214  particles = std::move(maxIt->second);
215  this->Active.erase(maxIt);
216  }
217 
218  return !particles.empty();
219  }
220 
221  void ExchangeParticles()
222  {
223 #ifndef VTKM_ENABLE_MPI
224  this->SerialExchange();
225 #else
226  // MPI with only 1 rank.
227  if (this->NumRanks == 1)
228  this->SerialExchange();
229  else
230  {
231  std::vector<ParticleType> outgoing;
232  std::vector<vtkm::Id> outgoingRanks;
233 
234  this->GetOutgoingParticles(outgoing, outgoingRanks);
235 
236  std::vector<ParticleType> incoming;
237  std::unordered_map<vtkm::Id, std::vector<vtkm::Id>> incomingBlockIDs;
238 
239  this->Exchanger.Exchange(
240  outgoing, outgoingRanks, this->ParticleBlockIDsMap, incoming, incomingBlockIDs);
241 
242  //Cleanup what was sent.
243  for (const auto& p : outgoing)
244  this->ParticleBlockIDsMap.erase(p.GetID());
245 
246  this->UpdateActive(incoming, incomingBlockIDs);
247  }
248 
249  this->Terminator.Control(this->HaveWork());
250 #endif
251  }
252 
253  void SerialExchange()
254  {
255  for (const auto& p : this->Inactive)
256  {
257  const auto& bid = this->ParticleBlockIDsMap[p.GetID()];
258  VTKM_ASSERT(!bid.empty());
259  this->Active[bid[0]].emplace_back(std::move(p));
260  }
261  this->Inactive.clear();
262  }
263 
264 #ifdef VTKM_ENABLE_MPI
265  void GetOutgoingParticles(std::vector<ParticleType>& outgoing,
266  std::vector<vtkm::Id>& outgoingRanks)
267  {
268  outgoing.clear();
269  outgoingRanks.clear();
270 
271  outgoing.reserve(this->Inactive.size());
272  outgoingRanks.reserve(this->Inactive.size());
273 
274  std::vector<ParticleType> particlesStaying;
275  std::unordered_map<vtkm::Id, std::vector<vtkm::Id>> particlesStayingBlockIDs;
276  //Send out Everything.
277  for (const auto& p : this->Inactive)
278  {
279  const auto& bid = this->ParticleBlockIDsMap[p.GetID()];
280  VTKM_ASSERT(!bid.empty());
281 
282  auto ranks = this->BoundsMap.FindRank(bid[0]);
283  VTKM_ASSERT(!ranks.empty());
284 
285  //Only 1 rank has the block.
286  if (ranks.size() == 1)
287  {
288  if (ranks[0] == this->Rank)
289  {
290  particlesStaying.emplace_back(p);
291  particlesStayingBlockIDs[p.GetID()] = this->ParticleBlockIDsMap[p.GetID()];
292  }
293  else
294  {
295  outgoing.emplace_back(p);
296  outgoingRanks.emplace_back(ranks[0]);
297  }
298  }
299  else
300  {
301  //Multiple ranks have the block, decide where it should go...
302 
303  //Random selection:
304  vtkm::Id outRank = std::rand() % ranks.size();
305  if (outRank == this->Rank)
306  {
307  particlesStayingBlockIDs[p.GetID()] = this->ParticleBlockIDsMap[p.GetID()];
308  particlesStaying.emplace_back(p);
309  }
310  else
311  {
312  outgoing.emplace_back(p);
313  outgoingRanks.emplace_back(outRank);
314  }
315  }
316  }
317 
318  this->Inactive.clear();
319  VTKM_ASSERT(outgoing.size() == outgoingRanks.size());
320 
321  VTKM_ASSERT(particlesStaying.size() == particlesStayingBlockIDs.size());
322  if (!particlesStaying.empty())
323  this->UpdateActive(particlesStaying, particlesStayingBlockIDs);
324  }
325 #endif
326 
327  virtual void UpdateActive(const std::vector<ParticleType>& particles,
328  const std::unordered_map<vtkm::Id, std::vector<vtkm::Id>>& idsMap)
329  {
330  VTKM_ASSERT(particles.size() == idsMap.size());
331 
332  if (!particles.empty())
333  {
334  for (auto pit = particles.begin(); pit != particles.end(); pit++)
335  {
336  vtkm::Id particleID = pit->GetID();
337  const auto& it = idsMap.find(particleID);
338  VTKM_ASSERT(it != idsMap.end() && !it->second.empty());
339  vtkm::Id blockId = it->second[0];
340  this->Active[blockId].emplace_back(*pit);
341  }
342 
343  for (const auto& it : idsMap)
344  this->ParticleBlockIDsMap[it.first] = it.second;
345  }
346  }
347 
348  virtual void UpdateInactive(const std::vector<ParticleType>& particles,
349  const std::unordered_map<vtkm::Id, std::vector<vtkm::Id>>& idsMap)
350  {
351  VTKM_ASSERT(particles.size() == idsMap.size());
352 
353  this->Inactive.insert(this->Inactive.end(), particles.begin(), particles.end());
354  for (const auto& it : idsMap)
355  this->ParticleBlockIDsMap[it.first] = it.second;
356  }
357 
358  vtkm::Id UpdateResult(const DSIHelperInfo<ParticleType>& stuff)
359  {
360  this->UpdateActive(stuff.InBounds.Particles, stuff.InBounds.BlockIDs);
361  this->UpdateInactive(stuff.OutOfBounds.Particles, stuff.OutOfBounds.BlockIDs);
362 
363  vtkm::Id numTerm = static_cast<vtkm::Id>(stuff.TermID.size());
364  //Update terminated particles.
365  if (numTerm > 0)
366  {
367  for (const auto& id : stuff.TermID)
368  this->ParticleBlockIDsMap.erase(id);
369  }
370 
371  return numTerm;
372  }
373 
374  //Member data
375  // {blockId, std::vector of particles}
376  std::unordered_map<vtkm::Id, std::vector<ParticleType>> Active;
377  std::vector<DSIType> Blocks;
378  vtkm::filter::flow::internal::BoundsMap BoundsMap;
379  vtkmdiy::mpi::communicator Comm = vtkm::cont::EnvironmentTracker::GetCommunicator();
380 #ifdef VTKM_ENABLE_MPI
381  ParticleExchanger<ParticleType> Exchanger;
382  AdvectAlgorithmTerminator Terminator;
383 #endif
384  std::vector<ParticleType> Inactive;
385  vtkm::Id MaxNumberOfSteps = 0;
386  vtkm::Id NumRanks;
387  //{particleId : {block IDs}}
388  std::unordered_map<vtkm::Id, std::vector<vtkm::Id>> ParticleBlockIDsMap;
389  vtkm::Id Rank;
390  vtkm::FloatDefault StepSize;
391 };
392 
393 }
394 }
395 }
396 } //vtkm::filter::flow::internal
397 
398 #endif //vtk_m_filter_flow_internal_AdvectAlgorithm_h
vtkm::cont::ArrayHandle
Manages an array-worth of data.
Definition: ArrayHandle.h:300
BoundsMap.h
vtkm
Groups connected points that have the same field value.
Definition: Atomic.h:19
VTKM_ASSERT
#define VTKM_ASSERT(condition)
Definition: Assert.h:43
vtkm::cont::ErrorFilterExecution
This class is primarily intended to filters to throw in the control environment to indicate an execut...
Definition: ErrorFilterExecution.h:27
vtkm::cont::DataSet
Contains and manages the geometric data structures that VTK-m operates on.
Definition: DataSet.h:57
vtkm::cont::ArrayHandle::GetNumberOfValues
vtkm::Id GetNumberOfValues() const
Returns the number of entries in the array.
Definition: ArrayHandle.h:468
DataSetIntegrator.h
VTKM_ENABLE_MPI
#define VTKM_ENABLE_MPI
Definition: Configure.h:309
vtkm::cont::PartitionedDataSet::AppendPartition
void AppendPartition(const vtkm::cont::DataSet &ds)
Add DataSet ds to the end of the list of partitions.
AdvectAlgorithmTerminator.h
vtkm::Id
vtkm::Int64 Id
Base type to use to index arrays.
Definition: Types.h:227
vtkm::cont::EnvironmentTracker::GetCommunicator
static const vtkmdiy::mpi::communicator & GetCommunicator()
vtkm::cont::ArrayHandle::ReadPortal
ReadPortalType ReadPortal() const
Get an array portal that can be used in the control environment.
Definition: ArrayHandle.h:433
vtkm::FloatDefault
vtkm::Float32 FloatDefault
The floating point type to use when no other precision is specified.
Definition: Types.h:236
PartitionedDataSet.h
ParticleExchanger.h
vtkm::cont::PartitionedDataSet
Comprises a set of vtkm::cont::DataSet objects.
Definition: PartitionedDataSet.h:26